From 058137327a4cb9163c4202e1ed3bd4d886d745cf Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 16 May 2023 00:27:00 +0800 Subject: [PATCH 01/13] Use new `engine_newPayloadV3` --- setup.py | 2 +- specs/_features/eip6110/beacon-chain.md | 10 +-- specs/bellatrix/beacon-chain.md | 25 +++++-- specs/capella/beacon-chain.md | 8 ++- specs/deneb/beacon-chain.md | 71 ++++++++----------- specs/deneb/validator.md | 4 +- .../test_process_execution_payload.py | 4 +- .../test/phase0/sanity/test_blocks.py | 2 +- 8 files changed, 67 insertions(+), 59 deletions(-) diff --git a/setup.py b/setup.py index b2316ed958..8ef66df232 100644 --- a/setup.py +++ b/setup.py @@ -578,7 +578,7 @@ def get_pow_chain_head() -> PowBlock: class NoopExecutionEngine(ExecutionEngine): - def notify_new_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: + def notify_new_payload(self: ExecutionEngine, new_payload_request: NewPayloadRequest) -> bool: return True def notify_forkchoice_updated(self: ExecutionEngine, diff --git a/specs/_features/eip6110/beacon-chain.md b/specs/_features/eip6110/beacon-chain.md index 708418e1cd..c9dccbdebb 100644 --- a/specs/_features/eip6110/beacon-chain.md +++ b/specs/_features/eip6110/beacon-chain.md @@ -178,12 +178,11 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_block_header(state, block) if is_execution_enabled(state, block.body): process_withdrawals(state, block.body.execution_payload) - process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [Modified in EIP6110] + process_execution_payload(state, block.body, EXECUTION_ENGINE) # [Modified in EIP6110] process_randao(state, block.body) process_eth1_data(state, block.body) process_operations(state, block.body) # [Modified in EIP6110] process_sync_aggregate(state, block.body.sync_aggregate) - process_blob_kzg_commitments(block.body) ``` #### Modified `process_operations` @@ -238,7 +237,9 @@ def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt) *Note*: The function `process_execution_payload` is modified to use the new `ExecutionPayloadHeader` type. ```python -def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None: +def process_execution_payload(state: BeaconState, body: BeaconBlockBody, execution_engine: ExecutionEngine) -> None: + payload = body.execution_payload + # Verify consistency of the parent hash with respect to the previous execution payload header if is_merge_transition_complete(state): assert payload.parent_hash == state.latest_execution_payload_header.block_hash @@ -247,7 +248,8 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe # Verify timestamp assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) # Verify the execution payload is valid - assert execution_engine.notify_new_payload(payload) + versioned_hashes = [kzg_commitment_to_versioned_hash(commitment) for commitment in body.blob_kzg_commitments] + assert execution_engine.notify_new_payload(NewPayloadRequest(execution_payload=payload)) # Cache execution payload header state.latest_execution_payload_header = ExecutionPayloadHeader( parent_hash=payload.parent_hash, diff --git a/specs/bellatrix/beacon-chain.md b/specs/bellatrix/beacon-chain.md index 1133cba06c..a4c7be16e9 100644 --- a/specs/bellatrix/beacon-chain.md +++ b/specs/bellatrix/beacon-chain.md @@ -33,6 +33,9 @@ - [Modified `slash_validator`](#modified-slash_validator) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Execution engine](#execution-engine) + - [Request data](#request-data) + - [`NewPayloadRequest`](#newpayloadrequest) + - [Engine APIs](#engine-apis) - [`notify_new_payload`](#notify_new_payload) - [Block processing](#block-processing) - [Execution payload](#execution-payload) @@ -300,6 +303,18 @@ def slash_validator(state: BeaconState, ### Execution engine +#### Request data + +##### `NewPayloadRequest` + +```python +@dataclass +class NewPayloadRequest(object): + execution_payload: ExecutionPayload +``` + +#### Engine APIs + The implementation-dependent `ExecutionEngine` protocol encapsulates the execution sub-system logic via: * a state object `self.execution_state` of type `ExecutionState` @@ -313,7 +328,7 @@ The Engine API may be used to implement this and similarly defined functions via #### `notify_new_payload` ```python -def notify_new_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: +def notify_new_payload(self: ExecutionEngine, new_payload_request: NewPayloadRequest) -> bool: """ Return ``True`` if and only if ``execution_payload`` is valid with respect to ``self.execution_state``. """ @@ -328,7 +343,7 @@ def notify_new_payload(self: ExecutionEngine, execution_payload: ExecutionPayloa def process_block(state: BeaconState, block: BeaconBlock) -> None: process_block_header(state, block) if is_execution_enabled(state, block.body): - process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [New in Bellatrix] + process_execution_payload(state, block.body, EXECUTION_ENGINE) # [New in Bellatrix] process_randao(state, block.body) process_eth1_data(state, block.body) process_operations(state, block.body) @@ -340,7 +355,9 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: ##### `process_execution_payload` ```python -def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None: +def process_execution_payload(state: BeaconState, body: BeaconBlockBody, execution_engine: ExecutionEngine) -> None: + payload = body.execution_payload + # Verify consistency of the parent hash with respect to the previous execution payload header if is_merge_transition_complete(state): assert payload.parent_hash == state.latest_execution_payload_header.block_hash @@ -349,7 +366,7 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe # Verify timestamp assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) # Verify the execution payload is valid - assert execution_engine.notify_new_payload(payload) + assert execution_engine.notify_new_payload(NewPayloadRequest(execution_payload=payload)) # Cache execution payload header state.latest_execution_payload_header = ExecutionPayloadHeader( parent_hash=payload.parent_hash, diff --git a/specs/capella/beacon-chain.md b/specs/capella/beacon-chain.md index 1df617daaf..1799f4e6e3 100644 --- a/specs/capella/beacon-chain.md +++ b/specs/capella/beacon-chain.md @@ -333,7 +333,7 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_block_header(state, block) if is_execution_enabled(state, block.body): process_withdrawals(state, block.body.execution_payload) # [New in Capella] - process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [Modified in Capella] + process_execution_payload(state, block.body, EXECUTION_ENGINE) # [Modified in Capella] process_randao(state, block.body) process_eth1_data(state, block.body) process_operations(state, block.body) # [Modified in Capella] @@ -407,7 +407,9 @@ def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None: *Note*: The function `process_execution_payload` is modified to use the new `ExecutionPayloadHeader` type. ```python -def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None: +def process_execution_payload(state: BeaconState, body: BeaconBlockBody, execution_engine: ExecutionEngine) -> None: + payload = body.execution_payload + # Verify consistency of the parent hash with respect to the previous execution payload header if is_merge_transition_complete(state): assert payload.parent_hash == state.latest_execution_payload_header.block_hash @@ -416,7 +418,7 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe # Verify timestamp assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) # Verify the execution payload is valid - assert execution_engine.notify_new_payload(payload) + assert execution_engine.notify_new_payload(NewPayloadRequest(execution_payload=payload)) # Cache execution payload header state.latest_execution_payload_header = ExecutionPayloadHeader( parent_hash=payload.parent_hash, diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index 359c7fc95b..52719dcf68 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -24,13 +24,15 @@ - [Helper functions](#helper-functions) - [Misc](#misc) - [`kzg_commitment_to_versioned_hash`](#kzg_commitment_to_versioned_hash) - - [`tx_peek_blob_versioned_hashes`](#tx_peek_blob_versioned_hashes) - - [`verify_kzg_commitments_against_transactions`](#verify_kzg_commitments_against_transactions) - [Beacon chain state transition function](#beacon-chain-state-transition-function) + - [Execution engine](#execution-engine) + - [Request data](#request-data) + - [Modified `NewPayloadRequest`](#modified-newpayloadrequest) + - [Engine APIs](#engine-apis) + - [Modified `notify_new_payload`](#modified-notify_new_payload) - [Block processing](#block-processing) - [Execution payload](#execution-payload) - [`process_execution_payload`](#process_execution_payload) - - [Blob KZG commitments](#blob-kzg-commitments) - [Testing](#testing) @@ -158,43 +160,33 @@ def kzg_commitment_to_versioned_hash(kzg_commitment: KZGCommitment) -> Versioned return VERSIONED_HASH_VERSION_KZG + hash(kzg_commitment)[1:] ``` -#### `tx_peek_blob_versioned_hashes` +## Beacon chain state transition function + +### Execution engine + +#### Request data -This function retrieves the hashes from the `SignedBlobTransaction` as defined in Deneb, using SSZ offsets. -Offsets are little-endian `uint32` values, as defined in the [SSZ specification](../../ssz/simple-serialize.md). -See [the full details of `blob_versioned_hashes` offset calculation](https://gist.github.com/protolambda/23bd106b66f6d4bb854ce46044aa3ca3). +##### Modified `NewPayloadRequest` ```python -def tx_peek_blob_versioned_hashes(opaque_tx: Transaction) -> Sequence[VersionedHash]: - assert opaque_tx[0] == BLOB_TX_TYPE - message_offset = 1 + uint32.decode_bytes(opaque_tx[1:5]) - # field offset: 32 + 8 + 32 + 32 + 8 + 4 + 32 + 4 + 4 + 32 = 188 - blob_versioned_hashes_offset = ( - message_offset - + uint32.decode_bytes(opaque_tx[(message_offset + 188):(message_offset + 192)]) - ) - # `VersionedHash` is a 32-byte object - assert (len(opaque_tx) - blob_versioned_hashes_offset) % 32 == 0 - return [ - VersionedHash(opaque_tx[x:(x + 32)]) - for x in range(blob_versioned_hashes_offset, len(opaque_tx), 32) - ] +@dataclass +class NewPayloadRequest(object): + execution_payload: ExecutionPayload + versioned_hashes: Sequence[VersionedHash] ``` -#### `verify_kzg_commitments_against_transactions` +#### Engine APIs + +#### Modified `notify_new_payload` ```python -def verify_kzg_commitments_against_transactions(transactions: Sequence[Transaction], - kzg_commitments: Sequence[KZGCommitment]) -> bool: - all_versioned_hashes: List[VersionedHash] = [] - for tx in transactions: - if tx[0] == BLOB_TX_TYPE: - all_versioned_hashes += tx_peek_blob_versioned_hashes(tx) - return all_versioned_hashes == [kzg_commitment_to_versioned_hash(commitment) for commitment in kzg_commitments] +def notify_new_payload(self: ExecutionEngine, new_payload_request: NewPayloadRequest) -> bool: + """ + Return ``True`` if and only if ``execution_payload`` is valid with respect to ``self.execution_state``. + """ + ... ``` -## Beacon chain state transition function - ### Block processing ```python @@ -202,12 +194,11 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_block_header(state, block) if is_execution_enabled(state, block.body): process_withdrawals(state, block.body.execution_payload) - process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [Modified in Deneb] + process_execution_payload(state, block.body, EXECUTION_ENGINE) # [Modified in Deneb] process_randao(state, block.body) process_eth1_data(state, block.body) process_operations(state, block.body) process_sync_aggregate(state, block.body.sync_aggregate) - process_blob_kzg_commitments(block.body) # [New in Deneb] ``` #### Execution payload @@ -215,7 +206,9 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: ##### `process_execution_payload` ```python -def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None: +def process_execution_payload(state: BeaconState, body: BeaconBlockBody, execution_engine: ExecutionEngine) -> None: + payload = body.execution_payload + # Verify consistency of the parent hash with respect to the previous execution payload header if is_merge_transition_complete(state): assert payload.parent_hash == state.latest_execution_payload_header.block_hash @@ -224,7 +217,8 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe # Verify timestamp assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) # Verify the execution payload is valid - assert execution_engine.notify_new_payload(payload) + versioned_hashes = [kzg_commitment_to_versioned_hash(commitment) for commitment in body.blob_kzg_commitments] + assert execution_engine.notify_new_payload(NewPayloadRequest(execution_payload=payload, versioned_hashes=versioned_hashes)) # Cache execution payload header state.latest_execution_payload_header = ExecutionPayloadHeader( @@ -247,13 +241,6 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe ) ``` -#### Blob KZG commitments - -```python -def process_blob_kzg_commitments(body: BeaconBlockBody) -> None: - assert verify_kzg_commitments_against_transactions(body.execution_payload.transactions, body.blob_kzg_commitments) -``` - ## Testing *Note*: The function `initialize_beacon_state_from_eth1` is modified for pure Deneb testing only. diff --git a/specs/deneb/validator.md b/specs/deneb/validator.md index 6562c91ddd..e52ce9eaac 100644 --- a/specs/deneb/validator.md +++ b/specs/deneb/validator.md @@ -98,8 +98,8 @@ def validate_blobs_and_kzg_commitments(execution_payload: ExecutionPayload, blobs: Sequence[Blob], blob_kzg_commitments: Sequence[KZGCommitment], blob_kzg_proofs: Sequence[KZGProof]) -> None: - # Optionally sanity-check that the KZG commitments match the versioned hashes in the transactions - assert verify_kzg_commitments_against_transactions(execution_payload.transactions, blob_kzg_commitments) + # TODO: can we just remove it? + # assert verify_kzg_commitments_against_transactions(execution_payload.transactions, blob_kzg_commitments) # Optionally sanity-check that the KZG commitments match the blobs (as produced by the execution engine) assert len(blob_kzg_commitments) == len(blobs) == len(blob_kzg_proofs) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py index 3ec58b31ed..05e16f1d1f 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py @@ -29,10 +29,10 @@ def run_execution_payload_processing(spec, state, execution_payload, valid=True, called_new_block = False class TestEngine(spec.NoopExecutionEngine): - def notify_new_payload(self, payload) -> bool: + def notify_new_payload(self, new_payload_request) -> bool: nonlocal called_new_block, execution_valid called_new_block = True - assert payload == execution_payload + assert new_payload_request.execution_payload == execution_payload return execution_valid if not valid: diff --git a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py index 2e1a2a369a..d31955e827 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/phase0/sanity/test_blocks.py @@ -143,7 +143,7 @@ def process_and_sign_block_without_header_validations(spec, state, block): ) if is_post_bellatrix(spec): if spec.is_execution_enabled(state, block.body): - spec.process_execution_payload(state, block.body.execution_payload, spec.EXECUTION_ENGINE) + spec.process_execution_payload(state, block.body, spec.EXECUTION_ENGINE) # Perform rest of process_block transitions spec.process_randao(state, block.body) From 0b2f604f86a037d5b92e82aa79ab8bfc3a55d812 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 16 May 2023 23:58:01 +0800 Subject: [PATCH 02/13] Fix tests --- specs/_features/eip6110/beacon-chain.md | 4 +- specs/deneb/beacon-chain.md | 5 ++- specs/deneb/validator.md | 6 +-- .../test_process_execution_payload.py | 12 ++--- .../test_process_withdrawals.py | 2 +- .../eth2spec/test/deneb/sanity/test_blocks.py | 45 ++++++++++++------- .../test/deneb/unittests/test_offset.py | 23 ---------- .../unittests/validator/test_validator.py | 6 +-- tests/formats/operations/README.md | 2 +- 9 files changed, 48 insertions(+), 57 deletions(-) delete mode 100644 tests/core/pyspec/eth2spec/test/deneb/unittests/test_offset.py diff --git a/specs/_features/eip6110/beacon-chain.md b/specs/_features/eip6110/beacon-chain.md index c9dccbdebb..591d8dc869 100644 --- a/specs/_features/eip6110/beacon-chain.md +++ b/specs/_features/eip6110/beacon-chain.md @@ -249,7 +249,9 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) # Verify the execution payload is valid versioned_hashes = [kzg_commitment_to_versioned_hash(commitment) for commitment in body.blob_kzg_commitments] - assert execution_engine.notify_new_payload(NewPayloadRequest(execution_payload=payload)) + assert execution_engine.notify_new_payload( + NewPayloadRequest(execution_payload=payload, versioned_hashes=versioned_hashes) + ) # Cache execution payload header state.latest_execution_payload_header = ExecutionPayloadHeader( parent_hash=payload.parent_hash, diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index 52719dcf68..2c4bb61335 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -217,8 +217,11 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi # Verify timestamp assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) # Verify the execution payload is valid + # [Modified in Deneb] versioned_hashes = [kzg_commitment_to_versioned_hash(commitment) for commitment in body.blob_kzg_commitments] - assert execution_engine.notify_new_payload(NewPayloadRequest(execution_payload=payload, versioned_hashes=versioned_hashes)) + assert execution_engine.notify_new_payload( + NewPayloadRequest(execution_payload=payload, versioned_hashes=versioned_hashes) + ) # Cache execution payload header state.latest_execution_payload_header = ExecutionPayloadHeader( diff --git a/specs/deneb/validator.md b/specs/deneb/validator.md index e52ce9eaac..03297c59e2 100644 --- a/specs/deneb/validator.md +++ b/specs/deneb/validator.md @@ -94,13 +94,9 @@ via `get_payload(payload_id).blobs_bundle`. 2. Validate `blobs` and `blob_kzg_commitments`: ```python -def validate_blobs_and_kzg_commitments(execution_payload: ExecutionPayload, - blobs: Sequence[Blob], +def validate_blobs_and_kzg_commitments(blobs: Sequence[Blob], blob_kzg_commitments: Sequence[KZGCommitment], blob_kzg_proofs: Sequence[KZGProof]) -> None: - # TODO: can we just remove it? - # assert verify_kzg_commitments_against_transactions(execution_payload.transactions, blob_kzg_commitments) - # Optionally sanity-check that the KZG commitments match the blobs (as produced by the execution engine) assert len(blob_kzg_commitments) == len(blobs) == len(blob_kzg_proofs) assert verify_blob_kzg_proof_batch(blobs, blob_kzg_commitments, blob_kzg_proofs) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py index 05e16f1d1f..b835632354 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py @@ -21,10 +21,12 @@ def run_execution_payload_processing(spec, state, execution_payload, valid=True, - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ + # Before Deneb, only `body.execution_payload` matters. `BeaconBlockBody` is just a wrapper. + body = spec.BeaconBlockBody(execution_payload=execution_payload) yield 'pre', state yield 'execution', {'execution_valid': execution_valid} - yield 'execution_payload', execution_payload + yield 'body', body called_new_block = False @@ -32,22 +34,22 @@ class TestEngine(spec.NoopExecutionEngine): def notify_new_payload(self, new_payload_request) -> bool: nonlocal called_new_block, execution_valid called_new_block = True - assert new_payload_request.execution_payload == execution_payload + assert new_payload_request.execution_payload == body.execution_payload return execution_valid if not valid: - expect_assertion_error(lambda: spec.process_execution_payload(state, execution_payload, TestEngine())) + expect_assertion_error(lambda: spec.process_execution_payload(state, body, TestEngine())) yield 'post', None return - spec.process_execution_payload(state, execution_payload, TestEngine()) + spec.process_execution_payload(state, body, TestEngine()) # Make sure we called the engine assert called_new_block yield 'post', state - assert state.latest_execution_payload_header == get_execution_payload_header(spec, execution_payload) + assert state.latest_execution_payload_header == get_execution_payload_header(spec, body.execution_payload) def run_success_test(spec, state): diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py index d7813fb1f2..ad71e2c73c 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py @@ -56,7 +56,7 @@ def verify_post_state(state, spec, expected_withdrawals, def run_withdrawals_processing(spec, state, execution_payload, num_expected_withdrawals=None, fully_withdrawable_indices=None, partial_withdrawals_indices=None, valid=True): """ - Run ``process_execution_payload``, yielding: + Run ``process_withdrawals``, yielding: - pre-state ('pre') - execution payload ('execution_payload') - post-state ('post'). diff --git a/tests/core/pyspec/eth2spec/test/deneb/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/deneb/sanity/test_blocks.py index 111565cce2..f27a774abe 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/deneb/sanity/test_blocks.py @@ -53,7 +53,10 @@ def test_max_blobs(spec, state): @with_deneb_and_later @spec_state_test -def test_invalid_incorrect_blob_tx_type(spec, state): +def test_incorrect_blob_tx_type(spec, state): + """ + The versioned hashes are wrong, but the testing ExecutionEngine returns VALID by default. + """ yield 'pre', state block = build_empty_block_for_next_slot(spec, state) @@ -62,15 +65,18 @@ def test_invalid_incorrect_blob_tx_type(spec, state): opaque_tx = b'\x04' + opaque_tx[1:] # incorrect tx type block.body.execution_payload.transactions = [opaque_tx] block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True) + signed_block = state_transition_and_sign_block(spec, state, block) yield 'blocks', [signed_block] - yield 'post', None + yield 'post', state @with_deneb_and_later @spec_state_test -def test_invalid_incorrect_transaction_length_1_byte(spec, state): +def test_incorrect_transaction_length_1_byte(spec, state): + """ + The versioned hashes are wrong, but the testing ExecutionEngine returns VALID by default. + """ yield 'pre', state block = build_empty_block_for_next_slot(spec, state) @@ -79,15 +85,18 @@ def test_invalid_incorrect_transaction_length_1_byte(spec, state): opaque_tx = opaque_tx + b'\x12' # incorrect tx length block.body.execution_payload.transactions = [opaque_tx] block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True) + signed_block = state_transition_and_sign_block(spec, state, block) yield 'blocks', [signed_block] - yield 'post', None + yield 'post', state @with_deneb_and_later @spec_state_test -def test_invalid_incorrect_transaction_length_32_bytes(spec, state): +def test_incorrect_transaction_length_32_bytes(spec, state): + """ + The versioned hashes are wrong, but the testing ExecutionEngine returns VALID by default. + """ yield 'pre', state block = build_empty_block_for_next_slot(spec, state) @@ -96,15 +105,18 @@ def test_invalid_incorrect_transaction_length_32_bytes(spec, state): opaque_tx = opaque_tx + b'\x12' * 32 # incorrect tx length block.body.execution_payload.transactions = [opaque_tx] block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True) + signed_block = state_transition_and_sign_block(spec, state, block) yield 'blocks', [signed_block] - yield 'post', None + yield 'post', state @with_deneb_and_later @spec_state_test -def test_invalid_incorrect_commitment(spec, state): +def test_incorrect_commitment(spec, state): + """ + The versioned hashes are wrong, but the testing ExecutionEngine returns VALID by default. + """ yield 'pre', state block = build_empty_block_for_next_slot(spec, state) @@ -113,15 +125,18 @@ def test_invalid_incorrect_commitment(spec, state): block.body.blob_kzg_commitments = blob_kzg_commitments block.body.execution_payload.transactions = [opaque_tx] block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True) + signed_block = state_transition_and_sign_block(spec, state, block) yield 'blocks', [signed_block] - yield 'post', None + yield 'post', state @with_deneb_and_later @spec_state_test -def test_invalid_incorrect_commitments_order(spec, state): +def test_incorrect_commitments_order(spec, state): + """ + The versioned hashes are wrong, but the testing ExecutionEngine returns VALID by default. + """ yield 'pre', state block = build_empty_block_for_next_slot(spec, state) @@ -129,10 +144,10 @@ def test_invalid_incorrect_commitments_order(spec, state): block.body.blob_kzg_commitments = [blob_kzg_commitments[1], blob_kzg_commitments[0]] # incorrect order block.body.execution_payload.transactions = [opaque_tx] block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - signed_block = state_transition_and_sign_block(spec, state, block, expect_fail=True) + signed_block = state_transition_and_sign_block(spec, state, block) yield 'blocks', [signed_block] - yield 'post', None + yield 'post', state @with_deneb_and_later diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/test_offset.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/test_offset.py deleted file mode 100644 index 3c3b51ff1a..0000000000 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/test_offset.py +++ /dev/null @@ -1,23 +0,0 @@ - -from eth2spec.test.helpers.constants import ( - DENEB, - MINIMAL, -) -from eth2spec.test.helpers.sharding import ( - get_sample_opaque_tx, -) -from eth2spec.test.context import ( - with_phases, - spec_state_test, - with_presets, -) - - -@with_phases([DENEB]) -@spec_state_test -@with_presets([MINIMAL]) -def test_tx_peek_blob_versioned_hashes(spec, state): - otx, _, commitments, _ = get_sample_opaque_tx(spec) - data_hashes = spec.tx_peek_blob_versioned_hashes(otx) - expected = [spec.kzg_commitment_to_versioned_hash(blob_commitment) for blob_commitment in commitments] - assert expected == data_hashes diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py index 07039ccfeb..62b8e9dc1d 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py @@ -31,8 +31,7 @@ def test_validate_blobs_and_kzg_commitments(spec, state): block.body.execution_payload.transactions = [opaque_tx] block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - spec.validate_blobs_and_kzg_commitments(block.body.execution_payload, - blobs, + spec.validate_blobs_and_kzg_commitments(blobs, blob_kzg_commitments, proofs) @@ -52,7 +51,6 @@ def test_validate_blobs_and_kzg_commitments_missing_blob(spec, state): expect_assertion_error( lambda: spec.validate_blobs_and_kzg_commitments( - block.body.execution_payload, blobs[:-1], blob_kzg_commitments, proofs @@ -75,7 +73,6 @@ def test_validate_blobs_and_kzg_commitments_missing_proof(spec, state): expect_assertion_error( lambda: spec.validate_blobs_and_kzg_commitments( - block.body.execution_payload, blobs, blob_kzg_commitments, proofs[:-1] @@ -100,7 +97,6 @@ def test_validate_blobs_and_kzg_commitments_incorrect_blob(spec, state): expect_assertion_error( lambda: spec.validate_blobs_and_kzg_commitments( - block.body.execution_payload, blobs, blob_kzg_commitments, proofs diff --git a/tests/formats/operations/README.md b/tests/formats/operations/README.md index 245ce85653..ba764879bd 100644 --- a/tests/formats/operations/README.md +++ b/tests/formats/operations/README.md @@ -42,7 +42,7 @@ Operations: | `proposer_slashing` | `ProposerSlashing` | `proposer_slashing` | `process_proposer_slashing(state, proposer_slashing)` | | `voluntary_exit` | `SignedVoluntaryExit` | `voluntary_exit` | `process_voluntary_exit(state, voluntary_exit)` | | `sync_aggregate` | `SyncAggregate` | `sync_aggregate` | `process_sync_aggregate(state, sync_aggregate)` (new in Altair) | -| `execution_payload` | `ExecutionPayload` | `execution_payload` | `process_execution_payload(state, execution_payload)` (new in Bellatrix) | +| `execution_payload` | `BeaconBlockBody` | **`body`** | `process_execution_payload(state, body)` (new in Bellatrix) | | `withdrawals` | `ExecutionPayload` | `execution_payload` | `process_withdrawals(state, execution_payload)` (new in Capella) | | `bls_to_execution_change` | `SignedBLSToExecutionChange` | `address_change` | `process_bls_to_execution_change(state, address_change)` (new in Capella) | | `deposit_receipt` | `DepositReceipt` | `deposit_receipt` | `process_deposit_receipt(state, deposit_receipt)` (new in EIP6110) | From 3247bcf8f721cef4b88deac6b3e22c18fbf815eb Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 23 May 2023 15:16:12 +0800 Subject: [PATCH 03/13] PR feedback from @ppopth --- specs/bellatrix/beacon-chain.md | 2 +- specs/deneb/beacon-chain.md | 17 +---------------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/specs/bellatrix/beacon-chain.md b/specs/bellatrix/beacon-chain.md index a4c7be16e9..9df07d69f4 100644 --- a/specs/bellatrix/beacon-chain.md +++ b/specs/bellatrix/beacon-chain.md @@ -330,7 +330,7 @@ The Engine API may be used to implement this and similarly defined functions via ```python def notify_new_payload(self: ExecutionEngine, new_payload_request: NewPayloadRequest) -> bool: """ - Return ``True`` if and only if ``execution_payload`` is valid with respect to ``self.execution_state``. + Return ``True`` if and only if ``new_payload_request`` is valid with respect to ``self.execution_state``. """ ... ``` diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index 2c4bb61335..20455ab0de 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -30,7 +30,6 @@ - [Modified `NewPayloadRequest`](#modified-newpayloadrequest) - [Engine APIs](#engine-apis) - [Modified `notify_new_payload`](#modified-notify_new_payload) - - [Block processing](#block-processing) - [Execution payload](#execution-payload) - [`process_execution_payload`](#process_execution_payload) - [Testing](#testing) @@ -182,25 +181,11 @@ class NewPayloadRequest(object): ```python def notify_new_payload(self: ExecutionEngine, new_payload_request: NewPayloadRequest) -> bool: """ - Return ``True`` if and only if ``execution_payload`` is valid with respect to ``self.execution_state``. + Return ``True`` if and only if ``new_payload_request`` is valid with respect to ``self.execution_state``. """ ... ``` -### Block processing - -```python -def process_block(state: BeaconState, block: BeaconBlock) -> None: - process_block_header(state, block) - if is_execution_enabled(state, block.body): - process_withdrawals(state, block.body.execution_payload) - process_execution_payload(state, block.body, EXECUTION_ENGINE) # [Modified in Deneb] - process_randao(state, block.body) - process_eth1_data(state, block.body) - process_operations(state, block.body) - process_sync_aggregate(state, block.body.sync_aggregate) -``` - #### Execution payload ##### `process_execution_payload` From fc45220a7d1ee5bc75be4c5a0a834512f812f8f1 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 23 May 2023 15:38:54 +0800 Subject: [PATCH 04/13] Move old Deneb sanity tests to block_processing (operations) tests --- .../test/deneb/block_processing/__init__.py | 0 .../test_process_execution_payload.py | 175 ++++++++++++++++++ .../eth2spec/test/deneb/sanity/test_blocks.py | 138 -------------- tests/generators/operations/main.py | 5 +- 4 files changed, 179 insertions(+), 139 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/deneb/block_processing/__init__.py create mode 100644 tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_execution_payload.py diff --git a/tests/core/pyspec/eth2spec/test/deneb/block_processing/__init__.py b/tests/core/pyspec/eth2spec/test/deneb/block_processing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_execution_payload.py new file mode 100644 index 0000000000..68da478b76 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_execution_payload.py @@ -0,0 +1,175 @@ +from random import Random + +from eth2spec.test.helpers.execution_payload import ( + build_empty_execution_payload, + compute_el_block_hash, + get_execution_payload_header, +) +from eth2spec.test.context import ( + spec_state_test, + expect_assertion_error, + with_deneb_and_later +) +from eth2spec.test.helpers.sharding import ( + get_sample_opaque_tx, +) + + +def run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments, + valid=True, execution_valid=True): + """ + Run ``process_execution_payload``, yielding: + - pre-state ('pre') + - execution payload ('execution_payload') + - execution details, to mock EVM execution ('execution.yml', a dict with 'execution_valid' key and boolean value) + - post-state ('post'). + If ``valid == False``, run expecting ``AssertionError`` + """ + # Before Deneb, only `body.execution_payload` matters. `BeaconBlockBody` is just a wrapper. + body = spec.BeaconBlockBody( + blob_kzg_commitments=blob_kzg_commitments, + execution_payload=execution_payload + ) + + yield 'pre', state + yield 'execution', {'execution_valid': execution_valid} + yield 'body', body + + called_new_block = False + + class TestEngine(spec.NoopExecutionEngine): + def notify_new_payload(self, new_payload_request) -> bool: + nonlocal called_new_block, execution_valid + called_new_block = True + assert new_payload_request.execution_payload == body.execution_payload + return execution_valid + + if not valid: + expect_assertion_error(lambda: spec.process_execution_payload(state, body, TestEngine())) + yield 'post', None + return + + spec.process_execution_payload(state, body, TestEngine()) + + # Make sure we called the engine + assert called_new_block + + yield 'post', state + + assert state.latest_execution_payload_header == get_execution_payload_header(spec, body.execution_payload) + + +@with_deneb_and_later +@spec_state_test +def test_incorrect_blob_tx_type(spec, state): + """ + The versioned hashes are wrong, but the testing ExecutionEngine returns VALID by default. + """ + execution_payload = build_empty_execution_payload(spec, state) + + opaque_tx, _, blob_kzg_commitments, _ = get_sample_opaque_tx(spec) + opaque_tx = b'\x04' + opaque_tx[1:] # incorrect tx type + + execution_payload.transactions = [opaque_tx] + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) + + yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) + + +@with_deneb_and_later +@spec_state_test +def test_incorrect_transaction_length_1_byte(spec, state): + """ + The versioned hashes are wrong, but the testing ExecutionEngine returns VALID by default. + """ + execution_payload = build_empty_execution_payload(spec, state) + + opaque_tx, _, blob_kzg_commitments, _ = get_sample_opaque_tx(spec) + opaque_tx = opaque_tx + b'\x12' # incorrect tx length + + execution_payload.transactions = [opaque_tx] + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) + + yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) + + +@with_deneb_and_later +@spec_state_test +def test_incorrect_transaction_length_32_bytes(spec, state): + """ + The versioned hashes are wrong, but the testing ExecutionEngine returns VALID by default. + """ + execution_payload = build_empty_execution_payload(spec, state) + + opaque_tx, _, blob_kzg_commitments, _ = get_sample_opaque_tx(spec) + opaque_tx = opaque_tx + b'\x12' * 32 # incorrect tx length + + execution_payload.transactions = [opaque_tx] + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) + + yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) + + +@with_deneb_and_later +@spec_state_test +def test_incorrect_commitment(spec, state): + """ + The versioned hashes are wrong, but the testing ExecutionEngine returns VALID by default. + """ + execution_payload = build_empty_execution_payload(spec, state) + + opaque_tx, _, blob_kzg_commitments, _ = get_sample_opaque_tx(spec) + blob_kzg_commitments[0] = b'\x12' * 48 # incorrect commitment + + execution_payload.transactions = [opaque_tx] + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) + + yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) + + +@with_deneb_and_later +@spec_state_test +def test_incorrect_commitments_order(spec, state): + """ + The versioned hashes are wrong, but the testing ExecutionEngine returns VALID by default. + """ + execution_payload = build_empty_execution_payload(spec, state) + + opaque_tx, _, blob_kzg_commitments, _ = get_sample_opaque_tx(spec, blob_count=2, rng=Random(1111)) + blob_kzg_commitments = [blob_kzg_commitments[1], blob_kzg_commitments[0]] # incorrect order + + execution_payload.transactions = [opaque_tx] + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) + + yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) + + +@with_deneb_and_later +@spec_state_test +def test_incorrect_block_hash(spec, state): + execution_payload = build_empty_execution_payload(spec, state) + + opaque_tx, _, blob_kzg_commitments, _ = get_sample_opaque_tx(spec) + + execution_payload.transactions = [opaque_tx] + execution_payload.block_hash = b'\x12' * 32 # incorrect block hash + + # CL itself doesn't verify EL block hash + yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) + + +@with_deneb_and_later +@spec_state_test +def test_zeroed_commitment(spec, state): + """ + The blob is invalid, but the commitment is in correct form. + """ + execution_payload = build_empty_execution_payload(spec, state) + + opaque_tx, _, blob_kzg_commitments, _ = get_sample_opaque_tx(spec, blob_count=1, is_valid_blob=False) + assert all(commitment == b'\x00' * 48 for commitment in blob_kzg_commitments) + + execution_payload.transactions = [opaque_tx] + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) + + yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) diff --git a/tests/core/pyspec/eth2spec/test/deneb/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/deneb/sanity/test_blocks.py index f65514f23d..36caacd3fa 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/deneb/sanity/test_blocks.py @@ -1,5 +1,3 @@ -import random - from eth2spec.test.helpers.state import ( state_transition_and_sign_block ) @@ -49,139 +47,3 @@ def test_one_blob(spec, state): @spec_state_test def test_max_blobs(spec, state): yield from run_block_with_blobs(spec, state, blob_count=spec.MAX_BLOBS_PER_BLOCK) - - -@with_deneb_and_later -@spec_state_test -def test_incorrect_blob_tx_type(spec, state): - """ - The versioned hashes are wrong, but the testing ExecutionEngine returns VALID by default. - """ - yield 'pre', state - - block = build_empty_block_for_next_slot(spec, state) - opaque_tx, _, blob_kzg_commitments, _ = get_sample_opaque_tx(spec) - block.body.blob_kzg_commitments = blob_kzg_commitments - opaque_tx = b'\x04' + opaque_tx[1:] # incorrect tx type - block.body.execution_payload.transactions = [opaque_tx] - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - signed_block = state_transition_and_sign_block(spec, state, block) - - yield 'blocks', [signed_block] - yield 'post', state - - -@with_deneb_and_later -@spec_state_test -def test_incorrect_transaction_length_1_byte(spec, state): - """ - The versioned hashes are wrong, but the testing ExecutionEngine returns VALID by default. - """ - yield 'pre', state - - block = build_empty_block_for_next_slot(spec, state) - opaque_tx, _, blob_kzg_commitments, _ = get_sample_opaque_tx(spec) - block.body.blob_kzg_commitments = blob_kzg_commitments - opaque_tx = opaque_tx + b'\x12' # incorrect tx length - block.body.execution_payload.transactions = [opaque_tx] - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - signed_block = state_transition_and_sign_block(spec, state, block) - - yield 'blocks', [signed_block] - yield 'post', state - - -@with_deneb_and_later -@spec_state_test -def test_incorrect_transaction_length_32_bytes(spec, state): - """ - The versioned hashes are wrong, but the testing ExecutionEngine returns VALID by default. - """ - yield 'pre', state - - block = build_empty_block_for_next_slot(spec, state) - opaque_tx, _, blob_kzg_commitments, _ = get_sample_opaque_tx(spec) - block.body.blob_kzg_commitments = blob_kzg_commitments - opaque_tx = opaque_tx + b'\x12' * 32 # incorrect tx length - block.body.execution_payload.transactions = [opaque_tx] - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - signed_block = state_transition_and_sign_block(spec, state, block) - - yield 'blocks', [signed_block] - yield 'post', state - - -@with_deneb_and_later -@spec_state_test -def test_incorrect_commitment(spec, state): - """ - The versioned hashes are wrong, but the testing ExecutionEngine returns VALID by default. - """ - yield 'pre', state - - block = build_empty_block_for_next_slot(spec, state) - opaque_tx, _, blob_kzg_commitments, _ = get_sample_opaque_tx(spec) - blob_kzg_commitments[0] = b'\x12' * 48 # incorrect commitment - block.body.blob_kzg_commitments = blob_kzg_commitments - block.body.execution_payload.transactions = [opaque_tx] - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - signed_block = state_transition_and_sign_block(spec, state, block) - - yield 'blocks', [signed_block] - yield 'post', state - - -@with_deneb_and_later -@spec_state_test -def test_incorrect_commitments_order(spec, state): - """ - The versioned hashes are wrong, but the testing ExecutionEngine returns VALID by default. - """ - yield 'pre', state - - block = build_empty_block_for_next_slot(spec, state) - opaque_tx, _, blob_kzg_commitments, _ = get_sample_opaque_tx(spec, blob_count=2, rng=random.Random(1111)) - block.body.blob_kzg_commitments = [blob_kzg_commitments[1], blob_kzg_commitments[0]] # incorrect order - block.body.execution_payload.transactions = [opaque_tx] - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - signed_block = state_transition_and_sign_block(spec, state, block) - - yield 'blocks', [signed_block] - yield 'post', state - - -@with_deneb_and_later -@spec_state_test -def test_incorrect_block_hash(spec, state): - yield 'pre', state - - block = build_empty_block_for_next_slot(spec, state) - opaque_tx, _, blob_kzg_commitments, _ = get_sample_opaque_tx(spec) - block.body.blob_kzg_commitments = blob_kzg_commitments - block.body.execution_payload.transactions = [opaque_tx] - block.body.execution_payload.block_hash = b'\x12' * 32 # incorrect block hash - # CL itself doesn't verify EL block hash - signed_block = state_transition_and_sign_block(spec, state, block) - - yield 'blocks', [signed_block] - yield 'post', state - - -@with_deneb_and_later -@spec_state_test -def test_zeroed_commitment(spec, state): - """ - The blob is invalid, but the commitment is in correct form. - """ - yield 'pre', state - - block = build_empty_block_for_next_slot(spec, state) - opaque_tx, _, blob_kzg_commitments, _ = get_sample_opaque_tx(spec, blob_count=1, is_valid_blob=False) - assert all(commitment == b'\x00' * 48 for commitment in blob_kzg_commitments) - block.body.blob_kzg_commitments = blob_kzg_commitments - block.body.execution_payload.transactions = [opaque_tx] - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - signed_block = state_transition_and_sign_block(spec, state, block) - - yield 'blocks', [signed_block] - yield 'post', state diff --git a/tests/generators/operations/main.py b/tests/generators/operations/main.py index fc22179176..293bcdea7d 100644 --- a/tests/generators/operations/main.py +++ b/tests/generators/operations/main.py @@ -36,7 +36,10 @@ ]} capella_mods = combine_mods(_new_capella_mods, bellatrix_mods) - deneb_mods = capella_mods + _new_deneb_mods = {key: 'eth2spec.test.deneb.block_processing.test_process_' + key for key in [ + 'execution_payload', + ]} + deneb_mods = combine_mods(_new_deneb_mods, capella_mods) _new_eip6110_mods = {key: 'eth2spec.test.eip6110.block_processing.test_process_' + key for key in [ 'deposit_receipt', From bee8fa16e567cec88f68372a3768e4fec476a6b2 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 23 May 2023 18:34:00 +0800 Subject: [PATCH 05/13] Add test_invalid_correct_input__execution_invalid --- .../test_process_execution_payload.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_execution_payload.py index 68da478b76..d3a04ddfec 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_execution_payload.py @@ -173,3 +173,20 @@ def test_zeroed_commitment(spec, state): execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments) + + +@with_deneb_and_later +@spec_state_test +def test_invalid_correct_input__execution_invalid(spec, state): + """ + The versioned hashes are wrong, but the testing ExecutionEngine returns VALID by default. + """ + execution_payload = build_empty_execution_payload(spec, state) + + opaque_tx, _, blob_kzg_commitments, _ = get_sample_opaque_tx(spec) + + execution_payload.transactions = [opaque_tx] + execution_payload.block_hash = compute_el_block_hash(spec, execution_payload) + + yield from run_execution_payload_processing(spec, state, execution_payload, blob_kzg_commitments, + valid=False, execution_valid=False) From 73df1935b1dbc09036f793e2759e3c4f794fc04b Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 23 May 2023 22:25:43 +0800 Subject: [PATCH 06/13] Use `verify_and_notify_new_payload` approach --- setup.py | 58 ++++++++++++++++++- specs/_features/eip6110/beacon-chain.md | 2 +- specs/bellatrix/beacon-chain.md | 6 +- specs/capella/beacon-chain.md | 2 +- specs/deneb/beacon-chain.md | 40 +++++++++++-- .../test_process_execution_payload.py | 8 ++- .../test_process_execution_payload.py | 2 +- 7 files changed, 104 insertions(+), 14 deletions(-) diff --git a/setup.py b/setup.py index e8bbeefbb9..609d2acf34 100644 --- a/setup.py +++ b/setup.py @@ -336,6 +336,10 @@ def sundry_functions(cls) -> str: """ raise NotImplementedError() + @classmethod + def execution_engine_cls(cls) -> str: + raise NotImplementedError() + @classmethod @abstractmethod def hardcoded_ssz_dep_constants(cls) -> Dict[str, str]: @@ -469,6 +473,12 @@ def wrapper(*args, **kw): # type: ignore ), _get_attesting_indices, lru_size=SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT * 3)''' + + @classmethod + def execution_engine_cls(cls) -> str: + return "" + + @classmethod def hardcoded_ssz_dep_constants(cls) -> Dict[str, str]: return {} @@ -573,12 +583,14 @@ def get_execution_state(_execution_state_root: Bytes32) -> ExecutionState: def get_pow_chain_head() -> PowBlock: - pass - + pass""" + @classmethod + def execution_engine_cls(cls) -> str: + return "\n\n" + """ class NoopExecutionEngine(ExecutionEngine): - def notify_new_payload(self: ExecutionEngine, new_payload_request: NewPayloadRequest) -> bool: + def notify_new_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: return True def notify_forkchoice_updated(self: ExecutionEngine, @@ -658,6 +670,39 @@ def retrieve_blobs_and_proofs(beacon_block_root: Root) -> PyUnion[Tuple[Blob, KZ # pylint: disable=unused-argument return ("TEST", "TEST")''' + @classmethod + def execution_engine_cls(cls) -> str: + return "\n\n" + """ +class NoopExecutionEngine(ExecutionEngine): + + def notify_new_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: + return True + + def notify_forkchoice_updated(self: ExecutionEngine, + head_block_hash: Hash32, + safe_block_hash: Hash32, + finalized_block_hash: Hash32, + payload_attributes: Optional[PayloadAttributes]) -> Optional[PayloadId]: + pass + + def get_payload(self: ExecutionEngine, payload_id: PayloadId) -> GetPayloadResponse: + # pylint: disable=unused-argument + raise NotImplementedError("no default block production") + + def is_valid_block_hash(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: + return True + + def is_valid_versioned_hashes(self: ExecutionEngine, new_payload_request: NewPayloadRequest) -> bool: + return True + + def verify_and_notify_new_payload(self: ExecutionEngine, + new_payload_request: NewPayloadRequest) -> bool: + return True + + +EXECUTION_ENGINE = NoopExecutionEngine()""" + + @classmethod def hardcoded_custom_type_dep_constants(cls, spec_object) -> str: constants = { @@ -708,6 +753,12 @@ def objects_to_spec(preset_name: str, ) def format_protocol(protocol_name: str, protocol_def: ProtocolDefinition) -> str: + if "verify_and_notify_new_payload" in protocol_def.functions: + # del protocol_def.functions['verify_and_notify_new_payload'] + protocol_def.functions['verify_and_notify_new_payload'] = """def verify_and_notify_new_payload(self: ExecutionEngine, + new_payload_request: NewPayloadRequest) -> bool: + ...""" + protocol = f"class {protocol_name}(Protocol):" for fn_source in protocol_def.functions.values(): fn_source = fn_source.replace("self: "+protocol_name, "self") @@ -783,6 +834,7 @@ def format_constant(name: str, vardef: VariableDefinition) -> str: + ('\n\n\n' + protocols_spec if protocols_spec != '' else '') + '\n\n\n' + functions_spec + '\n\n' + builder.sundry_functions() + + builder.execution_engine_cls() # Since some constants are hardcoded in setup.py, the following assertions verify that the hardcoded constants are # as same as the spec definition. + ('\n\n\n' + ssz_dep_constants_verification if ssz_dep_constants_verification != '' else '') diff --git a/specs/_features/eip6110/beacon-chain.md b/specs/_features/eip6110/beacon-chain.md index 591d8dc869..084f88f145 100644 --- a/specs/_features/eip6110/beacon-chain.md +++ b/specs/_features/eip6110/beacon-chain.md @@ -249,7 +249,7 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) # Verify the execution payload is valid versioned_hashes = [kzg_commitment_to_versioned_hash(commitment) for commitment in body.blob_kzg_commitments] - assert execution_engine.notify_new_payload( + assert execution_engine.verify_and_notify_new_payload( NewPayloadRequest(execution_payload=payload, versioned_hashes=versioned_hashes) ) # Cache execution payload header diff --git a/specs/bellatrix/beacon-chain.md b/specs/bellatrix/beacon-chain.md index 9df07d69f4..9ae1e5a711 100644 --- a/specs/bellatrix/beacon-chain.md +++ b/specs/bellatrix/beacon-chain.md @@ -328,9 +328,9 @@ The Engine API may be used to implement this and similarly defined functions via #### `notify_new_payload` ```python -def notify_new_payload(self: ExecutionEngine, new_payload_request: NewPayloadRequest) -> bool: +def notify_new_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: """ - Return ``True`` if and only if ``new_payload_request`` is valid with respect to ``self.execution_state``. + Return ``True`` if and only if ``execution_payload`` is valid with respect to ``self.execution_state``. """ ... ``` @@ -366,7 +366,7 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi # Verify timestamp assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) # Verify the execution payload is valid - assert execution_engine.notify_new_payload(NewPayloadRequest(execution_payload=payload)) + assert execution_engine.notify_new_payload(payload) # Cache execution payload header state.latest_execution_payload_header = ExecutionPayloadHeader( parent_hash=payload.parent_hash, diff --git a/specs/capella/beacon-chain.md b/specs/capella/beacon-chain.md index 1799f4e6e3..c8e969d251 100644 --- a/specs/capella/beacon-chain.md +++ b/specs/capella/beacon-chain.md @@ -418,7 +418,7 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi # Verify timestamp assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) # Verify the execution payload is valid - assert execution_engine.notify_new_payload(NewPayloadRequest(execution_payload=payload)) + assert execution_engine.notify_new_payload(payload) # Cache execution payload header state.latest_execution_payload_header = ExecutionPayloadHeader( parent_hash=payload.parent_hash, diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index 20455ab0de..bed64db513 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -29,7 +29,9 @@ - [Request data](#request-data) - [Modified `NewPayloadRequest`](#modified-newpayloadrequest) - [Engine APIs](#engine-apis) - - [Modified `notify_new_payload`](#modified-notify_new_payload) + - [`is_valid_block_hash`](#is_valid_block_hash) + - [`is_valid_versioned_hashes`](#is_valid_versioned_hashes) + - [`verify_and_notify_new_payload`](#verify_and_notify_new_payload) - [Execution payload](#execution-payload) - [`process_execution_payload`](#process_execution_payload) - [Testing](#testing) @@ -176,14 +178,44 @@ class NewPayloadRequest(object): #### Engine APIs -#### Modified `notify_new_payload` +#### `is_valid_block_hash` + +[0]: # (eth2spec: skip) + +```python +def is_valid_block_hash(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: + """ + Return ``True`` if and only if ``execution_payload.block_hash`` is computed correctly. + """ + ... +``` + +#### `is_valid_versioned_hashes` + +[0]: # (eth2spec: skip) + +```python +def is_valid_versioned_hashes(self: ExecutionEngine, new_payload_request: NewPayloadRequest) -> bool: + """ + Return ``True`` if and only if the version hashes computed by the blob transactions of + ``new_payload_request.execution_payload`` matches ``new_payload_request.version_hashes``. + """ + ... +``` + +#### `verify_and_notify_new_payload` ```python -def notify_new_payload(self: ExecutionEngine, new_payload_request: NewPayloadRequest) -> bool: +def verify_and_notify_new_payload(self: ExecutionEngine, + new_payload_request: NewPayloadRequest) -> bool: """ Return ``True`` if and only if ``new_payload_request`` is valid with respect to ``self.execution_state``. """ + assert self.is_valid_block_hash(new_payload_request.execution_payload) + assert self.is_valid_versioned_hashes(new_payload_request) + assert self.notify_new_payload(new_payload_request.execution_payload) ... + return True ``` #### Execution payload @@ -204,7 +236,7 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi # Verify the execution payload is valid # [Modified in Deneb] versioned_hashes = [kzg_commitment_to_versioned_hash(commitment) for commitment in body.blob_kzg_commitments] - assert execution_engine.notify_new_payload( + assert execution_engine.verify_and_notify_new_payload( NewPayloadRequest(execution_payload=payload, versioned_hashes=versioned_hashes) ) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py index b835632354..b54afafc34 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py @@ -31,7 +31,13 @@ def run_execution_payload_processing(spec, state, execution_payload, valid=True, called_new_block = False class TestEngine(spec.NoopExecutionEngine): - def notify_new_payload(self, new_payload_request) -> bool: + def notify_new_payload(self, payload) -> bool: + nonlocal called_new_block, execution_valid + called_new_block = True + assert payload == execution_payload + return execution_valid + + def verify_and_notify_new_payload(self, new_payload_request) -> bool: nonlocal called_new_block, execution_valid called_new_block = True assert new_payload_request.execution_payload == body.execution_payload diff --git a/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_execution_payload.py index d3a04ddfec..df59385ab4 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/deneb/block_processing/test_process_execution_payload.py @@ -38,7 +38,7 @@ def run_execution_payload_processing(spec, state, execution_payload, blob_kzg_co called_new_block = False class TestEngine(spec.NoopExecutionEngine): - def notify_new_payload(self, new_payload_request) -> bool: + def verify_and_notify_new_payload(self, new_payload_request) -> bool: nonlocal called_new_block, execution_valid called_new_block = True assert new_payload_request.execution_payload == body.execution_payload From 7ec5efb10666af3ccff9a3a84cbe46ba6578ac1a Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 23 May 2023 23:55:35 +0800 Subject: [PATCH 07/13] Add `### Block processing` back --- specs/deneb/beacon-chain.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index 20455ab0de..548d493ccc 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -30,6 +30,7 @@ - [Modified `NewPayloadRequest`](#modified-newpayloadrequest) - [Engine APIs](#engine-apis) - [Modified `notify_new_payload`](#modified-notify_new_payload) + - [Block processing](#block-processing) - [Execution payload](#execution-payload) - [`process_execution_payload`](#process_execution_payload) - [Testing](#testing) @@ -186,6 +187,8 @@ def notify_new_payload(self: ExecutionEngine, new_payload_request: NewPayloadReq ... ``` +### Block processing + #### Execution payload ##### `process_execution_payload` From dd5e6f813f82cd20636dfb36950ec281a428d48f Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 24 May 2023 01:58:49 +0800 Subject: [PATCH 08/13] Add `make_function_abstract` to make it more general --- setup.py | 14 +++++++++----- specs/deneb/beacon-chain.md | 4 ---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/setup.py b/setup.py index 609d2acf34..22a05ee320 100644 --- a/setup.py +++ b/setup.py @@ -736,6 +736,11 @@ def is_byte_vector(value: str) -> bool: return value.startswith(('ByteVector')) +def make_function_abstract(protocol_def: ProtocolDefinition, key: str): + function = protocol_def.functions[key].split('"""') + protocol_def.functions[key] = function[0] + "..." + + def objects_to_spec(preset_name: str, spec_object: SpecObject, builder: SpecBuilder, @@ -753,11 +758,10 @@ def objects_to_spec(preset_name: str, ) def format_protocol(protocol_name: str, protocol_def: ProtocolDefinition) -> str: - if "verify_and_notify_new_payload" in protocol_def.functions: - # del protocol_def.functions['verify_and_notify_new_payload'] - protocol_def.functions['verify_and_notify_new_payload'] = """def verify_and_notify_new_payload(self: ExecutionEngine, - new_payload_request: NewPayloadRequest) -> bool: - ...""" + abstract_functions = ["verify_and_notify_new_payload"] + for key in protocol_def.functions.keys(): + if key in abstract_functions: + make_function_abstract(protocol_def, key) protocol = f"class {protocol_name}(Protocol):" for fn_source in protocol_def.functions.values(): diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index bed64db513..615af60d4d 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -180,8 +180,6 @@ class NewPayloadRequest(object): #### `is_valid_block_hash` -[0]: # (eth2spec: skip) - ```python def is_valid_block_hash(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: """ @@ -192,8 +190,6 @@ def is_valid_block_hash(self: ExecutionEngine, execution_payload: ExecutionPaylo #### `is_valid_versioned_hashes` -[0]: # (eth2spec: skip) - ```python def is_valid_versioned_hashes(self: ExecutionEngine, new_payload_request: NewPayloadRequest) -> bool: """ From 289d8147be38438fac7b196622e2a133b9f2adf9 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 24 May 2023 11:12:03 +0800 Subject: [PATCH 09/13] Move `verify_and_notify_new_payload` to Bellatrix --- setup.py | 7 ++++ specs/bellatrix/beacon-chain.md | 32 ++++++++++++++++--- specs/capella/beacon-chain.md | 2 +- specs/deneb/beacon-chain.md | 14 ++------ sync/optimistic.md | 4 +-- .../test_process_execution_payload.py | 6 ---- .../eth2spec/test/helpers/optimistic_sync.py | 2 +- 7 files changed, 41 insertions(+), 26 deletions(-) diff --git a/setup.py b/setup.py index 22a05ee320..12a561514e 100644 --- a/setup.py +++ b/setup.py @@ -604,6 +604,13 @@ def get_payload(self: ExecutionEngine, payload_id: PayloadId) -> GetPayloadRespo # pylint: disable=unused-argument raise NotImplementedError("no default block production") + def is_valid_block_hash(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: + return True + + def verify_and_notify_new_payload(self: ExecutionEngine, + new_payload_request: NewPayloadRequest) -> bool: + return True + EXECUTION_ENGINE = NoopExecutionEngine()""" diff --git a/specs/bellatrix/beacon-chain.md b/specs/bellatrix/beacon-chain.md index 9ae1e5a711..e8eef346d2 100644 --- a/specs/bellatrix/beacon-chain.md +++ b/specs/bellatrix/beacon-chain.md @@ -320,13 +320,13 @@ The implementation-dependent `ExecutionEngine` protocol encapsulates the executi * a state object `self.execution_state` of type `ExecutionState` * a notification function `self.notify_new_payload` which may apply changes to the `self.execution_state` -*Note*: `notify_new_payload` is a function accessed through the `EXECUTION_ENGINE` module which instantiates the `ExecutionEngine` protocol. - -The body of this function is implementation dependent. +The body of these functions are implementation dependent. The Engine API may be used to implement this and similarly defined functions via an external execution engine. #### `notify_new_payload` +`notify_new_payload` is a function accessed through the `EXECUTION_ENGINE` module which instantiates the `ExecutionEngine` protocol. + ```python def notify_new_payload(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: """ @@ -335,6 +335,30 @@ def notify_new_payload(self: ExecutionEngine, execution_payload: ExecutionPayloa ... ``` +#### `is_valid_block_hash` + +```python +def is_valid_block_hash(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: + """ + Return ``True`` if and only if ``execution_payload.block_hash`` is computed correctly. + """ + ... +``` + +#### `verify_and_notify_new_payload` + +```python +def verify_and_notify_new_payload(self: ExecutionEngine, + new_payload_request: NewPayloadRequest) -> bool: + """ + Return ``True`` if and only if ``new_payload_request`` is valid with respect to ``self.execution_state``. + """ + assert self.is_valid_block_hash(new_payload_request.execution_payload) + assert self.notify_new_payload(new_payload_request.execution_payload) + ... + return True +``` + ### Block processing *Note*: The call to the `process_execution_payload` must happen before the call to the `process_randao` as the former depends on the `randao_mix` computed with the reveal of the previous block. @@ -366,7 +390,7 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi # Verify timestamp assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) # Verify the execution payload is valid - assert execution_engine.notify_new_payload(payload) + assert execution_engine.verify_and_notify_new_payload(NewPayloadRequest(execution_payload=payload)) # Cache execution payload header state.latest_execution_payload_header = ExecutionPayloadHeader( parent_hash=payload.parent_hash, diff --git a/specs/capella/beacon-chain.md b/specs/capella/beacon-chain.md index c8e969d251..af40830176 100644 --- a/specs/capella/beacon-chain.md +++ b/specs/capella/beacon-chain.md @@ -418,7 +418,7 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi # Verify timestamp assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) # Verify the execution payload is valid - assert execution_engine.notify_new_payload(payload) + assert execution_engine.verify_and_notify_new_payload(NewPayloadRequest(execution_payload=payload)) # Cache execution payload header state.latest_execution_payload_header = ExecutionPayloadHeader( parent_hash=payload.parent_hash, diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index 517913a366..bffa6a4567 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -179,16 +179,6 @@ class NewPayloadRequest(object): #### Engine APIs -#### `is_valid_block_hash` - -```python -def is_valid_block_hash(self: ExecutionEngine, execution_payload: ExecutionPayload) -> bool: - """ - Return ``True`` if and only if ``execution_payload.block_hash`` is computed correctly. - """ - ... -``` - #### `is_valid_versioned_hashes` ```python @@ -200,7 +190,7 @@ def is_valid_versioned_hashes(self: ExecutionEngine, new_payload_request: NewPay ... ``` -#### `verify_and_notify_new_payload` +#### Modified `verify_and_notify_new_payload` ```python def verify_and_notify_new_payload(self: ExecutionEngine, @@ -209,7 +199,7 @@ def verify_and_notify_new_payload(self: ExecutionEngine, Return ``True`` if and only if ``new_payload_request`` is valid with respect to ``self.execution_state``. """ assert self.is_valid_block_hash(new_payload_request.execution_payload) - assert self.is_valid_versioned_hashes(new_payload_request) + assert self.is_valid_versioned_hashes(new_payload_request) # [Modified in Deneb] assert self.notify_new_payload(new_payload_request.execution_payload) ... return True diff --git a/sync/optimistic.md b/sync/optimistic.md index 14eb99fb11..1fd1ba46e6 100644 --- a/sync/optimistic.md +++ b/sync/optimistic.md @@ -159,7 +159,7 @@ these conditions.* To optimistically import a block: -- The [`notify_new_payload`](../specs/bellatrix/beacon-chain.md#notify_new_payload) function MUST return `True` if the execution +- The [`verify_and_notify_new_payload`](../specs/bellatrix/beacon-chain.md#verify_and_notify_new_payload) function MUST return `True` if the execution engine returns `NOT_VALIDATED` or `VALID`. An `INVALIDATED` response MUST return `False`. - The [`validate_merge_block`](../specs/bellatrix/fork-choice.md#validate_merge_block) function MUST NOT raise an assertion if both the @@ -172,7 +172,7 @@ In addition to this change in validation, the consensus engine MUST track which blocks returned `NOT_VALIDATED` and which returned `VALID` for subsequent processing. Optimistically imported blocks MUST pass all verifications included in -`process_block` (withstanding the modifications to `notify_new_payload`). +`process_block` (withstanding the modifications to `verify_and_notify_new_payload`). A consensus engine MUST be able to retrospectively (i.e., after import) modify the status of `NOT_VALIDATED` blocks to be either `VALID` or `INVALIDATED` based upon responses diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py index b54afafc34..1aa4a2c37e 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/block_processing/test_process_execution_payload.py @@ -31,12 +31,6 @@ def run_execution_payload_processing(spec, state, execution_payload, valid=True, called_new_block = False class TestEngine(spec.NoopExecutionEngine): - def notify_new_payload(self, payload) -> bool: - nonlocal called_new_block, execution_valid - called_new_block = True - assert payload == execution_payload - return execution_valid - def verify_and_notify_new_payload(self, new_payload_request) -> bool: nonlocal called_new_block, execution_valid called_new_block = True diff --git a/tests/core/pyspec/eth2spec/test/helpers/optimistic_sync.py b/tests/core/pyspec/eth2spec/test/helpers/optimistic_sync.py index 816c7a10b7..ad23dbebc9 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/optimistic_sync.py +++ b/tests/core/pyspec/eth2spec/test/helpers/optimistic_sync.py @@ -91,7 +91,7 @@ def add_optimistic_block(spec, mega_store, signed_block, test_steps, Add a block with optimistic sync logic ``valid`` indicates if the given ``signed_block.message.body.execution_payload`` is valid/invalid - from ``notify_new_payload`` method response. + from ``verify_and_notify_new_payload`` method response. """ block = signed_block.message block_root = block.hash_tree_root() From 212a314287568a1414f389569a0bda0a8343354f Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 24 May 2023 11:27:26 +0800 Subject: [PATCH 10/13] Fix ToC and remove `validate_blobs_and_kzg_commitments` --- specs/bellatrix/beacon-chain.md | 2 + specs/deneb/beacon-chain.md | 3 +- specs/deneb/validator.md | 13 +-- .../unittests/validator/test_validator.py | 87 ------------------- 4 files changed, 4 insertions(+), 101 deletions(-) diff --git a/specs/bellatrix/beacon-chain.md b/specs/bellatrix/beacon-chain.md index e8eef346d2..6cc69d747f 100644 --- a/specs/bellatrix/beacon-chain.md +++ b/specs/bellatrix/beacon-chain.md @@ -37,6 +37,8 @@ - [`NewPayloadRequest`](#newpayloadrequest) - [Engine APIs](#engine-apis) - [`notify_new_payload`](#notify_new_payload) + - [`is_valid_block_hash`](#is_valid_block_hash) + - [`verify_and_notify_new_payload`](#verify_and_notify_new_payload) - [Block processing](#block-processing) - [Execution payload](#execution-payload) - [`process_execution_payload`](#process_execution_payload) diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index 5c31f1fc0f..85c1fbdceb 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -29,9 +29,8 @@ - [Request data](#request-data) - [Modified `NewPayloadRequest`](#modified-newpayloadrequest) - [Engine APIs](#engine-apis) - - [`is_valid_block_hash`](#is_valid_block_hash) - [`is_valid_versioned_hashes`](#is_valid_versioned_hashes) - - [`verify_and_notify_new_payload`](#verify_and_notify_new_payload) + - [Modified `verify_and_notify_new_payload`](#modified-verify_and_notify_new_payload) - [Block processing](#block-processing) - [Execution payload](#execution-payload) - [`process_execution_payload`](#process_execution_payload) diff --git a/specs/deneb/validator.md b/specs/deneb/validator.md index fa6f1651e4..ae5f266737 100644 --- a/specs/deneb/validator.md +++ b/specs/deneb/validator.md @@ -101,18 +101,7 @@ All validator responsibilities remain unchanged other than those noted below. 1. After retrieving the execution payload from the execution engine as specified in Capella, use the `payload_id` to retrieve `blobs`, `blob_kzg_commitments`, and `blob_kzg_proofs` via `get_payload(payload_id).blobs_bundle`. -2. Validate `blobs` and `blob_kzg_commitments`: - -```python -def validate_blobs_and_kzg_commitments(blobs: Sequence[Blob], - blob_kzg_commitments: Sequence[KZGCommitment], - blob_kzg_proofs: Sequence[KZGProof]) -> None: - # Optionally sanity-check that the KZG commitments match the blobs (as produced by the execution engine) - assert len(blob_kzg_commitments) == len(blobs) == len(blob_kzg_proofs) - assert verify_blob_kzg_proof_batch(blobs, blob_kzg_commitments, blob_kzg_proofs) -``` - -3. If valid, set `block.body.blob_kzg_commitments = blob_kzg_commitments`. +2. Set `block.body.blob_kzg_commitments = blob_kzg_commitments`. #### Constructing the `SignedBlobSidecar`s diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py index 62b8e9dc1d..876824107a 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/validator/test_validator.py @@ -2,7 +2,6 @@ always_bls, spec_state_test, with_deneb_and_later, - expect_assertion_error ) from eth2spec.test.helpers.execution_payload import ( compute_el_block_hash, @@ -18,92 +17,6 @@ ) -@with_deneb_and_later -@spec_state_test -def test_validate_blobs_and_kzg_commitments(spec, state): - """ - Test `validate_blobs_and_kzg_commitments` - """ - blob_count = 4 - block = build_empty_block_for_next_slot(spec, state) - opaque_tx, blobs, blob_kzg_commitments, proofs = get_sample_opaque_tx(spec, blob_count=blob_count) - block.body.blob_kzg_commitments = blob_kzg_commitments - block.body.execution_payload.transactions = [opaque_tx] - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - - spec.validate_blobs_and_kzg_commitments(blobs, - blob_kzg_commitments, - proofs) - - -@with_deneb_and_later -@spec_state_test -def test_validate_blobs_and_kzg_commitments_missing_blob(spec, state): - """ - Test `validate_blobs_and_kzg_commitments` - """ - blob_count = 4 - block = build_empty_block_for_next_slot(spec, state) - opaque_tx, blobs, blob_kzg_commitments, proofs = get_sample_opaque_tx(spec, blob_count=blob_count) - block.body.blob_kzg_commitments = blob_kzg_commitments - block.body.execution_payload.transactions = [opaque_tx] - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - - expect_assertion_error( - lambda: spec.validate_blobs_and_kzg_commitments( - blobs[:-1], - blob_kzg_commitments, - proofs - ) - ) - - -@with_deneb_and_later -@spec_state_test -def test_validate_blobs_and_kzg_commitments_missing_proof(spec, state): - """ - Test `validate_blobs_and_kzg_commitments` - """ - blob_count = 4 - block = build_empty_block_for_next_slot(spec, state) - opaque_tx, blobs, blob_kzg_commitments, proofs = get_sample_opaque_tx(spec, blob_count=blob_count) - block.body.blob_kzg_commitments = blob_kzg_commitments - block.body.execution_payload.transactions = [opaque_tx] - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - - expect_assertion_error( - lambda: spec.validate_blobs_and_kzg_commitments( - blobs, - blob_kzg_commitments, - proofs[:-1] - ) - ) - - -@with_deneb_and_later -@spec_state_test -def test_validate_blobs_and_kzg_commitments_incorrect_blob(spec, state): - """ - Test `validate_blobs_and_kzg_commitments` - """ - blob_count = 4 - block = build_empty_block_for_next_slot(spec, state) - opaque_tx, blobs, blob_kzg_commitments, proofs = get_sample_opaque_tx(spec, blob_count=blob_count) - block.body.blob_kzg_commitments = blob_kzg_commitments - block.body.execution_payload.transactions = [opaque_tx] - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - - blobs[1] = spec.Blob(blobs[1][:13] + bytes([(blobs[1][13] + 1) % 256]) + blobs[1][14:]) - - expect_assertion_error( - lambda: spec.validate_blobs_and_kzg_commitments( - blobs, - blob_kzg_commitments, - proofs - ) - ) - - @with_deneb_and_later @spec_state_test def test_blob_sidecar_signature(spec, state): From 53a9221d1a67799549f55438a2b1ce5e533d3b5b Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 24 May 2023 11:55:22 +0800 Subject: [PATCH 11/13] Fix ToC --- specs/deneb/beacon-chain.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index 85c1fbdceb..1ab1df6116 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -29,8 +29,8 @@ - [Request data](#request-data) - [Modified `NewPayloadRequest`](#modified-newpayloadrequest) - [Engine APIs](#engine-apis) - - [`is_valid_versioned_hashes`](#is_valid_versioned_hashes) - - [Modified `verify_and_notify_new_payload`](#modified-verify_and_notify_new_payload) + - [`is_valid_versioned_hashes`](#is_valid_versioned_hashes) + - [Modified `verify_and_notify_new_payload`](#modified-verify_and_notify_new_payload) - [Block processing](#block-processing) - [Execution payload](#execution-payload) - [`process_execution_payload`](#process_execution_payload) @@ -178,7 +178,7 @@ class NewPayloadRequest(object): #### Engine APIs -#### `is_valid_versioned_hashes` +##### `is_valid_versioned_hashes` ```python def is_valid_versioned_hashes(self: ExecutionEngine, new_payload_request: NewPayloadRequest) -> bool: @@ -189,7 +189,7 @@ def is_valid_versioned_hashes(self: ExecutionEngine, new_payload_request: NewPay ... ``` -#### Modified `verify_and_notify_new_payload` +##### Modified `verify_and_notify_new_payload` ```python def verify_and_notify_new_payload(self: ExecutionEngine, From ec1ee74edb97112fdd0361a6d7c46d8a509684a6 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 24 May 2023 12:17:07 +0800 Subject: [PATCH 12/13] Fix typo Co-authored-by: Danny Ryan --- specs/deneb/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index 1ab1df6116..735dcf628c 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -198,7 +198,7 @@ def verify_and_notify_new_payload(self: ExecutionEngine, Return ``True`` if and only if ``new_payload_request`` is valid with respect to ``self.execution_state``. """ assert self.is_valid_block_hash(new_payload_request.execution_payload) - assert self.is_valid_versioned_hashes(new_payload_request) # [Modified in Deneb] + assert self.is_valid_versioned_hashes(new_payload_request) # [New in Deneb] assert self.notify_new_payload(new_payload_request.execution_payload) ... return True From 7a827638e66c982a341e2b10b303f772ea9d1f70 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 24 May 2023 12:55:49 +0800 Subject: [PATCH 13/13] Ensure `verify_and_notify_new_payload` returns bool --- specs/bellatrix/beacon-chain.md | 7 ++++--- specs/deneb/beacon-chain.md | 14 ++++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/specs/bellatrix/beacon-chain.md b/specs/bellatrix/beacon-chain.md index 6cc69d747f..6f67be96a7 100644 --- a/specs/bellatrix/beacon-chain.md +++ b/specs/bellatrix/beacon-chain.md @@ -355,9 +355,10 @@ def verify_and_notify_new_payload(self: ExecutionEngine, """ Return ``True`` if and only if ``new_payload_request`` is valid with respect to ``self.execution_state``. """ - assert self.is_valid_block_hash(new_payload_request.execution_payload) - assert self.notify_new_payload(new_payload_request.execution_payload) - ... + if not self.is_valid_block_hash(new_payload_request.execution_payload): + return False + if not self.notify_new_payload(new_payload_request.execution_payload): + return False return True ``` diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index 735dcf628c..bcbf4d1d00 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -197,10 +197,16 @@ def verify_and_notify_new_payload(self: ExecutionEngine, """ Return ``True`` if and only if ``new_payload_request`` is valid with respect to ``self.execution_state``. """ - assert self.is_valid_block_hash(new_payload_request.execution_payload) - assert self.is_valid_versioned_hashes(new_payload_request) # [New in Deneb] - assert self.notify_new_payload(new_payload_request.execution_payload) - ... + if not self.is_valid_block_hash(new_payload_request.execution_payload): + return False + + # [New in Deneb] + if not self.is_valid_versioned_hashes(new_payload_request): + return False + + if not self.notify_new_payload(new_payload_request.execution_payload): + return False + return True ```