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.
|
**Notice**: This document is a work-in-progress for researchers and implementers.
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,9 @@
|
||||||
- [Introduction](#introduction)
|
- [Introduction](#introduction)
|
||||||
- [Helper functions](#helper-functions)
|
- [Helper functions](#helper-functions)
|
||||||
- [Shard block verification functions](#shard-block-verification-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)
|
- [Fraud proofs](#fraud-proofs)
|
||||||
- [Verifying the proof](#verifying-the-proof)
|
- [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
|
### Shard block verification functions
|
||||||
|
|
||||||
|
#### `verify_shard_block_message`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def verify_shard_block_message(beacon_parent_state: BeaconState,
|
def verify_shard_block_message(beacon_parent_state: BeaconState,
|
||||||
shard_parent_state: ShardState,
|
shard_parent_state: ShardState,
|
||||||
|
@ -49,6 +53,8 @@ def verify_shard_block_message(beacon_parent_state: BeaconState,
|
||||||
return True
|
return True
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `verify_shard_block_signature`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def verify_shard_block_signature(beacon_parent_state: BeaconState,
|
def verify_shard_block_signature(beacon_parent_state: BeaconState,
|
||||||
signed_block: SignedShardBlock) -> bool:
|
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)
|
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
|
```python
|
||||||
def shard_state_transition(shard_state: ShardState,
|
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 = spec.compute_shard_from_committee_index(state, committee_index, state.slot)
|
||||||
shard_transition = spec.get_shard_transition(state, shard, shard_blocks=shard_blocks)
|
shard_transition = spec.get_shard_transition(state, shard, shard_blocks=shard_blocks)
|
||||||
return shard_transition
|
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,
|
get_valid_on_time_attestation,
|
||||||
run_attestation_processing,
|
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 (
|
from eth2spec.test.helpers.shard_block import (
|
||||||
build_shard_block,
|
build_shard_block,
|
||||||
get_shard_transitions,
|
get_shard_transitions,
|
||||||
|
@ -42,11 +45,6 @@ def get_attestations_and_shard_transitions(spec, state, shard_block_dict):
|
||||||
return attestations, shard_transitions
|
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):
|
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)
|
state, shard, target_shard_slot = get_initial_env(spec, state, target_len_offset_slot)
|
||||||
init_slot = state.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.block import build_empty_block
|
||||||
from eth2spec.test.helpers.shard_block import (
|
from eth2spec.test.helpers.shard_block import (
|
||||||
build_shard_block,
|
build_shard_block,
|
||||||
|
get_sample_shard_block_body,
|
||||||
get_shard_transitions,
|
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
|
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):
|
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)
|
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 = 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]}
|
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_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()
|
||||||
|
|
||||||
|
if not valid:
|
||||||
|
state_transition_and_sign_block(spec, state, beacon_block, expect_fail=True)
|
||||||
yield 'block', beacon_block
|
yield 'block', beacon_block
|
||||||
state_transition_and_sign_block(spec, state, beacon_block)
|
|
||||||
if valid:
|
|
||||||
yield 'post', state
|
|
||||||
else:
|
|
||||||
yield 'post', None
|
yield 'post', None
|
||||||
return
|
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)):
|
for shard in range(spec.get_active_shard_count(state)):
|
||||||
post_shard_state = state.shard_states[shard]
|
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
|
@spec_state_test
|
||||||
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
|
# 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)
|
state = transition_to_valid_shard_slot(spec, state)
|
||||||
|
|
||||||
target_len_offset_slot = 1
|
target_len_offset_slot = 1
|
||||||
|
@ -81,6 +90,10 @@ def test_process_beacon_block_with_normal_shard_transition(spec, state):
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
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
|
# 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)
|
state = transition_to_valid_shard_slot(spec, state)
|
||||||
|
|
||||||
target_len_offset_slot = 1
|
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