2022-09-07 15:15:43 -04:00
package rln
/ *
# include "./librln.h"
* /
import "C"
import (
"encoding/binary"
"errors"
"unsafe"
2022-11-26 13:58:25 -04:00
"github.com/waku-org/go-zerokit-rln/rln/resources"
2022-09-07 15:15:43 -04:00
)
// RLN represents the context used for rln.
type RLN struct {
ptr * C . RLN
}
// NewRLN generates an instance of RLN. An instance supports both zkSNARKs logics
2022-10-04 18:44:07 -04:00
// and Merkle tree data structure and operations. It uses a depth of 20 by default
func NewRLN ( ) ( * RLN , error ) {
2022-09-07 15:15:43 -04:00
2022-10-04 18:44:07 -04:00
wasm , err := resources . Asset ( "tree_height_20/rln.wasm" )
2022-09-07 15:15:43 -04:00
if err != nil {
return nil , err
}
2022-10-04 18:44:07 -04:00
zkey , err := resources . Asset ( "tree_height_20/rln_final.zkey" )
2022-09-07 15:15:43 -04:00
if err != nil {
return nil , err
}
2022-10-04 18:44:07 -04:00
verifKey , err := resources . Asset ( "tree_height_20/verification_key.json" )
2022-09-07 15:15:43 -04:00
if err != nil {
return nil , err
}
2022-10-04 18:44:07 -04:00
r := & RLN { }
depth := 20
2022-09-07 15:15:43 -04:00
wasmBuffer := toCBufferPtr ( wasm )
zkeyBuffer := toCBufferPtr ( zkey )
verifKeyBuffer := toCBufferPtr ( verifKey )
if ! bool ( C . new_with_params ( C . uintptr_t ( depth ) , wasmBuffer , zkeyBuffer , verifKeyBuffer , & r . ptr ) ) {
return nil , errors . New ( "failed to initialize" )
}
return r , nil
}
// NewRLNWithParams generates an instance of RLN. An instance supports both zkSNARKs logics
2022-10-04 18:44:07 -04:00
// and Merkle tree data structure and operations. The parameter `depth“ indicates the depth of Merkle tree
2022-09-07 15:15:43 -04:00
func NewRLNWithParams ( depth int , wasm [ ] byte , zkey [ ] byte , verifKey [ ] byte ) ( * RLN , error ) {
r := & RLN { }
wasmBuffer := toCBufferPtr ( wasm )
zkeyBuffer := toCBufferPtr ( zkey )
verifKeyBuffer := toCBufferPtr ( verifKey )
if ! bool ( C . new_with_params ( C . uintptr_t ( depth ) , wasmBuffer , zkeyBuffer , verifKeyBuffer , & r . ptr ) ) {
return nil , errors . New ( "failed to initialize" )
}
return r , nil
}
// NewRLNWithFolder generates an instance of RLN. An instance supports both zkSNARKs logics
// and Merkle tree data structure and operations. The parameter `deptk` indicates the depth of Merkle tree
2022-10-04 18:44:07 -04:00
// The parameter “
2022-09-07 15:15:43 -04:00
func NewRLNWithFolder ( depth int , resourcesFolderPath string ) ( * RLN , error ) {
r := & RLN { }
pathBuffer := toCBufferPtr ( [ ] byte ( resourcesFolderPath ) )
if ! bool ( C . new ( C . uintptr_t ( depth ) , pathBuffer , & r . ptr ) ) {
return nil , errors . New ( "failed to initialize" )
}
return r , nil
}
func toCBufferPtr ( input [ ] byte ) * C . Buffer {
buf := toBuffer ( input )
size := int ( unsafe . Sizeof ( buf ) )
in := ( * C . Buffer ) ( C . malloc ( C . size_t ( size ) ) )
* in = buf
return in
}
// MembershipKeyGen generates a MembershipKeyPair that can be used for the registration into the rln membership contract
func ( r * RLN ) MembershipKeyGen ( ) ( * MembershipKeyPair , error ) {
buffer := toBuffer ( [ ] byte { } )
if ! bool ( C . key_gen ( r . ptr , & buffer ) ) {
return nil , errors . New ( "error in key generation" )
}
key := & MembershipKeyPair {
IDKey : [ 32 ] byte { } ,
IDCommitment : [ 32 ] byte { } ,
}
// the public and secret keys together are 64 bytes
generatedKeys := C . GoBytes ( unsafe . Pointer ( buffer . ptr ) , C . int ( buffer . len ) )
if len ( generatedKeys ) != 64 {
return nil , errors . New ( "the generated keys are invalid" )
}
copy ( key . IDKey [ : ] , generatedKeys [ : 32 ] )
copy ( key . IDCommitment [ : ] , generatedKeys [ 32 : 64 ] )
return key , nil
}
// appendLength returns length prefixed version of the input with the following format
// [len<8>|input<var>], the len is a 8 byte value serialized in little endian
func appendLength ( input [ ] byte ) [ ] byte {
inputLen := make ( [ ] byte , 8 )
binary . LittleEndian . PutUint64 ( inputLen , uint64 ( len ( input ) ) )
return append ( inputLen , input ... )
}
// toBuffer converts the input to a buffer object that is used to communicate data with the rln lib
func toBuffer ( data [ ] byte ) C . Buffer {
dataPtr , dataLen := sliceToPtr ( data )
return C . Buffer {
ptr : dataPtr ,
len : C . uintptr_t ( dataLen ) ,
}
}
func sliceToPtr ( slice [ ] byte ) ( * C . uchar , C . int ) {
if len ( slice ) == 0 {
return nil , 0
} else {
return ( * C . uchar ) ( unsafe . Pointer ( & slice [ 0 ] ) ) , C . int ( len ( slice ) )
}
}
// Hash hashes 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 holds the hash input as a byte slice, the output slice will contain a 32 byte slice
func ( r * RLN ) Hash ( data [ ] byte ) ( MerkleNode , error ) {
// a thin layer on top of the Nim wrapper of the Poseidon hasher
lenPrefData := appendLength ( data )
hashInputBuffer := toCBufferPtr ( lenPrefData )
var output [ ] byte
out := toBuffer ( output )
if ! bool ( C . hash ( r . ptr , hashInputBuffer , & out ) ) {
return MerkleNode { } , errors . New ( "failed to hash" )
}
b := C . GoBytes ( unsafe . Pointer ( out . ptr ) , C . int ( out . len ) )
var result MerkleNode
copy ( result [ : ] , b )
return result , nil
}
// GenerateProof generates a proof for the RLN given a KeyPair and the index in a merkle tree.
// The output will containt the proof data and should be parsed as |proof<128>|root<32>|epoch<32>|share_x<32>|share_y<32>|nullifier<32>|
// integers wrapped in <> indicate value sizes in bytes
func ( r * RLN ) GenerateProof ( data [ ] byte , key MembershipKeyPair , index MembershipIndex , epoch Epoch ) ( * RateLimitProof , error ) {
input := serialize ( key . IDKey , index , epoch , data )
inputBuffer := toCBufferPtr ( input )
var output [ ] byte
out := toBuffer ( output )
if ! bool ( C . generate_rln_proof ( r . ptr , inputBuffer , & out ) ) {
return nil , errors . New ( "could not generate the proof" )
}
proofBytes := C . GoBytes ( unsafe . Pointer ( out . ptr ) , C . int ( out . len ) )
if len ( proofBytes ) != 320 {
return nil , errors . New ( "invalid proof generated" )
}
// parse the proof as [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ]
proofOffset := 128
rootOffset := proofOffset + 32
epochOffset := rootOffset + 32
shareXOffset := epochOffset + 32
shareYOffset := shareXOffset + 32
nullifierOffset := shareYOffset + 32
rlnIdentifierOffset := nullifierOffset + 32
var zkproof ZKSNARK
var proofRoot , shareX , shareY MerkleNode
var epochR Epoch
var nullifier Nullifier
var rlnIdentifier RLNIdentifier
copy ( zkproof [ : ] , proofBytes [ 0 : proofOffset ] )
copy ( proofRoot [ : ] , proofBytes [ proofOffset : rootOffset ] )
copy ( epochR [ : ] , proofBytes [ rootOffset : epochOffset ] )
copy ( shareX [ : ] , proofBytes [ epochOffset : shareXOffset ] )
copy ( shareY [ : ] , proofBytes [ shareXOffset : shareYOffset ] )
copy ( nullifier [ : ] , proofBytes [ shareYOffset : nullifierOffset ] )
copy ( rlnIdentifier [ : ] , proofBytes [ nullifierOffset : rlnIdentifierOffset ] )
return & RateLimitProof {
Proof : zkproof ,
MerkleRoot : proofRoot ,
Epoch : epochR ,
ShareX : shareX ,
ShareY : shareY ,
Nullifier : nullifier ,
RLNIdentifier : rlnIdentifier ,
} , nil
}
// Verify verifies a proof generated for the RLN.
// proof [ proof<128>| root<32>| epoch<32>| share_x<32>| share_y<32>| nullifier<32> | signal_len<8> | signal<var> ]
2022-10-07 17:56:11 -04:00
func ( r * RLN ) Verify ( data [ ] byte , proof RateLimitProof ) ( bool , error ) {
2022-09-07 15:15:43 -04:00
proofBytes := proof . serialize ( data )
proofBuf := toCBufferPtr ( proofBytes )
res := C . bool ( false )
if ! bool ( C . verify_rln_proof ( r . ptr , proofBuf , & res ) ) {
2022-10-07 17:56:11 -04:00
return false , errors . New ( "could not verify rln proof" )
2022-09-07 15:15:43 -04:00
}
2022-10-07 17:56:11 -04:00
return bool ( res ) , nil
}
func serializeRoots ( roots [ ] [ 32 ] byte ) [ ] byte {
var result [ ] byte
for _ , r := range roots {
result = append ( result , r [ : ] ... )
}
return result
}
2022-11-26 13:58:25 -04:00
func serializeCommitments ( commitments [ ] IDCommitment ) [ ] byte {
// serializes a seq of IDCommitments to a byte seq
// the serialization is based on https://github.com/status-im/nwaku/blob/37bd29fbc37ce5cf636734e7dd410b1ed27b88c8/waku/v2/protocol/waku_rln_relay/rln.nim#L142
// the order of serialization is |id_commitment_len<8>|id_commitment<var>|
var result [ ] byte
inputLen := make ( [ ] byte , 8 )
binary . LittleEndian . PutUint64 ( inputLen , uint64 ( len ( commitments ) ) )
result = append ( result , inputLen ... )
for _ , idComm := range commitments {
result = append ( result , idComm [ : ] ... )
}
return result
}
2022-10-07 17:56:11 -04:00
func ( r * RLN ) VerifyWithRoots ( data [ ] byte , proof RateLimitProof , roots [ ] [ 32 ] byte ) ( bool , error ) {
proofBytes := proof . serialize ( data )
proofBuf := toCBufferPtr ( proofBytes )
rootBytes := serializeRoots ( roots )
rootBuf := toCBufferPtr ( rootBytes )
res := C . bool ( false )
if ! bool ( C . verify_with_roots ( r . ptr , proofBuf , rootBuf , & res ) ) {
return false , errors . New ( "could not verify with roots" )
}
return bool ( res ) , nil
2022-09-07 15:15:43 -04:00
}
// InsertMember adds the member to the tree
func ( r * RLN ) InsertMember ( idComm IDCommitment ) error {
idCommBuffer := toCBufferPtr ( idComm [ : ] )
insertionSuccess := bool ( C . set_next_leaf ( r . ptr , idCommBuffer ) )
if ! insertionSuccess {
return errors . New ( "could not insert member" )
}
return nil
}
2022-11-26 13:58:25 -04:00
// Insert multiple members i.e., identity commitments starting from index
// This proc is atomic, i.e., if any of the insertions fails, all the previous insertions are rolled back
func ( r * RLN ) InsertMembers ( index MembershipIndex , idComms [ ] IDCommitment ) error {
idCommBytes := serializeCommitments ( idComms )
idCommBuffer := toCBufferPtr ( idCommBytes )
insertionSuccess := bool ( C . set_leaves_from ( r . ptr , C . uintptr_t ( index ) , idCommBuffer ) )
if ! insertionSuccess {
return errors . New ( "could not insert members" )
}
return nil
}
2022-09-07 15:15:43 -04:00
// DeleteMember removes an IDCommitment key from the tree. The index
// parameter 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
func ( r * RLN ) DeleteMember ( index MembershipIndex ) error {
deletionSuccess := bool ( C . delete_leaf ( r . ptr , C . uintptr_t ( index ) ) )
if ! deletionSuccess {
return errors . New ( "could not delete member" )
}
return nil
}
// GetMerkleRoot reads the Merkle Tree root after insertion
func ( r * RLN ) GetMerkleRoot ( ) ( MerkleNode , error ) {
var output [ ] byte
out := toBuffer ( output )
if ! bool ( C . get_root ( r . ptr , & out ) ) {
return MerkleNode { } , errors . New ( "could not get the root" )
}
b := C . GoBytes ( unsafe . Pointer ( out . ptr ) , C . int ( out . len ) )
if len ( b ) != 32 {
return MerkleNode { } , errors . New ( "wrong output size" )
}
var result MerkleNode
copy ( result [ : ] , b )
return result , nil
}
// AddAll adds members to the Merkle tree
func ( r * RLN ) AddAll ( list [ ] IDCommitment ) error {
for _ , member := range list {
if err := r . InsertMember ( member ) ; err != nil {
return err
}
}
return nil
}
// CalcMerkleRoot returns the root of the Merkle tree that is computed from the supplied list
2022-10-04 18:44:07 -04:00
func CalcMerkleRoot ( list [ ] IDCommitment ) ( MerkleNode , error ) {
rln , err := NewRLN ( )
2022-09-07 15:15:43 -04:00
if err != nil {
return MerkleNode { } , err
}
// create a Merkle tree
for _ , c := range list {
if err := rln . InsertMember ( c ) ; err != nil {
return MerkleNode { } , err
}
}
return rln . GetMerkleRoot ( )
}
// CreateMembershipList produces a list of membership key pairs and also returns the root of a Merkle tree constructed
// out of the identity commitment keys of the generated list. The output of this function is used to initialize a static
// group keys (to test waku-rln-relay in the off-chain mode)
2022-10-04 18:44:07 -04:00
func CreateMembershipList ( n int ) ( [ ] MembershipKeyPair , MerkleNode , error ) {
2022-09-07 15:15:43 -04:00
// initialize a Merkle tree
2022-10-04 18:44:07 -04:00
rln , err := NewRLN ( )
2022-09-07 15:15:43 -04:00
if err != nil {
return nil , MerkleNode { } , err
}
var output [ ] MembershipKeyPair
for i := 0 ; i < n ; i ++ {
// generate a keypair
keypair , err := rln . MembershipKeyGen ( )
if err != nil {
return nil , MerkleNode { } , err
}
output = append ( output , * keypair )
// insert the key to the Merkle tree
if err := rln . InsertMember ( keypair . IDCommitment ) ; err != nil {
return nil , MerkleNode { } , err
}
}
root , err := rln . GetMerkleRoot ( )
if err != nil {
return nil , MerkleNode { } , err
}
return output , root , nil
}