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
|
name: Install pyspec requirements
|
||||||
command: make install_test
|
command: make install_test
|
||||||
- save_pyspec_cached_venv
|
- save_pyspec_cached_venv
|
||||||
test:
|
test-phase0:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/python:3.8
|
- image: circleci/python:3.8
|
||||||
working_directory: ~/specs-repo
|
working_directory: ~/specs-repo
|
||||||
|
@ -100,7 +100,33 @@ jobs:
|
||||||
- restore_pyspec_cached_venv
|
- restore_pyspec_cached_venv
|
||||||
- run:
|
- run:
|
||||||
name: Run py-tests
|
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:
|
- store_test_results:
|
||||||
path: tests/core/pyspec/test-reports
|
path: tests/core/pyspec/test-reports
|
||||||
table_of_contents:
|
table_of_contents:
|
||||||
|
@ -208,14 +234,20 @@ workflows:
|
||||||
- install_pyspec_test:
|
- install_pyspec_test:
|
||||||
requires:
|
requires:
|
||||||
- checkout_specs
|
- checkout_specs
|
||||||
- test:
|
- test-phase0:
|
||||||
|
requires:
|
||||||
|
- install_pyspec_test
|
||||||
|
- test-altair:
|
||||||
|
requires:
|
||||||
|
- install_pyspec_test
|
||||||
|
- test-merge:
|
||||||
requires:
|
requires:
|
||||||
- install_pyspec_test
|
- install_pyspec_test
|
||||||
- table_of_contents
|
- table_of_contents
|
||||||
- codespell
|
- codespell
|
||||||
- lint:
|
- lint:
|
||||||
requires:
|
requires:
|
||||||
- test
|
- install_pyspec_test
|
||||||
# NOTE: Since phase 0 has been launched, we disabled the deposit contract tests.
|
# NOTE: Since phase 0 has been launched, we disabled the deposit contract tests.
|
||||||
# - install_deposit_contract_web3_tester:
|
# - install_deposit_contract_web3_tester:
|
||||||
# requires:
|
# 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
|
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
|
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
|
python3 -m pytest -n 4 --bls-type=milagro --junitxml=eth2spec/test_results.xml eth2spec
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
open_cov:
|
open_cov:
|
||||||
((open "$(COV_INDEX_FILE)" || xdg-open "$(COV_INDEX_FILE)") &> /dev/null) &
|
((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
|
# TBD, 2**256-2**10 is a placeholder
|
||||||
TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129638912
|
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: 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: 18446744073709551615
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Genesis
|
# Genesis
|
||||||
|
|
|
@ -7,8 +7,10 @@ PRESET_BASE: 'minimal'
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
# TBD, 2**256-2**10 is a placeholder
|
# TBD, 2**256-2**10 is a placeholder
|
||||||
TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129638912
|
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: 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: 18446744073709551615
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Genesis
|
# Genesis
|
||||||
|
|
|
@ -1,3 +1,16 @@
|
||||||
# Mainnet preset - The Merge
|
# 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
|
# 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'''
|
return super().imports(preset_name) + f'''
|
||||||
from typing import Protocol
|
from typing import Protocol
|
||||||
from eth2spec.altair import {preset_name} as altair
|
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
|
@classmethod
|
||||||
|
@ -527,16 +527,12 @@ class NoopExecutionEngine(ExecutionEngine):
|
||||||
def execute_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool:
|
def execute_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool:
|
||||||
return True
|
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
|
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:
|
def get_payload(self: ExecutionEngine, payload_id: PayloadId) -> ExecutionPayload:
|
||||||
raise NotImplementedError("no default block production")
|
raise NotImplementedError("no default block production")
|
||||||
|
|
||||||
|
@ -547,7 +543,7 @@ EXECUTION_ENGINE = NoopExecutionEngine()"""
|
||||||
@classmethod
|
@classmethod
|
||||||
def hardcoded_custom_type_dep_constants(cls) -> str:
|
def hardcoded_custom_type_dep_constants(cls) -> str:
|
||||||
constants = {
|
constants = {
|
||||||
'MAX_BYTES_PER_OPAQUE_TRANSACTION': 'uint64(2**20)',
|
'MAX_BYTES_PER_TRANSACTION': 'uint64(2**20)',
|
||||||
}
|
}
|
||||||
return {**super().hardcoded_custom_type_dep_constants(), **constants}
|
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
|
# Access global dict of config vars for runtime configurables
|
||||||
for name in spec_object.config_vars.keys():
|
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:
|
def format_config_var(name: str, vardef: VariableDefinition) -> str:
|
||||||
if vardef.type_name is None:
|
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 = [
|
ignored_dependencies = [
|
||||||
'bit', 'boolean', 'Vector', 'List', 'Container', 'BLSPubkey', 'BLSSignature',
|
'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',
|
'uint8', 'uint16', 'uint32', 'uint64', 'uint128', 'uint256',
|
||||||
'bytes', 'byte', 'ByteList', 'ByteVector',
|
'bytes', 'byte', 'ByteList', 'ByteVector',
|
||||||
'Dict', 'dict', 'field', 'ceillog2', 'floorlog2', 'Set',
|
'Dict', 'dict', 'field', 'ceillog2', 'floorlog2', 'Set',
|
||||||
|
@ -1021,7 +1017,7 @@ setup(
|
||||||
"py_ecc==5.2.0",
|
"py_ecc==5.2.0",
|
||||||
"milagro_bls_binding==1.6.3",
|
"milagro_bls_binding==1.6.3",
|
||||||
"dataclasses==0.6",
|
"dataclasses==0.6",
|
||||||
"remerkleable==0.1.22",
|
"remerkleable==0.1.24",
|
||||||
RUAMEL_YAML_VERSION,
|
RUAMEL_YAML_VERSION,
|
||||||
"lru-dict==1.1.6",
|
"lru-dict==1.1.6",
|
||||||
MARKO_VERSION,
|
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 |
|
| 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` | `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|
|
||||||
| `Transaction` | `Union[OpaqueTransaction]` | a transaction |
|
|
||||||
| `ExecutionAddress` | `Bytes20` | Address of account on the execution layer |
|
| `ExecutionAddress` | `Bytes20` | Address of account on the execution layer |
|
||||||
|
|
||||||
## Constants
|
## Constants
|
||||||
|
@ -60,7 +59,7 @@ This patch adds transaction execution to the beacon chain as part of the Merge f
|
||||||
|
|
||||||
| Name | Value |
|
| 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) |
|
| `MAX_TRANSACTIONS_PER_PAYLOAD` | `uint64(2**14)` (= 16,384) |
|
||||||
| `BYTES_PER_LOGS_BLOOM` | `uint64(2**8)` (= 256) |
|
| `BYTES_PER_LOGS_BLOOM` | `uint64(2**8)` (= 256) |
|
||||||
| `GAS_LIMIT_DENOMINATOR` | `uint64(2**10)` (= 1,024) |
|
| `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 |
|
| Name | Value |
|
||||||
| - | - |
|
| - | - |
|
||||||
| `TERMINAL_TOTAL_DIFFICULTY` | **TBD** |
|
| `TERMINAL_TOTAL_DIFFICULTY` | **TBD** |
|
||||||
| `TERMINAL_BLOCK_HASH` | `Hash32('0x0000000000000000000000000000000000000000000000000000000000000000')` |
|
| `TERMINAL_BLOCK_HASH` | `Hash32()` |
|
||||||
|
| `TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH` | `FAR_FUTURE_EPOCH` |
|
||||||
|
|
||||||
## Containers
|
## 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).
|
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
|
### 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.
|
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 this setting takes precedence over the pre-configured `TERMINAL_BLOCK_HASH` parameter.
|
* 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)
|
- [`ExecutionEngine`](#executionengine)
|
||||||
- [`notify_forkchoice_updated`](#notify_forkchoice_updated)
|
- [`notify_forkchoice_updated`](#notify_forkchoice_updated)
|
||||||
- [Helpers](#helpers)
|
- [Helpers](#helpers)
|
||||||
|
- [`PayloadAttributes`](#payloadattributes)
|
||||||
- [`PowBlock`](#powblock)
|
- [`PowBlock`](#powblock)
|
||||||
- [`get_pow_block`](#get_pow_block)
|
- [`get_pow_block`](#get_pow_block)
|
||||||
- [`is_valid_terminal_pow_block`](#is_valid_terminal_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
|
* 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`.
|
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
|
```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).
|
*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
|
## 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`
|
### `PowBlock`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
@ -75,8 +95,8 @@ Used by fork-choice handler, `on_block`.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def is_valid_terminal_pow_block(block: PowBlock, parent: PowBlock) -> bool:
|
def is_valid_terminal_pow_block(block: PowBlock, parent: PowBlock) -> bool:
|
||||||
if block.block_hash == TERMINAL_BLOCK_HASH:
|
if TERMINAL_BLOCK_HASH != Hash32():
|
||||||
return True
|
return block.block_hash == TERMINAL_BLOCK_HASH
|
||||||
|
|
||||||
is_total_difficulty_reached = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
is_total_difficulty_reached = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
|
||||||
is_parent_total_difficulty_valid = parent.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]
|
# [New in Merge]
|
||||||
if is_merge_block(pre_state, block.body):
|
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.
|
# Note: unavailable PoW block(s) may later become available.
|
||||||
# Nodes should queue such beacon blocks for later processing.
|
# Nodes should queue such beacon blocks for later processing.
|
||||||
pow_block = get_pow_block(block.body.execution_payload.parent_hash)
|
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)
|
pow_parent = get_pow_block(pow_block.parent_hash)
|
||||||
assert pow_parent is not None
|
assert pow_parent is not None
|
||||||
assert is_valid_terminal_pow_block(pow_block, pow_parent)
|
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
|
# Add new block to the store
|
||||||
store.blocks[hash_tree_root(block)] = block
|
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)`.
|
-- i.e. `execution_payload.timestamp == compute_timestamp_at_slot(state, block.slot)`.
|
||||||
- _[REJECT]_ Gas used is less than the gas limit --
|
- _[REJECT]_ Gas used is less than the gas limit --
|
||||||
i.e. `execution_payload.gas_used <= execution_payload.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,
|
- _[REJECT]_ The execution payload transaction list data is within expected size limits,
|
||||||
the data MUST NOT be larger than the SSZ list-limit,
|
the data MUST NOT be larger than the SSZ list-limit,
|
||||||
and a client MAY be more strict.
|
and a client MAY be more strict.
|
||||||
|
|
|
@ -11,9 +11,12 @@
|
||||||
- [Introduction](#introduction)
|
- [Introduction](#introduction)
|
||||||
- [Prerequisites](#prerequisites)
|
- [Prerequisites](#prerequisites)
|
||||||
- [Custom types](#custom-types)
|
- [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)
|
- [Protocols](#protocols)
|
||||||
- [`ExecutionEngine`](#executionengine)
|
- [`ExecutionEngine`](#executionengine)
|
||||||
- [`prepare_payload`](#prepare_payload)
|
|
||||||
- [`get_payload`](#get_payload)
|
- [`get_payload`](#get_payload)
|
||||||
- [Beacon chain responsibilities](#beacon-chain-responsibilities)
|
- [Beacon chain responsibilities](#beacon-chain-responsibilities)
|
||||||
- [Block proposal](#block-proposal)
|
- [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 |
|
| 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
|
## Protocols
|
||||||
|
|
||||||
### `ExecutionEngine`
|
### `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 body of this function is implementation dependent.
|
||||||
The Engine API may be used to implement them with an external execution engine.
|
The Engine API may be used to implement it 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.
|
|
||||||
"""
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `get_payload`
|
#### `get_payload`
|
||||||
|
|
||||||
Given the `payload_id`, `get_payload` returns the most recent version of the execution payload that
|
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
|
```python
|
||||||
def get_payload(self: ExecutionEngine, payload_id: PayloadId) -> ExecutionPayload:
|
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:
|
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
|
* `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
|
* `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
|
* `fee_recipient` is the value suggested to be used for the `coinbase` field of the execution payload
|
||||||
|
|
||||||
|
|
||||||
```python
|
```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,
|
def prepare_execution_payload(state: BeaconState,
|
||||||
pow_chain: Sequence[PowBlock],
|
pow_chain: Sequence[PowBlock],
|
||||||
|
finalized_block_hash: Hash32,
|
||||||
fee_recipient: ExecutionAddress,
|
fee_recipient: ExecutionAddress,
|
||||||
execution_engine: ExecutionEngine) -> Optional[PayloadId]:
|
execution_engine: ExecutionEngine) -> Optional[PayloadId]:
|
||||||
if not is_merge_complete(state):
|
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)
|
terminal_pow_block = get_terminal_pow_block(pow_chain)
|
||||||
if terminal_pow_block is None:
|
if terminal_pow_block is None:
|
||||||
# Pre-merge, no prepare payload call is needed
|
# Pre-merge, no prepare payload call is needed
|
||||||
|
@ -138,9 +161,14 @@ def prepare_execution_payload(state: BeaconState,
|
||||||
# Post-merge, normal payload
|
# Post-merge, normal payload
|
||||||
parent_hash = state.latest_execution_payload_header.block_hash
|
parent_hash = state.latest_execution_payload_header.block_hash
|
||||||
|
|
||||||
timestamp = compute_timestamp_at_slot(state, state.slot)
|
# Set the forkchoice head and initiate the payload build process
|
||||||
random = get_randao_mix(state, get_current_epoch(state))
|
payload_attributes = PayloadAttributes(
|
||||||
return execution_engine.prepare_payload(parent_hash, timestamp, random, fee_recipient)
|
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:
|
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 import context
|
||||||
|
from eth2spec.test.helpers.constants import (
|
||||||
|
ALL_PHASES,
|
||||||
|
)
|
||||||
from eth2spec.utils import bls as bls_utils
|
from eth2spec.utils import bls as bls_utils
|
||||||
|
|
||||||
# We import pytest only when it's present, i.e. when we are running tests.
|
# 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",
|
"--preset", action="store", type=str, default="minimal",
|
||||||
help="preset: make the pyspec use the specified preset"
|
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(
|
parser.addoption(
|
||||||
"--disable-bls", action="store_true", default=False,
|
"--disable-bls", action="store_true", default=False,
|
||||||
help="bls-default: make tests that are not dependent on BLS run without BLS"
|
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)
|
@fixture(autouse=True)
|
||||||
def preset(request):
|
def preset(request):
|
||||||
context.DEFAULT_TEST_PRESET = request.config.getoption("--preset")
|
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)
|
@fixture(autouse=True)
|
||||||
def bls_default(request):
|
def bls_default(request):
|
||||||
disable_bls = request.config.getoption("--disable-bls")
|
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.
|
# Without pytest CLI arg or pyspec-test-generator 'preset' argument, this will be the config to apply.
|
||||||
DEFAULT_TEST_PRESET = MINIMAL
|
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.
|
# TODO: currently phases are defined as python modules.
|
||||||
# It would be better if they would be more well-defined interfaces for stronger typing.
|
# 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 decorator(fn):
|
||||||
def wrapper(*args, **kw):
|
def wrapper(*args, **kw):
|
||||||
run_phases = phases
|
run_phases = set(phases).intersection(DEFAULT_PYTEST_FORKS)
|
||||||
|
|
||||||
# limit phases if one explicitly specified
|
# limit phases if one explicitly specified
|
||||||
if 'phase' in kw:
|
if 'phase' in kw:
|
||||||
|
|
Loading…
Reference in New Issue