def test_blockchain_fixtures(fixture_data, fixture): try: chain = new_chain_from_fixture(fixture) except ValueError as e: raise AssertionError("could not load chain for {}".format( (fixture_data, ))) from e genesis_params = genesis_params_from_fixture(fixture) 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'] genesis_block = chain.get_canonical_block_by_number(0) genesis_header = genesis_block.header assert_imported_genesis_header_unchanged(expected_genesis_header, genesis_header) # 1 - mine the genesis block # 2 - loop over blocks: # - apply transactions # - mine block # 3 - diff resulting state with expected state # 4 - check that all previous blocks were valid for block_fixture in fixture['blocks']: should_be_good_block = 'blockHeader' in block_fixture if 'rlp_error' in block_fixture: assert not should_be_good_block continue if should_be_good_block: (block, mined_block, block_rlp) = apply_fixture_block_to_chain( block_fixture, chain, perform_validation=False # we manually validate below ) assert_mined_block_unchanged(block, mined_block) chain.validate_block(block) else: try: apply_fixture_block_to_chain(block_fixture, chain) except (TypeError, rlp.DecodingError, rlp.DeserializationError, ValidationError) as err: # failure is expected on this bad block pass else: raise AssertionError( "Block should have caused a validation error") latest_block_hash = chain.get_canonical_block_by_number( chain.get_block().number - 1).hash if latest_block_hash != fixture['lastblockhash']: verify_state(fixture['postState'], chain.get_vm().state)
def test_vm_fixtures(fixture, vm_class, computation_getter): db = get_db_backend() chaindb = ChainDB(db) consensus_context = ConsensusContext(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'], ) # None of the VM tests (currently) test chain ID, so the setting doesn't matter here. # When they *do* start testing ID, they will have to supply it as part of the environment. # For now, just hard-code it to something not used in practice: chain_context = ChainContext(chain_id=0) vm = vm_class(header=header, chaindb=chaindb, chain_context=chain_context, consensus_context=consensus_context) state = vm.state setup_state(fixture['pre'], state) code = state.get_code(fixture['exec']['address']) # Update state_root manually vm._block = vm.get_block().copy(header=vm.get_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.get_block().copy( header=vm.get_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(f"Got log entries: {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, f"Gas difference: {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_state(expected_account_db, vm.state)
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_state(fixture['pre'], state) code = state.get_code(fixture['exec']['address']) # Update state_root manually vm._block = vm.get_block().copy(header=vm.get_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.get_block().copy( header=vm.get_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_state(expected_account_db, vm.state)
def test_blockchain_fixtures(fixture_data, fixture): try: chain = new_chain_from_fixture(fixture) except ValueError as e: raise AssertionError(f"could not load chain for {fixture_data}") from e genesis_fields = genesis_fields_from_fixture(fixture) genesis_block = chain.get_canonical_block_by_number(0) genesis_header = genesis_block.header # Validate the genesis header RLP against the generated header if 'genesisRLP' in fixture: # Super hacky, but better than nothing: extract the header, then re-decode it fixture_decoded_block = rlp.decode(fixture['genesisRLP']) fixture_encoded_header = rlp.encode(fixture_decoded_block[0]) fixture_header = rlp.decode(fixture_encoded_header, sedes=HeaderSedes) # Error message with pretty output if header doesn't match assert_headers_eq(fixture_header, genesis_header) # Last gut check that transactions & receipts are valid, too assert rlp.encode(genesis_block) == fixture['genesisRLP'] assert_imported_genesis_header_unchanged(genesis_fields, genesis_header) # 1 - mine the genesis block # 2 - loop over blocks: # - apply transactions # - mine block # 3 - diff resulting state with expected state # 4 - check that all previous blocks were valid for block_fixture in fixture['blocks']: should_be_good_block = 'expectException' not in block_fixture if 'rlp_error' in block_fixture: assert not should_be_good_block continue if should_be_good_block: (original_block, executed_block, block_rlp) = apply_fixture_block_to_chain( block_fixture, chain, perform_validation=False # we manually validate below ) assert_mined_block_unchanged(original_block, executed_block) chain.validate_block(original_block) else: try: apply_fixture_block_to_chain(block_fixture, chain) except EXPECTED_BAD_BLOCK_EXCEPTIONS: # failure is expected on this bad block pass else: raise AssertionError( "Block should have caused a validation error") latest_block_hash = chain.get_canonical_block_by_number( chain.get_block().number - 1).hash if latest_block_hash != fixture['lastblockhash']: verify_state(fixture['postState'], chain.get_vm().state)