Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Whisk: Move validator whisk trackers and commitments to state #3407

Merged
merged 2 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 23 additions & 54 deletions specs/_features/whisk/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
- [Curdleproofs and opening proofs](#curdleproofs-and-opening-proofs)
- [Epoch processing](#epoch-processing)
- [`WhiskTracker`](#whisktracker)
- [`Validator`](#validator)
- [`BeaconState`](#beaconstate)
- [Block processing](#block-processing)
- [Block header](#block-header)
Expand Down Expand Up @@ -124,23 +123,6 @@ class WhiskTracker(Container):
k_r_G: BLSG1Point # k * r * G
```

### `Validator`

```python
class Validator(Container):
pubkey: BLSPubkey
withdrawal_credentials: Bytes32 # Commitment to pubkey for withdrawals
effective_balance: Gwei # Balance at stake
slashed: boolean
# Status epochs
activation_eligibility_epoch: Epoch # When criteria for activation were met
activation_epoch: Epoch
exit_epoch: Epoch
withdrawable_epoch: Epoch # When validator can withdraw funds
whisk_tracker: WhiskTracker # Whisk tracker (r * G, k * r * G) [New in Whisk]
whisk_k_commitment: BLSG1Point # Whisk k commitment k * BLS_G1_GENERATOR [New in Whisk]
```

### `BeaconState`

```python
Expand Down Expand Up @@ -186,8 +168,11 @@ class BeaconState(Container):
next_withdrawal_validator_index: ValidatorIndex
# Deep history valid from Capella onwards
historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT]
# Whisk
whisk_candidate_trackers: Vector[WhiskTracker, WHISK_CANDIDATE_TRACKERS_COUNT] # [New in Whisk]
whisk_proposer_trackers: Vector[WhiskTracker, WHISK_PROPOSER_TRACKERS_COUNT] # [New in Whisk]
whisk_trackers: List[WhiskTracker, VALIDATOR_REGISTRY_LIMIT] # [New in Whisk]
whisk_k_commitments: List[BLSG1Point, VALIDATOR_REGISTRY_LIMIT] # [New in Whisk]
```

```python
Expand All @@ -203,7 +188,7 @@ def select_whisk_trackers(state: BeaconState, epoch: Epoch) -> None:
for i in range(WHISK_CANDIDATE_TRACKERS_COUNT):
seed = hash(get_seed(state, epoch, DOMAIN_WHISK_CANDIDATE_SELECTION) + uint_to_bytes(i))
candidate_index = compute_proposer_index(state, active_validator_indices, seed) # sample by effective balance
state.whisk_candidate_trackers[i] = state.validators[candidate_index].whisk_tracker
state.whisk_candidate_trackers[i] = state.whisk_trackers[candidate_index]
```

```python
Expand Down Expand Up @@ -237,7 +222,7 @@ def process_epoch(state: BeaconState) -> None:
```python
def process_whisk_opening_proof(state: BeaconState, block: BeaconBlock) -> None:
tracker = state.whisk_proposer_trackers[state.slot % WHISK_PROPOSER_TRACKERS_COUNT]
k_commitment = state.validators[block.proposer_index].whisk_k_commitment
k_commitment = state.whisk_k_commitments[block.proposer_index]
assert IsValidWhiskOpeningProof(tracker, k_commitment, block.body.whisk_opening_proof)
```

Expand Down Expand Up @@ -297,7 +282,7 @@ class BeaconBlockBody(capella.BeaconBlockBody):
whisk_shuffle_proof_M_commitment: BLSG1Point # [New in Whisk]
whisk_registration_proof: WhiskTrackerProof # [New in Whisk]
whisk_tracker: WhiskTracker # [New in Whisk]
whisk_k_commitment: BLSG1Point # [New in Whisk]
whisk_k_commitment: BLSG1Point # k * BLS_G1_GENERATOR [New in Whisk]
```

```python
Expand Down Expand Up @@ -341,7 +326,7 @@ def process_shuffled_trackers(state: BeaconState, body: BeaconBlockBody) -> None

```python
def is_k_commitment_unique(state: BeaconState, k_commitment: BLSG1Point) -> bool:
return all([validator.whisk_k_commitment != k_commitment for validator in state.validators])
return all([whisk_k_commitment != k_commitment for whisk_k_commitment in state.whisk_k_commitments])
```

```python
Expand Down Expand Up @@ -382,49 +367,30 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:

### Deposits

```python
def get_initial_whisk_k(validator_index: ValidatorIndex, counter: int) -> BLSFieldElement:
# hash `validator_index || counter`
return BLSFieldElement(bytes_to_bls_field(hash(uint_to_bytes(validator_index) + uint_to_bytes(uint64(counter)))))
asn-d6 marked this conversation as resolved.
Show resolved Hide resolved
```

```python
def get_unique_whisk_k(state: BeaconState, validator_index: ValidatorIndex) -> BLSFieldElement:
counter = 0
while True:
# hash `validator_index || counter`
k = BLSFieldElement(bytes_to_bls_field(hash(uint_to_bytes(validator_index) + uint_to_bytes(uint64(counter)))))
k = get_initial_whisk_k(validator_index, counter)
if is_k_commitment_unique(state, BLSG1ScalarMultiply(k, BLS_G1_GENERATOR)):
return k # unique by trial and error
counter += 1
```

```python
def get_initial_commitments(k: BLSFieldElement) -> Tuple[BLSG1Point, WhiskTracker]:
return (
BLSG1ScalarMultiply(k, BLS_G1_GENERATOR),
WhiskTracker(r_G=BLS_G1_GENERATOR, k_r_G=BLSG1ScalarMultiply(k, BLS_G1_GENERATOR))
)
def get_k_commitment(k: BLSFieldElement) -> BLSG1Point:
dapplion marked this conversation as resolved.
Show resolved Hide resolved
return BLSG1ScalarMultiply(k, BLS_G1_GENERATOR)
```

```python
def get_validator_from_deposit_whisk(
state: BeaconState,
pubkey: BLSPubkey,
withdrawal_credentials: Bytes32,
amount: uint64
) -> Validator:
effective_balance = min(amount - amount % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE)
k = get_unique_whisk_k(state, ValidatorIndex(len(state.validators)))
whisk_k_commitment, whisk_tracker = get_initial_commitments(k)

validator = Validator(
pubkey=pubkey,
withdrawal_credentials=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=effective_balance,
# Whisk fields
whisk_tracker=whisk_tracker,
whisk_k_commitment=whisk_k_commitment,
)
return validator
def get_initial_tracker(k: BLSFieldElement) -> WhiskTracker:
return WhiskTracker(r_G=BLS_G1_GENERATOR, k_r_G=BLSG1ScalarMultiply(k, BLS_G1_GENERATOR))
```

```python
Expand All @@ -446,13 +412,16 @@ def apply_deposit(state: BeaconState,
# Initialize validator if the deposit signature is valid
if bls.Verify(pubkey, signing_root, signature):
index = get_index_for_new_validator(state)
validator = get_validator_from_deposit_whisk(state, pubkey, withdrawal_credentials, amount)
validator = get_validator_from_deposit(pubkey, withdrawal_credentials, amount)
set_or_append_list(state.validators, index, validator)
set_or_append_list(state.balances, index, amount)
# [New in Altair]
set_or_append_list(state.previous_epoch_participation, index, ParticipationFlags(0b0000_0000))
set_or_append_list(state.current_epoch_participation, index, ParticipationFlags(0b0000_0000))
set_or_append_list(state.inactivity_scores, index, uint64(0))
# [New in Whisk]
k = get_unique_whisk_k(state, ValidatorIndex(len(state.validators) - 1))
asn-d6 marked this conversation as resolved.
Show resolved Hide resolved
state.whisk_trackers.append(get_initial_tracker(k))
state.whisk_k_commitments.append(get_k_commitment(k))
else:
# Increase balance by deposit amount
index = ValidatorIndex(validator_pubkeys.index(pubkey))
Expand Down
35 changes: 16 additions & 19 deletions specs/_features/whisk/fork.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ def whisk_proposer_selection(state: BeaconState, epoch: Epoch) -> None:

```python
def upgrade_to_whisk(pre: bellatrix.BeaconState) -> BeaconState:
# Compute initial unsafe trackers for all validators
ks = [get_initial_whisk_k(ValidatorIndex(validator_index), 0) for validator_index in range(len(pre.validators))]
whisk_k_commitments = [get_k_commitment(k) for k in ks]
whisk_trackers = [get_initial_tracker(k) for k in ks]

epoch = bellatrix.get_current_epoch(pre)
post = BeaconState(
# Versioning
Expand Down Expand Up @@ -104,27 +109,19 @@ def upgrade_to_whisk(pre: bellatrix.BeaconState) -> BeaconState:
current_justified_checkpoint=pre.current_justified_checkpoint,
finalized_checkpoint=pre.finalized_checkpoint,
# Inactivity
inactivity_scores=pre.inactivity_Scores,
inactivity_scores=pre.inactivity_scores,
# Sync
current_sync_committee=pre.current_sync_committee,
next_sync_committee=pre.next_sync_committee,
# Execution-layer
latest_execution_payload_header=pre.latest_execution_payload_header,
# Whisk
whisk_proposer_trackers=[WhiskTracker() for _ in range(WHISK_PROPOSER_TRACKERS_COUNT)], # [New in Whisk]
whisk_candidate_trackers=[WhiskTracker() for _ in range(WHISK_CANDIDATE_TRACKERS_COUNT)], # [New in Whisk]
whisk_trackers=whisk_trackers, # [New in Whisk]
whisk_k_commitments=whisk_k_commitments, # [New in Whisk]
)

# Initialize all validators with predictable commitments
for val_index, pre_validator in enumerate(pre.validators):
whisk_commitment, whisk_tracker = get_initial_commitments(get_unique_whisk_k(post, ValidatorIndex(val_index)))

post_validator = Validator(
pubkey=pre_validator.pubkey,
withdrawal_credentials=pre_validator.withdrawal_credentials,
effective_balance=pre_validator.effective_balance,
slashed=pre_validator.slashed,
activation_eligibility_epoch=pre_validator.activation_eligibility_epoch,
activation_epoch=pre_validator.activation_epoch,
exit_epoch=pre_validator.exit_epoch,
withdrawable_epoch=pre_validator.withdrawable_epoch,
whisk_commitment=whisk_commitment,
whisk_tracker=whisk_tracker,
)
post.validators.append(post_validator)

# Do a candidate selection followed by a proposer selection so that we have proposers for the upcoming day
# Use an old epoch when selecting candidates so that we don't get the same seed as in the next candidate selection
whisk_candidate_selection(post, epoch - WHISK_PROPOSER_SELECTION_GAP - 1)
Expand Down