def prepare_computation(vm_class): message = Message( to=CANONICAL_ADDRESS_A, sender=CANONICAL_ADDRESS_B, value=100, data=b'', code=b'', gas=800, ) tx_context = vm_class._state_class.transaction_context_class( gas_price=1, origin=CANONICAL_ADDRESS_B, ) vm = vm_class(GENESIS_HEADER, ChainDB(AtomicDB())) computation = vm_class._state_class.computation_class( state=vm.state, message=message, transaction_context=tx_context, ) computation.state.account_db.touch_account( decode_hex(EMPTY_ADDRESS_IN_STATE)) computation.state.account_db.set_code(decode_hex(ADDRESS_WITH_CODE[0]), ADDRESS_WITH_CODE[1]) return computation
def message(): message = Message( to=CANONICAL_ADDRESS_A, sender=CANONICAL_ADDRESS_B, value=100, data=b'', code=b'', gas=100, ) return message
def build_evm_message(self, transaction): transaction_context = self.get_transaction_context(transaction) gas_fee = transaction.gas * transaction_context.gas_price # Buy Gas self.vm_state.account_db.delta_balance(transaction.sender, -1 * gas_fee) # Increment Nonce self.vm_state.account_db.increment_nonce(transaction.sender) # Setup VM Message message_gas = transaction.gas - transaction.intrinsic_gas if transaction.to == constants.CREATE_CONTRACT_ADDRESS: contract_address = generate_contract_address( transaction.sender, self.vm_state.account_db.get_nonce(transaction.sender) - 1, ) data = b'' code = transaction.data else: contract_address = None data = transaction.data code = self.vm_state.account_db.get_code(transaction.to) self.vm_state.logger.debug( ( "TRANSACTION: sender: %s | to: %s | value: %s | gas: %s | " "gas-price: %s | s: %s | r: %s | v: %s | data-hash: %s" ), encode_hex(transaction.sender), encode_hex(transaction.to), transaction.value, transaction.gas, transaction.gas_price, transaction.s, transaction.r, transaction.v, encode_hex(keccak(transaction.data)), ) message = Message( gas=message_gas, to=transaction.to, sender=transaction.sender, value=transaction.value, data=data, code=code, create_address=contract_address, ) return message
def _create_message(gas=1, to=ADDRESS_A, sender=ADDRESS_B, value=0, data=b"", code=b"", **kwargs): return Message(gas=gas, to=to, sender=sender, value=value, data=data, code=code, **kwargs)
def test_is_origin_computation(computation, transaction_context): assert computation.is_origin_computation message2 = Message( to=CANONICAL_ADDRESS_A, # Different sender than the tx context origin sender=CANONICAL_ADDRESS_A, value=100, data=b'', code=b'', gas=100, ) computation2 = DummyComputation( state=None, message=message2, transaction_context=transaction_context, ) assert not computation2.is_origin_computation
def execute_bytecode( self, origin, gas_price, gas, to, sender, value, data, code, code_address=None, ): exit("NOT IMPLEMENTED YET") """ Execute raw bytecode in the context of the current state of the virtual machine. """ if origin is None: origin = sender # Construct a message message = Message( gas=gas, to=to, sender=sender, value=value, data=data, code=code, code_address=code_address, ) # Construction a tx context transaction_context = self.state.get_transaction_context_class()( gas_price=gas_price, origin=origin, ) # Execute it in the VM return self.state.get_computation( message, transaction_context).apply_computation( self.state, message, transaction_context, )
def fixture_to_computation(fixture, code, vm): message = Message( to=fixture['exec']['address'], sender=fixture['exec']['caller'], value=fixture['exec']['value'], data=fixture['exec']['data'], code=code, gas=fixture['exec']['gas'], ) transaction_context = BaseTransactionContext( origin=fixture['exec']['origin'], gas_price=fixture['exec']['gasPrice'], ) return vm.state.get_computation(message, transaction_context).apply_computation( vm.state, message, transaction_context, )
def prepare_child_message(self, gas: int, to: bytes, value: int, data: bytes, code: bytes, **kwargs: Any) -> Message: """ Helper method for creating a child computation. """ kwargs.setdefault('sender', self.msg.storage_address) child_message = Message( gas=gas, to=to, value=value, data=data, code=code, depth=self.msg.depth + 1, **kwargs ) return child_message
def test_vm_fixtures(fixture, vm_class, computation_getter): chaindb = ChainDB(get_db_backend()) header = BlockHeader( coinbase=fixture['env']['currentCoinbase'], difficulty=fixture['env']['currentDifficulty'], block_number=fixture['env']['currentNumber'], gas_limit=fixture['env']['currentGasLimit'], timestamp=fixture['env']['currentTimestamp'], ) vm = vm_class(header=header, chaindb=chaindb) state = vm.state setup_account_db(fixture['pre'], state.account_db) code = state.account_db.get_code(fixture['exec']['address']) # Update state_root manually vm.block = vm.block.copy(header=vm.block.header.copy( state_root=state.state_root)) message = Message( to=fixture['exec']['address'], sender=fixture['exec']['caller'], value=fixture['exec']['value'], data=fixture['exec']['data'], code=code, gas=fixture['exec']['gas'], ) transaction_context = BaseTransactionContext( origin=fixture['exec']['origin'], gas_price=fixture['exec']['gasPrice'], ) computation = vm.state.get_computation( message, transaction_context).apply_computation( vm.state, message, transaction_context, ) # Update state_root manually vm.block = vm.block.copy( header=vm.block.header.copy(state_root=computation.state.state_root), ) if 'post' in fixture: # # Success checks # assert not computation.is_error log_entries = computation.get_log_entries() if 'logs' in fixture: actual_logs_hash = hash_log_entries(log_entries) expected_logs_hash = fixture['logs'] assert expected_logs_hash == actual_logs_hash elif log_entries: raise AssertionError("Got log entries: {0}".format(log_entries)) expected_output = fixture['out'] assert computation.output == expected_output gas_meter = computation._gas_meter expected_gas_remaining = fixture['gas'] actual_gas_remaining = gas_meter.gas_remaining gas_delta = actual_gas_remaining - expected_gas_remaining assert gas_delta == 0, "Gas difference: {0}".format(gas_delta) call_creates = fixture.get('callcreates', []) assert len(computation.children) == len(call_creates) call_creates = fixture.get('callcreates', []) for child_computation, created_call in zip(computation.children, call_creates): to_address = created_call['destination'] data = created_call['data'] gas_limit = created_call['gasLimit'] value = created_call['value'] assert child_computation.msg.to == to_address assert data == child_computation.msg.data or child_computation.msg.code assert gas_limit == child_computation.msg.gas assert value == child_computation.msg.value expected_account_db = fixture['post'] else: # # Error checks # assert computation.is_error assert isinstance(computation._error, VMError) expected_account_db = fixture['pre'] verify_account_db(expected_account_db, vm.state.account_db)
def build_evm_message( self, send_transaction: BaseTransaction, transaction_context: BaseTransactionContext, receive_transaction: BaseReceiveTransaction = None) -> Message: if transaction_context.is_refund == True: # Setup VM Message message_gas = 0 refund_amount = receive_transaction.remaining_refund contract_address = None data = b'' code = b'' self.vm_state.logger.debug( ("REFUND TRANSACTION: sender: %s | refund amount: %s "), encode_hex(send_transaction.sender), refund_amount, ) elif transaction_context.is_receive == True: # this is a receive transaction - now we get to execute any code or data # transaction_context = self.get_transaction_context(send_transaction) # gas_fee = transaction.transaction.gas * transaction_context.gas_price # TODO: # fail niceley here so we can put a failed tx. the failed tx can be seen in the receipt status_code # we will have to refund the sender the money if this is the case. # so the amount of gas the send tx paid is saved as transaction.transaction.gas # Setup VM Message # message_gas = transaction.transaction.gas - transaction.transaction.intrinsic_gas -1 * gas_fee # I tested this, if this tx uses more gas than what was charged to the send tx it will fail. # Setup VM Message message_gas = send_transaction.gas - send_transaction.intrinsic_gas refund_amount = 0 if send_transaction.to == constants.CREATE_CONTRACT_ADDRESS: # the contract address was already chosen on the send transaction. It is now the caller chain address contract_address = transaction_context.caller_chain_address data = b'' code = send_transaction.data else: contract_address = None data = send_transaction.data code = self.vm_state.account_db.get_code(send_transaction.to) self.vm_state.logger.debug( ("RECEIVE TRANSACTION: hash: %s | sender: %s | to: %s | value: %s | gas: %s | " "gas-price: %s | s: %s | r: %s | v: %s | data-hash: %s"), encode_hex(send_transaction.hash), encode_hex(send_transaction.sender), encode_hex(send_transaction.to), send_transaction.value, send_transaction.gas, send_transaction.gas_price, send_transaction.s, send_transaction.r, send_transaction.v, encode_hex(keccak(data)), ) else: # this is a send transaction #transaction_context = self.get_transaction_context(send_transaction, receive_transaction) gas_fee = send_transaction.gas * transaction_context.gas_price #this is the default gas fee for the send tx that needs to be subtracted on the receive of a smart contract # Buy Gas self.vm_state.account_db.delta_balance(send_transaction.sender, -1 * gas_fee) # Increment Nonce self.vm_state.account_db.increment_nonce(send_transaction.sender) # Setup VM Message message_gas = send_transaction.gas - send_transaction.intrinsic_gas refund_amount = 0 #when a contract is created with a send transaction, do no computation. #we have to put the computation back. because it needs to charge computation #gas on the send. We just have to make sure it doesnt execute the transaction... #TODO: make sure the computation is not executed #temporarily we will just do no computation. This means interactions with #smart contracts will cost no gas until we finish this. if send_transaction.to == constants.CREATE_CONTRACT_ADDRESS: contract_address = generate_contract_address( send_transaction.sender, self.vm_state.account_db.get_nonce(send_transaction.sender) - 1, ) data = b'' code = send_transaction.data else: contract_address = None data = send_transaction.data code = self.vm_state.account_db.get_code(send_transaction.to) self.vm_state.logger.debug( ("SEND TRANSACTION: sender: %s | to: %s | value: %s | gas: %s | " "gas-price: %s | s: %s | r: %s | v: %s | data-hash: %s"), encode_hex(send_transaction.sender), encode_hex(send_transaction.to), send_transaction.value, send_transaction.gas, send_transaction.gas_price, send_transaction.s, send_transaction.r, send_transaction.v, encode_hex(keccak(send_transaction.data)), ) message = Message( gas=message_gas, to=send_transaction.to, sender=send_transaction.sender, value=send_transaction.value, data=data, code=code, create_address=contract_address, refund_amount=refund_amount, ) return message