Update to RLN v2 (#21)
This commit is contained in:
parent
14960f3aff
commit
54bb48f178
7
go.mod
7
go.mod
|
@ -4,10 +4,11 @@ go 1.19
|
|||
|
||||
require (
|
||||
github.com/consensys/gnark-crypto v0.12.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240124080743-37fbb869c330
|
||||
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240124081101-5e4387508113
|
||||
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240124081123-f90cfc88a1dc
|
||||
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240523161310-d005fe7ba59c
|
||||
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240523161300-8203361a01d0
|
||||
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240523161247-6f16d12c5a86
|
||||
golang.org/x/crypto v0.18.0
|
||||
)
|
||||
|
||||
|
|
18
go.sum
18
go.sum
|
@ -3,6 +3,7 @@ github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6
|
|||
github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M=
|
||||
github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
|
@ -13,19 +14,32 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240124080743-37fbb869c330 h1:TJmn6GQ5HpxdZraZn6DjUqWy8UV+8pB4yWcsWFAngqE=
|
||||
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240124080743-37fbb869c330/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48=
|
||||
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240522110429-626138029176 h1:ezeAofaW3B6tfqS06FwKAKKXpNkimWnIwKjDU0dDPKE=
|
||||
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240522110429-626138029176/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48=
|
||||
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240523161310-d005fe7ba59c h1:/eGH8EAt5/zGfNRBQ0nJMrfZDeXRSJrm8E8uCPlsC3A=
|
||||
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240523161310-d005fe7ba59c/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48=
|
||||
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240124081101-5e4387508113 h1:dPwc4LAWLXb4Pssej/NtGA9A0UMQwi+JafQPdnhjRWM=
|
||||
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240124081101-5e4387508113/go.mod h1:7cSGUoGVIla1IpnChrLbkVjkYgdOcr7rcifEfh4ReR4=
|
||||
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240523161300-8203361a01d0 h1:IvtkZOcApOkEmHkT/drDmMtY6fdYpF7x4sesWyIURpI=
|
||||
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240523161300-8203361a01d0/go.mod h1:7cSGUoGVIla1IpnChrLbkVjkYgdOcr7rcifEfh4ReR4=
|
||||
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240124081123-f90cfc88a1dc h1:GUZlr25hXLu/PeASqm8P5dPOyD4CdfvkzyEtXEBLbr8=
|
||||
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240124081123-f90cfc88a1dc/go.mod h1:+LeEYoW5/uBUTVjtBGLEVCUe9mOYAlu5ZPkIxLOSr5Y=
|
||||
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240523161247-6f16d12c5a86 h1:PN1WSt3u/DEIn4hX5Oqrm9bm5nf5VBfenfXmbX4mg60=
|
||||
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240523161247-6f16d12c5a86/go.mod h1:+LeEYoW5/uBUTVjtBGLEVCUe9mOYAlu5ZPkIxLOSr5Y=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -5,7 +5,11 @@
|
|||
|
||||
package link
|
||||
|
||||
import r "github.com/waku-org/go-zerokit-rln-apple/rln"
|
||||
import (
|
||||
"errors"
|
||||
|
||||
r "github.com/waku-org/go-zerokit-rln-apple/rln"
|
||||
)
|
||||
|
||||
type RLNWrapper struct {
|
||||
ffi *r.RLN
|
||||
|
@ -92,7 +96,8 @@ func (i RLNWrapper) GenerateRLNProof(input []byte) ([]byte, error) {
|
|||
}
|
||||
|
||||
func (i RLNWrapper) GenerateRLNProofWithWitness(input []byte) ([]byte, error) {
|
||||
return i.ffi.GenerateRLNProofWithWitness(input)
|
||||
return nil, errors.New("not implemented")
|
||||
//return i.ffi.GenerateRLNProofWithWitness(input)
|
||||
}
|
||||
|
||||
func (i RLNWrapper) VerifyWithRoots(input []byte, roots []byte) (bool, error) {
|
||||
|
|
|
@ -4,7 +4,11 @@
|
|||
|
||||
package link
|
||||
|
||||
import r "github.com/waku-org/go-zerokit-rln-arm/rln"
|
||||
import (
|
||||
"errors"
|
||||
|
||||
r "github.com/waku-org/go-zerokit-rln-arm/rln"
|
||||
)
|
||||
|
||||
type RLNWrapper struct {
|
||||
ffi *r.RLN
|
||||
|
@ -91,7 +95,8 @@ func (i RLNWrapper) GenerateRLNProof(input []byte) ([]byte, error) {
|
|||
}
|
||||
|
||||
func (i RLNWrapper) GenerateRLNProofWithWitness(input []byte) ([]byte, error) {
|
||||
return i.ffi.GenerateRLNProofWithWitness(input)
|
||||
return nil, errors.New("not implemented")
|
||||
//return i.ffi.GenerateRLNProofWithWitness(input)
|
||||
}
|
||||
|
||||
func (i RLNWrapper) VerifyWithRoots(input []byte, roots []byte) (bool, error) {
|
||||
|
|
|
@ -5,7 +5,11 @@
|
|||
|
||||
package link
|
||||
|
||||
import r "github.com/waku-org/go-zerokit-rln-x86_64/rln"
|
||||
import (
|
||||
"errors"
|
||||
|
||||
r "github.com/waku-org/go-zerokit-rln-x86_64/rln"
|
||||
)
|
||||
|
||||
type RLNWrapper struct {
|
||||
ffi *r.RLN
|
||||
|
@ -92,7 +96,8 @@ func (i RLNWrapper) GenerateRLNProof(input []byte) ([]byte, error) {
|
|||
}
|
||||
|
||||
func (i RLNWrapper) GenerateRLNProofWithWitness(input []byte) ([]byte, error) {
|
||||
return i.ffi.GenerateRLNProofWithWitness(input)
|
||||
return nil, errors.New("not implemented")
|
||||
//return i.ffi.GenerateRLNProofWithWitness(input)
|
||||
}
|
||||
|
||||
func (i RLNWrapper) VerifyWithRoots(input []byte, roots []byte) (bool, error) {
|
||||
|
|
142
rln/rln.go
142
rln/rln.go
|
@ -14,6 +14,8 @@ import (
|
|||
// Prevents a RLN ZK proof generated for one application to be re-used in another one.
|
||||
var RLN_IDENTIFIER = [32]byte{166, 140, 43, 8, 8, 22, 206, 113, 151, 128, 118, 40, 119, 197, 218, 174, 11, 117, 84, 228, 96, 211, 212, 140, 145, 104, 146, 99, 24, 192, 217, 4}
|
||||
|
||||
var DEFAULT_USER_MESSAGE_LIMIT = uint32(10)
|
||||
|
||||
// RLN represents the context used for rln.
|
||||
type RLN struct {
|
||||
w *link.RLNWrapper
|
||||
|
@ -91,12 +93,14 @@ func (r *RLN) InitTreeWithMembers(idComms []IDCommitment) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func toIdentityCredential(generatedKeys []byte) (*IdentityCredential, error) {
|
||||
func toIdentityCredential(generatedKeys []byte, userMessageLimit uint32) (*IdentityCredential, error) {
|
||||
// add user message limit
|
||||
key := &IdentityCredential{
|
||||
IDTrapdoor: [32]byte{},
|
||||
IDNullifier: [32]byte{},
|
||||
IDSecretHash: [32]byte{},
|
||||
IDCommitment: [32]byte{},
|
||||
IDTrapdoor: [32]byte{},
|
||||
IDNullifier: [32]byte{},
|
||||
IDSecretHash: [32]byte{},
|
||||
IDCommitment: [32]byte{},
|
||||
UserMessageLimit: userMessageLimit,
|
||||
}
|
||||
|
||||
if len(generatedKeys) != 32*4 {
|
||||
|
@ -113,23 +117,45 @@ func toIdentityCredential(generatedKeys []byte) (*IdentityCredential, error) {
|
|||
|
||||
// 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) {
|
||||
// Accepts an optional parameter that sets the user message limit which defaults
|
||||
// to DEFAULT_USER_MESSAGE_LIMIT
|
||||
func (r *RLN) MembershipKeyGen(userMessageLimitParam ...uint32) (*IdentityCredential, error) {
|
||||
var userMessageLimit uint32
|
||||
if len(userMessageLimitParam) == 1 {
|
||||
userMessageLimit = userMessageLimitParam[0]
|
||||
} else if len(userMessageLimitParam) == 0 {
|
||||
userMessageLimit = DEFAULT_USER_MESSAGE_LIMIT
|
||||
} else {
|
||||
return nil, errors.New("just one user message limit is allowed")
|
||||
}
|
||||
|
||||
generatedKeys := r.w.ExtendedKeyGen()
|
||||
if generatedKeys == nil {
|
||||
return nil, errors.New("error in key generation")
|
||||
}
|
||||
return toIdentityCredential(generatedKeys)
|
||||
return toIdentityCredential(generatedKeys, userMessageLimit)
|
||||
}
|
||||
|
||||
// SeededMembershipKeyGen generates a deterministic IdentityCredential using a seed
|
||||
// that can be used for the registration into the rln membership contract.
|
||||
// Returns an error if the key generation fails
|
||||
func (r *RLN) SeededMembershipKeyGen(seed []byte) (*IdentityCredential, error) {
|
||||
// Accepts an optional parameter that sets the user message limit which defaults
|
||||
// to DEFAULT_USER_MESSAGE_LIMIT
|
||||
func (r *RLN) SeededMembershipKeyGen(seed []byte, userMessageLimitParam ...uint32) (*IdentityCredential, error) {
|
||||
var userMessageLimit uint32
|
||||
if len(userMessageLimitParam) == 1 {
|
||||
userMessageLimit = userMessageLimitParam[0]
|
||||
} else if len(userMessageLimitParam) == 0 {
|
||||
userMessageLimit = DEFAULT_USER_MESSAGE_LIMIT
|
||||
} else {
|
||||
return nil, errors.New("just one user message limit is allowed")
|
||||
}
|
||||
|
||||
generatedKeys := r.w.ExtendedSeededKeyGen(seed)
|
||||
if generatedKeys == nil {
|
||||
return nil, errors.New("error in key generation")
|
||||
}
|
||||
return toIdentityCredential(generatedKeys)
|
||||
return toIdentityCredential(generatedKeys, userMessageLimit)
|
||||
}
|
||||
|
||||
// appendLength returns length prefixed version of the input with the following format
|
||||
|
@ -181,65 +207,61 @@ func (r *RLN) Poseidon(input ...[]byte) (MerkleNode, error) {
|
|||
return result, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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 IdentityCredential, index MembershipIndex, epoch Epoch) (*RateLimitProof, error) {
|
||||
input := serialize(key.IDSecretHash, index, epoch, data)
|
||||
func (r *RLN) GenerateProof(
|
||||
data []byte,
|
||||
key IdentityCredential,
|
||||
index MembershipIndex,
|
||||
epoch Epoch,
|
||||
messageId uint32) (*RateLimitProof, error) {
|
||||
|
||||
externalNullifierInput, err := r.Poseidon(epoch[:], RLN_IDENTIFIER[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not construct the external nullifier: %w", err)
|
||||
}
|
||||
|
||||
input := serialize(key.IDSecretHash, index, key.UserMessageLimit, messageId, externalNullifierInput, data)
|
||||
proofBytes, err := r.w.GenerateRLNProof(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(proofBytes) != 320 {
|
||||
return nil, errors.New("invalid proof generated")
|
||||
expectedBytes := 288
|
||||
if len(proofBytes) != expectedBytes {
|
||||
return nil, fmt.Errorf("invalid proof generated. size: %d expected: %d",
|
||||
len(proofBytes), expectedBytes)
|
||||
}
|
||||
|
||||
// parse the proof as [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ]
|
||||
// parse proof taken from: https://github.com/vacp2p/zerokit/blob/v0.5.0/rln/src/public.rs#L750
|
||||
// [ proof<128> | root<32> | external_nullifier<32> | x<32> | y<32> | nullifier<32>]
|
||||
proofOffset := 128
|
||||
rootOffset := proofOffset + 32
|
||||
epochOffset := rootOffset + 32
|
||||
shareXOffset := epochOffset + 32
|
||||
externalNullifierOffset := rootOffset + 32
|
||||
shareXOffset := externalNullifierOffset + 32
|
||||
shareYOffset := shareXOffset + 32
|
||||
nullifierOffset := shareYOffset + 32
|
||||
rlnIdentifierOffset := nullifierOffset + 32
|
||||
|
||||
var zkproof ZKSNARK
|
||||
var proofRoot, shareX, shareY MerkleNode
|
||||
var epochR Epoch
|
||||
var externalNullifier Nullifier
|
||||
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(externalNullifier[:], proofBytes[rootOffset:externalNullifierOffset])
|
||||
copy(shareX[:], proofBytes[externalNullifierOffset: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,
|
||||
Proof: zkproof,
|
||||
MerkleRoot: proofRoot,
|
||||
ExternalNullifier: externalNullifier,
|
||||
ShareX: shareX,
|
||||
ShareY: shareY,
|
||||
Nullifier: nullifier,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -248,6 +270,8 @@ func (r *RLN) GenerateProof(data []byte, key IdentityCredential, index Membershi
|
|||
// input [ id_secret_hash<32> | num_elements<8> | path_elements<var1> | num_indexes<8> | path_indexes<var2> | x<32> | epoch<32> | rln_identifier<32> ]
|
||||
// output [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ]
|
||||
func (r *RLN) GenerateRLNProofWithWitness(witness RLNWitnessInput) (*RateLimitProof, error) {
|
||||
// TODO: Will be implemented once custom witness is supported in RLN v2
|
||||
return nil, errors.New("not implemented")
|
||||
|
||||
proofBytes, err := r.w.GenerateRLNProofWithWitness(witness.serialize())
|
||||
if err != nil {
|
||||
|
@ -282,13 +306,11 @@ func (r *RLN) GenerateRLNProofWithWitness(witness RLNWitnessInput) (*RateLimitPr
|
|||
copy(rlnIdentifier[:], proofBytes[nullifierOffset:rlnIdentifierOffset])
|
||||
|
||||
return &RateLimitProof{
|
||||
Proof: zkproof,
|
||||
MerkleRoot: proofRoot,
|
||||
Epoch: epochR,
|
||||
ShareX: shareX,
|
||||
ShareY: shareY,
|
||||
Nullifier: nullifier,
|
||||
RLNIdentifier: rlnIdentifier,
|
||||
Proof: zkproof,
|
||||
MerkleRoot: proofRoot,
|
||||
ShareX: shareX,
|
||||
ShareY: shareY,
|
||||
Nullifier: nullifier,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
@ -368,9 +390,17 @@ func (r *RLN) RecoverIDSecret(proof1 RateLimitProof, proof2 RateLimitProof) (IDS
|
|||
return result, nil
|
||||
}
|
||||
|
||||
// InsertMember adds the member to the tree
|
||||
func (r *RLN) InsertMember(idComm IDCommitment) error {
|
||||
insertionSuccess := r.w.SetNextLeaf(idComm[:])
|
||||
// InsertMember adds the member to the tree. The leaf is made of
|
||||
// the id commitment and the user message limit
|
||||
func (r *RLN) InsertMember(idComm IDCommitment, userMessageLimit uint32) error {
|
||||
userMessageLimitBytes := SerializeUint32(userMessageLimit)
|
||||
|
||||
hashedLeaf, err := r.Poseidon(idComm[:], userMessageLimitBytes[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
insertionSuccess := r.w.SetNextLeaf(hashedLeaf[:])
|
||||
if !insertionSuccess {
|
||||
return errors.New("could not insert member")
|
||||
}
|
||||
|
@ -476,9 +506,9 @@ func (r *RLN) GetMerkleProof(index MembershipIndex) (MerkleProof, error) {
|
|||
}
|
||||
|
||||
// AddAll adds members to the Merkle tree
|
||||
func (r *RLN) AddAll(list []IDCommitment) error {
|
||||
func (r *RLN) AddAll(list []IdentityCredential) error {
|
||||
for _, member := range list {
|
||||
if err := r.InsertMember(member); err != nil {
|
||||
if err := r.InsertMember(member.IDCommitment, member.UserMessageLimit); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -520,7 +550,7 @@ func CreateMembershipList(n int) ([]IdentityCredential, MerkleNode, error) {
|
|||
output = append(output, *keypair)
|
||||
|
||||
// insert the key to the Merkle tree
|
||||
if err := rln.InsertMember(keypair.IDCommitment); err != nil {
|
||||
if err := rln.InsertMember(keypair.IDCommitment, keypair.UserMessageLimit); err != nil {
|
||||
return nil, MerkleNode{}, err
|
||||
}
|
||||
}
|
||||
|
|
163
rln/rln_test.go
163
rln/rln_test.go
|
@ -76,7 +76,7 @@ func (s *RLNSuite) TestInsertMember() {
|
|||
keypair, err := rln.MembershipKeyGen()
|
||||
s.NoError(err)
|
||||
|
||||
err = rln.InsertMember(keypair.IDCommitment)
|
||||
err = rln.InsertMember(keypair.IDCommitment, keypair.UserMessageLimit)
|
||||
s.NoError(err)
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ func (s *RLNSuite) TestRemoveMember() {
|
|||
keypair, err := rln.MembershipKeyGen()
|
||||
s.NoError(err)
|
||||
|
||||
err = rln.InsertMember(keypair.IDCommitment)
|
||||
err = rln.InsertMember(keypair.IDCommitment, keypair.UserMessageLimit)
|
||||
s.NoError(err)
|
||||
|
||||
err = rln.DeleteMember(MembershipIndex(0))
|
||||
|
@ -123,7 +123,7 @@ func (s *RLNSuite) TestMerkleTreeConsistenceBetweenDeletionAndInsertion() {
|
|||
keypair, err := rln.MembershipKeyGen()
|
||||
s.NoError(err)
|
||||
|
||||
err = rln.InsertMember(keypair.IDCommitment)
|
||||
err = rln.InsertMember(keypair.IDCommitment, keypair.UserMessageLimit)
|
||||
s.NoError(err)
|
||||
|
||||
// read the Merkle Tree root after insertion
|
||||
|
@ -207,37 +207,64 @@ func (s *RLNSuite) TestCheckCorrectness() {
|
|||
s.Equal(expectedRoot, root[:])
|
||||
}
|
||||
|
||||
func (s *RLNSuite) TestGetLeaf() {
|
||||
rln, err := NewRLN()
|
||||
s.NoError(err)
|
||||
|
||||
amountLeafs := int(31)
|
||||
|
||||
for i := 0; i < amountLeafs; i++ {
|
||||
// allowed messages per epoch of the membership
|
||||
// using different values between 1 and 7
|
||||
userMessageLimit := uint32(amountLeafs%7 + 1)
|
||||
|
||||
// generate membership
|
||||
memKeys, err := rln.MembershipKeyGen(userMessageLimit)
|
||||
s.NoError(err)
|
||||
|
||||
// insert membership
|
||||
err = rln.InsertMember(memKeys.IDCommitment, memKeys.UserMessageLimit)
|
||||
s.NoError(err)
|
||||
|
||||
// retrieve the leaf
|
||||
retrievedLeaf, err := rln.GetLeaf(uint(i))
|
||||
s.NoError(err)
|
||||
|
||||
// calculate the leaf we would expect
|
||||
userMessageLimitBytes := SerializeUint32(userMessageLimit)
|
||||
hashedLeaf, err := rln.Poseidon(memKeys.IDCommitment[:], userMessageLimitBytes[:])
|
||||
s.NoError(err)
|
||||
|
||||
// assert it matches
|
||||
s.Equal(hashedLeaf, retrievedLeaf)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RLNSuite) TestValidProof() {
|
||||
rln, err := NewRLN()
|
||||
s.NoError(err)
|
||||
|
||||
memKeys, err := rln.MembershipKeyGen()
|
||||
s.NoError(err)
|
||||
// allowed messages per epoch of the membership
|
||||
userMessageLimit := uint32(10)
|
||||
|
||||
//peer's index in the Merkle Tree
|
||||
index := uint(5)
|
||||
|
||||
memKeys, err := rln.MembershipKeyGen(userMessageLimit)
|
||||
s.NoError(err)
|
||||
|
||||
// Create a Merkle tree with random members
|
||||
for i := uint(0); i < 10; i++ {
|
||||
if i == index {
|
||||
// insert the current peer's pk
|
||||
err = rln.InsertMember(memKeys.IDCommitment)
|
||||
err = rln.InsertMember(memKeys.IDCommitment, memKeys.UserMessageLimit)
|
||||
s.NoError(err)
|
||||
|
||||
fifthIndexLeaf, err := rln.GetLeaf(index)
|
||||
s.NoError(err)
|
||||
s.Equal(memKeys.IDCommitment, fifthIndexLeaf)
|
||||
} else {
|
||||
// create a new key pair
|
||||
memberKeys, err := rln.MembershipKeyGen()
|
||||
memberKeys, err := rln.MembershipKeyGen(userMessageLimit)
|
||||
s.NoError(err)
|
||||
|
||||
err = rln.InsertMember(memberKeys.IDCommitment)
|
||||
err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit)
|
||||
s.NoError(err)
|
||||
|
||||
leaf, err := rln.GetLeaf(i)
|
||||
s.NoError(err)
|
||||
s.Equal(memberKeys.IDCommitment, leaf)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -245,24 +272,75 @@ func (s *RLNSuite) TestValidProof() {
|
|||
msg := []byte("Hello")
|
||||
|
||||
// prepare the epoch
|
||||
var epoch Epoch
|
||||
var epoch Epoch = SerializeUint32(1000)
|
||||
|
||||
// generate proof
|
||||
proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(index), epoch)
|
||||
// generate multiple valid proofs for the same epoch
|
||||
for i := uint32(0); i < userMessageLimit; i++ {
|
||||
// message sequence within the epoch
|
||||
messageId := uint32(i)
|
||||
|
||||
// generate proof
|
||||
proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(index), epoch, messageId)
|
||||
s.NoError(err)
|
||||
|
||||
// verify the proof
|
||||
verified, err := rln.Verify(msg, *proofRes)
|
||||
s.NoError(err)
|
||||
s.True(verified)
|
||||
|
||||
// verify with roots
|
||||
root, err := rln.GetMerkleRoot()
|
||||
s.NoError(err)
|
||||
|
||||
verified, err = rln.Verify(msg, *proofRes, root)
|
||||
s.NoError(err)
|
||||
s.True(verified)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RLNSuite) TestProofBeyondLimit() {
|
||||
rln, err := NewRLN()
|
||||
s.NoError(err)
|
||||
|
||||
// verify the proof
|
||||
verified, err := rln.Verify(msg, *proofRes)
|
||||
s.NoError(err)
|
||||
s.True(verified)
|
||||
// allowed messages per epoch of the membership
|
||||
userMessageLimit := uint32(10)
|
||||
|
||||
// verify with roots
|
||||
root, err := rln.GetMerkleRoot()
|
||||
//peer's index in the Merkle Tree
|
||||
index := uint(5)
|
||||
|
||||
memKeys, err := rln.MembershipKeyGen(userMessageLimit)
|
||||
s.NoError(err)
|
||||
|
||||
verified, err = rln.Verify(msg, *proofRes, root)
|
||||
s.NoError(err)
|
||||
s.True(verified)
|
||||
// Create a Merkle tree with random members
|
||||
for i := uint(0); i < 10; i++ {
|
||||
if i == index {
|
||||
err = rln.InsertMember(memKeys.IDCommitment, memKeys.UserMessageLimit)
|
||||
s.NoError(err)
|
||||
} else {
|
||||
// create a new key pair
|
||||
memberKeys, err := rln.MembershipKeyGen(userMessageLimit)
|
||||
s.NoError(err)
|
||||
|
||||
err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit)
|
||||
s.NoError(err)
|
||||
}
|
||||
}
|
||||
|
||||
// prepare the message
|
||||
msg := []byte("Hello")
|
||||
|
||||
// prepare the epoch
|
||||
var epoch Epoch = SerializeUint32(876543456)
|
||||
|
||||
// TODO;:
|
||||
for i := uint32(userMessageLimit + 1); i < (userMessageLimit + 10); i++ {
|
||||
// message sequence within the epoch
|
||||
messageId := uint32(i)
|
||||
|
||||
// generate proof TODO:Errors!
|
||||
_, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(index), epoch, messageId)
|
||||
s.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RLNSuite) TestInvalidProof() {
|
||||
|
@ -279,14 +357,14 @@ func (s *RLNSuite) TestInvalidProof() {
|
|||
for i := 0; i < 10; i++ {
|
||||
if i == index {
|
||||
// insert the current peer's pk
|
||||
err := rln.InsertMember(memKeys.IDCommitment)
|
||||
err := rln.InsertMember(memKeys.IDCommitment, memKeys.UserMessageLimit)
|
||||
s.NoError(err)
|
||||
} else {
|
||||
// create a new key pair
|
||||
memberKeys, err := rln.MembershipKeyGen()
|
||||
s.NoError(err)
|
||||
|
||||
err = rln.InsertMember(memberKeys.IDCommitment)
|
||||
err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit)
|
||||
s.NoError(err)
|
||||
}
|
||||
}
|
||||
|
@ -302,8 +380,11 @@ func (s *RLNSuite) TestInvalidProof() {
|
|||
|
||||
badIndex := 4
|
||||
|
||||
// message sequence within the epoch
|
||||
messageId := uint32(1)
|
||||
|
||||
// generate proof
|
||||
proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(badIndex), epoch)
|
||||
proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(badIndex), epoch, messageId)
|
||||
s.NoError(err)
|
||||
|
||||
// verify the proof (should not be verified)
|
||||
|
@ -351,6 +432,7 @@ func (s *RLNSuite) TestGetMerkleProof() {
|
|||
}
|
||||
|
||||
func (s *RLNSuite) TestGenerateRLNProofWithWitness_VerifiesOK() {
|
||||
s.T().Skip("Skipped until proof generation with witness is implemented for RLNv2")
|
||||
treeSize := 20
|
||||
|
||||
rln, err := NewRLN()
|
||||
|
@ -363,7 +445,7 @@ func (s *RLNSuite) TestGenerateRLNProofWithWitness_VerifiesOK() {
|
|||
memberKeys, err := rln.MembershipKeyGen()
|
||||
s.NoError(err)
|
||||
|
||||
err = rln.InsertMember(memberKeys.IDCommitment)
|
||||
err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit)
|
||||
s.NoError(err)
|
||||
treeElements = append(treeElements, *memberKeys)
|
||||
}
|
||||
|
@ -393,8 +475,11 @@ func (s *RLNSuite) TestGenerateRLNProofWithWitness_VerifiesOK() {
|
|||
s.NoError(err)
|
||||
s.True(verified1)
|
||||
|
||||
// message sequence within the epoch
|
||||
messageId := uint32(1)
|
||||
|
||||
// Generate a proof without our custom witness, to ensure they match
|
||||
proofRes2, err := rln.GenerateProof(message, treeElements[memberIndex], MembershipIndex(memberIndex), epoch)
|
||||
proofRes2, err := rln.GenerateProof(message, treeElements[memberIndex], MembershipIndex(memberIndex), epoch, messageId)
|
||||
s.NoError(err)
|
||||
|
||||
// Ensure we have the same root
|
||||
|
@ -403,15 +488,17 @@ func (s *RLNSuite) TestGenerateRLNProofWithWitness_VerifiesOK() {
|
|||
// Proof generate with custom witness match the proof generate with the witness
|
||||
// from zerokit. Proof itself is not asserted, can be different.
|
||||
s.Equal(proofRes1.MerkleRoot, proofRes2.MerkleRoot)
|
||||
s.Equal(proofRes1.Epoch, proofRes2.Epoch)
|
||||
//s.Equal(proofRes1.Epoch, proofRes2.Epoch)
|
||||
s.Equal(proofRes1.ShareX, proofRes2.ShareX)
|
||||
s.Equal(proofRes1.ShareY, proofRes2.ShareY)
|
||||
s.Equal(proofRes1.Nullifier, proofRes2.Nullifier)
|
||||
s.Equal(proofRes1.RLNIdentifier, proofRes2.RLNIdentifier)
|
||||
//s.Equal(proofRes1.RLNIdentifier, proofRes2.RLNIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RLNSuite) TestGenerateRLNProofWithWitness_VerifiesNOK() {
|
||||
s.T().Skip("Skipped until proof generation with witness is implemented for RLNv2")
|
||||
|
||||
treeSize := 20
|
||||
|
||||
rln, err := NewRLN()
|
||||
|
@ -424,7 +511,7 @@ func (s *RLNSuite) TestGenerateRLNProofWithWitness_VerifiesNOK() {
|
|||
memberKeys, err := rln.MembershipKeyGen()
|
||||
s.NoError(err)
|
||||
|
||||
err = rln.InsertMember(memberKeys.IDCommitment)
|
||||
err = rln.InsertMember(memberKeys.IDCommitment, memberKeys.UserMessageLimit)
|
||||
s.NoError(err)
|
||||
treeElements = append(treeElements, *memberKeys)
|
||||
}
|
||||
|
@ -457,7 +544,7 @@ func (s *RLNSuite) TestGenerateRLNProofWithWitness_VerifiesNOK() {
|
|||
s.False(verified1)
|
||||
|
||||
// 2) Different epoch, does not verify
|
||||
proofRes1.Epoch = ToEpoch(999)
|
||||
//proofRes1.Epoch = ToEpoch(999)
|
||||
verified2, err := rln.Verify(message, *proofRes1, root)
|
||||
s.NoError(err)
|
||||
s.False(verified2)
|
||||
|
|
|
@ -8,18 +8,30 @@ import (
|
|||
)
|
||||
|
||||
// serialize converts a RateLimitProof and the data to a byte seq
|
||||
// this conversion is used in the proofGen function
|
||||
// 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> ]
|
||||
func serialize(idKey IDSecretHash, memIndex MembershipIndex, epoch Epoch, msg []byte) []byte {
|
||||
// format taken from: https://github.com/vacp2p/zerokit/blob/v0.5.0/rln/src/public.rs#L747
|
||||
// [identity_secret<32> | id_index<8> | user_message_limit<32> | message_id<32> | external_nullifier<32> | signal_len<8> | signal<var> ]
|
||||
func serialize(
|
||||
idKey IDSecretHash,
|
||||
memIndex MembershipIndex,
|
||||
userMessageLimit uint32,
|
||||
messageId uint32,
|
||||
externalNullifier [32]byte,
|
||||
msg []byte) []byte {
|
||||
|
||||
memIndexBytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(memIndexBytes, uint64(memIndex))
|
||||
|
||||
lenPrefMsg := appendLength(msg)
|
||||
|
||||
var userMessageLimitByte [32]byte
|
||||
var messageIdByte [32]byte
|
||||
binary.LittleEndian.PutUint32(userMessageLimitByte[0:], userMessageLimit)
|
||||
binary.LittleEndian.PutUint32(messageIdByte[0:], messageId)
|
||||
|
||||
output := append(idKey[:], memIndexBytes...)
|
||||
output = append(output, epoch[:]...)
|
||||
output = append(output, userMessageLimitByte[:]...)
|
||||
output = append(output, messageIdByte[:]...)
|
||||
output = append(output, externalNullifier[:]...)
|
||||
output = append(output, lenPrefMsg...)
|
||||
|
||||
return output
|
||||
|
@ -28,7 +40,7 @@ func serialize(idKey IDSecretHash, memIndex MembershipIndex, epoch Epoch, msg []
|
|||
// serialize converts a 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<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> | signal_len<8> | signal<var> ]
|
||||
// [ proof<128> | root<32> | external_nullifier<32> | x<32> | y<32> | nullifier<32> | signal_len<8> | signal<var> ]
|
||||
func (r RateLimitProof) serializeWithData(data []byte) []byte {
|
||||
lenPrefMsg := appendLength(data)
|
||||
proofBytes := r.serialize()
|
||||
|
@ -37,14 +49,13 @@ func (r RateLimitProof) serializeWithData(data []byte) []byte {
|
|||
}
|
||||
|
||||
// serialize converts a RateLimitProof to a byte seq
|
||||
// [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32>
|
||||
// [ proof<128> | root<32> | external_nullifier<32> | x<32> | y<32> | nullifier<32>]
|
||||
func (r RateLimitProof) serialize() []byte {
|
||||
proofBytes := append(r.Proof[:], r.MerkleRoot[:]...)
|
||||
proofBytes = append(proofBytes, r.Epoch[:]...)
|
||||
proofBytes = append(proofBytes, r.ExternalNullifier[:]...)
|
||||
proofBytes = append(proofBytes, r.ShareX[:]...)
|
||||
proofBytes = append(proofBytes, r.ShareY[:]...)
|
||||
proofBytes = append(proofBytes, r.Nullifier[:]...)
|
||||
proofBytes = append(proofBytes, r.RLNIdentifier[:]...)
|
||||
return proofBytes
|
||||
}
|
||||
|
||||
|
|
13
rln/types.go
13
rln/types.go
|
@ -36,12 +36,19 @@ type IdentityCredential = struct {
|
|||
// Poseidon hash function implemented in rln lib
|
||||
// more details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Membership
|
||||
IDCommitment IDCommitment `json:"idCommitment"`
|
||||
// user's allowed messages per epoch, added in RLN v2
|
||||
UserMessageLimit uint32 `json:"userMessageLimit"`
|
||||
}
|
||||
|
||||
func IdentityCredentialEquals(i IdentityCredential, i2 IdentityCredential) bool {
|
||||
return bytes.Equal(i.IDTrapdoor[:], i2.IDTrapdoor[:]) && bytes.Equal(i.IDNullifier[:], i2.IDNullifier[:]) && bytes.Equal(i.IDSecretHash[:], i2.IDSecretHash[:]) && bytes.Equal(i.IDCommitment[:], i2.IDCommitment[:])
|
||||
return bytes.Equal(i.IDTrapdoor[:], i2.IDTrapdoor[:]) &&
|
||||
bytes.Equal(i.IDNullifier[:], i2.IDNullifier[:]) &&
|
||||
bytes.Equal(i.IDSecretHash[:], i2.IDSecretHash[:]) &&
|
||||
bytes.Equal(i.IDCommitment[:], i2.IDCommitment[:]) &&
|
||||
i.UserMessageLimit == i2.UserMessageLimit
|
||||
}
|
||||
|
||||
// Equivalent plus proof: https://github.com/vacp2p/zerokit/blob/v0.5.0/rln/src/protocol.rs#L52
|
||||
type RateLimitProof struct {
|
||||
// RateLimitProof holds the public inputs to rln circuit as
|
||||
// defined in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Public-Inputs
|
||||
|
@ -50,7 +57,7 @@ type RateLimitProof struct {
|
|||
// the root of Merkle tree used for the generation of the `proof`
|
||||
MerkleRoot MerkleNode `json:"root"`
|
||||
// the epoch used for the generation of the `proof`
|
||||
Epoch Epoch `json:"epoch"`
|
||||
ExternalNullifier Nullifier `json:"external_nullifier"`
|
||||
// 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
|
||||
|
@ -59,8 +66,6 @@ type RateLimitProof struct {
|
|||
// nullifier enables linking two messages published during the same epoch
|
||||
// see details in https://hackmd.io/tMTLMYmTR5eynw2lwK9n1w?view#Nullifiers
|
||||
Nullifier Nullifier `json:"nullifier"`
|
||||
// Application specific RLN Identifier
|
||||
RLNIdentifier RLNIdentifier `json:"rlnIdentifier"`
|
||||
}
|
||||
|
||||
type MerkleProof struct {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package rln
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"hash"
|
||||
"math/big"
|
||||
|
@ -181,3 +182,10 @@ func HashToBN255(data []byte) [32]byte {
|
|||
copy(fixexLen[:], revert(frBN254Bytes[:]))
|
||||
return fixexLen
|
||||
}
|
||||
|
||||
func SerializeUint32(input uint32) [32]byte {
|
||||
var byte32Type [32]byte
|
||||
binary.LittleEndian.PutUint32(byte32Type[0:], input)
|
||||
|
||||
return byte32Type
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue