From 4b2b5815c919ed7263ae1a5105854a9b73ed1ef9 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Mon, 29 Jul 2019 16:43:55 -0400 Subject: [PATCH 01/18] Add shard state transition function --- specs/core/1_shard-data-chains.md | 216 +++++++++++++++++++++++------- 1 file changed, 170 insertions(+), 46 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index fc839930f..1c94741d8 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -34,6 +34,7 @@ - [`pad`](#pad) - [`flatten_shard_header`](#flatten_shard_header) - [`compute_crosslink_data_root`](#compute_crosslink_data_root) + - [`get_default_shard_state`](#get_default_shard_state) - [Object validity](#object-validity) - [Shard blocks](#shard-blocks) - [Beacon attestations](#beacon-attestations) @@ -61,8 +62,11 @@ We define the following Python custom types for type hinting and readability: | - | - | | `SHARD_HEADER_SIZE` | `2**9` (= 512) | | `SHARD_BLOCK_SIZE_LIMIT` | `2**16` (= 65,536) | +| `SHARD_BLOCK_SIZE_TARGET` | `2**14` (= 16,384) | | `SHARD_SLOTS_PER_BEACON_SLOT` | `2**1` (= 2) | | `MAX_PERSISTENT_COMMITTEE_SIZE` | `2**7` (= 128) | +| `REWARD_COEFFICIENT_BASE` | `2**20` ( = 1,048,576) | +| `BASEFEE_ADJUSTMENT_FACTOR` | `2**3` (= 8) | ### Initial values @@ -148,6 +152,31 @@ class ExtendedShardBlockCore(Container): attester_bitfield: Bitvector[MAX_PERSISTENT_COMMITTEE_SIZE * 2] ``` +### `ShardState` + +```python +class ShardState(Container): + history_acc: Vector[Hash, 64] + earlier_committee_rewards: List[uint64, MAX_PERSISTENT_COMMITTEE_SIZE] + later_committee_rewards: List[uint64, MAX_PERSISTENT_COMMITTEE_SIZE] + earlier_committee_fees: List[Gwei, MAX_PERSISTENT_COMMITTEE_SIZE] + later_committee_fees: List[Gwei, MAX_PERSISTENT_COMMITTEE_SIZE] + basefee: Gwei + slot: ShardSlot + shard: Shard + most_recent_block_core: ShardBlockCore + receipt_root: Hash +``` + +### `ShardReceiptDelta` + +```python +class ShardReceiptDelta(Container): + index: ValidatorIndex + reward_coefficient: uint64 + block_fee: Gwei +``` + ## Helper functions ### `compute_slot_of_shard_slot` @@ -167,7 +196,7 @@ def compute_epoch_of_shard_slot(slot: ShardSlot) -> Epoch: ### `get_shard_period_start_epoch` ```python -def get_shard_period_start_epoch(epoch: Epoch, lookback: Epoch=Epoch(0)) -> Epoch: +def get_shard_period_start_epoch(epoch: Epoch, lookback: uint64=0) -> Epoch: return Epoch(epoch - (epoch % EPOCHS_PER_SHARD_PERIOD) - lookback * EPOCHS_PER_SHARD_PERIOD) ``` @@ -201,8 +230,8 @@ def get_persistent_committee(state: BeaconState, """ epoch = compute_epoch_of_shard_slot(slot) - earlier_committee = get_period_committee(state, get_shard_period_start_epoch(epoch, lookback=Epoch(2)), shard) - later_committee = get_period_committee(state, get_shard_period_start_epoch(epoch, lookback=Epoch(1)), shard) + earlier_committee = get_period_committee(state, get_shard_period_start_epoch(epoch, lookback=2), shard) + later_committee = get_period_committee(state, get_shard_period_start_epoch(epoch, lookback=1), shard) # Take not-yet-cycled-out validators from earlier committee and already-cycled-in validators from # later committee; return a sorted list of the union of the two, deduplicated @@ -299,60 +328,130 @@ def compute_crosslink_data_root(blocks: Sequence[ShardBlock]) -> Hash: return hash_tree_root(BytesN[MAX_SIZE](pad(header + footer, MAX_SIZE))) ``` -## Object validity - -### Shard blocks - -Let: - -- `beacon_blocks` be the `BeaconBlock` list such that `beacon_blocks[slot]` is the canonical `BeaconBlock` at slot `slot` -- `beacon_state` be the canonical `BeaconState` after processing `beacon_blocks[-1]` -- `shard` is the shard ID -- `valid_shard_blocks` be the list of valid `ShardBlock`, recursively defined -- `candidate` be a candidate `ShardBlock` for which validity is to be determined by running `is_valid_shard_block` +### `get_default_shard_state` ```python -def is_valid_shard_block(beacon_state: BeaconState, - beacon_blocks: Sequence[BeaconBlock], - shard: Shard, - valid_shard_blocks: Sequence[ShardBlock], - candidate: ShardBlock) -> bool: - # Check if block is already determined valid - for _, block in enumerate(valid_shard_blocks): - if candidate == block: - return True +def get_default_shard_state(beacon_state: BeaconState, shard: Shard) -> ShardState: + earlier_committee = get_period_committee(beacon_state, PHASE_1_FORK_SLOT - SHARD_SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD * 2, shard) + later_committee = get_period_committee(beacon_state, PHASE_1_FORK_SLOT - SHARD_SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD, shard) + return ShardState( + basefee=1, + shard=shard, + slot=PHASE_1_FORK_SLOT, + earlier_committee_rewards=[REWARD_COEFFICIENT_BASE for _ in range(len(earlier_committee))], + later_committee_rewards=[REWARD_COEFFICIENT_BASE for _ in range(len(later_committee))], + earlier_committee_fees=[0 for _ in range(len(earlier_committee))], + later_committee_fees=[0 for _ in range(len(later_committee))], + ) +``` +## Object validity + +### Shard block validation: preliminary + +Accept a shard block `block` only if all of the following are correct: + +* Either `block.core.parent_root == ZERO_HASH` or a block `parent` such that `hash_tree_root(parent.core) == block.core.parent_root` has already been accepted. +* `block.core.beacon_chain_root == get_block_root(head_beacon_state, compute_epoch_of_shard_slot(parent.core.slot))` where `head_beacon_state` is the current beacon chain head state. Alternatively phrased, a beacon chain block `beacon_ref` such that `signing_root(beacon_ref) == block.core.beacon_chain_root` has already been accepted and is part of the canonical chain, and no block with slot `beacon_ref.slot < slot <= compute_start_slot_of_epoch(compute_epoch_of_shard_slot(parent.core.slot))` is part of the canonical chain. +* Let `beacon_state` be the state where `beacon_ref.state_root == hash_tree_root(beacon_state)`. Let `prev_state` be the post-state of the `parent` if the `parent` exists, otherwise let it be `get_default_shard_state(beacon_state, shard)` (defined below). `block.core.state_root` must equal the `hash_tree_root` of the state after applying `shard_state_transition(prev_state, beacon_state, block)`. + +Note that these acceptance conditions depend on the canonical beacon chain; when the canonical beacon chain reorganizes, the eligibility of shard blocks should be re-evaluated. + +### Shard state transition function helpers + +```python +def add_reward(state: ShardState, beacon_state: BeaconState, index: ValidatorIndex, delta: uint): + epoch = compute_epoch_of_shard_slot(state.slot) + earlier_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=2), state.shard) + later_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=1), state.shard) + if index in earlier_committee: + state.earlier_committee_rewards[earlier_committee.index(index)] += delta + elif index in later_committee: + state.later_committee_rewards[later_committee.index(index)] += delta + else: + raise Exception("Should never be here") +``` + +```python +def add_fee(state: ShardState, beacon_state: BeaconState, index: ValidatorIndex, delta: uint): + epoch = compute_epoch_of_shard_slot(state.slot) + earlier_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=2), state.shard) + later_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=1), state.shard) + if index in earlier_committee: + state.earlier_committee_fees[earlier_committee.index(index)] += delta + elif index in later_committee: + state.later_committee_fees[later_committee.index(index)] += delta + else: + raise Exception("Should never be here") +``` + +### Shard state transition function + +```python +def shard_state_transition(state: ShardState, beacon_state: BeaconState, block: ShardBlock): + assert block.core.slot > state.slot + for slot in range(state.slot, block.core.slot): + shard_slot_transition(state, beacon_state) + shard_block_transition(state, beacon_state, block) +``` + +```python +def shard_slot_transition(state: ShardState, beacon_state: BeaconState): + # Correct saved state root + if state.most_recent_block_core.state_root == ZERO_HASH: + state.most_recent_block_core.state_root = hash_tree_root(state) + + # Save states in history accumulator + depth = 0 + h = hash_tree_root(state) + while state.slot % 2**depth == 0: + state.history_acc[depth] = h + + # Period transitions + if (state.slot + 1) % (SHARD_SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD) == 0: + epoch = compute_epoch_of_shard_slot(state.slot) + earlier_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=2), state.shard) + later_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=1), state.shard) + state.receipt_root = hash_tree_root(List[ShardReceiptDelta, PLACEHOLDER]([ + ShardReceiptDelta(index, state.earlier_committee_rewards[i], state.earlier_committee_fees[i]) + for i, index in enumerate(committee) + ])) + state.earlier_committee_rewards = state.later_committee_rewards + state.earlier_committee_fees = state.later_committee_fees + state.later_committee_rewards = [REWARD_COEFFICIENT_BASE for _ in range(len(later_committee))], + state.later_committee_fees = [0 for _ in range(len(later_committee))], + else: + state.receipt_root = ZERO_HASH + state.slot += 1 +``` + +```python +def shard_block_transition(state: ShardState, beacon_state: BeaconState, block: ShardBlock): # Check slot number - assert compute_slot_of_shard_slot(candidate.core.slot) >= PHASE_1_FORK_SLOT - - # Check beacon block - beacon_block_slot = compute_start_slot_of_epoch(compute_epoch_of_shard_slot(candidate.core.slot)) - beacon_block = beacon_blocks[beacon_block_slot] - assert candidate.core.beacon_block_root == signing_root(beacon_block) - assert beacon_block.slot <= candidate.core.slot - - # Check state root - assert candidate.core.state_root == Hash() # [to be removed in phase 2] - + assert candidate.core.slot == state.slot + # Check parent block if candidate.core.parent_root != Hash(): - parent_block = next( - (block for block in valid_shard_blocks if hash_tree_root(block.core) == candidate.core.parent_root), - None - ) - assert parent_block is not None - assert parent_block.core.slot < candidate.core.slot - parent_beacon_block_slot = compute_start_slot_of_epoch(compute_epoch_of_shard_slot(parent_block.core.slot)) - assert signing_root(beacon_blocks[parent_beacon_block_slot]) == parent_block.core.beacon_chain_root - + assert candidate.core.parent_root == hash_tree_root(state.most_recent_block_core) + + # Calculate base reward + total_balance = get_total_active_balance(beacon_state) + base_reward = Gwei(REWARD_COEFFICIENT_BASE * BASE_REWARD_FACTOR // integer_squareroot(total_balance) // BASE_REWARDS_PER_EPOCH) + # Check attestations attester_committee = get_persistent_committee(beacon_state, shard, block.core.slot) pubkeys = [] + attestations = 0 + for i, index in enumerate(attester_committee): if block.core.attester_bitfield[i]: pubkeys.append(beacon_state.validators[index].pubkey) - for i in range(len(attester_committee), MAX_PERSISTENT_COMMITTEE_SIZE * 2): + add_reward(state, beacon_state, index, base_reward) + attestations += 1 + + for i in range(len(attester_committee), MAX_PERSISTENT_COMMITTEE_SIZE): assert block.attester_bitfield[i] is False + assert bls_verify( pubkey=bls_aggregate_pubkeys(pubkeys), message_hash=candidate.core.parent_root, @@ -363,14 +462,39 @@ def is_valid_shard_block(beacon_state: BeaconState, # Check proposer proposer_index = get_shard_block_proposer_index(beacon_state, shard, candidate.core.slot) assert proposer_index is not None + add_reward(state, beacon_state, proposer_index, attestations * base_reward // PROPOSER_REWARD_QUOTIENT) assert bls_verify( pubkey=beacon_state.validators[proposer_index].pubkey, message_hash=hash_tree_root(candidate.core), signature=candidate.signatures.proposer_signature, domain=get_domain(beacon_state, DOMAIN_SHARD_PROPOSER, compute_epoch_of_shard_slot(candidate.core.slot)), ) - - return True + + # Process and update block data fees + add_fee(state, beacon_state, proposer_index, state.basefee * len(block.core.data) // SHARD_BLOCK_SIZE_LIMIT) + QUOTIENT = SHARD_BLOCK_SIZE_LIMIT * BASEFEE_ADJUSTMENT_FACTOR) + if len(block.core.data) > SHARD_BLOCK_SIZE_TARGET: + state.basefee += min(1, state.basefee * (len(block.core.data) - SHARD_BLOCK_SIZE_TARGET) // QUOTIENT) + elif len(block.core.data) < SHARD_BLOCK_SIZE_TARGET: + state.basefee -= min(1, state.basefee * (len(block.core.data) - SHARD_BLOCK_SIZE_TARGET) // QUOTIENT) + state.basefee = max(1, min(EFFECTIVE_BALANCE_INCREMENT // EPOCHS_PER_SHARD_PERIOD // SHARD_SLOTS_PER_EPOCH, state.basefee)) + + # Check total bytes + assert block.core.total_bytes == state.most_recent_block_core.total_bytes + len(block.core.data) + + # Update in-state block header + state.most_recent_block_core = ShardBlockCore( + slot=block.core.slot, + beacon_chain_root=block.core.beacon_chain_root, + parent_root=block.core.parent_root, + data_root=block.core.data_root, + state_root=ZERO_HASH, + total_bytes=block.core.total_bytes, + attester_bitfield=block.core.attester_bitfield + ) + + # Check state root + assert hash_tree_root(state) == block.core.state_root ``` ### Beacon attestations From 058e63654d41df84a3e7bffd7bda3e6967fd11e0 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 31 Jul 2019 17:44:33 +0800 Subject: [PATCH 02/18] Fix typo --- specs/core/1_shard-data-chains.md | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 1c94741d8..d9eff358e 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -22,21 +22,24 @@ - [`ShardBlockSignatures`](#shardblocksignatures) - [`ShardBlockCore`](#shardblockcore) - [`ExtendedShardBlockCore`](#extendedshardblockcore) + - [`ShardState`](#shardstate) + - [`ShardReceiptDelta`](#shardreceiptdelta) - [Helper functions](#helper-functions) - - [`compute_epoch_of_shard_slot`](#compute_epoch_of_shard_slot) - [`compute_slot_of_shard_slot`](#compute_slot_of_shard_slot) + - [`compute_epoch_of_shard_slot`](#compute_epoch_of_shard_slot) - [`get_shard_period_start_epoch`](#get_shard_period_start_epoch) - [`get_period_committee`](#get_period_committee) - [`get_persistent_committee`](#get_persistent_committee) - [`get_shard_block_proposer_index`](#get_shard_block_proposer_index) - - [`get_shard_block_attester_committee`](#get_shard_block_attester_committee) - [`get_shard_header`](#get_shard_header) - [`pad`](#pad) - [`flatten_shard_header`](#flatten_shard_header) - [`compute_crosslink_data_root`](#compute_crosslink_data_root) - [`get_default_shard_state`](#get_default_shard_state) - [Object validity](#object-validity) - - [Shard blocks](#shard-blocks) + - [Shard block validation: preliminary](#shard-block-validation-preliminary) + - [Shard state transition function helpers](#shard-state-transition-function-helpers) + - [Shard state transition function](#shard-state-transition-function) - [Beacon attestations](#beacon-attestations) - [Shard fork choice rule](#shard-fork-choice-rule) @@ -332,8 +335,8 @@ def compute_crosslink_data_root(blocks: Sequence[ShardBlock]) -> Hash: ```python def get_default_shard_state(beacon_state: BeaconState, shard: Shard) -> ShardState: - earlier_committee = get_period_committee(beacon_state, PHASE_1_FORK_SLOT - SHARD_SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD * 2, shard) - later_committee = get_period_committee(beacon_state, PHASE_1_FORK_SLOT - SHARD_SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD, shard) + earlier_committee = get_period_committee(beacon_state, PHASE_1_FORK_SLOT - SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD * 2, shard) + later_committee = get_period_committee(beacon_state, PHASE_1_FORK_SLOT - SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD, shard) return ShardState( basefee=1, shard=shard, @@ -360,7 +363,7 @@ Note that these acceptance conditions depend on the canonical beacon chain; when ### Shard state transition function helpers ```python -def add_reward(state: ShardState, beacon_state: BeaconState, index: ValidatorIndex, delta: uint): +def add_reward(state: ShardState, beacon_state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: epoch = compute_epoch_of_shard_slot(state.slot) earlier_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=2), state.shard) later_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=1), state.shard) @@ -373,7 +376,7 @@ def add_reward(state: ShardState, beacon_state: BeaconState, index: ValidatorInd ``` ```python -def add_fee(state: ShardState, beacon_state: BeaconState, index: ValidatorIndex, delta: uint): +def add_fee(state: ShardState, beacon_state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: epoch = compute_epoch_of_shard_slot(state.slot) earlier_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=2), state.shard) later_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=1), state.shard) @@ -388,7 +391,7 @@ def add_fee(state: ShardState, beacon_state: BeaconState, index: ValidatorIndex, ### Shard state transition function ```python -def shard_state_transition(state: ShardState, beacon_state: BeaconState, block: ShardBlock): +def shard_state_transition(state: ShardState, beacon_state: BeaconState, block: ShardBlock) -> None: assert block.core.slot > state.slot for slot in range(state.slot, block.core.slot): shard_slot_transition(state, beacon_state) @@ -396,7 +399,7 @@ def shard_state_transition(state: ShardState, beacon_state: BeaconState, block: ``` ```python -def shard_slot_transition(state: ShardState, beacon_state: BeaconState): +def shard_slot_transition(state: ShardState, beacon_state: BeaconState) -> None: # Correct saved state root if state.most_recent_block_core.state_root == ZERO_HASH: state.most_recent_block_core.state_root = hash_tree_root(state) @@ -408,7 +411,7 @@ def shard_slot_transition(state: ShardState, beacon_state: BeaconState): state.history_acc[depth] = h # Period transitions - if (state.slot + 1) % (SHARD_SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD) == 0: + if (state.slot + 1) % (SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD) == 0: epoch = compute_epoch_of_shard_slot(state.slot) earlier_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=2), state.shard) later_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=1), state.shard) @@ -426,7 +429,7 @@ def shard_slot_transition(state: ShardState, beacon_state: BeaconState): ``` ```python -def shard_block_transition(state: ShardState, beacon_state: BeaconState, block: ShardBlock): +def shard_block_transition(state: ShardState, beacon_state: BeaconState, block: ShardBlock) -> None: # Check slot number assert candidate.core.slot == state.slot @@ -472,12 +475,12 @@ def shard_block_transition(state: ShardState, beacon_state: BeaconState, block: # Process and update block data fees add_fee(state, beacon_state, proposer_index, state.basefee * len(block.core.data) // SHARD_BLOCK_SIZE_LIMIT) - QUOTIENT = SHARD_BLOCK_SIZE_LIMIT * BASEFEE_ADJUSTMENT_FACTOR) + QUOTIENT = SHARD_BLOCK_SIZE_LIMIT * BASEFEE_ADJUSTMENT_FACTOR if len(block.core.data) > SHARD_BLOCK_SIZE_TARGET: state.basefee += min(1, state.basefee * (len(block.core.data) - SHARD_BLOCK_SIZE_TARGET) // QUOTIENT) elif len(block.core.data) < SHARD_BLOCK_SIZE_TARGET: state.basefee -= min(1, state.basefee * (len(block.core.data) - SHARD_BLOCK_SIZE_TARGET) // QUOTIENT) - state.basefee = max(1, min(EFFECTIVE_BALANCE_INCREMENT // EPOCHS_PER_SHARD_PERIOD // SHARD_SLOTS_PER_EPOCH, state.basefee)) + state.basefee = max(1, min(EFFECTIVE_BALANCE_INCREMENT // EPOCHS_PER_SHARD_PERIOD // SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH, state.basefee)) # Check total bytes assert block.core.total_bytes == state.most_recent_block_core.total_bytes + len(block.core.data) From f263b718759e6e94c9c2be5dfc3b3df2945083fa Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 31 Jul 2019 17:50:55 +0800 Subject: [PATCH 03/18] ZERO_HASH -> Hash() --- specs/core/1_shard-data-chains.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index d9eff358e..4d4ab3897 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -354,7 +354,7 @@ def get_default_shard_state(beacon_state: BeaconState, shard: Shard) -> ShardSta Accept a shard block `block` only if all of the following are correct: -* Either `block.core.parent_root == ZERO_HASH` or a block `parent` such that `hash_tree_root(parent.core) == block.core.parent_root` has already been accepted. +* Either `block.core.parent_root == Hash()` or a block `parent` such that `hash_tree_root(parent.core) == block.core.parent_root` has already been accepted. * `block.core.beacon_chain_root == get_block_root(head_beacon_state, compute_epoch_of_shard_slot(parent.core.slot))` where `head_beacon_state` is the current beacon chain head state. Alternatively phrased, a beacon chain block `beacon_ref` such that `signing_root(beacon_ref) == block.core.beacon_chain_root` has already been accepted and is part of the canonical chain, and no block with slot `beacon_ref.slot < slot <= compute_start_slot_of_epoch(compute_epoch_of_shard_slot(parent.core.slot))` is part of the canonical chain. * Let `beacon_state` be the state where `beacon_ref.state_root == hash_tree_root(beacon_state)`. Let `prev_state` be the post-state of the `parent` if the `parent` exists, otherwise let it be `get_default_shard_state(beacon_state, shard)` (defined below). `block.core.state_root` must equal the `hash_tree_root` of the state after applying `shard_state_transition(prev_state, beacon_state, block)`. @@ -401,7 +401,7 @@ def shard_state_transition(state: ShardState, beacon_state: BeaconState, block: ```python def shard_slot_transition(state: ShardState, beacon_state: BeaconState) -> None: # Correct saved state root - if state.most_recent_block_core.state_root == ZERO_HASH: + if state.most_recent_block_core.state_root == Hash(): state.most_recent_block_core.state_root = hash_tree_root(state) # Save states in history accumulator @@ -424,7 +424,7 @@ def shard_slot_transition(state: ShardState, beacon_state: BeaconState) -> None: state.later_committee_rewards = [REWARD_COEFFICIENT_BASE for _ in range(len(later_committee))], state.later_committee_fees = [0 for _ in range(len(later_committee))], else: - state.receipt_root = ZERO_HASH + state.receipt_root = Hash() state.slot += 1 ``` @@ -491,7 +491,7 @@ def shard_block_transition(state: ShardState, beacon_state: BeaconState, block: beacon_chain_root=block.core.beacon_chain_root, parent_root=block.core.parent_root, data_root=block.core.data_root, - state_root=ZERO_HASH, + state_root=Hash(), total_bytes=block.core.total_bytes, attester_bitfield=block.core.attester_bitfield ) From fe2adfa0e231519adc60fb3c8fc2e89abcecc11e Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 31 Jul 2019 18:18:07 +0800 Subject: [PATCH 04/18] Fix many typos and lint errors --- specs/core/1_shard-data-chains.md | 102 +++++++++++++++++++----------- 1 file changed, 65 insertions(+), 37 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 4d4ab3897..bc8a8817f 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -335,8 +335,16 @@ def compute_crosslink_data_root(blocks: Sequence[ShardBlock]) -> Hash: ```python def get_default_shard_state(beacon_state: BeaconState, shard: Shard) -> ShardState: - earlier_committee = get_period_committee(beacon_state, PHASE_1_FORK_SLOT - SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD * 2, shard) - later_committee = get_period_committee(beacon_state, PHASE_1_FORK_SLOT - SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD, shard) + earlier_committee = get_period_committee( + beacon_state, + Epoch(PHASE_1_FORK_SLOT - SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD * 2), + shard, + ) + later_committee = get_period_committee( + beacon_state, + Epoch(PHASE_1_FORK_SLOT - SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD), + shard, + ) return ShardState( basefee=1, shard=shard, @@ -344,7 +352,7 @@ def get_default_shard_state(beacon_state: BeaconState, shard: Shard) -> ShardSta earlier_committee_rewards=[REWARD_COEFFICIENT_BASE for _ in range(len(earlier_committee))], later_committee_rewards=[REWARD_COEFFICIENT_BASE for _ in range(len(later_committee))], earlier_committee_fees=[0 for _ in range(len(earlier_committee))], - later_committee_fees=[0 for _ in range(len(later_committee))], + later_committee_fees=[0 for _ in range(len(later_committee))], ) ``` @@ -365,7 +373,11 @@ Note that these acceptance conditions depend on the canonical beacon chain; when ```python def add_reward(state: ShardState, beacon_state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: epoch = compute_epoch_of_shard_slot(state.slot) - earlier_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=2), state.shard) + earlier_committee = get_period_committee( + beacon_state, + get_shard_period_start_epoch(epoch, lookback=2), + state.shard, + ) later_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=1), state.shard) if index in earlier_committee: state.earlier_committee_rewards[earlier_committee.index(index)] += delta @@ -403,21 +415,29 @@ def shard_slot_transition(state: ShardState, beacon_state: BeaconState) -> None: # Correct saved state root if state.most_recent_block_core.state_root == Hash(): state.most_recent_block_core.state_root = hash_tree_root(state) - + # Save states in history accumulator depth = 0 h = hash_tree_root(state) while state.slot % 2**depth == 0: state.history_acc[depth] = h - + # Period transitions if (state.slot + 1) % (SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD) == 0: epoch = compute_epoch_of_shard_slot(state.slot) - earlier_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=2), state.shard) - later_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=1), state.shard) + earlier_committee = get_period_committee( + beacon_state, + get_shard_period_start_epoch(epoch, lookback=2), + state.shard, + ) + later_committee = get_period_committee( + beacon_state, + get_shard_period_start_epoch(epoch, lookback=1), + state.shard, + ) state.receipt_root = hash_tree_root(List[ShardReceiptDelta, PLACEHOLDER]([ ShardReceiptDelta(index, state.earlier_committee_rewards[i], state.earlier_committee_fees[i]) - for i, index in enumerate(committee) + for i, index in enumerate(earlier_committee) ])) state.earlier_committee_rewards = state.later_committee_rewards state.earlier_committee_fees = state.later_committee_fees @@ -425,66 +445,74 @@ def shard_slot_transition(state: ShardState, beacon_state: BeaconState) -> None: state.later_committee_fees = [0 for _ in range(len(later_committee))], else: state.receipt_root = Hash() - state.slot += 1 + state.slot += ShardSlot(1) ``` ```python def shard_block_transition(state: ShardState, beacon_state: BeaconState, block: ShardBlock) -> None: # Check slot number - assert candidate.core.slot == state.slot - + assert block.core.slot == state.slot + # Check parent block - if candidate.core.parent_root != Hash(): - assert candidate.core.parent_root == hash_tree_root(state.most_recent_block_core) - + if block.core.parent_root != Hash(): + assert block.core.parent_root == hash_tree_root(state.most_recent_block_core) + # Calculate base reward total_balance = get_total_active_balance(beacon_state) - base_reward = Gwei(REWARD_COEFFICIENT_BASE * BASE_REWARD_FACTOR // integer_squareroot(total_balance) // BASE_REWARDS_PER_EPOCH) - + base_reward = Gwei( + REWARD_COEFFICIENT_BASE * BASE_REWARD_FACTOR // integer_squareroot(total_balance) // BASE_REWARDS_PER_EPOCH + ) + # Check attestations - attester_committee = get_persistent_committee(beacon_state, shard, block.core.slot) + attester_committee = get_persistent_committee(beacon_state, state.shard, block.core.slot) pubkeys = [] attestations = 0 - + for i, index in enumerate(attester_committee): if block.core.attester_bitfield[i]: pubkeys.append(beacon_state.validators[index].pubkey) add_reward(state, beacon_state, index, base_reward) attestations += 1 - + for i in range(len(attester_committee), MAX_PERSISTENT_COMMITTEE_SIZE): assert block.attester_bitfield[i] is False - + assert bls_verify( pubkey=bls_aggregate_pubkeys(pubkeys), - message_hash=candidate.core.parent_root, - signature=candidate.signatures.attestation_signature, - domain=get_domain(beacon_state, DOMAIN_SHARD_ATTESTER, compute_epoch_of_shard_slot(candidate.core.slot)) + message_hash=block.core.parent_root, + signature=block.signatures.attestation_signature, + domain=get_domain(beacon_state, DOMAIN_SHARD_ATTESTER, compute_epoch_of_shard_slot(block.core.slot)) ) # Check proposer - proposer_index = get_shard_block_proposer_index(beacon_state, shard, candidate.core.slot) + proposer_index = get_shard_block_proposer_index(beacon_state, state.shard, block.core.slot) assert proposer_index is not None - add_reward(state, beacon_state, proposer_index, attestations * base_reward // PROPOSER_REWARD_QUOTIENT) + add_reward(state, beacon_state, proposer_index, Gwei(attestations * base_reward // PROPOSER_REWARD_QUOTIENT)) assert bls_verify( pubkey=beacon_state.validators[proposer_index].pubkey, - message_hash=hash_tree_root(candidate.core), - signature=candidate.signatures.proposer_signature, - domain=get_domain(beacon_state, DOMAIN_SHARD_PROPOSER, compute_epoch_of_shard_slot(candidate.core.slot)), + message_hash=hash_tree_root(block.core), + signature=block.signatures.proposer_signature, + domain=get_domain(beacon_state, DOMAIN_SHARD_PROPOSER, compute_epoch_of_shard_slot(block.core.slot)), ) - + # Process and update block data fees - add_fee(state, beacon_state, proposer_index, state.basefee * len(block.core.data) // SHARD_BLOCK_SIZE_LIMIT) + add_fee(state, beacon_state, proposer_index, Gwei(state.basefee * len(block.core.data) // SHARD_BLOCK_SIZE_LIMIT)) QUOTIENT = SHARD_BLOCK_SIZE_LIMIT * BASEFEE_ADJUSTMENT_FACTOR if len(block.core.data) > SHARD_BLOCK_SIZE_TARGET: - state.basefee += min(1, state.basefee * (len(block.core.data) - SHARD_BLOCK_SIZE_TARGET) // QUOTIENT) + state.basefee += Gwei(min(1, state.basefee * (len(block.core.data) - SHARD_BLOCK_SIZE_TARGET) // QUOTIENT)) elif len(block.core.data) < SHARD_BLOCK_SIZE_TARGET: - state.basefee -= min(1, state.basefee * (len(block.core.data) - SHARD_BLOCK_SIZE_TARGET) // QUOTIENT) - state.basefee = max(1, min(EFFECTIVE_BALANCE_INCREMENT // EPOCHS_PER_SHARD_PERIOD // SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH, state.basefee)) - + state.basefee -= Gwei(min(1, state.basefee * (len(block.core.data) - SHARD_BLOCK_SIZE_TARGET) // QUOTIENT)) + state.basefee = Gwei(max( + 1, + min( + EFFECTIVE_BALANCE_INCREMENT // EPOCHS_PER_SHARD_PERIOD // SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH, + state.basefee, + ) + )) + # Check total bytes assert block.core.total_bytes == state.most_recent_block_core.total_bytes + len(block.core.data) - + # Update in-state block header state.most_recent_block_core = ShardBlockCore( slot=block.core.slot, @@ -495,7 +523,7 @@ def shard_block_transition(state: ShardState, beacon_state: BeaconState, block: total_bytes=block.core.total_bytes, attester_bitfield=block.core.attester_bitfield ) - + # Check state root assert hash_tree_root(state) == block.core.state_root ``` From 13d6a31c5cf26e7fdbb22fc56dd2a7fe80db31e6 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 1 Aug 2019 00:27:31 +0800 Subject: [PATCH 05/18] misc fix --- specs/core/1_shard-data-chains.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index bc8a8817f..0fcbfe991 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -282,9 +282,9 @@ def get_shard_header(block: ShardBlock) -> ShardBlockHeader: data_root=hash_tree_root(block.core.data), state_root=block.core.state_root, total_bytes=block.core.total_bytes, - attester_bitfield=block.core.attester_bitfield + attester_bitfield=block.core.attester_bitfield, ), - signatures=block.signatures + signatures=block.signatures, ) ``` @@ -475,7 +475,7 @@ def shard_block_transition(state: ShardState, beacon_state: BeaconState, block: attestations += 1 for i in range(len(attester_committee), MAX_PERSISTENT_COMMITTEE_SIZE): - assert block.attester_bitfield[i] is False + assert block.core.attester_bitfield[i] is False or block.core.attester_bitfield[i] == 0 # TODO: FIX Bitvector assert bls_verify( pubkey=bls_aggregate_pubkeys(pubkeys), @@ -521,7 +521,7 @@ def shard_block_transition(state: ShardState, beacon_state: BeaconState, block: data_root=block.core.data_root, state_root=Hash(), total_bytes=block.core.total_bytes, - attester_bitfield=block.core.attester_bitfield + attester_bitfield=block.core.attester_bitfield, ) # Check state root From e08c365e2fa3f8a8200258a59230b2b6b37f9653 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 31 Jul 2019 17:25:55 -0400 Subject: [PATCH 06/18] Update specs/core/1_shard-data-chains.md Co-Authored-By: Hsiao-Wei Wang --- specs/core/1_shard-data-chains.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 0fcbfe991..266e46d63 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -421,6 +421,7 @@ def shard_slot_transition(state: ShardState, beacon_state: BeaconState) -> None: h = hash_tree_root(state) while state.slot % 2**depth == 0: state.history_acc[depth] = h + depth += 1 # Period transitions if (state.slot + 1) % (SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD) == 0: From b57aed2380f8840cc96e7884f7eb4bd31c3d3d04 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 31 Jul 2019 21:26:47 -0400 Subject: [PATCH 07/18] A few fixes --- specs/core/1_shard-data-chains.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 266e46d63..3f54390d6 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -199,7 +199,7 @@ def compute_epoch_of_shard_slot(slot: ShardSlot) -> Epoch: ### `get_shard_period_start_epoch` ```python -def get_shard_period_start_epoch(epoch: Epoch, lookback: uint64=0) -> Epoch: +def get_shard_period_start_epoch(epoch: Epoch, lookback: int=0) -> Epoch: return Epoch(epoch - (epoch % EPOCHS_PER_SHARD_PERIOD) - lookback * EPOCHS_PER_SHARD_PERIOD) ``` @@ -337,12 +337,12 @@ def compute_crosslink_data_root(blocks: Sequence[ShardBlock]) -> Hash: def get_default_shard_state(beacon_state: BeaconState, shard: Shard) -> ShardState: earlier_committee = get_period_committee( beacon_state, - Epoch(PHASE_1_FORK_SLOT - SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD * 2), + PHASE_1_FORK_EPOCH - EPOCHS_PER_SHARD_PERIOD * 2, shard, ) later_committee = get_period_committee( beacon_state, - Epoch(PHASE_1_FORK_SLOT - SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH * EPOCHS_PER_SHARD_PERIOD), + PHASE_1_FORK_EPOCH - EPOCHS_PER_SHARD_PERIOD * 2, shard, ) return ShardState( @@ -519,7 +519,7 @@ def shard_block_transition(state: ShardState, beacon_state: BeaconState, block: slot=block.core.slot, beacon_chain_root=block.core.beacon_chain_root, parent_root=block.core.parent_root, - data_root=block.core.data_root, + data_root=hash_tree_root(block.core.data), state_root=Hash(), total_bytes=block.core.total_bytes, attester_bitfield=block.core.attester_bitfield, From 52705f6fab88bfef04dfbb0686830111c8e2fe6b Mon Sep 17 00:00:00 2001 From: vbuterin Date: Wed, 31 Jul 2019 21:28:07 -0400 Subject: [PATCH 08/18] Quick fix --- specs/core/1_shard-data-chains.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 3f54390d6..5348322c4 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -500,9 +500,9 @@ def shard_block_transition(state: ShardState, beacon_state: BeaconState, block: add_fee(state, beacon_state, proposer_index, Gwei(state.basefee * len(block.core.data) // SHARD_BLOCK_SIZE_LIMIT)) QUOTIENT = SHARD_BLOCK_SIZE_LIMIT * BASEFEE_ADJUSTMENT_FACTOR if len(block.core.data) > SHARD_BLOCK_SIZE_TARGET: - state.basefee += Gwei(min(1, state.basefee * (len(block.core.data) - SHARD_BLOCK_SIZE_TARGET) // QUOTIENT)) + state.basefee += Gwei(max(1, state.basefee * (len(block.core.data) - SHARD_BLOCK_SIZE_TARGET) // QUOTIENT)) elif len(block.core.data) < SHARD_BLOCK_SIZE_TARGET: - state.basefee -= Gwei(min(1, state.basefee * (len(block.core.data) - SHARD_BLOCK_SIZE_TARGET) // QUOTIENT)) + state.basefee -= Gwei(max(1, state.basefee * (len(block.core.data) - SHARD_BLOCK_SIZE_TARGET) // QUOTIENT)) state.basefee = Gwei(max( 1, min( From 7a4a136d6cecc74b57fc0aed7166428ad5a6c674 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 1 Aug 2019 13:32:37 +0800 Subject: [PATCH 09/18] Fix `later_committee` --- specs/core/1_shard-data-chains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 5348322c4..7286692cb 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -342,7 +342,7 @@ def get_default_shard_state(beacon_state: BeaconState, shard: Shard) -> ShardSta ) later_committee = get_period_committee( beacon_state, - PHASE_1_FORK_EPOCH - EPOCHS_PER_SHARD_PERIOD * 2, + PHASE_1_FORK_EPOCH - EPOCHS_PER_SHARD_PERIOD, shard, ) return ShardState( From ce3df38028da15f982980bda1170644f00f22396 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 1 Aug 2019 14:17:05 +0800 Subject: [PATCH 10/18] Some updates: 1. Clean up configurations 2. Add `HISTORY_ACCUMULATOR_VECTOR` 3. Add `validate_state_root` flag in `shard_state_transition` for testing 4. Rename `history_acc` to `history_accumulator` --- specs/core/1_shard-data-chains.md | 47 ++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 7286692cb..0ef54570a 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -14,6 +14,8 @@ - [Misc](#misc) - [Initial values](#initial-values) - [Time parameters](#time-parameters) + - [State list lengths](#state-list-lengths) + - [Rewards and penalties](#rewards-and-penalties) - [Signature domain types](#signature-domain-types) - [TODO PLACEHOLDER](#todo-placeholder) - [Data structures](#data-structures) @@ -63,13 +65,11 @@ We define the following Python custom types for type hinting and readability: | Name | Value | | - | - | -| `SHARD_HEADER_SIZE` | `2**9` (= 512) | -| `SHARD_BLOCK_SIZE_LIMIT` | `2**16` (= 65,536) | -| `SHARD_BLOCK_SIZE_TARGET` | `2**14` (= 16,384) | | `SHARD_SLOTS_PER_BEACON_SLOT` | `2**1` (= 2) | | `MAX_PERSISTENT_COMMITTEE_SIZE` | `2**7` (= 128) | -| `REWARD_COEFFICIENT_BASE` | `2**20` ( = 1,048,576) | -| `BASEFEE_ADJUSTMENT_FACTOR` | `2**3` (= 8) | +| `SHARD_HEADER_SIZE` | `2**9` (= 512) | +| `SHARD_BLOCK_SIZE_TARGET` | `2**14` (= 16,384) | +| `SHARD_BLOCK_SIZE_LIMIT` | `2**16` (= 65,536) | ### Initial values @@ -77,7 +77,6 @@ We define the following Python custom types for type hinting and readability: | - | - | | `PHASE_1_FORK_EPOCH` | **TBD** | | `PHASE_1_FORK_SLOT` | **TBD** | -| `GENESIS_SHARD_SLOT` | 0 | ### Time parameters @@ -86,6 +85,19 @@ We define the following Python custom types for type hinting and readability: | `CROSSLINK_LOOKBACK` | `2**0` (= 1) | epochs | 6.4 minutes | | `EPOCHS_PER_SHARD_PERIOD` | `2**8` (= 256) | epochs | ~27 hours | +### State list lengths + +| Name | Value | Unit | +| - | - | :-: | +| `HISTORY_ACCUMULATOR_VECTOR` | `2**6` (= 64) | state tree maximum depth | + +### Rewards and penalties + +| Name | Value | +| - | - | +| `BASEFEE_ADJUSTMENT_FACTOR` | `2**3` (= 8) | +| `REWARD_COEFFICIENT_BASE` | `2**20` ( = 1,048,576) | + ### Signature domain types The following types are defined, mapping into `DomainType` (little endian): @@ -159,7 +171,7 @@ class ExtendedShardBlockCore(Container): ```python class ShardState(Container): - history_acc: Vector[Hash, 64] + history_accumulator: Vector[Hash, HISTORY_ACCUMULATOR_VECTOR] earlier_committee_rewards: List[uint64, MAX_PERSISTENT_COMMITTEE_SIZE] later_committee_rewards: List[uint64, MAX_PERSISTENT_COMMITTEE_SIZE] earlier_committee_fees: List[Gwei, MAX_PERSISTENT_COMMITTEE_SIZE] @@ -337,12 +349,12 @@ def compute_crosslink_data_root(blocks: Sequence[ShardBlock]) -> Hash: def get_default_shard_state(beacon_state: BeaconState, shard: Shard) -> ShardState: earlier_committee = get_period_committee( beacon_state, - PHASE_1_FORK_EPOCH - EPOCHS_PER_SHARD_PERIOD * 2, + Epoch(PHASE_1_FORK_EPOCH - EPOCHS_PER_SHARD_PERIOD * 2), shard, ) later_committee = get_period_committee( beacon_state, - PHASE_1_FORK_EPOCH - EPOCHS_PER_SHARD_PERIOD, + Epoch(PHASE_1_FORK_EPOCH - EPOCHS_PER_SHARD_PERIOD), shard, ) return ShardState( @@ -403,11 +415,14 @@ def add_fee(state: ShardState, beacon_state: BeaconState, index: ValidatorIndex, ### Shard state transition function ```python -def shard_state_transition(state: ShardState, beacon_state: BeaconState, block: ShardBlock) -> None: +def shard_state_transition(state: ShardState, + beacon_state: BeaconState, + block: ShardBlock, + validate_state_root: bool=False) -> None: assert block.core.slot > state.slot for slot in range(state.slot, block.core.slot): shard_slot_transition(state, beacon_state) - shard_block_transition(state, beacon_state, block) + shard_block_transition(state, beacon_state, block, validate_state_root=validate_state_root) ``` ```python @@ -420,7 +435,7 @@ def shard_slot_transition(state: ShardState, beacon_state: BeaconState) -> None: depth = 0 h = hash_tree_root(state) while state.slot % 2**depth == 0: - state.history_acc[depth] = h + state.history_accumulator[depth] = h depth += 1 # Period transitions @@ -450,7 +465,10 @@ def shard_slot_transition(state: ShardState, beacon_state: BeaconState) -> None: ``` ```python -def shard_block_transition(state: ShardState, beacon_state: BeaconState, block: ShardBlock) -> None: +def shard_block_transition(state: ShardState, + beacon_state: BeaconState, + block: ShardBlock, + validate_state_root: bool) -> None: # Check slot number assert block.core.slot == state.slot @@ -526,7 +544,8 @@ def shard_block_transition(state: ShardState, beacon_state: BeaconState, block: ) # Check state root - assert hash_tree_root(state) == block.core.state_root + if validate_state_root: + assert block.core.state_root == hash_tree_root(state) ``` ### Beacon attestations From 3aba05e252298db960ace81c6113558c1f91d24c Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 1 Aug 2019 14:19:08 +0800 Subject: [PATCH 11/18] Add simple tests for `shard_state_transition` --- configs/constant_presets/minimal.yaml | 10 ++++ .../test/helpers/phase1/shard_block.py | 44 +++++++++++++-- .../shard_data_chain/test_shard_block.py | 54 ++++++++++++++++--- 3 files changed, 97 insertions(+), 11 deletions(-) diff --git a/configs/constant_presets/minimal.yaml b/configs/constant_presets/minimal.yaml index 34419a223..ab8aab3c4 100644 --- a/configs/constant_presets/minimal.yaml +++ b/configs/constant_presets/minimal.yaml @@ -129,3 +129,13 @@ DOMAIN_TRANSFER: 0x05000000 DOMAIN_CUSTODY_BIT_CHALLENGE: 0x06000000 DOMAIN_SHARD_PROPOSER: 0x80000000 DOMAIN_SHARD_ATTESTER: 0x81000000 + + +# Phase 1 +# --------------------------------------------------------------- +SHARD_SLOTS_PER_BEACON_SLOT: 2 +EPOCHS_PER_SHARD_PERIOD: 4 +# PHASE_1_FORK_EPOCH >= EPOCHS_PER_SHARD_PERIOD * 2 +PHASE_1_FORK_EPOCH: 8 +# PHASE_1_FORK_SLOT = PHASE_1_FORK_EPOCH * SHARD_SLOTS_PER_BEACON_SLOT * SLOTS_PER_EPOCH +PHASE_1_FORK_SLOT: 128 diff --git a/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py b/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py index 4e1981727..42e2765ea 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py +++ b/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py @@ -7,6 +7,10 @@ from eth2spec.utils.ssz.ssz_impl import ( signing_root, ) +from .attestations import ( + sign_shard_attestation, +) + @only_with_bls() def sign_shard_block(spec, state, block, shard, proposer_index=None): @@ -26,22 +30,52 @@ def sign_shard_block(spec, state, block, shard, proposer_index=None): ) -def build_empty_shard_block(spec, state, slot, shard, parent_root, signed=False): +def build_empty_shard_block(spec, + shard_state, + beacon_state, + slot, + parent_root, + signed=False, + full_attestation=False): if slot is None: - slot = state.slot + slot = shard_state.slot + block = spec.ShardBlock( core=spec.ExtendedShardBlockCore( slot=slot, - beacon_chain_root=state.block_roots[state.slot % spec.SLOTS_PER_HISTORICAL_ROOT], + beacon_chain_root=beacon_state.block_roots[beacon_state.slot % spec.SLOTS_PER_HISTORICAL_ROOT], parent_root=parent_root, ), signatures=spec.ShardBlockSignatures( - attestation_signature=b'\x12' * 96, + attestation_signature=b'\x00' * 96, proposer_signature=b'\x25' * 96, ) ) + # attestation + if full_attestation: + attester_committee = spec.get_persistent_committee(beacon_state, shard_state.shard, block.core.slot) + block.core.attester_bitfield = list( + (True,) * len(attester_committee) + + (False,) * (spec.MAX_PERSISTENT_COMMITTEE_SIZE * 2 - len(attester_committee)) + ) + block.signatures.attestation_signature = sign_shard_attestation( + spec, + shard_state, + beacon_state, + block, + participants=attester_committee, + ) + else: + block.signatures.attestation_signature = sign_shard_attestation( + spec, + shard_state, + beacon_state, + block, + participants=(), + ) + if signed: - sign_shard_block(spec, state, block, shard) + sign_shard_block(spec, beacon_state, block, shard_state.shard) return block diff --git a/test_libs/pyspec/eth2spec/test/phase_1/shard_data_chain/test_shard_block.py b/test_libs/pyspec/eth2spec/test/phase_1/shard_data_chain/test_shard_block.py index 359350d39..2bb0232f0 100644 --- a/test_libs/pyspec/eth2spec/test/phase_1/shard_data_chain/test_shard_block.py +++ b/test_libs/pyspec/eth2spec/test/phase_1/shard_data_chain/test_shard_block.py @@ -11,16 +11,58 @@ from eth2spec.test.context import ( @with_all_phases_except(['phase0']) @always_bls @spec_state_test -def test_is_valid_shard_block(spec, state): +def test_process_empty_shard_block(spec, state): + beacon_state = state + + shard_slot = spec.PHASE_1_FORK_SLOT + beacon_state.slot = spec.Slot(spec.PHASE_1_FORK_EPOCH * spec.SLOTS_PER_EPOCH) + shard_state = spec.get_default_shard_state(beacon_state, shard=spec.Shard(0)) + shard_state.slot = shard_slot + block = build_empty_shard_block( spec, - state, - slot=spec.Slot(spec.PERSISTENT_COMMITTEE_PERIOD * 100), - shard=spec.Shard(1), + shard_state, + beacon_state, + slot=shard_slot + 1, parent_root=spec.Hash(), signed=True, + full_attestation=False, ) - # TODO: test `is_valid_shard_block` + yield 'pre', shard_state + yield 'beacon_state', beacon_state + yield 'block', block - yield 'blocks', (block,) + spec.shard_state_transition(shard_state, beacon_state, block) + + yield 'post', shard_state + + +@with_all_phases_except(['phase0']) +@always_bls +@spec_state_test +def test_process_full_attestation_shard_block(spec, state): + beacon_state = state + + shard_slot = spec.PHASE_1_FORK_SLOT + beacon_state.slot = spec.Slot(spec.PHASE_1_FORK_EPOCH * spec.SLOTS_PER_EPOCH) + shard_state = spec.get_default_shard_state(beacon_state, shard=spec.Shard(0)) + shard_state.slot = shard_slot + + block = build_empty_shard_block( + spec, + shard_state, + beacon_state, + slot=shard_slot + 1, + parent_root=spec.Hash(), + signed=True, + full_attestation=True, + ) + + yield 'pre', shard_state + yield 'beacon_state', beacon_state + yield 'block', block + + spec.shard_state_transition(shard_state, beacon_state, block) + + yield 'post', shard_state From db292502567c67b9cec08d8d19bbde6ae8ab6aad Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 1 Aug 2019 14:22:01 +0800 Subject: [PATCH 12/18] Add testing helpers --- .../test/helpers/phase1/attestations.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 test_libs/pyspec/eth2spec/test/helpers/phase1/attestations.py diff --git a/test_libs/pyspec/eth2spec/test/helpers/phase1/attestations.py b/test_libs/pyspec/eth2spec/test/helpers/phase1/attestations.py new file mode 100644 index 000000000..750ab5048 --- /dev/null +++ b/test_libs/pyspec/eth2spec/test/helpers/phase1/attestations.py @@ -0,0 +1,37 @@ +from eth2spec.test.helpers.keys import privkeys +from eth2spec.utils.bls import ( + bls_aggregate_signatures, + bls_sign, +) + + +def sign_shard_attestation(spec, shard_state, beacon_state, block, participants): + signatures = [] + message_hash = block.core.parent_root + block_epoch = spec.compute_epoch_of_shard_slot(block.core.slot) + for validator_index in participants: + privkey = privkeys[validator_index] + signatures.append( + get_attestation_signature( + spec, + shard_state, + beacon_state, + message_hash, + block_epoch, + privkey, + ) + ) + + return bls_aggregate_signatures(signatures) + + +def get_attestation_signature(spec, shard_state, beacon_state, message_hash, block_epoch, privkey): + return bls_sign( + message_hash=message_hash, + privkey=privkey, + domain=spec.get_domain( + state=beacon_state, + domain_type=spec.DOMAIN_SHARD_ATTESTER, + message_epoch=block_epoch, + ) + ) From 4163053ccad9f55aefd13f8bcc51ec086a12acf6 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 1 Aug 2019 16:45:01 +0800 Subject: [PATCH 13/18] Clean up type hintings, especially `reward` is denominated in uint, and `fee` is in `Gwei` --- specs/core/1_shard-data-chains.md | 35 ++++++++++++++++--------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 0ef54570a..c4d8e2701 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -218,9 +218,7 @@ def get_shard_period_start_epoch(epoch: Epoch, lookback: int=0) -> Epoch: ### `get_period_committee` ```python -def get_period_committee(state: BeaconState, - epoch: Epoch, - shard: Shard) -> List[ValidatorIndex, MAX_PERSISTENT_COMMITTEE_SIZE]: +def get_period_committee(state: BeaconState, epoch: Epoch, shard: Shard) -> Sequence[ValidatorIndex]: """ Return committee for a period. Used to construct persistent committees. """ @@ -363,8 +361,8 @@ def get_default_shard_state(beacon_state: BeaconState, shard: Shard) -> ShardSta slot=PHASE_1_FORK_SLOT, earlier_committee_rewards=[REWARD_COEFFICIENT_BASE for _ in range(len(earlier_committee))], later_committee_rewards=[REWARD_COEFFICIENT_BASE for _ in range(len(later_committee))], - earlier_committee_fees=[0 for _ in range(len(earlier_committee))], - later_committee_fees=[0 for _ in range(len(later_committee))], + earlier_committee_fees=[Gwei(0) for _ in range(len(earlier_committee))], + later_committee_fees=[Gwei(0) for _ in range(len(later_committee))], ) ``` @@ -383,7 +381,7 @@ Note that these acceptance conditions depend on the canonical beacon chain; when ### Shard state transition function helpers ```python -def add_reward(state: ShardState, beacon_state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: +def add_reward(state: ShardState, beacon_state: BeaconState, index: ValidatorIndex, delta: int) -> None: epoch = compute_epoch_of_shard_slot(state.slot) earlier_committee = get_period_committee( beacon_state, @@ -400,7 +398,7 @@ def add_reward(state: ShardState, beacon_state: BeaconState, index: ValidatorInd ``` ```python -def add_fee(state: ShardState, beacon_state: BeaconState, index: ValidatorIndex, delta: Gwei) -> None: +def add_fee(state: ShardState, beacon_state: BeaconState, index: ValidatorIndex, delta: int) -> None: epoch = compute_epoch_of_shard_slot(state.slot) earlier_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=2), state.shard) later_committee = get_period_committee(beacon_state, get_shard_period_start_epoch(epoch, lookback=1), state.shard) @@ -452,13 +450,17 @@ def shard_slot_transition(state: ShardState, beacon_state: BeaconState) -> None: state.shard, ) state.receipt_root = hash_tree_root(List[ShardReceiptDelta, PLACEHOLDER]([ - ShardReceiptDelta(index, state.earlier_committee_rewards[i], state.earlier_committee_fees[i]) - for i, index in enumerate(earlier_committee) + ShardReceiptDelta( + index=validator_index, + reward_coefficient=state.earlier_committee_rewards[i], + block_fee=state.earlier_committee_fees[i], + ) + for i, validator_index in enumerate(earlier_committee) ])) state.earlier_committee_rewards = state.later_committee_rewards state.earlier_committee_fees = state.later_committee_fees state.later_committee_rewards = [REWARD_COEFFICIENT_BASE for _ in range(len(later_committee))], - state.later_committee_fees = [0 for _ in range(len(later_committee))], + state.later_committee_fees = [Gwei(0) for _ in range(len(later_committee))], else: state.receipt_root = Hash() state.slot += ShardSlot(1) @@ -478,19 +480,18 @@ def shard_block_transition(state: ShardState, # Calculate base reward total_balance = get_total_active_balance(beacon_state) - base_reward = Gwei( + base_reward = ( REWARD_COEFFICIENT_BASE * BASE_REWARD_FACTOR // integer_squareroot(total_balance) // BASE_REWARDS_PER_EPOCH ) - # Check attestations attester_committee = get_persistent_committee(beacon_state, state.shard, block.core.slot) pubkeys = [] attestations = 0 - for i, index in enumerate(attester_committee): + for i, validator_index in enumerate(attester_committee): if block.core.attester_bitfield[i]: - pubkeys.append(beacon_state.validators[index].pubkey) - add_reward(state, beacon_state, index, base_reward) + pubkeys.append(beacon_state.validators[validator_index].pubkey) + add_reward(state, beacon_state, validator_index, base_reward) attestations += 1 for i in range(len(attester_committee), MAX_PERSISTENT_COMMITTEE_SIZE): @@ -506,7 +507,7 @@ def shard_block_transition(state: ShardState, # Check proposer proposer_index = get_shard_block_proposer_index(beacon_state, state.shard, block.core.slot) assert proposer_index is not None - add_reward(state, beacon_state, proposer_index, Gwei(attestations * base_reward // PROPOSER_REWARD_QUOTIENT)) + add_reward(state, beacon_state, proposer_index, attestations * base_reward // PROPOSER_REWARD_QUOTIENT) assert bls_verify( pubkey=beacon_state.validators[proposer_index].pubkey, message_hash=hash_tree_root(block.core), @@ -515,7 +516,7 @@ def shard_block_transition(state: ShardState, ) # Process and update block data fees - add_fee(state, beacon_state, proposer_index, Gwei(state.basefee * len(block.core.data) // SHARD_BLOCK_SIZE_LIMIT)) + add_fee(state, beacon_state, proposer_index, state.basefee * len(block.core.data) // SHARD_BLOCK_SIZE_LIMIT) QUOTIENT = SHARD_BLOCK_SIZE_LIMIT * BASEFEE_ADJUSTMENT_FACTOR if len(block.core.data) > SHARD_BLOCK_SIZE_TARGET: state.basefee += Gwei(max(1, state.basefee * (len(block.core.data) - SHARD_BLOCK_SIZE_TARGET) // QUOTIENT)) From c29d85aafb99639930e49129e0c173082de450f5 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 2 Aug 2019 09:40:26 -0400 Subject: [PATCH 14/18] Update specs/core/1_shard-data-chains.md Co-Authored-By: Hsiao-Wei Wang --- specs/core/1_shard-data-chains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index c4d8e2701..f284574f2 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -432,7 +432,7 @@ def shard_slot_transition(state: ShardState, beacon_state: BeaconState) -> None: # Save states in history accumulator depth = 0 h = hash_tree_root(state) - while state.slot % 2**depth == 0: + while state.slot % 2**depth == 0 and depth <= HISTORY_ACCUMULATOR_VECTOR: state.history_accumulator[depth] = h depth += 1 From ddd43ad99d1e75affe77627d9f0c06f7a2825a35 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 2 Aug 2019 09:40:49 -0400 Subject: [PATCH 15/18] <= becomes < --- specs/core/1_shard-data-chains.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index f284574f2..317011716 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -432,7 +432,7 @@ def shard_slot_transition(state: ShardState, beacon_state: BeaconState) -> None: # Save states in history accumulator depth = 0 h = hash_tree_root(state) - while state.slot % 2**depth == 0 and depth <= HISTORY_ACCUMULATOR_VECTOR: + while state.slot % 2**depth == 0 and depth < HISTORY_ACCUMULATOR_VECTOR: state.history_accumulator[depth] = h depth += 1 From cb92aa91ddaedb9dc6cff67718803577bf82ee03 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Mon, 5 Aug 2019 14:37:38 -0400 Subject: [PATCH 16/18] Include state root blocks in crosslink data in non-block slots Also adds `total_bytes` to state. The goal is to facilitate easier fraud proofs, so that one needs to simply check two adjacent headers in a crosslink and their respective bodies to verify a fraud proof. --- specs/core/1_shard-data-chains.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 317011716..b82ae5732 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -181,6 +181,7 @@ class ShardState(Container): shard: Shard most_recent_block_core: ShardBlockCore receipt_root: Hash + total_bytes: uint64 ``` ### `ShardReceiptDelta` @@ -531,7 +532,9 @@ def shard_block_transition(state: ShardState, )) # Check total bytes - assert block.core.total_bytes == state.most_recent_block_core.total_bytes + len(block.core.data) + state.total_bytes += len(block.core.data) + assert block.core.total_bytes == state.total_bytes + # Update in-state block header state.most_recent_block_core = ShardBlockCore( @@ -554,14 +557,16 @@ def shard_block_transition(state: ShardState, Let: - `shard` be a valid `Shard` -- `shard_blocks` be the `ShardBlock` list such that `shard_blocks[slot]` is the canonical `ShardBlock` for shard `shard` at slot `slot` +- `pre_state` is the `ShardState` before processing any blocks +- `shard_blocks_or_state_roots` be the `Union[ShardBlock, Hash]` list such that `shard_blocks[slot]` is the canonical `ShardBlock` for shard `shard` at slot `slot` if a block exists, or the post-state-root of processing state up to and including that slot if a block does not exist. - `beacon_state` be the canonical `BeaconState` - `valid_attestations` be the set of valid `Attestation` objects, recursively defined - `candidate` be a candidate `Attestation` which is valid under Phase 0 rules, and for which validity is to be determined under Phase 1 rules by running `is_valid_beacon_attestation` ```python def is_valid_beacon_attestation(shard: Shard, - shard_blocks: Sequence[ShardBlock], + pre_state: ShardState, + shard_blocks_or_state_roots: Sequence[Union[ShardBlock, Hash]], beacon_state: BeaconState, valid_attestations: Set[Attestation], candidate: Attestation) -> bool: @@ -588,7 +593,14 @@ def is_valid_beacon_attestation(shard: Shard, start_epoch + MAX_EPOCHS_PER_CROSSLINK) blocks = [] for slot in range(start_epoch * SLOTS_PER_EPOCH, end_epoch * SLOTS_PER_EPOCH): - blocks.append(shard_blocks[slot]) + if isinstance(shard_blocks_or_state_roots[slot], ShardBlock): + blocks.append(shard_blocks_or_state_roots[slot]) + else: + blocks.append(ShardBlockHeader(ShardBlockCore( + slot=slot, + state_root=shard_blocks_or_state_roots[slot], + total_bytes=state.total_bytes + ), ShardBlockSignatures())) assert candidate.data.crosslink.data_root == compute_crosslink_data_root(blocks) return True From 095cfe6633f9dbe62e8d4745665bcf4361da3a4c Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 7 Aug 2019 19:29:24 +0800 Subject: [PATCH 17/18] Fix build_spec and typo --- scripts/build_spec.py | 2 +- specs/core/1_shard-data-chains.md | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/build_spec.py b/scripts/build_spec.py index 96866cc8a..52642c8f4 100644 --- a/scripts/build_spec.py +++ b/scripts/build_spec.py @@ -37,7 +37,7 @@ from eth2spec.utils.bls import ( from eth2spec.utils.hash_function import hash ''' PHASE1_IMPORTS = '''from typing import ( - Any, Dict, Optional, Set, Sequence, MutableSequence, Tuple, + Any, Dict, Optional, Set, Sequence, MutableSequence, Tuple, Union, ) from dataclasses import ( diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index b82ae5732..283c1a9ca 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -535,7 +535,6 @@ def shard_block_transition(state: ShardState, state.total_bytes += len(block.core.data) assert block.core.total_bytes == state.total_bytes - # Update in-state block header state.most_recent_block_core = ShardBlockCore( slot=block.core.slot, @@ -599,7 +598,7 @@ def is_valid_beacon_attestation(shard: Shard, blocks.append(ShardBlockHeader(ShardBlockCore( slot=slot, state_root=shard_blocks_or_state_roots[slot], - total_bytes=state.total_bytes + total_bytes=pre_state.total_bytes ), ShardBlockSignatures())) assert candidate.data.crosslink.data_root == compute_crosslink_data_root(blocks) From 5290b62465379f33ff15361a12eb2811d41d4832 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 11 Aug 2019 22:21:58 +0800 Subject: [PATCH 18/18] Fix + refactor `is_valid_beacon_attestation` and add basic test --- specs/core/1_shard-data-chains.md | 21 ++++---- .../test_beacon_attestation.py | 48 +++++++++++++++++++ 2 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 test_libs/pyspec/eth2spec/test/phase_1/shard_data_chain/test_beacon_attestation.py diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 283c1a9ca..079c0b4b7 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -555,16 +555,14 @@ def shard_block_transition(state: ShardState, Let: -- `shard` be a valid `Shard` - `pre_state` is the `ShardState` before processing any blocks -- `shard_blocks_or_state_roots` be the `Union[ShardBlock, Hash]` list such that `shard_blocks[slot]` is the canonical `ShardBlock` for shard `shard` at slot `slot` if a block exists, or the post-state-root of processing state up to and including that slot if a block does not exist. +- `shard_blocks_or_state_roots` be the `Union[ShardBlock, Hash]` list such that `shard_blocks[slot]` is the canonical `ShardBlock` for shard `pre_state.shard` at slot `slot` if a block exists, or the post-state-root of processing state up to and including that slot if a block does not exist. - `beacon_state` be the canonical `BeaconState` - `valid_attestations` be the set of valid `Attestation` objects, recursively defined - `candidate` be a candidate `Attestation` which is valid under Phase 0 rules, and for which validity is to be determined under Phase 1 rules by running `is_valid_beacon_attestation` ```python -def is_valid_beacon_attestation(shard: Shard, - pre_state: ShardState, +def is_valid_beacon_attestation(pre_state: ShardState, shard_blocks_or_state_roots: Sequence[Union[ShardBlock, Hash]], beacon_state: BeaconState, valid_attestations: Set[Attestation], @@ -587,7 +585,7 @@ def is_valid_beacon_attestation(shard: Shard, assert candidate.data.previous_attestation.epoch < compute_epoch_of_slot(candidate.data.slot) # Check crosslink data root - start_epoch = beacon_state.crosslinks[shard].epoch + start_epoch = beacon_state.crosslinks[pre_state.shard].epoch end_epoch = min(compute_epoch_of_slot(candidate.data.slot) - CROSSLINK_LOOKBACK, start_epoch + MAX_EPOCHS_PER_CROSSLINK) blocks = [] @@ -595,11 +593,14 @@ def is_valid_beacon_attestation(shard: Shard, if isinstance(shard_blocks_or_state_roots[slot], ShardBlock): blocks.append(shard_blocks_or_state_roots[slot]) else: - blocks.append(ShardBlockHeader(ShardBlockCore( - slot=slot, - state_root=shard_blocks_or_state_roots[slot], - total_bytes=pre_state.total_bytes - ), ShardBlockSignatures())) + blocks.append(ShardBlock( + core=ExtendedShardBlockCore( + slot=slot, + state_root=shard_blocks_or_state_roots[slot], + total_bytes=pre_state.total_bytes, + ), + signatures=ShardBlockSignatures(), + )) assert candidate.data.crosslink.data_root == compute_crosslink_data_root(blocks) return True diff --git a/test_libs/pyspec/eth2spec/test/phase_1/shard_data_chain/test_beacon_attestation.py b/test_libs/pyspec/eth2spec/test/phase_1/shard_data_chain/test_beacon_attestation.py new file mode 100644 index 000000000..aface905b --- /dev/null +++ b/test_libs/pyspec/eth2spec/test/phase_1/shard_data_chain/test_beacon_attestation.py @@ -0,0 +1,48 @@ +from eth2spec.test.context import ( + with_all_phases_except, + spec_state_test, + always_bls, +) +from eth2spec.test.helpers.phase1.shard_block import ( + build_empty_shard_block, +) +from eth2spec.test.helpers.attestations import get_valid_attestation + + +@with_all_phases_except(['phase0']) +@always_bls +@spec_state_test +def test_process_empty_shard_block(spec, state): + beacon_state = state + + shard_slot = spec.PHASE_1_FORK_SLOT + beacon_state.slot = spec.Slot(spec.PHASE_1_FORK_EPOCH * spec.SLOTS_PER_EPOCH) + shard_state = spec.get_default_shard_state(beacon_state, shard=spec.Shard(0)) + shard_state.slot = shard_slot + + block = build_empty_shard_block( + spec, + shard_state, + beacon_state, + slot=shard_slot + 1, + parent_root=spec.Hash(), + signed=True, + full_attestation=True, + ) + + yield 'pre', shard_state + yield 'beacon_state', beacon_state + yield 'block', block + + beacon_attestation = get_valid_attestation(spec, beacon_state, signed=True) + yield 'beacon_attestation', beacon_attestation + + is_valid_beacon_attestation = spec.is_valid_beacon_attestation( + pre_state=shard_state, + shard_blocks_or_state_roots=(block,), + beacon_state=beacon_state, + valid_attestations=set([beacon_attestation]), + candidate=beacon_attestation, + ) + assert is_valid_beacon_attestation + yield 'is_valid_beacon_attestation', is_valid_beacon_attestation