From 0a1113537db962dab250cbd2c92adac48541085f Mon Sep 17 00:00:00 2001 From: jangko Date: Tue, 7 Jul 2026 09:57:18 +0700 Subject: [PATCH 1/3] [Bogota] EIP-7805: Fork-choice enforced Inclusion Lists (FOCIL) --- execution_chain/beacon/api_handler.nim | 6 +- .../beacon/api_handler/api_forkchoice.nim | 11 ++- .../api_handler/api_getinclusionlist.nim | 20 +++++ .../beacon/api_handler/api_newpayload.nim | 13 ++- .../beacon/api_handler/api_utils.nim | 34 ++++---- execution_chain/beacon/beacon_engine.nim | 10 +-- execution_chain/rpc/engine_api.nim | 40 ++++++--- hive_integration/engine_client.nim | 83 +++++++++++++++---- hive_integration/types.nim | 3 +- tests/eest/eest_engine.nim | 21 +++-- tests/eest/eest_parser.nim | 1 + tests/test_engine_api.nim | 10 +-- vendor/nim-web3 | 2 +- 13 files changed, 184 insertions(+), 70 deletions(-) create mode 100644 execution_chain/beacon/api_handler/api_getinclusionlist.nim diff --git a/execution_chain/beacon/api_handler.nim b/execution_chain/beacon/api_handler.nim index 87c3823f11..fdaed4fd71 100644 --- a/execution_chain/beacon/api_handler.nim +++ b/execution_chain/beacon/api_handler.nim @@ -13,7 +13,8 @@ import ./api_handler/api_getbodies, ./api_handler/api_newpayload, ./api_handler/api_forkchoice, - ./api_handler/api_getblobs + ./api_handler/api_getblobs, + ./api_handler/api_getinclusionlist # ------------------------------------------------------------------------------ # Public functions @@ -34,4 +35,5 @@ export forkchoiceUpdated, getBlobsV1, getBlobsV2, - getBlobsV3 + getBlobsV3, + getInclusionList diff --git a/execution_chain/beacon/api_handler/api_forkchoice.nim b/execution_chain/beacon/api_handler/api_forkchoice.nim index 0620d2917e..3a18e17b65 100644 --- a/execution_chain/beacon/api_handler/api_forkchoice.nim +++ b/execution_chain/beacon/api_handler/api_forkchoice.nim @@ -30,7 +30,14 @@ template validateVersion(attr, com, apiVersion) = version = attr.version timestamp = ethTime(attr.timestamp) - if apiVersion == Version.V4: + if apiVersion == Version.V5: + if version != apiVersion: + raise invalidAttr("forkChoiceUpdatedV5 expect PayloadAttributesV5" & + " but got PayloadAttributes" & $version) + if not com.isBogotaOrLater(timestamp): + raise unsupportedFork( + "forkchoiceUpdatedV5 get invalid payloadAttributes timestamp") + elif apiVersion == Version.V4: if version != apiVersion: raise invalidAttr("forkChoiceUpdatedV4 expect PayloadAttributesV4" & " but got PayloadAttributes" & $version) @@ -80,7 +87,7 @@ template validateHeaderTimestamp(header, com, apiVersion) = proc forkchoiceUpdated*(ben: BeaconEngineRef, apiVersion: Version, - update: ForkchoiceStateV1, + update: ForkchoiceState, attrsOpt: Opt[PayloadAttributes]): Future[ForkchoiceUpdatedResponse] {.async: (raises: [CancelledError, ApplicationError]).} = diff --git a/execution_chain/beacon/api_handler/api_getinclusionlist.nim b/execution_chain/beacon/api_handler/api_getinclusionlist.nim new file mode 100644 index 0000000000..3338fd156b --- /dev/null +++ b/execution_chain/beacon/api_handler/api_getinclusionlist.nim @@ -0,0 +1,20 @@ +# Nimbus +# Copyright (c) 2026 Status Research & Development GmbH +# Licensed under either of +# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) +# * MIT license ([LICENSE-MIT](LICENSE-MIT)) +# at your option. +# This file may not be copied, modified, or distributed except according to +# those terms. + +import + eth/common/hashes, + web3/execution_types, + ../beacon_engine + +{.push gcsafe, raises:[].} + +proc getInclusionList*(ben: BeaconEngineRef, + apiVersion: Version, + parentHash: Hash32): InclusionList = + discard diff --git a/execution_chain/beacon/api_handler/api_newpayload.nim b/execution_chain/beacon/api_handler/api_newpayload.nim index bc722d8f1f..1706a9a9dd 100644 --- a/execution_chain/beacon/api_handler/api_newpayload.nim +++ b/execution_chain/beacon/api_handler/api_newpayload.nim @@ -111,7 +111,7 @@ template validatePayload(apiVersion, payloadVersion, payload) = # https://github.com/ethereum/execution-apis/blob/40088597b8b4f48c45184da002e27ffc3c37641f/src/engine/prague.md#request func validateExecutionRequest(blockHash: Hash32, requests: openArray[seq[byte]], apiVersion: Version): - Opt[PayloadStatusV1] {.raises: [ApplicationError].} = + Opt[PayloadStatus] {.raises: [ApplicationError].} = var previousRequestType = -1 for request in requests: if request.len == 0: @@ -141,8 +141,9 @@ proc newPayload*(ben: BeaconEngineRef, payload: ExecutionPayload, versionedHashes = Opt.none(seq[Hash32]), beaconRoot = Opt.none(Hash32), - executionRequests = Opt.none(seq[seq[byte]])): - Future[PayloadStatusV1] {.async: (raises: [CancelledError, ApplicationError, RlpError]).} = + executionRequests = Opt.none(seq[seq[byte]]), + inclusionList = Opt.none(InclusionList)): + Future[PayloadStatus] {.async: (raises: [CancelledError, ApplicationError, RlpError]).} = trace "Engine API request received", meth = "newPayload", @@ -162,6 +163,12 @@ proc newPayload*(ben: BeaconEngineRef, if res.isSome: return res.value + if apiVersion >= Version.V5: + if inclusionList.isNone: + raise invalidParams("newPayload" & $apiVersion & + ": inclusionList is expected from execution payload") + {.warning: "Please implement newPayload inclusionList validation".} + let com = ben.com chain = ben.chain diff --git a/execution_chain/beacon/api_handler/api_utils.nim b/execution_chain/beacon/api_handler/api_utils.nim index 65570b9bb4..7054bd5f04 100644 --- a/execution_chain/beacon/api_handler/api_utils.nim +++ b/execution_chain/beacon/api_handler/api_utils.nim @@ -51,7 +51,7 @@ proc computePayloadId*(blockHash: common.Hash32, proc validateBlockHash*(header: common.Header, wantHash: common.Hash32, - version: Version): Result[void, PayloadStatusV1] + version: Version): Result[void, PayloadStatus] {.gcsafe.} = let gotHash = header.computeBlockHash if wantHash != gotHash: @@ -60,7 +60,7 @@ proc validateBlockHash*(header: common.Header, else: PayloadExecutionStatus.invalid - let res = PayloadStatusV1( + let res = PayloadStatus( status: status, validationError: Opt.some("blockhash mismatch, want " & $wantHash & ", got " & $gotHash) @@ -72,16 +72,16 @@ proc validateBlockHash*(header: common.Header, template toValidHash*(x: common.Hash32): Opt[Hash32] = Opt.some(x) -proc simpleFCU*(status: PayloadStatusV1): ForkchoiceUpdatedResponse = +proc simpleFCU*(status: PayloadStatus): ForkchoiceUpdatedResponse = ForkchoiceUpdatedResponse(payloadStatus: status) proc simpleFCU*(status: PayloadExecutionStatus): ForkchoiceUpdatedResponse = - ForkchoiceUpdatedResponse(payloadStatus: PayloadStatusV1(status: status)) + ForkchoiceUpdatedResponse(payloadStatus: PayloadStatus(status: status)) proc simpleFCU*(status: PayloadExecutionStatus, msg: string): ForkchoiceUpdatedResponse = ForkchoiceUpdatedResponse( - payloadStatus: PayloadStatusV1( + payloadStatus: PayloadStatus( status: status, validationError: Opt.some(msg) ) @@ -91,7 +91,7 @@ proc invalidFCU*( validationError: string, hash = default(common.Hash32)): ForkchoiceUpdatedResponse = ForkchoiceUpdatedResponse(payloadStatus: - PayloadStatusV1( + PayloadStatus( status: PayloadExecutionStatus.invalid, latestValidHash: toValidHash(hash), validationError: Opt.some validationError @@ -101,39 +101,39 @@ proc invalidFCU*( proc validFCU*(id: Opt[Bytes8], validHash: common.Hash32): ForkchoiceUpdatedResponse = ForkchoiceUpdatedResponse( - payloadStatus: PayloadStatusV1( + payloadStatus: PayloadStatus( status: PayloadExecutionStatus.valid, latestValidHash: toValidHash(validHash) ), payloadId: id ) -proc invalidStatus*(validHash: common.Hash32, msg: string): PayloadStatusV1 = - PayloadStatusV1( +proc invalidStatus*(validHash: common.Hash32, msg: string): PayloadStatus = + PayloadStatus( status: PayloadExecutionStatus.invalid, latestValidHash: toValidHash(validHash), validationError: Opt.some(msg) ) -proc invalidStatus*(validHash = default(common.Hash32)): PayloadStatusV1 = - PayloadStatusV1( +proc invalidStatus*(validHash = default(common.Hash32)): PayloadStatus = + PayloadStatus( status: PayloadExecutionStatus.invalid, latestValidHash: toValidHash(validHash) ) -proc acceptedStatus*(validHash: common.Hash32): PayloadStatusV1 = - PayloadStatusV1( +proc acceptedStatus*(validHash: common.Hash32): PayloadStatus = + PayloadStatus( status: PayloadExecutionStatus.accepted, latestValidHash: toValidHash(validHash) ) -proc acceptedStatus*(): PayloadStatusV1 = - PayloadStatusV1( +proc acceptedStatus*(): PayloadStatus = + PayloadStatus( status: PayloadExecutionStatus.accepted ) -proc validStatus*(validHash: common.Hash32): PayloadStatusV1 = - PayloadStatusV1( +proc validStatus*(validHash: common.Hash32): PayloadStatus = + PayloadStatus( status: PayloadExecutionStatus.valid, latestValidHash: toValidHash(validHash) ) diff --git a/execution_chain/beacon/beacon_engine.nim b/execution_chain/beacon/beacon_engine.nim index b9f200cb2b..c59a03d3a8 100644 --- a/execution_chain/beacon/beacon_engine.nim +++ b/execution_chain/beacon/beacon_engine.nim @@ -192,7 +192,7 @@ func setInvalidAncestor*(ben: BeaconEngineRef, header: Header, blockHash: Hash32 # checkInvalidAncestor checks whether the specified chain end links to a known # bad ancestor. If yes, it constructs the payload failure response to return. proc checkInvalidAncestor*(ben: BeaconEngineRef, - check, head: Hash32): Opt[PayloadStatusV1] = + check, head: Hash32): Opt[PayloadStatus] = proc latestValidHash(chain: ForkedChainRef, invalid: auto): Hash32 = let parent = chain.headerByHash(invalid.parentHash).valueOr: return invalid.parentHash @@ -221,7 +221,7 @@ proc checkInvalidAncestor*(ben: BeaconEngineRef, for x in deleted: ben.invalidTipsets.del(x) - return Opt.none(PayloadStatusV1) + return Opt.none(PayloadStatus) # Not too many failures yet, mark the head of the invalid chain as invalid if check != head: @@ -244,7 +244,7 @@ proc checkInvalidAncestor*(ben: BeaconEngineRef, let lastValid = latestValidHash(ben.chain, invalid) return Opt.some invalidStatus(lastValid, "links to previously rejected block") do: - return Opt.none(PayloadStatusV1) + return Opt.none(PayloadStatus) # delayPayloadImport stashes the given block away for import at a later time, # either via a forkchoice update or a sync extension. This method is meant to @@ -255,14 +255,14 @@ proc delayPayloadImport*( blockHash: Hash32, blk: Block, blockAccessList: Opt[BlockAccessListRef] -): PayloadStatusV1 = +): PayloadStatus = # Sanity check that this block's parent is not on a previously invalidated # chain. If it is, mark the block as invalid too. ben.checkInvalidAncestor(blk.header.parentHash, blockHash).valueOr: # Stash the block away for a potential forced forkchoice update to it # at a later time. ben.chain.quarantine.addOrphan(blockHash, blk, blockAccessList) - return PayloadStatusV1(status: PayloadExecutionStatus.syncing) + return PayloadStatus(status: PayloadExecutionStatus.syncing) func latestFork*(ben: BeaconEngineRef): HardFork = let timestamp = max(ben.txPool.timestamp, ben.chain.latestHeader.timestamp) diff --git a/execution_chain/rpc/engine_api.nim b/execution_chain/rpc/engine_api.nim index 89cf34bbd1..07298a0443 100644 --- a/execution_chain/rpc/engine_api.nim +++ b/execution_chain/rpc/engine_api.nim @@ -26,6 +26,7 @@ const supportedMethods: HashSet[string] = "engine_newPayloadV3", "engine_newPayloadV4", "engine_newPayloadV5", + "engine_newPayloadV6", "engine_getPayloadV1", "engine_getPayloadV2", "engine_getPayloadV3", @@ -36,14 +37,16 @@ const supportedMethods: HashSet[string] = "engine_forkchoiceUpdatedV2", "engine_forkchoiceUpdatedV3", "engine_forkchoiceUpdatedV4", + "engine_forkchoiceUpdatedV5", "engine_getPayloadBodiesByHashV1", "engine_getPayloadBodiesByHashV2", - "engine_getPayloadBodiesByRangeV1", + "engine_getPayloadBodiesByRangeV1", "engine_getPayloadBodiesByRangeV2", "engine_getClientVersionV1", "engine_getBlobsV1", "engine_getBlobsV2", - "engine_getBlobsV3" + "engine_getBlobsV3", + "engine_getInclusionListV1", ]) # I'm trying to keep the handlers below very thin, and move the @@ -54,31 +57,39 @@ proc setupEngineAPI*(engine: BeaconEngineRef, server: RpcServer) = proc engine_exchangeCapabilities(methods: seq[string]): seq[string] = return methods.filterIt(supportedMethods.contains(it)) - proc engine_newPayloadV1(payload: ExecutionPayloadV1): PayloadStatusV1 {.async: (raises: [CancelledError, ApplicationError, RlpError]).} = + proc engine_newPayloadV1(payload: ExecutionPayloadV1): PayloadStatus {.async: (raises: [CancelledError, ApplicationError, RlpError]).} = await engine.newPayload(Version.V1, payload.executionPayload) - proc engine_newPayloadV2(payload: ExecutionPayload): PayloadStatusV1 {.async: (raises: [CancelledError, ApplicationError, RlpError]).} = + proc engine_newPayloadV2(payload: ExecutionPayload): PayloadStatus {.async: (raises: [CancelledError, ApplicationError, RlpError]).} = await engine.newPayload(Version.V2, payload) proc engine_newPayloadV3(payload: ExecutionPayload, expectedBlobVersionedHashes: Opt[seq[Hash32]], - parentBeaconBlockRoot: Opt[Hash32]): PayloadStatusV1 {.async: (raises: [CancelledError, ApplicationError, RlpError]).} = + parentBeaconBlockRoot: Opt[Hash32]): PayloadStatus {.async: (raises: [CancelledError, ApplicationError, RlpError]).} = await engine.newPayload(Version.V3, payload, expectedBlobVersionedHashes, parentBeaconBlockRoot) proc engine_newPayloadV4(payload: ExecutionPayload, expectedBlobVersionedHashes: Opt[seq[Hash32]], parentBeaconBlockRoot: Opt[Hash32], - executionRequests: Opt[seq[seq[byte]]]): PayloadStatusV1 {.async: (raises: [CancelledError, ApplicationError, RlpError]).} = + executionRequests: Opt[seq[seq[byte]]]): PayloadStatus {.async: (raises: [CancelledError, ApplicationError, RlpError]).} = await engine.newPayload(Version.V4, payload, expectedBlobVersionedHashes, parentBeaconBlockRoot, executionRequests) proc engine_newPayloadV5(payload: ExecutionPayload, expectedBlobVersionedHashes: Opt[seq[Hash32]], parentBeaconBlockRoot: Opt[Hash32], - executionRequests: Opt[seq[seq[byte]]]): PayloadStatusV1 {.async: (raises: [CancelledError, ApplicationError, RlpError]).} = + executionRequests: Opt[seq[seq[byte]]]): PayloadStatus {.async: (raises: [CancelledError, ApplicationError, RlpError]).} = await engine.newPayload(Version.V5, payload, expectedBlobVersionedHashes, parentBeaconBlockRoot, executionRequests) + proc engine_newPayloadV6(payload: ExecutionPayload, + expectedBlobVersionedHashes: Opt[seq[Hash32]], + parentBeaconBlockRoot: Opt[Hash32], + executionRequests: Opt[seq[seq[byte]]], + inclusionList: Opt[InclusionList]): PayloadStatus {.async: (raises: [CancelledError, ApplicationError, RlpError]).} = + await engine.newPayload(Version.V6, payload, + expectedBlobVersionedHashes, parentBeaconBlockRoot, executionRequests, inclusionList) + proc engine_getPayloadV1(payloadId: Bytes8): ExecutionPayloadV1 {.raises: [CatchableError].} = return engine.getPayload(Version.V1, payloadId).executionPayload.V1 @@ -97,22 +108,26 @@ proc setupEngineAPI*(engine: BeaconEngineRef, server: RpcServer) = proc engine_getPayloadV6(payloadId: Bytes8): GetPayloadV6Response {.raises: [CatchableError].} = return engine.getPayloadV6(payloadId) - proc engine_forkchoiceUpdatedV1(update: ForkchoiceStateV1, + proc engine_forkchoiceUpdatedV1(update: ForkchoiceState, attrs: Opt[PayloadAttributesV1]): ForkchoiceUpdatedResponse {.async: (raises: [CancelledError, ApplicationError]).} = await engine.forkchoiceUpdated(Version.V1, update, attrs.payloadAttributes) - proc engine_forkchoiceUpdatedV2(update: ForkchoiceStateV1, + proc engine_forkchoiceUpdatedV2(update: ForkchoiceState, attrs: Opt[PayloadAttributes]): ForkchoiceUpdatedResponse {.async: (raises: [CancelledError, ApplicationError]).} = await engine.forkchoiceUpdated(Version.V2, update, attrs) - proc engine_forkchoiceUpdatedV3(update: ForkchoiceStateV1, + proc engine_forkchoiceUpdatedV3(update: ForkchoiceState, attrs: Opt[PayloadAttributes]): ForkchoiceUpdatedResponse {.async: (raises: [CancelledError, ApplicationError]).} = await engine.forkchoiceUpdated(Version.V3, update, attrs) - proc engine_forkchoiceUpdatedV4(update: ForkchoiceStateV1, + proc engine_forkchoiceUpdatedV4(update: ForkchoiceState, attrs: Opt[PayloadAttributes]): ForkchoiceUpdatedResponse {.async: (raises: [CancelledError, ApplicationError]).} = await engine.forkchoiceUpdated(Version.V4, update, attrs) + proc engine_forkchoiceUpdatedV5(update: ForkchoiceState, + attrs: Opt[PayloadAttributes]): ForkchoiceUpdatedResponse {.async: (raises: [CancelledError, ApplicationError]).} = + await engine.forkchoiceUpdated(Version.V5, update, attrs) + proc engine_getPayloadBodiesByHashV1(hashes: seq[Hash32]): seq[Opt[ExecutionPayloadBodyV1]] {.raises: [CatchableError].} = return engine.getPayloadBodiesByHashV1(hashes) @@ -150,3 +165,6 @@ proc setupEngineAPI*(engine: BeaconEngineRef, server: RpcServer) = proc engine_getBlobsV3(versionedHashes: seq[VersionedHash]): seq[Opt[BlobAndProofV2]] {.raises: [ApplicationError].} = return engine.getBlobsV3(versionedHashes) + + proc engine_getInclusionListV1(parentHash: Hash32): InclusionList {.raises: [ApplicationError].} = + return engine.getInclusionList(Version.V5, parentHash) diff --git a/hive_integration/engine_client.nim b/hive_integration/engine_client.nim index d30519ab3f..252e048c1c 100644 --- a/hive_integration/engine_client.nim +++ b/hive_integration/engine_client.nim @@ -53,42 +53,64 @@ template wrapTrySimpleRes(body: untyped) = proc forkchoiceUpdatedV1*(client: RpcClient, update: ForkchoiceStateV1, payloadAttributes = Opt.none(PayloadAttributesV1)): - Result[ForkchoiceUpdatedResponse, string] = + Result[ForkchoiceUpdatedResponseV1, string] = wrapTrySimpleRes: client.engine_forkchoiceUpdatedV1(update, payloadAttributes) proc forkchoiceUpdatedV2*(client: RpcClient, update: ForkchoiceStateV1, payloadAttributes = Opt.none(PayloadAttributesV2)): - Result[ForkchoiceUpdatedResponse, string] = + Result[ForkchoiceUpdatedResponseV1, string] = wrapTrySimpleRes: client.engine_forkchoiceUpdatedV2(update, payloadAttributes) proc forkchoiceUpdatedV3*(client: RpcClient, update: ForkchoiceStateV1, payloadAttributes = Opt.none(PayloadAttributesV3)): - Result[ForkchoiceUpdatedResponse, string] = + Result[ForkchoiceUpdatedResponseV1, string] = wrapTrySimpleRes: client.engine_forkchoiceUpdatedV3(update, payloadAttributes) proc forkchoiceUpdatedV4*(client: RpcClient, update: ForkchoiceStateV1, payloadAttributes = Opt.none(PayloadAttributesV4)): - Result[ForkchoiceUpdatedResponse, string] = + Result[ForkchoiceUpdatedResponseV1, string] = wrapTrySimpleRes: client.engine_forkchoiceUpdatedV4(update, payloadAttributes) +proc forkchoiceUpdatedV5*(client: RpcClient, + update: ForkchoiceStateV1, + payloadAttributes = Opt.none(PayloadAttributesV5)): + Result[ForkchoiceUpdatedResponseV2, string] = + wrapTrySimpleRes: + client.engine_forkchoiceUpdatedV5(update, payloadAttributes) + +func fcur(res: Result[ForkchoiceUpdatedResponseV1, string]): + Result[ForkchoiceUpdatedResponse, string] = + if res.isOk: + return ok(res.value.forkchoiceUpdatedResponse) + else: + return err(res.error) + +func fcur(res: Result[ForkchoiceUpdatedResponseV2, string]): + Result[ForkchoiceUpdatedResponse, string] = + if res.isOk: + return ok(res.value.forkchoiceUpdatedResponse) + else: + return err(res.error) + proc forkchoiceUpdated*(client: RpcClient, version: Version, - update: ForkchoiceStateV1, + update: ForkchoiceState, attr = Opt.none(PayloadAttributes)): Result[ForkchoiceUpdatedResponse, string] = case version - of Version.V1: return client.forkchoiceUpdatedV1(update, attr.V1) - of Version.V2: return client.forkchoiceUpdatedV2(update, attr.V2) - of Version.V3: return client.forkchoiceUpdatedV3(update, attr.V3) - of Version.V4: return client.forkchoiceUpdatedV4(update, attr.V4) - of Version.V5, Version.V6: discard + of Version.V1: return client.forkchoiceUpdatedV1(update.V1, attr.V1).fcur + of Version.V2: return client.forkchoiceUpdatedV2(update.V1, attr.V2).fcur + of Version.V3: return client.forkchoiceUpdatedV3(update.V1, attr.V3).fcur + of Version.V4: return client.forkchoiceUpdatedV4(update.V1, attr.V4).fcur + of Version.V5: return client.forkchoiceUpdatedV5(update.V1, attr.V5).fcur + of Version.V6: discard proc getPayloadV1*(client: RpcClient, payloadId: Bytes8): Result[ExecutionPayloadV1, string] = wrapTrySimpleRes: @@ -217,15 +239,26 @@ proc newPayloadV5*(client: RpcClient, client.engine_newPayloadV5(payload, versionedHashes, parentBeaconBlockRoot, executionRequests) +proc newPayloadV6*(client: RpcClient, + payload: ExecutionPayloadV4, + versionedHashes: seq[VersionedHash], + parentBeaconBlockRoot: Hash32, + executionRequests: seq[seq[byte]], + inclusionList: InclusionList): + Result[PayloadStatusV2, string] = + wrapTrySimpleRes: + client.engine_newPayloadV6(payload, versionedHashes, + parentBeaconBlockRoot, executionRequests, inclusionList) + proc newPayloadV1*(client: RpcClient, payload: ExecutionPayload): - Result[PayloadStatusV1, string] = + Result[PayloadStatus, string] = wrapTrySimpleRes: client.engine_newPayloadV1(payload) proc newPayloadV2*(client: RpcClient, payload: ExecutionPayload): - Result[PayloadStatusV1, string] = + Result[PayloadStatus, string] = wrapTrySimpleRes: client.engine_newPayloadV2(payload) @@ -234,7 +267,7 @@ proc newPayloadV3*(client: RpcClient, versionedHashes: Opt[seq[VersionedHash]], parentBeaconBlockRoot: Opt[Hash32] ): - Result[PayloadStatusV1, string] = + Result[PayloadStatus, string] = wrapTrySimpleRes: client.engine_newPayloadV3(payload, versionedHashes, parentBeaconBlockRoot) @@ -243,7 +276,7 @@ proc newPayloadV4*(client: RpcClient, versionedHashes: Opt[seq[VersionedHash]], parentBeaconBlockRoot: Opt[Hash32], executionRequests: Opt[seq[seq[byte]]]): - Result[PayloadStatusV1, string] = + Result[PayloadStatus, string] = wrapTrySimpleRes: client.engine_newPayloadV4(payload, versionedHashes, parentBeaconBlockRoot, executionRequests) @@ -253,14 +286,25 @@ proc newPayloadV5*(client: RpcClient, versionedHashes: Opt[seq[VersionedHash]], parentBeaconBlockRoot: Opt[Hash32], executionRequests: Opt[seq[seq[byte]]]): - Result[PayloadStatusV1, string] = + Result[PayloadStatus, string] = wrapTrySimpleRes: client.engine_newPayloadV5(payload, versionedHashes, parentBeaconBlockRoot, executionRequests) +proc newPayloadV6*(client: RpcClient, + payload: ExecutionPayload, + versionedHashes: Opt[seq[VersionedHash]], + parentBeaconBlockRoot: Opt[Hash32], + executionRequests: Opt[seq[seq[byte]]], + inclusionList: Opt[InclusionList]): + Result[PayloadStatus, string] = + wrapTrySimpleRes: + client.engine_newPayloadV6(payload, versionedHashes, + parentBeaconBlockRoot, executionRequests, inclusionList) + proc newPayload*(client: RpcClient, version: Version, - payload: ExecutableData): Result[PayloadStatusV1, string] = + payload: ExecutableData): Result[PayloadStatus, string] = case version of Version.V1: return client.newPayloadV1(payload.basePayload) @@ -280,7 +324,12 @@ proc newPayload*(client: RpcClient, payload.versionedHashes, payload.beaconRoot, payload.executionRequests) - of Version.V6: discard + of Version.V6: + return client.newPayloadV6(payload.basePayload, + payload.versionedHashes, + payload.beaconRoot, + payload.executionRequests, + payload.inclusionList) proc exchangeCapabilities*(client: RpcClient, methods: seq[string]): diff --git a/hive_integration/types.nim b/hive_integration/types.nim index cc14008d1b..2b8551beac 100644 --- a/hive_integration/types.nim +++ b/hive_integration/types.nim @@ -1,5 +1,5 @@ # Nimbus -# Copyright (c) 2023-2025 Status Research & Development GmbH +# Copyright (c) 2023-2026 Status Research & Development GmbH # Licensed under either of # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or # http://www.apache.org/licenses/LICENSE-2.0) @@ -19,3 +19,4 @@ type beaconRoot* : Opt[Hash32] versionedHashes* : Opt[seq[Hash32]] executionRequests*: Opt[seq[seq[byte]]] + inclusionList* : Opt[InclusionList] diff --git a/tests/eest/eest_engine.nim b/tests/eest/eest_engine.nim index 5af5a648cb..55da1d65df 100644 --- a/tests/eest/eest_engine.nim +++ b/tests/eest/eest_engine.nim @@ -31,7 +31,7 @@ import ../../hive_integration/engine_client, ./eest_helpers -proc sendNewPayload(env: TestEnv, version: uint64, param: PayloadParam): Result[PayloadStatusV1, string] = +proc sendNewPayload(env: TestEnv, version: uint64, param: PayloadParam): Result[PayloadStatus, string] = if not env.client.isSome: return err("Client is not initialized") @@ -58,6 +58,13 @@ proc sendNewPayload(env: TestEnv, version: uint64, param: PayloadParam): Result[ param.versionedHashes, param.parentBeaconBlockRoot, param.executionRequests) + elif version == 6: + env.client.get().newPayloadV6( + param.payload, + param.versionedHashes, + param.parentBeaconBlockRoot, + param.executionRequests, + param.inclusionList) else: err("Unsupported NewPayload version: " & $version) @@ -65,19 +72,21 @@ proc sendFCU(env: TestEnv, version: uint64, param: PayloadParam): Result[Forkcho if not env.client.isSome: return err("Client is not initialized") - let update = ForkchoiceStateV1( + let update = ForkchoiceState( headblockHash: param.payload.blockHash, finalizedblockHash: param.payload.blockHash ) if version == 1: - env.client.get().forkchoiceUpdatedV1(update) + env.client.get().forkchoiceUpdated(Version.V1, update) elif version == 2: - env.client.get().forkchoiceUpdatedV2(update) + env.client.get().forkchoiceUpdated(Version.V2, update) elif version == 3: - env.client.get().forkchoiceUpdatedV3(update) + env.client.get().forkchoiceUpdated(Version.V3, update) elif version == 4: - env.client.get().forkchoiceUpdatedV4(update) + env.client.get().forkchoiceUpdated(Version.V4, update) + elif version == 5: + env.client.get().forkchoiceUpdated(Version.V5, update) else: err("Unsupported FCU version: " & $version) diff --git a/tests/eest/eest_parser.nim b/tests/eest/eest_parser.nim index b2c655c833..6bb0b109f2 100644 --- a/tests/eest/eest_parser.nim +++ b/tests/eest/eest_parser.nim @@ -66,6 +66,7 @@ type versionedHashes*: Opt[seq[Hash32]] parentBeaconBlockRoot*: Opt[Hash32] executionRequests*: Opt[seq[seq[byte]]] + inclusionList*: Opt[InclusionList] PayloadItem* = object params*: PayloadParam diff --git a/tests/test_engine_api.nim b/tests/test_engine_api.nim index 6d4b2ee7f3..f635ce270b 100644 --- a/tests/test_engine_api.nim +++ b/tests/test_engine_api.nim @@ -142,7 +142,7 @@ proc runBasicCycleTest(env: TestEnv): Result[void, string] = let client = env.client header = ? client.latestHeader() - update = ForkchoiceStateV1( + update = ForkchoiceState( headBlockHash: header.computeBlockHash ) time = getTime().toUnix @@ -156,7 +156,7 @@ proc runBasicCycleTest(env: TestEnv): Result[void, string] = payload = ? client.getPayload(Version.V1, fcuRes.payloadId.get) npRes = ? client.newPayloadV1(payload.executionPayload) - discard ? client.forkchoiceUpdated(Version.V1, ForkchoiceStateV1( + discard ? client.forkchoiceUpdated(Version.V1, ForkchoiceState( headBlockHash: npRes.latestValidHash.get )) let bn = ? client.blockNumber() @@ -194,7 +194,7 @@ proc runPayloadRebuildTest(env: TestEnv): Result[void, string] = let client = env.client header = ? client.latestHeader() - update = ForkchoiceStateV1( + update = ForkchoiceState( headBlockHash: header.computeBlockHash ) time = getTime().toUnix @@ -254,7 +254,7 @@ proc runNewPayloadV4Test(env: TestEnv): Result[void, string] = let client = env.client header = ? client.latestHeader() - update = ForkchoiceStateV1( + update = ForkchoiceState( headBlockHash: header.computeBlockHash ) time = getTime().toUnix @@ -331,7 +331,7 @@ proc genesisShouldCanonicalTest(env: TestEnv): Result[void, string] = return err("lastestValidHash should not empty") let - update = ForkchoiceStateV1( + update = ForkchoiceState( headBlockHash: params.payload.blockHash, safeBlockHash: params.payload.parentHash, finalizedBlockHash: params.payload.parentHash, diff --git a/vendor/nim-web3 b/vendor/nim-web3 index aa40059eb5..026ced657c 160000 --- a/vendor/nim-web3 +++ b/vendor/nim-web3 @@ -1 +1 @@ -Subproject commit aa40059eb54f516031025aefccae5c221c0a27a9 +Subproject commit 026ced657c4c96d05334a7420801a43fefe0c662 From 6a89b67ccaa6f1c167c247b364a8b4c774b839e2 Mon Sep 17 00:00:00 2001 From: jangko Date: Thu, 9 Jul 2026 10:02:05 +0700 Subject: [PATCH 2/3] Fix test_engine_api --- tests/test_engine_api.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_engine_api.nim b/tests/test_engine_api.nim index c1dd7004f5..4cb40fa6c2 100644 --- a/tests/test_engine_api.nim +++ b/tests/test_engine_api.nim @@ -275,7 +275,7 @@ proc runSiblingHeadPayloadTest(env: TestEnv): Result[void, string] = client = env.client genesisHeader = ? client.latestHeader() genesisHash = genesisHeader.computeBlockHash - update = ForkchoiceStateV1( + update = ForkchoiceState( headBlockHash: genesisHash ) time = getTime().toUnix @@ -314,7 +314,7 @@ proc runSiblingHeadPayloadTest(env: TestEnv): Result[void, string] = suggestedFeeRecipient: default(Address), withdrawals: Opt.some(newSeq[WithdrawalV1]()), ) - updateC = ForkchoiceStateV1( + updateC = ForkchoiceState( headBlockHash: payloadA.blockHash, finalizedBlockHash: genesisHash, ) @@ -503,7 +503,7 @@ proc payloadAttrV4PreserveWithdrawalsTest(env: TestEnv): Result[void, string] = let client = env.client header = ? client.latestHeader() - update = ForkchoiceStateV1( + update = ForkchoiceState( headBlockHash: header.computeBlockHash ) time = getTime().toUnix From b857a4d0555f2e847bf2da427e298e71b8711c85 Mon Sep 17 00:00:00 2001 From: jangko Date: Thu, 9 Jul 2026 12:15:05 +0700 Subject: [PATCH 3/3] Check for inclusion list for newPayloadV6 --- execution_chain/beacon/api_handler/api_newpayload.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/execution_chain/beacon/api_handler/api_newpayload.nim b/execution_chain/beacon/api_handler/api_newpayload.nim index bb36dd4737..7dbac8e09a 100644 --- a/execution_chain/beacon/api_handler/api_newpayload.nim +++ b/execution_chain/beacon/api_handler/api_newpayload.nim @@ -174,7 +174,7 @@ proc newPayload*(ben: BeaconEngineRef, if res.isSome: return res.value - if apiVersion >= Version.V5: + if apiVersion >= Version.V6: if inclusionList.isNone: raise invalidParams("newPayload" & $apiVersion & ": inclusionList is expected from execution payload")