Solidity
同一合約的 Gas 使用量不斷增長
這是智能合約。該契約的天然氣使用量不斷增長。所有狀態變數都在部署時初始化並且永遠不會改變。Signers 數組的大小不再是 2-3 左右。而這份合約從使用 150,000 個gas 到現在的 600,000 個。
程式碼有任何明顯的問題嗎?
這是一些交易ID:
https://etherscan.io/tx/0x26f13d3b3d0809ffc24f22b5f33966aa0c144e433cbe239649df5f00f3d29740 (使用氣體:117,013)
https://etherscan.io/tx/0xfcf395bd21ce4e69857184ee3d7b2164f3d0d8536b666dad46d5be8455aed73c(使用氣體:681,795)
程式碼:
pragma solidity ^0.4.24; contract MultiSigContract { mapping (address => bool) public IsSigner; address public Executer; address[] public Signers; function MultiSigContract(address[] _signers, address _executer) public { require(_executer != 0x0); Signers = _signers; Executer = _executer; for (uint i = 0; i < Signers.length; i++) { IsSigner[Signers[i]] = true; } } function execute(address destination, uint amount, uint8[] sigV, bytes32[] sigR, bytes32[] sigS) public { require(sigR.length == sigS.length && sigR.length == sigV.length); require(sigR.length == Signers.length); require(msg.sender == Executer); require(address(this).balance >= amount); address[] recoveredAddresses; bytes32 txHash = keccak256("\x19Ethereum Signed Message:\n72", this, destination, amount); for(uint8 i = 0; i < Signers.length; i++) { address recovered = ecrecover(txHash, sigV[i], sigR[i], sigS[i]); require(IsSigner[recovered] == true); recoveredAddresses.push(recovered); } for(uint8 j = 0; j < Signers.length; j++) { require(contains(Signers[j], recoveredAddresses)); } destination.transfer(amount); } function contains(address _address, address[] _addressArray) private pure returns (bool) { for(uint8 i = 0; i < _addressArray.length; i++) { if (_addressArray[i] == _address) { return true; } } return false; } function () public payable {} }
這是因為
recoveredAddresses
沒有標記為記憶體,所以預設情況下,它在儲存中。每次推送時,長度都會增加,即使跨事務呼叫也是如此。這也意味著該函式必須在每次執行時contains
迭代一個越來越大的數組,隨著時間的推移增加氣體成本。addressArray
解決方案是通過將其寫入來製作recoveredAddresses
一個記憶體數組address[] recoveredAddresses;
。這也意味著您必須圍繞調整大小進行一些返工,因為您不能擁有動態調整大小的記憶體數組。大小必須在編譯時知道。如果數組大小的上限很小,這非常簡單,否則可能會變得複雜。您可以通過查看“狀態更改”選項卡並展開合約地址來查看此範例。您不斷地將新恢復的地址附加到該數組中。