def etherscan_query_with_retries(url, sleep, retries=3): for _ in range(retries - 1): try: etherscan_block = quantity_decoder( requests.get(url).json()['result']) except (RequestException, ValueError, KeyError): pass else: return etherscan_block etherscan_block = quantity_decoder(requests.get(url).json()['result']) return etherscan_block
def eth_estimateGas(self, sender='', to='', value=0, data='', startgas=default_startgas, gasprice=default_gasprice): """ Makes a call or transaction, which won't be added to the blockchain and returns the used gas, which can be used for estimating the used gas. 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_estimateGas', json_data) return quantity_decoder(res)
def new_filter(self, fromBlock="", toBlock="", address=None, topics=[]): encoders = dict(fromBlock=block_tag_encoder, toBlock=block_tag_encoder, address=address_encoder, topics=lambda x: [topic_encoder(t) for t in x]) data = {k: encoders[k](v) for k, v in locals().items() if k not in ('self', 'encoders') and v is not None} fid = self.call('eth_newFilter', data) return quantity_decoder(fid)
def new_filter(self, fromBlock=None, toBlock=None, address=None, topics=None): """ Creates a filter object, based on filter options, to notify when the state changes (logs). To check if the state has changed, call eth_getFilterChanges. """ json_data = { 'fromBlock': block_tag_encoder(fromBlock or ''), 'toBlock': block_tag_encoder(toBlock or ''), } if address is not None: json_data['address'] = address_encoder(address) if topics is not None: if not isinstance(topics, list): raise ValueError('topics must be a list') json_data['topics'] = [topic_encoder(topic) for topic in topics] filter_id = self.call('eth_newFilter', json_data) return quantity_decoder(filter_id)
def signed_tx_example(): from ethereum.transactions import Transaction from pyethapp.accounts import mk_privkey, privtoaddr secret_seed = 'wow' privkey = mk_privkey(secret_seed) sender = privtoaddr(privkey) # fetch nonce nonce = quantity_decoder(JSONRPCClient().call('eth_getTransactionCount', address_encoder(sender), 'pending')) # create transaction tx = Transaction(nonce, default_gasprice, default_startgas, to=z_address, value=100, data='') tx.sign(privkey) tx_dict = tx.to_dict() tx_dict.pop('hash') res = JSONRPCClient().eth_sendTransaction(**tx_dict) if len(res) == 20: print 'contract created @', res.encode('hex') else: assert len(res) == 32 print 'tx hash', res.encode('hex')
def send_transaction(self, sender, to, value=0, data='', startgas=0, gasprice=10 * denoms.szabo): "can send a locally signed transaction if privkey is given" assert self.privkey or sender if self.privkey: _sender = sender sender = privtoaddr(self.privkey) assert sender == _sender assert sender # fetch nonce nonce = self.nonce(sender) if not startgas: startgas = quantity_decoder(self.call('eth_gasLimit')) - 1 # create transaction tx = Transaction(nonce, gasprice, startgas, to=to, value=value, data=data) if self.privkey: tx.sign(self.privkey) tx_dict = tx.to_dict() tx_dict.pop('hash') for k, v in dict(gasprice='gasPrice', startgas='gas').items(): tx_dict[v] = tx_dict.pop(k) tx_dict['sender'] = sender res = self.eth_sendTransaction(**tx_dict) assert len(res) in (20, 32) return res.encode('hex')
def new_filter(self, fromBlock="", toBlock="", address=None, topics=[]): encoders = dict(fromBlock=block_tag_encoder, toBlock=block_tag_encoder, address=address_encoder, topics=lambda x: [topic_encoder(t) for t in x]) data = { k: encoders[k](v) for k, v in locals().items() if k not in ('self', 'encoders') and v is not None } fid = self.call('eth_newFilter', data) return quantity_decoder(fid)
def nonce(self, address): if len(address) == 40: address = address.decode('hex') try: res = self.call('eth_nonce', address_encoder(address), 'pending') return quantity_decoder(res) except JSONRPCClientReplyError as e: if e.message == 'Method not found': raise JSONRPCClientReplyError( "'eth_nonce' is not supported by your endpoint (pyethapp only). " "For transactions use server-side nonces: " "('eth_sendTransaction' with 'nonce=None')") raise e
def is_synced(self): result = self.client.call('eth_syncing') # the node is synchronized if result is False: return True current_block = self.block_number() highest_block = quantity_decoder(result['highestBlock']) if highest_block - current_block > 2: return False return True
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 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 hydrachain_wait(privatekeys, number_of_nodes): """ Wait until the hydrchain cluster is ready. """ jsonrpc_client = JSONRPCClient( host='0.0.0.0', privkey=privatekeys[0], print_communication=False, ) quantity = jsonrpc_client.call('net_peerCount') tries = 5 while quantity != number_of_nodes and tries > 0: gevent.sleep(0.5) quantity = quantity_decoder(jsonrpc_client.call('net_peerCount')) if quantity != number_of_nodes: raise Exception('hydrachain is taking to long to initialize')
def signed_tx_example(to=z_address, value=100): from ethereum.transactions import Transaction from pyethapp.accounts import mk_privkey, privtoaddr secret_seed = 'wow' privkey = mk_privkey(secret_seed) sender = privtoaddr(privkey) # fetch nonce nonce = quantity_decoder( JSONRPCClient().call('eth_getTransactionCount', address_encoder(sender), 'pending')) # create transaction tx = Transaction(nonce, default_gasprice, default_startgas, to=z_address, value=value, data='') tx.sign(privkey) tx_dict = tx.to_dict() tx_dict.pop('hash') res = JSONRPCClient().eth_sendTransaction(**tx_dict) if len(res) == 20: print 'contract created @', res.encode('hex') else: assert len(res) == 32 print 'tx hash', res.encode('hex')
def signed_tx_example(): from ethereum.transactions import Transaction from pyethapp.accounts import mk_privkey, privtoaddr secret_seed = "wow" privkey = mk_privkey(secret_seed) sender = privtoaddr(privkey) # fetch nonce nonce = quantity_decoder(JSONRPCClient().call("eth_getTransactionCount", address_encoder(sender), "pending")) # create transaction tx = Transaction(nonce, default_gasprice, default_startgas, to=z_address, value=100, data="") tx.sign(privkey) tx_dict = tx.to_dict() tx_dict.pop("hash") res = JSONRPCClient().eth_sendTransaction(**tx_dict) if len(res) == 20: print "contract created @", res.encode("hex") else: assert len(res) == 32 print "tx hash", res.encode("hex")
def balance(self, account): b = quantity_decoder( self.call('eth_getBalance', address_encoder(account), 'pending')) return b
def get_balance(account): b = quantity_decoder( JSONRPCClient().call('eth_getBalance', address_encoder(account), 'pending')) return b
def blocknumber(self): return quantity_decoder(self.call('eth_blockNumber'))
def gaslimit(self): return quantity_decoder(self.call('eth_gasLimit'))
def lastgasprice(self): return quantity_decoder(self.call('eth_lastGasPrice'))
def nonce(self, address): if len(address) == 40: address = address.decode('hex') return quantity_decoder( self.call('eth_getTransactionCount', address_encoder(address), 'pending'))
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 blocknumber(self): """ Return the most recent block. """ return quantity_decoder(self.call('eth_blockNumber'))
def balance(self, account): """ Return the balance of the account of given address. """ res = self.call('eth_getBalance', address_encoder(account), 'pending') return quantity_decoder(res)