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:
Hsiao-Wei Wang 2020-06-24 18:08:26 +08:00
parent 021788a634
commit 4a46fb2a8a
No known key found for this signature in database
GPG Key ID: 95B070122902DEA4
6 changed files with 190 additions and 15 deletions

View File

@ -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.

View File

@ -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,

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)