Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial public version of the Verifying Web3Signer functionality #4912

Merged
merged 1 commit into from
May 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 16 additions & 14 deletions AllTests-mainnet.md
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,16 @@ OK: 4/4 Fail: 0/4 Skip: 0/4
+ Voluntary exit signatures OK
```
OK: 8/8 Fail: 0/8 Skip: 0/8
## Nimbus remote signer/signing test (verifying-web3signer)
```diff
+ Signing BeaconBlock (getBlockSignature(altair)) OK
+ Signing BeaconBlock (getBlockSignature(bellatrix)) OK
+ Signing BeaconBlock (getBlockSignature(capella)) OK
+ Signing BeaconBlock (getBlockSignature(deneb)) OK
+ Signing BeaconBlock (getBlockSignature(phase0)) OK
+ Waiting for signing node (/upcheck) test OK
```
OK: 6/6 Fail: 0/6 Skip: 0/6
## Nimbus remote signer/signing test (web3signer)
```diff
+ Connection timeout test OK
Expand All @@ -382,16 +392,6 @@ OK: 8/8 Fail: 0/8 Skip: 0/8
+ Waiting for signing node (/upcheck) test OK
```
OK: 22/22 Fail: 0/22 Skip: 0/22
## Nimbus remote signer/signing test (web3signer-diva)
```diff
+ Signing BeaconBlock (getBlockSignature(altair)) OK
+ Signing BeaconBlock (getBlockSignature(bellatrix)) OK
+ Signing BeaconBlock (getBlockSignature(capella)) OK
+ Signing BeaconBlock (getBlockSignature(deneb)) OK
+ Signing BeaconBlock (getBlockSignature(phase0)) OK
+ Waiting for signing node (/upcheck) test OK
```
OK: 6/6 Fail: 0/6 Skip: 0/6
## Old database versions [Preset: mainnet]
```diff
+ pre-1.1.0 OK
Expand Down Expand Up @@ -420,11 +420,13 @@ OK: 12/12 Fail: 0/12 Skip: 0/12
OK: 1/1 Fail: 0/1 Skip: 0/1
## Remove keystore testing suite
```diff
+ Many remotes OK
+ Single remote OK
+ Verifying Signer / Many remotes OK
+ Verifying Signer / Single remote OK
+ vesion 1 OK
+ vesion 2 many remotes OK
+ vesion 2 single remote OK
```
OK: 3/3 Fail: 0/3 Skip: 0/3
OK: 5/5 Fail: 0/5 Skip: 0/5
## Serialization/deserialization [Beacon Node] [Preset: mainnet]
```diff
+ Deserialization test vectors OK
Expand Down Expand Up @@ -667,4 +669,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2
OK: 9/9 Fail: 0/9 Skip: 0/9

---TOTAL---
OK: 380/385 Fail: 0/385 Skip: 5/385
OK: 382/387 Fail: 0/387 Skip: 5/387
7 changes: 4 additions & 3 deletions beacon_chain/nimbus_signing_node.nim
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# nimbus_sign_node
# Copyright (c) 2018-2022 Status Research & Development GmbH
# nimbus_signing_node
# Copyright (c) 2018-2023 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.

import std/[tables, os, strutils]
import serialization, json_serialization,
json_serialization/std/[options, net],
Expand Down Expand Up @@ -256,7 +257,7 @@ proc installApiHandlers*(node: SigningNodeRef) =
let feeRecipientRoot = hash_tree_root(distinctBase(
node.config.expectedFeeRecipient.get()))

if not(is_valid_merkle_branch(feeRecipientRoot, proof.merkleProofs,
if not(is_valid_merkle_branch(feeRecipientRoot, proof.proof,
log2trunc(proof.index),
get_subtree_index(proof.index),
blockHeader.body_root)):
Expand Down
2 changes: 1 addition & 1 deletion beacon_chain/spec/eth2_apis/rest_types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ type

Web3SignerMerkleProof* = object
index*: GeneralizedIndex
merkleProofs* {.serializedFieldName: "merkle_proofs".}: seq[Eth2Digest]
proof*: seq[Eth2Digest]

Web3SignerRequestKind* {.pure.} = enum
AggregationSlot, AggregateAndProof, Attestation, Block, BlockV2,
Expand Down
203 changes: 149 additions & 54 deletions beacon_chain/spec/keystore.nim
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,18 @@ type
ioHandle*: IoLockHandle
opened*: bool

RemoteSignerType* {.pure.} = enum
Web3Signer, VerifyingWeb3Signer

ProvenProperty* = object
path*: string
description*: Option[string]
phase0Index*: Option[GeneralizedIndex]
altairIndex*: Option[GeneralizedIndex]
bellatrixIndex*: Option[GeneralizedIndex]
capellaIndex*: Option[GeneralizedIndex]
denebIndex*: Option[GeneralizedIndex]

KeystoreData* = object
version*: uint64
pubkey*: ValidatorPubKey
Expand All @@ -157,7 +169,11 @@ type
flags*: set[RemoteKeystoreFlag]
remotes*: seq[RemoteSignerInfo]
threshold*: uint32
remoteType*: RemoteSignerType
case remoteType*: RemoteSignerType
of RemoteSignerType.Web3Signer:
discard
of RemoteSignerType.VerifyingWeb3Signer:
provenBlockProperties*: seq[ProvenProperty]

NetKeystore* = object
crypto*: Crypto
Expand All @@ -166,13 +182,14 @@ type
uuid*: string
version*: int

RemoteSignerType* {.pure.} = enum
Web3Signer, Web3SignerDiva

RemoteKeystore* = object
version*: uint64
description*: Option[string]
remoteType*: RemoteSignerType
case remoteType*: RemoteSignerType
of RemoteSignerType.Web3Signer:
discard
of RemoteSignerType.VerifyingWeb3Signer:
provenBlockProperties*: seq[ProvenProperty]
pubkey*: ValidatorPubKey
flags*: set[RemoteKeystoreFlag]
remotes*: seq[RemoteSignerInfo]
Expand Down Expand Up @@ -599,8 +616,9 @@ proc writeValue*(writer: var JsonWriter, value: RemoteKeystore)
case value.remoteType
of RemoteSignerType.Web3Signer:
writer.writeField("type", "web3signer")
of RemoteSignerType.Web3SignerDiva:
writer.writeField("type", "web3signer-diva")
of RemoteSignerType.VerifyingWeb3Signer:
writer.writeField("type", "verifying-web3signer")
writer.writeField("proven_block_properties", value.provenBlockProperties)
if value.description.isSome():
writer.writeField("description", value.description.get())
if RemoteKeystoreFlag.IgnoreSSLVerification in value.flags:
Expand All @@ -618,77 +636,139 @@ proc readValue*(reader: var JsonReader, value: var RemoteKeystore)
description: Option[string]
remote: Option[HttpHostUri]
remotes: Option[seq[RemoteSignerInfo]]
remoteType: Option[string]
remoteType: Option[RemoteSignerType]
provenBlockProperties: Option[seq[ProvenProperty]]
ignoreSslVerification: Option[bool]
pubkey: Option[ValidatorPubKey]
threshold: Option[uint32]
implicitVersion1 = false

# TODO: implementing deserializers for versioned objects
# manually is extremely error-prone. This should use
# the auto-generated deserializer from nim-json-serialization
for fieldName in readObjectFields(reader):
case fieldName:
of "pubkey":
if pubkey.isSome():
if pubkey.isSome:
reader.raiseUnexpectedField("Multiple `pubkey` fields found",
"RemoteKeystore")
pubkey = some(reader.readValue(ValidatorPubKey))
of "remote":
if version.isSome and version.get > 1:
reader.raiseUnexpectedField(
"The `remote` field is valid only in version 1 of the remote keystore format",
"RemoteKeystore")

if remote.isSome():
if remote.isSome:
reader.raiseUnexpectedField("Multiple `remote` fields found",
"RemoteKeystore")
if remotes.isSome:
reader.raiseUnexpectedField("The `remote` field cannot be specified together with `remotes`",
"RemoteKeystore")
remote = some(reader.readValue(HttpHostUri))
implicitVersion1 = true
of "remotes":
if remotes.isSome():
if remotes.isSome:
reader.raiseUnexpectedField("Multiple `remote` fields found",
"RemoteKeystore")
if remote.isSome:
reader.raiseUnexpectedField("The `remotes` field cannot be specified together with `remote`",
"RemoteKeystore")
if version.isNone:
reader.raiseUnexpectedField(
"The `remotes` field should be specified after the `version` field of the keystore",
"RemoteKeystore")
if version.get < 2:
reader.raiseUnexpectedField(
"The `remotes` field is valid only past version 2 of the remote keystore format",
"RemoteKeystore")
remotes = some(reader.readValue(seq[RemoteSignerInfo]))
of "version":
if version.isSome():
if version.isSome:
reader.raiseUnexpectedField("Multiple `version` fields found",
"RemoteKeystore")
version = some(reader.readValue(uint64))
if implicitVersion1 and version.get > 1'u64:
reader.raiseUnexpectedValue(
"Remote keystore format doesn't match the specified version number")
if version.get > 2'u64:
if version.get > 3'u64:
reader.raiseUnexpectedValue(
"Remote keystore version " & $version.get &
" requires a more recent version of Nimbus")
of "description":
let res = reader.readValue(string)
if description.isSome():
description = some(description.get() & "\n" & res)
else:
description = some(res)
if description.isSome:
reader.raiseUnexpectedField("Multiple `description` fields found",
"RemoteKeystore")
description = some(reader.readValue(string))
of "ignore_ssl_verification":
if ignoreSslVerification.isSome():
if ignoreSslVerification.isSome:
reader.raiseUnexpectedField("Multiple conflicting options found",
"RemoteKeystore")
ignoreSslVerification = some(reader.readValue(bool))
of "type":
if remoteType.isSome():
if remoteType.isSome:
reader.raiseUnexpectedField("Multiple `type` fields found",
"RemoteKeystore")
remoteType = some(reader.readValue(string))
if version.isNone:
reader.raiseUnexpectedField(
"The `type` field should be specified after the `version` field of the keystore",
"RemoteKeystore")
if version.get < 2:
reader.raiseUnexpectedField(
"The `type` field is valid only past version 2 of the remote keystore format",
"RemoteKeystore")
let remoteTypeValue = case reader.readValue(string).toLowerAscii()
of "web3signer":
RemoteSignerType.Web3Signer
of "verifying-web3signer":
RemoteSignerType.VerifyingWeb3Signer
else:
reader.raiseUnexpectedValue("Unsupported remote signer `type` value")
remoteType = some remoteTypeValue
of "proven_block_properties":
if provenBlockProperties.isSome:
reader.raiseUnexpectedField("Multiple `proven_block_properties` fields found",
"RemoteKeystore")
if version.isNone:
reader.raiseUnexpectedField(
"The `proven_block_properties` field should be specified after the `version` field of the keystore",
"RemoteKeystore")
if version.get < 3:
reader.raiseUnexpectedField(
"The `proven_block_properties` field is valid only past version 3 of the remote keystore format",
"RemoteKeystore")
if remoteType.isNone:
reader.raiseUnexpectedField(
"The `proven_block_properties` field should be specified after the `type` field of the keystore",
"RemoteKeystore")
if remoteType.get != RemoteSignerType.VerifyingWeb3Signer:
reader.raiseUnexpectedField(
"The `proven_block_properties` field can be specified only when the remote signer type is 'verifying-web3signer'",
"RemoteKeystore")
var provenProperties = reader.readValue(seq[ProvenProperty])
for prop in provenProperties.mitems:
if prop.path == ".execution_payload.fee_recipient":
prop.bellatrixIndex = some GeneralizedIndex(401)
prop.capellaIndex = some GeneralizedIndex(401)
prop.denebIndex = some GeneralizedIndex(401)
elif prop.path == ".graffiti":
prop.bellatrixIndex = some GeneralizedIndex(18)
prop.capellaIndex = some GeneralizedIndex(18)
prop.denebIndex = some GeneralizedIndex(18)
else:
reader.raiseUnexpectedValue("Keystores with proven properties different than " &
"`.execution_payload.fee_recipient` and `.graffiti` " &
"require a more recent version of Nimbus")
provenBlockProperties = some provenProperties
of "threshold":
if threshold.isSome():
if threshold.isSome:
reader.raiseUnexpectedField("Multiple `threshold` fields found",
"RemoteKeystore")
if version.isNone:
reader.raiseUnexpectedField(
"The `threshold` field should be specified after the `version` field of the keystore",
"RemoteKeystore")
if version.get < 2:
reader.raiseUnexpectedField(
"The `threshold` field is valid only past version 2 of the remote keystore format",
"RemoteKeystore")
threshold = some(reader.readValue(uint32))
else:
# Ignore unknown field names.
discard

if version.isNone():
reader.raiseUnexpectedValue("Field `version` is missing")
reader.raiseUnexpectedValue("The required field `version` is missing")
if remotes.isNone():
if remote.isSome and pubkey.isSome:
remotes = some @[RemoteSignerInfo(
Expand All @@ -697,22 +777,27 @@ proc readValue*(reader: var JsonReader, value: var RemoteKeystore)
url: remote.get
)]
else:
reader.raiseUnexpectedValue("Field `remotes` is missing")
reader.raiseUnexpectedValue("The required field `remotes` is missing")

if threshold.isNone:
if remotes.get.len > 1:
reader.raiseUnexpectedValue("The `threshold` field must be specified when using distributed keystores")
else:
if threshold.get.uint64 > remotes.get.lenu64:
reader.raiseUnexpectedValue("The specified `threshold` must be lower than the number of remote signers")

if pubkey.isNone():
reader.raiseUnexpectedValue("Field `pubkey` is missing")

let keystoreType =
if remoteType.isSome():
let res = remoteType.get()
case res.toLowerAscii()
of "web3signer":
RemoteSignerType.Web3Signer
of "web3signer-diva":
RemoteSignerType.Web3SignerDiva
else:
reader.raiseUnexpectedValue("Unsupported remote signer `type` value")
else:
RemoteSignerType.Web3Signer
if version.get >= 3:
if remoteType.isNone:
reader.raiseUnexpectedValue("The required field `type` is missing")
case remoteType.get
of RemoteSignerType.Web3Signer:
discard
of RemoteSignerType.VerifyingWeb3Signer:
if provenBlockProperties.isNone:
reader.raiseUnexpectedValue("The required field `proven_block_properties` is missing")

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

value = RemoteKeystore(
version: 2'u64,
pubkey: pubkey.get,
description: description,
remoteType: keystoreType,
remotes: remotes.get,
threshold: threshold.get(1),
)
value = case remoteType.get(RemoteSignerType.Web3Signer)
of RemoteSignerType.Web3Signer:
RemoteKeystore(
version: 2'u64,
pubkey: pubkey.get,
description: description,
remoteType: RemoteSignerType.Web3Signer,
remotes: remotes.get,
threshold: threshold.get(1))
of RemoteSignerType.VerifyingWeb3Signer:
RemoteKeystore(
version: 2'u64,
pubkey: pubkey.get,
description: description,
remoteType: RemoteSignerType.VerifyingWeb3Signer,
provenBlockProperties: provenBlockProperties.get,
remotes: remotes.get,
threshold: threshold.get(1))

template writeValue*(w: var JsonWriter,
value: Pbkdf2Salt|SimpleHexEncodedTypes|Aes128CtrIv) =
Expand Down
2 changes: 1 addition & 1 deletion beacon_chain/spec/mev/bellatrix_mev.nim
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type
signature*: ValidatorSig

# https://github.com/ethereum/builder-specs/blob/v0.3.0/specs/bellatrix/builder.md#blindedbeaconblockbody
BlindedBeaconBlockBody = object
BlindedBeaconBlockBody* = object
randao_reveal*: ValidatorSig
eth1_data*: Eth1Data
graffiti*: GraffitiBytes
Expand Down
Loading