Refactor phase 1 block tests a bit and add `shard_state_transition`
tests 1. Refacotr phase1/sanity/test_blocks.py 2. Add phase1/sanity/test_shard_blocks.py for testing `verify_shard_block_message` and `verify_shard_block_signature`
This commit is contained in:
parent
021788a634
commit
4a46fb2a8a
|
@ -1,4 +1,4 @@
|
|||
# Ethereum 2.0 Phase 1 -- The Beacon Chain for Shards
|
||||
# Ethereum 2.0 Phase 1 -- The Beacon Chain with Shards
|
||||
|
||||
**Notice**: This document is a work-in-progress for researchers and implementers.
|
||||
|
||||
|
|
|
@ -11,7 +11,9 @@
|
|||
- [Introduction](#introduction)
|
||||
- [Helper functions](#helper-functions)
|
||||
- [Shard block verification functions](#shard-block-verification-functions)
|
||||
- [Shard state transition](#shard-state-transition)
|
||||
- [`verify_shard_block_message`](#verify_shard_block_message)
|
||||
- [`verify_shard_block_signature`](#verify_shard_block_signature)
|
||||
- [Shard state transition function](#shard-state-transition-function)
|
||||
- [Fraud proofs](#fraud-proofs)
|
||||
- [Verifying the proof](#verifying-the-proof)
|
||||
|
||||
|
@ -25,6 +27,8 @@ This document describes the shard transition function and fraud proofs as part o
|
|||
|
||||
### Shard block verification functions
|
||||
|
||||
#### `verify_shard_block_message`
|
||||
|
||||
```python
|
||||
def verify_shard_block_message(beacon_parent_state: BeaconState,
|
||||
shard_parent_state: ShardState,
|
||||
|
@ -49,6 +53,8 @@ def verify_shard_block_message(beacon_parent_state: BeaconState,
|
|||
return True
|
||||
```
|
||||
|
||||
#### `verify_shard_block_signature`
|
||||
|
||||
```python
|
||||
def verify_shard_block_signature(beacon_parent_state: BeaconState,
|
||||
signed_block: SignedShardBlock) -> bool:
|
||||
|
@ -58,7 +64,9 @@ def verify_shard_block_signature(beacon_parent_state: BeaconState,
|
|||
return bls.Verify(proposer.pubkey, signing_root, signed_block.signature)
|
||||
```
|
||||
|
||||
## Shard state transition
|
||||
## Shard state transition function
|
||||
|
||||
The post-state corresponding to a pre-state `shard_state` and a signed block `signed_block` is defined as `shard_state_transition(state, signed_block)`. State transitions that trigger an unhandled exception (e.g. a failed `assert` or an out-of-range list access) are considered invalid. State transitions that cause a `uint64` overflow or underflow are also considered invalid.
|
||||
|
||||
```python
|
||||
def shard_state_transition(shard_state: ShardState,
|
||||
|
|
|
@ -35,3 +35,8 @@ def get_shard_transition_of_committee(spec, state, committee_index, shard_blocks
|
|||
shard = spec.compute_shard_from_committee_index(state, committee_index, state.slot)
|
||||
shard_transition = spec.get_shard_transition(state, shard, shard_blocks=shard_blocks)
|
||||
return shard_transition
|
||||
|
||||
|
||||
def is_full_crosslink(spec, state):
|
||||
epoch = spec.compute_epoch_at_slot(state.slot)
|
||||
return spec.get_committee_count_per_slot(state, epoch) >= spec.get_active_shard_count(state)
|
||||
|
|
|
@ -8,7 +8,10 @@ from eth2spec.test.helpers.attestations import (
|
|||
get_valid_on_time_attestation,
|
||||
run_attestation_processing,
|
||||
)
|
||||
from eth2spec.test.helpers.shard_transitions import run_shard_transitions_processing
|
||||
from eth2spec.test.helpers.shard_transitions import (
|
||||
run_shard_transitions_processing,
|
||||
is_full_crosslink,
|
||||
)
|
||||
from eth2spec.test.helpers.shard_block import (
|
||||
build_shard_block,
|
||||
get_shard_transitions,
|
||||
|
@ -42,11 +45,6 @@ def get_attestations_and_shard_transitions(spec, state, shard_block_dict):
|
|||
return attestations, shard_transitions
|
||||
|
||||
|
||||
def is_full_crosslink(spec, state):
|
||||
epoch = spec.compute_epoch_at_slot(state.slot)
|
||||
return spec.get_committee_count_per_slot(state, epoch) >= spec.get_active_shard_count(state)
|
||||
|
||||
|
||||
def run_successful_crosslink_tests(spec, state, target_len_offset_slot):
|
||||
state, shard, target_shard_slot = get_initial_env(spec, state, target_len_offset_slot)
|
||||
init_slot = state.slot
|
||||
|
|
|
@ -9,15 +9,17 @@ from eth2spec.test.helpers.attestations import get_valid_on_time_attestation
|
|||
from eth2spec.test.helpers.block import build_empty_block
|
||||
from eth2spec.test.helpers.shard_block import (
|
||||
build_shard_block,
|
||||
get_sample_shard_block_body,
|
||||
get_shard_transitions,
|
||||
)
|
||||
from eth2spec.test.helpers.shard_transitions import is_full_crosslink
|
||||
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, target_len_offset_slot, committee_index, shard, valid=True):
|
||||
transition_to(spec, state, state.slot + target_len_offset_slot)
|
||||
|
||||
body = b'\x56' * spec.MAX_SHARD_BLOCK_SIZE
|
||||
body = get_sample_shard_block_body(spec, is_max=True)
|
||||
shard_block = build_shard_block(spec, state, shard, body=body, slot=state.slot, signed=True)
|
||||
shard_block_dict: Dict[spec.Shard, Sequence[spec.SignedShardBlock]] = {shard: [shard_block]}
|
||||
|
||||
|
@ -40,13 +42,16 @@ def run_beacon_block_with_shard_blocks(spec, state, target_len_offset_slot, comm
|
|||
pre_gasprice = state.shard_states[shard].gasprice
|
||||
pre_shard_states = state.shard_states.copy()
|
||||
yield 'pre', state.copy()
|
||||
yield 'block', beacon_block
|
||||
state_transition_and_sign_block(spec, state, beacon_block)
|
||||
if valid:
|
||||
yield 'post', state
|
||||
else:
|
||||
|
||||
if not valid:
|
||||
state_transition_and_sign_block(spec, state, beacon_block, expect_fail=True)
|
||||
yield 'block', beacon_block
|
||||
yield 'post', None
|
||||
return
|
||||
else:
|
||||
state_transition_and_sign_block(spec, state, beacon_block)
|
||||
yield 'block', beacon_block
|
||||
yield 'post', None
|
||||
|
||||
for shard in range(spec.get_active_shard_count(state)):
|
||||
post_shard_state = state.shard_states[shard]
|
||||
|
@ -67,6 +72,10 @@ def run_beacon_block_with_shard_blocks(spec, state, target_len_offset_slot, comm
|
|||
@spec_state_test
|
||||
def test_process_beacon_block_with_normal_shard_transition(spec, state):
|
||||
# NOTE: this test is only for full crosslink (minimal config), not for mainnet
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
state = transition_to_valid_shard_slot(spec, state)
|
||||
|
||||
target_len_offset_slot = 1
|
||||
|
@ -81,6 +90,10 @@ def test_process_beacon_block_with_normal_shard_transition(spec, state):
|
|||
@spec_state_test
|
||||
def test_process_beacon_block_with_empty_proposal_transition(spec, state):
|
||||
# NOTE: this test is only for full crosslink (minimal config), not for mainnet
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
state = transition_to_valid_shard_slot(spec, state)
|
||||
|
||||
target_len_offset_slot = 1
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
from eth2spec.test.context import (
|
||||
PHASE0,
|
||||
always_bls,
|
||||
expect_assertion_error,
|
||||
spec_state_test,
|
||||
with_all_phases_except,
|
||||
)
|
||||
from eth2spec.test.helpers.shard_block import (
|
||||
build_shard_block,
|
||||
sign_shard_block,
|
||||
)
|
||||
from eth2spec.test.helpers.shard_transitions import is_full_crosslink
|
||||
from eth2spec.test.helpers.state import transition_to_valid_shard_slot
|
||||
|
||||
|
||||
def run_shard_blocks(spec, shard_state, signed_shard_block,
|
||||
beacon_parent_state,
|
||||
validate=True, valid=True):
|
||||
yield 'pre', shard_state.copy()
|
||||
yield 'signed_shard_block', signed_shard_block
|
||||
yield 'validate', validate
|
||||
yield 'beacon_parent_state', beacon_parent_state
|
||||
|
||||
if validate is False:
|
||||
beacon_parent_state = None
|
||||
|
||||
if not valid:
|
||||
expect_assertion_error(lambda: spec.shard_state_transition(
|
||||
shard_state, signed_shard_block, validate=validate, beacon_parent_state=beacon_parent_state)
|
||||
)
|
||||
yield 'post', None
|
||||
else:
|
||||
spec.shard_state_transition(shard_state, signed_shard_block,
|
||||
validate=validate, beacon_parent_state=beacon_parent_state)
|
||||
yield 'post', shard_state
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_valid_shard_block(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True)
|
||||
|
||||
yield from run_shard_blocks(spec, shard_state, signed_shard_block, beacon_state)
|
||||
|
||||
|
||||
#
|
||||
# verify_shard_block_message
|
||||
#
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_invalid_shard_parent_root(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True)
|
||||
signed_shard_block.message.shard_parent_root = b'\x12' * 32
|
||||
sign_shard_block(spec, beacon_state, shard, signed_shard_block)
|
||||
|
||||
yield from run_shard_blocks(spec, shard_state, signed_shard_block, beacon_state, valid=False)
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_invalid_beacon_parent_root(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True)
|
||||
signed_shard_block.message.beacon_parent_root = b'\x12' * 32
|
||||
sign_shard_block(spec, beacon_state, shard, signed_shard_block)
|
||||
|
||||
yield from run_shard_blocks(spec, shard_state, signed_shard_block, beacon_state, valid=False)
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_invalid_slot(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True)
|
||||
signed_shard_block.message.slot = beacon_state.slot + 1
|
||||
proposer_index = spec.get_shard_proposer_index(beacon_state, signed_shard_block.message.slot, shard)
|
||||
sign_shard_block(spec, beacon_state, shard, signed_shard_block, proposer_index=proposer_index)
|
||||
|
||||
yield from run_shard_blocks(spec, shard_state, signed_shard_block, beacon_state, valid=False)
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_invalid_proposer_index(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True)
|
||||
active_validator_indices = spec.get_active_validator_indices(beacon_state, spec.get_current_epoch(beacon_state))
|
||||
proposer_index = (
|
||||
(spec.get_shard_proposer_index(beacon_state, signed_shard_block.message.slot, shard) + 1)
|
||||
% len(active_validator_indices)
|
||||
)
|
||||
signed_shard_block.message.proposer_index = proposer_index
|
||||
sign_shard_block(spec, beacon_state, shard, signed_shard_block, proposer_index=proposer_index)
|
||||
|
||||
yield from run_shard_blocks(spec, shard_state, signed_shard_block, beacon_state, valid=False)
|
||||
|
||||
|
||||
#
|
||||
# verify_shard_block_signature
|
||||
#
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_invalid_signature(spec, state):
|
||||
if not is_full_crosslink(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=False)
|
||||
|
||||
yield from run_shard_blocks(spec, shard_state, signed_shard_block, beacon_state, valid=False)
|
Loading…
Reference in New Issue