def build_computation(self, message, transaction): """Apply the message to the VM.""" transaction_context = self.get_transaction_context(transaction) if message.is_create: is_collision = self.vm_state.account_db.account_has_code_or_nonce( message.storage_address ) if is_collision: # The address of the newly created contract has *somehow* collided # with an existing contract address. computation = self.vm_state.get_computation(message, transaction_context) computation._error = ContractCreationCollision( "Address collision while creating contract: {0}".format( encode_hex(message.storage_address), ) ) self.vm_state.logger.debug( "Address collision while creating contract: %s", encode_hex(message.storage_address), ) else: computation = self.vm_state.get_computation( message, transaction_context, ).apply_create_message() else: computation = self.vm_state.get_computation( message, transaction_context).apply_message() return computation
def apply_create_message(self): computation = self.apply_message() if computation.is_error: return computation else: contract_code = computation.output if contract_code: contract_code_gas_fee = len(contract_code) * constants.GAS_CODEDEPOSIT try: computation.consume_gas( contract_code_gas_fee, reason="Write contract code for CREATE", ) except OutOfGas: computation.output = b'' else: self.logger.debug( "SETTING CODE: %s -> length: %s | hash: %s", encode_hex(self.msg.storage_address), len(contract_code), encode_hex(keccak(contract_code)) ) self.state.account_db.set_code(self.msg.storage_address, contract_code) return computation
def apply_create_message(self, validate=True): snapshot = self.state.snapshot() if self.transaction_context.is_receive and not self.transaction_context.is_refund: # EIP161 nonce incrementation self.state.account_db.increment_nonce(self.msg.storage_address) computation = self.apply_message(validate=validate) if computation.is_error: self.state.revert(snapshot) return computation else: contract_code = computation.output if contract_code and len(contract_code) >= EIP170_CODE_SIZE_LIMIT: computation._error = OutOfGas( "Contract code size exceeds EIP170 limit of {0}. Got code of " "size: {1}".format( EIP170_CODE_SIZE_LIMIT, len(contract_code), )) self.state.revert(snapshot) elif contract_code: contract_code_gas_cost = len( contract_code) * constants.GAS_CODEDEPOSIT try: computation.consume_gas( contract_code_gas_cost, reason="Write contract code for CREATE", ) except OutOfGas as err: # Different from Frontier: reverts state on gas failure while # writing contract code. computation._error = err self.logger.debug("NOT ENOUGH GAS TO WRITE CONTRACT CODE") self.state.revert(snapshot) else: if self.transaction_context.is_receive and not self.transaction_context.is_refund: # we only set the code if it is a receive transaction if self.logger: self.logger.debug( "SETTING CODE: %s -> length: %s | hash: %s", encode_hex(self.msg.storage_address), len(contract_code), encode_hex(keccak(contract_code))) self.state.account_db.set_code( self.msg.storage_address, contract_code) self.state.commit(snapshot) else: self.state.commit(snapshot) return computation
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 check_pow(block_number: int, mining_hash: Hash32, mix_hash: Hash32, nonce: bytes, difficulty: int) -> None: validate_length(mix_hash, 32, title="Mix Hash") validate_length(mining_hash, 32, title="Mining Hash") validate_length(nonce, 8, title="POW Nonce") cache = get_cache(block_number) mining_output = hashimoto_light(block_number, cache, mining_hash, big_endian_to_int(nonce)) if mining_output[b'mix digest'] != mix_hash: raise ValidationError("mix hash mismatch; {0} != {1}".format( encode_hex(mining_output[b'mix digest']), encode_hex(mix_hash))) result = big_endian_to_int(mining_output[b'result']) validate_lte(result, 2**256 // difficulty, title="POW Difficulty")
def __enter__(self) -> 'BaseComputation': self.logger.debug( ( "COMPUTATION STARTING: gas: %s | from: %s | to: %s | value: %s " "| depth %s | static: %s" ), self.msg.gas, encode_hex(self.msg.sender), encode_hex(self.msg.to), self.msg.value, self.msg.depth, "y" if self.msg.is_static else "n", ) return self
def _decode_header_to_dict(cls, encoded_header: bytes) -> Iterator[Tuple[str, Any]]: if len(encoded_header) != cls.smc_encoded_size: raise ValidationError( "Expected encoded header to be of size: {0}. Got size {1} instead.\n- {2}".format( cls.smc_encoded_size, len(encoded_header), encode_hex(encoded_header), ) ) start_indices = accumulate(lambda i, field: i + field[2], cls.fields_with_sizes, 0) field_bounds = sliding_window(2, start_indices) for byte_range, field in zip(field_bounds, cls._meta.fields): start_index, end_index = byte_range field_name, field_type = field field_bytes = encoded_header[start_index:end_index] if field_type == rlp.sedes.big_endian_int: # remove the leading zeros, to avoid `not minimal length` error in deserialization formatted_field_bytes = field_bytes.lstrip(b'\x00') elif field_type == address: formatted_field_bytes = field_bytes[-20:] else: formatted_field_bytes = field_bytes yield field_name, field_type.deserialize(formatted_field_bytes)
def finalize_computation(self, transaction, computation): # Self Destruct Refunds num_deletions = len(computation.get_accounts_for_deletion()) if num_deletions: computation.refund_gas(REFUND_SELFDESTRUCT * num_deletions) # Gas Refunds gas_remaining = computation.get_gas_remaining() gas_refunded = computation.get_gas_refund() gas_used = transaction.gas - gas_remaining gas_refund = min(gas_refunded, gas_used // 2) gas_refund_amount = (gas_refund + gas_remaining) * transaction.gas_price if gas_refund_amount: self.vm_state.logger.debug( 'TRANSACTION REFUND: %s -> %s', gas_refund_amount, encode_hex(computation.msg.sender), ) self.vm_state.account_db.delta_balance(computation.msg.sender, gas_refund_amount) # Miner Fees transaction_fee = \ (transaction.gas - gas_remaining - gas_refund) * transaction.gas_price self.vm_state.logger.debug( 'TRANSACTION FEE: %s -> %s', transaction_fee, encode_hex(self.vm_state.coinbase), ) self.vm_state.account_db.delta_balance(self.vm_state.coinbase, transaction_fee) # Process Self Destructs for account, beneficiary in computation.get_accounts_for_deletion(): # TODO: need to figure out how we prevent multiple selfdestructs from # the same account and if this is the right place to put this. self.vm_state.logger.debug('DELETING ACCOUNT: %s', encode_hex(account)) # TODO: this balance setting is likely superflous and can be # removed since `delete_account` does this. self.vm_state.account_db.set_balance(account, 0) self.vm_state.account_db.delete_account(account) return computation
def __exit__(self, exc_type: None, exc_value: None, traceback: None) -> None: if exc_value and isinstance(exc_value, VMError): self.logger.debug( ( "COMPUTATION ERROR: gas: %s | from: %s | to: %s | value: %s | " "depth: %s | static: %s | error: %s" ), self.msg.gas, encode_hex(self.msg.sender), encode_hex(self.msg.to), self.msg.value, self.msg.depth, "y" if self.msg.is_static else "n", exc_value, ) self._error = exc_value if self.should_burn_gas: self.consume_gas( self._gas_meter.gas_remaining, reason=" ".join(( "Zeroing gas due to VM Exception:", str(exc_value), )), ) # suppress VM exceptions return True elif exc_type is None: self.logger.debug( ( "COMPUTATION SUCCESS: from: %s | to: %s | value: %s | " "depth: %s | static: %s | gas-used: %s | gas-remaining: %s" ), encode_hex(self.msg.sender), encode_hex(self.msg.to), self.msg.value, self.msg.depth, "y" if self.msg.is_static else "n", self.msg.gas - self._gas_meter.gas_remaining, self._gas_meter.gas_remaining, )
def apply_create_message(self): snapshot = self.state.snapshot() computation = self.apply_message() if computation.is_error: self.state.revert(snapshot) return computation else: contract_code = computation.output if contract_code: contract_code_gas_cost = len( contract_code) * constants.GAS_CODEDEPOSIT try: computation.consume_gas( contract_code_gas_cost, reason="Write contract code for CREATE", ) except OutOfGas as err: # Different from Frontier: reverts state on gas failure while # writing contract code. computation._error = err self.state.revert(snapshot) else: if self.logger: self.logger.debug( "SETTING CODE: %s -> length: %s | hash: %s", encode_hex(self.msg.storage_address), len(contract_code), encode_hex(keccak(contract_code))) self.state.account_db.set_code(self.msg.storage_address, contract_code) self.state.commit(snapshot) else: self.state.commit(snapshot) return computation
def apply_message(self): snapshot = self.state.snapshot() if self.msg.depth > constants.STACK_DEPTH_LIMIT: raise StackDepthLimit("Stack depth limit reached") if self.msg.should_transfer_value and self.msg.value: sender_balance = self.state.account_db.get_balance(self.msg.sender) if sender_balance < self.msg.value: raise InsufficientFunds( "Insufficient funds: {0} < {1}".format(sender_balance, self.msg.value) ) self.state.account_db.delta_balance(self.msg.sender, -1 * self.msg.value) self.state.account_db.delta_balance(self.msg.storage_address, self.msg.value) self.logger.debug( "TRANSFERRED: %s from %s -> %s", self.msg.value, encode_hex(self.msg.sender), encode_hex(self.msg.storage_address), ) self.state.account_db.touch_account(self.msg.storage_address) computation = self.apply_computation( self.state, self.msg, self.transaction_context, ) if computation.is_error: self.state.revert(snapshot) else: self.state.commit(snapshot) return computation
def _selfdestruct(computation, beneficiary): local_balance = computation.state.account_db.get_balance( computation.msg.storage_address) beneficiary_balance = computation.state.account_db.get_balance(beneficiary) # 1st: Transfer to beneficiary computation.state.account_db.set_balance( beneficiary, local_balance + beneficiary_balance) # 2nd: Zero the balance of the address being deleted (must come after # sending to beneficiary in case the contract named itself as the # beneficiary. computation.state.account_db.set_balance(computation.msg.storage_address, 0) computation.logger.debug( "SELFDESTRUCT: %s (%s) -> %s", encode_hex(computation.msg.storage_address), local_balance, encode_hex(beneficiary), ) # 3rd: Register the account to be deleted computation.register_account_for_deletion(beneficiary) raise Halt('SELFDESTRUCT')
def add_possible_refunds_to_currently_executing_transaction( self, send_transaction: BaseTransaction, computation: 'BaseComputation', receive_transaction: BaseReceiveTransaction = None, refund_transaction: BaseReceiveTransaction = None, ) -> Union[BaseTransaction, BaseReceiveTransaction]: ''' Receive transactions that have computation will have to refund any leftover gas. This refund amount depends on the computation which is why it is processed here and added the receive tx. :param send_transaction: :param computation: :param receive_transaction: :param refund_transaction: :return: ''' if computation.transaction_context.is_refund: # this kind of receive transaction will always have 0 remaining refund so it doesnt need to be modified return refund_transaction elif computation.transaction_context.is_receive: # this kind of receive transaction may include a nonzero gas refund. Must add it in now # It gets a refund if send has data and is not create. ie. there was a computation on receive if computation.msg.data != b'' and not computation.msg.is_create: gas_remaining = computation.get_gas_remaining() gas_refunded = computation.get_gas_refund() gas_used = send_transaction.gas - gas_remaining gas_refund = min(gas_refunded, gas_used // 2) gas_refund_amount = ( gas_refund + gas_remaining) * send_transaction.gas_price self.vm_state.logger.debug( 'SAVING REFUND TO RECEIVE TX: %s -> %s', gas_refund_amount, encode_hex(computation.msg.sender), ) receive_transaction = receive_transaction.copy( remaining_refund=gas_refund_amount) return receive_transaction else: #this is a send transaction. Refunds are only possible on receive tx. So send it back unmodified return send_transaction
def sstore(computation): slot, value = computation.stack_pop(num_items=2, type_hint=constants.UINT256) current_value = computation.state.account_db.get_storage( address=computation.msg.storage_address, slot=slot, ) is_currently_empty = not bool(current_value) is_going_to_be_empty = not bool(value) if is_currently_empty: gas_refund = 0 elif is_going_to_be_empty: gas_refund = constants.REFUND_SCLEAR else: gas_refund = 0 if is_currently_empty and is_going_to_be_empty: gas_cost = constants.GAS_SRESET elif is_currently_empty: gas_cost = constants.GAS_SSET elif is_going_to_be_empty: gas_cost = constants.GAS_SRESET else: gas_cost = constants.GAS_SRESET computation.consume_gas(gas_cost, reason="SSTORE: {0}[{1}] -> {2} ({3})".format( encode_hex(computation.msg.storage_address), slot, value, current_value, )) if gas_refund: computation.refund_gas(gas_refund) computation.state.account_db.set_storage( address=computation.msg.storage_address, slot=slot, value=value, )
def finalize_computation(self, transaction, computation): computation = super().finalize_computation(transaction, computation) # # EIP161 state clearing # touched_accounts = collect_touched_accounts(computation) for account in touched_accounts: should_delete = ( self.vm_state.account_db.account_exists(account) and self.vm_state.account_db.account_is_empty(account)) if should_delete: self.vm_state.logger.debug( "CLEARING EMPTY ACCOUNT: %s", encode_hex(account), ) self.vm_state.account_db.delete_account(account) return computation
for test_group, tests in test_groups.items(): for filler, filler_kwargs in tests: test_name = get_test_name(filler) filename = test_name + ".json" filler_src_path = os.path.join(filler_dir, test_group, filename) filler_path = os.path.join(FILLER_PARENT_DIR, filler_src_path) test_path = os.path.join(TEST_PARENT_DIR, test_dir, test_group, filename) for path in [filler_path, test_path]: os.makedirs(os.path.dirname(path), exist_ok=True) formatted_filler = filler_formatter(filler) filler_string = json.dumps(formatted_filler, indent=4, sort_keys=True) with open(filler_path, "w") as filler_file: filler_file.write(filler_string) filler_hash = keccak(filler_string.encode("ascii")) info = { "source": filler_src_path, "sourceHash": encode_hex(filler_hash), } test = fill_test(filler, info=info, **filler_kwargs or {}) with open(test_path, "w") as test_file: json.dump(test, test_file, indent=4, sort_keys=True)
def test_basic_hexadecimal_encoding(value, expected): actual = encode_hex(value) assert actual == expected
def __repr__(self) -> str: return "<CollationHeader shard={} period={} hash={}>".format( self.shard_id, self.period, encode_hex(self.hash)[2:10], )
def hex_hash(self): return encode_hex(self.hash)
def __repr__(self) -> str: return '<BaseBlockHeader #{0} {1}>'.format( self.block_number, encode_hex(self.hash)[2:10], )
def apply_message(self, validate=True): snapshot = self.state.snapshot() if self.msg.depth > constants.STACK_DEPTH_LIMIT: raise StackDepthLimit("Stack depth limit reached") if self.msg.should_transfer_value: if self.transaction_context.is_refund: #this is a refund receive transaction try: self.state.account_db.delete_receivable_transaction( self.msg.sender, self.transaction_context.receive_tx_hash) except ReceivableTransactionNotFound as e: if validate: raise e self.state.account_db.delta_balance(self.msg.sender, self.msg.refund_amount) self.logger.debug( "REFUNDED: %s into %s", self.msg.refund_amount, encode_hex(self.msg.sender), ) elif self.transaction_context.is_receive: #this is a receive transaction try: self.state.account_db.delete_receivable_transaction( self.msg.storage_address, self.transaction_context.send_tx_hash, is_contract_deploy=self.msg.is_create) except ReceivableTransactionNotFound as e: if validate: raise e if self.msg.value: self.state.account_db.delta_balance( self.msg.storage_address, self.msg.value) self.logger.debug( "RECEIVED: %s into %s", self.msg.value, encode_hex(self.msg.storage_address), ) elif self.msg.value: # this is a send transaction if validate: sender_balance = self.state.account_db.get_balance( self.msg.sender) if sender_balance < self.msg.value: raise InsufficientFunds( "Insufficient funds: {0} < {1}".format( sender_balance, self.msg.value)) self.state.account_db.delta_balance(self.msg.sender, -1 * self.msg.value) self.logger.debug( "SENT: %s from %s to pending transactions", self.msg.value, encode_hex(self.msg.sender), ) self.state.account_db.touch_account(self.msg.storage_address) if self.transaction_context.is_refund: # We never run computations on a refund self.state.commit(snapshot) computation = self elif self.transaction_context.is_receive: # this is when we run all computation normally computation = self.apply_computation( self.state, self.msg, self.transaction_context, ) if computation.is_error: self.state.revert(snapshot) else: self.state.commit(snapshot) else: # this is a send transaction. We only run computation if is_create = True, and in that case we only run it to determine # the gas cost. So we create a snapshot to remove any changes other thank calculating gas cost. if self.msg.is_create: computation_snapshot = self.state.snapshot() computation = self.apply_computation( self.state, self.msg, self.transaction_context, ) if computation.is_error: # This will revert the computation snapshot as well. self.state.revert(snapshot) else: # computation worked, but we don't want it yet until the receive transaction. So lets revert the computation # but commit the transaction above. if self.logger: self.logger.debug( "REVERTING COMPUTATION FOR CONTRACT DEPLOYMENT. WILL DEPLOY ON RECEIVE TX." ) self.state.revert(computation_snapshot) self.state.commit(snapshot) else: computation = self return computation
def test_round_trip_with_hex_string_start(value): intermediate_value = decode_hex(value) round_trip_value = encode_hex(intermediate_value) assert round_trip_value == value
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
def finalize_computation( self, send_transaction: BaseTransaction, computation: 'BaseComputation') -> 'BaseComputation': #we only have to do any of this if it is a send transaction # Self Destruct Refunds num_deletions = len(computation.get_accounts_for_deletion()) if num_deletions: computation.refund_gas(REFUND_SELFDESTRUCT * num_deletions) if not computation.transaction_context.is_receive and not computation.transaction_context.is_refund: # this is a send transaction. This is the only kind that could potentially refund gas if computation.msg.is_create or computation.msg.data == b'': # We are deploying a smart contract, we pay all computation fees now and refund leftover gas # OR # This transaction has no computation. It is just a HLS transaction. Send the transaction and refund leftover gas gas_remaining = computation.get_gas_remaining() gas_refunded = computation.get_gas_refund() gas_used = send_transaction.gas - gas_remaining gas_refund = min(gas_refunded, gas_used // 2) gas_refund_amount = ( gas_refund + gas_remaining) * send_transaction.gas_price if gas_refund_amount: self.vm_state.logger.debug( 'GAS REFUND: %s -> %s', gas_refund_amount, encode_hex(computation.msg.sender), ) self.vm_state.account_db.delta_balance( computation.msg.sender, gas_refund_amount) # In order to keep the state consistent with the block headers, we want the newly created smart contract chain # to have an empty state. The smart contract data will be stored when the recieve transaction is executed. # At that time, the smart contract state can also be saved in the block header to keep the local state and # block header state consistent at all times. # This is also in line with the policy to only run computation on recieve transactions. if computation.transaction_context.is_receive and not computation.transaction_context.is_refund: # This is a receive transaction. This is the only kind that can process computations. # So this is the only case where we need to clean up stuff # Process Self Destructs for account, beneficiary in computation.get_accounts_for_deletion( ): # TODO: need to figure out how we prevent multiple selfdestructs from # the same account and if this is the right place to put this. self.vm_state.logger.debug('DELETING ACCOUNT: %s', encode_hex(account)) self.vm_state.account_db.delete_account(account) # # EIP161 state clearing # touched_accounts = collect_touched_accounts(computation) for account in touched_accounts: should_delete = ( self.vm_state.account_db.account_exists(account) and self.vm_state.account_db.account_is_empty(account)) if should_delete: self.vm_state.logger.debug( "CLEARING EMPTY ACCOUNT: %s", encode_hex(account), ) self.vm_state.account_db.delete_account(account) return computation
def __call__(self, computation): computation.consume_gas(self.gas_cost, reason=self.mnemonic) value, start_position, size = computation.stack_pop( num_items=3, type_hint=constants.UINT256, ) computation.extend_memory(start_position, size) insufficient_funds = computation.state.account_db.get_balance( computation.msg.storage_address) < value stack_too_deep = computation.msg.depth + 1 > constants.STACK_DEPTH_LIMIT if insufficient_funds or stack_too_deep: computation.stack_push(0) return call_data = computation.memory_read(start_position, size) create_msg_gas = self.max_child_gas_modifier( computation.get_gas_remaining()) computation.consume_gas(create_msg_gas, reason="CREATE") creation_nonce = computation.state.account_db.get_nonce( computation.msg.storage_address) computation.state.account_db.increment_nonce( computation.msg.storage_address) contract_address = generate_contract_address( computation.msg.storage_address, creation_nonce, ) is_collision = computation.state.account_db.account_has_code_or_nonce( contract_address) if is_collision: self.logger.debug( "Address collision while creating contract: %s", encode_hex(contract_address), ) computation.stack_push(0) return child_msg = computation.prepare_child_message( gas=create_msg_gas, to=constants.CREATE_CONTRACT_ADDRESS, value=value, data=b'', code=call_data, create_address=contract_address, ) child_computation = computation.apply_child_computation(child_msg) if child_computation.is_error: computation.stack_push(0) else: computation.stack_push(contract_address) computation.return_gas(child_computation.get_gas_remaining())