Merge branch 'dev' into JustinDrake-patch-14

This commit is contained in:
Hsiao-Wei Wang 2019-04-19 09:37:11 +08:00
commit 7a435d2e1a
No known key found for this signature in database
GPG Key ID: 95B070122902DEA4
10 changed files with 401 additions and 171 deletions

View File

@ -76,8 +76,7 @@
- [`generate_seed`](#generate_seed) - [`generate_seed`](#generate_seed)
- [`get_beacon_proposer_index`](#get_beacon_proposer_index) - [`get_beacon_proposer_index`](#get_beacon_proposer_index)
- [`verify_merkle_branch`](#verify_merkle_branch) - [`verify_merkle_branch`](#verify_merkle_branch)
- [`get_crosslink_committee_for_attestation`](#get_crosslink_committee_for_attestation) - [`get_attesting_indices`](#get_attesting_indices)
- [`get_attestation_participants`](#get_attestation_participants)
- [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-) - [`int_to_bytes1`, `int_to_bytes2`, ...](#int_to_bytes1-int_to_bytes2-)
- [`bytes_to_int`](#bytes_to_int) - [`bytes_to_int`](#bytes_to_int)
- [`get_effective_balance`](#get_effective_balance) - [`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) | | `SHARD_COUNT` | `2**10` (= 1,024) |
| `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) | | `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) | | `MIN_PER_EPOCH_CHURN_LIMIT` | `2**2` (= 4) |
| `CHURN_LIMIT_QUOTIENT` | `2**16` (= 65,536) | | `CHURN_LIMIT_QUOTIENT` | `2**16` (= 65,536) |
| `BASE_REWARDS_PER_EPOCH` | `4` | | `BASE_REWARDS_PER_EPOCH` | `4` |
@ -301,6 +300,8 @@ The types are defined topologically to aid in facilitating an executable version
{ {
# Epoch number # Epoch number
'epoch': 'uint64', 'epoch': 'uint64',
# Root of the previous crosslink
'previous_crosslink_root': 'bytes32',
# Shard data since the previous crosslink # Shard data since the previous crosslink
'crosslink_data_root': 'bytes32', 'crosslink_data_root': 'bytes32',
} }
@ -334,7 +335,7 @@ The types are defined topologically to aid in facilitating an executable version
# Crosslink vote # Crosslink vote
'shard': 'uint64', 'shard': 'uint64',
'previous_crosslink': Crosslink, 'previous_crosslink_root': 'bytes32',
'crosslink_data_root': 'bytes32', 'crosslink_data_root': 'bytes32',
} }
``` ```
@ -586,7 +587,8 @@ The types are defined topologically to aid in facilitating an executable version
'finalized_root': 'bytes32', 'finalized_root': 'bytes32',
# Recent state # 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_block_roots': ['bytes32', SLOTS_PER_HISTORICAL_ROOT],
'latest_state_roots': ['bytes32', SLOTS_PER_HISTORICAL_ROOT], 'latest_state_roots': ['bytes32', SLOTS_PER_HISTORICAL_ROOT],
'latest_active_index_roots': ['bytes32', LATEST_ACTIVE_INDEX_ROOTS_LENGTH], 'latest_active_index_roots': ['bytes32', LATEST_ACTIVE_INDEX_ROOTS_LENGTH],
@ -1000,33 +1002,17 @@ def verify_merkle_branch(leaf: Bytes32, proof: List[Bytes32], depth: int, index:
return value == root return value == root
``` ```
### `get_crosslink_committee_for_attestation` ### `get_attesting_indices`
```python ```python
def get_crosslink_committee_for_attestation(state: BeaconState, def get_attesting_indices(state: BeaconState,
attestation_data: AttestationData) -> List[ValidatorIndex]:
"""
Return the crosslink committee corresponding to ``attestation_data``.
"""
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, attestation_data: AttestationData,
bitfield: bytes) -> List[ValidatorIndex]: bitfield: bytes) -> List[ValidatorIndex]:
""" """
Return the sorted participant indices corresponding to ``attestation_data`` and ``bitfield``. Return the sorted attesting indices corresponding to ``attestation_data`` and ``bitfield``.
""" """
crosslink_committee = get_crosslink_committee_for_attestation(state, attestation_data) crosslink_committees = get_crosslink_committees_at_slot(state, attestation_data.slot)
crosslink_committee = [committee for committee, shard in crosslink_committees if shard == attestation_data.shard][0]
assert verify_bitfield(bitfield, len(crosslink_committee)) assert verify_bitfield(bitfield, len(crosslink_committee))
return sorted([index for i, index in enumerate(crosslink_committee) if get_bitfield_bit(bitfield, i) == 0b1]) return sorted([index for i, index in enumerate(crosslink_committee) if get_bitfield_bit(bitfield, i) == 0b1])
``` ```
@ -1123,8 +1109,8 @@ def convert_to_indexed(state: BeaconState, attestation: Attestation) -> IndexedA
""" """
Convert ``attestation`` to (almost) indexed-verifiable form. Convert ``attestation`` to (almost) indexed-verifiable form.
""" """
attesting_indices = get_attestation_participants(state, attestation.data, attestation.aggregation_bitfield) attesting_indices = get_attesting_indices(state, attestation.data, attestation.aggregation_bitfield)
custody_bit_1_indices = get_attestation_participants(state, attestation.data, attestation.custody_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] custody_bit_0_indices = [index for index in attesting_indices if index not in custody_bit_1_indices]
return IndexedAttestation( return IndexedAttestation(
@ -1145,14 +1131,13 @@ def verify_indexed_attestation(state: BeaconState, indexed_attestation: IndexedA
custody_bit_0_indices = indexed_attestation.custody_bit_0_indices custody_bit_0_indices = indexed_attestation.custody_bit_0_indices
custody_bit_1_indices = indexed_attestation.custody_bit_1_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 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] if len(custody_bit_1_indices) > 0: # [TO BE REMOVED IN PHASE 1]
return False return False
total_attesting_indices = len(custody_bit_0_indices) + len(custody_bit_1_indices) if not (1 <= len(custody_bit_0_indices) + len(custody_bit_1_indices) <= MAX_INDICES_PER_ATTESTATION):
if not (1 <= total_attesting_indices <= MAX_ATTESTATION_PARTICIPANTS):
return False return False
if custody_bit_0_indices != sorted(custody_bit_0_indices): if custody_bit_0_indices != sorted(custody_bit_0_indices):
@ -1442,7 +1427,8 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
finalized_root=ZERO_HASH, finalized_root=ZERO_HASH,
# Recent state # 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_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_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)]), latest_active_index_roots=Vector([ZERO_HASH for _ in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH)]),
@ -1635,7 +1621,7 @@ def get_previous_epoch_matching_head_attestations(state: BeaconState) -> List[Pe
def get_unslashed_attesting_indices(state: BeaconState, attestations: List[PendingAttestation]) -> List[ValidatorIndex]: def get_unslashed_attesting_indices(state: BeaconState, attestations: List[PendingAttestation]) -> List[ValidatorIndex]:
output = set() output = set()
for a in attestations: 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))) return sorted(filter(lambda index: not state.validator_registry[index].slashed, list(output)))
``` ```
@ -1645,31 +1631,40 @@ def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestat
``` ```
```python ```python
def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple[Bytes32, List[ValidatorIndex]]: def get_crosslink_from_attestation_data(state: BeaconState, data: AttestationData) -> Crosslink:
all_attestations = state.current_epoch_attestations + state.previous_epoch_attestations return Crosslink(
valid_attestations = [ epoch=min(slot_to_epoch(data.slot), state.current_crosslinks[data.shard].epoch + MAX_CROSSLINK_EPOCHS),
a for a in all_attestations if a.data.previous_crosslink == state.latest_crosslinks[shard] 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 def get_attestations_for(crosslink: Crosslink) -> List[PendingAttestation]:
if len(all_roots) == 0: return [a for a in shard_attestations if get_crosslink_from_attestation_data(state, a.data) == crosslink]
return ZERO_HASH, [] # 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 winning_crosslink, get_unslashed_attesting_indices(state, get_attestations_for(winning_crosslink))
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))
``` ```
```python ```python
def get_earliest_attestation(state: BeaconState, attestations: List[PendingAttestation], index: ValidatorIndex) -> PendingAttestation: def get_earliest_attestation(state: BeaconState, attestations: List[PendingAttestation], index: ValidatorIndex) -> PendingAttestation:
return min([ 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) ], key=lambda a: a.inclusion_slot)
``` ```
@ -1727,18 +1722,14 @@ Run the following function:
```python ```python
def process_crosslinks(state: BeaconState) -> None: def process_crosslinks(state: BeaconState) -> None:
state.previous_crosslinks = [c for c in state.current_crosslinks]
previous_epoch = get_previous_epoch(state) previous_epoch = get_previous_epoch(state)
next_epoch = get_current_epoch(state) + 1 next_epoch = get_current_epoch(state) + 1
for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)): 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): for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot):
winning_root, participants = get_winning_root_and_participants(state, shard) winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, slot_to_epoch(slot), shard)
participating_balance = get_total_balance(state, participants) if 3 * get_total_balance(state, attesting_indices) >= 2 * get_total_balance(state, crosslink_committee):
total_balance = get_total_balance(state, crosslink_committee) state.current_crosslinks[shard] = winning_crosslink
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
)
``` ```
#### Rewards and penalties #### Rewards and penalties
@ -1800,17 +1791,15 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[
def get_crosslink_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]: def get_crosslink_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]:
rewards = [0 for index in range(len(state.validator_registry))] rewards = [0 for index in range(len(state.validator_registry))]
penalties = [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)) for slot in range(get_epoch_start_slot(get_previous_epoch(state)), get_epoch_start_slot(get_current_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 crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot): for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot):
winning_root, participants = get_winning_root_and_participants(state, shard) winning_crosslink, attesting_indices = get_winning_crosslink_and_attesting_indices(state, slot_to_epoch(slot), shard)
participating_balance = get_total_balance(state, participants) attesting_balance = get_total_balance(state, attesting_indices)
total_balance = get_total_balance(state, crosslink_committee) committee_balance = get_total_balance(state, crosslink_committee)
for index in crosslink_committee: for index in crosslink_committee:
base_reward = get_base_reward(state, get_previous_epoch_total_balance(state), index) base_reward = get_base_reward(state, get_previous_epoch_total_balance(state), index)
if index in participants: if index in attesting_indices:
rewards[index] += base_reward * participating_balance // total_balance rewards[index] += base_reward * attesting_balance // committee_balance
else: else:
penalties[index] += base_reward penalties[index] += base_reward
return [rewards, penalties] return [rewards, penalties]
@ -2061,32 +2050,26 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
Process ``Attestation`` operation. Process ``Attestation`` operation.
Note that this function mutates ``state``. 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 # Check target epoch, source epoch, source root, and source crosslink
target_epoch = slot_to_epoch(attestation.data.slot) target_epoch = slot_to_epoch(data.slot)
assert (target_epoch, attestation.data.source_epoch, attestation.data.source_root) in { 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), (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), (get_previous_epoch(state), state.previous_justified_epoch, state.previous_justified_root, hash_tree_root(state.previous_crosslinks[data.shard])),
} }
# Check crosslink data # Check crosslink data root
assert attestation.data.crosslink_data_root == ZERO_HASH # [to be removed in phase 1] assert 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 signature and bitfields # Check signature and bitfields
assert verify_indexed_attestation(state, convert_to_indexed(state, attestation)) assert verify_indexed_attestation(state, convert_to_indexed(state, attestation))
# Cache pending attestation # Cache pending attestation
pending_attestation = PendingAttestation( pending_attestation = PendingAttestation(
data=attestation.data, data=data,
aggregation_bitfield=attestation.aggregation_bitfield, aggregation_bitfield=attestation.aggregation_bitfield,
inclusion_slot=state.slot inclusion_slot=state.slot
) )

View File

@ -309,7 +309,7 @@ def process_chunk_challenge(state: BeaconState,
responder = state.validator_registry[challenge.responder_index] responder = state.validator_registry[challenge.responder_index]
assert responder.exit_epoch >= get_current_epoch(state) - MAX_CHUNK_CHALLENGE_DELAY assert responder.exit_epoch >= get_current_epoch(state) - MAX_CHUNK_CHALLENGE_DELAY
# Verify the responder participated in the attestation # 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 assert challenge.responder_index in attesters
# Verify the challenge is not a duplicate # Verify the challenge is not a duplicate
for record in state.custody_chunk_challenge_records: for record in state.custody_chunk_challenge_records:
@ -361,7 +361,7 @@ def process_bit_challenge(state: BeaconState,
min_challengeable_epoch = responder.exit_epoch - EPOCHS_PER_CUSTODY_PERIOD * (1 + responder.max_reveal_lateness) 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 # 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 assert challenge.responder_index in attesters
# A validator can be the challenger or responder for at most one challenge at a time # A validator can be the challenger or responder for at most one challenge at a time
for record in state.custody_bit_challenge_records: 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) - [Attestations](#attestations-1)
- [Attestation data](#attestation-data) - [Attestation data](#attestation-data)
- [Slot](#slot-1) - [Slot](#slot-1)
- [Shard](#shard)
- [Beacon block root](#beacon-block-root) - [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 epoch](#source-epoch)
- [Source root](#source-root) - [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) - [Construct attestation](#construct-attestation)
- [Data](#data) - [Data](#data)
- [Aggregation bitfield](#aggregation-bitfield) - [Aggregation bitfield](#aggregation-bitfield)
@ -250,14 +250,18 @@ First the validator should construct `attestation_data`, an [`AttestationData`](
Set `attestation_data.slot = head_state.slot`. 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 ##### Beacon block root
Set `attestation_data.beacon_block_root = signing_root(head_block)`. 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 ##### Target root
Set `attestation_data.target_root = signing_root(epoch_boundary)` where `epoch_boundary` is the block at the most recent epoch boundary. 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))`. * 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)`. * 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 ##### Crosslink data root
Set `attestation_data.crosslink_data_root = ZERO_HASH`. Set `attestation_data.crosslink_data_root = ZERO_HASH`.
_Note:_ This is a stub for phase 0. _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 #### Construct attestation
Next the validator creates `attestation`, an [`Attestation`](../core/0_beacon-chain.md#attestation) object. 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 `aggregation_bitfield[index_into_committee // 8] |= 2 ** (index_into_committee % 8)`.
* Set `attestation.aggregation_bitfield = aggregation_bitfield`. * 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 ##### Custody bitfield

View File

@ -1,5 +1,6 @@
from .hash_function import hash from .hash_function import hash
from typing import Any
BYTES_PER_CHUNK = 32 BYTES_PER_CHUNK = 32
BYTES_PER_LENGTH_PREFIX = 4 BYTES_PER_LENGTH_PREFIX = 4
@ -9,9 +10,10 @@ ZERO_CHUNK = b'\x00' * BYTES_PER_CHUNK
def SSZType(fields): def SSZType(fields):
class SSZObject(): class SSZObject():
def __init__(self, **kwargs): def __init__(self, **kwargs):
for f in fields: for f, t in fields.items():
if f not in kwargs: if f not in kwargs:
raise Exception("Missing constructor argument: %s" % f) setattr(self, f, get_zero_value(t))
else:
setattr(self, f, kwargs[f]) setattr(self, f, kwargs[f])
def __eq__(self, other): def __eq__(self, other):
@ -58,18 +60,40 @@ class Vector():
def is_basic(typ): def is_basic(typ):
return isinstance(typ, str) and (typ[:4] in ('uint', 'bool') or typ == 'byte') # if not a string, it is a complex, and cannot be basic
if not isinstance(typ, str):
return False
# "uintN": N-bit unsigned integer (where N in [8, 16, 32, 64, 128, 256])
elif typ[:4] == 'uint' and typ[4:] in ['8', '16', '32', '64', '128', '256']:
return True
# "bool": True or False
elif typ == 'bool':
return True
# alias: "byte" -> "uint8"
elif typ == 'byte':
return True
# default
else:
return False
def is_constant_sized(typ): def is_constant_sized(typ):
# basic objects are fixed size by definition
if is_basic(typ): if is_basic(typ):
return True return True
# dynamic size array type, "list": [elem_type].
# Not constant size by definition.
elif isinstance(typ, list) and len(typ) == 1: elif isinstance(typ, list) and len(typ) == 1:
return is_constant_sized(typ[0])
elif isinstance(typ, list) and len(typ) == 2:
return False return False
# fixed size array type, "vector": [elem_type, length]
# Constant size, but only if the elements are.
elif isinstance(typ, list) and len(typ) == 2:
return is_constant_sized(typ[0])
# bytes array (fixed or dynamic size)
elif isinstance(typ, str) and typ[:5] == 'bytes': elif isinstance(typ, str) and typ[:5] == 'bytes':
return len(typ) > 5 # if no length suffix, it has a dynamic size
return typ != 'bytes'
# containers are only constant-size if all of the fields are constant size.
elif hasattr(typ, 'fields'): elif hasattr(typ, 'fields'):
for subtype in typ.fields.values(): for subtype in typ.fields.values():
if not is_constant_sized(subtype): if not is_constant_sized(subtype):
@ -90,40 +114,98 @@ def coerce_to_bytes(x):
raise Exception("Expecting bytes") raise Exception("Expecting bytes")
def encode_bytes(value):
serialized_bytes = coerce_to_bytes(value)
assert len(serialized_bytes) < 2 ** (8 * BYTES_PER_LENGTH_PREFIX)
serialized_length = len(serialized_bytes).to_bytes(BYTES_PER_LENGTH_PREFIX, 'little')
return serialized_length + serialized_bytes
def encode_variable_size_container(values, types):
return encode_bytes(encode_fixed_size_container(values, types))
def encode_fixed_size_container(values, types):
return b''.join([serialize_value(v, typ) for (v, typ) in zip(values, types)])
def serialize_value(value, typ=None): def serialize_value(value, typ=None):
if typ is None: if typ is None:
typ = infer_type(value) typ = infer_type(value)
# "uintN"
if isinstance(typ, str) and typ[:4] == 'uint': if isinstance(typ, str) and typ[:4] == 'uint':
length = int(typ[4:]) length = int(typ[4:])
assert length in (8, 16, 32, 64, 128, 256) assert length in (8, 16, 32, 64, 128, 256)
return value.to_bytes(length // 8, 'little') return value.to_bytes(length // 8, 'little')
elif typ == 'bool': # "bool"
elif isinstance(typ, str) and typ == 'bool':
assert value in (True, False) assert value in (True, False)
return b'\x01' if value is True else b'\x00' return b'\x01' if value is True else b'\x00'
elif (isinstance(typ, list) and len(typ) == 1) or typ == 'bytes': # Vector
serialized_bytes = coerce_to_bytes(value) if typ == 'bytes' else b''.join([serialize_value(element, typ[0]) for element in value])
assert len(serialized_bytes) < 2**(8 * BYTES_PER_LENGTH_PREFIX)
serialized_length = len(serialized_bytes).to_bytes(BYTES_PER_LENGTH_PREFIX, 'little')
return serialized_length + serialized_bytes
elif isinstance(typ, list) and len(typ) == 2: elif isinstance(typ, list) and len(typ) == 2:
# (regardless of element type, sanity-check if the length reported in the vector type matches the value length)
assert len(value) == typ[1] assert len(value) == typ[1]
return b''.join([serialize_value(element, typ[0]) for element in value]) # If value is fixed-size (i.e. element type is fixed-size):
if is_constant_sized(typ):
return encode_fixed_size_container(value, [typ[0]] * len(value))
# If value is variable-size (i.e. element type is variable-size)
else:
return encode_variable_size_container(value, [typ[0]] * len(value))
# "bytes" (variable size)
elif isinstance(typ, str) and typ == 'bytes':
return encode_bytes(value)
# List
elif isinstance(typ, list) and len(typ) == 1:
return encode_variable_size_container(value, [typ[0]] * len(value))
# "bytesN" (fixed size)
elif isinstance(typ, str) and len(typ) > 5 and typ[:5] == 'bytes': elif isinstance(typ, str) and len(typ) > 5 and typ[:5] == 'bytes':
assert len(value) == int(typ[5:]), (value, int(typ[5:])) assert len(value) == int(typ[5:]), (value, int(typ[5:]))
return coerce_to_bytes(value) return coerce_to_bytes(value)
# containers
elif hasattr(typ, 'fields'): elif hasattr(typ, 'fields'):
serialized_bytes = b''.join([serialize_value(getattr(value, field), subtype) for field, subtype in typ.fields.items()]) values = [getattr(value, field) for field in typ.fields.keys()]
types = list(typ.fields.values())
if is_constant_sized(typ): if is_constant_sized(typ):
return serialized_bytes return encode_fixed_size_container(values, types)
else: else:
assert len(serialized_bytes) < 2**(8 * BYTES_PER_LENGTH_PREFIX) return encode_variable_size_container(values, types)
serialized_length = len(serialized_bytes).to_bytes(BYTES_PER_LENGTH_PREFIX, 'little')
return serialized_length + serialized_bytes
else: else:
print(value, typ) print(value, typ)
raise Exception("Type not recognized") raise Exception("Type not recognized")
def get_zero_value(typ: Any) -> Any:
if isinstance(typ, str):
# Bytes array
if typ == 'bytes':
return b''
# bytesN
elif typ[:5] == 'bytes' and len(typ) > 5:
length = int(typ[5:])
return b'\x00' * length
# Basic types
elif typ == 'bool':
return False
elif typ[:4] == 'uint':
return 0
elif typ == 'byte':
return 0x00
else:
raise ValueError("Type not recognized")
# Vector:
elif isinstance(typ, list) and len(typ) == 2:
return [get_zero_value(typ[0]) for _ in range(typ[1])]
# List:
elif isinstance(typ, list) and len(typ) == 1:
return []
# Container:
elif hasattr(typ, 'fields'):
return typ(**{field: get_zero_value(subtype) for field, subtype in typ.fields.items()})
else:
print(typ)
raise Exception("Type not recognized")
def chunkify(bytez): def chunkify(bytez):
bytez += b'\x00' * (-len(bytez) % BYTES_PER_CHUNK) bytez += b'\x00' * (-len(bytez) % BYTES_PER_CHUNK)
return [bytez[i:i + 32] for i in range(0, len(bytez), 32)] return [bytez[i:i + 32] for i in range(0, len(bytez), 32)]
@ -152,12 +234,27 @@ def mix_in_length(root, length):
def infer_type(value): def infer_type(value):
"""
Note: defaults to uint64 for integer type inference due to lack of information.
Other integer sizes are still supported, see spec.
:param value: The value to infer a SSZ type for.
:return: The SSZ type.
"""
if hasattr(value.__class__, 'fields'): if hasattr(value.__class__, 'fields'):
return value.__class__ return value.__class__
elif isinstance(value, Vector): elif isinstance(value, Vector):
return [infer_type(value[0]) if len(value) > 0 else 'uint64', len(value)] if len(value) > 0:
return [infer_type(value[0]), len(value)]
else:
# Element type does not matter too much,
# assumed to be a basic type for size-encoding purposes, vector is empty.
return ['uint64']
elif isinstance(value, list): elif isinstance(value, list):
return [infer_type(value[0])] if len(value) > 0 else ['uint64'] if len(value) > 0:
return [infer_type(value[0])]
else:
# Element type does not matter, list-content size will be encoded regardless, list is empty.
return ['uint64']
elif isinstance(value, (bytes, str)): elif isinstance(value, (bytes, str)):
return 'bytes' return 'bytes'
elif isinstance(value, int): elif isinstance(value, int):
@ -169,24 +266,41 @@ def infer_type(value):
def hash_tree_root(value, typ=None): def hash_tree_root(value, typ=None):
if typ is None: if typ is None:
typ = infer_type(value) typ = infer_type(value)
# -------------------------------------
# merkleize(pack(value))
# basic object: merkleize packed version (merkleization pads it to 32 bytes if it is not already)
if is_basic(typ): if is_basic(typ):
return merkleize(pack([value], typ)) return merkleize(pack([value], typ))
elif isinstance(typ, list) and len(typ) == 1 and is_basic(typ[0]): # or a vector of basic objects
return mix_in_length(merkleize(pack(value, typ[0])), len(value))
elif isinstance(typ, list) and len(typ) == 1 and not is_basic(typ[0]):
return mix_in_length(merkleize([hash_tree_root(element, typ[0]) for element in value]), len(value))
elif isinstance(typ, list) and len(typ) == 2 and is_basic(typ[0]): elif isinstance(typ, list) and len(typ) == 2 and is_basic(typ[0]):
assert len(value) == typ[1] assert len(value) == typ[1]
return merkleize(pack(value, typ[0])) return merkleize(pack(value, typ[0]))
# -------------------------------------
# mix_in_length(merkleize(pack(value)), len(value))
# if value is a list of basic objects
elif isinstance(typ, list) and len(typ) == 1 and is_basic(typ[0]):
return mix_in_length(merkleize(pack(value, typ[0])), len(value))
# (needs some extra work for non-fixed-sized bytes array)
elif typ == 'bytes': elif typ == 'bytes':
return mix_in_length(merkleize(chunkify(coerce_to_bytes(value))), len(value)) return mix_in_length(merkleize(chunkify(coerce_to_bytes(value))), len(value))
# -------------------------------------
# merkleize([hash_tree_root(element) for element in value])
# if value is a vector of composite objects
elif isinstance(typ, list) and len(typ) == 2 and not is_basic(typ[0]):
return merkleize([hash_tree_root(element, typ[0]) for element in value])
# (needs some extra work for fixed-sized bytes array)
elif isinstance(typ, str) and typ[:5] == 'bytes' and len(typ) > 5: elif isinstance(typ, str) and typ[:5] == 'bytes' and len(typ) > 5:
assert len(value) == int(typ[5:]) assert len(value) == int(typ[5:])
return merkleize(chunkify(coerce_to_bytes(value))) return merkleize(chunkify(coerce_to_bytes(value)))
elif isinstance(typ, list) and len(typ) == 2 and not is_basic(typ[0]): # or a container
return merkleize([hash_tree_root(element, typ[0]) for element in value])
elif hasattr(typ, 'fields'): elif hasattr(typ, 'fields'):
return merkleize([hash_tree_root(getattr(value, field), subtype) for field, subtype in typ.fields.items()]) return merkleize([hash_tree_root(getattr(value, field), subtype) for field, subtype in typ.fields.items()])
# -------------------------------------
# mix_in_length(merkleize([hash_tree_root(element) for element in value]), len(value))
# if value is a list of composite objects
elif isinstance(typ, list) and len(typ) == 1 and not is_basic(typ[0]):
return mix_in_length(merkleize([hash_tree_root(element, typ[0]) for element in value]), len(value))
# -------------------------------------
else: else:
raise Exception("Type not recognized") raise Exception("Type not recognized")

View File

@ -14,6 +14,8 @@ from eth2spec.phase0.spec import (
from tests.helpers import ( from tests.helpers import (
build_empty_block_for_next_slot, build_empty_block_for_next_slot,
get_valid_attestation, 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): def test_bad_previous_crosslink(state):
next_epoch(state)
attestation = get_valid_attestation(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) pre_state, post_state = run_attestation_processing(state, attestation, False)

View File

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

View File

@ -27,7 +27,8 @@ def overwrite_spec_config(config):
if field == "LATEST_RANDAO_MIXES_LENGTH": if field == "LATEST_RANDAO_MIXES_LENGTH":
spec.BeaconState.fields['latest_randao_mixes'][1] = config[field] spec.BeaconState.fields['latest_randao_mixes'][1] = config[field]
elif field == "SHARD_COUNT": 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": elif field == "SLOTS_PER_HISTORICAL_ROOT":
spec.BeaconState.fields['latest_block_roots'][1] = config[field] spec.BeaconState.fields['latest_block_roots'][1] = config[field]
spec.BeaconState.fields['latest_state_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 # functions
convert_to_indexed, convert_to_indexed,
get_active_validator_indices, get_active_validator_indices,
get_attestation_participants, get_attesting_indices,
get_block_root, get_block_root,
get_crosslink_committee_for_attestation, get_crosslink_committees_at_slot,
get_current_epoch, get_current_epoch,
get_domain, get_domain,
get_empty_block, get_empty_block,
@ -35,6 +35,7 @@ from eth2spec.phase0.spec import (
get_genesis_beacon_state, get_genesis_beacon_state,
get_previous_epoch, get_previous_epoch,
get_shard_delta, get_shard_delta,
hash_tree_root,
slot_to_epoch, slot_to_epoch,
verify_merkle_branch, verify_merkle_branch,
hash, hash,
@ -154,6 +155,7 @@ def build_attestation_data(state, slot, shard):
current_epoch_start_slot = get_epoch_start_slot(get_current_epoch(state)) current_epoch_start_slot = get_epoch_start_slot(get_current_epoch(state))
if slot < current_epoch_start_slot: if slot < current_epoch_start_slot:
print(slot)
epoch_boundary_root = get_block_root(state, get_epoch_start_slot(get_previous_epoch(state))) epoch_boundary_root = get_block_root(state, get_epoch_start_slot(get_previous_epoch(state)))
elif slot == current_epoch_start_slot: elif slot == current_epoch_start_slot:
epoch_boundary_root = block_root epoch_boundary_root = block_root
@ -167,6 +169,7 @@ def build_attestation_data(state, slot, shard):
justified_epoch = state.current_justified_epoch justified_epoch = state.current_justified_epoch
justified_block_root = state.current_justified_root 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( return AttestationData(
slot=slot, slot=slot,
shard=shard, shard=shard,
@ -175,7 +178,7 @@ def build_attestation_data(state, slot, shard):
source_root=justified_block_root, source_root=justified_block_root,
target_root=epoch_boundary_root, target_root=epoch_boundary_root,
crosslink_data_root=spec.ZERO_HASH, 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): def get_valid_attestation(state, slot=None):
if slot is None: if slot is None:
slot = state.slot slot = state.slot
@ -297,7 +308,7 @@ def get_valid_attestation(state, slot=None):
custody_bitfield=custody_bitfield, custody_bitfield=custody_bitfield,
aggregate_signature=EMPTY_SIGNATURE, aggregate_signature=EMPTY_SIGNATURE,
) )
participants = get_attestation_participants( participants = get_attesting_indices(
state, state,
attestation.data, attestation.data,
attestation.aggregation_bitfield, attestation.aggregation_bitfield,
@ -342,6 +353,13 @@ def fill_aggregate_attestation(state, attestation):
attestation.aggregation_bitfield = set_bitfield_bit(attestation.aggregation_bitfield, i) 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): def next_slot(state):
block = build_empty_block_for_next_slot(state) block = build_empty_block_for_next_slot(state)
state_transition(state, block) state_transition(state, block)

View File

@ -25,6 +25,7 @@ from eth2spec.phase0.spec import (
advance_slot, advance_slot,
cache_state, cache_state,
set_balance, set_balance,
slot_to_epoch,
verify_merkle_branch, verify_merkle_branch,
hash, hash,
) )
@ -382,33 +383,6 @@ def test_voluntary_exit(state):
return pre_state, [initiate_exit_block, exit_block], post_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): def test_transfer(state):
pre_state = deepcopy(state) pre_state = deepcopy(state)
current_epoch = get_current_epoch(pre_state) current_epoch = get_current_epoch(pre_state)