From 0e2931b9b3490027e11a9a4a7c0851466d533d5d Mon Sep 17 00:00:00 2001 From: Dankrad Feist Date: Tue, 28 Apr 2020 01:09:20 +0100 Subject: [PATCH] All tests passed --- configs/minimal.yaml | 2 - specs/phase1/beacon-chain.md | 54 +++++ specs/phase1/custody-game.md | 29 ++- specs/phase1/phase1-fork.md | 2 +- .../pyspec/eth2spec/test/helpers/custody.py | 5 +- .../test_process_final_custody_updates.py | 213 +++--------------- 6 files changed, 121 insertions(+), 184 deletions(-) diff --git a/configs/minimal.yaml b/configs/minimal.yaml index 256c2b3fa..5f8b6b8a2 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -208,8 +208,6 @@ EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS: 4096 EPOCHS_PER_CUSTODY_PERIOD: 8 # 2**11 (= 2,048) epochs CUSTODY_PERIOD_TO_RANDAO_PADDING: 8 -# 2**7 (= 128) epochs -MAX_REVEAL_LATENESS_DECREMENT: 128 # Max operations # 2**8 (= 256) diff --git a/specs/phase1/beacon-chain.md b/specs/phase1/beacon-chain.md index fc208cbaa..b6b21b721 100644 --- a/specs/phase1/beacon-chain.md +++ b/specs/phase1/beacon-chain.md @@ -282,6 +282,7 @@ class Validator(Container): next_custody_secret_to_reveal: uint64 # TODO: The max_reveal_lateness doesn't really make sense anymore. # So how do we incentivise early custody key reveals now? + all_custody_secrets_revealed_epoch: Epoch # to be initialized to FAR_FUTURE_EPOCH ``` ### Extended `BeaconBlockBody` @@ -925,6 +926,59 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: state.previous_epoch_attestations.append(pending_attestation) ``` +##### New deposits + +```python +def process_deposit(state: BeaconState, deposit: Deposit) -> None: + # Verify the Merkle branch + assert is_valid_merkle_branch( + leaf=hash_tree_root(deposit.data), + branch=deposit.proof, + depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # Add 1 for the List length mix-in + index=state.eth1_deposit_index, + root=state.eth1_data.deposit_root, + ) + + # Deposits must be processed in order + state.eth1_deposit_index += 1 + + pubkey = deposit.data.pubkey + amount = deposit.data.amount + validator_pubkeys = [v.pubkey for v in state.validators] + if pubkey not in validator_pubkeys: + # Verify the deposit signature (proof of possession) which is not checked by the deposit contract + deposit_message = DepositMessage( + pubkey=deposit.data.pubkey, + withdrawal_credentials=deposit.data.withdrawal_credentials, + amount=deposit.data.amount, + ) + domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks + signing_root = compute_signing_root(deposit_message, domain) + if not bls.Verify(pubkey, signing_root, deposit.data.signature): + return + + # Add validator and balance entries + # TODO: This function is duplicated from phase 1 just because the validator definition + # has changed and we need to initialize it properly. Is there a better solution for + # this? + state.validators.append(Validator( + pubkey=pubkey, + withdrawal_credentials=deposit.data.withdrawal_credentials, + activation_eligibility_epoch=FAR_FUTURE_EPOCH, + activation_epoch=FAR_FUTURE_EPOCH, + exit_epoch=FAR_FUTURE_EPOCH, + withdrawable_epoch=FAR_FUTURE_EPOCH, + effective_balance=min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE), + next_custody_secret_to_reveal=get_custody_period_for_validator(ValidatorIndex(len(state.validators)), get_current_epoch(state)), + all_custody_secrets_revealed_epoch=FAR_FUTURE_EPOCH, + )) + state.balances.append(amount) + else: + # Increase balance by deposit amount + index = ValidatorIndex(validator_pubkeys.index(pubkey)) + increase_balance(state, index, amount) +``` + ##### New Attester slashing processing ```python diff --git a/specs/phase1/custody-game.md b/specs/phase1/custody-game.md index 3047fb263..f40e936ce 100644 --- a/specs/phase1/custody-game.md +++ b/specs/phase1/custody-game.md @@ -293,7 +293,12 @@ def process_custody_key_reveal(state: BeaconState, reveal: CustodyKeyReveal) -> epoch_to_sign = get_randao_epoch_for_custody_period(revealer.next_custody_secret_to_reveal, reveal.revealer_index) custody_reveal_period = get_custody_period_for_validator(reveal.revealer_index, get_current_epoch(state)) - assert revealer.next_custody_secret_to_reveal < custody_reveal_period + # Only past custody periods can be revealed, except after exiting the exit + # period can be revealed + assert (revealer.next_custody_secret_to_reveal < custody_reveal_period + or (revealer.exit_epoch <= get_current_epoch(state) and + revealer.next_custody_secret_to_reveal + <= get_custody_period_for_validator(reveal.revealer_index, revealer.exit_epoch - 1))) # Revealed validator is active or exited, but not withdrawn assert is_slashable_validator(revealer, get_current_epoch(state)) @@ -304,6 +309,10 @@ def process_custody_key_reveal(state: BeaconState, reveal: CustodyKeyReveal) -> assert bls.Verify(revealer.pubkey, signing_root, reveal.reveal) # Process reveal + if (revealer.exit_epoch <= get_current_epoch(state) and + revealer.next_custody_secret_to_reveal + == get_custody_period_for_validator(reveal.revealer_index, revealer.exit_epoch - 1)): + revealer.all_custody_secrets_revealed_epoch = get_current_epoch(state) revealer.next_custody_secret_to_reveal += 1 # Reward Block Proposer @@ -480,4 +489,22 @@ After `process_final_updates(state)`, additional updates are made for the custod def process_custody_final_updates(state: BeaconState) -> None: # Clean up exposed RANDAO key reveals state.exposed_derived_secrets[get_current_epoch(state) % EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS] = [] + + # Reset withdrawable epochs if challenge records are empty + records = state.custody_chunk_challenge_records + validator_indices_in_records = set( + [record.responder_index for record in records] + ) + for index, validator in enumerate(state.validators): + if validator.exit_epoch != FAR_FUTURE_EPOCH: + if (index in validator_indices_in_records + or validator.all_custody_secrets_revealed_epoch == FAR_FUTURE_EPOCH): + # Delay withdrawable epochs if challenge records are not empty or not all + # custody secrets revealed + validator.withdrawable_epoch = FAR_FUTURE_EPOCH + else: + # Reset withdrawable epochs if challenge records are empty + if validator.withdrawable_epoch == FAR_FUTURE_EPOCH: + validator.withdrawable_epoch = Epoch(validator.all_custody_secrets_revealed_epoch + + MIN_VALIDATOR_WITHDRAWABILITY_DELAY) ``` diff --git a/specs/phase1/phase1-fork.md b/specs/phase1/phase1-fork.md index 173fceeb4..9d5f282f3 100644 --- a/specs/phase1/phase1-fork.md +++ b/specs/phase1/phase1-fork.md @@ -79,7 +79,7 @@ def upgrade_to_phase1(pre: phase0.BeaconState) -> BeaconState: exit_epoch=phase0_validator.exit_epoch, withdrawable_epoch=phase0_validator.withdrawable_epoch, next_custody_secret_to_reveal=get_custody_period_for_validator(ValidatorIndex(i), epoch), - max_reveal_lateness=0, # TODO custody refactor. Outdated? + all_custody_secrets_revealed_epoch=FAR_FUTURE_EPOCH, ) for i, phase0_validator in enumerate(pre.validators) ), balances=pre.balances, diff --git a/tests/core/pyspec/eth2spec/test/helpers/custody.py b/tests/core/pyspec/eth2spec/test/helpers/custody.py index f4b2d6a3d..2d937a2ee 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/custody.py +++ b/tests/core/pyspec/eth2spec/test/helpers/custody.py @@ -37,9 +37,10 @@ def get_valid_early_derived_secret_reveal(spec, state, epoch=None): ) -def get_valid_custody_key_reveal(spec, state, period=None): +def get_valid_custody_key_reveal(spec, state, period=None, validator_index=None): current_epoch = spec.get_current_epoch(state) - revealer_index = spec.get_active_validator_indices(state, current_epoch)[0] + revealer_index = (spec.get_active_validator_indices(state, current_epoch)[0] + if validator_index is None else validator_index) revealer = state.validators[revealer_index] if period is None: diff --git a/tests/core/pyspec/eth2spec/test/phase_1/epoch_processing/test_process_final_custody_updates.py b/tests/core/pyspec/eth2spec/test/phase_1/epoch_processing/test_process_final_custody_updates.py index d200ed2b3..566d795cf 100644 --- a/tests/core/pyspec/eth2spec/test/phase_1/epoch_processing/test_process_final_custody_updates.py +++ b/tests/core/pyspec/eth2spec/test/phase_1/epoch_processing/test_process_final_custody_updates.py @@ -1,16 +1,15 @@ from eth2spec.test.helpers.custody import ( - get_valid_bit_challenge, get_valid_chunk_challenge, - get_valid_custody_bit_response, get_valid_custody_chunk_response, get_valid_custody_key_reveal, get_custody_test_vector, get_custody_merkle_root, + get_shard_transition ) from eth2spec.test.helpers.attestations import ( - get_valid_attestation, + get_valid_on_time_attestation, ) -from eth2spec.test.helpers.state import next_epoch +from eth2spec.test.helpers.state import next_epoch, transition_to from eth2spec.test.helpers.block import apply_empty_block from eth2spec.test.context import ( with_all_phases_except, @@ -19,10 +18,6 @@ from eth2spec.test.context import ( from eth2spec.test.phase_0.block_processing.test_process_attestation import run_attestation_processing from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_with -from eth2spec.test.phase_1.block_processing.test_process_bit_challenge import ( - run_bit_challenge_processing, - run_custody_bit_response_processing, -) from eth2spec.test.phase_1.block_processing.test_process_chunk_challenge import ( run_chunk_challenge_processing, run_custody_chunk_response_processing, @@ -30,8 +25,8 @@ from eth2spec.test.phase_1.block_processing.test_process_chunk_challenge import from eth2spec.test.phase_1.block_processing.test_process_custody_key_reveal import run_custody_key_reveal_processing -def run_process_final_custody_updates(spec, state): - yield from run_epoch_processing_with(spec, state, 'process_final_custody_updates') +def run_process_custody_final_updates(spec, state): + yield from run_epoch_processing_with(spec, state, 'process_custody_final_updates') @with_all_phases_except(['phase0']) @@ -40,7 +35,7 @@ def test_validator_withdrawal_delay(spec, state): spec.initiate_validator_exit(state, 0) assert state.validators[0].withdrawable_epoch < spec.FAR_FUTURE_EPOCH - yield from run_process_final_custody_updates(spec, state) + yield from run_process_custody_final_updates(spec, state) assert state.validators[0].withdrawable_epoch == spec.FAR_FUTURE_EPOCH @@ -61,100 +56,39 @@ def test_validator_withdrawal_reenable_after_custody_reveal(spec, state): apply_empty_block(spec, state) while (state.validators[0].next_custody_secret_to_reveal - <= spec.get_custody_period_for_validator(state, 0, state.validators[0].exit_epoch - 1)): + <= spec.get_custody_period_for_validator(0, state.validators[0].exit_epoch - 1)): custody_key_reveal = get_valid_custody_key_reveal(spec, state, validator_index=0) _, _, _ = run_custody_key_reveal_processing(spec, state, custody_key_reveal) - yield from run_process_final_custody_updates(spec, state) + yield from run_process_custody_final_updates(spec, state) assert state.validators[0].withdrawable_epoch < spec.FAR_FUTURE_EPOCH -@with_all_phases_except(['phase0']) -@spec_state_test -def test_validator_withdrawal_suspend_after_bit_challenge(spec, state): - state.slot = spec.SLOTS_PER_EPOCH - - attestation = get_valid_attestation(spec, state, signed=True) - - test_vector = get_custody_test_vector( - spec.get_custody_chunk_count(attestation.data.crosslink) * spec.BYTES_PER_CUSTODY_CHUNK) - shard_root = get_custody_merkle_root(test_vector) - attestation.data.crosslink.data_root = shard_root - attestation.custody_bits[0] = 0 - state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - next_epoch(spec, state) - apply_empty_block(spec, state) - - _, _, _ = run_attestation_processing(spec, state, attestation) - - validator_index = spec.get_crosslink_committee( - state, - attestation.data.target.epoch, - attestation.data.crosslink.shard - )[0] - - spec.initiate_validator_exit(state, validator_index) - assert state.validators[validator_index].withdrawable_epoch < spec.FAR_FUTURE_EPOCH - - next_epoch(spec, state) - apply_empty_block(spec, state) - - assert state.validators[validator_index].withdrawable_epoch == spec.FAR_FUTURE_EPOCH - - while spec.get_current_epoch(state) < state.validators[validator_index].exit_epoch: - next_epoch(spec, state) - apply_empty_block(spec, state) - - while (state.validators[validator_index].next_custody_secret_to_reveal - <= spec.get_custody_period_for_validator( - state, - validator_index, - state.validators[validator_index].exit_epoch - 1)): - custody_key_reveal = get_valid_custody_key_reveal(spec, state, validator_index=validator_index) - _, _, _ = run_custody_key_reveal_processing(spec, state, custody_key_reveal) - - next_epoch(spec, state) - apply_empty_block(spec, state) - - challenge = get_valid_bit_challenge(spec, state, attestation) - - _, _, _ = run_bit_challenge_processing(spec, state, challenge) - - yield from run_process_final_custody_updates(spec, state) - - assert state.validators[validator_index].withdrawable_epoch == spec.FAR_FUTURE_EPOCH - - @with_all_phases_except(['phase0']) @spec_state_test def test_validator_withdrawal_suspend_after_chunk_challenge(spec, state): - state.slot = spec.SLOTS_PER_EPOCH + transition_to(spec, state, state.slot + 1) + shard = 0 + offset_slots = spec.get_offset_slots(state, shard) + shard_transition = get_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots)) + data_index = 0 + attestation = get_valid_on_time_attestation(spec, state, index=shard, signed=True, shard_transition=shard_transition) - attestation = get_valid_attestation(spec, state, signed=True) - - test_vector = get_custody_test_vector( - spec.get_custody_chunk_count(attestation.data.crosslink) * spec.BYTES_PER_CUSTODY_CHUNK) - shard_root = get_custody_merkle_root(test_vector) - attestation.data.crosslink.data_root = shard_root - attestation.custody_bits[0] = 0 - state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - next_epoch(spec, state) - apply_empty_block(spec, state) + transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY) _, _, _ = run_attestation_processing(spec, state, attestation) - validator_index = spec.get_crosslink_committee( + validator_index = spec.get_beacon_committee( state, - attestation.data.target.epoch, - attestation.data.crosslink.shard + attestation.data.slot, + attestation.data.index )[0] spec.initiate_validator_exit(state, validator_index) assert state.validators[validator_index].withdrawable_epoch < spec.FAR_FUTURE_EPOCH - next_epoch(spec, state) - apply_empty_block(spec, state) + transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH) assert state.validators[validator_index].withdrawable_epoch == spec.FAR_FUTURE_EPOCH @@ -164,7 +98,6 @@ def test_validator_withdrawal_suspend_after_chunk_challenge(spec, state): while (state.validators[validator_index].next_custody_secret_to_reveal <= spec.get_custody_period_for_validator( - state, validator_index, state.validators[validator_index].exit_epoch - 1)): custody_key_reveal = get_valid_custody_key_reveal(spec, state, validator_index=validator_index) @@ -173,108 +106,33 @@ def test_validator_withdrawal_suspend_after_chunk_challenge(spec, state): next_epoch(spec, state) apply_empty_block(spec, state) - challenge = get_valid_chunk_challenge(spec, state, attestation) + challenge = get_valid_chunk_challenge(spec, state, attestation, shard_transition) _, _, _ = run_chunk_challenge_processing(spec, state, challenge) - yield from run_process_final_custody_updates(spec, state) + yield from run_process_custody_final_updates(spec, state) assert state.validators[validator_index].withdrawable_epoch == spec.FAR_FUTURE_EPOCH -@with_all_phases_except(['phase0']) -@spec_state_test -def test_validator_withdrawal_resume_after_bit_challenge_response(spec, state): - state.slot = spec.SLOTS_PER_EPOCH - - attestation = get_valid_attestation(spec, state, signed=True) - - test_vector = get_custody_test_vector( - spec.get_custody_chunk_count(attestation.data.crosslink) * spec.BYTES_PER_CUSTODY_CHUNK) - shard_root = get_custody_merkle_root(test_vector) - attestation.data.crosslink.data_root = shard_root - attestation.custody_bits[0] = 0 - state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - next_epoch(spec, state) - apply_empty_block(spec, state) - - _, _, _ = run_attestation_processing(spec, state, attestation) - - validator_index = spec.get_crosslink_committee( - state, - attestation.data.target.epoch, - attestation.data.crosslink.shard - )[0] - - spec.initiate_validator_exit(state, validator_index) - assert state.validators[validator_index].withdrawable_epoch < spec.FAR_FUTURE_EPOCH - - next_epoch(spec, state) - apply_empty_block(spec, state) - - assert state.validators[validator_index].withdrawable_epoch == spec.FAR_FUTURE_EPOCH - - while spec.get_current_epoch(state) < state.validators[validator_index].exit_epoch: - next_epoch(spec, state) - apply_empty_block(spec, state) - - while (state.validators[validator_index].next_custody_secret_to_reveal - <= spec.get_custody_period_for_validator( - state, - validator_index, - state.validators[validator_index].exit_epoch - 1)): - custody_key_reveal = get_valid_custody_key_reveal(spec, state, validator_index=validator_index) - _, _, _ = run_custody_key_reveal_processing(spec, state, custody_key_reveal) - - next_epoch(spec, state) - apply_empty_block(spec, state) - - challenge = get_valid_bit_challenge(spec, state, attestation) - - _, _, _ = run_bit_challenge_processing(spec, state, challenge) - - next_epoch(spec, state) - apply_empty_block(spec, state) - - assert state.validators[validator_index].withdrawable_epoch == spec.FAR_FUTURE_EPOCH - - bit_challenge_index = state.custody_bit_challenge_index - 1 - response = get_valid_custody_bit_response( - spec, - state, - challenge, - test_vector, - bit_challenge_index, - invalid_chunk_bit=False) - - _, _, _ = run_custody_bit_response_processing(spec, state, response) - - yield from run_process_final_custody_updates(spec, state) - - assert state.validators[validator_index].withdrawable_epoch < spec.FAR_FUTURE_EPOCH - - @with_all_phases_except(['phase0']) @spec_state_test def test_validator_withdrawal_resume_after_chunk_challenge_response(spec, state): - state.slot = spec.SLOTS_PER_EPOCH + transition_to(spec, state, state.slot + 1) + shard = 0 + offset_slots = spec.get_offset_slots(state, shard) + shard_transition = get_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots)) + data_index = 0 + attestation = get_valid_on_time_attestation(spec, state, index=shard, signed=True, shard_transition=shard_transition) - attestation = get_valid_attestation(spec, state, signed=True) - - test_vector = get_custody_test_vector( - spec.get_custody_chunk_count(attestation.data.crosslink) * spec.BYTES_PER_CUSTODY_CHUNK) - shard_root = get_custody_merkle_root(test_vector) - attestation.data.crosslink.data_root = shard_root - state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - next_epoch(spec, state) - apply_empty_block(spec, state) + transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY) _, _, _ = run_attestation_processing(spec, state, attestation) - validator_index = spec.get_crosslink_committee( + validator_index = spec.get_beacon_committee( state, - attestation.data.target.epoch, - attestation.data.crosslink.shard + attestation.data.slot, + attestation.data.index )[0] spec.initiate_validator_exit(state, validator_index) @@ -291,7 +149,6 @@ def test_validator_withdrawal_resume_after_chunk_challenge_response(spec, state) while (state.validators[validator_index].next_custody_secret_to_reveal <= spec.get_custody_period_for_validator( - state, validator_index, state.validators[validator_index].exit_epoch - 1)): custody_key_reveal = get_valid_custody_key_reveal(spec, state, validator_index=validator_index) @@ -300,7 +157,7 @@ def test_validator_withdrawal_resume_after_chunk_challenge_response(spec, state) next_epoch(spec, state) apply_empty_block(spec, state) - challenge = get_valid_chunk_challenge(spec, state, attestation) + challenge = get_valid_chunk_challenge(spec, state, attestation, shard_transition) _, _, _ = run_chunk_challenge_processing(spec, state, challenge) @@ -310,10 +167,10 @@ def test_validator_withdrawal_resume_after_chunk_challenge_response(spec, state) assert state.validators[validator_index].withdrawable_epoch == spec.FAR_FUTURE_EPOCH chunk_challenge_index = state.custody_chunk_challenge_index - 1 - response = get_valid_custody_chunk_response(spec, state, challenge, test_vector, chunk_challenge_index) + custody_response = get_valid_custody_chunk_response(spec, state, challenge, 2**15 // 3, chunk_challenge_index) - _, _, _ = run_custody_chunk_response_processing(spec, state, response) + _, _, _ = run_custody_chunk_response_processing(spec, state, custody_response) - yield from run_process_final_custody_updates(spec, state) + yield from run_process_custody_final_updates(spec, state) assert state.validators[validator_index].withdrawable_epoch < spec.FAR_FUTURE_EPOCH