more refactoring: more immediate custody game, general phase1 beacon chain changes
This commit is contained in:
parent
7d2341b40d
commit
edef2fd8ae
|
@ -180,6 +180,32 @@ class Validator(Container):
|
|||
max_reveal_lateness: Epoch
|
||||
```
|
||||
|
||||
### New extended `BeaconBlockBody`
|
||||
|
||||
```python
|
||||
class BeaconBlockBody(phase0.BeaconBlockBody):
|
||||
randao_reveal: BLSSignature
|
||||
eth1_data: Eth1Data # Eth1 data vote
|
||||
graffiti: Bytes32 # Arbitrary data
|
||||
# Slashings
|
||||
proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS]
|
||||
attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS]
|
||||
# Attesting
|
||||
attestations: List[Attestation, MAX_ATTESTATIONS]
|
||||
# Enty & exit
|
||||
deposits: List[Deposit, MAX_DEPOSITS]
|
||||
voluntary_exits: List[VoluntaryExit, MAX_VOLUNTARY_EXITS]
|
||||
# Custody game
|
||||
custody_chunk_challenges: List[CustodyChunkChallenge, PLACEHOLDER]
|
||||
custody_bit_challenges: List[CustodyBitChallenge, PLACEHOLDER]
|
||||
custody_key_reveals: List[CustodyKeyReveal, PLACEHOLDER]
|
||||
early_derived_secret_reveals: List[EarlyDerivedSecretReveal, PLACEHOLDER]
|
||||
# Shards
|
||||
shard_transitions: Vector[ShardTransition, MAX_SHARDS]
|
||||
# Light clients
|
||||
light_client_signature_bitfield: Bitlist[LIGHT_CLIENT_COMMITTEE_SIZE]
|
||||
light_client_signature: BLSSignature
|
||||
```
|
||||
|
||||
### New extended `BeaconBlock`
|
||||
|
||||
|
@ -189,17 +215,6 @@ class BeaconBlock(phase0.BeaconBlock):
|
|||
parent_root: Hash
|
||||
state_root: Hash
|
||||
body: BeaconBlockBody
|
||||
shard_transitions: Vector[ShardTransition, MAX_SHARDS]
|
||||
light_client_signature_bitfield: Bitlist[LIGHT_CLIENT_COMMITTEE_SIZE]
|
||||
light_client_signature: BLSSignature
|
||||
|
||||
# TODO: older pre-proposal custody field additions, keep this?
|
||||
custody_chunk_challenges: List[CustodyChunkChallenge, PLACEHOLDER]
|
||||
custody_bit_challenges: List[CustodyBitChallenge, PLACEHOLDER]
|
||||
custody_responses: List[CustodyResponse, PLACEHOLDER]
|
||||
custody_key_reveals: List[CustodyKeyReveal, PLACEHOLDER]
|
||||
early_derived_secret_reveals: List[EarlyDerivedSecretReveal, PLACEHOLDER]
|
||||
|
||||
signature: BLSSignature
|
||||
```
|
||||
|
||||
|
@ -241,9 +256,7 @@ class BeaconState(phase0.BeaconState):
|
|||
current_light_committee: CompactCommittee
|
||||
next_light_committee: CompactCommittee
|
||||
|
||||
# TODO older pre-proposal custody field additions, keep this?
|
||||
custody_chunk_challenge_records: List[CustodyChunkChallengeRecord, PLACEHOLDER]
|
||||
custody_bit_challenge_records: List[CustodyBitChallengeRecord, PLACEHOLDER]
|
||||
# TODO: custody game refactor, no challenge-records, immediate processing.
|
||||
custody_challenge_index: uint64
|
||||
# Future derived secrets already exposed; contains the indices of the exposed validator
|
||||
# at RANDAO reveal period % EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS
|
||||
|
@ -439,11 +452,14 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
|
|||
process_operations(body.attester_slashings, process_attester_slashing)
|
||||
|
||||
# New attestation processing
|
||||
process_attestations(state, block, body.attestations)
|
||||
process_attestations(state, body, body.attestations)
|
||||
|
||||
process_operations(body.deposits, process_deposit)
|
||||
process_operations(body.voluntary_exits, process_voluntary_exit)
|
||||
|
||||
# See custody game spec.
|
||||
process_custody_game_operations(state, body)
|
||||
|
||||
# TODO process_operations(body.shard_receipt_proofs, process_shard_receipt_proofs)
|
||||
```
|
||||
|
||||
|
@ -526,7 +542,7 @@ def apply_shard_transition(state: BeaconState, shard: Shard, transition: ShardTr
|
|||
###### `process_attestations`
|
||||
|
||||
```python
|
||||
def process_attestations(state: BeaconState, block: BeaconBlock, attestations: Sequence[Attestation]) -> None:
|
||||
def process_attestations(state: BeaconState, block_body: BeaconBlockBody, attestations: Sequence[Attestation]) -> None:
|
||||
pending_attestations = []
|
||||
# Basic validation
|
||||
for attestation in attestations:
|
||||
|
@ -554,19 +570,19 @@ def process_attestations(state: BeaconState, block: BeaconBlock, attestations: S
|
|||
and success is False
|
||||
):
|
||||
# Attestation <-> shard transition consistency
|
||||
assert shard_transition_root == hash_tree_root(block.shard_transition)
|
||||
assert attestation.data.head_shard_root == chunks_to_body_root(block.shard_transition.shard_data_roots[-1])
|
||||
assert shard_transition_root == hash_tree_root(block_body.shard_transition)
|
||||
assert attestation.data.head_shard_root == chunks_to_body_root(block_body.shard_transition.shard_data_roots[-1])
|
||||
# Apply transition
|
||||
apply_shard_transition(state, shard, block.shard_transition)
|
||||
apply_shard_transition(state, shard, block_body.shard_transition)
|
||||
# Apply proposer reward and cost
|
||||
estimated_attester_reward = sum([get_base_reward(state, attester) for attester in all_participants])
|
||||
increase_balance(state, proposer, estimated_attester_reward // PROPOSER_REWARD_COEFFICIENT)
|
||||
for shard_state, slot, length in zip(block.shard_transition.shard_states, offset_slots, block.shard_transition.shard_block_lengths):
|
||||
for shard_state, slot, length in zip(block_body.shard_transition.shard_states, offset_slots, block_body.shard_transition.shard_block_lengths):
|
||||
decrease_balance(state, get_shard_proposer_index(state, shard, slot), shard_state.gasprice * length)
|
||||
winners.add((shard, shard_transition_root))
|
||||
success = True
|
||||
if not success:
|
||||
assert block.shard_transitions[shard] == ShardTransition()
|
||||
assert block_body.shard_transitions[shard] == ShardTransition()
|
||||
for attestation in attestations:
|
||||
pending_attestation = PendingAttestation(
|
||||
aggregation_bits=attestation.aggregation_bits,
|
||||
|
@ -586,22 +602,22 @@ def process_attestations(state: BeaconState, block: BeaconBlock, attestations: S
|
|||
#### Shard transition false positives
|
||||
|
||||
```python
|
||||
def verify_shard_transition_false_positives(state: BeaconState, block: BeaconBlock) -> None:
|
||||
def verify_shard_transition_false_positives(state: BeaconState, block_body: BeaconBlockBody) -> None:
|
||||
# Verify that a `shard_transition` in a block is empty if an attestation was not processed for it
|
||||
for shard in range(MAX_SHARDS):
|
||||
if state.shard_states[shard].slot != state.slot - 1:
|
||||
assert block.shard_transition[shard] == ShardTransition()
|
||||
assert block_body.shard_transition[shard] == ShardTransition()
|
||||
```
|
||||
|
||||
#### Light client processing
|
||||
|
||||
```python
|
||||
def process_light_client_signatures(state: BeaconState, block: BeaconBlock) -> None:
|
||||
def process_light_client_signatures(state: BeaconState, block_body: BeaconBlockBody) -> None:
|
||||
committee = get_light_client_committee(state, get_current_epoch(state))
|
||||
assert len(block.light_client_signature_bitfield) == len(committee)
|
||||
assert len(block_body.light_client_signature_bitfield) == len(committee)
|
||||
total_reward = Gwei(0)
|
||||
signer_keys = []
|
||||
for i, bit in enumerate(block.light_client_signature_bitfield):
|
||||
for i, bit in enumerate(block_body.light_client_signature_bitfield):
|
||||
if bit:
|
||||
signer_keys.append(state.validators[committee[i]].pubkey)
|
||||
increase_balance(state, committee[i], get_base_reward(state, committee[i]))
|
||||
|
@ -612,7 +628,7 @@ def process_light_client_signatures(state: BeaconState, block: BeaconBlock) -> N
|
|||
assert bls_verify(
|
||||
pubkey=bls_aggregate_pubkeys(signer_keys),
|
||||
message_hash=get_block_root_at_slot(state, state.slot - 1),
|
||||
signature=block.light_client_signature,
|
||||
signature=block_body.light_client_signature,
|
||||
domain=DOMAIN_LIGHT_CLIENT
|
||||
)
|
||||
```
|
||||
|
@ -627,26 +643,38 @@ def process_epoch(state: BeaconState) -> None:
|
|||
process_justification_and_finalization(state)
|
||||
process_rewards_and_penalties(state)
|
||||
process_registry_updates(state)
|
||||
# TODO process_reveal_deadlines
|
||||
# TODO process_challenge_deadlines
|
||||
process_reveal_deadlines(state)
|
||||
process_challenge_deadlines(state)
|
||||
process_slashings(state)
|
||||
# TODO update_period_committee
|
||||
process_final_updates(state)
|
||||
# TODO process_custody_final_updates
|
||||
process_custody_final_updates(state)
|
||||
process_online_tracking(state)
|
||||
process_light_client_committee_updates(state)
|
||||
```
|
||||
|
||||
#### Online-tracking
|
||||
|
||||
```python
|
||||
def process_online_tracking(state: BeaconState) -> None:
|
||||
# Slowly remove validators from the "online" set if they do not show up
|
||||
for index in range(len(state.validators)):
|
||||
if state.online_countdown[index] != 0:
|
||||
state.online_countdown[index] = state.online_countdown[index] - 1
|
||||
|
||||
# Update light client committees
|
||||
if get_current_epoch(state) % LIGHT_CLIENT_COMMITTEE_PERIOD == 0:
|
||||
state.current_light_committee = state.next_light_committee
|
||||
new_committee = get_light_client_committee(state, get_current_epoch(state) + LIGHT_CLIENT_COMMITTEE_PERIOD)
|
||||
state.next_light_committee = committee_to_compact_committee(state, new_committee)
|
||||
|
||||
# Process pending attestations
|
||||
for pending_attestation in state.current_epoch_attestations + state.previous_epoch_attestations:
|
||||
for index in get_attesting_indices(state, pending_attestation.data, pending_attestation.aggregation_bits):
|
||||
state.online_countdown[index] = ONLINE_PERIOD
|
||||
```
|
||||
|
||||
#### Light client committee updates
|
||||
|
||||
```python
|
||||
def process_light_client_committee_updates(state: BeaconState) -> None:
|
||||
# Update light client committees
|
||||
if get_current_epoch(state) % LIGHT_CLIENT_COMMITTEE_PERIOD == 0:
|
||||
state.current_light_committee = state.next_light_committee
|
||||
new_committee = get_light_client_committee(state, get_current_epoch(state) + LIGHT_CLIENT_COMMITTEE_PERIOD)
|
||||
state.next_light_committee = committee_to_compact_committee(state, new_committee)
|
||||
```
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
- [Max operations per block](#max-operations-per-block)
|
||||
- [Reward and penalty quotients](#reward-and-penalty-quotients)
|
||||
- [Signature domain types](#signature-domain-types)
|
||||
- [TODO PLACEHOLDER](#todo-placeholder)
|
||||
- [Data structures](#data-structures)
|
||||
- [Custody objects](#custody-objects)
|
||||
- [`CustodyChunkChallenge`](#custodychunkchallenge)
|
||||
|
@ -71,8 +70,6 @@ This document details the beacon chain additions and changes in Phase 1 of Ether
|
|||
- **Custody key**—
|
||||
- **Custody key reveal**—
|
||||
- **Custody key mask**—
|
||||
- **Custody response**—
|
||||
- **Custody response deadline**—
|
||||
|
||||
## Constants
|
||||
|
||||
|
@ -115,7 +112,6 @@ This document details the beacon chain additions and changes in Phase 1 of Ether
|
|||
| `MAX_EARLY_DERIVED_SECRET_REVEALS` | `1` |
|
||||
| `MAX_CUSTODY_CHUNK_CHALLENGES` | `2**2` (= 4) |
|
||||
| `MAX_CUSTODY_BIT_CHALLENGES` | `2**2` (= 4) |
|
||||
| `MAX_CUSTODY_RESPONSES` | `2**5` (= 32) |
|
||||
|
||||
### Reward and penalty quotients
|
||||
|
||||
|
@ -131,11 +127,6 @@ The following types are defined, mapping into `DomainType` (little endian):
|
|||
| - | - |
|
||||
| `DOMAIN_CUSTODY_BIT_CHALLENGE` | `6` |
|
||||
|
||||
### TODO PLACEHOLDER
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `PLACEHOLDER` | `2**32` |
|
||||
|
||||
## Data structures
|
||||
|
||||
|
@ -189,19 +180,7 @@ class CustodyBitChallengeRecord(Container):
|
|||
responder_key: BLSSignature
|
||||
```
|
||||
|
||||
#### `CustodyResponse`
|
||||
|
||||
```python
|
||||
class CustodyResponse(Container):
|
||||
challenge_index: uint64
|
||||
chunk_index: uint64
|
||||
chunk: ByteVector[BYTES_PER_CUSTODY_CHUNK]
|
||||
data_branch: List[Bytes32, CUSTODY_DATA_DEPTH]
|
||||
chunk_bits_branch: List[Bytes32, CUSTODY_CHUNK_BIT_DEPTH]
|
||||
chunk_bits_leaf: Bitvector[256]
|
||||
```
|
||||
|
||||
### New beacon operations
|
||||
### New Beacon Chain operations
|
||||
|
||||
#### `CustodyKeyReveal`
|
||||
|
||||
|
@ -363,16 +342,27 @@ def replace_empty_or_append(list: MutableSequence[Any], new_element: Any) -> int
|
|||
|
||||
## Per-block processing
|
||||
|
||||
### Operations
|
||||
### Custody Game Operations
|
||||
|
||||
Add the following operations to the per-block processing, in the order given below and after all other operations in Phase 0.
|
||||
```python
|
||||
def process_custody_game_operations(state: BeaconState, body: BeaconBlockBody) -> None:
|
||||
assert len(block.body.custody_key_reveals) <= MAX_CUSTODY_KEY_REVEALS
|
||||
assert len(block.body.early_derived_secret_reveals) <= MAX_EARLY_DERIVED_SECRET_REVEALS
|
||||
assert len(block.body.custody_bit_challenges) <= MAX_CUSTODY_BIT_CHALLENGES
|
||||
assert len(block.body.custody_chunk_challenges) <= MAX_CUSTODY_CHUNK_CHALLENGES
|
||||
|
||||
def process_operations(operations, fn):
|
||||
for operation in operations:
|
||||
fn(state, operation)
|
||||
|
||||
process_operations(body.custody_key_reveals, process_custody_key_reveal)
|
||||
process_operations(body.early_derived_secret_reveals, process_early_derived_secret_reveal)
|
||||
process_operations(body.custody_chunk_challenges, process_chunk_challenge)
|
||||
process_operations(body.custody_bit_challenges, process_bit_challenge)
|
||||
```
|
||||
|
||||
#### Custody key reveals
|
||||
|
||||
Verify that `len(block.body.custody_key_reveals) <= MAX_CUSTODY_KEY_REVEALS`.
|
||||
|
||||
For each `reveal` in `block.body.custody_key_reveals`, run the following function:
|
||||
|
||||
```python
|
||||
def process_custody_key_reveal(state: BeaconState, reveal: CustodyKeyReveal) -> None:
|
||||
"""
|
||||
|
@ -425,10 +415,6 @@ def process_custody_key_reveal(state: BeaconState, reveal: CustodyKeyReveal) ->
|
|||
|
||||
#### Early derived secret reveals
|
||||
|
||||
Verify that `len(block.body.early_derived_secret_reveals) <= MAX_EARLY_DERIVED_SECRET_REVEALS`.
|
||||
|
||||
For each `reveal` in `block.body.early_derived_secret_reveals`, run the following function:
|
||||
|
||||
```python
|
||||
def process_early_derived_secret_reveal(state: BeaconState, reveal: EarlyDerivedSecretReveal) -> None:
|
||||
"""
|
||||
|
@ -499,10 +485,6 @@ def process_early_derived_secret_reveal(state: BeaconState, reveal: EarlyDerived
|
|||
|
||||
#### Chunk challenges
|
||||
|
||||
Verify that `len(block.body.custody_chunk_challenges) <= MAX_CUSTODY_CHUNK_CHALLENGES`.
|
||||
|
||||
For each `challenge` in `block.body.custody_chunk_challenges`, run the following function:
|
||||
|
||||
```python
|
||||
def process_chunk_challenge(state: BeaconState, challenge: CustodyChunkChallenge) -> None:
|
||||
# Verify the attestation
|
||||
|
@ -541,12 +523,36 @@ def process_chunk_challenge(state: BeaconState, challenge: CustodyChunkChallenge
|
|||
responder.withdrawable_epoch = FAR_FUTURE_EPOCH
|
||||
```
|
||||
|
||||
TODO: immediate challenge processing, no state records.
|
||||
|
||||
```python
|
||||
def process_chunk_challenge_response(state: BeaconState,
|
||||
response: CustodyResponse,
|
||||
challenge: CustodyChunkChallengeRecord) -> None:
|
||||
# Verify chunk index
|
||||
assert response.chunk_index == challenge.chunk_index
|
||||
# Verify bit challenge data is null
|
||||
assert response.chunk_bits_branch == [] and response.chunk_bits_leaf == Hash()
|
||||
# Verify minimum delay
|
||||
assert get_current_epoch(state) >= challenge.inclusion_epoch + MAX_SEED_LOOKAHEAD
|
||||
# Verify the chunk matches the crosslink data root
|
||||
assert is_valid_merkle_branch(
|
||||
leaf=hash_tree_root(response.chunk),
|
||||
branch=response.data_branch,
|
||||
depth=challenge.depth,
|
||||
index=response.chunk_index,
|
||||
root=challenge.data_root,
|
||||
)
|
||||
# Clear the challenge
|
||||
records = state.custody_chunk_challenge_records
|
||||
records[records.index(challenge)] = CustodyChunkChallengeRecord()
|
||||
# Reward the proposer
|
||||
proposer_index = get_beacon_proposer_index(state)
|
||||
increase_balance(state, proposer_index, Gwei(get_base_reward(state, proposer_index) // MINOR_REWARD_QUOTIENT))
|
||||
```
|
||||
|
||||
#### Bit challenges
|
||||
|
||||
Verify that `len(block.body.custody_bit_challenges) <= MAX_CUSTODY_BIT_CHALLENGES`.
|
||||
|
||||
For each `challenge` in `block.body.custody_bit_challenges`, run the following function:
|
||||
|
||||
```python
|
||||
def process_bit_challenge(state: BeaconState, challenge: CustodyBitChallenge) -> None:
|
||||
attestation = challenge.attestation
|
||||
|
@ -606,52 +612,7 @@ def process_bit_challenge(state: BeaconState, challenge: CustodyBitChallenge) ->
|
|||
responder.withdrawable_epoch = FAR_FUTURE_EPOCH
|
||||
```
|
||||
|
||||
#### Custody responses
|
||||
|
||||
Verify that `len(block.body.custody_responses) <= MAX_CUSTODY_RESPONSES`.
|
||||
|
||||
For each `response` in `block.body.custody_responses`, run the following function:
|
||||
|
||||
```python
|
||||
def process_custody_response(state: BeaconState, response: CustodyResponse) -> None:
|
||||
chunk_challenge = next((record for record in state.custody_chunk_challenge_records
|
||||
if record.challenge_index == response.challenge_index), None)
|
||||
if chunk_challenge is not None:
|
||||
return process_chunk_challenge_response(state, response, chunk_challenge)
|
||||
|
||||
bit_challenge = next((record for record in state.custody_bit_challenge_records
|
||||
if record.challenge_index == response.challenge_index), None)
|
||||
if bit_challenge is not None:
|
||||
return process_bit_challenge_response(state, response, bit_challenge)
|
||||
|
||||
assert False
|
||||
```
|
||||
|
||||
```python
|
||||
def process_chunk_challenge_response(state: BeaconState,
|
||||
response: CustodyResponse,
|
||||
challenge: CustodyChunkChallengeRecord) -> None:
|
||||
# Verify chunk index
|
||||
assert response.chunk_index == challenge.chunk_index
|
||||
# Verify bit challenge data is null
|
||||
assert response.chunk_bits_branch == [] and response.chunk_bits_leaf == Bytes32()
|
||||
# Verify minimum delay
|
||||
assert get_current_epoch(state) >= challenge.inclusion_epoch + MAX_SEED_LOOKAHEAD
|
||||
# Verify the chunk matches the crosslink data root
|
||||
assert is_valid_merkle_branch(
|
||||
leaf=hash_tree_root(response.chunk),
|
||||
branch=response.data_branch,
|
||||
depth=challenge.depth,
|
||||
index=response.chunk_index,
|
||||
root=challenge.data_root,
|
||||
)
|
||||
# Clear the challenge
|
||||
records = state.custody_chunk_challenge_records
|
||||
records[records.index(challenge)] = CustodyChunkChallengeRecord()
|
||||
# Reward the proposer
|
||||
proposer_index = get_beacon_proposer_index(state)
|
||||
increase_balance(state, proposer_index, Gwei(get_base_reward(state, proposer_index) // MINOR_REWARD_QUOTIENT))
|
||||
```
|
||||
TODO: immediate challenge processing, no state records.
|
||||
|
||||
```python
|
||||
def process_bit_challenge_response(state: BeaconState,
|
||||
|
@ -703,23 +664,6 @@ def process_reveal_deadlines(state: BeaconState) -> None:
|
|||
slash_validator(state, ValidatorIndex(index))
|
||||
```
|
||||
|
||||
Run `process_challenge_deadlines(state)` immediately after `process_reveal_deadlines(state)`:
|
||||
|
||||
```python
|
||||
def process_challenge_deadlines(state: BeaconState) -> None:
|
||||
for custody_chunk_challenge in state.custody_chunk_challenge_records:
|
||||
if get_current_epoch(state) > custody_chunk_challenge.inclusion_epoch + CUSTODY_RESPONSE_DEADLINE:
|
||||
slash_validator(state, custody_chunk_challenge.responder_index, custody_chunk_challenge.challenger_index)
|
||||
records = state.custody_chunk_challenge
|
||||
records[records.index(custody_chunk_challenge)] = CustodyChunkChallengeRecord()
|
||||
|
||||
for custody_bit_challenge in state.custody_bit_challenge_records:
|
||||
if get_current_epoch(state) > custody_bit_challenge.inclusion_epoch + CUSTODY_RESPONSE_DEADLINE:
|
||||
slash_validator(state, custody_bit_challenge.responder_index, custody_bit_challenge.challenger_index)
|
||||
records = state.custody_bit_challenge_records
|
||||
records[records.index(custody_bit_challenge)] = CustodyBitChallengeRecord()
|
||||
```
|
||||
|
||||
After `process_final_updates(state)`, additional updates are made for the custody game:
|
||||
|
||||
```python
|
||||
|
|
Loading…
Reference in New Issue