2022-08-19 16:34:07 +00:00
package rln
import "C"
import (
"encoding/binary"
"errors"
2023-04-07 18:23:07 +00:00
"fmt"
2022-10-05 22:12:51 +00:00
2023-04-07 18:23:07 +00:00
"github.com/waku-org/go-zerokit-rln/rln/link"
2022-11-04 13:57:20 +00:00
"github.com/waku-org/go-zerokit-rln/rln/resources"
2022-08-19 16:34:07 +00:00
)
// RLN represents the context used for rln.
type RLN struct {
2023-04-07 18:23:07 +00:00
w * link . RLNWrapper
2022-08-19 16:34:07 +00:00
}
2022-10-05 22:12:51 +00:00
// NewRLN generates an instance of RLN. An instance supports both zkSNARKs logics
// and Merkle tree data structure and operations. It uses a depth of 20 by default
func NewRLN ( ) ( * RLN , error ) {
wasm , err := resources . Asset ( "tree_height_20/rln.wasm" )
if err != nil {
return nil , err
}
zkey , err := resources . Asset ( "tree_height_20/rln_final.zkey" )
if err != nil {
return nil , err
}
verifKey , err := resources . Asset ( "tree_height_20/verification_key.json" )
if err != nil {
return nil , err
}
r := & RLN { }
depth := 20
2023-04-07 18:23:07 +00:00
r . w , err = link . NewWithParams ( depth , wasm , zkey , verifKey )
if err != nil {
return nil , err
2022-10-05 22:12:51 +00:00
}
return r , nil
2022-08-19 16:34:07 +00:00
}
2022-10-05 22:12:51 +00:00
// NewRLNWithParams generates an instance of RLN. An instance supports both zkSNARKs logics
// and Merkle tree data structure and operations. The parameter `depth“ indicates the depth of Merkle tree
func NewRLNWithParams ( depth int , wasm [ ] byte , zkey [ ] byte , verifKey [ ] byte ) ( * RLN , error ) {
2022-08-19 16:34:07 +00:00
r := & RLN { }
2023-04-07 18:23:07 +00:00
var err error
2022-08-19 16:34:07 +00:00
2023-04-07 18:23:07 +00:00
r . w , err = link . NewWithParams ( depth , wasm , zkey , verifKey )
if err != nil {
return nil , err
2022-08-19 16:34:07 +00:00
}
2022-10-05 22:12:51 +00:00
return r , nil
}
2022-08-19 16:34:07 +00:00
2022-10-05 22:12:51 +00:00
// 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
// The parameter “
func NewRLNWithFolder ( depth int , resourcesFolderPath string ) ( * RLN , error ) {
r := & RLN { }
2022-08-19 16:34:07 +00:00
2023-04-07 18:23:07 +00:00
var err error
2022-10-05 22:12:51 +00:00
2023-04-07 18:23:07 +00:00
r . w , err = link . NewWithFolder ( depth , resourcesFolderPath )
if err != nil {
return nil , err
2022-08-19 16:34:07 +00:00
}
return r , nil
}
2023-04-07 18:23:07 +00:00
// MembershipKeyGen generates a IdentityCredential that can be used for the
// registration into the rln membership contract. Returns an error if the key generation fails
func ( r * RLN ) MembershipKeyGen ( ) ( * IdentityCredential , error ) {
generatedKeys := r . w . ExtendedKeyGen ( )
if generatedKeys == nil {
2022-08-19 16:34:07 +00:00
return nil , errors . New ( "error in key generation" )
}
2023-04-07 18:23:07 +00:00
key := & IdentityCredential {
IDTrapdoor : [ 32 ] byte { } ,
IDNullifier : [ 32 ] byte { } ,
IDSecretHash : [ 32 ] byte { } ,
2022-08-19 16:34:07 +00:00
IDCommitment : [ 32 ] byte { } ,
}
2023-04-07 18:23:07 +00:00
if len ( generatedKeys ) != 32 * 4 {
return nil , errors . New ( "generated keys are of invalid length" )
2022-08-19 16:34:07 +00:00
}
2023-04-07 18:23:07 +00:00
copy ( key . IDTrapdoor [ : ] , generatedKeys [ : 32 ] )
copy ( key . IDNullifier [ : ] , generatedKeys [ 32 : 64 ] )
copy ( key . IDSecretHash [ : ] , generatedKeys [ 64 : 96 ] )
copy ( key . IDCommitment [ : ] , generatedKeys [ 96 : 128 ] )
2022-08-19 16:34:07 +00:00
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
2023-04-07 18:23:07 +00:00
2022-08-19 16:34:07 +00:00
func appendLength ( input [ ] byte ) [ ] byte {
inputLen := make ( [ ] byte , 8 )
binary . LittleEndian . PutUint64 ( inputLen , uint64 ( len ( input ) ) )
return append ( inputLen , input ... )
}
2023-04-07 18:23:07 +00:00
func ( r * RLN ) Sha256 ( data [ ] byte ) ( MerkleNode , error ) {
lenPrefData := appendLength ( data )
2022-08-19 16:34:07 +00:00
2023-04-07 18:23:07 +00:00
b , err := r . w . Hash ( lenPrefData )
if err != nil {
return MerkleNode { } , err
2022-08-19 16:34:07 +00:00
}
2023-04-07 18:23:07 +00:00
var result MerkleNode
copy ( result [ : ] , b )
return result , nil
2022-08-19 16:34:07 +00:00
}
2023-04-07 18:23:07 +00:00
func ( r * RLN ) Poseidon ( input ... [ ] byte ) ( MerkleNode , error ) {
data := serializeSlice ( input )
2022-08-19 16:34:07 +00:00
2023-04-07 18:23:07 +00:00
inputLen := make ( [ ] byte , 8 )
binary . LittleEndian . PutUint64 ( inputLen , uint64 ( len ( input ) ) )
2022-08-19 16:34:07 +00:00
2023-04-07 18:23:07 +00:00
lenPrefData := append ( inputLen , data ... )
2022-08-19 16:34:07 +00:00
2023-04-07 18:23:07 +00:00
b , err := r . w . PoseidonHash ( lenPrefData )
if err != nil {
return MerkleNode { } , err
2022-08-19 16:34:07 +00:00
}
var result MerkleNode
copy ( result [ : ] , b )
return result , nil
}
2023-04-07 18:23:07 +00:00
func ( r * RLN ) ExtractMetadata ( proof RateLimitProof ) ( ProofMetadata , error ) {
externalNullifierRes , err := r . Poseidon ( proof . Epoch [ : ] , proof . RLNIdentifier [ : ] )
if err != nil {
return ProofMetadata { } , fmt . Errorf ( "could not construct the external nullifier: %w" , err )
}
return ProofMetadata {
Nullifier : proof . Nullifier ,
ShareX : proof . ShareX ,
ShareY : proof . ShareY ,
ExternalNullifier : externalNullifierRes ,
} , nil
}
2022-08-19 16:34:07 +00:00
// GenerateProof generates a proof for the RLN given a KeyPair and the index in a merkle tree.
2022-10-05 22:12:51 +00:00
// 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>|
2022-08-19 16:34:07 +00:00
// integers wrapped in <> indicate value sizes in bytes
2023-04-07 18:23:07 +00:00
func ( r * RLN ) GenerateProof ( data [ ] byte , key IdentityCredential , index MembershipIndex , epoch Epoch ) ( * RateLimitProof , error ) {
input := serialize ( key . IDSecretHash , index , epoch , data )
proofBytes , err := r . w . GenerateRLNProof ( input )
if err != nil {
return nil , err
2022-08-19 16:34:07 +00:00
}
2022-10-05 22:12:51 +00:00
if len ( proofBytes ) != 320 {
2022-08-19 16:34:07 +00:00
return nil , errors . New ( "invalid proof generated" )
}
2022-10-05 22:12:51 +00:00
// parse the proof as [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ]
proofOffset := 128
2022-08-19 16:34:07 +00:00
rootOffset := proofOffset + 32
epochOffset := rootOffset + 32
shareXOffset := epochOffset + 32
shareYOffset := shareXOffset + 32
nullifierOffset := shareYOffset + 32
2022-10-05 22:12:51 +00:00
rlnIdentifierOffset := nullifierOffset + 32
2022-08-19 16:34:07 +00:00
var zkproof ZKSNARK
var proofRoot , shareX , shareY MerkleNode
var epochR Epoch
var nullifier Nullifier
2022-10-05 22:12:51 +00:00
var rlnIdentifier RLNIdentifier
2022-08-19 16:34:07 +00:00
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 ] )
2022-10-05 22:12:51 +00:00
copy ( rlnIdentifier [ : ] , proofBytes [ nullifierOffset : rlnIdentifierOffset ] )
2022-08-19 16:34:07 +00:00
return & RateLimitProof {
2022-10-05 22:12:51 +00:00
Proof : zkproof ,
MerkleRoot : proofRoot ,
Epoch : epochR ,
ShareX : shareX ,
ShareY : shareY ,
Nullifier : nullifier ,
RLNIdentifier : rlnIdentifier ,
2022-08-19 16:34:07 +00:00
} , nil
}
2023-04-07 18:23:07 +00:00
func serialize32 ( roots [ ] [ 32 ] byte ) [ ] byte {
var result [ ] byte
for _ , r := range roots {
result = append ( result , r [ : ] ... )
2022-08-19 16:34:07 +00:00
}
2023-04-07 18:23:07 +00:00
return result
2022-11-04 13:57:20 +00:00
}
2023-04-07 18:23:07 +00:00
func serializeSlice ( roots [ ] [ ] byte ) [ ] byte {
2022-11-04 13:57:20 +00:00
var result [ ] byte
for _ , r := range roots {
result = append ( result , r [ : ] ... )
}
return result
}
2022-11-28 13:40:21 +00: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
}
2023-04-07 18:23:07 +00:00
// proof [ proof<128>| root<32>| epoch<32>| share_x<32>| share_y<32>| nullifier<32> | signal_len<8> | signal<var> ]
// validRoots should contain a sequence of roots in the acceptable windows.
// As default, it is set to an empty sequence of roots. This implies that the validity check for the proof's root is skipped
func ( r * RLN ) Verify ( data [ ] byte , proof RateLimitProof , roots ... [ 32 ] byte ) ( bool , error ) {
2022-11-04 13:57:20 +00:00
proofBytes := proof . serialize ( data )
2023-04-07 18:23:07 +00:00
rootBytes := serialize32 ( roots )
2022-11-04 13:57:20 +00:00
2023-04-07 18:23:07 +00:00
res , err := r . w . VerifyWithRoots ( proofBytes , rootBytes )
if err != nil {
return false , err
2022-11-04 13:57:20 +00:00
}
return bool ( res ) , nil
2022-08-19 16:34:07 +00:00
}
// InsertMember adds the member to the tree
2022-10-05 22:12:51 +00:00
func ( r * RLN ) InsertMember ( idComm IDCommitment ) error {
2023-04-07 18:23:07 +00:00
insertionSuccess := r . w . SetNextLeaf ( idComm [ : ] )
2022-10-05 22:12:51 +00:00
if ! insertionSuccess {
return errors . New ( "could not insert member" )
}
return nil
2022-08-19 16:34:07 +00:00
}
2022-11-28 13:40:21 +00: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 )
2023-04-07 18:23:07 +00:00
insertionSuccess := r . w . SetLeavesFrom ( index , idCommBytes )
2022-11-28 13:40:21 +00:00
if ! insertionSuccess {
return errors . New ( "could not insert members" )
}
return nil
}
2022-08-19 16:34:07 +00: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
2022-10-05 22:12:51 +00:00
func ( r * RLN ) DeleteMember ( index MembershipIndex ) error {
2023-04-07 18:23:07 +00:00
deletionSuccess := r . w . DeleteLeaf ( index )
2022-10-05 22:12:51 +00:00
if ! deletionSuccess {
return errors . New ( "could not delete member" )
}
return nil
2022-08-19 16:34:07 +00:00
}
// GetMerkleRoot reads the Merkle Tree root after insertion
func ( r * RLN ) GetMerkleRoot ( ) ( MerkleNode , error ) {
2023-04-07 18:23:07 +00:00
b , err := r . w . GetRoot ( )
if err != nil {
return MerkleNode { } , err
2022-08-19 16:34:07 +00:00
}
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
2022-10-05 22:12:51 +00:00
func ( r * RLN ) AddAll ( list [ ] IDCommitment ) error {
2022-08-19 16:34:07 +00:00
for _ , member := range list {
2022-10-05 22:12:51 +00:00
if err := r . InsertMember ( member ) ; err != nil {
return err
2022-08-19 16:34:07 +00:00
}
}
2022-10-05 22:12:51 +00:00
return nil
2022-08-19 16:34:07 +00:00
}
// CalcMerkleRoot returns the root of the Merkle tree that is computed from the supplied list
2022-10-05 22:12:51 +00:00
func CalcMerkleRoot ( list [ ] IDCommitment ) ( MerkleNode , error ) {
rln , err := NewRLN ( )
2022-08-19 16:34:07 +00:00
if err != nil {
return MerkleNode { } , err
}
// create a Merkle tree
for _ , c := range list {
2022-10-05 22:12:51 +00:00
if err := rln . InsertMember ( c ) ; err != nil {
return MerkleNode { } , err
2022-08-19 16:34:07 +00:00
}
}
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)
2023-04-07 18:23:07 +00:00
func CreateMembershipList ( n int ) ( [ ] IdentityCredential , MerkleNode , error ) {
2022-08-19 16:34:07 +00:00
// initialize a Merkle tree
2022-10-05 22:12:51 +00:00
rln , err := NewRLN ( )
2022-08-19 16:34:07 +00:00
if err != nil {
return nil , MerkleNode { } , err
}
2023-04-07 18:23:07 +00:00
var output [ ] IdentityCredential
2022-08-19 16:34:07 +00:00
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
2022-10-05 22:12:51 +00:00
if err := rln . InsertMember ( keypair . IDCommitment ) ; err != nil {
return nil , MerkleNode { } , err
2022-08-19 16:34:07 +00:00
}
}
root , err := rln . GetMerkleRoot ( )
if err != nil {
return nil , MerkleNode { } , err
}
return output , root , nil
}