def vm(): header = CollationHeader( shard_id=0, expected_period_number=2, period_start_prevhash=decode_hex( "3c4cc7b99c7eb9281e9a8d15cd4b2f98c5df085e929f15388c699b41cdde78d7" ), parent_hash=ZERO_HASH32, transaction_root=EMPTY_SHA3, coinbase=to_canonical_address( "8888f1f195afa192cfee860698584c030f4c9db1"), state_root=EMPTY_SHA3, receipt_root=EMPTY_SHA3, number=10, ) chaindb = ChainDB( get_db_backend(), account_state_class=ShardingAccountStateDB, trie_class=BinaryTrie, ) vm = ShardingVM(header=header, chaindb=chaindb) vm_state = vm.state with vm_state.state_db() as statedb: for address, code in HELPER_CONTRACTS.items(): statedb.set_code(address, code) statedb.set_balance(ACCOUNT_ADDRESS, INITIAL_BALANCE) # Update state_root manually vm.block.header.state_root = vm_state.state_root return vm
def chain_without_block_validation(): """ Return a Chain object containing just the genesis block. This Chain does not perform any validation when importing new blocks. The Chain's state includes one funded account and a private key for it, which can be found in the funded_address and private_keys variables in the chain itself. """ # Disable block validation so that we don't need to construct finalized blocks. overrides = { 'ensure_blocks_are_equal': lambda self, b1, b2: None, 'validate_block': lambda self, block: None, } klass = Chain.configure( name='TestChainWithoutBlockValidation', vm_configuration=((constants.GENESIS_BLOCK_NUMBER, FrontierVM), ), **overrides, ) private_key = decode_hex( '0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8') funded_addr = private_key_to_address(private_key) initial_balance = 100000000 genesis_params = { 'block_number': constants.GENESIS_BLOCK_NUMBER, 'difficulty': constants.GENESIS_DIFFICULTY, 'gas_limit': constants.GENESIS_GAS_LIMIT, 'parent_hash': constants.GENESIS_PARENT_HASH, 'coinbase': constants.GENESIS_COINBASE, 'nonce': constants.GENESIS_NONCE, 'mix_hash': constants.GENESIS_MIX_HASH, 'extra_data': constants.GENESIS_EXTRA_DATA, 'timestamp': 1501851927, 'state_root': decode_hex( '0x9d354f9b5ba851a35eced279ef377111387197581429cfcc7f744ef89a30b5d4' ) } genesis_state = { funded_addr: { 'balance': initial_balance, 'nonce': 0, 'code': b'', 'storage': {}, } } chain = klass.from_genesis(get_db_backend(), genesis_params, genesis_state) chain.funded_address = funded_addr chain.funded_address_initial_balance = initial_balance chain.funded_address_private_key = private_key return chain
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_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 chaindb(request): if request.param is MainAccountStateDB: trie_class = HexaryTrie else: trie_class = BinaryTrie return ChainDB( get_db_backend(), account_state_class=request.param, trie_class=trie_class, )
def setup_tester_chain(): from evm.vm.flavors import MainnetTesterChain from evm.db import get_db_backend db = get_db_backend() genesis_params = get_default_genesis_params() account_keys = get_default_account_keys() genesis_state = generate_genesis_state(account_keys) chain = MainnetTesterChain.from_genesis(db, genesis_params, genesis_state) return account_keys, chain
def setup_tester_chain(): from evm.chains.tester import MainnetTesterChain from evm.db import get_db_backend from evm.db.chain import ChainDB db = ChainDB(get_db_backend()) genesis_params = get_default_genesis_params() account_keys = get_default_account_keys() genesis_state = generate_genesis_state(account_keys) chain = MainnetTesterChain.from_genesis(db, genesis_params, genesis_state) return account_keys, chain
def test_get_vm_class_for_block_number(): chain_class = Chain.configure(vm_configuration=( (constants.GENESIS_BLOCK_NUMBER, FrontierVM), (constants.HOMESTEAD_MAINNET_BLOCK, HomesteadVM), ), ) chain = chain_class(get_db_backend(), BlockHeader(1, 0, 100)) assert chain.get_vm_class_for_block_number( constants.GENESIS_BLOCK_NUMBER, ) == FrontierVM assert chain.get_vm_class_for_block_number( constants.HOMESTEAD_MAINNET_BLOCK - 1) == FrontierVM assert chain.get_vm_class_for_block_number( constants.HOMESTEAD_MAINNET_BLOCK) == HomesteadVM assert chain.get_vm_class_for_block_number( constants.HOMESTEAD_MAINNET_BLOCK + 1) == HomesteadVM
def test_transaction_fixtures(fixture_name, fixture): header = BlockHeader(1, fixture.get('blocknumber', 0), 100) chain = MainnetChain(get_db_backend(), header=header) vm = chain.get_vm() TransactionClass = vm.get_transaction_class() if 'sender' in fixture: transaction = rlp.decode(fixture['rlp'], sedes=TransactionClass) expected = normalize_signed_transaction(fixture['transaction']) assert transaction.nonce == expected['nonce'] assert transaction.gas_price == expected['gasPrice'] assert transaction.gas == expected['gasLimit'] assert transaction.to == expected['to'] assert transaction.value == expected['value'] assert transaction.data == expected['data'] assert transaction.v == expected['v'] assert transaction.r == expected['r'] assert transaction.s == expected['s'] sender = to_canonical_address(fixture['sender']) try: assert transaction.sender == sender except BadSignature: assert not (27 <= transaction.v <= 34) else: # check RLP correctness try: transaction = rlp.decode(fixture['rlp'], sedes=TransactionClass) # fixture normalization changes the fixture key from rlp to rlpHex except KeyError: assert fixture['rlpHex'] return # rlp is a list of bytes when it shouldn't be except TypeError as err: assert err.args == ( "'bytes' object cannot be interpreted as an integer", ) return # rlp is invalid or not in the correct form except (rlp.exceptions.ObjectDeserializationError, rlp.exceptions.DecodingError): return # check parameter correctness try: transaction.validate() except ValidationError: return
def new_chain_from_fixture(fixture): db = ChainDB(get_db_backend()) vm_config = chain_vm_configuration(fixture) ChainFromFixture = MainnetChain.configure( 'ChainFromFixture', vm_configuration=vm_config, ) return ChainFromFixture.from_genesis( db, genesis_params=genesis_params_from_fixture(fixture), genesis_state=fixture['pre'], )
def new_chain_from_fixture(fixture): db = BaseChainDB(get_db_backend()) vm_config = chain_vm_configuration(fixture) ChainFromFixture = MainnetChain.configure( 'ChainFromFixture', vm_configuration=vm_config, ) return ChainFromFixture.from_genesis( db, genesis_params=genesis_params_from_fixture(fixture), genesis_state=fixture['pre'], )
def test_state_fixtures(fixture_name, fixture): 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'], ) db = get_db_backend() chain = ChainForTesting(db=db, header=header) state_db = setup_state_db(fixture['pre'], chain.get_state_db()) chain.header.state_root = state_db.root_hash unsigned_transaction = chain.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 = chain.apply_transaction(transaction) except ValidationError: 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 verify_state_db(fixture['post'], chain.get_state_db())
def reset_to_genesis(self, overrides=None): from evm.chains.tester import MainnetTesterChain from evm.db import get_db_backend base_db = get_db_backend() genesis_params = get_default_genesis_params() # Genesis params overrides if overrides is not None: genesis_params.update(overrides) account_keys = get_default_account_keys() genesis_state = generate_genesis_state(account_keys) chain = MainnetTesterChain.from_genesis(base_db, genesis_params, genesis_state) self.account_keys, self.chain = account_keys, chain
def test_transaction_fixtures(fixture): header = BlockHeader(1, fixture['blocknumber'], 100) chain = MainnetChain(ChainDB(get_db_backend()), header=header) vm = chain.get_vm() TransactionClass = vm.get_transaction_class() if 'sender' in fixture: transaction = rlp.decode(fixture['rlp'], sedes=TransactionClass) expected = normalize_signed_transaction(fixture['transaction']) assert transaction.nonce == expected['nonce'] assert transaction.gas_price == expected['gasPrice'] assert transaction.gas == expected['gasLimit'] assert transaction.to == expected['to'] assert transaction.value == expected['value'] assert transaction.data == expected['data'] assert transaction.v == expected['v'] assert transaction.r == expected['r'] assert transaction.s == expected['s'] sender = to_canonical_address(fixture['sender']) try: assert transaction.sender == sender except BadSignature: assert not (27 <= transaction.v <= 34) else: # check RLP correctness try: transaction = rlp.decode(fixture['rlp'], sedes=TransactionClass) # fixture normalization changes the fixture key from rlp to rlpHex except KeyError: assert fixture['rlpHex'] return # rlp is a list of bytes when it shouldn't be except TypeError as err: assert err.args == ("'bytes' object cannot be interpreted as an integer",) return # rlp is invalid or not in the correct form except (rlp.exceptions.ObjectDeserializationError, rlp.exceptions.DecodingError): return # check parameter correctness try: transaction.validate() except ValidationError: return
def test_get_vm_class_for_block_number(): chain_class = Chain.configure( name='TestChain', vm_configuration=( (constants.GENESIS_BLOCK_NUMBER, FrontierVM), (HOMESTEAD_MAINNET_BLOCK, HomesteadVM), ), ) chain = chain_class(get_db_backend(), BlockHeader(1, 0, 100)) assert chain.get_vm_class_for_block_number( constants.GENESIS_BLOCK_NUMBER,) == FrontierVM assert chain.get_vm_class_for_block_number( HOMESTEAD_MAINNET_BLOCK - 1) == FrontierVM assert chain.get_vm_class_for_block_number( HOMESTEAD_MAINNET_BLOCK) == HomesteadVM assert chain.get_vm_class_for_block_number( HOMESTEAD_MAINNET_BLOCK + 1) == HomesteadVM
def chain_without_block_validation(chaindb, funded_address, funded_address_initial_balance): """ Return a Chain object containing just the genesis block. This Chain does not perform any validation when importing new blocks. The Chain's state includes one funded account and a private key for it, which can be found in the funded_address and private_keys variables in the chain itself. """ # Disable block validation so that we don't need to construct finalized blocks. overrides = { 'import_block': import_block_without_validation, 'validate_block': lambda self, block: None, } klass = Chain.configure( name='TestChainWithoutBlockValidation', vm_configuration=((constants.GENESIS_BLOCK_NUMBER, FrontierVM), ), **overrides, ) genesis_params = { 'block_number': constants.GENESIS_BLOCK_NUMBER, 'difficulty': constants.GENESIS_DIFFICULTY, 'gas_limit': constants.GENESIS_GAS_LIMIT, 'parent_hash': constants.GENESIS_PARENT_HASH, 'coinbase': constants.GENESIS_COINBASE, 'nonce': constants.GENESIS_NONCE, 'mix_hash': constants.GENESIS_MIX_HASH, 'extra_data': constants.GENESIS_EXTRA_DATA, 'timestamp': 1501851927, } genesis_state = { funded_address: { 'balance': funded_address_initial_balance, 'nonce': 0, 'code': b'', 'storage': {}, } } chain = klass.from_genesis(ChainDB(get_db_backend()), genesis_params, genesis_state) return chain
def setup_tester_chain(): from evm.chains.tester import MainnetTesterChain from evm.db import get_db_backend from evm.vm.forks.byzantium import ByzantiumVM class ByzantiumNoProofVM(ByzantiumVM): """Byzantium VM rules, without validating any miner proof of work""" def validate_seal(self, header): pass class MainnetTesterNoProofChain(MainnetTesterChain): vm_configuration = ((0, ByzantiumNoProofVM), ) genesis_params = get_default_genesis_params() account_keys = get_default_account_keys() genesis_state = generate_genesis_state(account_keys) base_db = get_db_backend() chain = MainnetTesterNoProofChain.from_genesis(base_db, genesis_params, genesis_state) return account_keys, chain
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 test_raises_if_db_path_is_not_specified(config_env): with pytest.raises(TypeError): get_db_backend()
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_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) 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_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)
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 chaindb(): return ChainDB(get_db_backend())
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.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): 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 chain(): """ Return a Chain object containing just the genesis block. The Chain's state includes one funded account, which can be found in the funded_address in the chain itself. This Chain will perform all validations when importing new blocks, so only valid and finalized blocks can be used with it. If you want to test importing arbitrarily constructe, not finalized blocks, use the chain_without_block_validation fixture instead. """ genesis_params = { "bloom": 0, "coinbase": to_canonical_address("8888f1f195afa192cfee860698584c030f4c9db1"), "difficulty": 131072, "extra_data": b"B", "gas_limit": 3141592, "gas_used": 0, "mix_hash": decode_hex( "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" ), "nonce": decode_hex("0102030405060708"), "block_number": 0, "parent_hash": decode_hex( "0000000000000000000000000000000000000000000000000000000000000000" ), "receipt_root": decode_hex( "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" ), "state_root": decode_hex( "cafd881ab193703b83816c49ff6c2bf6ba6f464a1be560c42106128c8dbc35e7" ), "timestamp": 1422494849, "transaction_root": decode_hex( "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" ), "uncles_hash": decode_hex( "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") } funded_addr = to_canonical_address( "a94f5374fce5edbc8e2a8697c15331677e6ebf0b") initial_balance = 10000000000 genesis_state = { funded_addr: { "balance": initial_balance, "nonce": 0, "code": b"", "storage": {} } } klass = Chain.configure(name='TestChain', vm_configuration=((constants.GENESIS_BLOCK_NUMBER, FrontierVM), )) chain = klass.from_genesis(ChainDB(get_db_backend()), genesis_params, genesis_state) chain.funded_address = funded_addr chain.funded_address_initial_balance = initial_balance return chain
def level_db(config_env, tmpdir): level_db_path = str(tmpdir.mkdir("level_db_path")) return get_db_backend(db_path=level_db_path)
def chaindb(request): return ChainDB(get_db_backend())
def test_raises_if_db_path_is_not_specified(config_env): with pytest.raises(TypeError): get_db_backend()
def chaindb(): return ChainDB(get_db_backend())
def shard_db(): return ShardDB(get_db_backend())
def level_db(config_env, tmpdir): level_db_path = str(tmpdir.mkdir("level_db_path")) return get_db_backend(db_path=level_db_path)
def shard_chaindb(): return ChainDB( get_db_backend(), account_state_class=ShardingAccountStateDB, trie_class=BinaryTrie, )
def db(): return get_db_backend()
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']
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_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))