From d95ba733ab79d5a864604c6c5fe130ae1249f477 Mon Sep 17 00:00:00 2001 From: Guilherme Salgado Date: Mon, 24 Jul 2017 14:53:23 +0100 Subject: [PATCH] Implement difficulty calculation for homestead --- evm/constants.py | 1 + evm/vm/flavors/homestead/__init__.py | 6 +++ evm/vm/flavors/homestead/blocks.py | 11 +++++ evm/vm/flavors/homestead/headers.py | 58 ++++++++++++++++++++++++++ tests/json-fixtures/test_blockchain.py | 30 ++++++++----- 5 files changed, 95 insertions(+), 11 deletions(-) create mode 100644 evm/vm/flavors/homestead/headers.py diff --git a/evm/constants.py b/evm/constants.py index 34e27273d2..4a256c4066 100644 --- a/evm/constants.py +++ b/evm/constants.py @@ -109,6 +109,7 @@ DIFFICULTY_MINIMUM = 131072 FRONTIER_DIFFICULTY_ADJUSTMENT_CUTOFF = 13 +HOMESTEAD_DIFF_ADJUSTMENT_CUTOFF = 10 BOMB_EXPONENTIAL_PERIOD = 100000 BOMB_EXPONENTIAL_FREE_PERIODS = 2 diff --git a/evm/vm/flavors/homestead/__init__.py b/evm/vm/flavors/homestead/__init__.py index cfe359abc5..cd6b1267af 100644 --- a/evm/vm/flavors/homestead/__init__.py +++ b/evm/vm/flavors/homestead/__init__.py @@ -12,6 +12,10 @@ from .opcodes import HOMESTEAD_OPCODES from .blocks import HomesteadBlock from .validation import validate_homestead_transaction +from .headers import ( + create_homestead_header_from_parent, + configure_homestead_header, +) def _apply_homestead_create_message(vm, message): @@ -62,4 +66,6 @@ def _apply_homestead_create_message(vm, message): # method overrides validate_transaction=validate_homestead_transaction, apply_create_message=_apply_homestead_create_message, + create_header_from_parent=staticmethod(create_homestead_header_from_parent), + configure_header=configure_homestead_header, ) diff --git a/evm/vm/flavors/homestead/blocks.py b/evm/vm/flavors/homestead/blocks.py index aed731b462..f836d917db 100644 --- a/evm/vm/flavors/homestead/blocks.py +++ b/evm/vm/flavors/homestead/blocks.py @@ -1,3 +1,9 @@ +from rlp.sedes import ( + CountableList, +) +from evm.rlp.headers import ( + BlockHeader, +) from evm.vm.flavors.frontier.blocks import ( FrontierBlock, ) @@ -8,3 +14,8 @@ class HomesteadBlock(FrontierBlock): transaction_class = HomesteadTransaction + fields = [ + ('header', BlockHeader), + ('transactions', CountableList(HomesteadTransaction)), + ('uncles', CountableList(BlockHeader)) + ] diff --git a/evm/vm/flavors/homestead/headers.py b/evm/vm/flavors/homestead/headers.py new file mode 100644 index 0000000000..65584600ea --- /dev/null +++ b/evm/vm/flavors/homestead/headers.py @@ -0,0 +1,58 @@ +from evm.validation import ( + validate_gt, +) +from evm.constants import ( + DIFFICULTY_ADJUSTMENT_DENOMINATOR, + DIFFICULTY_MINIMUM, + BOMB_EXPONENTIAL_PERIOD, + BOMB_EXPONENTIAL_FREE_PERIODS, + HOMESTEAD_DIFF_ADJUSTMENT_CUTOFF, +) + +from evm.vm.flavors.frontier.headers import ( + configure_frontier_header, + create_frontier_header_from_parent, +) + + +def compute_homestead_difficulty(parent_header, timestamp): + """ + Computes the difficulty for a homestead block based on the parent block. + """ + parent_tstamp = parent_header.timestamp + validate_gt(timestamp, parent_tstamp) + offset = parent_header.difficulty // DIFFICULTY_ADJUSTMENT_DENOMINATOR + sign = max( + 1 - (timestamp - parent_tstamp) // HOMESTEAD_DIFF_ADJUSTMENT_CUTOFF, + -99) + difficulty = int(max( + parent_header.difficulty + offset * sign, + min(parent_header.difficulty, DIFFICULTY_MINIMUM))) + num_bomb_periods = ( + (parent_header.block_number + 1) // BOMB_EXPONENTIAL_PERIOD + ) - BOMB_EXPONENTIAL_FREE_PERIODS + if num_bomb_periods >= 0: + return max(difficulty + 2**num_bomb_periods, DIFFICULTY_MINIMUM) + else: + return difficulty + + +def create_homestead_header_from_parent(parent_header, **header_params): + if 'difficulty' not in header_params: + timestamp = header_params.get('timestamp', parent_header.timestamp + 1) + header_params['difficulty'] = compute_homestead_difficulty( + parent_header, + timestamp, + ) + return create_frontier_header_from_parent(parent_header, **header_params) + + +def configure_homestead_header(vm, **header_params): + header = configure_frontier_header(vm, **header_params) + if 'timestamp' in header_params and header.block_number > 0: + parent_header = vm.block.get_parent_header() + header.difficulty = compute_homestead_difficulty( + parent_header, + header_params['timestamp'], + ) + return header diff --git a/tests/json-fixtures/test_blockchain.py b/tests/json-fixtures/test_blockchain.py index d8d568480c..20d280a43c 100644 --- a/tests/json-fixtures/test_blockchain.py +++ b/tests/json-fixtures/test_blockchain.py @@ -12,10 +12,12 @@ keccak, ) +from evm import EVM from evm.exceptions import ( ValidationError, ) from evm.vm.flavors import ( + HomesteadVM, MainnetEVM, ) from evm.rlp.headers import ( @@ -42,6 +44,10 @@ DISABLED_INDIVIDUAL_TESTS = [ "bcInvalidHeaderTest.json:ExtraData1024", "bcInvalidHeaderTest.json:DifferentExtraData1025", + # This test alone takes more than 10 minutes to run, and that causes the + # travis build to be terminated so it's disabled until we figure out how + # to make it run faster. + "Homestead/bcSuicideIssue.json:SuicideIssue", ] def blockchain_fixture_skip_fn(fixture_path, fixture_name, fixture): @@ -49,8 +55,6 @@ def blockchain_fixture_skip_fn(fixture_path, fixture_name, fixture): return ( ":".join([fixture_path, fixture_name]) in DISABLED_INDIVIDUAL_TESTS or fixture_path.startswith('TestNetwork') or # TODO: enable - 'Homestead' in fixture_path or # TODO: enable - 'Homestead' in fixture_name or # TODO: enable 'EIP150' in fixture_path or # TODO: enable 'EIP150' in fixture_name or # TODO: enable 'EIP158' in fixture_path or # TODO: enable @@ -59,16 +63,11 @@ def blockchain_fixture_skip_fn(fixture_path, fixture_name, fixture): SLOW_FIXTURE_NAMES = { - 'GeneralStateTests/stAttackTest/ContractCreationSpam.json:ContractCreationSpam_d0g0v0_Frontier', - 'GeneralStateTests/stBoundsTest/MLOAD_Bounds.json:MLOAD_Bounds_d0g0v0_Frontier', - 'GeneralStateTests/stMemoryStressTest/CALLCODE_Bounds3.json:CALLCODE_Bounds3_d0g0v0_Frontier', - 'GeneralStateTests/stMemoryStressTest/CALL_Bounds2.json:CALL_Bounds2_d0g0v0_Frontier', - 'GeneralStateTests/stMemoryStressTest/CALL_Bounds2a.json:CALL_Bounds2a_d0g0v0_Frontier', - 'GeneralStateTests/stCallCreateCallCodeTest/Call1024OOG.json:Call1024OOG_d0g0v0_Frontier', - 'GeneralStateTests/stCallCreateCallCodeTest/Callcode1024OOG.json:Callcode1024OOG_d0g0v0_Frontier', - 'GeneralStateTests/stCallCreateCallCodeTest/CallRecursiveBombPreCall.json:CallRecursiveBombPreCall_d0g0v0_Frontier', 'bcForkStressTest.json:ForkStressTest', 'bcWalletTest.json:walletReorganizeOwners', + 'Homestead/bcExploitTest.json:DelegateCallSpam', + "Homestead/bcWalletTest.json:walletReorganizeOwners", + "Homestead/bcShanghaiLove.json:Devcon2Attack", } @@ -116,7 +115,16 @@ def test_blockchain_fixtures(fixture_name, fixture): # assert rlp.encode(genesis_header) == fixture['genesisRLP'] db = MemoryDB() - evm = MainnetEVM.from_genesis( + + evm = MainnetEVM + # TODO: It would be great if we can figure out an API for re-configuring + # start block numbers that was more elegant. + if fixture_name.startswith('Homestead'): + evm = EVM.configure( + 'HomesteadEVM', + vm_configuration=[(0, HomesteadVM)]) + + evm = evm.from_genesis( db, genesis_params=genesis_params, genesis_state=fixture['pre'],