Merge remote-tracking branch 'origin/dev' into dankrad-patch-8
# Conflicts: # specs/core/0_beacon-chain.md
This commit is contained in:
commit
2622548ba7
|
@ -71,7 +71,7 @@ We require:
|
|||
G2_cofactor = 305502333931268344200999753193121504214466019254188142667664032982267604182971884026507427359259977847832272839041616661285803823378372096355777062779109
|
||||
q = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787
|
||||
|
||||
def hash_to_G2(message_hash: Bytes32, domain: uint64) -> [uint384]:
|
||||
def hash_to_G2(message_hash: Bytes32, domain: uint64) -> Tuple[uint384, uint384]:
|
||||
# Initial candidate x coordinate
|
||||
x_re = int.from_bytes(hash(message_hash + bytes8(domain) + b'\x01'), 'big')
|
||||
x_im = int.from_bytes(hash(message_hash + bytes8(domain) + b'\x02'), 'big')
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
- [Containers](#containers)
|
||||
- [Misc dependencies](#misc-dependencies)
|
||||
- [`Fork`](#fork)
|
||||
- [`Checkpoint`](#checkpoint)
|
||||
- [`Validator`](#validator)
|
||||
- [`Crosslink`](#crosslink)
|
||||
- [`AttestationData`](#attestationdata)
|
||||
|
@ -289,6 +290,14 @@ class Fork(Container):
|
|||
epoch: Epoch # Epoch of latest fork
|
||||
```
|
||||
|
||||
#### `Checkpoint`
|
||||
|
||||
```python
|
||||
class Checkpoint(Container):
|
||||
epoch: Epoch
|
||||
root: Hash
|
||||
```
|
||||
|
||||
#### `Validator`
|
||||
|
||||
```python
|
||||
|
@ -323,10 +332,8 @@ class AttestationData(Container):
|
|||
# LMD GHOST vote
|
||||
beacon_block_root: Hash
|
||||
# FFG vote
|
||||
source_epoch: Epoch
|
||||
source_root: Hash
|
||||
target_epoch: Epoch
|
||||
target_root: Hash
|
||||
source: Checkpoint
|
||||
target: Checkpoint
|
||||
# Crosslink vote
|
||||
crosslink: Crosslink
|
||||
```
|
||||
|
@ -519,15 +526,11 @@ class BeaconState(Container):
|
|||
# Crosslinks
|
||||
previous_crosslinks: Vector[Crosslink, SHARD_COUNT] # Previous epoch snapshot
|
||||
current_crosslinks: Vector[Crosslink, SHARD_COUNT]
|
||||
# Justification
|
||||
previous_justified_epoch: Epoch # Previous epoch snapshot
|
||||
previous_justified_root: Hash # Previous epoch snapshot
|
||||
current_justified_epoch: Epoch
|
||||
current_justified_root: Hash
|
||||
justification_bitfield: Bitvector[JUSTIFICATION_BITVECTOR_LENGTH] # Bit set for every recent justified epoch
|
||||
# Finality
|
||||
finalized_epoch: Epoch
|
||||
finalized_root: Hash
|
||||
justification_bitfield: Bitvector[JUSTIFICATION_BITVECTOR_LENGTH] # Bit set for every recent justified epoch
|
||||
previous_justified_checkpoint: Checkpoint # Previous epoch snapshot
|
||||
current_justified_checkpoint: Checkpoint
|
||||
finalized_checkpoint: Checkpoint
|
||||
```
|
||||
|
||||
## Helper functions
|
||||
|
@ -701,9 +704,9 @@ def get_epoch_start_shard(state: BeaconState, epoch: Epoch) -> Shard:
|
|||
|
||||
```python
|
||||
def get_attestation_data_slot(state: BeaconState, data: AttestationData) -> Slot:
|
||||
committee_count = get_epoch_committee_count(state, data.target_epoch)
|
||||
offset = (data.crosslink.shard + SHARD_COUNT - get_epoch_start_shard(state, data.target_epoch)) % SHARD_COUNT
|
||||
return Slot(get_epoch_start_slot(data.target_epoch) + offset // (committee_count // SLOTS_PER_EPOCH))
|
||||
committee_count = get_epoch_committee_count(state, data.target.epoch)
|
||||
offset = (data.crosslink.shard + SHARD_COUNT - get_epoch_start_shard(state, data.target.epoch)) % SHARD_COUNT
|
||||
return Slot(get_epoch_start_slot(data.target.epoch) + offset // (committee_count // SLOTS_PER_EPOCH))
|
||||
```
|
||||
|
||||
### `get_block_root_at_slot`
|
||||
|
@ -863,12 +866,12 @@ def get_crosslink_committee(state: BeaconState, epoch: Epoch, shard: Shard) -> S
|
|||
|
||||
```python
|
||||
def get_attesting_indices(state: BeaconState,
|
||||
attestation_data: AttestationData,
|
||||
data: AttestationData,
|
||||
bitfield: Bitlist[MAX_INDICES_PER_ATTESTATION]) -> Sequence[ValidatorIndex]:
|
||||
"""
|
||||
Return the sorted attesting indices corresponding to ``attestation_data`` and ``bitfield``.
|
||||
Return the sorted attesting indices corresponding to ``data`` and ``bitfield``.
|
||||
"""
|
||||
committee = get_crosslink_committee(state, attestation_data.target_epoch, attestation_data.crosslink.shard)
|
||||
committee = get_crosslink_committee(state, data.target.epoch, data.crosslink.shard)
|
||||
return sorted([index for i, index in enumerate(committee) if bitfield[i]])
|
||||
```
|
||||
|
||||
|
@ -959,7 +962,7 @@ def validate_indexed_attestation(state: BeaconState, indexed_attestation: Indexe
|
|||
hash_tree_root(AttestationDataAndCustodyBit(data=indexed_attestation.data, custody_bit=0b1)),
|
||||
],
|
||||
signature=indexed_attestation.signature,
|
||||
domain=get_domain(state, DOMAIN_ATTESTATION, indexed_attestation.data.target_epoch),
|
||||
domain=get_domain(state, DOMAIN_ATTESTATION, indexed_attestation.data.target.epoch),
|
||||
)
|
||||
```
|
||||
|
||||
|
@ -972,9 +975,9 @@ def is_slashable_attestation_data(data_1: AttestationData, data_2: AttestationDa
|
|||
"""
|
||||
return (
|
||||
# Double vote
|
||||
(data_1 != data_2 and data_1.target_epoch == data_2.target_epoch) or
|
||||
(data_1 != data_2 and data_1.target.epoch == data_2.target.epoch) or
|
||||
# Surround vote
|
||||
(data_1.source_epoch < data_2.source_epoch and data_2.target_epoch < data_1.target_epoch)
|
||||
(data_1.source.epoch < data_2.source.epoch and data_2.target.epoch < data_1.target.epoch)
|
||||
)
|
||||
```
|
||||
|
||||
|
@ -1226,7 +1229,7 @@ def get_total_active_balance(state: BeaconState) -> Gwei:
|
|||
|
||||
```python
|
||||
def get_matching_source_attestations(state: BeaconState, epoch: Epoch) -> Sequence[PendingAttestation]:
|
||||
assert epoch in (get_current_epoch(state), get_previous_epoch(state))
|
||||
assert epoch in (get_previous_epoch(state), get_current_epoch(state))
|
||||
return state.current_epoch_attestations if epoch == get_current_epoch(state) else state.previous_epoch_attestations
|
||||
```
|
||||
|
||||
|
@ -1234,7 +1237,7 @@ def get_matching_source_attestations(state: BeaconState, epoch: Epoch) -> Sequen
|
|||
def get_matching_target_attestations(state: BeaconState, epoch: Epoch) -> Sequence[PendingAttestation]:
|
||||
return [
|
||||
a for a in get_matching_source_attestations(state, epoch)
|
||||
if a.data.target_root == get_block_root(state, epoch)
|
||||
if a.data.target.root == get_block_root(state, epoch)
|
||||
]
|
||||
```
|
||||
|
||||
|
@ -1286,12 +1289,11 @@ def process_justification_and_finalization(state: BeaconState) -> None:
|
|||
|
||||
previous_epoch = get_previous_epoch(state)
|
||||
current_epoch = get_current_epoch(state)
|
||||
old_previous_justified_epoch = state.previous_justified_epoch
|
||||
old_current_justified_epoch = state.current_justified_epoch
|
||||
old_previous_justified_checkpoint = state.previous_justified_checkpoint
|
||||
old_current_justified_checkpoint = state.current_justified_checkpoint
|
||||
|
||||
# Process justifications
|
||||
state.previous_justified_epoch = state.current_justified_epoch
|
||||
state.previous_justified_root = state.current_justified_root
|
||||
state.previous_justified_checkpoint = state.current_justified_checkpoint
|
||||
state.justification_bitfield[1:JUSTIFICATION_BITVECTOR_LENGTH] = \
|
||||
state.justification_bitfield[0:JUSTIFICATION_BITVECTOR_LENGTH - 1]
|
||||
state.justification_bitfield[0] = 0b0
|
||||
|
@ -1299,35 +1301,30 @@ def process_justification_and_finalization(state: BeaconState) -> None:
|
|||
state, get_matching_target_attestations(state, previous_epoch)
|
||||
)
|
||||
if previous_epoch_matching_target_balance * 3 >= get_total_active_balance(state) * 2:
|
||||
state.current_justified_epoch = previous_epoch
|
||||
state.current_justified_root = get_block_root(state, state.current_justified_epoch)
|
||||
state.current_justified_checkpoint = Checkpoint(epoch=previous_epoch,
|
||||
root=get_block_root(state, previous_epoch))
|
||||
state.justification_bitfield[1] = 0b1
|
||||
current_epoch_matching_target_balance = get_attesting_balance(
|
||||
state, get_matching_target_attestations(state, current_epoch)
|
||||
)
|
||||
if current_epoch_matching_target_balance * 3 >= get_total_active_balance(state) * 2:
|
||||
state.current_justified_epoch = current_epoch
|
||||
state.current_justified_root = get_block_root(state, state.current_justified_epoch)
|
||||
state.current_justified_checkpoint = Checkpoint(epoch=current_epoch, root=get_block_root(state, current_epoch))
|
||||
state.justification_bitfield[0] = 0b1
|
||||
|
||||
# Process finalizations
|
||||
bitfield = state.justification_bitfield
|
||||
# The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th as source
|
||||
if all(bitfield[1:4]) and old_previous_justified_epoch + 3 == current_epoch:
|
||||
state.finalized_epoch = old_previous_justified_epoch
|
||||
state.finalized_root = get_block_root(state, state.finalized_epoch)
|
||||
if all(bitfield[1:4]) and old_previous_justified_checkpoint.epoch + 3 == current_epoch:
|
||||
state.finalized_checkpoint = old_previous_justified_checkpoint
|
||||
# The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as source
|
||||
if all(bitfield[1:3]) and old_previous_justified_epoch + 2 == current_epoch:
|
||||
state.finalized_epoch = old_previous_justified_epoch
|
||||
state.finalized_root = get_block_root(state, state.finalized_epoch)
|
||||
if all(bitfield[1:3]) and old_previous_justified_checkpoint.epoch + 2 == current_epoch:
|
||||
state.finalized_checkpoint = old_previous_justified_checkpoint
|
||||
# The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as source
|
||||
if all(bitfield[0:3]) and old_current_justified_epoch + 2 == current_epoch:
|
||||
state.finalized_epoch = old_current_justified_epoch
|
||||
state.finalized_root = get_block_root(state, state.finalized_epoch)
|
||||
if all(bitfield[0:3]) and old_current_justified_checkpoint.epoch + 2 == current_epoch:
|
||||
state.finalized_checkpoint = old_current_justified_checkpoint
|
||||
# The 1st/2nd most recent epochs are justified, the 1st using the 2nd as source
|
||||
if all(bitfield[0:2]) and old_current_justified_epoch + 1 == current_epoch:
|
||||
state.finalized_epoch = old_current_justified_epoch
|
||||
state.finalized_root = get_block_root(state, state.finalized_epoch)
|
||||
if all(bitfield[0:2]) and old_current_justified_checkpoint.epoch + 1 == current_epoch:
|
||||
state.finalized_checkpoint = old_current_justified_checkpoint
|
||||
```
|
||||
|
||||
#### Crosslinks
|
||||
|
@ -1390,7 +1387,7 @@ def get_attestation_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence
|
|||
rewards[index] += Gwei(max_attester_reward * MIN_ATTESTATION_INCLUSION_DELAY // attestation.inclusion_delay)
|
||||
|
||||
# Inactivity penalty
|
||||
finality_delay = previous_epoch - state.finalized_epoch
|
||||
finality_delay = previous_epoch - state.finalized_checkpoint.epoch
|
||||
if finality_delay > MIN_EPOCHS_TO_INACTIVITY_PENALTY:
|
||||
matching_target_attesting_indices = get_unslashed_attesting_indices(state, matching_target_attestations)
|
||||
for index in eligible_validator_indices:
|
||||
|
@ -1455,7 +1452,7 @@ def process_registry_updates(state: BeaconState) -> None:
|
|||
activation_queue = sorted([
|
||||
index for index, validator in enumerate(state.validators) if
|
||||
validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and
|
||||
validator.activation_epoch >= get_delayed_activation_exit_epoch(state.finalized_epoch)
|
||||
validator.activation_epoch >= get_delayed_activation_exit_epoch(state.finalized_checkpoint.epoch)
|
||||
], key=lambda index: state.validators[index].activation_eligibility_epoch)
|
||||
# Dequeued validators for activation up to churn limit (without resetting activation epoch)
|
||||
for index in activation_queue[:get_churn_limit(state)]:
|
||||
|
@ -1656,9 +1653,8 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
|||
Process ``Attestation`` operation.
|
||||
"""
|
||||
data = attestation.data
|
||||
|
||||
assert data.crosslink.shard < SHARD_COUNT
|
||||
assert data.target_epoch in (get_previous_epoch(state), get_current_epoch(state))
|
||||
assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state))
|
||||
|
||||
attestation_slot = get_attestation_data_slot(state, data)
|
||||
assert attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= attestation_slot + SLOTS_PER_EPOCH
|
||||
|
@ -1670,21 +1666,22 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
|||
proposer_index=get_beacon_proposer_index(state),
|
||||
)
|
||||
|
||||
if data.target_epoch == get_current_epoch(state):
|
||||
ffg_data = (state.current_justified_epoch, state.current_justified_root, get_current_epoch(state))
|
||||
if data.target.epoch == get_current_epoch(state):
|
||||
assert data.source == state.current_justified_checkpoint
|
||||
parent_crosslink = state.current_crosslinks[data.crosslink.shard]
|
||||
state.current_epoch_attestations.append(pending_attestation)
|
||||
else:
|
||||
ffg_data = (state.previous_justified_epoch, state.previous_justified_root, get_previous_epoch(state))
|
||||
assert data.source == state.previous_justified_checkpoint
|
||||
parent_crosslink = state.previous_crosslinks[data.crosslink.shard]
|
||||
state.previous_epoch_attestations.append(pending_attestation)
|
||||
|
||||
# Check FFG data, crosslink data, and signature
|
||||
assert ffg_data == (data.source_epoch, data.source_root, data.target_epoch)
|
||||
assert data.crosslink.start_epoch == parent_crosslink.end_epoch
|
||||
assert data.crosslink.end_epoch == min(data.target_epoch, parent_crosslink.end_epoch + MAX_EPOCHS_PER_CROSSLINK)
|
||||
# Check crosslink against expected parent crosslink
|
||||
assert data.crosslink.parent_root == hash_tree_root(parent_crosslink)
|
||||
assert data.crosslink.start_epoch == parent_crosslink.end_epoch
|
||||
assert data.crosslink.end_epoch == min(data.target.epoch, parent_crosslink.end_epoch + MAX_EPOCHS_PER_CROSSLINK)
|
||||
assert data.crosslink.data_root == ZERO_HASH # [to be removed in phase 1]
|
||||
|
||||
# Check signature
|
||||
validate_indexed_attestation(state, convert_to_indexed(state, attestation))
|
||||
```
|
||||
|
||||
|
@ -1767,8 +1764,8 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None:
|
|||
"""
|
||||
Process ``Transfer`` operation.
|
||||
"""
|
||||
# Verify the amount and fee are not individually too big (for anti-overflow purposes)
|
||||
assert state.balances[transfer.sender] >= max(transfer.amount, transfer.fee)
|
||||
# Verify the balance the covers amount and fee (with overflow protection)
|
||||
assert state.balances[transfer.sender] >= max(transfer.amount + transfer.fee, transfer.amount, transfer.fee)
|
||||
# A transfer is valid in only one slot
|
||||
assert state.slot == transfer.slot
|
||||
# Sender must satisfy at least one of the following conditions in the parenthesis:
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
- [Time parameters](#time-parameters)
|
||||
- [Fork choice](#fork-choice)
|
||||
- [Helpers](#helpers)
|
||||
- [`Target`](#target)
|
||||
- [`Checkpoint`](#checkpoint)
|
||||
- [`Store`](#store)
|
||||
- [`get_genesis_store`](#get_genesis_store)
|
||||
- [`get_ancestor`](#get_ancestor)
|
||||
|
@ -55,11 +55,11 @@ The head block root associated with a `store` is defined as `get_head(store)`. A
|
|||
|
||||
### Helpers
|
||||
|
||||
#### `Target`
|
||||
#### `LatestMessage`
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class Target(object):
|
||||
@dataclass(eq=True, frozen=True)
|
||||
class LatestMessage(object):
|
||||
epoch: Epoch
|
||||
root: Hash
|
||||
```
|
||||
|
@ -69,12 +69,13 @@ class Target(object):
|
|||
```python
|
||||
@dataclass
|
||||
class Store(object):
|
||||
time: int
|
||||
justified_checkpoint: Checkpoint
|
||||
finalized_checkpoint: Checkpoint
|
||||
blocks: Dict[Hash, BeaconBlock] = field(default_factory=dict)
|
||||
states: Dict[Hash, BeaconState] = field(default_factory=dict)
|
||||
time: int = 0
|
||||
latest_targets: Dict[ValidatorIndex, Target] = field(default_factory=dict)
|
||||
justified_root: Hash = ZERO_HASH
|
||||
finalized_root: Hash = ZERO_HASH
|
||||
block_states: Dict[Hash, BeaconState] = field(default_factory=dict)
|
||||
checkpoint_states: Dict[Checkpoint, BeaconState] = field(default_factory=dict)
|
||||
latest_messages: Dict[ValidatorIndex, LatestMessage] = field(default_factory=dict)
|
||||
```
|
||||
|
||||
#### `get_genesis_store`
|
||||
|
@ -83,12 +84,15 @@ class Store(object):
|
|||
def get_genesis_store(genesis_state: BeaconState) -> Store:
|
||||
genesis_block = BeaconBlock(state_root=hash_tree_root(genesis_state))
|
||||
root = signing_root(genesis_block)
|
||||
justified_checkpoint = Checkpoint(epoch=GENESIS_EPOCH, root=root)
|
||||
finalized_checkpoint = Checkpoint(epoch=GENESIS_EPOCH, root=root)
|
||||
return Store(
|
||||
blocks={root: genesis_block},
|
||||
states={root: genesis_state},
|
||||
time=genesis_state.genesis_time,
|
||||
justified_root=root,
|
||||
finalized_root=root,
|
||||
justified_checkpoint=justified_checkpoint,
|
||||
finalized_checkpoint=finalized_checkpoint,
|
||||
blocks={root: genesis_block},
|
||||
block_states={root: genesis_state.copy()},
|
||||
checkpoint_states={justified_checkpoint: genesis_state.copy()},
|
||||
)
|
||||
```
|
||||
|
||||
|
@ -105,11 +109,12 @@ def get_ancestor(store: Store, root: Hash, slot: Slot) -> Hash:
|
|||
|
||||
```python
|
||||
def get_latest_attesting_balance(store: Store, root: Hash) -> Gwei:
|
||||
state = store.states[store.justified_root]
|
||||
active_indices = get_active_validator_indices(state.validator_registry, get_current_epoch(state))
|
||||
state = store.checkpoint_states[store.justified_checkpoint]
|
||||
active_indices = get_active_validator_indices(state, get_current_epoch(state))
|
||||
return Gwei(sum(
|
||||
state.validator_registry[i].effective_balance for i in active_indices
|
||||
if get_ancestor(store, store.latest_targets[i].root, store.blocks[root].slot) == root
|
||||
state.validators[i].effective_balance for i in active_indices
|
||||
if (i in store.latest_messages and
|
||||
get_ancestor(store, store.latest_messages[i].root, store.blocks[root].slot) == root)
|
||||
))
|
||||
```
|
||||
|
||||
|
@ -118,9 +123,13 @@ def get_latest_attesting_balance(store: Store, root: Hash) -> Gwei:
|
|||
```python
|
||||
def get_head(store: Store) -> Hash:
|
||||
# Execute the LMD-GHOST fork choice
|
||||
head = store.justified_root
|
||||
head = store.justified_checkpoint.root
|
||||
justified_slot = get_epoch_start_slot(store.justified_checkpoint.epoch)
|
||||
while True:
|
||||
children = [root for root in store.blocks.keys() if store.blocks[root].parent_root == head]
|
||||
children = [
|
||||
root for root in store.blocks.keys()
|
||||
if store.blocks[root].parent_root == head and store.blocks[root].slot > justified_slot
|
||||
]
|
||||
if len(children) == 0:
|
||||
return head
|
||||
# Sort by latest attesting balance with ties broken lexicographically
|
||||
|
@ -141,35 +150,65 @@ def on_tick(store: Store, time: int) -> None:
|
|||
```python
|
||||
def on_block(store: Store, block: BeaconBlock) -> None:
|
||||
# Make a copy of the state to avoid mutability issues
|
||||
pre_state = store.states[block.parent_root].copy()
|
||||
assert block.parent_root in store.block_states
|
||||
pre_state = store.block_states[block.parent_root].copy()
|
||||
# Blocks cannot be in the future. If they are, their consideration must be delayed until the are in the past.
|
||||
assert store.time >= pre_state.genesis_time + block.slot * SECONDS_PER_SLOT
|
||||
# Add new block to the store
|
||||
store.blocks[signing_root(block)] = block
|
||||
# Check block is a descendant of the finalized block
|
||||
assert get_ancestor(store, signing_root(block), store.blocks[store.finalized_root].slot) == store.finalized_root
|
||||
assert (
|
||||
get_ancestor(store, signing_root(block), store.blocks[store.finalized_checkpoint.root].slot) ==
|
||||
store.finalized_checkpoint.root
|
||||
)
|
||||
# Check that block is later than the finalized epoch slot
|
||||
assert block.slot > get_epoch_start_slot(store.finalized_checkpoint.epoch)
|
||||
# Check the block is valid and compute the post-state
|
||||
state = state_transition(pre_state, block)
|
||||
# Add new state to the store
|
||||
store.states[signing_root(block)] = state
|
||||
# Update justified block root
|
||||
if state.current_justified_epoch > slot_to_epoch(store.blocks[store.justified_root].slot):
|
||||
store.justified_root = state.current_justified_root
|
||||
elif state.previous_justified_epoch > slot_to_epoch(store.blocks[store.justified_root].slot):
|
||||
store.justified_root = state.previous_justified_root
|
||||
# Update finalized block root
|
||||
if state.finalized_epoch > slot_to_epoch(store.blocks[store.finalized_root].slot):
|
||||
store.finalized_root = state.finalized_root
|
||||
# Add new state for this block to the store
|
||||
store.block_states[signing_root(block)] = state
|
||||
|
||||
# Update justified checkpoint
|
||||
if state.current_justified_checkpoint.epoch > store.justified_checkpoint.epoch:
|
||||
store.justified_checkpoint = state.current_justified_checkpoint
|
||||
elif state.previous_justified_checkpoint.epoch > store.justified_checkpoint.epoch:
|
||||
store.justified_checkpoint = state.previous_justified_checkpoint
|
||||
|
||||
# Update finalized checkpoint
|
||||
if state.finalized_checkpoint.epoch > store.finalized_checkpoint.epoch:
|
||||
store.finalized_checkpoint = state.finalized_checkpoint
|
||||
```
|
||||
|
||||
#### `on_attestation`
|
||||
|
||||
```python
|
||||
def on_attestation(store: Store, attestation: Attestation) -> None:
|
||||
state = store.states[get_head(store)]
|
||||
indexed_attestation = convert_to_indexed(state, attestation)
|
||||
validate_indexed_attestation(state, indexed_attestation)
|
||||
target = attestation.data.target
|
||||
|
||||
# Cannot calculate the current shuffling if have not seen the target
|
||||
assert target.root in store.blocks
|
||||
|
||||
# Attestations cannot be from future epochs. If they are, delay consideration until the epoch arrivesr
|
||||
base_state = store.block_states[target.root].copy()
|
||||
assert store.time >= base_state.genesis_time + get_epoch_start_slot(target.epoch) * SECONDS_PER_SLOT
|
||||
|
||||
# Store target checkpoint state if not yet seen
|
||||
if target not in store.checkpoint_states:
|
||||
process_slots(base_state, get_epoch_start_slot(target.epoch))
|
||||
store.checkpoint_states[target] = base_state
|
||||
target_state = store.checkpoint_states[target]
|
||||
|
||||
# Attestations can only affect the fork choice of subsequent slots.
|
||||
# Delay consideration in the fork choice until their slot is in the past.
|
||||
attestation_slot = get_attestation_data_slot(target_state, attestation.data)
|
||||
assert store.time >= (attestation_slot + 1) * SECONDS_PER_SLOT
|
||||
|
||||
# Get state at the `target` to validate attestation and calculate the committees
|
||||
indexed_attestation = convert_to_indexed(target_state, attestation)
|
||||
validate_indexed_attestation(target_state, indexed_attestation)
|
||||
|
||||
# Update latest messages
|
||||
for i in indexed_attestation.custody_bit_0_indices + indexed_attestation.custody_bit_1_indices:
|
||||
if i not in store.latest_targets or attestation.data.target_epoch > store.latest_targets[i].epoch:
|
||||
store.latest_targets[i] = Target(attestation.data.target_epoch, attestation.data.target_root)
|
||||
if i not in store.latest_messages or target.epoch > store.latest_messages[i].epoch:
|
||||
store.latest_messages[i] = LatestMessage(epoch=target.epoch, root=attestation.data.beacon_block_root)
|
||||
```
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
from eth2spec.test.context import with_all_phases, with_state, bls_switch
|
||||
from eth2spec.test.helpers.attestations import get_valid_attestation
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
|
||||
from eth2spec.test.helpers.state import state_transition_and_sign_block
|
||||
|
||||
|
||||
def add_block_to_store(spec, store, block):
|
||||
pre_state = store.block_states[block.parent_root]
|
||||
block_time = pre_state.genesis_time + block.slot * spec.SECONDS_PER_SLOT
|
||||
|
||||
if store.time < block_time:
|
||||
spec.on_tick(store, block_time)
|
||||
|
||||
spec.on_block(store, block)
|
||||
|
||||
|
||||
def add_attestation_to_store(spec, store, attestation):
|
||||
parent_block = store.blocks[attestation.data.beacon_block_root]
|
||||
pre_state = store.block_states[spec.signing_root(parent_block)]
|
||||
block_time = pre_state.genesis_time + parent_block.slot * spec.SECONDS_PER_SLOT
|
||||
next_epoch_time = block_time + spec.SLOTS_PER_EPOCH * spec.SECONDS_PER_SLOT
|
||||
|
||||
if store.time < next_epoch_time:
|
||||
spec.on_tick(store, next_epoch_time)
|
||||
|
||||
spec.on_attestation(store, attestation)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@with_state
|
||||
@bls_switch
|
||||
def test_genesis(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_genesis_store(state)
|
||||
genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root())
|
||||
assert spec.get_head(store) == spec.signing_root(genesis_block)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@with_state
|
||||
@bls_switch
|
||||
def test_chain_no_attestations(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_genesis_store(state)
|
||||
genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root())
|
||||
assert spec.get_head(store) == spec.signing_root(genesis_block)
|
||||
|
||||
# On receiving a block of `GENESIS_SLOT + 1` slot
|
||||
block_1 = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, block_1)
|
||||
add_block_to_store(spec, store, block_1)
|
||||
|
||||
# On receiving a block of next epoch
|
||||
block_2 = build_empty_block_for_next_slot(spec, state)
|
||||
state_transition_and_sign_block(spec, state, block_2)
|
||||
add_block_to_store(spec, store, block_2)
|
||||
|
||||
assert spec.get_head(store) == spec.signing_root(block_2)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@with_state
|
||||
@bls_switch
|
||||
def test_split_tie_breaker_no_attestations(spec, state):
|
||||
genesis_state = state.copy()
|
||||
|
||||
# Initialization
|
||||
store = spec.get_genesis_store(state)
|
||||
genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root())
|
||||
assert spec.get_head(store) == spec.signing_root(genesis_block)
|
||||
|
||||
# block at slot 1
|
||||
block_1_state = genesis_state.copy()
|
||||
block_1 = build_empty_block_for_next_slot(spec, block_1_state)
|
||||
state_transition_and_sign_block(spec, block_1_state, block_1)
|
||||
add_block_to_store(spec, store, block_1)
|
||||
|
||||
# additional block at slot 1
|
||||
block_2_state = genesis_state.copy()
|
||||
block_2 = build_empty_block_for_next_slot(spec, block_2_state)
|
||||
block_2.body.graffiti = b'\x42' * 32
|
||||
state_transition_and_sign_block(spec, block_2_state, block_2)
|
||||
add_block_to_store(spec, store, block_2)
|
||||
|
||||
highest_root = max(spec.signing_root(block_1), spec.signing_root(block_2))
|
||||
|
||||
assert spec.get_head(store) == highest_root
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@with_state
|
||||
@bls_switch
|
||||
def test_shorter_chain_but_heavier_weight(spec, state):
|
||||
genesis_state = state.copy()
|
||||
|
||||
# Initialization
|
||||
store = spec.get_genesis_store(state)
|
||||
genesis_block = spec.BeaconBlock(state_root=state.hash_tree_root())
|
||||
assert spec.get_head(store) == spec.signing_root(genesis_block)
|
||||
|
||||
# build longer tree
|
||||
long_state = genesis_state.copy()
|
||||
for i in range(3):
|
||||
long_block = build_empty_block_for_next_slot(spec, long_state)
|
||||
state_transition_and_sign_block(spec, long_state, long_block)
|
||||
add_block_to_store(spec, store, long_block)
|
||||
|
||||
# build short tree
|
||||
short_state = genesis_state.copy()
|
||||
short_block = build_empty_block_for_next_slot(spec, short_state)
|
||||
short_block.body.graffiti = b'\x42' * 32
|
||||
state_transition_and_sign_block(spec, short_state, short_block)
|
||||
add_block_to_store(spec, store, short_block)
|
||||
|
||||
short_attestation = get_valid_attestation(spec, short_state, short_block.slot, signed=True)
|
||||
add_attestation_to_store(spec, store, short_attestation)
|
||||
|
||||
assert spec.get_head(store) == spec.signing_root(short_block)
|
|
@ -0,0 +1,122 @@
|
|||
from eth2spec.test.context import with_all_phases, with_state, bls_switch
|
||||
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
|
||||
from eth2spec.test.helpers.attestations import get_valid_attestation
|
||||
from eth2spec.test.helpers.state import next_slot
|
||||
|
||||
|
||||
def run_on_attestation(spec, state, store, attestation, valid=True):
|
||||
if not valid:
|
||||
try:
|
||||
spec.on_attestation(store, attestation)
|
||||
except AssertionError:
|
||||
return
|
||||
else:
|
||||
assert False
|
||||
|
||||
indexed_attestation = spec.convert_to_indexed(state, attestation)
|
||||
spec.on_attestation(store, attestation)
|
||||
assert (
|
||||
store.latest_messages[indexed_attestation.custody_bit_0_indices[0]] ==
|
||||
spec.LatestMessage(
|
||||
epoch=attestation.data.target.epoch,
|
||||
root=attestation.data.beacon_block_root,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@with_state
|
||||
@bls_switch
|
||||
def test_on_attestation(spec, state):
|
||||
store = spec.get_genesis_store(state)
|
||||
time = 100
|
||||
spec.on_tick(store, time)
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state, signed=True)
|
||||
|
||||
# store block in store
|
||||
spec.on_block(store, block)
|
||||
|
||||
next_slot(spec, state)
|
||||
|
||||
attestation = get_valid_attestation(spec, state, slot=block.slot)
|
||||
run_on_attestation(spec, state, store, attestation)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@with_state
|
||||
@bls_switch
|
||||
def test_on_attestation_target_not_in_store(spec, state):
|
||||
store = spec.get_genesis_store(state)
|
||||
time = 100
|
||||
spec.on_tick(store, time)
|
||||
|
||||
# move to next epoch to make block new target
|
||||
state.slot += spec.SLOTS_PER_EPOCH
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state, signed=True)
|
||||
|
||||
# do not add block to store
|
||||
|
||||
next_slot(spec, state)
|
||||
attestation = get_valid_attestation(spec, state, slot=block.slot)
|
||||
run_on_attestation(spec, state, store, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@with_state
|
||||
@bls_switch
|
||||
def test_on_attestation_future_epoch(spec, state):
|
||||
store = spec.get_genesis_store(state)
|
||||
time = 3 * spec.SECONDS_PER_SLOT
|
||||
spec.on_tick(store, time)
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state, signed=True)
|
||||
|
||||
# store block in store
|
||||
spec.on_block(store, block)
|
||||
next_slot(spec, state)
|
||||
|
||||
# move state forward but not store
|
||||
attestation_slot = block.slot + spec.SLOTS_PER_EPOCH
|
||||
state.slot = attestation_slot
|
||||
|
||||
attestation = get_valid_attestation(spec, state, slot=state.slot)
|
||||
run_on_attestation(spec, state, store, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@with_state
|
||||
@bls_switch
|
||||
def test_on_attestation_same_slot(spec, state):
|
||||
store = spec.get_genesis_store(state)
|
||||
time = 1 * spec.SECONDS_PER_SLOT
|
||||
spec.on_tick(store, time)
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state, signed=True)
|
||||
|
||||
spec.on_block(store, block)
|
||||
next_slot(spec, state)
|
||||
|
||||
attestation = get_valid_attestation(spec, state, slot=block.slot)
|
||||
run_on_attestation(spec, state, store, attestation, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@with_state
|
||||
@bls_switch
|
||||
def test_on_attestation_invalid_attestation(spec, state):
|
||||
store = spec.get_genesis_store(state)
|
||||
time = 3 * spec.SECONDS_PER_SLOT
|
||||
spec.on_tick(store, time)
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state, signed=True)
|
||||
|
||||
spec.on_block(store, block)
|
||||
next_slot(spec, state)
|
||||
|
||||
attestation = get_valid_attestation(spec, state, slot=block.slot)
|
||||
# make attestation invalid
|
||||
attestation.custody_bitfield[0:8] = [0, 0, 0, 0, 1, 1, 1, 1]
|
||||
run_on_attestation(spec, state, store, attestation, False)
|
|
@ -0,0 +1,89 @@
|
|||
from eth2spec.utils.ssz.ssz_impl import signing_root
|
||||
|
||||
from eth2spec.test.context import with_all_phases, with_state, bls_switch
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
|
||||
|
||||
|
||||
def run_on_block(spec, state, store, block, valid=True):
|
||||
if not valid:
|
||||
try:
|
||||
spec.on_block(store, block)
|
||||
except AssertionError:
|
||||
return
|
||||
else:
|
||||
assert False
|
||||
|
||||
spec.on_block(store, block)
|
||||
assert store.blocks[signing_root(block)] == block
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@with_state
|
||||
@bls_switch
|
||||
def test_basic(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_genesis_store(state)
|
||||
time = 100
|
||||
spec.on_tick(store, time)
|
||||
assert store.time == time
|
||||
|
||||
# On receiving a block of `GENESIS_SLOT + 1` slot
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
run_on_block(spec, state, store, block)
|
||||
|
||||
# On receiving a block of next epoch
|
||||
store.time = time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.slot += spec.SLOTS_PER_EPOCH
|
||||
|
||||
run_on_block(spec, state, store, block)
|
||||
|
||||
# TODO: add tests for justified_root and finalized_root
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@with_state
|
||||
@bls_switch
|
||||
def test_on_block_future_block(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_genesis_store(state)
|
||||
|
||||
# do not tick time
|
||||
|
||||
# Fail receiving block of `GENESIS_SLOT + 1` slot
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
run_on_block(spec, state, store, block, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@with_state
|
||||
@bls_switch
|
||||
def test_on_block_bad_parent_root(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_genesis_store(state)
|
||||
time = 100
|
||||
spec.on_tick(store, time)
|
||||
|
||||
# Fail receiving block of `GENESIS_SLOT + 1` slot
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.parent_root = b'\x45' * 32
|
||||
run_on_block(spec, state, store, block, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@with_state
|
||||
@bls_switch
|
||||
def test_on_block_before_finalized(spec, state):
|
||||
# Initialization
|
||||
store = spec.get_genesis_store(state)
|
||||
time = 100
|
||||
spec.on_tick(store, time)
|
||||
|
||||
store.finalized_checkpoint = spec.Checkpoint(
|
||||
epoch=store.finalized_checkpoint.epoch + 2,
|
||||
root=store.finalized_checkpoint.root
|
||||
)
|
||||
|
||||
# Fail receiving block of `GENESIS_SLOT + 1` slot
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
run_on_block(spec, state, store, block, False)
|
|
@ -24,11 +24,11 @@ def build_attestation_data(spec, state, slot, shard):
|
|||
epoch_boundary_root = spec.get_block_root(state, spec.get_current_epoch(state))
|
||||
|
||||
if slot < current_epoch_start_slot:
|
||||
justified_epoch = state.previous_justified_epoch
|
||||
justified_block_root = state.previous_justified_root
|
||||
source_epoch = state.previous_justified_checkpoint.epoch
|
||||
source_root = state.previous_justified_checkpoint.root
|
||||
else:
|
||||
justified_epoch = state.current_justified_epoch
|
||||
justified_block_root = state.current_justified_root
|
||||
source_epoch = state.current_justified_checkpoint.epoch
|
||||
source_root = state.current_justified_checkpoint.root
|
||||
|
||||
if spec.slot_to_epoch(slot) == spec.get_current_epoch(state):
|
||||
parent_crosslink = state.current_crosslinks[shard]
|
||||
|
@ -37,10 +37,8 @@ def build_attestation_data(spec, state, slot, shard):
|
|||
|
||||
return spec.AttestationData(
|
||||
beacon_block_root=block_root,
|
||||
source_epoch=justified_epoch,
|
||||
source_root=justified_block_root,
|
||||
target_epoch=spec.slot_to_epoch(slot),
|
||||
target_root=epoch_boundary_root,
|
||||
source=spec.Checkpoint(epoch=source_epoch, root=source_root),
|
||||
target=spec.Checkpoint(epoch=spec.slot_to_epoch(slot), root=epoch_boundary_root),
|
||||
crosslink=spec.Crosslink(
|
||||
shard=shard,
|
||||
start_epoch=parent_crosslink.end_epoch,
|
||||
|
@ -64,8 +62,8 @@ def get_valid_attestation(spec, state, slot=None, signed=False):
|
|||
|
||||
crosslink_committee = spec.get_crosslink_committee(
|
||||
state,
|
||||
attestation_data.target_epoch,
|
||||
attestation_data.crosslink.shard
|
||||
attestation_data.target.epoch,
|
||||
attestation_data.crosslink.shard,
|
||||
)
|
||||
|
||||
committee_size = len(crosslink_committee)
|
||||
|
@ -125,7 +123,7 @@ def get_attestation_signature(spec, state, attestation_data, privkey, custody_bi
|
|||
domain=spec.get_domain(
|
||||
state=state,
|
||||
domain_type=spec.DOMAIN_ATTESTATION,
|
||||
message_epoch=attestation_data.target_epoch,
|
||||
message_epoch=attestation_data.target.epoch,
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -133,7 +131,7 @@ def get_attestation_signature(spec, state, attestation_data, privkey, custody_bi
|
|||
def fill_aggregate_attestation(spec, state, attestation):
|
||||
crosslink_committee = spec.get_crosslink_committee(
|
||||
state,
|
||||
attestation.data.target_epoch,
|
||||
attestation.data.target.epoch,
|
||||
attestation.data.crosslink.shard,
|
||||
)
|
||||
for i in range(len(crosslink_committee)):
|
||||
|
|
|
@ -7,7 +7,7 @@ def get_valid_attester_slashing(spec, state, signed_1=False, signed_2=False):
|
|||
attestation_1 = get_valid_attestation(spec, state, signed=signed_1)
|
||||
|
||||
attestation_2 = deepcopy(attestation_1)
|
||||
attestation_2.data.target_root = b'\x01' * 32
|
||||
attestation_2.data.target.root = b'\x01' * 32
|
||||
|
||||
if signed_2:
|
||||
sign_attestation(spec, state, attestation_2)
|
||||
|
|
|
@ -28,7 +28,9 @@ def create_genesis_state(spec, num_validators):
|
|||
deposit_root=deposit_root,
|
||||
deposit_count=num_validators,
|
||||
block_hash=spec.ZERO_HASH,
|
||||
))
|
||||
),
|
||||
latest_block_header=spec.BeaconBlockHeader(body_root=spec.hash_tree_root(spec.BeaconBlockBody())),
|
||||
)
|
||||
|
||||
# We "hack" in the initial validators,
|
||||
# as it is much faster than creating and processing genesis deposits for every single test case.
|
||||
|
|
|
@ -39,7 +39,7 @@ def run_attestation_processing(spec, state, attestation, valid=True):
|
|||
spec.process_attestation(state, attestation)
|
||||
|
||||
# Make sure the attestation has been processed
|
||||
if attestation.data.target_epoch == spec.get_current_epoch(state):
|
||||
if attestation.data.target.epoch == spec.get_current_epoch(state):
|
||||
assert len(state.current_epoch_attestations) == current_epoch_count + 1
|
||||
else:
|
||||
assert len(state.previous_epoch_attestations) == previous_epoch_count + 1
|
||||
|
@ -120,16 +120,16 @@ def test_after_epoch_slots(spec, state):
|
|||
@spec_state_test
|
||||
def test_old_source_epoch(spec, state):
|
||||
state.slot = spec.SLOTS_PER_EPOCH * 5
|
||||
state.finalized_epoch = 2
|
||||
state.previous_justified_epoch = 3
|
||||
state.current_justified_epoch = 4
|
||||
state.finalized_checkpoint.epoch = 2
|
||||
state.previous_justified_checkpoint.epoch = 3
|
||||
state.current_justified_checkpoint.epoch = 4
|
||||
attestation = get_valid_attestation(spec, state, slot=(spec.SLOTS_PER_EPOCH * 3) + 1)
|
||||
|
||||
# test logic sanity check: make sure the attestation is pointing to oldest known source epoch
|
||||
assert attestation.data.source_epoch == state.previous_justified_epoch
|
||||
assert attestation.data.source.epoch == state.previous_justified_checkpoint.epoch
|
||||
|
||||
# Now go beyond that, it will be invalid
|
||||
attestation.data.source_epoch -= 1
|
||||
attestation.data.source.epoch -= 1
|
||||
|
||||
sign_attestation(spec, state, attestation)
|
||||
|
||||
|
@ -155,7 +155,7 @@ def test_new_source_epoch(spec, state):
|
|||
attestation = get_valid_attestation(spec, state)
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
attestation.data.source_epoch += 1
|
||||
attestation.data.source.epoch += 1
|
||||
|
||||
sign_attestation(spec, state, attestation)
|
||||
|
||||
|
@ -168,7 +168,7 @@ def test_source_root_is_target_root(spec, state):
|
|||
attestation = get_valid_attestation(spec, state)
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
attestation.data.source_root = attestation.data.target_root
|
||||
attestation.data.source.root = attestation.data.target.root
|
||||
|
||||
sign_attestation(spec, state, attestation)
|
||||
|
||||
|
@ -179,23 +179,20 @@ def test_source_root_is_target_root(spec, state):
|
|||
@spec_state_test
|
||||
def test_invalid_current_source_root(spec, state):
|
||||
state.slot = spec.SLOTS_PER_EPOCH * 5
|
||||
state.finalized_epoch = 2
|
||||
state.finalized_checkpoint.epoch = 2
|
||||
|
||||
state.previous_justified_epoch = 3
|
||||
state.previous_justified_root = b'\x01' * 32
|
||||
|
||||
state.current_justified_epoch = 4
|
||||
state.current_justified_root = b'\xff' * 32
|
||||
state.previous_justified_checkpoint = spec.Checkpoint(epoch=3, root=b'\x01' * 32)
|
||||
state.current_justified_checkpoint = spec.Checkpoint(epoch=4, root=b'\x32' * 32)
|
||||
|
||||
attestation = get_valid_attestation(spec, state, slot=(spec.SLOTS_PER_EPOCH * 3) + 1)
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
# Test logic sanity checks:
|
||||
assert state.current_justified_root != state.previous_justified_root
|
||||
assert attestation.data.source_root == state.previous_justified_root
|
||||
assert state.current_justified_checkpoint.root != state.previous_justified_checkpoint.root
|
||||
assert attestation.data.source.root == state.previous_justified_checkpoint.root
|
||||
|
||||
# Make attestation source root invalid: should be previous justified, not current one
|
||||
attestation.data.source_root = state.current_justified_root
|
||||
attestation.data.source.root = state.current_justified_checkpoint.root
|
||||
|
||||
sign_attestation(spec, state, attestation)
|
||||
|
||||
|
@ -208,7 +205,7 @@ def test_bad_source_root(spec, state):
|
|||
attestation = get_valid_attestation(spec, state)
|
||||
state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||
|
||||
attestation.data.source_root = b'\x42' * 32
|
||||
attestation.data.source.root = b'\x42' * 32
|
||||
|
||||
sign_attestation(spec, state, attestation)
|
||||
|
||||
|
|
|
@ -68,12 +68,14 @@ def test_success_surround(spec, state):
|
|||
next_epoch(spec, state)
|
||||
apply_empty_block(spec, state)
|
||||
|
||||
state.current_justified_epoch += 1
|
||||
state.current_justified_checkpoint.epoch += 1
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True)
|
||||
attestation_1 = attester_slashing.attestation_1
|
||||
attestation_2 = attester_slashing.attestation_2
|
||||
|
||||
# set attestion1 to surround attestation 2
|
||||
attester_slashing.attestation_1.data.source_epoch = attester_slashing.attestation_2.data.source_epoch - 1
|
||||
attester_slashing.attestation_1.data.target_epoch = attester_slashing.attestation_2.data.target_epoch + 1
|
||||
attestation_1.data.source.epoch = attestation_2.data.source.epoch - 1
|
||||
attestation_1.data.target.epoch = attestation_2.data.target.epoch + 1
|
||||
|
||||
sign_indexed_attestation(spec, state, attester_slashing.attestation_1)
|
||||
|
||||
|
@ -120,7 +122,7 @@ def test_same_data(spec, state):
|
|||
def test_no_double_or_surround(spec, state):
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True)
|
||||
|
||||
attester_slashing.attestation_1.data.target_epoch += 1
|
||||
attester_slashing.attestation_1.data.target.epoch += 1
|
||||
sign_indexed_attestation(spec, state, attester_slashing.attestation_1)
|
||||
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
|
|
@ -114,7 +114,7 @@ def test_incorrect_slot(spec, state):
|
|||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_insufficient_balance_for_fee(spec, state):
|
||||
def test_insufficient_balance_for_fee_result_dust(spec, state):
|
||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE
|
||||
transfer = get_valid_transfer(spec, state, sender_index=sender_index, amount=0, fee=1, signed=True)
|
||||
|
@ -127,7 +127,20 @@ def test_insufficient_balance_for_fee(spec, state):
|
|||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_insufficient_balance(spec, state):
|
||||
def test_insufficient_balance_for_fee_result_full(spec, state):
|
||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
transfer = get_valid_transfer(spec, state, sender_index=sender_index,
|
||||
amount=0, fee=state.balances[sender_index] + 1, signed=True)
|
||||
|
||||
# un-activate so validator can transfer
|
||||
state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_insufficient_balance_for_amount_result_dust(spec, state):
|
||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE
|
||||
transfer = get_valid_transfer(spec, state, sender_index=sender_index, amount=1, fee=0, signed=True)
|
||||
|
@ -138,6 +151,127 @@ def test_insufficient_balance(spec, state):
|
|||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_insufficient_balance_for_amount_result_full(spec, state):
|
||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
transfer = get_valid_transfer(spec, state, sender_index=sender_index,
|
||||
amount=state.balances[sender_index] + 1, fee=0, signed=True)
|
||||
|
||||
# un-activate so validator can transfer
|
||||
state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_insufficient_balance_for_combined_result_dust(spec, state):
|
||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
# Enough to pay fee without dust, and amount without dust, but not both.
|
||||
state.balances[sender_index] = spec.MIN_DEPOSIT_AMOUNT + 1
|
||||
transfer = get_valid_transfer(spec, state, sender_index=sender_index, amount=1, fee=1, signed=True)
|
||||
|
||||
# un-activate so validator can transfer
|
||||
state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_insufficient_balance_for_combined_result_full(spec, state):
|
||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
# Enough to pay fee fully without dust left, and amount fully without dust left, but not both.
|
||||
state.balances[sender_index] = spec.MIN_DEPOSIT_AMOUNT * 2 + 1
|
||||
transfer = get_valid_transfer(spec, state, sender_index=sender_index,
|
||||
amount=spec.MIN_DEPOSIT_AMOUNT + 1,
|
||||
fee=spec.MIN_DEPOSIT_AMOUNT + 1, signed=True)
|
||||
|
||||
# un-activate so validator can transfer
|
||||
state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_insufficient_balance_for_combined_big_amount(spec, state):
|
||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
# Enough to pay fee fully without dust left, and amount fully without dust left, but not both.
|
||||
# Try to create a dust balance (off by 1) with combination of fee and amount.
|
||||
state.balances[sender_index] = spec.MIN_DEPOSIT_AMOUNT * 2 + 1
|
||||
transfer = get_valid_transfer(spec, state, sender_index=sender_index,
|
||||
amount=spec.MIN_DEPOSIT_AMOUNT + 1, fee=1, signed=True)
|
||||
|
||||
# un-activate so validator can transfer
|
||||
state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_insufficient_balance_for_combined_big_fee(spec, state):
|
||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
# Enough to pay fee fully without dust left, and amount fully without dust left, but not both.
|
||||
# Try to create a dust balance (off by 1) with combination of fee and amount.
|
||||
state.balances[sender_index] = spec.MIN_DEPOSIT_AMOUNT * 2 + 1
|
||||
transfer = get_valid_transfer(spec, state, sender_index=sender_index,
|
||||
amount=1, fee=spec.MIN_DEPOSIT_AMOUNT + 1, signed=True)
|
||||
|
||||
# un-activate so validator can transfer
|
||||
state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_insufficient_balance_off_by_1_fee(spec, state):
|
||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
# Enough to pay fee fully without dust left, and amount fully without dust left, but not both.
|
||||
# Try to print money by using the full balance as amount, plus 1 for fee.
|
||||
transfer = get_valid_transfer(spec, state, sender_index=sender_index,
|
||||
amount=state.balances[sender_index], fee=1, signed=True)
|
||||
|
||||
# un-activate so validator can transfer
|
||||
state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_insufficient_balance_off_by_1_amount(spec, state):
|
||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
# Enough to pay fee fully without dust left, and amount fully without dust left, but not both.
|
||||
# Try to print money by using the full balance as fee, plus 1 for amount.
|
||||
transfer = get_valid_transfer(spec, state, sender_index=sender_index, amount=1,
|
||||
fee=state.balances[sender_index], signed=True)
|
||||
|
||||
# un-activate so validator can transfer
|
||||
state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_insufficient_balance_duplicate_as_fee_and_amount(spec, state):
|
||||
sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
# Enough to pay fee fully without dust left, and amount fully without dust left, but not both.
|
||||
# Try to print money by using the full balance, twice.
|
||||
transfer = get_valid_transfer(spec, state, sender_index=sender_index,
|
||||
amount=state.balances[sender_index],
|
||||
fee=state.balances[sender_index], signed=True)
|
||||
|
||||
# un-activate so validator can transfer
|
||||
state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH
|
||||
|
||||
yield from run_transfer_processing(spec, state, transfer, False)
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_no_dust_sender(spec, state):
|
||||
|
|
|
@ -96,7 +96,7 @@ def test_single_crosslink_update_from_previous_epoch(spec, state):
|
|||
# ensure rewarded
|
||||
for index in spec.get_crosslink_committee(
|
||||
state,
|
||||
attestation.data.target_epoch,
|
||||
attestation.data.target.epoch,
|
||||
attestation.data.crosslink.shard):
|
||||
assert crosslink_deltas[0][index] > 0
|
||||
assert crosslink_deltas[1][index] == 0
|
||||
|
@ -148,7 +148,7 @@ def test_double_late_crosslink(spec, state):
|
|||
# ensure no reward, only penalties for the failed crosslink
|
||||
for index in spec.get_crosslink_committee(
|
||||
state,
|
||||
attestation_2.data.target_epoch,
|
||||
attestation_2.data.target.epoch,
|
||||
attestation_2.data.crosslink.shard):
|
||||
assert crosslink_deltas[0][index] == 0
|
||||
assert crosslink_deltas[1][index] > 0
|
||||
|
|
|
@ -13,25 +13,22 @@ def check_finality(spec,
|
|||
previous_justified_changed,
|
||||
finalized_changed):
|
||||
if current_justified_changed:
|
||||
assert state.current_justified_epoch > prev_state.current_justified_epoch
|
||||
assert state.current_justified_root != prev_state.current_justified_root
|
||||
assert state.current_justified_checkpoint.epoch > prev_state.current_justified_checkpoint.epoch
|
||||
assert state.current_justified_checkpoint.root != prev_state.current_justified_checkpoint.root
|
||||
else:
|
||||
assert state.current_justified_epoch == prev_state.current_justified_epoch
|
||||
assert state.current_justified_root == prev_state.current_justified_root
|
||||
assert state.current_justified_checkpoint == prev_state.current_justified_checkpoint
|
||||
|
||||
if previous_justified_changed:
|
||||
assert state.previous_justified_epoch > prev_state.previous_justified_epoch
|
||||
assert state.previous_justified_root != prev_state.previous_justified_root
|
||||
assert state.previous_justified_checkpoint.epoch > prev_state.previous_justified_checkpoint.epoch
|
||||
assert state.previous_justified_checkpoint.root != prev_state.previous_justified_checkpoint.root
|
||||
else:
|
||||
assert state.previous_justified_epoch == prev_state.previous_justified_epoch
|
||||
assert state.previous_justified_root == prev_state.previous_justified_root
|
||||
assert state.previous_justified_checkpoint == prev_state.previous_justified_checkpoint
|
||||
|
||||
if finalized_changed:
|
||||
assert state.finalized_epoch > prev_state.finalized_epoch
|
||||
assert state.finalized_root != prev_state.finalized_root
|
||||
assert state.finalized_checkpoint.epoch > prev_state.finalized_checkpoint.epoch
|
||||
assert state.finalized_checkpoint.root != prev_state.finalized_checkpoint.root
|
||||
else:
|
||||
assert state.finalized_epoch == prev_state.finalized_epoch
|
||||
assert state.finalized_root == prev_state.finalized_root
|
||||
assert state.finalized_checkpoint == prev_state.finalized_checkpoint
|
||||
|
||||
|
||||
def next_epoch_with_attestations(spec,
|
||||
|
@ -107,8 +104,7 @@ def test_finality_rule_4(spec, state):
|
|||
elif epoch == 1:
|
||||
# rule 4 of finality
|
||||
check_finality(spec, state, prev_state, True, True, True)
|
||||
assert state.finalized_epoch == prev_state.current_justified_epoch
|
||||
assert state.finalized_root == prev_state.current_justified_root
|
||||
assert state.finalized_checkpoint == prev_state.current_justified_checkpoint
|
||||
|
||||
yield 'blocks', blocks
|
||||
yield 'post', state
|
||||
|
@ -138,8 +134,7 @@ def test_finality_rule_1(spec, state):
|
|||
elif epoch == 2:
|
||||
# finalized by rule 1
|
||||
check_finality(spec, state, prev_state, True, True, True)
|
||||
assert state.finalized_epoch == prev_state.previous_justified_epoch
|
||||
assert state.finalized_root == prev_state.previous_justified_root
|
||||
assert state.finalized_checkpoint == prev_state.previous_justified_checkpoint
|
||||
|
||||
yield 'blocks', blocks
|
||||
yield 'post', state
|
||||
|
@ -169,8 +164,7 @@ def test_finality_rule_2(spec, state):
|
|||
prev_state, new_blocks, state = next_epoch_with_attestations(spec, state, False, True)
|
||||
# finalized by rule 2
|
||||
check_finality(spec, state, prev_state, True, False, True)
|
||||
assert state.finalized_epoch == prev_state.previous_justified_epoch
|
||||
assert state.finalized_root == prev_state.previous_justified_root
|
||||
assert state.finalized_checkpoint == prev_state.previous_justified_checkpoint
|
||||
|
||||
blocks += new_blocks
|
||||
|
||||
|
@ -221,8 +215,7 @@ def test_finality_rule_3(spec, state):
|
|||
blocks += new_blocks
|
||||
# rule 3
|
||||
check_finality(spec, state, prev_state, True, True, True)
|
||||
assert state.finalized_epoch == prev_state.current_justified_epoch
|
||||
assert state.finalized_root == prev_state.current_justified_root
|
||||
assert state.finalized_checkpoint == prev_state.current_justified_checkpoint
|
||||
|
||||
yield 'blocks', blocks
|
||||
yield 'post', state
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
from eth2spec.utils.ssz.ssz_impl import signing_root, hash_tree_root
|
||||
|
||||
from eth2spec.test.context import with_all_phases, with_state, bls_switch
|
||||
|
||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
|
||||
from eth2spec.test.helpers.attestations import get_valid_attestation
|
||||
from eth2spec.test.helpers.state import next_slot
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@with_state
|
||||
@bls_switch
|
||||
def test_basic(spec, state):
|
||||
state.latest_block_header = spec.BeaconBlockHeader(body_root=hash_tree_root(spec.BeaconBlockBody()))
|
||||
|
||||
# Initialization
|
||||
store = spec.get_genesis_store(state)
|
||||
blocks = []
|
||||
time = 100
|
||||
spec.on_tick(store, time)
|
||||
assert store.time == time
|
||||
|
||||
# On receiving a block of `GENESIS_SLOT + 1` slot
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
blocks.append(block)
|
||||
spec.on_block(store, block)
|
||||
assert store.blocks[signing_root(block)] == block
|
||||
|
||||
# On receiving a block of next epoch
|
||||
store.time = time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.slot += spec.SLOTS_PER_EPOCH
|
||||
blocks.append(block)
|
||||
|
||||
spec.on_block(store, block)
|
||||
assert store.blocks[signing_root(block)] == block
|
||||
|
||||
# TODO: add tests for justified_root and finalized_root
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@with_state
|
||||
@bls_switch
|
||||
def test_on_attestation(spec, state):
|
||||
store = spec.get_genesis_store(state)
|
||||
time = 100
|
||||
spec.on_tick(store, time)
|
||||
|
||||
next_slot(spec, state)
|
||||
|
||||
attestation = get_valid_attestation(spec, state, slot=1)
|
||||
indexed_attestation = spec.convert_to_indexed(state, attestation)
|
||||
spec.on_attestation(store, attestation)
|
||||
assert (
|
||||
store.latest_targets[indexed_attestation.custody_bit_0_indices[0]] ==
|
||||
spec.Target(
|
||||
epoch=attestation.data.target_epoch,
|
||||
root=attestation.data.target_root,
|
||||
)
|
||||
)
|
Loading…
Reference in New Issue