我正在玩以太坊和蟒蛇,我遇到了一些我无法理解的奇怪行为。在使用python w3客户端调用契约函数时,我无法理解返回值的工作原理。这是一个最小的例子,它让我以几种不同的方式让我感到困惑:
合同:
pragma solidity ^0.4.0; contract test { function test(){ } function return_true() public returns (bool) { return true; } function return_address() public returns (address) { return 0x111111111111111111111111111111111111111; } }
Python unittest代码
from web3 import Web3, EthereumTesterProvider from solc import compile_source from web3.contract import ConciseContract import unittest import os def get_contract_source(file_name): with open(file_name) as f: return f.read() class TestContract(unittest.TestCase): CONTRACT_FILE_PATH = "test.sol" DEFAULT_PROPOSAL_ADDRESS = "0x1111111111111111111111111111111111111111" def setUp(self): # copied from https://github.com/ethereum/web3.py/tree/1802e0f6c7871d921e6c5f6e43db6bf2ef06d8d1 with MIT licence # has slight modifications to work with this unittest contract_source_code = get_contract_source(self.CONTRACT_FILE_PATH) compiled_sol = compile_source(contract_source_code) # Compiled source code contract_interface = compiled_sol[':test'] # web3.py instance self.w3 = Web3(EthereumTesterProvider()) # Instantiate and deploy contract self.contract = self.w3.eth.contract(abi=contract_interface['abi'], bytecode=contract_interface['bin']) # Get transaction hash from deployed contract tx_hash = self.contract.constructor().transact({'from': self.w3.eth.accounts[0]}) # Get tx receipt to get contract address tx_receipt = self.w3.eth.getTransactionReceipt(tx_hash) self.contract_address = tx_receipt['contractAddress'] # Contract instance in concise mode abi = contract_interface['abi'] self.contract_instance = self.w3.eth.contract(address=self.contract_address, abi=abi, ContractFactoryClass=ConciseContract) def test_return_true_with_gas(self): # Fails with HexBytes('0xd302f7841b5d7c1b6dcff6fca0cd039666dbd0cba6e8827e72edb4d06bbab38f') != True self.assertEqual(True, self.contract_instance.return_true(transact={"from": self.w3.eth.accounts[0]})) def test_return_true_no_gas(self): # passes self.assertEqual(True, self.contract_instance.return_true()) def test_return_address(self): # fails with AssertionError: '0x1111111111111111111111111111111111111111' != '0x0111111111111111111111111111111111111111' self.assertEqual(self.DEFAULT_PROPOSAL_ADDRESS, self.contract_instance.return_address())
我有三种方法对合同中的功能进行测试。在其中一个中,返回非True
值,而返回HexBytes
。在另一个方面,契约函数返回一个地址常量,但python看到的值与预期值不同。在另一种情况下,我在没有气体的情况下调用return_true
契约函数,并且python可以看到True
常量。
return_true
调用transact={"from": self.w3.eth.accounts[0]}
会导致函数的返回值为HexBytes(...)
?return_address
返回的地址与我的期望不同?我认为我对气体如何影响函数调用存在某种根本性的误解。
答案 0 :(得分:1)
返回的值是区块链上的交易哈希。进行交易时(即,使用“交易”而不是“调用”时),区块链会被修改,您正在使用的库将返回交易哈希。在该过程中,您必须已支付以太币才能修改区块链。但是,以只读模式运行完全不需要醚,因此无需指定气体。
以开头的“ 0x”折扣,以太坊地址的长度为40,但是在您的测试中,您使用的是39个字符长的地址,因此此处缺少“ 1”。意思是,测试是正确的,您输入的内容有误。
Offtopic,return_true
和return_address
在Solidity中均应标记为view
,因为它们实际上并未修改状态。我很确定您会在混音中收到警告。完成此操作后,就无需使用“交易”和支付以太币来访问这两种方法,您可以免费使用“通话”来实现。
忘记提及:如果使用transact
后需要访问事务哈希,可以对返回的.hex()
对象调用HexBytes
方法。这样一来,您就可以将交易哈希值作为字符串使用,通常比HexBytes
有用。
希望对您有帮助!