Add the --verifying-web3-signer-url configuration option (#5504)
This commit is contained in:
parent
dc4d366a6d
commit
35bf03a3fb
|
@ -127,6 +127,10 @@ type
|
|||
Poll = "poll"
|
||||
Event = "event"
|
||||
|
||||
Web3SignerUrl* = object
|
||||
url*: Uri
|
||||
provenBlockProperties*: seq[string] # empty if this is not a verifying Web3Signer
|
||||
|
||||
BeaconNodeConf* = object
|
||||
configFile* {.
|
||||
desc: "Loads the configuration from a TOML file"
|
||||
|
@ -164,7 +168,15 @@ type
|
|||
desc: "A directory containing validator keystores"
|
||||
name: "validators-dir" .}: Option[InputDir]
|
||||
|
||||
web3signers* {.
|
||||
verifyingWeb3Signers* {.
|
||||
desc: "Remote Web3Signer URL that will be used as a source of validators"
|
||||
name: "verifying-web3-signer-url" .}: seq[Uri]
|
||||
|
||||
provenBlockProperties* {.
|
||||
desc: "The field path of a block property that will be sent for verification to the verifying Web3Signer (for example \".execution_payload.fee_recipient\")"
|
||||
name: "proven-block-property" .}: seq[string]
|
||||
|
||||
web3Signers* {.
|
||||
desc: "Remote Web3Signer URL that will be used as a source of validators"
|
||||
name: "web3-signer-url" .}: seq[Uri]
|
||||
|
||||
|
@ -896,15 +908,23 @@ type
|
|||
desc: "A directory containing validator keystores"
|
||||
name: "validators-dir" .}: Option[InputDir]
|
||||
|
||||
web3signers* {.
|
||||
verifyingWeb3Signers* {.
|
||||
desc: "Remote Web3Signer URL that will be used as a source of validators"
|
||||
name: "web3-signer-url" .}: seq[Uri]
|
||||
name: "verifying-web3-signer-url" .}: seq[Uri]
|
||||
|
||||
provenBlockProperties* {.
|
||||
desc: "The field path of a block property that will be sent for verification to the verifying Web3Signer (for example \".execution_payload.fee_recipient\")"
|
||||
name: "proven-block-property" .}: seq[string]
|
||||
|
||||
web3signerUpdateInterval* {.
|
||||
desc: "Number of seconds between validator list updates"
|
||||
name: "web3-signer-update-interval"
|
||||
defaultValue: 3600 .}: Natural
|
||||
|
||||
web3Signers* {.
|
||||
desc: "Remote Web3Signer URL that will be used as a source of validators"
|
||||
name: "web3-signer-url" .}: seq[Uri]
|
||||
|
||||
secretsDirFlag* {.
|
||||
desc: "A directory containing validator keystore passwords"
|
||||
name: "secrets-dir" .}: Option[InputDir]
|
||||
|
@ -1287,6 +1307,14 @@ func runAsService*(config: BeaconNodeConf): bool =
|
|||
else:
|
||||
false
|
||||
|
||||
func web3SignerUrls*(conf: AnyConf): seq[Web3SignerUrl] =
|
||||
for url in conf.web3signers:
|
||||
result.add Web3SignerUrl(url: url)
|
||||
|
||||
for url in conf.verifyingWeb3signers:
|
||||
result.add Web3SignerUrl(url: url,
|
||||
provenBlockProperties: conf.provenBlockProperties)
|
||||
|
||||
template writeValue*(writer: var JsonWriter,
|
||||
value: TypedInputFile|InputFile|InputDir|OutPath|OutDir|OutFile) =
|
||||
writer.writeValue(string value)
|
||||
|
|
|
@ -1732,7 +1732,7 @@ proc run(node: BeaconNode) {.raises: [CatchableError].} =
|
|||
|
||||
waitFor node.updateGossipStatus(wallSlot)
|
||||
|
||||
for web3signerUrl in node.config.web3signers:
|
||||
for web3signerUrl in node.config.web3SignerUrls:
|
||||
# TODO
|
||||
# The current strategy polls all remote signers independently
|
||||
# from each other which may lead to some race conditions of
|
||||
|
|
|
@ -98,7 +98,7 @@ proc initGenesis(vc: ValidatorClientRef): Future[RestGenesis] {.async.} =
|
|||
dec(counter)
|
||||
return melem
|
||||
|
||||
proc addValidatorsFromWeb3Signer(vc: ValidatorClientRef, web3signerUrl: Uri) {.async.} =
|
||||
proc addValidatorsFromWeb3Signer(vc: ValidatorClientRef, web3signerUrl: Web3SignerUrl) {.async.} =
|
||||
let res = await queryValidatorsSource(web3signerUrl)
|
||||
if res.isOk():
|
||||
let dynamicKeystores = res.get()
|
||||
|
@ -111,7 +111,7 @@ proc initValidators(vc: ValidatorClientRef): Future[bool] {.async.} =
|
|||
vc.addValidator(keystore)
|
||||
|
||||
let web3signerValidatorsFuts = mapIt(
|
||||
vc.config.web3signers,
|
||||
vc.config.web3SignerUrls,
|
||||
vc.addValidatorsFromWeb3Signer(it))
|
||||
|
||||
# We use `allFutures` because all failures are already reported as
|
||||
|
|
|
@ -725,6 +725,26 @@ template writeValue*(w: var JsonWriter,
|
|||
value: Pbkdf2Salt|SimpleHexEncodedTypes|Aes128CtrIv) =
|
||||
writeJsonHexString(w.stream, distinctBase value)
|
||||
|
||||
func parseProvenBlockProperty*(propertyPath: string): Result[ProvenProperty, string] =
|
||||
if propertyPath == ".execution_payload.fee_recipient":
|
||||
ok ProvenProperty(
|
||||
path: propertyPath,
|
||||
bellatrixIndex: some GeneralizedIndex(401),
|
||||
capellaIndex: some GeneralizedIndex(401),
|
||||
denebIndex: some GeneralizedIndex(801))
|
||||
elif propertyPath == ".graffiti":
|
||||
ok ProvenProperty(
|
||||
path: propertyPath,
|
||||
# TODO: graffiti is present since genesis, so the correct index in the early
|
||||
# forks can be supplied here
|
||||
bellatrixIndex: some GeneralizedIndex(18),
|
||||
capellaIndex: some GeneralizedIndex(18),
|
||||
denebIndex: some GeneralizedIndex(18))
|
||||
else:
|
||||
err("Keystores with proven properties different than " &
|
||||
"`.execution_payload.fee_recipient` and `.graffiti` " &
|
||||
"require a more recent version of Nimbus")
|
||||
|
||||
proc readValue*(reader: var JsonReader, value: var RemoteKeystore)
|
||||
{.raises: [SerializationError, IOError].} =
|
||||
var
|
||||
|
@ -830,6 +850,8 @@ proc readValue*(reader: var JsonReader, value: var RemoteKeystore)
|
|||
prop.capellaIndex = some GeneralizedIndex(401)
|
||||
prop.denebIndex = some GeneralizedIndex(801)
|
||||
elif prop.path == ".graffiti":
|
||||
# TODO: graffiti is present since genesis, so the correct index in the early
|
||||
# forks can be supplied here
|
||||
prop.bellatrixIndex = some GeneralizedIndex(18)
|
||||
prop.capellaIndex = some GeneralizedIndex(18)
|
||||
prop.denebIndex = some GeneralizedIndex(18)
|
||||
|
|
|
@ -601,7 +601,7 @@ proc validatorIndexLoop(service: DutiesServiceRef) {.async.} =
|
|||
await service.waitForNextSlot()
|
||||
|
||||
proc dynamicValidatorsLoop*(service: DutiesServiceRef,
|
||||
web3signerUrl: Uri,
|
||||
web3signerUrl: Web3SignerUrl,
|
||||
intervalInSeconds: int) {.async.} =
|
||||
let vc = service.client
|
||||
doAssert(intervalInSeconds > 0)
|
||||
|
@ -624,7 +624,7 @@ proc dynamicValidatorsLoop*(service: DutiesServiceRef,
|
|||
let keystores = res.get()
|
||||
debug "Web3Signer has been polled for validators",
|
||||
keystores_found = len(keystores),
|
||||
web3signer_url = web3signerUrl
|
||||
web3signer_url = web3signerUrl.url
|
||||
vc.attachedValidators.updateDynamicValidators(web3signerUrl,
|
||||
keystores,
|
||||
addValidatorProc)
|
||||
|
@ -710,11 +710,12 @@ proc mainLoop(service: DutiesServiceRef) {.async.} =
|
|||
nil
|
||||
dynamicFuts =
|
||||
if vc.config.web3signerUpdateInterval > 0:
|
||||
mapIt(vc.config.web3signers,
|
||||
mapIt(vc.config.web3SignerUrls,
|
||||
service.dynamicValidatorsLoop(it, vc.config.web3signerUpdateInterval))
|
||||
else:
|
||||
debug "Dynamic validators update loop disabled"
|
||||
@[]
|
||||
web3SignerUrls = vc.config.web3SignerUrls
|
||||
|
||||
while true:
|
||||
# This loop could look much more nicer/better, when
|
||||
|
@ -746,7 +747,7 @@ proc mainLoop(service: DutiesServiceRef) {.async.} =
|
|||
for i in 0 ..< dynamicFuts.len:
|
||||
checkAndRestart(DynamicValidatorsLoop, dynamicFuts[i],
|
||||
service.dynamicValidatorsLoop(
|
||||
vc.config.web3signers[i],
|
||||
web3SignerUrls[i],
|
||||
vc.config.web3signerUpdateInterval))
|
||||
false
|
||||
except CancelledError:
|
||||
|
|
|
@ -115,7 +115,7 @@ proc getValidator*(validators: auto,
|
|||
Opt.some ValidatorAndIndex(index: ValidatorIndex(idx),
|
||||
validator: validators[idx])
|
||||
|
||||
proc addValidatorsFromWeb3Signer(node: BeaconNode, web3signerUrl: Uri, epoch: Epoch) {.async.} =
|
||||
proc addValidatorsFromWeb3Signer(node: BeaconNode, web3signerUrl: Web3SignerUrl, epoch: Epoch) {.async.} =
|
||||
let dynamicStores =
|
||||
try:
|
||||
let res = await queryValidatorsSource(web3signerUrl)
|
||||
|
@ -173,7 +173,7 @@ proc addValidators*(node: BeaconNode) =
|
|||
# user-visible warnings in `queryValidatorsSource`.
|
||||
# We don't consider them fatal because the Web3Signer may be experiencing
|
||||
# a temporary hiccup that will be resolved later.
|
||||
waitFor allFutures(mapIt(node.config.web3signers,
|
||||
waitFor allFutures(mapIt(node.config.web3SignerUrls,
|
||||
node.addValidatorsFromWeb3Signer(it, epoch)))
|
||||
except CatchableError as err:
|
||||
# This should never happen because all errors are handled within
|
||||
|
@ -184,7 +184,7 @@ proc addValidators*(node: BeaconNode) =
|
|||
err = err.msg
|
||||
|
||||
proc pollForDynamicValidators*(node: BeaconNode,
|
||||
web3signerUrl: Uri,
|
||||
web3signerUrl: Web3SignerUrl,
|
||||
intervalInSeconds: int) {.async.} =
|
||||
if intervalInSeconds == 0:
|
||||
return
|
||||
|
@ -215,7 +215,7 @@ proc pollForDynamicValidators*(node: BeaconNode,
|
|||
let keystores = res.get()
|
||||
debug "Validators source has been polled for validators",
|
||||
keystores_found = len(keystores),
|
||||
web3signer_url = web3signerUrl
|
||||
web3signer_url = web3signerUrl.url
|
||||
node.attachedValidators.updateDynamicValidators(web3signerUrl,
|
||||
keystores,
|
||||
addValidatorProc)
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/[os, unicode],
|
||||
std/[os, unicode, sequtils],
|
||||
chronicles, chronos, json_serialization,
|
||||
bearssl/rand,
|
||||
serialization, blscurve, eth/common/eth_types, confutils,
|
||||
|
@ -28,7 +28,7 @@ from std/wordwrap import wrapWords
|
|||
from zxcvbn import passwordEntropy
|
||||
|
||||
export
|
||||
keystore, validator_pool, crypto, rand
|
||||
keystore, validator_pool, crypto, rand, Web3SignerUrl
|
||||
|
||||
when defined(windows):
|
||||
import stew/[windows/acl]
|
||||
|
@ -632,11 +632,11 @@ proc existsKeystore(keystoreDir: string,
|
|||
return true
|
||||
false
|
||||
|
||||
proc queryValidatorsSource*(web3signerUrl: Uri): Future[QueryResult] {.async.} =
|
||||
proc queryValidatorsSource*(web3signerUrl: Web3SignerUrl): Future[QueryResult] {.async.} =
|
||||
var keystores: seq[KeystoreData]
|
||||
|
||||
logScope:
|
||||
web3signer_url = web3signerUrl
|
||||
web3signer_url = web3signerUrl.url
|
||||
|
||||
let
|
||||
httpFlags: HttpClientFlags = {}
|
||||
|
@ -644,7 +644,7 @@ proc queryValidatorsSource*(web3signerUrl: Uri): Future[QueryResult] {.async.} =
|
|||
socketFlags = {SocketFlags.TcpNoDelay}
|
||||
client =
|
||||
block:
|
||||
let res = RestClientRef.new($web3signerUrl, prestoFlags,
|
||||
let res = RestClientRef.new($web3signerUrl.url, prestoFlags,
|
||||
httpFlags, socketFlags = socketFlags)
|
||||
if res.isErr():
|
||||
warn "Unable to resolve validator's source distributed signer " &
|
||||
|
@ -679,16 +679,29 @@ proc queryValidatorsSource*(web3signerUrl: Uri): Future[QueryResult] {.async.} =
|
|||
error = $exc.name, reason = $exc.msg
|
||||
return QueryResult.err($exc.msg)
|
||||
|
||||
remoteType = if web3signerUrl.provenBlockProperties.len == 0:
|
||||
RemoteSignerType.Web3Signer
|
||||
else:
|
||||
RemoteSignerType.VerifyingWeb3Signer
|
||||
|
||||
provenBlockProperties = mapIt(web3signerUrl.provenBlockProperties,
|
||||
block:
|
||||
parseProvenBlockProperty(it).valueOr:
|
||||
return QueryResult.err(error))
|
||||
|
||||
for pubkey in keys:
|
||||
keystores.add(KeystoreData(
|
||||
kind: KeystoreKind.Remote,
|
||||
handle: FileLockHandle(opened: false),
|
||||
pubkey: pubkey,
|
||||
remotes: @[RemoteSignerInfo(
|
||||
url: HttpHostUri(web3signerUrl),
|
||||
url: HttpHostUri(web3signerUrl.url),
|
||||
pubkey: pubkey)],
|
||||
flags: {RemoteKeystoreFlag.DynamicKeystore},
|
||||
remoteType: RemoteSignerType.Web3Signer))
|
||||
remoteType: remoteType))
|
||||
|
||||
if provenBlockProperties.len > 0:
|
||||
keystores[^1].provenBlockProperties = provenBlockProperties
|
||||
|
||||
QueryResult.ok(keystores)
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ import
|
|||
../spec/datatypes/[phase0, altair],
|
||||
../spec/eth2_apis/[rest_types, eth2_rest_serialization,
|
||||
rest_remote_signer_calls],
|
||||
../filepath,
|
||||
../filepath, ../conf,
|
||||
./slashing_protection
|
||||
|
||||
export
|
||||
|
@ -380,7 +380,7 @@ func triggersDoppelganger*(
|
|||
v.isSome() and v[].triggersDoppelganger(epoch)
|
||||
|
||||
proc updateDynamicValidators*(pool: ref ValidatorPool,
|
||||
web3signerUrl: Uri,
|
||||
web3signerUrl: Web3SignerUrl,
|
||||
keystores: openArray[KeystoreData],
|
||||
addProc: AddValidatorProc) =
|
||||
var
|
||||
|
@ -400,7 +400,7 @@ proc updateDynamicValidators*(pool: ref ValidatorPool,
|
|||
if keystore.isSome():
|
||||
# Just update validator's `data` field with new data from keystore.
|
||||
validator.data = keystore.get()
|
||||
elif validator.data.remotes[0].url == HttpHostUri(web3signerUrl):
|
||||
elif validator.data.remotes[0].url == HttpHostUri(web3signerUrl.url):
|
||||
# The "dynamic" keystores are guaratneed to not be distributed
|
||||
# so they have a single remote. This code ensures that we are
|
||||
# deleting all previous dynamically obtained keystores which
|
||||
|
|
|
@ -33,6 +33,9 @@ The following options are available:
|
|||
--network The Eth2 network to join [=mainnet].
|
||||
-d, --data-dir The directory where nimbus will store all blockchain data.
|
||||
--validators-dir A directory containing validator keystores.
|
||||
--verifying-web3-signer-url Remote Web3Signer URL that will be used as a source of validators.
|
||||
--proven-block-property The field path of a block property that will be sent for verification to the
|
||||
verifying Web3Signer (for example ".execution_payload.fee_recipient").
|
||||
--web3-signer-url Remote Web3Signer URL that will be used as a source of validators.
|
||||
--web3-signer-update-interval Number of seconds between validator list updates [=3600].
|
||||
--secrets-dir A directory containing validator keystore passwords.
|
||||
|
|
|
@ -156,3 +156,5 @@ Since the generalized index of a particular field may change in a hard-fork, in
|
|||
|
||||
Nimbus automatically computes the generalized index depending on the currently active fork.
|
||||
The remote signer is expected to verify the incoming Merkle proof through the standardized [is_valid_merkle_branch](https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.2/specs/phase0/beacon-chain.md#is_valid_merkle_branch) function by utilizing a similar automatic mapping mechanism for the generalized index.
|
||||
|
||||
You can instruct Nimbus to use the verifying Web3Signer protocol by either supplying the `--verifying-web3-signer` command-line option or by creating a remote keystore file in the format described above. You can use the command-line option `--proven-block-property` once or multiple times to enumerate the properties of the block for which Merkle proofs will be supplied.
|
||||
|
|
|
@ -31,7 +31,7 @@ func createDynamic(url: Uri, pubkey: ValidatorPubKey): KeystoreData =
|
|||
flags: {RemoteKeystoreFlag.DynamicKeystore})
|
||||
|
||||
const
|
||||
remoteSignerUrl = parseUri("http://nimbus.team/signer1")
|
||||
remoteSignerUrl = Web3SignerUrl(url: parseUri("http://nimbus.team/signer1"))
|
||||
|
||||
func makeValidatorAndIndex(
|
||||
index: ValidatorIndex, activation_epoch: Epoch): Opt[ValidatorAndIndex] =
|
||||
|
@ -166,35 +166,36 @@ suite "Validator pool":
|
|||
mapIt(["--web3-signer-url=http://" & $serverAddress], it))
|
||||
except Exception as exc:
|
||||
raiseAssert exc.msg
|
||||
web3SignerUrls = config.web3SignerUrls
|
||||
|
||||
server.start()
|
||||
try:
|
||||
block:
|
||||
testStage = 0
|
||||
let res = await queryValidatorsSource(config.web3signers[0])
|
||||
let res = await queryValidatorsSource(web3SignerUrls[0])
|
||||
check:
|
||||
res.isOk()
|
||||
checkResponse(
|
||||
res.get(),
|
||||
[
|
||||
createDynamic(remoteSignerUrl, createPubKey(1)),
|
||||
createDynamic(remoteSignerUrl, createPubKey(2))
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(1)),
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(2))
|
||||
])
|
||||
block:
|
||||
testStage = 1
|
||||
let res = await queryValidatorsSource(config.web3signers[0])
|
||||
let res = await queryValidatorsSource(web3SignerUrls[0])
|
||||
check:
|
||||
res.isOk()
|
||||
checkResponse(res.get(), [createDynamic(remoteSignerUrl, createPubKey(1))])
|
||||
checkResponse(res.get(), [createDynamic(remoteSignerUrl.url, createPubKey(1))])
|
||||
block:
|
||||
testStage = 2
|
||||
let res = await queryValidatorsSource(config.web3signers[0])
|
||||
let res = await queryValidatorsSource(web3SignerUrls[0])
|
||||
check:
|
||||
res.isOk()
|
||||
len(res.get()) == 0
|
||||
block:
|
||||
testStage = 3
|
||||
let res = await queryValidatorsSource(config.web3signers[0])
|
||||
let res = await queryValidatorsSource(web3SignerUrls[0])
|
||||
check:
|
||||
res.isErr()
|
||||
finally:
|
||||
|
@ -221,7 +222,7 @@ suite "Validator pool":
|
|||
var pool = (ref ValidatorPool)()
|
||||
discard pool[].addValidator(createLocal(createPubKey(1)), fee, gas)
|
||||
discard pool[].addValidator(createRemote(createPubKey(2)), fee, gas)
|
||||
discard pool[].addValidator(createDynamic(remoteSignerUrl, createPubKey(3)), fee, gas)
|
||||
discard pool[].addValidator(createDynamic(remoteSignerUrl.url, createPubKey(3)), fee, gas)
|
||||
|
||||
proc addValidator(data: KeystoreData) {.gcsafe.} =
|
||||
discard pool[].addValidator(data, fee, gas)
|
||||
|
@ -232,14 +233,14 @@ suite "Validator pool":
|
|||
expected = [
|
||||
createLocal(createPubKey(1)),
|
||||
createRemote(createPubKey(2)),
|
||||
createDynamic(remoteSignerUrl, createPubKey(3)),
|
||||
createDynamic(remoteSignerUrl, createPubKey(4)),
|
||||
createDynamic(remoteSignerUrl, createPubKey(5))
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(3)),
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(4)),
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(5))
|
||||
]
|
||||
keystores = [
|
||||
createDynamic(remoteSignerUrl, createPubKey(3)),
|
||||
createDynamic(remoteSignerUrl, createPubKey(4)),
|
||||
createDynamic(remoteSignerUrl, createPubKey(5))
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(3)),
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(4)),
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(5))
|
||||
]
|
||||
pool.updateDynamicValidators(remoteSignerUrl, keystores, addValidator)
|
||||
pool[].checkPool(expected)
|
||||
|
@ -250,10 +251,10 @@ suite "Validator pool":
|
|||
expected = [
|
||||
createLocal(createPubKey(1)),
|
||||
createRemote(createPubKey(2)),
|
||||
createDynamic(remoteSignerUrl, createPubKey(3))
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(3))
|
||||
]
|
||||
keystores = [
|
||||
createDynamic(remoteSignerUrl, createPubKey(3)),
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(3)),
|
||||
]
|
||||
pool.updateDynamicValidators(remoteSignerUrl, keystores, addValidator)
|
||||
pool[].checkPool(expected)
|
||||
|
@ -264,12 +265,12 @@ suite "Validator pool":
|
|||
expected = [
|
||||
createLocal(createPubKey(1)),
|
||||
createRemote(createPubKey(2)),
|
||||
createDynamic(remoteSignerUrl, createPubKey(4)),
|
||||
createDynamic(remoteSignerUrl, createPubKey(5))
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(4)),
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(5))
|
||||
]
|
||||
keystores = [
|
||||
createDynamic(remoteSignerUrl, createPubKey(4)),
|
||||
createDynamic(remoteSignerUrl, createPubKey(5))
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(4)),
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(5))
|
||||
]
|
||||
pool.updateDynamicValidators(remoteSignerUrl, keystores, addValidator)
|
||||
pool[].checkPool(expected)
|
||||
|
@ -280,12 +281,12 @@ suite "Validator pool":
|
|||
expected = [
|
||||
createLocal(createPubKey(1)),
|
||||
createRemote(createPubKey(2)),
|
||||
createDynamic(remoteSignerUrl, createPubKey(3))
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(3))
|
||||
]
|
||||
keystores = [
|
||||
createDynamic(remoteSignerUrl, createPubKey(1)),
|
||||
createDynamic(remoteSignerUrl, createPubKey(2)),
|
||||
createDynamic(remoteSignerUrl, createPubKey(3)),
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(1)),
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(2)),
|
||||
createDynamic(remoteSignerUrl.url, createPubKey(3)),
|
||||
]
|
||||
pool.updateDynamicValidators(remoteSignerUrl, keystores, addValidator)
|
||||
pool[].checkPool(expected)
|
||||
|
|
Loading…
Reference in New Issue