def test_get_beacon_proposer_index(monkeypatch, num_validators, epoch_length, committee, slot, success, sample_state, target_committee_size, shard_count): from eth2.beacon import helpers def mock_get_crosslink_committees_at_slot(state, slot, epoch_length, target_committee_size, shard_count): return (( committee, 1, ), ) monkeypatch.setattr(helpers, 'get_crosslink_committees_at_slot', mock_get_crosslink_committees_at_slot) if success: proposer_index = get_beacon_proposer_index( sample_state, slot, epoch_length, target_committee_size, shard_count, ) assert proposer_index == committee[slot % len(committee)] else: with pytest.raises(ValidationError): get_beacon_proposer_index( sample_state, slot, epoch_length, target_committee_size, shard_count, )
def create_mock_block( state: BeaconState, config: BeaconConfig, block_class: Type[BaseBeaconBlock], parent_block: BaseBeaconBlock, keymap: Dict[BLSPubkey, int], slot: SlotNumber = None, attestations: Sequence[Attestation] = () ) -> BaseBeaconBlock: """ Create a mocking block with the given block parameters and ``keymap``. """ proposer_index = get_beacon_proposer_index( state.copy(slot=slot, ), slot, config.EPOCH_LENGTH, config.TARGET_COMMITTEE_SIZE, config.SHARD_COUNT, ) proposer_pubkey = state.validator_registry[proposer_index].pubkey proposer_privkey = keymap[proposer_pubkey] block = create_block_on_state( state, config, block_class, parent_block, slot, validator_index=proposer_index, privkey=proposer_privkey, attestations=attestations, ) return block
def validate_serenity_proposer_signature( state: BeaconState, block: BaseBeaconBlock, beacon_chain_shard_number: ShardNumber, epoch_length: int) -> None: block_without_signature_root = block.block_without_signature_root # TODO: Replace this root with tree hash root proposal_root = ProposalSignedData( state.slot, beacon_chain_shard_number, block_without_signature_root, ).root # Get the public key of proposer beacon_proposer_index = get_beacon_proposer_index(state, state.slot, epoch_length) proposer_pubkey = state.validator_registry[beacon_proposer_index].pubkey is_valid_signature = bls.verify( pubkey=proposer_pubkey, message=proposal_root, signature=block.signature, domain=get_domain(state.fork_data, state.slot, SignatureDomain.DOMAIN_PROPOSAL), ) if not is_valid_signature: raise ValidationError("Invalid Proposer Signature on block")
def test_settle_penality_to_validator_and_whistleblower( monkeypatch, num_validators, committee, ten_validators_state, latest_penalized_exit_length, whistleblower_reward_quotient, epoch_length, max_deposit, target_committee_size, shard_count): from eth2.beacon import helpers def mock_get_crosslink_committees_at_slot(state, slot, epoch_length, target_committee_size, shard_count): return (( committee, 1, ), ) monkeypatch.setattr(helpers, 'get_crosslink_committees_at_slot', mock_get_crosslink_committees_at_slot) state = ten_validators_state validator_index = 5 whistleblower_index = get_beacon_proposer_index( state, state.slot, epoch_length, target_committee_size, shard_count, ) effective_balance = max_deposit * GWEI_PER_ETH # Check the initial balance assert (state.validator_balances[validator_index] == state.validator_balances[whistleblower_index] == effective_balance) state = _settle_penality_to_validator_and_whistleblower( state=state, validator_index=validator_index, latest_penalized_exit_length=latest_penalized_exit_length, whistleblower_reward_quotient=whistleblower_reward_quotient, epoch_length=epoch_length, max_deposit=max_deposit, target_committee_size=target_committee_size, shard_count=shard_count, ) # Check `state.latest_penalized_balances` latest_penalized_balances_list = list(state.latest_penalized_balances) last_penalized_epoch = (state.slot // epoch_length) % latest_penalized_exit_length latest_penalized_balances_list[ last_penalized_epoch] = max_deposit * GWEI_PER_ETH latest_penalized_balances = tuple(latest_penalized_balances_list) assert state.latest_penalized_balances == latest_penalized_balances # Check penality and reward whistleblower_reward = (effective_balance // whistleblower_reward_quotient) whistleblower_balance = state.validator_balances[whistleblower_index] validator_balance = state.validator_balances[validator_index] balance_difference = whistleblower_balance - validator_balance assert balance_difference == whistleblower_reward * 2
def create_block_on_state(state: BeaconState, config: BeaconConfig, block_class: BaseBeaconBlock, parent_block: BaseBeaconBlock, slot: SlotNumber, validator_index: int, privkey: int, attestations: Sequence[Attestation]): """ Create a beacon block with the given parameters. """ # Check proposer beacon_proposer_index = get_beacon_proposer_index( state.copy(slot=slot, ), slot, config.EPOCH_LENGTH, config.TARGET_COMMITTEE_SIZE, config.SHARD_COUNT, ) if validator_index != beacon_proposer_index: raise ProposerIndexError # Prepare block: slot and parent_root block = block_class.from_parent( parent_block=parent_block, block_params=FromBlockParams(slot=slot), ) # TODO: Add more operations randao_reveal = ZERO_HASH32 eth1_data = Eth1Data.create_empty_data() body = BeaconBlockBody.create_empty_body().copy( attestations=attestations, ) block = block.copy( randao_reveal=randao_reveal, eth1_data=eth1_data, body=body, ) # Sign empty_signature_block_root = block.block_without_signature_root proposal_root = ProposalSignedData( slot, config.BEACON_CHAIN_SHARD_NUMBER, empty_signature_block_root, ).root domain = get_domain( state.fork, slot, SignatureDomain.DOMAIN_PROPOSAL, ) block = block.copy(signature=bls.sign( message=proposal_root, privkey=privkey, domain=domain, ), ) return block
def _settle_penality_to_validator_and_whistleblower( *, state: BeaconState, validator_index: ValidatorIndex, latest_penalized_exit_length: int, whistleblower_reward_quotient: int, epoch_length: int, max_deposit: Ether) -> BeaconState: """ Apply penality/reward to validator and whistleblower and update the meta data More intuitive pseudo-code: current_epoch_penalization_index = (state.slot // EPOCH_LENGTH) % LATEST_PENALIZED_EXIT_LENGTH state.latest_penalized_exit_balances[current_epoch_penalization_index] += ( get_effective_balance(state, index) ) whistleblower_index = get_beacon_proposer_index(state, state.slot) whistleblower_reward = get_effective_balance(state, index) // WHISTLEBLOWER_REWARD_QUOTIENT state.validator_balances[whistleblower_index] += whistleblower_reward state.validator_balances[index] -= whistleblower_reward validator.penalized_slot = state.slot """ # Update `state.latest_penalized_exit_balances` current_epoch_penalization_index = ( state.slot // epoch_length) % latest_penalized_exit_length effective_balance = get_effective_balance( state.validator_balances, validator_index, max_deposit, ) penalized_exit_balance = ( state.latest_penalized_exit_balances[current_epoch_penalization_index] + effective_balance) latest_penalized_exit_balances = update_tuple_item( tuple_data=state.latest_penalized_exit_balances, index=current_epoch_penalization_index, new_value=penalized_exit_balance, ) state = state.copy( latest_penalized_exit_balances=latest_penalized_exit_balances, ) # Update whistleblower's balance whistleblower_reward = (effective_balance // whistleblower_reward_quotient) whistleblower_index = get_beacon_proposer_index(state, state.slot, epoch_length) state = state.update_validator_balance( whistleblower_index, state.validator_balances[whistleblower_index] + whistleblower_reward, ) # Update validator's balance and `penalized_slot` field validator = state.validator_registry[validator_index] validator = validator.copy(penalized_slot=state.slot, ) state = state.update_validator( validator_index, validator, state.validator_balances[validator_index] - whistleblower_reward, ) return state
def test_get_beacon_proposer_index( monkeypatch, num_validators, epoch_length, committee, slot, success, sample_state): from eth2.beacon import helpers def mock_get_shard_committees_at_slot(state, slot, epoch_length): return ( ShardCommittee( shard=1, committee=committee, total_validator_count=num_validators, ), ) monkeypatch.setattr( helpers, 'get_shard_committees_at_slot', mock_get_shard_committees_at_slot ) if success: proposer_index = get_beacon_proposer_index( sample_state, slot, epoch_length ) assert proposer_index == committee[slot % len(committee)] else: with pytest.raises(ValidationError): get_beacon_proposer_index( sample_state, slot, epoch_length )
def validate_proposer_index(state: BeaconState, config: BeaconConfig, slot: SlotNumber, validator_index: ValidatorIndex): beacon_proposer_index = get_beacon_proposer_index( state.copy(slot=slot, ), slot, config.GENESIS_EPOCH, config.EPOCH_LENGTH, config.TARGET_COMMITTEE_SIZE, config.SHARD_COUNT, ) if validator_index != beacon_proposer_index: raise ProposerIndexError
def create_mock_block( *, state: BeaconState, config: BeaconConfig, state_machine: BaseBeaconStateMachine, block_class: Type[BaseBeaconBlock], parent_block: BaseBeaconBlock, keymap: Dict[BLSPubkey, int], slot: SlotNumber = None, attestations: Sequence[Attestation] = () ) -> BaseBeaconBlock: """ Create a mocking block with the given block parameters and ``keymap``. Note that it doesn't return the correct ``state_root``. """ proposer_index = get_beacon_proposer_index( state.copy(slot=slot, ), slot, config.GENESIS_EPOCH, config.EPOCH_LENGTH, config.TARGET_COMMITTEE_SIZE, config.SHARD_COUNT, ) proposer_pubkey = state.validator_registry[proposer_index].pubkey proposer_privkey = keymap[proposer_pubkey] result_block = create_block_on_state( state=state, config=config, state_machine=state_machine, block_class=block_class, parent_block=parent_block, slot=slot, validator_index=proposer_index, privkey=proposer_privkey, attestations=attestations, check_proposer_index=False, ) return result_block
def test_demo(base_db, sample_beacon_block_params, genesis_state, fixture_sm_class, config, privkeys, pubkeys): chaindb = BeaconChainDB(base_db) state = genesis_state block = SerenityBeaconBlock(**sample_beacon_block_params).copy( slot=state.slot + 2, state_root=state.root, ) # Sign block beacon_proposer_index = get_beacon_proposer_index( state, block.slot, config.EPOCH_LENGTH, ) index_in_privkeys = pubkeys.index( state.validator_registry[beacon_proposer_index].pubkey) beacon_proposer_privkey = privkeys[index_in_privkeys] empty_signature_block_root = block.block_without_signature_root proposal_root = ProposalSignedData( block.slot, config.BEACON_CHAIN_SHARD_NUMBER, empty_signature_block_root, ).root block = block.copy(signature=bls.sign( message=proposal_root, privkey=beacon_proposer_privkey, domain=SignatureDomain.DOMAIN_PROPOSAL, ), ) # Store in chaindb chaindb.persist_block(block, SerenityBeaconBlock) chaindb.persist_state(state) # Get state machine instance sm = fixture_sm_class(chaindb, block.root, SerenityBeaconBlock) result_state, _ = sm.import_block(block) assert state.slot == 0 assert result_state.slot == block.slot assert isinstance(sm.block, SerenityBeaconBlock)
def validate_proposer_signature(state: BeaconState, block: BaseBeaconBlock, beacon_chain_shard_number: ShardNumber, epoch_length: int, target_committee_size: int, shard_count: int) -> None: block_without_signature_root = block.block_without_signature_root # TODO: Replace this root with tree hash root proposal_root = ProposalSignedData( state.slot, beacon_chain_shard_number, block_without_signature_root, ).root # Get the public key of proposer beacon_proposer_index = get_beacon_proposer_index( state, state.slot, epoch_length, target_committee_size, shard_count, ) proposer_pubkey = state.validator_registry[beacon_proposer_index].pubkey domain = get_domain(state.fork, state.slot, SignatureDomain.DOMAIN_PROPOSAL) is_valid_signature = bls.verify( pubkey=proposer_pubkey, message=proposal_root, signature=block.signature, domain=domain, ) if not is_valid_signature: raise ValidationError( f"Invalid Proposer Signature on block, beacon_proposer_index={beacon_proposer_index}, " f"pubkey={proposer_pubkey}, message={proposal_root}," f"block.signature={block.signature}, domain={domain}")
def test_per_slot_transition(base_db, genesis_block, genesis_state, fixture_sm_class, config, state_slot, keymap): chaindb = BeaconChainDB(base_db) chaindb.persist_block(genesis_block, SerenityBeaconBlock) chaindb.persist_state(genesis_state) state = genesis_state # Create a block block = create_mock_block( state=state, config=config, state_machine=fixture_sm_class( chaindb, genesis_block, ), block_class=SerenityBeaconBlock, parent_block=genesis_block, keymap=keymap, slot=state_slot, ) # Store in chaindb chaindb.persist_block(block, SerenityBeaconBlock) # Get state machine instance sm = fixture_sm_class( chaindb, block, ) # Get state transition instance st = sm.state_transition_class(sm.config) updated_state = st.per_slot_transition(state, block.parent_root) # Ensure that slot gets increased by 1 assert updated_state.slot == state.slot + 1 # Validator Registry # Tweaking the slot, so that we get the correct proposer index beacon_proposer_index = get_beacon_proposer_index( state, state.slot + 1, st.config.GENESIS_EPOCH, st.config.EPOCH_LENGTH, st.config.TARGET_COMMITTEE_SIZE, st.config.SHARD_COUNT, ) for validator_index, _ in enumerate(updated_state.validator_registry): if validator_index != beacon_proposer_index: # Validator Record shouldn't change if not proposer assert (updated_state.validator_registry[validator_index] == state.validator_registry[validator_index]) else: # randao layers of proposer's record should increase by 1 assert ( updated_state.validator_registry[validator_index].randao_layers == state.validator_registry[validator_index].randao_layers + 1) # latest_randao_mixes assert ( updated_state.latest_randao_mixes[updated_state.slot % st.config.LATEST_RANDAO_MIXES_LENGTH] == state.latest_randao_mixes[(state.slot) % st.config.LATEST_RANDAO_MIXES_LENGTH]) # latest_block_roots latest_block_roots_index = (updated_state.slot - 1) % st.config.LATEST_BLOCK_ROOTS_LENGTH assert updated_state.latest_block_roots[ latest_block_roots_index] == block.parent_root # batched_block_roots if updated_state.slot % st.config.LATEST_BLOCK_ROOTS_LENGTH == 0: assert updated_state.batched_block_roots[-1] == get_merkle_root( updated_state.latest_block_roots) else: assert updated_state.batched_block_roots == state.batched_block_roots