Skip to content

Commit 5bf9284

Browse files
authored
Initial public version of the Verifying Web3Signer functionality (#4912)
* Allow the list of proved properties for web3signer to be configured * Document the Web3Signer setups (regular, distributed and verified)
1 parent 3a1c468 commit 5bf9284

File tree

13 files changed

+640
-267
lines changed

13 files changed

+640
-267
lines changed

AllTests-mainnet.md

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,16 @@ OK: 4/4 Fail: 0/4 Skip: 0/4
356356
+ Voluntary exit signatures OK
357357
```
358358
OK: 8/8 Fail: 0/8 Skip: 0/8
359+
## Nimbus remote signer/signing test (verifying-web3signer)
360+
```diff
361+
+ Signing BeaconBlock (getBlockSignature(altair)) OK
362+
+ Signing BeaconBlock (getBlockSignature(bellatrix)) OK
363+
+ Signing BeaconBlock (getBlockSignature(capella)) OK
364+
+ Signing BeaconBlock (getBlockSignature(deneb)) OK
365+
+ Signing BeaconBlock (getBlockSignature(phase0)) OK
366+
+ Waiting for signing node (/upcheck) test OK
367+
```
368+
OK: 6/6 Fail: 0/6 Skip: 0/6
359369
## Nimbus remote signer/signing test (web3signer)
360370
```diff
361371
+ Connection timeout test OK
@@ -382,16 +392,6 @@ OK: 8/8 Fail: 0/8 Skip: 0/8
382392
+ Waiting for signing node (/upcheck) test OK
383393
```
384394
OK: 22/22 Fail: 0/22 Skip: 0/22
385-
## Nimbus remote signer/signing test (web3signer-diva)
386-
```diff
387-
+ Signing BeaconBlock (getBlockSignature(altair)) OK
388-
+ Signing BeaconBlock (getBlockSignature(bellatrix)) OK
389-
+ Signing BeaconBlock (getBlockSignature(capella)) OK
390-
+ Signing BeaconBlock (getBlockSignature(deneb)) OK
391-
+ Signing BeaconBlock (getBlockSignature(phase0)) OK
392-
+ Waiting for signing node (/upcheck) test OK
393-
```
394-
OK: 6/6 Fail: 0/6 Skip: 0/6
395395
## Old database versions [Preset: mainnet]
396396
```diff
397397
+ pre-1.1.0 OK
@@ -420,11 +420,13 @@ OK: 12/12 Fail: 0/12 Skip: 0/12
420420
OK: 1/1 Fail: 0/1 Skip: 0/1
421421
## Remove keystore testing suite
422422
```diff
423+
+ Many remotes OK
424+
+ Single remote OK
425+
+ Verifying Signer / Many remotes OK
426+
+ Verifying Signer / Single remote OK
423427
+ vesion 1 OK
424-
+ vesion 2 many remotes OK
425-
+ vesion 2 single remote OK
426428
```
427-
OK: 3/3 Fail: 0/3 Skip: 0/3
429+
OK: 5/5 Fail: 0/5 Skip: 0/5
428430
## Serialization/deserialization [Beacon Node] [Preset: mainnet]
429431
```diff
430432
+ Deserialization test vectors OK
@@ -667,4 +669,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2
667669
OK: 9/9 Fail: 0/9 Skip: 0/9
668670

669671
---TOTAL---
670-
OK: 380/385 Fail: 0/385 Skip: 5/385
672+
OK: 382/387 Fail: 0/387 Skip: 5/387

beacon_chain/nimbus_signing_node.nim

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
# nimbus_sign_node
2-
# Copyright (c) 2018-2022 Status Research & Development GmbH
1+
# nimbus_signing_node
2+
# Copyright (c) 2018-2023 Status Research & Development GmbH
33
# Licensed and distributed under either of
44
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
55
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
66
# at your option. This file may not be copied, modified, or distributed except according to those terms.
7+
78
import std/[tables, os, strutils]
89
import serialization, json_serialization,
910
json_serialization/std/[options, net],
@@ -256,7 +257,7 @@ proc installApiHandlers*(node: SigningNodeRef) =
256257
let feeRecipientRoot = hash_tree_root(distinctBase(
257258
node.config.expectedFeeRecipient.get()))
258259

259-
if not(is_valid_merkle_branch(feeRecipientRoot, proof.merkleProofs,
260+
if not(is_valid_merkle_branch(feeRecipientRoot, proof.proof,
260261
log2trunc(proof.index),
261262
get_subtree_index(proof.index),
262263
blockHeader.body_root)):

beacon_chain/spec/eth2_apis/rest_types.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ type
568568

569569
Web3SignerMerkleProof* = object
570570
index*: GeneralizedIndex
571-
merkleProofs* {.serializedFieldName: "merkle_proofs".}: seq[Eth2Digest]
571+
proof*: seq[Eth2Digest]
572572

573573
Web3SignerRequestKind* {.pure.} = enum
574574
AggregationSlot, AggregateAndProof, Attestation, Block, BlockV2,

beacon_chain/spec/keystore.nim

Lines changed: 149 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,18 @@ type
143143
ioHandle*: IoLockHandle
144144
opened*: bool
145145

146+
RemoteSignerType* {.pure.} = enum
147+
Web3Signer, VerifyingWeb3Signer
148+
149+
ProvenProperty* = object
150+
path*: string
151+
description*: Option[string]
152+
phase0Index*: Option[GeneralizedIndex]
153+
altairIndex*: Option[GeneralizedIndex]
154+
bellatrixIndex*: Option[GeneralizedIndex]
155+
capellaIndex*: Option[GeneralizedIndex]
156+
denebIndex*: Option[GeneralizedIndex]
157+
146158
KeystoreData* = object
147159
version*: uint64
148160
pubkey*: ValidatorPubKey
@@ -157,7 +169,11 @@ type
157169
flags*: set[RemoteKeystoreFlag]
158170
remotes*: seq[RemoteSignerInfo]
159171
threshold*: uint32
160-
remoteType*: RemoteSignerType
172+
case remoteType*: RemoteSignerType
173+
of RemoteSignerType.Web3Signer:
174+
discard
175+
of RemoteSignerType.VerifyingWeb3Signer:
176+
provenBlockProperties*: seq[ProvenProperty]
161177

162178
NetKeystore* = object
163179
crypto*: Crypto
@@ -166,13 +182,14 @@ type
166182
uuid*: string
167183
version*: int
168184

169-
RemoteSignerType* {.pure.} = enum
170-
Web3Signer, Web3SignerDiva
171-
172185
RemoteKeystore* = object
173186
version*: uint64
174187
description*: Option[string]
175-
remoteType*: RemoteSignerType
188+
case remoteType*: RemoteSignerType
189+
of RemoteSignerType.Web3Signer:
190+
discard
191+
of RemoteSignerType.VerifyingWeb3Signer:
192+
provenBlockProperties*: seq[ProvenProperty]
176193
pubkey*: ValidatorPubKey
177194
flags*: set[RemoteKeystoreFlag]
178195
remotes*: seq[RemoteSignerInfo]
@@ -599,8 +616,9 @@ proc writeValue*(writer: var JsonWriter, value: RemoteKeystore)
599616
case value.remoteType
600617
of RemoteSignerType.Web3Signer:
601618
writer.writeField("type", "web3signer")
602-
of RemoteSignerType.Web3SignerDiva:
603-
writer.writeField("type", "web3signer-diva")
619+
of RemoteSignerType.VerifyingWeb3Signer:
620+
writer.writeField("type", "verifying-web3signer")
621+
writer.writeField("proven_block_properties", value.provenBlockProperties)
604622
if value.description.isSome():
605623
writer.writeField("description", value.description.get())
606624
if RemoteKeystoreFlag.IgnoreSSLVerification in value.flags:
@@ -618,77 +636,139 @@ proc readValue*(reader: var JsonReader, value: var RemoteKeystore)
618636
description: Option[string]
619637
remote: Option[HttpHostUri]
620638
remotes: Option[seq[RemoteSignerInfo]]
621-
remoteType: Option[string]
639+
remoteType: Option[RemoteSignerType]
640+
provenBlockProperties: Option[seq[ProvenProperty]]
622641
ignoreSslVerification: Option[bool]
623642
pubkey: Option[ValidatorPubKey]
624643
threshold: Option[uint32]
625-
implicitVersion1 = false
626644

627645
# TODO: implementing deserializers for versioned objects
628646
# manually is extremely error-prone. This should use
629647
# the auto-generated deserializer from nim-json-serialization
630648
for fieldName in readObjectFields(reader):
631649
case fieldName:
632650
of "pubkey":
633-
if pubkey.isSome():
651+
if pubkey.isSome:
634652
reader.raiseUnexpectedField("Multiple `pubkey` fields found",
635653
"RemoteKeystore")
636654
pubkey = some(reader.readValue(ValidatorPubKey))
637655
of "remote":
638-
if version.isSome and version.get > 1:
639-
reader.raiseUnexpectedField(
640-
"The `remote` field is valid only in version 1 of the remote keystore format",
641-
"RemoteKeystore")
642-
643-
if remote.isSome():
656+
if remote.isSome:
644657
reader.raiseUnexpectedField("Multiple `remote` fields found",
645658
"RemoteKeystore")
659+
if remotes.isSome:
660+
reader.raiseUnexpectedField("The `remote` field cannot be specified together with `remotes`",
661+
"RemoteKeystore")
646662
remote = some(reader.readValue(HttpHostUri))
647-
implicitVersion1 = true
648663
of "remotes":
649-
if remotes.isSome():
664+
if remotes.isSome:
650665
reader.raiseUnexpectedField("Multiple `remote` fields found",
651666
"RemoteKeystore")
667+
if remote.isSome:
668+
reader.raiseUnexpectedField("The `remotes` field cannot be specified together with `remote`",
669+
"RemoteKeystore")
670+
if version.isNone:
671+
reader.raiseUnexpectedField(
672+
"The `remotes` field should be specified after the `version` field of the keystore",
673+
"RemoteKeystore")
674+
if version.get < 2:
675+
reader.raiseUnexpectedField(
676+
"The `remotes` field is valid only past version 2 of the remote keystore format",
677+
"RemoteKeystore")
652678
remotes = some(reader.readValue(seq[RemoteSignerInfo]))
653679
of "version":
654-
if version.isSome():
680+
if version.isSome:
655681
reader.raiseUnexpectedField("Multiple `version` fields found",
656682
"RemoteKeystore")
657683
version = some(reader.readValue(uint64))
658-
if implicitVersion1 and version.get > 1'u64:
659-
reader.raiseUnexpectedValue(
660-
"Remote keystore format doesn't match the specified version number")
661-
if version.get > 2'u64:
684+
if version.get > 3'u64:
662685
reader.raiseUnexpectedValue(
663686
"Remote keystore version " & $version.get &
664687
" requires a more recent version of Nimbus")
665688
of "description":
666-
let res = reader.readValue(string)
667-
if description.isSome():
668-
description = some(description.get() & "\n" & res)
669-
else:
670-
description = some(res)
689+
if description.isSome:
690+
reader.raiseUnexpectedField("Multiple `description` fields found",
691+
"RemoteKeystore")
692+
description = some(reader.readValue(string))
671693
of "ignore_ssl_verification":
672-
if ignoreSslVerification.isSome():
694+
if ignoreSslVerification.isSome:
673695
reader.raiseUnexpectedField("Multiple conflicting options found",
674696
"RemoteKeystore")
675697
ignoreSslVerification = some(reader.readValue(bool))
676698
of "type":
677-
if remoteType.isSome():
699+
if remoteType.isSome:
678700
reader.raiseUnexpectedField("Multiple `type` fields found",
679701
"RemoteKeystore")
680-
remoteType = some(reader.readValue(string))
702+
if version.isNone:
703+
reader.raiseUnexpectedField(
704+
"The `type` field should be specified after the `version` field of the keystore",
705+
"RemoteKeystore")
706+
if version.get < 2:
707+
reader.raiseUnexpectedField(
708+
"The `type` field is valid only past version 2 of the remote keystore format",
709+
"RemoteKeystore")
710+
let remoteTypeValue = case reader.readValue(string).toLowerAscii()
711+
of "web3signer":
712+
RemoteSignerType.Web3Signer
713+
of "verifying-web3signer":
714+
RemoteSignerType.VerifyingWeb3Signer
715+
else:
716+
reader.raiseUnexpectedValue("Unsupported remote signer `type` value")
717+
remoteType = some remoteTypeValue
718+
of "proven_block_properties":
719+
if provenBlockProperties.isSome:
720+
reader.raiseUnexpectedField("Multiple `proven_block_properties` fields found",
721+
"RemoteKeystore")
722+
if version.isNone:
723+
reader.raiseUnexpectedField(
724+
"The `proven_block_properties` field should be specified after the `version` field of the keystore",
725+
"RemoteKeystore")
726+
if version.get < 3:
727+
reader.raiseUnexpectedField(
728+
"The `proven_block_properties` field is valid only past version 3 of the remote keystore format",
729+
"RemoteKeystore")
730+
if remoteType.isNone:
731+
reader.raiseUnexpectedField(
732+
"The `proven_block_properties` field should be specified after the `type` field of the keystore",
733+
"RemoteKeystore")
734+
if remoteType.get != RemoteSignerType.VerifyingWeb3Signer:
735+
reader.raiseUnexpectedField(
736+
"The `proven_block_properties` field can be specified only when the remote signer type is 'verifying-web3signer'",
737+
"RemoteKeystore")
738+
var provenProperties = reader.readValue(seq[ProvenProperty])
739+
for prop in provenProperties.mitems:
740+
if prop.path == ".execution_payload.fee_recipient":
741+
prop.bellatrixIndex = some GeneralizedIndex(401)
742+
prop.capellaIndex = some GeneralizedIndex(401)
743+
prop.denebIndex = some GeneralizedIndex(401)
744+
elif prop.path == ".graffiti":
745+
prop.bellatrixIndex = some GeneralizedIndex(18)
746+
prop.capellaIndex = some GeneralizedIndex(18)
747+
prop.denebIndex = some GeneralizedIndex(18)
748+
else:
749+
reader.raiseUnexpectedValue("Keystores with proven properties different than " &
750+
"`.execution_payload.fee_recipient` and `.graffiti` " &
751+
"require a more recent version of Nimbus")
752+
provenBlockProperties = some provenProperties
681753
of "threshold":
682-
if threshold.isSome():
754+
if threshold.isSome:
683755
reader.raiseUnexpectedField("Multiple `threshold` fields found",
684756
"RemoteKeystore")
757+
if version.isNone:
758+
reader.raiseUnexpectedField(
759+
"The `threshold` field should be specified after the `version` field of the keystore",
760+
"RemoteKeystore")
761+
if version.get < 2:
762+
reader.raiseUnexpectedField(
763+
"The `threshold` field is valid only past version 2 of the remote keystore format",
764+
"RemoteKeystore")
685765
threshold = some(reader.readValue(uint32))
686766
else:
687767
# Ignore unknown field names.
688768
discard
689769

690770
if version.isNone():
691-
reader.raiseUnexpectedValue("Field `version` is missing")
771+
reader.raiseUnexpectedValue("The required field `version` is missing")
692772
if remotes.isNone():
693773
if remote.isSome and pubkey.isSome:
694774
remotes = some @[RemoteSignerInfo(
@@ -697,22 +777,27 @@ proc readValue*(reader: var JsonReader, value: var RemoteKeystore)
697777
url: remote.get
698778
)]
699779
else:
700-
reader.raiseUnexpectedValue("Field `remotes` is missing")
780+
reader.raiseUnexpectedValue("The required field `remotes` is missing")
781+
782+
if threshold.isNone:
783+
if remotes.get.len > 1:
784+
reader.raiseUnexpectedValue("The `threshold` field must be specified when using distributed keystores")
785+
else:
786+
if threshold.get.uint64 > remotes.get.lenu64:
787+
reader.raiseUnexpectedValue("The specified `threshold` must be lower than the number of remote signers")
788+
701789
if pubkey.isNone():
702790
reader.raiseUnexpectedValue("Field `pubkey` is missing")
703791

704-
let keystoreType =
705-
if remoteType.isSome():
706-
let res = remoteType.get()
707-
case res.toLowerAscii()
708-
of "web3signer":
709-
RemoteSignerType.Web3Signer
710-
of "web3signer-diva":
711-
RemoteSignerType.Web3SignerDiva
712-
else:
713-
reader.raiseUnexpectedValue("Unsupported remote signer `type` value")
714-
else:
715-
RemoteSignerType.Web3Signer
792+
if version.get >= 3:
793+
if remoteType.isNone:
794+
reader.raiseUnexpectedValue("The required field `type` is missing")
795+
case remoteType.get
796+
of RemoteSignerType.Web3Signer:
797+
discard
798+
of RemoteSignerType.VerifyingWeb3Signer:
799+
if provenBlockProperties.isNone:
800+
reader.raiseUnexpectedValue("The required field `proven_block_properties` is missing")
716801

717802
let keystoreFlags =
718803
block:
@@ -721,14 +806,24 @@ proc readValue*(reader: var JsonReader, value: var RemoteKeystore)
721806
res.incl(RemoteKeystoreFlag.IgnoreSSLVerification)
722807
res
723808

724-
value = RemoteKeystore(
725-
version: 2'u64,
726-
pubkey: pubkey.get,
727-
description: description,
728-
remoteType: keystoreType,
729-
remotes: remotes.get,
730-
threshold: threshold.get(1),
731-
)
809+
value = case remoteType.get(RemoteSignerType.Web3Signer)
810+
of RemoteSignerType.Web3Signer:
811+
RemoteKeystore(
812+
version: 2'u64,
813+
pubkey: pubkey.get,
814+
description: description,
815+
remoteType: RemoteSignerType.Web3Signer,
816+
remotes: remotes.get,
817+
threshold: threshold.get(1))
818+
of RemoteSignerType.VerifyingWeb3Signer:
819+
RemoteKeystore(
820+
version: 2'u64,
821+
pubkey: pubkey.get,
822+
description: description,
823+
remoteType: RemoteSignerType.VerifyingWeb3Signer,
824+
provenBlockProperties: provenBlockProperties.get,
825+
remotes: remotes.get,
826+
threshold: threshold.get(1))
732827

733828
template writeValue*(w: var JsonWriter,
734829
value: Pbkdf2Salt|SimpleHexEncodedTypes|Aes128CtrIv) =

beacon_chain/spec/mev/bellatrix_mev.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ type
3535
signature*: ValidatorSig
3636

3737
# https://github.com/ethereum/builder-specs/blob/v0.3.0/specs/bellatrix/builder.md#blindedbeaconblockbody
38-
BlindedBeaconBlockBody = object
38+
BlindedBeaconBlockBody* = object
3939
randao_reveal*: ValidatorSig
4040
eth1_data*: Eth1Data
4141
graffiti*: GraffitiBytes

0 commit comments

Comments
 (0)