From 2646a10c7422c7c7d925862a294e586f00a924f7 Mon Sep 17 00:00:00 2001 From: NIC619 Date: Fri, 5 Jan 2018 17:07:30 +0800 Subject: [PATCH] update sharding computation to new computation logic introduced in #244 --- evm/vm/forks/sharding/__init__.py | 121 +++------------------------ evm/vm/forks/sharding/computation.py | 115 +++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 108 deletions(-) create mode 100644 evm/vm/forks/sharding/computation.py diff --git a/evm/vm/forks/sharding/__init__.py b/evm/vm/forks/sharding/__init__.py index 07891c164c..8bdf5eb3d0 100644 --- a/evm/vm/forks/sharding/__init__.py +++ b/evm/vm/forks/sharding/__init__.py @@ -1,20 +1,16 @@ from evm.constants import ( ENTRY_POINT, - STACK_DEPTH_LIMIT, ) from evm.exceptions import ( - InsufficientFunds, - StackDepthLimit, ContractCreationCollision, - OutOfGas, ) from evm.vm.message import ( ShardingMessage, ) -from evm.vm.computation import ( - Computation, +from evm.vm.vm_state import ( + VMState, ) from evm.utils.address import ( @@ -28,13 +24,10 @@ ) from ..byzantium import ByzantiumVM -from ..spurious_dragon.constants import ( - EIP170_CODE_SIZE_LIMIT, - GAS_CODEDEPOSIT, -) from ..frontier.constants import ( REFUND_SELFDESTRUCT, ) +from .computation import ShardingComputation from .validation import validate_sharding_transaction from .blocks import ShardingBlock @@ -50,7 +43,7 @@ def _execute_sharding_transaction(vm, transaction): vm.validate_transaction(transaction) gas_fee = transaction.gas * transaction.gas_price - with vm.state_db() as state_db: + with vm.state.state_db() as state_db: # Buy Gas state_db.delta_balance(transaction.to, -1 * gas_fee) @@ -98,13 +91,13 @@ def _execute_sharding_transaction(vm, transaction): # 2) Apply the message to the VM. # if message.is_create: - with vm.state_db(read_only=True) as state_db: + with vm.state.state_db(read_only=True) as state_db: is_collision = state_db.account_has_code_or_nonce(contract_address) if is_collision: # The address of the newly created contract has collided # with an existing contract address. - computation = Computation(vm, message) + computation = vm.get_computation(message) computation._error = ContractCreationCollision( "Address collision while creating contract: {0}".format( encode_hex(contract_address), @@ -115,9 +108,9 @@ def _execute_sharding_transaction(vm, transaction): encode_hex(contract_address), ) else: - computation = vm.apply_create_message(message) + computation = vm.get_computation(message).apply_create_message() else: - computation = vm.apply_message(message) + computation = vm.get_computation(message).apply_message() # # 2) Post Computation @@ -141,7 +134,7 @@ def _execute_sharding_transaction(vm, transaction): encode_hex(message.to), ) - with vm.state_db() as state_db: + with vm.state.state_db() as state_db: state_db.delta_balance(message.to, gas_refund_amount) # Miner Fees @@ -151,11 +144,11 @@ def _execute_sharding_transaction(vm, transaction): transaction_fee, encode_hex(vm.block.header.coinbase), ) - with vm.state_db() as state_db: + with vm.state.state_db() as state_db: state_db.delta_balance(vm.block.header.coinbase, transaction_fee) # Process Self Destructs - with vm.state_db() as state_db: + with vm.state.state_db() 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. @@ -169,100 +162,12 @@ def _execute_sharding_transaction(vm, transaction): return computation -def _apply_sharding_message(vm, message): - snapshot = vm.snapshot() - - if message.depth > STACK_DEPTH_LIMIT: - raise StackDepthLimit("Stack depth limit reached") - - if message.should_transfer_value and message.value: - with vm.state_db() as state_db: - sender_balance = state_db.get_balance(message.sender) - - if sender_balance < message.value: - raise InsufficientFunds( - "Insufficient funds: {0} < {1}".format(sender_balance, message.value) - ) - - state_db.delta_balance(message.sender, -1 * message.value) - state_db.delta_balance(message.storage_address, message.value) - - vm.logger.debug( - "TRANSFERRED: %s from %s -> %s", - message.value, - encode_hex(message.sender), - encode_hex(message.storage_address), - ) - - with vm.state_db() as state_db: - state_db.touch_account(message.storage_address) - - computation = vm.apply_computation(message) - - if computation.is_error: - vm.revert(snapshot) - else: - vm.commit(snapshot) - - return computation - - -def _apply_sharding_create_message(vm, message): - # Remove EIP160 nonce increment but keep EIP170 contract code size limit - snapshot = vm.snapshot() - - computation = vm.apply_message(message) - - if computation.is_error: - vm.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), - ) - ) - vm.revert(snapshot) - elif contract_code: - contract_code_gas_cost = len(contract_code) * GAS_CODEDEPOSIT - try: - computation.gas_meter.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 - vm.revert(snapshot) - else: - if vm.logger: - vm.logger.debug( - "SETTING CODE: %s -> length: %s | hash: %s", - encode_hex(message.storage_address), - len(contract_code), - encode_hex(keccak(contract_code)) - ) - - with vm.state_db() as state_db: - state_db.set_code(message.storage_address, contract_code) - vm.commit(snapshot) - else: - vm.commit(snapshot) - return computation - - ShardingVM = ByzantiumVM.configure( name='ShardingVM', + _computation_class=ShardingComputation, _block_class=ShardingBlock, + _state_class=VMState, # Method overrides validate_transaction=validate_sharding_transaction, - apply_message=_apply_sharding_message, - apply_create_message=_apply_sharding_create_message, execute_transaction=_execute_sharding_transaction, ) diff --git a/evm/vm/forks/sharding/computation.py b/evm/vm/forks/sharding/computation.py new file mode 100644 index 0000000000..a070b39570 --- /dev/null +++ b/evm/vm/forks/sharding/computation.py @@ -0,0 +1,115 @@ +from evm.constants import ( + STACK_DEPTH_LIMIT, +) +from evm.exceptions import ( + OutOfGas, + InsufficientFunds, + StackDepthLimit, +) +from evm.utils.hexadecimal import ( + encode_hex, +) +from evm.utils.keccak import ( + keccak, +) + +from ..spurious_dragon.computation import ( + SpuriousDragonComputation, +) +from ..spurious_dragon.constants import ( + EIP170_CODE_SIZE_LIMIT, + GAS_CODEDEPOSIT, +) + + +class ShardingComputation(SpuriousDragonComputation): + def apply_message(self): + snapshot = self.vm_state.snapshot() + + if self.msg.depth > STACK_DEPTH_LIMIT: + raise StackDepthLimit("Stack depth limit reached") + + if self.msg.should_transfer_value and self.msg.value: + with self.vm_state.state_db() as state_db: + sender_balance = state_db.get_balance(self.msg.sender) + + if sender_balance < self.msg.value: + raise InsufficientFunds( + "Insufficient funds: {0} < {1}".format(sender_balance, self.msg.value) + ) + + state_db.delta_balance(self.msg.sender, -1 * self.msg.value) + state_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), + ) + + with self.vm_state.state_db() as state_db: + state_db.touch_account(self.msg.storage_address) + + computation = self.apply_computation( + self.vm_state, + self.msg, + self.opcodes, + self.precompiles, + ) + + if computation.is_error: + self.vm_state.revert(snapshot) + else: + self.vm_state.commit(snapshot) + + return computation + + def apply_create_message(self): + # Remove EIP160 nonce increment but keep EIP170 contract code size limit + snapshot = self.vm_state.snapshot() + + computation = self.apply_message() + + if computation.is_error: + self.vm_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.vm_state.revert(snapshot) + elif contract_code: + contract_code_gas_cost = len(contract_code) * GAS_CODEDEPOSIT + try: + computation.gas_meter.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.vm_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)) + ) + + with self.vm_state.state_db() as state_db: + state_db.set_code(self.msg.storage_address, contract_code) + self.vm_state.commit(snapshot) + else: + self.vm_state.commit(snapshot) + return computation