def test_chain_builder_build_uncle_fork(mining_chain): chain = build( mining_chain, mine_block(), # 1 mine_block(), # 2 ) fork_chain = build( chain, at_block_number(1), mine_block(extra_data=b'fork-it!'), # fork 2 ) # we don't use canonical head here because the fork chain is non-canonical. uncle = fork_chain.get_block_header_by_hash(fork_chain.header.parent_hash) assert uncle.block_number == 2 assert uncle != chain.get_canonical_head() chain = build( chain, mine_block(uncles=[uncle]), # 3 ) header = chain.get_canonical_head() block = chain.get_block_by_hash(header.hash) assert len(block.uncles) == 1 assert block.uncles[0] == uncle
def test_uncle_block_inclusion_validity(vm_fn): # This test ensures that a forked block which is behind by # more than 6 layers cannot act as an ancestor to the current block OTHER_MINER_ADDRESS = 20 * b'\x01' chain = build( MiningChain, vm_fn(0), disable_pow_check(), genesis(), mine_block(), # 1 mine_block(), # 2 ) fork_chain = build( chain, at_block_number(1), mine_block(extra_data=b'fork-it!', coinbase=OTHER_MINER_ADDRESS), # fork 2 ) # we don't use canonical head here because the fork chain is non-canonical. uncle = fork_chain.get_block_header_by_hash(fork_chain.header.parent_hash) assert uncle.block_number == 2 with pytest.raises(ValidationError): chain = build( chain, # Mines blocks from 3 to 8 (both numbers inclusive) mine_blocks(6), # Mine block 9 with uncle mine_block(uncles=[uncle]), )
def test_chain_builder_at_block_number(mining_chain): pre_fork_chain = build( mining_chain, mine_block(), # 1 mine_block(), # 2 mine_block(), # 3 ) block_1 = pre_fork_chain.get_canonical_block_by_number(1) block_2 = pre_fork_chain.get_canonical_block_by_number(2) block_3 = pre_fork_chain.get_canonical_block_by_number(3) chain = build( pre_fork_chain, at_block_number(2), mine_block(extra_data=b'fork-it!'), # fork 3 mine_block(), # fork 4 mine_block(), # fork 5 ) # ensure that our chain is ahead of the pre_fork_chain head = chain.get_canonical_head() assert head.block_number == 5 pre_fork_head = pre_fork_chain.get_canonical_head() assert pre_fork_head.block_number == 3 f_block_1 = chain.get_canonical_block_by_number(1) f_block_2 = chain.get_canonical_block_by_number(2) f_block_3 = chain.get_canonical_block_by_number(3) # verify that the fork diverges from the pre_fork_chain assert f_block_1 == block_1 assert f_block_2 == block_2 assert f_block_3 != block_3
def test_rewards_nephew_uncle_different_vm(vm_fn_uncle, vm_fn_nephew, miner_1_balance, miner_2_balance): OTHER_MINER_ADDRESS = 20 * b'\x01' TOTAL_BLOCKS_CANONICAL_CHAIN = 10 VM_CHANGE_BLOCK_NUMBER = 4 chain = build( MiningChain, vm_fn_uncle(0), vm_fn_nephew(VM_CHANGE_BLOCK_NUMBER), disable_pow_check(), genesis(), mine_blocks(TOTAL_BLOCKS_CANONICAL_CHAIN - 1), ) fork_chain = build( chain, at_block_number(3), mine_block(extra_data=b'fork-it!', coinbase=OTHER_MINER_ADDRESS), # fork 2 ) # we don't use canonical head here because the fork chain is non-canonical. uncle = fork_chain.get_block_header_by_hash(fork_chain.header.parent_hash) assert uncle.block_number == 4 chain = build( chain, mine_block(uncles=[uncle]), ) header = chain.get_canonical_head() block = chain.get_block_by_hash(header.hash) assert len(block.uncles) == 1 assert block.uncles[0] == uncle vm = chain.get_vm() coinbase_balance = vm.state.get_balance(block.header.coinbase) other_miner_balance = vm.state.get_balance(uncle.coinbase) uncle_vm = chain.get_vm_class_for_block_number(0) nephew_vm = chain.get_vm_class_for_block_number(VM_CHANGE_BLOCK_NUMBER) # We first test if the balance matches what we would determine # if we made all the API calls involved ourselves. assert coinbase_balance == (uncle_vm.get_block_reward() * 3 + nephew_vm.get_block_reward() * (TOTAL_BLOCKS_CANONICAL_CHAIN - 3) + vm.get_nephew_reward()) assert other_miner_balance == vm.get_uncle_reward(block.number, uncle) # But we also ensure the balance matches the numbers that we calculated on paper assert coinbase_balance == to_wei(miner_1_balance, 'ether') assert other_miner_balance == to_wei(miner_2_balance, 'ether')
def _create(cls, model_class: Type[AsyncHeaderDB], *args: Any, **kwargs: Any) -> AsyncHeaderDB: headerdb = model_class(*args, **kwargs) from eth.chains.base import Chain from eth.tools.builder.chain import build, latest_mainnet_at, genesis build( Chain, latest_mainnet_at(0), genesis(db=headerdb.db), ) return headerdb
def _create(cls, model_class: Type[AsyncHeaderDB], *args: Any, **kwargs: Any) -> AsyncHeaderDB: from eth.chains.base import Chain from eth.tools.builder.chain import build, latest_mainnet_at, genesis genesis_params = kwargs.pop('genesis_params', None) headerdb = model_class(*args, **kwargs) build( Chain, latest_mainnet_at(0), genesis(db=headerdb.db, params=genesis_params), ) return headerdb
def test_eip1559_txn_rewards(max_total_price, max_priority_price, expected_miner_tips, funded_address, funded_address_private_key): chain = build( MiningChain, berlin_at(0), london_at(1), # Start London at block one to get easy 1gwei base fee disable_pow_check(), genesis( params=dict(gas_limit=10**7), state={funded_address: dict(balance=10**20)}, ), ) vm = chain.get_vm() txn = new_dynamic_fee_transaction( vm, from_=funded_address, to=funded_address, private_key=funded_address_private_key, max_priority_fee_per_gas=max_priority_price, max_fee_per_gas=max_total_price, ) MINER = b'\x0f' * 20 original_balance = vm.state.get_balance(MINER) chain.mine_all([txn], coinbase=MINER) new_balance = chain.get_vm().state.get_balance(MINER) BLOCK_REWARD = 2 * (10**18) assert original_balance + BLOCK_REWARD + expected_miner_tips == new_balance
def get_chain(vm: Type[BaseVM], genesis_state: GenesisState) -> MiningChain: chain = build(MiningChain, fork_at(vm, constants.GENESIS_BLOCK_NUMBER), disable_pow_check(), genesis(params=GENESIS_PARAMS, state=genesis_state)) return chain
def test_rewards_uncle_created_at_different_generations( vm_fn, fork_at_block_number, miner_1_balance, miner_2_balance): OTHER_MINER_ADDRESS = 20 * b'\x01' TOTAL_BLOCKS_CANONICAL_CHAIN = 10 chain = build( MiningChain, vm_fn(0), disable_pow_check(), genesis(), mine_blocks(TOTAL_BLOCKS_CANONICAL_CHAIN - 1), ) fork_chain = build( chain, at_block_number(fork_at_block_number), mine_block(extra_data=b'fork-it!', coinbase=OTHER_MINER_ADDRESS), # fork 2 ) # we don't use canonical head here because the fork chain is non-canonical. uncle = fork_chain.get_block_header_by_hash(fork_chain.header.parent_hash) assert uncle.block_number == fork_at_block_number + 1 chain = build( chain, mine_block(uncles=[uncle]), ) header = chain.get_canonical_head() block = chain.get_block_by_hash(header.hash) vm = chain.get_vm() coinbase_balance = vm.state.account_db.get_balance(block.header.coinbase) other_miner_balance = vm.state.account_db.get_balance(uncle.coinbase) # We first test if the balance matches what we would determine # if we made all the API calls involved ourselves. assert coinbase_balance == ( vm.get_block_reward() * TOTAL_BLOCKS_CANONICAL_CHAIN + vm.get_nephew_reward()) assert other_miner_balance == vm.get_uncle_reward(block.number, uncle) # But we also ensure the balance matches the numbers that we calculated on paper assert coinbase_balance == to_wei(miner_1_balance, 'ether') assert other_miner_balance == to_wei(miner_2_balance, 'ether')
def bob_chain(): chain = build( MiningChain, latest_mainnet_at(0), disable_pow_check(), genesis(), ) return chain
def test_chain_builder_mine_block_with_parameters(mining_chain): chain = build( mining_chain, mine_block(extra_data=b'test-setting-extra-data'), ) header = chain.get_canonical_head() assert header.extra_data == b'test-setting-extra-data'
def test_chain_builder_construct_chain_name(): chain = build( MiningChain, name('ChainForTest'), ) assert issubclass(chain, MiningChain) assert chain.__name__ == 'ChainForTest'
def test_chain_builder_build_single_default_block(mining_chain): chain = build( mining_chain, mine_block(), ) header = chain.get_canonical_head() assert header.block_number == 1
def test_chain_builder_build_mine_multiple_blocks(mining_chain): chain = build( mining_chain, mine_blocks(5), ) header = chain.get_canonical_head() assert header.block_number == 5
def test_chain_builder_without_any_mining_config(): chain = build( MiningChain, frontier_at(0), genesis(), ) with pytest.raises(ValidationError, match='mix hash mismatch'): chain.mine_block()
def _create(cls, model_class: Type[AsyncHeaderDB], *args: Any, **kwargs: Any) -> AsyncHeaderDB: from eth.chains.base import Chain from eth.tools.builder.chain import build, latest_mainnet_at, genesis genesis_params = kwargs.pop('genesis_params', None) headerdb = model_class(*args, **kwargs) # SIDE EFFECT! # This uses the side effect of initializing a chain using the `builder` # tool to populate the genesis state into the database. build( Chain, latest_mainnet_at(0), genesis(db=headerdb.db, params=genesis_params), ) return headerdb
def test_chain_import_blocks_many(mining_chain): temp_chain = build( mining_chain, copy(), mine_blocks(3), ) block_1, block_2, block_3 = ( temp_chain.get_canonical_block_by_number(1), temp_chain.get_canonical_block_by_number(2), temp_chain.get_canonical_block_by_number(3), ) chain = build( mining_chain, import_blocks(block_1, block_2, block_3), ) head = chain.get_canonical_head() assert head == block_3.header
def test_chain_builder_enable_pow_mining(): chain = build( MiningChain, frontier_at(0), enable_pow_mining(), genesis(), ) block = chain.mine_block() check_eccpow(block.header.parent_hash, block.header.mining_hash, 24, 3, 6)
def alice_chain(bob_chain): bob_genesis = bob_chain.headerdb.get_canonical_block_header_by_number(0) chain = build( MiningChain, latest_mainnet_at(0), disable_pow_check(), genesis(params={"timestamp": bob_genesis.timestamp}), ) return chain
async def test_handshake_with_incompatible_fork_id(alice_chain, bob_chain): alice_chain = build(alice_chain, mine_block()) pair_factory = ETHPeerPairFactory(alice_peer_context=ChainContextFactory( headerdb=AsyncHeaderDB(alice_chain.headerdb.db), vm_configuration=((1, PetersburgVM), (2, MuirGlacierVM))), ) with pytest.raises(WrongForkIDFailure): async with pair_factory as (alice, bob): pass
def test_chain_builder_construct_chain_vm_configuration_single_fork(): chain = build( MiningChain, fork_at(FrontierVM, 0), ) assert issubclass(chain, MiningChain) assert len(chain.vm_configuration) == 1 assert chain.vm_configuration[0][0] == 0 assert chain.vm_configuration[0][1] == FrontierVM
def get_chain(vm: Type[VirtualMachineAPI], genesis_state: GenesisState) -> Iterable[MiningChain]: with tempfile.TemporaryDirectory() as temp_dir: level_db_obj = LevelDB(Path(temp_dir)) level_db_chain = build( MiningChain, fork_at(vm, constants.GENESIS_BLOCK_NUMBER), disable_pow_check(), genesis(db=level_db_obj, params=GENESIS_PARAMS, state=genesis_state)) yield level_db_chain
def test_chain_builder_construct_chain_vm_configuration_multiple_forks(): chain = build( MiningChain, fork_at(FrontierVM, 0), fork_at(HomesteadVM, 5), ) assert issubclass(chain, MiningChain) assert len(chain.vm_configuration) == 2 assert chain.vm_configuration[0][0] == 0 assert chain.vm_configuration[0][1] == FrontierVM assert chain.vm_configuration[1][0] == 5 assert chain.vm_configuration[1][1] == HomesteadVM
async def test_eth_api_head_info_updates_with_announce(alice, bob, common_base_chain, LESAPI_class): # bob mines two blocks on his chain got_announce = asyncio.Event() async def _handle_announce(connection, msg): got_announce.set() alice.connection.add_command_handler(Announce, _handle_announce) bob_genesis = common_base_chain.headerdb.get_canonical_block_header_by_number( 0) assert alice.connection.has_logic(LESAPI_class.name) les_api = alice.connection.get_logic(LESAPI_class.name, LESAPI_class) assert les_api.head_info.head_hash == bob_genesis.hash assert les_api.head_info.head_td == bob_genesis.difficulty assert les_api.head_info.head_number == 0 bob_chain = build( common_base_chain, mine_block(), mine_block(), ) head = bob_chain.get_canonical_head() les_proto = bob.les_api.protocol assert isinstance(les_proto, BaseLESProtocol) assert head.block_number == 2 total_difficulty = bob_chain.headerdb.get_score(head.hash) les_proto.send( Announce( AnnouncePayload( head_hash=head.hash, head_number=head.block_number, head_td=total_difficulty, reorg_depth=0, params=(), ))) await asyncio.wait_for(got_announce.wait(), timeout=1) await asyncio.sleep(0.1) assert les_api.head_info.head_hash == head.hash assert les_api.head_info.head_td == total_difficulty assert les_api.head_info.head_number == 2
def test_chain_builder_enable_pow_mining(): chain = build( MiningChain, frontier_at(0), enable_pow_mining(), genesis(), ) block = chain.mine_block() check_pow( block.number, block.header.mining_hash, block.header.mix_hash, block.header.nonce, block.header.difficulty, )
def test_chain_builder_disable_pow_check(): chain = build( MiningChain, frontier_at(0), disable_pow_check(), genesis(), ) block = chain.mine_block() # check_eccpow( # block.header.parent_hash, # block.header.mining_hash, # 24, 3, 6) with pytest.raises(ValidationError, match='mix hash mismatch'): # ToDo: Have to change difficulty check_eccpow(block.header.parent_hash, block.header.mining_hash, 24, 3, 6)
def test_chain_builder_disable_pow_check(): chain = build( MiningChain, frontier_at(0), disable_pow_check(), genesis(), ) block = chain.mine_block() with pytest.raises(ValidationError, match='mix hash mismatch'): check_pow( block.number, block.header.mining_hash, block.header.mix_hash, block.header.nonce, block.header.difficulty, )
def test_chain_builder_mine_block_with_transactions( mining_chain, funded_address, funded_address_private_key): tx = new_transaction( mining_chain.get_vm(), from_=funded_address, to=ZERO_ADDRESS, private_key=funded_address_private_key, ) chain = build( mining_chain, mine_block(transactions=[tx]), ) block = chain.get_canonical_block_by_number(1) assert len(block.transactions) == 1 assert block.transactions[0] == tx
def test_chain_builder_construct_chain_fork_specific_helpers( fork_fn, vm_class): class DummyVM(FrontierVM): pass class ChainForTest(MiningChain): vm_configuration = ((0, DummyVM), ) chain = build( ChainForTest, fork_fn(12), ) assert issubclass(chain, MiningChain) assert len(chain.vm_configuration) == 2 assert chain.vm_configuration[0][0] == 0 assert chain.vm_configuration[0][1] is DummyVM assert chain.vm_configuration[1][0] == 12 assert chain.vm_configuration[1][1] is vm_class
def load_mining_chain(db): GENESIS_PARAMS = { 'coinbase': constants.ZERO_ADDRESS, 'difficulty': 5, 'gas_limit': 3141592, 'timestamp': 1514764800, } GENESIS_STATE = { FUNDED_ACCT.public_key.to_canonical_address(): { "balance": 100000000000000000, } } return build( FakeAsyncChain, byzantium_at(0), enable_pow_mining(), genesis(db=db, params=GENESIS_PARAMS, state=GENESIS_STATE), )