Merge pull request #1875 from ethereum/hwwhww/shard_fork_choice_part2
Handle `beacon_parent_root` checks
This commit is contained in:
commit
9b3f45dfd0
|
@ -46,7 +46,7 @@ class ShardStore:
|
||||||
def get_forkchoice_shard_store(anchor_state: BeaconState, shard: Shard) -> ShardStore:
|
def get_forkchoice_shard_store(anchor_state: BeaconState, shard: Shard) -> ShardStore:
|
||||||
return ShardStore(
|
return ShardStore(
|
||||||
shard=shard,
|
shard=shard,
|
||||||
blocks={anchor_state.shard_states[shard].latest_block_root: ShardBlock(slot=anchor_state.slot)},
|
blocks={anchor_state.shard_states[shard].latest_block_root: ShardBlock(slot=anchor_state.slot, shard=shard)},
|
||||||
block_states={anchor_state.shard_states[shard].latest_block_root: anchor_state.copy().shard_states[shard]},
|
block_states={anchor_state.shard_states[shard].latest_block_root: anchor_state.copy().shard_states[shard]},
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
@ -151,10 +151,11 @@ def on_shard_block(store: Store, shard_store: ShardStore, signed_shard_block: Si
|
||||||
|
|
||||||
# Check shard parent exists
|
# Check shard parent exists
|
||||||
assert shard_block.shard_parent_root in shard_store.block_states
|
assert shard_block.shard_parent_root in shard_store.block_states
|
||||||
pre_shard_state = shard_store.block_states[shard_block.shard_parent_root]
|
shard_parent_state = shard_store.block_states[shard_block.shard_parent_root]
|
||||||
|
|
||||||
# Check beacon parent exists
|
# Check beacon parent exists
|
||||||
assert shard_block.beacon_parent_root in store.block_states
|
assert shard_block.beacon_parent_root in store.block_states
|
||||||
|
beacon_parent_state = store.block_states[shard_block.beacon_parent_root]
|
||||||
|
|
||||||
# Check that block is later than the finalized shard state slot (optimization to reduce calls to get_ancestor)
|
# Check that block is later than the finalized shard state slot (optimization to reduce calls to get_ancestor)
|
||||||
finalized_beacon_state = store.block_states[store.finalized_checkpoint.root]
|
finalized_beacon_state = store.block_states[store.finalized_checkpoint.root]
|
||||||
|
@ -167,15 +168,15 @@ def on_shard_block(store: Store, shard_store: ShardStore, signed_shard_block: Si
|
||||||
get_ancestor(store, shard_block.beacon_parent_root, finalized_slot) == store.finalized_checkpoint.root
|
get_ancestor(store, shard_block.beacon_parent_root, finalized_slot) == store.finalized_checkpoint.root
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Check the block is valid and compute the post-state
|
||||||
|
assert verify_shard_block_message(beacon_parent_state, shard_parent_state, shard_block)
|
||||||
|
assert verify_shard_block_signature(beacon_parent_state, signed_shard_block)
|
||||||
|
|
||||||
|
post_state = get_post_shard_state(beacon_parent_state, shard_parent_state, shard_block)
|
||||||
|
|
||||||
# Add new block to the store
|
# Add new block to the store
|
||||||
shard_store.blocks[hash_tree_root(shard_block)] = shard_block
|
shard_store.blocks[hash_tree_root(shard_block)] = shard_block
|
||||||
|
|
||||||
# Check the block is valid and compute the post-state
|
|
||||||
beacon_head_root = get_head(store)
|
|
||||||
beacon_head_state = store.block_states[beacon_head_root]
|
|
||||||
assert verify_shard_block_message(beacon_head_state, pre_shard_state, shard_block, shard_block.slot, shard)
|
|
||||||
assert verify_shard_block_signature(beacon_head_state, signed_shard_block)
|
|
||||||
post_state = get_post_shard_state(beacon_head_state, pre_shard_state, shard_block)
|
|
||||||
# Add new state for this block to the store
|
# Add new state for this block to the store
|
||||||
shard_store.block_states[hash_tree_root(shard_block)] = post_state
|
shard_store.block_states[hash_tree_root(shard_block)] = post_state
|
||||||
```
|
```
|
||||||
|
|
|
@ -30,7 +30,7 @@ This document describes the shard transition function and fraud proofs as part o
|
||||||
### Misc
|
### Misc
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def compute_shard_transition_digest(beacon_state: BeaconState,
|
def compute_shard_transition_digest(beacon_parent_state: BeaconState,
|
||||||
shard_state: ShardState,
|
shard_state: ShardState,
|
||||||
beacon_parent_root: Root,
|
beacon_parent_root: Root,
|
||||||
shard_body_root: Root) -> Bytes32:
|
shard_body_root: Root) -> Bytes32:
|
||||||
|
@ -43,18 +43,27 @@ def compute_shard_transition_digest(beacon_state: BeaconState,
|
||||||
### Shard block verification functions
|
### Shard block verification functions
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def verify_shard_block_message(beacon_state: BeaconState,
|
def verify_shard_block_message(beacon_parent_state: BeaconState,
|
||||||
shard_state: ShardState,
|
shard_parent_state: ShardState,
|
||||||
block: ShardBlock,
|
block: ShardBlock) -> bool:
|
||||||
slot: Slot,
|
# Check `shard_parent_root` field
|
||||||
shard: Shard) -> bool:
|
assert block.shard_parent_root == shard_parent_state.latest_block_root
|
||||||
assert block.shard_parent_root == shard_state.latest_block_root
|
# Check `beacon_parent_root` field
|
||||||
assert block.slot == slot
|
beacon_parent_block_header = beacon_parent_state.latest_block_header.copy()
|
||||||
next_slot = Slot(beacon_state.slot + 1)
|
if beacon_parent_block_header.state_root == Root():
|
||||||
offset_slots = compute_offset_slots(get_latest_slot_for_shard(beacon_state, shard), next_slot)
|
beacon_parent_block_header.state_root = hash_tree_root(beacon_parent_state)
|
||||||
assert slot in offset_slots
|
beacon_parent_root = hash_tree_root(beacon_parent_block_header)
|
||||||
|
assert block.beacon_parent_root == beacon_parent_root
|
||||||
|
# Check `slot` field
|
||||||
|
shard = block.shard
|
||||||
|
next_slot = Slot(block.slot + 1)
|
||||||
|
offset_slots = compute_offset_slots(get_latest_slot_for_shard(beacon_parent_state, shard), next_slot)
|
||||||
|
assert block.slot in offset_slots
|
||||||
|
# Check `shard` field
|
||||||
assert block.shard == shard
|
assert block.shard == shard
|
||||||
assert block.proposer_index == get_shard_proposer_index(beacon_state, slot, shard)
|
# Check `proposer_index` field
|
||||||
|
assert block.proposer_index == get_shard_proposer_index(beacon_parent_state, block.slot, shard)
|
||||||
|
# Check `body` field
|
||||||
assert 0 < len(block.body) <= MAX_SHARD_BLOCK_SIZE
|
assert 0 < len(block.body) <= MAX_SHARD_BLOCK_SIZE
|
||||||
return True
|
return True
|
||||||
```
|
```
|
||||||
|
@ -175,38 +184,9 @@ def compute_shard_body_roots(proposals: Sequence[SignedShardBlock]) -> Sequence[
|
||||||
return [hash_tree_root(proposal.message.body) for proposal in proposals]
|
return [hash_tree_root(proposal.message.body) for proposal in proposals]
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
|
||||||
def get_proposal_choices_at_slot(beacon_state: BeaconState,
|
|
||||||
shard_state: ShardState,
|
|
||||||
slot: Slot,
|
|
||||||
shard: Shard,
|
|
||||||
shard_blocks: Sequence[SignedShardBlock],
|
|
||||||
validate_signature: bool=True) -> Sequence[SignedShardBlock]:
|
|
||||||
"""
|
|
||||||
Return the valid shard blocks at the given ``slot``.
|
|
||||||
Note that this function doesn't change the state.
|
|
||||||
"""
|
|
||||||
choices = []
|
|
||||||
shard_blocks_at_slot = [block for block in shard_blocks if block.message.slot == slot]
|
|
||||||
for block in shard_blocks_at_slot:
|
|
||||||
try:
|
|
||||||
# Verify block message and signature
|
|
||||||
# TODO these validations should have been checked upon receiving shard blocks.
|
|
||||||
assert verify_shard_block_message(beacon_state, shard_state, block.message, slot, shard)
|
|
||||||
if validate_signature:
|
|
||||||
assert verify_shard_block_signature(beacon_state, block)
|
|
||||||
|
|
||||||
shard_state = get_post_shard_state(beacon_state, shard_state, block.message)
|
|
||||||
except Exception:
|
|
||||||
pass # TODO: throw error in the test helper
|
|
||||||
else:
|
|
||||||
choices.append(block)
|
|
||||||
return choices
|
|
||||||
```
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_proposal_at_slot(beacon_state: BeaconState,
|
def get_proposal_at_slot(beacon_state: BeaconState,
|
||||||
shard_state: ShardState,
|
shard_parent_state: ShardState,
|
||||||
slot: Shard,
|
slot: Shard,
|
||||||
shard: Shard,
|
shard: Shard,
|
||||||
shard_blocks: Sequence[SignedShardBlock],
|
shard_blocks: Sequence[SignedShardBlock],
|
||||||
|
@ -215,24 +195,17 @@ def get_proposal_at_slot(beacon_state: BeaconState,
|
||||||
Return ``proposal``, ``shard_state`` of the given ``slot``.
|
Return ``proposal``, ``shard_state`` of the given ``slot``.
|
||||||
Note that this function doesn't change the state.
|
Note that this function doesn't change the state.
|
||||||
"""
|
"""
|
||||||
choices = get_proposal_choices_at_slot(
|
shard_blocks = [block for block in shard_blocks if block.message.slot == slot]
|
||||||
beacon_state=beacon_state,
|
if len(shard_blocks) == 0:
|
||||||
shard_state=shard_state,
|
|
||||||
slot=slot,
|
|
||||||
shard=shard,
|
|
||||||
shard_blocks=shard_blocks,
|
|
||||||
validate_signature=validate_signature,
|
|
||||||
)
|
|
||||||
if len(choices) == 0:
|
|
||||||
block = ShardBlock(slot=slot, shard=shard)
|
block = ShardBlock(slot=slot, shard=shard)
|
||||||
proposal = SignedShardBlock(message=block)
|
proposal = SignedShardBlock(message=block)
|
||||||
elif len(choices) == 1:
|
elif len(shard_blocks) == 1:
|
||||||
proposal = choices[0]
|
proposal = shard_blocks[0]
|
||||||
else:
|
else:
|
||||||
proposal = get_winning_proposal(beacon_state, choices)
|
proposal = get_winning_proposal(beacon_state, shard_blocks)
|
||||||
|
|
||||||
# Apply state transition
|
# Apply state transition
|
||||||
shard_state = get_post_shard_state(beacon_state, shard_state, proposal.message)
|
shard_state = get_post_shard_state(beacon_state, shard_parent_state, proposal.message)
|
||||||
|
|
||||||
return proposal, shard_state
|
return proposal, shard_state
|
||||||
```
|
```
|
||||||
|
@ -247,10 +220,11 @@ def get_shard_state_transition_result(
|
||||||
proposals = []
|
proposals = []
|
||||||
shard_states = []
|
shard_states = []
|
||||||
shard_state = beacon_state.shard_states[shard]
|
shard_state = beacon_state.shard_states[shard]
|
||||||
for slot in get_offset_slots(beacon_state, shard):
|
offset_slots = compute_offset_slots(get_latest_slot_for_shard(beacon_state, shard), Slot(beacon_state.slot + 1))
|
||||||
|
for slot in offset_slots:
|
||||||
proposal, shard_state = get_proposal_at_slot(
|
proposal, shard_state = get_proposal_at_slot(
|
||||||
beacon_state=beacon_state,
|
beacon_state=beacon_state,
|
||||||
shard_state=shard_state,
|
shard_parent_state=shard_state,
|
||||||
slot=slot,
|
slot=slot,
|
||||||
shard=shard,
|
shard=shard,
|
||||||
shard_blocks=shard_blocks,
|
shard_blocks=shard_blocks,
|
||||||
|
@ -272,7 +246,7 @@ Suppose you are a committee member on shard `shard` at slot `current_slot` and y
|
||||||
def get_shard_transition(beacon_state: BeaconState,
|
def get_shard_transition(beacon_state: BeaconState,
|
||||||
shard: Shard,
|
shard: Shard,
|
||||||
shard_blocks: Sequence[SignedShardBlock]) -> ShardTransition:
|
shard_blocks: Sequence[SignedShardBlock]) -> ShardTransition:
|
||||||
offset_slots = get_offset_slots(beacon_state, shard)
|
offset_slots = compute_offset_slots(get_latest_slot_for_shard(beacon_state, shard), Slot(beacon_state.slot + 1))
|
||||||
proposals, shard_states, shard_data_roots = get_shard_state_transition_result(beacon_state, shard, shard_blocks)
|
proposals, shard_states, shard_data_roots = get_shard_state_transition_result(beacon_state, shard, shard_blocks)
|
||||||
|
|
||||||
shard_block_lengths = []
|
shard_block_lengths = []
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||||
|
|
||||||
from eth2spec.test.context import PHASE0, spec_state_test, with_all_phases_except, never_bls
|
from eth2spec.test.context import PHASE0, spec_state_test, with_all_phases_except, never_bls
|
||||||
|
from eth2spec.test.helpers.attestations import get_valid_on_time_attestation
|
||||||
from eth2spec.test.helpers.shard_block import (
|
from eth2spec.test.helpers.shard_block import (
|
||||||
build_attestation_with_shard_transition,
|
|
||||||
build_shard_block,
|
build_shard_block,
|
||||||
build_shard_transitions_till_slot,
|
get_shard_transitions,
|
||||||
get_committee_index_of_shard,
|
get_committee_index_of_shard,
|
||||||
)
|
)
|
||||||
from eth2spec.test.helpers.fork_choice import add_block_to_store, get_anchor_root
|
from eth2spec.test.helpers.fork_choice import add_block_to_store, get_anchor_root
|
||||||
|
@ -25,15 +25,15 @@ def run_on_shard_block(spec, store, shard_store, signed_block, valid=True):
|
||||||
assert shard_store.blocks[hash_tree_root(signed_block.message)] == signed_block.message
|
assert shard_store.blocks[hash_tree_root(signed_block.message)] == signed_block.message
|
||||||
|
|
||||||
|
|
||||||
def apply_shard_block(spec, store, shard_store, beacon_head_state, shard_blocks_buffer):
|
def apply_shard_block(spec, store, shard_store, beacon_parent_state, shard_blocks_buffer):
|
||||||
shard = shard_store.shard
|
shard = shard_store.shard
|
||||||
body = b'\x56' * 4
|
body = b'\x56' * 4
|
||||||
shard_head_root = spec.get_shard_head(store, shard_store)
|
shard_head_root = spec.get_shard_head(store, shard_store)
|
||||||
shard_parent_state = shard_store.block_states[shard_head_root]
|
shard_parent_state = shard_store.block_states[shard_head_root]
|
||||||
assert shard_parent_state.slot != beacon_head_state.slot
|
assert shard_parent_state.slot != beacon_parent_state.slot
|
||||||
shard_block = build_shard_block(
|
shard_block = build_shard_block(
|
||||||
spec, beacon_head_state, shard,
|
spec, beacon_parent_state, shard,
|
||||||
shard_parent_state=shard_parent_state, slot=beacon_head_state.slot, body=body, signed=True
|
shard_parent_state=shard_parent_state, slot=beacon_parent_state.slot, body=body, signed=True
|
||||||
)
|
)
|
||||||
shard_blocks_buffer.append(shard_block)
|
shard_blocks_buffer.append(shard_block)
|
||||||
run_on_shard_block(spec, store, shard_store, shard_block)
|
run_on_shard_block(spec, store, shard_store, shard_block)
|
||||||
|
@ -62,30 +62,26 @@ def apply_shard_and_beacon(spec, state, store, shard_store, shard_blocks_buffer)
|
||||||
committee_index = get_committee_index_of_shard(spec, state, state.slot, shard)
|
committee_index = get_committee_index_of_shard(spec, state, state.slot, shard)
|
||||||
has_shard_committee = committee_index is not None # has committee of `shard` at this slot
|
has_shard_committee = committee_index is not None # has committee of `shard` at this slot
|
||||||
|
|
||||||
# On beacon blocks at `state.slot + 1`
|
|
||||||
beacon_block = build_empty_block(spec, state, slot=state.slot + 1)
|
beacon_block = build_empty_block(spec, state, slot=state.slot + 1)
|
||||||
|
|
||||||
# If next slot has committee of `shard`, add `shard_transtion` to the proposing beacon block
|
# If next slot has committee of `shard`, add `shard_transtion` to the proposing beacon block
|
||||||
if has_shard_committee and len(shard_blocks_buffer) > 0:
|
if has_shard_committee and len(shard_blocks_buffer) > 0:
|
||||||
# Sanity check `get_pending_shard_blocks` function
|
# Sanity check `get_pending_shard_blocks` function
|
||||||
check_pending_shard_blocks(spec, store, shard_store, shard_blocks_buffer)
|
check_pending_shard_blocks(spec, store, shard_store, shard_blocks_buffer)
|
||||||
|
|
||||||
# Use temporary next state to get ShardTransition of shard block
|
# Use temporary next state to get ShardTransition of shard block
|
||||||
shard_transitions = build_shard_transitions_till_slot(
|
shard_transitions = get_shard_transitions(
|
||||||
spec,
|
spec,
|
||||||
state,
|
state,
|
||||||
shard_blocks={shard: shard_blocks_buffer},
|
shard_blocks={shard: shard_blocks_buffer},
|
||||||
on_time_slot=state.slot + 1,
|
|
||||||
)
|
)
|
||||||
shard_transition = shard_transitions[shard]
|
shard_transition = shard_transitions[shard]
|
||||||
attestation = build_attestation_with_shard_transition(
|
attestation = get_valid_on_time_attestation(
|
||||||
spec,
|
spec,
|
||||||
state,
|
state,
|
||||||
index=committee_index,
|
index=committee_index,
|
||||||
on_time_slot=state.slot + 1,
|
|
||||||
shard_transition=shard_transition,
|
shard_transition=shard_transition,
|
||||||
|
signed=False,
|
||||||
)
|
)
|
||||||
assert attestation.data.slot == state.slot
|
|
||||||
assert spec.get_shard(state, attestation) == shard
|
assert spec.get_shard(state, attestation) == shard
|
||||||
beacon_block.body.attestations = [attestation]
|
beacon_block.body.attestations = [attestation]
|
||||||
beacon_block.body.shard_transitions = shard_transitions
|
beacon_block.body.shard_transitions = shard_transitions
|
||||||
|
@ -93,11 +89,11 @@ def apply_shard_and_beacon(spec, state, store, shard_store, shard_blocks_buffer)
|
||||||
# Clear buffer
|
# Clear buffer
|
||||||
shard_blocks_buffer.clear()
|
shard_blocks_buffer.clear()
|
||||||
|
|
||||||
signed_beacon_block = state_transition_and_sign_block(spec, state, beacon_block)
|
signed_beacon_block = state_transition_and_sign_block(spec, state, beacon_block) # transition!
|
||||||
add_block_to_store(spec, store, signed_beacon_block)
|
add_block_to_store(spec, store, signed_beacon_block)
|
||||||
assert spec.get_head(store) == beacon_block.hash_tree_root()
|
assert spec.get_head(store) == beacon_block.hash_tree_root()
|
||||||
|
|
||||||
# On shard block at updated `state.slot`
|
# On shard block at transitioned `state.slot`
|
||||||
if is_in_offset_sets(spec, state, shard):
|
if is_in_offset_sets(spec, state, shard):
|
||||||
# The created shard block would be appended to `shard_blocks_buffer`
|
# The created shard block would be appended to `shard_blocks_buffer`
|
||||||
apply_shard_block(spec, store, shard_store, state, shard_blocks_buffer)
|
apply_shard_block(spec, store, shard_store, state, shard_blocks_buffer)
|
||||||
|
@ -129,7 +125,6 @@ def test_basic(spec, state):
|
||||||
shard_committee_counter = 2
|
shard_committee_counter = 2
|
||||||
shard_blocks_buffer = []
|
shard_blocks_buffer = []
|
||||||
while shard_committee_counter > 0:
|
while shard_committee_counter > 0:
|
||||||
print(f'state.slot', state.slot)
|
|
||||||
has_shard_committee = apply_shard_and_beacon(
|
has_shard_committee = apply_shard_and_beacon(
|
||||||
spec, state, store, shard_store, shard_blocks_buffer
|
spec, state, store, shard_store, shard_blocks_buffer
|
||||||
)
|
)
|
||||||
|
|
|
@ -85,9 +85,7 @@ def build_attestation_data(spec, state, slot, index, shard_transition=None, on_t
|
||||||
# No shard transition -> no shard block
|
# No shard transition -> no shard block
|
||||||
shard = spec.get_shard(state, spec.Attestation(data=attestation_data))
|
shard = spec.get_shard(state, spec.Attestation(data=attestation_data))
|
||||||
if on_time:
|
if on_time:
|
||||||
temp_state = state.copy()
|
shard_transition = spec.get_shard_transition(state, shard, shard_blocks=[])
|
||||||
next_slot(spec, temp_state)
|
|
||||||
shard_transition = spec.get_shard_transition(temp_state, shard, shard_blocks=[])
|
|
||||||
lastest_shard_data_root_index = len(shard_transition.shard_data_roots) - 1
|
lastest_shard_data_root_index = len(shard_transition.shard_data_roots) - 1
|
||||||
attestation_data.shard_head_root = shard_transition.shard_data_roots[lastest_shard_data_root_index]
|
attestation_data.shard_head_root = shard_transition.shard_data_roots[lastest_shard_data_root_index]
|
||||||
attestation_data.shard_transition_root = shard_transition.hash_tree_root()
|
attestation_data.shard_transition_root = shard_transition.hash_tree_root()
|
||||||
|
@ -320,9 +318,7 @@ def next_epoch_with_attestations(spec,
|
||||||
for index in range(committees_per_slot):
|
for index in range(committees_per_slot):
|
||||||
if spec.fork == PHASE1:
|
if spec.fork == PHASE1:
|
||||||
shard = spec.compute_shard_from_committee_index(post_state, index, slot_to_attest)
|
shard = spec.compute_shard_from_committee_index(post_state, index, slot_to_attest)
|
||||||
shard_transition = get_shard_transition_of_committee(
|
shard_transition = get_shard_transition_of_committee(spec, post_state, index)
|
||||||
spec, post_state, index, slot=slot_to_attest
|
|
||||||
)
|
|
||||||
block.body.shard_transitions[shard] = shard_transition
|
block.body.shard_transitions[shard] = shard_transition
|
||||||
else:
|
else:
|
||||||
shard_transition = None
|
shard_transition = None
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
from eth2spec.test.helpers.attestations import get_valid_on_time_attestation
|
|
||||||
from eth2spec.test.helpers.block import get_state_and_beacon_parent_root_at_slot
|
from eth2spec.test.helpers.block import get_state_and_beacon_parent_root_at_slot
|
||||||
from eth2spec.test.helpers.state import transition_to
|
|
||||||
from eth2spec.test.helpers.keys import privkeys
|
from eth2spec.test.helpers.keys import privkeys
|
||||||
from eth2spec.utils import bls
|
from eth2spec.utils import bls
|
||||||
from eth2spec.utils.bls import only_with_bls
|
from eth2spec.utils.bls import only_with_bls
|
||||||
|
@ -34,11 +32,8 @@ def build_shard_block(spec,
|
||||||
if body is None:
|
if body is None:
|
||||||
body = b'\x56' * 128
|
body = b'\x56' * 128
|
||||||
|
|
||||||
temp_state = beacon_state.copy()
|
|
||||||
transition_to(spec, temp_state, slot)
|
|
||||||
beacon_state, beacon_parent_root = get_state_and_beacon_parent_root_at_slot(spec, beacon_state, slot)
|
beacon_state, beacon_parent_root = get_state_and_beacon_parent_root_at_slot(spec, beacon_state, slot)
|
||||||
assert beacon_state == temp_state
|
proposer_index = spec.get_shard_proposer_index(beacon_state, slot, shard)
|
||||||
proposer_index = spec.get_shard_proposer_index(temp_state, slot, shard)
|
|
||||||
block = spec.ShardBlock(
|
block = spec.ShardBlock(
|
||||||
shard_parent_root=shard_parent_state.latest_block_root,
|
shard_parent_root=shard_parent_state.latest_block_root,
|
||||||
beacon_parent_root=beacon_parent_root,
|
beacon_parent_root=beacon_parent_root,
|
||||||
|
@ -57,14 +52,17 @@ def build_shard_block(spec,
|
||||||
return signed_block
|
return signed_block
|
||||||
|
|
||||||
|
|
||||||
def build_shard_transitions_till_slot(spec, state, shard_blocks, on_time_slot):
|
def get_shard_transitions(spec, parent_beacon_state, shard_blocks):
|
||||||
temp_state = state.copy()
|
|
||||||
transition_to(spec, temp_state, on_time_slot)
|
|
||||||
shard_transitions = [spec.ShardTransition()] * spec.MAX_SHARDS
|
shard_transitions = [spec.ShardTransition()] * spec.MAX_SHARDS
|
||||||
|
on_time_slot = parent_beacon_state.slot + 1
|
||||||
for shard, blocks in shard_blocks.items():
|
for shard, blocks in shard_blocks.items():
|
||||||
offset_slots = spec.get_offset_slots(temp_state, shard)
|
offset_slots = spec.compute_offset_slots(
|
||||||
|
spec.get_latest_slot_for_shard(parent_beacon_state, shard),
|
||||||
|
on_time_slot,
|
||||||
|
)
|
||||||
len_offset_slots = len(offset_slots)
|
len_offset_slots = len(offset_slots)
|
||||||
shard_transition = spec.get_shard_transition(temp_state, shard, blocks)
|
shard_transition = spec.get_shard_transition(parent_beacon_state, shard, blocks)
|
||||||
|
|
||||||
if len(blocks) > 0:
|
if len(blocks) > 0:
|
||||||
shard_block_root = blocks[-1].message.hash_tree_root()
|
shard_block_root = blocks[-1].message.hash_tree_root()
|
||||||
assert shard_transition.shard_states[len_offset_slots - 1].latest_block_root == shard_block_root
|
assert shard_transition.shard_states[len_offset_slots - 1].latest_block_root == shard_block_root
|
||||||
|
@ -74,22 +72,6 @@ def build_shard_transitions_till_slot(spec, state, shard_blocks, on_time_slot):
|
||||||
return shard_transitions
|
return shard_transitions
|
||||||
|
|
||||||
|
|
||||||
def build_attestation_with_shard_transition(spec, state, index, on_time_slot, shard_transition):
|
|
||||||
temp_state = state.copy()
|
|
||||||
transition_to(spec, temp_state, on_time_slot - 1)
|
|
||||||
attestation = get_valid_on_time_attestation(
|
|
||||||
spec,
|
|
||||||
temp_state,
|
|
||||||
index=index,
|
|
||||||
shard_transition=shard_transition,
|
|
||||||
signed=True,
|
|
||||||
)
|
|
||||||
assert attestation.data.slot == temp_state.slot
|
|
||||||
if shard_transition is not None:
|
|
||||||
assert attestation.data.shard_transition_root == shard_transition.hash_tree_root()
|
|
||||||
return attestation
|
|
||||||
|
|
||||||
|
|
||||||
def get_committee_index_of_shard(spec, state, slot, shard): # Optional[CommitteeIndex]
|
def get_committee_index_of_shard(spec, state, slot, shard): # Optional[CommitteeIndex]
|
||||||
active_shard_count = spec.get_active_shard_count(state)
|
active_shard_count = spec.get_active_shard_count(state)
|
||||||
committee_count = spec.get_committee_count_at_slot(state, slot)
|
committee_count = spec.get_committee_count_at_slot(state, slot)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from eth2spec.test.context import expect_assertion_error
|
from eth2spec.test.context import expect_assertion_error
|
||||||
from eth2spec.test.helpers.state import transition_to
|
|
||||||
|
|
||||||
|
|
||||||
def run_shard_transitions_processing(spec, state, shard_transitions, attestations, valid=True):
|
def run_shard_transitions_processing(spec, state, shard_transitions, attestations, valid=True):
|
||||||
|
@ -29,15 +28,10 @@ def run_shard_transitions_processing(spec, state, shard_transitions, attestation
|
||||||
yield 'post', state
|
yield 'post', state
|
||||||
|
|
||||||
|
|
||||||
def get_shard_transition_of_committee(spec, state, committee_index, slot=None, shard_blocks=None):
|
def get_shard_transition_of_committee(spec, state, committee_index, shard_blocks=None):
|
||||||
if shard_blocks is None:
|
if shard_blocks is None:
|
||||||
shard_blocks = []
|
shard_blocks = []
|
||||||
|
|
||||||
if slot is None:
|
|
||||||
slot = state.slot
|
|
||||||
|
|
||||||
shard = spec.compute_shard_from_committee_index(state, committee_index, state.slot)
|
shard = spec.compute_shard_from_committee_index(state, committee_index, state.slot)
|
||||||
temp_state = state.copy()
|
shard_transition = spec.get_shard_transition(state, shard, shard_blocks=shard_blocks)
|
||||||
transition_to(spec, temp_state, slot + 1)
|
|
||||||
shard_transition = spec.get_shard_transition(temp_state, shard, shard_blocks=shard_blocks)
|
|
||||||
return shard_transition
|
return shard_transition
|
||||||
|
|
|
@ -2,71 +2,64 @@ from eth2spec.test.context import (
|
||||||
PHASE0,
|
PHASE0,
|
||||||
with_all_phases_except,
|
with_all_phases_except,
|
||||||
spec_state_test,
|
spec_state_test,
|
||||||
always_bls,
|
|
||||||
)
|
)
|
||||||
|
from eth2spec.test.helpers.attestations import get_valid_on_time_attestation
|
||||||
from eth2spec.test.helpers.shard_transitions import run_shard_transitions_processing
|
from eth2spec.test.helpers.shard_transitions import run_shard_transitions_processing
|
||||||
from eth2spec.test.helpers.shard_block import (
|
from eth2spec.test.helpers.shard_block import (
|
||||||
build_attestation_with_shard_transition,
|
|
||||||
build_shard_block,
|
build_shard_block,
|
||||||
build_shard_transitions_till_slot,
|
get_shard_transitions,
|
||||||
)
|
)
|
||||||
from eth2spec.test.helpers.state import transition_to, transition_to_valid_shard_slot
|
from eth2spec.test.helpers.state import transition_to, transition_to_valid_shard_slot, next_slot
|
||||||
|
|
||||||
|
|
||||||
def run_basic_crosslink_tests(spec, state, target_len_offset_slot, valid=True):
|
def run_basic_crosslink_tests(spec, state, target_len_offset_slot, valid=True):
|
||||||
state = transition_to_valid_shard_slot(spec, state)
|
state = transition_to_valid_shard_slot(spec, state)
|
||||||
init_slot = state.slot
|
|
||||||
committee_index = spec.CommitteeIndex(0)
|
committee_index = spec.CommitteeIndex(0)
|
||||||
shard = spec.compute_shard_from_committee_index(state, committee_index, state.slot + target_len_offset_slot - 1)
|
shard = spec.compute_shard_from_committee_index(state, committee_index, state.slot + target_len_offset_slot - 1)
|
||||||
assert state.shard_states[shard].slot == state.slot - 1
|
assert state.shard_states[shard].slot == state.slot - 1
|
||||||
|
transition_to(spec, state, state.slot + target_len_offset_slot)
|
||||||
|
assert state.shard_states[shard].slot == state.slot - target_len_offset_slot - 1
|
||||||
|
|
||||||
# Create SignedShardBlock
|
# Create SignedShardBlock
|
||||||
body = b'\x56' * spec.MAX_SHARD_BLOCK_SIZE
|
body = b'\x56' * spec.MAX_SHARD_BLOCK_SIZE
|
||||||
shard_block = build_shard_block(spec, state, shard, body=body, signed=True)
|
shard_block = build_shard_block(spec, state, shard, body=body, slot=state.slot, signed=True)
|
||||||
shard_blocks = [shard_block]
|
shard_blocks = [shard_block]
|
||||||
# Create a shard_transitions that would be included at beacon block `state.slot + target_len_offset_slot`
|
shard_transitions = get_shard_transitions(
|
||||||
shard_transitions = build_shard_transitions_till_slot(
|
|
||||||
spec,
|
spec,
|
||||||
state,
|
state,
|
||||||
shard_blocks={shard: shard_blocks},
|
shard_blocks={shard: shard_blocks},
|
||||||
on_time_slot=state.slot + target_len_offset_slot,
|
|
||||||
)
|
)
|
||||||
shard_transition = shard_transitions[shard]
|
shard_transition = shard_transitions[shard]
|
||||||
# Create an attestation that would be included at beacon block `state.slot + target_len_offset_slot`
|
attestation = get_valid_on_time_attestation(
|
||||||
attestation = build_attestation_with_shard_transition(
|
|
||||||
spec,
|
spec,
|
||||||
state,
|
state,
|
||||||
index=committee_index,
|
index=committee_index,
|
||||||
on_time_slot=state.slot + target_len_offset_slot,
|
|
||||||
shard_transition=shard_transition,
|
shard_transition=shard_transition,
|
||||||
|
signed=False,
|
||||||
)
|
)
|
||||||
|
next_slot(spec, state)
|
||||||
pre_gasprice = state.shard_states[shard].gasprice
|
pre_gasprice = state.shard_states[shard].gasprice
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + target_len_offset_slot)
|
|
||||||
pre_shard_state = state.shard_states[shard]
|
pre_shard_state = state.shard_states[shard]
|
||||||
|
|
||||||
yield from run_shard_transitions_processing(spec, state, shard_transitions, [attestation], valid=valid)
|
yield from run_shard_transitions_processing(spec, state, shard_transitions, [attestation], valid=valid)
|
||||||
|
|
||||||
if valid:
|
if valid:
|
||||||
# After state transition,
|
|
||||||
assert state.slot == init_slot + target_len_offset_slot
|
|
||||||
shard_state = state.shard_states[shard]
|
shard_state = state.shard_states[shard]
|
||||||
assert shard_state != pre_shard_state
|
assert shard_state != pre_shard_state
|
||||||
assert shard_state == shard_transition.shard_states[len(shard_transition.shard_states) - 1]
|
assert shard_state == shard_transition.shard_states[len(shard_transition.shard_states) - 1]
|
||||||
|
assert shard_state.latest_block_root == shard_block.message.hash_tree_root()
|
||||||
if target_len_offset_slot == 1:
|
if target_len_offset_slot == 1:
|
||||||
assert shard_state.gasprice > pre_gasprice
|
assert shard_state.gasprice > pre_gasprice
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases_except([PHASE0])
|
@with_all_phases_except([PHASE0])
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
@always_bls
|
|
||||||
def test_basic_crosslinks(spec, state):
|
def test_basic_crosslinks(spec, state):
|
||||||
|
# NOTE: this test is only for full crosslink (minimal config), not for mainnet
|
||||||
yield from run_basic_crosslink_tests(spec, state, target_len_offset_slot=1, valid=True)
|
yield from run_basic_crosslink_tests(spec, state, target_len_offset_slot=1, valid=True)
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases_except([PHASE0])
|
@with_all_phases_except([PHASE0])
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
@always_bls
|
|
||||||
def test_multiple_offset_slots(spec, state):
|
def test_multiple_offset_slots(spec, state):
|
||||||
yield from run_basic_crosslink_tests(spec, state, target_len_offset_slot=3, valid=True)
|
# NOTE: this test is only for full crosslink (minimal config), not for mainnet
|
||||||
|
yield from run_basic_crosslink_tests(spec, state, target_len_offset_slot=2, valid=True)
|
||||||
|
|
|
@ -4,37 +4,40 @@ from eth2spec.test.context import (
|
||||||
PHASE0,
|
PHASE0,
|
||||||
with_all_phases_except,
|
with_all_phases_except,
|
||||||
spec_state_test,
|
spec_state_test,
|
||||||
always_bls,
|
|
||||||
)
|
)
|
||||||
|
from eth2spec.test.helpers.attestations import get_valid_on_time_attestation
|
||||||
from eth2spec.test.helpers.block import build_empty_block
|
from eth2spec.test.helpers.block import build_empty_block
|
||||||
from eth2spec.test.helpers.shard_block import (
|
from eth2spec.test.helpers.shard_block import (
|
||||||
build_attestation_with_shard_transition,
|
|
||||||
build_shard_block,
|
build_shard_block,
|
||||||
build_shard_transitions_till_slot,
|
get_shard_transitions,
|
||||||
)
|
)
|
||||||
from eth2spec.test.helpers.state import state_transition_and_sign_block, transition_to_valid_shard_slot
|
from eth2spec.test.helpers.state import state_transition_and_sign_block, transition_to_valid_shard_slot, transition_to
|
||||||
|
|
||||||
|
|
||||||
def run_beacon_block_with_shard_blocks(spec, state, shard_blocks, target_len_offset_slot, committee_index, valid=True):
|
def run_beacon_block_with_shard_blocks(spec, state, target_len_offset_slot, committee_index, shard, valid=True):
|
||||||
shard_transitions = build_shard_transitions_till_slot(
|
transition_to(spec, state, state.slot + target_len_offset_slot)
|
||||||
spec, state, shard_blocks, on_time_slot=state.slot + target_len_offset_slot
|
|
||||||
)
|
body = b'\x56' * spec.MAX_SHARD_BLOCK_SIZE
|
||||||
|
shard_block = build_shard_block(spec, state, shard, body=body, slot=state.slot, signed=True)
|
||||||
|
shard_blocks: Dict[spec.Shard, Sequence[spec.SignedShardBlock]] = {shard: [shard_block]}
|
||||||
|
|
||||||
|
shard_transitions = get_shard_transitions(spec, state, shard_blocks)
|
||||||
attestations = [
|
attestations = [
|
||||||
build_attestation_with_shard_transition(
|
get_valid_on_time_attestation(
|
||||||
spec,
|
spec,
|
||||||
state,
|
state,
|
||||||
on_time_slot=state.slot + target_len_offset_slot,
|
|
||||||
index=committee_index,
|
index=committee_index,
|
||||||
shard_transition=shard_transitions[shard],
|
shard_transition=shard_transitions[shard],
|
||||||
|
signed=True,
|
||||||
)
|
)
|
||||||
for shard in shard_blocks.keys()
|
for shard in shard_blocks.keys()
|
||||||
]
|
]
|
||||||
|
|
||||||
# Propose beacon block at slot `x + 1`
|
beacon_block = build_empty_block(spec, state, slot=state.slot + 1)
|
||||||
beacon_block = build_empty_block(spec, state, slot=state.slot + target_len_offset_slot)
|
|
||||||
beacon_block.body.attestations = attestations
|
beacon_block.body.attestations = attestations
|
||||||
beacon_block.body.shard_transitions = shard_transitions
|
beacon_block.body.shard_transitions = shard_transitions
|
||||||
|
|
||||||
|
pre_gasprice = state.shard_states[shard].gasprice
|
||||||
pre_shard_states = state.shard_states.copy()
|
pre_shard_states = state.shard_states.copy()
|
||||||
yield 'pre', state.copy()
|
yield 'pre', state.copy()
|
||||||
yield 'block', beacon_block
|
yield 'block', beacon_block
|
||||||
|
@ -52,17 +55,18 @@ def run_beacon_block_with_shard_blocks(spec, state, shard_blocks, target_len_off
|
||||||
assert post_shard_state == shard_transitions[shard].shard_states[
|
assert post_shard_state == shard_transitions[shard].shard_states[
|
||||||
len(shard_transitions[shard].shard_states) - 1
|
len(shard_transitions[shard].shard_states) - 1
|
||||||
]
|
]
|
||||||
assert beacon_block.slot == shard_transitions[shard].shard_states[0].slot + target_len_offset_slot
|
|
||||||
assert post_shard_state.slot == state.slot - 1
|
assert post_shard_state.slot == state.slot - 1
|
||||||
if len(shard_blocks[shard]) == 0:
|
if len(shard_blocks[shard]) == 0:
|
||||||
# `latest_block_root` is the same
|
# `latest_block_root` is the same
|
||||||
assert post_shard_state.latest_block_root == pre_shard_states[shard].latest_block_root
|
assert post_shard_state.latest_block_root == pre_shard_states[shard].latest_block_root
|
||||||
|
if target_len_offset_slot == 1 and len(shard_blocks) > 0:
|
||||||
|
assert post_shard_state.gasprice > pre_gasprice
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases_except([PHASE0])
|
@with_all_phases_except([PHASE0])
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
@always_bls
|
|
||||||
def test_process_beacon_block_with_normal_shard_transition(spec, state):
|
def test_process_beacon_block_with_normal_shard_transition(spec, state):
|
||||||
|
# NOTE: this test is only for full crosslink (minimal config), not for mainnet
|
||||||
state = transition_to_valid_shard_slot(spec, state)
|
state = transition_to_valid_shard_slot(spec, state)
|
||||||
|
|
||||||
target_len_offset_slot = 1
|
target_len_offset_slot = 1
|
||||||
|
@ -70,25 +74,13 @@ def test_process_beacon_block_with_normal_shard_transition(spec, state):
|
||||||
shard = spec.compute_shard_from_committee_index(state, committee_index, state.slot + target_len_offset_slot - 1)
|
shard = spec.compute_shard_from_committee_index(state, committee_index, state.slot + target_len_offset_slot - 1)
|
||||||
assert state.shard_states[shard].slot == state.slot - 1
|
assert state.shard_states[shard].slot == state.slot - 1
|
||||||
|
|
||||||
pre_gasprice = state.shard_states[shard].gasprice
|
yield from run_beacon_block_with_shard_blocks(spec, state, target_len_offset_slot, committee_index, shard)
|
||||||
|
|
||||||
# Create SignedShardBlock at slot `shard_state.slot + 1`
|
|
||||||
body = b'\x56' * spec.MAX_SHARD_BLOCK_SIZE
|
|
||||||
shard_block = build_shard_block(spec, state, shard, body=body, signed=True)
|
|
||||||
shard_blocks: Dict[spec.Shard, Sequence[spec.SignedShardBlock]] = {shard: [shard_block]}
|
|
||||||
|
|
||||||
yield from run_beacon_block_with_shard_blocks(spec, state, shard_blocks, target_len_offset_slot, committee_index)
|
|
||||||
|
|
||||||
shard_state = state.shard_states[shard]
|
|
||||||
|
|
||||||
if target_len_offset_slot == 1 and len(shard_blocks) > 0:
|
|
||||||
assert shard_state.gasprice > pre_gasprice
|
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases_except([PHASE0])
|
@with_all_phases_except([PHASE0])
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
@always_bls
|
|
||||||
def test_process_beacon_block_with_empty_proposal_transition(spec, state):
|
def test_process_beacon_block_with_empty_proposal_transition(spec, state):
|
||||||
|
# NOTE: this test is only for full crosslink (minimal config), not for mainnet
|
||||||
state = transition_to_valid_shard_slot(spec, state)
|
state = transition_to_valid_shard_slot(spec, state)
|
||||||
|
|
||||||
target_len_offset_slot = 1
|
target_len_offset_slot = 1
|
||||||
|
@ -96,12 +88,4 @@ def test_process_beacon_block_with_empty_proposal_transition(spec, state):
|
||||||
shard = spec.compute_shard_from_committee_index(state, committee_index, state.slot + target_len_offset_slot - 1)
|
shard = spec.compute_shard_from_committee_index(state, committee_index, state.slot + target_len_offset_slot - 1)
|
||||||
assert state.shard_states[shard].slot == state.slot - 1
|
assert state.shard_states[shard].slot == state.slot - 1
|
||||||
|
|
||||||
# No new shard block
|
yield from run_beacon_block_with_shard_blocks(spec, state, target_len_offset_slot, committee_index, shard)
|
||||||
shard_blocks = {}
|
|
||||||
|
|
||||||
pre_gasprice = state.shard_states[shard].gasprice
|
|
||||||
|
|
||||||
yield from run_beacon_block_with_shard_blocks(spec, state, shard_blocks, target_len_offset_slot, committee_index)
|
|
||||||
|
|
||||||
if target_len_offset_slot == 1 and len(shard_blocks) > 0:
|
|
||||||
assert state.shard_states[shard].gasprice > pre_gasprice
|
|
||||||
|
|
Loading…
Reference in New Issue