Example #1
0
def binary_gas_search(state: StateAPI,
                      transaction: SignedTransactionAPI,
                      tolerance: int = 1) -> int:
    """
    Run the transaction with various gas limits, progressively
    approaching the minimum needed to succeed without an OutOfGas exception.

    The starting range of possible estimates is:
    [transaction.intrinsic_gas, state.gas_limit].
    After the first OutOfGas exception, the range is: (largest_limit_out_of_gas, state.gas_limit].
    After the first run not out of gas, the range is: (largest_limit_out_of_gas, smallest_success].

    :param int tolerance: When the range of estimates is less than tolerance,
        return the top of the range.
    :returns int: The smallest confirmed gas to not throw an OutOfGas exception,
        subject to tolerance. If OutOfGas is thrown at block limit, return block limit.
    :raises VMError: if the computation fails even when given the block gas_limit to complete
    """
    if not hasattr(transaction, 'sender'):
        raise TypeError(
            "Transaction is missing attribute sender.",
            "If sending an unsigned transaction, use SpoofTransaction and provide the",
            "sender using the 'from' parameter")

    minimum_transaction = cast(
        SignedTransactionAPI,
        SpoofTransaction(
            transaction,
            gas=transaction.intrinsic_gas,
            gas_price=0,
        ))

    if _get_computation_error(state, minimum_transaction) is None:
        return transaction.intrinsic_gas

    maximum_transaction = cast(
        SignedTransactionAPI,
        SpoofTransaction(
            transaction,
            gas=state.gas_limit,
            gas_price=0,
        ))
    error = _get_computation_error(state, maximum_transaction)
    if error is not None:
        raise error

    minimum_viable = state.gas_limit
    maximum_out_of_gas = transaction.intrinsic_gas
    while minimum_viable - maximum_out_of_gas > tolerance:
        midpoint = (minimum_viable + maximum_out_of_gas) // 2
        test_transaction = cast(SignedTransactionAPI,
                                SpoofTransaction(transaction, gas=midpoint))
        if _get_computation_error(state, test_transaction) is None:
            minimum_viable = midpoint
        else:
            maximum_out_of_gas = midpoint

    return minimum_viable
Example #2
0
def dict_to_spoof_transaction(
        chain: AsyncChainAPI, header: BlockHeaderAPI,
        transaction_dict: Dict[str, Any]) -> SignedTransactionAPI:
    """
    Convert dicts used in calls & gas estimates into a spoof transaction
    """
    txn_dict = normalize_transaction_dict(transaction_dict)
    sender = txn_dict.get('from', ZERO_ADDRESS)

    if 'nonce' in txn_dict:
        nonce = txn_dict['nonce']
    else:
        vm = chain.get_vm(header)
        nonce = vm.state.get_nonce(sender)

    gas_price = txn_dict.get('gasPrice', 0)
    gas = txn_dict.get('gas', header.gas_limit)

    unsigned = chain.get_vm_class(header).create_unsigned_transaction(
        nonce=nonce,
        gas_price=gas_price,
        gas=gas,
        to=txn_dict['to'],
        value=txn_dict['value'],
        data=txn_dict['data'],
    )
    return cast(SignedTransactionAPI, SpoofTransaction(unsigned, from_=sender))
Example #3
0
def new_transaction(vm,
                    from_,
                    to,
                    amount=0,
                    private_key=None,
                    gas_price=10,
                    gas=100000,
                    data=b'',
                    chain_id=None):
    """
    Create and return a transaction sending amount from <from_> to <to>.

    The transaction will be signed with the given private key.
    """
    nonce = vm.state.account_db.get_nonce(from_)
    tx = vm.create_unsigned_transaction(
        nonce=nonce,
        gas_price=gas_price,
        gas=gas,
        to=to,
        value=amount,
        data=data,
    )
    if private_key:
        if chain_id is None:
            return tx.as_signed_transaction(private_key)
        else:
            return tx.as_signed_transaction(private_key, chain_id=chain_id)
    else:
        return SpoofTransaction(tx, from_=from_)
Example #4
0
def new_transaction(
        vm,
        from_,
        to,
        amount=0,
        private_key=None,
        gas_price=10**
    10,  # 10 gwei, to easily cover the initial London fee of 1 gwei
        gas=100000,
        data=b'',
        nonce=None,
        chain_id=None):
    """
    Create and return a transaction sending amount from <from_> to <to>.

    The transaction will be signed with the given private key.
    """
    if nonce is None:
        nonce = vm.state.get_nonce(from_)

    tx = vm.create_unsigned_transaction(
        nonce=nonce,
        gas_price=gas_price,
        gas=gas,
        to=to,
        value=amount,
        data=data,
    )
    if private_key:
        if chain_id is None:
            return tx.as_signed_transaction(private_key)
        else:
            return tx.as_signed_transaction(private_key, chain_id=chain_id)
    else:
        return SpoofTransaction(tx, from_=from_)
Example #5
0
    def deploy_transaction(self,
                           input,
                           gas_price=settings.GAS_PRICE,
                           debug=False):
        transaction = input["transaction"]
        from_account = decode_hex(transaction["from"])
        nonce = self.vm.state.get_nonce(from_account)
        try:
            to = decode_hex(transaction["to"])
        except:
            to = transaction["to"]
        tx = self.vm.create_unsigned_transaction(
            nonce=nonce,
            gas_price=gas_price,
            gas=transaction["gaslimit"],
            to=to,
            value=transaction["value"],
            data=decode_hex(transaction["data"]),
        )
        tx = SpoofTransaction(tx, from_=from_account)

        block = input["block"]
        if "timestamp" in block and block["timestamp"] is not None:
            self.vm.state.fuzzed_timestamp = block["timestamp"]
        else:
            self.vm.state.fuzzed_timestamp = None
        if "blocknumber" in block and block["blocknumber"] is not None:
            self.vm.state.fuzzed_blocknumber = block["blocknumber"]
        else:
            self.vm.state.fuzzed_blocknumber = None

        global_state = input["global_state"]
        if "balance" in global_state and global_state["balance"] is not None:
            self.vm.state.fuzzed_balance = global_state["balance"]
        else:
            self.vm.state.fuzzed_balance = None

        if "call_return" in global_state and global_state["call_return"] is not None \
                and len(global_state["call_return"]) > 0:
            self.vm.state.fuzzed_call_return = global_state["call_return"]
        if "extcodesize" in global_state and global_state["extcodesize"] is not None \
                and len(global_state["extcodesize"]) > 0:
            self.vm.state.fuzzed_extcodesize = global_state["extcodesize"]

        environment = input["environment"]
        if "returndatasize" in environment and environment[
                "returndatasize"] is not None:
            self.vm.state.fuzzed_returndatasize = environment["returndatasize"]

        self.storage_emulator.set_balance(from_account,
                                          settings.ACCOUNT_BALANCE)
        return self.execute(tx, debug=debug)
Example #6
0
 def deploy_contract(self, creator, bin_code, amount=0, gas=settings.GAS_LIMIT, gas_price=settings.GAS_PRICE, debug=False):
     nonce = self.vm.state.get_nonce(decode_hex(creator))
     tx = self.vm.create_unsigned_transaction(
         nonce=nonce,
         gas_price=gas_price,
         gas=gas,
         to=CREATE_CONTRACT_ADDRESS,
         value=amount,
         data=decode_hex(bin_code),
     )
     tx = SpoofTransaction(tx, from_=decode_hex(creator))
     result = self.execute(tx, debug=debug)
     address = to_canonical_address(encode_hex(result.msg.storage_address))
     self.storage_emulator.set_balance(address, 1)
     return result
Example #7
0
def run_computation(vm,
                    create_address,
                    code,
                    gas=1000000,
                    to=CANONICAL_ADDRESS_A,
                    transaction_sender=b'\x11' * 20,
                    data=b'',
                    access_list=None):
    executor = vm.state.get_transaction_executor()

    message = Message(
        to=to,
        sender=CANONICAL_ADDRESS_B,
        create_address=create_address,
        value=0,
        data=data,
        code=code,
        gas=gas,
    )
    if access_list is not None:
        txn_builder = vm.get_transaction_builder()
        unsigned_transaction = txn_builder.new_unsigned_access_list_transaction(
            vm.chain_context.chain_id,
            nonce=2,
            gas_price=1,
            gas=gas,
            to=to,
            value=3,
            data=data,
            access_list=access_list,
        )
    else:
        unsigned_transaction = vm.create_unsigned_transaction(
            nonce=2,
            gas_price=1,
            gas=gas,
            to=to,
            value=3,
            data=data,
        )
    transaction = SpoofTransaction(unsigned_transaction,
                                   from_=transaction_sender)

    return executor.build_computation(message, transaction)