Merge branch 'dev' into get_pow_block-not-found
This commit is contained in:
commit
5b9652bf93
|
@ -90,7 +90,7 @@ jobs:
|
|||
name: Install pyspec requirements
|
||||
command: make install_test
|
||||
- save_pyspec_cached_venv
|
||||
test:
|
||||
test-phase0:
|
||||
docker:
|
||||
- image: circleci/python:3.8
|
||||
working_directory: ~/specs-repo
|
||||
|
@ -100,7 +100,33 @@ jobs:
|
|||
- restore_pyspec_cached_venv
|
||||
- run:
|
||||
name: Run py-tests
|
||||
command: make citest
|
||||
command: make citest fork=phase0
|
||||
- store_test_results:
|
||||
path: tests/core/pyspec/test-reports
|
||||
test-altair:
|
||||
docker:
|
||||
- image: circleci/python:3.8
|
||||
working_directory: ~/specs-repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
|
||||
- restore_pyspec_cached_venv
|
||||
- run:
|
||||
name: Run py-tests
|
||||
command: make citest fork=altair
|
||||
- store_test_results:
|
||||
path: tests/core/pyspec/test-reports
|
||||
test-merge:
|
||||
docker:
|
||||
- image: circleci/python:3.8
|
||||
working_directory: ~/specs-repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
key: v3-specs-repo-{{ .Branch }}-{{ .Revision }}
|
||||
- restore_pyspec_cached_venv
|
||||
- run:
|
||||
name: Run py-tests
|
||||
command: make citest fork=merge
|
||||
- store_test_results:
|
||||
path: tests/core/pyspec/test-reports
|
||||
table_of_contents:
|
||||
|
@ -208,14 +234,20 @@ workflows:
|
|||
- install_pyspec_test:
|
||||
requires:
|
||||
- checkout_specs
|
||||
- test:
|
||||
- test-phase0:
|
||||
requires:
|
||||
- install_pyspec_test
|
||||
- test-altair:
|
||||
requires:
|
||||
- install_pyspec_test
|
||||
- test-merge:
|
||||
requires:
|
||||
- install_pyspec_test
|
||||
- table_of_contents
|
||||
- codespell
|
||||
- lint:
|
||||
requires:
|
||||
- test
|
||||
- install_pyspec_test
|
||||
# NOTE: Since phase 0 has been launched, we disabled the deposit contract tests.
|
||||
# - install_deposit_contract_web3_tester:
|
||||
# requires:
|
||||
|
|
9
Makefile
9
Makefile
|
@ -105,8 +105,15 @@ find_test: pyspec
|
|||
python3 -m pytest -k=$(K) --disable-bls --cov=eth2spec.phase0.minimal --cov=eth2spec.altair.minimal --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec
|
||||
|
||||
citest: pyspec
|
||||
mkdir -p tests/core/pyspec/test-reports/eth2spec; . venv/bin/activate; cd $(PY_SPEC_DIR); \
|
||||
mkdir -p tests/core/pyspec/test-reports/eth2spec;
|
||||
ifdef fork
|
||||
. venv/bin/activate; cd $(PY_SPEC_DIR); \
|
||||
python3 -m pytest -n 4 --bls-type=milagro --fork=$(fork) --junitxml=eth2spec/test_results.xml eth2spec
|
||||
else
|
||||
. venv/bin/activate; cd $(PY_SPEC_DIR); \
|
||||
python3 -m pytest -n 4 --bls-type=milagro --junitxml=eth2spec/test_results.xml eth2spec
|
||||
endif
|
||||
|
||||
|
||||
open_cov:
|
||||
((open "$(COV_INDEX_FILE)" || xdg-open "$(COV_INDEX_FILE)") &> /dev/null) &
|
||||
|
|
|
@ -7,8 +7,10 @@ PRESET_BASE: 'mainnet'
|
|||
# ---------------------------------------------------------------
|
||||
# TBD, 2**256-2**10 is a placeholder
|
||||
TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129638912
|
||||
# By default, don't use this param
|
||||
# By default, don't use these params
|
||||
TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: 18446744073709551615
|
||||
|
||||
|
||||
|
||||
# Genesis
|
||||
|
|
|
@ -7,8 +7,10 @@ PRESET_BASE: 'minimal'
|
|||
# ---------------------------------------------------------------
|
||||
# TBD, 2**256-2**10 is a placeholder
|
||||
TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129638912
|
||||
# By default, don't use this param
|
||||
# By default, don't use these params
|
||||
TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: 18446744073709551615
|
||||
|
||||
|
||||
|
||||
# Genesis
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
# Mainnet preset - The Merge
|
||||
|
||||
# No presets here.
|
||||
# Execution
|
||||
# ---------------------------------------------------------------
|
||||
# 2**20 (= 1,048,576)
|
||||
MAX_BYTES_PER_TRANSACTION: 1048576
|
||||
# 2**14 (= 16,384)
|
||||
MAX_TRANSACTIONS_PER_PAYLOAD: 16384
|
||||
# 2**8 (= 256)
|
||||
BYTES_PER_LOGS_BLOOM: 256
|
||||
# 2**10 (= 1,024)
|
||||
GAS_LIMIT_DENOMINATOR: 1024
|
||||
# 5,000
|
||||
MIN_GAS_LIMIT: 5000
|
||||
# 2**5 (= 32)
|
||||
MAX_EXTRA_DATA_BYTES: 32
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
# Minimal preset - The Merge
|
||||
|
||||
# No presets here.
|
||||
# Execution
|
||||
# ---------------------------------------------------------------
|
||||
# 2**20 (= 1,048,576)
|
||||
MAX_BYTES_PER_TRANSACTION: 1048576
|
||||
# 2**14 (= 16,384)
|
||||
MAX_TRANSACTIONS_PER_PAYLOAD: 16384
|
||||
# 2**8 (= 256)
|
||||
BYTES_PER_LOGS_BLOOM: 256
|
||||
# 2**10 (= 1,024)
|
||||
GAS_LIMIT_DENOMINATOR: 1024
|
||||
# 5,000
|
||||
MIN_GAS_LIMIT: 5000
|
||||
# 2**5 (= 32)
|
||||
MAX_EXTRA_DATA_BYTES: 32
|
||||
|
|
22
setup.py
22
setup.py
|
@ -497,7 +497,7 @@ class MergeSpecBuilder(AltairSpecBuilder):
|
|||
return super().imports(preset_name) + f'''
|
||||
from typing import Protocol
|
||||
from eth2spec.altair import {preset_name} as altair
|
||||
from eth2spec.utils.ssz.ssz_typing import Bytes20, ByteList, ByteVector, uint256, Union
|
||||
from eth2spec.utils.ssz.ssz_typing import Bytes8, Bytes20, ByteList, ByteVector, uint256
|
||||
'''
|
||||
|
||||
@classmethod
|
||||
|
@ -527,16 +527,12 @@ class NoopExecutionEngine(ExecutionEngine):
|
|||
def execute_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool:
|
||||
return True
|
||||
|
||||
def notify_forkchoice_updated(self: ExecutionEngine, head_block_hash: Hash32, finalized_block_hash: Hash32) -> None:
|
||||
def notify_forkchoice_updated(self: ExecutionEngine,
|
||||
head_block_hash: Hash32,
|
||||
finalized_block_hash: Hash32,
|
||||
payload_attributes: Optional[PayloadAttributes]) -> None:
|
||||
pass
|
||||
|
||||
def prepare_payload(self: ExecutionEngine,
|
||||
parent_hash: Hash32,
|
||||
timestamp: uint64,
|
||||
random: Bytes32,
|
||||
feeRecipient: ExecutionAddress) -> PayloadId:
|
||||
raise NotImplementedError("no default block production")
|
||||
|
||||
def get_payload(self: ExecutionEngine, payload_id: PayloadId) -> ExecutionPayload:
|
||||
raise NotImplementedError("no default block production")
|
||||
|
||||
|
@ -547,7 +543,7 @@ EXECUTION_ENGINE = NoopExecutionEngine()"""
|
|||
@classmethod
|
||||
def hardcoded_custom_type_dep_constants(cls) -> str:
|
||||
constants = {
|
||||
'MAX_BYTES_PER_OPAQUE_TRANSACTION': 'uint64(2**20)',
|
||||
'MAX_BYTES_PER_TRANSACTION': 'uint64(2**20)',
|
||||
}
|
||||
return {**super().hardcoded_custom_type_dep_constants(), **constants}
|
||||
|
||||
|
@ -603,7 +599,7 @@ def objects_to_spec(preset_name: str,
|
|||
|
||||
# Access global dict of config vars for runtime configurables
|
||||
for name in spec_object.config_vars.keys():
|
||||
functions_spec = functions_spec.replace(name, 'config.' + name)
|
||||
functions_spec = re.sub(r"\b%s\b" % name, 'config.' + name, functions_spec)
|
||||
|
||||
def format_config_var(name: str, vardef: VariableDefinition) -> str:
|
||||
if vardef.type_name is None:
|
||||
|
@ -683,7 +679,7 @@ def combine_dicts(old_dict: Dict[str, T], new_dict: Dict[str, T]) -> Dict[str, T
|
|||
|
||||
ignored_dependencies = [
|
||||
'bit', 'boolean', 'Vector', 'List', 'Container', 'BLSPubkey', 'BLSSignature',
|
||||
'Bytes1', 'Bytes4', 'Bytes20', 'Bytes32', 'Bytes48', 'Bytes96', 'Bitlist', 'Bitvector',
|
||||
'Bytes1', 'Bytes4', 'Bytes8', 'Bytes20', 'Bytes32', 'Bytes48', 'Bytes96', 'Bitlist', 'Bitvector',
|
||||
'uint8', 'uint16', 'uint32', 'uint64', 'uint128', 'uint256',
|
||||
'bytes', 'byte', 'ByteList', 'ByteVector',
|
||||
'Dict', 'dict', 'field', 'ceillog2', 'floorlog2', 'Set',
|
||||
|
@ -1021,7 +1017,7 @@ setup(
|
|||
"py_ecc==5.2.0",
|
||||
"milagro_bls_binding==1.6.3",
|
||||
"dataclasses==0.6",
|
||||
"remerkleable==0.1.22",
|
||||
"remerkleable==0.1.24",
|
||||
RUAMEL_YAML_VERSION,
|
||||
"lru-dict==1.1.6",
|
||||
MARKO_VERSION,
|
||||
|
|
|
@ -50,8 +50,7 @@ This patch adds transaction execution to the beacon chain as part of the Merge f
|
|||
|
||||
| Name | SSZ equivalent | Description |
|
||||
| - | - | - |
|
||||
| `OpaqueTransaction` | `ByteList[MAX_BYTES_PER_OPAQUE_TRANSACTION]` | a [typed transaction envelope](https://eips.ethereum.org/EIPS/eip-2718#opaque-byte-array-rather-than-an-rlp-array) structured as `TransactionType \|\| TransactionPayload` |
|
||||
| `Transaction` | `Union[OpaqueTransaction]` | a transaction |
|
||||
| `Transaction` | `ByteList[MAX_BYTES_PER_TRANSACTION]` | either a [typed transaction envelope](https://eips.ethereum.org/EIPS/eip-2718#opaque-byte-array-rather-than-an-rlp-array) or a legacy transaction|
|
||||
| `ExecutionAddress` | `Bytes20` | Address of account on the execution layer |
|
||||
|
||||
## Constants
|
||||
|
@ -60,7 +59,7 @@ This patch adds transaction execution to the beacon chain as part of the Merge f
|
|||
|
||||
| Name | Value |
|
||||
| - | - |
|
||||
| `MAX_BYTES_PER_OPAQUE_TRANSACTION` | `uint64(2**20)` (= 1,048,576) |
|
||||
| `MAX_BYTES_PER_TRANSACTION` | `uint64(2**20)` (= 1,048,576) |
|
||||
| `MAX_TRANSACTIONS_PER_PAYLOAD` | `uint64(2**14)` (= 16,384) |
|
||||
| `BYTES_PER_LOGS_BLOOM` | `uint64(2**8)` (= 256) |
|
||||
| `GAS_LIMIT_DENOMINATOR` | `uint64(2**10)` (= 1,024) |
|
||||
|
@ -74,7 +73,8 @@ 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')` |
|
||||
| `TERMINAL_BLOCK_HASH` | `Hash32()` |
|
||||
| `TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH` | `FAR_FUTURE_EPOCH` |
|
||||
|
||||
## Containers
|
||||
|
||||
|
|
|
@ -18,11 +18,12 @@ This document specifies configurable settings that clients must implement for th
|
|||
|
||||
To coordinate manual overrides to [`TERMINAL_TOTAL_DIFFICULTY`](./beacon-chain.md#Transition-settings) parameter, clients must provide `--terminal-total-difficulty-override` as a configurable setting. The value provided by this setting must take precedence over pre-configured `TERMINAL_TOTAL_DIFFICULTY` parameter. Clients should accept the setting as a decimal value (i.e., *not* hexadecimal).
|
||||
|
||||
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.
|
||||
Except under exceptional scenarios, this setting is not expected to 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.
|
||||
To allow for transition coordination around a specific PoW block, clients must also provide `--terminal-block-hash-override` and `--terminal-block-hash-epoch-override` as configurable settings.
|
||||
* The value provided by `--terminal-block-hash-override` takes precedence over the pre-configured `TERMINAL_BLOCK_HASH` parameter.
|
||||
* The value provided by `--terminal-block-hash-epoch-override` takes precedence over the pre-configured `TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH` 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.
|
||||
Except under exceptional scenarios, these settings are not expected to be used. Sufficient warning to the user about this exceptional configurable setting should be provided.
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
- [`ExecutionEngine`](#executionengine)
|
||||
- [`notify_forkchoice_updated`](#notify_forkchoice_updated)
|
||||
- [Helpers](#helpers)
|
||||
- [`PayloadAttributes`](#payloadattributes)
|
||||
- [`PowBlock`](#powblock)
|
||||
- [`get_pow_block`](#get_pow_block)
|
||||
- [`is_valid_terminal_pow_block`](#is_valid_terminal_pow_block)
|
||||
|
@ -43,15 +44,34 @@ This function performs two actions *atomically*:
|
|||
* Applies finality to the execution state: it irreversibly persists the chain of all execution payloads
|
||||
and corresponding state, up to and including `finalized_block_hash`.
|
||||
|
||||
Additionally, if `payload_attributes` is provided, this function sets in motion a payload build process on top of
|
||||
`head_block_hash` with the result to be gathered by a followup call to `get_payload`.
|
||||
|
||||
```python
|
||||
def notify_forkchoice_updated(self: ExecutionEngine, head_block_hash: Hash32, finalized_block_hash: Hash32) -> None:
|
||||
def notify_forkchoice_updated(self: ExecutionEngine,
|
||||
head_block_hash: Hash32,
|
||||
finalized_block_hash: Hash32,
|
||||
payload_attributes: Optional[PayloadAttributes]) -> None:
|
||||
...
|
||||
```
|
||||
|
||||
*Note*: The call of the `notify_forkchoice_updated` function maps on the `POS_FORKCHOICE_UPDATED` event defined in the [EIP-3675](https://eips.ethereum.org/EIPS/eip-3675#definitions).
|
||||
As per EIP-3675, before a post-transition block is finalized, `notify_forkchoice_updated` must be called with `finalized_block_hash = Hash32()`.
|
||||
|
||||
## Helpers
|
||||
|
||||
### `PayloadAttributes`
|
||||
|
||||
Used to signal to initiate the payload build process via `notify_forkchoice_updated`.
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class PayloadAttributes(object):
|
||||
timestamp: uint64
|
||||
random: Bytes32
|
||||
fee_recipient: ExecutionAddress
|
||||
```
|
||||
|
||||
### `PowBlock`
|
||||
|
||||
```python
|
||||
|
@ -75,8 +95,8 @@ 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
|
||||
if TERMINAL_BLOCK_HASH != Hash32():
|
||||
return block.block_hash == TERMINAL_BLOCK_HASH
|
||||
|
||||
is_total_difficulty_reached = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
||||
is_parent_total_difficulty_valid = parent.total_difficulty < TERMINAL_TOTAL_DIFFICULTY
|
||||
|
@ -117,6 +137,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
|||
|
||||
# [New in Merge]
|
||||
if is_merge_block(pre_state, block.body):
|
||||
# Check the parent PoW block of execution payload is a valid terminal PoW block.
|
||||
# Note: unavailable PoW block(s) may later become available.
|
||||
# Nodes should queue such beacon blocks for later processing.
|
||||
pow_block = get_pow_block(block.body.execution_payload.parent_hash)
|
||||
|
@ -124,6 +145,9 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
|
|||
pow_parent = get_pow_block(pow_block.parent_hash)
|
||||
assert pow_parent is not None
|
||||
assert is_valid_terminal_pow_block(pow_block, pow_parent)
|
||||
# If `TERMINAL_BLOCK_HASH` is used as an override, the activation epoch must be reached.
|
||||
if TERMINAL_BLOCK_HASH != Hash32():
|
||||
assert compute_epoch_at_slot(block.slot) >= TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH
|
||||
|
||||
# Add new block to the store
|
||||
store.blocks[hash_tree_root(block)] = block
|
||||
|
|
|
@ -74,8 +74,6 @@ Alias `block = signed_beacon_block.message`, `execution_payload = block.body.exe
|
|||
-- i.e. `execution_payload.timestamp == compute_timestamp_at_slot(state, block.slot)`.
|
||||
- _[REJECT]_ Gas used is less than the gas limit --
|
||||
i.e. `execution_payload.gas_used <= execution_payload.gas_limit`.
|
||||
- _[REJECT]_ The execution payload block hash is not equal to the parent hash --
|
||||
i.e. `execution_payload.block_hash != execution_payload.parent_hash`.
|
||||
- _[REJECT]_ The execution payload transaction list data is within expected size limits,
|
||||
the data MUST NOT be larger than the SSZ list-limit,
|
||||
and a client MAY be more strict.
|
||||
|
|
|
@ -11,9 +11,12 @@
|
|||
- [Introduction](#introduction)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Custom types](#custom-types)
|
||||
- [Helpers](#helpers)
|
||||
- [`get_pow_block_at_terminal_total_difficulty`](#get_pow_block_at_terminal_total_difficulty)
|
||||
- [`get_terminal_pow_block`](#get_terminal_pow_block)
|
||||
- [`get_payload_id`](#get_payload_id)
|
||||
- [Protocols](#protocols)
|
||||
- [`ExecutionEngine`](#executionengine)
|
||||
- [`prepare_payload`](#prepare_payload)
|
||||
- [`get_payload`](#get_payload)
|
||||
- [Beacon chain responsibilities](#beacon-chain-responsibilities)
|
||||
- [Block proposal](#block-proposal)
|
||||
|
@ -39,38 +42,74 @@ Please see related Beacon Chain doc before continuing and use them as a referenc
|
|||
|
||||
| Name | SSZ equivalent | Description |
|
||||
| - | - | - |
|
||||
| `PayloadId` | `uint64` | Identifier of a payload building process |
|
||||
| `PayloadId` | `Bytes8` | Identifier of a payload building process |
|
||||
|
||||
## Helpers
|
||||
|
||||
### `get_pow_block_at_terminal_total_difficulty`
|
||||
|
||||
```python
|
||||
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)
|
||||
assert parent is not None
|
||||
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
|
||||
```
|
||||
|
||||
### `get_terminal_pow_block`
|
||||
|
||||
```python
|
||||
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)
|
||||
```
|
||||
|
||||
### `get_payload_id`
|
||||
|
||||
Given the `head_block_hash` and the `payload_attributes` that were used to
|
||||
initiate the build process via `notify_forkchoice_updated`, `get_payload_id()`
|
||||
returns the `payload_id` used to retrieve the payload via `get_payload`.
|
||||
|
||||
```python
|
||||
def get_payload_id(parent_hash: Hash32, payload_attributes: PayloadAttributes) -> PayloadId:
|
||||
return PayloadId(
|
||||
hash(
|
||||
parent_hash
|
||||
+ uint_to_bytes(payload_attributes.timestamp)
|
||||
+ payload_attributes.random
|
||||
+ payload_attributes.fee_recipient
|
||||
)[0:8]
|
||||
)
|
||||
```
|
||||
|
||||
*Note*: This function does *not* use simple serialize `hash_tree_root` as to
|
||||
avoid requiring simple serialize hashing capabilities in the Execution Layer.
|
||||
|
||||
## Protocols
|
||||
|
||||
### `ExecutionEngine`
|
||||
|
||||
*Note*: `prepare_payload` and `get_payload` functions are added to the `ExecutionEngine` protocol for use as a validator.
|
||||
*Note*: `get_payload` function is added to the `ExecutionEngine` protocol for use as a validator.
|
||||
|
||||
The body of each of these functions is implementation dependent.
|
||||
The Engine API may be used to implement them with an external execution engine.
|
||||
|
||||
#### `prepare_payload`
|
||||
|
||||
Given the set of execution payload attributes, `prepare_payload` initiates a process of building an execution payload
|
||||
on top of the execution chain tip identified by `parent_hash`.
|
||||
|
||||
```python
|
||||
def prepare_payload(self: ExecutionEngine,
|
||||
parent_hash: Hash32,
|
||||
timestamp: uint64,
|
||||
random: Bytes32,
|
||||
fee_recipient: ExecutionAddress) -> PayloadId:
|
||||
"""
|
||||
Return ``payload_id`` that is used to obtain the execution payload in a subsequent ``get_payload`` call.
|
||||
"""
|
||||
...
|
||||
```
|
||||
The body of this function is implementation dependent.
|
||||
The Engine API may be used to implement it with an external execution engine.
|
||||
|
||||
#### `get_payload`
|
||||
|
||||
Given the `payload_id`, `get_payload` returns the most recent version of the execution payload that
|
||||
has been built since the corresponding call to `prepare_payload` method.
|
||||
has been built since the corresponding call to `notify_forkchoice_updated` method.
|
||||
|
||||
```python
|
||||
def get_payload(self: ExecutionEngine, payload_id: PayloadId) -> ExecutionPayload:
|
||||
|
@ -92,42 +131,26 @@ All validator responsibilities remain unchanged other than those noted below. Na
|
|||
|
||||
To obtain an execution payload, a block proposer building a block on top of a `state` must take the following actions:
|
||||
|
||||
1. Set `payload_id = prepare_execution_payload(state, pow_chain, fee_recipient, execution_engine)`, where:
|
||||
1. Set `payload_id = prepare_execution_payload(state, pow_chain, finalized_block_hash, fee_recipient, execution_engine)`, where:
|
||||
* `state` is the state object after applying `process_slots(state, slot)` transition to the resulting state of the parent block processing
|
||||
* `pow_chain` is a list that abstractly represents all blocks in the PoW chain
|
||||
* `finalized_block_hash` is the hash of the latest finalized execution payload (`Hash32()` if none yet finalized)
|
||||
* `fee_recipient` is the value suggested to be used for the `coinbase` field of the execution payload
|
||||
|
||||
|
||||
```python
|
||||
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)
|
||||
assert parent is not None
|
||||
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],
|
||||
finalized_block_hash: Hash32,
|
||||
fee_recipient: ExecutionAddress,
|
||||
execution_engine: ExecutionEngine) -> Optional[PayloadId]:
|
||||
if not is_merge_complete(state):
|
||||
is_terminal_block_hash_set = TERMINAL_BLOCK_HASH != Hash32()
|
||||
is_activation_epoch_reached = get_current_epoch(state.slot) < TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH
|
||||
if is_terminal_block_hash_set and is_activation_epoch_reached:
|
||||
# Terminal block hash is set but activation epoch is not yet reached, no prepare payload call is needed
|
||||
return None
|
||||
|
||||
terminal_pow_block = get_terminal_pow_block(pow_chain)
|
||||
if terminal_pow_block is None:
|
||||
# Pre-merge, no prepare payload call is needed
|
||||
|
@ -138,9 +161,14 @@ def prepare_execution_payload(state: BeaconState,
|
|||
# Post-merge, normal payload
|
||||
parent_hash = state.latest_execution_payload_header.block_hash
|
||||
|
||||
timestamp = compute_timestamp_at_slot(state, state.slot)
|
||||
random = get_randao_mix(state, get_current_epoch(state))
|
||||
return execution_engine.prepare_payload(parent_hash, timestamp, random, fee_recipient)
|
||||
# Set the forkchoice head and initiate the payload build process
|
||||
payload_attributes = PayloadAttributes(
|
||||
timestamp=compute_timestamp_at_slot(state, state.slot),
|
||||
random=get_randao_mix(state, get_current_epoch(state)),
|
||||
fee_recipient=fee_recipient,
|
||||
)
|
||||
execution_engine.notify_forkchoice_updated(parent_hash, finalized_block_hash, payload_attributes)
|
||||
return get_payload_id(parent_hash, payload_attributes)
|
||||
```
|
||||
|
||||
2. Set `block.body.execution_payload = get_execution_payload(payload_id, execution_engine)`, where:
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
from eth2spec.test import context
|
||||
from eth2spec.test.helpers.constants import (
|
||||
ALL_PHASES,
|
||||
)
|
||||
from eth2spec.utils import bls as bls_utils
|
||||
|
||||
# We import pytest only when it's present, i.e. when we are running tests.
|
||||
|
@ -29,6 +32,13 @@ def pytest_addoption(parser):
|
|||
"--preset", action="store", type=str, default="minimal",
|
||||
help="preset: make the pyspec use the specified preset"
|
||||
)
|
||||
parser.addoption(
|
||||
"--fork", action="append", type=str,
|
||||
help=(
|
||||
"fork: make the pyspec only run with the specified phase."
|
||||
" To run multiple phases, e.g., --fork=phase0 --fork=altair"
|
||||
)
|
||||
)
|
||||
parser.addoption(
|
||||
"--disable-bls", action="store_true", default=False,
|
||||
help="bls-default: make tests that are not dependent on BLS run without BLS"
|
||||
|
@ -39,11 +49,31 @@ def pytest_addoption(parser):
|
|||
)
|
||||
|
||||
|
||||
def _validate_fork_name(forks):
|
||||
for fork in forks:
|
||||
if fork not in ALL_PHASES:
|
||||
raise ValueError(
|
||||
f'The given --fork argument "{fork}" is not an available fork.'
|
||||
f' The available forks: {ALL_PHASES}'
|
||||
)
|
||||
|
||||
|
||||
@fixture(autouse=True)
|
||||
def preset(request):
|
||||
context.DEFAULT_TEST_PRESET = request.config.getoption("--preset")
|
||||
|
||||
|
||||
@fixture(autouse=True)
|
||||
def run_phases(request):
|
||||
forks = request.config.getoption("--fork", default=None)
|
||||
if forks:
|
||||
forks = [fork.lower() for fork in forks]
|
||||
_validate_fork_name(forks)
|
||||
context.DEFAULT_PYTEST_FORKS = set(forks)
|
||||
else:
|
||||
context.DEFAULT_PYTEST_FORKS = ALL_PHASES
|
||||
|
||||
|
||||
@fixture(autouse=True)
|
||||
def bls_default(request):
|
||||
disable_bls = request.config.getoption("--disable-bls")
|
||||
|
|
|
@ -22,6 +22,9 @@ from lru import LRU
|
|||
# Without pytest CLI arg or pyspec-test-generator 'preset' argument, this will be the config to apply.
|
||||
DEFAULT_TEST_PRESET = MINIMAL
|
||||
|
||||
# Without pytest CLI arg or pyspec-test-generator 'run-phase' argument, this will be the config to apply.
|
||||
DEFAULT_PYTEST_FORKS = ALL_PHASES
|
||||
|
||||
|
||||
# TODO: currently phases are defined as python modules.
|
||||
# It would be better if they would be more well-defined interfaces for stronger typing.
|
||||
|
@ -351,7 +354,7 @@ def with_phases(phases, other_phases=None):
|
|||
"""
|
||||
def decorator(fn):
|
||||
def wrapper(*args, **kw):
|
||||
run_phases = phases
|
||||
run_phases = set(phases).intersection(DEFAULT_PYTEST_FORKS)
|
||||
|
||||
# limit phases if one explicitly specified
|
||||
if 'phase' in kw:
|
||||
|
|
Loading…
Reference in New Issue