From 156f0cea0983f954543dc60ef972d9aed0fb0788 Mon Sep 17 00:00:00 2001 From: Pedro Miranda Date: Mon, 16 Sep 2024 23:34:30 +0100 Subject: [PATCH 1/8] new V2 endpoint for beacon getBlockAttestations --- beacon_chain/rpc/rest_beacon_api.nim | 22 ++++++++++++++++ .../eth2_apis/eth2_rest_serialization.nim | 25 +++++++++++++++++++ .../spec/eth2_apis/rest_beacon_calls.nim | 6 +++++ 3 files changed, 53 insertions(+) diff --git a/beacon_chain/rpc/rest_beacon_api.nim b/beacon_chain/rpc/rest_beacon_api.nim index 2808bf914c..d81c5d0a2a 100644 --- a/beacon_chain/rpc/rest_beacon_api.nim +++ b/beacon_chain/rpc/rest_beacon_api.nim @@ -1274,6 +1274,26 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = node.dag.isFinalized(bid) ) + # https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getBlockAttestationsV2 + router.api2(MethodGet, + "/eth/v2/beacon/blocks/{block_id}/attestations") do ( + block_id: BlockIdent) -> RestApiResponse: + let + blockIdent = block_id.valueOr: + return RestApiResponse.jsonError(Http400, InvalidBlockIdValueError, + $error) + bdata = node.getForkedBlock(blockIdent).valueOr: + return RestApiResponse.jsonError(Http404, BlockNotFoundError) + + withBlck(bdata): + let bid = BlockId(root: forkyBlck.root, slot: forkyBlck.message.slot) + RestApiResponse.jsonResponseFinalizedWVersion( + forkyBlck.message.body.attestations.asSeq(), + node.getBlockOptimistic(bdata), + node.dag.isFinalized(bid), + consensusFork + ) + # https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolAttestations router.api2(MethodGet, "/eth/v1/beacon/pool/attestations") do ( slot: Option[Slot], @@ -1302,6 +1322,8 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = res.add(item) RestApiResponse.jsonResponse(res) + + # https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolAttestations router.api2(MethodPost, "/eth/v1/beacon/pool/attestations") do ( contentBody: Option[ContentBody]) -> RestApiResponse: diff --git a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim index b0e49bc468..e47e6bb9c5 100644 --- a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim +++ b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim @@ -695,6 +695,31 @@ proc jsonResponseFinalized*(t: typedesc[RestApiResponse], data: auto, default RestApiResponse.response(res, Http200, "application/json") +proc jsonResponseFinalizedWVersion*(t: typedesc[RestApiResponse], + data: auto, + exec: Opt[bool], + finalized: bool, + version: ConsensusFork): RestApiResponse = + let + headers = [("eth-consensus-version", version.toString())] + res = + block: + var default: seq[byte] + try: + var stream = memoryOutput() + var writer = JsonWriter[RestJson].init(stream) + writer.beginRecord() + writer.writeField("version", version.toString()) + if exec.isSome(): + writer.writeField("execution_optimistic", exec.get()) + writer.writeField("finalized", finalized) + writer.writeField("data", data) + writer.endRecord() + stream.getOutput(seq[byte]) + except IOError: + default + RestApiResponse.response(res, Http200, "application/json", headers = headers) + proc jsonResponseWVersion*(t: typedesc[RestApiResponse], data: auto, version: ConsensusFork): RestApiResponse = let diff --git a/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim b/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim index 370b3dff52..2de04969d7 100644 --- a/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim +++ b/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim @@ -313,6 +313,12 @@ proc getBlockAttestations*(block_id: BlockIdent meth: MethodGet.} ## https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockAttestations +proc getBlockAttestationsV2Plain*(block_id: BlockIdent + ): RestPlainResponse {. + rest, endpoint: "/eth/v2/beacon/blocks/{block_id}/attestations", + meth: MethodGet.} + ## https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getBlockAttestationsV2 + proc getPoolAttestations*( slot: Option[Slot], committee_index: Option[CommitteeIndex] From 5a667d1c786ffc4526fb1e0d16f67535468454ee Mon Sep 17 00:00:00 2001 From: Pedro Miranda Date: Tue, 17 Sep 2024 15:33:07 +0100 Subject: [PATCH 2/8] nnew GET endpoint version (V2) for getPoolAttestations --- .../attestation_pool.nim | 42 +++++ beacon_chain/rpc/rest_beacon_api.nim | 35 ++++ .../spec/eth2_apis/rest_beacon_calls.nim | 8 + ncli/resttest-rules.json | 156 ++++++++++++++++++ 4 files changed, 241 insertions(+) diff --git a/beacon_chain/consensus_object_pools/attestation_pool.nim b/beacon_chain/consensus_object_pools/attestation_pool.nim index 18d648249a..4ad6fc2411 100644 --- a/beacon_chain/consensus_object_pools/attestation_pool.nim +++ b/beacon_chain/consensus_object_pools/attestation_pool.nim @@ -570,6 +570,48 @@ iterator attestations*( for v in entry.aggregates: yield entry.toAttestation(v) +iterator electraAttestations*( + pool: AttestationPool, slot: Opt[Slot], + committee_index: Opt[CommitteeIndex]): electra.Attestation = + let candidateIndices = + if slot.isSome(): + let candidateIdx = pool.candidateIdx(slot.get(), true) + if candidateIdx.isSome(): + candidateIdx.get() .. candidateIdx.get() + else: + 1 .. 0 + else: + 0 ..< pool.electraCandidates.len() + + for candidateIndex in candidateIndices: + for _, entry in pool.electraCandidates[candidateIndex]: + ## data.index field from phase0 is still being used while we have + ## 2 attestations pools (pre and pos electra). Refer to template addAttToPool + ## at addAttestation proc. + if committee_index.isNone() or entry.data.index == committee_index.get(): + var committee_bits: AttestationCommitteeBits + committee_bits[int(entry.data.index)] = true + + var singleAttestation = electra.Attestation( + aggregation_bits: ElectraCommitteeValidatorsBits.init(entry.committee_len), + committee_bits: committee_bits, + data: AttestationData( + slot: entry.data.slot, + index: 0, + beacon_block_root: entry.data.beacon_block_root, + source: entry.data.source, + target: entry.data.target) + ) + + for index, signature in entry.singles: + singleAttestation.aggregation_bits.setBit(index) + singleAttestation.signature = signature.toValidatorSig() + yield singleAttestation + singleAttestation.aggregation_bits.clearBit(index) + + for v in entry.aggregates: + yield entry.toElectraAttestation(v) + type AttestationCacheKey = (Slot, uint64) AttestationCache[CVBType] = Table[AttestationCacheKey, CVBType] ##\ diff --git a/beacon_chain/rpc/rest_beacon_api.nim b/beacon_chain/rpc/rest_beacon_api.nim index d81c5d0a2a..7f238640ba 100644 --- a/beacon_chain/rpc/rest_beacon_api.nim +++ b/beacon_chain/rpc/rest_beacon_api.nim @@ -1322,7 +1322,42 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = res.add(item) RestApiResponse.jsonResponse(res) + # https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getPoolAttestationsV2 + router.api2(MethodGet, "/eth/v2/beacon/pool/attestations") do ( + slot: Option[Slot], + committee_index: Option[CommitteeIndex]) -> RestApiResponse: + let vindex = + if committee_index.isSome(): + let rindex = committee_index.get() + if rindex.isErr(): + return RestApiResponse.jsonError(Http400, + InvalidCommitteeIndexValueError, + $rindex.error) + Opt.some(rindex.get()) + else: + Opt.none(CommitteeIndex) + let vslot = + if slot.isSome(): + let rslot = slot.get() + if rslot.isErr(): + return RestApiResponse.jsonError(Http400, InvalidSlotValueError, + $rslot.error) + Opt.some(rslot.get()) + else: + Opt.none(Slot) + withConsensusFork( + node.dag.cfg.consensusForkAtEpoch(vslot.get().epoch)): + if consensusFork < ConsensusFork.Electra: + var res: seq[phase0.Attestation] + for item in node.attestationPool[].attestations(vslot, vindex): + res.add(item) + return RestApiResponse.jsonResponseWVersion(res, consensusFork) + else: + var res: seq[electra.Attestation] + for item in node.attestationPool[].electraAttestations(vslot, vindex): + res.add(item) + return RestApiResponse.jsonResponseWVersion(res, consensusFork) # https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolAttestations router.api2(MethodPost, "/eth/v1/beacon/pool/attestations") do ( diff --git a/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim b/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim index 2de04969d7..f1529d02f5 100644 --- a/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim +++ b/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim @@ -327,6 +327,14 @@ proc getPoolAttestations*( meth: MethodGet.} ## https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolAttestations +proc getPoolAttestationsV2*( + slot: Option[Slot], + committee_index: Option[CommitteeIndex] + ): RestPlainResponse {. + rest, endpoint: "/eth/v2/beacon/pool/attestations", + meth: MethodGet.} + ## https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getPoolAttestationsV2 + proc submitPoolAttestations*(body: seq[phase0.Attestation]): RestPlainResponse {. rest, endpoint: "/eth/v1/beacon/pool/attestations", diff --git a/ncli/resttest-rules.json b/ncli/resttest-rules.json index 071a8c38bf..93ec197e30 100644 --- a/ncli/resttest-rules.json +++ b/ncli/resttest-rules.json @@ -3807,6 +3807,162 @@ "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] } }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v2/beacon/pool/attestations", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"],"value": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?slot=0", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"],"value": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?slot=18446744073709551615", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"],"value": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?slot=18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?slot=word", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?committee_index=0", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"],"value": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?committee_index=18446744073709551615", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?committee_index=18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?committee_index=word", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?slot=0&committee_index=0", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"],"value": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?slot=word&committee_index=word", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?slot=18446744073709551615&committee_index=18446744073709551615", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?slot=18446744073709551616&committee_index=18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, { "topics": ["beacon", "pool_attester_slashings"], "request": { From 65ee98c2c60690e1e585b3c6ed264dc44e501b17 Mon Sep 17 00:00:00 2001 From: Pedro Miranda Date: Tue, 17 Sep 2024 15:56:51 +0100 Subject: [PATCH 3/8] new POST endpoint version (V2) for submitPoolAttestations --- beacon_chain/rpc/rest_beacon_api.nim | 47 +++++++++++++++++++ .../spec/eth2_apis/rest_beacon_calls.nim | 7 +++ 2 files changed, 54 insertions(+) diff --git a/beacon_chain/rpc/rest_beacon_api.nim b/beacon_chain/rpc/rest_beacon_api.nim index 7f238640ba..404c8b2764 100644 --- a/beacon_chain/rpc/rest_beacon_api.nim +++ b/beacon_chain/rpc/rest_beacon_api.nim @@ -1406,6 +1406,53 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = else: RestApiResponse.jsonMsgResponse(AttestationValidationSuccess) + # https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/submitPoolAttestationsV2 + router.api2(MethodPost, "/eth/v2/beacon/pool/attestations") do ( + contentBody: Option[ContentBody]) -> RestApiResponse: + let attestations = + block: + if contentBody.isNone(): + return RestApiResponse.jsonError(Http400, EmptyRequestBodyError) + let dres = decodeBody(seq[phase0.Attestation], contentBody.get()) + if dres.isErr(): + return RestApiResponse.jsonError(Http400, + InvalidAttestationObjectError, + $dres.error) + dres.get() + + # Since our validation logic supports batch processing, we will submit all + # attestations for validation. + let pending = + block: + var res: seq[Future[SendResult]] + for attestation in attestations: + res.add(node.router.routeAttestation(attestation)) + res + let failures = + block: + var res: seq[RestIndexedErrorMessageItem] + await allFutures(pending) + for index, future in pending: + if future.completed(): + let fres = future.value() + if fres.isErr(): + let failure = RestIndexedErrorMessageItem(index: index, + message: $fres.error) + res.add(failure) + elif future.failed() or future.cancelled(): + # This is unexpected failure, so we log the error message. + let exc = future.error() + let failure = RestIndexedErrorMessageItem(index: index, + message: $exc.msg) + res.add(failure) + res + + if len(failures) > 0: + RestApiResponse.jsonErrorList(Http400, AttestationValidationError, + failures) + else: + RestApiResponse.jsonMsgResponse(AttestationValidationSuccess) + # https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolAttesterSlashings router.api2(MethodGet, "/eth/v1/beacon/pool/attester_slashings") do ( ) -> RestApiResponse: diff --git a/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim b/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim index f1529d02f5..84b6f66449 100644 --- a/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim +++ b/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim @@ -341,6 +341,13 @@ proc submitPoolAttestations*(body: seq[phase0.Attestation]): meth: MethodPost.} ## https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolAttestations +proc submitPoolAttestationsV2*( + body: seq[phase0.Attestation] | seq[electra.Attestation]): + RestPlainResponse {. + rest, endpoint: "/eth/v2/beacon/pool/attestations", + meth: MethodPost.} + ## https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/submitPoolAttestationsV2 + proc getPoolAttesterSlashings*(): RestResponse[GetPoolAttesterSlashingsResponse] {. rest, endpoint: "/eth/v1/beacon/pool/attester_slashings", meth: MethodGet.} From 1115e4d8fb0a82e39e080179b410f56a2ec23aa8 Mon Sep 17 00:00:00 2001 From: Pedro Miranda Date: Tue, 17 Sep 2024 22:50:21 +0100 Subject: [PATCH 4/8] remove premature ncli tests --- .../spec/eth2_apis/rest_beacon_calls.nim | 2 +- ncli/resttest-rules.json | 156 ------------------ 2 files changed, 1 insertion(+), 157 deletions(-) diff --git a/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim b/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim index 84b6f66449..4620ccaba2 100644 --- a/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim +++ b/beacon_chain/spec/eth2_apis/rest_beacon_calls.nim @@ -327,7 +327,7 @@ proc getPoolAttestations*( meth: MethodGet.} ## https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolAttestations -proc getPoolAttestationsV2*( +proc getPoolAttestationsV2Plain*( slot: Option[Slot], committee_index: Option[CommitteeIndex] ): RestPlainResponse {. diff --git a/ncli/resttest-rules.json b/ncli/resttest-rules.json index 93ec197e30..071a8c38bf 100644 --- a/ncli/resttest-rules.json +++ b/ncli/resttest-rules.json @@ -3807,162 +3807,6 @@ "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] } }, - { - "topics": ["beacon", "pool_attestations"], - "request": { - "url": "/eth/v2/beacon/pool/attestations", - "headers": {"Accept": "application/json"} - }, - "response": { - "status": {"operator": "equals", "value": "200"}, - "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmps", "start": ["data"],"value": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] - } - }, - { - "topics": ["beacon", "pool_attestations"], - "request": { - "url": "/eth/v2/beacon/pool/attestations?slot=0", - "headers": {"Accept": "application/json"} - }, - "response": { - "status": {"operator": "equals", "value": "200"}, - "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmps", "start": ["data"],"value": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] - } - }, - { - "topics": ["beacon", "pool_attestations"], - "request": { - "url": "/eth/v2/beacon/pool/attestations?slot=18446744073709551615", - "headers": {"Accept": "application/json"} - }, - "response": { - "status": {"operator": "equals", "value": "200"}, - "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmps", "start": ["data"],"value": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] - } - }, - { - "topics": ["beacon", "pool_attestations"], - "request": { - "url": "/eth/v2/beacon/pool/attestations?slot=18446744073709551616", - "headers": {"Accept": "application/json"} - }, - "response": { - "status": {"operator": "equals", "value": "400"}, - "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] - } - }, - { - "topics": ["beacon", "pool_attestations"], - "request": { - "url": "/eth/v2/beacon/pool/attestations?slot=word", - "headers": {"Accept": "application/json"} - }, - "response": { - "status": {"operator": "equals", "value": "400"}, - "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] - } - }, - { - "topics": ["beacon", "pool_attestations"], - "request": { - "url": "/eth/v2/beacon/pool/attestations?committee_index=0", - "headers": {"Accept": "application/json"} - }, - "response": { - "status": {"operator": "equals", "value": "200"}, - "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmps", "start": ["data"],"value": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] - } - }, - { - "topics": ["beacon", "pool_attestations"], - "request": { - "url": "/eth/v2/beacon/pool/attestations?committee_index=18446744073709551615", - "headers": {"Accept": "application/json"} - }, - "response": { - "status": {"operator": "equals", "value": "400"}, - "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] - } - }, - { - "topics": ["beacon", "pool_attestations"], - "request": { - "url": "/eth/v2/beacon/pool/attestations?committee_index=18446744073709551616", - "headers": {"Accept": "application/json"} - }, - "response": { - "status": {"operator": "equals", "value": "400"}, - "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] - } - }, - { - "topics": ["beacon", "pool_attestations"], - "request": { - "url": "/eth/v2/beacon/pool/attestations?committee_index=word", - "headers": {"Accept": "application/json"} - }, - "response": { - "status": {"operator": "equals", "value": "400"}, - "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] - } - }, - { - "topics": ["beacon", "pool_attestations"], - "request": { - "url": "/eth/v2/beacon/pool/attestations?slot=0&committee_index=0", - "headers": {"Accept": "application/json"} - }, - "response": { - "status": {"operator": "equals", "value": "200"}, - "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmps", "start": ["data"],"value": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] - } - }, - { - "topics": ["beacon", "pool_attestations"], - "request": { - "url": "/eth/v2/beacon/pool/attestations?slot=word&committee_index=word", - "headers": {"Accept": "application/json"} - }, - "response": { - "status": {"operator": "equals", "value": "400"}, - "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] - } - }, - { - "topics": ["beacon", "pool_attestations"], - "request": { - "url": "/eth/v2/beacon/pool/attestations?slot=18446744073709551615&committee_index=18446744073709551615", - "headers": {"Accept": "application/json"} - }, - "response": { - "status": {"operator": "equals", "value": "400"}, - "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] - } - }, - { - "topics": ["beacon", "pool_attestations"], - "request": { - "url": "/eth/v2/beacon/pool/attestations?slot=18446744073709551616&committee_index=18446744073709551616", - "headers": {"Accept": "application/json"} - }, - "response": { - "status": {"operator": "equals", "value": "400"}, - "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] - } - }, { "topics": ["beacon", "pool_attester_slashings"], "request": { From ecc9fa32dd61bfc403802c1e255761050684701e Mon Sep 17 00:00:00 2001 From: Pedro Miranda Date: Wed, 18 Sep 2024 13:07:17 +0100 Subject: [PATCH 5/8] review improvements --- beacon_chain/rpc/rest_beacon_api.nim | 75 ++++++++++++---------- beacon_chain/validators/message_router.nim | 6 +- 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/beacon_chain/rpc/rest_beacon_api.nim b/beacon_chain/rpc/rest_beacon_api.nim index 404c8b2764..f0ad3ced51 100644 --- a/beacon_chain/rpc/rest_beacon_api.nim +++ b/beacon_chain/rpc/rest_beacon_api.nim @@ -1347,17 +1347,17 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = Opt.none(Slot) withConsensusFork( - node.dag.cfg.consensusForkAtEpoch(vslot.get().epoch)): - if consensusFork < ConsensusFork.Electra: - var res: seq[phase0.Attestation] - for item in node.attestationPool[].attestations(vslot, vindex): - res.add(item) - return RestApiResponse.jsonResponseWVersion(res, consensusFork) - else: - var res: seq[electra.Attestation] - for item in node.attestationPool[].electraAttestations(vslot, vindex): - res.add(item) - return RestApiResponse.jsonResponseWVersion(res, consensusFork) + node.dag.cfg.consensusForkAtEpoch(vslot.get().epoch)): + if consensusFork < ConsensusFork.Electra: + var res: seq[phase0.Attestation] + for item in node.attestationPool[].attestations(vslot, vindex): + res.add(item) + return RestApiResponse.jsonResponseWVersion(res, consensusFork) + else: + var res: seq[electra.Attestation] + for item in node.attestationPool[].electraAttestations(vslot, vindex): + res.add(item) + return RestApiResponse.jsonResponseWVersion(res, consensusFork) # https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolAttestations router.api2(MethodPost, "/eth/v1/beacon/pool/attestations") do ( @@ -1376,11 +1376,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = # Since our validation logic supports batch processing, we will submit all # attestations for validation. let pending = - block: - var res: seq[Future[SendResult]] - for attestation in attestations: - res.add(node.router.routeAttestation(attestation)) - res + mapIt(attestations, node.router.routeAttestation(it)) let failures = block: var res: seq[RestIndexedErrorMessageItem] @@ -1409,30 +1405,39 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = # https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/submitPoolAttestationsV2 router.api2(MethodPost, "/eth/v2/beacon/pool/attestations") do ( contentBody: Option[ContentBody]) -> RestApiResponse: - let attestations = - block: - if contentBody.isNone(): + + let + headerVersion = request.headers.getString("Eth-Consensus-Version") + consensusVersion = ConsensusFork.init(headerVersion) + if consensusVersion.isNone(): + return RestApiResponse.jsonError(Http400, FailedToObtainConsensusForkError) + + if contentBody.isNone(): return RestApiResponse.jsonError(Http400, EmptyRequestBodyError) - let dres = decodeBody(seq[phase0.Attestation], contentBody.get()) - if dres.isErr(): - return RestApiResponse.jsonError(Http400, - InvalidAttestationObjectError, - $dres.error) - dres.get() - # Since our validation logic supports batch processing, we will submit all - # attestations for validation. - let pending = - block: - var res: seq[Future[SendResult]] - for attestation in attestations: - res.add(node.router.routeAttestation(attestation)) - res + var pendingAttestations: seq[Future[SendResult]] + template decodeAttestations(AttestationType: untyped) = + let dres = decodeBody(seq[AttestationType], contentBody.get()) + if dres.isErr(): + return RestApiResponse.jsonError(Http400, + InvalidAttestationObjectError, + $dres.error) + # Since our validation logic supports batch processing, we will submit all + # attestations for validation. + for attestation in dres.get(): + pendingAttestations.add(node.router.routeAttestation(attestation)) + + case consensusVersion.get(): + of ConsensusFork.Phase0 .. ConsensusFork.Deneb: + decodeAttestations(phase0.Attestation) + of ConsensusFork.Electra: + decodeAttestations(electra.Attestation) + let failures = block: var res: seq[RestIndexedErrorMessageItem] - await allFutures(pending) - for index, future in pending: + await allFutures(pendingAttestations) + for index, future in pendingAttestations: if future.completed(): let fres = future.value() if fres.isErr(): diff --git a/beacon_chain/validators/message_router.nim b/beacon_chain/validators/message_router.nim index a96d865e13..7a65774fa1 100644 --- a/beacon_chain/validators/message_router.nim +++ b/beacon_chain/validators/message_router.nim @@ -235,7 +235,9 @@ proc routeAttestation*( return ok() proc routeAttestation*( - router: ref MessageRouter, attestation: phase0.Attestation | electra.Attestation): + router: ref MessageRouter, + attestation: phase0.Attestation | electra.Attestation, + on_chain: static bool = false): Future[SendResult] {.async: (raises: [CancelledError]).} = # Compute subnet, then route attestation let @@ -252,7 +254,7 @@ proc routeAttestation*( attestation = shortLog(attestation) return committee_index = - shufflingRef.get_committee_index(attestation.committee_index()).valueOr: + shufflingRef.get_committee_index(attestation.committee_index(on_chain)).valueOr: notice "Invalid committee index in attestation", attestation = shortLog(attestation) return err("Invalid committee index in attestation") From 1d6aeb35763bd294f0e7db26625a804f5fefc430 Mon Sep 17 00:00:00 2001 From: Pedro Miranda Date: Mon, 23 Sep 2024 19:26:32 +0100 Subject: [PATCH 6/8] review comments and increased test coverage --- beacon_chain/rpc/rest_beacon_api.nim | 27 ++--- ncli/resttest-rules.json | 156 +++++++++++++++++++++++++++ 2 files changed, 171 insertions(+), 12 deletions(-) diff --git a/beacon_chain/rpc/rest_beacon_api.nim b/beacon_chain/rpc/rest_beacon_api.nim index f0ad3ced51..f42a2944f1 100644 --- a/beacon_chain/rpc/rest_beacon_api.nim +++ b/beacon_chain/rpc/rest_beacon_api.nim @@ -1346,18 +1346,21 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = else: Opt.none(Slot) - withConsensusFork( - node.dag.cfg.consensusForkAtEpoch(vslot.get().epoch)): - if consensusFork < ConsensusFork.Electra: - var res: seq[phase0.Attestation] - for item in node.attestationPool[].attestations(vslot, vindex): - res.add(item) - return RestApiResponse.jsonResponseWVersion(res, consensusFork) - else: - var res: seq[electra.Attestation] - for item in node.attestationPool[].electraAttestations(vslot, vindex): - res.add(item) - return RestApiResponse.jsonResponseWVersion(res, consensusFork) + let consensusFork = + block: + if vslot.isNone(): + node.dag.cfg.consensusForkAtEpoch(node.currentSlot().epoch()) + else: + node.dag.cfg.consensusForkAtEpoch(vslot.get().epoch) + + if consensusFork < ConsensusFork.Electra: + return RestApiResponse.jsonResponseWVersion( + toSeq(node.attestationPool[].attestations(vslot, vindex)), + consensusFork) + else: + return RestApiResponse.jsonResponseWVersion( + toSeq(node.attestationPool[].electraAttestations(vslot, vindex)), + consensusFork) # https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolAttestations router.api2(MethodPost, "/eth/v1/beacon/pool/attestations") do ( diff --git a/ncli/resttest-rules.json b/ncli/resttest-rules.json index 071a8c38bf..6d9a2a5114 100644 --- a/ncli/resttest-rules.json +++ b/ncli/resttest-rules.json @@ -3807,6 +3807,162 @@ "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] } }, + { + "topics": ["beacon", "pool_attestations_electra"], + "request": { + "url": "/eth/v2/beacon/pool/attestations", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals", "eth-consensus-version": "electra"}], + "body": [{"operator": "jstructcmps", "start": ["data"],"value": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "pool_attestations_electra"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?slot=0", + "headers": {"Eth-Consensus-Version": "electra", "Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals", "eth-consensus-version": "electra"}], + "body": [{"operator": "jstructcmps", "start": ["data"],"value": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "pool_attestations_electra"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?slot=18446744073709551615", + "headers": {"Eth-Consensus-Version": "electra", "Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals", "eth-consensus-version": "electra"}], + "body": [{"operator": "jstructcmps", "start": ["data"],"value": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "pool_attestations_electra"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?slot=18446744073709551616", + "headers": {"Eth-Consensus-Version": "electra", "Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals", "eth-consensus-version": "electra"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations_electra"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?slot=word", + "headers": {"Eth-Consensus-Version": "electra", "Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals", "eth-consensus-version": "electra"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations_electra"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?committee_index=0", + "headers": {"Eth-Consensus-Version": "electra", "Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals", "eth-consensus-version": "electra"}], + "body": [{"operator": "jstructcmps", "start": ["data"],"value": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "pool_attestations_electra"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?committee_index=18446744073709551615", + "headers": {"Eth-Consensus-Version": "electra", "Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals", "eth-consensus-version": "electra"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations_electra"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?committee_index=18446744073709551616", + "headers": {"Eth-Consensus-Version": "electra", "Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals", "eth-consensus-version": "electra"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations_electra"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?committee_index=word", + "headers": {"Eth-Consensus-Version": "electra", "Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals", "eth-consensus-version": "electra"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations_electra"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?slot=0&committee_index=0", + "headers": {"Eth-Consensus-Version": "electra", "Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals", "eth-consensus-version": "electra"}], + "body": [{"operator": "jstructcmps", "start": ["data"],"value": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "pool_attestations_electra"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?slot=word&committee_index=word", + "headers": {"Eth-Consensus-Version": "electra", "Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals", "eth-consensus-version": "electra"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations_electra"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?slot=18446744073709551615&committee_index=18446744073709551615", + "headers": {"Eth-Consensus-Version": "electra", "Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals", "eth-consensus-version": "electra"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations_electra"], + "request": { + "url": "/eth/v2/beacon/pool/attestations?slot=18446744073709551616&committee_index=18446744073709551616", + "headers": {"Eth-Consensus-Version": "electra", "Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals", "eth-consensus-version": "electra"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, { "topics": ["beacon", "pool_attester_slashings"], "request": { From 98013bec7452ff3054ad12dcc8d11370f0887df5 Mon Sep 17 00:00:00 2001 From: Pedro Miranda Date: Tue, 24 Sep 2024 15:22:43 +0100 Subject: [PATCH 7/8] small improvements --- beacon_chain/rpc/rest_beacon_api.nim | 9 ++++---- .../eth2_apis/eth2_rest_serialization.nim | 22 +++++++++---------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/beacon_chain/rpc/rest_beacon_api.nim b/beacon_chain/rpc/rest_beacon_api.nim index f42a2944f1..750c202a68 100644 --- a/beacon_chain/rpc/rest_beacon_api.nim +++ b/beacon_chain/rpc/rest_beacon_api.nim @@ -1347,11 +1347,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = Opt.none(Slot) let consensusFork = - block: - if vslot.isNone(): - node.dag.cfg.consensusForkAtEpoch(node.currentSlot().epoch()) - else: - node.dag.cfg.consensusForkAtEpoch(vslot.get().epoch) + if vslot.isNone(): + node.dag.cfg.consensusForkAtEpoch(node.currentSlot().epoch()) + else: + node.dag.cfg.consensusForkAtEpoch(vslot.get().epoch) if consensusFork < ConsensusFork.Electra: return RestApiResponse.jsonResponseWVersion( diff --git a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim index e47e6bb9c5..70de88fbc0 100644 --- a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim +++ b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim @@ -808,18 +808,16 @@ proc jsonResponseWMeta*(t: typedesc[RestApiResponse], proc jsonMsgResponse*(t: typedesc[RestApiResponse], msg: string = ""): RestApiResponse = let data = - block: - var default: seq[byte] - try: - var stream = memoryOutput() - var writer = JsonWriter[RestJson].init(stream) - writer.beginRecord() - writer.writeField("code", 200) - writer.writeField("message", msg) - writer.endRecord() - stream.getOutput(seq[byte]) - except IOError: - default + try: + var stream = memoryOutput() + var writer = JsonWriter[RestJson].init(stream) + writer.beginRecord() + writer.writeField("code", 200) + writer.writeField("message", msg) + writer.endRecord() + stream.getOutput(seq[byte]) + except IOError: + default(seq[byte]) RestApiResponse.response(data, Http200, "application/json") proc jsonError*(t: typedesc[RestApiResponse], status: HttpCode = Http200, From e298672f267689bedf178b71aaa151327e4bc716 Mon Sep 17 00:00:00 2001 From: Pedro Miranda Date: Wed, 25 Sep 2024 10:10:43 +0100 Subject: [PATCH 8/8] documentation typos --- beacon_chain/consensus_object_pools/attestation_pool.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beacon_chain/consensus_object_pools/attestation_pool.nim b/beacon_chain/consensus_object_pools/attestation_pool.nim index 4ad6fc2411..be4b3eac95 100644 --- a/beacon_chain/consensus_object_pools/attestation_pool.nim +++ b/beacon_chain/consensus_object_pools/attestation_pool.nim @@ -586,7 +586,7 @@ iterator electraAttestations*( for candidateIndex in candidateIndices: for _, entry in pool.electraCandidates[candidateIndex]: ## data.index field from phase0 is still being used while we have - ## 2 attestations pools (pre and pos electra). Refer to template addAttToPool + ## 2 attestation pools (pre and post electra). Refer to template addAttToPool ## at addAttestation proc. if committee_index.isNone() or entry.data.index == committee_index.get(): var committee_bits: AttestationCommitteeBits