mirror of https://github.com/waku-org/nwaku.git
Zerokit RLN integration in nwaku: alternative approach with inline compilation flag (#1060)
* refactor(rln): add inline compilation flags for RLN zerokit * update submodule update submodule * fix(rln): fix wrong merge conflict resolution * fix(rln): align hardcoded values in light of vacp2p/zerokit/issues/28
This commit is contained in:
parent
115982f5c3
commit
8a14b5b243
|
@ -148,3 +148,7 @@
|
||||||
url = https://github.com/status-im/nim-presto.git
|
url = https://github.com/status-im/nim-presto.git
|
||||||
ignore = untracked
|
ignore = untracked
|
||||||
branch = master
|
branch = master
|
||||||
|
[submodule "vendor/zerokit"]
|
||||||
|
path = vendor/zerokit
|
||||||
|
url = https://github.com/vacp2p/zerokit.git
|
||||||
|
branch = master
|
||||||
|
|
24
Makefile
24
Makefile
|
@ -75,6 +75,14 @@ else ifeq ($(CI), true)
|
||||||
NIM_PARAMS := $(NIM_PARAMS) -d:rln
|
NIM_PARAMS := $(NIM_PARAMS) -d:rln
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# control rln code compilation
|
||||||
|
ifeq ($(RLNZEROKIT), true)
|
||||||
|
NIM_PARAMS := $(NIM_PARAMS) -d:rlnzerokit
|
||||||
|
#To avoid redefinition conflicts, we disable rln zerokit default compilation in CI
|
||||||
|
#else ifeq ($(CI), true)
|
||||||
|
#NIM_PARAMS := $(NIM_PARAMS) -d:rlnzerokit
|
||||||
|
endif
|
||||||
|
|
||||||
# detecting the os
|
# detecting the os
|
||||||
ifeq ($(OS),Windows_NT) # is Windows_NT on XP, 2000, 7, Vista, 10...
|
ifeq ($(OS),Windows_NT) # is Windows_NT on XP, 2000, 7, Vista, 10...
|
||||||
detected_OS := Windows
|
detected_OS := Windows
|
||||||
|
@ -103,7 +111,7 @@ NIM_PARAMS := $(NIM_PARAMS) -d:discv5_protocol_id:d5waku
|
||||||
GIT_VERSION := "$(shell git describe --abbrev=6 --always --tags)"
|
GIT_VERSION := "$(shell git describe --abbrev=6 --always --tags)"
|
||||||
NIM_PARAMS := $(NIM_PARAMS) -d:git_version:\"$(GIT_VERSION)\"
|
NIM_PARAMS := $(NIM_PARAMS) -d:git_version:\"$(GIT_VERSION)\"
|
||||||
|
|
||||||
deps: | deps-common nat-libs waku.nims rlnlib
|
deps: | deps-common nat-libs waku.nims rlnlib rlnzerokitlib
|
||||||
ifneq ($(USE_LIBBACKTRACE), 0)
|
ifneq ($(USE_LIBBACKTRACE), 0)
|
||||||
deps: | libbacktrace
|
deps: | libbacktrace
|
||||||
endif
|
endif
|
||||||
|
@ -173,6 +181,15 @@ else ifeq ($(CI), true)
|
||||||
cargo build --manifest-path vendor/rln/Cargo.toml
|
cargo build --manifest-path vendor/rln/Cargo.toml
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
rlnzerokitlib:
|
||||||
|
ifeq ($(RLNZEROKIT), true)
|
||||||
|
cargo build --manifest-path vendor/zerokit/rln/Cargo.toml --release
|
||||||
|
#To avoid redefinition conflicts, we disable rln zerokit default compilation in CI
|
||||||
|
#else ifeq ($(CI), true)
|
||||||
|
# cargo build --manifest-path vendor/zerokit/rln/Cargo.toml --release
|
||||||
|
endif
|
||||||
|
|
||||||
test2: | build deps installganache
|
test2: | build deps installganache
|
||||||
echo -e $(BUILD_MSG) "build/$@" && \
|
echo -e $(BUILD_MSG) "build/$@" && \
|
||||||
$(ENV_SCRIPT) nim test2 $(NIM_PARAMS) waku.nims
|
$(ENV_SCRIPT) nim test2 $(NIM_PARAMS) waku.nims
|
||||||
|
@ -228,11 +245,16 @@ ifneq ($(USE_LIBBACKTRACE), 0)
|
||||||
+ $(MAKE) -C vendor/nim-libbacktrace clean $(HANDLE_OUTPUT)
|
+ $(MAKE) -C vendor/nim-libbacktrace clean $(HANDLE_OUTPUT)
|
||||||
endif
|
endif
|
||||||
cargo clean --manifest-path vendor/rln/Cargo.toml
|
cargo clean --manifest-path vendor/rln/Cargo.toml
|
||||||
|
cargo clean --manifest-path vendor/zerokit/rln/Cargo.toml
|
||||||
|
|
||||||
# clean the rln build (forces recompile of old crates on next build)
|
# clean the rln build (forces recompile of old crates on next build)
|
||||||
cleanrln:
|
cleanrln:
|
||||||
cargo clean --manifest-path vendor/rln/Cargo.toml
|
cargo clean --manifest-path vendor/rln/Cargo.toml
|
||||||
|
|
||||||
|
# clean the rln build (forces recompile of old crates on next build)
|
||||||
|
cleanrlnzerokit:
|
||||||
|
cargo clean --manifest-path vendor/zerokit/rln/Cargo.toml
|
||||||
|
|
||||||
endif # "variables.mk" was not included
|
endif # "variables.mk" was not included
|
||||||
|
|
||||||
libwaku.so: | build deps
|
libwaku.so: | build deps
|
||||||
|
|
|
@ -31,7 +31,7 @@ import
|
||||||
./v2/test_peer_exchange,
|
./v2/test_peer_exchange,
|
||||||
./v2/test_waku_noise
|
./v2/test_waku_noise
|
||||||
|
|
||||||
when defined(rln):
|
when defined(rln) or defined(rlnzerokit):
|
||||||
import ./v2/test_waku_rln_relay
|
import ./v2/test_waku_rln_relay
|
||||||
when defined(onchain_rln):
|
when defined(onchain_rln):
|
||||||
import ./v2/test_waku_rln_relay_onchain
|
import ./v2/test_waku_rln_relay_onchain
|
||||||
|
|
|
@ -64,45 +64,72 @@ procSuite "Waku rln relay":
|
||||||
await node.stop()
|
await node.stop()
|
||||||
|
|
||||||
suite "Waku rln relay":
|
suite "Waku rln relay":
|
||||||
test "key_gen Nim Wrappers":
|
|
||||||
var
|
|
||||||
merkleDepth: csize_t = 32
|
|
||||||
# parameters.key contains the parameters related to the Poseidon hasher
|
|
||||||
# to generate this file, clone this repo https://github.com/kilic/rln
|
|
||||||
# and run the following command in the root directory of the cloned project
|
|
||||||
# cargo run --example export_test_keys
|
|
||||||
# the file is generated separately and copied here
|
|
||||||
parameters = readFile("waku/v2/protocol/waku_rln_relay/parameters.key")
|
|
||||||
pbytes = parameters.toBytes()
|
|
||||||
len: csize_t = uint(pbytes.len)
|
|
||||||
parametersBuffer = Buffer(`ptr`: addr(pbytes[0]), len: len)
|
|
||||||
check:
|
|
||||||
# check the parameters.key is not empty
|
|
||||||
pbytes.len != 0
|
|
||||||
|
|
||||||
var
|
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||||
rlnInstance: RLN[Bn256]
|
test "key_gen Nim Wrappers":
|
||||||
let res = new_circuit_from_params(merkleDepth, addr parametersBuffer,
|
var
|
||||||
addr rlnInstance)
|
merkleDepth: csize_t = 32
|
||||||
check:
|
# parameters.key contains the parameters related to the Poseidon hasher
|
||||||
# check whether the circuit parameters are generated successfully
|
# to generate this file, clone this repo https://github.com/kilic/rln
|
||||||
res == true
|
# and run the following command in the root directory of the cloned project
|
||||||
|
# cargo run --example export_test_keys
|
||||||
# keysBufferPtr will hold the generated key pairs i.e., secret and public keys
|
# the file is generated separately and copied here
|
||||||
var
|
parameters = readFile("waku/v2/protocol/waku_rln_relay/parameters.key")
|
||||||
keysBuffer: Buffer
|
pbytes = parameters.toBytes()
|
||||||
keysBufferPtr = addr(keysBuffer)
|
len: csize_t = uint(pbytes.len)
|
||||||
done = key_gen(rlnInstance, keysBufferPtr)
|
parametersBuffer = Buffer(`ptr`: addr(pbytes[0]), len: len)
|
||||||
check:
|
|
||||||
# check whether the keys are generated successfully
|
|
||||||
done == true
|
|
||||||
|
|
||||||
if done:
|
|
||||||
var generatedKeys = cast[ptr array[64, byte]](keysBufferPtr.`ptr`)[]
|
|
||||||
check:
|
check:
|
||||||
# the public and secret keys together are 64 bytes
|
# check the parameters.key is not empty
|
||||||
generatedKeys.len == 64
|
pbytes.len != 0
|
||||||
debug "generated keys: ", generatedKeys
|
|
||||||
|
var
|
||||||
|
rlnInstance: RLN[Bn256]
|
||||||
|
let res = new_circuit_from_params(merkleDepth, addr parametersBuffer,
|
||||||
|
addr rlnInstance)
|
||||||
|
check:
|
||||||
|
# check whether the circuit parameters are generated successfully
|
||||||
|
res == true
|
||||||
|
|
||||||
|
# keysBufferPtr will hold the generated key pairs i.e., secret and public keys
|
||||||
|
var
|
||||||
|
keysBuffer: Buffer
|
||||||
|
keysBufferPtr = addr(keysBuffer)
|
||||||
|
done = key_gen(rlnInstance, keysBufferPtr)
|
||||||
|
check:
|
||||||
|
# check whether the keys are generated successfully
|
||||||
|
done == true
|
||||||
|
|
||||||
|
if done:
|
||||||
|
var generatedKeys = cast[ptr array[64, byte]](keysBufferPtr.`ptr`)[]
|
||||||
|
check:
|
||||||
|
# the public and secret keys together are 64 bytes
|
||||||
|
generatedKeys.len == 64
|
||||||
|
debug "generated keys: ", generatedKeys
|
||||||
|
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
test "key_gen Nim Wrappers":
|
||||||
|
var
|
||||||
|
merkleDepth: csize_t = 20
|
||||||
|
|
||||||
|
var rlnInstance = createRLNInstance()
|
||||||
|
check:
|
||||||
|
rlnInstance.isOk == true
|
||||||
|
|
||||||
|
# keysBufferPtr will hold the generated key pairs i.e., secret and public keys
|
||||||
|
var
|
||||||
|
keysBuffer: Buffer
|
||||||
|
keysBufferPtr = addr(keysBuffer)
|
||||||
|
done = key_gen(rlnInstance.value(), keysBufferPtr)
|
||||||
|
check:
|
||||||
|
# check whether the keys are generated successfully
|
||||||
|
done == true
|
||||||
|
|
||||||
|
if done:
|
||||||
|
var generatedKeys = cast[ptr array[64, byte]](keysBufferPtr.`ptr`)[]
|
||||||
|
check:
|
||||||
|
# the public and secret keys together are 64 bytes
|
||||||
|
generatedKeys.len == 64
|
||||||
|
debug "generated keys: ", generatedKeys
|
||||||
|
|
||||||
test "membership Key Gen":
|
test "membership Key Gen":
|
||||||
# create an RLN instance
|
# create an RLN instance
|
||||||
|
@ -371,8 +398,14 @@ suite "Waku rln relay":
|
||||||
check:
|
check:
|
||||||
hashSuccess
|
hashSuccess
|
||||||
let outputArr = cast[ptr array[32, byte]](outputBuffer.`ptr`)[]
|
let outputArr = cast[ptr array[32, byte]](outputBuffer.`ptr`)[]
|
||||||
check:
|
|
||||||
"efb8ac39dc22eaf377fe85b405b99ba78dbc2f3f32494add4501741df946bd1d" ==
|
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||||
|
check:
|
||||||
|
"efb8ac39dc22eaf377fe85b405b99ba78dbc2f3f32494add4501741df946bd1d" ==
|
||||||
|
outputArr.toHex()
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
check:
|
||||||
|
"4c6ea217404bd5f10e243bac29dc4f1ec36bf4a41caba7b4c8075c54abb3321e" ==
|
||||||
outputArr.toHex()
|
outputArr.toHex()
|
||||||
|
|
||||||
var
|
var
|
||||||
|
@ -392,8 +425,14 @@ suite "Waku rln relay":
|
||||||
let msg = "Hello".toBytes()
|
let msg = "Hello".toBytes()
|
||||||
|
|
||||||
let hash = rln.hash(msg)
|
let hash = rln.hash(msg)
|
||||||
check:
|
|
||||||
"efb8ac39dc22eaf377fe85b405b99ba78dbc2f3f32494add4501741df946bd1d" ==
|
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||||
|
check:
|
||||||
|
"efb8ac39dc22eaf377fe85b405b99ba78dbc2f3f32494add4501741df946bd1d" ==
|
||||||
|
hash.toHex()
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
check:
|
||||||
|
"4c6ea217404bd5f10e243bac29dc4f1ec36bf4a41caba7b4c8075c54abb3321e" ==
|
||||||
hash.toHex()
|
hash.toHex()
|
||||||
|
|
||||||
test "create a list of membership keys and construct a Merkle tree based on the list":
|
test "create a list of membership keys and construct a Merkle tree based on the list":
|
||||||
|
@ -428,35 +467,71 @@ suite "Waku rln relay":
|
||||||
# compare the calculated root against the correct root
|
# compare the calculated root against the correct root
|
||||||
root == STATIC_GROUP_MERKLE_ROOT
|
root == STATIC_GROUP_MERKLE_ROOT
|
||||||
|
|
||||||
test "RateLimitProof Protobuf encode/init test":
|
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||||
var
|
test "RateLimitProof Protobuf encode/init test":
|
||||||
proof: ZKSNARK
|
var
|
||||||
merkleRoot: MerkleNode
|
proof: ZKSNARK
|
||||||
epoch: Epoch
|
merkleRoot: MerkleNode
|
||||||
shareX: MerkleNode
|
epoch: Epoch
|
||||||
shareY: MerkleNode
|
shareX: MerkleNode
|
||||||
nullifier: Nullifier
|
shareY: MerkleNode
|
||||||
# populate fields with dummy values
|
nullifier: Nullifier
|
||||||
for x in proof.mitems: x = 1
|
# populate fields with dummy values
|
||||||
for x in merkleRoot.mitems: x = 2
|
for x in proof.mitems: x = 1
|
||||||
for x in epoch.mitems: x = 3
|
for x in merkleRoot.mitems: x = 2
|
||||||
for x in shareX.mitems: x = 4
|
for x in epoch.mitems: x = 3
|
||||||
for x in shareY.mitems: x = 5
|
for x in shareX.mitems: x = 4
|
||||||
for x in nullifier.mitems: x = 6
|
for x in shareY.mitems: x = 5
|
||||||
|
for x in nullifier.mitems: x = 6
|
||||||
|
|
||||||
let
|
let
|
||||||
rateLimitProof = RateLimitProof(proof: proof,
|
rateLimitProof = RateLimitProof(proof: proof,
|
||||||
merkleRoot: merkleRoot,
|
merkleRoot: merkleRoot,
|
||||||
epoch: epoch,
|
epoch: epoch,
|
||||||
shareX: shareX,
|
shareX: shareX,
|
||||||
shareY: shareY,
|
shareY: shareY,
|
||||||
nullifier: nullifier)
|
nullifier: nullifier)
|
||||||
protobuf = rateLimitProof.encode()
|
protobuf = rateLimitProof.encode()
|
||||||
decodednsp = RateLimitProof.init(protobuf.buffer)
|
decodednsp = RateLimitProof.init(protobuf.buffer)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
decodednsp.isErr == false
|
decodednsp.isErr == false
|
||||||
decodednsp.value == rateLimitProof
|
decodednsp.value == rateLimitProof
|
||||||
|
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
test "RateLimitProof Protobuf encode/init test":
|
||||||
|
var
|
||||||
|
proof: ZKSNARK
|
||||||
|
merkleRoot: MerkleNode
|
||||||
|
epoch: Epoch
|
||||||
|
shareX: MerkleNode
|
||||||
|
shareY: MerkleNode
|
||||||
|
nullifier: Nullifier
|
||||||
|
rlnIdentifier: RlnIdentifier
|
||||||
|
|
||||||
|
# populate fields with dummy values
|
||||||
|
for x in proof.mitems: x = 1
|
||||||
|
for x in merkleRoot.mitems: x = 2
|
||||||
|
for x in epoch.mitems: x = 3
|
||||||
|
for x in shareX.mitems: x = 4
|
||||||
|
for x in shareY.mitems: x = 5
|
||||||
|
for x in nullifier.mitems: x = 6
|
||||||
|
for x in rlnIdentifier.mitems: x = 7
|
||||||
|
|
||||||
|
let
|
||||||
|
rateLimitProof = RateLimitProof(proof: proof,
|
||||||
|
merkleRoot: merkleRoot,
|
||||||
|
epoch: epoch,
|
||||||
|
shareX: shareX,
|
||||||
|
shareY: shareY,
|
||||||
|
nullifier: nullifier,
|
||||||
|
rlnIdentifier: rlnIdentifier)
|
||||||
|
protobuf = rateLimitProof.encode()
|
||||||
|
decodednsp = RateLimitProof.init(protobuf.buffer)
|
||||||
|
|
||||||
|
check:
|
||||||
|
decodednsp.isErr == false
|
||||||
|
decodednsp.value == rateLimitProof
|
||||||
|
|
||||||
test "test proofVerify and proofGen for a valid proof":
|
test "test proofVerify and proofGen for a valid proof":
|
||||||
var rlnInstance = createRLNInstance()
|
var rlnInstance = createRLNInstance()
|
||||||
|
@ -600,6 +675,7 @@ suite "Waku rln relay":
|
||||||
for index, x in shareX3.mpairs: shareX3[index] = 3
|
for index, x in shareX3.mpairs: shareX3[index] = 3
|
||||||
let shareY3 = shareX3
|
let shareY3 = shareX3
|
||||||
|
|
||||||
|
## TODO: when zerokit rln is integrated, RateLimitProof should be initialized passing a rlnIdentifier too (now implicitely set to 0)
|
||||||
let
|
let
|
||||||
wm1 = WakuMessage(proof: RateLimitProof(epoch: epoch,
|
wm1 = WakuMessage(proof: RateLimitProof(epoch: epoch,
|
||||||
nullifier: nullifier1, shareX: shareX1, shareY: shareY1))
|
nullifier: nullifier1, shareX: shareX1, shareY: shareY1))
|
||||||
|
@ -679,6 +755,8 @@ suite "Waku rln relay":
|
||||||
proofAdded3 = wakuRlnRelay.appendRLNProof(wm3, time+EPOCH_UNIT_SECONDS)
|
proofAdded3 = wakuRlnRelay.appendRLNProof(wm3, time+EPOCH_UNIT_SECONDS)
|
||||||
wm4 = WakuMessage(payload: "Invalid message".toBytes())
|
wm4 = WakuMessage(payload: "Invalid message".toBytes())
|
||||||
|
|
||||||
|
echo wm1.proof
|
||||||
|
|
||||||
# checks proofs are added
|
# checks proofs are added
|
||||||
check:
|
check:
|
||||||
proofAdded1
|
proofAdded1
|
||||||
|
@ -702,6 +780,7 @@ suite "Waku rln relay":
|
||||||
msgValidate2 == MessageValidationResult.Spam
|
msgValidate2 == MessageValidationResult.Spam
|
||||||
msgValidate3 == MessageValidationResult.Valid
|
msgValidate3 == MessageValidationResult.Valid
|
||||||
msgValidate4 == MessageValidationResult.Invalid
|
msgValidate4 == MessageValidationResult.Invalid
|
||||||
|
|
||||||
test "toIDCommitment and toUInt256":
|
test "toIDCommitment and toUInt256":
|
||||||
# create an instance of rln
|
# create an instance of rln
|
||||||
var rlnInstance = createRLNInstance()
|
var rlnInstance = createRLNInstance()
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 3378aed85716144231e036c076fe3f14ed9538de
|
|
@ -6,7 +6,11 @@ import
|
||||||
os,
|
os,
|
||||||
waku_rln_relay_types
|
waku_rln_relay_types
|
||||||
|
|
||||||
const libPath = "vendor/rln/target/debug/"
|
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||||
|
const libPath = "vendor/rln/target/debug/"
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
const libPath = "vendor/zerokit/target/release/"
|
||||||
|
|
||||||
when defined(Windows):
|
when defined(Windows):
|
||||||
const libName* = libPath / "rln.dll"
|
const libName* = libPath / "rln.dll"
|
||||||
elif defined(Linux):
|
elif defined(Linux):
|
||||||
|
@ -23,66 +27,175 @@ elif defined(MacOsX):
|
||||||
type Buffer* = object
|
type Buffer* = object
|
||||||
`ptr`*: ptr uint8
|
`ptr`*: ptr uint8
|
||||||
len*: uint
|
len*: uint
|
||||||
|
|
||||||
#------------------------------ Merkle Tree operations -----------------------------------------
|
|
||||||
proc update_next_member*(ctx: RLN[Bn256],
|
|
||||||
input_buffer: ptr Buffer): bool {.importc: "update_next_member".}
|
|
||||||
## input_buffer points to the id commitment byte seq
|
|
||||||
## the return bool value indicates the success or failure of the operation
|
|
||||||
|
|
||||||
proc delete_member*(ctx: RLN[Bn256], index: uint): bool {.importc: "delete_member".}
|
######################################################################
|
||||||
## index is the position of the id commitment key to be deleted from the tree
|
## Kilic's RLN module APIs
|
||||||
## the deleted id commitment key is replaced with a zero leaf
|
######################################################################
|
||||||
## the return bool value indicates the success or failure of the operation
|
|
||||||
|
|
||||||
proc get_root*(ctx: RLN[Bn256], output_buffer: ptr Buffer): bool {.importc: "get_root".}
|
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||||
## get_root populates the passed pointer output_buffer with the current tree root
|
|
||||||
## the output_buffer holds the Merkle tree root of size 32 bytes
|
|
||||||
## the return bool value indicates the success or failure of the operation
|
|
||||||
##
|
|
||||||
#----------------------------------------------------------------------------------------------
|
|
||||||
#-------------------------------- zkSNARKs operations -----------------------------------------
|
|
||||||
proc key_gen*(ctx: RLN[Bn256], keypair_buffer: ptr Buffer): bool {.importc: "key_gen".}
|
|
||||||
## generates id key and id commitment key serialized inside keypair_buffer as | id_key <32 bytes>| id_commitment_key <32 bytes> |
|
|
||||||
## id commitment is the poseidon hash of the id key
|
|
||||||
## the return bool value indicates the success or failure of the operation
|
|
||||||
|
|
||||||
proc generate_proof*(ctx: RLN[Bn256],
|
#------------------------------ Merkle Tree operations -----------------------------------------
|
||||||
input_buffer: ptr Buffer,
|
proc update_next_member*(ctx: RLN[Bn256],
|
||||||
output_buffer: ptr Buffer): bool {.importc: "generate_proof".}
|
input_buffer: ptr Buffer): bool {.importc: "update_next_member".}
|
||||||
## input_buffer serialized as [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> ]
|
## input_buffer points to the id commitment byte seq
|
||||||
## output_buffer holds the proof data and should be parsed as |proof<256>|root<32>|epoch<32>|share_x<32>|share_y<32>|nullifier<32>|
|
## the return bool value indicates the success or failure of the operation
|
||||||
## integers wrapped in <> indicate value sizes in bytes
|
|
||||||
## the return bool value indicates the success or failure of the operation
|
proc delete_member*(ctx: RLN[Bn256], index: uint): bool {.importc: "delete_member".}
|
||||||
##
|
## index is the position of the id commitment key to be deleted from the tree
|
||||||
proc verify*(ctx: RLN[Bn256],
|
## the deleted id commitment key is replaced with a zero leaf
|
||||||
proof_buffer: ptr Buffer,
|
## the return bool value indicates the success or failure of the operation
|
||||||
result_ptr: ptr uint32): bool {.importc: "verify".}
|
|
||||||
## proof_buffer [ proof<256>| root<32>| epoch<32>| share_x<32>| share_y<32>| nullifier<32> | signal_len<8> | signal<var> ]
|
proc get_root*(ctx: RLN[Bn256], output_buffer: ptr Buffer): bool {.importc: "get_root".}
|
||||||
## the return bool value indicates the success or failure of the call to the verify function
|
## get_root populates the passed pointer output_buffer with the current tree root
|
||||||
## the result of the verification of the zk proof is stored in the value pointed by result_ptr, where 0 indicates success and 1 is failure
|
## the output_buffer holds the Merkle tree root of size 32 bytes
|
||||||
|
## the return bool value indicates the success or failure of the operation
|
||||||
|
##
|
||||||
|
#----------------------------------------------------------------------------------------------
|
||||||
|
#-------------------------------- zkSNARKs operations -----------------------------------------
|
||||||
|
proc key_gen*(ctx: RLN[Bn256], keypair_buffer: ptr Buffer): bool {.importc: "key_gen".}
|
||||||
|
## generates id key and id commitment key serialized inside keypair_buffer as | id_key <32 bytes>| id_commitment_key <32 bytes> |
|
||||||
|
## id commitment is the poseidon hash of the id key
|
||||||
|
## the return bool value indicates the success or failure of the operation
|
||||||
|
|
||||||
|
proc generate_proof*(ctx: RLN[Bn256],
|
||||||
|
input_buffer: ptr Buffer,
|
||||||
|
output_buffer: ptr Buffer): bool {.importc: "generate_proof".}
|
||||||
|
## input_buffer serialized as [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> ]
|
||||||
|
## output_buffer holds the proof data and should be parsed as |proof<256>|root<32>|epoch<32>|share_x<32>|share_y<32>|nullifier<32>|
|
||||||
|
## integers wrapped in <> indicate value sizes in bytes
|
||||||
|
## the return bool value indicates the success or failure of the operation
|
||||||
|
##
|
||||||
|
proc verify*(ctx: RLN[Bn256],
|
||||||
|
proof_buffer: ptr Buffer,
|
||||||
|
result_ptr: ptr uint32): bool {.importc: "verify".}
|
||||||
|
## proof_buffer [ proof<256>| root<32>| epoch<32>| share_x<32>| share_y<32>| nullifier<32> | signal_len<8> | signal<var> ]
|
||||||
|
## the return bool value indicates the success or failure of the call to the verify function
|
||||||
|
## the result of the verification of the zk proof is stored in the value pointed by result_ptr, where 0 indicates success and 1 is failure
|
||||||
|
|
||||||
|
|
||||||
#----------------------------------------------------------------------------------------------
|
#----------------------------------------------------------------------------------------------
|
||||||
#-------------------------------- Common procedures -------------------------------------------
|
#-------------------------------- Common procedures -------------------------------------------
|
||||||
|
|
||||||
proc new_circuit_from_params*(merkle_depth: uint,
|
proc new_circuit_from_params*(merkle_depth: uint,
|
||||||
parameters_buffer: ptr Buffer,
|
parameters_buffer: ptr Buffer,
|
||||||
ctx: ptr RLN[Bn256]): bool {.importc: "new_circuit_from_params".}
|
ctx: ptr RLN[Bn256]): bool {.importc: "new_circuit_from_params".}
|
||||||
## creates an instance of rln object as defined by the rln lib https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L48
|
## creates an instance of rln object as defined by the rln lib https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L48
|
||||||
## merkle_depth represent the depth of the Merkle tree
|
## merkle_depth represent the depth of the Merkle tree
|
||||||
## parameters_buffer holds prover and verifier keys
|
## parameters_buffer holds prover and verifier keys
|
||||||
## ctx holds the final created rln object
|
## ctx holds the final created rln object
|
||||||
## the return bool value indicates the success or failure of the operation
|
## the return bool value indicates the success or failure of the operation
|
||||||
|
|
||||||
|
|
||||||
proc hash*(ctx: RLN[Bn256],
|
proc hash*(ctx: RLN[Bn256],
|
||||||
inputs_buffer: ptr Buffer,
|
inputs_buffer: ptr Buffer,
|
||||||
output_buffer: ptr Buffer): bool {.importc: "signal_to_field".}
|
output_buffer: ptr Buffer): bool {.importc: "signal_to_field".}
|
||||||
## as explained in https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L135, it hashes (sha256) the plain text supplied in inputs_buffer and then maps it to a field element
|
## as explained in https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L135, it hashes (sha256) the plain text supplied in inputs_buffer and then maps it to a field element
|
||||||
## this proc is used to map arbitrary signals to field element for the sake of proof generation
|
## this proc is used to map arbitrary signals to field element for the sake of proof generation
|
||||||
## inputs_buffer holds the hash input as a byte seq
|
## inputs_buffer holds the hash input as a byte seq
|
||||||
## the hash output is generated and populated inside output_buffer
|
## the hash output is generated and populated inside output_buffer
|
||||||
## the output_buffer contains 32 bytes hash output
|
## the output_buffer contains 32 bytes hash output
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
## RLN Zerokit module APIs
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
#------------------------------ Merkle Tree operations -----------------------------------------
|
||||||
|
proc update_next_member*(ctx: ptr RLN, input_buffer: ptr Buffer): bool {.importc: "set_next_leaf".}
|
||||||
|
## adds an element in the merkle tree to the next available position
|
||||||
|
## input_buffer points to the id commitment byte seq
|
||||||
|
## the return bool value indicates the success or failure of the operation
|
||||||
|
|
||||||
|
proc delete_member*(ctx: ptr RLN, index: uint): bool {.importc: "delete_leaf".}
|
||||||
|
## index is the position of the id commitment key to be deleted from the tree
|
||||||
|
## the deleted id commitment key is replaced with a zero leaf
|
||||||
|
## the return bool value indicates the success or failure of the operation
|
||||||
|
|
||||||
|
proc get_root*(ctx: ptr RLN, output_buffer: ptr Buffer): bool {.importc: "get_root".}
|
||||||
|
## get_root populates the passed pointer output_buffer with the current tree root
|
||||||
|
## the output_buffer holds the Merkle tree root of size 32 bytes
|
||||||
|
## the return bool value indicates the success or failure of the operation
|
||||||
|
|
||||||
|
proc get_merkle_proof*(ctx: ptr RLN, index: uint, output_buffer: ptr Buffer): bool {.importc: "get_proof".}
|
||||||
|
## populates the passed pointer output_buffer with the merkle proof for the leaf at position index in the tree stored by ctx
|
||||||
|
## the output_buffer holds a serialized Merkle proof (vector of 32 bytes nodes)
|
||||||
|
## the return bool value indicates the success or failure of the operation
|
||||||
|
|
||||||
|
proc set_leaf*(ctx: ptr RLN, index: uint, input_buffer: ptr Buffer): bool {.importc: "set_leaf".}
|
||||||
|
## sets the leaf at position index in the tree stored by ctx to the value passed by input_buffer
|
||||||
|
## the input_buffer holds a serialized leaf of 32 bytes
|
||||||
|
## the return bool value indicates the success or failure of the operation
|
||||||
|
|
||||||
|
proc set_leaves*(ctx: ptr RLN, input_buffer: ptr Buffer): bool {.importc: "set_leaves".}
|
||||||
|
## sets multiple leaves in the tree stored by ctx to the value passed by input_buffer
|
||||||
|
## the input_buffer holds a serialized vector of leaves (32 bytes each)
|
||||||
|
## leaves are set one after each other starting from index 0
|
||||||
|
## the return bool value indicates the success or failure of the operation
|
||||||
|
|
||||||
|
proc reset_tree*(ctx: ptr RLN, tree_height: uint): bool {.importc: "set_tree".}
|
||||||
|
## resets the tree stored by ctx to the the empty tree (all leaves set to 0) of height tree_height
|
||||||
|
## the return bool value indicates the success or failure of the operation
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#-------------------------------- zkSNARKs operations -----------------------------------------
|
||||||
|
proc key_gen*(ctx: ptr RLN, output_buffer: ptr Buffer): bool {.importc: "key_gen".}
|
||||||
|
## generates id key and id commitment key serialized inside keypair_buffer as | id_key <32 bytes>| id_commitment_key <32 bytes> |
|
||||||
|
## id commitment is the poseidon hash of the id key
|
||||||
|
## the return bool value indicates the success or failure of the operation
|
||||||
|
|
||||||
|
proc generate_proof*(ctx: ptr RLN,
|
||||||
|
input_buffer: ptr Buffer,
|
||||||
|
output_buffer: ptr Buffer): bool {.importc: "generate_rln_proof".}
|
||||||
|
## input_buffer has to be serialized as [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> ]
|
||||||
|
## output_buffer holds the proof data and should be parsed as [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ]
|
||||||
|
## integers wrapped in <> indicate value sizes in bytes
|
||||||
|
## the return bool value indicates the success or failure of the operation
|
||||||
|
|
||||||
|
proc verify*(ctx: ptr RLN,
|
||||||
|
proof_buffer: ptr Buffer,
|
||||||
|
proof_is_valid_ptr: ptr bool): bool {.importc: "verify_rln_proof".}
|
||||||
|
## proof_buffer has to be serialized as [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> | signal_len<8> | signal<var> ]
|
||||||
|
## the return bool value indicates the success or failure of the call to the verify function
|
||||||
|
## the verification of the zk proof is available in proof_is_valid_ptr, where a value of true indicates success and false a failure
|
||||||
|
|
||||||
|
proc zk_prove*(ctx: ptr RLN,
|
||||||
|
input_buffer: ptr Buffer,
|
||||||
|
output_buffer: ptr Buffer): bool {.importc: "prove".}
|
||||||
|
## Computes the zkSNARK proof and stores it in output_buffer for input values stored in input_buffer
|
||||||
|
## input_buffer is serialized as input_data as [ id_key<32> | path_elements<Vec<32>> | identity_path_index<Vec<1>> | x<32> | epoch<32> | rln_identifier<32> ]
|
||||||
|
## output_buffer holds the proof data and should be parsed as [ proof<128> ]
|
||||||
|
## path_elements and indentity_path elements serialize a merkle proof for id_key and are vectors of elements of 32 and 1 bytes, respectively (not. Vec<>).
|
||||||
|
## x is the x coordinate of the Shamir's secret share for which the proof is computed
|
||||||
|
## epoch is the input epoch (equivalently, the nullifier)
|
||||||
|
## the return bool value indicates the success or failure of the operation
|
||||||
|
|
||||||
|
proc zk_verify*(ctx: ptr RLN,
|
||||||
|
proof_buffer: ptr Buffer,
|
||||||
|
proof_is_valid_ptr: ptr bool): bool {.importc: "verify".}
|
||||||
|
## Verifies the zkSNARK proof passed in proof_buffer
|
||||||
|
## input_buffer is serialized as input_data as [ proof<128> ]
|
||||||
|
## the verification of the zk proof is available in proof_is_valid_ptr, where a value of true indicates success and false a failure
|
||||||
|
## the return bool value indicates the success or failure of the operation
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#-------------------------------- Common procedures -------------------------------------------
|
||||||
|
proc new_circuit*(tree_height: uint, input_buffer: ptr Buffer, ctx: ptr (ptr RLN)): bool {.importc: "new".}
|
||||||
|
## creates an instance of rln object as defined by the zerokit RLN lib
|
||||||
|
## merkle_depth represent the depth of the Merkle tree
|
||||||
|
## input_buffer contains a serialization of the path where the circuit resources can be found (.r1cs, .wasm, .zkey and optionally the verification_key.json)
|
||||||
|
## ctx holds the final created rln object
|
||||||
|
## the return bool value indicates the success or failure of the operation
|
||||||
|
|
||||||
|
proc hash*(ctx: ptr RLN,
|
||||||
|
input_buffer: ptr Buffer,
|
||||||
|
output_buffer: ptr Buffer): bool {.importc: "hash".}
|
||||||
|
## it hashes (sha256) the plain text supplied in inputs_buffer and then maps it to a field element
|
||||||
|
## this proc is used to map arbitrary signals to field element for the sake of proof generation
|
||||||
|
## inputs_buffer holds the hash input as a byte seq
|
||||||
|
## the hash output is generated and populated inside output_buffer
|
||||||
|
## the output_buffer contains 32 bytes hash output
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
@ -9,11 +9,15 @@ import
|
||||||
stew/arrayops,
|
stew/arrayops,
|
||||||
../../utils/protobuf
|
../../utils/protobuf
|
||||||
|
|
||||||
## Bn256 and RLN are Nim wrappers for the data types used in
|
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||||
## the rln library https://github.com/kilic/rln/blob/3bbec368a4adc68cd5f9bfae80b17e1bbb4ef373/src/ffi.rs
|
## Bn256 and RLN are Nim wrappers for the data types used in
|
||||||
type Bn256* = pointer
|
## the rln library https://github.com/kilic/rln/blob/3bbec368a4adc68cd5f9bfae80b17e1bbb4ef373/src/ffi.rs
|
||||||
type RLN*[E] = pointer
|
type Bn256* = pointer
|
||||||
|
type RLN*[E] = pointer
|
||||||
|
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
## RLN is a Nim wrapper for the data types used in zerokit RLN
|
||||||
|
type RLN* {.incompleteStruct.} = object
|
||||||
|
|
||||||
type
|
type
|
||||||
# identity key as defined in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Membership
|
# identity key as defined in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Membership
|
||||||
|
@ -21,13 +25,19 @@ type
|
||||||
# hash of identity key as defined ed in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Membership
|
# hash of identity key as defined ed in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Membership
|
||||||
IDCommitment* = array[32, byte]
|
IDCommitment* = array[32, byte]
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
MerkleNode* = array[32, byte] # Each node of the Merkle tee is a Poseidon hash which is a 32 byte value
|
MerkleNode* = array[32, byte] # Each node of the Merkle tee is a Poseidon hash which is a 32 byte value
|
||||||
Nullifier* = array[32, byte]
|
Nullifier* = array[32, byte]
|
||||||
ZKSNARK* = array[256, byte]
|
|
||||||
Epoch* = array[32, byte]
|
Epoch* = array[32, byte]
|
||||||
|
|
||||||
|
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||||
|
type
|
||||||
|
ZKSNARK* = array[256, byte]
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
type
|
||||||
|
ZKSNARK* = array[128, byte]
|
||||||
|
RlnIdentifier* = array[32, byte]
|
||||||
|
|
||||||
# Custom data types defined for waku rln relay -------------------------
|
# Custom data types defined for waku rln relay -------------------------
|
||||||
type MembershipKeyPair* = object
|
type MembershipKeyPair* = object
|
||||||
## user's identity key (a secret key) which is selected randomly
|
## user's identity key (a secret key) which is selected randomly
|
||||||
|
@ -38,55 +48,100 @@ type MembershipKeyPair* = object
|
||||||
# more details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Membership
|
# more details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Membership
|
||||||
idCommitment*: IDCommitment
|
idCommitment*: IDCommitment
|
||||||
|
|
||||||
|
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||||
|
type RateLimitProof* = object
|
||||||
|
## RateLimitProof holds the public inputs to rln circuit as
|
||||||
|
## defined in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Public-Inputs
|
||||||
|
## the `proof` field carries the actual zkSNARK proof
|
||||||
|
proof*: ZKSNARK
|
||||||
|
## the root of Merkle tree used for the generation of the `proof`
|
||||||
|
merkleRoot*: MerkleNode
|
||||||
|
## the epoch used for the generation of the `proof`
|
||||||
|
epoch*: Epoch
|
||||||
|
## shareX and shareY are shares of user's identity key
|
||||||
|
## these shares are created using Shamir secret sharing scheme
|
||||||
|
## see details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Linear-Equation-amp-SSS
|
||||||
|
shareX*: MerkleNode
|
||||||
|
shareY*: MerkleNode
|
||||||
|
## nullifier enables linking two messages published during the same epoch
|
||||||
|
## see details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Nullifiers
|
||||||
|
nullifier*: Nullifier
|
||||||
|
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
type RateLimitProof* = object
|
||||||
|
## RateLimitProof holds the public inputs to rln circuit as
|
||||||
|
## defined in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Public-Inputs
|
||||||
|
## the `proof` field carries the actual zkSNARK proof
|
||||||
|
proof*: ZKSNARK
|
||||||
|
## the root of Merkle tree used for the generation of the `proof`
|
||||||
|
merkleRoot*: MerkleNode
|
||||||
|
## the epoch used for the generation of the `proof`
|
||||||
|
epoch*: Epoch
|
||||||
|
## shareX and shareY are shares of user's identity key
|
||||||
|
## these shares are created using Shamir secret sharing scheme
|
||||||
|
## see details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Linear-Equation-amp-SSS
|
||||||
|
shareX*: MerkleNode
|
||||||
|
shareY*: MerkleNode
|
||||||
|
## nullifier enables linking two messages published during the same epoch
|
||||||
|
## see details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Nullifiers
|
||||||
|
nullifier*: Nullifier
|
||||||
|
## Application specific RLN Identifier
|
||||||
|
rlnIdentifier*: RlnIdentifier
|
||||||
|
|
||||||
type MembershipIndex* = uint
|
type MembershipIndex* = uint
|
||||||
|
|
||||||
type RlnMembershipCredentials* = object
|
type RlnMembershipCredentials* = object
|
||||||
membershipKeyPair*: MembershipKeyPair
|
membershipKeyPair*: MembershipKeyPair
|
||||||
rlnIndex*: MembershipIndex
|
rlnIndex*: MembershipIndex
|
||||||
|
|
||||||
type RateLimitProof* = object
|
|
||||||
## RateLimitProof holds the public inputs to rln circuit as
|
|
||||||
## defined in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Public-Inputs
|
|
||||||
## the `proof` field carries the actual zkSNARK proof
|
|
||||||
proof*: ZKSNARK
|
|
||||||
## the root of Merkle tree used for the generation of the `proof`
|
|
||||||
merkleRoot*: MerkleNode
|
|
||||||
## the epoch used for the generation of the `proof`
|
|
||||||
epoch*: Epoch
|
|
||||||
## shareX and shareY are shares of user's identity key
|
|
||||||
## these shares are created using Shamir secret sharing scheme
|
|
||||||
## see details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Linear-Equation-amp-SSS
|
|
||||||
shareX*: MerkleNode
|
|
||||||
shareY*: MerkleNode
|
|
||||||
## nullifier enables linking two messages published during the same epoch
|
|
||||||
## see details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Nullifiers
|
|
||||||
nullifier*: Nullifier
|
|
||||||
|
|
||||||
type ProofMetadata* = object
|
type ProofMetadata* = object
|
||||||
nullifier*: Nullifier
|
nullifier*: Nullifier
|
||||||
shareX*: MerkleNode
|
shareX*: MerkleNode
|
||||||
shareY*: MerkleNode
|
shareY*: MerkleNode
|
||||||
|
|
||||||
type WakuRLNRelay* = ref object
|
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||||
membershipKeyPair*: MembershipKeyPair
|
type WakuRLNRelay* = ref object
|
||||||
# membershipIndex denotes the index of a leaf in the Merkle tree
|
membershipKeyPair*: MembershipKeyPair
|
||||||
# that contains the pk of the current peer
|
# membershipIndex denotes the index of a leaf in the Merkle tree
|
||||||
# this index is used to retrieve the peer's authentication path
|
# that contains the pk of the current peer
|
||||||
membershipIndex*: MembershipIndex
|
# this index is used to retrieve the peer's authentication path
|
||||||
membershipContractAddress*: Address
|
membershipIndex*: MembershipIndex
|
||||||
ethClientAddress*: string
|
membershipContractAddress*: Address
|
||||||
ethAccountAddress*: Address
|
ethClientAddress*: string
|
||||||
# this field is required for signing transactions
|
ethAccountAddress*: Address
|
||||||
# TODO may need to erase this ethAccountPrivateKey when is not used
|
# this field is required for signing transactions
|
||||||
# TODO may need to make ethAccountPrivateKey mandatory
|
# TODO may need to erase this ethAccountPrivateKey when is not used
|
||||||
ethAccountPrivateKey*: Option[PrivateKey]
|
# TODO may need to make ethAccountPrivateKey mandatory
|
||||||
rlnInstance*: RLN[Bn256]
|
ethAccountPrivateKey*: Option[PrivateKey]
|
||||||
pubsubTopic*: string # the pubsub topic for which rln relay is mounted
|
rlnInstance*: RLN[Bn256]
|
||||||
# contentTopic should be of type waku_message.ContentTopic, however, due to recursive module dependency, the underlying type of ContentTopic is used instead
|
pubsubTopic*: string # the pubsub topic for which rln relay is mounted
|
||||||
# TODO a long-term solution is to place types with recursive dependency inside one file
|
# contentTopic should be of type waku_message.ContentTopic, however, due to recursive module dependency, the underlying type of ContentTopic is used instead
|
||||||
contentTopic*: string
|
# TODO a long-term solution is to place types with recursive dependency inside one file
|
||||||
# the log of nullifiers and Shamir shares of the past messages grouped per epoch
|
contentTopic*: string
|
||||||
nullifierLog*: Table[Epoch, seq[ProofMetadata]]
|
# the log of nullifiers and Shamir shares of the past messages grouped per epoch
|
||||||
|
nullifierLog*: Table[Epoch, seq[ProofMetadata]]
|
||||||
|
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
type WakuRLNRelay* = ref object
|
||||||
|
membershipKeyPair*: MembershipKeyPair
|
||||||
|
# membershipIndex denotes the index of a leaf in the Merkle tree
|
||||||
|
# that contains the pk of the current peer
|
||||||
|
# this index is used to retrieve the peer's authentication path
|
||||||
|
membershipIndex*: MembershipIndex
|
||||||
|
membershipContractAddress*: Address
|
||||||
|
ethClientAddress*: string
|
||||||
|
ethAccountAddress*: Address
|
||||||
|
# this field is required for signing transactions
|
||||||
|
# TODO may need to erase this ethAccountPrivateKey when is not used
|
||||||
|
# TODO may need to make ethAccountPrivateKey mandatory
|
||||||
|
ethAccountPrivateKey*: Option[PrivateKey]
|
||||||
|
rlnInstance*: ptr RLN
|
||||||
|
pubsubTopic*: string # the pubsub topic for which rln relay is mounted
|
||||||
|
# contentTopic should be of type waku_message.ContentTopic, however, due to recursive module dependency, the underlying type of ContentTopic is used instead
|
||||||
|
# TODO a long-term solution is to place types with recursive dependency inside one file
|
||||||
|
contentTopic*: string
|
||||||
|
# the log of nullifiers and Shamir shares of the past messages grouped per epoch
|
||||||
|
nullifierLog*: Table[Epoch, seq[ProofMetadata]]
|
||||||
|
|
||||||
type MessageValidationResult* {.pure.} = enum
|
type MessageValidationResult* {.pure.} = enum
|
||||||
Valid, Invalid, Spam
|
Valid, Invalid, Spam
|
||||||
|
@ -94,7 +149,7 @@ type MessageValidationResult* {.pure.} = enum
|
||||||
# RLN membership key and index files path
|
# RLN membership key and index files path
|
||||||
const
|
const
|
||||||
RLN_CREDENTIALS_FILEPATH* = "rlnCredentials.txt"
|
RLN_CREDENTIALS_FILEPATH* = "rlnCredentials.txt"
|
||||||
|
|
||||||
# inputs of the membership contract constructor
|
# inputs of the membership contract constructor
|
||||||
# TODO may be able to make these constants private and put them inside the waku_rln_relay_utils
|
# TODO may be able to make these constants private and put them inside the waku_rln_relay_utils
|
||||||
const
|
const
|
||||||
|
@ -111,6 +166,12 @@ const
|
||||||
# the size of poseidon hash output as the number hex digits
|
# the size of poseidon hash output as the number hex digits
|
||||||
HASH_HEX_SIZE* = int(HASH_BIT_SIZE/4)
|
HASH_HEX_SIZE* = int(HASH_BIT_SIZE/4)
|
||||||
|
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
const
|
||||||
|
# The relative folder where the circuit, proving and verification key for RLN can be found
|
||||||
|
# Note that resources has to be compiled with respect to the above MERKLE_TREE_DEPTH
|
||||||
|
RLN_RESOURCE_FOLDER* = "vendor/zerokit/rln/resources/tree_height_" & $MERKLE_TREE_DEPTH & "/"
|
||||||
|
|
||||||
# temporary variables to test waku-rln-relay performance in the static group mode
|
# temporary variables to test waku-rln-relay performance in the static group mode
|
||||||
const
|
const
|
||||||
STATIC_GROUP_SIZE* = 100
|
STATIC_GROUP_SIZE* = 100
|
||||||
|
@ -319,10 +380,20 @@ const
|
||||||
"d1ce3aea6cfb7be132d17e8d76fcbe4b7e34cef3979b4b905acfeff2f6d19724",
|
"d1ce3aea6cfb7be132d17e8d76fcbe4b7e34cef3979b4b905acfeff2f6d19724",
|
||||||
"be47b76297791f535f4b56f973a19f07ec22d4eede2a41ff23c696089938bb21")]
|
"be47b76297791f535f4b56f973a19f07ec22d4eede2a41ff23c696089938bb21")]
|
||||||
|
|
||||||
# STATIC_GROUP_MERKLE_ROOT is the root of the Merkle tree constructed from the STATIC_GROUP_KEYS above
|
|
||||||
# only identity commitments are used for the Merkle tree construction
|
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||||
# the root is created locally, using createMembershipList proc from waku_rln_relay_utils module, and the result is hardcoded in here
|
const
|
||||||
STATIC_GROUP_MERKLE_ROOT* = "a1877a553eff12e1b21632a0545a916a5c5b8060ad7cc6c69956741134397b2d"
|
# STATIC_GROUP_MERKLE_ROOT is the root of the Merkle tree constructed from the STATIC_GROUP_KEYS above
|
||||||
|
# only identity commitments are used for the Merkle tree construction
|
||||||
|
# the root is created locally, using createMembershipList proc from waku_rln_relay_utils module, and the result is hardcoded in here
|
||||||
|
STATIC_GROUP_MERKLE_ROOT* = "a1877a553eff12e1b21632a0545a916a5c5b8060ad7cc6c69956741134397b2d"
|
||||||
|
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
const
|
||||||
|
# STATIC_GROUP_MERKLE_ROOT is the root of the Merkle tree constructed from the STATIC_GROUP_KEYS above
|
||||||
|
# only identity commitments are used for the Merkle tree construction
|
||||||
|
# the root is created locally, using createMembershipList proc from waku_rln_relay_utils module, and the result is hardcoded in here
|
||||||
|
STATIC_GROUP_MERKLE_ROOT* = "9abaf9fda6af9b8237185bfd85b6e56a99ec60c3661e4c8a67f3f6c691603a2d"
|
||||||
|
|
||||||
const EPOCH_UNIT_SECONDS* = float64(10) # the rln-relay epoch length in seconds
|
const EPOCH_UNIT_SECONDS* = float64(10) # the rln-relay epoch length in seconds
|
||||||
const MAX_CLOCK_GAP_SECONDS* = 20.0 # the maximum clock difference between peers in seconds
|
const MAX_CLOCK_GAP_SECONDS* = 20.0 # the maximum clock difference between peers in seconds
|
||||||
|
@ -359,6 +430,11 @@ proc init*(T: type RateLimitProof, buffer: seq[byte]): ProtoResult[T] =
|
||||||
discard ? pb.getField(6, nullifier)
|
discard ? pb.getField(6, nullifier)
|
||||||
discard nsp.nullifier.copyFrom(nullifier)
|
discard nsp.nullifier.copyFrom(nullifier)
|
||||||
|
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
var rlnIdentifier: seq[byte]
|
||||||
|
discard ? pb.getField(7, rlnIdentifier)
|
||||||
|
discard nsp.rlnIdentifier.copyFrom(rlnIdentifier)
|
||||||
|
|
||||||
return ok(nsp)
|
return ok(nsp)
|
||||||
|
|
||||||
proc encode*(nsp: RateLimitProof): ProtoBuffer =
|
proc encode*(nsp: RateLimitProof): ProtoBuffer =
|
||||||
|
@ -371,6 +447,9 @@ proc encode*(nsp: RateLimitProof): ProtoBuffer =
|
||||||
output.write3(5, nsp.shareY)
|
output.write3(5, nsp.shareY)
|
||||||
output.write3(6, nsp.nullifier)
|
output.write3(6, nsp.nullifier)
|
||||||
|
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
output.write3(7, nsp.rlnIdentifier)
|
||||||
|
|
||||||
output.finish3()
|
output.finish3()
|
||||||
|
|
||||||
return output
|
return output
|
|
@ -24,7 +24,13 @@ import
|
||||||
logScope:
|
logScope:
|
||||||
topics = "wakurlnrelayutils"
|
topics = "wakurlnrelayutils"
|
||||||
|
|
||||||
type RLNResult* = Result[RLN[Bn256], string]
|
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||||
|
type RLNResult* = Result[RLN[Bn256], string]
|
||||||
|
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
type RLNResult* = Result[ptr RLN, string]
|
||||||
|
|
||||||
|
|
||||||
type MerkleNodeResult* = Result[MerkleNode, string]
|
type MerkleNodeResult* = Result[MerkleNode, string]
|
||||||
type RateLimitProofResult* = Result[RateLimitProof, string]
|
type RateLimitProofResult* = Result[RateLimitProof, string]
|
||||||
type SpamHandler* = proc(wakuMessage: WakuMessage): void {.gcsafe, closure,
|
type SpamHandler* = proc(wakuMessage: WakuMessage): void {.gcsafe, closure,
|
||||||
|
@ -39,76 +45,138 @@ contract(MembershipContract):
|
||||||
# proc withdraw(secret: Uint256, pubkeyIndex: Uint256, receiver: Address)
|
# proc withdraw(secret: Uint256, pubkeyIndex: Uint256, receiver: Address)
|
||||||
# proc withdrawBatch( secrets: seq[Uint256], pubkeyIndex: seq[Uint256], receiver: seq[Address])
|
# proc withdrawBatch( secrets: seq[Uint256], pubkeyIndex: seq[Uint256], receiver: seq[Address])
|
||||||
|
|
||||||
proc createRLNInstance*(d: int = MERKLE_TREE_DEPTH): RLNResult
|
proc toBuffer*(x: openArray[byte]): Buffer =
|
||||||
{.raises: [Defect, IOError].} =
|
## converts the input to a Buffer object
|
||||||
|
## the Buffer object is used to communicate data with the rln lib
|
||||||
|
var temp = @x
|
||||||
|
let output = Buffer(`ptr`: addr(temp[0]), len: uint(temp.len))
|
||||||
|
return output
|
||||||
|
|
||||||
## generates an instance of RLN
|
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||||
## An RLN instance supports both zkSNARKs logics and Merkle tree data structure and operations
|
|
||||||
## d indicates the depth of Merkle tree
|
|
||||||
var
|
|
||||||
rlnInstance: RLN[Bn256]
|
|
||||||
merkleDepth: csize_t = uint(d)
|
|
||||||
## parameters.key contains the prover and verifier keys
|
|
||||||
## to generate this file, clone this repo https://github.com/kilic/rln
|
|
||||||
## and run the following command in the root directory of the cloned project
|
|
||||||
## cargo run --example export_test_keys
|
|
||||||
## the file is generated separately and copied here
|
|
||||||
## parameters are function of tree depth and poseidon hasher
|
|
||||||
## to generate parameters for a different tree depth, change the tree size in the following line of rln library
|
|
||||||
## https://github.com/kilic/rln/blob/3bbec368a4adc68cd5f9bfae80b17e1bbb4ef373/examples/export_test_keys/main.rs#L4
|
|
||||||
## and then proceed as explained above
|
|
||||||
parameters = readFile("waku/v2/protocol/waku_rln_relay/parameters.key")
|
|
||||||
pbytes = parameters.toBytes()
|
|
||||||
len: csize_t = uint(pbytes.len)
|
|
||||||
parametersBuffer = Buffer(`ptr`: addr(pbytes[0]), len: len)
|
|
||||||
|
|
||||||
# check the parameters.key is not empty
|
proc createRLNInstance*(d: int = MERKLE_TREE_DEPTH): RLNResult
|
||||||
if (pbytes.len == 0):
|
{.raises: [Defect, IOError].} =
|
||||||
debug "error in parameters.key"
|
|
||||||
return err("error in parameters.key")
|
|
||||||
|
|
||||||
# create an instance of RLN
|
## generates an instance of RLN
|
||||||
let res = new_circuit_from_params(merkleDepth, addr parametersBuffer,
|
## An RLN instance supports both zkSNARKs logics and Merkle tree data structure and operations
|
||||||
addr rlnInstance)
|
## d indicates the depth of Merkle tree
|
||||||
# check whether the circuit parameters are generated successfully
|
var
|
||||||
if (res == false):
|
rlnInstance: RLN[Bn256]
|
||||||
debug "error in parameters generation"
|
merkleDepth: csize_t = uint(d)
|
||||||
return err("error in parameters generation")
|
## parameters.key contains the prover and verifier keys
|
||||||
return ok(rlnInstance)
|
## to generate this file, clone this repo https://github.com/kilic/rln
|
||||||
|
## and run the following command in the root directory of the cloned project
|
||||||
|
## cargo run --example export_test_keys
|
||||||
|
## the file is generated separately and copied here
|
||||||
|
## parameters are function of tree depth and poseidon hasher
|
||||||
|
## to generate parameters for a different tree depth, change the tree size in the following line of rln library
|
||||||
|
## https://github.com/kilic/rln/blob/3bbec368a4adc68cd5f9bfae80b17e1bbb4ef373/examples/export_test_keys/main.rs#L4
|
||||||
|
## and then proceed as explained above
|
||||||
|
parameters = readFile("waku/v2/protocol/waku_rln_relay/parameters.key")
|
||||||
|
pbytes = parameters.toBytes()
|
||||||
|
len: csize_t = uint(pbytes.len)
|
||||||
|
parametersBuffer = Buffer(`ptr`: addr(pbytes[0]), len: len)
|
||||||
|
|
||||||
|
# check the parameters.key is not empty
|
||||||
proc membershipKeyGen*(ctxPtr: RLN[Bn256]): Option[MembershipKeyPair] =
|
if (pbytes.len == 0):
|
||||||
## generates a MembershipKeyPair that can be used for the registration into the rln membership contract
|
debug "error in parameters.key"
|
||||||
|
return err("error in parameters.key")
|
||||||
|
|
||||||
# keysBufferPtr will hold the generated key pairs i.e., secret and public keys
|
# create an instance of RLN
|
||||||
var
|
let res = new_circuit_from_params(merkleDepth, addr parametersBuffer,
|
||||||
keysBuffer: Buffer
|
addr rlnInstance)
|
||||||
keysBufferPtr = addr(keysBuffer)
|
# check whether the circuit parameters are generated successfully
|
||||||
done = key_gen(ctxPtr, keysBufferPtr)
|
if (res == false):
|
||||||
|
debug "error in parameters generation"
|
||||||
|
return err("error in parameters generation")
|
||||||
|
return ok(rlnInstance)
|
||||||
|
|
||||||
# check whether the keys are generated successfully
|
|
||||||
if(done == false):
|
proc membershipKeyGen*(ctxPtr: RLN[Bn256]): Option[MembershipKeyPair] =
|
||||||
debug "error in key generation"
|
## generates a MembershipKeyPair that can be used for the registration into the rln membership contract
|
||||||
return none(MembershipKeyPair)
|
|
||||||
|
|
||||||
var generatedKeys = cast[ptr array[64, byte]](keysBufferPtr.`ptr`)[]
|
# keysBufferPtr will hold the generated key pairs i.e., secret and public keys
|
||||||
# the public and secret keys together are 64 bytes
|
var
|
||||||
if (generatedKeys.len != 64):
|
keysBuffer: Buffer
|
||||||
debug "the generated keys are invalid"
|
keysBufferPtr = addr(keysBuffer)
|
||||||
return none(MembershipKeyPair)
|
done = key_gen(ctxPtr, keysBufferPtr)
|
||||||
|
|
||||||
# TODO define a separate proc to decode the generated keys to the secret and public components
|
# check whether the keys are generated successfully
|
||||||
var
|
if(done == false):
|
||||||
secret: array[32, byte]
|
debug "error in key generation"
|
||||||
public: array[32, byte]
|
return none(MembershipKeyPair)
|
||||||
for (i, x) in secret.mpairs: x = generatedKeys[i]
|
|
||||||
for (i, x) in public.mpairs: x = generatedKeys[i+32]
|
|
||||||
|
|
||||||
var
|
var generatedKeys = cast[ptr array[64, byte]](keysBufferPtr.`ptr`)[]
|
||||||
keypair = MembershipKeyPair(idKey: secret, idCommitment: public)
|
# the public and secret keys together are 64 bytes
|
||||||
|
if (generatedKeys.len != 64):
|
||||||
|
debug "the generated keys are invalid"
|
||||||
|
return none(MembershipKeyPair)
|
||||||
|
|
||||||
|
# TODO define a separate proc to decode the generated keys to the secret and public components
|
||||||
|
var
|
||||||
|
secret: array[32, byte]
|
||||||
|
public: array[32, byte]
|
||||||
|
for (i, x) in secret.mpairs: x = generatedKeys[i]
|
||||||
|
for (i, x) in public.mpairs: x = generatedKeys[i+32]
|
||||||
|
|
||||||
|
var
|
||||||
|
keypair = MembershipKeyPair(idKey: secret, idCommitment: public)
|
||||||
|
|
||||||
|
|
||||||
return some(keypair)
|
return some(keypair)
|
||||||
|
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
proc createRLNInstance*(d: int = MERKLE_TREE_DEPTH): RLNResult
|
||||||
|
{.raises: [Defect, IOError].} =
|
||||||
|
|
||||||
|
## generates an instance of RLN
|
||||||
|
## An RLN instance supports both zkSNARKs logics and Merkle tree data structure and operations
|
||||||
|
## d indicates the depth of Merkle tree
|
||||||
|
var
|
||||||
|
rlnInstance: ptr RLN
|
||||||
|
merkleDepth: csize_t = uint(d)
|
||||||
|
resourcesPathBuffer = RLN_RESOURCE_FOLDER.toOpenArrayByte(0, RLN_RESOURCE_FOLDER.high).toBuffer()
|
||||||
|
|
||||||
|
# create an instance of RLN
|
||||||
|
let res = new_circuit(merkleDepth, addr resourcesPathBuffer, addr rlnInstance)
|
||||||
|
# check whether the circuit parameters are generated successfully
|
||||||
|
if (res == false):
|
||||||
|
debug "error in parameters generation"
|
||||||
|
return err("error in parameters generation")
|
||||||
|
return ok(rlnInstance)
|
||||||
|
|
||||||
|
|
||||||
|
proc membershipKeyGen*(ctxPtr: ptr RLN): Option[MembershipKeyPair] =
|
||||||
|
## generates a MembershipKeyPair that can be used for the registration into the rln membership contract
|
||||||
|
|
||||||
|
# keysBufferPtr will hold the generated key pairs i.e., secret and public keys
|
||||||
|
var
|
||||||
|
keysBuffer: Buffer
|
||||||
|
keysBufferPtr = addr(keysBuffer)
|
||||||
|
done = key_gen(ctxPtr, keysBufferPtr)
|
||||||
|
|
||||||
|
# check whether the keys are generated successfully
|
||||||
|
if(done == false):
|
||||||
|
debug "error in key generation"
|
||||||
|
return none(MembershipKeyPair)
|
||||||
|
|
||||||
|
var generatedKeys = cast[ptr array[64, byte]](keysBufferPtr.`ptr`)[]
|
||||||
|
# the public and secret keys together are 64 bytes
|
||||||
|
if (generatedKeys.len != 64):
|
||||||
|
debug "the generated keys are invalid"
|
||||||
|
return none(MembershipKeyPair)
|
||||||
|
|
||||||
|
# TODO define a separate proc to decode the generated keys to the secret and public components
|
||||||
|
var
|
||||||
|
secret: array[32, byte]
|
||||||
|
public: array[32, byte]
|
||||||
|
for (i, x) in secret.mpairs: x = generatedKeys[i]
|
||||||
|
for (i, x) in public.mpairs: x = generatedKeys[i+32]
|
||||||
|
|
||||||
|
var
|
||||||
|
keypair = MembershipKeyPair(idKey: secret, idCommitment: public)
|
||||||
|
|
||||||
|
return some(keypair)
|
||||||
|
|
||||||
proc toUInt256*(idCommitment: IDCommitment): UInt256 =
|
proc toUInt256*(idCommitment: IDCommitment): UInt256 =
|
||||||
let pk = UInt256.fromBytesBE(idCommitment)
|
let pk = UInt256.fromBytesBE(idCommitment)
|
||||||
|
@ -188,27 +256,37 @@ proc appendLength*(input: openArray[byte]): seq[byte] =
|
||||||
output = concat(@len, @input)
|
output = concat(@len, @input)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
proc toBuffer*(x: openArray[byte]): Buffer =
|
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||||
## converts the input to a Buffer object
|
proc hash*(rlnInstance: RLN[Bn256], data: openArray[byte]): MerkleNode =
|
||||||
## the Buffer object is used to communicate data with the rln lib
|
## a thin layer on top of the Nim wrapper of the Poseidon hasher
|
||||||
var temp = @x
|
debug "hash input", hashhex = data.toHex()
|
||||||
let output = Buffer(`ptr`: addr(temp[0]), len: uint(temp.len))
|
var lenPrefData = appendLength(data)
|
||||||
return output
|
var
|
||||||
|
hashInputBuffer = lenPrefData.toBuffer()
|
||||||
|
outputBuffer: Buffer # will holds the hash output
|
||||||
|
|
||||||
proc hash*(rlnInstance: RLN[Bn256], data: openArray[byte]): MerkleNode =
|
debug "hash input buffer length", bufflen = hashInputBuffer.len
|
||||||
## a thin layer on top of the Nim wrapper of the Poseidon hasher
|
let
|
||||||
debug "hash input", hashhex = data.toHex()
|
hashSuccess = hash(rlnInstance, addr hashInputBuffer, addr outputBuffer)
|
||||||
var lenPrefData = appendLength(data)
|
output = cast[ptr MerkleNode](outputBuffer.`ptr`)[]
|
||||||
var
|
|
||||||
hashInputBuffer = lenPrefData.toBuffer()
|
|
||||||
outputBuffer: Buffer # will holds the hash output
|
|
||||||
|
|
||||||
debug "hash input buffer length", bufflen = hashInputBuffer.len
|
return output
|
||||||
let
|
|
||||||
hashSuccess = hash(rlnInstance, addr hashInputBuffer, addr outputBuffer)
|
|
||||||
output = cast[ptr MerkleNode](outputBuffer.`ptr`)[]
|
|
||||||
|
|
||||||
return output
|
when defined(rlnzerokit):
|
||||||
|
proc hash*(rlnInstance: ptr RLN, data: openArray[byte]): MerkleNode =
|
||||||
|
## a thin layer on top of the Nim wrapper of the Poseidon hasher
|
||||||
|
debug "hash input", hashhex = data.toHex()
|
||||||
|
var lenPrefData = appendLength(data)
|
||||||
|
var
|
||||||
|
hashInputBuffer = lenPrefData.toBuffer()
|
||||||
|
outputBuffer: Buffer # will holds the hash output
|
||||||
|
|
||||||
|
debug "hash input buffer length", bufflen = hashInputBuffer.len
|
||||||
|
let
|
||||||
|
hashSuccess = hash(rlnInstance, addr hashInputBuffer, addr outputBuffer)
|
||||||
|
output = cast[ptr MerkleNode](outputBuffer.`ptr`)[]
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
proc serialize(idKey: IDKey, memIndex: MembershipIndex, epoch: Epoch,
|
proc serialize(idKey: IDKey, memIndex: MembershipIndex, epoch: Epoch,
|
||||||
msg: openArray[byte]): seq[byte] =
|
msg: openArray[byte]): seq[byte] =
|
||||||
|
@ -221,117 +299,232 @@ proc serialize(idKey: IDKey, memIndex: MembershipIndex, epoch: Epoch,
|
||||||
let output = concat(@idKey, @memIndexBytes, @epoch, lenPrefMsg)
|
let output = concat(@idKey, @memIndexBytes, @epoch, lenPrefMsg)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
proc proofGen*(rlnInstance: RLN[Bn256], data: openArray[byte],
|
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||||
memKeys: MembershipKeyPair, memIndex: MembershipIndex,
|
proc proofGen*(rlnInstance: RLN[Bn256], data: openArray[byte],
|
||||||
epoch: Epoch): RateLimitProofResult =
|
memKeys: MembershipKeyPair, memIndex: MembershipIndex,
|
||||||
|
epoch: Epoch): RateLimitProofResult =
|
||||||
|
|
||||||
# serialize inputs
|
# serialize inputs
|
||||||
let serializedInputs = serialize(idKey = memKeys.idKey,
|
let serializedInputs = serialize(idKey = memKeys.idKey,
|
||||||
memIndex = memIndex,
|
memIndex = memIndex,
|
||||||
epoch = epoch,
|
epoch = epoch,
|
||||||
msg = data)
|
msg = data)
|
||||||
var inputBuffer = toBuffer(serializedInputs)
|
var inputBuffer = toBuffer(serializedInputs)
|
||||||
|
|
||||||
debug "input buffer ", inputBuffer
|
debug "input buffer ", inputBuffer
|
||||||
|
|
||||||
# generate the proof
|
# generate the proof
|
||||||
var proof: Buffer
|
var proof: Buffer
|
||||||
let proofIsSuccessful = generate_proof(rlnInstance, addr inputBuffer, addr proof)
|
let proofIsSuccessful = generate_proof(rlnInstance, addr inputBuffer, addr proof)
|
||||||
# check whether the generate_proof call is done successfully
|
# check whether the generate_proof call is done successfully
|
||||||
if not proofIsSuccessful:
|
if not proofIsSuccessful:
|
||||||
return err("could not generate the proof")
|
return err("could not generate the proof")
|
||||||
|
|
||||||
var proofValue = cast[ptr array[416, byte]] (proof.`ptr`)
|
var proofValue = cast[ptr array[416, byte]] (proof.`ptr`)
|
||||||
let proofBytes: array[416, byte] = proofValue[]
|
let proofBytes: array[416, byte] = proofValue[]
|
||||||
debug "proof content", proofHex = proofValue[].toHex
|
debug "proof content", proofHex = proofValue[].toHex
|
||||||
|
|
||||||
## parse the proof as |zkSNARKs<256>|root<32>|epoch<32>|share_x<32>|share_y<32>|nullifier<32>|
|
## parse the proof as |zkSNARKs<256>|root<32>|epoch<32>|share_x<32>|share_y<32>|nullifier<32>|
|
||||||
let
|
let
|
||||||
proofOffset = 256
|
proofOffset = 256
|
||||||
rootOffset = proofOffset + 32
|
rootOffset = proofOffset + 32
|
||||||
epochOffset = rootOffset + 32
|
epochOffset = rootOffset + 32
|
||||||
shareXOffset = epochOffset + 32
|
shareXOffset = epochOffset + 32
|
||||||
shareYOffset = shareXOffset + 32
|
shareYOffset = shareXOffset + 32
|
||||||
nullifierOffset = shareYOffset + 32
|
nullifierOffset = shareYOffset + 32
|
||||||
|
|
||||||
var
|
var
|
||||||
zkproof: ZKSNARK
|
zkproof: ZKSNARK
|
||||||
proofRoot, shareX, shareY: MerkleNode
|
proofRoot, shareX, shareY: MerkleNode
|
||||||
epoch: Epoch
|
epoch: Epoch
|
||||||
nullifier: Nullifier
|
nullifier: Nullifier
|
||||||
|
|
||||||
discard zkproof.copyFrom(proofBytes[0..proofOffset-1])
|
discard zkproof.copyFrom(proofBytes[0..proofOffset-1])
|
||||||
discard proofRoot.copyFrom(proofBytes[proofOffset..rootOffset-1])
|
discard proofRoot.copyFrom(proofBytes[proofOffset..rootOffset-1])
|
||||||
discard epoch.copyFrom(proofBytes[rootOffset..epochOffset-1])
|
discard epoch.copyFrom(proofBytes[rootOffset..epochOffset-1])
|
||||||
discard shareX.copyFrom(proofBytes[epochOffset..shareXOffset-1])
|
discard shareX.copyFrom(proofBytes[epochOffset..shareXOffset-1])
|
||||||
discard shareY.copyFrom(proofBytes[shareXOffset..shareYOffset-1])
|
discard shareY.copyFrom(proofBytes[shareXOffset..shareYOffset-1])
|
||||||
discard nullifier.copyFrom(proofBytes[shareYOffset..nullifierOffset-1])
|
discard nullifier.copyFrom(proofBytes[shareYOffset..nullifierOffset-1])
|
||||||
|
|
||||||
let output = RateLimitProof(proof: zkproof,
|
let output = RateLimitProof(proof: zkproof,
|
||||||
merkleRoot: proofRoot,
|
merkleRoot: proofRoot,
|
||||||
epoch: epoch,
|
epoch: epoch,
|
||||||
shareX: shareX,
|
shareX: shareX,
|
||||||
shareY: shareY,
|
shareY: shareY,
|
||||||
nullifier: nullifier)
|
nullifier: nullifier)
|
||||||
|
|
||||||
return ok(output)
|
return ok(output)
|
||||||
|
|
||||||
proc serialize(proof: RateLimitProof, data: openArray[byte]): seq[byte] =
|
proc serialize(proof: RateLimitProof, data: openArray[byte]): seq[byte] =
|
||||||
## a private proc to convert RateLimitProof and data to a byte seq
|
## a private proc to convert RateLimitProof and data to a byte seq
|
||||||
## this conversion is used in the proof verification proc
|
## this conversion is used in the proof verification proc
|
||||||
## the order of serialization is based on https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L205
|
## the order of serialization is based on https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L205
|
||||||
## [ proof<256>| root<32>| epoch<32>| share_x<32>| share_y<32>| nullifier<32> | signal_len<8> | signal<var> ]
|
## [ proof<256>| root<32>| epoch<32>| share_x<32>| share_y<32>| nullifier<32> | signal_len<8> | signal<var> ]
|
||||||
let lenPrefMsg = appendLength(@data)
|
let lenPrefMsg = appendLength(@data)
|
||||||
var proofBytes = concat(@(proof.proof),
|
var proofBytes = concat(@(proof.proof),
|
||||||
@(proof.merkleRoot),
|
@(proof.merkleRoot),
|
||||||
@(proof.epoch),
|
@(proof.epoch),
|
||||||
@(proof.shareX),
|
@(proof.shareX),
|
||||||
@(proof.shareY),
|
@(proof.shareY),
|
||||||
@(proof.nullifier),
|
@(proof.nullifier),
|
||||||
lenPrefMsg)
|
lenPrefMsg)
|
||||||
|
|
||||||
return proofBytes
|
return proofBytes
|
||||||
|
|
||||||
proc proofVerify*(rlnInstance: RLN[Bn256], data: openArray[byte],
|
proc proofVerify*(rlnInstance: RLN[Bn256], data: openArray[byte],
|
||||||
proof: RateLimitProof): bool =
|
proof: RateLimitProof): bool =
|
||||||
var
|
var
|
||||||
proofBytes = serialize(proof, data)
|
proofBytes = serialize(proof, data)
|
||||||
proofBuffer = proofBytes.toBuffer()
|
proofBuffer = proofBytes.toBuffer()
|
||||||
f = 0.uint32
|
f = 0.uint32
|
||||||
trace "serialized proof", proof = proofBytes.toHex()
|
trace "serialized proof", proof = proofBytes.toHex()
|
||||||
|
|
||||||
let verifyIsSuccessful = verify(rlnInstance, addr proofBuffer, addr f)
|
let verifyIsSuccessful = verify(rlnInstance, addr proofBuffer, addr f)
|
||||||
if not verifyIsSuccessful:
|
if not verifyIsSuccessful:
|
||||||
# something went wrong in verification
|
# something went wrong in verification
|
||||||
|
return false
|
||||||
|
# f = 0 means the proof is verified
|
||||||
|
if f == 0:
|
||||||
|
return true
|
||||||
return false
|
return false
|
||||||
# f = 0 means the proof is verified
|
|
||||||
if f == 0:
|
|
||||||
return true
|
|
||||||
return false
|
|
||||||
|
|
||||||
proc insertMember*(rlnInstance: RLN[Bn256], idComm: IDCommitment): bool =
|
proc insertMember*(rlnInstance: RLN[Bn256], idComm: IDCommitment): bool =
|
||||||
var pkBuffer = toBuffer(idComm)
|
var pkBuffer = toBuffer(idComm)
|
||||||
let pkBufferPtr = addr pkBuffer
|
let pkBufferPtr = addr pkBuffer
|
||||||
|
|
||||||
# add the member to the tree
|
# add the member to the tree
|
||||||
var member_is_added = update_next_member(rlnInstance, pkBufferPtr)
|
var member_is_added = update_next_member(rlnInstance, pkBufferPtr)
|
||||||
return member_is_added
|
return member_is_added
|
||||||
|
|
||||||
proc removeMember*(rlnInstance: RLN[Bn256], index: MembershipIndex): bool =
|
proc removeMember*(rlnInstance: RLN[Bn256], index: MembershipIndex): bool =
|
||||||
let deletion_success = delete_member(rlnInstance, index)
|
let deletion_success = delete_member(rlnInstance, index)
|
||||||
return deletion_success
|
return deletion_success
|
||||||
|
|
||||||
proc getMerkleRoot*(rlnInstance: RLN[Bn256]): MerkleNodeResult =
|
proc getMerkleRoot*(rlnInstance: RLN[Bn256]): MerkleNodeResult =
|
||||||
# read the Merkle Tree root after insertion
|
# read the Merkle Tree root after insertion
|
||||||
var
|
var
|
||||||
root {.noinit.}: Buffer = Buffer()
|
root {.noinit.}: Buffer = Buffer()
|
||||||
rootPtr = addr(root)
|
rootPtr = addr(root)
|
||||||
get_root_successful = get_root(rlnInstance, rootPtr)
|
get_root_successful = get_root(rlnInstance, rootPtr)
|
||||||
if (not get_root_successful): return err("could not get the root")
|
if (not get_root_successful): return err("could not get the root")
|
||||||
if (not (root.len == 32)): return err("wrong output size")
|
if (not (root.len == 32)): return err("wrong output size")
|
||||||
|
|
||||||
var rootValue = cast[ptr MerkleNode] (root.`ptr`)[]
|
var rootValue = cast[ptr MerkleNode] (root.`ptr`)[]
|
||||||
return ok(rootValue)
|
return ok(rootValue)
|
||||||
|
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
proc proofGen*(rlnInstance: ptr RLN, data: openArray[byte],
|
||||||
|
memKeys: MembershipKeyPair, memIndex: MembershipIndex,
|
||||||
|
epoch: Epoch): RateLimitProofResult =
|
||||||
|
|
||||||
|
# serialize inputs
|
||||||
|
let serializedInputs = serialize(idKey = memKeys.idKey,
|
||||||
|
memIndex = memIndex,
|
||||||
|
epoch = epoch,
|
||||||
|
msg = data)
|
||||||
|
var inputBuffer = toBuffer(serializedInputs)
|
||||||
|
|
||||||
|
debug "input buffer ", inputBuffer
|
||||||
|
|
||||||
|
# generate the proof
|
||||||
|
var proof: Buffer
|
||||||
|
let proofIsSuccessful = generate_proof(rlnInstance, addr inputBuffer, addr proof)
|
||||||
|
# check whether the generate_proof call is done successfully
|
||||||
|
if not proofIsSuccessful:
|
||||||
|
return err("could not generate the proof")
|
||||||
|
|
||||||
|
var proofValue = cast[ptr array[320, byte]] (proof.`ptr`)
|
||||||
|
let proofBytes: array[320, byte] = proofValue[]
|
||||||
|
debug "proof content", proofHex = proofValue[].toHex
|
||||||
|
|
||||||
|
## parse the proof as [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ]
|
||||||
|
|
||||||
|
let
|
||||||
|
proofOffset = 128
|
||||||
|
rootOffset = proofOffset + 32
|
||||||
|
epochOffset = rootOffset + 32
|
||||||
|
shareXOffset = epochOffset + 32
|
||||||
|
shareYOffset = shareXOffset + 32
|
||||||
|
nullifierOffset = shareYOffset + 32
|
||||||
|
rlnIdentifierOffset = nullifierOffset + 32
|
||||||
|
|
||||||
|
var
|
||||||
|
zkproof: ZKSNARK
|
||||||
|
proofRoot, shareX, shareY: MerkleNode
|
||||||
|
epoch: Epoch
|
||||||
|
nullifier: Nullifier
|
||||||
|
rlnIdentifier: RlnIdentifier
|
||||||
|
|
||||||
|
discard zkproof.copyFrom(proofBytes[0..proofOffset-1])
|
||||||
|
discard proofRoot.copyFrom(proofBytes[proofOffset..rootOffset-1])
|
||||||
|
discard epoch.copyFrom(proofBytes[rootOffset..epochOffset-1])
|
||||||
|
discard shareX.copyFrom(proofBytes[epochOffset..shareXOffset-1])
|
||||||
|
discard shareY.copyFrom(proofBytes[shareXOffset..shareYOffset-1])
|
||||||
|
discard nullifier.copyFrom(proofBytes[shareYOffset..nullifierOffset-1])
|
||||||
|
discard rlnIdentifier.copyFrom(proofBytes[nullifierOffset..rlnIdentifierOffset-1])
|
||||||
|
|
||||||
|
let output = RateLimitProof(proof: zkproof,
|
||||||
|
merkleRoot: proofRoot,
|
||||||
|
epoch: epoch,
|
||||||
|
shareX: shareX,
|
||||||
|
shareY: shareY,
|
||||||
|
nullifier: nullifier,
|
||||||
|
rlnIdentifier: rlnIdentifier)
|
||||||
|
|
||||||
|
return ok(output)
|
||||||
|
|
||||||
|
proc serialize(proof: RateLimitProof, data: openArray[byte]): seq[byte] =
|
||||||
|
## a private proc to convert RateLimitProof and data to a byte seq
|
||||||
|
## this conversion is used in the proof verification proc
|
||||||
|
## [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> | signal_len<8> | signal<var> ]
|
||||||
|
let lenPrefMsg = appendLength(@data)
|
||||||
|
var proofBytes = concat(@(proof.proof),
|
||||||
|
@(proof.merkleRoot),
|
||||||
|
@(proof.epoch),
|
||||||
|
@(proof.shareX),
|
||||||
|
@(proof.shareY),
|
||||||
|
@(proof.nullifier),
|
||||||
|
@(proof.rlnIdentifier),
|
||||||
|
lenPrefMsg)
|
||||||
|
|
||||||
|
return proofBytes
|
||||||
|
|
||||||
|
proc proofVerify*(rlnInstance: ptr RLN, data: openArray[byte], proof: RateLimitProof): bool =
|
||||||
|
var
|
||||||
|
proofBytes = serialize(proof, data)
|
||||||
|
proofBuffer = proofBytes.toBuffer()
|
||||||
|
proof_is_valid: bool
|
||||||
|
trace "serialized proof", proof = proofBytes.toHex()
|
||||||
|
|
||||||
|
let verifyIsSuccessful = verify(rlnInstance, addr proofBuffer, addr proof_is_valid)
|
||||||
|
if not verifyIsSuccessful:
|
||||||
|
# something went wrong in verification call
|
||||||
|
return false
|
||||||
|
return proof_is_valid
|
||||||
|
|
||||||
|
proc insertMember*(rlnInstance: ptr RLN, idComm: IDCommitment): bool =
|
||||||
|
var pkBuffer = toBuffer(idComm)
|
||||||
|
let pkBufferPtr = addr pkBuffer
|
||||||
|
|
||||||
|
# add the member to the tree
|
||||||
|
var member_is_added = update_next_member(rlnInstance, pkBufferPtr)
|
||||||
|
return member_is_added
|
||||||
|
|
||||||
|
proc removeMember*(rlnInstance: ptr RLN, index: MembershipIndex): bool =
|
||||||
|
let deletion_success = delete_member(rlnInstance, index)
|
||||||
|
return deletion_success
|
||||||
|
|
||||||
|
proc getMerkleRoot*(rlnInstance: ptr RLN): MerkleNodeResult =
|
||||||
|
# read the Merkle Tree root after insertion
|
||||||
|
var
|
||||||
|
root {.noinit.}: Buffer = Buffer()
|
||||||
|
rootPtr = addr(root)
|
||||||
|
get_root_successful = get_root(rlnInstance, rootPtr)
|
||||||
|
if (not get_root_successful): return err("could not get the root")
|
||||||
|
if (not (root.len == 32)): return err("wrong output size")
|
||||||
|
|
||||||
|
var rootValue = cast[ptr MerkleNode] (root.`ptr`)[]
|
||||||
|
return ok(rootValue)
|
||||||
|
|
||||||
proc toMembershipKeyPairs*(groupKeys: seq[(string, string)]): seq[
|
proc toMembershipKeyPairs*(groupKeys: seq[(string, string)]): seq[
|
||||||
MembershipKeyPair] {.raises: [Defect, ValueError].} =
|
MembershipKeyPair] {.raises: [Defect, ValueError].} =
|
||||||
|
@ -601,14 +794,25 @@ proc appendRLNProof*(rlnPeer: WakuRLNRelay, msg: var WakuMessage,
|
||||||
msg.proof = proof.value
|
msg.proof = proof.value
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc addAll*(rlnInstance: RLN[Bn256], list: seq[IDCommitment]): bool =
|
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||||
# add members to the Merkle tree of the `rlnInstance`
|
proc addAll*(rlnInstance: RLN[Bn256], list: seq[IDCommitment]): bool =
|
||||||
for i in 0..list.len-1:
|
# add members to the Merkle tree of the `rlnInstance`
|
||||||
let member = list[i]
|
for i in 0..list.len-1:
|
||||||
let member_is_added = rlnInstance.insertMember(member)
|
let member = list[i]
|
||||||
if not member_is_added:
|
let member_is_added = rlnInstance.insertMember(member)
|
||||||
return false
|
if not member_is_added:
|
||||||
return true
|
return false
|
||||||
|
return true
|
||||||
|
|
||||||
|
when defined(rlnzerokit):
|
||||||
|
proc addAll*(rlnInstance: ptr RLN, list: seq[IDCommitment]): bool =
|
||||||
|
# add members to the Merkle tree of the `rlnInstance`
|
||||||
|
for i in 0..list.len-1:
|
||||||
|
let member = list[i]
|
||||||
|
let member_is_added = rlnInstance.insertMember(member)
|
||||||
|
if not member_is_added:
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
|
||||||
# the types of inputs to this handler matches the MemberRegistered event/proc defined in the MembershipContract interface
|
# the types of inputs to this handler matches the MemberRegistered event/proc defined in the MembershipContract interface
|
||||||
type RegistrationEventHandler = proc(pubkey: Uint256, index: Uint256): void {.gcsafe, closure, raises: [Defect].}
|
type RegistrationEventHandler = proc(pubkey: Uint256, index: Uint256): void {.gcsafe, closure, raises: [Defect].}
|
||||||
|
@ -644,6 +848,7 @@ proc handleGroupUpdates*(rlnPeer: WakuRLNRelay, handler: RegistrationEventHandle
|
||||||
# mounts the supplied handler for the registration events emitting from the membership contract
|
# mounts the supplied handler for the registration events emitting from the membership contract
|
||||||
await subscribeToGroupEvents(ethClientUri = rlnPeer.ethClientAddress, ethAccountAddress = rlnPeer.ethAccountAddress, contractAddress = rlnPeer.membershipContractAddress, handler = handler)
|
await subscribeToGroupEvents(ethClientUri = rlnPeer.ethClientAddress, ethAccountAddress = rlnPeer.ethAccountAddress, contractAddress = rlnPeer.membershipContractAddress, handler = handler)
|
||||||
|
|
||||||
|
|
||||||
proc addRLNRelayValidator*(node: WakuNode, pubsubTopic: string, contentTopic: ContentTopic, spamHandler: Option[SpamHandler] = none(SpamHandler)) =
|
proc addRLNRelayValidator*(node: WakuNode, pubsubTopic: string, contentTopic: ContentTopic, spamHandler: Option[SpamHandler] = none(SpamHandler)) =
|
||||||
## this procedure is a thin wrapper for the pubsub addValidator method
|
## this procedure is a thin wrapper for the pubsub addValidator method
|
||||||
## it sets a validator for the waku messages published on the supplied pubsubTopic and contentTopic
|
## it sets a validator for the waku messages published on the supplied pubsubTopic and contentTopic
|
||||||
|
@ -904,4 +1109,4 @@ proc mountRlnRelay*(node: WakuNode, conf: WakuNodeConf|Chat2Conf, spamHandler: O
|
||||||
info "no rln credential is provided"
|
info "no rln credential is provided"
|
||||||
waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr,
|
waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr,
|
||||||
ethAccAddr = ethAccountAddr, ethAccountPrivKeyOpt = ethAccountPrivKeyOpt, pubsubTopic = conf.rlnRelayPubsubTopic,
|
ethAccAddr = ethAccountAddr, ethAccountPrivKeyOpt = ethAccountPrivKeyOpt, pubsubTopic = conf.rlnRelayPubsubTopic,
|
||||||
contentTopic = conf.rlnRelayContentTopic, spamHandler = spamHandler)
|
contentTopic = conf.rlnRelayContentTopic, spamHandler = spamHandler)
|
Loading…
Reference in New Issue