more refactoring: more immediate custody game, general phase1 beacon chain changes

This commit is contained in:
protolambda 2019-11-15 23:42:28 +01:00 committed by Danny Ryan
parent 7d2341b40d
commit edef2fd8ae
No known key found for this signature in database
GPG Key ID: 2765A792E42CE07A
2 changed files with 126 additions and 154 deletions

View File

@ -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
```
@ -240,10 +255,8 @@ class BeaconState(phase0.BeaconState):
online_countdown: Bytes[VALIDATOR_REGISTRY_LIMIT]
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:
@ -548,25 +564,25 @@ def process_attestations(state: BeaconState, block: BeaconBlock, attestations: S
participating_attestations.append(attestation)
if attestation.data.shard_transition_root == shard_transition_root:
all_participants = all_participants.union(get_attesting_indices(state, attestation.data, attestation.aggregation_bits))
if (
get_total_balance(state, online_indices.intersection(all_participants)) * 3 >=
get_total_balance(state, online_indices.intersection(this_shard_committee)) * 2
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])
# Apply transition
apply_shard_transition(state, shard, block.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):
decrease_balance(state, get_shard_proposer_index(state, shard, slot), shard_state.gasprice * length)
winners.add((shard, shard_transition_root))
success = True
if (
get_total_balance(state, online_indices.intersection(all_participants)) * 3 >=
get_total_balance(state, online_indices.intersection(this_shard_committee)) * 2
and success is False
):
# Attestation <-> shard transition consistency
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_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_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)
```

View File

@ -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