智能合約太聰明了:不同版本的 geth 或 solidity 的智能合約的不同行為
最近我正在學習如何在乙太坊上部署和呼叫智能合約。我正在製定一個非常簡單的契約,如下所示:
pragma solidity >=0.4.24 <0.6.0; contract TestContract{ uint public a; constructor() public { a = 2; } function addNumber(uint b) public { a = a + b; } function read() public view returns (uint) { return a; } }
我首先嘗試在我的私有鏈上部署合約,geth 是從目前主預設 go-ethereum 原始碼編譯而來的。雖然可以成功部署合約,但是
addNumber
通過web3呼叫函式會拋出以下錯誤gas required exceeds allowance (1726103387) or always failing transaction
。我不知道為什麼會這樣,因為我的私鏈正在挖礦,而且我的賬戶有足夠的乙太幣給這麼小的合約程式碼。然後我在舊版本的 go-ethereum 上部署了合約,一開始部署和呼叫函式
addNumber
看起來都很好,web3 呼叫沒有返回任何錯誤,但後來我發現函式addNumber
實際上並沒有增加(b = 1
在我的測試中case),a
儘管呼叫了很多次,但仍為 0。我還檢查了 geth 中的相應交易,並了解到它們已經在開采的區塊中退出。然後我改為使用solidity ^0.4.0 編譯合約程式碼(同時刪除建構子和關鍵字視圖,因為solidity ^0.4.0 不支持它們),並部署在鏈上。在這種情況下,該合約適用於兩個版本的 go-ethereum。即,
a
每當呼叫= 1的函式addNumber
時都會增加b
所以我的問題是:
- 為什麼呼叫
addNumber
最新版本的 go-ethereum 會引發錯誤:所需的氣體超過限額(1726103387)或總是失敗的交易。- 為什麼同一個合約在不同版本的 go-ethereum 和solidity 中表現完全不同。
- 在我看來,智能合約“太聰明了”。有什麼想法可以在不同版本的 go-ethereum 和solidity 之間進行選擇嗎?
你的合約中的任何內容都不應該燃燒接近 300 萬個 gas,所以你可以很確定這是一個讓合約恢復的恐慌性 gas 消耗。很多時候,所有剩餘的氣體都會被破壞以使 evm 中止,並且再多的氣體也無法解決最初的問題。
然後我改用solidity ^0.4.0編譯合約程式碼
你在球場上。
問題是 EVM 更改。你沒有說什麼版本的 Geth 和什麼版本的 Solidity,但這些都是關鍵因素。
一般來說,我不喜歡
^
在編譯指示中,因為它沒有明確,從快速瀏覽程式碼,正在使用什麼編譯器,或者應該使用什麼編譯器來複製結果。而且,事情是這樣的……硬分叉是協議更改。對於拜占庭,這些更改影響了界面。添加了預期長度,以支持函式簽名中的字元串和其他可變長度參數。因此,有必要遷移到支持該功能的更新編譯器。
在實踐中,這意味著如果您使用 < 0.4.2.(4?) 的編譯器編譯程式碼,那麼您必須使用低於某個版本(拜占庭之前)的 geth、ganache-cli 等,否則它將無法工作。同樣,如果 geth、ganache-cli 是更新的,那麼您必須使用更新的編譯器進行編譯。
希望能幫助到你。
ps 作為一種風格,我傾向於在頂級合約中使用精確的編譯指示,因為這樣可以消除歧義,並且
^
在繼承的模組中,為了方便並儘量避免修補解決良好的庫。就像是
pragma solidity 0.5.8; contract MyContract { using SafeMath for uint;
即使 SafeMath 包含
^
or>= <=
,毫無疑問它是用 0.5.8 編譯和測試的。