mirror of
https://github.com/status-im/eth2.0-specs.git
synced 2025-02-20 22:38:11 +00:00
Merge branch 'dev' into shard-fork-choice-fix
This commit is contained in:
commit
f6fd151872
@ -2,7 +2,7 @@
|
||||
|
||||
[](https://discord.gg/hpFs23p) [](https://gitter.im/ethereum/sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
To learn more about sharding and Ethereum 2.0 (Serenity), see the [sharding FAQ](https://github.com/ethereum/wiki/wiki/Sharding-FAQ) and the [research compendium](https://notes.ethereum.org/s/H1PGqDhpm).
|
||||
To learn more about sharding and Ethereum 2.0 (Serenity), see the [sharding FAQ](https://eth.wiki/sharding/Sharding-FAQs) and the [research compendium](https://notes.ethereum.org/s/H1PGqDhpm).
|
||||
|
||||
This repository hosts the current Eth2 specifications. Discussions about design rationale and proposed changes can be brought up and discussed as issues. Solidified, agreed-upon changes to the spec can be made through pull requests.
|
||||
|
||||
|
@ -15,7 +15,7 @@ Over time, the need to sync an older state may be deprecated.
|
||||
In this case, the prefix on the new constant may be removed, and the old constant will keep a special name before completely being removed.
|
||||
|
||||
A previous iteration of forking made use of "timelines", but this collides with the definitions used in the spec (constants for special forking slots, etc.), and was not integrated sufficiently in any of the spec tools or implementations.
|
||||
Instead, the config essentially doubles as fork definition now, e.g. changing the value for `PHASE_1_GENESIS_SLOT` changes the fork.
|
||||
Instead, the config essentially doubles as fork definition now, e.g. changing the value for `PHASE_1_FORK_SLOT` changes the fork.
|
||||
|
||||
Another reason to prefer forking through constants is the ability to program a forking moment based on context, instead of being limited to a static slot number.
|
||||
|
||||
|
@ -51,6 +51,9 @@ SECONDS_PER_ETH1_BLOCK: 14
|
||||
|
||||
# Deposit contract
|
||||
# ---------------------------------------------------------------
|
||||
# Ethereum PoW Mainnet
|
||||
DEPOSIT_CHAIN_ID: 1
|
||||
DEPOSIT_NETWORK_ID: 1
|
||||
# **TBD**
|
||||
DEPOSIT_CONTRACT_ADDRESS: 0x1234567890123456789012345678901234567890
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
# ---------------------------------------------------------------
|
||||
PHASE_1_FORK_VERSION: 0x01000000
|
||||
# [STUB]
|
||||
PHASE_1_GENESIS_SLOT: 32
|
||||
PHASE_1_FORK_SLOT: 0
|
||||
INITIAL_ACTIVE_SHARDS: 64
|
||||
|
||||
|
||||
|
@ -51,6 +51,9 @@ SECONDS_PER_ETH1_BLOCK: 14
|
||||
|
||||
# Deposit contract
|
||||
# ---------------------------------------------------------------
|
||||
# Ethereum Goerli testnet
|
||||
DEPOSIT_CHAIN_ID: 5
|
||||
DEPOSIT_NETWORK_ID: 5
|
||||
# **TBD**
|
||||
DEPOSIT_CONTRACT_ADDRESS: 0x1234567890123456789012345678901234567890
|
||||
|
||||
|
@ -5,8 +5,8 @@
|
||||
# ---------------------------------------------------------------
|
||||
# [customized] for testnet distinction
|
||||
PHASE_1_FORK_VERSION: 0x01000001
|
||||
# [customized] for testing
|
||||
PHASE_1_GENESIS_SLOT: 8
|
||||
# [STUB]
|
||||
PHASE_1_FORK_SLOT: 0
|
||||
# [customized] reduced for testing
|
||||
INITIAL_ACTIVE_SHARDS: 2
|
||||
|
||||
|
21
setup.py
21
setup.py
@ -109,9 +109,9 @@ from dataclasses import (
|
||||
|
||||
from lru import LRU
|
||||
|
||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root, copy, uint_to_bytes
|
||||
from eth2spec.utils.ssz.ssz_typing import (
|
||||
View, boolean, Container, List, Vector, uint64,
|
||||
View, boolean, Container, List, Vector, uint8, uint32, uint64,
|
||||
Bytes1, Bytes4, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector,
|
||||
)
|
||||
from eth2spec.utils import bls
|
||||
@ -133,9 +133,9 @@ from dataclasses import (
|
||||
|
||||
from lru import LRU
|
||||
|
||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root, copy, uint_to_bytes
|
||||
from eth2spec.utils.ssz.ssz_typing import (
|
||||
View, boolean, Container, List, Vector, uint64, uint8, bit,
|
||||
View, boolean, Container, List, Vector, uint8, uint32, uint64, bit,
|
||||
ByteList, ByteVector, Bytes1, Bytes4, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector,
|
||||
)
|
||||
from eth2spec.utils import bls
|
||||
@ -156,11 +156,6 @@ def ceillog2(x: uint64) -> int:
|
||||
return (x - 1).bit_length()
|
||||
'''
|
||||
PHASE0_SUNDRY_FUNCTIONS = '''
|
||||
# Monkey patch hash cache
|
||||
_hash = hash
|
||||
hash_cache: Dict[bytes, Bytes32] = {}
|
||||
|
||||
|
||||
def get_eth1_data(block: Eth1Block) -> Eth1Data:
|
||||
"""
|
||||
A stub function return mocking Eth1Data.
|
||||
@ -171,12 +166,6 @@ def get_eth1_data(block: Eth1Block) -> Eth1Data:
|
||||
block_hash=hash_tree_root(block))
|
||||
|
||||
|
||||
def hash(x: bytes) -> Bytes32: # type: ignore
|
||||
if x not in hash_cache:
|
||||
hash_cache[x] = Bytes32(_hash(x))
|
||||
return hash_cache[x]
|
||||
|
||||
|
||||
def cache_this(key_fn, value_fn, lru_size): # type: ignore
|
||||
cache_dict = LRU(size=lru_size)
|
||||
|
||||
@ -547,7 +536,7 @@ setup(
|
||||
"py_ecc==4.0.0",
|
||||
"milagro_bls_binding==1.3.0",
|
||||
"dataclasses==0.6",
|
||||
"remerkleable==0.1.16",
|
||||
"remerkleable==0.1.17",
|
||||
"ruamel.yaml==0.16.5",
|
||||
"lru-dict==1.1.6"
|
||||
]
|
||||
|
@ -55,8 +55,8 @@
|
||||
- [Math](#math)
|
||||
- [`integer_squareroot`](#integer_squareroot)
|
||||
- [`xor`](#xor)
|
||||
- [`int_to_bytes`](#int_to_bytes)
|
||||
- [`bytes_to_int`](#bytes_to_int)
|
||||
- [`uint_to_bytes`](#uint_to_bytes)
|
||||
- [`bytes_to_uint64`](#bytes_to_uint64)
|
||||
- [Crypto](#crypto)
|
||||
- [`hash`](#hash)
|
||||
- [`hash_tree_root`](#hash_tree_root)
|
||||
@ -170,9 +170,9 @@ The following values are (non-configurable) constants used throughout the specif
|
||||
| `GENESIS_SLOT` | `Slot(0)` |
|
||||
| `GENESIS_EPOCH` | `Epoch(0)` |
|
||||
| `FAR_FUTURE_EPOCH` | `Epoch(2**64 - 1)` |
|
||||
| `BASE_REWARDS_PER_EPOCH` | `4` |
|
||||
| `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) |
|
||||
| `JUSTIFICATION_BITS_LENGTH` | `4` |
|
||||
| `BASE_REWARDS_PER_EPOCH` | `uint64(4)` |
|
||||
| `DEPOSIT_CONTRACT_TREE_DEPTH` | `uint64(2**5)` (= 32) |
|
||||
| `JUSTIFICATION_BITS_LENGTH` | `uint64(4)` |
|
||||
| `ENDIANNESS` | `'little'` |
|
||||
|
||||
## Configuration
|
||||
@ -183,18 +183,18 @@ The following values are (non-configurable) constants used throughout the specif
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `ETH1_FOLLOW_DISTANCE` | `2**10` (= 1,024) |
|
||||
| `MAX_COMMITTEES_PER_SLOT` | `2**6` (= 64) |
|
||||
| `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) |
|
||||
| `MAX_VALIDATORS_PER_COMMITTEE` | `2**11` (= 2,048) |
|
||||
| `MIN_PER_EPOCH_CHURN_LIMIT` | `2**2` (= 4) |
|
||||
| `CHURN_LIMIT_QUOTIENT` | `2**16` (= 65,536) |
|
||||
| `SHUFFLE_ROUND_COUNT` | `90` |
|
||||
| `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT` | `2**14` (= 16,384) |
|
||||
| `MIN_GENESIS_TIME` | `1578009600` (Jan 3, 2020) |
|
||||
| `HYSTERESIS_QUOTIENT` | `4` |
|
||||
| `HYSTERESIS_DOWNWARD_MULTIPLIER` | `1` |
|
||||
| `HYSTERESIS_UPWARD_MULTIPLIER` | `5` |
|
||||
| `ETH1_FOLLOW_DISTANCE` | `uint64(2**10)` (= 1,024) |
|
||||
| `MAX_COMMITTEES_PER_SLOT` | `uint64(2**6)` (= 64) |
|
||||
| `TARGET_COMMITTEE_SIZE` | `uint64(2**7)` (= 128) |
|
||||
| `MAX_VALIDATORS_PER_COMMITTEE` | `uint64(2**11)` (= 2,048) |
|
||||
| `MIN_PER_EPOCH_CHURN_LIMIT` | `uint64(2**2)` (= 4) |
|
||||
| `CHURN_LIMIT_QUOTIENT` | `uint64(2**16)` (= 65,536) |
|
||||
| `SHUFFLE_ROUND_COUNT` | `uint64(90)` |
|
||||
| `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT` | `uint64(2**14)` (= 16,384) |
|
||||
| `MIN_GENESIS_TIME` | `uint64(1578009600)` (Jan 3, 2020) |
|
||||
| `HYSTERESIS_QUOTIENT` | `uint64(4)` |
|
||||
| `HYSTERESIS_DOWNWARD_MULTIPLIER` | `uint64(1)` |
|
||||
| `HYSTERESIS_UPWARD_MULTIPLIER` | `uint64(5)` |
|
||||
|
||||
- For the safety of committees, `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](http://web.archive.org/web/20190504131341/https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes of at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.)
|
||||
|
||||
@ -218,37 +218,37 @@ The following values are (non-configurable) constants used throughout the specif
|
||||
|
||||
| Name | Value | Unit | Duration |
|
||||
| - | - | :-: | :-: |
|
||||
| `GENESIS_DELAY` | `172800` | seconds | 2 days |
|
||||
| `SECONDS_PER_SLOT` | `12` | seconds | 12 seconds |
|
||||
| `SECONDS_PER_ETH1_BLOCK` | `14` | seconds | 14 seconds |
|
||||
| `MIN_ATTESTATION_INCLUSION_DELAY` | `2**0` (= 1) | slots | 12 seconds |
|
||||
| `SLOTS_PER_EPOCH` | `2**5` (= 32) | slots | 6.4 minutes |
|
||||
| `MIN_SEED_LOOKAHEAD` | `2**0` (= 1) | epochs | 6.4 minutes |
|
||||
| `MAX_SEED_LOOKAHEAD` | `2**2` (= 4) | epochs | 25.6 minutes |
|
||||
| `MIN_EPOCHS_TO_INACTIVITY_PENALTY` | `2**2` (= 4) | epochs | 25.6 minutes |
|
||||
| `EPOCHS_PER_ETH1_VOTING_PERIOD` | `2**5` (= 32) | epochs | ~3.4 hours |
|
||||
| `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~27 hours |
|
||||
| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours |
|
||||
| `SHARD_COMMITTEE_PERIOD` | `Epoch(2**8)` (= 256) | epochs | ~27 hours |
|
||||
| `GENESIS_DELAY` | `uint64(172800)` | seconds | 2 days |
|
||||
| `SECONDS_PER_SLOT` | `uint64(12)` | seconds | 12 seconds |
|
||||
| `SECONDS_PER_ETH1_BLOCK` | `uint64(14)` | seconds | 14 seconds |
|
||||
| `MIN_ATTESTATION_INCLUSION_DELAY` | `uint64(2**0)` (= 1) | slots | 12 seconds |
|
||||
| `SLOTS_PER_EPOCH` | `uint64(2**5)` (= 32) | slots | 6.4 minutes |
|
||||
| `MIN_SEED_LOOKAHEAD` | `uint64(2**0)` (= 1) | epochs | 6.4 minutes |
|
||||
| `MAX_SEED_LOOKAHEAD` | `uint64(2**2)` (= 4) | epochs | 25.6 minutes |
|
||||
| `MIN_EPOCHS_TO_INACTIVITY_PENALTY` | `uint64(2**2)` (= 4) | epochs | 25.6 minutes |
|
||||
| `EPOCHS_PER_ETH1_VOTING_PERIOD` | `uint64(2**5)` (= 32) | epochs | ~3.4 hours |
|
||||
| `SLOTS_PER_HISTORICAL_ROOT` | `uint64(2**13)` (= 8,192) | slots | ~27 hours |
|
||||
| `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `uint64(2**8)` (= 256) | epochs | ~27 hours |
|
||||
| `SHARD_COMMITTEE_PERIOD` | `uint64(2**8)` (= 256) | epochs | ~27 hours |
|
||||
|
||||
### State list lengths
|
||||
|
||||
| Name | Value | Unit | Duration |
|
||||
| - | - | :-: | :-: |
|
||||
| `EPOCHS_PER_HISTORICAL_VECTOR` | `2**16` (= 65,536) | epochs | ~0.8 years |
|
||||
| `EPOCHS_PER_SLASHINGS_VECTOR` | `2**13` (= 8,192) | epochs | ~36 days |
|
||||
| `HISTORICAL_ROOTS_LIMIT` | `2**24` (= 16,777,216) | historical roots | ~52,262 years |
|
||||
| `VALIDATOR_REGISTRY_LIMIT` | `2**40` (= 1,099,511,627,776) | validators |
|
||||
| `EPOCHS_PER_HISTORICAL_VECTOR` | `uint64(2**16)` (= 65,536) | epochs | ~0.8 years |
|
||||
| `EPOCHS_PER_SLASHINGS_VECTOR` | `uint64(2**13)` (= 8,192) | epochs | ~36 days |
|
||||
| `HISTORICAL_ROOTS_LIMIT` | `uint64(2**24)` (= 16,777,216) | historical roots | ~52,262 years |
|
||||
| `VALIDATOR_REGISTRY_LIMIT` | `uint64(2**40)` (= 1,099,511,627,776) | validators |
|
||||
|
||||
### Rewards and penalties
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `BASE_REWARD_FACTOR` | `2**6` (= 64) |
|
||||
| `WHISTLEBLOWER_REWARD_QUOTIENT` | `2**9` (= 512) |
|
||||
| `PROPOSER_REWARD_QUOTIENT` | `2**3` (= 8) |
|
||||
| `INACTIVITY_PENALTY_QUOTIENT` | `2**24` (= 16,777,216) |
|
||||
| `MIN_SLASHING_PENALTY_QUOTIENT` | `2**5` (= 32) |
|
||||
| `BASE_REWARD_FACTOR` | `uint64(2**6)` (= 64) |
|
||||
| `WHISTLEBLOWER_REWARD_QUOTIENT` | `uint64(2**9)` (= 512) |
|
||||
| `PROPOSER_REWARD_QUOTIENT` | `uint64(2**3)` (= 8) |
|
||||
| `INACTIVITY_PENALTY_QUOTIENT` | `uint64(2**24)` (= 16,777,216) |
|
||||
| `MIN_SLASHING_PENALTY_QUOTIENT` | `uint64(2**5)` (= 32) |
|
||||
|
||||
- The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**12` epochs (about 18 days) is the time it takes the inactivity penalty to reduce the balance of non-participating validators to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline validators after `n` epochs is about `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(n**2/2)`; so after `INVERSE_SQRT_E_DROP_TIME` epochs, it is roughly `(1 - 1/INACTIVITY_PENALTY_QUOTIENT)**(INACTIVITY_PENALTY_QUOTIENT/2) ~= 1/sqrt(e)`.
|
||||
|
||||
@ -576,24 +576,18 @@ def xor(bytes_1: Bytes32, bytes_2: Bytes32) -> Bytes32:
|
||||
return Bytes32(a ^ b for a, b in zip(bytes_1, bytes_2))
|
||||
```
|
||||
|
||||
#### `int_to_bytes`
|
||||
#### `uint_to_bytes`
|
||||
|
||||
`def uint_to_bytes(n: uint) -> bytes` is a function for serializing the `uint` type object to bytes in ``ENDIANNESS``-endian. The expected length of the output is the byte-length of the `uint` type.
|
||||
|
||||
#### `bytes_to_uint64`
|
||||
|
||||
```python
|
||||
def int_to_bytes(n: uint64, length: uint64) -> bytes:
|
||||
"""
|
||||
Return the ``length``-byte serialization of ``n`` in ``ENDIANNESS``-endian.
|
||||
"""
|
||||
return n.to_bytes(length, ENDIANNESS)
|
||||
```
|
||||
|
||||
#### `bytes_to_int`
|
||||
|
||||
```python
|
||||
def bytes_to_int(data: bytes) -> uint64:
|
||||
def bytes_to_uint64(data: bytes) -> uint64:
|
||||
"""
|
||||
Return the integer deserialization of ``data`` interpreted as ``ENDIANNESS``-endian.
|
||||
"""
|
||||
return int.from_bytes(data, ENDIANNESS)
|
||||
return uint64(int.from_bytes(data, ENDIANNESS))
|
||||
```
|
||||
|
||||
### Crypto
|
||||
@ -733,11 +727,15 @@ def compute_shuffled_index(index: uint64, index_count: uint64, seed: Bytes32) ->
|
||||
# Swap or not (https://link.springer.com/content/pdf/10.1007%2F978-3-642-32009-5_1.pdf)
|
||||
# See the 'generalized domain' algorithm on page 3
|
||||
for current_round in range(SHUFFLE_ROUND_COUNT):
|
||||
pivot = bytes_to_int(hash(seed + int_to_bytes(current_round, length=1))[0:8]) % index_count
|
||||
pivot = bytes_to_uint64(hash(seed + uint_to_bytes(uint8(current_round)))[0:8]) % index_count
|
||||
flip = (pivot + index_count - index) % index_count
|
||||
position = max(index, flip)
|
||||
source = hash(seed + int_to_bytes(current_round, length=1) + int_to_bytes(position // 256, length=4))
|
||||
byte = source[(position % 256) // 8]
|
||||
source = hash(
|
||||
seed
|
||||
+ uint_to_bytes(uint8(current_round))
|
||||
+ uint_to_bytes(uint32(position // 256))
|
||||
)
|
||||
byte = uint8(source[(position % 256) // 8])
|
||||
bit = (byte >> (position % 8)) % 2
|
||||
index = flip if bit else index
|
||||
|
||||
@ -753,10 +751,11 @@ def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex]
|
||||
"""
|
||||
assert len(indices) > 0
|
||||
MAX_RANDOM_BYTE = 2**8 - 1
|
||||
i = 0
|
||||
i = uint64(0)
|
||||
total = uint64(len(indices))
|
||||
while True:
|
||||
candidate_index = indices[compute_shuffled_index(i % len(indices), len(indices), seed)]
|
||||
random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32]
|
||||
candidate_index = indices[compute_shuffled_index(i % total, total, seed)]
|
||||
random_byte = hash(seed + uint_to_bytes(uint64(i // 32)))[i % 32]
|
||||
effective_balance = state.validators[candidate_index].effective_balance
|
||||
if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte:
|
||||
return candidate_index
|
||||
@ -775,7 +774,7 @@ def compute_committee(indices: Sequence[ValidatorIndex],
|
||||
"""
|
||||
start = (len(indices) * index) // count
|
||||
end = (len(indices) * (index + 1)) // count
|
||||
return [indices[compute_shuffled_index(i, len(indices), seed)] for i in range(start, end)]
|
||||
return [indices[compute_shuffled_index(uint64(i), uint64(len(indices)), seed)] for i in range(start, end)]
|
||||
```
|
||||
|
||||
#### `compute_epoch_at_slot`
|
||||
@ -934,7 +933,7 @@ def get_validator_churn_limit(state: BeaconState) -> uint64:
|
||||
Return the validator churn limit for the current epoch.
|
||||
"""
|
||||
active_validator_indices = get_active_validator_indices(state, get_current_epoch(state))
|
||||
return max(MIN_PER_EPOCH_CHURN_LIMIT, len(active_validator_indices) // CHURN_LIMIT_QUOTIENT)
|
||||
return max(MIN_PER_EPOCH_CHURN_LIMIT, uint64(len(active_validator_indices)) // CHURN_LIMIT_QUOTIENT)
|
||||
```
|
||||
|
||||
#### `get_seed`
|
||||
@ -945,7 +944,7 @@ def get_seed(state: BeaconState, epoch: Epoch, domain_type: DomainType) -> Bytes
|
||||
Return the seed at ``epoch``.
|
||||
"""
|
||||
mix = get_randao_mix(state, Epoch(epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1)) # Avoid underflow
|
||||
return hash(domain_type + int_to_bytes(epoch, length=8) + mix)
|
||||
return hash(domain_type + uint_to_bytes(epoch) + mix)
|
||||
```
|
||||
|
||||
#### `get_committee_count_per_slot`
|
||||
@ -955,9 +954,9 @@ def get_committee_count_per_slot(state: BeaconState, epoch: Epoch) -> uint64:
|
||||
"""
|
||||
Return the number of committees in each slot for the given ``epoch``.
|
||||
"""
|
||||
return max(1, min(
|
||||
return max(uint64(1), min(
|
||||
MAX_COMMITTEES_PER_SLOT,
|
||||
len(get_active_validator_indices(state, epoch)) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE,
|
||||
uint64(len(get_active_validator_indices(state, epoch))) // SLOTS_PER_EPOCH // TARGET_COMMITTEE_SIZE,
|
||||
))
|
||||
```
|
||||
|
||||
@ -986,7 +985,7 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
|
||||
Return the beacon proposer index at the current slot.
|
||||
"""
|
||||
epoch = get_current_epoch(state)
|
||||
seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + int_to_bytes(state.slot, length=8))
|
||||
seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + uint_to_bytes(state.slot))
|
||||
indices = get_active_validator_indices(state, epoch)
|
||||
return compute_proposer_index(state, indices, seed)
|
||||
```
|
||||
@ -1316,7 +1315,7 @@ def process_justification_and_finalization(state: BeaconState) -> None:
|
||||
|
||||
# Process justifications
|
||||
state.previous_justified_checkpoint = state.current_justified_checkpoint
|
||||
state.justification_bits[1:] = state.justification_bits[:-1]
|
||||
state.justification_bits[1:] = state.justification_bits[:JUSTIFICATION_BITS_LENGTH - 1]
|
||||
state.justification_bits[0] = 0b0
|
||||
matching_target_attestations = get_matching_target_attestations(state, previous_epoch) # Previous epoch
|
||||
if get_attesting_balance(state, matching_target_attestations) * 3 >= get_total_active_balance(state) * 2:
|
||||
@ -1719,10 +1718,10 @@ def process_attester_slashing(state: BeaconState, attester_slashing: AttesterSla
|
||||
```python
|
||||
def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
||||
data = attestation.data
|
||||
assert data.index < get_committee_count_per_slot(state, data.target.epoch)
|
||||
assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state))
|
||||
assert data.target.epoch == compute_epoch_at_slot(data.slot)
|
||||
assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= data.slot + SLOTS_PER_EPOCH
|
||||
assert data.index < get_committee_count_per_slot(state, data.target.epoch)
|
||||
|
||||
committee = get_beacon_committee(state, data.slot, data.index)
|
||||
assert len(attestation.aggregation_bits) == len(committee)
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
- [Introduction](#introduction)
|
||||
- [Constants](#constants)
|
||||
- [Contract](#contract)
|
||||
- [Configuration](#configuration)
|
||||
- [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract)
|
||||
- [`deposit` function](#deposit-function)
|
||||
- [Deposit amount](#deposit-amount)
|
||||
@ -27,16 +27,29 @@ This document represents the specification for the beacon chain deposit contract
|
||||
|
||||
## Constants
|
||||
|
||||
### Contract
|
||||
The following values are (non-configurable) constants used throughout the specification.
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `DEPOSIT_CONTRACT_ADDRESS` | **TBD** |
|
||||
| `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) |
|
||||
|
||||
## Configuration
|
||||
|
||||
*Note*: The default mainnet configuration values are included here for spec-design purposes.
|
||||
The different configurations for mainnet, testnets, and YAML-based testing can be found in the [`configs/constant_presets`](../../configs) directory.
|
||||
These configurations are updated for releases and may be out of sync during `dev` changes.
|
||||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `DEPOSIT_CHAIN_ID` | `1` |
|
||||
| `DEPOSIT_NETWORK_ID` | `1` |
|
||||
| `DEPOSIT_CONTRACT_ADDRESS` | **TBD** |
|
||||
|
||||
## Ethereum 1.0 deposit contract
|
||||
|
||||
The initial deployment phases of Ethereum 2.0 are implemented without consensus changes to Ethereum 1.0. A deposit contract at address `DEPOSIT_CONTRACT_ADDRESS` is added to Ethereum 1.0 for deposits of ETH to the beacon chain. Validator balances will be withdrawable to the shards in Phase 2.
|
||||
The initial deployment phases of Ethereum 2.0 are implemented without consensus changes to Ethereum 1.0. A deposit contract at address `DEPOSIT_CONTRACT_ADDRESS` is added to the Ethereum 1.0 chain defined by the [chain-id](https://eips.ethereum.org/EIPS/eip-155) -- `DEPOSIT_CHAIN_ID` -- and the network-id -- `DEPOSIT_NETWORK_ID` -- for deposits of ETH to the beacon chain. Validator balances will be withdrawable to the shards in Phase 2.
|
||||
|
||||
_Note_: See [here](https://chainid.network/) for a comprehensive list of public Ethereum chain chain-id's and network-id's.
|
||||
|
||||
### `deposit` function
|
||||
|
||||
|
@ -102,7 +102,7 @@ _The block for `anchor_root` is incorrectly initialized to the block header, rat
|
||||
|
||||
```python
|
||||
def get_forkchoice_store(anchor_state: BeaconState) -> Store:
|
||||
anchor_block_header = anchor_state.latest_block_header.copy()
|
||||
anchor_block_header = copy(anchor_state.latest_block_header)
|
||||
if anchor_block_header.state_root == Bytes32():
|
||||
anchor_block_header.state_root = hash_tree_root(anchor_state)
|
||||
anchor_root = hash_tree_root(anchor_block_header)
|
||||
@ -110,14 +110,14 @@ def get_forkchoice_store(anchor_state: BeaconState) -> Store:
|
||||
justified_checkpoint = Checkpoint(epoch=anchor_epoch, root=anchor_root)
|
||||
finalized_checkpoint = Checkpoint(epoch=anchor_epoch, root=anchor_root)
|
||||
return Store(
|
||||
time=anchor_state.genesis_time + SECONDS_PER_SLOT * anchor_state.slot,
|
||||
time=uint64(anchor_state.genesis_time + SECONDS_PER_SLOT * anchor_state.slot),
|
||||
genesis_time=anchor_state.genesis_time,
|
||||
justified_checkpoint=justified_checkpoint,
|
||||
finalized_checkpoint=finalized_checkpoint,
|
||||
best_justified_checkpoint=justified_checkpoint,
|
||||
blocks={anchor_root: anchor_block_header},
|
||||
block_states={anchor_root: anchor_state.copy()},
|
||||
checkpoint_states={justified_checkpoint: anchor_state.copy()},
|
||||
block_states={anchor_root: copy(anchor_state)},
|
||||
checkpoint_states={justified_checkpoint: copy(anchor_state)},
|
||||
)
|
||||
```
|
||||
|
||||
@ -302,7 +302,7 @@ def validate_on_attestation(store: Store, attestation: Attestation) -> None:
|
||||
def store_target_checkpoint_state(store: Store, target: Checkpoint) -> None:
|
||||
# Store target checkpoint state if not yet seen
|
||||
if target not in store.checkpoint_states:
|
||||
base_state = store.block_states[target.root].copy()
|
||||
base_state = copy(store.block_states[target.root])
|
||||
if base_state.slot < compute_start_slot_at_epoch(target.epoch):
|
||||
process_slots(base_state, compute_start_slot_at_epoch(target.epoch))
|
||||
store.checkpoint_states[target] = base_state
|
||||
@ -348,7 +348,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
||||
# Parent block must be known
|
||||
assert block.parent_root in store.block_states
|
||||
# Make a copy of the state to avoid mutability issues
|
||||
pre_state = store.block_states[block.parent_root].copy()
|
||||
pre_state = copy(store.block_states[block.parent_root])
|
||||
# Blocks cannot be in the future. If they are, their consideration must be delayed until the are in the past.
|
||||
assert get_current_slot(store) >= block.slot
|
||||
|
||||
|
@ -41,7 +41,7 @@ It consists of four main sections:
|
||||
- [Requesting side](#requesting-side)
|
||||
- [Responding side](#responding-side)
|
||||
- [Encoding strategies](#encoding-strategies)
|
||||
- [SSZ-encoding strategy (with or without Snappy)](#ssz-encoding-strategy-with-or-without-snappy)
|
||||
- [SSZ-snappy encoding strategy](#ssz-snappy-encoding-strategy)
|
||||
- [Messages](#messages)
|
||||
- [Status](#status)
|
||||
- [Goodbye](#goodbye)
|
||||
@ -79,6 +79,7 @@ It consists of four main sections:
|
||||
- [Why must all clients use the same gossip topic instead of one negotiated between each peer pair?](#why-must-all-clients-use-the-same-gossip-topic-instead-of-one-negotiated-between-each-peer-pair)
|
||||
- [Why are the topics strings and not hashes?](#why-are-the-topics-strings-and-not-hashes)
|
||||
- [Why are we overriding the default libp2p pubsub `message-id`?](#why-are-we-overriding-the-default-libp2p-pubsub-message-id)
|
||||
- [Why are these specific gossip parameters chosen?](#why-are-these-specific-gossip-parameters-chosen)
|
||||
- [Why is there `MAXIMUM_GOSSIP_CLOCK_DISPARITY` when validating slot ranges of messages in gossip subnets?](#why-is-there-maximum_gossip_clock_disparity-when-validating-slot-ranges-of-messages-in-gossip-subnets)
|
||||
- [Why are there `ATTESTATION_SUBNET_COUNT` attestation subnets?](#why-are-there-attestation_subnet_count-attestation-subnets)
|
||||
- [Why are attestations limited to be broadcast on gossip channels within `SLOTS_PER_EPOCH` slots?](#why-are-attestations-limited-to-be-broadcast-on-gossip-channels-within-slots_per_epoch-slots)
|
||||
@ -201,7 +202,7 @@ and will in most cases be out of sync with the ENR sequence number.
|
||||
|
||||
## The gossip domain: gossipsub
|
||||
|
||||
Clients MUST support the [gossipsub v1](https://github.com/libp2p/specs/tree/master/pubsub/gossipsub) libp2p Protocol
|
||||
Clients MUST support the [gossipsub v1](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.0.md) libp2p Protocol
|
||||
including the [gossipsub v1.1](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md) extension.
|
||||
|
||||
**Protocol ID:** `/meshsub/1.1.0`
|
||||
@ -210,16 +211,17 @@ including the [gossipsub v1.1](https://github.com/libp2p/specs/blob/master/pubsu
|
||||
|
||||
*Note*: Parameters listed here are subject to a large-scale network feasibility study.
|
||||
|
||||
The following gossipsub [parameters](https://github.com/libp2p/specs/tree/master/pubsub/gossipsub#meshsub-an-overlay-mesh-router) will be used:
|
||||
The following gossipsub [parameters](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.0.md#parameters) will be used:
|
||||
|
||||
- `D` (topic stable mesh target count): 6
|
||||
- `D_low` (topic stable mesh low watermark): 4
|
||||
- `D_low` (topic stable mesh low watermark): 5
|
||||
- `D_high` (topic stable mesh high watermark): 12
|
||||
- `D_lazy` (gossip target): 6
|
||||
- `heartbeat_interval` (frequency of heartbeat, seconds): 0.7
|
||||
- `fanout_ttl` (ttl for fanout maps for topics we are not subscribed to but have published to, seconds): 60
|
||||
- `gossip_advertise` (number of windows to gossip about): 3
|
||||
- `gossip_history` (number of heartbeat intervals to retain message IDs): 5
|
||||
- `heartbeat_interval` (frequency of heartbeat, seconds): 1
|
||||
- `mcache_len` (number of windows to retain full messages in cache for `IWANT` responses): 6
|
||||
- `mcache_gossip` (number of windows to gossip about): 3
|
||||
- `seen_ttl` (number of heartbeat intervals to retain message IDs): 550
|
||||
|
||||
### Topics and messages
|
||||
|
||||
@ -293,15 +295,18 @@ The following validations MUST pass before forwarding the `signed_beacon_block`
|
||||
(a client MAY choose to validate and store such blocks for additional purposes -- e.g. slashing detection, archive nodes, etc).
|
||||
- _[IGNORE]_ The block is the first block with valid signature received for the proposer for the slot, `signed_beacon_block.message.slot`.
|
||||
- _[REJECT]_ The proposer signature, `signed_beacon_block.signature`, is valid with respect to the `proposer_index` pubkey.
|
||||
- _[IGNORE]_ The block's parent (defined by `block.parent_root`) has been seen
|
||||
(via both gossip and non-gossip sources)
|
||||
(a client MAY queue blocks for processing once the parent block is retrieved).
|
||||
- _[REJECT]_ The block's parent (defined by `block.parent_root`) passes validation.
|
||||
- _[REJECT]_ The current `finalized_checkpoint` is an ancestor of `block` -- i.e.
|
||||
`get_ancestor(store, block.parent_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch))
|
||||
== store.finalized_checkpoint.root`
|
||||
- _[REJECT]_ The block is proposed by the expected `proposer_index` for the block's slot
|
||||
in the context of the current shuffling (defined by `parent_root`/`slot`).
|
||||
If the `proposer_index` cannot immediately be verified against the expected shuffling,
|
||||
the block MAY be queued for later processing while proposers for the block's branch are calculated --
|
||||
in such a case _do not_ `REJECT`, instead `IGNORE` this message.
|
||||
- _[IGNORE]_ The block's parent (defined by `block.parent_root`) has been seen
|
||||
(via both gossip and non-gossip sources)
|
||||
(a client MAY queue blocks for processing once the parent block is retrieved).
|
||||
- _[REJECT]_ The block's parent (defined by `block.parent_root`) passes validation.
|
||||
|
||||
##### `beacon_aggregate_and_proof`
|
||||
|
||||
@ -331,6 +336,10 @@ The following validations MUST pass before forwarding the `signed_aggregate_and_
|
||||
(via both gossip and non-gossip sources)
|
||||
(a client MAY queue aggregates for processing once block is retrieved).
|
||||
- _[REJECT]_ The block being voted for (`aggregate.data.beacon_block_root`) passes validation.
|
||||
- _[REJECT]_ The current `finalized_checkpoint` is an ancestor of the `block` defined by `aggregate.data.beacon_block_root` -- i.e.
|
||||
`get_ancestor(store, aggregate.data.beacon_block_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch))
|
||||
== store.finalized_checkpoint.root`
|
||||
|
||||
|
||||
##### `voluntary_exit`
|
||||
|
||||
@ -391,6 +400,11 @@ The following validations MUST pass before forwarding the `attestation` on the s
|
||||
(via both gossip and non-gossip sources)
|
||||
(a client MAY queue aggregates for processing once block is retrieved).
|
||||
- _[REJECT]_ The block being voted for (`attestation.data.beacon_block_root`) passes validation.
|
||||
- _[REJECT]_ The current `finalized_checkpoint` is an ancestor of the `block` defined by `attestation.data.beacon_block_root` -- i.e.
|
||||
`get_ancestor(store, attestation.data.beacon_block_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch))
|
||||
== store.finalized_checkpoint.root`
|
||||
|
||||
|
||||
|
||||
#### Attestations and Aggregation
|
||||
|
||||
@ -548,20 +562,19 @@ Clients MUST treat as valid any byte sequences.
|
||||
### Encoding strategies
|
||||
|
||||
The token of the negotiated protocol ID specifies the type of encoding to be used for the req/resp interaction.
|
||||
Two values are possible at this time:
|
||||
Only one value is possible at this time:
|
||||
|
||||
- `ssz`: the contents are [SSZ-encoded](../../ssz/simple-serialize.md).
|
||||
This encoding type MUST be supported by all clients.
|
||||
- `ssz_snappy`: The contents are first [SSZ-encoded](../../ssz/simple-serialize.md)
|
||||
and then compressed with [Snappy](https://github.com/google/snappy) frames compression.
|
||||
For objects containing a single field, only the field is SSZ-encoded not a container with a single field.
|
||||
For example, the `BeaconBlocksByRoot` request is an SSZ-encoded list of `Root`'s.
|
||||
- `ssz_snappy`: The contents are SSZ-encoded and then compressed with [Snappy](https://github.com/google/snappy) frames compression.
|
||||
This encoding type MUST be supported by all clients.
|
||||
|
||||
#### SSZ-encoding strategy (with or without Snappy)
|
||||
#### SSZ-snappy encoding strategy
|
||||
|
||||
The [SimpleSerialize (SSZ) specification](../../ssz/simple-serialize.md) outlines how objects are SSZ-encoded.
|
||||
|
||||
If the Snappy variant is selected, we feed the serialized form of the object to the Snappy compressor on encoding.
|
||||
To achieve snappy encoding on top of SSZ, we feed the serialized form of the object to the Snappy compressor on encoding.
|
||||
The inverse happens on decoding.
|
||||
|
||||
Snappy has two formats: "block" and "frames" (streaming).
|
||||
@ -570,14 +583,14 @@ To support large requests and response chunks, snappy-framing is used.
|
||||
Since snappy frame contents [have a maximum size of `65536` bytes](https://github.com/google/snappy/blob/master/framing_format.txt#L104)
|
||||
and frame headers are just `identifier (1) + checksum (4)` bytes, the expected buffering of a single frame is acceptable.
|
||||
|
||||
**Encoding-dependent header:** Req/Resp protocols using the `ssz` or `ssz_snappy` encoding strategies MUST encode the length of the raw SSZ bytes,
|
||||
**Encoding-dependent header:** Req/Resp protocols using the `ssz_snappy` encoding strategy MUST encode the length of the raw SSZ bytes,
|
||||
encoded as an unsigned [protobuf varint](https://developers.google.com/protocol-buffers/docs/encoding#varints).
|
||||
|
||||
*Writing*: By first computing and writing the SSZ byte length, the SSZ encoder can then directly write the chunk contents to the stream.
|
||||
If Snappy is applied, it can be passed through a buffered Snappy writer to compress frame by frame.
|
||||
When Snappy is applied, it can be passed through a buffered Snappy writer to compress frame by frame.
|
||||
|
||||
*Reading*: After reading the expected SSZ byte length, the SSZ decoder can directly read the contents from the stream.
|
||||
If snappy is applied, it can be passed through a buffered Snappy reader to decompress frame by frame.
|
||||
When snappy is applied, it can be passed through a buffered Snappy reader to decompress frame by frame.
|
||||
|
||||
Before reading the payload, the header MUST be validated:
|
||||
- The unsigned protobuf varint used for the length-prefix MUST not be longer than 10 bytes, which is sufficient for any `uint64`.
|
||||
@ -586,7 +599,6 @@ Before reading the payload, the header MUST be validated:
|
||||
After reading a valid header, the payload MAY be read, while maintaining the size constraints from the header.
|
||||
|
||||
A reader SHOULD NOT read more than `max_encoded_len(n)` bytes after reading the SSZ length-prefix `n` from the header.
|
||||
- For `ssz` this is: `n`
|
||||
- For `ssz_snappy` this is: `32 + n + n // 6`.
|
||||
This is considered the [worst-case compression result](https://github.com/google/snappy/blob/537f4ad6240e586970fe554614542e9717df7902/snappy.cc#L98) by Snappy.
|
||||
|
||||
@ -1159,6 +1171,25 @@ Some examples of where messages could be duplicated:
|
||||
Partial aggregates could be duplicated
|
||||
* Clients re-publishing seen messages
|
||||
|
||||
### Why are these specific gossip parameters chosen?
|
||||
|
||||
- `D`, `D_low`, `D_high`, `D_lazy`: recommended defaults.
|
||||
- `heartbeat_interval`: 0.7 seconds, recommended for eth2 in the [GossipSub evaluation report by Protocol Labs](https://gateway.ipfs.io/ipfs/QmRAFP5DBnvNjdYSbWhEhVRJJDFCLpPyvew5GwCCB4VxM4).
|
||||
- `fanout_ttl`: 60 seconds, recommended default.
|
||||
Fanout is primarily used by committees publishing attestations to subnets.
|
||||
This happens once per epoch per validator and the subnet changes each epoch
|
||||
so there is little to gain in having a `fanout_ttl` be increased from the recommended default.
|
||||
- `mcache_len`: 6, increase by one to ensure that mcache is around for long
|
||||
enough for `IWANT`s to respond to `IHAVE`s in the context of the shorter
|
||||
`heartbeat_interval`. If `mcache_gossip` is increased, this param should be
|
||||
increased to be at least `3` (~2 seconds) more than `mcache_gossip`.
|
||||
- `mcache_gossip`: 3, recommended default. This can be increased to 5 or 6
|
||||
(~4 seconds) if gossip times are longer than expected and the current window
|
||||
does not provide enough responsiveness during adverse conditions.
|
||||
- `seen_ttl`: `SLOTS_PER_EPOCH * SECONDS_PER_SLOT / heartbeat_interval = approx. 550`.
|
||||
Attestation gossip validity is bounded by an epoch, so this is the safe max bound.
|
||||
|
||||
|
||||
### Why is there `MAXIMUM_GOSSIP_CLOCK_DISPARITY` when validating slot ranges of messages in gossip subnets?
|
||||
|
||||
For some gossip channels (e.g. those for Attestations and BeaconBlocks),
|
||||
@ -1278,7 +1309,7 @@ Thus, libp2p transparently handles message delimiting in the underlying stream.
|
||||
libp2p streams are full-duplex, and each party is responsible for closing their write side (like in TCP).
|
||||
We can therefore use stream closure to mark the end of the request and response independently.
|
||||
|
||||
Nevertheless, in the case of `ssz` and `ssz_snappy`, messages are still length-prefixed with the length of the underlying data:
|
||||
Nevertheless, in the case of `ssz_snappy`, messages are still length-prefixed with the length of the underlying data:
|
||||
* A basic reader can prepare a correctly sized buffer before reading the message
|
||||
* A more advanced reader can stream-decode SSZ given the length of the SSZ data.
|
||||
* Alignment with protocols like gRPC over HTTP/2 that prefix with length
|
||||
|
@ -111,7 +111,7 @@ The validator constructs their `withdrawal_credentials` via the following:
|
||||
|
||||
### Submit deposit
|
||||
|
||||
In Phase 0, all incoming validator deposits originate from the Ethereum 1.0 proof-of-work chain. Deposits are made to the [deposit contract](./deposit-contract.md) located at `DEPOSIT_CONTRACT_ADDRESS`.
|
||||
In Phase 0, all incoming validator deposits originate from the Ethereum 1.0 chain defined by `DEPOSIT_CHAIN_ID` and `DEPOSIT_NETWORK_ID`. Deposits are made to the [deposit contract](./deposit-contract.md) located at `DEPOSIT_CONTRACT_ADDRESS`.
|
||||
|
||||
To submit a deposit:
|
||||
|
||||
@ -273,7 +273,7 @@ An honest block proposer sets `block.body.eth1_data = get_eth1_vote(state)` wher
|
||||
|
||||
```python
|
||||
def compute_time_at_slot(state: BeaconState, slot: Slot) -> uint64:
|
||||
return state.genesis_time + slot * SECONDS_PER_SLOT
|
||||
return uint64(state.genesis_time + slot * SECONDS_PER_SLOT)
|
||||
```
|
||||
|
||||
```python
|
||||
@ -443,7 +443,7 @@ def compute_subnet_for_attestation(committees_per_slot: uint64, slot: Slot, comm
|
||||
slots_since_epoch_start = slot % SLOTS_PER_EPOCH
|
||||
committees_since_epoch_start = committees_per_slot * slots_since_epoch_start
|
||||
|
||||
return (committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT
|
||||
return uint64((committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT)
|
||||
```
|
||||
|
||||
### Attestation aggregation
|
||||
@ -465,7 +465,7 @@ def get_slot_signature(state: BeaconState, slot: Slot, privkey: int) -> BLSSigna
|
||||
def is_aggregator(state: BeaconState, slot: Slot, index: CommitteeIndex, slot_signature: BLSSignature) -> bool:
|
||||
committee = get_beacon_committee(state, slot, index)
|
||||
modulo = max(1, len(committee) // TARGET_AGGREGATORS_PER_COMMITTEE)
|
||||
return bytes_to_int(hash(slot_signature)[0:8]) % modulo == 0
|
||||
return bytes_to_uint64(hash(slot_signature)[0:8]) % modulo == 0
|
||||
```
|
||||
|
||||
#### Construct aggregate
|
||||
|
@ -581,8 +581,8 @@ def get_shard_proposer_index(beacon_state: BeaconState, slot: Slot, shard: Shard
|
||||
"""
|
||||
epoch = compute_epoch_at_slot(slot)
|
||||
committee = get_shard_committee(beacon_state, epoch, shard)
|
||||
seed = hash(get_seed(beacon_state, epoch, DOMAIN_SHARD_COMMITTEE) + int_to_bytes(slot, length=8))
|
||||
r = bytes_to_int(seed[:8])
|
||||
seed = hash(get_seed(beacon_state, epoch, DOMAIN_SHARD_COMMITTEE) + uint_to_bytes(slot))
|
||||
r = bytes_to_uint64(seed[:8])
|
||||
return committee[r % len(committee)]
|
||||
```
|
||||
|
||||
@ -760,20 +760,24 @@ def validate_attestation(state: BeaconState, attestation: Attestation) -> None:
|
||||
committee = get_beacon_committee(state, data.slot, data.index)
|
||||
assert len(attestation.aggregation_bits) == len(committee)
|
||||
|
||||
if attestation.data.target.epoch == get_current_epoch(state):
|
||||
assert attestation.data.source == state.current_justified_checkpoint
|
||||
if data.target.epoch == get_current_epoch(state):
|
||||
assert data.source == state.current_justified_checkpoint
|
||||
else:
|
||||
assert attestation.data.source == state.previous_justified_checkpoint
|
||||
assert data.source == state.previous_justified_checkpoint
|
||||
|
||||
# Type 1: on-time attestations
|
||||
if is_on_time_attestation(state, attestation.data):
|
||||
if is_on_time_attestation(state, data):
|
||||
# Correct parent block root
|
||||
assert data.beacon_block_root == get_block_root_at_slot(state, compute_previous_slot(state.slot))
|
||||
# Correct shard number
|
||||
shard = compute_shard_from_committee_index(state, attestation.data.index, attestation.data.slot)
|
||||
assert attestation.data.shard == shard
|
||||
# On-time attestations should have a non-empty shard transition root
|
||||
assert attestation.data.shard_transition_root != hash_tree_root(ShardTransition())
|
||||
shard = compute_shard_from_committee_index(state, data.index, data.slot)
|
||||
assert data.shard == shard
|
||||
# NOTE: We currently set `PHASE_1_FORK_SLOT` to `GENESIS_SLOT` for test vectors.
|
||||
if data.slot > GENESIS_SLOT:
|
||||
# On-time attestations should have a non-empty shard transition root
|
||||
assert data.shard_transition_root != hash_tree_root(ShardTransition())
|
||||
else:
|
||||
assert data.shard_transition_root == hash_tree_root(ShardTransition())
|
||||
# Type 2: no shard transition
|
||||
else:
|
||||
# Ensure delayed attestation
|
||||
@ -811,7 +815,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
||||
```python
|
||||
def apply_shard_transition(state: BeaconState, shard: Shard, transition: ShardTransition) -> None:
|
||||
# TODO: only need to check it once when phase 1 starts
|
||||
assert state.slot > PHASE_1_GENESIS_SLOT
|
||||
assert state.slot > PHASE_1_FORK_SLOT
|
||||
|
||||
# Correct data root count
|
||||
offset_slots = get_offset_slots(state, shard)
|
||||
@ -976,8 +980,11 @@ def verify_empty_shard_transition(state: BeaconState, shard_transitions: Sequenc
|
||||
def process_shard_transitions(state: BeaconState,
|
||||
shard_transitions: Sequence[ShardTransition],
|
||||
attestations: Sequence[Attestation]) -> None:
|
||||
# Process crosslinks
|
||||
process_crosslinks(state, shard_transitions, attestations)
|
||||
# NOTE: We currently set `PHASE_1_FORK_SLOT` to `GENESIS_SLOT` for test vectors.
|
||||
if compute_previous_slot(state.slot) > GENESIS_SLOT:
|
||||
# Process crosslinks
|
||||
process_crosslinks(state, shard_transitions, attestations)
|
||||
|
||||
# Verify the empty proposal shard states
|
||||
assert verify_empty_shard_transition(state, shard_transitions)
|
||||
```
|
||||
|
@ -35,18 +35,18 @@ Warning: this configuration is not definitive.
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `PHASE_1_FORK_VERSION` | `Version('0x01000000')` |
|
||||
| `PHASE_1_GENESIS_SLOT` | `2**5` **TBD** |
|
||||
| `PHASE_1_FORK_SLOT` | `Slot(0)` **TBD** |
|
||||
| `INITIAL_ACTIVE_SHARDS` | `2**6` (= 64) |
|
||||
|
||||
## Fork to Phase 1
|
||||
|
||||
### Fork trigger
|
||||
|
||||
TBD. Social consensus, along with state conditions such as epoch boundary, finality, deposits, active validator count, etc. may be part of the decision process to trigger the fork. For now we assume the condition will be triggered at slot `PHASE_1_GENESIS_SLOT`, where `PHASE_1_GENESIS_SLOT % SLOTS_PER_EPOCH == 0`.
|
||||
TBD. Social consensus, along with state conditions such as epoch boundary, finality, deposits, active validator count, etc. may be part of the decision process to trigger the fork. For now we assume the condition will be triggered at slot `PHASE_1_FORK_SLOT`, where `PHASE_1_FORK_SLOT % SLOTS_PER_EPOCH == 0`.
|
||||
|
||||
### Upgrading the state
|
||||
|
||||
After `process_slots` of Phase 0 finishes, if `state.slot == PHASE_1_GENESIS_SLOT`, an irregular state change is made to upgrade to Phase 1.
|
||||
After `process_slots` of Phase 0 finishes, if `state.slot == PHASE_1_FORK_SLOT`, an irregular state change is made to upgrade to Phase 1.
|
||||
|
||||
```python
|
||||
def upgrade_to_phase1(pre: phase0.BeaconState) -> BeaconState:
|
||||
@ -102,7 +102,7 @@ def upgrade_to_phase1(pre: phase0.BeaconState) -> BeaconState:
|
||||
current_epoch_start_shard=Shard(0),
|
||||
shard_states=List[ShardState, MAX_SHARDS](
|
||||
ShardState(
|
||||
slot=pre.slot,
|
||||
slot=compute_previous_slot(pre.slot),
|
||||
gasprice=MIN_GASPRICE,
|
||||
latest_block_root=Root(),
|
||||
) for i in range(INITIAL_ACTIVE_SHARDS)
|
||||
|
@ -37,7 +37,7 @@ def get_forkchoice_shard_store(anchor_state: BeaconState, shard: Shard) -> Shard
|
||||
shard=shard,
|
||||
signed_blocks={
|
||||
anchor_state.shard_states[shard].latest_block_root: SignedShardBlock(
|
||||
message=ShardBlock(slot=anchor_state.slot, shard=shard)
|
||||
message=ShardBlock(slot=compute_previous_slot(anchor_state.slot), shard=shard)
|
||||
)
|
||||
},
|
||||
block_states={anchor_state.shard_states[shard].latest_block_root: anchor_state.copy().shard_states[shard]},
|
||||
|
@ -31,7 +31,7 @@
|
||||
- [`FullAttestation`](#fullattestation)
|
||||
- [Timing](#timing)
|
||||
- [Attestation data](#attestation-data)
|
||||
- [Head shard root](#head-shard-root)
|
||||
- [Shard head root](#shard-head-root)
|
||||
- [Shard transition](#shard-transition)
|
||||
- [Construct attestation](#construct-attestation)
|
||||
- [Attestation Aggregation](#attestation-aggregation)
|
||||
@ -267,9 +267,9 @@ A validator should create and broadcast the `attestation` to the associated atte
|
||||
|
||||
*Note*: We assume that the fork choice only follows branches with valid `offset_slots` with respect to the most recent beacon state shard transition for the queried shard.
|
||||
|
||||
##### Head shard root
|
||||
##### Shard head root
|
||||
|
||||
Set `attestation_data.shard_head_root = hash_tree_root(shard_head_block)`.
|
||||
If `attestation_data.slot == GENESIS_SLOT`, set `attestation_data.shard_head_root = Root()`. Otherwise, set `attestation_data.shard_head_root = hash_tree_root(shard_head_block)`.
|
||||
|
||||
##### Shard transition
|
||||
|
||||
@ -310,6 +310,10 @@ def get_shard_transition_fields(
|
||||
def get_shard_transition(beacon_state: BeaconState,
|
||||
shard: Shard,
|
||||
shard_blocks: Sequence[SignedShardBlock]) -> ShardTransition:
|
||||
# NOTE: We currently set `PHASE_1_FORK_SLOT` to `GENESIS_SLOT` for test vectors.
|
||||
if beacon_state.slot == GENESIS_SLOT:
|
||||
return ShardTransition()
|
||||
|
||||
offset_slots = compute_offset_slots(
|
||||
get_latest_slot_for_shard(beacon_state, shard),
|
||||
Slot(beacon_state.slot + 1),
|
||||
@ -463,7 +467,7 @@ def get_light_client_slot_signature(state: BeaconState, slot: Slot, privkey: int
|
||||
def is_light_client_aggregator(state: BeaconState, slot: Slot, slot_signature: BLSSignature) -> bool:
|
||||
committee = get_light_client_committee(state, compute_epoch_at_slot(slot))
|
||||
modulo = max(1, len(committee) // TARGET_LIGHT_CLIENT_AGGREGATORS_PER_SLOT)
|
||||
return bytes_to_int(hash(slot_signature)[0:8]) % modulo == 0
|
||||
return bytes_to_uint64(hash(slot_signature)[0:8]) % modulo == 0
|
||||
```
|
||||
|
||||
#### Construct aggregate
|
||||
|
@ -1 +1 @@
|
||||
0.12.1
|
||||
0.12.2
|
@ -59,8 +59,8 @@ def bls_default(request):
|
||||
def bls_type(request):
|
||||
bls_type = request.config.getoption("--bls-type")
|
||||
if bls_type == "py_ecc":
|
||||
bls_utils.bls = bls_utils.py_ecc_bls
|
||||
bls_utils.use_py_ecc()
|
||||
elif bls_type == "milagro":
|
||||
bls_utils.bls = bls_utils.milagro_bls
|
||||
bls_utils.use_milagro()
|
||||
else:
|
||||
raise Exception(f"unrecognized bls type: {bls_type}")
|
||||
|
@ -63,9 +63,6 @@ def _prepare_state(balances_fn: Callable[[Any], Sequence[int]], threshold_fn: Ca
|
||||
# TODO: instead of upgrading a test phase0 genesis state we can also write a phase1 state helper.
|
||||
# Decide based on performance/consistency results later.
|
||||
state = phases[PHASE1].upgrade_to_phase1(state)
|
||||
# Shard state slot must lag behind BeaconState slot by at least 1
|
||||
# Will handle this more elegantly with fork mechanics
|
||||
spec.process_slots(state, state.slot + 1)
|
||||
|
||||
return state
|
||||
|
||||
|
@ -69,7 +69,7 @@ def build_attestation_data(spec, state, slot, index, shard=None, shard_transitio
|
||||
source_epoch = state.current_justified_checkpoint.epoch
|
||||
source_root = state.current_justified_checkpoint.root
|
||||
|
||||
attestation_data = spec.AttestationData(
|
||||
data = spec.AttestationData(
|
||||
slot=slot,
|
||||
index=index,
|
||||
beacon_block_root=block_root,
|
||||
@ -79,23 +79,27 @@ def build_attestation_data(spec, state, slot, index, shard=None, shard_transitio
|
||||
|
||||
if spec.fork == PHASE1:
|
||||
if shard is None:
|
||||
shard = spec.compute_shard_from_committee_index(state, attestation_data.index, attestation_data.slot)
|
||||
attestation_data.shard = shard
|
||||
shard = spec.compute_shard_from_committee_index(state, data.index, data.slot)
|
||||
data.shard = shard
|
||||
|
||||
if shard_transition is not None:
|
||||
last_offset_index = len(shard_transition.shard_data_roots) - 1
|
||||
attestation_data.shard_head_root = shard_transition.shard_states[last_offset_index].latest_block_root
|
||||
attestation_data.shard_transition_root = shard_transition.hash_tree_root()
|
||||
data.shard_head_root = shard_transition.shard_states[last_offset_index].latest_block_root
|
||||
data.shard_transition_root = shard_transition.hash_tree_root()
|
||||
else:
|
||||
if on_time:
|
||||
shard_transition = spec.get_shard_transition(state, shard, shard_blocks=[])
|
||||
last_offset_index = len(shard_transition.shard_data_roots) - 1
|
||||
attestation_data.shard_head_root = shard_transition.shard_states[last_offset_index].latest_block_root
|
||||
attestation_data.shard_transition_root = shard_transition.hash_tree_root()
|
||||
if data.slot == spec.GENESIS_SLOT:
|
||||
data.shard_head_root = spec.Root()
|
||||
data.shard_transition_root = spec.ShardTransition().hash_tree_root()
|
||||
else:
|
||||
shard_transition = spec.get_shard_transition(state, shard, shard_blocks=[])
|
||||
last_offset_index = len(shard_transition.shard_data_roots) - 1
|
||||
data.shard_head_root = shard_transition.shard_states[last_offset_index].latest_block_root
|
||||
data.shard_transition_root = shard_transition.hash_tree_root()
|
||||
else:
|
||||
attestation_data.shard_head_root = state.shard_states[shard].latest_block_root
|
||||
attestation_data.shard_transition_root = spec.Root()
|
||||
return attestation_data
|
||||
data.shard_head_root = state.shard_states[shard].latest_block_root
|
||||
data.shard_transition_root = spec.Root()
|
||||
return data
|
||||
|
||||
|
||||
def get_valid_on_time_attestation(spec, state, slot=None, index=None, shard_transition=None, signed=False):
|
||||
|
@ -42,12 +42,10 @@ def transition_to_slot_via_block(spec, state, slot):
|
||||
|
||||
def transition_to_valid_shard_slot(spec, state):
|
||||
"""
|
||||
Transition to slot `spec.PHASE_1_GENESIS_SLOT + 1` and fork at `spec.PHASE_1_GENESIS_SLOT`.
|
||||
Transition to slot `spec.PHASE_1_FORK_SLOT + 1` and fork at `spec.PHASE_1_FORK_SLOT`.
|
||||
"""
|
||||
transition_to(spec, state, spec.PHASE_1_GENESIS_SLOT)
|
||||
state = spec.upgrade_to_phase1(state) # `upgrade_to_phase1` is a pure function
|
||||
transition_to(spec, state, spec.PHASE_1_FORK_SLOT)
|
||||
next_slot(spec, state)
|
||||
return state
|
||||
|
||||
|
||||
def next_epoch(spec, state):
|
||||
|
@ -1,6 +1,5 @@
|
||||
from eth2spec.test.context import (
|
||||
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
|
||||
)
|
||||
from eth2spec.test.helpers.attestations import sign_indexed_attestation
|
||||
from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing, \
|
||||
@ -197,7 +196,7 @@ def test_participants_already_slashed(spec, state):
|
||||
# Some of the following tests are phase0 only: phase 1 lists participants with bitfields instead of index list.
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att1_high_index(spec, state):
|
||||
@ -210,7 +209,7 @@ def test_att1_high_index(spec, state):
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att2_high_index(spec, state):
|
||||
@ -223,7 +222,7 @@ def test_att2_high_index(spec, state):
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att1_empty_indices(spec, state):
|
||||
@ -235,7 +234,7 @@ def test_att1_empty_indices(spec, state):
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att2_empty_indices(spec, state):
|
||||
@ -247,7 +246,7 @@ def test_att2_empty_indices(spec, state):
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_all_empty_indices(spec, state):
|
||||
@ -262,7 +261,7 @@ def test_all_empty_indices(spec, state):
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att1_bad_extra_index(spec, state):
|
||||
@ -278,7 +277,7 @@ def test_att1_bad_extra_index(spec, state):
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att1_bad_replaced_index(spec, state):
|
||||
@ -294,7 +293,7 @@ def test_att1_bad_replaced_index(spec, state):
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att2_bad_extra_index(spec, state):
|
||||
@ -310,7 +309,7 @@ def test_att2_bad_extra_index(spec, state):
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att2_bad_replaced_index(spec, state):
|
||||
@ -326,7 +325,7 @@ def test_att2_bad_replaced_index(spec, state):
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att1_duplicate_index_normal_signed(spec, state):
|
||||
@ -346,7 +345,7 @@ def test_att1_duplicate_index_normal_signed(spec, state):
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att2_duplicate_index_normal_signed(spec, state):
|
||||
@ -366,7 +365,7 @@ def test_att2_duplicate_index_normal_signed(spec, state):
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att1_duplicate_index_double_signed(spec, state):
|
||||
@ -381,7 +380,7 @@ def test_att1_duplicate_index_double_signed(spec, state):
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@always_bls
|
||||
def test_att2_duplicate_index_double_signed(spec, state):
|
||||
@ -396,7 +395,7 @@ def test_att2_duplicate_index_double_signed(spec, state):
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_unsorted_att_1(spec, state):
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True)
|
||||
@ -409,7 +408,7 @@ def test_unsorted_att_1(spec, state):
|
||||
yield from run_attester_slashing_processing(spec, state, attester_slashing, False)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_unsorted_att_2(spec, state):
|
||||
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=False)
|
||||
|
@ -1,7 +1,6 @@
|
||||
from eth2spec.test.context import (
|
||||
PHASE0,
|
||||
spec_state_test, spec_test,
|
||||
with_all_phases, with_phases, single_phase,
|
||||
with_all_phases, single_phase,
|
||||
with_custom_state,
|
||||
zero_activation_threshold,
|
||||
misc_balances, low_single_balance,
|
||||
@ -25,7 +24,7 @@ def run_process_rewards_and_penalties(spec, state):
|
||||
yield from run_epoch_processing_with(spec, state, 'process_rewards_and_penalties')
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_genesis_epoch_no_attestations_no_penalties(spec, state):
|
||||
pre_state = state.copy()
|
||||
@ -38,7 +37,7 @@ def test_genesis_epoch_no_attestations_no_penalties(spec, state):
|
||||
assert state.balances[index] == pre_state.balances[index]
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_genesis_epoch_full_attestations_no_rewards(spec, state):
|
||||
attestations = []
|
||||
@ -48,7 +47,7 @@ def test_genesis_epoch_full_attestations_no_rewards(spec, state):
|
||||
attestation = get_valid_attestation(spec, state, signed=True)
|
||||
attestations.append(attestation)
|
||||
# fill each created slot in state after inclusion delay
|
||||
if slot - spec.MIN_ATTESTATION_INCLUSION_DELAY >= 0:
|
||||
if slot >= spec.MIN_ATTESTATION_INCLUSION_DELAY:
|
||||
include_att = attestations[slot - spec.MIN_ATTESTATION_INCLUSION_DELAY]
|
||||
add_attestations_to_state(spec, state, [include_att], state.slot)
|
||||
next_slot(spec, state)
|
||||
|
@ -1,4 +1,4 @@
|
||||
from eth2spec.test.context import PHASE0, spec_state_test, never_bls, with_all_phases, with_phases
|
||||
from eth2spec.test.context import spec_state_test, with_all_phases
|
||||
from eth2spec.test.helpers.state import next_epoch_via_block
|
||||
from eth2spec.test.helpers.attestations import next_epoch_with_attestations
|
||||
|
||||
@ -28,9 +28,8 @@ def check_finality(spec,
|
||||
assert state.finalized_checkpoint == prev_state.finalized_checkpoint
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@never_bls
|
||||
def test_finality_no_updates_at_genesis(spec, state):
|
||||
assert spec.get_current_epoch(state) == spec.GENESIS_EPOCH
|
||||
|
||||
@ -54,7 +53,6 @@ def test_finality_no_updates_at_genesis(spec, state):
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@never_bls
|
||||
def test_finality_rule_4(spec, state):
|
||||
# get past first two epochs that finality does not run on
|
||||
next_epoch_via_block(spec, state)
|
||||
@ -80,7 +78,6 @@ def test_finality_rule_4(spec, state):
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@never_bls
|
||||
def test_finality_rule_1(spec, state):
|
||||
# get past first two epochs that finality does not run on
|
||||
next_epoch_via_block(spec, state)
|
||||
@ -108,7 +105,6 @@ def test_finality_rule_1(spec, state):
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@never_bls
|
||||
def test_finality_rule_2(spec, state):
|
||||
# get past first two epochs that finality does not run on
|
||||
next_epoch_via_block(spec, state)
|
||||
@ -138,7 +134,6 @@ def test_finality_rule_2(spec, state):
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
@never_bls
|
||||
def test_finality_rule_3(spec, state):
|
||||
"""
|
||||
Test scenario described here
|
@ -313,6 +313,28 @@ def test_empty_epoch_transition_not_finalizing(spec, state):
|
||||
assert state.balances[index] < pre_balances[index]
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_proposer_self_slashing(spec, state):
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
assert not state.validators[block.proposer_index].slashed
|
||||
|
||||
proposer_slashing = get_valid_proposer_slashing(
|
||||
spec, state, slashed_index=block.proposer_index, signed_1=True, signed_2=True)
|
||||
block.body.proposer_slashings.append(proposer_slashing)
|
||||
|
||||
# The header is processed *before* the block body:
|
||||
# the proposer was not slashed before the body, thus the block is valid.
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
# The proposer slashed themselves.
|
||||
assert state.validators[block.proposer_index].slashed
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_proposer_slashing(spec, state):
|
||||
@ -484,7 +506,7 @@ def test_duplicate_attester_slashing(spec, state):
|
||||
|
||||
# All AttesterSlashing tests should be adopted for Phase 1 but helper support is not yet there
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_multiple_attester_slashings_no_overlap(spec, state):
|
||||
# Skip test if config cannot handle multiple AttesterSlashings per block
|
||||
@ -525,7 +547,7 @@ def test_multiple_attester_slashings_no_overlap(spec, state):
|
||||
check_attester_slashing_effect(spec, pre_state, state, full_indices)
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_multiple_attester_slashings_partial_overlap(spec, state):
|
||||
# Skip test if config cannot handle multiple AttesterSlashings per block
|
||||
@ -740,7 +762,7 @@ def prepare_signed_exits(spec, state, indices):
|
||||
# exceeding the minimal-config randao mixes memory size.
|
||||
# Applies to all voluntary-exit sanity block tests.
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_voluntary_exit(spec, state):
|
||||
validator_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
@ -768,7 +790,7 @@ def test_voluntary_exit(spec, state):
|
||||
assert state.validators[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_double_validator_exit_same_block(spec, state):
|
||||
validator_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1]
|
||||
@ -789,7 +811,7 @@ def test_double_validator_exit_same_block(spec, state):
|
||||
yield 'post', None
|
||||
|
||||
|
||||
@with_phases([PHASE0])
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_multiple_different_validator_exits_same_block(spec, state):
|
||||
validator_indices = [
|
||||
|
@ -40,9 +40,9 @@ def run_is_candidate_block(spec, eth1_block, period_start, success=True):
|
||||
|
||||
|
||||
def get_min_new_period_epochs(spec):
|
||||
return int(
|
||||
spec.SECONDS_PER_ETH1_BLOCK * spec.ETH1_FOLLOW_DISTANCE * 2 # to seconds
|
||||
/ spec.SECONDS_PER_SLOT / spec.SLOTS_PER_EPOCH
|
||||
return (
|
||||
(spec.SECONDS_PER_ETH1_BLOCK * spec.ETH1_FOLLOW_DISTANCE * 2) # to seconds
|
||||
// spec.SECONDS_PER_SLOT // spec.SLOTS_PER_EPOCH
|
||||
)
|
||||
|
||||
|
@ -6,7 +6,7 @@ from eth2spec.test.helpers.custody import (
|
||||
from eth2spec.test.helpers.attestations import (
|
||||
get_valid_on_time_attestation,
|
||||
)
|
||||
from eth2spec.test.helpers.state import transition_to
|
||||
from eth2spec.test.helpers.state import transition_to, transition_to_valid_shard_slot
|
||||
from eth2spec.test.context import (
|
||||
PHASE0,
|
||||
with_all_phases_except,
|
||||
@ -68,7 +68,8 @@ def run_custody_chunk_response_processing(spec, state, custody_response, valid=T
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_challenge_appended(spec, state):
|
||||
transition_to(spec, state, state.slot + 1)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
shard = 0
|
||||
offset_slots = spec.get_offset_slots(state, shard)
|
||||
shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
||||
@ -89,7 +90,8 @@ def test_challenge_appended(spec, state):
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_challenge_empty_element_replaced(spec, state):
|
||||
transition_to(spec, state, state.slot + 1)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
shard = 0
|
||||
offset_slots = spec.get_offset_slots(state, shard)
|
||||
shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
||||
@ -112,7 +114,8 @@ def test_challenge_empty_element_replaced(spec, state):
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_duplicate_challenge(spec, state):
|
||||
transition_to(spec, state, state.slot + 1)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
shard = 0
|
||||
offset_slots = spec.get_offset_slots(state, shard)
|
||||
shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
||||
@ -135,7 +138,8 @@ def test_duplicate_challenge(spec, state):
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_second_challenge(spec, state):
|
||||
transition_to(spec, state, state.slot + 1)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
shard = 0
|
||||
offset_slots = spec.get_offset_slots(state, shard)
|
||||
shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
||||
@ -160,6 +164,7 @@ def test_second_challenge(spec, state):
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_multiple_epochs_custody(spec, state):
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * 3)
|
||||
|
||||
shard = 0
|
||||
@ -182,6 +187,7 @@ def test_multiple_epochs_custody(spec, state):
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_many_epochs_custody(spec, state):
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * 20)
|
||||
|
||||
shard = 0
|
||||
@ -204,6 +210,7 @@ def test_many_epochs_custody(spec, state):
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_off_chain_attestation(spec, state):
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
|
||||
shard = 0
|
||||
@ -222,6 +229,7 @@ def test_off_chain_attestation(spec, state):
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_custody_response(spec, state):
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH)
|
||||
|
||||
shard = 0
|
||||
@ -251,6 +259,7 @@ def test_custody_response(spec, state):
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_custody_response_multiple_epochs(spec, state):
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * 3)
|
||||
|
||||
shard = 0
|
||||
@ -280,6 +289,7 @@ def test_custody_response_multiple_epochs(spec, state):
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_custody_response_many_epochs(spec, state):
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + spec.SLOTS_PER_EPOCH * 20)
|
||||
|
||||
shard = 0
|
||||
|
@ -63,6 +63,7 @@ def run_standard_custody_slashing_test(spec,
|
||||
slashing_message_data=None,
|
||||
correct=True,
|
||||
valid=True):
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
if shard_lateness is None:
|
||||
shard_lateness = spec.SLOTS_PER_EPOCH
|
||||
transition_to(spec, state, state.slot + shard_lateness)
|
||||
|
@ -22,7 +22,7 @@ from eth2spec.test.helpers.state import transition_to, transition_to_valid_shard
|
||||
|
||||
|
||||
def get_initial_env(spec, state, target_len_offset_slot):
|
||||
state = transition_to_valid_shard_slot(spec, state)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
committee_index = spec.CommitteeIndex(0)
|
||||
target_shard_slot = state.slot + target_len_offset_slot - 1
|
||||
shard = spec.compute_shard_from_committee_index(state, committee_index, target_shard_slot)
|
||||
|
@ -5,7 +5,7 @@ from eth2spec.test.helpers.custody import (
|
||||
from eth2spec.test.helpers.attestations import (
|
||||
get_valid_on_time_attestation,
|
||||
)
|
||||
from eth2spec.test.helpers.state import transition_to
|
||||
from eth2spec.test.helpers.state import transition_to, transition_to_valid_shard_slot
|
||||
from eth2spec.test.context import (
|
||||
PHASE0,
|
||||
with_all_phases_except,
|
||||
@ -26,7 +26,8 @@ def run_process_challenge_deadlines(spec, state):
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_validator_slashed_after_chunk_challenge(spec, state):
|
||||
transition_to(spec, state, state.slot + 1)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
shard = 0
|
||||
offset_slots = spec.get_offset_slots(state, shard)
|
||||
shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
||||
|
@ -10,7 +10,7 @@ from eth2spec.test.helpers.custody import (
|
||||
from eth2spec.test.helpers.attestations import (
|
||||
get_valid_on_time_attestation,
|
||||
)
|
||||
from eth2spec.test.helpers.state import next_epoch_via_block, transition_to
|
||||
from eth2spec.test.helpers.state import next_epoch_via_block, transition_to, transition_to_valid_shard_slot
|
||||
from eth2spec.test.context import (
|
||||
with_all_phases_except,
|
||||
spec_state_test,
|
||||
@ -32,6 +32,8 @@ def run_process_custody_final_updates(spec, state):
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_validator_withdrawal_delay(spec, state):
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
spec.initiate_validator_exit(state, 0)
|
||||
assert state.validators[0].withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
||||
|
||||
@ -43,6 +45,8 @@ def test_validator_withdrawal_delay(spec, state):
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_validator_withdrawal_reenable_after_custody_reveal(spec, state):
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
spec.initiate_validator_exit(state, 0)
|
||||
assert state.validators[0].withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
||||
|
||||
@ -66,7 +70,8 @@ def test_validator_withdrawal_reenable_after_custody_reveal(spec, state):
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_validator_withdrawal_suspend_after_chunk_challenge(spec, state):
|
||||
transition_to(spec, state, state.slot + 1)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
shard = 0
|
||||
offset_slots = spec.get_offset_slots(state, shard)
|
||||
shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
||||
@ -114,7 +119,8 @@ def test_validator_withdrawal_suspend_after_chunk_challenge(spec, state):
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_validator_withdrawal_resume_after_chunk_challenge_response(spec, state):
|
||||
transition_to(spec, state, state.slot + 1)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + 1) # Make len(offset_slots) == 1
|
||||
shard = 0
|
||||
offset_slots = spec.get_offset_slots(state, shard)
|
||||
shard_transition = get_sample_shard_transition(spec, state.slot, [2**15 // 3] * len(offset_slots))
|
||||
|
@ -105,7 +105,7 @@ def test_process_beacon_block_with_normal_shard_transition(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
state = transition_to_valid_shard_slot(spec, state)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
|
||||
target_len_offset_slot = 1
|
||||
committee_index = spec.CommitteeIndex(0)
|
||||
@ -123,7 +123,7 @@ def test_process_beacon_block_with_empty_proposal_transition(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
state = transition_to_valid_shard_slot(spec, state)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
|
||||
target_len_offset_slot = 1
|
||||
committee_index = spec.CommitteeIndex(0)
|
||||
@ -146,7 +146,7 @@ def test_with_shard_transition_with_custody_challenge_and_response(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
state = transition_to_valid_shard_slot(spec, state)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
|
||||
# build shard block
|
||||
shard = 0
|
||||
@ -179,7 +179,7 @@ def test_with_shard_transition_with_custody_challenge_and_response(spec, state):
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_custody_key_reveal(spec, state):
|
||||
state = transition_to_valid_shard_slot(spec, state)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
transition_to(spec, state, state.slot + spec.EPOCHS_PER_CUSTODY_PERIOD * spec.SLOTS_PER_EPOCH)
|
||||
|
||||
block = build_empty_block(spec, state, slot=state.slot + 1)
|
||||
@ -192,7 +192,7 @@ def test_custody_key_reveal(spec, state):
|
||||
@with_all_phases_except([PHASE0])
|
||||
@spec_state_test
|
||||
def test_early_derived_secret_reveal(spec, state):
|
||||
state = transition_to_valid_shard_slot(spec, state)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
block = build_empty_block(spec, state, slot=state.slot + 1)
|
||||
early_derived_secret_reveal = get_valid_early_derived_secret_reveal(spec, state)
|
||||
block.body.early_derived_secret_reveals = [early_derived_secret_reveal]
|
||||
@ -208,7 +208,7 @@ def test_custody_slashing(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
state = transition_to_valid_shard_slot(spec, state)
|
||||
transition_to_valid_shard_slot(spec, state)
|
||||
|
||||
# Build shard block
|
||||
shard = 0
|
||||
|
@ -51,10 +51,12 @@ def test_valid_shard_block(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True)
|
||||
signed_shard_block = build_shard_block(spec, state, shard, slot=beacon_state.slot, signed=True)
|
||||
|
||||
yield from run_shard_blocks(spec, shard_state, signed_shard_block, beacon_state)
|
||||
|
||||
@ -71,7 +73,9 @@ def test_invalid_shard_parent_root(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True)
|
||||
@ -88,7 +92,8 @@ def test_invalid_beacon_parent_root(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True)
|
||||
@ -105,7 +110,8 @@ def test_invalid_slot(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True)
|
||||
@ -123,7 +129,8 @@ def test_invalid_proposer_index(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True)
|
||||
@ -147,7 +154,8 @@ def test_out_of_bound_offset(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
shard = 0
|
||||
slot = (
|
||||
beacon_state.shard_states[shard].slot
|
||||
@ -170,7 +178,8 @@ def test_invalid_offset(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
# 4 is not in `SHARD_BLOCK_OFFSETS`
|
||||
shard = 0
|
||||
slot = beacon_state.shard_states[shard].slot + 4
|
||||
@ -191,7 +200,8 @@ def test_empty_block_body(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, body=b'', signed=True)
|
||||
@ -212,7 +222,8 @@ def test_invalid_signature(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=False)
|
||||
@ -233,7 +244,8 @@ def test_max_offset(spec, state):
|
||||
# skip
|
||||
return
|
||||
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
shard = 0
|
||||
slot = beacon_state.shard_states[shard].slot + spec.SHARD_BLOCK_OFFSETS[spec.MAX_SHARD_BLOCKS_PER_ATTESTATION - 1]
|
||||
transition_to(spec, beacon_state, slot)
|
||||
@ -253,7 +265,8 @@ def test_pending_shard_parent_block(spec, state):
|
||||
return
|
||||
|
||||
# Block N
|
||||
beacon_state = transition_to_valid_shard_slot(spec, state)
|
||||
beacon_state = state.copy()
|
||||
transition_to_valid_shard_slot(spec, beacon_state)
|
||||
shard = 0
|
||||
shard_state = beacon_state.shard_states[shard]
|
||||
signed_shard_block_1 = build_shard_block(spec, beacon_state, shard, slot=beacon_state.slot, signed=True)
|
||||
|
@ -14,6 +14,22 @@ Z2_SIGNATURE = b'\xc0' + b'\x00' * 95
|
||||
STUB_COORDINATES = _signature_to_G2(Z2_SIGNATURE)
|
||||
|
||||
|
||||
def use_milagro():
|
||||
"""
|
||||
Shortcut to use Milagro as BLS library
|
||||
"""
|
||||
global bls
|
||||
bls = milagro_bls
|
||||
|
||||
|
||||
def use_py_ecc():
|
||||
"""
|
||||
Shortcut to use Py-ecc as BLS library
|
||||
"""
|
||||
global bls
|
||||
bls = py_ecc_bls
|
||||
|
||||
|
||||
def only_with_bls(alt_return=None):
|
||||
"""
|
||||
Decorator factory to make a function only run when BLS is active. Otherwise return the default.
|
||||
|
@ -1,17 +1,9 @@
|
||||
from hashlib import sha256
|
||||
from typing import Dict, Union
|
||||
from remerkleable.byte_arrays import Bytes32
|
||||
from typing import Union
|
||||
|
||||
ZERO_BYTES32 = b'\x00' * 32
|
||||
|
||||
|
||||
def _hash(x: Union[bytes, bytearray, memoryview]) -> bytes:
|
||||
return sha256(x).digest()
|
||||
|
||||
|
||||
hash_cache: Dict[bytes, bytes] = {}
|
||||
|
||||
|
||||
def hash(x: bytes) -> bytes:
|
||||
if x in hash_cache:
|
||||
return hash_cache[x]
|
||||
return _hash(x)
|
||||
def hash(x: Union[bytes, bytearray, memoryview]) -> Bytes32:
|
||||
return Bytes32(sha256(x).digest())
|
||||
|
@ -1,3 +1,6 @@
|
||||
from typing import TypeVar
|
||||
|
||||
from remerkleable.basic import uint
|
||||
from remerkleable.core import View
|
||||
from remerkleable.byte_arrays import Bytes32
|
||||
|
||||
@ -8,3 +11,15 @@ def serialize(obj: View) -> bytes:
|
||||
|
||||
def hash_tree_root(obj: View) -> Bytes32:
|
||||
return Bytes32(obj.get_backing().merkle_root())
|
||||
|
||||
|
||||
def uint_to_bytes(n: uint) -> bytes:
|
||||
return serialize(n)
|
||||
|
||||
|
||||
V = TypeVar('V', bound=View)
|
||||
|
||||
|
||||
# Helper method for typing copies, and avoiding a example_input.copy() method call, instead of copy(example_input)
|
||||
def copy(obj: V) -> V:
|
||||
return obj.copy()
|
||||
|
@ -5,4 +5,4 @@ from remerkleable.complex import Container, Vector, List
|
||||
from remerkleable.basic import boolean, bit, uint, byte, uint8, uint16, uint32, uint64, uint128, uint256
|
||||
from remerkleable.bitfields import Bitvector, Bitlist
|
||||
from remerkleable.byte_arrays import ByteVector, Bytes1, Bytes4, Bytes8, Bytes32, Bytes48, Bytes96, ByteList
|
||||
from remerkleable.core import BasicView, View, TypeDef
|
||||
from remerkleable.core import BasicView, View
|
||||
|
43
tests/formats/finality/README.md
Normal file
43
tests/formats/finality/README.md
Normal file
@ -0,0 +1,43 @@
|
||||
# Finality tests
|
||||
|
||||
The aim of the tests for the finality rules.
|
||||
|
||||
- `finality`: transitions triggered by one or more blocks.
|
||||
|
||||
## Test case format
|
||||
|
||||
### `meta.yaml`
|
||||
|
||||
```yaml
|
||||
description: string -- Optional. Description of test case, purely for debugging purposes.
|
||||
bls_setting: int -- see general test-format spec.
|
||||
blocks_count: int -- the number of blocks processed in this test.
|
||||
```
|
||||
|
||||
### `pre.yaml`
|
||||
|
||||
A YAML-encoded `BeaconState`, the state before running the block transitions.
|
||||
|
||||
Also available as `pre.ssz`.
|
||||
|
||||
|
||||
### `blocks_<index>.yaml`
|
||||
|
||||
A series of files, with `<index>` in range `[0, blocks_count)`. Blocks need to be processed in order,
|
||||
following the main transition function (i.e. process slot and epoch transitions in between blocks as normal)
|
||||
|
||||
Each file is a YAML-encoded `SignedBeaconBlock`.
|
||||
|
||||
Each block is also available as `blocks_<index>.ssz`
|
||||
|
||||
### `post.yaml`
|
||||
|
||||
A YAML-encoded `BeaconState`, the state after applying the block transitions.
|
||||
|
||||
Also available as `post.ssz`.
|
||||
|
||||
|
||||
## Condition
|
||||
|
||||
The resulting state should match the expected `post` state, or if the `post` state is left blank,
|
||||
the handler should reject the series of blocks as invalid.
|
@ -149,7 +149,9 @@ def case03_aggregate():
|
||||
else:
|
||||
raise Exception("Should have been INVALID")
|
||||
|
||||
yield f'aggregate_na_pubkeys', {
|
||||
# No signatures to aggregate. Follow IETF BLS spec, return `None` to represent INVALID.
|
||||
# https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-02#section-2.8
|
||||
yield f'aggregate_na_signatures', {
|
||||
'input': [],
|
||||
'output': None,
|
||||
}
|
||||
@ -319,6 +321,7 @@ def create_provider(handler_name: str,
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
bls.use_py_ecc() # Py-ecc is chosen instead of Milagro, since the code is better understood to be correct.
|
||||
gen_runner.run_generator("bls", [
|
||||
create_provider('sign', case01_sign),
|
||||
create_provider('verify', case02_verify),
|
||||
|
@ -14,6 +14,7 @@ from gen_from_tests.gen import generate_from_tests
|
||||
from importlib import reload
|
||||
from eth2spec.config import config_util
|
||||
from eth2spec.test.context import PHASE0
|
||||
from eth2spec.utils import bls
|
||||
|
||||
|
||||
def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider:
|
||||
@ -22,6 +23,7 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin
|
||||
config_util.prepare_config(configs_path, config_name)
|
||||
reload(spec_phase0)
|
||||
reload(spec_phase1)
|
||||
bls.use_milagro()
|
||||
return config_name
|
||||
|
||||
def cases_fn() -> Iterable[gen_typing.TestCase]:
|
||||
|
5
tests/generators/finality/README.md
Normal file
5
tests/generators/finality/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Finality tests
|
||||
|
||||
Finality tests cover regular state-transitions in a common block-list format to test finality rules.
|
||||
|
||||
Information on the format of the tests can be found in the [finality test formats documentation](../../formats/finality/README.md).
|
39
tests/generators/finality/main.py
Normal file
39
tests/generators/finality/main.py
Normal file
@ -0,0 +1,39 @@
|
||||
from typing import Iterable
|
||||
from importlib import reload
|
||||
|
||||
from gen_base import gen_runner, gen_typing
|
||||
from gen_from_tests.gen import generate_from_tests
|
||||
|
||||
from eth2spec.test.context import PHASE0
|
||||
from eth2spec.test.phase0.finality import test_finality
|
||||
from eth2spec.config import config_util
|
||||
from eth2spec.phase0 import spec as spec_phase0
|
||||
from eth2spec.phase1 import spec as spec_phase1
|
||||
from eth2spec.utils import bls
|
||||
|
||||
|
||||
def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider:
|
||||
|
||||
def prepare_fn(configs_path: str) -> str:
|
||||
config_util.prepare_config(configs_path, config_name)
|
||||
reload(spec_phase0)
|
||||
reload(spec_phase1)
|
||||
bls.use_milagro()
|
||||
return config_name
|
||||
|
||||
def cases_fn() -> Iterable[gen_typing.TestCase]:
|
||||
return generate_from_tests(
|
||||
runner_name='finality',
|
||||
handler_name=handler_name,
|
||||
src=tests_src,
|
||||
fork_name=PHASE0,
|
||||
)
|
||||
|
||||
return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
gen_runner.run_generator("finality", [
|
||||
create_provider('finality', test_finality, 'minimal'),
|
||||
create_provider('finality', test_finality, 'mainnet'),
|
||||
])
|
2
tests/generators/finality/requirements.txt
Normal file
2
tests/generators/finality/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
../../core/gen_helpers
|
||||
../../../
|
@ -1,13 +1,14 @@
|
||||
from typing import Iterable
|
||||
|
||||
from eth2spec.test.context import PHASE0
|
||||
from eth2spec.test.genesis import test_initialization, test_validity
|
||||
from eth2spec.test.phase0.genesis import test_initialization, test_validity
|
||||
|
||||
from gen_base import gen_runner, gen_typing
|
||||
from gen_from_tests.gen import generate_from_tests
|
||||
from eth2spec.phase0 import spec as spec
|
||||
from importlib import reload
|
||||
from eth2spec.config import config_util
|
||||
from eth2spec.utils import bls
|
||||
|
||||
|
||||
def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider:
|
||||
@ -15,6 +16,7 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin
|
||||
def prepare_fn(configs_path: str) -> str:
|
||||
config_util.prepare_config(configs_path, config_name)
|
||||
reload(spec)
|
||||
bls.use_milagro()
|
||||
return config_name
|
||||
|
||||
def cases_fn() -> Iterable[gen_typing.TestCase]:
|
||||
|
@ -16,6 +16,7 @@ from eth2spec.config import config_util
|
||||
from eth2spec.phase0 import spec as spec_phase0
|
||||
from eth2spec.phase1 import spec as spec_phase1
|
||||
from eth2spec.test.context import PHASE0
|
||||
from eth2spec.utils import bls
|
||||
|
||||
|
||||
def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider:
|
||||
@ -24,6 +25,7 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin
|
||||
config_util.prepare_config(configs_path, config_name)
|
||||
reload(spec_phase0)
|
||||
reload(spec_phase1)
|
||||
bls.use_milagro()
|
||||
return config_name
|
||||
|
||||
def cases_fn() -> Iterable[gen_typing.TestCase]:
|
||||
|
@ -12,6 +12,7 @@ from gen_from_tests.gen import generate_from_tests
|
||||
from importlib import reload
|
||||
from eth2spec.config import config_util
|
||||
from eth2spec.test.context import PHASE0
|
||||
from eth2spec.utils import bls
|
||||
|
||||
|
||||
def create_provider(tests_src, config_name: str) -> gen_typing.TestProvider:
|
||||
@ -20,6 +21,7 @@ def create_provider(tests_src, config_name: str) -> gen_typing.TestProvider:
|
||||
config_util.prepare_config(configs_path, config_name)
|
||||
reload(spec_phase0)
|
||||
reload(spec_phase1)
|
||||
bls.use_milagro()
|
||||
return config_name
|
||||
|
||||
def cases_fn() -> Iterable[gen_typing.TestCase]:
|
||||
|
@ -9,6 +9,7 @@ from eth2spec.test.phase0.sanity import test_blocks, test_slots
|
||||
from eth2spec.config import config_util
|
||||
from eth2spec.phase0 import spec as spec_phase0
|
||||
from eth2spec.phase1 import spec as spec_phase1
|
||||
from eth2spec.utils import bls
|
||||
|
||||
|
||||
def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider:
|
||||
@ -17,6 +18,7 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin
|
||||
config_util.prepare_config(configs_path, config_name)
|
||||
reload(spec_phase0)
|
||||
reload(spec_phase1)
|
||||
bls.use_milagro()
|
||||
return config_name
|
||||
|
||||
def cases_fn() -> Iterable[gen_typing.TestCase]:
|
||||
|
Loading…
x
Reference in New Issue
Block a user