Merge branch 'dev'
This commit is contained in:
commit
67fd7979ff
|
@ -5,8 +5,10 @@ PRESET_BASE: 'mainnet'
|
|||
|
||||
# Transition
|
||||
# ---------------------------------------------------------------
|
||||
# TBD, 2**256-1 is a placeholder
|
||||
TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129639935
|
||||
# TBD, 2**256-2**10 is a placeholder
|
||||
TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129638912
|
||||
# By default, don't use this param
|
||||
TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
|
||||
|
||||
# Genesis
|
||||
|
@ -29,7 +31,7 @@ GENESIS_DELAY: 604800
|
|||
|
||||
# Altair
|
||||
ALTAIR_FORK_VERSION: 0x01000000
|
||||
ALTAIR_FORK_EPOCH: 18446744073709551615
|
||||
ALTAIR_FORK_EPOCH: 74240 # Oct 27, 2021, 10:56:23am UTC
|
||||
# Merge
|
||||
MERGE_FORK_VERSION: 0x02000000
|
||||
MERGE_FORK_EPOCH: 18446744073709551615
|
||||
|
|
|
@ -5,8 +5,10 @@ PRESET_BASE: 'minimal'
|
|||
|
||||
# Transition
|
||||
# ---------------------------------------------------------------
|
||||
# TBD, 2**256-1 is a placeholder
|
||||
TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129639935
|
||||
# TBD, 2**256-2**10 is a placeholder
|
||||
TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129638912
|
||||
# By default, don't use this param
|
||||
TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
|
||||
|
||||
# Genesis
|
||||
|
|
12
setup.py
12
setup.py
|
@ -10,15 +10,18 @@ import textwrap
|
|||
from typing import Dict, NamedTuple, List, Sequence, Optional, TypeVar
|
||||
from abc import ABC, abstractmethod
|
||||
import ast
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# NOTE: have to programmatically include third-party dependencies in `setup.py`.
|
||||
def installPackage(package: str):
|
||||
subprocess.check_call([sys.executable, '-m', 'pip', 'install', package])
|
||||
|
||||
RUAMEL_YAML_VERSION = "ruamel.yaml==0.16.5"
|
||||
try:
|
||||
import ruamel.yaml
|
||||
except ImportError:
|
||||
import pip
|
||||
pip.main(["install", RUAMEL_YAML_VERSION])
|
||||
installPackage(RUAMEL_YAML_VERSION)
|
||||
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
|
@ -26,8 +29,7 @@ MARKO_VERSION = "marko==1.0.2"
|
|||
try:
|
||||
import marko
|
||||
except ImportError:
|
||||
import pip
|
||||
pip.main(["install", MARKO_VERSION])
|
||||
installPackage(MARKO_VERSION)
|
||||
|
||||
from marko.block import Heading, FencedCode, LinkRefDef, BlankLine
|
||||
from marko.inline import CodeSpan
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
# Altair -- Fork Logic
|
||||
|
||||
**Notice**: This document is a work-in-progress for researchers and implementers.
|
||||
|
||||
## Table of contents
|
||||
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
@ -26,13 +24,13 @@ Warning: this configuration is not definitive.
|
|||
| Name | Value |
|
||||
| - | - |
|
||||
| `ALTAIR_FORK_VERSION` | `Version('0x01000000')` |
|
||||
| `ALTAIR_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** |
|
||||
| `ALTAIR_FORK_EPOCH` | `Epoch(74240)` (Oct 27, 2021, 10:56:23am UTC) |
|
||||
|
||||
## Fork to Altair
|
||||
|
||||
### 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 epoch `ALTAIR_FORK_EPOCH`.
|
||||
The fork is triggered at epoch `ALTAIR_FORK_EPOCH`.
|
||||
|
||||
Note that for the pure Altair networks, we don't apply `upgrade_to_altair` since it starts with Altair version logic.
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ This patch adds transaction execution to the beacon chain as part of the Merge f
|
|||
| Name | Value |
|
||||
| - | - |
|
||||
| `TERMINAL_TOTAL_DIFFICULTY` | **TBD** |
|
||||
| `TERMINAL_BLOCK_HASH` | `Hash32('0x0000000000000000000000000000000000000000000000000000000000000000')` |
|
||||
|
||||
## Containers
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
- [The Merge -- Client Settings](#the-merge----client-settings)
|
||||
- [Override terminal total difficulty](#override-terminal-total-difficulty)
|
||||
- [Override terminal block hash](#override-terminal-block-hash)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
|
@ -19,3 +20,9 @@ To coordinate manual overrides to [`TERMINAL_TOTAL_DIFFICULTY`](./beacon-chain.m
|
|||
|
||||
Except under exceptional scenarios, this setting is expected to not be used. Sufficient warning to the user about this exceptional configurable setting should be provided.
|
||||
|
||||
### Override terminal block hash
|
||||
|
||||
To allow for transition coordination around a specific PoW block, clients must also provide `--terminal-block-hash-override` as a configurable setting.
|
||||
The value provided by this setting takes precedence over the pre-configured `TERMINAL_BLOCK_HASH` parameter.
|
||||
|
||||
Except under exceptional scenarios, this setting is expected to not be used. Sufficient warning to the user about this exceptional configurable setting should be provided.
|
||||
|
|
|
@ -55,8 +55,7 @@ def notify_forkchoice_updated(self: ExecutionEngine, head_block_hash: Hash32, fi
|
|||
### `PowBlock`
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class PowBlock(object):
|
||||
class PowBlock(Container):
|
||||
block_hash: Hash32
|
||||
parent_hash: Hash32
|
||||
total_difficulty: uint256
|
||||
|
@ -75,6 +74,9 @@ Used by fork-choice handler, `on_block`.
|
|||
|
||||
```python
|
||||
def is_valid_terminal_pow_block(block: PowBlock, parent: PowBlock) -> bool:
|
||||
if block.block_hash == TERMINAL_BLOCK_HASH:
|
||||
return True
|
||||
|
||||
is_total_difficulty_reached = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
||||
is_parent_total_difficulty_valid = parent.total_difficulty < TERMINAL_TOTAL_DIFFICULTY
|
||||
return is_total_difficulty_reached and is_parent_total_difficulty_valid
|
||||
|
@ -101,7 +103,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
|||
assert block.slot > finalized_slot
|
||||
# Check block is a descendant of the finalized block at the checkpoint finalized slot
|
||||
assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root
|
||||
|
||||
|
||||
# Check the block is valid and compute the post-state
|
||||
state = pre_state.copy()
|
||||
state_transition(state, signed_block, True)
|
||||
|
@ -127,7 +129,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
|||
# Update finalized checkpoint
|
||||
if state.finalized_checkpoint.epoch > store.finalized_checkpoint.epoch:
|
||||
store.finalized_checkpoint = state.finalized_checkpoint
|
||||
|
||||
|
||||
# Potentially update justified if different from store
|
||||
if store.justified_checkpoint != state.current_justified_checkpoint:
|
||||
# Update justified if new justified is later than store justified
|
||||
|
|
|
@ -97,29 +97,42 @@ To obtain an execution payload, a block proposer building a block on top of a `s
|
|||
* `pow_chain` is a list that abstractly represents all blocks in the PoW chain
|
||||
* `fee_recipient` is the value suggested to be used for the `coinbase` field of the execution payload
|
||||
|
||||
|
||||
```python
|
||||
def get_pow_block_at_total_difficulty(total_difficulty: uint256, pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]:
|
||||
def get_pow_block_at_terminal_total_difficulty(pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]:
|
||||
# `pow_chain` abstractly represents all blocks in the PoW chain
|
||||
for block in pow_chain:
|
||||
parent = get_pow_block(block.parent_hash)
|
||||
if block.total_difficulty >= total_difficulty and parent.total_difficulty < total_difficulty:
|
||||
block_reached_ttd = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
||||
parent_reached_ttd = parent.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
||||
if block_reached_ttd and not parent_reached_ttd:
|
||||
return block
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_terminal_pow_block(pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]:
|
||||
if TERMINAL_BLOCK_HASH != Hash32():
|
||||
# Terminal block hash override takes precedence over terminal total difficulty
|
||||
pow_block_overrides = [block for block in pow_chain if block.block_hash == TERMINAL_BLOCK_HASH]
|
||||
if not any(pow_block_overrides):
|
||||
return None
|
||||
return pow_block_overrides[0]
|
||||
|
||||
return get_pow_block_at_terminal_total_difficulty(pow_chain)
|
||||
|
||||
|
||||
def prepare_execution_payload(state: BeaconState,
|
||||
pow_chain: Sequence[PowBlock],
|
||||
fee_recipient: ExecutionAddress,
|
||||
execution_engine: ExecutionEngine) -> Optional[PayloadId]:
|
||||
if not is_merge_complete(state):
|
||||
terminal_pow_block = get_pow_block_at_total_difficulty(TERMINAL_TOTAL_DIFFICULTY, pow_chain)
|
||||
terminal_pow_block = get_terminal_pow_block(pow_chain)
|
||||
if terminal_pow_block is None:
|
||||
# Pre-merge, no prepare payload call is needed
|
||||
return None
|
||||
else:
|
||||
# Signify merge via producing on top of the last PoW block
|
||||
parent_hash = terminal_pow_block.block_hash
|
||||
# Signify merge via producing on top of the terminal PoW block
|
||||
parent_hash = terminal_pow_block.block_hash
|
||||
else:
|
||||
# Post-merge, normal payload
|
||||
parent_hash = state.latest_execution_payload_header.block_hash
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.1.0-beta.5
|
||||
1.1.0
|
||||
|
|
|
@ -8,8 +8,14 @@ from eth2spec.test.helpers.merkle import build_proof
|
|||
|
||||
@with_phases([ALTAIR])
|
||||
@spec_state_test
|
||||
def test_next_sync_committee_tree(spec, state):
|
||||
def test_next_sync_committee_merkle_proof(spec, state):
|
||||
yield "state", state
|
||||
next_sync_committee_branch = build_proof(state.get_backing(), spec.NEXT_SYNC_COMMITTEE_INDEX)
|
||||
yield "proof", {
|
||||
"leaf": "0x" + state.next_sync_committee.hash_tree_root().hex(),
|
||||
"leaf_index": spec.NEXT_SYNC_COMMITTEE_INDEX,
|
||||
"branch": ['0x' + root.hex() for root in next_sync_committee_branch]
|
||||
}
|
||||
assert spec.is_valid_merkle_branch(
|
||||
leaf=state.next_sync_committee.hash_tree_root(),
|
||||
branch=next_sync_committee_branch,
|
||||
|
@ -21,8 +27,15 @@ def test_next_sync_committee_tree(spec, state):
|
|||
|
||||
@with_phases([ALTAIR])
|
||||
@spec_state_test
|
||||
def test_finality_root_tree(spec, state):
|
||||
def test_finality_root_merkle_proof(spec, state):
|
||||
yield "state", state
|
||||
finality_branch = build_proof(state.get_backing(), spec.FINALIZED_ROOT_INDEX)
|
||||
yield "proof", {
|
||||
"leaf": "0x" + state.finalized_checkpoint.root.hex(),
|
||||
"leaf_index": spec.FINALIZED_ROOT_INDEX,
|
||||
"branch": ['0x' + root.hex() for root in finality_branch]
|
||||
}
|
||||
|
||||
assert spec.is_valid_merkle_branch(
|
||||
leaf=state.finalized_checkpoint.root,
|
||||
branch=finality_branch,
|
|
@ -1,2 +1,6 @@
|
|||
class SkippedTest(Exception):
|
||||
...
|
||||
|
||||
|
||||
class BlockNotFoundException(Exception):
|
||||
...
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
from random import Random
|
||||
from eth_utils import encode_hex
|
||||
from eth2spec.test.exceptions import BlockNotFoundException
|
||||
from eth2spec.utils.ssz.ssz_typing import uint256
|
||||
from eth2spec.test.helpers.attestations import (
|
||||
next_epoch_with_attestations,
|
||||
next_slots_with_attestations,
|
||||
|
@ -22,15 +25,22 @@ def add_block_to_store(spec, store, signed_block):
|
|||
spec.on_block(store, signed_block)
|
||||
|
||||
|
||||
def tick_and_add_block(spec, store, signed_block, test_steps, valid=True, allow_invalid_attestations=False):
|
||||
def tick_and_add_block(spec, store, signed_block, test_steps, valid=True, allow_invalid_attestations=False,
|
||||
merge_block=False, block_not_found=False):
|
||||
pre_state = store.block_states[signed_block.message.parent_root]
|
||||
block_time = pre_state.genesis_time + signed_block.message.slot * spec.config.SECONDS_PER_SLOT
|
||||
if merge_block:
|
||||
assert spec.is_merge_block(pre_state, signed_block.message.body)
|
||||
|
||||
if store.time < block_time:
|
||||
on_tick_and_append_step(spec, store, block_time, test_steps)
|
||||
|
||||
post_state = yield from add_block(
|
||||
spec, store, signed_block, test_steps, valid=valid, allow_invalid_attestations=allow_invalid_attestations)
|
||||
spec, store, signed_block, test_steps,
|
||||
valid=valid,
|
||||
allow_invalid_attestations=allow_invalid_attestations,
|
||||
block_not_found=block_not_found,
|
||||
)
|
||||
|
||||
return post_state
|
||||
|
||||
|
@ -118,7 +128,13 @@ def run_on_block(spec, store, signed_block, valid=True):
|
|||
assert store.blocks[signed_block.message.hash_tree_root()] == signed_block.message
|
||||
|
||||
|
||||
def add_block(spec, store, signed_block, test_steps, valid=True, allow_invalid_attestations=False):
|
||||
def add_block(spec,
|
||||
store,
|
||||
signed_block,
|
||||
test_steps,
|
||||
valid=True,
|
||||
allow_invalid_attestations=False,
|
||||
block_not_found=False):
|
||||
"""
|
||||
Run on_block and on_attestation
|
||||
"""
|
||||
|
@ -127,7 +143,9 @@ def add_block(spec, store, signed_block, test_steps, valid=True, allow_invalid_a
|
|||
if not valid:
|
||||
try:
|
||||
run_on_block(spec, store, signed_block, valid=True)
|
||||
except AssertionError:
|
||||
except (AssertionError, BlockNotFoundException) as e:
|
||||
if isinstance(e, BlockNotFoundException) and not block_not_found:
|
||||
assert False
|
||||
test_steps.append({
|
||||
'block': get_block_file_name(signed_block),
|
||||
'valid': False,
|
||||
|
@ -227,3 +245,21 @@ def apply_next_slots_with_attestations(spec,
|
|||
assert store.block_states[block_root].hash_tree_root() == post_state.hash_tree_root()
|
||||
|
||||
return post_state, store, last_signed_block
|
||||
|
||||
|
||||
def prepare_empty_pow_block(spec, rng=Random(3131)):
|
||||
return spec.PowBlock(
|
||||
block_hash=spec.Hash32(spec.hash(bytearray(rng.getrandbits(8) for _ in range(32)))),
|
||||
parent_hash=spec.Hash32(spec.hash(bytearray(rng.getrandbits(8) for _ in range(32)))),
|
||||
total_difficulty=uint256(0),
|
||||
difficulty=uint256(0)
|
||||
)
|
||||
|
||||
|
||||
def get_pow_block_file_name(pow_block):
|
||||
return f"pow_block_{encode_hex(pow_block.block_hash)}"
|
||||
|
||||
|
||||
def add_pow_block(spec, store, pow_block, test_steps):
|
||||
yield get_pow_block_file_name(pow_block), pow_block
|
||||
test_steps.append({'pow_block': get_pow_block_file_name(pow_block)})
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from eth2spec.utils.ssz.ssz_typing import uint64
|
||||
from eth2spec.test.helpers.execution_payload import (
|
||||
build_empty_execution_payload,
|
||||
get_execution_payload_header,
|
||||
|
@ -227,3 +228,157 @@ def test_bad_timestamp_regular_payload(spec, state):
|
|||
execution_payload.timestamp = execution_payload.timestamp + 1
|
||||
|
||||
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_gaslimit_zero_first_payload(spec, state):
|
||||
# pre-state
|
||||
state = build_state_with_incomplete_transition(spec, state)
|
||||
next_slot(spec, state)
|
||||
|
||||
# execution payload
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
execution_payload.gas_limit = uint64(0)
|
||||
|
||||
yield from run_execution_payload_processing(spec, state, execution_payload)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_gaslimit_max_first_payload(spec, state):
|
||||
# pre-state
|
||||
state = build_state_with_incomplete_transition(spec, state)
|
||||
next_slot(spec, state)
|
||||
|
||||
# execution payload
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
execution_payload.gas_limit = uint64(2**64 - 1)
|
||||
|
||||
yield from run_execution_payload_processing(spec, state, execution_payload)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_gaslimit_upper_plus_regular_payload(spec, state):
|
||||
# pre-state
|
||||
state = build_state_with_complete_transition(spec, state)
|
||||
next_slot(spec, state)
|
||||
|
||||
# execution payload
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
execution_payload.gas_limit = (
|
||||
execution_payload.gas_limit +
|
||||
execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR
|
||||
)
|
||||
|
||||
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_gaslimit_upper_regular_payload(spec, state):
|
||||
# pre-state
|
||||
state = build_state_with_complete_transition(spec, state)
|
||||
next_slot(spec, state)
|
||||
|
||||
# execution payload
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
execution_payload.gas_limit = (
|
||||
execution_payload.gas_limit +
|
||||
execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR - uint64(1)
|
||||
)
|
||||
|
||||
yield from run_execution_payload_processing(spec, state, execution_payload)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_gaslimit_lower_minus_regular_payload(spec, state):
|
||||
# pre-state
|
||||
state = build_state_with_complete_transition(spec, state)
|
||||
next_slot(spec, state)
|
||||
|
||||
# execution payload
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
execution_payload.gas_limit = (
|
||||
execution_payload.gas_limit -
|
||||
execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR
|
||||
)
|
||||
|
||||
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_gaslimit_lower_regular_payload(spec, state):
|
||||
# pre-state
|
||||
state = build_state_with_complete_transition(spec, state)
|
||||
next_slot(spec, state)
|
||||
|
||||
# execution payload
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
execution_payload.gas_limit = (
|
||||
execution_payload.gas_limit -
|
||||
execution_payload.gas_limit // spec.GAS_LIMIT_DENOMINATOR + uint64(1)
|
||||
)
|
||||
|
||||
yield from run_execution_payload_processing(spec, state, execution_payload)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_gaslimit_minimum_regular_payload(spec, state):
|
||||
# pre-state
|
||||
state = build_state_with_complete_transition(spec, state)
|
||||
next_slot(spec, state)
|
||||
state.latest_execution_payload_header.gas_limit = spec.MIN_GAS_LIMIT
|
||||
|
||||
# execution payload
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
execution_payload.gas_limit = execution_payload.gas_limit
|
||||
|
||||
yield from run_execution_payload_processing(spec, state, execution_payload)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_gaslimit_minimum_minus_regular_payload(spec, state):
|
||||
# pre-state
|
||||
state = build_state_with_complete_transition(spec, state)
|
||||
next_slot(spec, state)
|
||||
state.latest_execution_payload_header.gas_limit = spec.MIN_GAS_LIMIT
|
||||
|
||||
# execution payload
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
execution_payload.gas_limit = execution_payload.gas_limit - uint64(1)
|
||||
|
||||
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_gasused_gaslimit_regular_payload(spec, state):
|
||||
# pre-state
|
||||
state = build_state_with_complete_transition(spec, state)
|
||||
next_slot(spec, state)
|
||||
|
||||
# execution payload
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
execution_payload.gas_used = execution_payload.gas_limit
|
||||
|
||||
yield from run_execution_payload_processing(spec, state, execution_payload)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_gasused_gaslimit_plus_regular_payload(spec, state):
|
||||
# pre-state
|
||||
state = build_state_with_complete_transition(spec, state)
|
||||
next_slot(spec, state)
|
||||
|
||||
# execution payload
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
execution_payload.gas_used = execution_payload.gas_limit + uint64(1)
|
||||
|
||||
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
from eth2spec.utils.ssz.ssz_typing import uint256
|
||||
from eth2spec.test.exceptions import BlockNotFoundException
|
||||
from eth2spec.test.context import spec_state_test, with_phases, MERGE
|
||||
from eth2spec.test.helpers.block import (
|
||||
build_empty_block_for_next_slot,
|
||||
)
|
||||
from eth2spec.test.helpers.fork_choice import (
|
||||
get_genesis_forkchoice_store_and_block,
|
||||
on_tick_and_append_step,
|
||||
tick_and_add_block,
|
||||
)
|
||||
from eth2spec.test.helpers.state import (
|
||||
state_transition_and_sign_block,
|
||||
)
|
||||
from eth2spec.test.helpers.fork_choice import (
|
||||
prepare_empty_pow_block,
|
||||
add_pow_block,
|
||||
)
|
||||
from eth2spec.test.helpers.execution_payload import (
|
||||
build_state_with_incomplete_transition,
|
||||
)
|
||||
|
||||
|
||||
def with_pow_block_patch(spec, blocks, func):
|
||||
def get_pow_block(hash: spec.Bytes32) -> spec.PowBlock:
|
||||
for block in blocks:
|
||||
if block.block_hash == hash:
|
||||
return block
|
||||
raise BlockNotFoundException()
|
||||
get_pow_block_backup = spec.get_pow_block
|
||||
spec.get_pow_block = get_pow_block
|
||||
|
||||
class AtomicBoolean():
|
||||
value = False
|
||||
is_called = AtomicBoolean()
|
||||
|
||||
def wrap(flag: AtomicBoolean):
|
||||
yield from func()
|
||||
flag.value = True
|
||||
|
||||
try:
|
||||
yield from wrap(is_called)
|
||||
finally:
|
||||
spec.get_pow_block = get_pow_block_backup
|
||||
assert is_called.value
|
||||
|
||||
|
||||
@with_phases([MERGE])
|
||||
@spec_state_test
|
||||
def test_all_valid(spec, state):
|
||||
test_steps = []
|
||||
# Initialization
|
||||
state = build_state_with_incomplete_transition(spec, state)
|
||||
store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state)
|
||||
yield 'anchor_state', state
|
||||
yield 'anchor_block', anchor_block
|
||||
current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
|
||||
on_tick_and_append_step(spec, store, current_time, test_steps)
|
||||
assert store.time == current_time
|
||||
|
||||
pow_block_parent = prepare_empty_pow_block(spec)
|
||||
pow_block_parent.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
|
||||
pow_block = prepare_empty_pow_block(spec)
|
||||
pow_block.parent_hash = pow_block_parent.block_hash
|
||||
pow_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
|
||||
pow_blocks = [pow_block, pow_block_parent]
|
||||
for pb in pow_blocks:
|
||||
yield from add_pow_block(spec, store, pb, test_steps)
|
||||
|
||||
def run_func():
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.execution_payload.parent_hash = pow_block.block_hash
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
yield from tick_and_add_block(spec, store, signed_block, test_steps, merge_block=True)
|
||||
# valid
|
||||
assert spec.get_head(store) == signed_block.message.hash_tree_root()
|
||||
|
||||
yield from with_pow_block_patch(spec, pow_blocks, run_func)
|
||||
yield 'steps', test_steps
|
||||
|
||||
|
||||
@with_phases([MERGE])
|
||||
@spec_state_test
|
||||
def test_block_lookup_failed(spec, state):
|
||||
test_steps = []
|
||||
# Initialization
|
||||
state = build_state_with_incomplete_transition(spec, state)
|
||||
store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state)
|
||||
yield 'anchor_state', state
|
||||
yield 'anchor_block', anchor_block
|
||||
current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
|
||||
on_tick_and_append_step(spec, store, current_time, test_steps)
|
||||
assert store.time == current_time
|
||||
|
||||
pow_block = prepare_empty_pow_block(spec)
|
||||
pow_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
|
||||
pow_blocks = [pow_block]
|
||||
for pb in pow_blocks:
|
||||
yield from add_pow_block(spec, store, pb, test_steps)
|
||||
|
||||
def run_func():
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.execution_payload.parent_hash = pow_block.block_hash
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
yield from tick_and_add_block(spec, store, signed_block, test_steps, valid=False, merge_block=True,
|
||||
block_not_found=True)
|
||||
|
||||
yield from with_pow_block_patch(spec, pow_blocks, run_func)
|
||||
yield 'steps', test_steps
|
||||
|
||||
|
||||
@with_phases([MERGE])
|
||||
@spec_state_test
|
||||
def test_too_early_for_merge(spec, state):
|
||||
test_steps = []
|
||||
# Initialization
|
||||
state = build_state_with_incomplete_transition(spec, state)
|
||||
store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state)
|
||||
yield 'anchor_state', state
|
||||
yield 'anchor_block', anchor_block
|
||||
current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
|
||||
on_tick_and_append_step(spec, store, current_time, test_steps)
|
||||
assert store.time == current_time
|
||||
|
||||
pow_block_parent = prepare_empty_pow_block(spec)
|
||||
pow_block_parent.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(2)
|
||||
pow_block = prepare_empty_pow_block(spec)
|
||||
pow_block.parent_hash = pow_block_parent.block_hash
|
||||
pow_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
|
||||
pow_blocks = [pow_block, pow_block_parent]
|
||||
for pb in pow_blocks:
|
||||
yield from add_pow_block(spec, store, pb, test_steps)
|
||||
|
||||
def run_func():
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.execution_payload.parent_hash = pow_block.block_hash
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
yield from tick_and_add_block(spec, store, signed_block, test_steps, valid=False, merge_block=True)
|
||||
|
||||
yield from with_pow_block_patch(spec, pow_blocks, run_func)
|
||||
yield 'steps', test_steps
|
||||
|
||||
|
||||
@with_phases([MERGE])
|
||||
@spec_state_test
|
||||
def test_too_late_for_merge(spec, state):
|
||||
test_steps = []
|
||||
# Initialization
|
||||
state = build_state_with_incomplete_transition(spec, state)
|
||||
store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state)
|
||||
yield 'anchor_state', state
|
||||
yield 'anchor_block', anchor_block
|
||||
current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
|
||||
on_tick_and_append_step(spec, store, current_time, test_steps)
|
||||
assert store.time == current_time
|
||||
|
||||
pow_block_parent = prepare_empty_pow_block(spec)
|
||||
pow_block_parent.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
|
||||
pow_block = prepare_empty_pow_block(spec)
|
||||
pow_block.parent_hash = pow_block_parent.block_hash
|
||||
pow_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + uint256(1)
|
||||
pow_blocks = [pow_block, pow_block_parent]
|
||||
for pb in pow_blocks:
|
||||
yield from add_pow_block(spec, store, pb, test_steps)
|
||||
|
||||
def run_func():
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.execution_payload.parent_hash = pow_block.block_hash
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
yield from tick_and_add_block(spec, store, signed_block, test_steps, valid=False, merge_block=True)
|
||||
|
||||
yield from with_pow_block_patch(spec, pow_blocks, run_func)
|
||||
yield 'steps', test_steps
|
|
@ -0,0 +1,143 @@
|
|||
from eth2spec.test.exceptions import BlockNotFoundException
|
||||
from eth2spec.utils.ssz.ssz_typing import uint256
|
||||
from eth2spec.test.helpers.fork_choice import (
|
||||
prepare_empty_pow_block,
|
||||
)
|
||||
from eth2spec.test.context import spec_state_test, with_merge_and_later
|
||||
|
||||
|
||||
# Copy of conditional merge part of `on_block(store: Store, signed_block: SignedBeaconBlock)` handler
|
||||
def validate_transition_execution_payload(spec, execution_payload):
|
||||
pow_block = spec.get_pow_block(execution_payload.parent_hash)
|
||||
pow_parent = spec.get_pow_block(pow_block.parent_hash)
|
||||
assert spec.is_valid_terminal_pow_block(pow_block, pow_parent)
|
||||
|
||||
|
||||
def run_validate_transition_execution_payload(spec, block, parent_block, payload,
|
||||
valid=True, block_lookup_success=True):
|
||||
"""
|
||||
Run ``validate_transition_execution_payload``
|
||||
If ``valid == False``, run expecting ``AssertionError``
|
||||
If ``block_lookup_success == False``, run expecting ``BlockNotFoundException``
|
||||
"""
|
||||
|
||||
def get_pow_block(hash: spec.Bytes32) -> spec.PowBlock:
|
||||
if hash == block.block_hash:
|
||||
return block
|
||||
elif hash == parent_block.block_hash:
|
||||
return parent_block
|
||||
else:
|
||||
raise BlockNotFoundException()
|
||||
save_pow_block = spec.get_pow_block
|
||||
|
||||
# Guido authorized everyone to do this
|
||||
spec.get_pow_block = get_pow_block
|
||||
exception_caught = False
|
||||
block_not_found_exception_caught = False
|
||||
try:
|
||||
validate_transition_execution_payload(spec, payload)
|
||||
except BlockNotFoundException:
|
||||
block_not_found_exception_caught = True
|
||||
except AssertionError:
|
||||
exception_caught = True
|
||||
except Exception as e:
|
||||
spec.get_pow_block = save_pow_block
|
||||
raise e
|
||||
spec.get_pow_block = save_pow_block
|
||||
|
||||
if block_lookup_success:
|
||||
assert not block_not_found_exception_caught
|
||||
else:
|
||||
assert block_not_found_exception_caught
|
||||
if valid:
|
||||
assert not exception_caught
|
||||
else:
|
||||
assert exception_caught
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_valid_terminal_pow_block_success_valid(spec, state):
|
||||
parent_block = prepare_empty_pow_block(spec)
|
||||
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
|
||||
block = prepare_empty_pow_block(spec)
|
||||
block.parent_hash = parent_block.block_hash
|
||||
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
|
||||
|
||||
assert spec.is_valid_terminal_pow_block(block, parent_block)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_valid_terminal_pow_block_fail_before_terminal(spec, state):
|
||||
parent_block = prepare_empty_pow_block(spec)
|
||||
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(2)
|
||||
block = prepare_empty_pow_block(spec)
|
||||
block.parent_hash = parent_block.block_hash
|
||||
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
|
||||
|
||||
assert not spec.is_valid_terminal_pow_block(block, parent_block)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_valid_terminal_pow_block_fail_just_after_terminal(spec, state):
|
||||
parent_block = prepare_empty_pow_block(spec)
|
||||
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
|
||||
block = prepare_empty_pow_block(spec)
|
||||
block.parent_hash = parent_block.block_hash
|
||||
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + uint256(1)
|
||||
|
||||
assert not spec.is_valid_terminal_pow_block(block, parent_block)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_validate_transition_execution_payload_success(spec, state):
|
||||
parent_block = prepare_empty_pow_block(spec)
|
||||
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
|
||||
block = prepare_empty_pow_block(spec)
|
||||
block.parent_hash = parent_block.block_hash
|
||||
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
|
||||
payload = spec.ExecutionPayload()
|
||||
payload.parent_hash = block.block_hash
|
||||
run_validate_transition_execution_payload(spec, block, parent_block, payload)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_validate_transition_execution_payload_fail_block_lookup(spec, state):
|
||||
parent_block = prepare_empty_pow_block(spec)
|
||||
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
|
||||
block = prepare_empty_pow_block(spec)
|
||||
block.parent_hash = parent_block.block_hash
|
||||
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
|
||||
payload = spec.ExecutionPayload()
|
||||
run_validate_transition_execution_payload(spec, block, parent_block, payload,
|
||||
block_lookup_success=False)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_validate_transition_execution_payload_fail_parent_block_lookup(spec, state):
|
||||
parent_block = prepare_empty_pow_block(spec)
|
||||
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY - uint256(1)
|
||||
block = prepare_empty_pow_block(spec)
|
||||
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
|
||||
payload = spec.ExecutionPayload()
|
||||
payload.parent_hash = block.block_hash
|
||||
run_validate_transition_execution_payload(spec, block, parent_block, payload,
|
||||
block_lookup_success=False)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_validate_transition_execution_payload_fail_after_terminal(spec, state):
|
||||
parent_block = prepare_empty_pow_block(spec)
|
||||
parent_block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY
|
||||
block = prepare_empty_pow_block(spec)
|
||||
block.parent_hash = parent_block.block_hash
|
||||
block.total_difficulty = spec.config.TERMINAL_TOTAL_DIFFICULTY + 1
|
||||
payload = spec.ExecutionPayload()
|
||||
payload.parent_hash = block.block_hash
|
||||
run_validate_transition_execution_payload(spec, block, parent_block, payload, valid=False)
|
|
@ -0,0 +1,55 @@
|
|||
from eth2spec.test.helpers.execution_payload import (
|
||||
build_empty_execution_payload,
|
||||
build_state_with_incomplete_transition,
|
||||
build_state_with_complete_transition,
|
||||
)
|
||||
from eth2spec.test.context import (
|
||||
spec_state_test,
|
||||
with_merge_and_later
|
||||
)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_fail_merge_complete(spec, state):
|
||||
state = build_state_with_incomplete_transition(spec, state)
|
||||
assert not spec.is_merge_complete(state)
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_success_merge_complete(spec, state):
|
||||
state = build_state_with_complete_transition(spec, state)
|
||||
assert spec.is_merge_complete(state)
|
||||
|
||||
|
||||
# with_complete_transition', 'with_execution_payload', 'is_merge_block', 'is_execution_enabled'
|
||||
expected_results = [
|
||||
(True, True, False, True),
|
||||
(True, False, False, True),
|
||||
(False, True, True, True),
|
||||
(False, False, False, False)
|
||||
]
|
||||
|
||||
|
||||
@with_merge_and_later
|
||||
@spec_state_test
|
||||
def test_is_merge_block_and_is_execution_enabled(spec, state):
|
||||
for result in expected_results:
|
||||
(
|
||||
with_complete_transition,
|
||||
with_execution_payload,
|
||||
is_merge_block,
|
||||
is_execution_enabled
|
||||
) = result
|
||||
if with_complete_transition:
|
||||
state = build_state_with_complete_transition(spec, state)
|
||||
else:
|
||||
state = build_state_with_incomplete_transition(spec, state)
|
||||
|
||||
body = spec.BeaconBlockBody()
|
||||
if with_execution_payload:
|
||||
body.execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
assert spec.is_merge_block(state, body) == is_merge_block
|
||||
assert spec.is_execution_enabled(state, body) == is_execution_enabled
|
|
@ -69,6 +69,18 @@ The file is located in the same folder (see below).
|
|||
|
||||
After this step, the `store` object may have been updated.
|
||||
|
||||
#### `on_merge_block` execution
|
||||
|
||||
Adds `PowBlock` data which is required for executing `on_block(store, block)`.
|
||||
```yaml
|
||||
{
|
||||
pow_block: string -- the name of the `pow_block_<32-byte-root>.ssz_snappy` file.
|
||||
To be used in `get_pow_block` lookup
|
||||
}
|
||||
```
|
||||
The file is located in the same folder (see below).
|
||||
PowBlocks should be used as return values for `get_pow_block(hash: Hash32) -> PowBlock` function if hashes match.
|
||||
|
||||
#### Checks step
|
||||
|
||||
The checks to verify the current status of `store`.
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
# Merkle tests
|
||||
|
||||
This series of tests provides reference test vectors for validating correct
|
||||
generation and verification of merkle proofs based on static data.
|
||||
|
||||
Handlers:
|
||||
- `single_proof`: see [Single leaf proof test format](./single_proof.md)
|
||||
- Different types of merkle proofs may be supported in the future.
|
|
@ -0,0 +1,28 @@
|
|||
# Single leaf merkle proof tests
|
||||
|
||||
This series of tests provides reference test vectors for validating correct
|
||||
generation and verification of merkle proofs based on static data.
|
||||
|
||||
## Test case format
|
||||
|
||||
### `state.ssz_snappy`
|
||||
|
||||
An SSZ-snappy encoded `BeaconState` object from which other data is generated.
|
||||
|
||||
### `proof.yaml`
|
||||
|
||||
A proof of the leaf value (a merkle root) at generalized-index `leaf_index` in the given `state`.
|
||||
|
||||
```yaml
|
||||
leaf: Bytes32 # string, hex encoded, with 0x prefix
|
||||
leaf_index: int # integer, decimal
|
||||
branch: list of Bytes32 # list, each element is a string, hex encoded, with 0x prefix
|
||||
```
|
||||
|
||||
## Condition
|
||||
|
||||
A test-runner can implement the following assertions:
|
||||
- Check that `is_valid_merkle_branch` confirms `leaf` at `leaf_index` to verify
|
||||
against `has_tree_root(state)` and `proof`.
|
||||
- If the implementation supports generating merkle proofs, check that the
|
||||
self-generated proof matches the `proof` provided with the test.
|
|
@ -9,8 +9,14 @@ if __name__ == "__main__":
|
|||
]}
|
||||
# No additional Altair specific finality tests, yet.
|
||||
altair_mods = phase_0_mods
|
||||
# No specific Merge tests yet.
|
||||
merge_mods = altair_mods
|
||||
# For merge `on_merge_block` test kind added with `pow_block_N.ssz` files with several
|
||||
# PowBlock's which should be resolved by `get_pow_block(hash: Hash32) -> PowBlock` function
|
||||
merge_mods = {
|
||||
**{key: 'eth2spec.test.merge.fork_choice.test_' + key for key in [
|
||||
'on_merge_block',
|
||||
]},
|
||||
**altair_mods,
|
||||
}
|
||||
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
|
||||
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -8,9 +8,12 @@ if __name__ == "__main__":
|
|||
'validity',
|
||||
]}
|
||||
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
|
||||
all_mods = {
|
||||
PHASE0: phase_0_mods,
|
||||
ALTAIR: altair_mods,
|
||||
MERGE: merge_mods,
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="genesis", all_mods=all_mods)
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
# Merkle
|
||||
|
||||
The purpose of this test-generator is to provide test-vectors for validating the
|
||||
correct merkleization of objects and corresponding merkle proofs.
|
||||
|
||||
Test-format documentation can be found [here](../../formats/merkle/README.md).
|
|
@ -0,0 +1,14 @@
|
|||
from eth2spec.test.helpers.constants import ALTAIR
|
||||
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
altair_mods = {key: 'eth2spec.test.altair.merkle.test_' + key for key in [
|
||||
'single_proof',
|
||||
]}
|
||||
|
||||
all_mods = {
|
||||
ALTAIR: altair_mods
|
||||
}
|
||||
|
||||
run_state_test_generators(runner_name="merkle", all_mods=all_mods)
|
|
@ -0,0 +1,2 @@
|
|||
pytest>=4.4
|
||||
../../../[generator]
|
Loading…
Reference in New Issue