feat(mix): bump libp2p stack to v2.0.0 + adopt stateless RLN spam protection

Combines five dep-and-build changes that all flow from the libp2p v2.0.0
upgrade and the move to the extracted libp2p_mix / mix-rln plugin stack:

waku.nimble:
  * libp2p: ff8d51857 -> c43199378 (release/v2.0.0 tip; sha-pinned until
    vacp2p cuts a v2.0.0 tag).
  * Drop the bare `zlib < 0.2` cap — no longer needed by the upgraded
    libp2p.
  * websock: bare ">= 0.4.0" — replaces the d4cd68b URL+SHA workaround
    that pinned through a libp2p commit-specific websock SHA.
  * nim-json-rpc: switch to chaitanyaprem/nim-json-rpc#f05fad25 — relaxes
    websock cap to allow >=0.4.0. TODO: revert to status-im/nim-json-rpc
    once status-im/nim-json-rpc#277 merges and a tag is cut.
  * lsquic: bare ">= 0.4.1" (drops URL form).
  * Add mix-rln-spam-protection-plugin pin (23b278b4) and nim-libp2p-mix
    pin (50c4ab4f — PR #14 HEAD); the plugin pins the same libp2p_mix
    SHA so the diamond dep collapses to a single source.

waku/factory/waku.nim:
  * Explicit HPService.setup(switch) / AutonatService.setup(switch)
    calls. libp2p v2.0.0's Service lifecycle refactor (libp2p#2462)
    removed switch.start's auto-setup loop, so any caller that assigns
    directly to switch.services (we do) is responsible for calling
    setup() themselves. Without it, AutonatService.addressMapper stays
    nil and peerInfo.expandAddrs SIGSEGVs during start(). Wrapped in
    try/except for ServiceSetupError so a setup failure surfaces as a
    logged error rather than a crash.

Build / scripts:
  * scripts/build_rln_mix.sh removed and Makefile simplified — librln
    is now a single shared archive built from zerokit's `stateless`
    features (no separate librln_mix archive).
  * simulations/mixnet/build_setup.sh + setup_credentials.nim updated
    to use librln_v2.0.2.a directly and run RLN keystore setup before
    nodes start.

Validated:
  * Cold local-cache nimble setup --localdeps -y.
  * wakunode2 and chat2mix link cleanly.
  * Mixnet roundtrip sim: [PASS] bob received message from alice.
  * RLN proof generation + verification on every in-path mix node:
    5 gen_called == 5 verified, 0 SPAM_PROOF_* errors.
This commit is contained in:
Prem Chaitanya Prathi 2026-06-04 16:54:44 +05:30
parent fb72f18d45
commit d8bbef0c5b
No known key found for this signature in database
37 changed files with 245 additions and 165 deletions

View File

@ -172,15 +172,10 @@ deps: | nimble
##################
## RLN ##
##################
.PHONY: librln mix-librln
.PHONY: librln
LIBRLN_BUILDDIR := $(CURDIR)/vendor/zerokit
LIBRLN_VERSION := v2.0.2
MIX_LIBRLN_VERSION ?= v2.0.0
MIX_LIBRLN_REPO ?= https://github.com/vacp2p/zerokit.git
MIX_LIBRLN_SRCDIR ?= $(CURDIR)/build/zerokit_$(MIX_LIBRLN_VERSION)
MIX_LIBRLN_FILE ?= $(CURDIR)/build/librln_mix_$(MIX_LIBRLN_VERSION).a
MIX_LIBRLN_NIM_PARAMS := --passL:$(MIX_LIBRLN_FILE) --passL:-lm
ifeq ($(detected_OS),Windows)
LIBRLN_FILE ?= rln.lib
@ -193,20 +188,16 @@ $(LIBRLN_FILE):
echo -e $(BUILD_MSG) "$@" && \
bash scripts/build_rln.sh $(LIBRLN_BUILDDIR) $(LIBRLN_VERSION) $(LIBRLN_FILE)
$(MIX_LIBRLN_FILE):
echo -e $(BUILD_MSG) "$@" && \
./scripts/build_rln_mix.sh $(MIX_LIBRLN_SRCDIR) $(MIX_LIBRLN_VERSION) $(MIX_LIBRLN_FILE) $(MIX_LIBRLN_REPO)
# Single zerokit archive (stateless features) for both relay and mix plugin.
# Plugin keeps its Merkle tree Nim-side, so it does not need the pmtree FFIs
# the default features would expose -- and including a second archive built
# with different features causes duplicate-symbol link errors.
librln: | $(LIBRLN_FILE)
$(eval NIM_PARAMS += --passL:$(LIBRLN_FILE) --passL:-lm)
mix-librln: | $(MIX_LIBRLN_FILE)
$(eval NIM_PARAMS += --passL:$(MIX_LIBRLN_FILE) --passL:-lm)
clean-librln:
cargo clean --manifest-path vendor/zerokit/rln/Cargo.toml
rm -f $(LIBRLN_FILE)
rm -f $(MIX_LIBRLN_FILE)
clean: | clean-librln
@ -228,7 +219,7 @@ testwaku: | build-deps build rln-deps librln
echo -e $(BUILD_MSG) "build/$@" && \
nimble test
wakunode2: | build-deps build deps librln mix-librln
wakunode2: | build-deps build deps librln
echo -e $(BUILD_MSG) "build/$@" && \
nimble wakunode2
@ -248,7 +239,7 @@ chat2: | build-deps build deps librln
echo -e $(BUILD_MSG) "build/$@" && \
nimble chat2
chat2mix: | build-deps build deps librln mix-librln
chat2mix: | build-deps build deps librln
echo -e $(BUILD_MSG) "build/$@" && \
nimble chat2mix

View File

@ -18,6 +18,7 @@ import
metrics,
metrics/chronos_httpserver
import
libp2p/crypto/rng as libp2p_rng,
libp2p/[
switch, # manage transports, a single entry point for dialing and listening
crypto/crypto, # cryptographic functions
@ -29,9 +30,9 @@ import
peerid, # Implement how peers interact
protobuf/minprotobuf, # message serialisation/deserialisation from and to protobufs
nameresolving/dnsresolver,
protocols/mix/curve25519,
protocols/mix/mix_protocol,
] # define DNS resolution
# libp2p_mix has been extracted into its own package; import from there.
import libp2p_mix/curve25519, libp2p_mix/mix_protocol
import
waku/[
waku_core,
@ -60,7 +61,6 @@ import ../../waku/waku_rln_relay
logScope:
topics = "chat2 mix"
#########################
## Mix Spam Protection ##
#########################
@ -160,8 +160,7 @@ proc maintainSpamProtectionSubscription(
noFailedSubscribes = 0
await sleepAsync(SubscriptionMaintenance)
const Help =
"""
const Help = """
Commands: /[?|help|connect|nick|exit]
help: Prints this help
connect: dials a remote peer
@ -501,7 +500,7 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
if conf.nodekey.isSome():
conf.nodekey.get()
else:
PrivateKey.random(Secp256k1, rng[]).tryGet()
PrivateKey.random(Secp256k1, libp2p_rng.newBearSslRng(rng)).tryGet()
# set log level
if conf.logLevel != LogLevel.NONE:
@ -576,12 +575,13 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
if kadBootstrapPeers.len > 0:
node.wakuKademlia = WakuKademlia.new(
node.switch,
ExtendedKademliaDiscoveryParams(
ExtendedServiceDiscoveryParams(
bootstrapNodes: kadBootstrapPeers,
mixPubKey: some(mixPubKey),
advertiseMix: false,
),
node.peerManager,
rng = libp2p_rng.newBearSslRng(node.rng),
getMixNodePoolSize = proc(): int {.gcsafe, raises: [].} =
if node.wakuMix.isNil():
0
@ -788,7 +788,7 @@ proc main(rng: ref HmacDrbgContext) {.async.} =
raise e
when isMainModule: # isMainModule = true when the module is compiled as the main file
let rng = crypto.newRng()
let rng = HmacDrbgContext.new()
try:
waitFor(main(rng))
except CatchableError as e:

View File

@ -90,6 +90,14 @@ if not defined(macosx) and not defined(android):
nimStackTraceOverride
switch("import", "libbacktrace")
# Auto-import the Option[T] valueOr/withValue shim for every compilation
# unit (libp2p 1.15.3 dropped the Option overloads previously provided by
# libp2p/utility). Per-file imports follow cover-traffic's pattern for
# the 15 files touched there, but our branch has additional downstream
# call sites that need the shim too -- auto-import covers them without
# requiring every file to remember the explicit import.
switch("import", "waku/common/option_shims")
--define:
nimOldCaseObjects
# https://github.com/status-im/nim-confutils/issues/9

View File

@ -7,8 +7,8 @@ import
confutils,
libp2p/crypto/crypto,
libp2p/crypto/curve25519,
libp2p/protocols/mix,
libp2p/protocols/mix/curve25519,
libp2p_mix,
libp2p_mix/curve25519,
libp2p/multiaddress,
eth/keys,
eth/p2p/discoveryv5/enr,

View File

@ -1,43 +0,0 @@
#!/usr/bin/env bash
# Build a separate, pinned RLN library for mix spam-protection usage.
# This keeps the main nwaku RLN dependency flow unchanged.
set -euo pipefail
source_dir="${1:-}"
version="${2:-}"
output_file="${3:-}"
repo_url="${4:-https://github.com/vacp2p/zerokit.git}"
if [[ -z "${source_dir}" || -z "${version}" || -z "${output_file}" ]]; then
echo "Usage: $0 <source_dir> <version_tag> <output_file> [repo_url]"
exit 1
fi
mkdir -p "$(dirname "${source_dir}")"
mkdir -p "$(dirname "${output_file}")"
if [[ ! -d "${source_dir}/.git" ]]; then
echo "Cloning zerokit ${version} from ${repo_url}..."
if [[ -e "${source_dir}" ]]; then
echo "Path exists but is not a git repository: ${source_dir}"
echo "Please remove it and retry."
exit 1
fi
git clone --depth 1 --branch "${version}" "${repo_url}" "${source_dir}"
else
echo "Using existing zerokit checkout in ${source_dir}"
current_tag="$(git -C "${source_dir}" describe --tags --exact-match 2>/dev/null || true)"
if [[ "${current_tag}" != "${version}" ]]; then
echo "Updating zerokit checkout to ${version}..."
git -C "${source_dir}" fetch --tags origin "${version}"
git -C "${source_dir}" checkout --detach "${version}"
fi
fi
echo "Building mix RLN library from source (version ${version})..."
cargo build --release -p rln --manifest-path "${source_dir}/rln/Cargo.toml"
cp "${source_dir}/target/release/librln.a" "${output_file}"
echo "Successfully built ${output_file}"

View File

@ -5,8 +5,8 @@ cd ../..
ROOT_DIR=$(pwd)
source "$ROOT_DIR/env.sh"
# Prefer explicitly provided RLN library path, otherwise use the one built by `make mix-librln`.
LIBRLN_PATH=${LIBRLN_PATH:-"$ROOT_DIR/build/librln_mix_v2.0.0.a"}
# Prefer explicitly provided RLN library path, otherwise use the one built by `make librln`.
LIBRLN_PATH=${LIBRLN_PATH:-"$ROOT_DIR/librln_v2.0.2.a"}
# Clean up old files first
rm -f "$MIXNET_DIR/rln_tree.db" "$MIXNET_DIR"/rln_keystore_*.json

View File

@ -27,19 +27,23 @@ requires "nim >= 2.2.4",
"toml_serialization",
"faststreams",
# Networking & P2P
"https://github.com/vacp2p/nim-libp2p.git#ff8d51857b4b79a68468e7bcc27b2026cca02996",
# nim-libp2p: release/v2.0.0 tip (c43199378). SHA-pinned because vacp2p
# hasn't published a v2.0.0 git tag yet.
"https://github.com/vacp2p/nim-libp2p.git#c43199378f46d0aaf61be1cad1ee1d63e8f665d6",
"eth",
"nat_traversal",
"dnsdisc",
"dnsclient",
"httputils >= 0.4.1",
"websock >= 0.3.0",
"https://github.com/status-im/nim-websock >= 0.4.0",
# Cryptography
"nimcrypto == 0.6.4", # 0.6.4 used in libp2p. Version 0.7.3 makes test to crash on Ubuntu.
"secp256k1",
"bearssl",
# RPC & APIs
"https://github.com/status-im/nim-json-rpc.git#43bbf499143eb45046c83ac9794c9e3280a2b8e7",
# TODO: revert to status-im/nim-json-rpc once
# https://github.com/status-im/nim-json-rpc/pull/277 merges + tag cut.
"https://github.com/chaitanyaprem/nim-json-rpc.git#f05fad251a1ceb845db963902b54295e7f37fb99",
"presto",
"web3",
# Database
@ -53,20 +57,28 @@ requires "nim >= 2.2.4",
"unicodedb",
"results",
"minilru",
"zlib < 0.2",
"zlib",
# Debug & Testing
"testutils",
"unittest2"
# Packages not on nimble (use git URLs)
requires "https://github.com/logos-messaging/nim-ffi#v0.1.3"
requires "https://github.com/logos-co/mix-rln-spam-protection-plugin.git#037f8e100bfedffdbad1c4442e760d10a2437428"
requires "https://github.com/logos-co/mix-rln-spam-protection-plugin.git#23b278b4ab21193ad4e9ce76015f008db7332a6f"
# nim-libp2p-mix: extracted mix protocol used by the plugin and by waku's
# mix integration layer. Tip of experiment/drop-nimble-lock (PR #14, stacked
# on chore/bump-libp2p-v2.0.0). Carries the v2.0.0 bump + sink overrides +
# AddressConfidence.Infinite + deeper move-semantics propagation + the
# lockfile-as-build-artefact cleanup. Re-bump to master SHA once #14 lands.
# The plugin pins the same SHA — keeps the diamond dep collapsed.
requires "https://github.com/logos-co/nim-libp2p-mix.git#50c4ab4fa788a33eb12a0a2cecaa708873352b58"
requires "https://github.com/logos-messaging/nim-sds.git#abdd40cc645f1b024c3ee99cced7e287c4e4c441"
requires "https://github.com/NagyZoltanPeter/nim-brokers.git#v3.1.1"
requires "https://github.com/vacp2p/nim-lsquic"
requires "lsquic >= 0.4.1"
requires "https://github.com/vacp2p/nim-jwt.git#057ec95eb5af0eea9c49bfe9025b3312c95dc5f2"
proc getMyCPU(): string =

View File

@ -0,0 +1,31 @@
# Compatibility shims for std/options.
# libp2p 1.15.2 (and earlier) exposed `valueOr` / `withValue` for std `Option[T]`
# via `libp2p/utility`. libp2p 1.15.3 dropped those overloads (only `Opt[T]` and
# `Result[T, E]` remain), which breaks existing waku code that still uses
# std `Option`. Restore the templates locally so the codebase keeps compiling
# while the upstream API gap is resolved.
{.push raises: [].}
import std/options
template valueOr*[T](self: Option[T], def: untyped): T =
let tmp = self
if tmp.isSome():
tmp.get()
else:
def
template withValue*[T](self: Option[T], value, body: untyped): untyped =
let tmp = self
if tmp.isSome():
let value {.inject.} = tmp.get()
body
template withValue*[T](self: Option[T], value, body, elseBody: untyped): untyped =
let tmp = self
if tmp.isSome():
let value {.inject.} = tmp.get()
body
else:
elseBody

View File

@ -24,6 +24,7 @@ import
import std/times except TimeInterval, Duration, seconds, minutes
import ../option_shims
import ./[single_token_limiter, service_metrics, timed_map]
export token_bucket, setting, service_metrics

View File

@ -2,6 +2,7 @@ import
chronos,
chronicles,
bearssl/rand,
libp2p/crypto/rng as libp2p_rng,
libp2p/protocols/connectivity/autonat/client,
libp2p/protocols/connectivity/autonat/service
@ -15,7 +16,8 @@ proc getAutonatService*(rng: ref HmacDrbgContext): AutonatService =
## in the calculation.
let autonatService = AutonatService.new(
autonatClient = AutonatClient.new(),
rng = rng,
# libp2p 1.15.3: AutonatService.new now takes libp2p `Rng`.
rng = libp2p_rng.newBearSslRng(rng),
scheduleInterval = AutonatCheckInterval,
askNewConnectedPeers = false,
numPeersToAsk = 3,

View File

@ -10,6 +10,7 @@ import
eth/keys as eth_keys,
eth/p2p/discoveryv5/node,
eth/p2p/discoveryv5/protocol
import ../common/option_shims
import waku/[net/auto_port, node/peer_manager/peer_manager, waku_core, waku_enr]
export protocol, waku_enr

View File

@ -9,9 +9,10 @@ import
libp2p/[peerid, multiaddress, switch],
libp2p/extended_peer_record,
libp2p/crypto/curve25519,
libp2p/protocols/[kademlia, kad_disco],
libp2p/protocols/kademlia_discovery/types as kad_types,
libp2p/protocols/mix/mix_protocol
libp2p/protocols/[kademlia, service_discovery],
libp2p/protocols/service_discovery/types as kad_types,
libp2p/crypto/rng as libp2p_rng,
libp2p_mix/mix_protocol
import waku/waku_core, waku/node/peer_manager
@ -19,20 +20,20 @@ logScope:
topics = "waku extended kademlia discovery"
const
DefaultExtendedKademliaDiscoveryInterval* = chronos.seconds(5)
ExtendedKademliaDiscoveryStartupDelay* = chronos.seconds(5)
DefaultExtendedServiceDiscoveryInterval* = chronos.seconds(5)
ExtendedServiceDiscoveryStartupDelay* = chronos.seconds(5)
type
MixNodePoolSizeProvider* = proc(): int {.gcsafe, raises: [].}
NodeStartedProvider* = proc(): bool {.gcsafe, raises: [].}
ExtendedKademliaDiscoveryParams* = object
ExtendedServiceDiscoveryParams* = object
bootstrapNodes*: seq[(PeerId, seq[MultiAddress])]
mixPubKey*: Option[Curve25519Key]
advertiseMix*: bool = false
WakuKademlia* = ref object
protocol*: KademliaDiscovery
protocol*: ServiceDiscovery
peerManager: PeerManager
discoveryLoop: Future[void]
running*: bool
@ -42,21 +43,24 @@ type
proc new*(
T: type WakuKademlia,
switch: Switch,
params: ExtendedKademliaDiscoveryParams,
params: ExtendedServiceDiscoveryParams,
peerManager: PeerManager,
rng: libp2p_rng.Rng,
getMixNodePoolSize: MixNodePoolSizeProvider = nil,
isNodeStarted: NodeStartedProvider = nil,
): Result[T, string] =
if params.bootstrapNodes.len == 0:
info "creating kademlia discovery as seed node (no bootstrap nodes)"
let kademlia = KademliaDiscovery.new(
# libp2p 1.15.3: ServiceDiscovery.new requires `rng: Rng` (libp2p type).
let kademlia = ServiceDiscovery.new(
switch,
bootstrapNodes = params.bootstrapNodes,
config = KadDHTConfig.new(
validator = kad_types.ExtEntryValidator(), selector = kad_types.ExtEntrySelector()
),
codec = ExtendedKademliaDiscoveryCodec,
rng = rng,
codec = ExtendedServiceDiscoveryCodec,
)
try:
@ -68,11 +72,9 @@ proc new*(
# initial self-signed peer record published to the DHT
if params.advertiseMix:
if params.mixPubKey.isSome():
let alreadyAdvertising = kademlia.startAdvertising(
kademlia.startAdvertising(
ServiceInfo(id: MixProtocolID, data: @(params.mixPubKey.get()))
)
if alreadyAdvertising:
warn "mix service was already being advertised"
debug "extended kademlia advertising mix service",
keyHex = byteutils.toHex(params.mixPubKey.get()),
bootstrapNodes = params.bootstrapNodes.len
@ -162,17 +164,18 @@ proc lookupMixPeers*(
return err("cannot lookup mix peers: kademlia not mounted")
let mixService = ServiceInfo(id: MixProtocolID, data: @[])
var records: seq[ExtendedPeerRecord]
try:
records = await wk.protocol.lookup(mixService)
except CatchableError:
return err("mix peer lookup failed: " & getCurrentExceptionMsg())
let advertisements =
try:
(await wk.protocol.lookup(mixService)).valueOr:
return err("mix peer lookup failed: " & error)
except CatchableError:
return err("mix peer lookup failed: " & getCurrentExceptionMsg())
debug "mix peer lookup returned records", numRecords = records.len
debug "mix peer lookup returned records", numRecords = advertisements.len
var added = 0
for record in records:
let peerOpt = remotePeerInfoFrom(record)
for ad in advertisements:
let peerOpt = remotePeerInfoFrom(ad.data)
if peerOpt.isNone():
continue
@ -197,12 +200,12 @@ proc runDiscoveryLoop(
while wk.running:
# Wait for node to be started
if not wk.isNodeStarted.isNil() and not wk.isNodeStarted():
await sleepAsync(ExtendedKademliaDiscoveryStartupDelay)
await sleepAsync(ExtendedServiceDiscoveryStartupDelay)
continue
var records: seq[ExtendedPeerRecord]
try:
records = await wk.protocol.randomRecords()
records = await wk.protocol.lookupRandom()
except CatchableError as e:
warn "extended kademlia discovery failed", error = e.msg
await sleepAsync(interval)
@ -247,7 +250,7 @@ proc runDiscoveryLoop(
proc start*(
wk: WakuKademlia,
interval: Duration = DefaultExtendedKademliaDiscoveryInterval,
interval: Duration = DefaultExtendedServiceDiscoveryInterval,
minMixPeers: int = 0,
): Future[Result[void, string]] {.async: (raises: []).} =
if wk.running:

View File

@ -5,6 +5,8 @@ import
results,
chronicles,
libp2p/crypto/crypto,
libp2p/crypto/rng as libp2p_rng,
bearssl/rand,
libp2p/builders,
libp2p/nameresolving/nameresolver,
libp2p/transports/wstransport,
@ -15,13 +17,14 @@ import
../waku_enr,
../discovery/waku_discv5,
../waku_node,
../node/waku_switch,
../node/peer_manager,
../common/rate_limit/setting,
../common/utils/parse_size_units
type
WakuNodeBuilder* = object # General
nodeRng: Option[ref crypto.HmacDrbgContext]
nodeRng: Option[ref HmacDrbgContext]
nodeKey: Option[crypto.PrivateKey]
netConfig: Option[NetConfig]
record: Option[enr.Record]
@ -57,7 +60,7 @@ proc init*(T: type WakuNodeBuilder): WakuNodeBuilder =
## General
proc withRng*(builder: var WakuNodeBuilder, rng: ref crypto.HmacDrbgContext) =
proc withRng*(builder: var WakuNodeBuilder, rng: ref HmacDrbgContext) =
builder.nodeRng = some(rng)
proc withNodeKey*(builder: var WakuNodeBuilder, nodeKey: crypto.PrivateKey) =
@ -157,9 +160,9 @@ proc withSwitchConfiguration*(
## Build
proc build*(builder: WakuNodeBuilder): Result[WakuNode, string] =
var rng: ref crypto.HmacDrbgContext
var rng: ref HmacDrbgContext
if builder.nodeRng.isNone():
rng = crypto.newRng()
rng = HmacDrbgContext.new()
else:
rng = builder.nodeRng.get()
@ -189,8 +192,9 @@ proc build*(builder: WakuNodeBuilder): Result[WakuNode, string] =
address = builder.netConfig.get().hostAddress,
wsAddress = builder.netConfig.get().wsHostAddress,
transportFlags = {ServerFlags.ReuseAddr, ServerFlags.TcpNoDelay},
rng = rng,
maxConnections = builder.switchMaxConnections.get(builders.MaxConnections),
# newWakuSwitch now expects libp2p `Rng`; wrap our BearSSL rng.
rng = libp2p_rng.newBearSslRng(rng),
maxConnections = builder.switchMaxConnections.get(waku_switch.MaxConnections),
wssEnabled = builder.netConfig.get().wssEnabled,
secureKeyPath = builder.switchSslSecureKey.get(""),
secureCertPath = builder.switchSslSecureCert.get(""),
@ -210,7 +214,7 @@ proc build*(builder: WakuNodeBuilder): Result[WakuNode, string] =
maxServicePeers = some(builder.maxServicePeers),
colocationLimit = builder.colocationLimit,
shardedPeerManagement = builder.shardAware,
maxConnections = builder.switchMaxConnections.get(builders.MaxConnections),
maxConnections = builder.switchMaxConnections.get(waku_switch.MaxConnections),
)
var node: WakuNode

View File

@ -1,5 +1,5 @@
import chronicles, std/options, results
import libp2p/crypto/crypto, libp2p/crypto/curve25519, libp2p/protocols/mix/curve25519
import libp2p/crypto/crypto, libp2p/crypto/curve25519, libp2p_mix/curve25519
import ../waku_conf, waku/waku_mix
logScope:

View File

@ -1,6 +1,8 @@
import
libp2p/crypto/crypto,
libp2p/crypto/rng as libp2p_rng,
libp2p/multiaddress,
bearssl/rand,
std/[net, options, sequtils],
stint,
chronicles,
@ -299,7 +301,9 @@ proc nodeKey(
return ok(builder.nodeKey.get())
else:
warn "missing node key, generating new set"
let nodeKey = crypto.PrivateKey.random(Secp256k1, rng[]).valueOr:
# libp2p 1.15.3: PrivateKey.random now expects the libp2p `Rng` (ref
# object wrapping a ref HmacDrbgContext). Wrap the BearSSL rng.
let nodeKey = crypto.PrivateKey.random(Secp256k1, libp2p_rng.newBearSslRng(rng)).valueOr:
error "Failed to generate key", error = error
return err("Failed to generate key: " & $error)
return ok(nodeKey)
@ -443,7 +447,7 @@ proc applyNetworkConf(builder: var WakuConfBuilder) =
warn "Failed to process entry nodes from network conf", error = processed.error()
proc build*(
builder: var WakuConfBuilder, rng: ref HmacDrbgContext = crypto.newRng()
builder: var WakuConfBuilder, rng: ref HmacDrbgContext = HmacDrbgContext.new()
): Result[WakuConf, string] =
## Return a WakuConf that contains all mandatory parameters
## Applies some sane defaults that are applicable across any usage

View File

@ -7,7 +7,9 @@ import
libp2p/protocols/connectivity/relay/relay,
libp2p/nameresolving/dnsresolver,
libp2p/crypto/crypto,
libp2p/crypto/curve25519
libp2p/crypto/curve25519,
libp2p/crypto/rng as libp2p_rng,
bearssl/rand
import
./internal_config,
@ -175,12 +177,13 @@ proc setupProtocols(
node.wakuKademlia = WakuKademlia.new(
node.switch,
ExtendedKademliaDiscoveryParams(
ExtendedServiceDiscoveryParams(
bootstrapNodes: conf.kademliaDiscoveryConf.get().bootstrapNodes,
mixPubKey: mixPubKey,
advertiseMix: conf.mixConf.isSome(),
),
node.peerManager,
rng = libp2p_rng.newBearSslRng(node.rng),
getMixNodePoolSize = proc(): int {.gcsafe, raises: [].} =
if node.wakuMix.isNil():
0
@ -458,7 +461,7 @@ proc startNode*(
return ok()
proc setupNode*(
wakuConf: WakuConf, rng: ref HmacDrbgContext = crypto.newRng(), relay: Relay
wakuConf: WakuConf, rng: ref HmacDrbgContext = HmacDrbgContext.new(), relay: Relay
): Future[Result[WakuNode, string]] {.async.} =
let netConfig = (
await networkConfiguration(

View File

@ -9,6 +9,8 @@ import
libp2p/protocols/connectivity/relay/client,
libp2p/wire,
libp2p/crypto/crypto,
libp2p/crypto/rng as libp2p_rng,
bearssl/rand,
libp2p/protocols/pubsub/gossipsub,
libp2p/services/autorelayservice,
libp2p/services/hpservice,
@ -102,12 +104,29 @@ proc setupSwitchServices(
## The node is considered to be behind a NAT or firewall and then it
## should struggle to be reachable and establish connections to other nodes
const MaxNumRelayServers = 2
# libp2p 1.15.3: AutoRelayService.new now expects libp2p `Rng`.
let autoRelayService = AutoRelayService.new(
MaxNumRelayServers, RelayClient(circuitRelay), onReservation, rng
MaxNumRelayServers,
RelayClient(circuitRelay),
onReservation,
libp2p_rng.newBearSslRng(rng),
)
let holePunchService = HPService.new(autonatService, autoRelayService)
# libp2p v2.0.0: switch.start() no longer auto-calls service.setup() (part
# of the Service lifecycle refactor in libp2p#2462). Without setup,
# HPService's wrapped Autonat/AutoRelay leave their addressMapper field
# nil, which makes peerInfo.expandAddrs SIGSEGV during start().
try:
holePunchService.setup(waku.node.switch)
except ServiceSetupError as e:
error "HPService setup failed", description = e.msg
waku.node.switch.services = @[Service(holePunchService)]
else:
# Same reason as above: AutonatService.setup() initializes addressMapper.
try:
autonatService.setup(waku.node.switch)
except ServiceSetupError as e:
error "AutonatService setup failed", description = e.msg
waku.node.switch.services = @[Service(autonatService)]
## Initialisation
@ -175,7 +194,7 @@ proc setupAppCallbacks(
proc new*(
T: type Waku, wakuConf: WakuConf, appCallbacks: AppCallbacks = nil
): Future[Result[Waku, string]] {.async.} =
let rng = crypto.newRng()
let rng = HmacDrbgContext.new()
let brokerCtx = globalBrokerContext()
logging.setupLog(wakuConf.logLevel, wakuConf.logFormat)

View File

@ -1,7 +1,11 @@
import chronicles, chronos, results
import std/options
import brokers/broker_context
import waku/node/peer_manager, waku/waku_core, waku/waku_lightpush/[common, client, rpc]
import
waku/common/option_shims,
waku/node/peer_manager,
waku/waku_core,
waku/waku_lightpush/[common, client, rpc]
import ./[delivery_task, send_processor]

View File

@ -17,7 +17,7 @@ import
libp2p/transports/tcptransport,
libp2p/transports/wstransport,
libp2p/utility,
libp2p/protocols/mix
libp2p_mix
import
../waku_node,
@ -28,6 +28,7 @@ import
../../waku_lightpush/client as lightpush_client,
../../waku_lightpush as lightpush_protocol,
../peer_manager,
../../common/option_shims,
../../common/rate_limit/setting,
../../waku_rln_relay

View File

@ -12,6 +12,7 @@ import
libp2p/utility
import ../waku_node, ../peer_manager
import libp2p/crypto/rng as libp2p_rng
logScope:
topics = "waku node ping api"
@ -20,7 +21,9 @@ proc mountLibp2pPing*(node: WakuNode) {.async: (raises: []).} =
info "mounting libp2p ping protocol"
try:
node.libp2pPing = Ping.new(rng = node.rng)
# libp2p 1.15.3: Ping.new now expects libp2p's `Rng` (ref object
# wrapping a ref HmacDrbgContext). Wrap the node's BearSSL rng.
node.libp2pPing = Ping.new(rng = libp2p_rng.newBearSslRng(node.rng))
except Exception as e:
error "failed to create ping", error = getCurrentExceptionMsg()

View File

@ -31,6 +31,7 @@ import
waku_mix,
node/waku_node,
node/peer_manager,
common/option_shims,
events/message_events,
]

View File

@ -20,12 +20,14 @@ import
waku_enr/sharding,
waku_enr/capabilities,
events/peer_events,
common/option_shims,
common/nimchronos,
common/enr,
common/callbacks,
common/utils/parse_size_units,
node/health_monitor/online_monitor,
],
../waku_switch,
./peer_store/peer_storage,
./waku_peer_store

View File

@ -7,9 +7,11 @@ import
eth/p2p/discoveryv5/enr,
libp2p/builders,
libp2p/peerstore,
libp2p/crypto/curve25519
libp2p/crypto/curve25519,
libp2p_mix/pool
import
../../common/option_shims,
../../waku_core,
../../waku_enr/sharding,
../../waku_enr/capabilities,

View File

@ -23,8 +23,8 @@ import
libp2p/transports/wstransport,
libp2p/utility,
libp2p/utils/offsettedseq,
libp2p/protocols/mix,
libp2p/protocols/mix/mix_protocol,
libp2p_mix,
libp2p_mix/mix_protocol,
brokers/broker_context,
brokers/request_broker
@ -52,6 +52,7 @@ import
waku_enr,
waku_peer_exchange,
waku_rln_relay,
common/option_shims,
common/rate_limit/setting,
common/callbacks,
common/nimchronos,
@ -219,7 +220,7 @@ proc new*(
peerManager: PeerManager,
rateLimitSettings: ProtocolRateLimitSettings = DefaultProtocolRateLimit,
# TODO: make this argument required after tests are updated
rng: ref HmacDrbgContext = crypto.newRng(),
rng: ref HmacDrbgContext = HmacDrbgContext.new(),
): T {.raises: [Defect, LPError, IOError, TLSStreamProtocolError].} =
## Creates a Waku Node instance.

View File

@ -7,6 +7,7 @@ import
chronicles,
eth/keys,
libp2p/crypto/crypto,
libp2p/crypto/rng as libp2p_rng,
libp2p/protocols/pubsub/gossipsub,
libp2p/protocols/rendezvous,
libp2p/protocols/connectivity/relay/relay,
@ -18,11 +19,15 @@ import
# override nim-libp2p default value (which is also 1)
const MaxConnectionsPerPeer* = 1
proc withWsTransport*(b: SwitchBuilder): SwitchBuilder =
b.withTransport(
proc(upgr: Upgrade, privateKey: crypto.PrivateKey): Transport =
WsTransport.new(upgr)
)
# libp2p 1.15.3 ships a built-in `withWsTransport` matching this name, so
# the plain-WS wrapper that used to live here is now redundant. Callers
# that did `b.withWsTransport()` resolve to libp2p's overload (zero args =
# no TLS, no flags). Callers passing `tlsPrivateKey=`/`tlsCertificate=`
# also use libp2p's built-in.
# nim-libp2p#2329 made libp2p's MaxConnections const private (renamed to
# DefaultMaxConnections); redeclare here to keep waku's cap explicit.
const MaxConnections* = 50
proc getSecureKey(path: string): TLSPrivateKey {.raises: [Defect, IOError].} =
trace "Key path is.", path = path
@ -59,7 +64,7 @@ proc newWakuSwitch*(
wsAddress = none(MultiAddress),
secureManagers: openarray[SecureProtocol] = [SecureProtocol.Noise],
transportFlags: set[ServerFlags] = {},
rng: ref HmacDrbgContext,
rng: libp2p_rng.Rng,
inTimeout: Duration = 5.minutes,
outTimeout: Duration = 5.minutes,
maxConnections = MaxConnections,
@ -73,15 +78,16 @@ proc newWakuSwitch*(
secureCertPath: string = "",
agentString = none(string), # defaults to nim-libp2p version
peerStoreCapacity = none(int), # defaults to 1.25 maxConnections
rendezvous: RendezVous = nil,
rendezvous: Opt[RendezVousConfig] = Opt.none(RendezVousConfig),
circuitRelay: Relay,
): Switch {.raises: [Defect, IOError, LPError].} =
var b = SwitchBuilder
.new()
.withRng(rng)
.withMaxConnections(maxConnections)
.withMaxIn(maxIn)
.withMaxOut(maxOut)
var b = SwitchBuilder.new().withRng(rng).withMaxConnections(maxConnections)
# libp2p 1.15.3 asserts both maxIn and maxOut > 0; only opt into independent
# in/out caps when the caller actually supplied them. Otherwise the single
# `withMaxConnections` cap from above remains in effect.
if maxIn > 0 and maxOut > 0:
b = b.withMaxInOut(maxIn, maxOut)
b = b
.withMaxConnsPerPeer(maxConnsPerPeer)
.withYamux()
.withMplex(inTimeout, outTimeout)
@ -111,7 +117,7 @@ proc newWakuSwitch*(
else:
b = b.withAddress(address)
if not rendezvous.isNil():
b = b.withRendezVous(rendezvous)
if rendezvous.isSome():
b = b.withRendezVous(rendezvous.get())
b.build()

View File

@ -10,6 +10,7 @@ import
presto/route,
presto/common
import
../../../common/option_shims,
../../../waku_core,
../../../waku_node,
../../../node/peer_manager,

View File

@ -10,6 +10,7 @@ import
presto/common
import
waku/common/option_shims,
waku/node/peer_manager,
waku/waku_lightpush_legacy/common,
../../../waku_node,

View File

@ -10,6 +10,7 @@ import
presto/common
import
waku/common/option_shims,
waku/node/peer_manager,
waku/waku_lightpush/common,
../../../waku_node,

View File

@ -3,6 +3,7 @@
import
std/[strformat, sugar], results, chronicles, uri, json_serialization, presto/route
import
../../../common/option_shims,
../../../waku_core,
../../../waku_store/common,
../../../waku_store/self_req_handler,

View File

@ -3,7 +3,7 @@
import
std/[options, bitops, sequtils, net, tables], results, eth/keys, libp2p/crypto/crypto
import ../common/enr, ../waku_core/codecs
import libp2p/protocols/mix
import libp2p_mix
const CapabilitiesEnrField* = "waku2"

View File

@ -8,7 +8,7 @@ import
eth/keys,
libp2p/[multiaddress, multicodec],
libp2p/crypto/crypto
import ../common/enr, ../waku_core/topics/pubsub_topic
import ../common/enr, ../common/option_shims, ../waku_core/topics/pubsub_topic
logScope:
topics = "waku enr sharding"

View File

@ -3,6 +3,7 @@
import std/options, results, chronicles, chronos, metrics, bearssl/rand, stew/byteutils
import libp2p/peerid, libp2p/stream/connection
import
../common/option_shims,
../waku_core/peers,
../node/peer_manager,
../utils/requests,

View File

@ -5,12 +5,12 @@ import chronicles, std/[options, sequtils], chronos, results, metrics
import
libp2p/crypto/curve25519,
libp2p/crypto/crypto,
libp2p/protocols/mix,
libp2p/protocols/mix/mix_node,
libp2p/protocols/mix/mix_protocol,
libp2p/protocols/mix/mix_metrics,
libp2p/protocols/mix/delay_strategy,
libp2p/protocols/mix/spam_protection,
libp2p_mix,
libp2p_mix/mix_node,
libp2p_mix/mix_protocol,
libp2p_mix/mix_metrics,
libp2p_mix/delay_strategy,
libp2p_mix/spam_protection,
libp2p/[multiaddress, multicodec, peerid, peerinfo],
eth/common/keys
@ -126,8 +126,11 @@ proc new*(
localMixNodeInfo,
peermgr.switch,
spamProtection = Opt.some(SpamProtection(spamProtection)),
delayStrategy =
ExponentialDelayStrategy.new(meanDelayMs = 100, rng = crypto.newRng()),
delayStrategy = Opt.some(
DelayStrategy(
ExponentialDelayStrategy.new(meanDelay = 100, rng = crypto.newRng())
)
),
)
processBootNodes(bootnodes, peermgr, m)
@ -300,8 +303,7 @@ proc dosRegistrationRetryLoop(mix: WakuMix) {.async.} =
try:
let registerRes = await mix.mixRlnSpamProtection.registerSelf()
if registerRes.isOk():
debug "DoS-protection self-registration succeeded",
index = registerRes.get()
debug "DoS-protection self-registration succeeded", index = registerRes.get()
# Persist tree only after a successful register — for fresh nodes this
# captures the new index; for keystore nodes it's a harmless no-op.
let saveRes = mix.mixRlnSpamProtection.saveTree()

View File

@ -13,6 +13,7 @@ import
chronicles,
metrics,
libp2p/multihash,
libp2p/crypto/rng,
libp2p/protocols/pubsub/gossipsub,
libp2p/protocols/pubsub/rpc/messages,
libp2p/stream/connection,
@ -366,6 +367,8 @@ proc new*(
triggerSelf = true,
msgIdProvider = defaultMessageIdProvider,
maxMessageSize = maxMessageSize,
# libp2p 1.15.3 made `rng` a required parameter of PubSub.init.
rng = newRng(),
parameters = GossipsubParameters,
)
w.brokerCtx = globalBrokerContext()

View File

@ -7,8 +7,7 @@ import
chronicles,
libp2p/protocols/rendezvous,
libp2p/crypto/curve25519,
libp2p/switch,
libp2p/utils/semaphore
libp2p/switch
import metrics except collect
@ -107,10 +106,14 @@ proc new*(
switch: switch,
rng: rng,
sema: newAsyncSemaphore(MaxSimultanesousAdvertisements),
minDuration: rendezvous.MinimumAcceptedDuration,
maxDuration: rendezvous.MaximumDuration,
minTTL: rendezvous.MinimumAcceptedDuration.seconds.uint64,
maxTTL: rendezvous.MaximumDuration.seconds.uint64,
# libp2p 1.15.3 moved minDuration/maxDuration/minTTL/maxTTL onto
# GenericRendezVous.config (RendezVousConfig).
config: RendezVousConfig(
minDuration: rendezvous.MinimumAcceptedDuration,
maxDuration: rendezvous.MaximumDuration,
minTTL: rendezvous.MinimumAcceptedDuration.seconds.uint64,
maxTTL: rendezvous.MaximumDuration.seconds.uint64,
),
peers: @[], # Will be populated from selectPeer calls
cookiesSaved: initTable[PeerId, Table[string, seq[byte]]](),
peerRecordValidator: checkWakuPeerRecord,

View File

@ -8,7 +8,7 @@ import
stew/byteutils,
libp2p/protocols/rendezvous,
libp2p/protocols/rendezvous/protobuf,
libp2p/utils/semaphore,
libp2p/crypto/rng,
libp2p/utils/offsettedseq,
libp2p/crypto/curve25519,
libp2p/switch,
@ -51,7 +51,7 @@ proc advertise*(
self: WakuRendezVous,
namespace: string,
peers: seq[PeerId],
ttl: timer.Duration = self.minDuration,
ttl: timer.Duration = self.config.minDuration,
): Future[Result[void, string]] {.async: (raises: []).} =
trace "advertising via waku rendezvous",
namespace = namespace, ttl = ttl, peers = $peers, peerRecord = $self.getPeerRecord()
@ -154,14 +154,20 @@ proc new*(
let rng = newRng()
let wrv = T(
rng: rng,
salt: string.fromBytes(generateBytes(rng[], 8)),
# libp2p 1.15.3: generateBytes now takes the libp2p Rng directly
# (used to take the underlying ref HmacDrbgContext via `rng[]`).
salt: string.fromBytes(generateBytes(rng, 8)),
registered: initOffsettedSeq[RegisteredData](),
expiredDT: Moment.now() - 1.days,
sema: newAsyncSemaphore(SemaphoreDefaultSize),
minDuration: rendezvous.MinimumAcceptedDuration,
maxDuration: rendezvous.MaximumDuration,
minTTL: rendezvous.MinimumAcceptedDuration.seconds.uint64,
maxTTL: rendezvous.MaximumDuration.seconds.uint64,
# libp2p 1.15.3 moved minDuration/maxDuration/minTTL/maxTTL onto
# GenericRendezVous.config (RendezVousConfig).
config: RendezVousConfig(
minDuration: rendezvous.MinimumAcceptedDuration,
maxDuration: rendezvous.MaximumDuration,
minTTL: rendezvous.MinimumAcceptedDuration.seconds.uint64,
maxTTL: rendezvous.MaximumDuration.seconds.uint64,
),
peerRecordValidator: checkWakuPeerRecord,
)

View File

@ -8,7 +8,12 @@ import
metrics,
bearssl/rand
import
../node/peer_manager, ../utils/requests, ./protocol_metrics, ./common, ./rpc_codec
../common/option_shims,
../node/peer_manager,
../utils/requests,
./protocol_metrics,
./common,
./rpc_codec
logScope:
topics = "waku store client"