def _format_call(self, sender='', to='', value=0, data='', startgas=default_startgas, gasprice=default_gasprice): """ Helper to format the transaction data. """ json_data = dict() if sender is not None: json_data['from'] = address_encoder(sender) if to is not None: json_data['to'] = data_encoder(to) if value is not None: json_data['value'] = quantity_encoder(value) if gasprice is not None: json_data['gasPrice'] = quantity_encoder(gasprice) if startgas is not None: json_data['gas'] = quantity_encoder(startgas) if data is not None: json_data['data'] = data_encoder(data) return json_data
def eth_sendTransaction(self, nonce=None, sender='', to='', value=0, data='', gasPrice=default_gasprice, gas=default_startgas, v=None, r=None, s=None): """ Creates new message call transaction or a contract creation, if the data field contains code. Note: The support for local signing through the variables v,r,s is not part of the standard spec, a extended server is required. Args: from (address): The 20 bytes address the transaction is send from. to (address): DATA, 20 Bytes - (optional when creating new contract) The address the transaction is directed to. gas (int): Gas provided for the transaction execution. It will return unused gas. gasPrice (int): gasPrice used for each paid gas. value (int): Value send with this transaction. data (bin): The compiled code of a contract OR the hash of the invoked method signature and encoded parameters. nonce (int): This allows to overwrite your own pending transactions that use the same nonce. """ if to == '' and data.isalnum(): warnings.warn( 'Verify that the data parameter is _not_ hex encoded, if this is the case ' 'the data will be double encoded and result in unexpected ' 'behavior.' ) if to == '0' * 40: warnings.warn('For contract creating the empty string must be used.') json_data = { 'to': data_encoder(normalize_address(to, allow_blank=True)), 'value': quantity_encoder(value), 'gasPrice': quantity_encoder(gasPrice), 'gas': quantity_encoder(gas), 'data': data_encoder(data), } if not sender and not (v and r and s): raise ValueError('Either sender or v, r, s needs to be informed.') if sender is not None: json_data['from'] = address_encoder(sender) if v and r and s: json_data['v'] = quantity_encoder(v) json_data['r'] = quantity_encoder(r) json_data['s'] = quantity_encoder(s) if nonce is not None: json_data['nonce'] = quantity_encoder(nonce) res = self.call('eth_sendTransaction', json_data) return data_decoder(res)
def test_new_block_filter(test_app): filter_id = test_app.rpc_request('eth_newBlockFilter') assert test_app.rpc_request('eth_getFilterChanges', filter_id) == [] h = test_app.mine_next_block().hash assert test_app.rpc_request('eth_getFilterChanges', filter_id) == [data_encoder(h)] assert test_app.rpc_request('eth_getFilterChanges', filter_id) == [] hashes = [data_encoder(test_app.mine_next_block().hash) for i in range(3)] assert test_app.rpc_request('eth_getFilterChanges', filter_id) == hashes assert test_app.rpc_request('eth_getFilterChanges', filter_id) == [] assert test_app.rpc_request('eth_getFilterChanges', filter_id) == []
def test_new_block_filter(test_app): filter_id = test_app.rpc_request("eth_newBlockFilter") assert test_app.rpc_request("eth_getFilterChanges", filter_id) == [] h = test_app.mine_next_block().hash assert test_app.rpc_request("eth_getFilterChanges", filter_id) == [data_encoder(h)] assert test_app.rpc_request("eth_getFilterChanges", filter_id) == [] hashes = [data_encoder(test_app.mine_next_block().hash) for i in range(3)] assert test_app.rpc_request("eth_getFilterChanges", filter_id) == hashes assert test_app.rpc_request("eth_getFilterChanges", filter_id) == [] assert test_app.rpc_request("eth_getFilterChanges", filter_id) == []
def eth_call(self, sender='', to='', value=0, data='', startgas=default_startgas, gasprice=default_gasprice, block_number='latest'): """ Executes a new message call immediately without creating a transaction on the block chain. Args: from: The address the transaction is send from. to: The address the transaction is directed to. gas (int): Gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions. gasPrice (int): gasPrice used for each paid gas. value (int): Integer of the value send with this transaction. data (bin): Hash of the method signature and encoded parameters. For details see Ethereum Contract ABI. block_number: Determines the state of ethereum used in the call. """ json_data = dict() if sender is not None: json_data['from'] = address_encoder(sender) if to is not None: json_data['to'] = data_encoder(to) if value is not None: json_data['value'] = quantity_encoder(value) if gasprice is not None: json_data['gasPrice'] = quantity_encoder(gasprice) if startgas is not None: json_data['gas'] = quantity_encoder(startgas) if data is not None: json_data['data'] = data_encoder(data) res = self.call('eth_call', json_data, block_number) return data_decoder(res)
def send_transaction(sender, to, value=0, data='', startgas=GAS_LIMIT, gasprice=GAS_PRICE, nonce=None): """Custom implementation for `pyethapp.rpc_client.JSONRPCClient.send_transaction`. This is necessary to support other remotes that don't support pyethapp's extended specs. @see https://github.com/ethereum/pyethapp/blob/develop/pyethapp/rpc_client.py#L359 """ pending_transactions_hex = client.call( 'eth_getTransactionCount', address_encoder(sender), 'pending', ) pending_transactions = int(pending_transactions_hex, 16) nonce = pending_transactions + nonce_offset tx = Transaction(nonce, gasprice, startgas, to, value, data) assert hasattr(client, 'privkey') and client.privkey tx.sign(client.privkey) result = client.call( 'eth_sendRawTransaction', data_encoder(rlp.encode(tx)), ) return result[2 if result.startswith('0x') else 0:]
def test_compileSolidity(): from pyethapp.jsonrpc import Compilers, data_encoder import ethereum._solidity s = ethereum._solidity.get_solidity() if s == None: pytest.xfail("solidity not installed, not tested") else: c = Compilers() bc = s.compile(solidity_code) abi = s.mk_full_signature(solidity_code) A = dict( test=dict( code=data_encoder(bc), info=dict( source=solidity_code, language="Solidity", languageVersion="0", compilerVersion="0", abiDefinition=abi, userDoc=dict(methods=dict()), developerDoc=dict(methods=dict()), ), ) ) B = c.compileSolidity(solidity_code) assert A.keys() == B.keys() At = A["test"] Bt = B["test"] assert At["code"] == Bt["code"] for k, Av in At["info"].items(): if k == "compilerVersion": continue assert Av == Bt["info"][k]
def test_send_raw_transaction_with_contract(test_app): serpent_code = ''' def main(a,b): return(a ^ b) ''' tx_to = b'' evm_code = serpent.compile(serpent_code) chain = test_app.services.chain.chain assert chain.head_candidate.get_balance(tx_to) == 0 sender = test_app.services.accounts.unlocked_accounts[0].address assert chain.head_candidate.get_balance(sender) > 0 nonce = chain.head_candidate.get_nonce(sender) tx = ethereum.transactions.Transaction(nonce, default_gasprice, default_startgas, tx_to, 0, evm_code, 0, 0, 0) test_app.services.accounts.sign_tx(sender, tx) raw_transaction = data_encoder(rlp.codec.encode(tx, ethereum.transactions.Transaction)) data_decoder(test_app.client.call('eth_sendRawTransaction', raw_transaction)) creates = chain.head_candidate.get_transaction(0).creates code = chain.head_candidate.account_to_dict(creates)['code'] assert len(code) > 2 assert code != '0x' test_app.mine_next_block() creates = chain.head.get_transaction(0).creates code = chain.head.account_to_dict(creates)['code'] assert len(code) > 2 assert code != '0x'
def test_compileSolidity(): from pyethapp.jsonrpc import Compilers, data_encoder import ethereum._solidity s = ethereum._solidity.get_solidity() if s == None: pytest.xfail("solidity not installed, not tested") else: c = Compilers() bc = s.compile(solidity_code) abi = s.mk_full_signature(solidity_code) A = dict(test=dict(code=data_encoder(bc), info=dict( source=solidity_code, language='Solidity', languageVersion='0', compilerVersion='0', abiDefinition=abi, userDoc=dict(methods=dict()), developerDoc=dict(methods=dict()), ))) B = c.compileSolidity(solidity_code) assert A.keys() == B.keys() At = A['test'] Bt = B['test'] assert At['code'] == Bt['code'] for k, Av in At['info'].items(): if k == 'compilerVersion': continue assert Av == Bt['info'][k]
def block_tag_encoder(val): if isinstance(val, int): return quantity_encoder(val) elif val and isinstance(val, bytes): assert val in ('latest', 'pending') return data_encoder(val) else: assert not val
def send_transaction(sender, to, value=0, data='', startgas=GAS_LIMIT, gasprice=GAS_PRICE, nonce=None): """Custom implementation for `pyethapp.rpc_client.JSONRPCClient.send_transaction`. This is necessary to support other remotes that don't support pyethapp's extended specs. @see https://github.com/ethereum/pyethapp/blob/develop/pyethapp/rpc_client.py#L359 """ def get_nonce(): """Eventually syncing nonce counter. This will keep a local nonce counter that is only syncing against the remote every `UPDATE_INTERVAL`. If the remote counter is lower than the current local counter, it will wait for the remote to catch up. """ with client.nonce_lock: UPDATE_INTERVAL = 5. query_time = now() needs_update = abs(query_time - client.last_nonce_update) > UPDATE_INTERVAL not_initialized = client.current_nonce is None if needs_update or not_initialized: nonce = _query_nonce() # we may have hammered the server and not all tx are # registered as `pending` yet while nonce < client.current_nonce: log.debug( "nonce on server too low; retrying", server=nonce, local=client.current_nonce ) nonce = _query_nonce() query_time = now() client.current_nonce = nonce client.last_nonce_update = query_time else: client.current_nonce += 1 return client.current_nonce def _query_nonce(): pending_transactions_hex = client.call( 'eth_getTransactionCount', address_encoder(sender), 'pending', ) pending_transactions = int(pending_transactions_hex, 16) nonce = pending_transactions + nonce_offset return nonce nonce = get_nonce() tx = Transaction(nonce, gasprice, startgas, to, value, data) assert hasattr(client, 'privkey') and client.privkey tx.sign(client.privkey) result = client.call( 'eth_sendRawTransaction', data_encoder(rlp.encode(tx)), ) return result[2 if result.startswith('0x') else 0:]
def check_transaction_threw(client, transaction_hash): """Check if the transaction threw or if it executed properly""" encoded_transaction = data_encoder(transaction_hash.decode('hex')) transaction = client.call('eth_getTransactionByHash', encoded_transaction) receipt = client.call('eth_getTransactionReceipt', encoded_transaction) if int(transaction['gas'], 0) != int(receipt['gasUsed'], 0): return None else: return receipt
def test_compile_solidity(): with open(path.join(path.dirname(__file__), 'contracts', 'multiply.sol')) as handler: solidity_code = handler.read() solidity = ethereum.tools._solidity.get_solidity() # pylint: disable=protected-access abi = solidity.mk_full_signature(solidity_code) code = data_encoder(solidity.compile(solidity_code)) info = { 'abiDefinition': abi, 'compilerVersion': '0', 'developerDoc': { 'methods': None, }, 'language': 'Solidity', 'languageVersion': '0', 'source': solidity_code, 'userDoc': { 'methods': None, }, } test_result = { 'test': { 'code': code, 'info': info, } } compiler_result = Compilers().compileSolidity(solidity_code) assert set(compiler_result.keys()) == { 'test', } assert set(compiler_result['test'].keys()) == { 'info', 'code', } assert set(compiler_result['test']['info']) == { 'abiDefinition', 'compilerVersion', 'developerDoc', 'language', 'languageVersion', 'source', 'userDoc', } assert test_result['test']['code'] == compiler_result['test']['code'] compiler_info = dict(compiler_result['test']['info']) compiler_info.pop('compilerVersion') info.pop('compilerVersion') assert compiler_info['abiDefinition'] == info['abiDefinition']
def check_transaction_threw(client, transaction_hash): """Check if the transaction threw or if it executed properly""" encoded_transaction = data_encoder(transaction_hash.decode('hex')) debug = client.call('debug_traceTransaction', encoded_transaction) # struct_logs will be empty if it's a call to a contract that previously # self destructed: # https://github.com/ethereum/go-ethereum/issues/2542 struct_logs = debug['structLogs'] if not struct_logs or struct_logs[-1]['op'] not in ('RETURN', 'STOP'): return debug
def poll(self, transaction_hash, confirmations=None, timeout=None): """ Wait until the `transaction_hash` is applied or rejected. If timeout is None, this could wait indefinitely! Args: transaction_hash (hash): Transaction hash that we are waiting for. confirmations (int): Number of block confirmations that we will wait for. timeout (float): Timeout in seconds, raise an Excpetion on timeout. """ if transaction_hash.startswith('0x'): warnings.warn( 'transaction_hash seems to be already encoded, this will result ' 'in unexpected behavior') if len(transaction_hash) != 32: raise ValueError( 'transaction_hash length must be 32 (it might be hex encode)') deadline = None if timeout: deadline = time.time() + timeout transaction_hash = data_encoder(transaction_hash) transaction = self.call('eth_getTransactionByHash', transaction_hash) while transaction is None or transaction["blockNumber"] is None: if deadline and time.time() > deadline: raise Exception('timeout when polling for transaction') gevent.sleep(.5) transaction = self.call('eth_getTransactionByHash', transaction_hash) if confirmations is None: return # this will wait for both APPLIED and REVERTED transactions transaction_block = quantity_decoder(transaction['blockNumber']) confirmation_block = transaction_block + confirmations block_number = self.blocknumber() while confirmation_block > block_number: if deadline and time.time() > deadline: raise Exception('timeout when waiting for confirmation') gevent.sleep(.5) block_number = self.blocknumber()
def eth_getTransactionByHash(self, transaction_hash): """ Returns the information about a transaction requested by transaction hash. """ if transaction_hash.startswith('0x'): warnings.warn( 'transaction_hash seems to be already encoded, this will' ' result in unexpected behavior' ) if len(transaction_hash) != 32: raise ValueError( 'transaction_hash length must be 32 (it might be hex encode)' ) transaction_hash = data_encoder(transaction_hash) return self.call('eth_getTransactionByHash', transaction_hash)
def test_compileSolidity(): from pyethapp.jsonrpc import Compilers, data_encoder import ethereum._solidity s = ethereum._solidity.get_solidity() c = Compilers() assert 'solidity' in c.getCompilers() bc = s.compile(solidity_code) abi = s.mk_full_signature(solidity_code) r = dict(code=data_encoder(bc), info=dict(source=solidity_code, language='Solidity', languageVersion='0', compilerVersion='0', abiDefinition=abi, userDoc=dict(methods=dict()), developerDoc=dict(methods=dict()), ) ) assert r == c.compileSolidity(solidity_code)
def test_compileSolidity(): from pyethapp.jsonrpc import Compilers, data_encoder import ethereum._solidity s = ethereum._solidity.get_solidity() if s == None: pytest.xfail("solidity not installed, not tested") else: c = Compilers() bc = s.compile(solidity_code) abi = s.mk_full_signature(solidity_code) r = dict(code=data_encoder(bc), info=dict(source=solidity_code, language='Solidity', languageVersion='0', compilerVersion='0', abiDefinition=abi, userDoc=dict(methods=dict()), developerDoc=dict(methods=dict()), ) ) assert r == c.compileSolidity(solidity_code)
def test_compileSolidity(): from pyethapp.jsonrpc import Compilers, data_encoder import ethereum._solidity s = ethereum._solidity.get_solidity() if s == None: pytest.xfail("solidity not installed, not tested") else: c = Compilers() bc = s.compile(solidity_code) abi = s.mk_full_signature(solidity_code) r = dict(code=data_encoder(bc), info=dict( source=solidity_code, language='Solidity', languageVersion='0', compilerVersion='0', abiDefinition=abi, userDoc=dict(methods=dict()), developerDoc=dict(methods=dict()), )) assert r == c.compileSolidity(solidity_code)
def eth_getTransactionReceipt(self, transaction_hash): """ Returns the receipt of a transaction by transaction hash. Args: transaction_hash: Hash of a transaction. Returns: A dict representing the transaction receipt object, or null when no receipt was found. """ if transaction_hash.startswith('0x'): warnings.warn( 'transaction_hash seems to be already encoded, this will' ' result in unexpected behavior' ) if len(transaction_hash) != 32: raise ValueError( 'transaction_hash length must be 32 (it might be hex encode)' ) transaction_hash = data_encoder(transaction_hash) return self.call('eth_getTransactionReceipt', transaction_hash)
def test_get_logs(test_app): test_app.mine_next_block() # start with a fresh block n0 = test_app.services.chain.chain.head.number sender = address_encoder(test_app.services.accounts.unlocked_accounts()[0].address) contract_creation = { 'from': sender, 'data': data_encoder(LOG_EVM) } tx_hash = test_app.rpc_request('eth_sendTransaction', contract_creation) test_app.mine_next_block() receipt = test_app.rpc_request('eth_getTransactionReceipt', tx_hash) contract_address = receipt['contractAddress'] tx = { 'from': sender, 'to': contract_address } # single log in pending block test_app.rpc_request('eth_sendTransaction', tx) logs1 = test_app.rpc_request('eth_getLogs', { 'fromBlock': 'pending', 'toBlock': 'pending' }); assert len(logs1) == 1 assert logs1[0]['type'] == 'pending' assert logs1[0]['logIndex'] == None assert logs1[0]['transactionIndex'] == None assert logs1[0]['transactionHash'] == None assert logs1[0]['blockHash'] == None assert logs1[0]['blockNumber'] == None assert logs1[0]['address'] == contract_address logs2 = test_app.rpc_request('eth_getLogs', { 'fromBlock': 'pending', 'toBlock': 'pending' }); assert logs2 == logs1 # same log, but now mined in head test_app.mine_next_block() logs3 = test_app.rpc_request('eth_getLogs', { 'fromBlock': 'latest', 'toBlock': 'latest' }); assert len(logs3) == 1 assert logs3[0]['type'] == 'mined' assert logs3[0]['logIndex'] == '0x0' assert logs3[0]['transactionIndex'] == '0x0' assert logs3[0]['blockHash'] == data_encoder(test_app.services.chain.chain.head.hash) assert logs3[0]['blockNumber'] == quantity_encoder(test_app.services.chain.chain.head.number) assert logs3[0]['address'] == contract_address # another log in pending block test_app.rpc_request('eth_sendTransaction', tx) logs4 = test_app.rpc_request('eth_getLogs', { 'fromBlock': 'latest', 'toBlock': 'pending' }) assert logs4 == [logs1[0], logs3[0]] or logs4 == [logs3[0], logs1[0]] # two logs in pending block test_app.rpc_request('eth_sendTransaction', tx) logs5 = test_app.rpc_request('eth_getLogs', { 'fromBlock': 'pending', 'toBlock': 'pending' }) assert len(logs5) == 2 assert logs5[0] == logs5[1] == logs1[0] # two logs in head test_app.mine_next_block() logs6 = test_app.rpc_request('eth_getLogs', { 'fromBlock': 'latest', 'toBlock': 'pending' }) for log in logs6: assert log['type'] == 'mined' assert log['logIndex'] == '0x0' assert log['blockHash'] == data_encoder(test_app.services.chain.chain.head.hash) assert log['blockNumber'] == quantity_encoder(test_app.services.chain.chain.head.number) assert log['address'] == contract_address assert sorted([log['transactionIndex'] for log in logs6]) == ['0x0', '0x1'] # everything together with another log in pending block test_app.rpc_request('eth_sendTransaction', tx) logs7 = test_app.rpc_request('eth_getLogs', { 'fromBlock': quantity_encoder(n0), 'toBlock': 'pending' }) assert sorted(logs7) == sorted(logs3 + logs6 + logs1)
def test_get_filter_changes(test_app): test_app.mine_next_block() # start with a fresh block n0 = test_app.services.chain.chain.head.number sender = address_encoder(test_app.services.accounts.unlocked_accounts()[0].address) contract_creation = {"from": sender, "data": data_encoder(LOG_EVM)} tx_hash = test_app.rpc_request("eth_sendTransaction", contract_creation) test_app.mine_next_block() receipt = test_app.rpc_request("eth_getTransactionReceipt", tx_hash) contract_address = receipt["contractAddress"] tx = {"from": sender, "to": contract_address} pending_filter_id = test_app.rpc_request("eth_newFilter", {"fromBlock": "pending", "toBlock": "pending"}) latest_filter_id = test_app.rpc_request("eth_newFilter", {"fromBlock": "latest", "toBlock": "latest"}) tx_hashes = [] logs = [] # tx in pending block tx_hashes.append(test_app.rpc_request("eth_sendTransaction", tx)) logs.append(test_app.rpc_request("eth_getFilterChanges", pending_filter_id)) assert len(logs[-1]) == 1 assert logs[-1][0]["type"] == "pending" assert logs[-1][0]["logIndex"] == None assert logs[-1][0]["transactionIndex"] == None assert logs[-1][0]["transactionHash"] == None assert logs[-1][0]["blockHash"] == None assert logs[-1][0]["blockNumber"] == None assert logs[-1][0]["address"] == contract_address pending_log = logs[-1][0] logs.append(test_app.rpc_request("eth_getFilterChanges", pending_filter_id)) assert logs[-1] == [] logs.append(test_app.rpc_request("eth_getFilterChanges", latest_filter_id)) assert logs[-1] == [] test_app.mine_next_block() logs.append(test_app.rpc_request("eth_getFilterChanges", latest_filter_id)) assert len(logs[-1]) == 1 # log from before, but now mined assert logs[-1][0]["type"] == "mined" assert logs[-1][0]["logIndex"] == "0x0" assert logs[-1][0]["transactionIndex"] == "0x0" assert logs[-1][0]["transactionHash"] == tx_hashes[-1] assert logs[-1][0]["blockHash"] == data_encoder(test_app.services.chain.chain.head.hash) assert logs[-1][0]["blockNumber"] == quantity_encoder(test_app.services.chain.chain.head.number) assert logs[-1][0]["address"] == contract_address logs_in_range = [logs[-1][0]] # send tx and mine block tx_hashes.append(test_app.rpc_request("eth_sendTransaction", tx)) test_app.mine_next_block() logs.append(test_app.rpc_request("eth_getFilterChanges", pending_filter_id)) assert len(logs[-1]) == 1 assert logs[-1][0]["type"] == "mined" assert logs[-1][0]["logIndex"] == "0x0" assert logs[-1][0]["transactionIndex"] == "0x0" assert logs[-1][0]["transactionHash"] == tx_hashes[-1] assert logs[-1][0]["blockHash"] == data_encoder(test_app.services.chain.chain.head.hash) assert logs[-1][0]["blockNumber"] == quantity_encoder(test_app.services.chain.chain.head.number) assert logs[-1][0]["address"] == contract_address logs_in_range.append(logs[-1][0]) logs.append(test_app.rpc_request("eth_getFilterChanges", latest_filter_id)) assert logs[-1] == logs[-2] # latest and pending filter see same (mined) log logs.append(test_app.rpc_request("eth_getFilterChanges", latest_filter_id)) assert logs[-1] == [] test_app.mine_next_block() logs.append(test_app.rpc_request("eth_getFilterChanges", pending_filter_id)) assert logs[-1] == [] range_filter_id = test_app.rpc_request( "eth_newFilter", {"fromBlock": quantity_encoder(test_app.services.chain.chain.head.number - 3), "toBlock": "pending"}, ) tx_hashes.append(test_app.rpc_request("eth_sendTransaction", tx)) logs.append(test_app.rpc_request("eth_getFilterChanges", range_filter_id)) assert sorted(logs[-1]) == sorted(logs_in_range + [pending_log])
def test_get_logs(test_app): test_app.mine_next_block() # start with a fresh block n0 = test_app.services.chain.chain.head.number sender = address_encoder(test_app.services.accounts.unlocked_accounts()[0].address) contract_creation = {"from": sender, "data": data_encoder(LOG_EVM)} tx_hash = test_app.rpc_request("eth_sendTransaction", contract_creation) test_app.mine_next_block() receipt = test_app.rpc_request("eth_getTransactionReceipt", tx_hash) contract_address = receipt["contractAddress"] tx = {"from": sender, "to": contract_address} # single log in pending block test_app.rpc_request("eth_sendTransaction", tx) logs1 = test_app.rpc_request("eth_getLogs", {"fromBlock": "pending", "toBlock": "pending"}) assert len(logs1) == 1 assert logs1[0]["type"] == "pending" assert logs1[0]["logIndex"] == None assert logs1[0]["transactionIndex"] == None assert logs1[0]["transactionHash"] == None assert logs1[0]["blockHash"] == None assert logs1[0]["blockNumber"] == None assert logs1[0]["address"] == contract_address logs2 = test_app.rpc_request("eth_getLogs", {"fromBlock": "pending", "toBlock": "pending"}) assert logs2 == logs1 # same log, but now mined in head test_app.mine_next_block() logs3 = test_app.rpc_request("eth_getLogs", {"fromBlock": "latest", "toBlock": "latest"}) assert len(logs3) == 1 assert logs3[0]["type"] == "mined" assert logs3[0]["logIndex"] == "0x0" assert logs3[0]["transactionIndex"] == "0x0" assert logs3[0]["blockHash"] == data_encoder(test_app.services.chain.chain.head.hash) assert logs3[0]["blockNumber"] == quantity_encoder(test_app.services.chain.chain.head.number) assert logs3[0]["address"] == contract_address # another log in pending block test_app.rpc_request("eth_sendTransaction", tx) logs4 = test_app.rpc_request("eth_getLogs", {"fromBlock": "latest", "toBlock": "pending"}) assert logs4 == [logs1[0], logs3[0]] or logs4 == [logs3[0], logs1[0]] # two logs in pending block test_app.rpc_request("eth_sendTransaction", tx) logs5 = test_app.rpc_request("eth_getLogs", {"fromBlock": "pending", "toBlock": "pending"}) assert len(logs5) == 2 assert logs5[0] == logs5[1] == logs1[0] # two logs in head test_app.mine_next_block() logs6 = test_app.rpc_request("eth_getLogs", {"fromBlock": "latest", "toBlock": "pending"}) for log in logs6: assert log["type"] == "mined" assert log["logIndex"] == "0x0" assert log["blockHash"] == data_encoder(test_app.services.chain.chain.head.hash) assert log["blockNumber"] == quantity_encoder(test_app.services.chain.chain.head.number) assert log["address"] == contract_address assert sorted([log["transactionIndex"] for log in logs6]) == ["0x0", "0x1"] # everything together with another log in pending block test_app.rpc_request("eth_sendTransaction", tx) logs7 = test_app.rpc_request("eth_getLogs", {"fromBlock": quantity_encoder(n0), "toBlock": "pending"}) assert sorted(logs7) == sorted(logs3 + logs6 + logs1)
def poll(self, transaction_hash, confirmations=None, timeout=None): """ Wait until the `transaction_hash` is applied or rejected. If timeout is None, this could wait indefinitely! Args: transaction_hash (hash): Transaction hash that we are waiting for. confirmations (int): Number of block confirmations that we will wait for. timeout (float): Timeout in seconds, raise an Excpetion on timeout. """ if transaction_hash.startswith('0x'): warnings.warn( 'transaction_hash seems to be already encoded, this will' ' result in unexpected behavior' ) if len(transaction_hash) != 32: raise ValueError( 'transaction_hash length must be 32 (it might be hex encode)' ) transaction_hash = data_encoder(transaction_hash) deadline = None if timeout: deadline = gevent.Timeout(timeout) deadline.start() try: # used to check if the transaction was removed, this could happen # if gas price is to low: # # > Transaction (acbca3d6) below gas price (tx=1 Wei ask=18 # > Shannon). All sequential txs from this address(7d0eae79) # > will be ignored # last_result = None while True: # Could return None for a short period of time, until the # transaction is added to the pool transaction = self.call('eth_getTransactionByHash', transaction_hash) # if the transaction was added to the pool and then removed if transaction is None and last_result is not None: raise Exception('invalid transaction, check gas price') # the transaction was added to the pool and mined if transaction and transaction['blockNumber'] is not None: break last_result = transaction gevent.sleep(.5) if confirmations: # this will wait for both APPLIED and REVERTED transactions transaction_block = quantity_decoder(transaction['blockNumber']) confirmation_block = transaction_block + confirmations block_number = self.blocknumber() while block_number < confirmation_block: gevent.sleep(.5) block_number = self.blocknumber() except gevent.Timeout: raise Exception('timeout when polling for transaction') finally: if deadline: deadline.cancel()
def topic_encoder(t): assert isinstance(t, (int, long)) return data_encoder(int_to_big_endian(t))
def topic_encoder(topic): assert isinstance(topic, int) return data_encoder(int_to_big_endian(topic))
def test_get_filter_changes(test_app): test_app.mine_next_block() # start with a fresh block n0 = test_app.services.chain.chain.head.number sender = address_encoder( test_app.services.accounts.unlocked_accounts()[0].address) contract_creation = {'from': sender, 'data': data_encoder(LOG_EVM)} tx_hash = test_app.rpc_request('eth_sendTransaction', contract_creation) test_app.mine_next_block() receipt = test_app.rpc_request('eth_getTransactionReceipt', tx_hash) contract_address = receipt['contractAddress'] tx = {'from': sender, 'to': contract_address} pending_filter_id = test_app.rpc_request('eth_newFilter', { 'fromBlock': 'pending', 'toBlock': 'pending' }) latest_filter_id = test_app.rpc_request('eth_newFilter', { 'fromBlock': 'latest', 'toBlock': 'latest' }) tx_hashes = [] logs = [] # tx in pending block tx_hashes.append(test_app.rpc_request('eth_sendTransaction', tx)) logs.append(test_app.rpc_request('eth_getFilterChanges', pending_filter_id)) assert len(logs[-1]) == 1 assert logs[-1][0]['type'] == 'pending' assert logs[-1][0]['logIndex'] == None assert logs[-1][0]['transactionIndex'] == None assert logs[-1][0]['transactionHash'] == None assert logs[-1][0]['blockHash'] == None assert logs[-1][0]['blockNumber'] == None assert logs[-1][0]['address'] == contract_address pending_log = logs[-1][0] logs.append(test_app.rpc_request('eth_getFilterChanges', pending_filter_id)) assert logs[-1] == [] logs.append(test_app.rpc_request('eth_getFilterChanges', latest_filter_id)) assert logs[-1] == [] test_app.mine_next_block() logs.append(test_app.rpc_request('eth_getFilterChanges', latest_filter_id)) assert len(logs[-1]) == 1 # log from before, but now mined assert logs[-1][0]['type'] == 'mined' assert logs[-1][0]['logIndex'] == '0x0' assert logs[-1][0]['transactionIndex'] == '0x0' assert logs[-1][0]['transactionHash'] == tx_hashes[-1] assert logs[-1][0]['blockHash'] == data_encoder( test_app.services.chain.chain.head.hash) assert logs[-1][0]['blockNumber'] == quantity_encoder( test_app.services.chain.chain.head.number) assert logs[-1][0]['address'] == contract_address logs_in_range = [logs[-1][0]] # send tx and mine block tx_hashes.append(test_app.rpc_request('eth_sendTransaction', tx)) test_app.mine_next_block() logs.append(test_app.rpc_request('eth_getFilterChanges', pending_filter_id)) assert len(logs[-1]) == 1 assert logs[-1][0]['type'] == 'mined' assert logs[-1][0]['logIndex'] == '0x0' assert logs[-1][0]['transactionIndex'] == '0x0' assert logs[-1][0]['transactionHash'] == tx_hashes[-1] assert logs[-1][0]['blockHash'] == data_encoder( test_app.services.chain.chain.head.hash) assert logs[-1][0]['blockNumber'] == quantity_encoder( test_app.services.chain.chain.head.number) assert logs[-1][0]['address'] == contract_address logs_in_range.append(logs[-1][0]) logs.append(test_app.rpc_request('eth_getFilterChanges', latest_filter_id)) assert logs[-1] == logs[ -2] # latest and pending filter see same (mined) log logs.append(test_app.rpc_request('eth_getFilterChanges', latest_filter_id)) assert logs[-1] == [] test_app.mine_next_block() logs.append(test_app.rpc_request('eth_getFilterChanges', pending_filter_id)) assert logs[-1] == [] range_filter_id = test_app.rpc_request( 'eth_newFilter', { 'fromBlock': quantity_encoder(test_app.services.chain.chain.head.number - 3), 'toBlock': 'pending' }) tx_hashes.append(test_app.rpc_request('eth_sendTransaction', tx)) logs.append(test_app.rpc_request('eth_getFilterChanges', range_filter_id)) assert sorted(logs[-1]) == sorted(logs_in_range + [pending_log])
def test_get_filter_changes(test_app): test_app.mine_next_block() # start with a fresh block n0 = test_app.services.chain.chain.head.number sender = address_encoder(test_app.services.accounts.unlocked_accounts()[0].address) contract_creation = { 'from': sender, 'data': data_encoder(LOG_EVM) } tx_hash = test_app.rpc_request('eth_sendTransaction', contract_creation) test_app.mine_next_block() receipt = test_app.rpc_request('eth_getTransactionReceipt', tx_hash) contract_address = receipt['contractAddress'] tx = { 'from': sender, 'to': contract_address } pending_filter_id = test_app.rpc_request('eth_newFilter', { 'fromBlock': 'pending', 'toBlock': 'pending' }); latest_filter_id = test_app.rpc_request('eth_newFilter', { 'fromBlock': 'latest', 'toBlock': 'latest' }); tx_hashes = [] logs = [] # tx in pending block tx_hashes.append(test_app.rpc_request('eth_sendTransaction', tx)) logs.append(test_app.rpc_request('eth_getFilterChanges', pending_filter_id)) assert len(logs[-1]) == 1 assert logs[-1][0]['type'] == 'pending' assert logs[-1][0]['logIndex'] == None assert logs[-1][0]['transactionIndex'] == None assert logs[-1][0]['transactionHash'] == None assert logs[-1][0]['blockHash'] == None assert logs[-1][0]['blockNumber'] == None assert logs[-1][0]['address'] == contract_address pending_log = logs[-1][0] logs.append(test_app.rpc_request('eth_getFilterChanges', pending_filter_id)) assert logs[-1] == [] logs.append(test_app.rpc_request('eth_getFilterChanges', latest_filter_id)) assert logs[-1] == [] test_app.mine_next_block() logs.append(test_app.rpc_request('eth_getFilterChanges', latest_filter_id)) assert len(logs[-1]) == 1 # log from before, but now mined assert logs[-1][0]['type'] == 'mined' assert logs[-1][0]['logIndex'] == '0x0' assert logs[-1][0]['transactionIndex'] == '0x0' assert logs[-1][0]['transactionHash'] == tx_hashes[-1] assert logs[-1][0]['blockHash'] == data_encoder(test_app.services.chain.chain.head.hash) assert logs[-1][0]['blockNumber'] == quantity_encoder(test_app.services.chain.chain.head.number) assert logs[-1][0]['address'] == contract_address logs_in_range = [logs[-1][0]] # send tx and mine block tx_hashes.append(test_app.rpc_request('eth_sendTransaction', tx)) test_app.mine_next_block() logs.append(test_app.rpc_request('eth_getFilterChanges', pending_filter_id)) assert len(logs[-1]) == 1 assert logs[-1][0]['type'] == 'mined' assert logs[-1][0]['logIndex'] == '0x0' assert logs[-1][0]['transactionIndex'] == '0x0' assert logs[-1][0]['transactionHash'] == tx_hashes[-1] assert logs[-1][0]['blockHash'] == data_encoder(test_app.services.chain.chain.head.hash) assert logs[-1][0]['blockNumber'] == quantity_encoder(test_app.services.chain.chain.head.number) assert logs[-1][0]['address'] == contract_address logs_in_range.append(logs[-1][0]) logs.append(test_app.rpc_request('eth_getFilterChanges', latest_filter_id)) assert logs[-1] == logs[-2] # latest and pending filter see same (mined) log logs.append(test_app.rpc_request('eth_getFilterChanges', latest_filter_id)) assert logs[-1] == [] test_app.mine_next_block() logs.append(test_app.rpc_request('eth_getFilterChanges', pending_filter_id)) assert logs[-1] == [] range_filter_id = test_app.rpc_request('eth_newFilter', { 'fromBlock': quantity_encoder(test_app.services.chain.chain.head.number - 3), 'toBlock': 'pending' }) tx_hashes.append(test_app.rpc_request('eth_sendTransaction', tx)) logs.append(test_app.rpc_request('eth_getFilterChanges', range_filter_id)) assert sorted(logs[-1]) == sorted(logs_in_range + [pending_log])
def _serialize(self, value, attr, obj): return data_encoder(value)
def test_get_logs(test_app): test_app.mine_next_block() # start with a fresh block n0 = test_app.services.chain.chain.head.number sender = address_encoder( test_app.services.accounts.unlocked_accounts()[0].address) contract_creation = {'from': sender, 'data': data_encoder(LOG_EVM)} tx_hash = test_app.rpc_request('eth_sendTransaction', contract_creation) test_app.mine_next_block() receipt = test_app.rpc_request('eth_getTransactionReceipt', tx_hash) contract_address = receipt['contractAddress'] tx = {'from': sender, 'to': contract_address} # single log in pending block test_app.rpc_request('eth_sendTransaction', tx) logs1 = test_app.rpc_request('eth_getLogs', { 'fromBlock': 'pending', 'toBlock': 'pending' }) assert len(logs1) == 1 assert logs1[0]['type'] == 'pending' assert logs1[0]['logIndex'] == None assert logs1[0]['transactionIndex'] == None assert logs1[0]['transactionHash'] == None assert logs1[0]['blockHash'] == None assert logs1[0]['blockNumber'] == None assert logs1[0]['address'] == contract_address logs2 = test_app.rpc_request('eth_getLogs', { 'fromBlock': 'pending', 'toBlock': 'pending' }) assert logs2 == logs1 # same log, but now mined in head test_app.mine_next_block() logs3 = test_app.rpc_request('eth_getLogs', { 'fromBlock': 'latest', 'toBlock': 'latest' }) assert len(logs3) == 1 assert logs3[0]['type'] == 'mined' assert logs3[0]['logIndex'] == '0x0' assert logs3[0]['transactionIndex'] == '0x0' assert logs3[0]['blockHash'] == data_encoder( test_app.services.chain.chain.head.hash) assert logs3[0]['blockNumber'] == quantity_encoder( test_app.services.chain.chain.head.number) assert logs3[0]['address'] == contract_address # another log in pending block test_app.rpc_request('eth_sendTransaction', tx) logs4 = test_app.rpc_request('eth_getLogs', { 'fromBlock': 'latest', 'toBlock': 'pending' }) assert logs4 == [logs1[0], logs3[0]] or logs4 == [logs3[0], logs1[0]] # two logs in pending block test_app.rpc_request('eth_sendTransaction', tx) logs5 = test_app.rpc_request('eth_getLogs', { 'fromBlock': 'pending', 'toBlock': 'pending' }) assert len(logs5) == 2 assert logs5[0] == logs5[1] == logs1[0] # two logs in head test_app.mine_next_block() logs6 = test_app.rpc_request('eth_getLogs', { 'fromBlock': 'latest', 'toBlock': 'pending' }) for log in logs6: assert log['type'] == 'mined' assert log['logIndex'] == '0x0' assert log['blockHash'] == data_encoder( test_app.services.chain.chain.head.hash) assert log['blockNumber'] == quantity_encoder( test_app.services.chain.chain.head.number) assert log['address'] == contract_address assert sorted([log['transactionIndex'] for log in logs6]) == ['0x0', '0x1'] # everything together with another log in pending block test_app.rpc_request('eth_sendTransaction', tx) logs7 = test_app.rpc_request('eth_getLogs', { 'fromBlock': quantity_encoder(n0), 'toBlock': 'pending' }) assert sorted(logs7) == sorted(logs3 + logs6 + logs1)