def test_ensure_update_eth1_vote_if_exists(genesis_state, config, vote_offsets): # one less than a majority is the majority divided by 2 threshold = config.SLOTS_PER_ETH1_VOTING_PERIOD // 2 data_votes = tuple( concat((Eth1Data.create(block_hash=(i).to_bytes(32, "little")), ) * (threshold + offset) for i, offset in enumerate(vote_offsets))) state = genesis_state for vote in data_votes: state = process_eth1_data( state, BeaconBlock.create(body=BeaconBlockBody.create(eth1_data=vote)), config, ) if not vote_offsets: assert state.eth1_data == genesis_state.eth1_data # we should update the 'latest' entry if we have a majority for offset in vote_offsets: if offset <= 0: assert genesis_state.eth1_data == state.eth1_data else: assert state.eth1_data == data_votes[0]
def create_mock_genesis( pubkeys: Sequence[BLSPubkey], config: Eth2Config, keymap: Dict[BLSPubkey, int], genesis_block_class: Type[BaseBeaconBlock], genesis_time: Timestamp = ZERO_TIMESTAMP, ) -> Tuple[BeaconState, BaseBeaconBlock]: genesis_deposits, deposit_root = create_mock_deposits_and_root( pubkeys=pubkeys, keymap=keymap, config=config) genesis_eth1_data = Eth1Data.create( deposit_root=deposit_root, deposit_count=len(genesis_deposits), block_hash=ZERO_HASH32, ) state = initialize_beacon_state_from_eth1( eth1_block_hash=genesis_eth1_data.block_hash, eth1_timestamp=genesis_time, deposits=genesis_deposits, config=config, ) block = get_genesis_block(genesis_state_root=state.hash_tree_root, block_class=genesis_block_class) assert len(state.validators) == len(pubkeys) return state, block
def test_process_eth1_data( original_votes, block_data, expected_votes, sample_beacon_state_params, sample_beacon_block_params, sample_beacon_block_body_params, config, ): eth1_data_votes = tuple(mapcat(_expand_eth1_votes, original_votes)) state = BeaconState.create(**sample_beacon_state_params).set( "eth1_data_votes", eth1_data_votes) block_body = BeaconBlockBody.create( **sample_beacon_block_body_params).mset( "eth1_data", Eth1Data.create(block_hash=block_data)) block = BeaconBlock.create(**sample_beacon_block_params).set( "body", block_body) updated_state = process_eth1_data(state, block, config) updated_votes = updated_state.eth1_data_votes expanded_expected_votes = tuple(mapcat(_expand_eth1_votes, expected_votes)) assert tuple(updated_votes) == expanded_expected_votes
def genesis_state(genesis_validators, genesis_balances, genesis_time, sample_eth1_data_params, config): genesis_eth1_data = Eth1Data.create(**sample_eth1_data_params).set( "deposit_count", len(genesis_validators)) return create_mock_genesis_state_from_validators(genesis_time, genesis_eth1_data, genesis_validators, genesis_balances, config)
def sample_beacon_block_body_params(sample_signature, sample_eth1_data_params): return { "randao_reveal": sample_signature, "eth1_data": Eth1Data.create(**sample_eth1_data_params), "graffiti": ZERO_HASH32, "proposer_slashings": (), "attester_slashings": (), "attestations": (), "deposits": (), "voluntary_exits": (), }
def initialize_beacon_state_from_eth1(*, eth1_block_hash: Hash32, eth1_timestamp: Timestamp, deposits: Sequence[Deposit], config: Eth2Config) -> BeaconState: fork = Fork.create( previous_version=config.GENESIS_FORK_VERSION, current_version=config.GENESIS_FORK_VERSION, epoch=GENESIS_EPOCH, ) state = BeaconState.create( genesis_time=_genesis_time_from_eth1_timestamp(eth1_timestamp, config.GENESIS_DELAY), fork=fork, eth1_data=Eth1Data.create(block_hash=eth1_block_hash, deposit_count=len(deposits)), latest_block_header=BeaconBlockHeader.create( body_root=BeaconBlockBody.create().hash_tree_root), block_roots=(ZERO_ROOT, ) * config.SLOTS_PER_HISTORICAL_ROOT, state_roots=(ZERO_HASH32, ) * config.SLOTS_PER_HISTORICAL_ROOT, randao_mixes=(eth1_block_hash, ) * config.EPOCHS_PER_HISTORICAL_VECTOR, slashings=(Gwei(0), ) * config.EPOCHS_PER_SLASHINGS_VECTOR, config=config, ) # Process genesis deposits for index, deposit in enumerate(deposits): deposit_data_list = tuple(deposit.data for deposit in deposits[:index + 1]) deposit_root = ssz.get_hash_tree_root( deposit_data_list, ssz.List(DepositData, 2**DEPOSIT_CONTRACT_TREE_DEPTH)) state = state.transform(("eth1_data", "deposit_root"), deposit_root) state = process_deposit(state=state, deposit=deposit, config=config) # Process genesis activations for validator_index in range(len(state.validators)): validator_index = ValidatorIndex(validator_index) balance = state.balances[validator_index] effective_balance = calculate_effective_balance(balance, config) state = state.transform( ("validators", validator_index, "effective_balance"), effective_balance) if effective_balance == config.MAX_EFFECTIVE_BALANCE: activated_validator = activate_validator( state.validators[validator_index], GENESIS_EPOCH) state = state.transform(("validators", validator_index), activated_validator) return state.set("genesis_validators_root", state.validators.hash_tree_root)
def _get_eth1_data( self, distance: BlockNumber, eth1_voting_period_start_timestamp: Timestamp) -> Eth1Data: """ Return `Eth1Data` at `distance` relative to the eth1 block earlier and closest to the timestamp `eth1_voting_period_start_timestamp`. Ref: https://github.com/ethereum/eth2.0-specs/blob/61f2a0662ebcfb4c097360cc1835c5f01872705c/specs/validator/0_beacon-chain-validator.md#eth1-data # noqa: E501 First, we find the `eth1_block` whose timestamp is the largest timestamp which is smaller than `eth1_voting_period_start_timestamp`. Then, find the block `target_block` at number `eth1_block.number - distance`. Therefore, we can return `Eth1Data` according to the information of this block. """ eth1_voting_period_start_block_number = self._get_closest_eth1_voting_period_start_block( eth1_voting_period_start_timestamp) target_block_number = BlockNumber( eth1_voting_period_start_block_number - distance) if target_block_number < 0: raise Eth1MonitorValidationError( f"`distance` is larger than `eth1_voting_period_start_block_number`: " f"`distance`={distance}, ", f"eth1_voting_period_start_block_number={eth1_voting_period_start_block_number}", ) try: block = self._eth1_data_provider.get_block(target_block_number) except BlockNotFound: raise Eth1MonitorValidationError( f"Block does not exist for block number={target_block_number}") block_hash = block.block_hash # `Eth1Data.deposit_count`: get the `deposit_count` corresponding to the block. accumulated_deposit_count = self._get_accumulated_deposit_count( target_block_number) if accumulated_deposit_count == 0: raise Eth1MonitorValidationError( f"failed to make `Eth1Data`: `deposit_count = 0` at block #{target_block_number}" ) # Verify that the deposit data in db and the deposit data in contract match deposit_data_in_range = self._db.get_deposit_data_range( 0, accumulated_deposit_count) _, deposit_root = make_deposit_tree_and_root(deposit_data_in_range) contract_deposit_root = self._get_deposit_root_from_contract( target_block_number) if contract_deposit_root != deposit_root: raise DepositDataCorrupted( "deposit root built locally mismatches the one in the contract on chain: " f"contract_deposit_root={contract_deposit_root.hex()}, " f"deposit_root={deposit_root.hex()}") return Eth1Data.create( deposit_root=deposit_root, deposit_count=accumulated_deposit_count, block_hash=block_hash, )
def sample_beacon_state_params( config, genesis_slot, genesis_epoch, sample_fork_params, sample_eth1_data_params, sample_block_header_params, ): return { # Versioning "genesis_time": 0, "slot": genesis_slot + 100, "fork": Fork.create(**sample_fork_params), # History "latest_block_header": BeaconBlockHeader.create(**sample_block_header_params), "block_roots": (ZERO_HASH32, ) * config.SLOTS_PER_HISTORICAL_ROOT, "state_roots": (ZERO_HASH32, ) * config.SLOTS_PER_HISTORICAL_ROOT, "historical_roots": (), # Eth1 "eth1_data": Eth1Data.create(**sample_eth1_data_params), "eth1_data_votes": (), "eth1_deposit_index": 0, # Registry "validators": (), "balances": (), # Shuffling "randao_mixes": (ZERO_HASH32, ) * config.EPOCHS_PER_HISTORICAL_VECTOR, # Slashings "slashings": (0, ) * config.EPOCHS_PER_SLASHINGS_VECTOR, # Attestations "previous_epoch_attestations": (), "current_epoch_attestations": (), # Justification "justification_bits": (False, ) * JUSTIFICATION_BITS_LENGTH, "previous_justified_checkpoint": Checkpoint.create(epoch=0, root=b"\x99" * 32), "current_justified_checkpoint": Checkpoint.create(epoch=0, root=b"\x55" * 32), # Finality "finalized_checkpoint": Checkpoint.create(epoch=0, root=b"\x33" * 32), }
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
def test_get_genesis_beacon_state( validator_count, pubkeys, genesis_epoch, genesis_slot, max_committees_per_slot, slots_per_historical_root, epochs_per_slashings_vector, epochs_per_historical_vector, config, keymap, ): genesis_deposits, deposit_root = create_mock_deposits_and_root( pubkeys=pubkeys[:validator_count], keymap=keymap, config=config) genesis_eth1_data = Eth1Data.create( deposit_count=len(genesis_deposits), deposit_root=deposit_root, block_hash=ZERO_HASH32, ) eth1_timestamp = 10 eth1_block_hash = genesis_eth1_data.block_hash state = initialize_beacon_state_from_eth1( eth1_block_hash=eth1_block_hash, eth1_timestamp=eth1_timestamp, deposits=genesis_deposits, config=config, ) # Versioning assert state.slot == genesis_slot assert state.genesis_time == _genesis_time_from_eth1_timestamp( eth1_timestamp) assert state.fork == Fork.create() # History assert state.latest_block_header == BeaconBlockHeader.create( body_root=BeaconBlockBody.create().hash_tree_root) assert len(state.block_roots) == slots_per_historical_root assert tuple( state.block_roots) == (ZERO_HASH32, ) * slots_per_historical_root assert len(state.state_roots) == slots_per_historical_root assert tuple( state.block_roots) == (ZERO_HASH32, ) * slots_per_historical_root assert len(state.historical_roots) == 0 # Ethereum 1.0 chain data assert state.eth1_data == genesis_eth1_data assert len(state.eth1_data_votes) == 0 assert state.eth1_deposit_index == len(genesis_deposits) # Validator registry assert len(state.validators) == validator_count assert len(state.balances) == validator_count # Shuffling assert len(state.randao_mixes) == epochs_per_historical_vector assert (tuple(state.randao_mixes) == (eth1_block_hash, ) * epochs_per_historical_vector) # Slashings assert len(state.slashings) == epochs_per_slashings_vector assert tuple(state.slashings) == (Gwei(0), ) * epochs_per_slashings_vector # Attestations assert len(state.previous_epoch_attestations) == 0 assert len(state.current_epoch_attestations) == 0 # Justification assert state.previous_justified_checkpoint.epoch == genesis_epoch assert state.previous_justified_checkpoint.root == ZERO_HASH32 assert state.current_justified_checkpoint.epoch == genesis_epoch assert state.current_justified_checkpoint.root == ZERO_HASH32 assert state.justification_bits == (False, ) * JUSTIFICATION_BITS_LENGTH # Finalization assert state.finalized_checkpoint.epoch == genesis_epoch assert state.finalized_checkpoint.root == ZERO_HASH32 for i in range(len(genesis_deposits)): assert state.validators[i].is_active(genesis_epoch)
def _expand_eth1_votes(args): block_hash, vote_count = args return (Eth1Data.create(block_hash=block_hash), ) * vote_count
def _mini_stf(state: BeaconState, block: Optional[BeaconBlock], config: Eth2Config) -> BeaconState: """ A simplified state transition for testing state storage. - updates ``state_roots`` with the previous slot's state root - updates ``block_roots`` with the previous slot's block root - updates ``randao_mixes`` with an arbitrary mix at the current epoch - creates a new ``latest_block_header`` and adds it to the state - fills in the rest of the attributes with arbitrary values """ current_slot = state.slot + 1 current_epoch = current_slot // config.SLOTS_PER_EPOCH if block: latest_block_header = block.header else: latest_block_header = state.latest_block_header # state changes that depend on the previous state for retrieval randao_mix = Root(Hash32(current_slot.to_bytes(32, byteorder="little"))) state = (state.transform( ("state_roots", state.slot % config.SLOTS_PER_HISTORICAL_ROOT), state.hash_tree_root, ).transform( ("block_roots", state.slot % config.SLOTS_PER_HISTORICAL_ROOT), state.latest_block_header.hash_tree_root, ).transform( ("randao_mixes", current_epoch % config.EPOCHS_PER_HISTORICAL_VECTOR), randao_mix, ).mset("slot", current_slot, "latest_block_header", latest_block_header)) # state changes that do not depend on the previous state for retrieval new_validators = [ Validator.create(pubkey=BLSPubkey(n.to_bytes(48, byteorder="little"))) for n in range(current_slot, current_slot + 20) ] new_eth1_data_votes = [ Eth1Data.create( deposit_root=Root(Hash32(n.to_bytes(32, byteorder="little")))) for n in range(current_slot, current_slot + 7) ] new_previous_epoch_attestations = [ PendingAttestation.create(proposer_index=ValidatorIndex(n)) for n in range(current_slot, current_slot + 5) ] new_current_epoch_attestations = [ PendingAttestation.create(proposer_index=ValidatorIndex(n)) for n in range(current_slot + 5, current_slot + 10) ] state = state.mset( "validators", new_validators, "balances", (32, ) * len(new_validators), "eth1_data_votes", new_eth1_data_votes, "eth1_data", new_eth1_data_votes[0], "previous_epoch_attestations", new_previous_epoch_attestations, "current_epoch_attestations", new_current_epoch_attestations, "previous_justified_checkpoint", Checkpoint.create(epoch=Epoch(current_slot + 42)), "current_justified_checkpoint", Checkpoint.create(epoch=Epoch(current_slot + 43)), "finalized_checkpoint", Checkpoint.create(epoch=Epoch(current_slot + 44)), ) return state
def test_defaults(sample_eth1_data_params): eth1_data = Eth1Data.create(**sample_eth1_data_params) assert eth1_data.deposit_root == sample_eth1_data_params["deposit_root"] assert eth1_data.block_hash == sample_eth1_data_params["block_hash"]