Solidity
來自 web3 的持續合約呼叫返回不一致的值
我在這裡的主網上有這個 dapp 。
以下合約已部署在此地址:
pragma solidity ^0.4.19; import 'zeppelin-solidity/contracts/ownership/Ownable.sol'; import 'zeppelin-solidity/contracts/lifecycle/Destructible.sol'; import 'zeppelin-solidity/contracts/lifecycle/Pausable.sol'; import 'zeppelin-solidity/contracts/math/SafeMath.sol'; contract CryptoTwittos is Ownable, Pausable, Destructible { using SafeMath for uint; // A Twitto is owned by a stealer and has a price struct Twitto { address stealer; uint price; } // Look up Twitto by ids mapping(uint => Twitto) public twittos; // All Twitto ids and counter uint[] public twittoIds; uint public twittosCounter; // Fire event when steal happens event stealEvent( uint indexed id, address indexed owner, uint price, address indexed stealer, uint newPrice ); // Get twittoIds function getTwittoIds(bool all) public view returns (uint[]) { // Return empty array if counter is zero if (twittosCounter == 0) return new uint[](0); if (all) { // Return all of them return twittoIds; } else { // Create memory array to store filtered ids uint[] memory filteredIds = new uint[](twittosCounter); // Store number of belongings uint twittosCount = 0; for (uint i = 0; i < twittosCounter; i++) { // Check if stealer is sender if (twittos[twittoIds[i]].stealer == msg.sender) { filteredIds[twittosCount] = twittoIds[i]; twittosCount++; } } // Copy the filteredIds array into a smaller array uint[] memory trophies = new uint[](twittosCount); for (uint j = 0; j < twittosCount; j++) { trophies[j] = filteredIds[j]; } return trophies; } } // Steal a Twitto by paying its price and setting a new one function steal(uint id, uint256 newPrice) payable whenNotPaused public { // look up the twitto and put on storage Twitto storage _twitto = twittos[id]; // Prevent self stealing! require(msg.sender != _twitto.stealer); // Make sure the sender pays the right price require(msg.value == _twitto.price); // Make sure that the new price is higher than the old price require(newPrice > _twitto.price); // Transfer value with the 1% dev fee if (msg.value > 0) { _twitto.stealer.transfer(msg.value.mul(99).div(100)); } // Push new Twitto if not existing if (_twitto.price == 0) { twittoIds.push(id); twittosCounter++; } // Trigger event stealEvent(id, _twitto.stealer, _twitto.price, msg.sender, newPrice); // Store new stealer _twitto.stealer = msg.sender; // Store new price _twitto.price = newPrice; } function withdraw() public onlyOwner { // Transfer balance to owner msg.sender.transfer(address(this).balance); } }
引起麻煩的常量方法是
getTwittoIds
:一切正常,但是數組的大小增加了一點(正如您可以看到Etherscan上的事務數),現在這個常量方法呼叫有時會返回一個空數組有時不是。閱讀類似這樣的問題時,我認為這是由於氣體限製過低(對於不斷呼叫),但問題仍然存在。
重現此“錯誤”的最簡單步驟如下:
- 轉到https://cryptotwittos.com/
- 確保 MetaMask 已啟用
- 打開控制台
- 與 abi 簽訂契約:
var CT = web3.eth.contract([{"constant":true,"inputs":[],"name":"twittosCounter","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"destroy","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"twittos","outputs":[{"name":"stealer","type":"address"},{"name":"price","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"twittoIds","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_recipient","type":"address"}],"name":"destroyAndSend","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"id","type":"uint256"},{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"price","type":"uint256"},{"indexed":true,"name":"stealer","type":"address"},{"indexed":false,"name":"newPrice","type":"uint256"}],"name":"stealEvent","type":"event"},{"anonymous":false,"inputs":[],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"constant":true,"inputs":[{"name":"all","type":"bool"}],"name":"getTwittoIds","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"newPrice","type":"uint256"}],"name":"steal","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}])
- 獲取實例
var CTInstance = CT.at("0xa4b9054417ee4f06453152a45ebeba7786c84c66")
- 應返回所有 TwittoIds 的呼叫方法
CTInstance.getTwittoIds(true, console.log)
- 說服自己該方法不應該返回一個空數組
CTInstance.twittosCounter(console.log)
經過兩天的研究,我仍然不知道發生了什麼,所以任何幫助將不勝感激。
非常感謝!
我嘗試在
geth
節點上執行它並且確實返回0x
但是在奇偶校驗節點上執行它會返回一個巨大的輸出(我想這就是你需要的)
> eth.call({to:"0xa4b9054417ee4f06453152a45ebeba7786c84c66", data:"0xd15e656c0000000000000000000000000000000000000000000000000000000000000001", gas: "440000"})
輸出: https ://gist.github.com/cleanunicorn/b90fc5141d157e7dcc861522af25cb5a
該操作消耗的氣體接近
440000
並且會增加。我還沒有檢查過 geth 原始碼,但似乎它在停止之前可以執行多少是有限制的。
Infura 有
geth
節點,這就是為什麼(如果你正在使用它們)它返回0x
. 可能他們也在嘗試parity
或自定義建構,有時您會得到正確的結果