mirror of
https://github.com/status-im/eth2.0-specs.git
synced 2025-02-20 22:38:11 +00:00
Merge pull request #1202 from ethereum/test_genesis
Add `test_genesis.py` and fix `is_genesis_trigger`
This commit is contained in:
commit
2f43f9c339
@ -17,6 +17,8 @@ MIN_PER_EPOCH_CHURN_LIMIT: 4
|
||||
CHURN_LIMIT_QUOTIENT: 65536
|
||||
# See issue 563
|
||||
SHUFFLE_ROUND_COUNT: 90
|
||||
# `2**16` (= 65,536)
|
||||
MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 65536
|
||||
|
||||
|
||||
# Deposit contract
|
||||
|
@ -16,6 +16,8 @@ MIN_PER_EPOCH_CHURN_LIMIT: 4
|
||||
CHURN_LIMIT_QUOTIENT: 65536
|
||||
# [customized] Faster, but unsecure.
|
||||
SHUFFLE_ROUND_COUNT: 10
|
||||
# [customized]
|
||||
MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 64
|
||||
|
||||
|
||||
# Deposit contract
|
||||
|
File diff suppressed because one or more lines are too long
@ -6,7 +6,7 @@ WITHDRAWAL_CREDENTIALS_LENGTH: constant(uint256) = 32 # bytes
|
||||
AMOUNT_LENGTH: constant(uint256) = 8 # bytes
|
||||
SIGNATURE_LENGTH: constant(uint256) = 96 # bytes
|
||||
|
||||
Deposit: event({
|
||||
DepositEvent: event({
|
||||
pubkey: bytes[48],
|
||||
withdrawal_credentials: bytes[32],
|
||||
amount: bytes[8],
|
||||
@ -42,8 +42,9 @@ def to_little_endian_64(value: uint256) -> bytes[8]:
|
||||
|
||||
@public
|
||||
@constant
|
||||
def get_deposit_root() -> bytes32:
|
||||
node: bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
def get_hash_tree_root() -> bytes32:
|
||||
zero_bytes32: bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
node: bytes32 = zero_bytes32
|
||||
size: uint256 = self.deposit_count
|
||||
for height in range(DEPOSIT_CONTRACT_TREE_DEPTH):
|
||||
if bitwise_and(size, 1) == 1: # More gas efficient than `size % 2 == 1`
|
||||
@ -51,7 +52,7 @@ def get_deposit_root() -> bytes32:
|
||||
else:
|
||||
node = sha256(concat(node, self.zero_hashes[height]))
|
||||
size /= 2
|
||||
return node
|
||||
return sha256(concat(node, self.to_little_endian_64(self.deposit_count), slice(zero_bytes32, start=0, len=24)))
|
||||
|
||||
|
||||
@public
|
||||
@ -75,11 +76,11 @@ def deposit(pubkey: bytes[PUBKEY_LENGTH],
|
||||
assert len(withdrawal_credentials) == WITHDRAWAL_CREDENTIALS_LENGTH
|
||||
assert len(signature) == SIGNATURE_LENGTH
|
||||
|
||||
# Emit `Deposit` log
|
||||
# Emit `DepositEvent` log
|
||||
amount: bytes[8] = self.to_little_endian_64(deposit_amount)
|
||||
log.Deposit(pubkey, withdrawal_credentials, amount, signature, self.to_little_endian_64(self.deposit_count))
|
||||
log.DepositEvent(pubkey, withdrawal_credentials, amount, signature, self.to_little_endian_64(self.deposit_count))
|
||||
|
||||
# Compute `DepositData` root
|
||||
# Compute `DepositData` hash tree root
|
||||
zero_bytes32: bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
pubkey_root: bytes32 = sha256(concat(pubkey, slice(zero_bytes32, start=0, len=64 - PUBKEY_LENGTH)))
|
||||
signature_root: bytes32 = sha256(concat(
|
||||
@ -91,7 +92,7 @@ def deposit(pubkey: bytes[PUBKEY_LENGTH],
|
||||
sha256(concat(amount, slice(zero_bytes32, start=0, len=32 - AMOUNT_LENGTH), signature_root)),
|
||||
))
|
||||
|
||||
# Add `DepositData` root to Merkle tree (update a single `branch` node)
|
||||
# Add `DepositData` hash tree root to Merkle tree (update a single `branch` node)
|
||||
self.deposit_count += 1
|
||||
size: uint256 = self.deposit_count
|
||||
for height in range(DEPOSIT_CONTRACT_TREE_DEPTH):
|
||||
|
@ -15,26 +15,12 @@ from eth2spec.phase0.spec import (
|
||||
DepositData,
|
||||
)
|
||||
from eth2spec.utils.hash_function import hash
|
||||
from eth2spec.utils.ssz.ssz_typing import List
|
||||
from eth2spec.utils.ssz.ssz_impl import (
|
||||
hash_tree_root,
|
||||
)
|
||||
|
||||
|
||||
def compute_merkle_root(leaf_nodes):
|
||||
assert len(leaf_nodes) >= 1
|
||||
empty_node = b'\x00' * 32
|
||||
child_nodes = leaf_nodes[:]
|
||||
for _ in range(DEPOSIT_CONTRACT_TREE_DEPTH):
|
||||
parent_nodes = []
|
||||
if len(child_nodes) % 2 == 1:
|
||||
child_nodes.append(empty_node)
|
||||
for j in range(0, len(child_nodes), 2):
|
||||
parent_nodes.append(hash(child_nodes[j] + child_nodes[j + 1]))
|
||||
child_nodes = parent_nodes
|
||||
empty_node = hash(empty_node + empty_node)
|
||||
return child_nodes[0]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def deposit_input():
|
||||
"""
|
||||
@ -110,8 +96,8 @@ def test_deposit_inputs(registration_contract,
|
||||
)
|
||||
|
||||
|
||||
def test_deposit_log(registration_contract, a0, w3, deposit_input):
|
||||
log_filter = registration_contract.events.Deposit.createFilter(
|
||||
def test_deposit_event_log(registration_contract, a0, w3, deposit_input):
|
||||
log_filter = registration_contract.events.DepositEvent.createFilter(
|
||||
fromBlock='latest',
|
||||
)
|
||||
|
||||
@ -131,13 +117,14 @@ def test_deposit_log(registration_contract, a0, w3, deposit_input):
|
||||
assert log['signature'] == deposit_input[2]
|
||||
assert log['index'] == i.to_bytes(8, 'little')
|
||||
|
||||
|
||||
def test_deposit_tree(registration_contract, w3, assert_tx_failed, deposit_input):
|
||||
log_filter = registration_contract.events.Deposit.createFilter(
|
||||
log_filter = registration_contract.events.DepositEvent.createFilter(
|
||||
fromBlock='latest',
|
||||
)
|
||||
|
||||
deposit_amount_list = [randint(MIN_DEPOSIT_AMOUNT, FULL_DEPOSIT_AMOUNT * 2) for _ in range(10)]
|
||||
leaf_nodes = []
|
||||
deposit_data_list = []
|
||||
for i in range(0, 10):
|
||||
tx_hash = registration_contract.functions.deposit(
|
||||
*deposit_input,
|
||||
@ -151,13 +138,12 @@ def test_deposit_tree(registration_contract, w3, assert_tx_failed, deposit_input
|
||||
|
||||
assert log["index"] == i.to_bytes(8, 'little')
|
||||
|
||||
deposit_data = DepositData(
|
||||
deposit_data_list.append(DepositData(
|
||||
pubkey=deposit_input[0],
|
||||
withdrawal_credentials=deposit_input[1],
|
||||
amount=deposit_amount_list[i],
|
||||
signature=deposit_input[2],
|
||||
)
|
||||
hash_tree_root_result = hash_tree_root(deposit_data)
|
||||
leaf_nodes.append(hash_tree_root_result)
|
||||
root = compute_merkle_root(leaf_nodes)
|
||||
assert root == registration_contract.functions.get_deposit_root().call()
|
||||
))
|
||||
|
||||
root = hash_tree_root(List[DepositData, 2**32](*deposit_data_list))
|
||||
assert root == registration_contract.functions.get_hash_tree_root().call()
|
||||
|
@ -95,7 +95,6 @@
|
||||
- [`initiate_validator_exit`](#initiate_validator_exit)
|
||||
- [`slash_validator`](#slash_validator)
|
||||
- [Genesis](#genesis)
|
||||
- [Genesis trigger](#genesis-trigger)
|
||||
- [Genesis state](#genesis-state)
|
||||
- [Genesis block](#genesis-block)
|
||||
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
|
||||
@ -126,7 +125,6 @@
|
||||
This document represents the specification for Phase 0 of Ethereum 2.0 -- The Beacon Chain.
|
||||
|
||||
At the core of Ethereum 2.0 is a system chain called the "beacon chain". The beacon chain stores and manages the registry of [validators](#dfn-validator). In the initial deployment phases of Ethereum 2.0, the only mechanism to become a [validator](#dfn-validator) is to make a one-way ETH transaction to a deposit contract on Ethereum 1.0. Activation as a [validator](#dfn-validator) happens when Ethereum 1.0 deposit receipts are processed by the beacon chain, the activation balance is reached, and a queuing process is completed. Exit is either voluntary or done forcibly as a penalty for misbehavior.
|
||||
|
||||
The primary source of load on the beacon chain is "attestations". Attestations are simultaneously availability votes for a shard block and proof-of-stake votes for a beacon block. A sufficient number of attestations for the same shard block create a "crosslink", confirming the shard segment up to that shard block into the beacon chain. Crosslinks also serve as infrastructure for asynchronous cross-shard communication.
|
||||
|
||||
## Notation
|
||||
@ -176,6 +174,7 @@ The following values are (non-configurable) constants used throughout the specif
|
||||
| `ZERO_HASH` | `Hash(b'\x00' * 32)` |
|
||||
| `BASE_REWARDS_PER_EPOCH` | `5` |
|
||||
| `DEPOSIT_CONTRACT_TREE_DEPTH` | `2**5` (= 32) |
|
||||
| `SECONDS_PER_DAY` | `86400` |
|
||||
|
||||
## Configuration
|
||||
|
||||
@ -191,6 +190,8 @@ The following values are (non-configurable) constants used throughout the specif
|
||||
| `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**16` (= 65,536) |
|
||||
| `MIN_GENESIS_TIME` | `1578009600` (Jan 3, 2020) |
|
||||
| `JUSTIFICATION_BITS_LENGTH` | `4` |
|
||||
|
||||
* For the safety of crosslinks, `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](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.)
|
||||
@ -446,7 +447,7 @@ class Attestation(Container):
|
||||
|
||||
```python
|
||||
class Deposit(Container):
|
||||
proof: Vector[Hash, DEPOSIT_CONTRACT_TREE_DEPTH] # Merkle path to deposit root
|
||||
proof: Vector[Hash, DEPOSIT_CONTRACT_TREE_DEPTH + 1] # Merkle path to deposit data list root
|
||||
data: DepositData
|
||||
```
|
||||
|
||||
@ -1109,61 +1110,43 @@ def slash_validator(state: BeaconState,
|
||||
|
||||
## Genesis
|
||||
|
||||
### Genesis trigger
|
||||
|
||||
Before genesis has been triggered and whenever the deposit contract emits a `Deposit` log, call the function `is_genesis_trigger(deposits: Sequence[Deposit], timestamp: uint64) -> bool` where:
|
||||
|
||||
* `deposits` is the list of all deposits, ordered chronologically, up to and including the deposit triggering the latest `Deposit` log
|
||||
* `timestamp` is the Unix timestamp in the Ethereum 1.0 block that emitted the latest `Deposit` log
|
||||
|
||||
When `is_genesis_trigger(deposits, timestamp) is True` for the first time, let:
|
||||
|
||||
* `genesis_deposits = deposits`
|
||||
* `genesis_time = timestamp - timestamp % SECONDS_PER_DAY + 2 * SECONDS_PER_DAY` where `SECONDS_PER_DAY = 86400`
|
||||
* `genesis_eth1_data` be the object of type `Eth1Data` where:
|
||||
* `genesis_eth1_data.block_hash` is the Ethereum 1.0 block hash that emitted the log for the last deposit in `deposits`
|
||||
* `genesis_eth1_data.deposit_root` is the deposit root for the last deposit in `deposits`
|
||||
* `genesis_eth1_data.deposit_count = len(genesis_deposits)`
|
||||
|
||||
*Note*: The function `is_genesis_trigger` has yet to be agreed upon by the community, and can be updated as necessary. We define the following testing placeholder:
|
||||
|
||||
```python
|
||||
def is_genesis_trigger(deposits: Sequence[Deposit], timestamp: uint64) -> bool:
|
||||
# Process deposits
|
||||
state = BeaconState()
|
||||
for deposit in deposits:
|
||||
process_deposit(state, deposit)
|
||||
|
||||
# Count active validators at genesis
|
||||
active_validator_count = 0
|
||||
for validator in state.validators:
|
||||
if validator.effective_balance == MAX_EFFECTIVE_BALANCE:
|
||||
active_validator_count += 1
|
||||
|
||||
# Check effective balance to trigger genesis
|
||||
GENESIS_ACTIVE_VALIDATOR_COUNT = 2**16
|
||||
return active_validator_count == GENESIS_ACTIVE_VALIDATOR_COUNT
|
||||
```
|
||||
|
||||
### Genesis state
|
||||
|
||||
Let `genesis_state = get_genesis_beacon_state(genesis_deposits, genesis_time, genesis_eth1_data)`.
|
||||
Before the Ethereum 2.0 genesis has been triggered, and for every Ethereum 1.0 block, call `initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits)` where:
|
||||
|
||||
* `eth1_block_hash` is the hash of the Ethereum 1.0 block
|
||||
* `eth1_timestamp` is the Unix timestamp corresponding to `eth1_block_hash`
|
||||
* `deposits` is the sequence of all deposits, ordered chronologically, up to the block with hash `eth1_block_hash`
|
||||
|
||||
The genesis state `genesis_state` is the return value of calling `initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits)` only if `is_valid_genesis_state(genesis_state) is True`.
|
||||
|
||||
Implementations can choose to support different (more optimized) variations of the below initialization approach:
|
||||
- Build the `genesis_state` from a stream of deposits by incrementally updating the `state.eth1_data.deposit_root`.
|
||||
- Compute deposit proofs for the final `state.eth1_data.deposit_root`, and process as a pre-determined collection.
|
||||
|
||||
*Note*: The two constants `MIN_GENESIS_TIME` and `MIN_GENESIS_ACTIVE_VALIDATOR_COUNT` have yet to be agreed upon by the community, and can be updated as necessary.
|
||||
|
||||
```python
|
||||
def get_genesis_beacon_state(deposits: Sequence[Deposit], genesis_time: int, eth1_data: Eth1Data) -> BeaconState:
|
||||
def initialize_beacon_state_from_eth1(eth1_block_hash: Hash,
|
||||
eth1_timestamp: uint64,
|
||||
deposits: Sequence[Deposit]) -> BeaconState:
|
||||
state = BeaconState(
|
||||
genesis_time=genesis_time,
|
||||
eth1_data=eth1_data,
|
||||
genesis_time=eth1_timestamp - eth1_timestamp % SECONDS_PER_DAY + 2 * SECONDS_PER_DAY,
|
||||
eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=len(deposits)),
|
||||
latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())),
|
||||
)
|
||||
|
||||
# Process genesis deposits
|
||||
for deposit in deposits:
|
||||
# Process deposits
|
||||
leaves = list(map(lambda deposit: deposit.data, deposits))
|
||||
for index, deposit in enumerate(deposits):
|
||||
state.eth1_data.deposit_root = hash_tree_root(
|
||||
List[DepositData, 2**DEPOSIT_CONTRACT_TREE_DEPTH](*leaves[:index + 1])
|
||||
)
|
||||
process_deposit(state, deposit)
|
||||
|
||||
# Process genesis activations
|
||||
for validator in state.validators:
|
||||
if validator.effective_balance >= MAX_EFFECTIVE_BALANCE:
|
||||
# Process activations
|
||||
for index, validator in enumerate(state.validators):
|
||||
if state.balances[index] >= MAX_EFFECTIVE_BALANCE:
|
||||
validator.activation_eligibility_epoch = GENESIS_EPOCH
|
||||
validator.activation_epoch = GENESIS_EPOCH
|
||||
|
||||
@ -1174,6 +1157,16 @@ def get_genesis_beacon_state(deposits: Sequence[Deposit], genesis_time: int, eth
|
||||
return state
|
||||
```
|
||||
|
||||
```python
|
||||
def is_valid_genesis_state(state: BeaconState) -> bool:
|
||||
if state.genesis_time < MIN_GENESIS_TIME:
|
||||
return False
|
||||
elif len(get_active_validator_indices(state, GENESIS_EPOCH)) < MIN_GENESIS_ACTIVE_VALIDATOR_COUNT:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
```
|
||||
|
||||
### Genesis block
|
||||
|
||||
Let `genesis_block = BeaconBlock(state_root=hash_tree_root(genesis_state))`.
|
||||
@ -1455,7 +1448,7 @@ def process_registry_updates(state: BeaconState) -> None:
|
||||
for index, validator in enumerate(state.validators):
|
||||
if (
|
||||
validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and
|
||||
validator.effective_balance >= MAX_EFFECTIVE_BALANCE
|
||||
validator.effective_balance == MAX_EFFECTIVE_BALANCE
|
||||
):
|
||||
validator.activation_eligibility_epoch = get_current_epoch(state)
|
||||
|
||||
@ -1695,7 +1688,7 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
||||
assert verify_merkle_branch(
|
||||
leaf=hash_tree_root(deposit.data),
|
||||
proof=deposit.proof,
|
||||
depth=DEPOSIT_CONTRACT_TREE_DEPTH,
|
||||
depth=DEPOSIT_CONTRACT_TREE_DEPTH + 1, # add 1 for the SSZ length mix-in
|
||||
index=state.eth1_deposit_index,
|
||||
root=state.eth1_data.deposit_root,
|
||||
)
|
||||
|
@ -14,7 +14,7 @@
|
||||
- [`deposit` function](#deposit-function)
|
||||
- [Deposit amount](#deposit-amount)
|
||||
- [Withdrawal credentials](#withdrawal-credentials)
|
||||
- [`Deposit` log](#deposit-log)
|
||||
- [`DepositEvent` log](#depositevent-log)
|
||||
- [Vyper code](#vyper-code)
|
||||
|
||||
<!-- /TOC -->
|
||||
@ -53,9 +53,9 @@ One of the `DepositData` fields is `withdrawal_credentials`. It is a commitment
|
||||
|
||||
The private key corresponding to `withdrawal_pubkey` will be required to initiate a withdrawal. It can be stored separately until a withdrawal is required, e.g. in cold storage.
|
||||
|
||||
#### `Deposit` log
|
||||
#### `DepositEvent` log
|
||||
|
||||
Every Ethereum 1.0 deposit emits a `Deposit` log for consumption by the beacon chain. The deposit contract does little validation, pushing most of the validator onboarding logic to the beacon chain. In particular, the proof of possession (a BLS12-381 signature) is not verified by the deposit contract.
|
||||
Every Ethereum 1.0 deposit emits a `DepositEvent` log for consumption by the beacon chain. The deposit contract does little validation, pushing most of the validator onboarding logic to the beacon chain. In particular, the proof of possession (a BLS12-381 signature) is not verified by the deposit contract.
|
||||
|
||||
## Vyper code
|
||||
|
||||
|
@ -221,7 +221,7 @@ epoch_signature = bls_sign(
|
||||
|
||||
##### Eth1 Data
|
||||
|
||||
The `block.eth1_data` field is for block proposers to vote on recent Eth 1.0 data. This recent data contains an Eth 1.0 block hash as well as the associated deposit root (as calculated by the `get_deposit_root()` method of the deposit contract) and deposit count after execution of the corresponding Eth 1.0 block. If over half of the block proposers in the current Eth 1.0 voting period vote for the same `eth1_data` then `state.eth1_data` updates at the end of the voting period. Each deposit in `block.body.deposits` must verify against `state.eth1_data.eth1_deposit_root`.
|
||||
The `block.eth1_data` field is for block proposers to vote on recent Eth 1.0 data. This recent data contains an Eth 1.0 block hash as well as the associated deposit root (as calculated by the `get_hash_tree_root()` method of the deposit contract) and deposit count after execution of the corresponding Eth 1.0 block. If over half of the block proposers in the current Eth 1.0 voting period vote for the same `eth1_data` then `state.eth1_data` updates at the end of the voting period. Each deposit in `block.body.deposits` must verify against `state.eth1_data.eth1_deposit_root`.
|
||||
|
||||
Let `get_eth1_data(distance: int) -> Eth1Data` be the (subjective) function that returns the Eth 1.0 data at distance `distance` relative to the Eth 1.0 head at the start of the current Eth 1.0 voting period. Let `previous_eth1_distance` be the distance relative to the Eth 1.0 block corresponding to `state.eth1_data.block_hash` at the start of the current Eth 1.0 voting period. An honest block proposer sets `block.eth1_data = get_eth1_vote(state, previous_eth1_distance)` where:
|
||||
|
||||
|
@ -27,9 +27,13 @@ def with_state(fn):
|
||||
DEFAULT_BLS_ACTIVE = False
|
||||
|
||||
|
||||
def spectest_with_bls_switch(fn):
|
||||
return bls_switch(spectest()(fn))
|
||||
|
||||
|
||||
# shorthand for decorating @with_state @spectest()
|
||||
def spec_state_test(fn):
|
||||
return with_state(bls_switch(spectest()(fn)))
|
||||
return with_state(spectest_with_bls_switch(fn))
|
||||
|
||||
|
||||
def expect_assertion_error(fn):
|
||||
|
0
test_libs/pyspec/eth2spec/test/genesis/__init__.py
Normal file
0
test_libs/pyspec/eth2spec/test/genesis/__init__.py
Normal file
@ -0,0 +1,30 @@
|
||||
from eth2spec.test.context import spectest_with_bls_switch, with_phases
|
||||
from eth2spec.test.helpers.deposits import (
|
||||
prepare_genesis_deposits,
|
||||
)
|
||||
|
||||
|
||||
@with_phases(['phase0'])
|
||||
@spectest_with_bls_switch
|
||||
def test_initialize_beacon_state_from_eth1(spec):
|
||||
deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT
|
||||
deposits, deposit_root = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE, signed=True)
|
||||
|
||||
eth1_block_hash = b'\x12' * 32
|
||||
eth1_timestamp = spec.MIN_GENESIS_TIME
|
||||
|
||||
yield 'eth1_block_hash', eth1_block_hash
|
||||
yield 'eth1_timestamp', eth1_timestamp
|
||||
yield 'deposits', deposits
|
||||
|
||||
# initialize beacon_state
|
||||
state = spec.initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits)
|
||||
|
||||
assert state.genesis_time == eth1_timestamp - eth1_timestamp % spec.SECONDS_PER_DAY + 2 * spec.SECONDS_PER_DAY
|
||||
assert len(state.validators) == deposit_count
|
||||
assert state.eth1_data.deposit_root == deposit_root
|
||||
assert state.eth1_data.deposit_count == deposit_count
|
||||
assert state.eth1_data.block_hash == eth1_block_hash
|
||||
|
||||
# yield state
|
||||
yield 'state', state
|
@ -0,0 +1,86 @@
|
||||
from eth2spec.test.context import spectest_with_bls_switch, with_phases
|
||||
from eth2spec.test.helpers.deposits import (
|
||||
prepare_genesis_deposits,
|
||||
)
|
||||
|
||||
|
||||
def create_valid_beacon_state(spec):
|
||||
deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT
|
||||
deposits, _ = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE, signed=True)
|
||||
|
||||
eth1_block_hash = b'\x12' * 32
|
||||
eth1_timestamp = spec.MIN_GENESIS_TIME
|
||||
return spec.initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits)
|
||||
|
||||
|
||||
def run_is_valid_genesis_state(spec, state, valid=True):
|
||||
"""
|
||||
Run ``is_valid_genesis_state``, yielding:
|
||||
- state ('state')
|
||||
- is_valid ('is_valid')
|
||||
If ``valid == False``, run expecting ``AssertionError``
|
||||
"""
|
||||
yield state
|
||||
is_valid = spec.is_valid_genesis_state(state)
|
||||
yield 'is_valid', is_valid
|
||||
|
||||
|
||||
@with_phases(['phase0'])
|
||||
@spectest_with_bls_switch
|
||||
def test_is_valid_genesis_state_true(spec):
|
||||
state = create_valid_beacon_state(spec)
|
||||
|
||||
yield from run_is_valid_genesis_state(spec, state, valid=True)
|
||||
|
||||
|
||||
@with_phases(['phase0'])
|
||||
@spectest_with_bls_switch
|
||||
def test_is_valid_genesis_state_false_invalid_timestamp(spec):
|
||||
state = create_valid_beacon_state(spec)
|
||||
state.genesis_time = spec.MIN_GENESIS_TIME - 1
|
||||
|
||||
yield from run_is_valid_genesis_state(spec, state, valid=True)
|
||||
|
||||
|
||||
@with_phases(['phase0'])
|
||||
@spectest_with_bls_switch
|
||||
def test_is_valid_genesis_state_true_more_balance(spec):
|
||||
state = create_valid_beacon_state(spec)
|
||||
state.validators[0].effective_balance = spec.MAX_EFFECTIVE_BALANCE + 1
|
||||
|
||||
yield from run_is_valid_genesis_state(spec, state, valid=True)
|
||||
|
||||
|
||||
@with_phases(['phase0'])
|
||||
@spectest_with_bls_switch
|
||||
def test_is_valid_genesis_state_false_not_enough_balance(spec):
|
||||
state = create_valid_beacon_state(spec)
|
||||
state.validators[0].effective_balance = spec.MAX_EFFECTIVE_BALANCE - 1
|
||||
|
||||
yield from run_is_valid_genesis_state(spec, state, valid=False)
|
||||
|
||||
|
||||
@with_phases(['phase0'])
|
||||
@spectest_with_bls_switch
|
||||
def test_is_valid_genesis_state_true_one_more_validator(spec):
|
||||
deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT + 1
|
||||
deposits, _ = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE, signed=True)
|
||||
|
||||
eth1_block_hash = b'\x12' * 32
|
||||
eth1_timestamp = spec.MIN_GENESIS_TIME
|
||||
state = spec.initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits)
|
||||
|
||||
yield from run_is_valid_genesis_state(spec, state, valid=True)
|
||||
|
||||
|
||||
@with_phases(['phase0'])
|
||||
@spectest_with_bls_switch
|
||||
def test_is_valid_genesis_state_false_not_enough_validator(spec):
|
||||
deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT - 1
|
||||
deposits, _ = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE, signed=True)
|
||||
|
||||
eth1_block_hash = b'\x12' * 32
|
||||
eth1_timestamp = spec.MIN_GENESIS_TIME
|
||||
state = spec.initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits)
|
||||
|
||||
yield from run_is_valid_genesis_state(spec, state, valid=False)
|
@ -1,66 +1,88 @@
|
||||
from eth2spec.test.helpers.keys import pubkeys, privkeys
|
||||
from eth2spec.utils.bls import bls_sign
|
||||
from eth2spec.utils.merkle_minimal import calc_merkle_tree_from_leaves, get_merkle_root, get_merkle_proof
|
||||
from eth2spec.utils.ssz.ssz_impl import signing_root
|
||||
from eth2spec.utils.merkle_minimal import calc_merkle_tree_from_leaves, get_merkle_proof
|
||||
from eth2spec.utils.ssz.ssz_impl import signing_root, hash_tree_root
|
||||
from eth2spec.utils.ssz.ssz_typing import List
|
||||
|
||||
|
||||
def build_deposit_data(spec, state, pubkey, privkey, amount, withdrawal_credentials, signed=False):
|
||||
def build_deposit_data(spec, pubkey, privkey, amount, withdrawal_credentials, state=None, signed=False):
|
||||
deposit_data = spec.DepositData(
|
||||
pubkey=pubkey,
|
||||
withdrawal_credentials=withdrawal_credentials,
|
||||
amount=amount,
|
||||
)
|
||||
if signed:
|
||||
sign_deposit_data(spec, state, deposit_data, privkey)
|
||||
sign_deposit_data(spec, deposit_data, privkey, state)
|
||||
return deposit_data
|
||||
|
||||
|
||||
def sign_deposit_data(spec, state, deposit_data, privkey):
|
||||
signature = bls_sign(
|
||||
message_hash=signing_root(deposit_data),
|
||||
privkey=privkey,
|
||||
domain=spec.get_domain(
|
||||
def sign_deposit_data(spec, deposit_data, privkey, state=None):
|
||||
if state is None:
|
||||
# Genesis
|
||||
domain = spec.bls_domain(spec.DOMAIN_DEPOSIT)
|
||||
else:
|
||||
domain = spec.get_domain(
|
||||
state,
|
||||
spec.DOMAIN_DEPOSIT,
|
||||
)
|
||||
|
||||
signature = bls_sign(
|
||||
message_hash=signing_root(deposit_data),
|
||||
privkey=privkey,
|
||||
domain=domain,
|
||||
)
|
||||
deposit_data.signature = signature
|
||||
|
||||
|
||||
def build_deposit(spec,
|
||||
state,
|
||||
deposit_data_leaves,
|
||||
deposit_data_list,
|
||||
pubkey,
|
||||
privkey,
|
||||
amount,
|
||||
withdrawal_credentials,
|
||||
signed):
|
||||
deposit_data = build_deposit_data(spec, state, pubkey, privkey, amount, withdrawal_credentials, signed)
|
||||
deposit_data = build_deposit_data(spec, pubkey, privkey, amount, withdrawal_credentials, state=state, signed=signed)
|
||||
index = len(deposit_data_list)
|
||||
deposit_data_list.append(deposit_data)
|
||||
root = hash_tree_root(List[spec.DepositData, 2**spec.DEPOSIT_CONTRACT_TREE_DEPTH](*deposit_data_list))
|
||||
tree = calc_merkle_tree_from_leaves(tuple([d.hash_tree_root() for d in deposit_data_list]))
|
||||
proof = list(get_merkle_proof(tree, item_index=index)) + [(index + 1).to_bytes(32, 'little')]
|
||||
leaf = deposit_data.hash_tree_root()
|
||||
assert spec.verify_merkle_branch(leaf, proof, spec.DEPOSIT_CONTRACT_TREE_DEPTH + 1, index, root)
|
||||
deposit = spec.Deposit(proof=proof, data=deposit_data)
|
||||
|
||||
item = deposit_data.hash_tree_root()
|
||||
index = len(deposit_data_leaves)
|
||||
deposit_data_leaves.append(item)
|
||||
tree = calc_merkle_tree_from_leaves(tuple(deposit_data_leaves))
|
||||
root = get_merkle_root((tuple(deposit_data_leaves)))
|
||||
proof = list(get_merkle_proof(tree, item_index=index))
|
||||
assert spec.verify_merkle_branch(item, proof, spec.DEPOSIT_CONTRACT_TREE_DEPTH, index, root)
|
||||
return deposit, root, deposit_data_list
|
||||
|
||||
deposit = spec.Deposit(
|
||||
proof=list(proof),
|
||||
index=index,
|
||||
data=deposit_data,
|
||||
)
|
||||
|
||||
return deposit, root, deposit_data_leaves
|
||||
def prepare_genesis_deposits(spec, genesis_validator_count, amount, signed=False):
|
||||
deposit_data_list = []
|
||||
genesis_deposits = []
|
||||
for validator_index in range(genesis_validator_count):
|
||||
pubkey = pubkeys[validator_index]
|
||||
privkey = privkeys[validator_index]
|
||||
# insecurely use pubkey as withdrawal key if no credentials provided
|
||||
withdrawal_credentials = spec.int_to_bytes(spec.BLS_WITHDRAWAL_PREFIX, length=1) + spec.hash(pubkey)[1:]
|
||||
deposit, root, deposit_data_list = build_deposit(
|
||||
spec,
|
||||
None,
|
||||
deposit_data_list,
|
||||
pubkey,
|
||||
privkey,
|
||||
amount,
|
||||
withdrawal_credentials,
|
||||
signed,
|
||||
)
|
||||
genesis_deposits.append(deposit)
|
||||
|
||||
return genesis_deposits, root
|
||||
|
||||
|
||||
def prepare_state_and_deposit(spec, state, validator_index, amount, withdrawal_credentials=None, signed=False):
|
||||
"""
|
||||
Prepare the state for the deposit, and create a deposit for the given validator, depositing the given amount.
|
||||
"""
|
||||
pre_validator_count = len(state.validators)
|
||||
# fill previous deposits with zero-hash
|
||||
deposit_data_leaves = [spec.ZERO_HASH] * pre_validator_count
|
||||
deposit_data_list = []
|
||||
|
||||
pubkey = pubkeys[validator_index]
|
||||
privkey = privkeys[validator_index]
|
||||
@ -69,10 +91,10 @@ def prepare_state_and_deposit(spec, state, validator_index, amount, withdrawal_c
|
||||
if withdrawal_credentials is None:
|
||||
withdrawal_credentials = spec.int_to_bytes(spec.BLS_WITHDRAWAL_PREFIX, length=1) + spec.hash(pubkey)[1:]
|
||||
|
||||
deposit, root, deposit_data_leaves = build_deposit(
|
||||
deposit, root, deposit_data_list = build_deposit(
|
||||
spec,
|
||||
state,
|
||||
deposit_data_leaves,
|
||||
deposit_data_list,
|
||||
pubkey,
|
||||
privkey,
|
||||
amount,
|
||||
@ -80,6 +102,7 @@ def prepare_state_and_deposit(spec, state, validator_index, amount, withdrawal_c
|
||||
signed,
|
||||
)
|
||||
|
||||
state.eth1_deposit_index = 0
|
||||
state.eth1_data.deposit_root = root
|
||||
state.eth1_data.deposit_count = len(deposit_data_leaves)
|
||||
state.eth1_data.deposit_count = len(deposit_data_list)
|
||||
return deposit
|
||||
|
@ -147,7 +147,7 @@ def test_invalid_withdrawal_credentials_top_up(spec, state):
|
||||
@with_all_phases
|
||||
@spec_state_test
|
||||
def test_wrong_deposit_for_deposit_count(spec, state):
|
||||
deposit_data_leaves = [spec.ZERO_HASH] * len(state.validators)
|
||||
deposit_data_leaves = [spec.DepositData() for _ in range(len(state.validators))]
|
||||
|
||||
# build root for deposit_1
|
||||
index_1 = len(deposit_data_leaves)
|
||||
@ -197,6 +197,6 @@ def test_bad_merkle_proof(spec, state):
|
||||
# mess up merkle branch
|
||||
deposit.proof[5] = spec.ZERO_HASH
|
||||
|
||||
sign_deposit_data(spec, state, deposit.data, privkeys[validator_index])
|
||||
sign_deposit_data(spec, deposit.data, privkeys[validator_index], state=state)
|
||||
|
||||
yield from run_deposit_processing(spec, state, deposit, validator_index, valid=False)
|
||||
|
@ -1,4 +1,5 @@
|
||||
from .hash_function import hash
|
||||
from math import log2
|
||||
|
||||
|
||||
ZERO_BYTES32 = b'\x00' * 32
|
||||
@ -8,11 +9,10 @@ for layer in range(1, 100):
|
||||
zerohashes.append(hash(zerohashes[layer - 1] + zerohashes[layer - 1]))
|
||||
|
||||
|
||||
# Compute a Merkle root of a right-zerobyte-padded 2**32 sized tree
|
||||
def calc_merkle_tree_from_leaves(values):
|
||||
def calc_merkle_tree_from_leaves(values, layer_count=32):
|
||||
values = list(values)
|
||||
tree = [values[::]]
|
||||
for h in range(32):
|
||||
for h in range(layer_count):
|
||||
if len(values) % 2 == 1:
|
||||
values.append(zerohashes[h])
|
||||
values = [hash(values[i] + values[i + 1]) for i in range(0, len(values), 2)]
|
||||
@ -20,8 +20,11 @@ def calc_merkle_tree_from_leaves(values):
|
||||
return tree
|
||||
|
||||
|
||||
def get_merkle_root(values):
|
||||
return calc_merkle_tree_from_leaves(values)[-1][0]
|
||||
def get_merkle_root(values, pad_to=1):
|
||||
layer_count = int(log2(pad_to))
|
||||
if len(values) == 0:
|
||||
return zerohashes[layer_count]
|
||||
return calc_merkle_tree_from_leaves(values, layer_count)[-1][0]
|
||||
|
||||
|
||||
def get_merkle_proof(tree, item_index):
|
||||
@ -32,19 +35,7 @@ def get_merkle_proof(tree, item_index):
|
||||
return proof
|
||||
|
||||
|
||||
def next_power_of_two(v: int) -> int:
|
||||
"""
|
||||
Get the next power of 2. (for 64 bit range ints).
|
||||
0 is a special case, to have non-empty defaults.
|
||||
Examples:
|
||||
0 -> 1, 1 -> 1, 2 -> 2, 3 -> 4, 32 -> 32, 33 -> 64
|
||||
"""
|
||||
if v == 0:
|
||||
return 1
|
||||
return 1 << (v - 1).bit_length()
|
||||
|
||||
|
||||
def merkleize_chunks(chunks, pad_to: int = 1):
|
||||
def merkleize_chunks(chunks, pad_to: int=1):
|
||||
count = len(chunks)
|
||||
depth = max(count - 1, 0).bit_length()
|
||||
max_depth = max(depth, (pad_to - 1).bit_length())
|
||||
|
@ -1,5 +1,5 @@
|
||||
import pytest
|
||||
from .merkle_minimal import zerohashes, merkleize_chunks
|
||||
from .merkle_minimal import zerohashes, merkleize_chunks, get_merkle_root
|
||||
from .hash_function import hash
|
||||
|
||||
|
||||
@ -53,6 +53,7 @@ cases = [
|
||||
'depth,count,pow2,value',
|
||||
cases,
|
||||
)
|
||||
def test_merkleize_chunks(depth, count, pow2, value):
|
||||
def test_merkleize_chunks_and_get_merkle_root(depth, count, pow2, value):
|
||||
chunks = [e(i) for i in range(count)]
|
||||
assert merkleize_chunks(chunks, pad_to=pow2) == value
|
||||
assert get_merkle_root(chunks, pad_to=pow2) == value
|
||||
|
Loading…
x
Reference in New Issue
Block a user