Merge pull request #1889 from ethereum/dankrad-custody-0.01bit
0.01 bit proof of custody [depends on 256 bit custody atoms]
This commit is contained in:
commit
eec323c7b6
|
@ -151,3 +151,9 @@ DOMAIN_DEPOSIT: 0x03000000
|
||||||
DOMAIN_VOLUNTARY_EXIT: 0x04000000
|
DOMAIN_VOLUNTARY_EXIT: 0x04000000
|
||||||
DOMAIN_SELECTION_PROOF: 0x05000000
|
DOMAIN_SELECTION_PROOF: 0x05000000
|
||||||
DOMAIN_AGGREGATE_AND_PROOF: 0x06000000
|
DOMAIN_AGGREGATE_AND_PROOF: 0x06000000
|
||||||
|
<<<<<<< HEAD:configs/mainnet.yaml
|
||||||
|
# Phase 1
|
||||||
|
DOMAIN_SHARD_PROPOSAL: 0x80000000
|
||||||
|
DOMAIN_SHARD_COMMITTEE: 0x81000000
|
||||||
|
DOMAIN_LIGHT_CLIENT: 0x82000000
|
||||||
|
DOMAIN_CUSTODY_BIT_SLASHING: 0x83000000
|
||||||
|
|
|
@ -62,14 +62,26 @@ DOMAIN_CUSTODY_BIT_SLASHING: 0x83000000
|
||||||
# Time parameters
|
# Time parameters
|
||||||
# 2**1 (= 2) epochs, 12.8 minutes
|
# 2**1 (= 2) epochs, 12.8 minutes
|
||||||
RANDAO_PENALTY_EPOCHS: 2
|
RANDAO_PENALTY_EPOCHS: 2
|
||||||
|
# 2**15 (= 32,768) epochs, ~146 days
|
||||||
|
EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS: 32768
|
||||||
# 2**14 (= 16,384) epochs ~73 days
|
# 2**14 (= 16,384) epochs ~73 days
|
||||||
EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS: 16384
|
EPOCHS_PER_CUSTODY_PERIOD: 16384
|
||||||
# 2**11 (= 2,048) epochs, ~9 days
|
|
||||||
EPOCHS_PER_CUSTODY_PERIOD: 2048
|
|
||||||
# 2**11 (= 2,048) epochs, ~9 days
|
# 2**11 (= 2,048) epochs, ~9 days
|
||||||
CUSTODY_PERIOD_TO_RANDAO_PADDING: 2048
|
CUSTODY_PERIOD_TO_RANDAO_PADDING: 2048
|
||||||
# 2**14 (= 16,384) epochs
|
# 2**14 (= 16,384) epochs
|
||||||
CUSTODY_RESPONSE_DEADLINE: 16384
|
CUSTODY_RESPONSE_DEADLINE: 16384
|
||||||
|
# 2**15 (= 32,768) epochs, ~146 days
|
||||||
|
MAX_CHUNK_CHALLENGE_DELAY: 32768
|
||||||
|
|
||||||
|
# Misc parameters
|
||||||
|
# 2**256 - 189
|
||||||
|
CUSTODY_PRIME: 115792089237316195423570985008687907853269984665640564039457584007913129639747
|
||||||
|
# 3
|
||||||
|
CUSTODY_SECRETS: 3
|
||||||
|
# 2**5 (= 32) bytes
|
||||||
|
BYTES_PER_CUSTODY_ATOM: 32
|
||||||
|
# 1/1024 chance of custody bit 1
|
||||||
|
CUSTODY_PROBABILITY_EXPONENT: 10
|
||||||
|
|
||||||
# Max operations
|
# Max operations
|
||||||
# 2**8 (= 256)
|
# 2**8 (= 256)
|
||||||
|
|
|
@ -65,13 +65,27 @@ DOMAIN_CUSTODY_BIT_SLASHING: 0x83000000
|
||||||
# 2**1 (= 2) epochs
|
# 2**1 (= 2) epochs
|
||||||
RANDAO_PENALTY_EPOCHS: 2
|
RANDAO_PENALTY_EPOCHS: 2
|
||||||
# [customized] quicker for testing
|
# [customized] quicker for testing
|
||||||
EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS: 4096
|
EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS: 128
|
||||||
# [customized] quicker for testing
|
# [customized] quicker for testing
|
||||||
EPOCHS_PER_CUSTODY_PERIOD: 8
|
EPOCHS_PER_CUSTODY_PERIOD: 64
|
||||||
# [customized] quicker for testing
|
# [customized] quicker for testing
|
||||||
CUSTODY_PERIOD_TO_RANDAO_PADDING: 8
|
CUSTODY_PERIOD_TO_RANDAO_PADDING: 8
|
||||||
# [customized] quicker for testing
|
# [customized] quicker for testing
|
||||||
CUSTODY_RESPONSE_DEADLINE: 32
|
CUSTODY_RESPONSE_DEADLINE: 128
|
||||||
|
# [customize for faster testing]
|
||||||
|
MAX_CHUNK_CHALLENGE_DELAY: 128
|
||||||
|
|
||||||
|
|
||||||
|
# Misc parameters
|
||||||
|
# 2**256 - 189
|
||||||
|
CUSTODY_PRIME: 115792089237316195423570985008687907853269984665640564039457584007913129639747
|
||||||
|
# 3
|
||||||
|
CUSTODY_SECRETS: 3
|
||||||
|
# 2**5 (= 32) bytes
|
||||||
|
BYTES_PER_CUSTODY_ATOM: 32
|
||||||
|
# 1/4 chance of custody bit 1 [customized for faster testing]
|
||||||
|
CUSTODY_PROBABILITY_EXPONENT: 2
|
||||||
|
|
||||||
|
|
||||||
# Max operations
|
# Max operations
|
||||||
# 2**8 (= 256)
|
# 2**8 (= 256)
|
||||||
|
|
|
@ -53,15 +53,12 @@
|
||||||
- [`get_shard_committee`](#get_shard_committee)
|
- [`get_shard_committee`](#get_shard_committee)
|
||||||
- [`get_light_client_committee`](#get_light_client_committee)
|
- [`get_light_client_committee`](#get_light_client_committee)
|
||||||
- [`get_shard_proposer_index`](#get_shard_proposer_index)
|
- [`get_shard_proposer_index`](#get_shard_proposer_index)
|
||||||
- [`get_indexed_attestation`](#get_indexed_attestation)
|
|
||||||
- [`get_committee_count_delta`](#get_committee_count_delta)
|
- [`get_committee_count_delta`](#get_committee_count_delta)
|
||||||
- [`get_start_shard`](#get_start_shard)
|
- [`get_start_shard`](#get_start_shard)
|
||||||
- [`get_shard`](#get_shard)
|
- [`get_shard`](#get_shard)
|
||||||
- [`get_latest_slot_for_shard`](#get_latest_slot_for_shard)
|
- [`get_latest_slot_for_shard`](#get_latest_slot_for_shard)
|
||||||
- [`get_offset_slots`](#get_offset_slots)
|
- [`get_offset_slots`](#get_offset_slots)
|
||||||
- [Predicates](#predicates)
|
- [Predicates](#predicates)
|
||||||
- [`verify_attestation_custody`](#verify_attestation_custody)
|
|
||||||
- [Updated `is_valid_indexed_attestation`](#updated-is_valid_indexed_attestation)
|
|
||||||
- [`is_on_time_attestation`](#is_on_time_attestation)
|
- [`is_on_time_attestation`](#is_on_time_attestation)
|
||||||
- [`is_winning_attestation`](#is_winning_attestation)
|
- [`is_winning_attestation`](#is_winning_attestation)
|
||||||
- [`optional_aggregate_verify`](#optional_aggregate_verify)
|
- [`optional_aggregate_verify`](#optional_aggregate_verify)
|
||||||
|
@ -78,7 +75,6 @@
|
||||||
- [`verify_empty_shard_transition`](#verify_empty_shard_transition)
|
- [`verify_empty_shard_transition`](#verify_empty_shard_transition)
|
||||||
- [`process_shard_transitions`](#process_shard_transitions)
|
- [`process_shard_transitions`](#process_shard_transitions)
|
||||||
- [New default validator for deposits](#new-default-validator-for-deposits)
|
- [New default validator for deposits](#new-default-validator-for-deposits)
|
||||||
- [New Attester slashing processing](#new-attester-slashing-processing)
|
|
||||||
- [Light client processing](#light-client-processing)
|
- [Light client processing](#light-client-processing)
|
||||||
- [Epoch transition](#epoch-transition)
|
- [Epoch transition](#epoch-transition)
|
||||||
- [Phase 1 final updates](#phase-1-final-updates)
|
- [Phase 1 final updates](#phase-1-final-updates)
|
||||||
|
@ -186,7 +182,6 @@ class AttestationData(Container):
|
||||||
class Attestation(Container):
|
class Attestation(Container):
|
||||||
aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]
|
aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]
|
||||||
data: AttestationData
|
data: AttestationData
|
||||||
custody_bits_blocks: List[Bitlist[MAX_VALIDATORS_PER_COMMITTEE], MAX_SHARD_BLOCKS_PER_ATTESTATION]
|
|
||||||
signature: BLSSignature
|
signature: BLSSignature
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -206,8 +201,9 @@ class PendingAttestation(Container):
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class IndexedAttestation(Container):
|
class IndexedAttestation(Container):
|
||||||
committee: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE]
|
attesting_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE]
|
||||||
attestation: Attestation
|
data: AttestationData
|
||||||
|
signature: BLSSignature
|
||||||
```
|
```
|
||||||
|
|
||||||
### Extended `AttesterSlashing`
|
### Extended `AttesterSlashing`
|
||||||
|
@ -593,17 +589,6 @@ def get_shard_proposer_index(beacon_state: BeaconState, slot: Slot, shard: Shard
|
||||||
return committee[r % len(committee)]
|
return committee[r % len(committee)]
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `get_indexed_attestation`
|
|
||||||
|
|
||||||
```python
|
|
||||||
def get_indexed_attestation(beacon_state: BeaconState, attestation: Attestation) -> IndexedAttestation:
|
|
||||||
committee = get_beacon_committee(beacon_state, attestation.data.slot, attestation.data.index)
|
|
||||||
return IndexedAttestation(
|
|
||||||
committee=committee,
|
|
||||||
attestation=attestation,
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `get_committee_count_delta`
|
#### `get_committee_count_delta`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
@ -673,65 +658,6 @@ def get_offset_slots(state: BeaconState, shard: Shard) -> Sequence[Slot]:
|
||||||
|
|
||||||
### Predicates
|
### Predicates
|
||||||
|
|
||||||
#### `verify_attestation_custody`
|
|
||||||
|
|
||||||
```python
|
|
||||||
def verify_attestation_custody(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool:
|
|
||||||
"""
|
|
||||||
Check if ``indexed_attestation`` has valid signature against non-empty custody bits.
|
|
||||||
"""
|
|
||||||
attestation = indexed_attestation.attestation
|
|
||||||
aggregation_bits = attestation.aggregation_bits
|
|
||||||
domain = get_domain(state, DOMAIN_BEACON_ATTESTER, attestation.data.target.epoch)
|
|
||||||
all_pubkeys = []
|
|
||||||
all_signing_roots = []
|
|
||||||
for block_index, custody_bits in enumerate(attestation.custody_bits_blocks):
|
|
||||||
assert len(custody_bits) == len(indexed_attestation.committee)
|
|
||||||
for participant, aggregation_bit, custody_bit in zip(
|
|
||||||
indexed_attestation.committee, aggregation_bits, custody_bits
|
|
||||||
):
|
|
||||||
if aggregation_bit:
|
|
||||||
all_pubkeys.append(state.validators[participant].pubkey)
|
|
||||||
# Note: only 2N distinct message hashes
|
|
||||||
attestation_wrapper = AttestationCustodyBitWrapper(
|
|
||||||
attestation_data_root=hash_tree_root(attestation.data),
|
|
||||||
block_index=block_index,
|
|
||||||
bit=custody_bit,
|
|
||||||
)
|
|
||||||
all_signing_roots.append(compute_signing_root(attestation_wrapper, domain))
|
|
||||||
else:
|
|
||||||
assert not custody_bit
|
|
||||||
return bls.AggregateVerify(all_pubkeys, all_signing_roots, signature=attestation.signature)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Updated `is_valid_indexed_attestation`
|
|
||||||
|
|
||||||
Note that this replaces the Phase 0 `is_valid_indexed_attestation`.
|
|
||||||
|
|
||||||
```python
|
|
||||||
def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: IndexedAttestation) -> bool:
|
|
||||||
"""
|
|
||||||
Check if ``indexed_attestation`` has valid indices and signature.
|
|
||||||
"""
|
|
||||||
# Verify aggregate signature
|
|
||||||
attestation = indexed_attestation.attestation
|
|
||||||
aggregation_bits = attestation.aggregation_bits
|
|
||||||
if not any(aggregation_bits) or len(aggregation_bits) != len(indexed_attestation.committee):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if len(attestation.custody_bits_blocks) == 0:
|
|
||||||
# fall back on phase0 behavior if there is no shard data.
|
|
||||||
domain = get_domain(state, DOMAIN_BEACON_ATTESTER, attestation.data.target.epoch)
|
|
||||||
all_pubkeys = []
|
|
||||||
for participant, aggregation_bit in zip(indexed_attestation.committee, aggregation_bits):
|
|
||||||
if aggregation_bit:
|
|
||||||
all_pubkeys.append(state.validators[participant].pubkey)
|
|
||||||
signing_root = compute_signing_root(indexed_attestation.attestation.data, domain)
|
|
||||||
return bls.FastAggregateVerify(all_pubkeys, signing_root, signature=attestation.signature)
|
|
||||||
else:
|
|
||||||
return verify_attestation_custody(state, indexed_attestation)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `is_on_time_attestation`
|
#### `is_on_time_attestation`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
@ -849,16 +775,11 @@ def validate_attestation(state: BeaconState, attestation: Attestation) -> None:
|
||||||
else:
|
else:
|
||||||
assert attestation.data.source == state.previous_justified_checkpoint
|
assert attestation.data.source == state.previous_justified_checkpoint
|
||||||
|
|
||||||
# Type 1: on-time attestations, the custody bits should be non-empty.
|
# Type 1: on-time attestations
|
||||||
if attestation.custody_bits_blocks != []:
|
if is_on_time_attestation(state, attestation):
|
||||||
# Ensure on-time attestation
|
|
||||||
assert is_on_time_attestation(state, attestation)
|
|
||||||
# Correct data root count
|
|
||||||
shard = get_shard(state, attestation)
|
|
||||||
assert len(attestation.custody_bits_blocks) == len(get_offset_slots(state, shard))
|
|
||||||
# Correct parent block root
|
# Correct parent block root
|
||||||
assert data.beacon_block_root == get_block_root_at_slot(state, compute_previous_slot(state.slot))
|
assert data.beacon_block_root == get_block_root_at_slot(state, compute_previous_slot(state.slot))
|
||||||
# Type 2: no shard transition, no custody bits
|
# Type 2: no shard transition
|
||||||
else:
|
else:
|
||||||
# Ensure delayed attestation
|
# Ensure delayed attestation
|
||||||
assert data.slot < compute_previous_slot(state.slot)
|
assert data.slot < compute_previous_slot(state.slot)
|
||||||
|
@ -1081,46 +1002,6 @@ def get_validator_from_deposit(state: BeaconState, deposit: Deposit) -> Validato
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
##### New Attester slashing processing
|
|
||||||
|
|
||||||
```python
|
|
||||||
def get_indices_from_committee(
|
|
||||||
committee: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE],
|
|
||||||
bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE]) -> Sequence[ValidatorIndex]:
|
|
||||||
assert len(bits) == len(committee)
|
|
||||||
return [validator_index for i, validator_index in enumerate(committee) if bits[i]]
|
|
||||||
```
|
|
||||||
|
|
||||||
```python
|
|
||||||
def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSlashing) -> None:
|
|
||||||
indexed_attestation_1 = attester_slashing.attestation_1
|
|
||||||
indexed_attestation_2 = attester_slashing.attestation_2
|
|
||||||
|
|
||||||
assert is_slashable_attestation_data(
|
|
||||||
indexed_attestation_1.attestation.data,
|
|
||||||
indexed_attestation_2.attestation.data,
|
|
||||||
)
|
|
||||||
assert is_valid_indexed_attestation(state, indexed_attestation_1)
|
|
||||||
assert is_valid_indexed_attestation(state, indexed_attestation_2)
|
|
||||||
|
|
||||||
indices_1 = get_indices_from_committee(
|
|
||||||
indexed_attestation_1.committee,
|
|
||||||
indexed_attestation_1.attestation.aggregation_bits,
|
|
||||||
)
|
|
||||||
indices_2 = get_indices_from_committee(
|
|
||||||
indexed_attestation_2.committee,
|
|
||||||
indexed_attestation_2.attestation.aggregation_bits,
|
|
||||||
)
|
|
||||||
|
|
||||||
slashed_any = False
|
|
||||||
indices = set(indices_1).intersection(indices_2)
|
|
||||||
for index in sorted(indices):
|
|
||||||
if is_slashable_validator(state.validators[index], get_current_epoch(state)):
|
|
||||||
slash_validator(state, index)
|
|
||||||
slashed_any = True
|
|
||||||
assert slashed_any
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Light client processing
|
#### Light client processing
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
|
@ -60,6 +60,7 @@ This document details the beacon chain additions and changes in Phase 1 of Ether
|
||||||
| `CUSTODY_PRIME` | `2 ** 256 - 189` | - |
|
| `CUSTODY_PRIME` | `2 ** 256 - 189` | - |
|
||||||
| `CUSTODY_SECRETS` | `3` | - |
|
| `CUSTODY_SECRETS` | `3` | - |
|
||||||
| `BYTES_PER_CUSTODY_ATOM` | `32` | bytes |
|
| `BYTES_PER_CUSTODY_ATOM` | `32` | bytes |
|
||||||
|
| `CUSTODY_PROBABILITY_EXPONENT` | `10` | - |
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
@ -68,11 +69,11 @@ This document details the beacon chain additions and changes in Phase 1 of Ether
|
||||||
| Name | Value | Unit | Duration |
|
| Name | Value | Unit | Duration |
|
||||||
| - | - | :-: | :-: |
|
| - | - | :-: | :-: |
|
||||||
| `RANDAO_PENALTY_EPOCHS` | `2**1` (= 2) | epochs | 12.8 minutes |
|
| `RANDAO_PENALTY_EPOCHS` | `2**1` (= 2) | epochs | 12.8 minutes |
|
||||||
| `EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS` | `2**14` (= 16,384) | epochs | ~73 days |
|
| `EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS` | `2**15` (= 32,768) | epochs | ~146 days |
|
||||||
| `EPOCHS_PER_CUSTODY_PERIOD` | `2**11` (= 2,048) | epochs | ~9 days |
|
| `EPOCHS_PER_CUSTODY_PERIOD` | `2**14` (= 16,384) | epochs | ~73 days |
|
||||||
| `CUSTODY_PERIOD_TO_RANDAO_PADDING` | `2**11` (= 2,048) | epochs | ~9 days |
|
| `CUSTODY_PERIOD_TO_RANDAO_PADDING` | `2**11` (= 2,048) | epochs | ~9 days |
|
||||||
| `MAX_CHUNK_CHALLENGE_DELAY` | `2**11` (= 2,048) | epochs | ~9 days |
|
| `MAX_CHUNK_CHALLENGE_DELAY` | `2**15` (= 32,768) | epochs | ~146 days |
|
||||||
| `CUSTODY_RESPONSE_DEADLINE` | `2**14` (= 16,384) | epochs | ~73 days |
|
| `CHUNK_RESPONSE_DEADLINE` | `2**14` (= 16,384) | epochs | ~73 days |
|
||||||
|
|
||||||
### Max operations per block
|
### Max operations per block
|
||||||
|
|
||||||
|
@ -140,7 +141,6 @@ class CustodyChunkResponse(Container):
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class CustodySlashing(Container):
|
class CustodySlashing(Container):
|
||||||
# Attestation.custody_bits_blocks[data_index][committee.index(malefactor_index)] is the target custody bit to check.
|
|
||||||
# (Attestation.data.shard_transition_root as ShardTransition).shard_data_roots[data_index] is the root of the data.
|
# (Attestation.data.shard_transition_root as ShardTransition).shard_data_roots[data_index] is the root of the data.
|
||||||
data_index: uint64
|
data_index: uint64
|
||||||
malefactor_index: ValidatorIndex
|
malefactor_index: ValidatorIndex
|
||||||
|
@ -276,7 +276,8 @@ def compute_custody_bit(key: BLSSignature, data: ByteList[MAX_SHARD_BLOCK_SIZE])
|
||||||
custody_atoms = get_custody_atoms(data)
|
custody_atoms = get_custody_atoms(data)
|
||||||
secrets = get_custody_secrets(key)
|
secrets = get_custody_secrets(key)
|
||||||
uhf = universal_hash_function(custody_atoms, secrets)
|
uhf = universal_hash_function(custody_atoms, secrets)
|
||||||
return legendre_bit(uhf + secrets[0], CUSTODY_PRIME)
|
legendre_bits = [legendre_bit(uhf + secrets[0] + i, CUSTODY_PRIME) for i in range(CUSTODY_PROBABILITY_EXPONENT)]
|
||||||
|
return all(legendre_bits)
|
||||||
```
|
```
|
||||||
|
|
||||||
### `get_randao_epoch_for_custody_period`
|
### `get_randao_epoch_for_custody_period`
|
||||||
|
@ -517,9 +518,6 @@ def process_custody_slashing(state: BeaconState, signed_custody_slashing: Signed
|
||||||
# Verify the attestation
|
# Verify the attestation
|
||||||
assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation))
|
assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation))
|
||||||
|
|
||||||
# TODO: custody_slashing.data is not chunked like shard blocks yet, result is lots of padding.
|
|
||||||
# ??? What does this mean?
|
|
||||||
|
|
||||||
# TODO: can do a single combined merkle proof of data being attested.
|
# TODO: can do a single combined merkle proof of data being attested.
|
||||||
# Verify the shard transition is indeed attested by the attestation
|
# Verify the shard transition is indeed attested by the attestation
|
||||||
shard_transition = custody_slashing.shard_transition
|
shard_transition = custody_slashing.shard_transition
|
||||||
|
@ -544,18 +542,14 @@ def process_custody_slashing(state: BeaconState, signed_custody_slashing: Signed
|
||||||
signing_root = compute_signing_root(epoch_to_sign, domain)
|
signing_root = compute_signing_root(epoch_to_sign, domain)
|
||||||
assert bls.Verify(malefactor.pubkey, signing_root, custody_slashing.malefactor_secret)
|
assert bls.Verify(malefactor.pubkey, signing_root, custody_slashing.malefactor_secret)
|
||||||
|
|
||||||
# Get the custody bit
|
|
||||||
custody_bits = attestation.custody_bits_blocks[custody_slashing.data_index]
|
|
||||||
committee = get_beacon_committee(state, attestation.data.slot, attestation.data.index)
|
|
||||||
claimed_custody_bit = custody_bits[committee.index(custody_slashing.malefactor_index)]
|
|
||||||
|
|
||||||
# Compute the custody bit
|
# Compute the custody bit
|
||||||
computed_custody_bit = compute_custody_bit(custody_slashing.malefactor_secret, custody_slashing.data)
|
computed_custody_bit = compute_custody_bit(custody_slashing.malefactor_secret, custody_slashing.data)
|
||||||
|
|
||||||
# Verify the claim
|
# Verify the claim
|
||||||
if claimed_custody_bit != computed_custody_bit:
|
if computed_custody_bit == 1:
|
||||||
# Slash the malefactor, reward the other committee members
|
# Slash the malefactor, reward the other committee members
|
||||||
slash_validator(state, custody_slashing.malefactor_index)
|
slash_validator(state, custody_slashing.malefactor_index)
|
||||||
|
committee = get_beacon_committee(state, attestation.data.slot, attestation.data.index)
|
||||||
others_count = len(committee) - 1
|
others_count = len(committee) - 1
|
||||||
whistleblower_reward = Gwei(malefactor.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT // others_count)
|
whistleblower_reward = Gwei(malefactor.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT // others_count)
|
||||||
for attester_index in attesters:
|
for attester_index in attesters:
|
||||||
|
@ -576,7 +570,7 @@ def process_custody_slashing(state: BeaconState, signed_custody_slashing: Signed
|
||||||
def process_reveal_deadlines(state: BeaconState) -> None:
|
def process_reveal_deadlines(state: BeaconState) -> None:
|
||||||
epoch = get_current_epoch(state)
|
epoch = get_current_epoch(state)
|
||||||
for index, validator in enumerate(state.validators):
|
for index, validator in enumerate(state.validators):
|
||||||
deadline = validator.next_custody_secret_to_reveal + (CUSTODY_RESPONSE_DEADLINE // EPOCHS_PER_CUSTODY_PERIOD)
|
deadline = validator.next_custody_secret_to_reveal + 1
|
||||||
if get_custody_period_for_validator(ValidatorIndex(index), epoch) > deadline:
|
if get_custody_period_for_validator(ValidatorIndex(index), epoch) > deadline:
|
||||||
slash_validator(state, ValidatorIndex(index))
|
slash_validator(state, ValidatorIndex(index))
|
||||||
```
|
```
|
||||||
|
@ -584,7 +578,7 @@ def process_reveal_deadlines(state: BeaconState) -> None:
|
||||||
```python
|
```python
|
||||||
def process_challenge_deadlines(state: BeaconState) -> None:
|
def process_challenge_deadlines(state: BeaconState) -> None:
|
||||||
for custody_chunk_challenge in state.custody_chunk_challenge_records:
|
for custody_chunk_challenge in state.custody_chunk_challenge_records:
|
||||||
if get_current_epoch(state) > custody_chunk_challenge.inclusion_epoch + CUSTODY_RESPONSE_DEADLINE:
|
if get_current_epoch(state) > custody_chunk_challenge.inclusion_epoch + EPOCHS_PER_CUSTODY_PERIOD:
|
||||||
slash_validator(state, custody_chunk_challenge.responder_index, custody_chunk_challenge.challenger_index)
|
slash_validator(state, custody_chunk_challenge.responder_index, custody_chunk_challenge.challenger_index)
|
||||||
index_in_records = state.custody_chunk_challenge_records.index(custody_chunk_challenge)
|
index_in_records = state.custody_chunk_challenge_records.index(custody_chunk_challenge)
|
||||||
state.custody_chunk_challenge_records[index_in_records] = CustodyChunkChallengeRecord()
|
state.custody_chunk_challenge_records[index_in_records] = CustodyChunkChallengeRecord()
|
||||||
|
|
|
@ -9,11 +9,9 @@
|
||||||
|
|
||||||
|
|
||||||
- [Introduction](#introduction)
|
- [Introduction](#introduction)
|
||||||
- [Fork choice](#fork-choice)
|
|
||||||
- [Helpers](#helpers)
|
- [Helpers](#helpers)
|
||||||
- [Extended `LatestMessage`](#extended-latestmessage)
|
- [Extended `LatestMessage`](#extended-latestmessage)
|
||||||
- [Updated `update_latest_messages`](#updated-update_latest_messages)
|
- [Updated `update_latest_messages`](#updated-update_latest_messages)
|
||||||
- [Handlers](#handlers)
|
|
||||||
|
|
||||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
<!-- /TOC -->
|
<!-- /TOC -->
|
||||||
|
@ -22,12 +20,6 @@
|
||||||
|
|
||||||
This document is the beacon chain fork choice spec for part of Ethereum 2.0 Phase 1.
|
This document is the beacon chain fork choice spec for part of Ethereum 2.0 Phase 1.
|
||||||
|
|
||||||
## Fork choice
|
|
||||||
|
|
||||||
Due to the changes in the structure of `IndexedAttestation` in Phase 1, `on_attestation` must be re-specified to handle this. The bulk of `on_attestation` has been moved out into a few helpers to reduce code duplication where possible.
|
|
||||||
|
|
||||||
The rest of the fork choice remains stable.
|
|
||||||
|
|
||||||
### Helpers
|
### Helpers
|
||||||
|
|
||||||
#### Extended `LatestMessage`
|
#### Extended `LatestMessage`
|
||||||
|
@ -54,29 +46,3 @@ def update_latest_messages(store: Store, attesting_indices: Sequence[ValidatorIn
|
||||||
epoch=target.epoch, root=beacon_block_root, shard=shard, shard_root=attestation.data.shard_head_root
|
epoch=target.epoch, root=beacon_block_root, shard=shard, shard_root=attestation.data.shard_head_root
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Handlers
|
|
||||||
|
|
||||||
```python
|
|
||||||
def on_attestation(store: Store, attestation: Attestation) -> None:
|
|
||||||
"""
|
|
||||||
Run ``on_attestation`` upon receiving a new ``attestation`` from either within a block or directly on the wire.
|
|
||||||
|
|
||||||
An ``attestation`` that is asserted as invalid may be valid at a later time,
|
|
||||||
consider scheduling it for later processing in such case.
|
|
||||||
"""
|
|
||||||
validate_on_attestation(store, attestation)
|
|
||||||
store_target_checkpoint_state(store, attestation.data.target)
|
|
||||||
|
|
||||||
# Get state at the `target` to fully validate attestation
|
|
||||||
target_state = store.checkpoint_states[attestation.data.target]
|
|
||||||
indexed_attestation = get_indexed_attestation(target_state, attestation)
|
|
||||||
assert is_valid_indexed_attestation(target_state, indexed_attestation)
|
|
||||||
|
|
||||||
# Update latest messages for attesting indices
|
|
||||||
attesting_indices = [
|
|
||||||
index for i, index in enumerate(indexed_attestation.committee)
|
|
||||||
if attestation.aggregation_bits[i]
|
|
||||||
]
|
|
||||||
update_latest_messages(store, attesting_indices, attestation)
|
|
||||||
```
|
|
||||||
|
|
|
@ -16,18 +16,13 @@ def run_on_attestation(spec, state, store, attestation, valid=True):
|
||||||
indexed_attestation = spec.get_indexed_attestation(state, attestation)
|
indexed_attestation = spec.get_indexed_attestation(state, attestation)
|
||||||
spec.on_attestation(store, attestation)
|
spec.on_attestation(store, attestation)
|
||||||
|
|
||||||
|
sample_index = indexed_attestation.attesting_indices[0]
|
||||||
if spec.fork == PHASE0:
|
if spec.fork == PHASE0:
|
||||||
sample_index = indexed_attestation.attesting_indices[0]
|
|
||||||
latest_message = spec.LatestMessage(
|
latest_message = spec.LatestMessage(
|
||||||
epoch=attestation.data.target.epoch,
|
epoch=attestation.data.target.epoch,
|
||||||
root=attestation.data.beacon_block_root,
|
root=attestation.data.beacon_block_root,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
attesting_indices = [
|
|
||||||
index for i, index in enumerate(indexed_attestation.committee)
|
|
||||||
if attestation.aggregation_bits[i]
|
|
||||||
]
|
|
||||||
sample_index = attesting_indices[0]
|
|
||||||
latest_message = spec.LatestMessage(
|
latest_message = spec.LatestMessage(
|
||||||
epoch=attestation.data.target.epoch,
|
epoch=attestation.data.target.epoch,
|
||||||
root=attestation.data.beacon_block_root,
|
root=attestation.data.beacon_block_root,
|
||||||
|
|
|
@ -2,14 +2,13 @@ from lru import LRU
|
||||||
|
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from eth2spec.test.context import expect_assertion_error, PHASE0, PHASE1
|
from eth2spec.test.context import expect_assertion_error, PHASE1
|
||||||
from eth2spec.test.helpers.state import state_transition_and_sign_block, next_epoch, next_slot
|
from eth2spec.test.helpers.state import state_transition_and_sign_block, next_epoch, next_slot
|
||||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
|
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
|
||||||
from eth2spec.test.helpers.shard_transitions import get_shard_transition_of_committee
|
from eth2spec.test.helpers.shard_transitions import get_shard_transition_of_committee
|
||||||
from eth2spec.test.helpers.keys import privkeys
|
from eth2spec.test.helpers.keys import privkeys
|
||||||
from eth2spec.utils import bls
|
from eth2spec.utils import bls
|
||||||
from eth2spec.utils.ssz.ssz_typing import Bitlist
|
from eth2spec.utils.ssz.ssz_typing import Bitlist
|
||||||
from eth2spec.test.helpers.custody import get_custody_test_vector
|
|
||||||
|
|
||||||
|
|
||||||
def run_attestation_processing(spec, state, attestation, valid=True):
|
def run_attestation_processing(spec, state, attestation, valid=True):
|
||||||
|
@ -97,44 +96,7 @@ def build_attestation_data(spec, state, slot, index, shard_transition=None, on_t
|
||||||
return attestation_data
|
return attestation_data
|
||||||
|
|
||||||
|
|
||||||
def convert_to_valid_on_time_attestation(spec, state, attestation, shard_transition,
|
def get_valid_on_time_attestation(spec, state, slot=None, index=None, shard_transition=None, signed=False):
|
||||||
signed=False, valid_custody_bits=None):
|
|
||||||
shard = spec.get_shard(state, attestation)
|
|
||||||
offset_slots = spec.compute_offset_slots(spec.get_latest_slot_for_shard(state, shard), state.slot + 1)
|
|
||||||
|
|
||||||
if valid_custody_bits is not None:
|
|
||||||
beacon_committee = spec.get_beacon_committee(
|
|
||||||
state,
|
|
||||||
attestation.data.slot,
|
|
||||||
attestation.data.index,
|
|
||||||
)
|
|
||||||
custody_secrets = [None for i in beacon_committee]
|
|
||||||
for i in range(len(beacon_committee)):
|
|
||||||
period = spec.get_custody_period_for_validator(beacon_committee[i], attestation.data.target.epoch)
|
|
||||||
epoch_to_sign = spec.get_randao_epoch_for_custody_period(period, beacon_committee[i])
|
|
||||||
domain = spec.get_domain(state, spec.DOMAIN_RANDAO, epoch_to_sign)
|
|
||||||
signing_root = spec.compute_signing_root(spec.Epoch(epoch_to_sign), domain)
|
|
||||||
custody_secrets[i] = bls.Sign(privkeys[beacon_committee[i]], signing_root)
|
|
||||||
|
|
||||||
for i in range(len(offset_slots)):
|
|
||||||
attestation.custody_bits_blocks.append(
|
|
||||||
Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE]([0 for _ in attestation.aggregation_bits])
|
|
||||||
)
|
|
||||||
if valid_custody_bits is not None:
|
|
||||||
test_vector = get_custody_test_vector(shard_transition.shard_block_lengths[i])
|
|
||||||
for j in range(len(attestation.custody_bits_blocks[i])):
|
|
||||||
if attestation.aggregation_bits[j]:
|
|
||||||
attestation.custody_bits_blocks[i][j] = \
|
|
||||||
spec.compute_custody_bit(custody_secrets[j], test_vector) ^ (not valid_custody_bits)
|
|
||||||
|
|
||||||
if signed:
|
|
||||||
sign_attestation(spec, state, attestation)
|
|
||||||
|
|
||||||
return attestation
|
|
||||||
|
|
||||||
|
|
||||||
def get_valid_on_time_attestation(spec, state, slot=None, index=None,
|
|
||||||
shard_transition=None, valid_custody_bits=None, signed=False):
|
|
||||||
'''
|
'''
|
||||||
Construct on-time attestation for next slot
|
Construct on-time attestation for next slot
|
||||||
'''
|
'''
|
||||||
|
@ -149,7 +111,6 @@ def get_valid_on_time_attestation(spec, state, slot=None, index=None,
|
||||||
slot=slot,
|
slot=slot,
|
||||||
index=index,
|
index=index,
|
||||||
shard_transition=shard_transition,
|
shard_transition=shard_transition,
|
||||||
valid_custody_bits=valid_custody_bits,
|
|
||||||
signed=signed,
|
signed=signed,
|
||||||
on_time=True,
|
on_time=True,
|
||||||
)
|
)
|
||||||
|
@ -174,7 +135,6 @@ def get_valid_attestation(spec,
|
||||||
index=None,
|
index=None,
|
||||||
filter_participant_set=None,
|
filter_participant_set=None,
|
||||||
shard_transition=None,
|
shard_transition=None,
|
||||||
valid_custody_bits=None,
|
|
||||||
signed=False,
|
signed=False,
|
||||||
on_time=True):
|
on_time=True):
|
||||||
# If filter_participant_set filters everything, the attestation has 0 participants, and cannot be signed.
|
# If filter_participant_set filters everything, the attestation has 0 participants, and cannot be signed.
|
||||||
|
@ -203,14 +163,6 @@ def get_valid_attestation(spec,
|
||||||
# fill the attestation with (optionally filtered) participants, and optionally sign it
|
# fill the attestation with (optionally filtered) participants, and optionally sign it
|
||||||
fill_aggregate_attestation(spec, state, attestation, signed=signed, filter_participant_set=filter_participant_set)
|
fill_aggregate_attestation(spec, state, attestation, signed=signed, filter_participant_set=filter_participant_set)
|
||||||
|
|
||||||
if spec.fork == PHASE1 and on_time:
|
|
||||||
attestation = convert_to_valid_on_time_attestation(
|
|
||||||
spec, state, attestation,
|
|
||||||
shard_transition,
|
|
||||||
valid_custody_bits=valid_custody_bits,
|
|
||||||
signed=signed,
|
|
||||||
)
|
|
||||||
|
|
||||||
return attestation
|
return attestation
|
||||||
|
|
||||||
|
|
||||||
|
@ -230,43 +182,9 @@ def sign_aggregate_attestation(spec, state, attestation_data, participants: List
|
||||||
|
|
||||||
|
|
||||||
def sign_indexed_attestation(spec, state, indexed_attestation):
|
def sign_indexed_attestation(spec, state, indexed_attestation):
|
||||||
if spec.fork == PHASE0:
|
participants = indexed_attestation.attesting_indices
|
||||||
participants = indexed_attestation.attesting_indices
|
data = indexed_attestation.data
|
||||||
data = indexed_attestation.data
|
indexed_attestation.signature = sign_aggregate_attestation(spec, state, data, participants)
|
||||||
indexed_attestation.signature = sign_aggregate_attestation(spec, state, data, participants)
|
|
||||||
else:
|
|
||||||
participants = spec.get_indices_from_committee(
|
|
||||||
indexed_attestation.committee,
|
|
||||||
indexed_attestation.attestation.aggregation_bits,
|
|
||||||
)
|
|
||||||
data = indexed_attestation.attestation.data
|
|
||||||
if any(indexed_attestation.attestation.custody_bits_blocks):
|
|
||||||
sign_on_time_attestation(spec, state, indexed_attestation.attestation)
|
|
||||||
else:
|
|
||||||
indexed_attestation.attestation.signature = sign_aggregate_attestation(spec, state, data, participants)
|
|
||||||
|
|
||||||
|
|
||||||
def sign_on_time_attestation(spec, state, attestation):
|
|
||||||
if not any(attestation.custody_bits_blocks):
|
|
||||||
sign_attestation(spec, state, attestation)
|
|
||||||
return
|
|
||||||
|
|
||||||
committee = spec.get_beacon_committee(state, attestation.data.slot, attestation.data.index)
|
|
||||||
signatures = []
|
|
||||||
for block_index, custody_bits in enumerate(attestation.custody_bits_blocks):
|
|
||||||
for participant, abit, cbit in zip(committee, attestation.aggregation_bits, custody_bits):
|
|
||||||
if not abit:
|
|
||||||
continue
|
|
||||||
signatures.append(get_attestation_custody_signature(
|
|
||||||
spec,
|
|
||||||
state,
|
|
||||||
attestation.data,
|
|
||||||
block_index,
|
|
||||||
cbit,
|
|
||||||
privkeys[participant]
|
|
||||||
))
|
|
||||||
|
|
||||||
attestation.signature = bls.Aggregate(signatures)
|
|
||||||
|
|
||||||
|
|
||||||
def get_attestation_custody_signature(spec, state, attestation_data, block_index, bit, privkey):
|
def get_attestation_custody_signature(spec, state, attestation_data, block_index, bit, privkey):
|
||||||
|
@ -283,10 +201,6 @@ def get_attestation_custody_signature(spec, state, attestation_data, block_index
|
||||||
|
|
||||||
|
|
||||||
def sign_attestation(spec, state, attestation):
|
def sign_attestation(spec, state, attestation):
|
||||||
if spec.fork == PHASE1 and any(attestation.custody_bits_blocks):
|
|
||||||
sign_on_time_attestation(spec, state, attestation)
|
|
||||||
return
|
|
||||||
|
|
||||||
participants = spec.get_attesting_indices(
|
participants = spec.get_attesting_indices(
|
||||||
state,
|
state,
|
||||||
attestation.data,
|
attestation.data,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
from eth2spec.test.context import PHASE1
|
|
||||||
from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation, sign_indexed_attestation
|
from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation, sign_indexed_attestation
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,34 +40,19 @@ def get_indexed_attestation_participants(spec, indexed_att):
|
||||||
"""
|
"""
|
||||||
Wrapper around index-attestation to return the list of participant indices, regardless of spec phase.
|
Wrapper around index-attestation to return the list of participant indices, regardless of spec phase.
|
||||||
"""
|
"""
|
||||||
if spec.fork == PHASE1:
|
return list(indexed_att.attesting_indices)
|
||||||
return list(spec.get_indices_from_committee(
|
|
||||||
indexed_att.committee,
|
|
||||||
indexed_att.attestation.aggregation_bits,
|
|
||||||
))
|
|
||||||
else:
|
|
||||||
return list(indexed_att.attesting_indices)
|
|
||||||
|
|
||||||
|
|
||||||
def set_indexed_attestation_participants(spec, indexed_att, participants):
|
def set_indexed_attestation_participants(spec, indexed_att, participants):
|
||||||
"""
|
"""
|
||||||
Wrapper around index-attestation to return the list of participant indices, regardless of spec phase.
|
Wrapper around index-attestation to return the list of participant indices, regardless of spec phase.
|
||||||
"""
|
"""
|
||||||
if spec.fork == PHASE1:
|
indexed_att.attesting_indices = participants
|
||||||
indexed_att.attestation.aggregation_bits = [bool(i in participants) for i in indexed_att.committee]
|
|
||||||
else:
|
|
||||||
indexed_att.attesting_indices = participants
|
|
||||||
|
|
||||||
|
|
||||||
def get_attestation_1_data(spec, att_slashing):
|
def get_attestation_1_data(spec, att_slashing):
|
||||||
if spec.fork == PHASE1:
|
return att_slashing.attestation_1.data
|
||||||
return att_slashing.attestation_1.attestation.data
|
|
||||||
else:
|
|
||||||
return att_slashing.attestation_1.data
|
|
||||||
|
|
||||||
|
|
||||||
def get_attestation_2_data(spec, att_slashing):
|
def get_attestation_2_data(spec, att_slashing):
|
||||||
if spec.fork == PHASE1:
|
return att_slashing.attestation_2.data
|
||||||
return att_slashing.attestation_2.attestation.data
|
|
||||||
else:
|
|
||||||
return att_slashing.attestation_2.data
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ def bitlist_from_int(max_len, num_bits, n):
|
||||||
return Bitlist[max_len](*[(n >> i) & 0b1 for i in range(num_bits)])
|
return Bitlist[max_len](*[(n >> i) & 0b1 for i in range(num_bits)])
|
||||||
|
|
||||||
|
|
||||||
def get_valid_custody_slashing(spec, state, attestation, shard_transition, invalid_custody_bit=False):
|
def get_valid_custody_slashing(spec, state, attestation, shard_transition, custody_secret, data, data_index=0):
|
||||||
beacon_committee = spec.get_beacon_committee(
|
beacon_committee = spec.get_beacon_committee(
|
||||||
state,
|
state,
|
||||||
attestation.data.slot,
|
attestation.data.slot,
|
||||||
|
@ -68,21 +68,10 @@ def get_valid_custody_slashing(spec, state, attestation, shard_transition, inval
|
||||||
malefactor_index = beacon_committee[0]
|
malefactor_index = beacon_committee[0]
|
||||||
whistleblower_index = beacon_committee[-1]
|
whistleblower_index = beacon_committee[-1]
|
||||||
|
|
||||||
epoch = spec.get_randao_epoch_for_custody_period(attestation.data.target.epoch,
|
|
||||||
malefactor_index)
|
|
||||||
|
|
||||||
# Generate the responder key
|
|
||||||
domain = spec.get_domain(state, spec.DOMAIN_RANDAO, epoch)
|
|
||||||
signing_root = spec.compute_signing_root(spec.Epoch(epoch), domain)
|
|
||||||
malefactor_key = bls.Sign(privkeys[malefactor_index], signing_root)
|
|
||||||
data_index = 0
|
|
||||||
data = ByteList[spec.MAX_SHARD_BLOCK_SIZE](
|
|
||||||
get_custody_test_vector(shard_transition.shard_block_lengths[data_index]))
|
|
||||||
|
|
||||||
slashing = spec.CustodySlashing(
|
slashing = spec.CustodySlashing(
|
||||||
data_index=data_index,
|
data_index=data_index,
|
||||||
malefactor_index=malefactor_index,
|
malefactor_index=malefactor_index,
|
||||||
malefactor_secret=malefactor_key,
|
malefactor_secret=custody_secret,
|
||||||
whistleblower_index=whistleblower_index,
|
whistleblower_index=whistleblower_index,
|
||||||
shard_transition=shard_transition,
|
shard_transition=shard_transition,
|
||||||
attestation=attestation,
|
attestation=attestation,
|
||||||
|
@ -165,9 +154,9 @@ def get_valid_custody_chunk_response(spec, state, chunk_challenge, block_length,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_custody_test_vector(bytelength):
|
def get_custody_test_vector(bytelength, offset=0):
|
||||||
ints = bytelength // 4 + 1
|
ints = bytelength // 4 + 1
|
||||||
return (b"".join(i.to_bytes(4, "little") for i in range(ints)))[:bytelength]
|
return (b"".join((i + offset).to_bytes(4, "little") for i in range(ints)))[:bytelength]
|
||||||
|
|
||||||
|
|
||||||
def get_shard_transition(spec, start_slot, block_lengths):
|
def get_shard_transition(spec, start_slot, block_lengths):
|
||||||
|
@ -181,3 +170,30 @@ def get_shard_transition(spec, start_slot, block_lengths):
|
||||||
proposer_signature_aggregate=spec.BLSSignature(),
|
proposer_signature_aggregate=spec.BLSSignature(),
|
||||||
)
|
)
|
||||||
return shard_transition
|
return shard_transition
|
||||||
|
|
||||||
|
|
||||||
|
def get_custody_secret(spec, state, validator_index, epoch=None):
|
||||||
|
period = spec.get_custody_period_for_validator(validator_index, epoch if epoch is not None
|
||||||
|
else spec.get_current_epoch(state))
|
||||||
|
epoch_to_sign = spec.get_randao_epoch_for_custody_period(period, validator_index)
|
||||||
|
domain = spec.get_domain(state, spec.DOMAIN_RANDAO, epoch_to_sign)
|
||||||
|
signing_root = spec.compute_signing_root(spec.Epoch(epoch_to_sign), domain)
|
||||||
|
return bls.Sign(privkeys[validator_index], signing_root)
|
||||||
|
|
||||||
|
|
||||||
|
def get_custody_slashable_test_vector(spec, custody_secret, length, slashable=True):
|
||||||
|
test_vector = get_custody_test_vector(length)
|
||||||
|
offset = 0
|
||||||
|
while spec.compute_custody_bit(custody_secret, test_vector) != slashable:
|
||||||
|
offset += 1
|
||||||
|
test_vector = test_vector = get_custody_test_vector(length, offset)
|
||||||
|
return test_vector
|
||||||
|
|
||||||
|
|
||||||
|
def get_custody_slashable_shard_transition(spec, start_slot, block_lengths, custody_secret, slashable=True):
|
||||||
|
shard_transition = get_shard_transition(spec, start_slot, block_lengths)
|
||||||
|
slashable_test_vector = get_custody_slashable_test_vector(spec, custody_secret,
|
||||||
|
block_lengths[0], slashable=slashable)
|
||||||
|
block_data = ByteList[spec.MAX_SHARD_BLOCK_SIZE](slashable_test_vector)
|
||||||
|
shard_transition.shard_data_roots[0] = block_data.get_backing().get_left().merkle_root()
|
||||||
|
return shard_transition, slashable_test_vector
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from eth2spec.test.context import (
|
from eth2spec.test.context import (
|
||||||
PHASE0, PHASE1,
|
PHASE0,
|
||||||
spec_state_test, expect_assertion_error, always_bls, with_all_phases, with_phases
|
spec_state_test, expect_assertion_error, always_bls, with_all_phases, with_phases
|
||||||
)
|
)
|
||||||
from eth2spec.test.helpers.attestations import sign_indexed_attestation
|
from eth2spec.test.helpers.attestations import sign_indexed_attestation
|
||||||
|
@ -162,10 +162,7 @@ def test_same_data(spec, state):
|
||||||
|
|
||||||
indexed_att_1 = attester_slashing.attestation_1
|
indexed_att_1 = attester_slashing.attestation_1
|
||||||
att_2_data = get_attestation_2_data(spec, attester_slashing)
|
att_2_data = get_attestation_2_data(spec, attester_slashing)
|
||||||
if spec.fork == PHASE1:
|
indexed_att_1.data = att_2_data
|
||||||
indexed_att_1.attestation.data = att_2_data
|
|
||||||
else:
|
|
||||||
indexed_att_1.data = att_2_data
|
|
||||||
sign_indexed_attestation(spec, state, attester_slashing.attestation_1)
|
sign_indexed_attestation(spec, state, attester_slashing.attestation_1)
|
||||||
|
|
||||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||||
|
|
|
@ -25,22 +25,9 @@ def test_on_time_success(spec, state):
|
||||||
@with_all_phases_except(['phase0'])
|
@with_all_phases_except(['phase0'])
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
@always_bls
|
@always_bls
|
||||||
def test_on_time_empty_custody_bits_blocks(spec, state):
|
def test_late_success(spec, state):
|
||||||
attestation = get_valid_late_attestation(spec, state, signed=True)
|
attestation = get_valid_late_attestation(spec, state, signed=True)
|
||||||
|
|
||||||
assert not any(attestation.custody_bits_blocks)
|
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY)
|
|
||||||
|
|
||||||
yield from run_attestation_processing(spec, state, attestation, False)
|
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases_except(['phase0'])
|
|
||||||
@spec_state_test
|
|
||||||
@always_bls
|
|
||||||
def test_late_with_custody_bits_blocks(spec, state):
|
|
||||||
attestation = get_valid_on_time_attestation(spec, state, signed=True)
|
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY + 1)
|
transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY + 1)
|
||||||
|
|
||||||
yield from run_attestation_processing(spec, state, attestation, False)
|
yield from run_attestation_processing(spec, state, attestation)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from eth2spec.test.helpers.custody import (
|
from eth2spec.test.helpers.custody import (
|
||||||
get_valid_custody_slashing,
|
get_valid_custody_slashing,
|
||||||
get_shard_transition,
|
get_custody_secret,
|
||||||
|
get_custody_slashable_shard_transition,
|
||||||
)
|
)
|
||||||
from eth2spec.test.helpers.attestations import (
|
from eth2spec.test.helpers.attestations import (
|
||||||
get_valid_on_time_attestation,
|
get_valid_on_time_attestation,
|
||||||
|
@ -52,15 +53,39 @@ def run_custody_slashing_processing(spec, state, custody_slashing, valid=True, c
|
||||||
yield 'post', state
|
yield 'post', state
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases_except(['phase0'])
|
def run_standard_custody_slashing_test(spec,
|
||||||
@spec_state_test
|
state,
|
||||||
def test_custody_slashing(spec, state):
|
shard_lateness=None,
|
||||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
shard=None,
|
||||||
shard = 0
|
validator_index=None,
|
||||||
|
block_lengths=None,
|
||||||
|
slashing_message_data=None,
|
||||||
|
correct=True,
|
||||||
|
valid=True):
|
||||||
|
if shard_lateness is None:
|
||||||
|
shard_lateness = spec.SLOTS_PER_EPOCH
|
||||||
|
transition_to(spec, state, state.slot + shard_lateness)
|
||||||
|
|
||||||
|
if shard is None:
|
||||||
|
shard = 0
|
||||||
|
if validator_index is None:
|
||||||
|
validator_index = spec.get_beacon_committee(state, state.slot, shard)[0]
|
||||||
|
|
||||||
offset_slots = spec.get_offset_slots(state, shard)
|
offset_slots = spec.get_offset_slots(state, shard)
|
||||||
shard_transition = get_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
if block_lengths is None:
|
||||||
|
block_lengths = [2**15 // 3] * len(offset_slots)
|
||||||
|
|
||||||
|
custody_secret = get_custody_secret(spec, state, validator_index)
|
||||||
|
shard_transition, slashable_test_vector = get_custody_slashable_shard_transition(
|
||||||
|
spec,
|
||||||
|
state.slot,
|
||||||
|
block_lengths,
|
||||||
|
custody_secret,
|
||||||
|
slashable=correct,
|
||||||
|
)
|
||||||
|
|
||||||
attestation = get_valid_on_time_attestation(spec, state, index=shard, signed=True,
|
attestation = get_valid_on_time_attestation(spec, state, index=shard, signed=True,
|
||||||
shard_transition=shard_transition, valid_custody_bits=False)
|
shard_transition=shard_transition)
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY)
|
transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY)
|
||||||
|
|
||||||
|
@ -68,109 +93,45 @@ def test_custody_slashing(spec, state):
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_CUSTODY_PERIOD - 1))
|
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_CUSTODY_PERIOD - 1))
|
||||||
|
|
||||||
slashing = get_valid_custody_slashing(spec, state, attestation, shard_transition)
|
slashing = get_valid_custody_slashing(spec, state, attestation, shard_transition,
|
||||||
|
custody_secret, slashable_test_vector)
|
||||||
|
|
||||||
yield from run_custody_slashing_processing(spec, state, slashing, correct=True)
|
if slashing_message_data is not None:
|
||||||
|
slashing.message.data = slashing_message_data
|
||||||
|
|
||||||
|
yield from run_custody_slashing_processing(spec, state, slashing, valid=valid, correct=correct)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases_except(['phase0'])
|
||||||
|
@spec_state_test
|
||||||
|
def test_custody_slashing(spec, state):
|
||||||
|
yield from run_standard_custody_slashing_test(spec, state)
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases_except(['phase0'])
|
@with_all_phases_except(['phase0'])
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
def test_incorrect_custody_slashing(spec, state):
|
def test_incorrect_custody_slashing(spec, state):
|
||||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
yield from run_standard_custody_slashing_test(spec, state, correct=False)
|
||||||
shard = 0
|
|
||||||
offset_slots = spec.get_offset_slots(state, shard)
|
|
||||||
shard_transition = get_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
|
||||||
attestation = get_valid_on_time_attestation(spec, state, index=shard, signed=True,
|
|
||||||
shard_transition=shard_transition, valid_custody_bits=True)
|
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY)
|
|
||||||
|
|
||||||
_, _, _ = run_attestation_processing(spec, state, attestation)
|
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_CUSTODY_PERIOD - 1))
|
|
||||||
|
|
||||||
slashing = get_valid_custody_slashing(spec, state, attestation, shard_transition)
|
|
||||||
|
|
||||||
yield from run_custody_slashing_processing(spec, state, slashing, correct=False)
|
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases_except(['phase0'])
|
@with_all_phases_except(['phase0'])
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
def test_multiple_epochs_custody(spec, state):
|
def test_multiple_epochs_custody(spec, state):
|
||||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * 3)
|
yield from run_standard_custody_slashing_test(spec, state, shard_lateness=spec.SLOTS_PER_EPOCH * 3)
|
||||||
shard = 0
|
|
||||||
offset_slots = spec.get_offset_slots(state, shard)
|
|
||||||
shard_transition = get_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
|
||||||
attestation = get_valid_on_time_attestation(spec, state, index=shard, signed=True,
|
|
||||||
shard_transition=shard_transition, valid_custody_bits=False)
|
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY)
|
|
||||||
|
|
||||||
_, _, _ = run_attestation_processing(spec, state, attestation)
|
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_CUSTODY_PERIOD - 1))
|
|
||||||
|
|
||||||
slashing = get_valid_custody_slashing(spec, state, attestation, shard_transition)
|
|
||||||
|
|
||||||
yield from run_custody_slashing_processing(spec, state, slashing, correct=True)
|
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases_except(['phase0'])
|
@with_all_phases_except(['phase0'])
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
def test_many_epochs_custody(spec, state):
|
def test_many_epochs_custody(spec, state):
|
||||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * 20)
|
yield from run_standard_custody_slashing_test(spec, state, shard_lateness=spec.SLOTS_PER_EPOCH * 10)
|
||||||
shard = 0
|
|
||||||
offset_slots = spec.get_offset_slots(state, shard)
|
|
||||||
shard_transition = get_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
|
||||||
attestation = get_valid_on_time_attestation(spec, state, index=shard, signed=True,
|
|
||||||
shard_transition=shard_transition, valid_custody_bits=False)
|
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY)
|
|
||||||
|
|
||||||
_, _, _ = run_attestation_processing(spec, state, attestation)
|
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_CUSTODY_PERIOD - 1))
|
|
||||||
|
|
||||||
slashing = get_valid_custody_slashing(spec, state, attestation, shard_transition)
|
|
||||||
|
|
||||||
yield from run_custody_slashing_processing(spec, state, slashing, correct=True)
|
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases_except(['phase0'])
|
|
||||||
@spec_state_test
|
|
||||||
def test_off_chain_attestation(spec, state):
|
|
||||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
|
||||||
shard = 0
|
|
||||||
offset_slots = spec.get_offset_slots(state, shard)
|
|
||||||
shard_transition = get_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
|
||||||
attestation = get_valid_on_time_attestation(spec, state, index=shard, signed=True,
|
|
||||||
shard_transition=shard_transition, valid_custody_bits=False)
|
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_CUSTODY_PERIOD - 1))
|
|
||||||
|
|
||||||
slashing = get_valid_custody_slashing(spec, state, attestation, shard_transition)
|
|
||||||
|
|
||||||
yield from run_custody_slashing_processing(spec, state, slashing, correct=True)
|
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases_except(['phase0'])
|
@with_all_phases_except(['phase0'])
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
def test_invalid_custody_slashing(spec, state):
|
def test_invalid_custody_slashing(spec, state):
|
||||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
yield from run_standard_custody_slashing_test(
|
||||||
shard = 0
|
spec,
|
||||||
offset_slots = spec.get_offset_slots(state, shard)
|
state,
|
||||||
shard_transition = get_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
slashing_message_data=ByteList[spec.MAX_SHARD_BLOCK_SIZE](),
|
||||||
attestation = get_valid_on_time_attestation(spec, state, index=shard, signed=True,
|
valid=False,
|
||||||
shard_transition=shard_transition, valid_custody_bits=False)
|
)
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + spec.MIN_ATTESTATION_INCLUSION_DELAY)
|
|
||||||
|
|
||||||
_, _, _ = run_attestation_processing(spec, state, attestation)
|
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * (spec.EPOCHS_PER_CUSTODY_PERIOD - 1))
|
|
||||||
|
|
||||||
slashing = get_valid_custody_slashing(spec, state, attestation, shard_transition)
|
|
||||||
|
|
||||||
slashing.message.data = ByteList[spec.MAX_SHARD_BLOCK_SIZE]()
|
|
||||||
|
|
||||||
yield from run_custody_slashing_processing(spec, state, slashing, valid=False)
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
from eth2spec.test.helpers.custody import (
|
from eth2spec.test.helpers.custody import (
|
||||||
get_valid_chunk_challenge,
|
get_valid_chunk_challenge,
|
||||||
get_shard_transition,
|
get_shard_transition,
|
||||||
get_valid_custody_key_reveal,
|
|
||||||
)
|
)
|
||||||
from eth2spec.test.helpers.attestations import (
|
from eth2spec.test.helpers.attestations import (
|
||||||
get_valid_on_time_attestation,
|
get_valid_on_time_attestation,
|
||||||
)
|
)
|
||||||
from eth2spec.test.helpers.state import next_epoch_via_block, transition_to
|
from eth2spec.test.helpers.state import transition_to
|
||||||
from eth2spec.test.context import (
|
from eth2spec.test.context import (
|
||||||
with_all_phases_except,
|
with_all_phases_except,
|
||||||
spec_state_test,
|
spec_state_test,
|
||||||
|
@ -17,7 +16,6 @@ from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_ep
|
||||||
from eth2spec.test.phase_1.block_processing.test_process_chunk_challenge import (
|
from eth2spec.test.phase_1.block_processing.test_process_chunk_challenge import (
|
||||||
run_chunk_challenge_processing,
|
run_chunk_challenge_processing,
|
||||||
)
|
)
|
||||||
from eth2spec.test.phase_1.block_processing.test_process_custody_key_reveal import run_custody_key_reveal_processing
|
|
||||||
|
|
||||||
|
|
||||||
def run_process_challenge_deadlines(spec, state):
|
def run_process_challenge_deadlines(spec, state):
|
||||||
|
@ -44,32 +42,15 @@ def test_validator_slashed_after_chunk_challenge(spec, state):
|
||||||
attestation.data.index
|
attestation.data.index
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
spec.initiate_validator_exit(state, validator_index)
|
|
||||||
assert state.validators[validator_index].withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
|
||||||
|
|
||||||
assert state.validators[validator_index].withdrawable_epoch == spec.FAR_FUTURE_EPOCH
|
|
||||||
|
|
||||||
while spec.get_current_epoch(state) < state.validators[validator_index].exit_epoch:
|
|
||||||
next_epoch_via_block(spec, state)
|
|
||||||
while (state.validators[validator_index].next_custody_secret_to_reveal
|
|
||||||
<= spec.get_custody_period_for_validator(
|
|
||||||
validator_index,
|
|
||||||
state.validators[validator_index].exit_epoch - 1)):
|
|
||||||
custody_key_reveal = get_valid_custody_key_reveal(spec, state, validator_index=validator_index)
|
|
||||||
_, _, _ = run_custody_key_reveal_processing(spec, state, custody_key_reveal)
|
|
||||||
|
|
||||||
next_epoch_via_block(spec, state)
|
|
||||||
|
|
||||||
challenge = get_valid_chunk_challenge(spec, state, attestation, shard_transition)
|
challenge = get_valid_chunk_challenge(spec, state, attestation, shard_transition)
|
||||||
|
|
||||||
_, _, _ = run_chunk_challenge_processing(spec, state, challenge)
|
_, _, _ = run_chunk_challenge_processing(spec, state, challenge)
|
||||||
|
|
||||||
assert state.validators[validator_index].slashed == 0
|
assert state.validators[validator_index].slashed == 0
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + (spec.CUSTODY_RESPONSE_DEADLINE +
|
transition_to(spec, state, state.slot + spec.MAX_CHUNK_CHALLENGE_DELAY * spec.SLOTS_PER_EPOCH)
|
||||||
spec.EPOCHS_PER_CUSTODY_PERIOD) * spec.SLOTS_PER_EPOCH)
|
|
||||||
|
state.validators[validator_index].slashed = 0
|
||||||
|
|
||||||
yield from run_process_challenge_deadlines(spec, state)
|
yield from run_process_challenge_deadlines(spec, state)
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,13 @@ def run_process_challenge_deadlines(spec, state):
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
def test_validator_slashed_after_reveal_deadline(spec, state):
|
def test_validator_slashed_after_reveal_deadline(spec, state):
|
||||||
assert state.validators[0].slashed == 0
|
assert state.validators[0].slashed == 0
|
||||||
|
|
||||||
transition_to(spec, state, spec.get_randao_epoch_for_custody_period(0, 0) * spec.SLOTS_PER_EPOCH)
|
transition_to(spec, state, spec.get_randao_epoch_for_custody_period(0, 0) * spec.SLOTS_PER_EPOCH)
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + ((spec.CUSTODY_RESPONSE_DEADLINE)
|
# Need to run at least one reveal so that not all validators are slashed (otherwise spec fails to find proposers)
|
||||||
* spec.SLOTS_PER_EPOCH))
|
custody_key_reveal = get_valid_custody_key_reveal(spec, state, validator_index=1)
|
||||||
|
_, _, _ = run_custody_key_reveal_processing(spec, state, custody_key_reveal)
|
||||||
|
|
||||||
|
transition_to(spec, state, state.slot + spec.EPOCHS_PER_CUSTODY_PERIOD * spec.SLOTS_PER_EPOCH)
|
||||||
|
|
||||||
state.validators[0].slashed = 0
|
state.validators[0].slashed = 0
|
||||||
|
|
||||||
|
@ -34,15 +36,14 @@ def test_validator_slashed_after_reveal_deadline(spec, state):
|
||||||
@with_all_phases_except(['phase0'])
|
@with_all_phases_except(['phase0'])
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
def test_validator_not_slashed_after_reveal(spec, state):
|
def test_validator_not_slashed_after_reveal(spec, state):
|
||||||
state.slot += spec.EPOCHS_PER_CUSTODY_PERIOD * spec.SLOTS_PER_EPOCH
|
transition_to(spec, state, spec.EPOCHS_PER_CUSTODY_PERIOD * spec.SLOTS_PER_EPOCH)
|
||||||
custody_key_reveal = get_valid_custody_key_reveal(spec, state)
|
custody_key_reveal = get_valid_custody_key_reveal(spec, state)
|
||||||
|
|
||||||
_, _, _ = run_custody_key_reveal_processing(spec, state, custody_key_reveal)
|
_, _, _ = run_custody_key_reveal_processing(spec, state, custody_key_reveal)
|
||||||
|
|
||||||
assert state.validators[0].slashed == 0
|
assert state.validators[0].slashed == 0
|
||||||
|
|
||||||
transition_to(spec, state, state.slot + ((spec.CUSTODY_RESPONSE_DEADLINE)
|
transition_to(spec, state, state.slot + spec.EPOCHS_PER_CUSTODY_PERIOD * spec.SLOTS_PER_EPOCH)
|
||||||
* spec.SLOTS_PER_EPOCH))
|
|
||||||
|
|
||||||
yield from run_process_challenge_deadlines(spec, state)
|
yield from run_process_challenge_deadlines(spec, state)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue