您好,登錄后才能下訂單哦!
這篇文章主要講解了“Solidity故障怎么排查”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Solidity故障怎么排查”吧!
1、推薦編輯器
目前嘗試 Solidity 編程的最好的方式是使用 Remix(https://remix.ethereum.org/) (需要時間加載,請耐心等待)。Remix 是一個基于 Web 的 IDE,它可以讓你編寫 Solidity 智能合約,然后部署并運行該智能合約。
2、Visual Studio Extension
Microsoft Visual Studio 的 Solidity 插件,包含 Solidity 編譯器。
3、Visual Studio Code extension
Microsoft Visual Studio Code 插件,包含語法高亮和 Solidity 編譯器。
function mint(address receiver, uint amount)
1、在REMIX輸入時,地址一定要有""表示,否則amount就取不到值。
例如是mint("0xca35b7d915458ef540ade6068dfe2f44e8fa733c",101)
2、程序中,如果注釋包含中文,單步調試的位置不準確。
<address>.balance (uint256): 該地址有多少以太坊余額(wei為單位)
<address>.transfer(uint256 amount): 發送特定數量(wei為單位)的以太坊到對應地址,當出現錯誤時會扔出異常,但不會因異常而停止。固定需要耗費2300個gas。
<address>.send(uint256 amount) returns (bool): 發送特定數量(wei為單位)的以太坊到對應地址,當出現錯誤時會返回flase。固定需要耗費2300個gas。
【警告】send() 執行有一些風險:如果調用棧的深度超過1024或gas耗光,交易都會失敗。因此,為了保證安全,必須檢查send的返回值,如果交易失敗,會回退以太幣。如果用transfer會更好。
<address>.call(...) returns (bool): CALL的低級調用函數,當失敗時返回false。執行需要消耗不固定的gas。
【說明】不鼓勵使用call函數,后期將會被移除。調用該函數可能造成安全攻擊,詳見后期安全相關文章。
<address>.callcode(...) returns (bool): CALLCODE的低級調用函數,當失敗時返回false。執行需要消耗不固定的gas。
不建議使用,后續版本會刪除。
<address>.delegatecall(...) returns (bool): DELEGATECALL的低級調用函數,當失敗時返回false。執行需要消耗不固定的gas。
【說明】為了和非ABI協議的合約進行交互,可以使用call() 函數, 它用來向另一個合約發送原始數據,支持任何類型任意數量的參數,每個參數會按規則(ABI協議)打包成32字節并一一拼接到一起。一個例外是:如果第一個參數恰好4個字節,在這種情況下,會被認為根據ABI協議定義的函數器指定的函數簽名而直接使用。如果僅想發送消息體,需要避免第一個參數是4個字節。如下面的例子:
function callfunc(address addr) returns (bool){ bytes4 methodId = bytes4(keccak256("setScore(uint256)")); return addr.call(methodId, 100); }
測試地址和地址調用代碼舉例
pragma solidity ^0.4.18; contract AddrTest{ /*event函數知識把參數信息打印在REMIX等編譯器的LOG位置區,不需要函數定義。*/ event logdata(bytes data); event LogContractAddress(address exAccount, address contractAddress); uint score = 0; /*回調函數,沒有函數名。任何調用不存在的函數,這時被調用的合約的fallback函數會執行。 payable:如果一個函數需要進行貨幣操作,必須要帶上payable關鍵字*/ function() payable { logdata(msg.data); } /*智能合約構建函數*/ function AddrTest(){ LogContractAddress(msg.sender,this); } function getBalance() returns (uint) { return this.balance; } function setScore(uint s) public { score = s; } function getScore() returns ( uint){ return score; } } contract CallTest{ /*該函數有函數申明沒有實際函數內容,在remix的value區域設置以太坊的個數,調用該函數會把外部賬戶(ACCOUNT)中的 以太坊轉移到智能合約賬戶中*/ function deposit() payable { } event logSendEvent(address to, uint value); event LogContractAddress(address exAccount, address contractAddress); /*轉以太坊給目標地址*/ function transferEther(address towho) payable { towho.transfer(10);/*單位為wei*/ logSendEvent(towho, 10); } /*不指定調用函數,則調用無函數名的回調函數*/ function callNoFunc(address addr) returns (bool){ return addr.call("tinyxiong", 1234); } /*制定調用函數的方法*/ function callfunc(address addr) returns (bool){ bytes4 methodId = bytes4(keccak256("setScore(uint256)")); return addr.call(methodId, 100); } /*返回當前合約的以太坊余額*/ function getBalance() returns (uint) { return this.balance;//0 } /*銷毀智能合約,把以太坊余額返回給當前外部賬戶*/ function ContractSuide() { LogContractAddress(this,msg.sender); suicide(msg.sender); } }
this (current contract’s type): 表示當前合約,可以顯式的轉換為Address
selfdestruct(address recipient): destroy the current contract, sending its funds to the given Address
銷毀當前合約,發送當前以太坊余額到給定的地址 suicide(address recipient):
selfdestruct的別名函數
**block.blockhash(uint blockNumber) returns (bytes32): **
給定區塊的哈希—僅對最近的 256 個區塊有效而不包括當前區塊
**block.coinbase (address): **
挖出當前區塊的礦工地址
**block.difficulty (uint): **
當前區塊難度
block.gaslimit (uint):
當前區塊 gas 限額
**block.number (uint): **
當前區塊號
block.timestamp (uint):
自 unix epoch 起始當前區塊以秒計的時間戳
**msg.data (bytes): **
完整的 calldata
**msg.gas (uint): **
剩余 gas
msg.sender (address):
消息發送者(當前調用)
**msg.sig (bytes4): **
calldata 的前 4 字節(也就是函數標識符)
msg.value (uint):
隨消息發送的 wei 的數量
**now (uint): **
目前區塊時間戳(block.timestamp)
**tx.gasprice (uint): **
交易的 gas 價格
**tx.origin (address): **
交易發起者(完全的調用鏈)
注解 對于每一個外部函數調用,包括 msg.sender 和 msg.value 在內所有 msg 成員的值都會變化。這里包括對庫函數的調用。
注解 不要依賴 block.timestamp、 now 和 block.blockhash 產生隨機數,除非你知道自己在做什么。
時間戳和區塊哈希在一定程度上都可能受到挖礦礦工影響。例如,挖礦社區中的惡意礦工可以用某個給定的哈希來運行賭場合約的 payout 函數,而如果他們沒收到錢,還可以用一個不同的哈希重新嘗試。 當前區塊的時間戳必須嚴格大于最后一個區塊的時間戳,但這里唯一能確保的只是它會是在權威鏈上的兩個連續區塊的時間戳之間的數值。
注解 基于可擴展因素,區塊哈希不是對所有區塊都有效。你僅僅可以訪問最近 256 個區塊的哈希,其余的哈希均為零。
assert(bool condition): 如果條件不滿足就拋出—用于內部錯誤。
require(bool condition): 如果條件不滿足就拋掉—用于輸入或者外部組件引起的錯誤。
revert(): 終止運行并恢復狀態變動。
addmod(uint x, uint y, uint k) returns (uint): 計算 (x + y) % k,加法會在任意精度下執行,并且加法的結果即使超過 2**256 也不會被截取。從 0.5.0 版本的編譯器開始會加入對 k != 0 的校驗(assert)。
mulmod(uint x, uint y, uint k) returns (uint): 計算 (x * y) % k,乘法會在任意精度下執行,并且乘法的結果即使超過 2**256 也不會被截取。從 0.5.0 版本的編譯器開始會加入對 k != 0 的校驗(assert)。
keccak256(...) returns (bytes32): 計算 (tightly packed) arguments 的 Ethereum-SHA-3 (Keccak-256)哈希。
sha256(...) returns (bytes32): 計算 (tightly packed) arguments 的 SHA-256 哈希。
sha3(...) returns (bytes32): 等價于 keccak256。
ripemd160(...) returns (bytes20): 計算 (tightly packed) arguments 的 RIPEMD-160 哈希。
ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address) : 利用橢圓曲線簽名恢復與公鑰相關的地址,錯誤返回零值。(example usage:https://ethereum.stackexchange.com/q/1777/222)
上文中的“tightly packed”是指不會對參數值進行 padding 處理(就是說所有參數值的字節碼是連續存放的,譯者注),這意味著下邊這些調用都是等價的:
keccak256("ab", "c") keccak256("abc") keccak256(0x616263) keccak256(6382179) keccak256(97, 98, 99)
如果需要 padding,可以使用顯式類型轉換:keccak256("\x00\x12") 和 keccak256(uint16(0x12)) 是一樣的。
請注意,常量值會使用存儲它們所需要的最少字節數進行打包。例如:keccak256(0) == keccak256(uint8(0)),keccak256(0x12345678) == keccak256(uint32(0x12345678))。
在一個私鏈上,你很有可能碰到由于 sha256、ripemd160 或者 ecrecover 引起的 Out-of-Gas。這個原因就是他們被當做所謂的預編譯合約而執行,并且在第一次收到消息后這些合約才真正存在(盡管合約代碼是硬代碼)。發送到不存在的合約的消息非常昂貴,所以實際的執行會導致 Out-of-Gas 錯誤。在你的合約中實際使用它們之前,給每個合約發送一點兒以太幣,比如 1 Wei。這在官方網絡或測試網絡上不是問題。
1、智能合約執行失敗
告警描述: " Warning! Error encountered during contract execution [Out of gas] "
發生場景: 執行官網眾籌智能合約代碼,在給智能合約打代幣前,往智能合約地址賬戶打ETH,交易失敗,提示如下:
點擊查看信息鏈接:https://ropsten.etherscan.io/tx/0x8b4da573b36dbf7361c95a0156dfe060553874fbb4d401f989c7a4a6d539ebfa
代碼及原因分析: 下面是執行的回調函數,其中“tokenReward.transfer(msg.sender, amount / price);”的意思就是把智能合約的代幣打給發送者賬號。這個賬號還沒有代幣,所以肯定會執行失敗。
function () payable { require(!crowdsaleClosed); uint amount = msg.value; balanceOf[msg.sender] += amount; amountRaised += amount; tokenReward.transfer(msg.sender, amount / price); FundTransfer(msg.sender, amount, true); }
解決方法: 先往智能合約賬號打代幣,然后打ETH,就不會執行失敗了。
2、 REMIX+MetaMASK的場景下,調用智能合約函數,提示不可知地址
告警描述:
REMIX輸出框輸出以下告警內容: transact to Crowdsale.checkGoalReached errored: Unknown address - unable to sign transaction for this address: "0x3d7dfb80e71096f2c4ee63c42c4d849f2cbbe363"
發生場景: 更換了Meta賬號后,調用之前賬號創建的智能合約的函數
原因分析: Remix輸出框提示未知地址:
告警描述
查看MetaMask的當前賬號,發現是Account 1的賬號,所以無法執行合約。
MetaMask的當前賬號
解決方法:
更換MetaMask為Account8(地址為0x3D7DfB80E71096F2c4Ee63C42C4D849F2CBBE363)的賬號即可正常運行。
3、GETH安裝時出現異常
告警描述: GETH安裝時出現以下告警:
E: Failed to fetch http://101.110.118.22/ppa.launchpad.net/ethereum/ethereum/ubuntu/pool/main/e/ethereum/ethereum_1.8.10+build13740+artful_i386.deb Could not connect to 101.110.118.22:80 (101.110.118.22), connection timed out
E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?
發生場景:
GETH安裝,執行以下命令出現。
sudo apt-get install ethereum
原因分析:
解決方法:
<1> 參考網上解決方案 sudo vim /etc/resolv.conf ,添加:
nameserver 8.8.8.8
然后執行:
sudo /etc/init.d/networking restart
還是不行。
<2> 從綠地金融中心搬到家里,做相同的操作,就可以安裝成功了。 應該是綠地金融中心的路由器做了某些配置,導致下載不成功。
4、問題描述模板
告警描述: 發生場景: 原因分析: 解決方法:
1).modifer函數是干什么的?
2).如何打幣回支付賬號?
3).智能合約的定時器和系統函數是什么?
4).當創建一個智能合約時,msg.sender和this的區別?
答復:msg.sender是指外部賬戶的地址,this是指當前創建的智能合約的地址。
contract AddrTest{ event LogContractAddress(address exAccount, address contractAddress); function AddrTest(){ LogContractAddress(msg.sender,this); } }
在remix運行,可以證明這個推測
LOG說明
感謝各位的閱讀,以上就是“Solidity故障怎么排查”的內容了,經過本文的學習后,相信大家對Solidity故障怎么排查這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。