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

chore: Update master from release v0.30 #2866

Merged
merged 12 commits into from
Jul 2, 2024
Merged
33 changes: 32 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,35 @@
## v0.29.0 (2024-06-11)
## v0.30.0 (2024-07-01)

### Notes

* Before upgrading to this version, if you are currently using RLN, make sure to remove your existing `keystore` folder and `rln_tree`
and start your installation from scratch, as
explained in [nwaku-compose](https://github.com/waku-org/nwaku-compose/blob/1b56575df9ddb904af0941a19ea1df3d36bfddfa/README.md).

### Release highlights

* RLN_v2 is used. The maximum rate can be set to
`N` messages per epoch, instead of just one message per epoch. See [this](https://github.com/waku-org/nwaku/issues/2345) for more details.

### Changes

- rln-relay: add chain-id flag to wakunode and restrict usage if mismatches rpc provider ([#2858](https://github.com/waku-org/nwaku/pull/2858))
- rln: fix nullifierlog vulnerability ([#2855](https://github.com/waku-org/nwaku/pull/2855))
- chore: add TWN parameters for RLNv2 ([#2843](https://github.com/waku-org/nwaku/pull/2843))
- fix(rln-relay): clear nullifier log only if length is over max epoch gap ([#2836](https://github.com/waku-org/nwaku/pull/2836))
- rlnv2: clean fork of rlnv2 ([#2828](https://github.com/waku-org/nwaku/issues/2828)) ([a02832fe](https://github.com/waku-org/nwaku/commit/a02832fe))
- zerokit: bump submodule ([#2830](https://github.com/waku-org/nwaku/issues/2830)) ([bd064882](https://github.com/waku-org/nwaku/commit/bd064882))

This release supports the following [libp2p protocols](https://docs.libp2p.io/concepts/protocols/):
| Protocol | Spec status | Protocol id |
| ---: | :---: | :--- |
| [`11/WAKU2-RELAY`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) | `stable` | `/vac/waku/relay/2.0.0` |
| [`12/WAKU2-FILTER`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) | `draft` | `/vac/waku/filter/2.0.0-beta1` <br />`/vac/waku/filter-subscribe/2.0.0-beta1` <br />`/vac/waku/filter-push/2.0.0-beta1` |
| [`13/WAKU2-STORE`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/13/store.md) | `draft` | `/vac/waku/store/2.0.0-beta4` |
| [`19/WAKU2-LIGHTPUSH`](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) | `draft` | `/vac/waku/lightpush/2.0.0-beta1` |
| [`66/WAKU2-METADATA`](https://github.com/waku-org/specs/blob/master/standards/core/metadata.md) | `raw` | `/vac/waku/metadata/1.0.0` |

## v0.29.0 (2024-06-19)

## What's Changed

Expand Down
19 changes: 18 additions & 1 deletion tests/waku_rln_relay/test_rln_group_manager_onchain.nim
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import
../testlib/common,
./utils

const CHAIN_ID = 1337

proc generateCredentials(rlnInstance: ptr RLN): IdentityCredential =
let credRes = membershipKeyGen(rlnInstance)
return credRes.get()
Expand Down Expand Up @@ -137,7 +139,7 @@ proc runAnvil(): Process =
anvilPath,
args = [
"--port", "8540", "--gas-limit", "300000000000000", "--balance", "1000000000",
"--chain-id", "1337",
"--chain-id", $CHAIN_ID,
],
options = {poUsePath},
)
Expand Down Expand Up @@ -192,6 +194,7 @@ proc setup(): Future[OnchainGroupManager] {.async.} =
let manager = OnchainGroupManager(
ethClientUrl: EthClient,
ethContractAddress: $contractAddress,
chainId: CHAIN_ID,
ethPrivateKey: pk,
rlnInstance: rlnInstance,
)
Expand All @@ -215,6 +218,20 @@ suite "Onchain group manager":

await manager.stop()

asyncTest "should error on initialization when chainId does not match":
let manager = await setup()
manager.chainId = CHAIN_ID + 1

(await manager.init()).isErrOr:
raiseAssert "Expected error when chainId does not match"

asyncTest "should initialize when chainId is set to 0":
let manager = await setup()
manager.chainId = 0

(await manager.init()).isOkOr:
raiseAssert $error

asyncTest "should error on initialization when loaded metadata does not match":
let manager = await setup()
(await manager.init()).isOkOr:
Expand Down
37 changes: 37 additions & 0 deletions tests/waku_rln_relay/test_waku_rln_relay.nim
Original file line number Diff line number Diff line change
Expand Up @@ -886,3 +886,40 @@ suite "Waku rln relay":
check:
buckets.len == 5
buckets == [2.0, 4.0, 6.0, 8.0, 10.0]

asyncTest "nullifierLog clearing only after epoch has passed":
let index = MembershipIndex(0)

proc runTestForEpochSizeSec(rlnEpochSizeSec: uint) {.async.} =
let wakuRlnConfig = WakuRlnConfig(
rlnRelayDynamic: false,
rlnRelayCredIndex: some(index),
rlnRelayUserMessageLimit: 1,
rlnEpochSizeSec: rlnEpochSizeSec,
rlnRelayTreePath: genTempPath("rln_tree", "waku_rln_relay_4"),
)

let wakuRlnRelay = (await WakuRlnRelay.new(wakuRlnConfig)).valueOr:
raiseAssert $error

let rlnMaxEpochGap = wakuRlnRelay.rlnMaxEpochGap
let testProofMetadata = default(ProofMetadata)
let testProofMetadataTable = {testProofMetadata.nullifier: testProofMetadata}.toTable()

for i in 0..rlnMaxEpochGap:
# we add epochs to the nullifierLog
let testEpoch = wakuRlnRelay.calcEpoch(epochTime() + float(rlnEpochSizeSec * i))
wakuRlnRelay.nullifierLog[testEpoch] = testProofMetadataTable
check: wakuRlnRelay.nullifierLog.len().uint == i + 1

check: wakuRlnRelay.nullifierLog.len().uint == rlnMaxEpochGap + 1

# clearing it now will remove 1 epoch
wakuRlnRelay.clearNullifierLog()

check: wakuRlnRelay.nullifierLog.len().uint == rlnMaxEpochGap

var testEpochSizes: seq[uint] = @[1,5,10,30,60,600]
for i in testEpochSizes:
await runTestForEpochSizeSec(i)

4 changes: 2 additions & 2 deletions tools/rln_keystore_generator/rln_keystore_generator.nim
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,15 @@ proc doRlnKeystoreGenerator*(conf: WakuNodeConf) =
debug "Transaction hash", txHash = groupManager.registrationTxHash.get()

info "Your membership has been registered on-chain.",
chainId = $groupManager.chainId.get(),
chainId = $groupManager.chainId,
contractAddress = conf.rlnRelayEthContractAddress,
membershipIndex = groupManager.membershipIndex.get()
info "Your user message limit is", userMessageLimit = conf.rlnRelayUserMessageLimit

# 6. write to keystore
let keystoreCred = KeystoreMembership(
membershipContract: MembershipContract(
chainId: $groupManager.chainId.get(), address: conf.rlnRelayEthContractAddress
chainId: $groupManager.chainId, address: conf.rlnRelayEthContractAddress
),
treeIndex: groupManager.membershipIndex.get(),
identityCredential: credential,
Expand Down
6 changes: 6 additions & 0 deletions waku/factory/external_config.nim
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ type WakuNodeConf* = object
name: "rln-relay-eth-contract-address"
.}: string

rlnRelayChainId* {.
desc: "Chain ID of the provided contract (optional, will fetch from RPC provider if not used)",
defaultValue: 0,
name: "rln-relay-chain-id"
.}: uint

rlnRelayCredPassword* {.
desc: "Password for encrypting RLN credentials",
defaultValue: "",
Expand Down
9 changes: 5 additions & 4 deletions waku/factory/networks_config.nim
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type ClusterConf* = object
clusterId*: uint16
rlnRelay*: bool
rlnRelayEthContractAddress*: string
rlnRelayChainId*: uint
rlnRelayDynamic*: bool
rlnRelayBandwidthThreshold*: int
rlnEpochSizeSec*: uint64
Expand Down Expand Up @@ -32,12 +33,12 @@ proc TheWakuNetworkConf*(T: type ClusterConf): ClusterConf =
maxMessageSize: "150KiB",
clusterId: 1,
rlnRelay: true,
rlnRelayEthContractAddress: "0xF471d71E9b1455bBF4b85d475afb9BB0954A29c4",
rlnRelayEthContractAddress: "0x4976Df0f61135EF3E5720D92eadE2e5F47A68Ef9",
rlnRelayDynamic: true,
rlnRelayChainId: 2442, # https://chainlist.org/chain/2442
rlnRelayBandwidthThreshold: 0,
rlnEpochSizeSec: 1,
# parameter to be defined with rln_v2
rlnRelayUserMessageLimit: 1,
rlnEpochSizeSec: 600,
rlnRelayUserMessageLimit: 20,
pubsubTopics:
@[
"/waku/2/rs/1/0", "/waku/2/rs/1/1", "/waku/2/rs/1/2", "/waku/2/rs/1/3",
Expand Down
1 change: 1 addition & 0 deletions waku/factory/node_factory.nim
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ proc setupProtocols(
rlnRelayDynamic: conf.rlnRelayDynamic,
rlnRelayCredIndex: conf.rlnRelayCredIndex,
rlnRelayEthContractAddress: conf.rlnRelayEthContractAddress,
rlnRelayChainId: conf.rlnRelayChainId,
rlnRelayEthClientAddress: string(conf.rlnRelayethClientAddress),
rlnRelayCredPath: conf.rlnRelayCredPath,
rlnRelayCredPassword: conf.rlnRelayCredPassword,
Expand Down
1 change: 1 addition & 0 deletions waku/factory/waku.nim
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ proc init*(T: type Waku, conf: WakuNodeConf): Result[Waku, string] =
confCopy.clusterId = twnClusterConf.clusterId
confCopy.rlnRelay = twnClusterConf.rlnRelay
confCopy.rlnRelayEthContractAddress = twnClusterConf.rlnRelayEthContractAddress
confCopy.rlnRelayChainId = twnClusterConf.rlnRelayChainId
confCopy.rlnRelayDynamic = twnClusterConf.rlnRelayDynamic
confCopy.rlnRelayBandwidthThreshold = twnClusterConf.rlnRelayBandwidthThreshold
confCopy.discv5Discovery = twnClusterConf.discv5Discovery
Expand Down
23 changes: 15 additions & 8 deletions waku/waku_rln_relay/group_manager/on_chain/group_manager.nim
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ type
wakuRlnContract*: Option[WakuRlnContractWithSender]
latestProcessedBlock*: BlockNumber
registrationTxHash*: Option[TxHash]
chainId*: Option[Quantity]
chainId*: uint
keystorePath*: Option[string]
keystorePassword*: Option[string]
registrationHandler*: Option[RegistrationHandler]
Expand Down Expand Up @@ -103,7 +103,7 @@ proc setMetadata*(
let metadataSetRes = g.rlnInstance.setMetadata(
RlnMetadata(
lastProcessedBlock: normalizedBlock,
chainId: uint64(g.chainId.get()),
chainId: g.chainId,
contractAddress: g.ethContractAddress,
validRoots: g.validRoots.toSeq(),
)
Expand Down Expand Up @@ -537,11 +537,18 @@ method init*(g: OnchainGroupManager): Future[GroupManagerResult[void]] {.async.}
g.retryWrapper(ethRpc, "Failed to connect to the Ethereum client"):
await newWeb3(g.ethClientUrl)

var fetchedChainId: uint
g.retryWrapper(fetchedChainId, "Failed to get the chain id"):
uint(await ethRpc.provider.eth_chainId())

# Set the chain id
var chainId: Quantity
g.retryWrapper(chainId, "Failed to get the chain id"):
await ethRpc.provider.eth_chainId()
g.chainId = some(chainId)
if g.chainId == 0:
warn "Chain ID not set in config, using RPC Provider's Chain ID", providerChainId = fetchedChainId

if g.chainId != 0 and g.chainId != fetchedChainId:
return err("The RPC Provided a Chain ID which is different than the provided Chain ID: provided = " & $g.chainId & ", actual = " & $fetchedChainId)

g.chainId = fetchedChainId

if g.ethPrivateKey.isSome():
let pk = g.ethPrivateKey.get()
Expand All @@ -564,7 +571,7 @@ method init*(g: OnchainGroupManager): Future[GroupManagerResult[void]] {.async.}

var keystoreQuery = KeystoreMembership(
membershipContract:
MembershipContract(chainId: $g.chainId.get(), address: g.ethContractAddress)
MembershipContract(chainId: $g.chainId, address: g.ethContractAddress)
)
if g.membershipIndex.isSome():
keystoreQuery.treeIndex = MembershipIndex(g.membershipIndex.get())
Expand Down Expand Up @@ -596,7 +603,7 @@ method init*(g: OnchainGroupManager): Future[GroupManagerResult[void]] {.async.}
warn "could not initialize with persisted rln metadata"
elif metadataGetOptRes.get().isSome():
let metadata = metadataGetOptRes.get().get()
if metadata.chainId != uint64(g.chainId.get()):
if metadata.chainId != uint(g.chainId):
return err("persisted data: chain id mismatch")

if metadata.contractAddress != g.ethContractAddress.toLower():
Expand Down
33 changes: 21 additions & 12 deletions waku/waku_rln_relay/rln_relay.nim
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

{.push raises: [].}

import
Expand Down Expand Up @@ -37,6 +38,7 @@ type WakuRlnConfig* = object
rlnRelayCredIndex*: Option[uint]
rlnRelayEthContractAddress*: string
rlnRelayEthClientAddress*: string
rlnRelayChainId*: uint
rlnRelayCredPath*: string
rlnRelayCredPassword*: string
rlnRelayTreePath*: string
Expand Down Expand Up @@ -141,7 +143,7 @@ proc updateLog*(
try:
# check if an identical record exists
if rlnPeer.nullifierLog[epoch].hasKeyOrPut(proofMetadata.nullifier, proofMetadata):
# the above condition could be `discarded` but it is kept for clarity, that slashing will
# the above condition could be `discarded` but it is kept for clarity, that slashing will
# be implemented here
# TODO: slashing logic
return ok()
Expand Down Expand Up @@ -276,8 +278,10 @@ proc validateMessageAndUpdateLog*(
if proofMetadataRes.isErr():
return MessageValidationResult.Invalid

# insert the message to the log (never errors)
discard rlnPeer.updateLog(msgProof.epoch, proofMetadataRes.get())
# insert the message to the log (never errors) only if the
# message is valid.
if isValidMessage == MessageValidationResult.Valid:
discard rlnPeer.updateLog(msgProof.epoch, proofMetadataRes.get())

return isValidMessage

Expand Down Expand Up @@ -305,21 +309,25 @@ proc appendRLNProof*(
let proof = rlnPeer.groupManager.generateProof(input, epoch, nonce).valueOr:
return err("could not generate rln-v2 proof: " & $error)


msg.proof = proof.encode().buffer
return ok()

proc clearNullifierLog(rlnPeer: WakuRlnRelay) =
proc clearNullifierLog*(rlnPeer: WakuRlnRelay) =
# clear the first MaxEpochGap epochs of the nullifer log
# if more than MaxEpochGap epochs are in the log
# note: the epochs are ordered ascendingly
if rlnPeer.nullifierLog.len().uint < rlnPeer.rlnMaxEpochGap:
return
let currentEpoch = fromEpoch(rlnPeer.getCurrentEpoch())

var epochsToRemove: seq[Epoch] = @[]
for epoch in rlnPeer.nullifierLog.keys():
let epochInt = fromEpoch(epoch)

trace "clearing epochs from the nullifier log", count = rlnPeer.rlnMaxEpochGap
let epochsToClear = rlnPeer.nullifierLog.keys().toSeq()[0 ..< rlnPeer.rlnMaxEpochGap]
for epoch in epochsToClear:
rlnPeer.nullifierLog.del(epoch)
# clean all epochs that are +- rlnMaxEpochGap from the current epoch
if (currentEpoch+rlnPeer.rlnMaxEpochGap) <= epochInt or epochInt <= (currentEpoch-rlnPeer.rlnMaxEpochGap):
epochsToRemove.add(epoch)

for epochRemove in epochsToRemove:
trace "clearing epochs from the nullifier log", currentEpoch = currentEpoch, cleanedEpoch = fromEpoch(epochRemove)
rlnPeer.nullifierLog.del(epochRemove)

proc generateRlnValidator*(
wakuRlnRelay: WakuRLNRelay, spamHandler = none(SpamHandler)
Expand Down Expand Up @@ -420,6 +428,7 @@ proc mount(
groupManager = OnchainGroupManager(
ethClientUrl: string(conf.rlnRelayethClientAddress),
ethContractAddress: $conf.rlnRelayEthContractAddress,
chainId: conf.rlnRelayChainId,
rlnInstance: rlnInstance,
registrationHandler: registrationHandler,
keystorePath: rlnRelayCredPath,
Expand Down
Loading