async def test_header_sync_with_multi_peers(request, event_loop, chaindb_mainnet_100): # In this test we start with one of our peers announcing block #100, and we sync all # headers up to that... light_chain, _, server = await get_lightchain_with_peers( request, event_loop, chaindb_mainnet_100) head = server.chaindb.get_canonical_head() await wait_for_head(light_chain.chaindb, head) assert_canonical_chains_are_equal(light_chain.chaindb, server.chaindb, head.block_number) # Now a second peer comes along and announces block #100 as well, but it's different # from the one we already have, so we need to fetch that too. And since it has a higher total # difficulty than the current head, it will become our new chain head. server2_chaindb = server.chaindb head_parent = server2_chaindb.get_block_header_by_hash(head.parent_hash) difficulty = head.difficulty + 1 new_head = BlockHeader.from_parent( head_parent, head_parent.gas_limit, difficulty=difficulty, timestamp=head.timestamp, coinbase=head.coinbase) server2_chaindb.persist_header_to_db(new_head) assert server2_chaindb.get_canonical_head() == new_head client2, server2 = await get_client_and_server_peer_pair( request, event_loop, client_chaindb=light_chain.chaindb, server_chaindb=server2_chaindb) light_chain.register_peer(client2) await wait_for_head(light_chain.chaindb, new_head) assert_canonical_chains_are_equal(light_chain.chaindb, server2.chaindb, new_head.block_number)
def create_frontier_header_from_parent(parent_header, **header_params): if 'difficulty' not in header_params: # Use setdefault to ensure the new header has the same timestamp we use to calculate its # difficulty. header_params.setdefault('timestamp', parent_header.timestamp + 1) header_params['difficulty'] = compute_frontier_difficulty( parent_header, header_params['timestamp'], ) if 'gas_limit' not in header_params: header_params['gas_limit'] = compute_gas_limit( parent_header, gas_limit_floor=GENESIS_GAS_LIMIT, ) header = BlockHeader.from_parent(parent=parent_header, **header_params) return header
def from_genesis(cls, chaindb, genesis_params, genesis_state=None): """ Initialize the Chain from a genesis state. """ state_db = chaindb.get_state_db(BLANK_ROOT_HASH, read_only=False) if genesis_state is None: genesis_state = {} for account, account_data in genesis_state.items(): state_db.set_balance(account, account_data['balance']) state_db.set_nonce(account, account_data['nonce']) state_db.set_code(account, account_data['code']) for slot, value in account_data['storage'].items(): state_db.set_storage(account, slot, value) if 'state_root' not in genesis_params: # If the genesis state_root was not specified, use the value # computed from the initialized state database. genesis_params = assoc(genesis_params, 'state_root', state_db.root_hash) elif genesis_params['state_root'] != state_db.root_hash: # If the genesis state_root was specified, validate that it matches # the computed state from the initialized state database. raise ValidationError( "The provided genesis state root does not match the computed " "genesis state root. Got {0}. Expected {1}".format( state_db.root_hash, genesis_params['state_root'], ) ) genesis_header = BlockHeader(**genesis_params) genesis_chain = cls(chaindb, genesis_header) chaindb.persist_block_to_db(genesis_chain.get_block()) return cls.from_genesis_header(chaindb, genesis_header)
async def test_full_header_sync_and_reorg(request, event_loop, chaindb_mainnet_100): # Here we create our server with a populated chaindb, so upon startup it will announce its # chain head and the client will fetch all headers light_chain, _, server = await get_lightchain_with_peers( request, event_loop, chaindb_mainnet_100) # ... and our client should then fetch all headers. head = server.chaindb.get_canonical_head() await wait_for_head(light_chain.chaindb, head) assert_canonical_chains_are_equal(light_chain.chaindb, server.chaindb, head.block_number) head_parent = server.chaindb.get_block_header_by_hash(head.parent_hash) difficulty = head.difficulty + 1 new_head = BlockHeader.from_parent( head_parent, head_parent.gas_limit, difficulty=difficulty, timestamp=head.timestamp, coinbase=head.coinbase) server.chaindb.persist_header(new_head) assert server.chaindb.get_canonical_head() == new_head server.send_announce(head_number=head.block_number, reorg_depth=1) await wait_for_head(light_chain.chaindb, new_head) assert_canonical_chains_are_equal(light_chain.chaindb, server.chaindb, new_head.block_number)
def from_genesis(cls, chaindb: BaseChainDB, genesis_params: Dict[str, HeaderParams], genesis_state: AccountState=None) -> 'BaseChain': """ Initializes the Chain from a genesis state. """ genesis_vm_class = cls.get_vm_class_for_block_number(BlockNumber(0)) account_db = genesis_vm_class.get_state_class().get_account_db_class()( chaindb.db, BLANK_ROOT_HASH, ) if genesis_state is None: genesis_state = {} # mutation apply_state_dict(account_db, genesis_state) account_db.persist() if 'state_root' not in genesis_params: # If the genesis state_root was not specified, use the value # computed from the initialized state database. genesis_params = assoc(genesis_params, 'state_root', account_db.state_root) elif genesis_params['state_root'] != account_db.state_root: # If the genesis state_root was specified, validate that it matches # the computed state from the initialized state database. raise ValidationError( "The provided genesis state root does not match the computed " "genesis state root. Got {0}. Expected {1}".format( account_db.state_root, genesis_params['state_root'], ) ) genesis_header = BlockHeader(**genesis_params) genesis_chain = cls(chaindb, genesis_header) chaindb.persist_block(genesis_chain.get_block()) return cls.from_genesis_header(chaindb, genesis_header)
async def test_full_header_sync_and_reorg(request, event_loop, chaindb_mainnet_100): # Here we create our server with a populated chaindb, so upon startup it will announce its # chain head and the client will fetch all headers light_chain, _, server = await get_lightchain_with_peers( request, event_loop, chaindb_mainnet_100) # ... and our client should then fetch all headers. head = server.chaindb.get_canonical_head() await wait_for_head(light_chain.chaindb, head) assert_canonical_chains_are_equal(light_chain.chaindb, server.chaindb, head.block_number) head_parent = server.chaindb.get_block_header_by_hash(head.parent_hash) difficulty = head.difficulty + 1 new_head = BlockHeader.from_parent( head_parent, head_parent.gas_limit, difficulty=difficulty, timestamp=head.timestamp, coinbase=head.coinbase) server.chaindb.persist_header_to_db(new_head) assert server.chaindb.get_canonical_head() == new_head server.send_announce(head_number=head.block_number, reorg_depth=1) await wait_for_head(light_chain.chaindb, new_head) assert_canonical_chains_are_equal(light_chain.chaindb, server.chaindb, new_head.block_number)
def test_vm_success_using_fixture(fixture_name, fixture): db = MemoryDB() meta_evm = EVMForTesting(db=db) header = BlockHeader( coinbase=fixture['env']['currentCoinbase'], difficulty=fixture['env']['currentDifficulty'], block_number=fixture['env']['currentNumber'], gas_limit=fixture['env']['currentGasLimit'], timestamp=fixture['env']['currentTimestamp'], parent_hash=fixture['env']['previousHash'], ) evm = meta_evm(header=header) block = evm.block setup_state_db(fixture['pre'], block.state_db) Transaction = evm.get_transaction_class() unsigned_transaction = Transaction.create_unsigned_transaction( nonce=fixture['transaction']['nonce'], gas_price=fixture['transaction']['gasPrice'], gas=fixture['transaction']['gasLimit'], to=fixture['transaction']['to'], value=fixture['transaction']['value'], data=fixture['transaction']['data'], ) transaction = unsigned_transaction.as_signed_transaction( private_key=fixture['transaction']['secretKey']) try: computation = evm.apply_transaction(transaction) except InvalidTransaction: transaction_error = True else: transaction_error = False if not transaction_error: expected_logs = [{ 'address': log_entry[0], 'topics': log_entry[1], 'data': log_entry[2], } for log_entry in computation.get_log_entries()] expected_logs == fixture['logs'] expected_output = fixture['out'] if isinstance(expected_output, int): assert len(computation.output) == expected_output else: assert computation.output == expected_output for account, account_data in sorted(fixture['post'].items()): for slot, expected_storage_value in sorted( account_data['storage'].items()): actual_storage_value = evm.block.state_db.get_storage( account, slot) assert actual_storage_value == expected_storage_value expected_nonce = account_data['nonce'] expected_code = account_data['code'] expected_balance = account_data['balance'] actual_nonce = evm.block.state_db.get_nonce(account) actual_code = evm.block.state_db.get_code(account) actual_balance = evm.block.state_db.get_balance(account) balance_delta = expected_balance - actual_balance assert actual_nonce == expected_nonce assert actual_code == expected_code assert balance_delta == 0, "Expected: {0} - Actual: {1} | Delta: {2}".format( expected_balance, actual_balance, balance_delta) assert evm.block.state_db.state.root_hash == fixture['postStateRoot']
def test_vm_fixtures(fixture_name, fixture): db = MemoryDB() header = BlockHeader( coinbase=fixture['env']['currentCoinbase'], difficulty=fixture['env']['currentDifficulty'], block_number=fixture['env']['currentNumber'], gas_limit=fixture['env']['currentGasLimit'], timestamp=fixture['env']['currentTimestamp'], ) meta_evm = EVMForTesting.configure(db=db)(header=header) evm = meta_evm.get_evm() setup_state_db(fixture['pre'], evm.state_db) message = Message( origin=fixture['exec']['origin'], to=fixture['exec']['address'], sender=fixture['exec']['caller'], value=fixture['exec']['value'], data=fixture['exec']['data'], code=evm.state_db.get_code(fixture['exec']['address']), gas=fixture['exec']['gas'], gas_price=fixture['exec']['gasPrice'], ) computation = evm.apply_computation(message) if 'post' in fixture: # # Success checks # assert computation.error is None expected_logs = [{ 'address': log_entry[0], 'topics': log_entry[1], 'data': log_entry[2], } for log_entry in computation.get_log_entries()] expected_logs == fixture['logs'] 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) for child_computation, created_call in zip( computation.children, fixture.get('callcreates', [])): 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 post_state = fixture['post'] else: # # Error checks # assert computation.error assert isinstance(computation.error, VMError) post_state = fixture['pre'] for account, account_data in post_state.items(): for slot, expected_storage_value in account_data['storage'].items(): actual_storage_value = evm.state_db.get_storage(account, slot) assert actual_storage_value == expected_storage_value expected_nonce = account_data['nonce'] expected_code = account_data['code'] expected_balance = account_data['balance'] actual_nonce = evm.state_db.get_nonce(account) actual_code = evm.state_db.get_code(account) actual_balance = evm.state_db.get_balance(account) assert actual_nonce == expected_nonce assert actual_code == expected_code assert actual_balance == expected_balance
(SPURIOUS_DRAGON_MAINNET_BLOCK, SpuriousDragonVM), (BYZANTIUM_MAINNET_BLOCK, ByzantiumVM), ) MAINNET_NETWORK_ID = 1 MainnetChain = Chain.configure( 'MainnetChain', vm_configuration=MAINNET_VM_CONFIGURATION, network_id=MAINNET_NETWORK_ID, ) MAINNET_GENESIS_HEADER = BlockHeader( difficulty=17179869184, extra_data=decode_hex( "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), gas_limit=5000, gas_used=0, bloom=0, mix_hash=constants.ZERO_HASH32, nonce=constants.GENESIS_NONCE, block_number=0, parent_hash=constants.ZERO_HASH32, receipt_root=constants.BLANK_ROOT_HASH, uncles_hash=constants.EMPTY_UNCLE_HASH, state_root=decode_hex( "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"), timestamp=0, transaction_root=constants.BLANK_ROOT_HASH, )
def test_state_fixtures(fixture, fixture_vm_class): if fixture_vm_class is not ShardingVMForTesting: account_state_class = MainAccountStateDB trie_class = HexaryTrie header = BlockHeader( coinbase=fixture['env']['currentCoinbase'], difficulty=fixture['env']['currentDifficulty'], block_number=fixture['env']['currentNumber'], gas_limit=fixture['env']['currentGasLimit'], timestamp=fixture['env']['currentTimestamp'], parent_hash=fixture['env']['previousHash'], ) else: account_state_class = ShardingAccountStateDB trie_class = BinaryTrie header = CollationHeader( shard_id=fixture['env']['shardID'], expected_period_number=fixture['env']['expectedPeriodNumber'], period_start_prevhash=fixture['env']['periodStartHash'], parent_hash=fixture['env']['previousHash'], coinbase=fixture['env']['currentCoinbase'], number=fixture['env']['currentNumber'], ) chaindb = ChainDB(get_db_backend(), account_state_class=account_state_class, trie_class=trie_class) vm = fixture_vm_class(header=header, chaindb=chaindb) vm_state = vm.state with vm_state.mutable_state_db() as state_db: state_db.apply_state_dict(fixture['pre']) # Update state_root manually vm.block.header.state_root = vm_state.state_root if 'secretKey' in fixture['transaction']: unsigned_transaction = vm.create_unsigned_transaction( nonce=fixture['transaction']['nonce'], gas_price=fixture['transaction']['gasPrice'], gas=fixture['transaction']['gasLimit'], to=fixture['transaction']['to'], value=fixture['transaction']['value'], data=fixture['transaction']['data'], ) private_key = keys.PrivateKey(fixture['transaction']['secretKey']) transaction = unsigned_transaction.as_signed_transaction( private_key=private_key) elif 'vrs' in fixture['transaction']: v, r, s = ( fixture['transaction']['v'], fixture['transaction']['r'], fixture['transaction']['s'], ) transaction = vm.create_transaction( nonce=fixture['transaction']['nonce'], gas_price=fixture['transaction']['gasPrice'], gas=fixture['transaction']['gasLimit'], to=fixture['transaction']['to'], value=fixture['transaction']['value'], data=fixture['transaction']['data'], v=v, r=r, s=s, ) else: # sharding transaction transaction = vm.create_transaction( chain_id=fixture['transaction']['chainID'], shard_id=fixture['transaction']['shardID'], to=fixture['transaction']['to'], data=fixture['transaction']['data'], gas=fixture['transaction']['gasLimit'], gas_price=fixture['transaction']['gasPrice'], access_list=fixture['transaction']['accessList'], code=fixture['transaction']['code'], ) try: computation, _ = vm.apply_transaction(transaction) except ValidationError as err: transaction_error = err LOGGER.warn("Got transaction error", exc_info=True) else: transaction_error = False if not transaction_error: log_entries = computation.get_log_entries() actual_logs_hash = hash_log_entries(log_entries) if 'logs' in fixture['post']: expected_logs_hash = fixture['post']['logs'] assert expected_logs_hash == actual_logs_hash elif log_entries: raise AssertionError("Got log {0} entries. hash:{1}".format( len(log_entries), actual_logs_hash, )) if 'out' in fixture: expected_output = fixture['out'] if isinstance(expected_output, int): assert len(computation.output) == expected_output else: assert computation.output == expected_output assert vm.block.header.state_root == fixture['post']['hash']
def test_get_vm_class_for_block_number_evm_not_found(): evm_class = EVM.configure(vm_configuration=()) evm = evm_class(MemoryDB(), BlockHeader(1, 0, 100)) with pytest.raises(EVMNotFound): evm.get_vm_class_for_block_number(constants.GENESIS_BLOCK_NUMBER)
def test_blockchain_fixtures(fixture_name, fixture): genesis_params = { 'parent_hash': fixture['genesisBlockHeader']['parentHash'], 'uncles_hash': fixture['genesisBlockHeader']['uncleHash'], 'coinbase': fixture['genesisBlockHeader']['coinbase'], 'state_root': fixture['genesisBlockHeader']['stateRoot'], 'transaction_root': fixture['genesisBlockHeader']['transactionsTrie'], 'receipt_root': fixture['genesisBlockHeader']['receiptTrie'], 'bloom': fixture['genesisBlockHeader']['bloom'], 'difficulty': fixture['genesisBlockHeader']['difficulty'], 'block_number': fixture['genesisBlockHeader']['number'], 'gas_limit': fixture['genesisBlockHeader']['gasLimit'], 'gas_used': fixture['genesisBlockHeader']['gasUsed'], 'timestamp': fixture['genesisBlockHeader']['timestamp'], 'extra_data': fixture['genesisBlockHeader']['extraData'], 'mix_hash': fixture['genesisBlockHeader']['mixHash'], 'nonce': fixture['genesisBlockHeader']['nonce'], } expected_genesis_header = BlockHeader(**genesis_params) # TODO: find out if this is supposed to pass? # if 'genesisRLP' in fixture: # assert rlp.encode(genesis_header) == fixture['genesisRLP'] db = get_db_backend() chain = MainnetChain # 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'): chain = Chain.configure('HomesteadChain', vm_configuration=[(0, HomesteadVM)]) elif fixture_name.startswith('EIP150'): chain = Chain.configure('EIP150VM', vm_configuration=[(0, EIP150VM)]) elif fixture_name.startswith('TestNetwork'): homestead_vm = HomesteadVM.configure(dao_fork_block_number=8) chain = Chain.configure('TestNetworkChain', vm_configuration=[ (0, FrontierVM), (5, homestead_vm), (10, EIP150VM), ]) chain = chain.from_genesis( db, genesis_params=genesis_params, genesis_state=fixture['pre'], ) genesis_block = chain.get_canonical_block_by_number(0) genesis_header = genesis_block.header assert_rlp_equal(genesis_header, expected_genesis_header) # 1 - mine the genesis block # 2 - loop over blocks: # - apply transactions # - mine block # 4 - profit!! for block_data in fixture['blocks']: should_be_good_block = 'blockHeader' in block_data if 'rlp_error' in block_data: assert not should_be_good_block continue # The block to import may be in a different block-class-range than the # chain's current one, so we use the block number specified in the # fixture to look up the correct block class. if should_be_good_block: block_number = block_data['blockHeader']['number'] block_class = chain.get_vm_class_for_block_number( block_number).get_block_class() else: block_class = chain.get_vm().get_block_class() try: block = rlp.decode(block_data['rlp'], sedes=block_class, db=db) except (TypeError, rlp.DecodingError, rlp.DeserializationError) as err: assert not should_be_good_block, "Block should be good: {0}".format( err) continue try: mined_block = chain.import_block(block) except ValidationError as err: assert not should_be_good_block, "Block should be good: {0}".format( err) continue else: assert_rlp_equal(mined_block, block) assert should_be_good_block, "Block should have caused a validation error" latest_block_hash = chain.get_canonical_block_by_number( chain.get_block().number - 1).hash assert latest_block_hash == fixture['lastblockhash'] verify_state_db(fixture['postState'], chain.get_state_db())
def header(request): block_number = request.param difficulty = 1 gas_limit = 1 return BlockHeader(difficulty, block_number, gas_limit)
def genesis_header(): return BlockHeader( difficulty=GENESIS_DIFFICULTY, block_number=GENESIS_BLOCK_NUMBER, gas_limit=GENESIS_GAS_LIMIT, )
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) vm_state = vm.state with vm_state.state_db() as state_db: setup_state_db(fixture['pre'], state_db) code = state_db.get_code(fixture['exec']['address']) # Update state_root manually vm.block.header.state_root = vm_state.state_root computation = computation_getter(fixture, code, vm) # Update state_root manually vm.block.header.state_root = computation.vm_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 post_state = fixture['post'] else: # # Error checks # assert computation.is_error assert isinstance(computation._error, VMError) post_state = fixture['pre'] with vm.state.state_db(read_only=True) as state_db: verify_state_db(post_state, state_db)
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 test_vm_not_found_if_no_matching_block_number(): chain_class = Chain.configure('TestChain', vm_configuration=((10, FrontierVM), )) chain = chain_class(get_db_backend(), BlockHeader(1, 0, 100)) with pytest.raises(VMNotFound): chain.get_vm_class_for_block_number(9)
def test_invalid_if_no_vm_configuration(): chain_class = Chain.configure('TestChain', vm_configuration=()) with pytest.raises(ValueError): chain_class(get_db_backend(), BlockHeader(1, 0, 100))
def test_vm_fixtures(fixture_name, fixture): db = 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'], ) chain = ChainForTesting(db=db, header=header) vm = chain.get_vm() with vm.state_db() as state_db: setup_state_db(fixture['pre'], state_db) code = state_db.get_code(fixture['exec']['address']) message = Message( origin=fixture['exec']['origin'], to=fixture['exec']['address'], sender=fixture['exec']['caller'], value=fixture['exec']['value'], data=fixture['exec']['data'], code=code, gas=fixture['exec']['gas'], gas_price=fixture['exec']['gasPrice'], ) computation = vm.apply_computation(message) if 'post' in fixture: # # Success checks # assert computation.error is None expected_logs = [{ 'address': log_entry[0], 'topics': log_entry[1], 'data': log_entry[2], } for log_entry in computation.get_log_entries()] expected_logs == fixture['logs'] 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 post_state = fixture['post'] else: # # Error checks # assert computation.error assert isinstance(computation.error, VMError) post_state = fixture['pre'] with vm.state_db(read_only=True) as state_db: verify_state_db(post_state, state_db)
def test_blockchain_fixtures(fixture_name, fixture): genesis_params = { 'parent_hash': fixture['genesisBlockHeader']['parentHash'], 'uncles_hash': fixture['genesisBlockHeader']['uncleHash'], 'coinbase': fixture['genesisBlockHeader']['coinbase'], 'state_root': fixture['genesisBlockHeader']['stateRoot'], 'transaction_root': fixture['genesisBlockHeader']['transactionsTrie'], 'receipt_root': fixture['genesisBlockHeader']['receiptTrie'], 'bloom': fixture['genesisBlockHeader']['bloom'], 'difficulty': fixture['genesisBlockHeader']['difficulty'], 'block_number': fixture['genesisBlockHeader']['number'], 'gas_limit': fixture['genesisBlockHeader']['gasLimit'], 'gas_used': fixture['genesisBlockHeader']['gasUsed'], 'timestamp': fixture['genesisBlockHeader']['timestamp'], 'extra_data': fixture['genesisBlockHeader']['extraData'], 'mix_hash': fixture['genesisBlockHeader']['mixHash'], 'nonce': fixture['genesisBlockHeader']['nonce'], } expected_genesis_header = BlockHeader(**genesis_params) # TODO: find out if this is supposed to pass? # if 'genesisRLP' in fixture: # assert rlp.encode(genesis_header) == fixture['genesisRLP'] db = MemoryDB() evm = MainnetEVM.from_genesis( db, genesis_params=genesis_params, genesis_state=fixture['pre'], ) genesis_block = evm.get_block_by_number(0) genesis_header = genesis_block.header assert_rlp_equal(genesis_header, expected_genesis_header) # 1 - mine the genesis block # 2 - loop over blocks: # - apply transactions # - mine block # 4 - profit!! for block_data in fixture['blocks']: should_be_good_block = 'blockHeader' in block_data if 'rlp_error' in block_data: assert not should_be_good_block continue try: block = rlp.decode( block_data['rlp'], sedes=evm.get_vm().get_block_class(), db=db, ) except (TypeError, rlp.DecodingError, rlp.DeserializationError) as err: assert not should_be_good_block, "Block should be good: {0}".format( err) continue try: mined_block = evm.import_block(block) assert_rlp_equal(mined_block, block) except ValidationError as err: assert not should_be_good_block, "Block should be good: {0}".format( err) continue else: assert should_be_good_block, "Block should have caused a validation error" assert evm.get_block_by_number(evm.get_block().number - 1).hash == fixture['lastblockhash'] verify_state_db(fixture['postState'], evm.get_state_db())
# Ethereum mainnet headers, from two headers before to ten headers after the fork: ETH_HEADERS_NEAR_FORK = [ BlockHeader( difficulty=62352470509925, block_number=1919998, gas_limit=4712388, timestamp=1469020835, coinbase= b'\xbc\xdf\xc3[\x86\xbe\xdfr\xf0\xcd\xa0F\xa3\xc1h)\xa2\xefA\xd1', parent_hash= b'\xe7\xe3\xe8+\xf3C\xbe\xf9\xa2R\xb8\x7f\x06r\x9adZop\x9b.RK\x9e\xf4\xf9;\xb9\xf2]S\x8d', # noqa: E501 uncles_hash= b'\x1d\xccM\xe8\xde\xc7]z\xab\x85\xb5g\xb6\xcc\xd4\x1a\xd3\x12E\x1b\x94\x8at\x13\xf0\xa1B\xfd@\xd4\x93G', # noqa: E501 state_root= b'\x1f!\x88?4\xde&\x93\xb4\xadGD\xc26a\xdbd\xca\xcb=\xa2\x1dr \xceW\xb97d\xb3\xbb\xfe', # noqa: E501 transaction_root= b'\xf2n\xb9\x94\x0e\xbb\xe8\x0c\xc3\xab\xbc\x9ev\xe9\xb7\xb1\x0f\xbcG\xc0\xd2\x12\xf9\x81\xa6q/\xf7\xf4\x97\xd3\xb4', # noqa: E501 receipt_root= b'D\xda\xa2\x9c4?\xa0/\xe8\x8fH\xf8?z\xc2\x1e\xfa\xc8j\xb0w8\r\xed\x81[(n\xd2jx\x1f', # noqa: E501 bloom=0, gas_used=420000, extra_data=b'\xd7\x83\x01\x04\n\x84Geth\x87go1.6.2\x85linux', mix_hash= b'\x8d\x03\xe0$?1\xa6\xcd\x11\x04E\x1f\xfc\x10#[\x04\x16N\xbe[\xd4u-\xa6\xb54t\x8d\x87}\x9f', # noqa: E501 nonce=b'a\xd8\xc5\xdf\xfd\x0e\xb2v', ), BlockHeader( difficulty=62382916183238, block_number=1919999, gas_limit=4707788,
def test_state_fixtures(fixture, fixture_vm_class): header = BlockHeader( coinbase=fixture['env']['currentCoinbase'], difficulty=fixture['env']['currentDifficulty'], block_number=fixture['env']['currentNumber'], gas_limit=fixture['env']['currentGasLimit'], timestamp=fixture['env']['currentTimestamp'], parent_hash=fixture['env']['previousHash'], ) chaindb = ChainDB(get_db_backend()) vm = fixture_vm_class(header=header, chaindb=chaindb) vm_state = vm.state with vm_state.mutable_state_db() as state_db: setup_state_db(fixture['pre'], state_db) # Update state_root manually vm.block.header.state_root = vm_state.state_root if 'secretKey' in fixture['transaction']: unsigned_transaction = vm.create_unsigned_transaction( nonce=fixture['transaction']['nonce'], gas_price=fixture['transaction']['gasPrice'], gas=fixture['transaction']['gasLimit'], to=fixture['transaction']['to'], value=fixture['transaction']['value'], data=fixture['transaction']['data'], ) private_key = keys.PrivateKey(fixture['transaction']['secretKey']) transaction = unsigned_transaction.as_signed_transaction( private_key=private_key) elif 'vrs' in fixture['transaction']: v, r, s = ( fixture['transaction']['v'], fixture['transaction']['r'], fixture['transaction']['s'], ) transaction = vm.create_transaction( nonce=fixture['transaction']['nonce'], gas_price=fixture['transaction']['gasPrice'], gas=fixture['transaction']['gasLimit'], to=fixture['transaction']['to'], value=fixture['transaction']['value'], data=fixture['transaction']['data'], v=v, r=r, s=s, ) try: computation, _ = vm.apply_transaction(transaction) except ValidationError as err: transaction_error = err LOGGER.warn("Got transaction error:") LOGGER.warn(traceback.format_exc()) else: transaction_error = False if not transaction_error: log_entries = computation.get_log_entries() actual_logs_hash = hash_log_entries(log_entries) if 'logs' in fixture['post']: expected_logs_hash = fixture['post']['logs'] assert expected_logs_hash == actual_logs_hash elif log_entries: raise AssertionError("Got log {0} entries. hash:{1}".format( len(log_entries), actual_logs_hash, )) if 'out' in fixture: expected_output = fixture['out'] if isinstance(expected_output, int): assert len(computation.output) == expected_output else: assert computation.output == expected_output assert vm.block.header.state_root == fixture['post']['hash']
def test_state_fixtures(fixture, fixture_vm_class): header = BlockHeader( coinbase=fixture['env']['currentCoinbase'], difficulty=fixture['env']['currentDifficulty'], block_number=fixture['env']['currentNumber'], gas_limit=fixture['env']['currentGasLimit'], timestamp=fixture['env']['currentTimestamp'], parent_hash=fixture['env']['previousHash'], ) chaindb = ChainDB(get_db_backend()) vm = fixture_vm_class(header=header, chaindb=chaindb) state = vm.state apply_state_dict(state.account_db, fixture['pre']) state.account_db.persist() # Update state_root manually vm.block = vm.block.copy(header=vm.block.header.copy(state_root=state.state_root)) if 'secretKey' in fixture['transaction']: unsigned_transaction = vm.create_unsigned_transaction( nonce=fixture['transaction']['nonce'], gas_price=fixture['transaction']['gasPrice'], gas=fixture['transaction']['gasLimit'], to=fixture['transaction']['to'], value=fixture['transaction']['value'], data=fixture['transaction']['data'], ) private_key = keys.PrivateKey(fixture['transaction']['secretKey']) transaction = unsigned_transaction.as_signed_transaction(private_key=private_key) elif 'vrs' in fixture['transaction']: v, r, s = ( fixture['transaction']['v'], fixture['transaction']['r'], fixture['transaction']['s'], ) transaction = vm.create_transaction( nonce=fixture['transaction']['nonce'], gas_price=fixture['transaction']['gasPrice'], gas=fixture['transaction']['gasLimit'], to=fixture['transaction']['to'], value=fixture['transaction']['value'], data=fixture['transaction']['data'], v=v, r=r, s=s, ) try: header, receipt, computation = vm.apply_transaction(vm.block.header, transaction) transactions = vm.block.transactions + (transaction, ) receipts = vm.block.get_receipts(chaindb) + (receipt, ) block = vm.set_block_transactions(vm.block, header, transactions, receipts) except ValidationError as err: block = vm.block transaction_error = err LOGGER.warn("Got transaction error", exc_info=True) else: transaction_error = False if not transaction_error: log_entries = computation.get_log_entries() actual_logs_hash = hash_log_entries(log_entries) if 'logs' in fixture['post']: expected_logs_hash = fixture['post']['logs'] assert expected_logs_hash == actual_logs_hash elif log_entries: raise AssertionError("Got log {0} entries. hash:{1}".format( len(log_entries), actual_logs_hash, )) if 'out' in fixture: expected_output = fixture['out'] if isinstance(expected_output, int): assert len(computation.output) == expected_output else: assert computation.output == expected_output assert block.header.state_root == fixture['post']['hash']
(SPURIOUS_DRAGON_ROPSTEN_BLOCK, SpuriousDragonVM), (BYZANTIUM_ROPSTEN_BLOCK, ByzantiumVM), ) ROPSTEN_NETWORK_ID = 3 RopstenChain = Chain.configure( 'RopstenChain', vm_configuration=ROPSTEN_VM_CONFIGURATION, network_id=ROPSTEN_NETWORK_ID, ) ROPSTEN_GENESIS_HEADER = BlockHeader( difficulty=1048576, extra_data=decode_hex( "0x3535353535353535353535353535353535353535353535353535353535353535"), gas_limit=16777216, gas_used=0, bloom=0, mix_hash=constants.ZERO_HASH32, nonce=constants.GENESIS_NONCE, block_number=0, parent_hash=constants.ZERO_HASH32, receipt_root=constants.BLANK_ROOT_HASH, uncles_hash=constants.EMPTY_UNCLE_HASH, state_root=decode_hex( "0x217b0bbcfb72e2d57e28f33cb361b9983513177755dc3f33ce3e7022ed62b77b"), timestamp=0, transaction_root=constants.BLANK_ROOT_HASH, )
def test_blockchain_fixtures(fixture, chain_vm_configuration): genesis_params = { 'parent_hash': fixture['genesisBlockHeader']['parentHash'], 'uncles_hash': fixture['genesisBlockHeader']['uncleHash'], 'coinbase': fixture['genesisBlockHeader']['coinbase'], 'state_root': fixture['genesisBlockHeader']['stateRoot'], 'transaction_root': fixture['genesisBlockHeader']['transactionsTrie'], 'receipt_root': fixture['genesisBlockHeader']['receiptTrie'], 'bloom': fixture['genesisBlockHeader']['bloom'], 'difficulty': fixture['genesisBlockHeader']['difficulty'], 'block_number': fixture['genesisBlockHeader']['number'], 'gas_limit': fixture['genesisBlockHeader']['gasLimit'], 'gas_used': fixture['genesisBlockHeader']['gasUsed'], 'timestamp': fixture['genesisBlockHeader']['timestamp'], 'extra_data': fixture['genesisBlockHeader']['extraData'], 'mix_hash': fixture['genesisBlockHeader']['mixHash'], 'nonce': fixture['genesisBlockHeader']['nonce'], } expected_genesis_header = BlockHeader(**genesis_params) # TODO: find out if this is supposed to pass? # if 'genesisRLP' in fixture: # assert rlp.encode(genesis_header) == fixture['genesisRLP'] db = BaseChainDB(get_db_backend()) ChainForTesting = MainnetChain.configure( 'ChainForTesting', vm_configuration=chain_vm_configuration, ) chain = ChainForTesting.from_genesis( db, genesis_params=genesis_params, genesis_state=fixture['pre'], ) genesis_block = chain.get_canonical_block_by_number(0) genesis_header = genesis_block.header assert_rlp_equal(genesis_header, expected_genesis_header) # 1 - mine the genesis block # 2 - loop over blocks: # - apply transactions # - mine block # 4 - profit!! for block_data in fixture['blocks']: should_be_good_block = 'blockHeader' in block_data if 'rlp_error' in block_data: assert not should_be_good_block continue # The block to import may be in a different block-class-range than the # chain's current one, so we use the block number specified in the # fixture to look up the correct block class. if should_be_good_block: block_number = block_data['blockHeader']['number'] block_class = chain.get_vm_class_for_block_number( block_number).get_block_class() else: block_class = chain.get_vm().get_block_class() try: block = rlp.decode(block_data['rlp'], sedes=block_class, chaindb=chain.chaindb) except (TypeError, rlp.DecodingError, rlp.DeserializationError) as err: assert not should_be_good_block, "Block should be good: {0}".format( err) continue try: mined_block = chain.import_block(block) except ValidationError as err: assert not should_be_good_block, "Block should be good: {0}".format( err) continue else: assert_rlp_equal(mined_block, block) assert should_be_good_block, "Block should have caused a validation error" latest_block_hash = chain.get_canonical_block_by_number( chain.get_block().number - 1).hash assert latest_block_hash == fixture['lastblockhash'] with chain.get_vm().state_db(read_only=True) as state_db: verify_state_db(fixture['postState'], state_db)