Merge pull request #1418 from ethereum/shard-chain-test
Shard chain sanity test
This commit is contained in:
commit
e1c1cc4bf3
|
@ -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
|
||||
|
|
|
@ -247,6 +247,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,
|
||||
)
|
||||
```
|
||||
|
@ -333,10 +338,19 @@ 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)
|
||||
assert block.beacon_block_root == get_block_root(beacon_state, parent_epoch)
|
||||
epoch = compute_epoch_of_shard_slot(shard_state.slot)
|
||||
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 == 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,
|
||||
|
@ -379,7 +393,7 @@ def process_shard_attestations(beacon_state: BeaconState, shard_state: ShardStat
|
|||
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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from copy import deepcopy
|
||||
|
||||
from eth2spec.test.helpers.keys import privkeys
|
||||
from eth2spec.utils.bls import (
|
||||
bls_sign,
|
||||
|
@ -13,69 +15,68 @@ from .attestations import (
|
|||
|
||||
|
||||
@only_with_bls()
|
||||
def sign_shard_block(spec, 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_block_proposer_index(state, shard, block.core.slot)
|
||||
proposer_index = spec.get_shard_proposer_index(beacon_state, shard_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
|
||||
|
||||
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.Bytes32():
|
||||
previous_block_header.state_root = shard_state.hash_tree_root()
|
||||
parent_root = signing_root(previous_block_header)
|
||||
|
||||
block = spec.ShardBlock(
|
||||
core=spec.ExtendedShardBlockCore(
|
||||
shard=shard_state.shard,
|
||||
slot=slot,
|
||||
beacon_chain_root=beacon_state.block_roots[beacon_state.slot % spec.SLOTS_PER_HISTORICAL_ROOT],
|
||||
beacon_block_root=beacon_block_root,
|
||||
parent_root=parent_root,
|
||||
),
|
||||
signatures=spec.ShardBlockSignatures(
|
||||
attestation_signature=b'\x00' * 96,
|
||||
proposer_signature=b'\x25' * 96,
|
||||
)
|
||||
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))
|
||||
)
|
||||
block.signatures.attestation_signature = sign_shard_attestation(
|
||||
spec,
|
||||
shard_state,
|
||||
beacon_state,
|
||||
block,
|
||||
participants=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))
|
||||
)
|
||||
else:
|
||||
block.signatures.attestation_signature = sign_shard_attestation(
|
||||
shard_committee = []
|
||||
|
||||
block.attestations = sign_shard_attestation(
|
||||
spec,
|
||||
shard_state,
|
||||
beacon_state,
|
||||
shard_state,
|
||||
block,
|
||||
participants=(),
|
||||
participants=shard_committee,
|
||||
)
|
||||
|
||||
if signed:
|
||||
sign_shard_block(spec, beacon_state, block, shard_state.shard)
|
||||
sign_shard_block(spec, beacon_state, shard_state, block)
|
||||
|
||||
return block
|
||||
|
|
|
@ -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)
|
|
@ -0,0 +1,177 @@
|
|||
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
|
||||
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
|
||||
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
|
|
@ -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
|
|
@ -1,68 +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'])
|
||||
@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=False,
|
||||
)
|
||||
|
||||
yield 'pre', shard_state
|
||||
yield 'beacon_state', beacon_state
|
||||
yield 'block', block
|
||||
|
||||
spec.shard_state_transition(shard_state, beacon_state, block)
|
||||
|
||||
yield 'post', shard_state
|
||||
|
||||
|
||||
@with_all_phases_except(['phase0'])
|
||||
@always_bls
|
||||
@spec_state_test
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
spec.shard_state_transition(shard_state, beacon_state, block)
|
||||
|
||||
yield 'post', shard_state
|
Loading…
Reference in New Issue