Contract-Invocation

簡單智能合約的非確定性結果

  • January 28, 2018

我目前正在調試一個更複雜的智能合約,由於我不明白的原因,儘管輸入相同,但它的行為不同。更準確地說,它在多次部署時為相同的輸入返回不同的值。我試圖盡可能簡化契約和對 web3.py 的呼叫。以下智能合約是我能想到的最小工作範例,但仍然存在此問題:

pragma solidity ^0.4.0;

contract TestContract
{
   bytes32 recorded_value;
   bytes32 recorded_salt; //Unused, but required for the problem to appear

   function TestContract(bytes32 value, bytes32 salt) public
   {
       recorded_value = value;
   }

   function getHash() public constant
       returns (bytes32)
   {
       return keccak256(recorded_value);
   }
}

這就是我編譯和呼叫智能合約的方式:

#Based on the source code from https://github.com/pipermerriam/web3.py

import json
import web3

from web3 import Web3, HTTPProvider, TestRPCProvider
from solc import compile_source
from web3.contract import ConciseContract

w3 = Web3(TestRPCProvider())

def deploy_smart_contract(source_code_file_path, contract_name, constructor_arguments):
 with open(source_code_file_path) as f:
   commitment_helper_source_code = f.read()
 compiled_sol = compile_source(commitment_helper_source_code)
 contract_interface = compiled_sol['<stdin>:' + contract_name]
 smart_contract = w3.eth.contract(contract_interface['abi'], bytecode=contract_interface['bin'])
 tx_hash = smart_contract.deploy(transaction={'from': w3.eth.accounts[0]}, args=constructor_arguments)
 tx_receipt = w3.eth.getTransactionReceipt(tx_hash)
 smart_contract_address = tx_receipt['contractAddress']
 deployed_smart_contract = w3.eth.contract(contract_interface['abi'], smart_contract_address, ContractFactoryClass=ConciseContract)
 return deployed_smart_contract, smart_contract

def print_hex(char_array):
 output = "0x";
 for char in char_array:
   output += str("%02x" % ord(char))
 return output

contract_instance, contract = deploy_smart_contract('contract.sol', 'TestContract', {Web3.toBytes(123), Web3.toBytes(100)})

value = contract_instance.getHash()
print("Hash: " + print_hex(value))

如果我多次執行此程式碼,結果如下所示:

(.venv-py3) andreas@***:/media/sf_temp$ python test.py
127.0.0.1 - - [27/Jan/2018 15:39:56] "POST / HTTP/1.1" 200 499
127.0.0.1 - - [27/Jan/2018 15:39:56] "POST / HTTP/1.1" 200 48
127.0.0.1 - - [27/Jan/2018 15:39:56] "POST / HTTP/1.1" 200 44
127.0.0.1 - - [27/Jan/2018 15:39:56] "POST / HTTP/1.1" 200 1350
127.0.0.1 - - [27/Jan/2018 15:39:56] "POST / HTTP/1.1" 200 107
127.0.0.1 - - [27/Jan/2018 15:39:56] "POST / HTTP/1.1" 200 391
127.0.0.1 - - [27/Jan/2018 15:39:56] "POST / HTTP/1.1" 200 391
Hash: 0x4cab01aa70c7d27ce12007ae744261ed3e0ee9162bc588f68526ea903ec2eb99
(.venv-py3) andreas@***:/media/sf_temp$ python test.py
127.0.0.1 - - [27/Jan/2018 15:39:58] "POST / HTTP/1.1" 200 499
127.0.0.1 - - [27/Jan/2018 15:39:58] "POST / HTTP/1.1" 200 48
127.0.0.1 - - [27/Jan/2018 15:39:58] "POST / HTTP/1.1" 200 44
127.0.0.1 - - [27/Jan/2018 15:39:58] "POST / HTTP/1.1" 200 1350
127.0.0.1 - - [27/Jan/2018 15:39:58] "POST / HTTP/1.1" 200 107
127.0.0.1 - - [27/Jan/2018 15:39:58] "POST / HTTP/1.1" 200 391
127.0.0.1 - - [27/Jan/2018 15:39:58] "POST / HTTP/1.1" 200 391
Hash: 0x75a12a7aab601952ef71dd6da71725eaa4eb7883096067546c8ae3f99cc9222e

注意不同的雜湊。這種情況每隔幾次我執行智能合約就會發生一次,但出乎意料的是,似乎沒有與時間或任何其他環境變數相關的模式。這裡發生了什麼?

不使用的時候也會出現這個問題keccak256。然後輸出有時會顯示0x7b00000000000000000000000000000000000000000000000000000000000000而不是0x6400000000000000000000000000000000000000000000000000000000000000,但它更難激發。當我刪除參數(來自契約和我的 API 呼叫)時,問題完全消失recorded_saltsalt,所以我假設我以某種方式錯誤地呼叫了建構子。

這真的很微妙。正如我在 Github 上打開的問題的答案所指出的:

您正在使用一組參數。集合沒有排序,所以當你迭代它們時,它們會出現什麼順序是半隨機的。如果您只是將其更改為元組或列表

$$ .. $$你會變得一致$$ t $$結果。

所以解決方案是為建構子參數使用一個列表(注意方括號而不是花括號):

contract_instance, contract = deploy_smart_contract('contract.sol', 'TestContract', [Web3.toBytes(123), Web3.toBytes(100)])

引用自:https://ethereum.stackexchange.com/questions/37678