def test_update_validator(sample_beacon_state_params, sample_validator_record_params, max_deposit): state = BeaconState(**sample_beacon_state_params).copy( validator_registry=[ mock_validator_record( pubkey, ) for pubkey in range(10) ], validator_balances=( max_deposit for _ in range(10) ) ) new_pubkey = 100 validator_index = 5 balance = 5566 validator = state.validator_registry[validator_index].copy( pubkey=new_pubkey, ) result_state = state.update_validator( validator_index=validator_index, validator=validator, balance=balance, ) assert result_state.validator_balances[validator_index] == balance assert result_state.validator_registry[validator_index].pubkey == new_pubkey assert state.validator_registry[validator_index].pubkey != new_pubkey
def add_pending_validator( state: BeaconState, validator: ValidatorRecord, deposit: Gwei, zero_balance_validator_ttl: int) -> Tuple[BeaconState, ValidatorIndex]: """ Add a validator to the existing minimum empty validator index or append to ``validator_registry``. """ # Check if there's empty validator index in `validator_registry` try: index = get_min_empty_validator_index( state.validator_registry, state.validator_balances, state.slot, zero_balance_validator_ttl, ) except MinEmptyValidatorIndexNotFound: index = None # Append to the validator_registry validator_registry = state.validator_registry + (validator, ) state = state.copy(validator_registry=validator_registry, validator_balances=state.validator_balances + (deposit, )) index = ValidatorIndex(len(state.validator_registry) - 1) else: # Use the empty validator index state = state.update_validator(index, validator, deposit) return state, index
def empty_beacon_state(): return BeaconState( slot=0, genesis_time=0, fork_data=ForkData( pre_fork_version=0, post_fork_version=0, fork_slot=0, ), validator_registry=(), validator_registry_latest_change_slot=10, validator_registry_exit_count=10, validator_registry_delta_chain_tip=b'\x55' * 32, latest_randao_mixes=(), latest_vdf_outputs=(), shard_committees_at_slots=(), persistent_committees=(), persistent_committee_reassignments=(), previous_justified_slot=0, justified_slot=0, justification_bitfield=0, finalized_slot=0, latest_crosslinks=(), latest_block_roots=(), latest_penalized_exit_balances=(), latest_attestations=(), batched_block_roots=(), processed_pow_receipt_root=b'\x55' * 32, candidate_pow_receipt_roots=(), )
def test_add_pending_validator(monkeypatch, sample_beacon_state_params, sample_validator_record_params, validator_registry_len, min_empty_validator_index_result, expected_index): from eth.beacon import deposit_helpers def mock_get_min_empty_validator_index(validators, validator_balances, current_slot, zero_balance_validator_ttl): if min_empty_validator_index_result is None: raise MinEmptyValidatorIndexNotFound() else: return min_empty_validator_index_result monkeypatch.setattr(deposit_helpers, 'get_min_empty_validator_index', mock_get_min_empty_validator_index) state = BeaconState(**sample_beacon_state_params).copy( validator_registry=[ ValidatorRecord(**sample_validator_record_params) for _ in range(validator_registry_len) ], validator_balances=[100 for _ in range(validator_registry_len)], ) validator = ValidatorRecord(**sample_validator_record_params) deposit = 5566 state, index = add_pending_validator( state, validator, deposit, zero_balance_validator_ttl=0, # it's for `get_min_empty_validator_index` ) assert index == expected_index assert state.validator_registry[index] == validator
def test_verify_slashable_vote_data( num_validators, param_mapper, should_succeed, needs_fork_data, privkeys, sample_beacon_state_params, activated_genesis_validators, genesis_balances, sample_slashable_vote_data_params, sample_fork_data_params, max_casper_votes): state = BeaconState(**sample_beacon_state_params).copy( validator_registry=activated_genesis_validators, validator_balances=genesis_balances, fork_data=ForkData(**sample_fork_data_params), ) # NOTE: we can do this before "correcting" the params as they # touch disjoint subsets of the provided params messages = _create_slashable_vote_data_messages( sample_slashable_vote_data_params) params = _correct_slashable_vote_data_params( num_validators, sample_slashable_vote_data_params, messages, privkeys, state.fork_data, ) if needs_fork_data: params = param_mapper(params, state.fork_data) else: params = param_mapper(params) _run_verify_slashable_vote(params, state, max_casper_votes, should_succeed)
def process_attestations(state: BeaconState, block: BaseBeaconBlock, config: BeaconConfig) -> BeaconState: """ Implements 'per-block-processing.operations.attestations' portion of Phase 0 spec: https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestations-1 Validate the ``attestations`` contained within the ``block`` in the context of ``state``. If any invalid, throw ``ValidationError``. Otherwise, append an ``PendingAttestationRecords`` for each to ``latest_attestations``. Return resulting ``state``. """ for attestation in block.body.attestations: validate_serenity_attestation( state, attestation, config.EPOCH_LENGTH, config.MIN_ATTESTATION_INCLUSION_DELAY, config.LATEST_BLOCK_ROOTS_LENGTH, ) # update_latest_attestations additional_pending_attestations = tuple( PendingAttestationRecord( data=attestation.data, participation_bitfield=attestation.participation_bitfield, custody_bitfield=attestation.custody_bitfield, slot_included=state.slot, ) for attestation in block.body.attestations ) state = state.copy( latest_attestations=state.latest_attestations + additional_pending_attestations, ) return state
def test_verify_slashable_vote_data_signature( num_validators, privkeys, sample_beacon_state_params, activated_genesis_validators, genesis_balances, sample_slashable_vote_data_params, sample_fork_data_params): state = BeaconState(**sample_beacon_state_params).copy( validator_registry=activated_genesis_validators, validator_balances=genesis_balances, fork_data=ForkData(**sample_fork_data_params), ) # NOTE: we can do this before "correcting" the params as they # touch disjoint subsets of the provided params messages = _create_slashable_vote_data_messages( sample_slashable_vote_data_params) valid_params = _correct_slashable_vote_data_params( num_validators, sample_slashable_vote_data_params, messages, privkeys, state.fork_data, ) valid_votes = SlashableVoteData(**valid_params) assert verify_slashable_vote_data_signature(state, valid_votes) invalid_params = _corrupt_signature(valid_params, state.fork_data) invalid_votes = SlashableVoteData(**invalid_params) assert not verify_slashable_vote_data_signature(state, invalid_votes)
def process_deposit(*, state: BeaconState, pubkey: BLSPubkey, deposit: Gwei, proof_of_possession: BLSSignature, withdrawal_credentials: Hash32, randao_commitment: Hash32, custody_commitment: Hash32, zero_balance_validator_ttl: int) -> Tuple[BeaconState, ValidatorIndex]: """ Process a deposit from Ethereum 1.0. """ validate_proof_of_possession( state, pubkey, proof_of_possession, withdrawal_credentials, randao_commitment, custody_commitment, ) validator_pubkeys = tuple(v.pubkey for v in state.validator_registry) if pubkey not in validator_pubkeys: validator = ValidatorRecord.get_pending_validator( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, latest_status_change_slot=state.slot, custody_commitment=custody_commitment, ) state, index = add_pending_validator( state, validator, deposit, zero_balance_validator_ttl, ) else: # Top-up - increase balance by deposit index = ValidatorIndex(validator_pubkeys.index(pubkey)) validator = state.validator_registry[index] if validator.withdrawal_credentials != withdrawal_credentials: raise ValidationError( "`withdrawal_credentials` are incorrect:\n" "\texpected: %s, found: %s" % ( validator.withdrawal_credentials, validator.withdrawal_credentials, ) ) # Update validator's balance and state state = state.update_validator( validator_index=index, validator=validator, balance=state.validator_balances[index] + deposit, ) return state, index
def per_slot_transition(self, state: BeaconState, previous_block_root: Hash32) -> BeaconState: # TODO state = state.copy( slot=state.slot + 1 ) return state
def test_update_validator(sample_beacon_state_params, sample_validator_record_params, max_deposit): state = BeaconState(**sample_beacon_state_params).copy(validator_registry=[ mock_validator_record( pubkey, max_deposit, ) for pubkey in range(10) ]) new_pubkey = 100 validator_index = 5 validator = state.validator_registry[validator_index].copy( pubkey=new_pubkey, ) result_state = state.update_validator(validator_index=validator_index, validator=validator) assert result_state.validator_registry[ validator_index].pubkey == new_pubkey assert state.validator_registry[validator_index].pubkey != new_pubkey
def test_validate_proposer_signature( proposer_privkey, proposer_pubkey, is_valid_signature, sample_beacon_block_params, sample_beacon_state_params, sample_shard_committee_params, beacon_chain_shard_number, epoch_length): state = BeaconState(**sample_beacon_state_params).copy( validator_registry=[ mock_validator_record(proposer_pubkey) for _ in range(10) ], shard_committees_at_slots=get_sample_shard_committees_at_slots( num_slot=128, num_shard_committee_per_slot=10, sample_shard_committee_params=sample_shard_committee_params, ), ) default_block = BaseBeaconBlock(**sample_beacon_block_params) empty_signature_block_root = default_block.block_without_signature_root proposal_root = ProposalSignedData( state.slot, beacon_chain_shard_number, empty_signature_block_root, ).root proposed_block = BaseBeaconBlock(**sample_beacon_block_params).copy( signature=bls.sign( message=proposal_root, privkey=proposer_privkey, domain=SignatureDomain.DOMAIN_PROPOSAL, ), ) if is_valid_signature: validate_proposer_signature( state, proposed_block, beacon_chain_shard_number, epoch_length, ) else: with pytest.raises(ValidationError): validate_proposer_signature( state, proposed_block, beacon_chain_shard_number, epoch_length, )
def test_demo(base_db, sample_beacon_block_params, sample_beacon_state_params, fixture_sm_class): chaindb = BeaconChainDB(base_db) block = BaseBeaconBlock(**sample_beacon_block_params) state = BeaconState(**sample_beacon_state_params) sm = fixture_sm_class(chaindb, block, state) result_state, result_block = sm.import_block(block) assert state.slot == 0 assert result_state.slot == sm.config.ZERO_BALANCE_VALIDATOR_TTL
def add_pending_validator(state: BeaconState, validator: ValidatorRecord, amount: Gwei) -> BeaconState: """ Add a validator to ``state``. """ state = state.copy( validator_registry=state.validator_registry + (validator, ), validator_balances=state.validator_balances + (amount, ), ) return state
def process_deposit(*, state: BeaconState, pubkey: BLSPubkey, amount: Gwei, proof_of_possession: BLSSignature, withdrawal_credentials: Hash32, randao_commitment: Hash32, custody_commitment: Hash32) -> BeaconState: """ Process a deposit from Ethereum 1.0. """ validate_proof_of_possession( state=state, pubkey=pubkey, proof_of_possession=proof_of_possession, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, custody_commitment=custody_commitment, ) validator_pubkeys = tuple(v.pubkey for v in state.validator_registry) if pubkey not in validator_pubkeys: validator = ValidatorRecord.create_pending_validator( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, custody_commitment=custody_commitment, ) # Note: In phase 2 registry indices that has been withdrawn for a long time # will be recycled. state = add_pending_validator( state, validator, amount, ) else: # Top-up - increase balance by deposit index = ValidatorIndex(validator_pubkeys.index(pubkey)) validator = state.validator_registry[index] if validator.withdrawal_credentials != withdrawal_credentials: raise ValidationError("`withdrawal_credentials` are incorrect:\n" "\texpected: %s, found: %s" % ( validator.withdrawal_credentials, validator.withdrawal_credentials, )) # Update validator's balance and state state = state.update_validator_balance( validator_index=index, balance=state.validator_balances[index] + amount, ) return state
def test_validate_proof_of_possession(sample_beacon_state_params, pubkeys, privkeys, expected): state = BeaconState(**sample_beacon_state_params) privkey = privkeys[0] pubkey = pubkeys[0] withdrawal_credentials = b'\x34' * 32 custody_commitment = b'\x12' * 32 randao_commitment = b'\x56' * 32 domain = get_domain( state.fork_data, state.slot, SignatureDomain.DOMAIN_DEPOSIT, ) deposit_input = make_deposit_input( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, custody_commitment=custody_commitment, ) if expected is True: proof_of_possession = sign_proof_of_possession(deposit_input, privkey, domain) validate_proof_of_possession( state=state, pubkey=pubkey, proof_of_possession=proof_of_possession, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, custody_commitment=custody_commitment, ) else: proof_of_possession = b'\x11' * 32 with pytest.raises(ValidationError): validate_proof_of_possession( state=state, pubkey=pubkey, proof_of_possession=proof_of_possession, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, custody_commitment=custody_commitment, )
def test_add_pending_validator(sample_beacon_state_params, sample_validator_record_params): validator_registry_len = 2 state = BeaconState(**sample_beacon_state_params).copy( validator_registry=[ ValidatorRecord(**sample_validator_record_params) for _ in range(validator_registry_len) ], validator_balances=(100, ) * validator_registry_len, ) validator = ValidatorRecord(**sample_validator_record_params) amount = 5566 state = add_pending_validator( state, validator, amount, ) assert state.validator_registry[-1] == validator
def test_verify_slashable_vote_data(param_mapper, should_succeed, privkeys, sample_beacon_state_params, genesis_validators, sample_slashable_vote_data_params, max_casper_votes): sample_beacon_state_params["validator_registry"] = genesis_validators state = BeaconState(**sample_beacon_state_params) # NOTE: we can do this before "correcting" the params as they # touch disjoint subsets of the provided params messages = _create_slashable_vote_data_messages( sample_slashable_vote_data_params) params = _correct_slashable_vote_data_params( sample_slashable_vote_data_params, genesis_validators, messages, privkeys, ) params = param_mapper(params) _run_verify_slashable_vote(params, state, max_casper_votes, should_succeed)
def genesis_state(sample_beacon_state_params, genesis_validators, epoch_length, target_committee_size, initial_slot_number, shard_count, latest_block_roots_length): initial_shuffling = get_new_shuffling( seed=ZERO_HASH32, validators=genesis_validators, crosslinking_start_shard=0, epoch_length=epoch_length, target_committee_size=target_committee_size, shard_count=shard_count) return BeaconState(**sample_beacon_state_params).copy( validator_registry=genesis_validators, shard_committees_at_slots=initial_shuffling + initial_shuffling, latest_block_roots=tuple(ZERO_HASH32 for _ in range(latest_block_roots_length)), latest_crosslinks=tuple( CrosslinkRecord( slot=initial_slot_number, shard_block_root=ZERO_HASH32, ) for _ in range(shard_count)))
def test_verify_slashable_vote_data_signature( privkeys, sample_beacon_state_params, genesis_validators, sample_slashable_vote_data_params): sample_beacon_state_params["validator_registry"] = genesis_validators state = BeaconState(**sample_beacon_state_params) # NOTE: we can do this before "correcting" the params as they # touch disjoint subsets of the provided params messages = _create_slashable_vote_data_messages( sample_slashable_vote_data_params) valid_params = _correct_slashable_vote_data_params( sample_slashable_vote_data_params, genesis_validators, messages, privkeys, ) valid_votes = SlashableVoteData(**valid_params) assert verify_slashable_vote_data_signature(state, valid_votes) invalid_params = _corrupt_signature(valid_params) invalid_votes = SlashableVoteData(**invalid_params) assert not verify_slashable_vote_data_signature(state, invalid_votes)
def test_process_deposit(sample_beacon_state_params, privkeys, pubkeys, max_deposit): state = BeaconState(**sample_beacon_state_params).copy( slot=1, validator_registry=(), ) privkey_1 = privkeys[0] pubkey_1 = pubkeys[0] amount = max_deposit * GWEI_PER_ETH withdrawal_credentials = b'\x34' * 32 custody_commitment = b'\x11' * 32 randao_commitment = b'\x56' * 32 deposit_input = make_deposit_input( pubkey=pubkey_1, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, custody_commitment=custody_commitment, ) proof_of_possession = sign_proof_of_possession( deposit_input, privkey_1, state.fork_data, state.slot, ) # Add the first validator result_state = process_deposit( state=state, pubkey=pubkey_1, amount=amount, proof_of_possession=proof_of_possession, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, custody_commitment=custody_commitment, ) assert len(result_state.validator_registry) == 1 index = 0 assert result_state.validator_registry[0].pubkey == pubkey_1 assert result_state.validator_registry[ index].withdrawal_credentials == withdrawal_credentials assert result_state.validator_registry[ index].randao_commitment == randao_commitment assert result_state.validator_balances[index] == amount # test immutable assert len(state.validator_registry) == 0 # Add the second validator privkey_2 = privkeys[1] pubkey_2 = pubkeys[1] deposit_input = make_deposit_input( pubkey=pubkey_2, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, custody_commitment=custody_commitment, ) proof_of_possession = sign_proof_of_possession( deposit_input, privkey_2, state.fork_data, state.slot, ) result_state = process_deposit( state=result_state, pubkey=pubkey_2, amount=amount, proof_of_possession=proof_of_possession, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, custody_commitment=custody_commitment, ) assert len(result_state.validator_registry) == 2 assert result_state.validator_registry[1].pubkey == pubkey_2
def test_validator_registry_and_balances_length(sample_beacon_state_params): # When len(BeaconState.validator_registry) != len(BeaconState.validtor_balances) with pytest.raises(ValueError): BeaconState(**sample_beacon_state_params).copy( validator_registry=tuple( mock_validator_record(pubkey) for pubkey in range(10)), )
def test_process_deposit(sample_beacon_state_params, zero_balance_validator_ttl, privkeys, pubkeys): state = BeaconState(**sample_beacon_state_params).copy( slot=zero_balance_validator_ttl + 1, validator_registry=(), ) privkey_1 = privkeys[0] pubkey_1 = pubkeys[0] deposit = 32 * denoms.gwei withdrawal_credentials = b'\x34' * 32 custody_commitment = b'\x11' * 32 randao_commitment = b'\x56' * 32 domain = get_domain( state.fork_data, state.slot, SignatureDomain.DOMAIN_DEPOSIT, ) deposit_input = make_deposit_input( pubkey=pubkey_1, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, custody_commitment=custody_commitment, ) proof_of_possession = sign_proof_of_possession(deposit_input, privkey_1, domain) # Add the first validator result_state, index = process_deposit( state=state, pubkey=pubkey_1, deposit=deposit, proof_of_possession=proof_of_possession, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, custody_commitment=custody_commitment, zero_balance_validator_ttl=zero_balance_validator_ttl, ) assert len(result_state.validator_registry) == 1 index = 0 assert result_state.validator_registry[0].pubkey == pubkey_1 assert result_state.validator_registry[ index].withdrawal_credentials == withdrawal_credentials assert result_state.validator_registry[ index].randao_commitment == randao_commitment assert result_state.validator_balances[index] == deposit # test immutable assert len(state.validator_registry) == 0 # Add the second validator privkey_2 = privkeys[1] pubkey_2 = pubkeys[1] deposit_input = make_deposit_input( pubkey=pubkey_2, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, custody_commitment=custody_commitment, ) proof_of_possession = sign_proof_of_possession(deposit_input, privkey_2, domain) result_state, index = process_deposit( state=result_state, pubkey=pubkey_2, deposit=deposit, proof_of_possession=proof_of_possession, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, custody_commitment=custody_commitment, zero_balance_validator_ttl=zero_balance_validator_ttl, ) assert len(result_state.validator_registry) == 2 assert result_state.validator_registry[1].pubkey == pubkey_2 # Force the first validator exited -> a empty slot in state.validator_registry. result_state = result_state.copy(validator_registry=( result_state.validator_registry[0].copy(latest_status_change_slot=0, ), result_state.validator_registry[1], ), validator_balances=(0, ) + result_state.validator_balances[1:]) # Add the third validator. # Should overwrite previously exited validator. privkey_3 = privkeys[2] pubkey_3 = pubkeys[2] deposit_input = make_deposit_input( pubkey=pubkey_3, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, custody_commitment=custody_commitment, ) proof_of_possession = sign_proof_of_possession(deposit_input, privkey_3, domain) result_state, index = process_deposit( state=result_state, pubkey=pubkey_3, deposit=deposit, proof_of_possession=proof_of_possession, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, custody_commitment=custody_commitment, zero_balance_validator_ttl=zero_balance_validator_ttl, ) # Overwrite the second validator. assert len(result_state.validator_registry) == 2 assert result_state.validator_registry[0].pubkey == pubkey_3
def sample_state(sample_beacon_state_params): return BeaconState(**sample_beacon_state_params)
def process_attestations(state: BeaconState, block: BaseBeaconBlock, config: BeaconConfig) -> BeaconState: # TODO # It's just for demo!!! state = state.copy(slot=config.ZERO_BALANCE_VALIDATOR_TTL, ) return state
def test_defaults(sample_beacon_state_params): state = BeaconState(**sample_beacon_state_params) assert state.validator_registry == sample_beacon_state_params['validator_registry'] assert state.validator_registry_latest_change_slot == sample_beacon_state_params['validator_registry_latest_change_slot'] # noqa: E501
def test_hash(sample_beacon_state_params): state = BeaconState(**sample_beacon_state_params) assert state.hash == blake(rlp.encode(state))
def test_hash(sample_beacon_state_params): state = BeaconState(**sample_beacon_state_params) assert state.root == hash_eth2(rlp.encode(state))