resolving conflicts

This commit is contained in:
stubbsta 2025-04-11 10:11:20 +02:00
commit f41aab55d9
28 changed files with 660 additions and 139 deletions

View File

@ -76,14 +76,14 @@ jobs:
tar -cvzf ${{steps.vars.outputs.nwakutools}} ./build/wakucanary ./build/networkmonitor
- name: upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: wakunode2
path: ${{steps.vars.outputs.nwaku}}
retention-days: 2
- name: upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: wakutools
path: ${{steps.vars.outputs.nwakutools}}

12
.gitmodules vendored
View File

@ -168,7 +168,7 @@
path = vendor/db_connector
url = https://github.com/nim-lang/db_connector.git
ignore = untracked
branch = master
branch = devel
[submodule "vendor/nph"]
ignore = untracked
branch = master
@ -179,16 +179,6 @@
url = https://github.com/status-im/nim-minilru.git
ignore = untracked
branch = master
[submodule "vendor/nim-quic"]
path = vendor/nim-quic
url = https://github.com/status-im/nim-quic.git
ignore = untracked
branch = master
[submodule "vendor/nim-ngtcp2"]
path = vendor/nim-ngtcp2
url = https://github.com/vacp2p/nim-ngtcp2.git
ignore = untracked
branch = master
[submodule "vendor/waku-rlnv2-contract"]
path = vendor/waku-rlnv2-contract
url = https://github.com/waku-org/waku-rlnv2-contract.git

View File

@ -4,8 +4,8 @@
# - MIT license
# at your option. This file may not be copied, modified, or distributed except
# according to those terms.
BUILD_SYSTEM_DIR := vendor/nimbus-build-system
EXCLUDED_NIM_PACKAGES := vendor/nim-dnsdisc/vendor
export BUILD_SYSTEM_DIR := vendor/nimbus-build-system
export EXCLUDED_NIM_PACKAGES := vendor/nim-dnsdisc/vendor
LINK_PCRE := 0
FORMAT_MSG := "\\x1B[95mFormatting:\\x1B[39m"
# we don't want an error here, so we can handle things later, in the ".DEFAULT" target
@ -152,6 +152,12 @@ endif
clean: | clean-libbacktrace
### Create nimble links (used when building with Nix)
nimbus-build-system-nimble-dir:
NIMBLE_DIR="$(CURDIR)/$(NIMBLE_DIR)" \
PWD_CMD="$(PWD)" \
$(CURDIR)/scripts/generate_nimble_links.sh
##################
## RLN ##

View File

@ -21,6 +21,13 @@ The standard developer tools, including a C compiler, GNU Make, Bash, and Git. M
> In some distributions (Fedora linux for example), you may need to install `which` utility separately. Nimbus build system is relying on it.
You'll also need an installation of Rust and its toolchain (specifically `rustc` and `cargo`).
The easiest way to install these, is using `rustup`:
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
### Wakunode
```bash

49
flake.lock generated Normal file
View File

@ -0,0 +1,49 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1740603184,
"narHash": "sha256-t+VaahjQAWyA+Ctn2idyo1yxRIYpaDxMgHkgCNiMJa4=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "f44bd8ca21e026135061a0a57dcf3d0775b67a49",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "f44bd8ca21e026135061a0a57dcf3d0775b67a49",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"zerokit": "zerokit"
}
},
"zerokit": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1743756626,
"narHash": "sha256-SvhfEl0bJcRsCd79jYvZbxQecGV2aT+TXjJ57WVv7Aw=",
"owner": "vacp2p",
"repo": "zerokit",
"rev": "c60e0c33fc6350a4b1c20e6b6727c44317129582",
"type": "github"
},
"original": {
"owner": "vacp2p",
"repo": "zerokit",
"rev": "c60e0c33fc6350a4b1c20e6b6727c44317129582",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

63
flake.nix Normal file
View File

@ -0,0 +1,63 @@
{
description = "NWaku build flake";
nixConfig = {
extra-substituters = [ "https://nix-cache.status.im/" ];
extra-trusted-public-keys = [ "nix-cache.status.im-1:x/93lOfLU+duPplwMSBR+OlY4+mo+dCN7n0mr4oPwgY=" ];
};
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs?rev=f44bd8ca21e026135061a0a57dcf3d0775b67a49";
zerokit = {
url = "github:vacp2p/zerokit?rev=c60e0c33fc6350a4b1c20e6b6727c44317129582";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, zerokit }:
let
stableSystems = [
"x86_64-linux" "aarch64-linux"
"x86_64-darwin" "aarch64-darwin"
"x86_64-windows" "i686-linux"
"i686-windows"
];
forAllSystems = f: nixpkgs.lib.genAttrs stableSystems (system: f system);
pkgsFor = forAllSystems (
system: import nixpkgs {
inherit system;
config = {
android_sdk.accept_license = true;
allowUnfree = true;
};
overlays = [
(final: prev: {
androidEnvCustom = prev.callPackage ./nix/pkgs/android-sdk { };
androidPkgs = final.androidEnvCustom.pkgs;
androidShell = final.androidEnvCustom.shell;
})
];
}
);
in rec {
packages = forAllSystems (system: let
pkgs = pkgsFor.${system};
in rec {
libwaku-android-arm64 = pkgs.callPackage ./nix/default.nix {
inherit stableSystems;
src = self;
targets = ["libwaku-android-arm64"];
androidArch = "aarch64-linux-android";
zerokitPkg = zerokit.packages.${system}.zerokit-android-arm64;
};
default = libwaku-android-arm64;
});
devShells = forAllSystems (system: {
default = pkgsFor.${system}.callPackage ./nix/shell.nix {};
});
};
}

View File

@ -52,10 +52,6 @@ template callEventCallback(ctx: ptr WakuContext, eventName: string, body: untype
error eventName & " - eventCallback is nil"
return
if isNil(ctx[].eventUserData):
error eventName & " - eventUserData is nil"
return
foreignThreadGc:
try:
let event = body

35
nix/README.md Normal file
View File

@ -0,0 +1,35 @@
# Usage
## Shell
A development shell can be started using:
```sh
nix develop
```
## Building
To build a Codex you can use:
```sh
nix build '.?submodules=1#default'
```
The `?submodules=1` part should eventually not be necessary.
For more details see:
https://github.com/NixOS/nix/issues/4423
It can be also done without even cloning the repo:
```sh
nix build 'git+https://github.com/waku-org/nwaku?submodules=1#'
```
## Running
```sh
nix run 'git+https://github.com/waku-org/nwaku?submodules=1#''
```
## Testing
```sh
nix flake check ".?submodules=1#"
```

12
nix/atlas.nix Normal file
View File

@ -0,0 +1,12 @@
{ pkgs ? import <nixpkgs> { } }:
let
tools = pkgs.callPackage ./tools.nix {};
sourceFile = ../vendor/nimbus-build-system/vendor/Nim/koch.nim;
in pkgs.fetchFromGitHub {
owner = "nim-lang";
repo = "atlas";
rev = tools.findKeyValue "^ +AtlasStableCommit = \"([a-f0-9]+)\"$" sourceFile;
# WARNING: Requires manual updates when Nim compiler version changes.
hash = "sha256-G1TZdgbRPSgxXZ3VsBP2+XFCLHXVb3an65MuQx67o/k=";
}

12
nix/checksums.nix Normal file
View File

@ -0,0 +1,12 @@
{ pkgs ? import <nixpkgs> { } }:
let
tools = pkgs.callPackage ./tools.nix {};
sourceFile = ../vendor/nimbus-build-system/vendor/Nim/koch.nim;
in pkgs.fetchFromGitHub {
owner = "nim-lang";
repo = "checksums";
rev = tools.findKeyValue "^ +ChecksumsStableCommit = \"([a-f0-9]+)\"$" sourceFile;
# WARNING: Requires manual updates when Nim compiler version changes.
hash = "sha256-Bm5iJoT2kAvcTexiLMFBa9oU5gf7d4rWjo3OiN7obWQ=";
}

12
nix/csources.nix Normal file
View File

@ -0,0 +1,12 @@
{ pkgs ? import <nixpkgs> { } }:
let
tools = pkgs.callPackage ./tools.nix {};
sourceFile = ../vendor/nimbus-build-system/vendor/Nim/config/build_config.txt;
in pkgs.fetchFromGitHub {
owner = "nim-lang";
repo = "csources_v2";
rev = tools.findKeyValue "^nim_csourcesHash=([a-f0-9]+)$" sourceFile;
# WARNING: Requires manual updates when Nim compiler version changes.
hash = "sha256-UCLtoxOcGYjBdvHx7A47x6FjLMi6VZqpSs65MN7fpBs=";
}

111
nix/default.nix Normal file
View File

@ -0,0 +1,111 @@
{
config ? {},
pkgs ? import <nixpkgs> { },
src ? ../.,
targets ? ["libwaku-android-arm64"],
verbosity ? 2,
useSystemNim ? true,
quickAndDirty ? true,
stableSystems ? [
"x86_64-linux" "aarch64-linux"
],
androidArch,
zerokitPkg,
}:
assert pkgs.lib.assertMsg ((src.submodules or true) == true)
"Unable to build without submodules. Append '?submodules=1#' to the URI.";
let
inherit (pkgs) stdenv lib writeScriptBin callPackage;
revision = lib.substring 0 8 (src.rev or "dirty");
in stdenv.mkDerivation rec {
pname = "nwaku";
version = "1.0.0-${revision}";
inherit src;
buildInputs = with pkgs; [
openssl
gmp
];
# Dependencies that should only exist in the build environment.
nativeBuildInputs = let
# Fix for Nim compiler calling 'git rev-parse' and 'lsb_release'.
fakeGit = writeScriptBin "git" "echo ${version}";
# Fix for the zerokit package that is built with cargo/rustup/cross.
fakeCargo = writeScriptBin "cargo" "echo ${version}";
# Fix for the zerokit package that is built with cargo/rustup/cross.
fakeRustup = writeScriptBin "rustup" "echo ${version}";
# Fix for the zerokit package that is built with cargo/rustup/cross.
fakeCross = writeScriptBin "cross" "echo ${version}";
in
with pkgs; [
cmake
which
lsb-release
zerokitPkg
nim-unwrapped-2_0
fakeGit
fakeCargo
fakeRustup
fakeCross
];
# Environment variables required for Android builds
ANDROID_SDK_ROOT="${pkgs.androidPkgs.sdk}";
ANDROID_NDK_HOME="${pkgs.androidPkgs.ndk}";
NIMFLAGS = "-d:disableMarchNative -d:git_revision_override=${revision}";
XDG_CACHE_HOME = "/tmp";
makeFlags = targets ++ [
"V=${toString verbosity}"
"QUICK_AND_DIRTY_COMPILER=${if quickAndDirty then "1" else "0"}"
"QUICK_AND_DIRTY_NIMBLE=${if quickAndDirty then "1" else "0"}"
"USE_SYSTEM_NIM=${if useSystemNim then "1" else "0"}"
];
configurePhase = ''
patchShebangs . vendor/nimbus-build-system > /dev/null
make nimbus-build-system-paths
make nimbus-build-system-nimble-dir
'';
preBuild = ''
ln -s waku.nimble waku.nims
pushd vendor/nimbus-build-system/vendor/Nim
mkdir dist
cp -r ${callPackage ./nimble.nix {}} dist/nimble
chmod 777 -R dist/nimble
mkdir -p dist/nimble/dist
cp -r ${callPackage ./checksums.nix {}} dist/checksums # need both
cp -r ${callPackage ./checksums.nix {}} dist/nimble/dist/checksums
cp -r ${callPackage ./atlas.nix {}} dist/atlas
chmod 777 -R dist/atlas
mkdir dist/atlas/dist
cp -r ${callPackage ./sat.nix {}} dist/nimble/dist/sat
cp -r ${callPackage ./sat.nix {}} dist/atlas/dist/sat
cp -r ${callPackage ./csources.nix {}} csources_v2
chmod 777 -R dist/nimble csources_v2
popd
mkdir -p vendor/zerokit/target/${androidArch}/release
cp ${zerokitPkg}/librln.so vendor/zerokit/target/${androidArch}/release/
'';
installPhase = ''
mkdir -p $out/build/android
cp -r ./build/android/* $out/build/android/
'';
meta = with pkgs.lib; {
description = "NWaku derivation to build libwaku for mobile targets using Android NDK and Rust.";
homepage = "https://github.com/status-im/nwaku";
license = licenses.mit;
platforms = stableSystems;
};
}

12
nix/nimble.nix Normal file
View File

@ -0,0 +1,12 @@
{ pkgs ? import <nixpkgs> { } }:
let
tools = pkgs.callPackage ./tools.nix {};
sourceFile = ../vendor/nimbus-build-system/vendor/Nim/koch.nim;
in pkgs.fetchFromGitHub {
owner = "nim-lang";
repo = "nimble";
rev = tools.findKeyValue "^ +NimbleStableCommit = \"([a-f0-9]+)\".+" sourceFile;
# WARNING: Requires manual updates when Nim compiler version changes.
hash = "sha256-MVHf19UbOWk8Zba2scj06PxdYYOJA6OXrVyDQ9Ku6Us=";
}

View File

@ -0,0 +1,26 @@
#
# This Nix expression centralizes the configuration
# for the Android development environment.
#
{ androidenv, lib, stdenv }:
assert lib.assertMsg (stdenv.system != "aarch64-darwin")
"aarch64-darwin not supported for Android SDK. Use: NIXPKGS_SYSTEM_OVERRIDE=x86_64-darwin";
# The "android-sdk-license" license is accepted
# by setting android_sdk.accept_license = true.
androidenv.composeAndroidPackages {
cmdLineToolsVersion = "9.0";
toolsVersion = "26.1.1";
platformToolsVersion = "33.0.3";
buildToolsVersions = [ "34.0.0" ];
platformVersions = [ "34" ];
cmakeVersions = [ "3.22.1" ];
ndkVersion = "25.2.9519653";
includeNDK = true;
includeExtras = [
"extras;android;m2repository"
"extras;google;m2repository"
];
}

View File

@ -0,0 +1,14 @@
#
# This Nix expression centralizes the configuration
# for the Android development environment.
#
{ callPackage }:
let
compose = callPackage ./compose.nix { };
pkgs = callPackage ./pkgs.nix { inherit compose; };
shell = callPackage ./shell.nix { androidPkgs = pkgs; };
in {
inherit compose pkgs shell;
}

View File

@ -0,0 +1,17 @@
{ stdenv, compose }:
#
# This derivation simply symlinks some stuff to get
# shorter paths as libexec/android-sdk is quite the mouthful.
# With this you can just do `androidPkgs.sdk` and `androidPkgs.ndk`.
#
stdenv.mkDerivation {
name = "${compose.androidsdk.name}-mod";
phases = [ "symlinkPhase" ];
outputs = [ "out" "sdk" "ndk" ];
symlinkPhase = ''
ln -s ${compose.androidsdk} $out
ln -s ${compose.androidsdk}/libexec/android-sdk $sdk
ln -s ${compose.androidsdk}/libexec/android-sdk/ndk-bundle $ndk
'';
}

View File

@ -0,0 +1,19 @@
{ mkShell, openjdk, androidPkgs }:
mkShell {
name = "android-sdk-shell";
buildInputs = [ openjdk ];
shellHook = ''
export ANDROID_HOME="${androidPkgs.sdk}"
export ANDROID_NDK_ROOT="${androidPkgs.ndk}"
export ANDROID_SDK_ROOT="$ANDROID_HOME"
export ANDROID_NDK_HOME="${androidPkgs.ndk}"
export PATH="$ANDROID_NDK_ROOT:$PATH"
export PATH="$ANDROID_SDK_ROOT/tools:$PATH"
export PATH="$ANDROID_SDK_ROOT/tools/bin:$PATH"
export PATH="$(echo $ANDROID_SDK_ROOT/cmdline-tools/*/bin):$PATH"
export PATH="$ANDROID_SDK_ROOT/platform-tools:$PATH"
'';
}

12
nix/sat.nix Normal file
View File

@ -0,0 +1,12 @@
{ pkgs ? import <nixpkgs> { } }:
let
tools = pkgs.callPackage ./tools.nix {};
sourceFile = ../vendor/nimbus-build-system/vendor/Nim/koch.nim;
in pkgs.fetchFromGitHub {
owner = "nim-lang";
repo = "sat";
rev = tools.findKeyValue "^ +SatStableCommit = \"([a-f0-9]+)\"$" sourceFile;
# WARNING: Requires manual updates when Nim compiler version changes.
hash = "sha256-JFrrSV+mehG0gP7NiQ8hYthL0cjh44HNbXfuxQNhq7c=";
}

26
nix/shell.nix Normal file
View File

@ -0,0 +1,26 @@
{
pkgs ? import <nixpkgs> { },
}:
let
optionalDarwinDeps = pkgs.lib.optionals pkgs.stdenv.isDarwin [
pkgs.libiconv
pkgs.darwin.apple_sdk.frameworks.Security
];
in
pkgs.mkShell {
inputsFrom = [
pkgs.androidShell
] ++ optionalDarwinDeps;
buildInputs = with pkgs; [
git
cargo
rustup
cmake
nim-unwrapped-2_0
];
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [
pkgs.pcre
];
}

15
nix/tools.nix Normal file
View File

@ -0,0 +1,15 @@
{ pkgs ? import <nixpkgs> { } }:
let
inherit (pkgs.lib) fileContents last splitString flatten remove;
inherit (builtins) map match;
in {
findKeyValue = regex: sourceFile:
let
linesFrom = file: splitString "\n" (fileContents file);
matching = regex: lines: map (line: match regex line) lines;
extractMatch = matches: last (flatten (remove null matches));
in
extractMatch (matching regex (linesFrom sourceFile));
}

View File

@ -0,0 +1,29 @@
#!/usr/bin/env bash
# This script is used for building Nix derivation which doesn't allow Git commands.
# It implements similar logic as $(NIMBLE_DIR) target in nimbus-build-system Makefile.
create_nimble_link_script_path="$(pwd)/${BUILD_SYSTEM_DIR}/scripts/create_nimble_link.sh"
process_gitmodules() {
local gitmodules_file="$1"
local gitmodules_dir=$(dirname "$gitmodules_file")
# Extract all submodule paths from the .gitmodules file
grep "path" $gitmodules_file | awk '{print $3}' | while read submodule_path; do
# Change pwd to the submodule dir and execute script
pushd "$gitmodules_dir/$submodule_path" > /dev/null
NIMBLE_DIR=$NIMBLE_DIR PWD_CMD=$PWD_CMD EXCLUDED_NIM_PACKAGES=$EXCLUDED_NIM_PACKAGES \
"$create_nimble_link_script_path" "$submodule_path"
popd > /dev/null
done
}
# Create the base directory if it doesn't exist
mkdir -p "${NIMBLE_DIR}/pkgs"
# Find all .gitmodules files and process them
for gitmodules_file in $(find . -name '.gitmodules'); do
echo "Processing .gitmodules file: $gitmodules_file"
process_gitmodules "$gitmodules_file"
done

View File

@ -1,22 +0,0 @@
{ pkgs ? import (builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/dbf1d73cd1a17276196afeee169b4cf7834b7a96.tar.gz";
sha256 = "sha256:1k5nvn2yzw370cqsfh62lncsgydq2qkbjrx34cprzf0k6b93v7ch";
}) {} }:
pkgs.mkShell {
name = "nim-waku-build-shell";
# Versions dependent on nixpkgs commit. Update manually.
buildInputs = with pkgs; [
git # 2.37.3
which # 2.21
rustc # 1.63.0
] ++ lib.optionals stdenv.isDarwin [
libiconv
darwin.apple_sdk.frameworks.Security
];
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [
pkgs.pcre
];
}

View File

@ -14,7 +14,7 @@ import
import
waku/[waku_core/topics, waku_enr, discovery/waku_discv5, waku_enr/capabilities],
../testlib/[wakucore, testasync, assertions, futures, wakunode],
../testlib/[wakucore, testasync, assertions, futures, wakunode, testutils],
../waku_enr/utils,
./utils as discv5_utils
@ -300,7 +300,9 @@ suite "Waku Discovery v5":
# Cleanup
await allFutures(node1.stop(), node2.stop(), node3.stop(), node4.stop())
asyncTest "find random peers with instance predicate":
xasyncTest "find random peers with instance predicate":
## This is skipped because is flaky and made CI randomly fail but is useful to run manually
## Setup
# Records
let

View File

@ -487,6 +487,10 @@ procSuite "WakuNode - RLN relay":
await node3.stop()
xasyncTest "clearNullifierLog: should clear epochs > MaxEpochGap":
<<<<<<< HEAD
=======
## This is skipped because is flaky and made CI randomly fail but is useful to run manually
>>>>>>> deprecate_sync_strategy
# Given two nodes
let
contentTopic = ContentTopic("/waku/2/default-content/proto")

View File

@ -116,15 +116,16 @@ proc serialize*(memIndices: seq[MembershipIndex]): seq[byte] =
return memIndicesBytes
proc serialize*(witness: Witness): seq[byte] =
proc serialize*(witness: RLNWitnessInput): seq[byte] =
## Serializes the witness into a byte array according to the RLN protocol format
var buffer: seq[byte]
# Convert Fr types to bytes and add them to buffer
buffer.add(@(witness.identity_secret))
buffer.add(@(witness.user_message_limit))
buffer.add(@(witness.message_id))
buffer.add(toBytes(uint64(witness.path_elements.len), Endianness.littleEndian))
for element in witness.path_elements:
buffer.add(@element)
buffer.add(toBytes(uint64(witness.path_elements.len), Endianness.littleEndian))
buffer.add(witness.identity_path_index)
buffer.add(@(witness.x))
buffer.add(@(witness.external_nullifier))

View File

@ -93,6 +93,44 @@ proc setMetadata*(
return err("failed to persist rln metadata: " & getCurrentExceptionMsg())
return ok()
proc toArray32LE*(x: UInt256): array[32, byte] {.inline.} =
## Convert UInt256 to byte array without endianness conversion
when nimvm:
for i in 0 ..< 32:
result[i] = byte((x shr (i * 8)).truncate(uint8) and 0xff)
else:
copyMem(addr result, unsafeAddr x, 32)
# Hashes arbitrary signal to the underlying prime field.
proc hash_to_field*(signal: seq[byte]): array[32, byte] =
var ctx: keccak256
ctx.init()
ctx.update(signal)
var hash = ctx.finish()
var result: array[32, byte]
copyMem(result[0].addr, hash.data[0].addr, 32)
return result
proc toArray32LE*(x: array[32, byte]): array[32, byte] =
for i in 0 ..< 32:
result[i] = x[31 - i]
return result
proc toArray32LE*(s: seq[byte]): array[32, byte] =
var output: array[32, byte]
for i in 0 ..< 32:
output[i] = 0
for i in 0 ..< 32:
output[i] = s[31 - i]
return output
proc toArray32LE*(v: uint64): array[32, byte] =
let bytes = toBytes(v, Endianness.littleEndian)
var output: array[32, byte]
discard output.copyFrom(bytes)
return output
proc fetchMerkleProofElements*(
g: OnchainGroupManager
): Future[Result[seq[array[32, byte]], string]] {.async.} =
@ -120,22 +158,32 @@ proc fetchMerkleProofElements*(
let responseBytes = await g.ethRpc.get().provider.eth_call(tx, "latest")
var merkleProof = newSeqOfCap[array[32, byte]](20)
for i in 0 ..< 20:
let startIndex = 32 + (i * 32) # Skip first 32 bytes (ABI encoding offset)
if startIndex + 32 <= responseBytes.len:
var element: array[32, byte]
for j in 0 ..< 32:
if startIndex + j < responseBytes.len:
element[j] = responseBytes[startIndex + j]
merkleProof.add(element)
else:
var element: array[32, byte]
merkleProof.add(element)
debug "---- raw response ----",
total_bytes = responseBytes.len, # Should be 640
non_zero_bytes = responseBytes.countIt(it != 0),
response = responseBytes
var i = 0
var merkleProof = newSeq[array[32, byte]]()
while (i * 32) + 31 < responseBytes.len:
var element: array[32, byte]
let startIndex = i * 32
let endIndex = startIndex + 31
element = responseBytes.toOpenArray(startIndex, endIndex)
merkleProof.add(element)
i += 1
debug "---- element ----",
startIndex = startIndex,
startElement = responseBytes[startIndex],
endIndex = endIndex,
endElement = responseBytes[endIndex],
element = element
# debug "merkleProof", responseBytes = responseBytes, merkleProof = merkleProof
return ok(merkleProof)
except CatchableError:
error "------ Failed to fetch Merkle proof elements ------",
error "Failed to fetch Merkle proof elements",
errMsg = getCurrentExceptionMsg(), index = g.membershipIndex.get()
return err("Failed to fetch Merkle proof elements: " & getCurrentExceptionMsg())
@ -164,39 +212,50 @@ method validateRoot*(g: OnchainGroupManager, root: MerkleNode): bool =
return true
return false
# Add this utility function to the file
proc toMerkleNode*(uint256Value: UInt256): MerkleNode =
## Converts a UInt256 value to a MerkleNode (array[32, byte])
var merkleNode: MerkleNode
let byteArray = uint256Value.toBytesBE()
for i in 0 ..< min(byteArray.len, merkleNode.len):
merkleNode[i] = byteArray[i]
return merkleNode
proc updateRoots*(g: OnchainGroupManager): Future[bool] {.async.} =
let rootRes = await g.fetchMerkleRoot()
if rootRes.isErr():
return false
let merkleRoot = toMerkleNode(rootRes.get())
if g.validRoots.len > 0 and g.validRoots[g.validRoots.len - 1] != merkleRoot:
let overflowCount = g.validRoots.len - AcceptableRootWindowSize + 1
if overflowCount > 0:
for i in 0 ..< overflowCount:
discard g.validRoots.popFirst()
let merkleRoot = toArray32LE(rootRes.get())
if g.validRoots.len == 0:
g.validRoots.addLast(merkleRoot)
return true
debug "--- validRoots ---", rootRes = rootRes.get(), validRoots = merkleRoot
if g.validRoots[g.validRoots.len - 1] != merkleRoot:
var overflow = g.validRoots.len - AcceptableRootWindowSize + 1
while overflow > 0:
discard g.validRoots.popFirst()
overflow = overflow - 1
g.validRoots.addLast(merkleRoot)
debug "~~~~~~~~~~~~~ Detected new Merkle root ~~~~~~~~~~~~~~~~",
root = merkleRoot.toHex, totalRoots = g.validRoots.len
return true
else:
debug "~~~~~~~~~~~~~ No new Merkle root ~~~~~~~~~~~~~~~~",
root = merkleRoot.toHex, totalRoots = g.validRoots.len
return false
proc trackRootChanges*(g: OnchainGroupManager) {.async.} =
let ethRpc = g.ethRpc.get()
let wakuRlnContract = g.wakuRlnContract.get()
# Set up the polling interval - more frequent to catch roots
const rpcDelay = 5.seconds
while true:
let rootUpdated = await g.updateRoots()
let proofResult = await g.fetchMerkleProofElements()
if proofResult.isErr():
error "Failed to fetch Merkle proof", error = proofResult.error
g.merkleProofCache = proofResult.get()
debug "--- track update ---",
len = g.validRoots.len,
validRoots = g.validRoots,
merkleProof = g.merkleProofCache
await sleepAsync(rpcDelay)
method atomicBatch*(
g: OnchainGroupManager,
start: MembershipIndex,
@ -295,32 +354,69 @@ method withdrawBatch*(
): Future[void] {.async: (raises: [Exception]).} =
initializedGuard(g)
proc toArray32*(s: seq[byte]): array[32, byte] =
var output: array[32, byte]
for i in 0 ..< 32:
output[i] = 0
let len = min(s.len, 32)
for i in 0 ..< len:
output[i] = s[s.len - 1 - i]
return output
proc indexToPath*(membershipIndex: uint, tree_depth: int): seq[byte] =
result = newSeq[byte](tree_depth)
var idx = membershipIndex
proc indexToPath(index: uint64): seq[byte] =
# Fixed tree height of 32 for RLN
const treeHeight = 20
result = newSeq[byte](treeHeight)
for i in 0 ..< treeHeight:
result[i] = byte((index shr i) and 1)
for i in 0 ..< tree_depth:
let bit = (idx shr (tree_depth - 1 - i)) and 1
result[i] = byte(bit)
# Hashes arbitrary signal to the underlying prime field.
proc hashToField*(signal: seq[byte]): array[32, byte] =
var ctx: keccak256
ctx.init()
ctx.update(signal)
var hash = ctx.finish()
debug "indexToPath", index = membershipIndex, path = result
var result: array[32, byte]
copyMem(result[0].addr, hash.data[0].addr, 32)
return result
proc createZerokitWitness(
g: OnchainGroupManager,
data: seq[byte],
epoch: Epoch,
messageId: MessageId,
extNullifier: array[32, byte],
): RLNWitnessInput =
let identitySecret = g.idCredentials.get().idSecretHash.toArray32LE()
# seq[byte] to array[32, byte] and convert to little-endian
let userMsgLimit = g.userMessageLimit.get().toArray32LE()
# uint64 to array[32, byte] and convert to little-endian
let msgId = messageId.toArray32LE()
# uint64 to array[32, byte] and convert to little-endian
try:
discard waitFor g.updateRoots()
except CatchableError:
error "Error updating roots", error = getCurrentExceptionMsg()
try:
let proofResult = waitFor g.fetchMerkleProofElements()
if proofResult.isErr():
error "Failed to fetch Merkle proof", error = proofResult.error
g.merkleProofCache = proofResult.get()
except CatchableError:
error "Error fetching Merkle proof", error = getCurrentExceptionMsg()
var pathElements: seq[array[32, byte]]
for elem in g.merkleProofCache:
pathElements.add(toArray32LE(elem)) # convert every element to little-endian
# Convert index to byte array (no endianness needed for path index)
let pathIndex = indexToPath(g.membershipIndex.get(), pathElements.len)
# uint to seq[byte]
debug "---- pathElements & pathIndex -----",
pathElements = pathElements,
pathIndex = pathIndex,
pathElementsLength = pathElements.len,
pathIndexLength = pathIndex.len
# Calculate hash using zerokit's hash_to_field equivalent
let x = hash_to_field(data).toArray32LE() # convert to little-endian
RLNWitnessInput(
identity_secret: identitySecret,
user_message_limit: userMsgLimit,
message_id: msgId,
path_elements: pathElements,
identity_path_index: pathIndex,
x: x,
external_nullifier: extNullifier,
)
method generateProof*(
g: OnchainGroupManager,
@ -338,52 +434,25 @@ method generateProof*(
if g.userMessageLimit.isNone():
return err("user message limit is not set")
debug "------ calling generateProof from generateProof from group_manager onchain ------",
debug "calling generateProof from group_manager onchain",
data = data,
membershipIndex = g.membershipIndex.get(),
userMessageLimit = g.userMessageLimit.get()
let externalNullifierRes = poseidon(@[@(epoch), @(rlnIdentifier)])
let externalNullifierRes =
poseidon(@[hash_to_field(@epoch).toSeq(), hash_to_field(@rlnIdentifier).toSeq()])
let extNullifier = externalNullifierRes.get().toArray32LE()
try:
let proofResult = waitFor g.fetchMerkleProofElements()
if proofResult.isErr():
return err("Failed to fetch Merkle proof: " & proofResult.error)
g.merkleProofCache = proofResult.get()
debug "Merkle proof fetched",
membershipIndex = g.membershipIndex.get(), elementCount = g.merkleProofCache.len
except CatchableError:
error "Failed to fetch merkle proof", error = getCurrentExceptionMsg()
return err("Failed to fetch Merkle proof: " & getCurrentExceptionMsg())
let witness = Witness(
identity_secret: g.idCredentials.get().idSecretHash.toArray32(),
user_message_limit: serialize(g.userMessageLimit.get()),
message_id: serialize(messageId),
path_elements: g.merkleProofCache,
identity_path_index: indexToPath(g.membershipIndex.get()),
x: hashToField(data),
external_nullifier: externalNullifierRes.get(),
)
let witness = createZerokitWitness(g, data, epoch, messageId, extNullifier)
debug "------ Generating proof with witness ------",
identity_secret = inHex(witness.identity_secret),
user_message_limit = inHex(witness.user_message_limit),
message_id = inHex(witness.message_id),
path_elements = witness.path_elements.map(inHex),
identity_path_index = witness.identity_path_index.mapIt($it).join(", "),
x = inHex(witness.x),
external_nullifier = inHex(witness.external_nullifier)
debug "------ Witness parameters ------",
identity_secret_len = witness.identity_secret.len,
user_message_limit_len = witness.user_message_limit.len,
message_id_len = witness.message_id.len,
path_elements_count = witness.path_elements.len,
identity_path_index_len = witness.identity_path_index.len,
x_len = witness.x.len,
external_nullifier_len = witness.external_nullifier.len
let serializedWitness = serialize(witness)
var inputBuffer = toBuffer(serializedWitness)
@ -394,7 +463,7 @@ method generateProof*(
if not success:
return err("Failed to generate proof")
else:
debug "------ Proof generated successfully --------"
debug "Proof generated successfully"
# Parse the proof into a RateLimitProof object
var proofValue = cast[ptr array[320, byte]](outputBuffer.`ptr`)

View File

@ -55,7 +55,7 @@ type RateLimitProof* = object
type
Fr = array[32, byte] # Field element representation (256 bits)
Witness* = object
RLNWitnessInput* = object
identity_secret*: Fr
user_message_limit*: Fr
message_id*: Fr

View File

@ -193,7 +193,7 @@ proc validateMessage*(
## `timeOption` indicates Unix epoch time (fractional part holds sub-seconds)
## if `timeOption` is supplied, then the current epoch is calculated based on that
debug "calling validateMessage from rln_relay", msg = msg
debug "calling validateMessage from rln_relay", msg_len = msg.payload.len
let decodeRes = RateLimitProof.init(msg.proof)
if decodeRes.isErr():
@ -472,6 +472,10 @@ proc mount(
(await groupManager.init()).isOkOr:
return err("could not initialize the group manager: " & $error)
if groupManager of OnchainGroupManager:
let onchainManager = cast[OnchainGroupManager](groupManager)
asyncSpawn trackRootChanges(onchainManager)
wakuRlnRelay = WakuRLNRelay(
groupManager: groupManager,
nonceManager: