From b892d46f26da482af948c7da53586299d335c4f6 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 27 Sep 2019 13:02:16 +0900 Subject: [PATCH 1/8] working through shard chain tests --- scripts/function_puller.py | 2 +- specs/core/1_shard-data-chains.md | 21 ++++++- .../test/helpers/phase1/attestations.py | 13 ++-- .../test/helpers/phase1/shard_block.py | 61 +++++++++---------- .../shard_data_chain/test_shard_block.py | 34 +++++------ 5 files changed, 71 insertions(+), 60 deletions(-) diff --git a/scripts/function_puller.py b/scripts/function_puller.py index 26671bafc..b30e5b75c 100644 --- a/scripts/function_puller.py +++ b/scripts/function_puller.py @@ -81,7 +81,7 @@ def get_spec(file_name: str) -> SpecObject: if c not in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789': is_constant_def = False if is_constant_def: - constants[row[0]] = row[1].replace('**TBD**', '0x1234567890123456789012345678901234567890') + constants[row[0]] = row[1].replace('**TBD**', '2**32') elif row[1].startswith('uint') or row[1].startswith('Bytes'): custom_types[row[0]] = row[1] return functions, custom_types, constants, ssz_objects, inserts diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 96b2de41a..866140831 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -248,6 +248,11 @@ def get_genesis_shard_state(shard: Shard) -> ShardState: return ShardState( shard=shard, slot=ShardSlot(SHARD_GENESIS_EPOCH * SHARD_SLOTS_PER_EPOCH), + latest_block_header=ShardBlockHeader( + shard=shard, + slot=ShardSlot(SHARD_GENESIS_EPOCH * SHARD_SLOTS_PER_EPOCH), + body_root=hash_tree_root(List[byte, MAX_SHARD_BLOCK_SIZE - SHARD_HEADER_SIZE]()), + ), block_body_price=MIN_BLOCK_BODY_PRICE, ) ``` @@ -335,9 +340,14 @@ def process_shard_block_header(beacon_state: BeaconState, shard_state: ShardStat assert block.slot == shard_state.slot # Verify the beacon chain root parent_epoch = compute_epoch_of_shard_slot(shard_state.latest_block_header.slot) - assert block.beacon_block_root == get_block_root(beacon_state, parent_epoch) + # --super dirty. need to think-- # + if parent_epoch * SLOTS_PER_EPOCH == beacon_state.slot: + beacon_block_root = signing_root(beacon_state.latest_block_header) + else: + beacon_block_root = get_block_root(beacon_state, parent_epoch) + assert block.beacon_block_root == beacon_block_root # Verify the parent root - assert block.parent_root == hash_tree_root(shard_state.latest_block_header) + assert block.parent_root == signing_root(shard_state.latest_block_header) # Save current block as the new latest block shard_state.latest_block_header = ShardBlockHeader( shard=block.shard, @@ -376,12 +386,17 @@ def process_shard_attestations(beacon_state: BeaconState, shard_state: ShardStat pubkeys.append(beacon_state.validators[validator_index].pubkey) process_delta(beacon_state, shard_state, validator_index, get_base_reward(beacon_state, validator_index)) attestation_count += 1 + # Exit early if no participants + if not any(pubkeys): + assert block.attestations == BLSSignature() + return + # Verify there are no extraneous bits set beyond the shard committee for i in range(len(shard_committee), 2 * MAX_PERIOD_COMMITTEE_SIZE): assert block.aggregation_bits[i] == 0b0 # Verify attester aggregate signature domain = get_domain(beacon_state, DOMAIN_SHARD_ATTESTER, compute_epoch_of_shard_slot(block.slot)) - message = hash_tree_root(ShardAttestationData(shard_state.slot, block.parent_root)) + message = hash_tree_root(ShardAttestationData(slot=shard_state.slot, parent_root=block.parent_root)) assert bls_verify(bls_aggregate_pubkeys(pubkeys), message, block.attestations, domain) # Proposer micro-reward proposer_index = get_shard_proposer_index(beacon_state, shard_state.shard, block.slot) diff --git a/test_libs/pyspec/eth2spec/test/helpers/phase1/attestations.py b/test_libs/pyspec/eth2spec/test/helpers/phase1/attestations.py index 750ab5048..4f0a9fb0a 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/phase1/attestations.py +++ b/test_libs/pyspec/eth2spec/test/helpers/phase1/attestations.py @@ -5,17 +5,20 @@ from eth2spec.utils.bls import ( ) -def sign_shard_attestation(spec, shard_state, beacon_state, block, participants): +def sign_shard_attestation(spec, beacon_state, shard_state, block, participants): signatures = [] - message_hash = block.core.parent_root - block_epoch = spec.compute_epoch_of_shard_slot(block.core.slot) + message_hash = spec.ShardAttestationData( + slot=block.slot, + parent_root=block.parent_root, + ).hash_tree_root() + block_epoch = spec.compute_epoch_of_shard_slot(block.slot) for validator_index in participants: privkey = privkeys[validator_index] signatures.append( get_attestation_signature( spec, - shard_state, beacon_state, + shard_state, message_hash, block_epoch, privkey, @@ -25,7 +28,7 @@ def sign_shard_attestation(spec, shard_state, beacon_state, block, participants) return bls_aggregate_signatures(signatures) -def get_attestation_signature(spec, shard_state, beacon_state, message_hash, block_epoch, privkey): +def get_attestation_signature(spec, beacon_state, shard_state, message_hash, block_epoch, privkey): return bls_sign( message_hash=message_hash, privkey=privkey, 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 b9c388a3f..3ceb3e0aa 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py +++ b/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py @@ -1,3 +1,5 @@ +from copy import deepcopy + from eth2spec.test.helpers.keys import privkeys from eth2spec.utils.bls import ( bls_sign, @@ -13,66 +15,63 @@ from .attestations import ( @only_with_bls() -def sign_shard_block(spec, state, block, shard, proposer_index=None): +def sign_shard_block(spec, beacon_state, block, shard, proposer_index=None): if proposer_index is None: - proposer_index = spec.get_shard_block_proposer_index(state, shard, block.core.slot) + proposer_index = spec.get_shard_proposer_index(beacon_state, shard, block.slot) privkey = privkeys[proposer_index] - block.signatures.proposer_signature = bls_sign( + block.signature = bls_sign( message_hash=signing_root(block), privkey=privkey, domain=spec.get_domain( - state, + beacon_state, spec.DOMAIN_SHARD_PROPOSER, - spec.compute_epoch_of_shard_slot(block.core.slot), + spec.compute_epoch_of_shard_slot(block.slot), ) ) def build_empty_shard_block(spec, - shard_state, beacon_state, + shard_state, slot, - parent_root, signed=False, full_attestation=False): if slot is None: slot = shard_state.slot + parent_epoch = spec.compute_epoch_of_shard_slot(shard_state.latest_block_header.slot) + if parent_epoch * spec.SLOTS_PER_EPOCH == beacon_state.slot: + beacon_block_root = spec.signing_root(beacon_state.latest_block_header) + else: + beacon_block_root = spec.get_block_root(beacon_state, parent_epoch) + + previous_block_header = deepcopy(shard_state.latest_block_header) + if previous_block_header.state_root == spec.Hash(): + previous_block_header.state_root = shard_state.hash_tree_root() + parent_root = signing_root(previous_block_header) + block = spec.ShardBlock( - core=spec.ExtendedShardBlockCore( - slot=slot, - 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'\x00' * 96, - proposer_signature=b'\x25' * 96, - ) + shard=shard_state.shard, + slot=slot, + beacon_block_root=beacon_block_root, + parent_root=parent_root, + block_size_sum=shard_state.block_size_sum + spec.SHARD_HEADER_SIZE, ) - # 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.TARGET_PERSISTENT_COMMITTEE_SIZE * 2 - len(attester_committee)) + shard_committee = spec.get_shard_committee(beacon_state, shard_state.shard, block.slot) + block.aggregation_bits = list( + (True,) * len(shard_committee) + + (False,) * (spec.MAX_PERIOD_COMMITTEE_SIZE * 2 - len(shard_committee)) ) - block.signatures.attestation_signature = sign_shard_attestation( + block.attestations = 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=(), + participants=shard_committee, ) if signed: 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 2bb0232f0..b0d8ad5e6 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 @@ -9,22 +9,19 @@ from eth2spec.test.context import ( @with_all_phases_except(['phase0']) -@always_bls @spec_state_test +@always_bls 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 + beacon_state.slot = spec.Slot(spec.SHARD_GENESIS_EPOCH * spec.SLOTS_PER_EPOCH) + shard_state = spec.get_genesis_shard_state(spec.Shard(0)) + shard_state.slot = spec.ShardSlot(spec.SHARD_GENESIS_EPOCH * spec.SHARD_SLOTS_PER_EPOCH) block = build_empty_shard_block( spec, - shard_state, beacon_state, - slot=shard_slot + 1, - parent_root=spec.Hash(), + shard_state, + slot=shard_state.slot + 1, signed=True, full_attestation=False, ) @@ -33,28 +30,25 @@ def test_process_empty_shard_block(spec, state): yield 'beacon_state', beacon_state yield 'block', block - spec.shard_state_transition(shard_state, beacon_state, block) + spec.shard_state_transition(beacon_state, shard_state, block) yield 'post', shard_state @with_all_phases_except(['phase0']) -@always_bls @spec_state_test +@always_bls 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 + beacon_state.slot = spec.Slot(spec.SHARD_GENESIS_EPOCH * spec.SLOTS_PER_EPOCH) + shard_state = spec.get_genesis_shard_state(spec.Shard(0)) + shard_state.slot = spec.SHARD_GENESIS_EPOCH * spec.SHARD_SLOTS_PER_EPOCH block = build_empty_shard_block( spec, - shard_state, beacon_state, - slot=shard_slot + 1, - parent_root=spec.Hash(), + shard_state, + slot=shard_state.slot + 1, signed=True, full_attestation=True, ) @@ -63,6 +57,6 @@ def test_process_full_attestation_shard_block(spec, state): yield 'beacon_state', beacon_state yield 'block', block - spec.shard_state_transition(shard_state, beacon_state, block) + spec.shard_state_transition(beacon_state, shard_state, block) yield 'post', shard_state From 3bc05dfff7cf0833a381d54b679f898a7d9753bf Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 27 Sep 2019 13:20:23 +0900 Subject: [PATCH 2/8] remove outdated beacon attestation test --- .../test_beacon_attestation.py | 48 ------------------- .../shard_data_chain/test_shard_block.py | 6 +-- 2 files changed, 2 insertions(+), 52 deletions(-) delete mode 100644 test_libs/pyspec/eth2spec/test/phase_1/shard_data_chain/test_beacon_attestation.py 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 deleted file mode 100644 index aface905b..000000000 --- a/test_libs/pyspec/eth2spec/test/phase_1/shard_data_chain/test_beacon_attestation.py +++ /dev/null @@ -1,48 +0,0 @@ -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 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 b0d8ad5e6..6622c1940 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,8 +11,7 @@ from eth2spec.test.context import ( @with_all_phases_except(['phase0']) @spec_state_test @always_bls -def test_process_empty_shard_block(spec, state): - beacon_state = state +def test_process_empty_shard_block(spec, beacon_state): beacon_state.slot = spec.Slot(spec.SHARD_GENESIS_EPOCH * spec.SLOTS_PER_EPOCH) shard_state = spec.get_genesis_shard_state(spec.Shard(0)) shard_state.slot = spec.ShardSlot(spec.SHARD_GENESIS_EPOCH * spec.SHARD_SLOTS_PER_EPOCH) @@ -38,8 +37,7 @@ def test_process_empty_shard_block(spec, state): @with_all_phases_except(['phase0']) @spec_state_test @always_bls -def test_process_full_attestation_shard_block(spec, state): - beacon_state = state +def test_process_full_attestation_shard_block(spec, beacon_state): beacon_state.slot = spec.Slot(spec.SHARD_GENESIS_EPOCH * spec.SLOTS_PER_EPOCH) shard_state = spec.get_genesis_shard_state(spec.Shard(0)) shard_state.slot = spec.SHARD_GENESIS_EPOCH * spec.SHARD_SLOTS_PER_EPOCH From 77faa026a03834b33a01852175f8919f0089ab72 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 27 Sep 2019 13:30:31 +0900 Subject: [PATCH 3/8] minor fix to tests --- .../test/phase_1/shard_data_chain/test_shard_block.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 6622c1940..b0d8ad5e6 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,7 +11,8 @@ from eth2spec.test.context import ( @with_all_phases_except(['phase0']) @spec_state_test @always_bls -def test_process_empty_shard_block(spec, beacon_state): +def test_process_empty_shard_block(spec, state): + beacon_state = state beacon_state.slot = spec.Slot(spec.SHARD_GENESIS_EPOCH * spec.SLOTS_PER_EPOCH) shard_state = spec.get_genesis_shard_state(spec.Shard(0)) shard_state.slot = spec.ShardSlot(spec.SHARD_GENESIS_EPOCH * spec.SHARD_SLOTS_PER_EPOCH) @@ -37,7 +38,8 @@ def test_process_empty_shard_block(spec, beacon_state): @with_all_phases_except(['phase0']) @spec_state_test @always_bls -def test_process_full_attestation_shard_block(spec, beacon_state): +def test_process_full_attestation_shard_block(spec, state): + beacon_state = state beacon_state.slot = spec.Slot(spec.SHARD_GENESIS_EPOCH * spec.SLOTS_PER_EPOCH) shard_state = spec.get_genesis_shard_state(spec.Shard(0)) shard_state.slot = spec.SHARD_GENESIS_EPOCH * spec.SHARD_SLOTS_PER_EPOCH From 49a291909936cec4653db407f8e53ca09631f136 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 30 Sep 2019 12:58:05 +0900 Subject: [PATCH 4/8] add more shard block sanity tests --- .../test/helpers/phase1/shard_block.py | 6 +- .../test/helpers/phase1/shard_state.py | 18 ++ .../test/phase_1/sanity/test_shard_blocks.py | 170 ++++++++++++++++++ .../shard_data_chain/test_shard_block.py | 62 ------- 4 files changed, 191 insertions(+), 65 deletions(-) create mode 100644 test_libs/pyspec/eth2spec/test/helpers/phase1/shard_state.py create mode 100644 test_libs/pyspec/eth2spec/test/phase_1/sanity/test_shard_blocks.py delete mode 100644 test_libs/pyspec/eth2spec/test/phase_1/shard_data_chain/test_shard_block.py 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 3ceb3e0aa..ea5783655 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py +++ b/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py @@ -15,9 +15,9 @@ from .attestations import ( @only_with_bls() -def sign_shard_block(spec, beacon_state, block, shard, proposer_index=None): +def sign_shard_block(spec, beacon_state, shard_state, block, proposer_index=None): if proposer_index is None: - proposer_index = spec.get_shard_proposer_index(beacon_state, shard, block.slot) + proposer_index = spec.get_shard_proposer_index(beacon_state, shard_state.shard, block.slot) privkey = privkeys[proposer_index] @@ -75,6 +75,6 @@ def build_empty_shard_block(spec, ) if signed: - sign_shard_block(spec, beacon_state, block, shard_state.shard) + sign_shard_block(spec, beacon_state, shard_state, block) return block diff --git a/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_state.py b/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_state.py new file mode 100644 index 000000000..24240b5fa --- /dev/null +++ b/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_state.py @@ -0,0 +1,18 @@ +from eth2spec.test.helpers.phase1.shard_block import sign_shard_block + + +def configure_shard_state(spec, beacon_state, shard=0): + beacon_state.slot = spec.Slot(spec.SHARD_GENESIS_EPOCH * spec.SLOTS_PER_EPOCH) + shard_state = spec.get_genesis_shard_state(spec.Shard(shard)) + shard_state.slot = spec.ShardSlot(spec.SHARD_GENESIS_EPOCH * spec.SHARD_SLOTS_PER_EPOCH) + return beacon_state, shard_state + + +def shard_state_transition_and_sign_block(spec, beacon_state, shard_state, block): + """ + Shard state transition via the provided ``block`` + then package the block with the state root and signature. + """ + spec.shard_state_transition(beacon_state, shard_state, block) + block.state_root = shard_state.hash_tree_root() + sign_shard_block(spec, beacon_state, shard_state, block) diff --git a/test_libs/pyspec/eth2spec/test/phase_1/sanity/test_shard_blocks.py b/test_libs/pyspec/eth2spec/test/phase_1/sanity/test_shard_blocks.py new file mode 100644 index 000000000..2669ffb53 --- /dev/null +++ b/test_libs/pyspec/eth2spec/test/phase_1/sanity/test_shard_blocks.py @@ -0,0 +1,170 @@ +from copy import deepcopy + +from eth2spec.test.helpers.phase1.shard_block import ( + build_empty_shard_block, + sign_shard_block, +) +from eth2spec.test.helpers.phase1.shard_state import ( + configure_shard_state, + shard_state_transition_and_sign_block, +) +from eth2spec.test.context import ( + always_bls, + expect_assertion_error, + spec_state_test, + with_all_phases_except, +) + + +@with_all_phases_except(['phase0']) +@spec_state_test +@always_bls +def test_process_empty_shard_block(spec, state): + beacon_state, shard_state = configure_shard_state(spec, state) + + block = build_empty_shard_block( + spec, + beacon_state, + shard_state, + slot=shard_state.slot + 1, + signed=True, + full_attestation=False, + ) + + yield 'pre', shard_state + yield 'beacon_state', beacon_state + + shard_state_transition_and_sign_block(spec, beacon_state, shard_state, block) + + yield 'blocks', [block] + yield 'post', shard_state + + +@with_all_phases_except(['phase0']) +@spec_state_test +@always_bls +def test_process_full_attestation_shard_block(spec, state): + beacon_state, shard_state = configure_shard_state(spec, state) + + block = build_empty_shard_block( + spec, + beacon_state, + shard_state, + slot=shard_state.slot + 1, + signed=True, + full_attestation=True, + ) + + yield 'pre', shard_state + yield 'beacon_state', beacon_state + + shard_state_transition_and_sign_block(spec, beacon_state, shard_state, block) + + yield 'blocks', [block] + yield 'post', shard_state + + +@with_all_phases_except(['phase0']) +@spec_state_test +def test_prev_slot_block_transition(spec, state): + beacon_state, shard_state = configure_shard_state(spec, state) + + # Go to clean slot + spec.process_shard_slots(shard_state, shard_state.slot + 1) + # Make a block for it + block = build_empty_shard_block(spec, beacon_state, shard_state, slot=shard_state.slot, signed=True) + # Transition to next slot, above block will not be invalid on top of new state. + spec.process_shard_slots(shard_state, shard_state.slot + 1) + + yield 'pre', shard_state + yield 'beacon_state', beacon_state + expect_assertion_error( + lambda: spec.shard_state_transition(beacon_state, shard_state, block) + ) + yield 'blocks', [block] + yield 'post', None + + +@with_all_phases_except(['phase0']) +@spec_state_test +def test_same_slot_block_transition(spec, state): + beacon_state, shard_state = configure_shard_state(spec, state) + + # Same slot on top of pre-state, but move out of slot 0 first. + spec.process_shard_slots(shard_state, shard_state.slot + 1) + block = build_empty_shard_block(spec, beacon_state, shard_state, slot=shard_state.slot, signed=True) + + yield 'pre', shard_state + yield 'beacon_state', beacon_state + + shard_state_transition_and_sign_block(spec, beacon_state, shard_state, block) + + yield 'blocks', [block] + yield 'post', shard_state + + +@with_all_phases_except(['phase0']) +@spec_state_test +def test_invalid_state_root(spec, state): + beacon_state, shard_state = configure_shard_state(spec, state) + + spec.process_shard_slots(shard_state, shard_state.slot + 1) + block = build_empty_shard_block(spec, beacon_state, shard_state, slot=shard_state.slot) + block.state_root = b'\x36' * 32 + sign_shard_block(spec, beacon_state, shard_state, block) + + yield 'pre', shard_state + yield 'beacon_state', beacon_state + expect_assertion_error( + lambda: spec.shard_state_transition(beacon_state, shard_state, block, validate_state_root=True) + ) + yield 'blocks', [block] + yield 'post', None + + +@with_all_phases_except(['phase0']) +@spec_state_test +def test_skipped_slots(spec, state): + beacon_state, shard_state = configure_shard_state(spec, state) + + block = build_empty_shard_block(spec, beacon_state, shard_state, slot=shard_state.slot + 3, signed=True) + + yield 'pre', shard_state + yield 'beacon_state', beacon_state + + shard_state_transition_and_sign_block(spec, beacon_state, shard_state, block) + + yield 'blocks', [block] + yield 'post', shard_state + + assert shard_state.slot == block.slot + latest_block_header = deepcopy(shard_state.latest_block_header) + latest_block_header.state_root = shard_state.hash_tree_root() + assert latest_block_header.signing_root() == block.signing_root() + + +@with_all_phases_except(['phase0']) +@spec_state_test +def test_empty_shard_period_transition(spec, state): + beacon_state, shard_state = configure_shard_state(spec, state) + + # modify some of the deltas to ensure the period transition works properly + stub_delta = 10 + shard_state.newer_committee_positive_deltas[0] = stub_delta + shard_state.newer_committee_negative_deltas[0] = stub_delta + + slot = shard_state.slot + spec.SHARD_SLOTS_PER_EPOCH * spec.EPOCHS_PER_SHARD_PERIOD + block = build_empty_shard_block(spec, beacon_state, shard_state, slot=slot, signed=True) + + yield 'pre', shard_state + yield 'beacon_state', beacon_state + + shard_state_transition_and_sign_block(spec, beacon_state, shard_state, block) + + yield 'blocks', [block] + yield 'post', shard_state + + shard_state.older_committee_positive_deltas[0] == stub_delta + shard_state.older_committee_negative_deltas[0] == stub_delta + shard_state.newer_committee_positive_deltas[0] == 0 + shard_state.newer_committee_negative_deltas[0] == 0 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 deleted file mode 100644 index b0d8ad5e6..000000000 --- a/test_libs/pyspec/eth2spec/test/phase_1/shard_data_chain/test_shard_block.py +++ /dev/null @@ -1,62 +0,0 @@ -from eth2spec.test.helpers.phase1.shard_block import ( - build_empty_shard_block, -) -from eth2spec.test.context import ( - with_all_phases_except, - spec_state_test, - always_bls, -) - - -@with_all_phases_except(['phase0']) -@spec_state_test -@always_bls -def test_process_empty_shard_block(spec, state): - beacon_state = state - beacon_state.slot = spec.Slot(spec.SHARD_GENESIS_EPOCH * spec.SLOTS_PER_EPOCH) - shard_state = spec.get_genesis_shard_state(spec.Shard(0)) - shard_state.slot = spec.ShardSlot(spec.SHARD_GENESIS_EPOCH * spec.SHARD_SLOTS_PER_EPOCH) - - block = build_empty_shard_block( - spec, - beacon_state, - shard_state, - slot=shard_state.slot + 1, - signed=True, - full_attestation=False, - ) - - yield 'pre', shard_state - yield 'beacon_state', beacon_state - yield 'block', block - - spec.shard_state_transition(beacon_state, shard_state, block) - - yield 'post', shard_state - - -@with_all_phases_except(['phase0']) -@spec_state_test -@always_bls -def test_process_full_attestation_shard_block(spec, state): - beacon_state = state - beacon_state.slot = spec.Slot(spec.SHARD_GENESIS_EPOCH * spec.SLOTS_PER_EPOCH) - shard_state = spec.get_genesis_shard_state(spec.Shard(0)) - shard_state.slot = spec.SHARD_GENESIS_EPOCH * spec.SHARD_SLOTS_PER_EPOCH - - block = build_empty_shard_block( - spec, - beacon_state, - shard_state, - slot=shard_state.slot + 1, - signed=True, - full_attestation=True, - ) - - yield 'pre', shard_state - yield 'beacon_state', beacon_state - yield 'block', block - - spec.shard_state_transition(beacon_state, shard_state, block) - - yield 'post', shard_state From a5fb9408aee6f6bdfc9b9ff57c3989d8b25dd080 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 30 Sep 2019 13:11:23 +0900 Subject: [PATCH 5/8] make beacon state for shard sate transition from current shard epoch --- 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 3fe6f6a65..2045c7a7e 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -338,12 +338,12 @@ def process_shard_block_header(beacon_state: BeaconState, shard_state: ShardStat # Verify the slot number assert block.slot == shard_state.slot # Verify the beacon chain root - parent_epoch = compute_epoch_of_shard_slot(shard_state.latest_block_header.slot) - # --super dirty. need to think-- # - if parent_epoch * SLOTS_PER_EPOCH == beacon_state.slot: + epoch = compute_epoch_of_shard_slot(shard_state.slot) + assert epoch == compute_epoch_of_slot(beacon_state.slot) + if epoch * SLOTS_PER_EPOCH == beacon_state.slot: beacon_block_root = signing_root(beacon_state.latest_block_header) else: - beacon_block_root = get_block_root(beacon_state, parent_epoch) + beacon_block_root = get_block_root(beacon_state, epoch) assert block.beacon_block_root == beacon_block_root # Verify the parent root assert block.parent_root == signing_root(shard_state.latest_block_header) From 2af39ad4694a1ad7eb91856e3820d2d50c21953f Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 30 Sep 2019 13:58:48 +0900 Subject: [PATCH 6/8] fix shard period sanity test --- .../pyspec/eth2spec/test/helpers/phase1/shard_block.py | 6 +++--- .../eth2spec/test/phase_1/sanity/test_shard_blocks.py | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) 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 ea5783655..a339a0e70 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py +++ b/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py @@ -41,11 +41,11 @@ def build_empty_shard_block(spec, if slot is None: slot = shard_state.slot - parent_epoch = spec.compute_epoch_of_shard_slot(shard_state.latest_block_header.slot) - if parent_epoch * spec.SLOTS_PER_EPOCH == beacon_state.slot: + epoch = spec.compute_epoch_of_shard_slot(slot) + if epoch * spec.SLOTS_PER_EPOCH == beacon_state.slot: beacon_block_root = spec.signing_root(beacon_state.latest_block_header) else: - beacon_block_root = spec.get_block_root(beacon_state, parent_epoch) + beacon_block_root = spec.get_block_root(beacon_state, epoch) previous_block_header = deepcopy(shard_state.latest_block_header) if previous_block_header.state_root == spec.Hash(): diff --git a/test_libs/pyspec/eth2spec/test/phase_1/sanity/test_shard_blocks.py b/test_libs/pyspec/eth2spec/test/phase_1/sanity/test_shard_blocks.py index 2669ffb53..51575f2d5 100644 --- a/test_libs/pyspec/eth2spec/test/phase_1/sanity/test_shard_blocks.py +++ b/test_libs/pyspec/eth2spec/test/phase_1/sanity/test_shard_blocks.py @@ -154,6 +154,13 @@ def test_empty_shard_period_transition(spec, state): shard_state.newer_committee_negative_deltas[0] = stub_delta slot = shard_state.slot + spec.SHARD_SLOTS_PER_EPOCH * spec.EPOCHS_PER_SHARD_PERIOD + beacon_state.slot = spec.compute_epoch_of_shard_slot(slot) * spec.SLOTS_PER_EPOCH - 4 + spec.process_slots(beacon_state, spec.compute_epoch_of_shard_slot(slot) * spec.SLOTS_PER_EPOCH) + + # all validators get slashed for not revealing keys + # undo this to allow for a block proposal + for index in range(len(beacon_state.validators)): + beacon_state.validators[index].slashed = False block = build_empty_shard_block(spec, beacon_state, shard_state, slot=slot, signed=True) yield 'pre', shard_state From dc4869349c4d0a14a07476863959add418c22ab2 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sat, 5 Oct 2019 06:56:54 +0900 Subject: [PATCH 7/8] remove explicitly handling empty attestation --- specs/core/1_shard-data-chains.md | 5 ----- .../eth2spec/test/helpers/phase1/shard_block.py | 17 ++++++++++------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index 2045c7a7e..a31f16880 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -384,11 +384,6 @@ def process_shard_attestations(beacon_state: BeaconState, shard_state: ShardStat pubkeys.append(beacon_state.validators[validator_index].pubkey) process_delta(beacon_state, shard_state, validator_index, get_base_reward(beacon_state, validator_index)) attestation_count += 1 - # Exit early if no participants - if not any(pubkeys): - assert block.attestations == BLSSignature() - return - # Verify there are no extraneous bits set beyond the shard committee for i in range(len(shard_committee), 2 * MAX_PERIOD_COMMITTEE_SIZE): assert block.aggregation_bits[i] == 0b0 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 a339a0e70..834adc93f 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py +++ b/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py @@ -66,13 +66,16 @@ def build_empty_shard_block(spec, (True,) * len(shard_committee) + (False,) * (spec.MAX_PERIOD_COMMITTEE_SIZE * 2 - len(shard_committee)) ) - block.attestations = sign_shard_attestation( - spec, - beacon_state, - shard_state, - block, - participants=shard_committee, - ) + else: + shard_committee = [] + + block.attestations = sign_shard_attestation( + spec, + beacon_state, + shard_state, + block, + participants=shard_committee, + ) if signed: sign_shard_block(spec, beacon_state, shard_state, block) From 3c3ae9ac02ed896b59b03c95f60778c6f4f22f8b Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sat, 5 Oct 2019 07:22:20 +0900 Subject: [PATCH 8/8] enforce beacon state at epoch boundary slot --- specs/core/1_shard-data-chains.md | 16 ++++++++++------ .../eth2spec/test/helpers/phase1/shard_block.py | 11 +++++------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index a31f16880..69962b6fe 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -339,12 +339,16 @@ def process_shard_block_header(beacon_state: BeaconState, shard_state: ShardStat assert block.slot == shard_state.slot # Verify the beacon chain root epoch = compute_epoch_of_shard_slot(shard_state.slot) - assert epoch == compute_epoch_of_slot(beacon_state.slot) - if epoch * SLOTS_PER_EPOCH == beacon_state.slot: - beacon_block_root = signing_root(beacon_state.latest_block_header) - else: - beacon_block_root = get_block_root(beacon_state, epoch) - assert block.beacon_block_root == beacon_block_root + assert epoch * SLOTS_PER_EPOCH == beacon_state.slot + beacon_block_header = BeaconBlockHeader( + slot=beacon_state.latest_block_header.slot, + parent_root=beacon_state.latest_block_header.parent_root, + state_root=beacon_state.latest_block_header.state_root, + body_root=beacon_state.latest_block_header.body_root, + ) + if beacon_block_header.state_root == Bytes32(): + beacon_block_header.state_root = hash_tree_root(beacon_state) + assert block.beacon_block_root == signing_root(beacon_block_header) # Verify the parent root assert block.parent_root == signing_root(shard_state.latest_block_header) # Save current block as the new latest block 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 834adc93f..7955c613e 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py +++ b/test_libs/pyspec/eth2spec/test/helpers/phase1/shard_block.py @@ -41,14 +41,13 @@ def build_empty_shard_block(spec, if slot is None: slot = shard_state.slot - epoch = spec.compute_epoch_of_shard_slot(slot) - if epoch * spec.SLOTS_PER_EPOCH == beacon_state.slot: - beacon_block_root = spec.signing_root(beacon_state.latest_block_header) - else: - beacon_block_root = spec.get_block_root(beacon_state, epoch) + previous_beacon_header = deepcopy(beacon_state.latest_block_header) + if previous_beacon_header.state_root == spec.Bytes32(): + previous_beacon_header.state_root = beacon_state.hash_tree_root() + beacon_block_root = spec.signing_root(previous_beacon_header) previous_block_header = deepcopy(shard_state.latest_block_header) - if previous_block_header.state_root == spec.Hash(): + if previous_block_header.state_root == spec.Bytes32(): previous_block_header.state_root = shard_state.hash_tree_root() parent_root = signing_root(previous_block_header)