diff --git a/go.mod b/go.mod index 9e8dfa8..bbe34dc 100644 --- a/go.mod +++ b/go.mod @@ -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 @@ -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 diff --git a/go.sum b/go.sum index e5c2097..7ed07a4 100644 --- a/go.sum +++ b/go.sum @@ -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= @@ -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= diff --git a/rln/rln.go b/rln/rln.go index 80b5353..e34b66a 100644 --- a/rln/rln.go +++ b/rln/rln.go @@ -181,65 +181,80 @@ 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 } @@ -247,6 +262,7 @@ func (r *RLN) GenerateProof(data []byte, key IdentityCredential, index Membershi // to calculate such proof. The witness can be created with GetMerkleProof data // input [ id_secret_hash<32> | num_elements<8> | path_elements | num_indexes<8> | path_indexes | 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()) @@ -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 } diff --git a/rln/rln_test.go b/rln/rln_test.go index f751cdb..77f879c 100644 --- a/rln/rln_test.go +++ b/rln/rln_test.go @@ -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) } } @@ -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) diff --git a/rln/serialize.go b/rln/serialize.go index b10664b..1d03307 100644 --- a/rln/serialize.go +++ b/rln/serialize.go @@ -8,10 +8,15 @@ 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 ] -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 ] +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)) @@ -19,7 +24,9 @@ func serialize(idKey IDSecretHash, memIndex MembershipIndex, epoch Epoch, msg [] 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 @@ -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 ] +// [ proof<128> | root<32> | external_nullifier<32> | x<32> | y<32> | nullifier<32> | signal_len<8> | signal ] func (r RateLimitProof) serializeWithData(data []byte) []byte { lenPrefMsg := appendLength(data) proofBytes := r.serialize() @@ -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 } diff --git a/rln/types.go b/rln/types.go index 7d8f0dc..a1e890a 100644 --- a/rln/types.go +++ b/rln/types.go @@ -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 @@ -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 @@ -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 {