Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initialize_beacon_state_from_eth1 for pre-transition merge #2640

Merged
merged 4 commits into from
Oct 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 12 additions & 19 deletions specs/merge/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
- [Constants](#constants)
- [Execution](#execution)
- [Configuration](#configuration)
- [Genesis testing settings](#genesis-testing-settings)
- [Transition settings](#transition-settings)
- [Containers](#containers)
- [Extended containers](#extended-containers)
Expand Down Expand Up @@ -71,15 +70,6 @@ This patch adds transaction execution to the beacon chain as part of the Merge f

## Configuration

### Genesis testing settings

*Note*: These configuration settings do not apply to the mainnet and are utilized only by pure Merge testing.

| Name | Value |
| - | - |
| `GENESIS_GAS_LIMIT` | `uint64(30000000)` (= 30,000,000) |
| `GENESIS_BASE_FEE_PER_GAS` | `Bytes32('0x00ca9a3b00000000000000000000000000000000000000000000000000000000')` (= 1,000,000,000) |

### Transition settings

| Name | Value |
Expand Down Expand Up @@ -354,13 +344,19 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe
## Testing

*Note*: The function `initialize_beacon_state_from_eth1` is modified for pure Merge testing only.

*Note*: The function `initialize_beacon_state_from_eth1` is modified: (1) using `MERGE_FORK_VERSION` as the current fork version, (2) utilizing the Merge `BeaconBlockBody` when constructing the initial `latest_block_header`, and (3) initialize `latest_execution_payload_header`.
Modifications include:
1. Use `MERGE_FORK_VERSION` as the current fork version.
2. Utilize the Merge `BeaconBlockBody` when constructing the initial `latest_block_header`.
3. Initialize `latest_execution_payload_header`.
If `execution_payload_header == ExecutionPayloadHeader()`, then the Merge has not yet occurred.
Else, the Merge starts from genesis and the transition is incomplete.

```python
def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32,
eth1_timestamp: uint64,
deposits: Sequence[Deposit]) -> BeaconState:
deposits: Sequence[Deposit],
execution_payload_header: ExecutionPayloadHeader=ExecutionPayloadHeader()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we still need eth1_block_hash & eth1_timestamp? They should be both in the passed execution_payload_header

) -> BeaconState:
fork = Fork(
previous_version=MERGE_FORK_VERSION, # [Modified in Merge] for testing only
current_version=MERGE_FORK_VERSION, # [Modified in Merge]
Expand Down Expand Up @@ -397,12 +393,9 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32,
state.current_sync_committee = get_next_sync_committee(state)
state.next_sync_committee = get_next_sync_committee(state)

# [New in Merge] Initialize the execution payload header (with block number set to 0)
state.latest_execution_payload_header.block_hash = eth1_block_hash
state.latest_execution_payload_header.timestamp = eth1_timestamp
state.latest_execution_payload_header.random = eth1_block_hash
state.latest_execution_payload_header.gas_limit = GENESIS_GAS_LIMIT
state.latest_execution_payload_header.base_fee_per_gas = GENESIS_BASE_FEE_PER_GAS
hwwhww marked this conversation as resolved.
Show resolved Hide resolved
# [New in Merge] Initialize the execution payload header
# If empty, will initialize a chain that has not yet gone through the Merge transition
state.latest_execution_payload_header = execution_payload_header

return state
```
27 changes: 23 additions & 4 deletions tests/core/pyspec/eth2spec/test/helpers/genesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,25 @@ def build_mock_validator(spec, i: int, balance: int):
)


def get_sample_genesis_execution_payload_header(spec,
eth1_block_hash=None):
if eth1_block_hash is None:
eth1_block_hash = b'\x55' * 32
return spec.ExecutionPayloadHeader(
parent_hash=b'\x30' * 32,
coinbase=b'\x42' * 20,
state_root=b'\x20' * 32,
receipt_root=b'\x20' * 32,
logs_bloom=b'\x35' * spec.BYTES_PER_LOGS_BLOOM,
random=eth1_block_hash,
block_number=0,
gas_limit=30000000,
base_fee_per_gas=spec.Bytes32('0x00ca9a3b00000000000000000000000000000000000000000000000000000000'),
block_hash=eth1_block_hash,
transactions_root=spec.Root(b'\x56' * 32),
)


def create_genesis_state(spec, validator_balances, activation_threshold):
deposit_root = b'\x42' * 32

Expand Down Expand Up @@ -76,9 +95,9 @@ def create_genesis_state(spec, validator_balances, activation_threshold):

if spec.fork not in FORKS_BEFORE_MERGE:
# Initialize the execution payload header (with block number and genesis time set to 0)
state.latest_execution_payload_header.block_hash = eth1_block_hash
state.latest_execution_payload_header.random = eth1_block_hash
state.latest_execution_payload_header.gas_limit = spec.GENESIS_GAS_LIMIT
state.latest_execution_payload_header.base_fee_per_gas = spec.GENESIS_BASE_FEE_PER_GAS
state.latest_execution_payload_header = get_sample_genesis_execution_payload_header(
spec,
eth1_block_hash=eth1_block_hash,
)

return state
Empty file.
122 changes: 122 additions & 0 deletions tests/core/pyspec/eth2spec/test/merge/genesis/test_initialization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
from eth2spec.test.context import (
MERGE,
single_phase,
spec_test,
with_presets,
with_phases,
with_merge_and_later,
)
from eth2spec.test.helpers.constants import MINIMAL
from eth2spec.test.helpers.deposits import (
prepare_full_genesis_deposits,
)
from eth2spec.test.helpers.genesis import (
get_sample_genesis_execution_payload_header,
)


def eth1_init_data(eth1_block_hash, eth1_timestamp):
yield 'eth1', {
'eth1_block_hash': '0x' + eth1_block_hash.hex(),
'eth1_timestamp': int(eth1_timestamp),
}


@with_phases([MERGE])
Copy link
Contributor

@hwwhww hwwhww Oct 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it to @with_phases([MERGE]) because I think for the subsequent forks, the execution_payload_header field would be required?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally think the transition logic might just stay in there, but we can debate it further down the line

@spec_test
@single_phase
@with_presets([MINIMAL], reason="too slow")
def test_initialize_pre_transition_no_param(spec):
deposit_count = spec.config.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT
deposits, deposit_root, _ = prepare_full_genesis_deposits(
spec,
spec.MAX_EFFECTIVE_BALANCE,
deposit_count,
signed=True,
)

eth1_block_hash = b'\x12' * 32
eth1_timestamp = spec.config.MIN_GENESIS_TIME

yield from eth1_init_data(eth1_block_hash, eth1_timestamp)
yield 'deposits', deposits

# initialize beacon_state *without* an execution_payload_header
yield 'execution_payload_header', 'meta', False
state = spec.initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits)

assert not spec.is_merge_complete(state)

yield 'state', state


@with_merge_and_later
@spec_test
@single_phase
@with_presets([MINIMAL], reason="too slow")
def test_initialize_pre_transition_empty_payload(spec):
deposit_count = spec.config.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT
deposits, deposit_root, _ = prepare_full_genesis_deposits(
spec,
spec.MAX_EFFECTIVE_BALANCE,
deposit_count,
signed=True,
)

eth1_block_hash = b'\x12' * 32
eth1_timestamp = spec.config.MIN_GENESIS_TIME

yield from eth1_init_data(eth1_block_hash, eth1_timestamp)
yield 'deposits', deposits

# initialize beacon_state *with* an *empty* execution_payload_header
yield 'execution_payload_header', 'meta', True
execution_payload_header = spec.ExecutionPayloadHeader()
state = spec.initialize_beacon_state_from_eth1(
eth1_block_hash,
eth1_timestamp,
deposits,
execution_payload_header=execution_payload_header,
)

assert not spec.is_merge_complete(state)

yield 'execution_payload_header', execution_payload_header

yield 'state', state


@with_merge_and_later
@spec_test
@single_phase
@with_presets([MINIMAL], reason="too slow")
def test_initialize_post_transition(spec):
deposit_count = spec.config.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT
deposits, deposit_root, _ = prepare_full_genesis_deposits(
spec,
spec.MAX_EFFECTIVE_BALANCE,
deposit_count,
signed=True,
)

eth1_block_hash = b'\x12' * 32
eth1_timestamp = spec.config.MIN_GENESIS_TIME

yield from eth1_init_data(eth1_block_hash, eth1_timestamp)
yield 'deposits', deposits

# initialize beacon_state *with* an execution_payload_header
yield 'execution_payload_header', 'meta', True
genesis_execution_payload_header = get_sample_genesis_execution_payload_header(spec)
state = spec.initialize_beacon_state_from_eth1(
eth1_block_hash,
eth1_timestamp,
deposits,
execution_payload_header=genesis_execution_payload_header,
)

yield 'execution_payload_header', genesis_execution_payload_header

assert spec.is_merge_complete(state)

yield 'state', state
12 changes: 9 additions & 3 deletions tests/formats/genesis/initialization.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,26 @@ eth1_timestamp: int -- An integer. The timestamp of the block, in seconds.
A yaml file to help read the deposit count:

```yaml
description: string -- Optional. Description of test case, purely for debugging purposes.
deposits_count: int -- Amount of deposits.
description: string -- Optional. Description of test case, purely for debugging purposes.
deposits_count: int -- Amount of deposits.
execution_payload_header: bool -- `execution_payload_header` field is filled or not. If `true`, `execution_payload_header.ssz_snappy` file exists.
```

### `deposits_<index>.ssz_snappy`

A series of files, with `<index>` in range `[0, deposits_count)`. Deposits need to be processed in order.
Each file is a SSZ-snappy encoded `Deposit` object.

### `execution_payload_header.ssz_snappy`

*Note*: Param added only for the Merge and subsequent forks.

The execution payload header that state is initialized with. An SSZ-snappy encoded `BeaconState` object.

### `state.ssz_snappy`

The expected genesis state. An SSZ-snappy encoded `BeaconState` object.


## Processing

To process this test, build a genesis state with the provided `eth1_block_hash`, `eth1_timestamp` and `deposits`:
Expand Down
7 changes: 6 additions & 1 deletion tests/generators/genesis/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
]}
altair_mods = phase_0_mods
# we have new unconditional lines in `initialize_beacon_state_from_eth1` and we want to test it
merge_mods = altair_mods
merge_mods = {
**{key: 'eth2spec.test.merge.genesis.test_' + key for key in [
'initialization',
]},
**altair_mods,
}
all_mods = {
PHASE0: phase_0_mods,
ALTAIR: altair_mods,
Expand Down