-
Notifications
You must be signed in to change notification settings - Fork 998
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
Starting on phase 1 misc beacon changes #1323
Changes from all commits
3dc7430
fe9fe8a
caadc0d
3f20aca
5dad213
fab37e7
b9fddfe
a273d9e
2ae7323
6560bc4
0fa4491
7132778
36a2283
c5acddc
a0b8e9b
e4e6c4d
cb3e0f2
f9849ca
0cf4545
4f92e7f
49a008d
d424863
fd24308
8255091
e4a18f6
c1f2e92
1392c93
56954ec
72b9781
bcdbf7d
1704389
a4f86a8
6923bdc
b6d854d
fb59160
7175ac5
a509c68
178dd23
01af8e6
9b3cb30
f1caa85
ffdc369
ab4820c
0b38ff0
0f2e814
17702e6
979fa38
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,238 @@ | ||||||||
# Phase 1 miscellaneous beacon chain changes | ||||||||
|
||||||||
## Table of contents | ||||||||
|
||||||||
<!-- TOC --> | ||||||||
|
||||||||
- [Phase 1 miscellaneous beacon chain changes](#phase-1-miscellaneous-beacon-chain-changes) | ||||||||
- [Table of contents](#table-of-contents) | ||||||||
- [Configuration](#configuration) | ||||||||
- [Containers](#containers) | ||||||||
- [`CompactCommittee`](#compactcommittee) | ||||||||
- [`ShardReceiptProof`](#shardreceiptproof) | ||||||||
- [Helper functions](#helper-functions) | ||||||||
- [`pack_compact_validator`](#pack_compact_validator) | ||||||||
- [`unpack_compact_validator`](#unpack_compact_validator) | ||||||||
- [`committee_to_compact_committee`](#committee_to_compact_committee) | ||||||||
- [`verify_merkle_proof`](#verify_merkle_proof) | ||||||||
- [`compute_historical_state_generalized_index`](#compute_historical_state_generalized_index) | ||||||||
- [`get_generalized_index_of_crosslink_header`](#get_generalized_index_of_crosslink_header) | ||||||||
- [`process_shard_receipt_proof`](#process_shard_receipt_proof) | ||||||||
- [Changes](#changes) | ||||||||
- [Phase 0 container updates](#phase-0-container-updates) | ||||||||
- [`BeaconState`](#beaconstate) | ||||||||
- [`BeaconBlockBody`](#beaconblockbody) | ||||||||
- [Persistent committees](#persistent-committees) | ||||||||
- [Shard receipt processing](#shard-receipt-processing) | ||||||||
|
||||||||
<!-- /TOC --> | ||||||||
|
||||||||
## Configuration | ||||||||
|
||||||||
| Name | Value | Unit | Duration | ||||||||
| - | - | - | - | | ||||||||
| `MAX_SHARD_RECEIPT_PROOFS` | `2**0` (= 1) | - | - | | ||||||||
| `PERIOD_COMMITTEE_ROOT_LENGTH` | `2**8` (= 256) | periods | ~9 months | | ||||||||
| `MINOR_REWARD_QUOTIENT` | `2**8` (=256) | - | - | | ||||||||
|
||||||||
## Containers | ||||||||
|
||||||||
#### `CompactCommittee` | ||||||||
|
||||||||
```python | ||||||||
class CompactCommittee(Container): | ||||||||
pubkeys: List[BLSPubkey, MAX_VALIDATORS_PER_COMMITTEE] | ||||||||
compact_validators: List[uint64, MAX_VALIDATORS_PER_COMMITTEE] | ||||||||
``` | ||||||||
|
||||||||
#### `ShardReceiptProof` | ||||||||
|
||||||||
```python | ||||||||
class ShardReceiptProof(Container): | ||||||||
shard: Shard | ||||||||
proof: List[Hash, PLACEHOLDER] | ||||||||
receipt: List[ShardReceiptDelta, PLACEHOLDER] | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Going to merge this and handle the conflict after reviewing #1383. Thanks for pointing this out :) |
||||||||
``` | ||||||||
|
||||||||
## Helper functions | ||||||||
|
||||||||
#### `pack_compact_validator` | ||||||||
|
||||||||
```python | ||||||||
def pack_compact_validator(index: int, slashed: bool, balance_in_increments: int) -> int: | ||||||||
""" | ||||||||
Creates a compact validator object representing index, slashed status, and compressed balance. | ||||||||
Takes as input balance-in-increments (// EFFECTIVE_BALANCE_INCREMENT) to preserve symmetry with | ||||||||
the unpacking function. | ||||||||
""" | ||||||||
return (index << 16) + (slashed << 15) + balance_in_increments | ||||||||
``` | ||||||||
|
||||||||
#### `unpack_compact_validator` | ||||||||
|
||||||||
```python | ||||||||
def unpack_compact_validator(compact_validator: int) -> Tuple[int, bool, int]: | ||||||||
""" | ||||||||
Returns validator index, slashed, balance // EFFECTIVE_BALANCE_INCREMENT | ||||||||
""" | ||||||||
return compact_validator >> 16, bool((compact_validator >> 15) % 2), compact_validator & (2**15 - 1) | ||||||||
``` | ||||||||
|
||||||||
#### `committee_to_compact_committee` | ||||||||
|
||||||||
```python | ||||||||
def committee_to_compact_committee(state: BeaconState, committee: Sequence[ValidatorIndex]) -> CompactCommittee: | ||||||||
""" | ||||||||
Given a state and a list of validator indices, outputs the CompactCommittee representing them. | ||||||||
""" | ||||||||
validators = [state.validators[i] for i in committee] | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this needed? This function could theoretically be used for different kinds of committees; doesn't seem wise to put a maximum designed around a specific type of committee. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a safety check since the size of class CompactCommittee(Container):
pubkeys: List[BLSPubkey, MAX_VALIDATORS_PER_COMMITTEE]
compact_validators: List[uint64, MAX_VALIDATORS_PER_COMMITTEE] It leads to one more implicit config validity condition of that If we want to make this function could be used for different kinds of committees, how about refactoring this function into: def pack_committee(state: BeaconState, committee: Sequence[ValidatorIndex]) -> Tuple[Sequence[BLSPubkey], Sequence[int]]:
validators = [state.validators[i] for i in committee]
compact_validators = [
pack_compact_validator(i, v.slashed, v.effective_balance // EFFECTIVE_BALANCE_INCREMENT)
for i, v in zip(committee, validators)
]
pubkeys = [v.pubkey for v in validators]
return pubkeys, compact_validators
def committee_to_compact_committee(state: BeaconState, committee: Sequence[ValidatorIndex]) -> CompactCommittee:
assert len(committee) <= MAX_VALIDATORS_PER_COMMITTEE
pubkeys, compact_validators = pack_committee(state, committee)
return CompactCommittee(pubkeys=pubkeys, compact_validators=compact_validators) |
||||||||
compact_validators = [ | ||||||||
pack_compact_validator(i, v.slashed, v.effective_balance // EFFECTIVE_BALANCE_INCREMENT) | ||||||||
for i, v in zip(committee, validators) | ||||||||
] | ||||||||
pubkeys = [v.pubkey for v in validators] | ||||||||
return CompactCommittee(pubkeys=pubkeys, compact_validators=compact_validators) | ||||||||
``` | ||||||||
|
||||||||
#### `verify_merkle_proof` | ||||||||
|
||||||||
```python | ||||||||
def verify_merkle_proof(leaf: Hash, proof: Sequence[Hash], index: GeneralizedIndex, root: Hash) -> bool: | ||||||||
hwwhww marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
assert len(proof) == get_generalized_index_length(index) | ||||||||
vbuterin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
for i, h in enumerate(proof): | ||||||||
if get_generalized_index_bit(index, i): | ||||||||
leaf = hash(h + leaf) | ||||||||
else: | ||||||||
leaf = hash(leaf + h) | ||||||||
return leaf == root | ||||||||
``` | ||||||||
|
||||||||
#### `compute_historical_state_generalized_index` | ||||||||
|
||||||||
```python | ||||||||
def compute_historical_state_generalized_index(earlier: ShardSlot, later: ShardSlot) -> GeneralizedIndex: | ||||||||
djrtwo marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
""" | ||||||||
Computes the generalized index of the state root of slot `frm` based on the state root of slot `to`. | ||||||||
Relies on the `history_acc` in the `ShardState`, where `history_acc[i]` maintains the most recent 2**i'th | ||||||||
slot state. Works by tracing a `log(later-earlier)` step path from `later` to `earlier` through intermediate | ||||||||
blocks at the next available multiples of descending powers of two. | ||||||||
""" | ||||||||
o = GeneralizedIndex(1) | ||||||||
for i in range(HISTORY_ACCUMULATOR_VECTOR - 1, -1, -1): | ||||||||
if (later - 1) & 2**i > (earlier - 1) & 2**i: | ||||||||
later = later - ((later - 1) % 2**i) - 1 | ||||||||
o = concat_generalized_indices(o, GeneralizedIndex(get_generalized_index(ShardState, ['history_acc', i]))) | ||||||||
return o | ||||||||
``` | ||||||||
|
||||||||
#### `get_generalized_index_of_crosslink_header` | ||||||||
|
||||||||
```python | ||||||||
def get_generalized_index_of_crosslink_header(index: int) -> GeneralizedIndex: | ||||||||
""" | ||||||||
Gets the generalized index for the root of the index'th header in a crosslink. | ||||||||
""" | ||||||||
MAX_CROSSLINK_SIZE = ( | ||||||||
SHARD_BLOCK_SIZE_LIMIT * SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH * MAX_EPOCHS_PER_CROSSLINK | ||||||||
) | ||||||||
assert MAX_CROSSLINK_SIZE == get_previous_power_of_two(MAX_CROSSLINK_SIZE) | ||||||||
return GeneralizedIndex(MAX_CROSSLINK_SIZE // SHARD_HEADER_SIZE + index) | ||||||||
``` | ||||||||
|
||||||||
#### `process_shard_receipt_proof` | ||||||||
|
||||||||
```python | ||||||||
def process_shard_receipt_proof(state: BeaconState, receipt_proof: ShardReceiptProof) -> None: | ||||||||
""" | ||||||||
Processes a ShardReceipt object. | ||||||||
""" | ||||||||
SHARD_SLOTS_PER_EPOCH = SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH | ||||||||
receipt_slot = ( | ||||||||
state.next_shard_receipt_period[receipt_proof.shard] * | ||||||||
SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD | ||||||||
) | ||||||||
first_slot_in_last_crosslink = state.current_crosslinks[receipt_proof.shard].start_epoch * SHARD_SLOTS_PER_EPOCH | ||||||||
gindex = concat_generalized_indices( | ||||||||
get_generalized_index_of_crosslink_header(0), | ||||||||
GeneralizedIndex(get_generalized_index(ShardBlockHeader, 'state_root')), | ||||||||
compute_historical_state_generalized_index(receipt_slot, first_slot_in_last_crosslink), | ||||||||
GeneralizedIndex(get_generalized_index(ShardState, 'receipt_root')) | ||||||||
) | ||||||||
assert verify_merkle_proof( | ||||||||
leaf=hash_tree_root(receipt_proof.receipt), | ||||||||
proof=receipt_proof.proof, | ||||||||
index=gindex, | ||||||||
root=state.current_crosslinks[receipt_proof.shard].data_root | ||||||||
) | ||||||||
for delta in receipt_proof.receipt: | ||||||||
if get_current_epoch(state) < state.validators[delta.index].withdrawable_epoch: | ||||||||
increase_amount = ( | ||||||||
state.validators[delta.index].effective_balance * delta.reward_coefficient // REWARD_COEFFICIENT_BASE | ||||||||
) | ||||||||
increase_balance(state, delta.index, increase_amount) | ||||||||
decrease_balance(state, delta.index, delta.block_fee) | ||||||||
state.next_shard_receipt_period[receipt_proof.shard] += 1 | ||||||||
proposer_index = get_beacon_proposer_index(state) | ||||||||
increase_balance(state, proposer_index, Gwei(get_base_reward(state, proposer_index) // MINOR_REWARD_QUOTIENT)) | ||||||||
``` | ||||||||
|
||||||||
## Changes | ||||||||
|
||||||||
### Phase 0 container updates | ||||||||
|
||||||||
Add the following fields to the end of the specified container objects. | ||||||||
|
||||||||
#### `BeaconState` | ||||||||
|
||||||||
```python | ||||||||
class BeaconState(Container): | ||||||||
# Period committees | ||||||||
period_committee_roots: Vector[Hash, PERIOD_COMMITTEE_ROOT_LENGTH] | ||||||||
next_shard_receipt_period: Vector[uint64, SHARD_COUNT] | ||||||||
``` | ||||||||
|
||||||||
`period_committee_roots` values are initialized to `Bytes32()` (empty bytes value). | ||||||||
`next_shard_receipt_period` values are initialized to `compute_epoch_of_slot(PHASE_1_FORK_SLOT) // EPOCHS_PER_SHARD_PERIOD`. | ||||||||
|
||||||||
#### `BeaconBlockBody` | ||||||||
|
||||||||
```python | ||||||||
class BeaconBlockBody(Container): | ||||||||
shard_receipt_proofs: List[ShardReceiptProof, MAX_SHARD_RECEIPT_PROOFS] | ||||||||
``` | ||||||||
|
||||||||
`shard_receipt_proofs` is initialized to `[]`. | ||||||||
|
||||||||
### Persistent committees | ||||||||
|
||||||||
Run `update_period_committee` immediately before `process_final_updates`: | ||||||||
|
||||||||
```python | ||||||||
# begin insert @update_period_committee | ||||||||
update_period_committee(state) | ||||||||
# end insert @update_period_committee | ||||||||
def update_period_committee(state: BeaconState) -> None: | ||||||||
""" | ||||||||
Updates period committee roots at boundary blocks. | ||||||||
""" | ||||||||
if (get_current_epoch(state) + 1) % EPOCHS_PER_SHARD_PERIOD == 0: | ||||||||
period = (get_current_epoch(state) + 1) // EPOCHS_PER_SHARD_PERIOD | ||||||||
committees = Vector[CompactCommittee, SHARD_COUNT]([ | ||||||||
committee_to_compact_committee( | ||||||||
state, | ||||||||
get_period_committee(state, Epoch(get_current_epoch(state) + 1), Shard(shard)), | ||||||||
) | ||||||||
for shard in range(SHARD_COUNT) | ||||||||
]) | ||||||||
state.period_committee_roots[period % PERIOD_COMMITTEE_ROOT_LENGTH] = hash_tree_root(committees) | ||||||||
``` | ||||||||
|
||||||||
### Shard receipt processing | ||||||||
|
||||||||
Run `process_shard_receipt_proof` on each `ShardReceiptProof` during block processing. | ||||||||
|
||||||||
```python | ||||||||
# begin insert @process_shard_receipt_proofs | ||||||||
(body.shard_receipt_proofs, process_shard_receipt_proof), | ||||||||
# end insert @process_shard_receipt_proofs | ||||||||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about adding a custom type
CompactValidator: uint64
?Edit: just realized that
CompactValidator
andunpack_compact_validator
are defined insync_protocol.md
. I'd say:unpack_compact_validator
from1_beacon-chain-misc.md
since it's not used here.CompactValidator
definition to1_beacon-chain-misc.md
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where would it be put though? In the light client file that actually uses it? I can see how that's theoretically better in one way, but it has a big disadvantage, which is that pack and unpack are no longer beside each other. So I'd still favor putting both in the same file, to make it easier for readers to see that they are inverses.