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
|
||||
ignore = untracked
|
||||
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
|
||||
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
|
||||
ifeq ($(OS),Windows_NT) # is Windows_NT on XP, 2000, 7, Vista, 10...
|
||||
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)"
|
||||
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)
|
||||
deps: | libbacktrace
|
||||
endif
|
||||
|
@ -173,6 +181,15 @@ else ifeq ($(CI), true)
|
|||
cargo build --manifest-path vendor/rln/Cargo.toml
|
||||
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
|
||||
echo -e $(BUILD_MSG) "build/$@" && \
|
||||
$(ENV_SCRIPT) nim test2 $(NIM_PARAMS) waku.nims
|
||||
|
@ -228,11 +245,16 @@ ifneq ($(USE_LIBBACKTRACE), 0)
|
|||
+ $(MAKE) -C vendor/nim-libbacktrace clean $(HANDLE_OUTPUT)
|
||||
endif
|
||||
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)
|
||||
cleanrln:
|
||||
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
|
||||
|
||||
libwaku.so: | build deps
|
||||
|
|
|
@ -31,7 +31,7 @@ import
|
|||
./v2/test_peer_exchange,
|
||||
./v2/test_waku_noise
|
||||
|
||||
when defined(rln):
|
||||
when defined(rln) or defined(rlnzerokit):
|
||||
import ./v2/test_waku_rln_relay
|
||||
when defined(onchain_rln):
|
||||
import ./v2/test_waku_rln_relay_onchain
|
||||
|
|
|
@ -64,45 +64,72 @@ procSuite "Waku rln relay":
|
|||
await node.stop()
|
||||
|
||||
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
|
||||
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`)[]
|
||||
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||
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:
|
||||
# the public and secret keys together are 64 bytes
|
||||
generatedKeys.len == 64
|
||||
debug "generated keys: ", generatedKeys
|
||||
# check the parameters.key is not empty
|
||||
pbytes.len != 0
|
||||
|
||||
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":
|
||||
# create an RLN instance
|
||||
|
@ -371,8 +398,14 @@ suite "Waku rln relay":
|
|||
check:
|
||||
hashSuccess
|
||||
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()
|
||||
|
||||
var
|
||||
|
@ -392,8 +425,14 @@ suite "Waku rln relay":
|
|||
let msg = "Hello".toBytes()
|
||||
|
||||
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()
|
||||
|
||||
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
|
||||
root == STATIC_GROUP_MERKLE_ROOT
|
||||
|
||||
test "RateLimitProof Protobuf encode/init test":
|
||||
var
|
||||
proof: ZKSNARK
|
||||
merkleRoot: MerkleNode
|
||||
epoch: Epoch
|
||||
shareX: MerkleNode
|
||||
shareY: MerkleNode
|
||||
nullifier: Nullifier
|
||||
# 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
|
||||
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||
test "RateLimitProof Protobuf encode/init test":
|
||||
var
|
||||
proof: ZKSNARK
|
||||
merkleRoot: MerkleNode
|
||||
epoch: Epoch
|
||||
shareX: MerkleNode
|
||||
shareY: MerkleNode
|
||||
nullifier: Nullifier
|
||||
# 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
|
||||
|
||||
let
|
||||
rateLimitProof = RateLimitProof(proof: proof,
|
||||
merkleRoot: merkleRoot,
|
||||
epoch: epoch,
|
||||
shareX: shareX,
|
||||
shareY: shareY,
|
||||
nullifier: nullifier)
|
||||
protobuf = rateLimitProof.encode()
|
||||
decodednsp = RateLimitProof.init(protobuf.buffer)
|
||||
let
|
||||
rateLimitProof = RateLimitProof(proof: proof,
|
||||
merkleRoot: merkleRoot,
|
||||
epoch: epoch,
|
||||
shareX: shareX,
|
||||
shareY: shareY,
|
||||
nullifier: nullifier)
|
||||
protobuf = rateLimitProof.encode()
|
||||
decodednsp = RateLimitProof.init(protobuf.buffer)
|
||||
|
||||
check:
|
||||
decodednsp.isErr == false
|
||||
decodednsp.value == rateLimitProof
|
||||
check:
|
||||
decodednsp.isErr == false
|
||||
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":
|
||||
var rlnInstance = createRLNInstance()
|
||||
|
@ -600,6 +675,7 @@ suite "Waku rln relay":
|
|||
for index, x in shareX3.mpairs: shareX3[index] = 3
|
||||
let shareY3 = shareX3
|
||||
|
||||
## TODO: when zerokit rln is integrated, RateLimitProof should be initialized passing a rlnIdentifier too (now implicitely set to 0)
|
||||
let
|
||||
wm1 = WakuMessage(proof: RateLimitProof(epoch: epoch,
|
||||
nullifier: nullifier1, shareX: shareX1, shareY: shareY1))
|
||||
|
@ -679,6 +755,8 @@ suite "Waku rln relay":
|
|||
proofAdded3 = wakuRlnRelay.appendRLNProof(wm3, time+EPOCH_UNIT_SECONDS)
|
||||
wm4 = WakuMessage(payload: "Invalid message".toBytes())
|
||||
|
||||
echo wm1.proof
|
||||
|
||||
# checks proofs are added
|
||||
check:
|
||||
proofAdded1
|
||||
|
@ -702,6 +780,7 @@ suite "Waku rln relay":
|
|||
msgValidate2 == MessageValidationResult.Spam
|
||||
msgValidate3 == MessageValidationResult.Valid
|
||||
msgValidate4 == MessageValidationResult.Invalid
|
||||
|
||||
test "toIDCommitment and toUInt256":
|
||||
# create an instance of rln
|
||||
var rlnInstance = createRLNInstance()
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 3378aed85716144231e036c076fe3f14ed9538de
|
|
@ -6,7 +6,11 @@ import
|
|||
os,
|
||||
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):
|
||||
const libName* = libPath / "rln.dll"
|
||||
elif defined(Linux):
|
||||
|
@ -23,66 +27,175 @@ elif defined(MacOsX):
|
|||
type Buffer* = object
|
||||
`ptr`*: ptr uint8
|
||||
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
|
||||
## the deleted id commitment key is replaced with a zero leaf
|
||||
## the return bool value indicates the success or failure of the operation
|
||||
######################################################################
|
||||
## Kilic's RLN module APIs
|
||||
######################################################################
|
||||
|
||||
proc get_root*(ctx: RLN[Bn256], 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
|
||||
##
|
||||
#----------------------------------------------------------------------------------------------
|
||||
#-------------------------------- 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
|
||||
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||
|
||||
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
|
||||
#------------------------------ 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
|
||||
## 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".}
|
||||
## 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],
|
||||
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,
|
||||
parameters_buffer: ptr Buffer,
|
||||
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
|
||||
## merkle_depth represent the depth of the Merkle tree
|
||||
## parameters_buffer holds prover and verifier keys
|
||||
## ctx holds the final created rln object
|
||||
## the return bool value indicates the success or failure of the operation
|
||||
proc new_circuit_from_params*(merkle_depth: uint,
|
||||
parameters_buffer: ptr Buffer,
|
||||
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
|
||||
## merkle_depth represent the depth of the Merkle tree
|
||||
## parameters_buffer holds prover and verifier keys
|
||||
## ctx holds the final created rln object
|
||||
## the return bool value indicates the success or failure of the operation
|
||||
|
||||
|
||||
proc hash*(ctx: RLN[Bn256],
|
||||
inputs_buffer: ptr Buffer,
|
||||
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
|
||||
## 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
|
||||
proc hash*(ctx: RLN[Bn256],
|
||||
inputs_buffer: ptr Buffer,
|
||||
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
|
||||
## 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
|
||||
|
||||
|
||||
######################################################################
|
||||
## 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.}
|
||||
|
|
|
@ -9,11 +9,15 @@ import
|
|||
stew/arrayops,
|
||||
../../utils/protobuf
|
||||
|
||||
## Bn256 and RLN are Nim wrappers for the data types used in
|
||||
## the rln library https://github.com/kilic/rln/blob/3bbec368a4adc68cd5f9bfae80b17e1bbb4ef373/src/ffi.rs
|
||||
type Bn256* = pointer
|
||||
type RLN*[E] = pointer
|
||||
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||
## Bn256 and RLN are Nim wrappers for the data types used in
|
||||
## the rln library https://github.com/kilic/rln/blob/3bbec368a4adc68cd5f9bfae80b17e1bbb4ef373/src/ffi.rs
|
||||
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
|
||||
# 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
|
||||
IDCommitment* = array[32, byte]
|
||||
|
||||
|
||||
type
|
||||
MerkleNode* = array[32, byte] # Each node of the Merkle tee is a Poseidon hash which is a 32 byte value
|
||||
Nullifier* = array[32, byte]
|
||||
ZKSNARK* = array[256, 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 -------------------------
|
||||
type MembershipKeyPair* = object
|
||||
## 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
|
||||
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 RlnMembershipCredentials* = object
|
||||
membershipKeyPair*: MembershipKeyPair
|
||||
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
|
||||
nullifier*: Nullifier
|
||||
shareX*: MerkleNode
|
||||
shareY*: MerkleNode
|
||||
|
||||
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*: RLN[Bn256]
|
||||
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]]
|
||||
when defined(rln) or (not defined(rln) and not 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*: RLN[Bn256]
|
||||
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]]
|
||||
|
||||
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
|
||||
Valid, Invalid, Spam
|
||||
|
@ -94,7 +149,7 @@ type MessageValidationResult* {.pure.} = enum
|
|||
# RLN membership key and index files path
|
||||
const
|
||||
RLN_CREDENTIALS_FILEPATH* = "rlnCredentials.txt"
|
||||
|
||||
|
||||
# inputs of the membership contract constructor
|
||||
# TODO may be able to make these constants private and put them inside the waku_rln_relay_utils
|
||||
const
|
||||
|
@ -111,6 +166,12 @@ const
|
|||
# the size of poseidon hash output as the number hex digits
|
||||
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
|
||||
const
|
||||
STATIC_GROUP_SIZE* = 100
|
||||
|
@ -319,10 +380,20 @@ const
|
|||
"d1ce3aea6cfb7be132d17e8d76fcbe4b7e34cef3979b4b905acfeff2f6d19724",
|
||||
"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
|
||||
# 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(rln) or (not defined(rln) and not 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* = "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 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 nsp.nullifier.copyFrom(nullifier)
|
||||
|
||||
when defined(rlnzerokit):
|
||||
var rlnIdentifier: seq[byte]
|
||||
discard ? pb.getField(7, rlnIdentifier)
|
||||
discard nsp.rlnIdentifier.copyFrom(rlnIdentifier)
|
||||
|
||||
return ok(nsp)
|
||||
|
||||
proc encode*(nsp: RateLimitProof): ProtoBuffer =
|
||||
|
@ -371,6 +447,9 @@ proc encode*(nsp: RateLimitProof): ProtoBuffer =
|
|||
output.write3(5, nsp.shareY)
|
||||
output.write3(6, nsp.nullifier)
|
||||
|
||||
when defined(rlnzerokit):
|
||||
output.write3(7, nsp.rlnIdentifier)
|
||||
|
||||
output.finish3()
|
||||
|
||||
return output
|
||||
return output
|
|
@ -24,7 +24,13 @@ import
|
|||
logScope:
|
||||
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 RateLimitProofResult* = Result[RateLimitProof, string]
|
||||
type SpamHandler* = proc(wakuMessage: WakuMessage): void {.gcsafe, closure,
|
||||
|
@ -39,76 +45,138 @@ contract(MembershipContract):
|
|||
# proc withdraw(secret: Uint256, pubkeyIndex: Uint256, receiver: Address)
|
||||
# proc withdrawBatch( secrets: seq[Uint256], pubkeyIndex: seq[Uint256], receiver: seq[Address])
|
||||
|
||||
proc createRLNInstance*(d: int = MERKLE_TREE_DEPTH): RLNResult
|
||||
{.raises: [Defect, IOError].} =
|
||||
proc toBuffer*(x: openArray[byte]): Buffer =
|
||||
## 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
|
||||
## 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)
|
||||
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||
|
||||
# check the parameters.key is not empty
|
||||
if (pbytes.len == 0):
|
||||
debug "error in parameters.key"
|
||||
return err("error in parameters.key")
|
||||
proc createRLNInstance*(d: int = MERKLE_TREE_DEPTH): RLNResult
|
||||
{.raises: [Defect, IOError].} =
|
||||
|
||||
# create an instance of RLN
|
||||
let res = new_circuit_from_params(merkleDepth, addr parametersBuffer,
|
||||
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)
|
||||
## 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: 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)
|
||||
|
||||
|
||||
proc membershipKeyGen*(ctxPtr: RLN[Bn256]): Option[MembershipKeyPair] =
|
||||
## generates a MembershipKeyPair that can be used for the registration into the rln membership contract
|
||||
# check the parameters.key is not empty
|
||||
if (pbytes.len == 0):
|
||||
debug "error in parameters.key"
|
||||
return err("error in parameters.key")
|
||||
|
||||
# keysBufferPtr will hold the generated key pairs i.e., secret and public keys
|
||||
var
|
||||
keysBuffer: Buffer
|
||||
keysBufferPtr = addr(keysBuffer)
|
||||
done = key_gen(ctxPtr, keysBufferPtr)
|
||||
# create an instance of RLN
|
||||
let res = new_circuit_from_params(merkleDepth, addr parametersBuffer,
|
||||
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)
|
||||
|
||||
# check whether the keys are generated successfully
|
||||
if(done == false):
|
||||
debug "error in key generation"
|
||||
return none(MembershipKeyPair)
|
||||
|
||||
proc membershipKeyGen*(ctxPtr: RLN[Bn256]): Option[MembershipKeyPair] =
|
||||
## generates a MembershipKeyPair that can be used for the registration into the rln membership contract
|
||||
|
||||
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)
|
||||
# keysBufferPtr will hold the generated key pairs i.e., secret and public keys
|
||||
var
|
||||
keysBuffer: Buffer
|
||||
keysBufferPtr = addr(keysBuffer)
|
||||
done = key_gen(ctxPtr, keysBufferPtr)
|
||||
|
||||
# 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]
|
||||
# check whether the keys are generated successfully
|
||||
if(done == false):
|
||||
debug "error in key generation"
|
||||
return none(MembershipKeyPair)
|
||||
|
||||
var
|
||||
keypair = MembershipKeyPair(idKey: secret, idCommitment: public)
|
||||
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)
|
||||
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 =
|
||||
let pk = UInt256.fromBytesBE(idCommitment)
|
||||
|
@ -188,27 +256,37 @@ proc appendLength*(input: openArray[byte]): seq[byte] =
|
|||
output = concat(@len, @input)
|
||||
return output
|
||||
|
||||
proc toBuffer*(x: openArray[byte]): Buffer =
|
||||
## 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
|
||||
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||
proc hash*(rlnInstance: RLN[Bn256], 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
|
||||
|
||||
proc hash*(rlnInstance: RLN[Bn256], 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`)[]
|
||||
|
||||
debug "hash input buffer length", bufflen = hashInputBuffer.len
|
||||
let
|
||||
hashSuccess = hash(rlnInstance, addr hashInputBuffer, addr outputBuffer)
|
||||
output = cast[ptr MerkleNode](outputBuffer.`ptr`)[]
|
||||
return output
|
||||
|
||||
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,
|
||||
msg: openArray[byte]): seq[byte] =
|
||||
|
@ -221,117 +299,232 @@ proc serialize(idKey: IDKey, memIndex: MembershipIndex, epoch: Epoch,
|
|||
let output = concat(@idKey, @memIndexBytes, @epoch, lenPrefMsg)
|
||||
return output
|
||||
|
||||
proc proofGen*(rlnInstance: RLN[Bn256], data: openArray[byte],
|
||||
memKeys: MembershipKeyPair, memIndex: MembershipIndex,
|
||||
epoch: Epoch): RateLimitProofResult =
|
||||
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||
proc proofGen*(rlnInstance: RLN[Bn256], 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)
|
||||
# serialize inputs
|
||||
let serializedInputs = serialize(idKey = memKeys.idKey,
|
||||
memIndex = memIndex,
|
||||
epoch = epoch,
|
||||
msg = data)
|
||||
var inputBuffer = toBuffer(serializedInputs)
|
||||
|
||||
debug "input buffer ", inputBuffer
|
||||
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")
|
||||
# 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[416, byte]] (proof.`ptr`)
|
||||
let proofBytes: array[416, byte] = proofValue[]
|
||||
debug "proof content", proofHex = proofValue[].toHex
|
||||
var proofValue = cast[ptr array[416, byte]] (proof.`ptr`)
|
||||
let proofBytes: array[416, byte] = proofValue[]
|
||||
debug "proof content", proofHex = proofValue[].toHex
|
||||
|
||||
## parse the proof as |zkSNARKs<256>|root<32>|epoch<32>|share_x<32>|share_y<32>|nullifier<32>|
|
||||
let
|
||||
proofOffset = 256
|
||||
rootOffset = proofOffset + 32
|
||||
epochOffset = rootOffset + 32
|
||||
shareXOffset = epochOffset + 32
|
||||
shareYOffset = shareXOffset + 32
|
||||
nullifierOffset = shareYOffset + 32
|
||||
## parse the proof as |zkSNARKs<256>|root<32>|epoch<32>|share_x<32>|share_y<32>|nullifier<32>|
|
||||
let
|
||||
proofOffset = 256
|
||||
rootOffset = proofOffset + 32
|
||||
epochOffset = rootOffset + 32
|
||||
shareXOffset = epochOffset + 32
|
||||
shareYOffset = shareXOffset + 32
|
||||
nullifierOffset = shareYOffset + 32
|
||||
|
||||
var
|
||||
zkproof: ZKSNARK
|
||||
proofRoot, shareX, shareY: MerkleNode
|
||||
epoch: Epoch
|
||||
nullifier: Nullifier
|
||||
var
|
||||
zkproof: ZKSNARK
|
||||
proofRoot, shareX, shareY: MerkleNode
|
||||
epoch: Epoch
|
||||
nullifier: Nullifier
|
||||
|
||||
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 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])
|
||||
|
||||
let output = RateLimitProof(proof: zkproof,
|
||||
merkleRoot: proofRoot,
|
||||
epoch: epoch,
|
||||
shareX: shareX,
|
||||
shareY: shareY,
|
||||
nullifier: nullifier)
|
||||
let output = RateLimitProof(proof: zkproof,
|
||||
merkleRoot: proofRoot,
|
||||
epoch: epoch,
|
||||
shareX: shareX,
|
||||
shareY: shareY,
|
||||
nullifier: nullifier)
|
||||
|
||||
return ok(output)
|
||||
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
|
||||
## 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> ]
|
||||
let lenPrefMsg = appendLength(@data)
|
||||
var proofBytes = concat(@(proof.proof),
|
||||
@(proof.merkleRoot),
|
||||
@(proof.epoch),
|
||||
@(proof.shareX),
|
||||
@(proof.shareY),
|
||||
@(proof.nullifier),
|
||||
lenPrefMsg)
|
||||
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
|
||||
## 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> ]
|
||||
let lenPrefMsg = appendLength(@data)
|
||||
var proofBytes = concat(@(proof.proof),
|
||||
@(proof.merkleRoot),
|
||||
@(proof.epoch),
|
||||
@(proof.shareX),
|
||||
@(proof.shareY),
|
||||
@(proof.nullifier),
|
||||
lenPrefMsg)
|
||||
|
||||
return proofBytes
|
||||
return proofBytes
|
||||
|
||||
proc proofVerify*(rlnInstance: RLN[Bn256], data: openArray[byte],
|
||||
proc proofVerify*(rlnInstance: RLN[Bn256], data: openArray[byte],
|
||||
proof: RateLimitProof): bool =
|
||||
var
|
||||
proofBytes = serialize(proof, data)
|
||||
proofBuffer = proofBytes.toBuffer()
|
||||
f = 0.uint32
|
||||
trace "serialized proof", proof = proofBytes.toHex()
|
||||
var
|
||||
proofBytes = serialize(proof, data)
|
||||
proofBuffer = proofBytes.toBuffer()
|
||||
f = 0.uint32
|
||||
trace "serialized proof", proof = proofBytes.toHex()
|
||||
|
||||
let verifyIsSuccessful = verify(rlnInstance, addr proofBuffer, addr f)
|
||||
if not verifyIsSuccessful:
|
||||
# something went wrong in verification
|
||||
let verifyIsSuccessful = verify(rlnInstance, addr proofBuffer, addr f)
|
||||
if not verifyIsSuccessful:
|
||||
# something went wrong in verification
|
||||
return false
|
||||
# f = 0 means the proof is verified
|
||||
if f == 0:
|
||||
return true
|
||||
return false
|
||||
# f = 0 means the proof is verified
|
||||
if f == 0:
|
||||
return true
|
||||
return false
|
||||
|
||||
proc insertMember*(rlnInstance: RLN[Bn256], idComm: IDCommitment): bool =
|
||||
var pkBuffer = toBuffer(idComm)
|
||||
let pkBufferPtr = addr pkBuffer
|
||||
proc insertMember*(rlnInstance: RLN[Bn256], 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
|
||||
# add the member to the tree
|
||||
var member_is_added = update_next_member(rlnInstance, pkBufferPtr)
|
||||
return member_is_added
|
||||
|
||||
proc removeMember*(rlnInstance: RLN[Bn256], index: MembershipIndex): bool =
|
||||
let deletion_success = delete_member(rlnInstance, index)
|
||||
return deletion_success
|
||||
proc removeMember*(rlnInstance: RLN[Bn256], index: MembershipIndex): bool =
|
||||
let deletion_success = delete_member(rlnInstance, index)
|
||||
return deletion_success
|
||||
|
||||
proc getMerkleRoot*(rlnInstance: RLN[Bn256]): 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")
|
||||
proc getMerkleRoot*(rlnInstance: RLN[Bn256]): 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)
|
||||
var rootValue = cast[ptr MerkleNode] (root.`ptr`)[]
|
||||
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[
|
||||
MembershipKeyPair] {.raises: [Defect, ValueError].} =
|
||||
|
@ -601,14 +794,25 @@ proc appendRLNProof*(rlnPeer: WakuRLNRelay, msg: var WakuMessage,
|
|||
msg.proof = proof.value
|
||||
return true
|
||||
|
||||
proc addAll*(rlnInstance: RLN[Bn256], 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
|
||||
when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
|
||||
proc addAll*(rlnInstance: RLN[Bn256], 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
|
||||
|
||||
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
|
||||
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
|
||||
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)) =
|
||||
## 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
|
||||
|
@ -904,4 +1109,4 @@ proc mountRlnRelay*(node: WakuNode, conf: WakuNodeConf|Chat2Conf, spamHandler: O
|
|||
info "no rln credential is provided"
|
||||
waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr,
|
||||
ethAccAddr = ethAccountAddr, ethAccountPrivKeyOpt = ethAccountPrivKeyOpt, pubsubTopic = conf.rlnRelayPubsubTopic,
|
||||
contentTopic = conf.rlnRelayContentTopic, spamHandler = spamHandler)
|
||||
contentTopic = conf.rlnRelayContentTopic, spamHandler = spamHandler)
|
Loading…
Reference in New Issue