[squashed] shard transition wip

Fix the wrong `get_shard_proposer_index` parameters order

Phase 1 WIP

Add shard transition basic test

Fix lint error

Fix
This commit is contained in:
Hsiao-Wei Wang 2020-04-06 19:30:32 +08:00
parent 849d3f83bf
commit 4e8a7ff115
No known key found for this signature in database
GPG Key ID: 95B070122902DEA4
10 changed files with 374 additions and 237 deletions

View File

@ -460,16 +460,17 @@ def get_online_validator_indices(state: BeaconState) -> Set[ValidatorIndex]:
```python
def get_shard_committee(beacon_state: BeaconState, epoch: Epoch, shard: Shard) -> Sequence[ValidatorIndex]:
source_epoch = epoch - epoch % SHARD_COMMITTEE_PERIOD
source_epoch = epoch - epoch % SHARD_COMMITTEE_PERIOD
if source_epoch > 0:
source_epoch -= SHARD_COMMITTEE_PERIOD
active_validator_indices = get_active_validator_indices(beacon_state, source_epoch)
seed = get_seed(beacon_state, source_epoch, DOMAIN_SHARD_COMMITTEE)
active_shards_count = get_active_shard_count(beacon_state)
return compute_committee(
indices=active_validator_indices,
seed=seed,
index=shard,
count=get_active_shard_count(beacon_state)
count=active_shards_count,
)
```
@ -712,29 +713,35 @@ def apply_shard_transition(state: BeaconState, shard: Shard, transition: ShardTr
)
assert transition.start_slot == offset_slots[0]
# Reconstruct shard headers
headers = []
header = ShardBlockHeader()
proposers = []
shard_parent_root = state.shard_states[shard].latest_block_root
for i in range(len(offset_slots)):
if any(transition.shard_data_roots):
headers.append(ShardBlockHeader(
shard_parent_root=shard_parent_root,
beacon_parent_root=get_block_root_at_slot(state, get_previous_slot(state.slot)),
slot=offset_slots[i],
body_root=transition.shard_data_roots[i]
))
proposers.append(get_shard_proposer_index(state, shard, offset_slots[i]))
shard_parent_root = hash_tree_root(headers[-1])
# Verify correct calculation of gas prices and slots
prev_gasprice = state.shard_states[shard].gasprice
shard_parent_root = state.shard_states[shard].latest_block_root
beacon_parent_root = get_block_root_at_slot(state, get_previous_slot(state.slot))
for i in range(len(offset_slots)):
shard_block_length = transition.shard_block_lengths[i]
is_empty_proposal = (shard_block_length == 0)
shard_state = transition.shard_states[i]
block_length = transition.shard_block_lengths[i]
assert shard_state.gasprice == get_updated_gasprice(prev_gasprice, block_length)
assert shard_state.slot == offset_slots[i]
prev_gasprice = shard_state.gasprice
proposal_index = get_shard_proposer_index(state, offset_slots[i], shard)
# Reconstruct shard headers
header = ShardBlockHeader(
shard_parent_root=shard_parent_root,
beacon_parent_root=beacon_parent_root,
proposer_index=proposal_index,
slot=offset_slots[i],
body_root=transition.shard_data_roots[i]
)
shard_parent_root = hash_tree_root(header)
if not is_empty_proposal:
# Only add non-empty signature
headers.append(header)
proposers.append(proposal_index)
# Verify correct calculation of gas prices and slots
assert shard_state.gasprice == get_updated_gasprice(prev_gasprice, shard_block_length)
assert shard_state.slot == offset_slots[i]
prev_gasprice = shard_state.gasprice
pubkeys = [state.validators[proposer].pubkey for proposer in proposers]
signing_roots = [
@ -745,7 +752,8 @@ def apply_shard_transition(state: BeaconState, shard: Shard, transition: ShardTr
assert bls.AggregateVerify(zip(pubkeys, signing_roots), signature=transition.proposer_signature_aggregate)
# Save updated state
state.shard_states[shard] = transition.shard_states[-1]
state.shard_states[shard] = transition.shard_states[len(transition.shard_states) - 1]
assert state.slot > 0
state.shard_states[shard].slot = state.slot - 1
```
@ -779,7 +787,9 @@ def process_crosslink_for_shard(state: BeaconState,
# Attestation <-> shard transition consistency
assert shard_transition_root == hash_tree_root(shard_transition)
assert attestation.data.head_shard_root == shard_transition.shard_data_roots[-1]
assert attestation.data.head_shard_root == shard_transition.shard_data_roots[
len(shard_transition.shard_data_roots) - 1
]
# Apply transition
apply_shard_transition(state, shard, shard_transition)
@ -790,11 +800,11 @@ def process_crosslink_for_shard(state: BeaconState,
increase_balance(state, beacon_proposer_index, proposer_reward)
states_slots_lengths = zip(
shard_transition.shard_states,
get_offset_slots(state, get_latest_slot_for_shard(state, shard)),
get_offset_slots(state, shard),
shard_transition.shard_block_lengths
)
for shard_state, slot, length in states_slots_lengths:
proposer_index = get_shard_proposer_index(state, shard, slot)
proposer_index = get_shard_proposer_index(state, slot, shard)
decrease_balance(state, proposer_index, shard_state.gasprice * length)
# Return winning transition root
@ -913,14 +923,13 @@ def process_light_client_signatures(state: BeaconState, block_body: BeaconBlockB
total_reward += get_base_reward(state, participant_index)
increase_balance(state, get_beacon_proposer_index(state), Gwei(total_reward // PROPOSER_REWARD_QUOTIENT))
slot = get_previous_slot(state.slot)
signing_root = compute_signing_root(get_block_root_at_slot(state, slot),
signing_root = compute_signing_root(get_block_root_at_slot(state, slot),
get_domain(state, DOMAIN_LIGHT_CLIENT, compute_epoch_at_slot(slot)))
assert bls.FastAggregateVerify(signer_pubkeys, signing_root, signature=block_body.light_client_signature)
```
### Epoch transition
This epoch transition overrides the phase0 epoch transition:

View File

@ -9,6 +9,8 @@
- [Shard state transition function](#shard-state-transition-function)
- [Verifying the proof](#verifying-the-proof)
- [Honest committee member behavior](#honest-committee-member-behavior)
- [Helper functions](#helper-functions)
- [Make attestations](#make-attestations)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@ -30,76 +32,76 @@ This document describes the shard transition function and fraud proofs as part o
## Fraud proofs
TODO. The intent is to have a single universal fraud proof type, which contains the following parts:
1. An on-time attestation on some `shard` signing a `ShardTransition`
2. An index `index` of a particular position to focus on
3. The `ShardTransition` itself
4. The full body of the block `ShardBlock`
5. A Merkle proof to the `shard_states` in the parent block `parent_block` the attestation is referencing
### Shard state transition function
```python
def shard_state_transition(beacon_state: BeaconState,
shard: Shard,
slot: Slot,
pre_state: Root,
previous_beacon_root: Root,
proposer_index: ValidatorIndex,
signed_block: SignedShardBlock,
validate_result: bool=True) -> Root:
# We will add something more substantive in phase 2
# Verify the proposer_index and signature
assert proposer_index == signed_block.message.proposer_index
if validate_result:
assert verify_shard_block_signature(beacon_state, signed_block)
return hash(pre_state + hash_tree_root(previous_beacon_root) + hash_tree_root(signed_block.message.data))
shard_state: ShardState,
beacon_parent_root: Root,
signed_block: SignedShardBlock) -> None:
# Update shard state
shard_state.data = hash(
hash_tree_root(shard_state) + hash_tree_root(beacon_parent_root) + hash_tree_root(signed_block.message.body)
)
shard_state.slot = slot
shard_state.latest_block_root = hash_tree_root(signed_block.message)
```
```python
def verify_shard_block_signature(beacon_state: BeaconState,
signed_block: SignedShardBlock) -> bool:
proposer = beacon_state.validators[signed_block.message.proposer_index]
signing_root = compute_signing_root(signed_block.message, get_domain(beacon_state, DOMAIN_SHARD_PROPOSAL))
domain = get_domain(beacon_state, DOMAIN_SHARD_PROPOSAL, compute_epoch_at_slot(signed_block.message.slot))
signing_root = compute_signing_root(signed_block.message, domain)
return bls.Verify(proposer.pubkey, signing_root, signed_block.signature)
```
### Verifying the proof
TODO. The intent is to have a single universal fraud proof type, which contains the following parts:
1. An on-time attestation `attestation` on some shard `shard` signing a `transition: ShardTransition`
2. An index `offset_index` of a particular position to focus on
3. The `transition: ShardTransition` itself
4. The full body of the shard block `shard_block`
5. A Merkle proof to the `shard_states` in the parent block the attestation is referencing
Call the following function to verify the proof:
```python
def verify_fraud_proof(beacon_state: BeaconState,
subkey: BLSPubkey,
attestation: Attestation,
index: uint64,
offset_index: uint64,
transition: ShardTransition,
signed_block: SignedShardBlock,
parent_block: ShardBlock) -> bool:
# 1. Check if `custody_bits[index][j] != generate_custody_bit(subkey, block_contents)` for any `j`
subkey: BLSPubkey,
beacon_parent_block: BeaconBlock) -> bool:
# 1. Check if `custody_bits[offset_index][j] != generate_custody_bit(subkey, block_contents)` for any `j`.
shard = get_shard(beacon_state, attestation)
slot = attestation.data.slot
custody_bits = attestation.custody_bits_blocks
for j in range(custody_bits[index]):
if custody_bits[index][j] != generate_custody_bit(subkey, signed_block):
for j in range(custody_bits[offset_index]):
if custody_bits[offset_index][j] != generate_custody_bit(subkey, signed_block):
return True
# 2. Verify the shard state transition
if index == 0:
parent_data = parent_block.shard_states[shard][-1].data
# 2. Check if the shard state transition result is wrong between
# `transition.shard_states[offset_index - 1]` to `transition.shard_states[offset_index]`.
if offset_index == 0:
shard_state = beacon_parent_block.shard_transitions[shard][-1]
else:
parent_data = parent_block.shard_states[shard][index].data
shard_state = transition.shard_states[offset_index - 1].copy() # Not doing the actual state updates here.
if shard_state_transition(
beacon_state,
shard,
slot,
transition.shard_states[index - 1].data,
hash_tree_root(parent_block),
get_shard_proposer_index(beacon_state, slot, shard),
signed_block,
) != parent_data:
shard_state_transition(
beacon_state=beacon_state,
shard=shard,
slot=slot,
shard_state=shard_state,
beacon_parent_root=hash_tree_root(beacon_parent_block),
signed_block=signed_block,
)
if shard_state.latest_block_root != transition.shard_states[offset_index].data:
return True
return False
@ -113,14 +115,141 @@ def generate_custody_bit(subkey: BLSPubkey, block: ShardBlock) -> bool:
## Honest committee member behavior
Suppose you are a committee member on shard `shard` at slot `current_slot`. Let `state` be the head beacon state you are building on, and let `QUARTER_PERIOD = SECONDS_PER_SLOT // 4`. `2 * QUARTER_PERIOD` seconds into slot `slot`, run the following procedure:
### Helper functions
* Initialize `proposals = []`, `shard_states = []`, `shard_state = state.shard_states[shard][-1]`, `start_slot = shard_state.slot`.
* For `slot in get_offset_slots(state, start_slot)`, do the following:
* Look for all valid proposals for `slot`; that is, a SignedShardBlock `proposal` where `shard_state_transition(shard, slot, shard_state, get_block_root_at_slot(state, state.slot - 1), get_shard_proposer_index(state, slot, shard), proposal)` returns a result and does not throw an exception. Let `choices` be the set of non-empty valid proposals you discover.
* If `len(choices) == 0`, do `proposals.append(make_empty_proposal(shard_state, slot))`
* If `len(choices) == 1`, do `proposals.append(choices[0])`
* If `len(choices) > 1`, let `winning_proposal` be the proposal with the largest number of total attestations from slots in `state.shard_next_slots[shard]....slot-1` supporting it or any of its descendants, breaking ties by choosing the first proposal locally seen. Do `proposals.append(winning_proposal)`.
* If `proposals.message.data[-1]` is NOT an empty proposal, set `shard_state = shard_state_transition(shard, slot, shard_state, get_block_root_at_slot(state, state.slot - 1), get_shard_proposer_index(state, slot, shard), proposals[-1])` and do `shard_states.append(shard_state)`. If it is an empty proposal, leave `shard_state` unchanged.
```python
def get_winning_proposal(beacon_state: BeaconState, proposals: Sequence[SignedShardBlock]) -> SignedShardBlock:
# TODO: Let `winning_proposal` be the proposal with the largest number of total attestationsfrom slots in
# `state.shard_next_slots[shard]....slot-1` supporting it or any of its descendants, breaking ties by choosing
# the first proposal locally seen. Do `proposals.append(winning_proposal)`.
return proposals[-1] # stub
```
Make an attestation using `shard_data_roots = [hash_tree_root(proposal.message.data) for proposal in proposals]` and `shard_state_roots = shard_states`.
```python
def get_empty_body_block(shard_parent_root: Root,
beacon_parent_root: Root,
slot: Slot,
proposer_index: ValidatorIndex) -> ShardBlock:
return ShardBlock(
shard_parent_root=shard_parent_root,
beacon_parent_root=beacon_parent_root,
slot=slot,
proposer_index=proposer_index,
)
```
```python
def is_empty_body(proposal: ShardBlock) -> bool:
# TODO
return len(proposal.body) == 0
```
```python
def compute_shard_data_roots(proposals: Sequence[SignedShardBlock]) -> Sequence[Root]:
return [hash_tree_root(proposal.message.body) for proposal in proposals]
```
### Make attestations
Suppose you are a committee member on shard `shard` at slot `current_slot` and you have received shard blocks `shard_blocks`. Let `state` be the head beacon state you are building on, and let `QUARTER_PERIOD = SECONDS_PER_SLOT // 4`. `2 * QUARTER_PERIOD` seconds into slot `current_slot`, run `get_shard_transition(beacon_state, shard, shard_blocks)` to get `shard_transition`.
```python
def get_shard_transition(beacon_state: BeaconState,
shard: Shard,
shard_blocks: Sequence[SignedShardBlock]) -> ShardTransition:
offset_slots = get_offset_slots(beacon_state, shard)
start_slot = offset_slots[0]
proposals, shard_states, shard_data_roots = get_shard_state_transition_result(beacon_state, shard, shard_blocks)
assert len(proposals) > 0
assert len(shard_data_roots) > 0
shard_block_lengths = []
proposer_signatures = []
for proposal in proposals:
shard_block_lengths.append(len(proposal.message.body))
if proposal.signature != BLSSignature():
proposer_signatures.append(proposal.signature)
proposer_signature_aggregate = bls.Aggregate(proposer_signatures)
return ShardTransition(
start_slot=start_slot,
shard_block_lengths=shard_block_lengths,
shard_data_roots=shard_data_roots,
shard_states=shard_states,
proposer_signature_aggregate=proposer_signature_aggregate,
)
```
```python
def get_shard_state_transition_result(
beacon_state: BeaconState,
shard: Shard,
shard_blocks: Sequence[SignedShardBlock],
validate_result: bool=True,
) -> Tuple[Sequence[SignedShardBlock], Sequence[ShardState], Sequence[Root]]:
proposals = []
shard_states = []
shard_state = beacon_state.shard_states[shard].copy()
shard_parent_root = beacon_state.shard_states[shard].latest_block_root
for slot in get_offset_slots(beacon_state, shard):
choices = []
beacon_parent_root = get_block_root_at_slot(beacon_state, get_previous_slot(beacon_state.slot))
proposer_index = get_shard_proposer_index(beacon_state, slot, shard)
shard_blocks_at_slot = [block for block in shard_blocks if block.message.slot == slot]
for block in shard_blocks_at_slot:
temp_shard_state = shard_state.copy() # Not doing the actual state updates here.
# Try to apply state transition to temp_shard_state.
try:
# Verify the proposer_index and signature
assert block.message.proposer_index == proposer_index
if validate_result:
assert verify_shard_block_signature(beacon_state, block)
shard_state_transition(
beacon_state=beacon_state,
shard=shard,
slot=slot,
shard_state=temp_shard_state,
beacon_parent_root=beacon_parent_root,
signed_block=block,
)
except Exception:
pass # TODO: throw error in the test helper
else:
choices.append(block)
if len(choices) == 0:
block_header = get_empty_body_block(
shard_parent_root=shard_parent_root,
beacon_parent_root=beacon_parent_root,
slot=slot,
proposer_index=proposer_index,
)
block = SignedShardBlock(message=block_header)
proposals.append(block)
elif len(choices) == 1:
proposals.append(choices[0])
else:
proposals.append(get_winning_proposal(beacon_state, choices))
shard_parent_root = hash_tree_root(proposals[-1].message)
if not is_empty_body(proposals[-1].message):
# Apply state transition to shard_state.
shard_state_transition(
beacon_state=beacon_state,
shard=shard,
slot=slot,
shard_state=shard_state,
beacon_parent_root=beacon_parent_root,
signed_block=block,
)
shard_states.append(shard_state)
shard_data_roots = compute_shard_data_roots(proposals)
return proposals, shard_states, shard_data_roots
```

View File

@ -1,6 +1,6 @@
from typing import List
from eth2spec.test.context import expect_assertion_error, PHASE0
from eth2spec.test.context import expect_assertion_error, PHASE0, PHASE1
from eth2spec.test.helpers.state import state_transition_and_sign_block
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
from eth2spec.test.helpers.keys import privkeys
@ -43,7 +43,7 @@ def run_attestation_processing(spec, state, attestation, valid=True):
yield 'post', state
def build_attestation_data(spec, state, slot, index):
def build_attestation_data(spec, state, slot, index, shard_transition=None):
assert state.slot >= slot
if slot == state.slot:
@ -66,12 +66,21 @@ def build_attestation_data(spec, state, slot, index):
source_epoch = state.current_justified_checkpoint.epoch
source_root = state.current_justified_checkpoint.root
if spec.fork == PHASE1 and shard_transition is not None:
shard_transition_root = shard_transition.hash_tree_root()
head_shard_root = shard_transition.shard_data_roots[len(shard_transition.shard_data_roots) - 1]
else:
shard_transition_root = spec.Root()
head_shard_root = spec.Root()
return spec.AttestationData(
slot=slot,
index=index,
beacon_block_root=block_root,
source=spec.Checkpoint(epoch=source_epoch, root=source_root),
target=spec.Checkpoint(epoch=spec.compute_epoch_at_slot(slot), root=epoch_boundary_root),
head_shard_root=head_shard_root,
shard_transition_root=shard_transition_root,
)
@ -89,7 +98,7 @@ def convert_to_valid_on_time_attestation(spec, state, attestation, signed=False)
return attestation
def get_valid_on_time_attestation(spec, state, slot=None, index=None, signed=False):
def get_valid_on_time_attestation(spec, state, slot=None, index=None, shard_transition=None, signed=False):
'''
Construct on-time attestation for next slot
'''
@ -98,7 +107,15 @@ def get_valid_on_time_attestation(spec, state, slot=None, index=None, signed=Fal
if index is None:
index = 0
return get_valid_attestation(spec, state, slot=slot, index=index, signed=signed, on_time=True)
return get_valid_attestation(
spec,
state,
slot=slot,
index=index,
shard_transition=shard_transition,
signed=signed,
on_time=True,
)
def get_valid_late_attestation(spec, state, slot=None, index=None, signed=False):
@ -113,13 +130,20 @@ def get_valid_late_attestation(spec, state, slot=None, index=None, signed=False)
return get_valid_attestation(spec, state, slot=slot, index=index, signed=signed, on_time=False)
def get_valid_attestation(spec, state, slot=None, index=None, empty=False, signed=False, on_time=True):
def get_valid_attestation(spec,
state,
slot=None,
index=None,
shard_transition=None,
empty=False,
signed=False,
on_time=True):
if slot is None:
slot = state.slot
if index is None:
index = 0
attestation_data = build_attestation_data(spec, state, slot, index)
attestation_data = build_attestation_data(spec, state, slot=slot, index=index, shard_transition=shard_transition)
beacon_committee = spec.get_beacon_committee(
state,
@ -138,7 +162,7 @@ def get_valid_attestation(spec, state, slot=None, index=None, empty=False, signe
if signed:
sign_attestation(spec, state, attestation)
if spec.fork == 'phase1' and on_time:
if spec.fork == PHASE1 and on_time:
attestation = convert_to_valid_on_time_attestation(spec, state, attestation, signed)
return attestation
@ -210,7 +234,7 @@ def get_attestation_custody_signature(spec, state, attestation_data, block_index
def sign_attestation(spec, state, attestation):
if spec.fork == 'phase1' and any(attestation.custody_bits_blocks):
if spec.fork == PHASE1 and any(attestation.custody_bits_blocks):
sign_on_time_attestation(spec, state, attestation)
return

View File

@ -0,0 +1,28 @@
from eth2spec.test.context import expect_assertion_error
def run_crosslinks_processing(spec, state, shard_transitions, attestations, valid=True):
"""
Run ``process_attestation``, yielding:
- pre-state ('pre')
- shard_transitions ('shard_transitions')
- attestations ('attestations')
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
# yield pre-state
yield 'pre', state
yield 'shard_transitions', shard_transitions
yield 'attestations', attestations
# If the attestation is invalid, processing is aborted, and there is no post-state.
if not valid:
expect_assertion_error(lambda: spec.process_crosslinks(state, shard_transitions, attestations))
yield 'post', None
return
# process crosslinks
spec.process_crosslinks(state, shard_transitions, attestations)
# yield post-state
yield 'post', state

View File

@ -1,63 +0,0 @@
from eth2spec.utils.ssz.ssz_typing import Bitlist
from eth2spec.utils import bls
from eth2spec.test.helpers.keys import privkeys
import eth2spec.test.helpers.attestations as phase0_attestations
def get_valid_on_time_attestation(spec, state, index=None, signed=False):
'''
Construct on-time attestation for next slot
'''
if index is None:
index = 0
attestation = phase0_attestations.get_valid_attestation(spec, state, state.slot, index, False)
shard = spec.get_shard(state, attestation)
offset_slots = spec.compute_offset_slots(spec.get_latest_slot_for_shard(state, shard), state.slot + 1)
for _ in offset_slots:
attestation.custody_bits_blocks.append(
Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE]([0 for _ in attestation.aggregation_bits])
)
if signed:
sign_attestation(spec, state, attestation)
return attestation
def sign_attestation(spec, state, attestation):
if not any(attestation.custody_bits_blocks):
phase0_attestations.sign_attestation(spec, state, attestation)
return
committee = spec.get_beacon_committee(state, attestation.data.slot, attestation.data.index)
signatures = []
for block_index, custody_bits in enumerate(attestation.custody_bits_blocks):
for participant, abit, cbit in zip(committee, attestation.aggregation_bits, custody_bits):
if not abit:
continue
signatures.append(get_attestation_custody_signature(
spec,
state,
attestation.data,
block_index,
cbit,
privkeys[participant]
))
attestation.signature = bls.Aggregate(signatures)
def get_attestation_custody_signature(spec, state, attestation_data, block_index, bit, privkey):
domain = spec.get_domain(state, spec.DOMAIN_BEACON_ATTESTER, attestation_data.target.epoch)
signing_root = spec.compute_signing_root(
spec.AttestationCustodyBitWrapper(
attestation_data.hash_tree_root(),
block_index,
bit,
),
domain,
)
return bls.Sign(privkey, signing_root)

View File

@ -1,71 +0,0 @@
from eth2spec.test.helpers.keys import privkeys
from eth2spec.utils import bls
from eth2spec.utils.bls import only_with_bls
from eth2spec.utils.ssz.ssz_impl import (
hash_tree_root,
)
from .attestations import (
sign_shard_attestation,
)
@only_with_bls()
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_state.shard, block.slot)
privkey = privkeys[proposer_index]
domain = spec.get_domain(beacon_state, spec.DOMAIN_SHARD_PROPOSER, spec.compute_epoch_of_shard_slot(block.slot))
signing_root = spec.compute_signing_root(block, domain)
block.signature = bls.Sign(privkey, signing_root)
def build_empty_shard_block(spec,
beacon_state,
shard_state,
slot,
signed=False,
full_attestation=False):
if slot is None:
slot = shard_state.slot
previous_beacon_header = beacon_state.latest_block_header.copy()
if previous_beacon_header.state_root == spec.Bytes32():
previous_beacon_header.state_root = beacon_state.hash_tree_root()
beacon_block_root = hash_tree_root(previous_beacon_header)
previous_block_header = shard_state.latest_block_header.copy()
if previous_block_header.state_root == spec.Bytes32():
previous_block_header.state_root = shard_state.hash_tree_root()
parent_root = hash_tree_root(previous_block_header)
block = spec.ShardBlock(
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,
)
if full_attestation:
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:
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)
return block

View File

@ -1,18 +0,0 @@
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)

View File

@ -0,0 +1,47 @@
from eth2spec.test.helpers.keys import privkeys
from eth2spec.utils import bls
from eth2spec.utils.bls import only_with_bls
@only_with_bls()
def sign_shard_block(spec, beacon_state, shard, block, proposer_index=None):
slot = block.message.slot
if proposer_index is None:
proposer_index = spec.get_shard_proposer_index(beacon_state, slot, shard)
privkey = privkeys[proposer_index]
domain = spec.get_domain(beacon_state, spec.DOMAIN_SHARD_PROPOSAL, spec.compute_epoch_at_slot(slot))
signing_root = spec.compute_signing_root(block.message, domain)
block.signature = bls.Sign(privkey, signing_root)
def build_empty_shard_block(spec,
beacon_state,
shard,
slot,
body=None,
signed=False):
shard_state = beacon_state.shard_states[shard]
if slot is None:
slot = shard_state.slot
if body is None:
body = []
proposer_index = spec.get_shard_proposer_index(beacon_state, slot, shard)
block = spec.ShardBlock(
shard_parent_root=shard_state.latest_block_root,
beacon_parent_root=spec.get_block_root_at_slot(beacon_state, spec.get_previous_slot(beacon_state.slot)),
slot=slot,
proposer_index=proposer_index,
body=body,
)
signed_block = spec.SignedShardBlock(
message=block,
)
if signed:
sign_shard_block(spec, beacon_state, shard, signed_block, proposer_index=proposer_index)
return signed_block

View File

@ -0,0 +1,52 @@
from eth2spec.test.context import (
with_all_phases_except,
spec_state_test,
always_bls,
)
from eth2spec.test.helpers.attestations import (
get_valid_on_time_attestation,
)
from eth2spec.test.helpers.crosslinks import (
run_crosslinks_processing,
)
from eth2spec.test.helpers.shard_block import build_empty_shard_block
from eth2spec.test.helpers.state import next_epoch, next_slot
@with_all_phases_except(['phase0'])
@spec_state_test
@always_bls
def test_basic_crosslinks(spec, state):
next_epoch(spec, state)
next_epoch(spec, state)
state = spec.upgrade_to_phase1(state)
next_slot(spec, state)
committee_index = spec.CommitteeIndex(0)
shard = spec.compute_shard_from_committee_index(state, committee_index, state.slot)
shard_block = build_empty_shard_block(spec, state, shard, body=b'\x12' * 10, slot=state.slot, signed=True)
shard_blocks = [shard_block]
next_slot(spec, state)
shard_transition = spec.get_shard_transition(state, shard, shard_blocks)
shard_transitions = [spec.ShardTransition()] * len(state.shard_states)
shard_transitions[shard] = shard_transition
attestation = get_valid_on_time_attestation(
spec,
state,
slot=state.slot,
index=committee_index,
shard_transition=shard_transition,
signed=True,
)
attestations = [attestation]
offset_slots = spec.get_offset_slots(state, shard)
yield from run_crosslinks_processing(spec, state, shard_transitions, attestations)
shard_state = state.shard_states[shard]
assert shard_state.slot == offset_slots[-1]
assert shard_state.latest_block_root == shard_block.message.hash_tree_root()