Skip to content

Commit

Permalink
Fix RLN v2 Proof Generation
Browse files Browse the repository at this point in the history
  • Loading branch information
alrevuelta committed May 21, 2024
1 parent 480fe4e commit 33bdca3
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 53 deletions.
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ go 1.19

require (
github.com/consensys/gnark-crypto v0.12.1
github.com/rs/zerolog v1.32.0
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-20240521112113-0f66746461ae
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240124081101-5e4387508113
Expand All @@ -15,6 +17,8 @@ require (
github.com/bits-and-blooms/bitset v1.10.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
golang.org/x/sys v0.16.0 // indirect
Expand Down
24 changes: 21 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,35 @@ github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsy
github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
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/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
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/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
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/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-20240521112113-0f66746461ae h1:3vk3VfPYxOfKqlNvTFTp0B2xdnkHlh0dxIIx/ZpcDJg=
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20240521112113-0f66746461ae/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48=
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20240124081101-5e4387508113 h1:dPwc4LAWLXb4Pssej/NtGA9A0UMQwi+JafQPdnhjRWM=
Expand All @@ -26,9 +39,14 @@ github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240124081123-f90cfc88a1dc h1:
github.com/waku-org/go-zerokit-rln-x86_64 v0.0.0-20240124081123-f90cfc88a1dc/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.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/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=
84 changes: 49 additions & 35 deletions rln/rln.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,72 +181,88 @@ func (r *RLN) Poseidon(input ...[]byte) (MerkleNode, error) {
return result, nil
}

// TODO: Dont care, unused
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)
}
//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,
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) (*RateLimitProof, error) {

var userMessageLimit [32]byte
var messageId [32]byte

externalNullifierInput, err := r.Poseidon(epoch[:], RLN_IDENTIFIER[:])
if err != nil {
return nil, fmt.Errorf("could not construct the external nullifier: %w", err)
}

binary.LittleEndian.PutUint32(userMessageLimit[0:], 100)
binary.LittleEndian.PutUint32(messageId[0:], 0)

input := serialize(key.IDSecretHash, index, 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")
if len(proofBytes) != 288 {
return nil, fmt.Errorf("invalid proof generated. size: %d expected: 288",
len(proofBytes))
}

// 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
}

// Returns a RLN proof with a custom witness, so no tree is required in the RLN instance
// to calculate such proof. The witness can be created with GetMerkleProof data
// 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> ]
// TODO: No implemented yet
func (r *RLN) GenerateRLNProofWithWitness(witness RLNWitnessInput) (*RateLimitProof, error) {

proofBytes, err := r.w.GenerateRLNProofWithWitness(witness.serialize())
Expand Down Expand Up @@ -282,13 +298,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

}
Expand Down
6 changes: 3 additions & 3 deletions rln/rln_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,11 +403,11 @@ 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)
}
}

Expand Down Expand Up @@ -457,7 +457,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)
Expand Down
24 changes: 15 additions & 9 deletions rln/serialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,25 @@ 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 [32]byte,
messageId [32]byte,
externalNullifier [32]byte,
msg []byte) []byte {

memIndexBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(memIndexBytes, uint64(memIndex))

lenPrefMsg := appendLength(msg)

output := append(idKey[:], memIndexBytes...)
output = append(output, epoch[:]...)
output = append(output, userMessageLimit[:]...)
output = append(output, messageId[:]...)
output = append(output, externalNullifier[:]...)
output = append(output, lenPrefMsg...)

return output
Expand All @@ -28,7 +35,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()
Expand All @@ -37,14 +44,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
}

Expand Down
5 changes: 2 additions & 3 deletions rln/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ 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[:])
}

// 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
Expand All @@ -50,7 +51,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
Expand All @@ -59,8 +60,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 {
Expand Down

0 comments on commit 33bdca3

Please sign in to comment.