From 134f02389700529f9c8a91c9c220ef9f21517d1f Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Sat, 2 Jul 2022 10:38:11 -0400 Subject: [PATCH] test: rln --- go.mod | 1 + go.sum | 3 + rln/rln.go | 18 ++- rln/rln_test.go | 319 ++++++++++++++++++++++++++++++++---------- rln/serialize.go | 1 + rln/waku_rln_relay.go | 14 +- 6 files changed, 269 insertions(+), 87 deletions(-) diff --git a/go.mod b/go.mod index 7e9776a..8c85738 100644 --- a/go.mod +++ b/go.mod @@ -5,4 +5,5 @@ go 1.16 require ( github.com/ethereum/go-ethereum v1.10.20 github.com/golang/protobuf v1.5.2 + github.com/stretchr/testify v1.7.2 ) diff --git a/go.sum b/go.sum index fdcd3b2..168f86b 100644 --- a/go.sum +++ b/go.sum @@ -233,9 +233,11 @@ github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH6 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v0.0.0-20170224010052-a616ab194758/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= @@ -592,6 +594,7 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= diff --git a/rln/rln.go b/rln/rln.go index a7fbbb9..fd8cabd 100644 --- a/rln/rln.go +++ b/rln/rln.go @@ -142,11 +142,14 @@ func (r *RLN) Hash(data []byte) (MerkleNode, error) { func (r *RLN) GenerateProof(data []byte, key MembershipKeyPair, index MembershipIndex, epoch Epoch) (*RateLimitProof, error) { input := serialize(key.IDKey, index, epoch, data) inputBuf := toBuffer(input) + size := int(unsafe.Sizeof(inputBuf)) + in := (*C.Buffer)(C.malloc(C.size_t(size))) + *in = inputBuf var output []byte out := toBuffer(output) - if !bool(C.generate_proof(r.ptr, &inputBuf, &out)) { + if !bool(C.generate_proof(r.ptr, in, &out)) { return nil, errors.New("could not generate the proof") } @@ -192,10 +195,13 @@ func (r *RLN) GenerateProof(data []byte, key MembershipKeyPair, index Membership func (r *RLN) Verify(data []byte, proof RateLimitProof) bool { proofBytes := proof.serialize(data) proofBuf := toBuffer(proofBytes) + size := int(unsafe.Sizeof(proofBuf)) + in := (*C.Buffer)(C.malloc(C.size_t(size))) + *in = proofBuf result := uint32(0) res := C.uint(result) - if !bool(C.verify(r.ptr, &proofBuf, &res)) { + if !bool(C.verify(r.ptr, in, &res)) { return false } @@ -205,7 +211,13 @@ func (r *RLN) Verify(data []byte, proof RateLimitProof) bool { // InsertMember adds the member to the tree func (r *RLN) InsertMember(idComm IDCommitment) bool { buf := toBuffer(idComm[:]) - return bool(C.update_next_member(r.ptr, &buf)) + + size := int(unsafe.Sizeof(buf)) + in := (*C.Buffer)(C.malloc(C.size_t(size))) + *in = buf + + res := C.update_next_member(r.ptr, in) + return bool(res) } // index is the position of the id commitment key to be deleted from the tree diff --git a/rln/rln_test.go b/rln/rln_test.go index 8e445c4..668c7eb 100644 --- a/rln/rln_test.go +++ b/rln/rln_test.go @@ -1,93 +1,264 @@ -package rln_test +package rln import ( + "bytes" "encoding/hex" - "reflect" + "math" "testing" - "github.com/decanus/go-rln/rln" + "github.com/stretchr/testify/suite" ) -func TestNew(t *testing.T) { - - _, err := rln.NewRLNWithDepth(32) - if err != nil { - t.Fatal(err) - } +func TestWakuRLNRelaySuite(t *testing.T) { + suite.Run(t, new(WakuRLNRelaySuite)) } -func TestGenerateKey(t *testing.T) { - r, err := rln.NewRLNWithDepth(32) - if err != nil { - t.Fatal(err) - } - - k, err := r.MembershipKeyGen() - if err != nil { - t.Fatal(err) - } - - if reflect.DeepEqual(k.IDKey, [32]byte{}) { - t.Fatal("k.IDKey was empty") - } - - if reflect.DeepEqual(k.IDCommitment, [32]byte{}) { - t.Fatal("k.IDCommitment was empty") - } +type WakuRLNRelaySuite struct { + suite.Suite } -func TestRLN_Hash(t *testing.T) { - // This test is based on tests from: - // https://github.com/status-im/nim-waku/blob/b7998de09d1ef04599a699938da69aecfa63cc6f/tests/v2/test_waku_rln_relay.nim#L527 - - r, err := rln.NewRLNWithDepth(32) - if err != nil { - t.Fatal(err) - } - - input := byteArray(32, 1) - - output, err := r.Hash(input) - if err != nil { - t.Fatal(err) - } - - expected := "9b581442bb7fe1bc29b5cfbc29377c0b080549bcd902d1290288d93fc773bb20" - if expected != hex.EncodeToString(output[:]) { - t.Fatalf("value %x did not match expected %s", output, expected) - } +// SetupTest is used here for reinitializing the mock before every +// test function to avoid faulty execution. +func (s *WakuRLNRelaySuite) SetupTest() { } -func TestRLN_GetRoot(t *testing.T) { - // This test is based on tests from: - // https://github.com/status-im/nim-waku/blob/b7998de09d1ef04599a699938da69aecfa63cc6f/tests/v2/test_waku_rln_relay.nim#L320 - - r, err := rln.NewRLNWithDepth(32) - if err != nil { - t.Fatal(err) - } - - root1, err := r.GetMerkleRoot() - if err != nil { - t.Fatal(err) - } - - root2, err := r.GetMerkleRoot() - if err != nil { - t.Fatal(err) - } - - if hex.EncodeToString(root1[:]) != hex.EncodeToString(root2[:]) { - t.Fatalf("value %x did not match expected %x", root1, root2) - } +func (s *WakuRLNRelaySuite) TearDownTest() { + // } -func byteArray(length int, value byte) []byte { - arr := make([]byte, length) +func (s *WakuRLNRelaySuite) TestMembershipKeyGen() { + rln, err := NewRLNWithDepth(32) + s.NoError(err) - for i := 0; i < length; i++ { - arr[i] = value + key, err := rln.MembershipKeyGen() + s.NoError(err) + s.Len(key.IDKey, 32) + s.Len(key.IDCommitment, 32) + s.NotEmpty(key.IDKey) + s.NotEmpty(key.IDCommitment) + s.False(bytes.Equal(key.IDCommitment[:], make([]byte, 32))) + s.False(bytes.Equal(key.IDKey[:], make([]byte, 32))) +} + +func (s *WakuRLNRelaySuite) TestGetMerkleRoot() { + rln, err := NewRLNWithDepth(32) + s.NoError(err) + + root1, err := rln.GetMerkleRoot() + s.NoError(err) + s.Len(root1, 32) + + root2, err := rln.GetMerkleRoot() + s.NoError(err) + s.Len(root2, 32) + + s.Equal(root1, root2) +} + +func (s *WakuRLNRelaySuite) TestInsertMember() { + rln, err := NewRLNWithDepth(32) + s.NoError(err) + + keypair, err := rln.MembershipKeyGen() + s.NoError(err) + + inserted := rln.InsertMember(keypair.IDCommitment) + s.True(inserted) +} + +func (s *WakuRLNRelaySuite) TestRemoveMember() { + rln, err := NewRLNWithDepth(32) + s.NoError(err) + + deleted := rln.DeleteMember(MembershipIndex(0)) + s.True(deleted) +} + +func (s *WakuRLNRelaySuite) TestMerkleTreeConsistenceBetweenDeletionAndInsertion() { + rln, err := NewRLNWithDepth(32) + s.NoError(err) + + root1, err := rln.GetMerkleRoot() + s.NoError(err) + s.Len(root1, 32) + + keypair, err := rln.MembershipKeyGen() + s.NoError(err) + + inserted := rln.InsertMember(keypair.IDCommitment) + s.True(inserted) + + // read the Merkle Tree root after insertion + root2, err := rln.GetMerkleRoot() + s.NoError(err) + s.Len(root2, 32) + + // delete the first member + deleted_member_index := MembershipIndex(0) + deleted := rln.DeleteMember(deleted_member_index) + s.True(deleted) + + // read the Merkle Tree root after the deletion + root3, err := rln.GetMerkleRoot() + s.NoError(err) + s.Len(root3, 32) + + // the root must change after the insertion + s.NotEqual(root1, root2) + + // The initial root of the tree (empty tree) must be identical to + // the root of the tree after one insertion followed by a deletion + s.Equal(root1, root3) +} + +func (s *WakuRLNRelaySuite) TestHash() { + rln, err := NewRLNWithDepth(32) + s.NoError(err) + + // prepare the input + msg := []byte("Hello") + + hash, err := rln.Hash(msg) + s.NoError(err) + + expectedHash, _ := hex.DecodeString("efb8ac39dc22eaf377fe85b405b99ba78dbc2f3f32494add4501741df946bd1d") + s.Equal(expectedHash, hash[:]) +} + +func (s *WakuRLNRelaySuite) TestCreateListMembershipKeysAndCreateMerkleTreeFromList() { + groupSize := 100 + list, root, err := createMembershipList(groupSize) + s.NoError(err) + s.Len(list, groupSize) + s.Len(root, HASH_HEX_SIZE) // check the size of the calculated tree root +} + +func (s *WakuRLNRelaySuite) TestCheckCorrectness() { + groupKeys := STATIC_GROUP_KEYS + + // create a set of MembershipKeyPair objects from groupKeys + groupKeyPairs, err := toMembershipKeyPairs(groupKeys) + s.NoError(err) + + // extract the id commitments + var groupIDCommitments []IDCommitment + for _, c := range groupKeyPairs { + groupIDCommitments = append(groupIDCommitments, c.IDCommitment) } - return arr + // calculate the Merkle tree root out of the extracted id commitments + root, err := CalcMerkleRoot(groupIDCommitments) + s.NoError(err) + + expectedRoot, _ := hex.DecodeString(STATIC_GROUP_MERKLE_ROOT) + + s.Len(groupKeyPairs, STATIC_GROUP_SIZE) + s.Equal(expectedRoot, root[:]) +} + +func (s *WakuRLNRelaySuite) TestValidProof() { + rln, err := NewRLN() + s.NoError(err) + + memKeys, err := rln.MembershipKeyGen() + s.NoError(err) + + //peer's index in the Merkle Tree + index := 5 + + // Create a Merkle tree with random members + for i := 0; i < 10; i++ { + memberIsAdded := false + if i == index { + // insert the current peer's pk + memberIsAdded = rln.InsertMember(memKeys.IDCommitment) + } else { + // create a new key pair + memberKeys, err := rln.MembershipKeyGen() + s.NoError(err) + + memberIsAdded = rln.InsertMember(memberKeys.IDCommitment) + } + s.True(memberIsAdded) + } + + // prepare the message + msg := []byte("Hello") + + // prepare the epoch + var epoch Epoch + + // generate proof + proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(index), epoch) + s.NoError(err) + + // verify the proof + verified := rln.Verify(msg, *proofRes) + + s.True(verified) +} + +func (s *WakuRLNRelaySuite) TestInvalidProof() { + rln, err := NewRLN() + s.NoError(err) + + memKeys, err := rln.MembershipKeyGen() + s.NoError(err) + + //peer's index in the Merkle Tree + index := 5 + + // Create a Merkle tree with random members + for i := 0; i < 10; i++ { + memberIsAdded := false + if i == index { + // insert the current peer's pk + memberIsAdded = rln.InsertMember(memKeys.IDCommitment) + } else { + // create a new key pair + memberKeys, err := rln.MembershipKeyGen() + s.NoError(err) + + memberIsAdded = rln.InsertMember(memberKeys.IDCommitment) + } + s.True(memberIsAdded) + } + + // prepare the message + msg := []byte("Hello") + + // prepare the epoch + var epoch Epoch + + badIndex := 4 + + // generate proof + proofRes, err := rln.GenerateProof(msg, *memKeys, MembershipIndex(badIndex), epoch) + s.NoError(err) + + // verify the proof (should not be verified) + verified := rln.Verify(msg, *proofRes) + + s.False(verified) +} + +func (s *WakuRLNRelaySuite) TestEpochConsistency() { + // check edge cases + var epoch uint64 = math.MaxUint64 + epochBytes := ToEpoch(epoch) + decodedEpoch := epochBytes.Uint64() + + s.Equal(epoch, decodedEpoch) +} + +func (s *WakuRLNRelaySuite) TestEpochComparison() { + // check edge cases + var time1 uint64 = math.MaxUint64 + var time2 uint64 = math.MaxUint64 - 1 + + epoch1 := ToEpoch(time1) + epoch2 := ToEpoch(time2) + + s.Equal(int64(1), Diff(epoch1, epoch2)) + s.Equal(int64(-1), Diff(epoch2, epoch1)) } diff --git a/rln/serialize.go b/rln/serialize.go index ccfa64d..94000d5 100644 --- a/rln/serialize.go +++ b/rln/serialize.go @@ -16,6 +16,7 @@ func serialize(idKey IDKey, memIndex MembershipIndex, epoch Epoch, msg []byte) [ output := append(idKey[:], memIndexBytes...) output = append(output, epoch[:]...) output = append(output, lenPrefMsg...) + return output } diff --git a/rln/waku_rln_relay.go b/rln/waku_rln_relay.go index ade4fe8..8691a6c 100644 --- a/rln/waku_rln_relay.go +++ b/rln/waku_rln_relay.go @@ -32,28 +32,22 @@ type WakuRLNRelay struct { NullifierLog map[Epoch][]ProofMetadata } -func CalcMerkleRoot(list []IDCommitment) (string, error) { +func CalcMerkleRoot(list []IDCommitment) (MerkleNode, error) { // returns the root of the Merkle tree that is computed from the supplied list - // the root is in hexadecimal format rln, err := NewRLN() if err != nil { - return "", err + return MerkleNode{}, err } // create a Merkle tree for _, c := range list { if !rln.InsertMember(c) { - return "", errors.New("could not add member") + return MerkleNode{}, errors.New("could not add member") } } - root, err := rln.GetMerkleRoot() - if err != nil { - return "", err - } - - return hex.EncodeToString(root[:]), nil + return rln.GetMerkleRoot() } func createMembershipList(n int) ([][]string, string, error) {