2019-07-27 12:17:06 +00:00
|
|
|
# Phase 1 miscellaneous beacon chain changes
|
|
|
|
|
|
|
|
## Table of contents
|
|
|
|
|
|
|
|
<!-- TOC -->
|
|
|
|
|
2019-07-30 15:58:33 +00:00
|
|
|
- [Phase 1 miscellaneous beacon chain changes](#phase-1-miscellaneous-beacon-chain-changes)
|
|
|
|
- [Table of contents](#table-of-contents)
|
2019-08-15 10:51:11 +00:00
|
|
|
- [Configuration](#configuration)
|
2019-08-23 18:44:22 +00:00
|
|
|
- [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)
|
|
|
|
- [`get_previous_power_of_2`](#get_previous_power_of_2)
|
|
|
|
- [`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)
|
2019-07-30 15:58:33 +00:00
|
|
|
- [Changes](#changes)
|
2019-08-23 18:44:22 +00:00
|
|
|
- [Phase 0 container updates](#phase-0-container-updates)
|
|
|
|
- [`BeaconState`](#beaconstate)
|
|
|
|
- [`BeaconBlockBody`](#beaconblockbody)
|
2019-07-30 15:58:33 +00:00
|
|
|
- [Persistent committees](#persistent-committees)
|
|
|
|
- [Shard receipt processing](#shard-receipt-processing)
|
2019-07-27 12:17:06 +00:00
|
|
|
|
|
|
|
<!-- /TOC -->
|
|
|
|
|
2019-08-15 10:51:11 +00:00
|
|
|
## Configuration
|
|
|
|
|
|
|
|
| Name | Value | Unit | Duration
|
|
|
|
| - | - | - | - |
|
|
|
|
| `MAX_SHARD_RECEIPT_PROOFS` | `2**0` (= 1) | - | - |
|
|
|
|
| `PERSISTENT_COMMITTEE_ROOT_LENGTH` | `2**8` (= 256) | periods | ~9 months |
|
2019-08-23 18:44:22 +00:00
|
|
|
| `MICRO_REWARD` | `Gwei(2**0)` (=1) | Gwei | - |
|
2019-08-15 10:51:11 +00:00
|
|
|
|
2019-08-23 18:44:22 +00:00
|
|
|
## Containers
|
2019-07-30 14:15:18 +00:00
|
|
|
|
2019-08-06 14:14:45 +00:00
|
|
|
#### `CompactCommittee`
|
|
|
|
|
|
|
|
```python
|
|
|
|
class CompactCommittee(Container):
|
|
|
|
pubkeys: List[BLSPubkey, MAX_VALIDATORS_PER_COMMITTEE]
|
|
|
|
compact_validators: List[uint64, MAX_VALIDATORS_PER_COMMITTEE]
|
|
|
|
```
|
|
|
|
|
2019-07-30 14:15:18 +00:00
|
|
|
#### `ShardReceiptProof`
|
|
|
|
|
|
|
|
```python
|
|
|
|
class ShardReceiptProof(Container):
|
2019-08-01 19:39:32 +00:00
|
|
|
shard: Shard
|
|
|
|
proof: List[Hash, PLACEHOLDER]
|
|
|
|
receipt: List[ShardReceiptDelta, PLACEHOLDER]
|
2019-07-30 14:15:18 +00:00
|
|
|
```
|
|
|
|
|
2019-08-23 18:44:22 +00:00
|
|
|
## Helper functions
|
2019-07-27 12:17:06 +00:00
|
|
|
|
|
|
|
#### `pack_compact_validator`
|
|
|
|
|
|
|
|
```python
|
2019-08-13 10:12:51 +00:00
|
|
|
def pack_compact_validator(index: int, slashed: bool, balance_in_increments: int) -> int:
|
2019-07-27 12:17:06 +00:00
|
|
|
"""
|
|
|
|
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
|
|
|
|
```
|
|
|
|
|
2019-07-30 14:15:18 +00:00
|
|
|
#### `unpack_compact_validator`
|
2019-07-27 12:17:06 +00:00
|
|
|
|
|
|
|
```python
|
2019-08-13 10:14:51 +00:00
|
|
|
def unpack_compact_validator(compact_validator: int) -> Tuple[int, bool, int]:
|
2019-07-27 12:17:06 +00:00
|
|
|
"""
|
|
|
|
Returns validator index, slashed, balance // EFFECTIVE_BALANCE_INCREMENT
|
|
|
|
"""
|
|
|
|
return compact_validator >> 16, (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:
|
2019-07-30 15:55:58 +00:00
|
|
|
"""
|
|
|
|
Given a state and a list of validator indices, outputs the CompactCommittee representing them.
|
|
|
|
"""
|
2019-07-27 12:17:06 +00:00
|
|
|
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 CompactCommittee(pubkeys=pubkeys, compact_validators=compact_validators)
|
|
|
|
```
|
|
|
|
|
2019-07-30 14:01:34 +00:00
|
|
|
#### `get_previous_power_of_2`
|
|
|
|
|
|
|
|
```python
|
|
|
|
def get_previous_power_of_2(x: int) -> int:
|
|
|
|
return x if x <= 2 else 2 * get_previous_power_of_2(x // 2)
|
|
|
|
```
|
|
|
|
|
2019-07-30 15:55:58 +00:00
|
|
|
#### `verify_merkle_proof`
|
|
|
|
|
|
|
|
```python
|
|
|
|
def verify_merkle_proof(leaf: Hash, proof: Sequence[Hash], index: GeneralizedIndex, root: Hash) -> bool:
|
2019-08-01 14:58:15 +00:00
|
|
|
assert len(proof) == get_generalized_index_length(index)
|
2019-07-30 15:55:58 +00:00
|
|
|
for i, h in enumerate(proof):
|
2019-08-01 14:58:15 +00:00
|
|
|
if get_generalized_index_bit(index, i):
|
2019-07-30 15:55:58 +00:00
|
|
|
leaf = hash(h + leaf)
|
|
|
|
else:
|
|
|
|
leaf = hash(leaf + h)
|
|
|
|
return leaf == root
|
|
|
|
```
|
2019-07-30 14:01:34 +00:00
|
|
|
|
|
|
|
#### `compute_historical_state_generalized_index`
|
|
|
|
|
|
|
|
```python
|
2019-07-30 15:55:58 +00:00
|
|
|
def compute_historical_state_generalized_index(earlier: ShardSlot, later: ShardSlot) -> GeneralizedIndex:
|
|
|
|
"""
|
|
|
|
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.
|
|
|
|
"""
|
2019-07-30 14:01:34 +00:00
|
|
|
o = GeneralizedIndex(1)
|
|
|
|
for i in range(63, -1, -1):
|
2019-08-01 19:39:32 +00:00
|
|
|
if (later - 1) & 2**i > (earlier - 1) & 2**i:
|
|
|
|
later = later - ((later - 1) % 2**i) - 1
|
|
|
|
o = concat_generalized_indices(o, get_generalized_index(ShardState, 'history_acc', i))
|
2019-07-30 14:01:34 +00:00
|
|
|
return o
|
|
|
|
```
|
|
|
|
|
|
|
|
#### `get_generalized_index_of_crosslink_header`
|
|
|
|
|
|
|
|
```python
|
|
|
|
def get_generalized_index_of_crosslink_header(index: int) -> GeneralizedIndex:
|
2019-07-30 15:55:58 +00:00
|
|
|
"""
|
|
|
|
Gets the generalized index for the root of the index'th header in a crosslink.
|
|
|
|
"""
|
2019-08-23 18:44:22 +00:00
|
|
|
MAX_CROSSLINK_SIZE = (
|
|
|
|
SHARD_BLOCK_SIZE_LIMIT * SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH * MAX_EPOCHS_PER_CROSSLINK
|
|
|
|
)
|
2019-07-30 14:01:34 +00:00
|
|
|
assert MAX_CROSSLINK_SIZE == get_previous_power_of_2(MAX_CROSSLINK_SIZE)
|
|
|
|
return GeneralizedIndex(MAX_CROSSLINK_SIZE // SHARD_HEADER_SIZE + index)
|
|
|
|
```
|
|
|
|
|
2019-08-14 21:35:26 +00:00
|
|
|
#### `process_shard_receipt_proof`
|
2019-07-30 14:01:34 +00:00
|
|
|
|
|
|
|
```python
|
2019-08-14 21:35:26 +00:00
|
|
|
def process_shard_receipt_proof(state: BeaconState, receipt_proof: ShardReceiptProof):
|
2019-07-30 15:55:58 +00:00
|
|
|
"""
|
|
|
|
Processes a ShardReceipt object.
|
|
|
|
"""
|
2019-07-30 14:15:18 +00:00
|
|
|
receipt_slot = state.next_shard_receipt_period[receipt_proof.shard] * SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD
|
|
|
|
first_slot_in_last_crosslink = state.current_crosslinks[receipt_proof.shard].start_epoch * SLOTS_PER_EPOCH
|
2019-07-30 14:01:34 +00:00
|
|
|
gindex = concat_generalized_indices(
|
|
|
|
get_generalized_index_of_crosslink_header(0),
|
2019-08-01 19:39:32 +00:00
|
|
|
get_generalized_index(ShardBlockHeader, 'state_root'),
|
|
|
|
compute_historical_state_generalized_index(receipt_slot, first_slot_in_last_crosslink),
|
2019-07-30 14:01:34 +00:00
|
|
|
get_generalized_index(ShardState, 'receipt_root')
|
|
|
|
)
|
|
|
|
assert verify_merkle_proof(
|
2019-07-30 14:15:18 +00:00
|
|
|
leaf=hash_tree_root(receipt_proof.receipt),
|
|
|
|
proof=receipt_proof.proof,
|
2019-07-30 14:01:34 +00:00
|
|
|
index=gindex,
|
2019-08-23 18:44:22 +00:00
|
|
|
root=state.current_crosslinks[receipt_proof.shard].data_root
|
2019-07-30 14:01:34 +00:00
|
|
|
)
|
2019-07-30 14:15:18 +00:00
|
|
|
for delta in receipt_proof.receipt:
|
2019-08-07 16:13:02 +00:00
|
|
|
if get_current_epoch(state) < state.validators[delta.index].withdrawable_epoch:
|
2019-08-23 18:44:22 +00:00
|
|
|
increase_amount = (
|
|
|
|
state.validators[delta.index].effective_balance * delta.reward_coefficient // REWARD_COEFFICIENT_BASE
|
|
|
|
)
|
|
|
|
increase_balance(state, delta.index, increase_amount)
|
2019-08-07 16:13:02 +00:00
|
|
|
decrease_balance(state, delta.index, delta.block_fee)
|
2019-07-30 14:15:18 +00:00
|
|
|
state.next_shard_receipt_period[receipt_proof.shard] += 1
|
|
|
|
increase_balance(state, get_beacon_proposer_index(state), MICRO_REWARD)
|
2019-07-30 14:01:34 +00:00
|
|
|
```
|
|
|
|
|
2019-07-27 12:17:06 +00:00
|
|
|
## Changes
|
|
|
|
|
2019-08-23 18:16:57 +00:00
|
|
|
### Phase 0 container updates
|
|
|
|
|
|
|
|
Add the following fields to the end of the specified container objects.
|
2019-07-27 12:17:06 +00:00
|
|
|
|
2019-08-23 18:16:57 +00:00
|
|
|
#### `BeaconState`
|
2019-07-27 12:17:06 +00:00
|
|
|
|
2019-08-01 15:42:39 +00:00
|
|
|
```python
|
2019-08-23 18:16:57 +00:00
|
|
|
class BeaconState(Container):
|
|
|
|
# Persistent committees
|
2019-08-15 10:51:11 +00:00
|
|
|
persistent_committee_roots: Vector[Hash, PERSISTENT_COMMITTEE_ROOT_LENGTH]
|
2019-08-23 10:33:25 +00:00
|
|
|
next_shard_receipt_period: Vector[uint64, SHARD_COUNT]
|
2019-08-01 15:42:39 +00:00
|
|
|
```
|
2019-08-23 18:16:57 +00:00
|
|
|
|
|
|
|
`persistent_committee_roots` values are initialized to `Bytes32()` (empty bytes value).
|
|
|
|
`next_shard_receipt_period` values are initialized to `PHASE_1_FORK_SLOT // SLOTS_PER_EPOCH // 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
|
2019-07-27 12:17:06 +00:00
|
|
|
|
2019-08-01 15:42:39 +00:00
|
|
|
Run `update_persistent_committee` immediately before `process_final_updates`:
|
2019-07-27 12:17:06 +00:00
|
|
|
|
|
|
|
```python
|
2019-08-01 15:42:39 +00:00
|
|
|
# begin insert @update_persistent_committee
|
|
|
|
update_persistent_committee(state)
|
|
|
|
# end insert @update_persistent_committee
|
|
|
|
def update_persistent_committee(state: BeaconState) -> None:
|
2019-07-30 15:55:58 +00:00
|
|
|
"""
|
|
|
|
Updates persistent committee roots at boundary blocks.
|
|
|
|
"""
|
2019-07-27 12:17:06 +00:00
|
|
|
if (get_current_epoch(state) + 1) % EPOCHS_PER_SHARD_PERIOD == 0:
|
2019-08-15 10:51:11 +00:00
|
|
|
period = (get_current_epoch(state) + 1) // EPOCHS_PER_SHARD_PERIOD
|
2019-07-27 12:17:06 +00:00
|
|
|
committees = Vector[CompactCommittee, SHARD_COUNT]([
|
|
|
|
committee_to_compact_committee(state, get_period_committee(state, get_current_epoch(state) + 1, i))
|
|
|
|
for i in range(SHARD_COUNT)
|
|
|
|
])
|
2019-08-15 10:51:11 +00:00
|
|
|
state.persistent_committee_roots[period % PERSISTENT_COMMITTEE_ROOT_LENGTH] = hash_tree_root(committees)
|
2019-07-27 12:17:06 +00:00
|
|
|
```
|
2019-07-30 14:15:18 +00:00
|
|
|
|
|
|
|
### Shard receipt processing
|
|
|
|
|
2019-08-23 18:16:57 +00:00
|
|
|
Run `process_shard_receipt_proof` on each `ShardReceiptProof` during block processing.
|
2019-08-01 15:42:39 +00:00
|
|
|
|
|
|
|
```python
|
|
|
|
# begin insert @process_shard_receipts
|
2019-08-23 18:16:57 +00:00
|
|
|
(body.shard_receipt_proofs, process_shard_receipt_proof),
|
2019-08-01 15:42:39 +00:00
|
|
|
# end insert @process_shard_receipts
|
|
|
|
```
|