mirror of https://github.com/waku-org/nwaku.git
Merge pull request #2868 from waku-org/update-master-from-release-v0.30
chore: Update master from release v0.30 (#2866) * CHANGELOG.md add info for v0.30.0 * fix(rln-relay): clear nullifier log only if length is over max epoch gap (#2836) * chore: add TWN parameters for RLNv2 (#2843) * fix(rln): nullifierlog vulnerability (#2855) * chore(rln-relay): add chain-id flag to wakunode and restrict usage if mismatches rpc provider (#2858) --------- Co-authored-by: Aaryamann Challani <43716372+rymnc@users.noreply.github.com> Co-authored-by: Alvaro Revuelta <alvrevuelta@gmail.com>
This commit is contained in:
commit
49f03f1243
33
CHANGELOG.md
33
CHANGELOG.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
@ -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},
|
||||
)
|
||||
|
@ -192,6 +194,7 @@ proc setup(): Future[OnchainGroupManager] {.async.} =
|
|||
let manager = OnchainGroupManager(
|
||||
ethClientUrl: EthClient,
|
||||
ethContractAddress: $contractAddress,
|
||||
chainId: CHAIN_ID,
|
||||
ethPrivateKey: pk,
|
||||
rlnInstance: rlnInstance,
|
||||
)
|
||||
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ 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
|
||||
|
@ -84,7 +84,7 @@ proc doRlnKeystoreGenerator*(conf: WakuNodeConf) =
|
|||
# 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,
|
||||
|
|
|
@ -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: "",
|
||||
|
|
|
@ -5,6 +5,7 @@ type ClusterConf* = object
|
|||
clusterId*: uint16
|
||||
rlnRelay*: bool
|
||||
rlnRelayEthContractAddress*: string
|
||||
rlnRelayChainId*: uint
|
||||
rlnRelayDynamic*: bool
|
||||
rlnRelayBandwidthThreshold*: int
|
||||
rlnEpochSizeSec*: uint64
|
||||
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
@ -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(),
|
||||
)
|
||||
|
@ -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()
|
||||
|
@ -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())
|
||||
|
@ -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():
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
|
@ -37,6 +38,7 @@ type WakuRlnConfig* = object
|
|||
rlnRelayCredIndex*: Option[uint]
|
||||
rlnRelayEthContractAddress*: string
|
||||
rlnRelayEthClientAddress*: string
|
||||
rlnRelayChainId*: uint
|
||||
rlnRelayCredPath*: string
|
||||
rlnRelayCredPassword*: string
|
||||
rlnRelayTreePath*: string
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
||||
|
@ -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())
|
||||
|
||||
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)
|
||||
var epochsToRemove: seq[Epoch] = @[]
|
||||
for epoch in rlnPeer.nullifierLog.keys():
|
||||
let epochInt = fromEpoch(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)
|
||||
|
@ -420,6 +428,7 @@ proc mount(
|
|||
groupManager = OnchainGroupManager(
|
||||
ethClientUrl: string(conf.rlnRelayethClientAddress),
|
||||
ethContractAddress: $conf.rlnRelayEthContractAddress,
|
||||
chainId: conf.rlnRelayChainId,
|
||||
rlnInstance: rlnInstance,
|
||||
registrationHandler: registrationHandler,
|
||||
keystorePath: rlnRelayCredPath,
|
||||
|
|
Loading…
Reference in New Issue