mirror of
https://github.com/status-im/eth2.0-specs.git
synced 2025-01-31 21:05:24 +00:00
[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:
parent
849d3f83bf
commit
4e8a7ff115
@ -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:
|
||||
|
@ -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
|
||||
```
|
||||
|
@ -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
|
||||
|
||||
|
28
tests/core/pyspec/eth2spec/test/helpers/crosslinks.py
Normal file
28
tests/core/pyspec/eth2spec/test/helpers/crosslinks.py
Normal 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
|
@ -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)
|
@ -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
|
@ -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)
|
47
tests/core/pyspec/eth2spec/test/helpers/shard_block.py
Normal file
47
tests/core/pyspec/eth2spec/test/helpers/shard_block.py
Normal 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
|
@ -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()
|
Loading…
x
Reference in New Issue
Block a user