def test_is_active(sample_validator_record_params, activation_epoch,
                   exit_epoch, epoch, expected):
    validator_record_params = {
        **sample_validator_record_params,
        'activation_epoch': activation_epoch,
        'exit_epoch': exit_epoch,
    }
    validator = Validator(**validator_record_params)
    assert validator.is_active(epoch) == expected
Example #2
0
def _process_activation_eligibility_or_ejections(
        state: BeaconState, validator: Validator,
        config: Eth2Config) -> Validator:
    current_epoch = state.current_epoch(config.SLOTS_PER_EPOCH)

    if (validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
            and validator.effective_balance == config.MAX_EFFECTIVE_BALANCE):
        validator = validator.copy(activation_eligibility_epoch=current_epoch)

    if (validator.is_active(current_epoch)
            and validator.effective_balance <= config.EJECTION_BALANCE):
        validator = initiate_exit_for_validator(validator, state, config)

    return validator
Example #3
0
def test_get_active_validator_indices(sample_validator_record_params):
    current_epoch = 1
    # 3 validators are ACTIVE
    validators = tuple(
        Validator.create(**sample_validator_record_params).mset(
            "activation_epoch", 0, "exit_epoch", FAR_FUTURE_EPOCH
        )
        for i in range(3)
    )
    active_validator_indices = get_active_validator_indices(validators, current_epoch)
    assert len(active_validator_indices) == 3

    # activation_epoch > current_epoch
    two_active_vals = update_tuple_item(
        validators, 0, validators[0].set("activation_epoch", current_epoch + 1)
    )
    active_validator_indices = get_active_validator_indices(
        two_active_vals, current_epoch
    )
    assert len(active_validator_indices) == 2

    # current_epoch == exit_epoch
    one_active_val = update_tuple_item(
        two_active_vals, 1, validators[1].set("exit_epoch", current_epoch)
    )
    active_validator_indices = get_active_validator_indices(
        one_active_val, current_epoch
    )
    assert len(active_validator_indices) == 1
Example #4
0
def test_get_active_validator_indices(sample_validator_record_params):
    current_epoch = 1
    # 3 validators are ACTIVE
    validators = [
        Validator.create(**sample_validator_record_params).mset(
            "activation_epoch", 0, "exit_epoch", FAR_FUTURE_EPOCH)
        for i in range(3)
    ]
    active_validator_indices = get_active_validator_indices(
        validators, current_epoch)
    assert len(active_validator_indices) == 3

    validators[0] = validators[0].set(
        "activation_epoch",
        current_epoch + 1  # activation_epoch > current_epoch
    )
    active_validator_indices = get_active_validator_indices(
        validators, current_epoch)
    assert len(active_validator_indices) == 2

    validators[1] = validators[1].set(
        "exit_epoch",
        current_epoch  # current_epoch == exit_epoch
    )
    active_validator_indices = get_active_validator_indices(
        validators, current_epoch)
    assert len(active_validator_indices) == 1
Example #5
0
def test_get_active_validator_indices(sample_validator_record_params):
    current_epoch = 1
    # 3 validators are ACTIVE
    validators = [
        Validator(
            **sample_validator_record_params,
        ).copy(
            activation_epoch=0,
            exit_epoch=FAR_FUTURE_EPOCH,
        )
        for i in range(3)
    ]
    active_validator_indices = get_active_validator_indices(validators, current_epoch)
    assert len(active_validator_indices) == 3

    validators[0] = validators[0].copy(
        activation_epoch=current_epoch + 1,  # activation_epoch > current_epoch
    )
    active_validator_indices = get_active_validator_indices(validators, current_epoch)
    assert len(active_validator_indices) == 2

    validators[1] = validators[1].copy(
        exit_epoch=current_epoch,  # current_epoch == exit_epoch
    )
    active_validator_indices = get_active_validator_indices(validators, current_epoch)
    assert len(active_validator_indices) == 1
Example #6
0
def genesis_validators(eth2_config, sample_bls_key_pairs):
    return tuple(
        Validator.create(
            pubkey=public_key,
            effective_balance=eth2_config.MAX_EFFECTIVE_BALANCE,
            exit_epoch=FAR_FUTURE_EPOCH,
            withdrawable_epoch=FAR_FUTURE_EPOCH,
        ) for public_key in sample_bls_key_pairs)
def _set_validator_slashed(v: Validator, current_epoch: Epoch,
                           epochs_per_slashings_vector: int) -> Validator:
    return v.copy(
        slashed=True,
        withdrawable_epoch=max(
            v.withdrawable_epoch,
            Epoch(current_epoch + epochs_per_slashings_vector)),
    )
Example #8
0
def activate_validator(validator: Validator,
                       activation_epoch: Epoch) -> Validator:
    return validator.mset(
        "activation_eligibility_epoch",
        activation_epoch,
        "activation_epoch",
        activation_epoch,
    )
Example #9
0
def _update_validator_activation_epoch(state: BeaconState, config: Eth2Config,
                                       validator: Validator) -> Validator:
    return validator.set(
        "activation_epoch",
        compute_activation_exit_epoch(
            state.current_epoch(config.SLOTS_PER_EPOCH),
            config.MAX_SEED_LOOKAHEAD),
    )
Example #10
0
def process_deposit(state: BeaconState, deposit: Deposit, slots_per_epoch: int,
                    deposit_contract_tree_depth: int) -> BeaconState:
    """
    Process a deposit from Ethereum 1.0.
    """
    validate_deposit(state, deposit, deposit_contract_tree_depth)

    # Increment the next deposit index we are expecting. Note that this
    # needs to be done here because while the deposit contract will never
    # create an invalid Merkle branch, it may admit an invalid deposit
    # object, and we need to be able to skip over it
    state = state.copy(deposit_index=state.deposit_index + 1, )

    validator_pubkeys = tuple(v.pubkey for v in state.validator_registry)
    deposit_input = deposit.deposit_data.deposit_input
    pubkey = deposit_input.pubkey
    amount = deposit.deposit_data.amount
    withdrawal_credentials = deposit_input.withdrawal_credentials

    if pubkey not in validator_pubkeys:
        # Verify the proof of possession
        proof_is_valid = bls.verify(
            pubkey=pubkey,
            message_hash=deposit_input.signing_root,
            signature=deposit_input.signature,
            domain=get_domain(
                state.fork,
                state.current_epoch(slots_per_epoch),
                SignatureDomain.DOMAIN_DEPOSIT,
            ),
        )
        if not proof_is_valid:
            return state

        validator = Validator.create_pending_validator(
            pubkey=pubkey,
            withdrawal_credentials=withdrawal_credentials,
        )

        # 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]

        # Update validator's balance and state
        state = state.update_validator_balance(
            validator_index=index,
            balance=state.validator_balances[index] + amount,
        )

    return state
def test_is_active(sample_validator_record_params, activation_epoch,
                   exit_epoch, epoch, expected):
    validator_record_params = {
        **sample_validator_record_params,
        "activation_epoch": activation_epoch,
        "exit_epoch": exit_epoch,
    }
    validator = Validator.create(**validator_record_params)
    assert validator.is_active(epoch) == expected
Example #12
0
def _set_validator_slashed(v: Validator, current_epoch: Epoch,
                           epochs_per_slashings_vector: int) -> Validator:
    return v.mset(
        "slashed",
        True,
        "withdrawable_epoch",
        max(v.withdrawable_epoch,
            Epoch(current_epoch + epochs_per_slashings_vector)),
    )
def test_process_registry_updates(validator_count, genesis_state, config,
                                  slots_per_epoch):
    eligible_index = len(genesis_state.validators)
    activation_index = len(genesis_state.validators) + 1
    exiting_index = len(genesis_state.validators) - 1

    eligible_validator = Validator.create_pending_validator(
        pubkey=b"\x10" * 48,
        withdrawal_credentials=b"\x11" * 32,
        amount=Gwei(32 * GWEI_PER_ETH),
        config=config,
    )
    activating_validator = eligible_validator.set(
        "activation_eligibility_epoch",
        genesis_state.finalized_checkpoint.epoch)

    state = genesis_state.mset(
        "validators",
        genesis_state.validators[:exiting_index] +
        (genesis_state.validators[exiting_index].set(
            "effective_balance", config.EJECTION_BALANCE - 1), ) +
        (eligible_validator, activating_validator),
        "balances",
        genesis_state.balances +
        (config.MAX_EFFECTIVE_BALANCE, config.MAX_EFFECTIVE_BALANCE),
    )

    # handles activations
    post_state = process_registry_updates(state, config)

    # Check if the eligible_validator is eligible
    pre_eligible_validator = state.validators[eligible_index]
    post_eligible_validator = post_state.validators[eligible_index]
    assert pre_eligible_validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
    assert pre_eligible_validator.activation_epoch == FAR_FUTURE_EPOCH
    assert (post_eligible_validator.activation_eligibility_epoch ==
            state.current_epoch(slots_per_epoch) + 1)

    # Check if the activating_validator is activated
    pre_activation_validator = state.validators[activation_index]
    post_activation_validator = post_state.validators[activation_index]
    assert pre_activation_validator.activation_epoch == FAR_FUTURE_EPOCH
    assert post_activation_validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH
    activation_epoch = compute_activation_exit_epoch(
        state.current_epoch(config.SLOTS_PER_EPOCH), config.MAX_SEED_LOOKAHEAD)
    assert post_activation_validator.is_active(activation_epoch)

    # Check if the exiting_validator is exited
    pre_exiting_validator = state.validators[exiting_index]
    post_exiting_validator = post_state.validators[exiting_index]
    assert pre_exiting_validator.exit_epoch == FAR_FUTURE_EPOCH
    assert pre_exiting_validator.withdrawable_epoch == FAR_FUTURE_EPOCH
    assert state.validators[
        exiting_index].effective_balance <= config.EJECTION_BALANCE
    assert post_exiting_validator.exit_epoch != FAR_FUTURE_EPOCH
    assert post_exiting_validator.withdrawable_epoch != FAR_FUTURE_EPOCH
    assert post_exiting_validator.withdrawable_epoch > post_exiting_validator.exit_epoch
Example #14
0
def validate_proposer_slashing_is_slashable(state: BeaconState,
                                            proposer: Validator,
                                            slots_per_epoch: int) -> None:
    current_epoch = state.current_epoch(slots_per_epoch)
    is_slashable = proposer.is_slashable(current_epoch)
    if not is_slashable:
        raise ValidationError(
            f"Proposer {encode_hex(proposer.pubkey)} is not slashable in epoch {current_epoch}."
        )
Example #15
0
def _update_validator_activation_epoch(state: BeaconState, config: Eth2Config,
                                       validator: Validator) -> Validator:
    if validator.activation_epoch == FAR_FUTURE_EPOCH:
        return validator.copy(activation_epoch=compute_activation_exit_epoch(
            state.current_epoch(config.SLOTS_PER_EPOCH),
            config.ACTIVATION_EXIT_DELAY,
        ))
    else:
        return validator
Example #16
0
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=[
            Validator(**sample_validator_record_params)
            for _ in range(validator_registry_len)
        ],
        validator_balances=(100, ) * validator_registry_len,
    )
    validator = Validator(**sample_validator_record_params)
    amount = 5566
    state = add_pending_validator(
        state,
        validator,
        amount,
    )

    assert state.validator_registry[-1] == validator
Example #17
0
def _update_validator_activation_epoch(
    state: BeaconState, config: Eth2Config, validator: Validator
) -> Validator:
    if validator.activation_epoch == FAR_FUTURE_EPOCH:
        return validator.copy(
            activation_epoch=compute_activation_exit_epoch(
                state.current_epoch(config.SLOTS_PER_EPOCH), config.MAX_SEED_LOOKAHEAD
            )
        )
    else:
        return validator
Example #18
0
def process_deposit(state: BeaconState,
                    deposit: Deposit,
                    config: Eth2Config) -> BeaconState:
    """
    Process a deposit from Ethereum 1.0.
    """
    validate_deposit_proof(state, deposit, DEPOSIT_CONTRACT_TREE_DEPTH)

    # Increment the next deposit index we are expecting. Note that this
    # needs to be done here because while the deposit contract will never
    # create an invalid Merkle branch, it may admit an invalid deposit
    # object, and we need to be able to skip over it
    state = state.copy(
        eth1_deposit_index=state.eth1_deposit_index + 1,
    )

    pubkey = deposit.data.pubkey
    amount = deposit.data.amount
    validator_pubkeys = tuple(v.pubkey for v in state.validators)
    if pubkey not in validator_pubkeys:
        # Verify the deposit signature (proof of possession) for new validators.
        # Note: The deposit contract does not check signatures.
        # Note: Deposits are valid across forks, thus the deposit domain
        # is retrieved directly from `compute_domain`.
        is_valid_proof_of_possession = bls.verify(
            message_hash=deposit.data.signing_root,
            pubkey=pubkey,
            signature=deposit.data.signature,
            domain=compute_domain(
                SignatureDomain.DOMAIN_DEPOSIT,
            ),
        )
        if not is_valid_proof_of_possession:
            return state

        withdrawal_credentials = deposit.data.withdrawal_credentials
        validator = Validator.create_pending_validator(
            pubkey,
            withdrawal_credentials,
            amount,
            config,
        )

        return state.copy(
            validators=state.validators + (validator,),
            balances=state.balances + (amount, ),
        )
    else:
        index = ValidatorIndex(validator_pubkeys.index(pubkey))
        return increase_balance(
            state,
            index,
            amount,
        )
Example #19
0
def create_mock_validator(
    pubkey: BLSPubkey,
    config: Eth2Config,
    withdrawal_credentials: Hash32 = ZERO_HASH32,
    is_active: bool = True,
) -> Validator:
    validator = Validator.create_pending_validator(
        pubkey, withdrawal_credentials, config.MAX_EFFECTIVE_BALANCE, config)
    if is_active:
        return activate_validator(validator, config.GENESIS_EPOCH)
    else:
        return validator
Example #20
0
def mock_validator(pubkey: BLSPubkey,
                   config: Eth2Config,
                   withdrawal_credentials: Hash32 = ZERO_HASH32,
                   is_active: bool = True) -> Validator:
    return Validator(
        pubkey=pubkey,
        withdrawal_credentials=withdrawal_credentials,
        activation_epoch=config.GENESIS_EPOCH
        if is_active else FAR_FUTURE_EPOCH,
        exit_epoch=FAR_FUTURE_EPOCH,
        withdrawable_epoch=FAR_FUTURE_EPOCH,
        initiated_exit=False,
        slashed=False,
    )
def test_create_pending_validator():
    pubkey = 123
    withdrawal_credentials = b'\x11' * 32

    validator = Validator.create_pending_validator(
        pubkey=pubkey,
        withdrawal_credentials=withdrawal_credentials,
    )

    assert validator.pubkey == pubkey
    assert validator.withdrawal_credentials == withdrawal_credentials
    assert validator.activation_epoch == FAR_FUTURE_EPOCH
    assert validator.exit_epoch == FAR_FUTURE_EPOCH
    assert validator.initiated_exit is False
    assert validator.slashed is False
Example #22
0
def initiate_exit_for_validator(validator: Validator,
                                state: BeaconState,
                                config: Eth2Config) -> Validator:
    """
    Performs the mutations to ``validator`` used to initiate an exit.
    More convenient given our immutability patterns compared to ``initiate_validator_exit``.
    """
    if validator.exit_epoch != FAR_FUTURE_EPOCH:
        return validator

    churn_limit = get_churn_limit(state, config)
    exit_queue_epoch = _compute_exit_queue_epoch(state, churn_limit, config)

    return validator.copy(
        exit_epoch=exit_queue_epoch,
        withdrawable_epoch=exit_queue_epoch + config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY,
    )
Example #23
0
def test_process_registry_updates(validator_count, genesis_state, config,
                                  slots_per_epoch):
    activation_index = len(genesis_state.validators)
    exiting_index = len(genesis_state.validators) - 1

    activating_validator = Validator.create_pending_validator(
        pubkey=b'\x10' * 48,
        withdrawal_credentials=b'\x11' * 32,
        amount=Gwei(32 * GWEI_PER_ETH),
        config=config,
    )

    state = genesis_state.copy(
        validators=genesis_state.validators[:exiting_index] +
        (genesis_state.validators[exiting_index].copy(
            effective_balance=config.EJECTION_BALANCE - 1, ), ) +
        (activating_validator, ),
        balances=genesis_state.balances + (config.MAX_EFFECTIVE_BALANCE, ),
    )

    # handles activations
    post_state = process_registry_updates(state, config)

    # Check if the activating_validator is activated
    pre_activation_validator = state.validators[activation_index]
    post_activation_validator = post_state.validators[activation_index]
    assert pre_activation_validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
    assert pre_activation_validator.activation_epoch == FAR_FUTURE_EPOCH
    assert post_activation_validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH
    activation_epoch = compute_activation_exit_epoch(
        state.current_epoch(config.SLOTS_PER_EPOCH),
        config.ACTIVATION_EXIT_DELAY,
    )
    assert post_activation_validator.is_active(activation_epoch)
    # Check if the activating_validator is exited
    pre_exiting_validator = state.validators[exiting_index]
    post_exiting_validator = post_state.validators[exiting_index]
    assert pre_exiting_validator.exit_epoch == FAR_FUTURE_EPOCH
    assert pre_exiting_validator.withdrawable_epoch == FAR_FUTURE_EPOCH
    assert state.validators[
        exiting_index].effective_balance <= config.EJECTION_BALANCE
    assert post_exiting_validator.exit_epoch != FAR_FUTURE_EPOCH
    assert post_exiting_validator.withdrawable_epoch != FAR_FUTURE_EPOCH
    assert post_exiting_validator.withdrawable_epoch > post_exiting_validator.exit_epoch
def test_create_pending_validator(config):
    pubkey = b"\x12" * 48
    withdrawal_credentials = b"\x11" * 32

    effective_balance = 22 * GWEI_PER_ETH
    amount = Gwei(effective_balance + config.EFFECTIVE_BALANCE_INCREMENT // 2)
    validator = Validator.create_pending_validator(
        pubkey=pubkey,
        withdrawal_credentials=withdrawal_credentials,
        amount=amount,
        config=config,
    )

    assert validator.pubkey == pubkey
    assert validator.withdrawal_credentials == withdrawal_credentials
    assert validator.activation_epoch == FAR_FUTURE_EPOCH
    assert validator.exit_epoch == FAR_FUTURE_EPOCH
    assert validator.slashed is False
    assert validator.effective_balance == effective_balance
def test_defaults(sample_validator_record_params):
    validator = Validator.create(**sample_validator_record_params)
    assert validator.pubkey == sample_validator_record_params["pubkey"]
    assert (validator.withdrawal_credentials ==
            sample_validator_record_params["withdrawal_credentials"]
            )  # noqa: E501
def test_defaults(sample_validator_record_params):
    validator = Validator(**sample_validator_record_params)
    assert validator.pubkey == sample_validator_record_params['pubkey']
    assert validator.withdrawal_credentials == sample_validator_record_params[
        'withdrawal_credentials']  # noqa: E501
Example #27
0
def _validate_validator_is_active(validator: Validator,
                                  target_epoch: Epoch) -> None:
    is_active = validator.is_active(target_epoch)
    if not is_active:
        raise ValidationError(
            f"Validator trying to exit in {target_epoch} is not active.")
Example #28
0
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
Example #29
0
def _set_validator_slashed(v: Validator,
                           withdrawable_epoch: Epoch) -> Validator:
    return v.copy(
        slashed=True,
        withdrawable_epoch=withdrawable_epoch,
    )
Example #30
0
def activate_validator(validator: Validator, activation_epoch: Epoch) -> Validator:
    return validator.copy(
        activation_eligibility_epoch=activation_epoch,
        activation_epoch=activation_epoch,
    )