chore(rln-relay): use the only key from keystore if only 1 exists (#1984)

* chore(rln-relay): use the only key from keystore if only 1 exists

* fix: convert iterator to seq and then index into it
This commit is contained in:
Aaryamann Challani 2023-09-04 15:46:44 +05:30 committed by GitHub
parent 1b835b4e2a
commit a14c32614a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 147 additions and 37 deletions

View File

@ -233,9 +233,8 @@ type
name: "rln-relay-cred-path" }: string
rlnRelayCredIndex* {.
desc: "the index of credentials to use",
defaultValue: 0
name: "rln-relay-cred-index" }: uint
desc: "the index of the onchain commitment to use",
name: "rln-relay-cred-index" }: Option[uint]
rlnRelayDynamic* {.
desc: "Enable waku-rln-relay with on-chain dynamic group management: true|false",
@ -297,6 +296,12 @@ proc parseCmdArg*(T: type Port, p: string): T =
proc completeCmdArg*(T: type Port, val: string): seq[string] =
return @[]
proc parseCmdArg*(T: type Option[uint], p: string): T =
try:
some(parseUint(p))
except CatchableError:
raise newException(ConfigurationError, "Invalid unsigned integer")
func defaultListenAddress*(conf: Chat2Conf): ValidIpAddress =
# TODO: How should we select between IPv4 and IPv6
# Maybe there should be a config option for this.

View File

@ -146,9 +146,8 @@ type
name: "rln-relay-cred-path" }: string
rlnRelayCredIndex* {.
desc: "the index of credentials to use",
defaultValue: 0
name: "rln-relay-membership-index" }: uint
desc: "the index of the onchain commitment to use",
name: "rln-relay-membership-index" }: Option[uint]
rlnRelayDynamic* {.
desc: "Enable waku-rln-relay with on-chain dynamic group management: true|false",
@ -506,6 +505,12 @@ proc parseCmdArg*(T: type Option[int], p: string): T =
except CatchableError:
raise newException(ConfigurationError, "Invalid number")
proc parseCmdArg*(T: type Option[uint], p: string): T =
try:
some(parseUint(p))
except CatchableError:
raise newException(ConfigurationError, "Invalid unsigned integer")
## Configuration validation
let DbUrlRegex = re"^[\w\+]+:\/\/[\w\/\\\.\:\@]+$"

View File

@ -3,7 +3,7 @@
import
std/[os, json],
chronos,
testutils/unittests
testutils/unittests
import
../../waku/waku_keystore,
./testlib/common
@ -123,3 +123,89 @@ procSuite "Credentials test suite":
recoveredCredentialsRes.isOk()
recoveredCredentialsRes.get() == expectedMembership
test "if the keystore contains only one credential, fetch that irrespective of treeIndex":
let filepath = "./testAppKeystore.txt"
defer: removeFile(filepath)
# We generate random identity credentials (inter-value constrains are not enforced, otherwise we need to load e.g. zerokit RLN keygen)
let
idTrapdoor = randomSeqByte(rng[], 32)
idNullifier = randomSeqByte(rng[], 32)
idSecretHash = randomSeqByte(rng[], 32)
idCommitment = randomSeqByte(rng[], 32)
idCredential = IdentityCredential(idTrapdoor: idTrapdoor, idNullifier: idNullifier, idSecretHash: idSecretHash, idCommitment: idCommitment)
let contract = MembershipContract(chainId: "5", address: "0x0123456789012345678901234567890123456789")
let index = MembershipIndex(1)
let membershipCredential = KeystoreMembership(membershipContract: contract,
treeIndex: index,
identityCredential: idCredential)
let password = "%m0um0ucoW%"
let keystoreRes = addMembershipCredentials(path = filepath,
membership = membershipCredential,
password = password,
appInfo = testAppInfo)
assert(keystoreRes.isOk(), $keystoreRes.error)
# We test retrieval of credentials.
let expectedMembership = membershipCredential
let membershipQuery = KeystoreMembership(membershipContract: contract)
let recoveredCredentialsRes = getMembershipCredentials(path = filepath,
password = password,
query = membershipQuery,
appInfo = testAppInfo)
assert(recoveredCredentialsRes.isOk(), $recoveredCredentialsRes.error)
check: recoveredCredentialsRes.get() == expectedMembership
test "if the keystore contains multiple credentials, then error out if treeIndex has not been passed in":
let filepath = "./testAppKeystore.txt"
defer: removeFile(filepath)
# We generate random identity credentials (inter-value constrains are not enforced, otherwise we need to load e.g. zerokit RLN keygen)
let
idTrapdoor = randomSeqByte(rng[], 32)
idNullifier = randomSeqByte(rng[], 32)
idSecretHash = randomSeqByte(rng[], 32)
idCommitment = randomSeqByte(rng[], 32)
idCredential = IdentityCredential(idTrapdoor: idTrapdoor, idNullifier: idNullifier, idSecretHash: idSecretHash, idCommitment: idCommitment)
# We generate two distinct membership groups
let contract = MembershipContract(chainId: "5", address: "0x0123456789012345678901234567890123456789")
let index = MembershipIndex(1)
var membershipCredential = KeystoreMembership(membershipContract: contract,
treeIndex: index,
identityCredential: idCredential)
let password = "%m0um0ucoW%"
let keystoreRes = addMembershipCredentials(path = filepath,
membership = membershipCredential,
password = password,
appInfo = testAppInfo)
assert(keystoreRes.isOk(), $keystoreRes.error)
membershipCredential.treeIndex = MembershipIndex(2)
let keystoreRes2 = addMembershipCredentials(path = filepath,
membership = membershipCredential,
password = password,
appInfo = testAppInfo)
assert(keystoreRes2.isOk(), $keystoreRes2.error)
# We test retrieval of credentials.
let membershipQuery = KeystoreMembership(membershipContract: contract)
let recoveredCredentialsRes = getMembershipCredentials(path = filepath,
password = password,
query = membershipQuery,
appInfo = testAppInfo)
check:
recoveredCredentialsRes.isErr()
recoveredCredentialsRes.error.kind == KeystoreCredentialNotFoundError

View File

@ -662,7 +662,7 @@ suite "Waku rln relay":
let index = MembershipIndex(5)
let rlnConf = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: index.uint,
rlnRelayCredIndex: some(index),
rlnRelayTreePath: genTempPath("rln_tree", "waku_rln_relay_2"))
let wakuRlnRelayRes = await WakuRlnRelay.new(rlnConf)
require:
@ -714,7 +714,7 @@ suite "Waku rln relay":
let index = MembershipIndex(5)
let rlnConf = WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: index.uint,
rlnRelayCredIndex: some(index),
rlnRelayBandwidthThreshold: 4,
rlnRelayTreePath: genTempPath("rln_tree", "waku_rln_relay_3"))
let wakuRlnRelayRes = await WakuRlnRelay.new(rlnConf)

View File

@ -45,7 +45,7 @@ procSuite "WakuNode - RLN relay":
# mount rlnrelay in off-chain mode
await node1.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: 1.uint,
rlnRelayCredIndex: some(1.uint),
rlnRelayTreePath: genTempPath("rln_tree", "wakunode"),
))
@ -55,7 +55,7 @@ procSuite "WakuNode - RLN relay":
await node2.mountRelay(@[DefaultPubsubTopic])
# mount rlnrelay in off-chain mode
await node2.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: 2.uint,
rlnRelayCredIndex: some(2.uint),
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_2"),
))
@ -65,7 +65,7 @@ procSuite "WakuNode - RLN relay":
await node3.mountRelay(@[DefaultPubsubTopic])
await node3.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: 3.uint,
rlnRelayCredIndex: some(3.uint),
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_3"),
))
@ -126,7 +126,7 @@ procSuite "WakuNode - RLN relay":
# mount rlnrelay in off-chain mode
for index, node in nodes:
await node.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: index.uint + 1,
rlnRelayCredIndex: some(index.uint + 1),
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_" & $(index+1))))
# start them
@ -204,7 +204,7 @@ procSuite "WakuNode - RLN relay":
# mount rlnrelay in off-chain mode
await node1.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: 1.uint,
rlnRelayCredIndex: some(1.uint),
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_4"),
rlnRelayBandwidthThreshold: 0,
))
@ -215,7 +215,7 @@ procSuite "WakuNode - RLN relay":
await node2.mountRelay(@[DefaultPubsubTopic])
# mount rlnrelay in off-chain mode
await node2.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: 2.uint,
rlnRelayCredIndex: some(2.uint),
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_5"),
rlnRelayBandwidthThreshold: 0,
))
@ -226,7 +226,7 @@ procSuite "WakuNode - RLN relay":
await node3.mountRelay(@[DefaultPubsubTopic])
await node3.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: 3.uint,
rlnRelayCredIndex: some(3.uint),
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_6"),
rlnRelayBandwidthThreshold: 0,
))
@ -306,7 +306,7 @@ procSuite "WakuNode - RLN relay":
# mount rlnrelay in off-chain mode
await node1.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: 1.uint,
rlnRelayCredIndex: some(1.uint),
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_7"),
rlnRelayBandwidthThreshold: 0,
))
@ -318,7 +318,7 @@ procSuite "WakuNode - RLN relay":
# mount rlnrelay in off-chain mode
await node2.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: 2.uint,
rlnRelayCredIndex: some(2.uint),
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_8"),
rlnRelayBandwidthThreshold: 0,
))
@ -330,7 +330,7 @@ procSuite "WakuNode - RLN relay":
# mount rlnrelay in off-chain mode
await node3.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: 3.uint,
rlnRelayCredIndex: some(3.uint),
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_9"),
rlnRelayBandwidthThreshold: 0,
))

View File

@ -106,11 +106,11 @@ suite "Waku v2 JSON-RPC API - Relay":
when defined(rln):
await srcNode.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: 1,
rlnRelayCredIndex: some(1.uint),
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_1")))
await dstNode.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: 2,
rlnRelayCredIndex: some(2.uint),
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_2")))
await srcNode.connectToNodes(@[dstNode.peerInfo.toRemotePeerInfo()])

View File

@ -188,7 +188,7 @@ suite "Waku v2 Rest API - Relay":
await node.mountRelay()
when defined(rln):
await node.mountRlnRelay(WakuRlnConfig(rlnRelayDynamic: false,
rlnRelayCredIndex: 1,
rlnRelayCredIndex: some(1.uint),
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_1")))
# RPC server setup

View File

@ -4,7 +4,10 @@ else:
{.push raises: [].}
import
options, json, strutils,
options,
json,
strutils,
sequtils,
std/[tables, os]
import
@ -144,7 +147,7 @@ proc addMembershipCredentials*(path: string,
let encodedMembershipCredential = membership.encode()
let keyfileRes = createKeyFileJson(encodedMembershipCredential, password)
if keyfileRes.isErr():
return err(AppKeystoreError(kind: KeystoreCreateKeyfileError,
return err(AppKeystoreError(kind: KeystoreCreateKeyfileError,
msg: $keyfileRes.error))
# We add it to the credentials field of the keystore
@ -183,13 +186,24 @@ proc getMembershipCredentials*(path: string,
if jsonKeystore.hasKey("credentials"):
# We get all credentials in keystore
var keystoreCredentials = jsonKeystore["credentials"]
let key = query.hash()
if not keystoreCredentials.hasKey(key):
if keystoreCredentials.len == 0:
# error
return err(AppKeystoreError(kind: KeystoreCredentialNotFoundError,
msg: "Credential not found in keystore"))
msg: "No credentials found in keystore"))
var keystoreCredential: JsonNode
if keystoreCredentials.len == 1:
keystoreCredential = keystoreCredentials
.getFields()
.values()
.toSeq()[0]
else:
let key = query.hash()
if not keystoreCredentials.hasKey(key):
# error
return err(AppKeystoreError(kind: KeystoreCredentialNotFoundError,
msg: "Credential not found in keystore"))
keystoreCredential = keystoreCredentials[key]
let keystoreCredential = keystoreCredentials[key]
let decodedKeyfileRes = decodeKeyFileJson(keystoreCredential, password)
if decodedKeyfileRes.isErr():
return err(AppKeystoreError(kind: KeystoreReadKeyfileError,

View File

@ -440,23 +440,23 @@ method init*(g: OnchainGroupManager): Future[void] {.async.} =
g.registryContract = some(registryContract)
if g.keystorePath.isSome() and g.keystorePassword.isSome():
if g.membershipIndex.isNone():
raise newException(CatchableError, "membership index is not set when keystore is provided")
let keystoreQuery = KeystoreMembership(
var keystoreQuery = KeystoreMembership(
membershipContract: MembershipContract(
chainId: $g.chainId.get(),
address: g.ethContractAddress
),
treeIndex: MembershipIndex(g.membershipIndex.get()),
)
)
if g.membershipIndex.isSome():
keystoreQuery.treeIndex = MembershipIndex(g.membershipIndex.get())
waku_rln_membership_credentials_import_duration_seconds.nanosecondTime:
let keystoreCredRes = getMembershipCredentials(path = g.keystorePath.get(),
password = g.keystorePassword.get(),
query = keystoreQuery,
appInfo = RLNAppInfo)
if keystoreCredRes.isErr():
raise newException(ValueError, "could not parse the keystore: " & $keystoreCredRes.error)
raise newException(CatchableError, "could not parse the keystore: " & $keystoreCredRes.error)
let keystoreCred = keystoreCredRes.get()
g.membershipIndex = some(keystoreCred.treeIndex)
# now we check on the contract if the commitment actually has a membership
try:
let membershipExists = await rlnContract.memberExists(keystoreCred

View File

@ -31,7 +31,7 @@ logScope:
type WakuRlnConfig* = object
rlnRelayDynamic*: bool
rlnRelayCredIndex*: uint
rlnRelayCredIndex*: Option[uint]
rlnRelayEthContractAddress*: string
rlnRelayEthClientAddress*: string
rlnRelayCredPath*: string
@ -374,7 +374,7 @@ proc mount(conf: WakuRlnConfig,
raise newException(ValueError, "Static group keys are not valid")
groupManager = StaticGroupManager(groupSize: StaticGroupSize,
groupKeys: parsedGroupKeysRes.get(),
membershipIndex: some(conf.rlnRelayCredIndex),
membershipIndex: conf.rlnRelayCredIndex,
rlnInstance: rlnInstance)
# we don't persist credentials in static mode since they exist in ./constants.nim
else:
@ -390,7 +390,7 @@ proc mount(conf: WakuRlnConfig,
registrationHandler: registrationHandler,
keystorePath: rlnRelayCredPath,
keystorePassword: rlnRelayCredPassword,
membershipIndex: some(conf.rlnRelayCredIndex))
membershipIndex: conf.rlnRelayCredIndex)
# Initialize the groupManager
await groupManager.init()
# Start the group sync