From 6750b2340f8a0a25c73e1e6ea236ad4c7e23f508 Mon Sep 17 00:00:00 2001 From: Marcin Czenko Date: Mon, 4 May 2026 01:30:25 +0200 Subject: [PATCH] initial commit --- .gitignore | 8 ++++ README.md | 47 +++++++++++++++++++ config.nims | 6 +++ libp2p_mix_example.nimble | 11 +++++ mix_ping.nim | 96 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 168 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 config.nims create mode 100644 libp2p_mix_example.nimble create mode 100644 mix_ping.nim diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e548e2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +mix_ping +mix_ping_quic +nimcache/ +nimbledeps/ +.agents/ +.codex +nimble.paths +nimble.develop diff --git a/README.md b/README.md new file mode 100644 index 0000000..a266e50 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# libp2p Mix Example + +Standalone Nim example for running a libp2p Mix protocol ping simulation. + +## Quick Start + +Prerequisites: + +- Nim 2.0 or newer +- Nimble +- Git + +From a fresh clone: + +```bash +nimble setup -l +nim c -r mix_ping.nim +``` + +`nimble setup -l` enables project-local dependency mode, installs dependencies +under `nimbledeps/`, and generates `nimble.paths` and `nimble.develop`. + +## Local Files + +The repository uses `config.nims` to keep Nim build output in the local +`nimcache/` directory and to include `nimble.paths` when it exists. + +These files and directories are local artifacts and should not be committed: + +- `nimbledeps/` +- `nimble.paths` +- `nimble.develop` +- `nimcache/` +- `mix_ping` + +## Clean Rebuild + +To verify the project can be rebuilt from committed files: + +```bash +rm -rf nimbledeps nimble.paths nimble.develop nimcache mix_ping +nimble setup -l +nim c -r mix_ping.nim +``` + +If `nimble setup -l` reports that it cannot determine the VCS revision, make at +least one Git commit first, then rerun the command. diff --git a/config.nims b/config.nims new file mode 100644 index 0000000..f7d9a1f --- /dev/null +++ b/config.nims @@ -0,0 +1,6 @@ +switch("nimcache", "nimcache") + +# begin Nimble config (version 2) +when withDir(thisDir(), system.fileExists("nimble.paths")): + include "nimble.paths" +# end Nimble config diff --git a/libp2p_mix_example.nimble b/libp2p_mix_example.nimble new file mode 100644 index 0000000..276276c --- /dev/null +++ b/libp2p_mix_example.nimble @@ -0,0 +1,11 @@ +version = "0.1.0" +author = "local" +description = "Standalone libp2p mix protocol example" +license = "MIT" +srcDir = "." + +requires "nim >= 2.0.0" +requires "libp2p >= 1.15.3" +requires "chronicles >= 0.11.0" +requires "chronos >= 4.2.2" +requires "results >= 0.5.0" diff --git a/mix_ping.nim b/mix_ping.nim new file mode 100644 index 0000000..bd27ce5 --- /dev/null +++ b/mix_ping.nim @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: Apache-2.0 OR MIT +# Copyright (c) Status Research & Development GmbH + +## Mix Protocol Ping Example +## +## This example demonstrates using the Mix protocol with the Ping protocol. +## It creates a set of mix nodes that form an anonymous overlay network, +## then sends a ping through the mix network to a destination node and +## receives the response via Single Use Reply Blocks (SURBs). + +{.used.} + +import chronicles, chronos, results +import std/[strformat, sequtils] +import libp2p/[ + protocols/mix, + protocols/mix/mix_protocol, + protocols/mix/curve25519, + protocols/ping, + peerid, + multiaddress, + switch, + builders, + crypto/crypto, + crypto/secp, + ] + +const NumMixNodes = 10 + +proc createSwitch( + multiAddr: MultiAddress, libp2pPrivKey: Opt[SkPrivateKey] = Opt.none(SkPrivateKey) +): Switch = + var rng = newRng() + let skkey = libp2pPrivKey.valueOr(SkKeyPair.random(rng[]).seckey) + let privKey = PrivateKey(scheme: Secp256k1, skkey: skkey) + newStandardSwitchBuilder(privKey = Opt.some(privKey), addrs = multiAddr).build() + +proc mixPingSimulation() {.async: (raises: [Exception]).} = + let mixNodeInfos = MixNodeInfo.generateRandomMany(NumMixNodes) + var switches: seq[Switch] = @[] + var mixProtos: seq[MixProtocol] = @[] + + # Set up mix protocols on each mix node + for nodeInfo in mixNodeInfos: + var switch = createSwitch(nodeInfo.multiAddr, Opt.some(nodeInfo.libp2pPrivKey)) + let proto = MixProtocol.new(nodeInfo, switch) + + # Populate nodePool with all other nodes' public info + proto.nodePool.add(mixNodeInfos.includeAllExcept(nodeInfo)) + + # Register how to read ping responses (32 bytes exactly) + proto.registerDestReadBehavior(PingCodec, readExactly(32)) + switch.mount(proto) + + switches.add(switch) + mixProtos.add(proto) + + defer: + await switches.mapIt(it.stop()).allFutures() + + # Create a destination node (not part of the mix network) + let destNode = createSwitch(MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()) + defer: + await destNode.stop() + + let pingProto = Ping.new() + destNode.mount(pingProto) + + # Start all switches + await switches.mapIt(it.start()).allFutures() + await destNode.start() + + # Pick sender (first mix node) and send ping through the mix network + let senderIndex = 0 + + info "Sending ping through mix network", + sender = switches[senderIndex].peerInfo.peerId, + destination = destNode.peerInfo.peerId + + # Create a connection through the mix network + let conn = mixProtos[senderIndex] + .toConnection( + MixDestination.init(destNode.peerInfo.peerId, destNode.peerInfo.addrs[0]), + PingCodec, + MixParameters(expectReply: Opt.some(true), numSurbs: Opt.some(byte(1))), + ) + .expect("could not build connection") + + # Send ping and wait for response through the mix network + let response = await pingProto.ping(conn) + await conn.close() + + info "Ping response received through mix network", rtt = response + +when isMainModule: + waitFor(mixPingSimulation()) \ No newline at end of file