def test_send_transaction_with_contract(test_app): serpent_code = ''' def main(a,b): return(a ^ b) ''' tx_to = b'' evm_code = serpent.compile(serpent_code) chainservice = test_app.services.chain chain = test_app.services.chain.chain state = State(chainservice.head_candidate.state_root, chain.env) sender = test_app.services.accounts.unlocked_accounts[0].address assert state.get_balance(sender) > 0 tx = { 'from': address_encoder(sender), 'to': address_encoder(tx_to), 'data': encode_hex(evm_code) } data_decoder(test_app.client.call('eth_sendTransaction', tx)) assert len(chainservice.head_candidate.transactions) == 1 creates = chainservice.head_candidate.transactions[0].creates candidate_state_dict = State(chainservice.head_candidate.state_root, chain.env).to_dict() code = candidate_state_dict[encode_hex(creates)]['code'] assert len(code) > 2 assert code != '0x' test_app.mine_next_block() assert len(chain.head.transactions) == 1 creates = chain.head.transactions[0].creates state_dict = State(chain.head.state_root, chain.env).to_dict() code = state_dict[encode_hex(creates)]['code'] assert len(code) > 2 assert code != '0x'
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_send_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 tx = { 'from': address_encoder(sender), 'to': address_encoder(tx_to), 'data': evm_code.encode('hex') } data_decoder(test_app.client.call('eth_sendTransaction', tx)) 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 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 = self._format_call( sender, to, value, data, startgas, gasprice, ) res = self.call('eth_call', json_data, block_number) return data_decoder(res)
def eth_sendTransaction( self, nonce=None, sender="", to="", value=0, data="", gasprice=default_gasprice, startgas=default_startgas, v=None, r=None, s=None, ): encoders = dict( nonce=quantity_encoder, sender=address_encoder, to=data_encoder, value=quantity_encoder, gasprice=quantity_encoder, startgas=quantity_encoder, data=data_encoder, v=quantity_encoder, r=quantity_encoder, s=quantity_encoder, ) data = {k: encoders[k](v) for k, v in locals().items() if k not in ("self", "encoders") and v is not None} data["from"] = data.pop("sender") assert data.get("from") or (v and r and s) res = self.call("eth_sendTransaction", data) return data_decoder(res)
def changes(self): filter_changes = self.client.call( 'eth_getFilterChanges', self.filter_id_raw, ) # geth could return None if filter_changes is None: return [] result = list() for log_event in filter_changes: address = address_decoder(log_event['address']) data = data_decoder(log_event['data']) topics = [ decode_topic(topic) for topic in log_event['topics'] ] result.append({ 'topics': topics, 'data': data, 'address': address, }) return result
def eth_sendTransaction(self, nonce=None, sender='', to='', value=0, data='', gasPrice=default_gasprice, gas=default_startgas, v=None, r=None, s=None, secret=None): to = address20(to) encoders = dict(nonce=quantity_encoder, sender=address_encoder, to=data_encoder, value=quantity_encoder, gasPrice=quantity_encoder, gas=quantity_encoder, data=data_encoder, v=quantity_encoder, r=quantity_encoder, s=quantity_encoder, secret=secret_encoder) data = { k: encoders[k](v) for k, v in locals().items() if k not in ('self', 'encoders') and v is not None } data['from'] = data.pop('sender') assert data.get('from') or (v and r and s) res = self.call('eth_sendTransaction', data) return data_decoder(res)
def _query_filter(self, function): filter_changes = self.client.call(function, self.filter_id_raw) # geth could return None if filter_changes is None: return [] result = list() for log_event in filter_changes: address = address_decoder(log_event['address']) data = data_decoder(log_event['data']) topics = [decode_topic(topic) for topic in log_event['topics']] block_number = log_event.get('blockNumber') if not block_number: block_number = 0 else: block_number = int(block_number, 0) result.append({ 'topics': topics, 'data': data, 'address': address, 'block_number': block_number, }) return result
def all_contract_events(rpc, contract_address, abi, start_block=None, end_block=None): """Find and decode all events for a deployed contract given its `contract_address` and `abi`. Args: rpc (pyethapp.rpc_client.JSONRPCClient): client instance. contract_address (string): hex encoded contract address. abi (list(dict)): the contract's ABI. start_block (int): read event-logs starting from this block number. end_block (int): read event-logs up to this block number. Returns: events (list) """ translator = ContractTranslator(abi) events_raw = all_contract_events_raw( rpc, contract_address, start_block=start_block, end_block=end_block, ) events = list() for event_encoded in events_raw: topics_ids = [decode_topic(topic) for topic in event_encoded['topics']] event_data = data_decoder(event_encoded['data']) event = translator.decode_event(topics_ids, event_data) events.append(event) return events
def all_contract_events(rpc, contract_address, abi, start_block=None, end_block=None): """Find and decode all events for a deployed contract given its `contract_address` and `abi`. Args: rpc (pyethapp.rpc_client.JSONRPCClient): client instance. contract_address (string): hex encoded contract address. abi (list(dict)): the contract's ABI. start_block (int): read event-logs starting from this block number. end_block (int): read event-logs up to this block number. Returns: events (list) """ translator = ContractTranslator(abi) events_raw = all_contract_events_raw( rpc, contract_address, start_block=start_block, end_block=end_block, ) events = list() for event_encoded in events_raw: topics_ids = [ decode_topic(topic) for topic in event_encoded['topics'] ] event_data = data_decoder(event_encoded['data']) event = translator.decode_event(topics_ids, event_data) events.append(event) return events
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_send_transaction(test_app): chain = test_app.services.chain.chain assert chain.head_candidate.get_balance("\xff" * 20) == 0 sender = test_app.services.accounts.unlocked_accounts()[0].address assert chain.head_candidate.get_balance(sender) > 0 tx = {"from": address_encoder(sender), "to": address_encoder("\xff" * 20), "value": quantity_encoder(1)} tx_hash = data_decoder(test_app.rpc_request("eth_sendTransaction", tx)) assert tx_hash == chain.head_candidate.get_transaction(0).hash assert chain.head_candidate.get_balance("\xff" * 20) == 1 test_app.mine_next_block() assert tx_hash == chain.head.get_transaction(0).hash assert chain.head.get_balance("\xff" * 20) == 1 # send transactions from account which can't pay gas tx["from"] = address_encoder(test_app.services.accounts.unlocked_accounts()[1].address) tx_hash = data_decoder(test_app.rpc_request("eth_sendTransaction", tx)) assert chain.head_candidate.get_transactions() == []
def test_send_transaction(test_app): chain = test_app.services.chain.chain assert chain.head_candidate.get_balance('\xff' * 20) == 0 tx = { 'from': address_encoder(test_app.services.accounts.unlocked_accounts()[0].address), 'to': address_encoder('\xff' * 20), 'value': quantity_encoder(1) } tx_hash = data_decoder(test_app.rpc_request('eth_sendTransaction', tx)) assert tx_hash == chain.head_candidate.get_transaction(0).hash assert chain.head_candidate.get_balance('\xff' * 20) == 1 test_app.mine_next_block() assert tx_hash == chain.head.get_transaction(0).hash assert chain.head.get_balance('\xff' * 20) == 1 # send transactions from account which can't pay gas tx['from'] = address_encoder(test_app.services.accounts.unlocked_accounts()[1].address) tx_hash = data_decoder(test_app.rpc_request('eth_sendTransaction', tx)) assert chain.head_candidate.get_transactions() == []
def filter_changes(self, fid): changes = self.call('eth_getFilterChanges', quantity_encoder(fid)) if not changes: return None elif isinstance(changes, bytes): return data_decoder(changes) else: decoders = dict(blockHash=data_decoder, transactionHash=data_decoder, data=data_decoder, address=address_decoder, topics=lambda x: [topic_decoder(t) for t in x], blockNumber=quantity_decoder, logIndex=quantity_decoder, transactionIndex=quantity_decoder) return [{k: decoders[k](v) for k, v in c.items() if v is not None} for c in changes]
def eth_call(self, sender='', to='', value=0, data='', startgas=default_startgas, gasprice=default_gasprice): "call on pending block" encoders = dict(sender=address_encoder, to=data_encoder, value=quantity_encoder, gasprice=quantity_encoder, startgas=quantity_encoder, data=data_encoder) data = {k: encoders[k](v) for k, v in locals().items() if k not in ('self', 'encoders') and v is not None} for k, v in dict(gasprice='gasPrice', startgas='gas', sender='from').items(): data[v] = data.pop(k) res = self.call('eth_call', data) return data_decoder(res)
def eth_sendTransaction(self, nonce=None, sender='', to='', value=0, data='', gasPrice=default_gasprice, gas=default_startgas, v=None, r=None, s=None): encoders = dict(nonce=quantity_encoder, sender=address_encoder, to=data_encoder, value=quantity_encoder, gasPrice=quantity_encoder, gas=quantity_encoder, data=data_encoder, v=quantity_encoder, r=quantity_encoder, s=quantity_encoder) data = {k: encoders[k](v) for k, v in locals().items() if k not in ('self', 'encoders') and v is not None} data['from'] = data.pop('sender') assert data.get('from') or (v and r and s) res = self.call('eth_sendTransaction', data) return data_decoder(res)
def send_transaction(self, sender, to, value=0, data='', startgas=0, gasprice=10 * denoms.szabo, nonce=None): """ Helper to send signed messages. This method will use the `privkey` provided in the constructor to locally sign the transaction. This requires an extended server implementation that accepts the variables v, r, and s. """ if not self.privkey and not sender: raise ValueError('Either privkey or sender needs to be supplied.') if self.privkey and not sender: sender = privtoaddr(self.privkey) if nonce is None: nonce = self.nonce(sender) elif self.privkey: if sender != privtoaddr(self.privkey): raise ValueError('sender for a different privkey.') if nonce is None: nonce = self.nonce(sender) else: if nonce is None: nonce = 0 if not startgas: startgas = self.gaslimit() - 1 tx = Transaction(nonce, gasprice, startgas, to=to, value=value, data=data) if self.privkey: # add the fields v, r and s tx.sign(self.privkey) tx_dict = tx.to_dict() # Transaction.to_dict() encodes 'data', so we need to decode it here. tx_dict['data'] = data_decoder(tx_dict['data']) # rename the fields to match the eth_sendTransaction signature tx_dict.pop('hash') tx_dict['sender'] = sender tx_dict['gasPrice'] = tx_dict.pop('gasprice') tx_dict['gas'] = tx_dict.pop('startgas') res = self.eth_sendTransaction(**tx_dict) assert len(res) in (20, 32) return encode_hex(res)
def tester_state(deploy_key, private_keys, tester_blockgas_limit): tester_state = tester.state() # special addresses 1 to 5 alloc = { int_to_addr(i): {'wei': 1} for i in range(1, 5) } for privkey in [deploy_key] + private_keys: address = privatekey_to_address(privkey) alloc[address] = { 'balance': DEFAULT_BALANCE, } for account in tester.accounts: alloc[account] = { 'balance': DEFAULT_BALANCE, } db = ethereum.db.EphemDB() env = ethereum.config.Env( db, ethereum.config.default_config, ) genesis_overwrite = { 'nonce': zpad(data_decoder('0x00006d6f7264656e'), 8), 'difficulty': quantity_decoder('0x20000'), 'mixhash': zpad(b'\x00', 32), 'coinbase': address_decoder('0x0000000000000000000000000000000000000000'), 'timestamp': 0, 'extra_data': b'', 'gas_limit': tester_blockgas_limit, 'start_alloc': alloc, } genesis_block = ethereum.blocks.genesis( env, **genesis_overwrite ) # enable DELEGATECALL opcode genesis_block.number = genesis_block.config['HOMESTEAD_FORK_BLKNUM'] + 1 tester_state.db = db tester_state.env = env tester_state.block = genesis_block tester_state.blocks = [genesis_block] return tester_state
def test_send_transaction(test_app): chainservice = test_app.services.chain chain = chainservice.chain hc = chainservice.head_candidate state = State(hc.state_root, chain.env) assert state.get_balance(b'\xff' * 20) == 0 sender = test_app.services.accounts.unlocked_accounts[0].address assert state.get_balance(sender) > 0 tx = { 'from': address_encoder(sender), 'to': address_encoder(b'\xff' * 20), 'value': quantity_encoder(1) } tx_hash = data_decoder(test_app.client.call('eth_sendTransaction', tx)) test_app.mine_next_block() assert len(chain.head.transactions) == 1 assert tx_hash == chain.head.transactions[0].hash assert chain.state.get_balance(b'\xff' * 20) == 1 # send transactions from account which can't pay gas tx['from'] = address_encoder( test_app.services.accounts.unlocked_accounts[1].address) tx_hash = data_decoder(test_app.client.call('eth_sendTransaction', tx)) assert chainservice.head_candidate.transactions == []
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 filter_changes(self, fid): changes = self.call('eth_getFilterChanges', quantity_encoder(fid)) if not changes: return None elif isinstance(changes, bytes): return data_decoder(changes) else: decoders = dict(blockHash=data_decoder, transactionHash=data_decoder, data=data_decoder, address=address_decoder, topics=lambda x: [topic_decoder(t) for t in x], blockNumber=quantity_decoder, logIndex=quantity_decoder, transactionIndex=quantity_decoder) return [{k: decoders[k](v) for k, v in list(c.items()) if v is not None} for c in changes]
def get_filter_events(jsonrpc_client, contract_address, topics, from_block=None, to_block=None): """ Custom new filter implementation to handle bad encoding from geth rpc. """ if isinstance(from_block, int): from_block = hex(from_block) if isinstance(to_block, int): to_block = hex(to_block) json_data = { 'fromBlock': from_block or hex(0), 'toBlock': to_block or 'latest', 'address': address_encoder(normalize_address(contract_address)), } if topics is not None: json_data['topics'] = [topic_encoder(topic) for topic in topics] filter_changes = jsonrpc_client.call('eth_getLogs', json_data) # geth could return None if filter_changes is None: return [] result = [] for log_event in filter_changes: address = address_decoder(log_event['address']) data = data_decoder(log_event['data']) topics = [decode_topic(topic) for topic in log_event['topics']] block_number = log_event.get('blockNumber') if not block_number: block_number = 0 else: block_number = int(block_number, 0) result.append({ 'topics': topics, 'data': data, 'address': address, 'block_number': block_number, }) return result
def _query_filter(self, function): filter_changes = self.client.call(function, self.filter_id_raw) # geth could return None if filter_changes is None: return [] result = list() for log_event in filter_changes: address = address_decoder(log_event['address']) data = data_decoder(log_event['data']) topics = [decode_topic(topic) for topic in log_event['topics']] result.append({ 'topics': topics, 'data': data, 'address': address, }) return result
def eth_call(self, sender='', to='', value=0, data='', startgas=default_startgas, gasprice=default_gasprice): "call on pending block" encoders = dict(sender=address_encoder, to=data_encoder, value=quantity_encoder, gasprice=quantity_encoder, startgas=quantity_encoder, data=data_encoder) data = { k: encoders[k](v) for k, v in locals().items() if k not in ('self', 'encoders') and v is not None } for k, v in dict(gasprice='gasPrice', startgas='gas', sender='from').items(): data[v] = data.pop(k) res = self.call('eth_call', data) return data_decoder(res)
def _deserialize(self, value, attr, obj): return data_decoder(value)
def topic_decoder(t): return big_endian_to_int(data_decoder(t))