Merge pull request #874 from ethereum/prev-cur-crosslinks

add previous and current crosslinks
This commit is contained in:
Danny Ryan 2019-04-18 11:44:53 -06:00 committed by GitHub
commit aaea74e4bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 258 additions and 142 deletions

View File

@ -76,8 +76,7 @@
- [`generate_seed`](#generate_seed)
- [`get_beacon_proposer_index`](#get_beacon_proposer_index)
- [`verify_merkle_branch`](#verify_merkle_branch)
- [`get_crosslink_committee_for_attestation`](#get_crosslink_committee_for_attestation)
- [`get_attestation_participants`](#get_attestation_participants)
- [`get_attesting_indices`](#get_attesting_indices)
- [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-)
- [`bytes_to_int`](#bytes_to_int)
- [`get_effective_balance`](#get_effective_balance)
@ -176,7 +175,7 @@ These configurations are updated for releases, but may be out of sync during `de
| - | - |
| `SHARD_COUNT` | `2**10` (= 1,024) |
| `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) |
| `MAX_ATTESTATION_PARTICIPANTS` | `2**12` (= 4,096) |
| `MAX_INDICES_PER_ATTESTATION` | `2**12` (= 4,096) |
| `MIN_PER_EPOCH_CHURN_LIMIT` | `2**2` (= 4) |
| `CHURN_LIMIT_QUOTIENT` | `2**16` (= 65,536) |
| `SHUFFLE_ROUND_COUNT` | 90 |
@ -299,6 +298,8 @@ The types are defined topologically to aid in facilitating an executable version
{
# Epoch number
'epoch': 'uint64',
# Root of the previous crosslink
'previous_crosslink_root': 'bytes32',
# Shard data since the previous crosslink
'crosslink_data_root': 'bytes32',
}
@ -332,7 +333,7 @@ The types are defined topologically to aid in facilitating an executable version
# Crosslink vote
'shard': 'uint64',
'previous_crosslink': Crosslink,
'previous_crosslink_root': 'bytes32',
'crosslink_data_root': 'bytes32',
}
```
@ -584,7 +585,8 @@ The types are defined topologically to aid in facilitating an executable version
'finalized_root': 'bytes32',
# Recent state
'latest_crosslinks': [Crosslink, SHARD_COUNT],
'current_crosslinks': [Crosslink, SHARD_COUNT],
'previous_crosslinks': [Crosslink, SHARD_COUNT],
'latest_block_roots': ['bytes32', SLOTS_PER_HISTORICAL_ROOT],
'latest_state_roots': ['bytes32', SLOTS_PER_HISTORICAL_ROOT],
'latest_active_index_roots': ['bytes32', LATEST_ACTIVE_INDEX_ROOTS_LENGTH],
@ -998,33 +1000,17 @@ def verify_merkle_branch(leaf: Bytes32, proof: List[Bytes32], depth: int, index:
return value == root
```
### `get_crosslink_committee_for_attestation`
### `get_attesting_indices`
```python
def get_crosslink_committee_for_attestation(state: BeaconState,
attestation_data: AttestationData) -> List[ValidatorIndex]:
def get_attesting_indices(state: BeaconState,
attestation_data: AttestationData,
bitfield: bytes) -> List[ValidatorIndex]:
"""
Return the crosslink committee corresponding to ``attestation_data``.
Return the sorted attesting indices corresponding to ``attestation_data`` and ``bitfield``.
"""
crosslink_committees = get_crosslink_committees_at_slot(state, attestation_data.slot)
# Find the committee in the list with the desired shard
assert attestation_data.shard in [shard for _, shard in crosslink_committees]
crosslink_committee = [committee for committee, shard in crosslink_committees if shard == attestation_data.shard][0]
return crosslink_committee
```
### `get_attestation_participants`
```python
def get_attestation_participants(state: BeaconState,
attestation_data: AttestationData,
bitfield: bytes) -> List[ValidatorIndex]:
"""
Return the sorted participant indices corresponding to ``attestation_data`` and ``bitfield``.
"""
crosslink_committee = get_crosslink_committee_for_attestation(state, attestation_data)
assert verify_bitfield(bitfield, len(crosslink_committee))
return sorted([index for i, index in enumerate(crosslink_committee) if get_bitfield_bit(bitfield, i) == 0b1])
```
@ -1121,8 +1107,8 @@ def convert_to_indexed(state: BeaconState, attestation: Attestation) -> IndexedA
"""
Convert ``attestation`` to (almost) indexed-verifiable form.
"""
attesting_indices = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)
custody_bit_1_indices = get_attestation_participants(state, attestation.data, attestation.custody_bitfield)
attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bitfield)
custody_bit_1_indices = get_attesting_indices(state, attestation.data, attestation.custody_bitfield)
custody_bit_0_indices = [index for index in attesting_indices if index not in custody_bit_1_indices]
return IndexedAttestation(
@ -1143,14 +1129,13 @@ def verify_indexed_attestation(state: BeaconState, indexed_attestation: IndexedA
custody_bit_0_indices = indexed_attestation.custody_bit_0_indices
custody_bit_1_indices = indexed_attestation.custody_bit_1_indices
# ensure no duplicate indices across custody bits
# Ensure no duplicate indices across custody bits
assert len(set(custody_bit_0_indices).intersection(set(custody_bit_1_indices))) == 0
if len(custody_bit_1_indices) > 0: # [TO BE REMOVED IN PHASE 1]
return False
total_attesting_indices = len(custody_bit_0_indices) + len(custody_bit_1_indices)
if not (1 <= total_attesting_indices <= MAX_ATTESTATION_PARTICIPANTS):
if not (1 <= len(custody_bit_0_indices) + len(custody_bit_1_indices) <= MAX_INDICES_PER_ATTESTATION):
return False
if custody_bit_0_indices != sorted(custody_bit_0_indices):
@ -1440,7 +1425,8 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
finalized_root=ZERO_HASH,
# Recent state
latest_crosslinks=Vector([Crosslink(epoch=GENESIS_EPOCH, crosslink_data_root=ZERO_HASH) for _ in range(SHARD_COUNT)]),
current_crosslinks=Vector([Crosslink(epoch=GENESIS_EPOCH, previous_crosslink_root=ZERO_HASH, crosslink_data_root=ZERO_HASH) for _ in range(SHARD_COUNT)]),
previous_crosslinks=Vector([Crosslink(epoch=GENESIS_EPOCH, previous_crosslink_root=ZERO_HASH, crosslink_data_root=ZERO_HASH) for _ in range(SHARD_COUNT)]),
latest_block_roots=Vector([ZERO_HASH for _ in range(SLOTS_PER_HISTORICAL_ROOT)]),
latest_state_roots=Vector([ZERO_HASH for _ in range(SLOTS_PER_HISTORICAL_ROOT)]),
latest_active_index_roots=Vector([ZERO_HASH for _ in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH)]),
@ -1606,7 +1592,7 @@ def get_previous_total_balance(state: BeaconState) -> Gwei:
def get_unslashed_attesting_indices(state: BeaconState, attestations: List[PendingAttestation]) -> List[ValidatorIndex]:
output = set()
for a in attestations:
output = output.union(get_attestation_participants(state, a.data, a.aggregation_bitfield))
output = output.union(get_attesting_indices(state, a.data, a.aggregation_bitfield))
return sorted(filter(lambda index: not state.validator_registry[index].slashed, list(output)))
```
@ -1642,31 +1628,40 @@ def get_previous_epoch_matching_head_attestations(state: BeaconState) -> List[Pe
**Note**: Total balances computed for the previous epoch might be marginally different than the actual total balances during the previous epoch transition. Due to the tight bound on validator churn each epoch and small per-epoch rewards/penalties, the potential balance difference is very low and only marginally affects consensus safety.
```python
def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple[Bytes32, List[ValidatorIndex]]:
all_attestations = state.current_epoch_attestations + state.previous_epoch_attestations
valid_attestations = [
a for a in all_attestations if a.data.previous_crosslink == state.latest_crosslinks[shard]
def get_crosslink_from_attestation_data(state: BeaconState, data: AttestationData) -> Crosslink:
return Crosslink(
epoch=min(slot_to_epoch(data.slot), state.current_crosslinks[data.shard].epoch + MAX_CROSSLINK_EPOCHS),
previous_crosslink_root=data.previous_crosslink_root,
crosslink_data_root=data.crosslink_data_root,
)
```
```python
def get_winning_crosslink_and_attesting_indices(state: BeaconState, epoch: Epoch, shard: Shard) -> Tuple[Crosslink, List[ValidatorIndex]]:
pending_attestations = state.current_epoch_attestations if epoch == get_current_epoch(state) else state.previous_epoch_attestations
shard_attestations = [a for a in pending_attestations if a.data.shard == shard]
shard_crosslinks = [get_crosslink_from_attestation_data(state, a.data) for a in shard_attestations]
candidate_crosslinks = [
c for c in shard_crosslinks
if hash_tree_root(state.current_crosslinks[shard]) in (c.previous_crosslink_root, hash_tree_root(c))
]
all_roots = [a.data.crosslink_data_root for a in valid_attestations]
if len(candidate_crosslinks) == 0:
return Crosslink(epoch=GENESIS_EPOCH, previous_crosslink_root=ZERO_HASH, crosslink_data_root=ZERO_HASH), []
# handle when no attestations for shard available
if len(all_roots) == 0:
return ZERO_HASH, []
def get_attestations_for(crosslink: Crosslink) -> List[PendingAttestation]:
return [a for a in shard_attestations if get_crosslink_from_attestation_data(state, a.data) == crosslink]
# Winning crosslink has the crosslink data root with the most balance voting for it (ties broken lexicographically)
winning_crosslink = max(candidate_crosslinks, key=lambda crosslink: (
get_attesting_balance(state, get_attestations_for(crosslink)), crosslink.crosslink_data_root
))
def get_attestations_for(root) -> List[PendingAttestation]:
return [a for a in valid_attestations if a.data.crosslink_data_root == root]
# Winning crosslink root is the root with the most votes for it, ties broken in favor of
# lexicographically higher hash
winning_root = max(all_roots, key=lambda r: (get_attesting_balance(state, get_attestations_for(r)), r))
return winning_root, get_unslashed_attesting_indices(state, get_attestations_for(winning_root))
return winning_crosslink, get_unslashed_attesting_indices(state, get_attestations_for(winning_crosslink))
```
```python
def get_earliest_attestation(state: BeaconState, attestations: List[PendingAttestation], index: ValidatorIndex) -> PendingAttestation:
return min([
a for a in attestations if index in get_attestation_participants(state, a.data, a.aggregation_bitfield)
a for a in attestations if index in get_attesting_indices(state, a.data, a.aggregation_bitfield)
], key=lambda a: a.inclusion_slot)
```
@ -1724,18 +1719,14 @@ Run the following function:
```python
def process_crosslinks(state: BeaconState) -> None:
state.previous_crosslinks = [c for c in state.current_crosslinks]
previous_epoch = get_previous_epoch(state)
next_epoch = get_current_epoch(state) + 1
for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)):
for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot):
winning_root, participants = get_winning_root_and_participants(state, shard)
participating_balance = get_total_balance(state, participants)
total_balance = get_total_balance(state, crosslink_committee)
if 3 * participating_balance >= 2 * total_balance:
state.latest_crosslinks[shard] = Crosslink(
epoch=min(slot_to_epoch(slot), state.latest_crosslinks[shard].epoch + MAX_CROSSLINK_EPOCHS),
crosslink_data_root=winning_root
)
winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, slot_to_epoch(slot), shard)
if 3 * get_total_balance(state, attesting_indices) >= 2 * get_total_balance(state, crosslink_committee):
state.current_crosslinks[shard] = winning_crosslink
```
#### Rewards and penalties
@ -1817,16 +1808,14 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[
def get_crosslink_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
rewards = [0 for index in range(len(state.validator_registry))]
penalties = [0 for index in range(len(state.validator_registry))]
previous_epoch_start_slot = get_epoch_start_slot(get_previous_epoch(state))
current_epoch_start_slot = get_epoch_start_slot(get_current_epoch(state))
for slot in range(previous_epoch_start_slot, current_epoch_start_slot):
for slot in range(get_epoch_start_slot(get_previous_epoch(state)), get_epoch_start_slot(get_current_epoch(state))):
for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot):
winning_root, participants = get_winning_root_and_participants(state, shard)
participating_balance = get_total_balance(state, participants)
total_balance = get_total_balance(state, crosslink_committee)
winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, slot_to_epoch(slot), shard)
attesting_balance = get_total_balance(state, attesting_indices)
committee_balance = get_total_balance(state, crosslink_committee)
for index in crosslink_committee:
if index in participants:
rewards[index] += get_base_reward(state, index) * participating_balance // total_balance
if index in attesting_indices:
rewards[index] += get_base_reward(state, index) * attesting_balance // committee_balance
else:
penalties[index] += get_base_reward(state, index)
return [rewards, penalties]
@ -2077,32 +2066,26 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
Process ``Attestation`` operation.
Note that this function mutates ``state``.
"""
assert attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= attestation.data.slot + SLOTS_PER_EPOCH
data = attestation.data
min_slot = state.slot - SLOTS_PER_EPOCH if get_current_epoch(state) > GENESIS_EPOCH else GENESIS_SLOT
assert min_slot <= data.slot <= state.slot - MIN_ATTESTATION_INCLUSION_DELAY
# Check target epoch, source epoch, and source root
target_epoch = slot_to_epoch(attestation.data.slot)
assert (target_epoch, attestation.data.source_epoch, attestation.data.source_root) in {
(get_current_epoch(state), state.current_justified_epoch, state.current_justified_root),
(get_previous_epoch(state), state.previous_justified_epoch, state.previous_justified_root),
# Check target epoch, source epoch, source root, and source crosslink
target_epoch = slot_to_epoch(data.slot)
assert (target_epoch, data.source_epoch, data.source_root, data.previous_crosslink_root) in {
(get_current_epoch(state), state.current_justified_epoch, state.current_justified_root, hash_tree_root(state.current_crosslinks[data.shard])),
(get_previous_epoch(state), state.previous_justified_epoch, state.previous_justified_root, hash_tree_root(state.previous_crosslinks[data.shard])),
}
# Check crosslink data
assert attestation.data.crosslink_data_root == ZERO_HASH # [to be removed in phase 1]
assert state.latest_crosslinks[attestation.data.shard] in {
attestation.data.previous_crosslink, # Case 1: latest crosslink matches previous crosslink
Crosslink( # Case 2: latest crosslink matches current crosslink
crosslink_data_root=attestation.data.crosslink_data_root,
epoch=min(slot_to_epoch(attestation.data.slot),
attestation.data.previous_crosslink.epoch + MAX_CROSSLINK_EPOCHS)
),
}
# Check crosslink data root
assert data.crosslink_data_root == ZERO_HASH # [to be removed in phase 1]
# Check signature and bitfields
assert verify_indexed_attestation(state, convert_to_indexed(state, attestation))
# Cache pending attestation
pending_attestation = PendingAttestation(
data=attestation.data,
data=data,
aggregation_bitfield=attestation.aggregation_bitfield,
inclusion_slot=state.slot
)

View File

@ -309,7 +309,7 @@ def process_chunk_challenge(state: BeaconState,
responder = state.validator_registry[challenge.responder_index]
assert responder.exit_epoch >= get_current_epoch(state) - MAX_CHUNK_CHALLENGE_DELAY
# Verify the responder participated in the attestation
attesters = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)
attesters = get_attesting_indices(state, attestation.data, attestation.aggregation_bitfield)
assert challenge.responder_index in attesters
# Verify the challenge is not a duplicate
for record in state.custody_chunk_challenge_records:
@ -359,9 +359,9 @@ def process_bit_challenge(state: BeaconState,
# Verify the attestation is eligible for challenging
responder = state.validator_registry[challenge.responder_index]
min_challengeable_epoch = responder.exit_epoch - EPOCHS_PER_CUSTODY_PERIOD * (1 + responder.max_reveal_lateness)
assert min_challengeable_epoch <= slot_to_epoch(challenge.attestation.data.slot)
assert min_challengeable_epoch <= slot_to_epoch(challenge.attestation.data.slot)
# Verify the responder participated in the attestation
attesters = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)
attesters = get_attesting_indices(state, attestation.data, attestation.aggregation_bitfield)
assert challenge.responder_index in attesters
# A validator can be the challenger or responder for at most one challenge at a time
for record in state.custody_bit_challenge_records:

View File

@ -38,13 +38,13 @@ __NOTICE__: This document is a work-in-progress for researchers and implementers
- [Attestations](#attestations-1)
- [Attestation data](#attestation-data)
- [Slot](#slot-1)
- [Shard](#shard)
- [Beacon block root](#beacon-block-root)
- [Target root](#target-root)
- [Crosslink data root](#crosslink-data-root)
- [Latest crosslink](#latest-crosslink)
- [Source epoch](#source-epoch)
- [Source root](#source-root)
- [Target root](#target-root)
- [Shard](#shard)
- [Previous crosslink root](#previous-crosslink-root)
- [Crosslink data root](#crosslink-data-root)
- [Construct attestation](#construct-attestation)
- [Data](#data)
- [Aggregation bitfield](#aggregation-bitfield)
@ -250,14 +250,18 @@ First the validator should construct `attestation_data`, an [`AttestationData`](
Set `attestation_data.slot = head_state.slot`.
##### Shard
Set `attestation_data.shard = shard` where `shard` is the shard associated with the validator's committee defined by `get_crosslink_committees_at_slot`.
##### Beacon block root
Set `attestation_data.beacon_block_root = signing_root(head_block)`.
##### Source epoch
Set `attestation_data.source_epoch = head_state.justified_epoch`.
##### Source root
Set `attestation_data.source_root = head_state.current_justified_root`.
##### Target root
Set `attestation_data.target_root = signing_root(epoch_boundary)` where `epoch_boundary` is the block at the most recent epoch boundary.
@ -266,24 +270,20 @@ _Note:_ This can be looked up in the state using:
* Let `epoch_start_slot = get_epoch_start_slot(get_current_epoch(head_state))`.
* Set `epoch_boundary = head if epoch_start_slot == head_state.slot else get_block_root(state, epoch_start_slot)`.
##### Shard
Set `attestation_data.shard = shard` where `shard` is the shard associated with the validator's committee defined by `get_crosslink_committees_at_slot`.
##### Previous crosslink root
Set `attestation_data.previous_crosslink_root = hash_tree_root(head_state.current_crosslinks[shard])`.
##### Crosslink data root
Set `attestation_data.crosslink_data_root = ZERO_HASH`.
_Note:_ This is a stub for phase 0.
##### Latest crosslink
Set `attestation_data.previous_crosslink = head_state.latest_crosslinks[shard]`.
##### Source epoch
Set `attestation_data.source_epoch = head_state.justified_epoch`.
##### Source root
Set `attestation_data.source_root = head_state.current_justified_root`.
#### Construct attestation
Next the validator creates `attestation`, an [`Attestation`](../core/0_beacon-chain.md#attestation) object.
@ -299,7 +299,7 @@ Set `attestation.data = attestation_data` where `attestation_data` is the `Attes
* Set `aggregation_bitfield[index_into_committee // 8] |= 2 ** (index_into_committee % 8)`.
* Set `attestation.aggregation_bitfield = aggregation_bitfield`.
_Note_: Calling `get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield)` should return a list of length equal to 1, containing `validator_index`.
_Note_: Calling `get_attesting_indices(state, attestation.data, attestation.aggregation_bitfield)` should return a list of length equal to 1, containing `validator_index`.
##### Custody bitfield

View File

@ -14,6 +14,8 @@ from eth2spec.phase0.spec import (
from tests.helpers import (
build_empty_block_for_next_slot,
get_valid_attestation,
next_epoch,
next_slot,
)
@ -120,10 +122,12 @@ def test_non_zero_crosslink_data_root(state):
def test_bad_previous_crosslink(state):
next_epoch(state)
attestation = get_valid_attestation(state)
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY):
next_slot(state)
state.latest_crosslinks[attestation.data.shard].epoch += 10
state.current_crosslinks[attestation.data.shard].epoch += 10
pre_state, post_state = run_attestation_processing(state, attestation, False)

View File

@ -11,7 +11,7 @@ from tests.helpers import (
get_valid_proposer_slashing,
)
# mark entire file as 'header'
# mark entire file as 'proposer_slashings'
pytestmark = pytest.mark.proposer_slashings

View File

@ -27,7 +27,8 @@ def overwrite_spec_config(config):
if field == "LATEST_RANDAO_MIXES_LENGTH":
spec.BeaconState.fields['latest_randao_mixes'][1] = config[field]
elif field == "SHARD_COUNT":
spec.BeaconState.fields['latest_crosslinks'][1] = config[field]
spec.BeaconState.fields['current_crosslinks'][1] = config[field]
spec.BeaconState.fields['previous_crosslinks'][1] = config[field]
elif field == "SLOTS_PER_HISTORICAL_ROOT":
spec.BeaconState.fields['latest_block_roots'][1] = config[field]
spec.BeaconState.fields['latest_state_roots'][1] = config[field]

View File

@ -0,0 +1,136 @@
from copy import deepcopy
import pytest
import eth2spec.phase0.spec as spec
from eth2spec.phase0.state_transition import (
state_transition,
)
from eth2spec.phase0.spec import (
cache_state,
get_crosslink_deltas,
process_crosslinks,
)
from tests.helpers import (
add_attestation_to_state,
build_empty_block_for_next_slot,
fill_aggregate_attestation,
get_crosslink_committee_for_attestation,
get_valid_attestation,
next_epoch,
next_slot,
set_bitfield_bit,
)
# mark entire file as 'crosslinks'
pytestmark = pytest.mark.crosslinks
def run_process_crosslinks(state, valid=True):
# transition state to slot before state transition
slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - 1
block = build_empty_block_for_next_slot(state)
block.slot = slot
state_transition(state, block)
# cache state before epoch transition
cache_state(state)
post_state = deepcopy(state)
process_crosslinks(post_state)
return state, post_state
def test_no_attestations(state):
pre_state, post_state = run_process_crosslinks(state)
for shard in range(spec.SHARD_COUNT):
assert post_state.previous_crosslinks[shard] == post_state.current_crosslinks[shard]
return pre_state, post_state
def test_single_crosslink_update_from_current_epoch(state):
next_epoch(state)
attestation = get_valid_attestation(state)
fill_aggregate_attestation(state, attestation)
add_attestation_to_state(state, attestation, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY)
assert len(state.current_epoch_attestations) == 1
pre_state, post_state = run_process_crosslinks(state)
shard = attestation.data.shard
assert post_state.previous_crosslinks[shard] != post_state.current_crosslinks[shard]
assert pre_state.current_crosslinks[shard] != post_state.current_crosslinks[shard]
return pre_state, post_state
def test_single_crosslink_update_from_previous_epoch(state):
next_epoch(state)
attestation = get_valid_attestation(state)
fill_aggregate_attestation(state, attestation)
add_attestation_to_state(state, attestation, state.slot + spec.SLOTS_PER_EPOCH)
assert len(state.previous_epoch_attestations) == 1
pre_state, post_state = run_process_crosslinks(state)
crosslink_deltas = get_crosslink_deltas(state)
shard = attestation.data.shard
assert post_state.previous_crosslinks[shard] != post_state.current_crosslinks[shard]
assert pre_state.current_crosslinks[shard] != post_state.current_crosslinks[shard]
# ensure rewarded
for index in get_crosslink_committee_for_attestation(state, attestation.data):
assert crosslink_deltas[0][index] > 0
assert crosslink_deltas[1][index] == 0
return pre_state, post_state
def test_double_late_crosslink(state):
next_epoch(state)
state.slot += 4
attestation_1 = get_valid_attestation(state)
fill_aggregate_attestation(state, attestation_1)
# add attestation_1 in the next epoch
next_epoch(state)
add_attestation_to_state(state, attestation_1, state.slot + 1)
for slot in range(spec.SLOTS_PER_EPOCH):
attestation_2 = get_valid_attestation(state)
if attestation_2.data.shard == attestation_1.data.shard:
break
next_slot(state)
fill_aggregate_attestation(state, attestation_2)
# add attestation_2 in the next epoch after attestation_1 has
# already updated the relevant crosslink
next_epoch(state)
add_attestation_to_state(state, attestation_2, state.slot + 1)
assert len(state.previous_epoch_attestations) == 1
assert len(state.current_epoch_attestations) == 0
pre_state, post_state = run_process_crosslinks(state)
crosslink_deltas = get_crosslink_deltas(state)
shard = attestation_2.data.shard
# ensure that the current crosslinks were not updated by the second attestation
assert post_state.previous_crosslinks[shard] == post_state.current_crosslinks[shard]
# ensure no reward, only penalties for the failed crosslink
for index in get_crosslink_committee_for_attestation(state, attestation_2.data):
assert crosslink_deltas[0][index] == 0
assert crosslink_deltas[1][index] > 0
return pre_state, post_state

View File

@ -25,9 +25,9 @@ from eth2spec.phase0.spec import (
# functions
convert_to_indexed,
get_active_validator_indices,
get_attestation_participants,
get_attesting_indices,
get_block_root,
get_crosslink_committee_for_attestation,
get_crosslink_committees_at_slot,
get_current_epoch,
get_domain,
get_empty_block,
@ -35,6 +35,7 @@ from eth2spec.phase0.spec import (
get_genesis_beacon_state,
get_previous_epoch,
get_shard_delta,
hash_tree_root,
slot_to_epoch,
verify_merkle_branch,
hash,
@ -154,6 +155,7 @@ def build_attestation_data(state, slot, shard):
current_epoch_start_slot = get_epoch_start_slot(get_current_epoch(state))
if slot < current_epoch_start_slot:
print(slot)
epoch_boundary_root = get_block_root(state, get_epoch_start_slot(get_previous_epoch(state)))
elif slot == current_epoch_start_slot:
epoch_boundary_root = block_root
@ -167,6 +169,7 @@ def build_attestation_data(state, slot, shard):
justified_epoch = state.current_justified_epoch
justified_block_root = state.current_justified_root
crosslinks = state.current_crosslinks if slot_to_epoch(slot) == get_current_epoch(state) else state.previous_crosslinks
return AttestationData(
slot=slot,
shard=shard,
@ -175,7 +178,7 @@ def build_attestation_data(state, slot, shard):
source_root=justified_block_root,
target_root=epoch_boundary_root,
crosslink_data_root=spec.ZERO_HASH,
previous_crosslink=deepcopy(state.latest_crosslinks[shard]),
previous_crosslink_root=hash_tree_root(crosslinks[shard]),
)
@ -273,6 +276,14 @@ def get_valid_attester_slashing(state):
)
def get_crosslink_committee_for_attestation(state, attestation_data):
"""
Return the crosslink committee corresponding to ``attestation_data``.
"""
crosslink_committees = get_crosslink_committees_at_slot(state, attestation_data.slot)
return [committee for committee, shard in crosslink_committees if shard == attestation_data.shard][0]
def get_valid_attestation(state, slot=None):
if slot is None:
slot = state.slot
@ -297,7 +308,7 @@ def get_valid_attestation(state, slot=None):
custody_bitfield=custody_bitfield,
aggregate_signature=EMPTY_SIGNATURE,
)
participants = get_attestation_participants(
participants = get_attesting_indices(
state,
attestation.data,
attestation.aggregation_bitfield,
@ -342,6 +353,13 @@ def fill_aggregate_attestation(state, attestation):
attestation.aggregation_bitfield = set_bitfield_bit(attestation.aggregation_bitfield, i)
def add_attestation_to_state(state, attestation, slot):
block = build_empty_block_for_next_slot(state)
block.slot = slot
block.body.attestations.append(attestation)
state_transition(state, block)
def next_slot(state):
block = build_empty_block_for_next_slot(state)
state_transition(state, block)

View File

@ -25,6 +25,7 @@ from eth2spec.phase0.spec import (
advance_slot,
cache_state,
set_balance,
slot_to_epoch,
verify_merkle_branch,
hash,
)
@ -382,33 +383,6 @@ def test_voluntary_exit(state):
return pre_state, [initiate_exit_block, exit_block], post_state
def test_no_exit_churn_too_long_since_change(state):
pre_state = deepcopy(state)
validator_index = get_active_validator_indices(
pre_state,
get_current_epoch(pre_state)
)[-1]
#
# setup pre_state
#
# move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit
pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
post_state = deepcopy(pre_state)
#
# Process registry change but ensure no exit
#
block = build_empty_block_for_next_slot(post_state)
block.slot += spec.SLOTS_PER_EPOCH
state_transition(post_state, block)
assert post_state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH
return pre_state, [block], post_state
def test_transfer(state):
pre_state = deepcopy(state)
current_epoch = get_current_epoch(pre_state)