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
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))
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_)
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_)
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)
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
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)