Skip to content

Commit

Permalink
chore(rln-relay): use the only key from keystore if only 1 exists (#1984
Browse files Browse the repository at this point in the history
)

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

* fix: convert iterator to seq and then index into it
  • Loading branch information
rymnc committed Sep 4, 2023
1 parent 1b835b4 commit a14c326
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 37 deletions.
11 changes: 8 additions & 3 deletions apps/chat2/config_chat2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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.
Expand Down
11 changes: 8 additions & 3 deletions apps/wakunode2/external_config.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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\/\\\.\:\@]+$"
Expand Down
88 changes: 87 additions & 1 deletion tests/test_waku_keystore.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import
std/[os, json],
chronos,
testutils/unittests
testutils/unittests
import
../../waku/waku_keystore,
./testlib/common
Expand Down Expand Up @@ -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
4 changes: 2 additions & 2 deletions tests/waku_rln_relay/test_waku_rln_relay.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand Down
20 changes: 10 additions & 10 deletions tests/waku_rln_relay/test_wakunode_rln_relay.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
))

Expand All @@ -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"),
))

Expand All @@ -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"),
))

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
))
Expand All @@ -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,
))
Expand All @@ -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,
))
Expand Down Expand Up @@ -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,
))
Expand All @@ -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,
))
Expand All @@ -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,
))
Expand Down
4 changes: 2 additions & 2 deletions tests/wakunode_jsonrpc/test_jsonrpc_relay.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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()])
Expand Down
2 changes: 1 addition & 1 deletion tests/wakunode_rest/test_rest_relay.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 20 additions & 6 deletions waku/waku_keystore/keystore.nim
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ else:
{.push raises: [].}

import
options, json, strutils,
options,
json,
strutils,
sequtils,
std/[tables, os]

import
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
12 changes: 6 additions & 6 deletions waku/waku_rln_relay/group_manager/on_chain/group_manager.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions waku/waku_rln_relay/rln_relay.nim
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ logScope:

type WakuRlnConfig* = object
rlnRelayDynamic*: bool
rlnRelayCredIndex*: uint
rlnRelayCredIndex*: Option[uint]
rlnRelayEthContractAddress*: string
rlnRelayEthClientAddress*: string
rlnRelayCredPath*: string
Expand Down Expand Up @@ -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:
Expand All @@ -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
Expand Down

0 comments on commit a14c326

Please sign in to comment.