def test_get_state_by_slot(valid_chain, genesis_block, genesis_state, keymap): # First, skip block and check if `get_state_by_slot` returns the expected state state_machine = valid_chain.get_state_machine(genesis_block.slot) config = state_machine.config state = valid_chain.get_head_state() block_skipped_slot = genesis_block.slot + 1 block_skipped_state, _ = state_machine.apply_state_transition( state, future_slot=block_skipped_slot) valid_chain.chaindb.persist_state(block_skipped_state) with pytest.raises(StateNotFound): valid_chain.get_state_by_slot(block_skipped_slot) # Next, import proposed block and check if `get_state_by_slot` returns the expected state proposed_slot = block_skipped_slot + 1 future_state, _ = state_machine.apply_state_transition( block_skipped_state, future_slot=proposed_slot) proposer_index = get_beacon_proposer_index(future_state, config) proposer = block_skipped_state.validators[proposer_index].pubkey private_key = keymap[proposer] randao_reveal = generate_randao_reveal(private_key, proposed_slot, block_skipped_state, config) block = create_block( proposed_slot, genesis_block.message.hash_tree_root, randao_reveal, state.eth1_data, (), block_skipped_state, state_machine, private_key, ) valid_chain.import_block(block) state = valid_chain.get_head_state() assert (valid_chain.get_state_by_slot(proposed_slot).hash_tree_root == state.hash_tree_root)
def test_import_blocks(valid_chain, genesis_block, genesis_state, config, keymap): state = genesis_state blocks = (genesis_block, ) valid_chain_2 = copy.deepcopy(valid_chain) for _ in range(3): state_machine = valid_chain.get_state_machine(blocks[-1].slot) parent_block = blocks[-1] slot = state.slot + 2 future_state, _ = state_machine.apply_state_transition( state, future_slot=state.slot + 2) proposer_index = get_beacon_proposer_index(future_state, config) proposer = state.validators[proposer_index].pubkey private_key = keymap[proposer] randao_reveal = generate_randao_reveal(private_key, slot, state, config) block = create_block( slot, parent_block.message.hash_tree_root, randao_reveal, state.eth1_data, (), state, state_machine, private_key, ) valid_chain.import_block(block) assert valid_chain.get_canonical_head() == block state = valid_chain.get_head_state() assert block == valid_chain.get_canonical_block_by_slot(block.slot) assert block.message.hash_tree_root == valid_chain.get_canonical_block_root( block.slot) blocks += (block, ) assert valid_chain.get_canonical_head( ) != valid_chain_2.get_canonical_head() for block in blocks[1:]: valid_chain_2.import_block(block) assert valid_chain.get_canonical_head( ) == valid_chain_2.get_canonical_head() assert valid_chain.get_head_state().slot != 0 assert valid_chain.get_head_state() == valid_chain_2.get_head_state()
def _build_chain_of_blocks_with_states( chain, state, parent_block, slots, config, keymap, attestation_participation=1.0, eth1_block_hash=ZERO_HASH32, ): blocks = () states = () for slot in range(parent_block.slot + 1, parent_block.slot + 1 + slots): sm = chain.get_state_machine(state.slot) pre_state, _ = sm.apply_state_transition(state, future_slot=slot) proposer_index = get_beacon_proposer_index(pre_state, config) public_key = state.validators[proposer_index].pubkey private_key = keymap[public_key] randao_reveal = generate_randao_reveal(private_key, slot, pre_state, config) attestations = create_mock_signed_attestations_at_slot( state, config, sm, slot - 1, parent_block.hash_tree_root, keymap, voted_attesters_ratio=attestation_participation, ) block = create_block( slot, parent_block.hash_tree_root, randao_reveal, Eth1Data.create(block_hash=eth1_block_hash), attestations, state, sm, private_key, ) parent_block = block.message state, block = sm.apply_state_transition(state, block) blocks += (block, ) states += (state, ) return blocks, states
async def propose_block( self, proposer_index: ValidatorIndex, slot: Slot, state: BeaconState, state_machine: BaseBeaconStateMachine, head_block: BaseBeaconBlock, ) -> BaseBeaconBlock: """ Propose a block and broadcast it. """ eth1_vote = await self._get_eth1_vote(slot, state, state_machine) # deposits = await self._get_deposit_data(state, state_machine, eth1_vote) # TODO(hwwhww): Check if need to aggregate and if they are overlapping. aggregated_attestations = self.get_ready_attestations(slot, True) unaggregated_attestations = self.get_ready_attestations(slot, False) ready_attestations = aggregated_attestations + unaggregated_attestations private_key = self.validator_privkeys[proposer_index] randao_reveal = generate_randao_reveal( private_key, slot, state, state_machine.config ) block = create_block_proposal( slot, head_block.message.hash_tree_root, randao_reveal, eth1_vote, ready_attestations, state, state_machine, ) block = sign_block(state, block, private_key, self.slots_per_epoch) self.logger.debug( bold_green("validator %s is proposing a block %s with attestations %s"), proposer_index, block, block.body.attestations, ) self.chain.import_block(block) self.logger.debug("broadcasting block %s", block) await self.p2p_node.broadcast_beacon_block(block) metrics.validator_proposed_blocks.inc() return block
def test_get_attestation_root(valid_chain, genesis_block, genesis_state, keymap, min_attestation_inclusion_delay): state_machine = valid_chain.get_state_machine() config = state_machine.config attestations = create_mock_signed_attestations_at_slot( state=genesis_state, config=config, state_machine=state_machine, attestation_slot=genesis_block.slot, beacon_block_root=genesis_block.message.hash_tree_root, keymap=keymap, ) state = genesis_state slot = genesis_state.slot + 1 future_state, _ = state_machine.apply_state_transition(state, future_slot=slot) proposer_index = get_beacon_proposer_index(future_state, config) proposer = state.validators[proposer_index].pubkey private_key = keymap[proposer] randao_reveal = generate_randao_reveal(private_key, slot, state, config) block = create_block( slot, genesis_block.message.hash_tree_root, randao_reveal, state.eth1_data, attestations, state, state_machine, private_key, ) valid_chain.import_block(block) # Only one attestation in attestations, so just check that one a0 = attestations[0] assert valid_chain.get_attestation_by_root(a0.hash_tree_root) == a0 assert valid_chain.attestation_exists(a0.hash_tree_root) fake_attestation = a0.set("signature", b"\x78" * 96) with pytest.raises(AttestationRootNotFound): valid_chain.get_attestation_by_root(fake_attestation.hash_tree_root) assert not valid_chain.attestation_exists(fake_attestation.hash_tree_root)
def test_demo(base_db, validator_count, keymap, pubkeys, fork_choice_scoring): bls.use_noop_backend() config = MINIMAL_SERENITY_CONFIG override_lengths(config) genesis_slot = GENESIS_SLOT genesis_epoch = GENESIS_EPOCH genesis_state, genesis_block = create_mock_genesis( pubkeys=pubkeys[:validator_count], config=config, keymap=keymap, genesis_block_class=SerenityBeaconBlock, ) chaindb = BeaconChainDB.from_genesis( base_db, genesis_state, SerenitySignedBeaconBlock, fork_choice_scoring ) fixture_sm = SkeletonLakeStateMachine(chaindb) for i in range(validator_count): assert genesis_state.validators[i].is_active(genesis_slot) state = genesis_state block = SerenitySignedBeaconBlock.create(message=genesis_block) chain_length = 4 * config.SLOTS_PER_EPOCH blocks = (block,) attestations_map = {} # Dict[Slot, Sequence[Attestation]] for current_slot in range(genesis_slot + 1, genesis_slot + chain_length + 1): if current_slot > genesis_slot + config.MIN_ATTESTATION_INCLUSION_DELAY: attestations = attestations_map[ current_slot - config.MIN_ATTESTATION_INCLUSION_DELAY ] else: attestations = () future_state, _ = fixture_sm.apply_state_transition( state, future_slot=current_slot ) proposer_index = get_beacon_proposer_index(future_state, config) proposer_pubkey = state.validators[proposer_index].pubkey private_key = keymap[proposer_pubkey] randao_reveal = generate_randao_reveal(private_key, current_slot, state, config) block_proposal = create_block_proposal( current_slot, block.message.hash_tree_root, randao_reveal, state.eth1_data, attestations, state, fixture_sm, ) block = sign_block(state, block_proposal, private_key, config.SLOTS_PER_EPOCH) state, block = fixture_sm.apply_state_transition(state, block) chaindb.persist_state(state) chaindb.persist_block(block, SerenitySignedBeaconBlock, fork_choice_scoring) blocks += (block,) # Mock attestations attestation_slot = current_slot attestations = create_mock_signed_attestations_at_slot( state=state, config=config, state_machine=fixture_sm, attestation_slot=attestation_slot, beacon_block_root=block.message.hash_tree_root, keymap=keymap, voted_attesters_ratio=1.0, ) attestations_map[attestation_slot] = attestations assert state.slot == chain_length + genesis_slot # Justification assertions # NOTE: why are the number `2` or `3` used in the checks below? # Answer: # "We do not check any justification and finality during epochs 0 or 1. We do check for # justification and finality from epoch 2 onward." # [epoch 0]------[epoch 1]------> # # "In epoch 2, we justify the current epoch. This epoch is in fact justified but we do not # recognize it in the protocol due to an artifact of the construction of the genesis state # (using the `zero` value for `Checkpoint` type)." # [epoch 0]------[epoch 1]------[epoch 2]*------> # []*: checkpoint justified # []**: checkpoint finalized # # "In epoch 3, we have the previous justified checkpoint at the prior current justified # checkpoint (so `GENESIS_EPOCH + 2`) and we justify this current epoch. we check finality here # and see that we finalize the prior justified checkpoint at epoch 2." # [epoch 0]------[epoch 1]------[epoch 2]**------[epoch 3]*------> # # "Given the way we handle epoch processing (i.e. process a given epoch at the start of # the next epoch), we need to transition through `4 * SLOTS_PER_EPOCH` worth of slots to # include the processing of epoch 3." # # source: https://github.com/ethereum/trinity/pull/1214#issuecomment-546184080 # # epoch | prev_justified_checkpoint | cur_justified_checkpoint | finalized_checkpoint # ------|---------------------------|--------------------------|--------------------- # 0 | 0 | 0 | 0 # 1 | 0 | 0 | 0 # 2 | 0 | 0 | 0 # 3 | 0 | 2 | 0 # 4 | 2 | 3 | 2 assert state.previous_justified_checkpoint.epoch == 2 + genesis_epoch assert state.current_justified_checkpoint.epoch == 3 + genesis_epoch assert state.finalized_checkpoint.epoch == 2 + genesis_epoch