Example #1
0
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):
            gevent.sleep(sleep)
        else:
            return etherscan_block

    etherscan_block = quantity_decoder(requests.get(url).json()['result'])
    return etherscan_block
Example #2
0
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):
            gevent.sleep(sleep)
        else:
            return etherscan_block

    etherscan_block = quantity_decoder(requests.get(url).json()['result'])
    return etherscan_block
Example #3
0
    def nonce(self, address):
        if len(address) == 40:
            address = unhexlify(address)

        with self.nonce_lock:
            initialized = self.nonce_current_value is not None
            query_time = now()

            if self.nonce_last_update > query_time:
                # Python's 2.7 time is not monotonic and it's affected by clock
                # resets, force an update.
                self.nonce_update_interval = query_time - self.nonce_update_interval
                needs_update = True

            else:
                last_update_interval = query_time - self.nonce_last_update
                needs_update = last_update_interval > self.nonce_update_interval

            if initialized and not needs_update:
                self.nonce_current_value += 1
                return self.nonce_current_value

            pending_transactions_hex = self.call(
                'eth_getTransactionCount',
                address_encoder(address),
                'pending',
            )
            pending_transactions = quantity_decoder(pending_transactions_hex)
            nonce = pending_transactions + self.nonce_offset

            # we may have hammered the server and not all tx are
            # registered as `pending` yet
            if initialized:
                while nonce < self.nonce_current_value:
                    log.debug(
                        'nonce on server too low; retrying',
                        server=nonce,
                        local=self.nonce_current_value,
                    )

                    query_time = now()
                    pending_transactions_hex = self.call(
                        'eth_getTransactionCount',
                        address_encoder(address),
                        'pending',
                    )
                    pending_transactions = quantity_decoder(
                        pending_transactions_hex)
                    nonce = pending_transactions + self.nonce_offset

            self.nonce_current_value = nonce
            self.nonce_last_update = query_time

            return self.nonce_current_value
    def nonce(self, address):
        if len(address) == 40:
            address = unhexlify(address)

        with self.nonce_lock:
            initialized = self.nonce_current_value is not None
            query_time = now()

            if self.nonce_last_update > query_time:
                # Python's 2.7 time is not monotonic and it's affected by clock
                # resets, force an update.
                self.nonce_update_interval = query_time - self.nonce_update_interval
                needs_update = True

            else:
                last_update_interval = query_time - self.nonce_last_update
                needs_update = last_update_interval > self.nonce_update_interval

            if initialized and not needs_update:
                self.nonce_current_value += 1
                return self.nonce_current_value

            pending_transactions_hex = self.call(
                'eth_getTransactionCount',
                address_encoder(address),
                'pending',
            )
            pending_transactions = quantity_decoder(pending_transactions_hex)
            nonce = pending_transactions + self.nonce_offset

            # we may have hammered the server and not all tx are
            # registered as `pending` yet
            if initialized:
                while nonce < self.nonce_current_value:
                    log.debug(
                        'nonce on server too low; retrying',
                        server=nonce,
                        local=self.nonce_current_value,
                    )

                    query_time = now()
                    pending_transactions_hex = self.call(
                        'eth_getTransactionCount',
                        address_encoder(address),
                        'pending',
                    )
                    pending_transactions = quantity_decoder(pending_transactions_hex)
                    nonce = pending_transactions + self.nonce_offset

            self.nonce_current_value = nonce
            self.nonce_last_update = query_time

            return self.nonce_current_value
Example #5
0
    def eth_estimateGas(self,
                        sender: address = b'',
                        to: address = b'',
                        value: int = 0,
                        data: bytes = b'',
                        startgas: int = None) -> Optional[int]:
        """ 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:
            sender: The address the transaction is sent from.
            to: The address the transaction is directed to.
            gas: Gas provided for the transaction execution. eth_call
                consumes zero gas, but this parameter may be needed by some
                executions.
            value: Integer of the value sent with this transaction.
            data: 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.
        """
        startgas = self.check_startgas(startgas)
        json_data = format_data_for_call(sender, to, value, data, startgas)
        try:
            res = self.call('eth_estimateGas', json_data)
        except EthNodeCommunicationError as e:
            tx_would_fail = e.error_code and e.error_code in (-32015, -32000)
            if tx_would_fail:  # -32015 is parity and -32000 is geth
                return None
            else:
                raise e

        return quantity_decoder(res)
Example #6
0
    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 is_synced(self) -> bool:
        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
Example #8
0
    def is_synced(self) -> bool:
        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
Example #9
0
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 eth_estimateGas(
            self,
            sender: address = b'',
            to: address = b'',
            value: int = 0,
            data: bytes = b'',
            startgas: int = GAS_PRICE,
            gasprice: int = GAS_PRICE) -> int:
        """ 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:
            sender: The address the transaction is sent from.
            to: The address the transaction is directed to.
            gas: Gas provided for the transaction execution. eth_call
                consumes zero gas, but this parameter may be needed by some
                executions.
            gasPrice: gasPrice used for unit of gas paid.
            value: Integer of the value sent with this transaction.
            data: 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 = format_data_for_call(
            sender,
            to,
            value,
            data,
            startgas,
            gasprice,
        )
        try:
            res = self.call('eth_estimateGas', json_data)
        except EthNodeCommunicationError as e:
            tx_would_fail = e.error_code and e.error_code in (-32015, -32000)
            if tx_would_fail:  # -32015 is parity and -32000 is geth
                return None
            else:
                raise e

        return quantity_decoder(res)
Example #11
0
    def nonce_update_from_node(self):
        nonce = -2
        nonce_available_value = self.nonce_available_value or -1

        # Wait until all tx are registered as pending
        while nonce < nonce_available_value:
            pending_transactions_hex = self.rpccall_with_retry(
                'eth_getTransactionCount',
                address_encoder(self.sender),
                'pending',
            )
            pending_transactions = quantity_decoder(pending_transactions_hex)
            nonce = pending_transactions + self.nonce_offset

            log.debug(
                'updated nonce from server',
                server=nonce,
                local=nonce_available_value,
            )

        self.nonce_last_update = time.time()
        self.nonce_available_value = nonce
    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)
Example #13
0
    def eth_estimateGas(
            self,
            sender='',
            to='',
            value=0,
            data='',
            startgas=GAS_PRICE,
            gasprice=GAS_PRICE):
        """ 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:
            sender: The address the transaction is sent 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 unit of gas paid.
            value (int): Integer of the value sent 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 = format_data_for_call(
            sender,
            to,
            value,
            data,
            startgas,
            gasprice,
        )
        res = self.call('eth_estimateGas', json_data)

        return quantity_decoder(res)
Example #14
0
 def gaslimit(self):
     last_block = self.call('eth_getBlockByNumber', 'latest', True)
     gas_limit = quantity_decoder(last_block['gasLimit'])
     return gas_limit
Example #15
0
 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)
Example #16
0
 def blocknumber(self):
     """ Return the most recent block. """
     return quantity_decoder(self.call('eth_blockNumber'))
 def block_number(self):
     """ Return the most recent block. """
     return quantity_decoder(self.call('eth_blockNumber'))
Example #18
0
    def _gasprice(self) -> int:
        if self.given_gas_price:
            return self.given_gas_price

        gas_price = self.call('eth_gasPrice')
        return quantity_decoder(gas_price)
Example #19
0
 def _gaslimit(self, location='pending') -> int:
     last_block = self.call('eth_getBlockByNumber', location, True)
     gas_limit = quantity_decoder(last_block['gasLimit'])
     # The gas limit can fluctuate from the actual pending limit by a maximum
     # of up to a 1/1024th of the previous gas limit
     return gas_limit - int(gas_limit / 1024)
Example #20
0
    def poll(
        self,
        transaction_hash: bytes,
        confirmations: int = None,
        timeout: float = None,
    ):
        """ Wait until the `transaction_hash` is applied or rejected.
        If timeout is None, this could wait indefinitely!

        Args:
            transaction_hash: Transaction hash that we are waiting for.
            confirmations: Number of block confirmations that we will
                wait for.
            timeout: Timeout in seconds, raise an Excpetion on timeout.
        """
        if transaction_hash.startswith(b'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 encoded)')

        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 too 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.web3.eth.getTransaction(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.block_number()

                while block_number < confirmation_block:
                    gevent.sleep(.5)
                    block_number = self.block_number()

        except gevent.Timeout:
            raise Exception('timeout when polling for transaction')

        finally:
            if deadline:
                deadline.cancel()
    def poll(
            self,
            transaction_hash: bytes,
            confirmations: Optional[int] = None,
            timeout: Optional[float] = None):
        """ Wait until the `transaction_hash` is applied or rejected.
        If timeout is None, this could wait indefinitely!

        Args:
            transaction_hash: Transaction hash that we are waiting for.
            confirmations: Number of block confirmations that we will
                wait for.
            timeout: Timeout in seconds, raise an Excpetion on timeout.
        """
        if transaction_hash.startswith(b'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 encoded)'
            )

        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 too 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.block_number()

                while block_number < confirmation_block:
                    gevent.sleep(.5)
                    block_number = self.block_number()

        except gevent.Timeout:
            raise Exception('timeout when polling for transaction')

        finally:
            if deadline:
                deadline.cancel()
 def gaslimit(self) -> int:
     last_block = self.call('eth_getBlockByNumber', 'latest', True)
     gas_limit = quantity_decoder(last_block['gasLimit'])
     return gas_limit
 def balance(self, account: address):
     """ Return the balance of the account of given address. """
     res = self.call('eth_getBalance', address_encoder(account), 'pending')
     return quantity_decoder(res)
Example #24
0
 def _gaslimit(self, location='pending') -> int:
     last_block = self.call('eth_getBlockByNumber', location, True)
     gas_limit = quantity_decoder(last_block['gasLimit'])
     return gas_limit * 8 // 10
Example #25
0
 def block_number(self):
     """ Return the most recent block. """
     return quantity_decoder(self.rpccall_with_retry('eth_blockNumber'))