From 5d11c5229b45b0842605fc8e110050a2bea139bd Mon Sep 17 00:00:00 2001 From: Eugene Kabanov Date: Tue, 8 Oct 2024 02:31:16 +0300 Subject: [PATCH] Add Web3signer electra support (#6607) * Add Electra support to nimbus_signing_node. Add tests. Annotate nimbus_signing_node with asyncraises. * Update AllTests. --- AllTests-mainnet.md | 7 +- beacon_chain/nimbus_signing_node.nim | 32 ++- .../eth2_apis/eth2_rest_serialization.nim | 201 +++++++++++------- beacon_chain/spec/eth2_apis/rest_types.nim | 29 ++- beacon_chain/spec/forks.nim | 79 ++++++- tests/test_signing_node.nim | 54 ++++- 6 files changed, 299 insertions(+), 103 deletions(-) diff --git a/AllTests-mainnet.md b/AllTests-mainnet.md index 637efca96..7d4ea735c 100644 --- a/AllTests-mainnet.md +++ b/AllTests-mainnet.md @@ -782,7 +782,8 @@ OK: 3/3 Fail: 0/3 Skip: 0/3 + Signing SC contribution and proof (getContributionAndProofSignature()) OK + Signing SC message (getSyncCommitteeMessage()) OK + Signing SC selection proof (getSyncCommitteeSelectionProof()) OK -+ Signing aggregate and proof (getAggregateAndProofSignature()) OK ++ Signing aggregate and proof (getAggregateAndProofSignature(electra)) OK ++ Signing aggregate and proof (getAggregateAndProofSignature(phase0)) OK + Signing aggregation slot (getSlotSignature()) OK + Signing attestation (getAttestationSignature()) OK + Signing deposit message (getDepositMessageSignature()) OK @@ -791,7 +792,7 @@ OK: 3/3 Fail: 0/3 Skip: 0/3 + Signing voluntary exit (getValidatorExitSignature()) OK + Waiting for signing node (/upcheck) test OK ``` -OK: 16/16 Fail: 0/16 Skip: 0/16 +OK: 17/17 Fail: 0/17 Skip: 0/17 ## Old database versions [Preset: mainnet] ```diff + pre-1.1.0 OK @@ -1128,4 +1129,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2 OK: 9/9 Fail: 0/9 Skip: 0/9 ---TOTAL--- -OK: 765/770 Fail: 0/770 Skip: 5/770 +OK: 766/771 Fail: 0/771 Skip: 5/771 diff --git a/beacon_chain/nimbus_signing_node.nim b/beacon_chain/nimbus_signing_node.nim index 5c4beeff2..503a5506c 100644 --- a/beacon_chain/nimbus_signing_node.nim +++ b/beacon_chain/nimbus_signing_node.nim @@ -71,14 +71,14 @@ proc start(sn: SigningNodeRef) = of SigningNodeKind.NonSecure: sn.signingServer.nserver.start() -proc stop(sn: SigningNodeRef) {.async.} = +proc stop(sn: SigningNodeRef) {.async: (raises: []).} = case sn.signingServer.kind of SigningNodeKind.Secure: await sn.signingServer.sserver.stop() of SigningNodeKind.NonSecure: await sn.signingServer.nserver.stop() -proc close(sn: SigningNodeRef) {.async.} = +proc close(sn: SigningNodeRef) {.async: (raises: []).} = case sn.signingServer.kind of SigningNodeKind.Secure: await sn.signingServer.sserver.closeWait() @@ -209,6 +209,15 @@ proc installApiHandlers*(node: SigningNodeRef) = forkInfo.genesis_validators_root, request.aggregateAndProof, validator.data.privateKey).toValidatorSig().toHex() signatureResponse(Http200, signature) + of Web3SignerRequestKind.AggregateAndProofV2: + let + forkInfo = request.forkInfo.get() + signature = + withAggregateAndProof(request.forkedAggregateAndProof): + get_aggregate_and_proof_signature(forkInfo.fork, + forkInfo.genesis_validators_root, forkyProof, + validator.data.privateKey).toValidatorSig().toHex() + signatureResponse(Http200, signature) of Web3SignerRequestKind.Attestation: let forkInfo = request.forkInfo.get() @@ -330,7 +339,7 @@ proc installApiHandlers*(node: SigningNodeRef) = validator.data.privateKey).toValidatorSig().toHex() signatureResponse(Http200, signature) -proc asyncInit(sn: SigningNodeRef) {.async.} = +proc asyncInit(sn: SigningNodeRef) {.async: (raises: [SigningNodeError]).} = notice "Launching signing node", version = fullVersionStr, cmdParams = commandLineParams(), config = sn.config @@ -403,7 +412,7 @@ proc asyncInit(sn: SigningNodeRef) {.async.} = raise newException(SigningNodeError, "") SigningNodeServer(kind: SigningNodeKind.NonSecure, nserver: res.get()) -proc asyncRun*(sn: SigningNodeRef) {.async.} = +proc asyncRun*(sn: SigningNodeRef) {.async: (raises: []).} = sn.runKeystoreCachePruningLoopFut = runKeystoreCachePruningLoop(sn.keystoreCache) sn.installApiHandlers() @@ -428,11 +437,18 @@ proc asyncRun*(sn: SigningNodeRef) {.async.} = template runWithSignals(sn: SigningNodeRef, body: untyped): bool = let future = body - discard await race(future, sn.sigintHandleFut, sn.sigtermHandleFut) + try: + discard await race(future, sn.sigintHandleFut, sn.sigtermHandleFut) + except CancelledError: + discard if future.finished(): if future.failed() or future.cancelled(): - discard future.readError() - debug "Signing node initialization failed" + let exc = future.error + if not(isNil(exc)): + debug "Signing node initialization failed", + error_name = $exc.name, reason = $exc.msg + else: + debug "Signing node initialization failed" var pending: seq[Future[void]] if not(sn.sigintHandleFut.finished()): pending.add(cancelAndWait(sn.sigintHandleFut)) @@ -453,7 +469,7 @@ template runWithSignals(sn: SigningNodeRef, body: untyped): bool = await noCancel allFutures(pending) false -proc runSigningNode(config: SigningNodeConf) {.async.} = +proc runSigningNode(config: SigningNodeConf) {.async: (raises: []).} = let sn = SigningNodeRef.new(config) if not sn.runWithSignals(asyncInit sn): return diff --git a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim index c36fe1440..eacc1fcde 100644 --- a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim +++ b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim @@ -1871,6 +1871,72 @@ proc readValue*[T: SomeForkedLightClientObject]( else: reader.raiseUnexpectedValue("Unsupported fork " & $version.get) +## ForkedAggregateAndProof +proc readValue*(reader: var JsonReader[RestJson], + value: var ForkedAggregateAndProof) {. + raises: [IOError, SerializationError].} = + var + version: Opt[ConsensusFork] + data: Opt[JsonString] + + for fieldName {.inject.} in readObjectFields(reader): + case fieldName + of "version": + if version.isSome(): + reader.raiseUnexpectedField("Multiple version fields found", + "ForkedAggregateAndProof") + let vres = reader.readValue(string).toLowerAscii() + version = ConsensusFork.init(vres) + if version.isNone(): + reader.raiseUnexpectedValue("Incorrect version field value") + of "data": + if data.isSome(): + reader.raiseUnexpectedField( + "Multiple '" & fieldName & "' fields found", + "ForkedAggregateAndProof") + data = Opt.some(reader.readValue(JsonString)) + else: + unrecognizedFieldWarning(fieldName, "ForkedAggregateAndProof") + + if version.isNone(): + reader.raiseUnexpectedValue("Field `version` is missing") + if data.isNone(): + reader.raiseUnexpectedValue("Field `data` is missing") + + withConsensusFork(version.get()): + when consensusFork < ConsensusFork.Electra: + let res = + try: + RestJson.decode(string(data.get()), + phase0.AggregateAndProof, + requireAllFields = true, + allowUnknownFields = true) + except SerializationError as exc: + reader.raiseUnexpectedValue( + "Incorrect phase0 aggregated attestation format, [" & + exc.formatMsg("ForkedAggregateAndProof") & "]") + value = ForkedAggregateAndProof.init(res, consensusFork) + else: + let res = + try: + RestJson.decode(string(data.get()), + electra.AggregateAndProof, + requireAllFields = true, + allowUnknownFields = true) + except SerializationError as exc: + reader.raiseUnexpectedValue( + "Incorrect electra aggregated attestation format, [" & + exc.formatMsg("ForkedAggregateAndProof") & "]") + value = ForkedAggregateAndProof.init(res, consensusFork) + +proc writeValue*(writer: var JsonWriter[RestJson], + proof: ForkedAggregateAndProof) {.raises: [IOError].} = + writer.beginRecord() + writer.writeField("version", proof.kind) + withAggregateAndProof(proof): + writer.writeField("data", forkyProof) + writer.endRecord() + ## Web3SignerRequest proc writeValue*( writer: var JsonWriter[RestJson], value: Web3SignerRequest @@ -1895,6 +1961,14 @@ proc writeValue*( if isSome(value.signingRoot): writer.writeField("signingRoot", value.signingRoot) writer.writeField("aggregate_and_proof", value.aggregateAndProof) + of Web3SignerRequestKind.AggregateAndProofV2: + doAssert(value.forkInfo.isSome(), + "forkInfo should be set for this type of request") + writer.writeField("type", "AGGREGATE_AND_PROOF_V2") + writer.writeField("fork_info", value.forkInfo.get()) + if isSome(value.signingRoot): + writer.writeField("signingRoot", value.signingRoot) + writer.writeField("aggregate_and_proof", value.forkedAggregateAndProof) of Web3SignerRequestKind.Attestation: doAssert(value.forkInfo.isSome(), "forkInfo should be set for this type of request") @@ -1998,6 +2072,8 @@ proc readValue*(reader: var JsonReader[RestJson], Web3SignerRequestKind.AggregationSlot of "AGGREGATE_AND_PROOF": Web3SignerRequestKind.AggregateAndProof + of "AGGREGATE_AND_PROOF_V2": + Web3SignerRequestKind.AggregateAndProofV2 of "ATTESTATION": Web3SignerRequestKind.Attestation of "BLOCK_V2": @@ -2055,13 +2131,10 @@ proc readValue*(reader: var JsonReader[RestJson], reader.raiseUnexpectedValue("Field `aggregation_slot` is missing") if forkInfo.isNone(): reader.raiseUnexpectedValue("Field `fork_info` is missing") - let data = - block: - let res = decodeJsonString(Web3SignerAggregationSlotData, data.get()) - if res.isErr(): - reader.raiseUnexpectedValue( - "Incorrect field `aggregation_slot` format") - res.get() + let data = decodeJsonString(Web3SignerAggregationSlotData, + data.get()).valueOr: + reader.raiseUnexpectedValue( + "Incorrect field `aggregation_slot` format") Web3SignerRequest(kind: Web3SignerRequestKind.AggregationSlot, forkInfo: forkInfo, signingRoot: signingRoot, aggregationSlot: data ) @@ -2070,29 +2143,33 @@ proc readValue*(reader: var JsonReader[RestJson], reader.raiseUnexpectedValue("Field `aggregate_and_proof` is missing") if forkInfo.isNone(): reader.raiseUnexpectedValue("Field `fork_info` is missing") - let data = - block: - let res = decodeJsonString(phase0.AggregateAndProof, data.get()) - if res.isErr(): - reader.raiseUnexpectedValue( - "Incorrect field `aggregate_and_proof` format") - res.get() + let data = decodeJsonString(phase0.AggregateAndProof, data.get()).valueOr: + reader.raiseUnexpectedValue( + "Incorrect field `aggregate_and_proof` format") Web3SignerRequest( kind: Web3SignerRequestKind.AggregateAndProof, forkInfo: forkInfo, signingRoot: signingRoot, aggregateAndProof: data ) + of Web3SignerRequestKind.AggregateAndProofV2: + if dataName != "aggregate_and_proof": + reader.raiseUnexpectedValue("Field `aggregate_and_proof` is missing") + if forkInfo.isNone(): + reader.raiseUnexpectedValue("Field `fork_info` is missing") + let data = decodeJsonString(ForkedAggregateAndProof, data.get()).valueOr: + reader.raiseUnexpectedValue( + "Incorrect field `aggregate_and_proof` format") + Web3SignerRequest( + kind: Web3SignerRequestKind.AggregateAndProofV2, + forkInfo: forkInfo, signingRoot: signingRoot, + forkedAggregateAndProof: data + ) of Web3SignerRequestKind.Attestation: if dataName != "attestation": reader.raiseUnexpectedValue("Field `attestation` is missing") if forkInfo.isNone(): reader.raiseUnexpectedValue("Field `fork_info` is missing") - let data = - block: - let res = decodeJsonString(AttestationData, data.get()) - if res.isErr(): - reader.raiseUnexpectedValue( - "Incorrect field `attestation` format") - res.get() + let data = decodeJsonString(AttestationData, data.get()).valueOr: + reader.raiseUnexpectedValue("Incorrect field `attestation` format") Web3SignerRequest( kind: Web3SignerRequestKind.Attestation, forkInfo: forkInfo, signingRoot: signingRoot, attestation: data @@ -2104,13 +2181,9 @@ proc readValue*(reader: var JsonReader[RestJson], reader.raiseUnexpectedValue("Field `beacon_block` is missing") if forkInfo.isNone(): reader.raiseUnexpectedValue("Field `fork_info` is missing") - let data = - block: - let res = decodeJsonString(Web3SignerForkedBeaconBlock, data.get()) - if res.isErr(): - reader.raiseUnexpectedValue( - "Incorrect field `beacon_block` format") - res.get() + let data = decodeJsonString(Web3SignerForkedBeaconBlock, + data.get()).valueOr: + reader.raiseUnexpectedValue("Incorrect field `beacon_block` format") if len(proofs) > 0: Web3SignerRequest( kind: Web3SignerRequestKind.BlockV2, @@ -2125,13 +2198,8 @@ proc readValue*(reader: var JsonReader[RestJson], of Web3SignerRequestKind.Deposit: if dataName != "deposit": reader.raiseUnexpectedValue("Field `deposit` is missing") - let data = - block: - let res = decodeJsonString(Web3SignerDepositData, data.get()) - if res.isErr(): - reader.raiseUnexpectedValue( - "Incorrect field `deposit` format") - res.get() + let data = decodeJsonString(Web3SignerDepositData, data.get()).valueOr: + reader.raiseUnexpectedValue("Incorrect field `deposit` format") Web3SignerRequest( kind: Web3SignerRequestKind.Deposit, signingRoot: signingRoot, deposit: data @@ -2141,13 +2209,9 @@ proc readValue*(reader: var JsonReader[RestJson], reader.raiseUnexpectedValue("Field `randao_reveal` is missing") if forkInfo.isNone(): reader.raiseUnexpectedValue("Field `fork_info` is missing") - let data = - block: - let res = decodeJsonString(Web3SignerRandaoRevealData, data.get()) - if res.isErr(): - reader.raiseUnexpectedValue( - "Incorrect field `randao_reveal` format") - res.get() + let data = decodeJsonString(Web3SignerRandaoRevealData, + data.get()).valueOr: + reader.raiseUnexpectedValue("Incorrect field `randao_reveal` format") Web3SignerRequest( kind: Web3SignerRequestKind.RandaoReveal, forkInfo: forkInfo, signingRoot: signingRoot, randaoReveal: data @@ -2157,13 +2221,8 @@ proc readValue*(reader: var JsonReader[RestJson], reader.raiseUnexpectedValue("Field `voluntary_exit` is missing") if forkInfo.isNone(): reader.raiseUnexpectedValue("Field `fork_info` is missing") - let data = - block: - let res = decodeJsonString(VoluntaryExit, data.get()) - if res.isErr(): - reader.raiseUnexpectedValue( - "Incorrect field `voluntary_exit` format") - res.get() + let data = decodeJsonString(VoluntaryExit, data.get()).valueOr: + reader.raiseUnexpectedValue("Incorrect field `voluntary_exit` format") Web3SignerRequest( kind: Web3SignerRequestKind.VoluntaryExit, forkInfo: forkInfo, signingRoot: signingRoot, voluntaryExit: data @@ -2174,13 +2233,10 @@ proc readValue*(reader: var JsonReader[RestJson], "Field `sync_committee_message` is missing") if forkInfo.isNone(): reader.raiseUnexpectedValue("Field `fork_info` is missing") - let data = - block: - let res = decodeJsonString(Web3SignerSyncCommitteeMessageData, data.get()) - if res.isErr(): - reader.raiseUnexpectedValue( - "Incorrect field `sync_committee_message` format") - res.get() + let data = decodeJsonString(Web3SignerSyncCommitteeMessageData, + data.get()).valueOr: + reader.raiseUnexpectedValue( + "Incorrect field `sync_committee_message` format") Web3SignerRequest( kind: Web3SignerRequestKind.SyncCommitteeMessage, forkInfo: forkInfo, signingRoot: signingRoot, @@ -2192,13 +2248,10 @@ proc readValue*(reader: var JsonReader[RestJson], "Field `sync_aggregator_selection_data` is missing") if forkInfo.isNone(): reader.raiseUnexpectedValue("Field `fork_info` is missing") - let data = - block: - let res = decodeJsonString(SyncAggregatorSelectionData, data.get()) - if res.isErr(): - reader.raiseUnexpectedValue( - "Incorrect field `sync_aggregator_selection_data` format") - res.get() + let data = decodeJsonString(SyncAggregatorSelectionData, + data.get()).valueOr: + reader.raiseUnexpectedValue( + "Incorrect field `sync_aggregator_selection_data` format") Web3SignerRequest( kind: Web3SignerRequestKind.SyncCommitteeSelectionProof, forkInfo: forkInfo, signingRoot: signingRoot, @@ -2210,13 +2263,9 @@ proc readValue*(reader: var JsonReader[RestJson], "Field `contribution_and_proof` is missing") if forkInfo.isNone(): reader.raiseUnexpectedValue("Field `fork_info` is missing") - let data = - block: - let res = decodeJsonString(ContributionAndProof, data.get()) - if res.isErr(): - reader.raiseUnexpectedValue( - "Incorrect field `contribution_and_proof` format") - res.get() + let data = decodeJsonString(ContributionAndProof, data.get()).valueOr: + reader.raiseUnexpectedValue( + "Incorrect field `contribution_and_proof` format") Web3SignerRequest( kind: Web3SignerRequestKind.SyncCommitteeContributionAndProof, forkInfo: forkInfo, signingRoot: signingRoot, @@ -2228,14 +2277,10 @@ proc readValue*(reader: var JsonReader[RestJson], "Field `validator_registration` is missing") if forkInfo.isNone(): reader.raiseUnexpectedValue("Field `fork_info` is missing") - let data = - block: - let res = - decodeJsonString(Web3SignerValidatorRegistration, data.get()) - if res.isErr(): - reader.raiseUnexpectedValue( - "Incorrect field `validator_registration` format") - res.get() + let data = decodeJsonString(Web3SignerValidatorRegistration, + data.get()).valueOr: + reader.raiseUnexpectedValue( + "Incorrect field `validator_registration` format") Web3SignerRequest( kind: Web3SignerRequestKind.ValidatorRegistration, forkInfo: forkInfo, signingRoot: signingRoot, diff --git a/beacon_chain/spec/eth2_apis/rest_types.nim b/beacon_chain/spec/eth2_apis/rest_types.nim index 6afd56e97..95d70205e 100644 --- a/beacon_chain/spec/eth2_apis/rest_types.nim +++ b/beacon_chain/spec/eth2_apis/rest_types.nim @@ -412,8 +412,8 @@ type proof*: seq[Eth2Digest] Web3SignerRequestKind* {.pure.} = enum - AggregationSlot, AggregateAndProof, Attestation, BlockV2, - Deposit, RandaoReveal, VoluntaryExit, SyncCommitteeMessage, + AggregationSlot, AggregateAndProof, AggregateAndProofV2, Attestation, + BlockV2, Deposit, RandaoReveal, VoluntaryExit, SyncCommitteeMessage, SyncCommitteeSelectionProof, SyncCommitteeContributionAndProof, ValidatorRegistration @@ -427,6 +427,9 @@ type of Web3SignerRequestKind.AggregateAndProof: aggregateAndProof* {. serializedFieldName: "aggregate_and_proof".}: phase0.AggregateAndProof + of Web3SignerRequestKind.AggregateAndProofV2: + forkedAggregateAndProof* {. + serializedFieldName: "aggregate_and_proof".}: ForkedAggregateAndProof of Web3SignerRequestKind.Attestation: attestation*: AttestationData of Web3SignerRequestKind.BlockV2: @@ -766,12 +769,22 @@ func init*(t: typedesc[Web3SignerRequest], fork: Fork, aggregateAndProof: data ) -func init*(t: typedesc[Web3SignerRequest], fork: Fork, - genesis_validators_root: Eth2Digest, data: electra.AggregateAndProof, - signingRoot: Opt[Eth2Digest] = Opt.none(Eth2Digest) - ): Web3SignerRequest = - debugComment "doesn't seem specified yet" - Web3SignerRequest() +func init*( + t: typedesc[Web3SignerRequest], + fork: Fork, + genesis_validators_root: Eth2Digest, + data: electra.AggregateAndProof, + signingRoot: Opt[Eth2Digest] = Opt.none(Eth2Digest) +): Web3SignerRequest = + Web3SignerRequest( + kind: Web3SignerRequestKind.AggregateAndProofV2, + forkInfo: Opt.some(Web3SignerForkInfo( + fork: fork, genesis_validators_root: genesis_validators_root + )), + signingRoot: signingRoot, + forkedAggregateAndProof: + ForkedAggregateAndProof.init(data, typeof(data).kind) + ) func init*(t: typedesc[Web3SignerRequest], fork: Fork, genesis_validators_root: Eth2Digest, data: AttestationData, diff --git a/beacon_chain/spec/forks.nim b/beacon_chain/spec/forks.nim index 5dc50ee20..9168f105c 100644 --- a/beacon_chain/spec/forks.nim +++ b/beacon_chain/spec/forks.nim @@ -153,6 +153,23 @@ type deneb_mev.BlindedBeaconBlock | electra_mev.BlindedBeaconBlock + ForkyAggregateAndProof* = + phase0.AggregateAndProof | + electra.AggregateAndProof + + ForkySignedAggregateAndProof* = + phase0.SignedAggregateAndProof | + electra.SignedAggregateAndProof + + ForkedAggregateAndProof* = object + case kind*: ConsensusFork + of ConsensusFork.Phase0: phase0Data*: phase0.AggregateAndProof + of ConsensusFork.Altair: altairData*: phase0.AggregateAndProof + of ConsensusFork.Bellatrix: bellatrixData*: phase0.AggregateAndProof + of ConsensusFork.Capella: capellaData*: phase0.AggregateAndProof + of ConsensusFork.Deneb: denebData*: phase0.AggregateAndProof + of ConsensusFork.Electra: electraData*: electra.AggregateAndProof + ForkedBeaconBlock* = object case kind*: ConsensusFork of ConsensusFork.Phase0: phase0Data*: phase0.BeaconBlock @@ -305,7 +322,8 @@ template kind*( phase0.TrustedBeaconBlockBody | phase0.SigVerifiedSignedBeaconBlock | phase0.MsgTrustedSignedBeaconBlock | - phase0.TrustedSignedBeaconBlock]): ConsensusFork = + phase0.TrustedSignedBeaconBlock | + phase0.AggregateAndProof]): ConsensusFork = ConsensusFork.Phase0 template kind*( @@ -396,7 +414,8 @@ template kind*( electra.SigVerifiedSignedBeaconBlock | electra.MsgTrustedSignedBeaconBlock | electra.TrustedSignedBeaconBlock | - electra_mev.SignedBlindedBeaconBlock]): ConsensusFork = + electra_mev.SignedBlindedBeaconBlock | + electra.AggregateAndProof]): ConsensusFork = ConsensusFork.Electra template BeaconState*(kind: static ConsensusFork): auto = @@ -1221,6 +1240,34 @@ template withStateAndBlck*( template forkyBlck: untyped {.inject, used.} = b.phase0Data body +template withAggregateAndProof*(a: ForkedAggregateAndProof, + body: untyped): untyped = + case a.kind + of ConsensusFork.Electra: + const consensusFork {.inject, used.} = ConsensusFork.Electra + template forkyProof: untyped {.inject.} = a.electraData + body + of ConsensusFork.Deneb: + const consensusFork {.inject, used.} = ConsensusFork.Deneb + template forkyProof: untyped {.inject.} = a.denebData + body + of ConsensusFork.Capella: + const consensusFork {.inject, used.} = ConsensusFork.Capella + template forkyProof: untyped {.inject.} = a.capellaData + body + of ConsensusFork.Bellatrix: + const consensusFork {.inject, used.} = ConsensusFork.Bellatrix + template forkyProof: untyped {.inject.} = a.bellatrixData + body + of ConsensusFork.Altair: + const consensusFork {.inject, used.} = ConsensusFork.Altair + template forkyProof: untyped {.inject.} = a.altairData + body + of ConsensusFork.Phase0: + const consensusFork {.inject, used.} = ConsensusFork.Phase0 + template forkyProof: untyped {.inject.} = a.phase0Data + body + func toBeaconBlockHeader*( blck: SomeForkyBeaconBlock | deneb_mev.BlindedBeaconBlock | electra_mev.BlindedBeaconBlock): BeaconBlockHeader = @@ -1549,3 +1596,31 @@ func committee_index*(v: electra.Attestation, on_chain: static bool): uint64 = {.error: "cannot get single committee_index for on_chain attestation".} else: uint64 v.committee_bits.get_committee_index_one().expect("network attestation") + +template init*(T: type ForkedAggregateAndProof, + proof: phase0.AggregateAndProof, + fork: ConsensusFork): T = + case fork + of ConsensusFork.Phase0: + ForkedAggregateAndProof(kind: ConsensusFork.Phase0, phase0Data: proof) + of ConsensusFork.Altair: + ForkedAggregateAndProof(kind: ConsensusFork.Altair, altairData: proof) + of ConsensusFork.Bellatrix: + ForkedAggregateAndProof(kind: ConsensusFork.Bellatrix, bellatrixData: proof) + of ConsensusFork.Capella: + ForkedAggregateAndProof(kind: ConsensusFork.Capella, capellaData: proof) + of ConsensusFork.Deneb: + ForkedAggregateAndProof(kind: ConsensusFork.Deneb, denebData: proof) + of ConsensusFork.Electra: + raiseAssert $fork & + " fork should not be used for this type of aggregate and proof" + +template init*(T: type ForkedAggregateAndProof, + proof: electra.AggregateAndProof, + fork: ConsensusFork): T = + case fork + of ConsensusFork.Phase0 .. ConsensusFork.Deneb: + raiseAssert $fork & + " fork should not be used for this type of aggregate and proof" + of ConsensusFork.Electra: + ForkedAggregateAndProof(kind: ConsensusFork.Electra, electraData: proof) diff --git a/tests/test_signing_node.nim b/tests/test_signing_node.nim index 0ea5fb00b..0379bd650 100644 --- a/tests/test_signing_node.nim +++ b/tests/test_signing_node.nim @@ -59,7 +59,8 @@ const SigningExpectedFeeRecipient = "0x000095e79eac4d76aab57cb2c1f091d553b36ca0" SigningOtherFeeRecipient = "0x000096e79eac4d76aab57cb2c1f091d553b36ca0" - AgAttestation = "{\"data\":{\"aggregation_bits\":\"0x01\",\"signature\":\"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505\",\"data\":{\"slot\":\"1\",\"index\":\"1\",\"beacon_block_root\":\"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2\",\"source\":{\"epoch\":\"1\",\"root\":\"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2\"},\"target\":{\"epoch\":\"1\",\"root\":\"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2\"}}}}" + AgAttestationPhase0 = "{\"data\":{\"aggregation_bits\":\"0x01\",\"signature\":\"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505\",\"data\":{\"slot\":\"1\",\"index\":\"1\",\"beacon_block_root\":\"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2\",\"source\":{\"epoch\":\"1\",\"root\":\"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2\"},\"target\":{\"epoch\":\"1\",\"root\":\"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2\"}}}}" + AgAttestationElectra = "{\"data\":{\"aggregation_bits\":\"0x01\",\"committee_bits\":\"0x0000000000000001\",\"signature\":\"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505\",\"data\":{\"slot\":\"1\",\"index\":\"1\",\"beacon_block_root\":\"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2\",\"source\":{\"epoch\":\"1\",\"root\":\"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2\"},\"target\":{\"epoch\":\"1\",\"root\":\"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2\"}}}}" CapellaBlock = "{\"message\":{\"slot\":\"5297696\",\"proposer_index\":\"153094\",\"parent_root\":\"0xe6106533af9be918120ead7440a8006c7f123cc3cb7daf1f11d951864abea014\",\"state_root\":\"0xf86196d34500ca25d1f4e7431d4d52f6f85540bcaf97dd0d2ad9ecdb3eebcdf0\",\"body\":{\"randao_reveal\":\"0xa7efee3d5ddceb60810b23e3b5d39734696418f41dfd13a0851c7be7a72acbdceaa61e1db27513801917d72519d1c1040ccfed829faf06abe06d9964949554bf4369134b66de715ea49eb4fecf3e2b7e646f1764a1993e31e53dbc6557929c12\",\"eth1_data\":{\"deposit_root\":\"0x8ec87d7219a3c873fff3bfe206b4f923d1b471ce4ff9d6d6ecc162ef07825e14\",\"deposit_count\":\"259476\",\"block_hash\":\"0x877b6f8332c7397251ff3f0c5cecec105ff7d4cb78251b47f91fd15a86a565ab\"},\"graffiti\":\"\",\"proposer_slashings\":[],\"attester_slashings\":[],\"attestations\":[],\"deposits\":[],\"voluntary_exits\":[],\"sync_aggregate\":{\"sync_committee_bits\":\"0x733dfda7f5ffde5ade73367fcbf7fffeef7fe43777ffdffab9dbad6f7eed5fff9bfec4affdefbfaddf35bf5efbff9ffff9dfd7dbf97fbfcdfaddfeffbf95f75f\",\"sync_committee_signature\":\"0x81fdf76e797f81b0116a1c1ae5200b613c8041115223cd89e8bd5477aab13de6097a9ebf42b130c59527bbb4c96811b809353a17c717549f82d4bd336068ef0b99b1feebd4d2432a69fa77fac12b78f1fcc9d7b59edbeb381adf10b15bc4a520\"},\"execution_payload\":{\"parent_hash\":\"0x14c2242a8cfbce559e84c391f5f16d10d7719751b8558873012dc88ae5a193e8\",\"fee_recipient\":\"$1\",\"state_root\":\"0xdf8d96b2c292736d39e72e25802c2744d34d3d3c616de5b362425cab01f72fa5\",\"receipts_root\":\"0x4938a2bf640846d213b156a1a853548b369cd02917fa63d8766ab665d7930bac\",\"logs_bloom\":\"0x298610600038408c201080013832408850a00bc8f801920121840030a015310010e2a0e0108628110552062811441c84802f43825c4fc82140b036c58025a28800054c80a44025c052090a0f2c209a0400058040019ea0008e589084078048050880930113a2894082e0112408b088382402a851621042212aa40018a408d07e178c68691486411aa9a2809043b000a04c040000065a030028018540b04b1820271d00821b00c29059095022322c10a530060223240416140190056608200063c82248274ba8f0098e402041cd9f451031481a1010b8220824833520490221071898802d206348449116812280014a10a2d1c210100a30010802490f0a221849\",\"prev_randao\":\"0xc061711e135cd40531ec3ee29d17d3824c0e5f80d07f721e792ab83240aa0ab5\",\"block_number\":\"8737497\",\"gas_limit\":\"30000000\",\"gas_used\":\"16367052\",\"timestamp\":\"1680080352\",\"extra_data\":\"0xd883010b05846765746888676f312e32302e32856c696e7578\",\"base_fee_per_gas\":\"231613172261\",\"block_hash\":\"0x5aa9fd22a9238925adb2b038fd6eafc77adabf554051db5bc16ae5168a52eff6\",\"transactions\":[],\"withdrawals\":[]},\"bls_to_execution_changes\":[]}},\"signature\":\"$2\"}" DenebBlockContents = "{\"signed_block\":{\"message\":{\"slot\":\"5297696\",\"proposer_index\":\"153094\",\"parent_root\":\"0xe6106533af9be918120ead7440a8006c7f123cc3cb7daf1f11d951864abea014\",\"state_root\":\"0xf86196d34500ca25d1f4e7431d4d52f6f85540bcaf97dd0d2ad9ecdb3eebcdf0\",\"body\":{\"randao_reveal\":\"0xa7efee3d5ddceb60810b23e3b5d39734696418f41dfd13a0851c7be7a72acbdceaa61e1db27513801917d72519d1c1040ccfed829faf06abe06d9964949554bf4369134b66de715ea49eb4fecf3e2b7e646f1764a1993e31e53dbc6557929c12\",\"eth1_data\":{\"deposit_root\":\"0x8ec87d7219a3c873fff3bfe206b4f923d1b471ce4ff9d6d6ecc162ef07825e14\",\"deposit_count\":\"259476\",\"block_hash\":\"0x877b6f8332c7397251ff3f0c5cecec105ff7d4cb78251b47f91fd15a86a565ab\"},\"graffiti\":\"\",\"proposer_slashings\":[],\"attester_slashings\":[],\"attestations\":[],\"deposits\":[],\"voluntary_exits\":[],\"sync_aggregate\":{\"sync_committee_bits\":\"0x733dfda7f5ffde5ade73367fcbf7fffeef7fe43777ffdffab9dbad6f7eed5fff9bfec4affdefbfaddf35bf5efbff9ffff9dfd7dbf97fbfcdfaddfeffbf95f75f\",\"sync_committee_signature\":\"0x81fdf76e797f81b0116a1c1ae5200b613c8041115223cd89e8bd5477aab13de6097a9ebf42b130c59527bbb4c96811b809353a17c717549f82d4bd336068ef0b99b1feebd4d2432a69fa77fac12b78f1fcc9d7b59edbeb381adf10b15bc4a520\"},\"execution_payload\":{\"parent_hash\":\"0x14c2242a8cfbce559e84c391f5f16d10d7719751b8558873012dc88ae5a193e8\",\"fee_recipient\":\"$1\",\"state_root\":\"0xdf8d96b2c292736d39e72e25802c2744d34d3d3c616de5b362425cab01f72fa5\",\"receipts_root\":\"0x4938a2bf640846d213b156a1a853548b369cd02917fa63d8766ab665d7930bac\",\"logs_bloom\":\"0x298610600038408c201080013832408850a00bc8f801920121840030a015310010e2a0e0108628110552062811441c84802f43825c4fc82140b036c58025a28800054c80a44025c052090a0f2c209a0400058040019ea0008e589084078048050880930113a2894082e0112408b088382402a851621042212aa40018a408d07e178c68691486411aa9a2809043b000a04c040000065a030028018540b04b1820271d00821b00c29059095022322c10a530060223240416140190056608200063c82248274ba8f0098e402041cd9f451031481a1010b8220824833520490221071898802d206348449116812280014a10a2d1c210100a30010802490f0a221849\",\"prev_randao\":\"0xc061711e135cd40531ec3ee29d17d3824c0e5f80d07f721e792ab83240aa0ab5\",\"block_number\":\"8737497\",\"gas_limit\":\"30000000\",\"gas_used\":\"16367052\",\"timestamp\":\"1680080352\",\"extra_data\":\"0xd883010b05846765746888676f312e32302e32856c696e7578\",\"base_fee_per_gas\":\"231613172261\",\"block_hash\":\"0x5aa9fd22a9238925adb2b038fd6eafc77adabf554051db5bc16ae5168a52eff6\",\"transactions\":[],\"withdrawals\":[],\"blob_gas_used\":\"2316131761\",\"excess_blob_gas\":\"231613172261\"},\"bls_to_execution_changes\":[],\"blob_kzg_commitments\":[]}},\"signature\":\"$2\"},\"kzg_proofs\":[],\"blobs\":[]}" @@ -657,13 +658,14 @@ block: sres2.get() == rres2.get() sres3.get() == rres3.get() - asyncTest "Signing aggregate and proof (getAggregateAndProofSignature())": + asyncTest "Signing aggregate and proof " & + "(getAggregateAndProofSignature(phase0))": let contentType = ContentTypeData( mediaType: MediaType.init("application/json")) agAttestation = decodeBytes( GetAggregatedAttestationResponse, - AgAttestation.toOpenArrayByte(0, len(AgAttestation) - 1), + AgAttestationPhase0.toOpenArrayByte(0, len(AgAttestationPhase0) - 1), Opt.some(contentType)).tryGet().data agProof = phase0.AggregateAndProof( aggregator_index: 1'u64, @@ -699,6 +701,50 @@ block: sres2.get() == rres2.get() sres3.get() == rres3.get() + asyncTest "Signing aggregate and proof " & + "(getAggregateAndProofSignature(electra))": + let + contentType = ContentTypeData( + mediaType: MediaType.init("application/json")) + agAttestation = decodeBytes( + GetElectraAggregatedAttestationResponse, + AgAttestationElectra.toOpenArrayByte(0, + len(AgAttestationElectra) - 1), + Opt.some(contentType)).tryGet().data + agProof = electra.AggregateAndProof( + aggregator_index: 1'u64, + aggregate: agAttestation, + selection_proof: ValidatorSig.fromHex(SomeSignature).get()) + sres1 = + await validator1.getAggregateAndProofSignature(SigningFork, + GenesisValidatorsRoot, agProof) + sres2 = + await validator2.getAggregateAndProofSignature(SigningFork, + GenesisValidatorsRoot, agProof) + sres3 = + await validator3.getAggregateAndProofSignature(SigningFork, + GenesisValidatorsRoot, agProof) + rres1 = + await validator4.getAggregateAndProofSignature(SigningFork, + GenesisValidatorsRoot, agProof) + rres2 = + await validator5.getAggregateAndProofSignature(SigningFork, + GenesisValidatorsRoot, agProof) + rres3 = + await validator6.getAggregateAndProofSignature(SigningFork, + GenesisValidatorsRoot, agProof) + + check: + sres1.isOk() + sres2.isOk() + sres3.isOk() + rres1.isOk() + rres2.isOk() + rres3.isOk() + sres1.get() == rres1.get() + sres2.get() == rres2.get() + sres3.get() == rres3.get() + asyncTest "Signing validator registration (getBuilderSignature())": let vdata = default(ValidatorRegistrationV1) @@ -1167,4 +1213,4 @@ block: await client.closeWait() waitFor(shutdownSigningNodeProcess(process)) - removeTestDir(RemoteSignerType.VerifyingWeb3Signer) \ No newline at end of file + removeTestDir(RemoteSignerType.VerifyingWeb3Signer)