Skip to content

Commit 5c814aa

Browse files
committed
integrate mix protocol with extended kademlia discovery, libwaku API updates for logos-chat-poc integration
1 parent 6421685 commit 5c814aa

35 files changed

Lines changed: 607 additions & 196 deletions

apps/chat2mix/chat2mix.nim

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ import
2424
stream/connection, # create and close stream read / write connections
2525
multiaddress,
2626
# encode different addressing schemes. For example, /ip4/7.7.7.7/tcp/6543 means it is using IPv4 protocol and TCP
27+
multicodec,
2728
peerinfo,
2829
# manage the information of a peer, such as peer ID and public / private key
2930
peerid, # Implement how peers interact
3031
protobuf/minprotobuf, # message serialisation/deserialisation from and to protobufs
3132
nameresolving/dnsresolver,
3233
protocols/mix/curve25519,
34+
protocols/mix/mix_protocol,
3335
] # define DNS resolution
3436
import
3537
waku/[
@@ -38,6 +40,7 @@ import
3840
waku_lightpush/rpc,
3941
waku_enr,
4042
discovery/waku_dnsdisc,
43+
discovery/waku_ext_kademlia,
4144
waku_node,
4245
node/waku_metrics,
4346
node/peer_manager,
@@ -84,6 +87,24 @@ type
8487

8588
const MinMixNodePoolSize = 4
8689

90+
proc parseKadBootstrapNode(
91+
multiAddrStr: string
92+
): Result[(PeerId, seq[MultiAddress]), string] =
93+
## Parse a multiaddr string that includes /p2p/<peerID> into (PeerId, seq[MultiAddress])
94+
let multiAddr = MultiAddress.init(multiAddrStr).valueOr:
95+
return err("Invalid multiaddress: " & multiAddrStr)
96+
97+
let peerIdPart = multiAddr.getPart(multiCodec("p2p")).valueOr:
98+
return err("Multiaddress must include /p2p/<peerID>: " & multiAddrStr)
99+
100+
let peerIdBytes = peerIdPart.protoArgument().valueOr:
101+
return err("Failed to extract peer ID from multiaddress: " & multiAddrStr)
102+
103+
let peerId = PeerId.init(peerIdBytes).valueOr:
104+
return err("Invalid peer ID in multiaddress: " & multiAddrStr)
105+
106+
ok((peerId, @[multiAddr]))
107+
87108
#####################
88109
## chat2 protobufs ##
89110
#####################
@@ -453,14 +474,39 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
453474
(await node.mountMix(conf.clusterId, mixPrivKey, conf.mixnodes)).isOkOr:
454475
error "failed to mount waku mix protocol: ", error = $error
455476
quit(QuitFailure)
456-
await node.mountRendezvousClient(conf.clusterId)
477+
478+
# Setup extended kademlia discovery if bootstrap nodes are provided
479+
if conf.kadBootstrapNodes.len > 0:
480+
var kadBootstrapPeers: seq[(PeerId, seq[MultiAddress])]
481+
for nodeStr in conf.kadBootstrapNodes:
482+
let parsed = parseKadBootstrapNode(nodeStr).valueOr:
483+
error "Failed to parse kademlia bootstrap node", node = nodeStr, error = error
484+
continue
485+
kadBootstrapPeers.add(parsed)
486+
487+
if kadBootstrapPeers.len > 0:
488+
(
489+
await setupExtendedKademliaDiscovery(
490+
node,
491+
ExtendedKademliaDiscoveryParams(
492+
bootstrapNodes: kadBootstrapPeers,
493+
mixPubKey: some(mixPubKey),
494+
advertiseMix: false,
495+
),
496+
)
497+
).isOkOr:
498+
error "failed to setup kademlia discovery", error = error
499+
quit(QuitFailure)
500+
501+
#await node.mountRendezvousClient(conf.clusterId)
457502

458503
await node.start()
459504

460505
node.peerManager.start()
506+
node.startExtendedKademliaDiscoveryLoop(minMixPeers = MinMixNodePoolSize)
461507

462508
await node.mountLibp2pPing()
463-
await node.mountPeerExchangeClient()
509+
#await node.mountPeerExchangeClient()
464510
let pubsubTopic = conf.getPubsubTopic(node, conf.contentTopic)
465511
echo "pubsub topic is: " & pubsubTopic
466512
let nick = await readNick(transp)
@@ -601,11 +647,6 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
601647
node, pubsubTopic, conf.contentTopic, servicePeerInfo, false
602648
)
603649
echo "waiting for mix nodes to be discovered..."
604-
while true:
605-
if node.getMixNodePoolSize() >= MinMixNodePoolSize:
606-
break
607-
discard await node.fetchPeerExchangePeers()
608-
await sleepAsync(1000)
609650

610651
while node.getMixNodePoolSize() < MinMixNodePoolSize:
611652
info "waiting for mix nodes to be discovered",

apps/chat2mix/config_chat2mix.nim

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,13 +203,13 @@ type
203203
fleet* {.
204204
desc:
205205
"Select the fleet to connect to. This sets the DNS discovery URL to the selected fleet.",
206-
defaultValue: Fleet.test,
206+
defaultValue: Fleet.none,
207207
name: "fleet"
208208
.}: Fleet
209209

210210
contentTopic* {.
211211
desc: "Content topic for chat messages.",
212-
defaultValue: "/toy-chat-mix/2/huilong/proto",
212+
defaultValue: "/toy-chat/2/baixa-chiado/proto",
213213
name: "content-topic"
214214
.}: string
215215

@@ -228,7 +228,14 @@ type
228228
desc: "WebSocket Secure Support.",
229229
defaultValue: false,
230230
name: "websocket-secure-support"
231-
.}: bool ## rln-relay configuration
231+
.}: bool
232+
233+
## Kademlia Discovery config
234+
kadBootstrapNodes* {.
235+
desc:
236+
"Peer multiaddr for kademlia discovery bootstrap node (must include /p2p/<peerID>). Argument may be repeated.",
237+
name: "kad-bootstrap-node"
238+
.}: seq[string]
232239

233240
proc parseCmdArg*(T: type MixNodePubInfo, p: string): T =
234241
let elements = p.split(":")

library/kernel_api/debug_node_api.nim

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import
99
metrics,
1010
ffi
1111
import
12-
waku/factory/waku, waku/node/waku_node, waku/node/health_monitor, library/declare_lib
12+
waku/factory/waku,
13+
waku/node/waku_node,
14+
waku/node/health_monitor,
15+
library/declare_lib,
16+
waku/waku_core/codecs
1317

1418
proc getMultiaddresses(node: WakuNode): seq[string] =
1519
return node.info().listenAddresses
@@ -48,3 +52,19 @@ proc waku_is_online(
4852
ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer
4953
) {.ffi.} =
5054
return ok($ctx.myLib[].healthMonitor.onlineMonitor.amIOnline())
55+
56+
proc waku_get_mixnode_pool_size(
57+
ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer
58+
) {.ffi.} =
59+
## Returns the number of mix nodes in the pool
60+
if ctx.myLib[].node.wakuMix.isNil():
61+
return ok("0")
62+
return ok($ctx.myLib[].node.getMixNodePoolSize())
63+
64+
proc waku_get_lightpush_peers_count(
65+
ctx: ptr FFIContext[Waku], callback: FFICallBack, userData: pointer
66+
) {.ffi.} =
67+
## Returns the count of all peers in peerstore supporting lightpush protocol
68+
let peers =
69+
ctx.myLib[].node.peerManager.switch.peerStore.getPeersByProtocol(WakuLightPushCodec)
70+
return ok($peers.len)

library/kernel_api/protocols/filter_api.nim

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ proc waku_filter_subscribe(
4747
error "fail filter subscribe", error = errorMsg
4848
return err(errorMsg)
4949

50+
let pubsubTopicOpt =
51+
if ($pubsubTopic).len > 0: some(PubsubTopic($pubsubTopic)) else: none(PubsubTopic)
5052
let subFut = ctx.myLib[].node.filterSubscribe(
51-
some(PubsubTopic($pubsubTopic)),
53+
pubsubTopicOpt,
5254
($contentTopics).split(",").mapIt(ContentTopic(it)),
5355
peer,
5456
)

library/kernel_api/protocols/lightpush_api.nim

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,15 @@ proc waku_lightpush_publish(
3939
let errorMsg = "failed to lightpublish message, no suitable remote peers"
4040
error "PUBLISH failed", error = errorMsg
4141
return err(errorMsg)
42+
let topic =
43+
if ($pubsubTopic).len == 0:
44+
none(PubsubTopic)
45+
else:
46+
some(PubsubTopic($pubsubTopic))
4247

43-
let msgHashHex = (
44-
await ctx.myLib[].node.wakuLegacyLightpushClient.publish(
45-
$pubsubTopic, msg, peer = peerOpt.get()
46-
)
47-
).valueOr:
48-
error "PUBLISH failed", error = error
49-
return err($error)
48+
discard (await ctx.myLib[].node.lightpushPublish(topic, msg, peerOpt)).valueOr:
49+
let errorMsg = error.desc.get($error.code.int)
50+
error "PUBLISH failed", error = errorMsg
51+
return err(errorMsg)
5052

51-
return ok(msgHashHex)
53+
return ok("")

library/kernel_api/protocols/relay_api.nim

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,30 +85,47 @@ proc waku_relay_subscribe(
8585
callback: FFICallBack,
8686
userData: pointer,
8787
pubSubTopic: cstring,
88+
contentTopic: cstring,
8889
) {.ffi.} =
89-
echo "Subscribing to topic: " & $pubSubTopic & " ..."
9090
proc onReceivedMessage(ctx: ptr FFIContext[Waku]): WakuRelayHandler =
9191
return proc(pubsubTopic: PubsubTopic, msg: WakuMessage) {.async.} =
9292
callEventCallback(ctx, "onReceivedMessage"):
9393
$JsonMessageEvent.new(pubsubTopic, msg)
9494

9595
var cb = onReceivedMessage(ctx)
9696

97-
ctx.myLib[].node.subscribe(
98-
(kind: SubscriptionKind.PubsubSub, topic: $pubsubTopic),
99-
handler = WakuRelayHandler(cb),
100-
).isOkOr:
97+
# If contentTopic is provided and non-empty, use ContentSub, otherwise use PubsubSub
98+
let subscription =
99+
if contentTopic != nil and len($contentTopic) > 0:
100+
echo "Subscribing to content topic: " & $contentTopic & " ..."
101+
(kind: SubscriptionKind.ContentSub, topic: $contentTopic)
102+
else:
103+
echo "Subscribing to pubsub topic: " & $pubSubTopic & " ..."
104+
(kind: SubscriptionKind.PubsubSub, topic: $pubsubTopic)
105+
106+
ctx.myLib[].node.subscribe(subscription, handler = WakuRelayHandler(cb)).isOkOr:
101107
error "SUBSCRIBE failed", error = error
102108
return err($error)
103109
return ok("")
104110

111+
# NOTE: When unsubscribing via contentTopic, this will unsubscribe from the entire
112+
# underlying pubsub topic/shard that the content topic maps to. This affects ALL
113+
# content topics on the same shard, not just the specified content topic.
105114
proc waku_relay_unsubscribe(
106115
ctx: ptr FFIContext[Waku],
107116
callback: FFICallBack,
108117
userData: pointer,
109118
pubSubTopic: cstring,
119+
contentTopic: cstring,
110120
) {.ffi.} =
111-
ctx.myLib[].node.unsubscribe((kind: SubscriptionKind.PubsubSub, topic: $pubsubTopic)).isOkOr:
121+
# If contentTopic is provided and non-empty, use ContentUnsub, otherwise use PubsubUnsub
122+
let subscription =
123+
if contentTopic != nil and len($contentTopic) > 0:
124+
(kind: SubscriptionKind.ContentUnsub, topic: $contentTopic)
125+
else:
126+
(kind: SubscriptionKind.PubsubUnsub, topic: $pubsubTopic)
127+
128+
ctx.myLib[].node.unsubscribe(subscription).isOkOr:
112129
error "UNSUBSCRIBE failed", error = error
113130
return err($error)
114131

library/kernel_api/protocols/store_api.nim

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@ import
88
waku/waku_store/common,
99
waku/waku_store/client,
1010
waku/common/paging,
11+
waku/common/base64,
1112
library/declare_lib
1213

14+
# Custom JSON serialization for seq[byte] to avoid ambiguity
15+
proc `%`*(data: seq[byte]): JsonNode =
16+
%base64.encode(data)
17+
1318
func fromJsonNode(jsonContent: JsonNode): Result[StoreQueryRequest, string] =
1419
var contentTopics: seq[string]
1520
if jsonContent.contains("contentTopics"):
@@ -90,5 +95,6 @@ proc waku_store_query(
9095
).valueOr:
9196
return err("StoreRequest failed store query: " & $error)
9297

93-
let res = $(%*(queryResponse.toHex()))
98+
let hexResponse = queryResponse.toHex()
99+
let res = $(%*hexResponse)
94100
return ok(res) ## returning the response in json format

library/libwaku.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ extern "C"
8585
int waku_relay_subscribe(void *ctx,
8686
FFICallBack callback,
8787
void *userData,
88-
const char *pubSubTopic);
88+
const char *pubSubTopic,
89+
const char *contentTopic);
8990

9091
int waku_relay_add_protected_shard(void *ctx,
9192
FFICallBack callback,
@@ -97,7 +98,8 @@ extern "C"
9798
int waku_relay_unsubscribe(void *ctx,
9899
FFICallBack callback,
99100
void *userData,
100-
const char *pubSubTopic);
101+
const char *pubSubTopic,
102+
const char *contentTopic);
101103

102104
int waku_filter_subscribe(void *ctx,
103105
FFICallBack callback,
@@ -247,6 +249,14 @@ extern "C"
247249
FFICallBack callback,
248250
void *userData);
249251

252+
int waku_get_mixnode_pool_size(void *ctx,
253+
FFICallBack callback,
254+
void *userData);
255+
256+
int waku_get_lightpush_peers_count(void *ctx,
257+
FFICallBack callback,
258+
void *userData);
259+
250260
#ifdef __cplusplus
251261
}
252262
#endif

nix/default.nix

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,6 @@ in stdenv.mkDerivation {
4545
pkgs.darwin.cctools gcc # Necessary for libbacktrace
4646
];
4747

48-
# Environment variables required for Android builds
49-
ANDROID_SDK_ROOT="${pkgs.androidPkgs.sdk}";
50-
ANDROID_NDK_HOME="${pkgs.androidPkgs.ndk}";
5148
NIMFLAGS = "-d:disableMarchNative -d:git_revision_override=${revision}";
5249
XDG_CACHE_HOME = "/tmp";
5350

@@ -61,6 +58,15 @@ in stdenv.mkDerivation {
6158

6259
configurePhase = ''
6360
patchShebangs . vendor/nimbus-build-system > /dev/null
61+
62+
# build_nim.sh guards "rm -rf dist/checksums" with NIX_BUILD_TOP != "/build",
63+
# but on macOS the nix sandbox uses /private/tmp/... so the check fails and
64+
# dist/checksums (provided via preBuild) gets deleted. Fix the check to skip
65+
# the removal whenever NIX_BUILD_TOP is set (i.e. any nix build).
66+
substituteInPlace vendor/nimbus-build-system/scripts/build_nim.sh \
67+
--replace 'if [[ "''${NIX_BUILD_TOP}" != "/build" ]]; then' \
68+
'if [[ -z "''${NIX_BUILD_TOP}" ]]; then'
69+
6470
make nimbus-build-system-paths
6571
make nimbus-build-system-nimble-dir
6672
'';
@@ -98,6 +104,9 @@ in stdenv.mkDerivation {
98104
cp library/libwaku.h $out/include/
99105
'';
100106

107+
ANDROID_SDK_ROOT = "${pkgs.androidPkgs.sdk}";
108+
ANDROID_NDK_HOME = "${pkgs.androidPkgs.ndk}";
109+
101110
meta = with pkgs.lib; {
102111
description = "NWaku derivation to build libwaku for mobile targets using Android NDK and Rust.";
103112
homepage = "https://github.com/status-im/nwaku";

simulations/mixnet/config.toml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
log-level = "INFO"
1+
log-level = "TRACE"
22
relay = true
33
mix = true
44
filter = true
55
store = false
66
lightpush = true
77
max-connections = 150
8-
peer-exchange = true
8+
peer-exchange = false
99
metrics-logging = false
1010
cluster-id = 2
11-
discv5-discovery = true
11+
discv5-discovery = false
1212
discv5-udp-port = 9000
1313
discv5-enr-auto-update = true
14+
enable-kad-discovery = true
1415
rest = true
1516
rest-admin = true
1617
ports-shift = 1
@@ -19,7 +20,9 @@ shard = [0]
1920
agent-string = "nwaku-mix"
2021
nodekey = "f98e3fba96c32e8d1967d460f1b79457380e1a895f7971cecc8528abe733781a"
2122
mixkey = "a87db88246ec0eedda347b9b643864bee3d6933eb15ba41e6d58cb678d813258"
22-
rendezvous = true
23+
rendezvous = false
2324
listen-address = "127.0.0.1"
2425
nat = "extip:127.0.0.1"
26+
ext-multiaddr = ["/ip4/127.0.0.1/tcp/60001"]
27+
ext-multiaddr-only = true
2528
ip-colocation-limit=0

0 commit comments

Comments
 (0)