def test_CREATE2_deploy_contract_edge_cases( unvalidated_shard_chain): # noqa: F811 CREATE2_contracts = json.load( open(os.path.join(DIR, '../contract_fixtures/CREATE2_contracts.json'))) simple_transfer_contract = CREATE2_contracts["simple_transfer_contract"] # First case: computed contract address not the same as provided in `transaction.to` chain = unvalidated_shard_chain code = "0xf3" computed_address = generate_CREATE2_contract_address(b"", decode_hex(code)) first_failed_deploy_tx = new_sharding_transaction( tx_initiator=decode_hex(simple_transfer_contract['address']), data_destination=b'', data_value=0, data_msgdata=b'', data_vrs=b'', code=code, access_list=[[decode_hex(simple_transfer_contract['address'])], [computed_address]]) vm = chain.get_vm() computation, _ = vm.apply_transaction(first_failed_deploy_tx) assert isinstance(computation._error, IncorrectContractCreationAddress) gas_used = vm.block.header.gas_used assert gas_used > first_failed_deploy_tx.intrinsic_gas last_gas_used = gas_used # Next, complete deploying the contract successful_deploy_tx = new_sharding_transaction( tx_initiator=decode_hex(simple_transfer_contract['address']), data_destination=b'', data_value=0, data_msgdata=b'', data_vrs=b'', code=simple_transfer_contract['bytecode'], ) computation, _ = vm.apply_transaction(successful_deploy_tx) assert not computation.is_error gas_used = vm.block.header.gas_used - last_gas_used assert gas_used > successful_deploy_tx.intrinsic_gas last_gas_used = gas_used # Second case: deploy to existing account second_failed_deploy_tx = successful_deploy_tx computation, _ = vm.apply_transaction(second_failed_deploy_tx) assert isinstance(computation._error, ContractCreationCollision) gas_used = vm.block.header.gas_used - last_gas_used assert gas_used > second_failed_deploy_tx.intrinsic_gas
def __call__(self, computation): if computation.msg.is_static: raise WriteProtection( "Cannot modify state while inside of a STATICCALL context") computation.consume_gas(self.gas_cost, reason=self.mnemonic) value = computation.stack_pop(type_hint=constants.UINT256, ) salt = computation.stack_pop(type_hint=constants.BYTES, ) start_position, size = computation.stack_pop( num_items=2, type_hint=constants.UINT256, ) computation.extend_memory(start_position, size) with computation.state_db(read_only=True) as state_db: insufficient_funds = state_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="CREATE2") contract_address = generate_CREATE2_contract_address( salt, call_data, ) with computation.state_db(read_only=True) as state_db: is_collision = state_db.account_has_code(contract_address) if is_collision: computation.vm.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=contract_address, value=value, data=b'', code=call_data, is_create=True, ) 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())
def run_computation(self, transaction, message): """Apply the message to the VM.""" state_db_cm = functools.partial(self.state_db, access_list=transaction.prefix_list) if transaction.code: contract_address = generate_CREATE2_contract_address( transaction.salt, transaction.code, ) else: contract_address = None transaction_context = self.get_transaction_context_class()( origin=transaction.to, sig_hash=transaction.sig_hash, transaction_gas_limit=transaction.gas, ) if message.is_create: with state_db_cm(read_only=True) as state_db: is_collision = state_db.account_has_code(contract_address) # Check if contract address provided by transaction is correct if contract_address != transaction.to: computation = self.get_computation(message, transaction_context) computation._error = IncorrectContractCreationAddress( "Contract address calculated: {0} but {1} is provided". format( encode_hex(contract_address), encode_hex(transaction.to), )) self.logger.debug( "Contract address calculated: %s but %s is provided", encode_hex(contract_address), encode_hex(transaction.to), ) elif is_collision: # The address of the newly created contract has collided # with an existing contract address. computation = self.get_computation(message, transaction_context) computation._error = ContractCreationCollision( "Address collision while creating contract: {0}".format( encode_hex(contract_address), )) self.logger.debug( "Address collision while creating contract: %s", encode_hex(contract_address), ) else: computation = self.get_computation( message, transaction_context, ).apply_create_message() else: computation = self.get_computation( message, transaction_context).apply_message() return computation
def execute_transaction(self, transaction): # state_db ontext manager that restricts access as specified in the transacion state_db_cm = functools.partial(self.state_db, access_list=transaction.prefix_list) # # 1) Pre Computation # # Validate the transaction transaction.validate() self.validate_transaction(transaction) with state_db_cm() as state_db: # Setup VM Message message_gas = transaction.gas - transaction.intrinsic_gas if transaction.code: contract_address = generate_CREATE2_contract_address( transaction.salt, transaction.code, ) data = b'' code = transaction.code is_create = True else: contract_address = None data = transaction.data code = state_db.get_code(transaction.to) is_create = False self.logger.info( ("TRANSACTION: to: %s | gas: %s | " "data-hash: %s | code-hash: %s | salt: %s"), encode_hex(transaction.to), transaction.gas, encode_hex(keccak(transaction.data)), encode_hex(keccak(transaction.code)), encode_hex(transaction.salt), ) message = ShardingMessage( gas=message_gas, to=transaction.to, sender=ENTRY_POINT, value=0, data=data, code=code, is_create=is_create, access_list=transaction.prefix_list, ) transaction_context = self.get_transaction_context_class()( origin=transaction.to, sig_hash=transaction.sig_hash, transaction_gas_limit=transaction.gas, ) # # 2) Apply the message to the VM. # if message.is_create: with state_db_cm(read_only=True) as state_db: is_collision = state_db.account_has_code(contract_address) # Check if contract address provided by transaction is correct if contract_address != transaction.to: computation = self.get_computation(message, transaction_context) computation._error = IncorrectContractCreationAddress( "Contract address calculated: {0} but {1} is provided". format( encode_hex(contract_address), encode_hex(transaction.to), )) self.logger.debug( "Contract address calculated: %s but %s is provided", encode_hex(contract_address), encode_hex(transaction.to), ) elif is_collision: # The address of the newly created contract has collided # with an existing contract address. computation = self.get_computation(message, transaction_context) computation._error = ContractCreationCollision( "Address collision while creating contract: {0}".format( encode_hex(contract_address), )) self.logger.debug( "Address collision while creating contract: %s", encode_hex(contract_address), ) else: computation = self.get_computation( message, transaction_context, ).apply_create_message() else: computation = self.get_computation( message, transaction_context).apply_message() # # 2) Post Computation # # Self Destruct Refunds num_deletions = len(computation.get_accounts_for_deletion()) if num_deletions: computation.refund_gas(REFUND_SELFDESTRUCT * num_deletions) # Gas Refunds transaction_fee, gas_refund_amount = computation.compute_transaction_fee_and_refund( ) if gas_refund_amount: self.logger.debug( 'TRANSACTION REFUND: %s -> %s', gas_refund_amount, encode_hex(message.to), ) with state_db_cm() as state_db: state_db.delta_balance(message.to, gas_refund_amount) # Miner Fees self.logger.debug( 'TRANSACTION FEE: %s', transaction_fee, ) # Process Self Destructs with state_db_cm() as state_db: 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.logger.debug('DELETING ACCOUNT: %s', encode_hex(account)) # TODO: this balance setting is likely superflous and can be # removed since `delete_account` does this. state_db.set_balance(account, 0) state_db.delete_account(account) return computation
from evm.auxiliary.user_account_contract.transaction import ( UserAccountTransaction, UnsignedUserAccountTransaction, ) from evm.auxiliary.user_account_contract.contract import ( generate_account_bytecode, NONCE_GETTER_ID, ECRECOVER_ADDRESS as ECRECOVER_ADDRESS_INT, ) PRIVATE_KEY = keys.PrivateKey(b"\x33" * 32) ACCOUNT_CODE = generate_account_bytecode( PRIVATE_KEY.public_key.to_canonical_address()) ACCOUNT_ADDRESS = generate_CREATE2_contract_address(b"", ACCOUNT_CODE) INITIAL_BALANCE = 10000000000 # contract that does nothing NOOP_CONTRACT_CODE = b"" NOOP_CONTRACT_ADDRESS = generate_CREATE2_contract_address( b"", NOOP_CONTRACT_CODE) # contract that reverts without returning data FAILING_CONTRACT_CODE = b"\x61\x00\x00\xfd" # PUSH2 0 0 REVERT FAILING_CONTRACT_ADDRESS = generate_CREATE2_contract_address( b"", FAILING_CONTRACT_CODE) # contract that logs available gas # GAS PUSH1 0 MSTORE PUSH1 32 PUSH1 0 LOG0 GAS_LOGGING_CONTRACT_CODE = b"\x5a\x60\x00\x52\x60\x20\x60\x00\xa0"
DIR = os.path.dirname(__file__) simple_transfer_contract_bytecode = assembly_to_evm( compile_to_assembly(LLLnode.from_list(simple_transfer_contract_lll_code))) CREATE2_contract_bytecode = assembly_to_evm( compile_to_assembly(LLLnode.from_list(CREATE2_contract_lll_code))) CREATE2_json = { "simple_transfer_contract": { "bytecode": encode_hex(simple_transfer_contract_bytecode), "address": encode_hex( generate_CREATE2_contract_address( b'', simple_transfer_contract_bytecode)), }, "CREATE2_contract": { "bytecode": encode_hex(CREATE2_contract_bytecode), "address": encode_hex( generate_CREATE2_contract_address(b'', CREATE2_contract_bytecode)), }, "simple_factory_contract": { "bytecode": encode_hex(simple_factory_contract_bytecode), } } with open(os.path.join(DIR, 'CREATE2_contracts.json'), 'w') as f: json.dump(CREATE2_json, f, indent=4, sort_keys=True)
def test_sharding_apply_transaction(unvalidated_shard_chain): # noqa: F811 chain = unvalidated_shard_chain CREATE2_contracts = json.load( open(os.path.join(DIR, '../contract_fixtures/CREATE2_contracts.json'))) simple_transfer_contract = CREATE2_contracts["simple_transfer_contract"] CREATE2_contract = CREATE2_contracts["CREATE2_contract"] simple_factory_contract_bytecode = CREATE2_contracts[ "simple_factory_contract"]["bytecode"] # First test: simple ether transfer contract first_deploy_tx = new_sharding_transaction( tx_initiator=decode_hex(simple_transfer_contract['address']), data_destination=b'', data_value=0, data_msgdata=b'', data_vrs=b'', code=simple_transfer_contract['bytecode'], ) vm = chain.get_vm() computation, _ = vm.apply_transaction(first_deploy_tx) assert not computation.is_error gas_used = vm.block.header.gas_used assert gas_used > first_deploy_tx.intrinsic_gas last_gas_used = gas_used # Transfer ether to recipient recipient = decode_hex('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0c') amount = 100 tx_initiator = decode_hex(simple_transfer_contract['address']) transfer_tx = new_sharding_transaction(tx_initiator, recipient, amount, b'', b'') computation, _ = vm.apply_transaction(transfer_tx) assert not computation.is_error gas_used = vm.block.header.gas_used - last_gas_used assert gas_used > transfer_tx.intrinsic_gas last_gas_used = vm.block.header.gas_used with vm.state.state_db(read_only=True) as state_db: assert state_db.get_balance(recipient) == amount # Second test: contract that deploy new contract with CREATE2 second_deploy_tx = new_sharding_transaction( tx_initiator=decode_hex(CREATE2_contract['address']), data_destination=b'', data_value=0, data_msgdata=b'', data_vrs=b'', code=CREATE2_contract['bytecode'], ) computation, _ = vm.apply_transaction(second_deploy_tx) assert not computation.is_error gas_used = vm.block.header.gas_used - last_gas_used assert gas_used > second_deploy_tx.intrinsic_gas last_gas_used = vm.block.header.gas_used # Invoke the contract to deploy new contract tx_initiator = decode_hex(CREATE2_contract['address']) newly_deployed_contract_address = generate_CREATE2_contract_address( int_to_big_endian(0), decode_hex(simple_factory_contract_bytecode)) invoke_tx = new_sharding_transaction( tx_initiator, b'', 0, b'', b'', access_list=[[tx_initiator, pad32(b'')], [newly_deployed_contract_address]]) computation, _ = vm.apply_transaction(invoke_tx) assert not computation.is_error gas_used = vm.block.header.gas_used - last_gas_used assert gas_used > invoke_tx.intrinsic_gas with vm.state.state_db(read_only=True) as state_db: newly_deployed_contract_address = generate_CREATE2_contract_address( int_to_big_endian(0), decode_hex(simple_factory_contract_bytecode)) assert state_db.get_code( newly_deployed_contract_address) == b'\xbe\xef' assert state_db.get_storage(decode_hex(CREATE2_contract['address']), 0) == 1