mirror of https://github.com/waku-org/nwaku.git
RLN-Relay: Adds fix to the hash, proofGen and proofVerify procs (to support arbitrary messages) (#753)
* wip: updates the hash interface * updates hash unittests * exposes appendLength * updates proof gen interface and the unittests * enables test, fixes proofGen and verify utils and unittest, beautifies the code * removes int size from mem index type * enables all the rln tests * adds documentation of two private procs * documentation for the appendLength * minor clean up * adds clarificaltion on the endianness of input length * Delete .DS_Store
This commit is contained in:
parent
2cb0b5ad98
commit
dbbc0f750b
|
@ -109,7 +109,7 @@
|
|||
[submodule "vendor/rln"]
|
||||
path = vendor/rln
|
||||
url = https://github.com/kilic/rln
|
||||
branch = full-node
|
||||
branch = master
|
||||
[submodule "vendor/nim-testutils"]
|
||||
path = vendor/nim-testutils
|
||||
url = https://github.com/status-im/nim-testutils.git
|
||||
|
|
Binary file not shown.
|
@ -80,6 +80,7 @@ const MEMBERSHIP_CONTRACT_CODE = readFile("tests/v2/membershipContract.txt")
|
|||
# _withdraw(secret, _pubkeyIndex, receiver);
|
||||
# }
|
||||
|
||||
|
||||
contract(MembershipContract):
|
||||
proc register(pubkey: Uint256) # external payable
|
||||
# proc registerBatch(pubkeys: seq[Uint256]) # external payable
|
||||
|
@ -364,16 +365,18 @@ suite "Waku rln relay":
|
|||
root1 {.noinit.} : Buffer = Buffer()
|
||||
rootPtr1 = addr(root1)
|
||||
get_root_successful1 = get_root(rlnInstance.value, rootPtr1)
|
||||
doAssert(get_root_successful1)
|
||||
doAssert(root1.len == 32)
|
||||
check:
|
||||
get_root_successful1
|
||||
root1.len == 32
|
||||
|
||||
# read the Merkle Tree root
|
||||
var
|
||||
root2 {.noinit.} : Buffer = Buffer()
|
||||
rootPtr2 = addr(root2)
|
||||
get_root_successful2 = get_root(rlnInstance.value, rootPtr2)
|
||||
doAssert(get_root_successful2)
|
||||
doAssert(root2.len == 32)
|
||||
check:
|
||||
get_root_successful2
|
||||
root2.len == 32
|
||||
|
||||
var rootValue1 = cast[ptr array[32,byte]] (root1.`ptr`)
|
||||
let rootHex1 = rootValue1[].toHex
|
||||
|
@ -382,7 +385,7 @@ suite "Waku rln relay":
|
|||
let rootHex2 = rootValue2[].toHex
|
||||
|
||||
# the two roots must be identical
|
||||
doAssert(rootHex1 == rootHex2)
|
||||
check rootHex1 == rootHex2
|
||||
test "getMerkleRoot utils":
|
||||
# create an RLN instance which also includes an empty Merkle tree
|
||||
var rlnInstance = createRLNInstance()
|
||||
|
@ -391,16 +394,16 @@ suite "Waku rln relay":
|
|||
|
||||
# read the Merkle Tree root
|
||||
var root1 = getMerkleRoot(rlnInstance.value())
|
||||
doAssert(root1.isOk)
|
||||
check root1.isOk
|
||||
let rootHex1 = root1.value().toHex
|
||||
|
||||
# read the Merkle Tree root
|
||||
var root2 = getMerkleRoot(rlnInstance.value())
|
||||
doAssert(root2.isOk)
|
||||
check root2.isOk
|
||||
let rootHex2 = root2.value().toHex
|
||||
|
||||
# the two roots must be identical
|
||||
doAssert(rootHex1 == rootHex2)
|
||||
check rootHex1 == rootHex2
|
||||
|
||||
test "update_next_member Nim Wrapper":
|
||||
# create an RLN instance which also includes an empty Merkle tree
|
||||
|
@ -410,8 +413,8 @@ suite "Waku rln relay":
|
|||
|
||||
# generate a key pair
|
||||
var keypair = membershipKeyGen(rlnInstance.value)
|
||||
doAssert(keypair.isSome())
|
||||
var pkBuffer = Buffer(`ptr`: addr(keypair.get().idCommitment[0]), len: 32)
|
||||
check keypair.isSome()
|
||||
var pkBuffer = toBuffer(keypair.get().idCommitment)
|
||||
let pkBufferPtr = addr pkBuffer
|
||||
|
||||
# add the member to the tree
|
||||
|
@ -428,7 +431,7 @@ suite "Waku rln relay":
|
|||
# delete the first member
|
||||
var deleted_member_index = MembershipIndex(0)
|
||||
let deletion_success = delete_member(rlnInstance.value, deleted_member_index)
|
||||
doAssert(deletion_success)
|
||||
check deletion_success
|
||||
|
||||
test "insertMember rln utils":
|
||||
# create an RLN instance which also includes an empty Merkle tree
|
||||
|
@ -438,7 +441,7 @@ suite "Waku rln relay":
|
|||
var rln = rlnInstance.value
|
||||
# generate a key pair
|
||||
var keypair = rln.membershipKeyGen()
|
||||
doAssert(keypair.isSome())
|
||||
check keypair.isSome()
|
||||
check:
|
||||
rln.insertMember(keypair.get().idCommitment)
|
||||
|
||||
|
@ -462,39 +465,42 @@ suite "Waku rln relay":
|
|||
root1 {.noinit.} : Buffer = Buffer()
|
||||
rootPtr1 = addr(root1)
|
||||
get_root_successful1 = get_root(rlnInstance.value, rootPtr1)
|
||||
doAssert(get_root_successful1)
|
||||
doAssert(root1.len == 32)
|
||||
check:
|
||||
get_root_successful1
|
||||
root1.len == 32
|
||||
|
||||
# generate a key pair
|
||||
var keypair = membershipKeyGen(rlnInstance.value)
|
||||
doAssert(keypair.isSome())
|
||||
var pkBuffer = Buffer(`ptr`: addr(keypair.get().idCommitment[0]), len: 32)
|
||||
check: keypair.isSome()
|
||||
var pkBuffer = toBuffer(keypair.get().idCommitment)
|
||||
let pkBufferPtr = addr pkBuffer
|
||||
|
||||
# add the member to the tree
|
||||
var member_is_added = update_next_member(rlnInstance.value, pkBufferPtr)
|
||||
doAssert(member_is_added)
|
||||
check member_is_added
|
||||
|
||||
# read the Merkle Tree root after insertion
|
||||
var
|
||||
root2 {.noinit.} : Buffer = Buffer()
|
||||
rootPtr2 = addr(root2)
|
||||
get_root_successful2 = get_root(rlnInstance.value, rootPtr2)
|
||||
doAssert(get_root_successful2)
|
||||
doAssert(root2.len == 32)
|
||||
check:
|
||||
get_root_successful2
|
||||
root2.len == 32
|
||||
|
||||
# delete the first member
|
||||
var deleted_member_index = MembershipIndex(0)
|
||||
let deletion_success = delete_member(rlnInstance.value, deleted_member_index)
|
||||
doAssert(deletion_success)
|
||||
check deletion_success
|
||||
|
||||
# read the Merkle Tree root after the deletion
|
||||
var
|
||||
root3 {.noinit.} : Buffer = Buffer()
|
||||
rootPtr3 = addr(root3)
|
||||
get_root_successful3 = get_root(rlnInstance.value, rootPtr3)
|
||||
doAssert(get_root_successful3)
|
||||
doAssert(root3.len == 32)
|
||||
check:
|
||||
get_root_successful3
|
||||
root3.len == 32
|
||||
|
||||
var rootValue1 = cast[ptr array[32,byte]] (root1.`ptr`)
|
||||
let rootHex1 = rootValue1[].toHex
|
||||
|
@ -509,11 +515,11 @@ suite "Waku rln relay":
|
|||
debug "The root after deletion", rootHex3
|
||||
|
||||
# the root must change after the insertion
|
||||
doAssert(not(rootHex1 == rootHex2))
|
||||
check: not(rootHex1 == rootHex2)
|
||||
|
||||
## The initial root of the tree (empty tree) must be identical to
|
||||
## the root of the tree after one insertion followed by a deletion
|
||||
doAssert(rootHex1 == rootHex3)
|
||||
check rootHex1 == rootHex3
|
||||
test "Merkle tree consistency check between deletion and insertion using rln utils":
|
||||
# create an RLN instance
|
||||
var rlnInstance = createRLNInstance()
|
||||
|
@ -523,29 +529,29 @@ suite "Waku rln relay":
|
|||
|
||||
# read the Merkle Tree root
|
||||
var root1 = rln.getMerkleRoot()
|
||||
doAssert(root1.isOk)
|
||||
check root1.isOk
|
||||
let rootHex1 = root1.value().toHex()
|
||||
|
||||
# generate a key pair
|
||||
var keypair = rln.membershipKeyGen()
|
||||
doAssert(keypair.isSome())
|
||||
check keypair.isSome()
|
||||
let member_inserted = rln.insertMember(keypair.get().idCommitment)
|
||||
check member_inserted
|
||||
|
||||
# read the Merkle Tree root after insertion
|
||||
var root2 = rln.getMerkleRoot()
|
||||
doAssert(root2.isOk)
|
||||
check root2.isOk
|
||||
let rootHex2 = root2.value().toHex()
|
||||
|
||||
|
||||
# delete the first member
|
||||
var deleted_member_index = MembershipIndex(0)
|
||||
let deletion_success = rln.removeMember(deleted_member_index)
|
||||
doAssert(deletion_success)
|
||||
check deletion_success
|
||||
|
||||
# read the Merkle Tree root after the deletion
|
||||
var root3 = rln.getMerkleRoot()
|
||||
doAssert(root3.isOk)
|
||||
check root3.isOk
|
||||
let rootHex3 = root3.value().toHex()
|
||||
|
||||
|
||||
|
@ -554,11 +560,11 @@ suite "Waku rln relay":
|
|||
debug "The root after deletion", rootHex3
|
||||
|
||||
# the root must change after the insertion
|
||||
doAssert(not(rootHex1 == rootHex2))
|
||||
check not(rootHex1 == rootHex2)
|
||||
|
||||
## The initial root of the tree (empty tree) must be identical to
|
||||
## the root of the tree after one insertion followed by a deletion
|
||||
doAssert(rootHex1 == rootHex3)
|
||||
check rootHex1 == rootHex3
|
||||
|
||||
test "hash Nim Wrappers":
|
||||
# create an RLN instance
|
||||
|
@ -567,24 +573,19 @@ suite "Waku rln relay":
|
|||
rlnInstance.isOk == true
|
||||
|
||||
# prepare the input
|
||||
var
|
||||
hashInput : array[32, byte]
|
||||
for x in hashInput.mitems: x= 1
|
||||
var
|
||||
hashInputHex = hashInput.toHex()
|
||||
hashInputBuffer = Buffer(`ptr`: addr hashInput[0], len: 32 )
|
||||
|
||||
debug "sample_hash_input_bytes", hashInputHex
|
||||
msg = "Hello".toBytes()
|
||||
hashInput = appendLength(msg)
|
||||
hashInputBuffer = toBuffer(hashInput)
|
||||
|
||||
# prepare other inputs to the hash function
|
||||
var
|
||||
outputBuffer: Buffer
|
||||
numOfInputs = 1.uint # the number of hash inputs that can be 1 or 2
|
||||
var outputBuffer: Buffer
|
||||
|
||||
let hashSuccess = hash(rlnInstance.value, addr hashInputBuffer, numOfInputs, addr outputBuffer)
|
||||
doAssert(hashSuccess)
|
||||
let hashSuccess = hash(rlnInstance.value, addr hashInputBuffer, addr outputBuffer)
|
||||
check hashSuccess
|
||||
let outputArr = cast[ptr array[32,byte]](outputBuffer.`ptr`)[]
|
||||
doAssert("53a6338cdbf02f0563cec1898e354d0d272c8f98b606c538945c6f41ef101828" == outputArr.toHex())
|
||||
check:
|
||||
"efb8ac39dc22eaf377fe85b405b99ba78dbc2f3f32494add4501741df946bd1d" == outputArr.toHex()
|
||||
|
||||
var
|
||||
hashOutput = cast[ptr array[32,byte]] (outputBuffer.`ptr`)[]
|
||||
|
@ -600,125 +601,11 @@ suite "Waku rln relay":
|
|||
let rln = rlnInstance.value
|
||||
|
||||
# prepare the input
|
||||
# TODO should add support for arbitrary messages, the following input is artificial
|
||||
var hashInput : array[32, byte]
|
||||
for x in hashInput.mitems: x = 1
|
||||
debug "sample_hash_input_bytes", hashInputHex=hashInput.toHex()
|
||||
let msg = "Hello".toBytes()
|
||||
|
||||
let hash = rln.hash(hashInput)
|
||||
doAssert("53a6338cdbf02f0563cec1898e354d0d272c8f98b606c538945c6f41ef101828" == hash.toHex())
|
||||
|
||||
test "generate_proof and verify Nim Wrappers":
|
||||
# create an RLN instance
|
||||
|
||||
# check if the rln instance is created successfully
|
||||
var rlnInstance = createRLNInstance()
|
||||
let hash = rln.hash(msg)
|
||||
check:
|
||||
rlnInstance.isOk == true
|
||||
|
||||
|
||||
# create the membership key
|
||||
var auth = membershipKeyGen(rlnInstance.value)
|
||||
var skBuffer = Buffer(`ptr`: addr(auth.get().idKey[0]), len: 32)
|
||||
|
||||
# peer's index in the Merkle Tree
|
||||
var index = 5
|
||||
|
||||
# prepare the authentication object with peer's index and sk
|
||||
var authObj: Auth = Auth(secret_buffer: addr skBuffer, index: MembershipIndex(index))
|
||||
|
||||
# Create a Merkle tree with random members
|
||||
for i in 0..10:
|
||||
var member_is_added: bool = false
|
||||
if (i == index):
|
||||
# insert the current peer's pk
|
||||
var pkBuffer = Buffer(`ptr`: addr(auth.get().idCommitment[0]), len: 32)
|
||||
member_is_added = update_next_member(rlnInstance.value, addr pkBuffer)
|
||||
else:
|
||||
var memberKeys = membershipKeyGen(rlnInstance.value)
|
||||
var pkBuffer = Buffer(`ptr`: addr(memberKeys.get().idCommitment[0]), len: 32)
|
||||
member_is_added = update_next_member(rlnInstance.value, addr pkBuffer)
|
||||
# check the member is added
|
||||
doAssert(member_is_added)
|
||||
|
||||
# prepare the message
|
||||
var messageBytes {.noinit.}: array[32, byte]
|
||||
for x in messageBytes.mitems: x = 1
|
||||
var messageHex = messageBytes.toHex()
|
||||
debug "message", messageHex
|
||||
|
||||
# prepare the epoch
|
||||
var epochBytes : array[32,byte]
|
||||
for x in epochBytes.mitems : x = 0
|
||||
var epochHex = epochBytes.toHex()
|
||||
debug "epoch", epochHex
|
||||
|
||||
|
||||
# serialize message and epoch
|
||||
# TODO add a proc for serializing
|
||||
var epochMessage = @epochBytes & @messageBytes
|
||||
doAssert(epochMessage.len == 64)
|
||||
var inputBytes{.noinit.}: array[64, byte] # holds epoch||Message
|
||||
for (i, x) in inputBytes.mpairs: x = epochMessage[i]
|
||||
var inputHex = inputBytes.toHex()
|
||||
debug "serialized epoch and message ", inputHex
|
||||
# put the serialized epoch||message into a buffer
|
||||
var inputBuffer = Buffer(`ptr`: addr(inputBytes[0]), len: 64)
|
||||
|
||||
# generate the proof
|
||||
var proof: Buffer
|
||||
let proofIsSuccessful = generate_proof(rlnInstance.value, addr inputBuffer, addr authObj, addr proof)
|
||||
# check whether the generate_proof call is done successfully
|
||||
doAssert(proofIsSuccessful)
|
||||
var proofValue = cast[ptr array[416,byte]] (proof.`ptr`)
|
||||
let proofHex = proofValue[].toHex
|
||||
debug "proof content", proofHex
|
||||
|
||||
# display the proof breakdown
|
||||
var
|
||||
zkSNARK = proofHex[0..511]
|
||||
proofRoot = proofHex[512..575]
|
||||
proofEpoch = proofHex[576..639]
|
||||
shareX = proofHex[640..703]
|
||||
shareY = proofHex[704..767]
|
||||
nullifier = proofHex[768..831]
|
||||
|
||||
doAssert(zkSNARK.len == 512)
|
||||
doAssert(proofRoot.len == 64)
|
||||
doAssert(proofEpoch.len == 64)
|
||||
doAssert(epochHex == proofEpoch)
|
||||
doAssert(shareX.len == 64)
|
||||
doAssert(shareY.len == 64)
|
||||
doAssert(nullifier.len == 64)
|
||||
|
||||
debug "zkSNARK ", zkSNARK
|
||||
debug "root ", proofRoot
|
||||
debug "epoch ", proofEpoch
|
||||
debug "shareX", shareX
|
||||
debug "shareY", shareY
|
||||
debug "nullifier", nullifier
|
||||
|
||||
var f = 0.uint32
|
||||
let verifyIsSuccessful = verify(rlnInstance.value, addr proof, addr f)
|
||||
doAssert(verifyIsSuccessful)
|
||||
# f = 0 means the proof is verified
|
||||
doAssert(f == 0)
|
||||
|
||||
# create and test a bad proof
|
||||
# prepare a bad authentication object with a wrong peer's index
|
||||
var badIndex = 8
|
||||
var badAuthObj: Auth = Auth(secret_buffer: addr skBuffer, index: MembershipIndex(badIndex))
|
||||
var badProof: Buffer
|
||||
let badProofIsSuccessful = generate_proof(rlnInstance.value, addr inputBuffer, addr badAuthObj, addr badProof)
|
||||
# check whether the generate_proof call is done successfully
|
||||
doAssert(badProofIsSuccessful)
|
||||
|
||||
var badF = 0.uint32
|
||||
let badVerifyIsSuccessful = verify(rlnInstance.value, addr badProof, addr badF)
|
||||
doAssert(badVerifyIsSuccessful)
|
||||
# badF=1 means the proof is not verified
|
||||
# verification of the bad proof should fail
|
||||
doAssert(badF == 1)
|
||||
"efb8ac39dc22eaf377fe85b405b99ba78dbc2f3f32494add4501741df946bd1d" == hash.toHex()
|
||||
|
||||
test "create a list of membership keys and construct a Merkle tree based on the list":
|
||||
let
|
||||
|
@ -769,23 +656,22 @@ suite "Waku rln relay":
|
|||
for x in nullifier.mitems : x = 6
|
||||
|
||||
let
|
||||
nsp = RateLimitProof(proof: proof,
|
||||
rateLimitProof = RateLimitProof(proof: proof,
|
||||
merkleRoot: merkleRoot,
|
||||
epoch: epoch,
|
||||
shareX: shareX,
|
||||
shareY: shareY,
|
||||
nullifier: nullifier)
|
||||
protobuf = nsp.encode()
|
||||
protobuf = rateLimitProof.encode()
|
||||
decodednsp = RateLimitProof.init(protobuf.buffer)
|
||||
|
||||
check:
|
||||
decodednsp.isErr == false
|
||||
decodednsp.value == nsp
|
||||
decodednsp.value == rateLimitProof
|
||||
|
||||
test "test proofVerify and proofGen for a valid proof":
|
||||
var rlnInstance = createRLNInstance()
|
||||
check:
|
||||
rlnInstance.isOk == true
|
||||
check rlnInstance.isOk
|
||||
var rln = rlnInstance.value
|
||||
|
||||
let
|
||||
|
@ -805,36 +691,26 @@ suite "Waku rln relay":
|
|||
let memberKeys = rln.membershipKeyGen()
|
||||
member_is_added = rln.insertMember(memberKeys.get().idCommitment)
|
||||
# check the member is added
|
||||
doAssert(member_is_added)
|
||||
check member_is_added
|
||||
|
||||
# prepare the message
|
||||
# TODO this message format is artificial (to bypass the Poseidon hasher issue)
|
||||
# TODO in practice we should be able to pick messages of arbitrary size and format
|
||||
var messageBytes {.noinit.}: array[32, byte]
|
||||
for x in messageBytes.mitems: x = 1
|
||||
debug "message", messageHex=messageBytes.toHex()
|
||||
let messageBytes = "Hello".toBytes()
|
||||
|
||||
# prepare the epoch
|
||||
var epoch : Epoch
|
||||
for x in epoch.mitems : x = 0
|
||||
debug "epoch", epochHex=epoch.toHex()
|
||||
|
||||
# hash the message
|
||||
let msgHash = rln.hash(messageBytes)
|
||||
debug "message hash", mh=byteutils.toHex(msgHash)
|
||||
|
||||
# generate proof
|
||||
let proofRes = rln.proofGen(data = msgHash,
|
||||
memKeys = memKeys,
|
||||
memIndex = MembershipIndex(index),
|
||||
epoch = epoch)
|
||||
|
||||
doAssert(proofRes.isOk())
|
||||
let proofRes = rln.proofGen(data = messageBytes,
|
||||
memKeys = memKeys,
|
||||
memIndex = MembershipIndex(index),
|
||||
epoch = epoch)
|
||||
check proofRes.isOk()
|
||||
let proof = proofRes.value
|
||||
|
||||
# verify the proof
|
||||
let verified = rln.proofVerify(data = messageBytes,
|
||||
proof = proof)
|
||||
proof = proof)
|
||||
check verified == true
|
||||
|
||||
test "test proofVerify and proofGen for an invalid proof":
|
||||
|
@ -860,32 +736,23 @@ suite "Waku rln relay":
|
|||
let memberKeys = rln.membershipKeyGen()
|
||||
member_is_added = rln.insertMember(memberKeys.get().idCommitment)
|
||||
# check the member is added
|
||||
doAssert(member_is_added)
|
||||
check member_is_added
|
||||
|
||||
# prepare the message
|
||||
# TODO this message format is artificial (to bypass the Poseidon hasher issue)
|
||||
# TODO in practice we should be able to pick messages of arbitrary size and format
|
||||
var messageBytes {.noinit.}: array[32, byte]
|
||||
for x in messageBytes.mitems: x = 1
|
||||
debug "message", messageHex=messageBytes.toHex()
|
||||
# prepare the message
|
||||
let messageBytes = "Hello".toBytes()
|
||||
|
||||
# prepare the epoch
|
||||
var epoch : Epoch
|
||||
for x in epoch.mitems : x = 0
|
||||
debug "epoch in bytes", epochHex=epoch.toHex()
|
||||
|
||||
# hash the message
|
||||
let msgHash = rln.hash(messageBytes)
|
||||
debug "message hash", mh=byteutils.toHex(msgHash)
|
||||
|
||||
let badIndex = 4
|
||||
# generate proof
|
||||
let proofRes = rln.proofGen(data = msgHash,
|
||||
memKeys = memKeys,
|
||||
memIndex = MembershipIndex(badIndex),
|
||||
epoch = epoch)
|
||||
|
||||
doAssert(proofRes.isOk())
|
||||
let proofRes = rln.proofGen(data = messageBytes,
|
||||
memKeys = memKeys,
|
||||
memIndex = MembershipIndex(badIndex),
|
||||
epoch = epoch)
|
||||
check proofRes.isOk()
|
||||
let proof = proofRes.value
|
||||
|
||||
# verify the proof (should not be verified)
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 628fd742237f49347469dfcbbe57fd591d6629b1
|
||||
Subproject commit 7ac74183f8b69b399e3bc96c1ae8ab61c026dc43
|
|
@ -44,13 +44,14 @@ proc key_gen*(ctx: RLN[Bn256], keypair_buffer: ptr Buffer): bool {.importc: "key
|
|||
|
||||
proc generate_proof*(ctx: RLN[Bn256],
|
||||
input_buffer: ptr Buffer,
|
||||
auth: ptr Auth,
|
||||
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>|
|
||||
## numbers are in bytes
|
||||
## sizes are in bytes
|
||||
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> ]
|
||||
#----------------------------------------------------------------------------------------------
|
||||
#-------------------------------- Common procedures -------------------------------------------
|
||||
|
||||
|
@ -60,6 +61,6 @@ proc new_circuit_from_params*(merkle_depth: uint,
|
|||
|
||||
proc hash*(ctx: RLN[Bn256],
|
||||
inputs_buffer: ptr Buffer,
|
||||
input_len: uint,
|
||||
output_buffer: ptr Buffer): bool {.importc: "hash".}
|
||||
output_buffer: ptr Buffer): bool {.importc: "signal_to_field".}
|
||||
|
||||
{.pop.}
|
||||
|
|
|
@ -5,7 +5,7 @@ import
|
|||
chronicles, options, chronos, stint,
|
||||
web3,
|
||||
stew/results,
|
||||
stew/[byteutils, arrayops],
|
||||
stew/[byteutils, arrayops, endians2],
|
||||
rln,
|
||||
waku_rln_relay_types
|
||||
|
||||
|
@ -103,6 +103,18 @@ proc register*(rlnPeer: WakuRLNRelay): Future[bool] {.async.} =
|
|||
await web3.close()
|
||||
return true
|
||||
|
||||
proc appendLength*(input: openArray[byte]): seq[byte] =
|
||||
## returns length prefixed version of the input
|
||||
## with the following format [len<8>|input<var>]
|
||||
## len: 8-byte value that represents the number of bytes in the `input`
|
||||
## len is serialized in little-endian
|
||||
## input: the supplied `input`
|
||||
let
|
||||
# the length should be serialized in little-endian
|
||||
len = toBytes(uint64(input.len), Endianness.littleEndian)
|
||||
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
|
||||
|
@ -113,43 +125,40 @@ proc toBuffer*(x: openArray[byte]): Buffer =
|
|||
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 = data.toBuffer()
|
||||
hashInputBuffer = lenPrefData.toBuffer()
|
||||
outputBuffer: Buffer # will holds the hash output
|
||||
numOfInputs = 1.uint # the number of hash inputs that can be 1 or 2
|
||||
|
||||
debug "hash input buffer length", bufflen=hashInputBuffer.len
|
||||
let
|
||||
hashSuccess = hash(rlnInstance, addr hashInputBuffer, numOfInputs, addr outputBuffer)
|
||||
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] =
|
||||
## a private proc to convert RateLimitProof and the data to a byte seq
|
||||
## this conversion is used in the proofGen proc
|
||||
## the serialization is done as instructed in https://github.com/kilic/rln/blob/7ac74183f8b69b399e3bc96c1ae8ab61c026dc43/src/public.rs#L146
|
||||
## [ id_key<32> | id_index<8> | epoch<32> | signal_len<8> | signal<var> ]
|
||||
let memIndexBytes = toBytes(uint64(memIndex), Endianness.littleEndian)
|
||||
let lenPrefMsg = appendLength(msg)
|
||||
let output = concat(@idKey, @memIndexBytes, @epoch, lenPrefMsg)
|
||||
return output
|
||||
|
||||
proc proofGen*(rlnInstance: RLN[Bn256], data: openArray[byte], memKeys: MembershipKeyPair, memIndex: MembershipIndex, epoch: Epoch): RateLimitProofResult =
|
||||
|
||||
var skBuffer = toBuffer(memKeys.idKey)
|
||||
|
||||
# peer's index in the Merkle Tree
|
||||
var index = memIndex
|
||||
|
||||
# prepare the authentication object with peer's index and sk
|
||||
var authObj: Auth = Auth(secret_buffer: addr skBuffer, index: index)
|
||||
|
||||
# serialize message and epoch
|
||||
# TODO add a proc for serializing
|
||||
var epochMessage = @epoch & @data
|
||||
|
||||
# convert the seq to an array
|
||||
var inputBytes{.noinit.}: array[64, byte] # holds epoch||Message
|
||||
for (i, x) in inputBytes.mpairs: x = epochMessage[i]
|
||||
debug "serialized epoch and message ", inputHex=inputBytes.toHex()
|
||||
|
||||
# put the serialized epoch||message into a buffer
|
||||
var inputBuffer = toBuffer(inputBytes)
|
||||
# serialize inputs
|
||||
let serializedInputs = serialize(idKey = memKeys.idKey,
|
||||
memIndex = memIndex,
|
||||
epoch = epoch,
|
||||
msg = data)
|
||||
var inputBuffer = toBuffer(serializedInputs)
|
||||
|
||||
# generate the proof
|
||||
var proof: Buffer
|
||||
let proofIsSuccessful = generate_proof(rlnInstance, addr inputBuffer, addr authObj, addr proof)
|
||||
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")
|
||||
|
@ -181,30 +190,33 @@ proc proofGen*(rlnInstance: RLN[Bn256], data: openArray[byte], memKeys: Membersh
|
|||
discard nullifier.copyFrom(proofBytes[shareYOffset..nullifierOffset-1])
|
||||
|
||||
let output = RateLimitProof(proof: zkproof,
|
||||
merkleRoot: proofRoot,
|
||||
epoch: epoch,
|
||||
shareX: shareX,
|
||||
shareY: shareY,
|
||||
nullifier: nullifier)
|
||||
merkleRoot: proofRoot,
|
||||
epoch: epoch,
|
||||
shareX: shareX,
|
||||
shareY: shareY,
|
||||
nullifier: nullifier)
|
||||
|
||||
return ok(output)
|
||||
|
||||
proc serializeProof(proof: RateLimitProof): seq[byte] =
|
||||
## a private proc to convert RateLimitProof to a byte seq
|
||||
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))
|
||||
@(proof.nullifier),
|
||||
lenPrefMsg)
|
||||
|
||||
return proofBytes
|
||||
|
||||
proc proofVerify*(rlnInstance: RLN[Bn256], data: openArray[byte], proof: RateLimitProof): bool =
|
||||
# TODO proof should be checked against the data
|
||||
var
|
||||
proofBytes= serializeProof(proof)
|
||||
proofBytes= serialize(proof, data)
|
||||
proofBuffer = proofBytes.toBuffer()
|
||||
f = 0.uint32
|
||||
debug "serialized proof", proof=proofBytes.toHex()
|
||||
|
|
Loading…
Reference in New Issue