# The Merge. Test coverage [toc] ## Engine API ### Field encoding * [ ] [EL mock] `QUANTITY` field values are big-endians ### `engine_newPayload` * Payload with invalid `block_hash` (not equal to `Keccak256(RLP(ExecutionBlockHeader))`), might be a payload with `block_hash` computed with `ommersHash`, `difficulty`, `nonce` and `mixHash` field values set to meaningful PoW values * [ ] [CL mock] EL client *isn't* `SYNCING` * a payload with invalid `block_hash` extends canonical chain * a payload with invalid `block_hash` extends a side chain * [ ] [CL mock] EL client *is* `SYNCING` * a payload with invalid `block_hash` extends canonical chain * a payload with invalid `block_hash` extends a side chain * [ ] [CL mock] in all cases the response is `{status: INVALID_BLOCK_HASH, latestValidHash: null, validationError: errorMessage | null}` * [ ] [EL mock] in all cases CL discards a block containing a payload having an invalid `block_hash` * Payload validation * [x] [[CL mock](https://github.com/ethereum/hive/blob/7d24e9bcf30dc6546fb821848ff0c8d279a80eaa/simulators/ethereum/engine/clmock.go#L244)] `VALID` payload extends canonical chain, the response is `{status: VALID, latestValidHash: payload.blockHash, validationError: null}` * `INVALID` payload extends canonical chain * [x] [[CL mock](https://github.com/ethereum/hive/blob/7d24e9bcf30dc6546fb821848ff0c8d279a80eaa/simulators/ethereum/engine/enginetests.go#L695)] the response is `{status: INVALID, latestValidHash: invalidPayload.parentHash, validationError: errorMessage | null}` * [ ] [CL mock] `INVALID` payload isn't available via JSON-RPC request * [ ] [CL mock + Network sim] `INVALID` payload isn't available via Eth network protocol requests * [ ] [EL mock] CL discarded a block with `INVALID` payload * while EL is `SYNCING` * send payloads `P0 <- INV_P <- P1` to EL, where `INV_P` is an `INVALID` payload from canonical chain, wait when `INV_P` is processed by EL and check the response to `newPayload(P1)`: * [ ] [[CL mock + Networking](https://github.com/ethereum/hive/blob/7d24e9bcf30dc6546fb821848ff0c8d279a80eaa/simulators/ethereum/engine/enginetests.go#L757)] the response MUST be `{status: INVALID, latestValidHash: P0.blockHash, validationError: errorMessage | null}` * [ ] [EL mock] CL discards a subchain starting from a block containing `INV_P`, and re-orgs to a valid chain, consider a test with multiple competing valid chains * [ ] [CL mock] re-org to already synced chain and send a valid payload that extends the synced chain, check that response is `{status: VALID, latestValidHash: payload.blockHash, validationError: null}` * [ ] [CL mock] The head isn't changed if `newPayload(P)` is successfully processed where `P` extends canonical chain ### `engine_forkchoiceUpdated` * `headBlockHash` refers to a payload from canonical chain * [ ] [CL mock] `headBlockHash` is updated * [ ] [CL mock] the response is `{payloadStatus: {status: VALID, latestValidHash: forkchoiceState.headBlockHash, validationError: null}, payloadId: null}` * `headBlockHash` refers to a payload from a side chain; wait for re-org to happen (might need polling if `engine_forkchoiceUpdated` has been responded with `SYNCING`) * [ ] [CL mock] `headBlockHash` is updated * [ ] [CL mock] the response is `{payloadStatus: {status: VALID, latestValidHash: forkchoiceState.headBlockHash, validationError: null}, payloadId: null}` * `safeBlockHash` is updated * [ ] [CL mock] when `safeBlockHash` refers to a payload from canonical chain * [ ] [CL mock] when `safeBlockHash` refers to a payload from side chain (re-org to a side chain happens) * [ ] [CL mock] `finalizedBlockHash` is updated * [ ] [[CL mock](https://github.com/ethereum/hive/pull/527)] `payloadAttributes.timestamp <= headBlock.timestamp` * forkchoice state is applied * error is returned * while EL is `SYNCING` * send payloads `P0 <- INV_P <- P1` to EL, where `INV_P` is an `INVALID` payload from canonical chain, wait when `INV_P` is processed by EL and check EL response to `forkchoiceUpdated(headBlockHash=P1.blockHash)`: * [ ] [CL mock + Networking] the response MUST be `{payloadStatus: {status: INVALID, latestValidHash: P0.blockHash, validationError: errorMessage | null}, payloadId: null}` * [ ] [EL mock] CL discards a subchain starting from a block containing `INV_P`, and re-orgs to a valid chain, consider a test with multiple competing valid chains * [ ] [CL mock] send a chain of payloads, wait for sync to finish by polling `forkchoiceUpdated` and check `finalized`, `safe` and `head` blocks are updated ### `engine_getPayload` * [ ] [CL mock] responds `-32001: Unknown payload` to the call with random `payloadId` * [[CL mock](https://github.com/ethereum/hive/blob/7d24e9bcf30dc6546fb821848ff0c8d279a80eaa/simulators/ethereum/engine/clmock.go#L295)] build process * [ ] initiate build process by sending `forkchoiceUpdated` with payload attributes and check that response is `{payloadStatus: {status: VALID, latestValidHash: forkchoiceState.headBlockHash, validationError: null}, payloadId: buildProcessId}`, remember `buildProcessId` * [ ] wait for a bit and send `getPayload(payloadId=buildProcessId)`, check that reponse contains a payload with expected field values (all from payload attributes: `timestamp`, `prevRandao`, `suggestedFeeRecipient` + `parentHash` and some other fields that make sense to check) * [ ] make `newPayload(P)` call where `P` is recently built payload, the response MUST be `{status: VALID, latestValidHash: payload.blockHash, validationError: null}` * [ ] [Engine API fuzzer] fuzz `getPayload` immediately followed by `forkchoiceUpdated` with initiated build process, `getPayload` MUST response correctly ### `engine_exchangeTransitionConfiguration` Less of a priority. * [ ] [CL mock] check `terminalTotalDifficulty` is correct * [ ] [CL mock] check `terminalBlockHash` override scenario (all three fields have correct values) ### Sync * [ ] [CL mock] `newPayload` is responsded with `{status: SYNCING, latestValidHash: null, validationError: null}` if the payload extends the canonical chain and requisite data for its validation is missing * `forkchoiceUpdated` is responded with `{payloadStatus: {status: SYNCING, latestValidHash: null, validationError: null}, payloadId: null}` if `forkchoiceState.headBlockHash` references: * [x] [[CL mock](https://github.com/ethereum/hive/blob/7d24e9bcf30dc6546fb821848ff0c8d279a80eaa/simulators/ethereum/engine/enginetests.go#L352)] an unknown payload * [ ] [CL mock] a payload that can't be validated because requisite data for the validation is missing ### Message ordering * [ ] [CL mock + Engine API fuzzer] Excectuion layer processes `forkchoiceUpdated` calls in the same order as they have been made -- might be better checked by fuzzer * [ ] [EL mock + CL fuzzer] Consensus layer makes `forkchoiceUpdated` calls respecting the order of fork choice updates in the fork choice store -- hard to test, less of a priority ### Timeouts * [ ] [EL mock] Method call timeout on CL side is as expected; for this check we need an agreement on the timeout, probably documented in the spec. May require calling to every method -- Not sure if it worth checking ### `eth` namespace Less of a priority. ### Authentication Less of a priority. ## Transition * TTD case * [[CL mock](https://github.com/ethereum/hive/blob/7d24e9bcf30dc6546fb821848ff0c8d279a80eaa/simulators/ethereum/engine/mergetests.go)] propose on top of a valid terminal block; send `engine_forkchoiceUpdated(headhBlockHash: TB.blockHash)` and run the following checks: * [x] EL left PoW and is in the transition mode * [x] EL's head is set to `headBlockHash` * [x] payload build process has begun * [ ] payload has been built successfully and is accepted by EL client, ensure that: * `ommersHash == 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347` * `difficulty == 0` * `mixHash == prevRandao` * `nonce == 0x0000000000000000` * [[CL mock](https://github.com/ethereum/hive/blob/7d24e9bcf30dc6546fb821848ff0c8d279a80eaa/simulators/ethereum/engine/enginetests.go#L195)] propose on top of an invalid terminal block; send `engine_forkchoiceUpdated(headhBlockHash: INV_TB.blockHash)` * [x] EL responds with `{status: INVALID_TERMINAL_BLOCK, latestValidHash: null, validationError: errorMessage | null}` * [ ] EL didn't change its head to `INV_TB.blockHash` * [ ] EL didn't leave PoW * do all above checks when: * [x] `INV_TB.TD < TTD` * [ ] `INV_TB.parent.TD >= TTD` * do all above checks when: * [ ] `INV_TB`'s PoW seal is invalid * [ ] `INV_TB` is invalid according to EE rules * [x] [CL mock] transition on a valid chain in lock-step 1. send `newPayload(P)`, where `TB <- P` and `TB` is a valid terminal block, and `P` is a valid payload following Block structure, Block validity, Block and ommer rewards section of [EIP-3675](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3675.md) * [ ] EL responds with `{status: VALID, latestValidHash: payload.blockHash, validationError: null}` * [ ] EL hasn't left PoW * [ ] EL's head points to `TB` 1. send `forkchoiceUpdated(headBlockHash: P.blockHash)` * [ ] EL responds with `{payloadStatus: {status: VALID, latestValidHash: forkchoiceState.headBlockHash, validationError: null}, payloadId: null}` * [ ] EL left PoW * [ ] EL's head points to `P` * [ ] Get `safe` block responded with error * [ ] Get `finalized` block responded with error 1. Let the chain justify the first payload (beacon block that has a payload) * [ ] EL's head is as expected * [ ] `safe` block points to the justified payload * [ ] Get `finalized` block responds with error 1. Let the chain finalize the first payload (beacon block that has a payload) * [ ] EL's head is as expected * [ ] `safe` block is as expected * [ ] Get `finalized` block points to the finalized payload * [x] [[CL mock](https://github.com/ethereum/hive/blob/8e78b1e5803bd67f726d8e071bcb11155c2c8e7a/simulators/ethereum/engine/mergetests.go#L90)] Same scenario with two valid terminal blocks `A` and `B` where `A` is the head of the PoW chain according to EL's observation and `P` (a transition payload) is ancestor of `B`. * [x] [CL mock] Same scenario with missed `forkchoiceUpdated(P)` 1. Send `newPayload(P)` 1. Send `newPayload(P1)`, where `P <- P1` 1. Send `forkchoiceUpdated(P1)` * [x] [CL mock] Same scenario with two valid chains that goes beyond the transition and a re-org: `TB1 <- P0 <- P1` and `TB2 <- P0' <- P1'` * [x] [CL mock] Same scenario with `P` having a transaction deploying a contract that uses `PREVRANDAO` and a transaction that triggers this opcode call and stores the return value into the state 1. Make the transition happen on `P1`, i.e. `P1` must become the head 2. Send `newPayload(P0') + forkchoiceUpdated(P0')` 3. And so on, i.e. continue to build on top of `P1'` -- it must become canonical chain * [x] transition on an invalid chain in lock-step * send `newPayload(P)` where `INV_TB <- P` and `INV_TB` is an invalid terminal block * [x] [[CL mock](https://github.com/ethereum/hive/blob/7d24e9bcf30dc6546fb821848ff0c8d279a80eaa/simulators/ethereum/engine/enginetests.go#L230)] EL responds with `{status: INVALID_TERMINAL_BLOCK, latestValidHash: null, validationError: errorMessage | null}` * [CL mock] do the above check when: * [x] `INV_TB.TD < TTD` * [ ] `INV_TB.parent.TD >= TTD` * [CL mock] do the above check when: * [ ] `INV_TB`'s PoW seal is invalid * [ ] `INV_TB` is invalid according to EE rules * [x] [CL mock] transition on an invalid chain in lock-step with re-org 1. Send `newPayload(P)` where `TB <- P`, and `TB` is a valid terminal block 1. Send `forkchocieUpdated(P)` * [ ] check that transition has happened 1. Send `newPayload(P')` where `INV_TB <- P'` 1. Send `forkchoiceUpdated(P')` * [ ] the head must still point to `P` * run the above scenario when: * [ ] `INV_TB.TD < TTD` * [ ] `INV_TB.parent.TD >= TTD` * run the above scenario when: * [ ] `INV_TB`'s PoW seal is invalid * [ ] `INV_TB` is invalid according to EE rules * [x] [CL mock] Above scenario with invalid transition payload, i.e. there are two chain `TB <- P0 <- P1` and `TB <- INV_P0' <- P1'`, where `INV_P0'` is a payload that is invalid according to EE rules * [ ] after submitting an invalid chain to EL via `newPayload` and subsequent `forkchoiceUpdated(P1')` the head must still point to `P1` * [x] [CL mock + Networking] transition on a valid chain through `SYNCING` 1. Make EL `SYNCING` with `TB <- P0 <- P1 <- P2 <- ... <- Pn` by submitting `newPayload(Pn) + forkchoiceUpdated(Pn)`; EL must not know the chain starting from `TB` and must pull it from a remote peer 1. Wait until EL sync is done by polling `forkchoiceUpdated`, and check: * [ ] EL left PoW * [ ] EL's head points to `Pn` * [ ] Repeate the scenario with long enough chain that finalizes the transition and check: * [ ] EL left PoW * [ ] EL's head points to `Pn` * [ ] `safe` block is as expected * [ ] `finalized` block is as expected * [x] [CL mock + Networking] transition on an invalid chain through `SYNCING` 1. Make EL `SYNCING` with `INV_TB <- P0 <- P1 <- P2 <- ... <- Pn` by submitting `newPayload(Pn) + forkchoiceUpdated(Pn)`; EL must not know the chain starting from `INV_TB` and must pull it from a remote peer 1. Wait until EL sync is *stopped* by polling `forkchoiceUpdated`, and check: * [ ] EL responded with `{payloadStatus: {status: INVALID_TERMINAL_BLOCK, latestValidHash: null, validationError: errorMessage | null}, payloadId: null}` * [ ] EL didn't leave PoW * [ ] EL's head didn't change * run the above scenario when: * [ ] `INV_TB.TD < TTD` * [ ] `INV_TB.parent.TD >= TTD` * run the above scenario when: * [ ] `INV_TB`'s PoW seal is invalid * [ ] `INV_TB` is invalid according to EE rules * Above scenario with `TB <- INV_P0 <- P1 <- P2 <- ... <- Pn` chain, where `TB` is a valid terminal block, `INV_P0` is an invalid payload of a transition block, checks: * [ ] EL responded with `{payloadStatus: {status: INVALID, latestValidHash: ?????, validationError: errorMessage | null}, payloadId: null}` * [ ] EL didn't leave PoW * [ ] EL's head didn't change * Above scenario with `TB <- P0 <- INV_P1 <- P2 <- ... <- Pn` chain, where `TB` is a valid terminal block, `INV_P0` is an invalid payload of a transition block, checks: * [ ] EL responded with `{payloadStatus: {status: INVALID, latestValidHash: P0.blockHash, validationError: errorMessage | null}, payloadId: null}` * [ ] EL left PoW * [ ] EL's head points to `P0` * [x] [CL mock + Networking] transition on invalid chain through `SYNCING` and re-org 1. Make EL import a valid chain `A: TB <- P0 <- P1 <- P2 <- ... <- Pn` chain, check: * [ ] EL left PoW * [ ] EL's head points to `Pn` 1. Make EL `SYNCING` with `B: INV_TB <- P0 <- P1 <- P2 <- ... <- Pn` by submitting `newPayload(Pn) + forkchoiceUpdated(Pn)`; EL must not know the chain starting from `INV_TB` and must pull it from a remote peer 1. Wait until EL sync is *stopped* by polling `forkchoiceUpdated`, and check: * [ ] EL responded with `{payloadStatus: {status: INVALID_TERMINAL_BLOCK, latestValidHash: null, validationError: errorMessage | null}, payloadId: null}` * [ ] EL's head points to `A_Pn` * run the above scenario when: * [ ] `INV_TB.TD < TTD` * [ ] `INV_TB.parent.TD >= TTD` * run the above scenario when: * [ ] `INV_TB`'s PoW seal is invalid * [ ] `INV_TB` is invalid according to EE rules * Above scenario with `B: TB <- INV_P0 <- P1 <- P2 <- ... <- Pn` chain, where `B_TB` is a valid terminal block, `INV_P0` is an invalid payload of a transition block, checks: * [ ] EL responded with `{payloadStatus: {status: INVALID, latestValidHash: ?????, validationError: errorMessage | null}, payloadId: null}` * [ ] EL's head points to `A: Pn` * Above scenario with `B: TB <- P0 <- INV_P1 <- P2 <- ... <- Pn` chain, where `B_TB` is a valid terminal block, `INV_P0` is an invalid payload of a transition block, checks: * [ ] EL responded with `{payloadStatus: {status: INVALID, latestValidHash: B_P0.blockHash, validationError: errorMessage | null}, payloadId: null}` * [ ] EL's head points to `B_P0` * [x] [CL mock] All the above scenarios adopted to `TERMINAL_BLOCK_HASH` override * [x] Additionally, check that transition succeedes when a `TB` with `TERMINAL_BLOCK_HASH` is not the head of PoW chain according to the TD rule, i.e. there is another block that is heavier than the one with `TERMINAL_BLOCK_HASH` and even having a higher block number * [x] [EL mock] All the above scenarios checking CL client on real beacon chain and EL chain data * [ ] CL discards all blocks starting from a transition block if terminal block is invalid * [ ] `safe_block_hash == justified_payload.block_hash` * [ ] [EL mock] `TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH` * [ ] early proposal test case * [ ] processing early proposed block case during lock-step and while `SYNCING` * [x] [CL mock + Networking] EL stops processing descendants of a terminal block received from gossip. Consider a PoW chain: `Genesis <- B0 <- ... <- Bi <- TB <- TB0 <- TB1`, where `TB` is a valid terminal block * TTD case 1. Send all blocks up to and including `TB` to a peer via `NewBlock` ETH subprotocol messsages * [ ] the head points to `TB` 1. Send `NewBlock(TB0)` to a peer * [ ] the head points to `TB` 1. Repeat the same with `TERMINAL_BLOCK_HASH = TB.blockHash` * [x] [CL mock + Networking] `TERMINAL_BLOCK_HASH` case with longer chain: EL stops processing descendants of a terminal block received from gossip. Consider a PoW chain: `Genesis <- B0 <- ... <- Bi <- TB <- TB0 <- TB1`, where `TB` is a valid terminal block 1. Make client import the chain up to and including `TB0` 1. Restart a client with `TERMINAL_BLOCK_HASH = TB.blockHash`, `TTD = TB.totalDifficulty`, `TERMINAL_BLOCK_NUMBER` set accordingly 1. Send `NewBlock(TB1)` to a peer * [ ] the head doesn't point to `TB1` -- the override has been applied retroactively and clients may not remove previously imported but presently invalid `TB0` from the canonical chain * [x] [CL mock + Networking] EL stops processing descendants of a terminal block received while syncing. Consider a PoW chain: `Genesis <- B0 <- ... <- Bi <- TB <- TB0 <- TB1`, where `TB` is a valid terminal block 1. Make EL client sync with a remote peer advertising the above chain * [ ] the head points to `TB` * [x] [CL mock + Networking] `NewBlock` and `NewBlockHashes` checks 1. Start the transition by submitting a transition payload `P`, where `TB <- P` and `TB` is a valid terminal block 1. Send `NewBlock(TB')` to a peer, where `TB'` is also a valid terminal block * [ ] a peer propagates `NewBlock(TB')` 1. Send `NewBlock(TB0')`, where `TB' <- TB0'` * [ ] a peer does not propagate `NewBlock(TB0')` 1. Finalize transition 1. Send `NewBlock(TB')` to a peer, where `TB''` is yet another valid terminal block * [ ] a peer does not propagate `NewBlock(TB'')` * [x] [[CL mock](https://github.com/ethereum/hive/blob/7d24e9bcf30dc6546fb821848ff0c8d279a80eaa/simulators/ethereum/engine/enginetests.go#L1546)] Node is bootstrapped from scratch after the merge has happened and synced successfully ### Test scenarios by @matt and @marioevz * Network partitioning with different portions of stake * https://hackmd.io/@matt/HyH46iOhY * EL client disruption * https://github.com/marioevz/merge-testnet-testcases * https://notes.ethereum.org/0cZdht4sTW-EaoUIU_eF_A ### Raw checks from Engine API spec * `engine_newPayload` * terminal block is invalid * [x] [[hive test](https://github.com/ethereum/hive/blob/7d24e9bcf30dc6546fb821848ff0c8d279a80eaa/simulators/ethereum/engine/enginetests.go#L230)] a payload is a child of a terminal block that doesn't meet terminal block conditions * [ ] a payload is a child of a terminal block that is `INVALID` with respect to * [ ] PoW seal * [ ] EE rules * [ ] a payload of transition block is `INVALID` with respect to EE rules * [ ] EL was `SYNCING` and during the sync it's been fed with `INV_TB <- P1`, where `INV_TB` is invalid terminal block and `P1` is a transition payload built on top of this block. Then EL receives `newPayload(P2)`, where `P2` is a child of `P1` and MUST respond with `INVALID_TERMINAL_BLOCK` * [ ] check TTD condition * [ ] check `TERMINAL_BLOCK_HASH` override condition * [ ] is responded with `{status: INVALID_TERMINAL_BLOCK, latestValidHash: null, validationError: errorMessage | null}` * [ ] EL was `SYNCING` and during the sync it's been fed with `TB <- INV_P1`, where `INV_P1` is `INVALID` payload of a transition block. Then EL receives `forkchoiceUpdated(INV_P1)` -- transition MUST NOT happen * [ ] CL Discards blocks starting from a transition block if receives `INVALID_TERMINAL_BLOCK` * valid terminal block * [ ] no transition must happen until `forkchoiceUpdated` has been called * `engine_forkchoiceUpdated` * `safeBlockHash` is zeroes * [ ] `safe` block requested via JSON-RPC is responded with error * `finalizedBlockHash` is zeroes * [ ] `finalized` block requested via JSON-RPC is responded with error * references an invalid terminal block * [x] [[hive test](https://github.com/ethereum/hive/blob/7d24e9bcf30dc6546fb821848ff0c8d279a80eaa/simulators/ethereum/engine/enginetests.go#L195)] send `forkchoiceState.headBlockHash` that references a terminal block that doesn't satisfy terminal block conditions; EL must return `{payloadStatus: {status: INVALID_TERMINAL_BLOCK, latestValidHash: null, validationError: errorMessage | null}, payloadId: null}` * [ ] check that EL is still on PoW and transition hasn't begun * [ ] the same checks with `TERMINAL_BLOCK_HASH` override * [[hive test](https://github.com/ethereum/hive/blob/7d24e9bcf30dc6546fb821848ff0c8d279a80eaa/simulators/ethereum/engine/mergetests.go)] references a valid terminal block * [x] build process has begun * [x] EL started the transition and left PoW * [x] EL's head is set to `headBlockHash` * [ ] two terminal blocks: `A` is the head to EL node's observation (`A` has the biggest TD), `B` is not; after a call with `headBlockHash = A.block_hash`, the head points to `A` * [ ] check with `TERMINAL_BLOCK_HASH` override * payload validation * [ ] EL was `SYNCING` and during the sync it's been fed with `INV_TB <- P1`, where `INV_TB` is invalid terminal block and `P1` is a transition payload built on top of this block. Then EL receives `forkchoiceUpdated(P1)` and MUST respond with `INVALID_TERMINAL_BLOCK` * [ ] no transition must happen * [ ] CL discards blocks starting from a transition block if receives `INVALID_TERMINAL_BLOCK` ### Raw checks from EIP-3675 * PoW blocks that are descendants of any terminal PoW block MUST NOT be imported * Received from gossip * Received from a remote peer while syncing * Beginning with `TRANSITION_BLOCK` * a number of previously dynamic block fields are deprecated by enforcing these values to instead be constants * `ommersHash == 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347` * `difficulty == 0` * `mixHash == prevRandao` * `nonce == 0x0000000000000000` * The length of the block's extraData MUST be less than or equal to `MAX_EXTRA_DATA_BYTES` bytes * PoW seal isn't verified anymore * As of the first `POS_FORKCHOICE_UPDATED` event, the fork choice rule MUST be altered in the following way: * Remove the existing PoW heaviest chain rule * Adhere to the new PoS LMD-GHOST rule * Consider the chain starting at genesis and ending with the head block nominated by the event as the canonical blockchain. * Set the head of the canonical blockchain to the corresponding block nominated by the event. * Beginning with the `FIRST_FINALIZED_BLOCK`, set the most recent finalized block to the corresponding block nominated by the event * Networking * Nodes MUST set the `FORK_NEXT` parameter to the `FORK_NEXT_VALUE` * Beginning with receiving the `FIRST_FINALIZED_BLOCK`, the networking stack MUST discard the following ingress messages: * NewBlockHashes (0x01) * NewBlock (0x07) * Security Considerations. Transition process * A scenario with long PoW chain having TB in the middle with `TERMINAL_BLOCK_HASH` override * Temporal network partitioning during transition with different portions of stake on each partition and two terminal blocks that the network jumps between ### Raw checks from EIP-4399 * Beginning with `TRANSITION_BLOCK`, client software MUST set the value of the `mixHash`, i.e. the field with the number 13 (0-indexed) in a block header, to the latest RANDAO mix of the post beacon state of the previous block * Beginning with `TRANSITION_BLOCK`, the `DIFFICULTY (0x44)` instruction MUST return the value of the `mixHash` field ### Raw checks from Consensus specs * `validate_merge_block` * `TERMINAL_BLOCK_HASH` + `TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH` * `is_valid_terminal_pow_block` * `block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY` * `parent.total_difficulty < TERMINAL_TOTAL_DIFFICULTY` * ... ## EIP-3675 * Block structure and validity * [ ] PoS blocks with `ommersHash != Keccak256(RLP([]))`, `difficulty != 0`, `nonce != 0x0000000000000000`, `len(extraData) > MAX_EXTRA_DATA_BYTES` are considered invalid * [ ] Block rewards aren't added to `beneficiary` account starting with `TRANSITION_BLOCK` * [ ] Client software adheres to PoS LMD-GHOST rule * Head and finalized blocks are set according to the recent `POS_FORKCHOICE_UPDATED` event * No fork choice state is updated unless `POS_FORKCHOICE_UPDATED` event is received * [ ] Transition process * Client software doesn't process any PoW block beyond a terminal block * Beginning with `TRANSITION_BLOCK`, client software applies new block validity rules * Beginning with the first `POS_FORKCHOICE_UPDATED` client software switched its fork choice rule to PoS LMD-GHOST * `TRANSITION_BLOCK` must be a child of a terminal PoW block * `NewBlockHashes (0x01)` and `NewBlock (0x07)` network messages are discarded after receiving the `FIRST_FINALIZED_BLOCK` ## EIP-4399 * [ ] Main scenario * In one of ancestors of `TRANSITION_BLOCK` deploy a contract that stores return value of `DIFFICULTY (0x44)` to the state * Check that value returned by `DIFFICULTY (0x44)` in transaction executed within the parent of `TRANSITION_BLOCK` equals `difficulty` field value * Check that value returned by `PREVRANDAO (0x44)` in transaction executed within `TRANSITION_BLOCK` equals `prevRandao` field value ## Consensus specs * Beacon chain * [x] `is_merge_transition_complete` * [x] `is_merge_transition_block` * [x] `is_execution_enabled` * [x] if `is_execution_enabled` call to `process_execution_payload` * [x] `process_execution_payload` * [x] `notify_new_payload` is called * [x] `assert payload.parent_hash == ...` * [x] `assert payload.prev_randao == ...` * [x] `assert payload.timestamp == ...` * [x] `assert execution_engine.notify_new_payload(payload)` * [x] `state.latest_execution_payload_header = ExecutionPayloadHeader(...)` * [x] `compute_timestamp_at_slot` * Modified in Bellatrix * [x] `get_inactivity_penalty_deltas` * [x] `slash_validator` * [x] `process_slashings` * Fork choice * [x] `is_valid_terminal_pow_block` * [x] `validate_merge_block` * [x] [Covered by Transition test cases] `notify_forkchoice_updated` * `finalized_block_hash = Hash32()` before transition finalized * `finalized_block_hash = store.blocks[store.finalized_block_hash.root].body.execution_payload.block_hash` after transition finalized * `safe_block_hash = Hash32()` before transition justified * `safe_block_hash = store.blocks[store.justified_checkpoint.root].body.execution_payload.block_hash` after transition justified * [x] [Covered by Transition test cases] `on_block` * calls `validate_merge_block` if `is_merge_transition_block` * `notify_forkchoice_updated` is called * [x] Fork (network upgrade) * [x] [Covered by Transition test cases] Validator * `get_pow_block_at_terminal_total_difficulty` * `get_terminal_pow_block` * `prepare_execution_payload` * `get_execution_payload` ## Optimistic sync * [ ] [EL mock] `is_optimistic_candidate_block` * The parent of the block has execution enabled * The current slot (as per the system clock) is at least `SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY` ahead of the slot of the block being imported * [ ] [EL mock] When a "merge block" (i.e. the first block which enables execution in a chain) is declared to be `VALID` by an execution engine (either directly or indirectly), the full `validate_merge_block` MUST be run against the merge block. * [ ] A subtree of blocks starting from the merge block is discarded if merge block is invalid * [ ] [EL mock] If `TERMINAL_BLOCK_HASH` override is activated assertions from `validate_merge_block` MUST prevent an optimistic import * [ ] [EL mock] MUST not optimistically import `INVALID` blocks * [ ] [EL mock] MUST not optimistically import block if EL didn't respond to `newPayload` * [EL mock] Optimistic VC (may be tested via Validator API interface) * [ ] MUST NOT produce a block * [ ] MUST NOT participate in attestation * [ ] MUST NOT participate in sync committees * [EL mock] Optimistic BN * `execution_optimistic` flag is set in API in the response to queries over optimistic block