Merge pull request #874 from ethereum/prev-cur-crosslinks
add previous and current crosslinks
This commit is contained in:
commit
aaea74e4bc
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue