From 9c5d1e88b1e6949fff8521c9ef578ae53e0f1736 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Tue, 4 Apr 2023 11:44:28 -0400 Subject: [PATCH] refactor: proof generation and merkleroot tracking --- waku/v2/node/wakunode2_rln.go | 2 +- waku/v2/protocol/rln/common.go | 62 +++++ .../{main.go => group_manager.go} | 0 .../rln/group_manager/root_tracker.go | 34 +++ .../rln/group_manager/static/static.go | 90 ++++--- .../rln/group_manager/static/utils.go | 32 +++ waku/v2/protocol/rln/types.go | 10 - waku/v2/protocol/rln/waku_rln_relay.go | 238 +++++++----------- 8 files changed, 272 insertions(+), 196 deletions(-) create mode 100644 waku/v2/protocol/rln/common.go rename waku/v2/protocol/rln/group_manager/{main.go => group_manager.go} (100%) create mode 100644 waku/v2/protocol/rln/group_manager/root_tracker.go create mode 100644 waku/v2/protocol/rln/group_manager/static/utils.go delete mode 100644 waku/v2/protocol/rln/types.go diff --git a/waku/v2/node/wakunode2_rln.go b/waku/v2/node/wakunode2_rln.go index 49011e02..420836f4 100644 --- a/waku/v2/node/wakunode2_rln.go +++ b/waku/v2/node/wakunode2_rln.go @@ -73,7 +73,7 @@ func (w *WakuNode) mountRlnRelay(ctx context.Context) error { return err } - expectedRoot, err := static.ToBytes32LE(r.STATIC_GROUP_MERKLE_ROOT) + expectedRoot, err := r.ToBytes32LE(r.STATIC_GROUP_MERKLE_ROOT) if err != nil { return err } diff --git a/waku/v2/protocol/rln/common.go b/waku/v2/protocol/rln/common.go new file mode 100644 index 00000000..6633608d --- /dev/null +++ b/waku/v2/protocol/rln/common.go @@ -0,0 +1,62 @@ +package rln + +import ( + "github.com/ethereum/go-ethereum/core/types" + "github.com/waku-org/go-waku/waku/v2/protocol/pb" + "github.com/waku-org/go-zerokit-rln/rln" +) + +type MessageValidationResult int + +const ( + MessageValidationResult_Unknown MessageValidationResult = iota + MessageValidationResult_Valid + MessageValidationResult_Invalid + MessageValidationResult_Spam +) + +// the maximum clock difference between peers in seconds +const MAX_CLOCK_GAP_SECONDS = 20 + +// maximum allowed gap between the epochs of messages' RateLimitProofs +const MAX_EPOCH_GAP = int64(MAX_CLOCK_GAP_SECONDS / rln.EPOCH_UNIT_SECONDS) + +// Acceptable roots for merkle root validation of incoming messages +const AcceptableRootWindowSize = 5 + +type AppInfo struct { + Application string + AppIdentifier string + Version string +} + +type RegistrationHandler = func(tx *types.Transaction) + +type SpamHandler = func(message *pb.WakuMessage) error + +func toRLNSignal(wakuMessage *pb.WakuMessage) []byte { + if wakuMessage == nil { + return []byte{} + } + + contentTopicBytes := []byte(wakuMessage.ContentTopic) + return append(wakuMessage.Payload, contentTopicBytes...) +} + +func toRateLimitProof(msg *pb.WakuMessage) *rln.RateLimitProof { + if msg == nil || msg.RateLimitProof == nil { + return nil + } + + result := &rln.RateLimitProof{ + Proof: rln.ZKSNARK(rln.Bytes128(msg.RateLimitProof.Proof)), + MerkleRoot: rln.MerkleNode(rln.Bytes32(msg.RateLimitProof.MerkleRoot)), + Epoch: rln.Epoch(rln.Bytes32(msg.RateLimitProof.Epoch)), + ShareX: rln.MerkleNode(rln.Bytes32(msg.RateLimitProof.ShareX)), + ShareY: rln.MerkleNode(rln.Bytes32(msg.RateLimitProof.ShareY)), + Nullifier: rln.Nullifier(rln.Bytes32(msg.RateLimitProof.Nullifier)), + RLNIdentifier: rln.RLNIdentifier(rln.Bytes32(msg.RateLimitProof.RlnIdentifier)), + } + + return result +} diff --git a/waku/v2/protocol/rln/group_manager/main.go b/waku/v2/protocol/rln/group_manager/group_manager.go similarity index 100% rename from waku/v2/protocol/rln/group_manager/main.go rename to waku/v2/protocol/rln/group_manager/group_manager.go diff --git a/waku/v2/protocol/rln/group_manager/root_tracker.go b/waku/v2/protocol/rln/group_manager/root_tracker.go new file mode 100644 index 00000000..4d78642c --- /dev/null +++ b/waku/v2/protocol/rln/group_manager/root_tracker.go @@ -0,0 +1,34 @@ +package group_manager + +import "github.com/waku-org/go-zerokit-rln/rln" + +type MerkleRootTracker struct { + rln *rln.RLN + acceptableRootWindowSize int + validMerkleRoots []rln.MerkleNode +} + +func NewMerkleRootTracker(acceptableRootWindowSize int, rlnInstance *rln.RLN) *MerkleRootTracker { + return &MerkleRootTracker{ + acceptableRootWindowSize: acceptableRootWindowSize, + rln: rlnInstance, + } +} + +func (m *MerkleRootTracker) Sync() error { + root, err := m.rln.GetMerkleRoot() + if err != nil { + return err + } + + m.validMerkleRoots = append(m.validMerkleRoots, root) + if len(m.validMerkleRoots) > m.acceptableRootWindowSize { + m.validMerkleRoots = m.validMerkleRoots[1:] + } + + return nil +} + +func (m *MerkleRootTracker) Roots() []rln.MerkleNode { + return m.validMerkleRoots +} diff --git a/waku/v2/protocol/rln/group_manager/static/static.go b/waku/v2/protocol/rln/group_manager/static/static.go index 700989dc..fe2b467b 100644 --- a/waku/v2/protocol/rln/group_manager/static/static.go +++ b/waku/v2/protocol/rln/group_manager/static/static.go @@ -4,6 +4,7 @@ import ( "context" "errors" + "github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager" "github.com/waku-org/go-zerokit-rln/rln" "go.uber.org/zap" ) @@ -12,34 +13,51 @@ type StaticGroupManager struct { rln *rln.RLN log *zap.Logger - group []rln.IDCommitment + identityCredential *rln.IdentityCredential + membershipIndex *rln.MembershipIndex + + group []rln.IDCommitment + rootTracker *group_manager.MerkleRootTracker } func NewStaticGroupManager( group []rln.IDCommitment, - memKeyPair rln.IdentityCredential, - memIndex rln.MembershipIndex, + identityCredential rln.IdentityCredential, + index rln.MembershipIndex, log *zap.Logger, ) (*StaticGroupManager, error) { // check the peer's index and the inclusion of user's identity commitment in the group - if memKeyPair.IDCommitment != group[int(memIndex)] { + if identityCredential.IDCommitment != group[int(index)] { return nil, errors.New("peer's IDCommitment does not match commitment in group") } return &StaticGroupManager{ - log: log.Named("rln-static"), - group: group, + log: log.Named("rln-static"), + group: group, + identityCredential: &identityCredential, + membershipIndex: &index, }, nil } -func (gm *StaticGroupManager) Start(ctx context.Context, rln *rln.RLN) error { +func (gm *StaticGroupManager) Start(ctx context.Context, rlnInstance *rln.RLN, rootTracker *group_manager.MerkleRootTracker) error { gm.log.Info("mounting rln-relay in off-chain/static mode") - gm.rln = rln + gm.rln = rlnInstance + gm.rootTracker = rootTracker + + err := rootTracker.Sync() + if err != nil { + return err + } // add members to the Merkle tree for _, member := range gm.group { - if err := rln.InsertMember(member); err != nil { + if err := rlnInstance.InsertMember(member); err != nil { + return err + } + + err = rootTracker.Sync() + if err != nil { return err } } @@ -49,39 +67,39 @@ func (gm *StaticGroupManager) Start(ctx context.Context, rln *rln.RLN) error { return nil } -func Setup(index rln.MembershipIndex) ([]rln.IDCommitment, rln.IdentityCredential, error) { - // static group - groupKeys := rln.STATIC_GROUP_KEYS - groupSize := rln.STATIC_GROUP_SIZE - - // validate the user-supplied membership index - if index >= rln.MembershipIndex(groupSize) { - return nil, rln.IdentityCredential{}, errors.New("wrong membership index") - } - - // create a sequence of MembershipKeyPairs from the group keys (group keys are in string format) - credentials, err := rln.ToIdentityCredentials(groupKeys) +func (gm *StaticGroupManager) InsertMember(pubkey rln.IDCommitment) error { + gm.log.Debug("a new key is added", zap.Binary("pubkey", pubkey[:])) + // assuming all the members arrive in order + err := gm.rln.InsertMember(pubkey) if err != nil { - return nil, rln.IdentityCredential{}, errors.New("invalid data on group keypairs") + gm.log.Error("inserting member into merkletree", zap.Error(err)) + return err } - // extract id commitment keys - var groupOpt []rln.IDCommitment - for _, c := range credentials { - groupOpt = append(groupOpt, c.IDCommitment) + err = gm.rootTracker.Sync() + if err != nil { + return err } - return groupOpt, credentials[index], nil + return nil +} + +func (gm *StaticGroupManager) IdentityCredentials() (rln.IdentityCredential, error) { + if gm.identityCredential == nil { + return rln.IdentityCredential{}, errors.New("identity credential has not been setup") + } + + return *gm.identityCredential, nil +} + +func (gm *StaticGroupManager) MembershipIndex() (rln.MembershipIndex, error) { + if gm.membershipIndex == nil { + return 0, errors.New("membership index has not been setup") + } + + return *gm.membershipIndex, nil } func (gm *StaticGroupManager) Stop() { - // TODO: -} - -func (gm *StaticGroupManager) GenerateProof(input []byte, epoch rln.Epoch) (*rln.RateLimitProof, error) { - return nil, nil // TODO -} - -func (gm *StaticGroupManager) VerifyProof(input []byte, msgProof *rln.RateLimitProof, ValidMerkleRoots ...rln.MerkleNode) (bool, error) { - return false, nil + // Do nothing } diff --git a/waku/v2/protocol/rln/group_manager/static/utils.go b/waku/v2/protocol/rln/group_manager/static/utils.go new file mode 100644 index 00000000..1112473b --- /dev/null +++ b/waku/v2/protocol/rln/group_manager/static/utils.go @@ -0,0 +1,32 @@ +package static + +import ( + "errors" + + "github.com/waku-org/go-zerokit-rln/rln" +) + +func Setup(index rln.MembershipIndex) ([]rln.IDCommitment, rln.IdentityCredential, error) { + // static group + groupKeys := rln.STATIC_GROUP_KEYS + groupSize := rln.STATIC_GROUP_SIZE + + // validate the user-supplied membership index + if index >= rln.MembershipIndex(groupSize) { + return nil, rln.IdentityCredential{}, errors.New("wrong membership index") + } + + // create a sequence of MembershipKeyPairs from the group keys (group keys are in string format) + credentials, err := rln.ToIdentityCredentials(groupKeys) + if err != nil { + return nil, rln.IdentityCredential{}, errors.New("invalid data on group keypairs") + } + + // extract id commitment keys + var groupOpt []rln.IDCommitment + for _, c := range credentials { + groupOpt = append(groupOpt, c.IDCommitment) + } + + return groupOpt, credentials[index], nil +} diff --git a/waku/v2/protocol/rln/types.go b/waku/v2/protocol/rln/types.go deleted file mode 100644 index c6327259..00000000 --- a/waku/v2/protocol/rln/types.go +++ /dev/null @@ -1,10 +0,0 @@ -package rln - -type MessageValidationResult int - -const ( - MessageValidationResult_Unknown MessageValidationResult = iota - MessageValidationResult_Valid - MessageValidationResult_Invalid - MessageValidationResult_Spam -) diff --git a/waku/v2/protocol/rln/waku_rln_relay.go b/waku/v2/protocol/rln/waku_rln_relay.go index 4e5522a9..b692582a 100644 --- a/waku/v2/protocol/rln/waku_rln_relay.go +++ b/waku/v2/protocol/rln/waku_rln_relay.go @@ -8,37 +8,18 @@ import ( "sync" "time" - "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/peer" "github.com/waku-org/go-waku/waku/v2/protocol/pb" "github.com/waku-org/go-waku/waku/v2/protocol/relay" + "github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager" "github.com/waku-org/go-waku/waku/v2/timesource" - r "github.com/waku-org/go-zerokit-rln/rln" + "github.com/waku-org/go-zerokit-rln/rln" "go.uber.org/zap" proto "google.golang.org/protobuf/proto" ) -// the maximum clock difference between peers in seconds -const MAX_CLOCK_GAP_SECONDS = 20 - -// maximum allowed gap between the epochs of messages' RateLimitProofs -const MAX_EPOCH_GAP = int64(MAX_CLOCK_GAP_SECONDS / r.EPOCH_UNIT_SECONDS) - -// Acceptable roots for merkle root validation of incoming messages -const AcceptableRootWindowSize = 5 - -type AppInfo struct { - Application string - AppIdentifier string - Version string -} - -type RegistrationHandler = func(tx *types.Transaction) - -type SpamHandler = func(message *pb.WakuMessage) error - var RLNAppInfo = AppInfo{ Application: "go-waku-rln-relay", AppIdentifier: "01234567890abcdef", @@ -46,30 +27,29 @@ var RLNAppInfo = AppInfo{ } type GroupManager interface { - Start(ctx context.Context, rln *r.RLN) error - GenerateProof(input []byte, epoch r.Epoch) (*r.RateLimitProof, error) - VerifyProof(input []byte, msgProof *r.RateLimitProof, ValidMerkleRoots ...r.MerkleNode) (bool, error) + Start(ctx context.Context, rln *rln.RLN, rootTracker *group_manager.MerkleRootTracker) error + IdentityCredentials() (rln.IdentityCredential, error) + MembershipIndex() (rln.MembershipIndex, error) Stop() } type WakuRLNRelay struct { + relay *relay.WakuRelay timesource timesource.Timesource groupManager GroupManager + rootTracker *group_manager.MerkleRootTracker // pubsubTopic is the topic for which rln relay is mounted pubsubTopic string contentTopic string - relay *relay.WakuRelay spamHandler SpamHandler - RLN *r.RLN - - validMerkleRoots []r.MerkleNode + RLN *rln.RLN // the log of nullifiers and Shamir shares of the past messages grouped per epoch nullifierLogLock sync.RWMutex - nullifierLog map[r.Nullifier][]r.ProofMetadata + nullifierLog map[rln.Nullifier][]rln.ProofMetadata log *zap.Logger } @@ -82,7 +62,7 @@ func New( spamHandler SpamHandler, timesource timesource.Timesource, log *zap.Logger) (*WakuRLNRelay, error) { - rlnInstance, err := r.NewRLN() + rlnInstance, err := rln.NewRLN() if err != nil { return nil, err } @@ -91,65 +71,50 @@ func New( rlnPeer := &WakuRLNRelay{ RLN: rlnInstance, groupManager: groupManager, + rootTracker: group_manager.NewMerkleRootTracker(AcceptableRootWindowSize, rlnRelay.RLN), pubsubTopic: pubsubTopic, contentTopic: contentTopic, relay: relay, spamHandler: spamHandler, log: log, timesource: timesource, - nullifierLog: make(map[r.MerkleNode][]r.ProofMetadata), + nullifierLog: make(map[rln.MerkleNode][]rln.ProofMetadata), } - // TODO: pass RLN to group manager - - root, err := rlnPeer.RLN.GetMerkleRoot() - if err != nil { - return nil, err - } - - rlnPeer.validMerkleRoots = append(rlnPeer.validMerkleRoots, root) - return rlnPeer, nil } -func (rln *WakuRLNRelay) Start(ctx context.Context) error { - err := rln.groupManager.Start(ctx, rln.RLN) +func (rlnRelay *WakuRLNRelay) Start(ctx context.Context) error { + err := rlnRelay.groupManager.Start(ctx, rlnRelay.RLN, rlnRelay.rootTracker) if err != nil { return err } - root, err := rln.RLN.GetMerkleRoot() - if err != nil { - return err - } - - rln.validMerkleRoots = append(rln.validMerkleRoots, root) - // adds a topic validator for the supplied pubsub topic at the relay protocol // messages published on this pubsub topic will be relayed upon a successful validation, otherwise they will be dropped // the topic validator checks for the correct non-spamming proof of the message - err = rln.addValidator(rln.relay, rln.pubsubTopic, rln.contentTopic, rln.spamHandler) + err = rlnRelay.addValidator(rlnRelay.relay, rlnRelay.pubsubTopic, rlnRelay.contentTopic, rlnRelay.spamHandler) if err != nil { return err } - log.Info("rln relay topic validator mounted", zap.String("pubsubTopic", rln.pubsubTopic), zap.String("contentTopic", rln.contentTopic)) + log.Info("rln relay topic validator mounted", zap.String("pubsubTopic", rlnRelay.pubsubTopic), zap.String("contentTopic", rlnRelay.contentTopic)) return nil } -func (rln *WakuRLNRelay) Stop() { - rln.groupManager.Stop() +func (rlnRelay *WakuRLNRelay) Stop() { + rlnRelay.groupManager.Stop() } -func (rln *WakuRLNRelay) HasDuplicate(proofMD r.ProofMetadata) (bool, error) { +func (rlnRelay *WakuRLNRelay) HasDuplicate(proofMD rln.ProofMetadata) (bool, error) { // returns true if there is another message in the `nullifierLog` of the `rlnPeer` with the same // epoch and nullifier as `msg`'s epoch and nullifier but different Shamir secret shares // otherwise, returns false - rln.nullifierLogLock.RLock() - proofs, ok := rln.nullifierLog[proofMD.ExternalNullifier] - rln.nullifierLogLock.RUnlock() + rlnRelay.nullifierLogLock.RLock() + proofs, ok := rlnRelay.nullifierLog[proofMD.ExternalNullifier] + rlnRelay.nullifierLogLock.RUnlock() // check if the epoch exists if !ok { @@ -175,14 +140,14 @@ func (rln *WakuRLNRelay) HasDuplicate(proofMD r.ProofMetadata) (bool, error) { return matched, nil } -func (rln *WakuRLNRelay) updateLog(proofMD r.ProofMetadata) (bool, error) { - rln.nullifierLogLock.Lock() - defer rln.nullifierLogLock.Unlock() - proofs, ok := rln.nullifierLog[proofMD.ExternalNullifier] +func (rlnRelay *WakuRLNRelay) updateLog(proofMD rln.ProofMetadata) (bool, error) { + rlnRelay.nullifierLogLock.Lock() + defer rlnRelay.nullifierLogLock.Unlock() + proofs, ok := rlnRelay.nullifierLog[proofMD.ExternalNullifier] // check if the epoch exists if !ok { - rln.nullifierLog[proofMD.ExternalNullifier] = []r.ProofMetadata{proofMD} + rlnRelay.nullifierLog[proofMD.ExternalNullifier] = []rln.ProofMetadata{proofMD} return true, nil } @@ -196,12 +161,12 @@ func (rln *WakuRLNRelay) updateLog(proofMD r.ProofMetadata) (bool, error) { // add proofMD to the log proofs = append(proofs, proofMD) - rln.nullifierLog[proofMD.ExternalNullifier] = proofs + rlnRelay.nullifierLog[proofMD.ExternalNullifier] = proofs return true, nil } -func (rln *WakuRLNRelay) ValidateMessage(msg *pb.WakuMessage, optionalTime *time.Time) (MessageValidationResult, error) { +func (rlnRelay *WakuRLNRelay) ValidateMessage(msg *pb.WakuMessage, optionalTime *time.Time) (MessageValidationResult, error) { // validate the supplied `msg` based on the waku-rln-relay routing protocol i.e., // the `msg`'s epoch is within MAX_EPOCH_GAP of the current epoch // the `msg` has valid rate limit proof @@ -214,77 +179,79 @@ func (rln *WakuRLNRelay) ValidateMessage(msg *pb.WakuMessage, optionalTime *time // checks if the `msg`'s epoch is far from the current epoch // it corresponds to the validation of rln external nullifier - var epoch r.Epoch + var epoch rln.Epoch if optionalTime != nil { - epoch = r.CalcEpoch(*optionalTime) + epoch = rln.CalcEpoch(*optionalTime) } else { // get current rln epoch - epoch = r.CalcEpoch(rln.timesource.Now()) + epoch = rln.CalcEpoch(rlnRelay.timesource.Now()) } - msgProof := ToRateLimitProof(msg) + msgProof := toRateLimitProof(msg) if msgProof == nil { // message does not contain a proof - rln.log.Debug("invalid message: message does not contain a proof") + rlnRelay.log.Debug("invalid message: message does not contain a proof") return MessageValidationResult_Invalid, nil } - proofMD, err := r.ExtractMetadata(*msgProof) + proofMD, err := rln.ExtractMetadata(*msgProof) if err != nil { - rln.log.Debug("could not extract metadata", zap.Error(err)) + rlnRelay.log.Debug("could not extract metadata", zap.Error(err)) return MessageValidationResult_Invalid, nil } // calculate the gaps and validate the epoch - gap := r.Diff(epoch, msgProof.Epoch) + gap := rln.Diff(epoch, msgProof.Epoch) if int64(math.Abs(float64(gap))) > MAX_EPOCH_GAP { // message's epoch is too old or too ahead // accept messages whose epoch is within +-MAX_EPOCH_GAP from the current epoch - rln.log.Debug("invalid message: epoch gap exceeds a threshold", zap.Int64("gap", gap)) + rlnRelay.log.Debug("invalid message: epoch gap exceeds a threshold", zap.Int64("gap", gap)) return MessageValidationResult_Invalid, nil } - // verify the proof - contentTopicBytes := []byte(msg.ContentTopic) - input := append(msg.Payload, contentTopicBytes...) - - valid, err := rln.groupManager.VerifyProof(input, msgProof, rln.validMerkleRoots...) + valid, err := rlnRelay.verifyProof(msg, msgProof) if err != nil { - rln.log.Debug("could not verify proof", zap.Error(err)) + rlnRelay.log.Debug("could not verify proof", zap.Error(err)) return MessageValidationResult_Invalid, nil } if !valid { // invalid proof - rln.log.Debug("Invalid proof") + rlnRelay.log.Debug("Invalid proof") return MessageValidationResult_Invalid, nil } // check if double messaging has happened - hasDup, err := rln.HasDuplicate(proofMD) + hasDup, err := rlnRelay.HasDuplicate(proofMD) if err != nil { - rln.log.Debug("validation error", zap.Error(err)) + rlnRelay.log.Debug("validation error", zap.Error(err)) return MessageValidationResult_Unknown, err } if hasDup { - rln.log.Debug("spam received") + rlnRelay.log.Debug("spam received") return MessageValidationResult_Spam, nil } // insert the message to the log // the result of `updateLog` is discarded because message insertion is guaranteed by the implementation i.e., // it will never error out - _, err = rln.updateLog(proofMD) + _, err = rlnRelay.updateLog(proofMD) if err != nil { return MessageValidationResult_Unknown, err } - rln.log.Debug("message is valid") + rlnRelay.log.Debug("message is valid") return MessageValidationResult_Valid, nil } -func (rln *WakuRLNRelay) AppendRLNProof(msg *pb.WakuMessage, senderEpochTime time.Time) error { +func (rlnRelay *WakuRLNRelay) verifyProof(msg *pb.WakuMessage, proof *rln.RateLimitProof) (bool, error) { + contentTopicBytes := []byte(msg.ContentTopic) + input := append(msg.Payload, contentTopicBytes...) + return rlnRelay.RLN.Verify(input, *proof, rlnRelay.rootTracker.Roots()...) +} + +func (rlnRelay *WakuRLNRelay) AppendRLNProof(msg *pb.WakuMessage, senderEpochTime time.Time) error { // returns error if it could not create and append a `RateLimitProof` to the supplied `msg` // `senderEpochTime` indicates the number of seconds passed since Unix epoch. The fractional part holds sub-seconds. // The `epoch` field of `RateLimitProof` is derived from the provided `senderEpochTime` (using `calcEpoch()`) @@ -295,76 +262,49 @@ func (rln *WakuRLNRelay) AppendRLNProof(msg *pb.WakuMessage, senderEpochTime tim input := toRLNSignal(msg) - proof, err := rln.groupManager.GenerateProof(input, r.CalcEpoch(senderEpochTime)) + proof, err := rlnRelay.generateProof(input, rln.CalcEpoch(senderEpochTime)) if err != nil { return err } - msg.RateLimitProof = &pb.RateLimitProof{ - Proof: proof.Proof[:], - MerkleRoot: proof.MerkleRoot[:], - Epoch: proof.Epoch[:], - ShareX: proof.ShareX[:], - ShareY: proof.ShareY[:], - Nullifier: proof.Nullifier[:], - RlnIdentifier: proof.RLNIdentifier[:], - } + msg.RateLimitProof = proof return nil } -func (r *WakuRLNRelay) insertMember(pubkey r.IDCommitment) error { // TODO: move to group manager? ######################################################### - r.log.Debug("a new key is added", zap.Binary("pubkey", pubkey[:])) - // assuming all the members arrive in order - err := r.RLN.InsertMember(pubkey) - if err == nil { - newRoot, err := r.RLN.GetMerkleRoot() - if err != nil { - r.log.Error("inserting member into merkletree", zap.Error(err)) - return err - } - r.validMerkleRoots = append(r.validMerkleRoots, newRoot) - if len(r.validMerkleRoots) > AcceptableRootWindowSize { - r.validMerkleRoots = r.validMerkleRoots[1:] - } - } - - return err -} - // this function sets a validator for the waku messages published on the supplied pubsubTopic and contentTopic // if contentTopic is empty, then validation takes place for All the messages published on the given pubsubTopic // the message validation logic is according to https://rfc.vac.dev/spec/17/ -func (r *WakuRLNRelay) addValidator( +func (rlnRelay *WakuRLNRelay) addValidator( relay *relay.WakuRelay, pubsubTopic string, contentTopic string, spamHandler SpamHandler) error { validator := func(ctx context.Context, peerID peer.ID, message *pubsub.Message) bool { - r.log.Debug("rln-relay topic validator called") + rlnRelay.log.Debug("rln-relay topic validator called") wakuMessage := &pb.WakuMessage{} if err := proto.Unmarshal(message.Data, wakuMessage); err != nil { - r.log.Debug("could not unmarshal message") + rlnRelay.log.Debug("could not unmarshal message") return true } // check the contentTopic if (wakuMessage.ContentTopic != "") && (contentTopic != "") && (wakuMessage.ContentTopic != contentTopic) { - r.log.Debug("content topic did not match", zap.String("contentTopic", contentTopic)) + rlnRelay.log.Debug("content topic did not match", zap.String("contentTopic", contentTopic)) return true } // validate the message - validationRes, err := r.ValidateMessage(wakuMessage, nil) + validationRes, err := rlnRelay.ValidateMessage(wakuMessage, nil) if err != nil { - r.log.Debug("validating message", zap.Error(err)) + rlnRelay.log.Debug("validating message", zap.Error(err)) return false } switch validationRes { case MessageValidationResult_Valid: - r.log.Debug("message verified", + rlnRelay.log.Debug("message verified", zap.String("contentTopic", wakuMessage.ContentTopic), zap.Binary("epoch", wakuMessage.RateLimitProof.Epoch), zap.Int("timestamp", int(wakuMessage.Timestamp)), @@ -376,7 +316,7 @@ func (r *WakuRLNRelay) addValidator( return true case MessageValidationResult_Invalid: - r.log.Debug("message could not be verified", + rlnRelay.log.Debug("message could not be verified", zap.String("contentTopic", wakuMessage.ContentTopic), zap.Binary("epoch", wakuMessage.RateLimitProof.Epoch), zap.Int("timestamp", int(wakuMessage.Timestamp)), @@ -385,7 +325,7 @@ func (r *WakuRLNRelay) addValidator( ) return false case MessageValidationResult_Spam: - r.log.Debug("spam message found", + rlnRelay.log.Debug("spam message found", zap.String("contentTopic", wakuMessage.ContentTopic), zap.Binary("epoch", wakuMessage.RateLimitProof.Epoch), zap.Int("timestamp", int(wakuMessage.Timestamp)), @@ -395,13 +335,13 @@ func (r *WakuRLNRelay) addValidator( if spamHandler != nil { if err := spamHandler(wakuMessage); err != nil { - r.log.Error("executing spam handler", zap.Error(err)) + rlnRelay.log.Error("executing spam handler", zap.Error(err)) } } return false default: - r.log.Debug("unhandled validation result", zap.Int("validationResult", int(validationRes))) + rlnRelay.log.Debug("unhandled validation result", zap.Int("validationResult", int(validationRes))) return false } } @@ -412,29 +352,29 @@ func (r *WakuRLNRelay) addValidator( return relay.PubSub().RegisterTopicValidator(pubsubTopic, validator) } -func toRLNSignal(wakuMessage *pb.WakuMessage) []byte { - if wakuMessage == nil { - return []byte{} +func (rlnRelay *WakuRLNRelay) generateProof(input []byte, epoch rln.Epoch) (*pb.RateLimitProof, error) { + identityCredentials, err := rlnRelay.groupManager.IdentityCredentials() + if err == nil { + return nil, err } - contentTopicBytes := []byte(wakuMessage.ContentTopic) - return append(wakuMessage.Payload, contentTopicBytes...) -} - -func ToRateLimitProof(msg *pb.WakuMessage) *r.RateLimitProof { - if msg == nil || msg.RateLimitProof == nil { - return nil - } - - result := &r.RateLimitProof{ - Proof: r.ZKSNARK(r.Bytes128(msg.RateLimitProof.Proof)), - MerkleRoot: r.MerkleNode(r.Bytes32(msg.RateLimitProof.MerkleRoot)), - Epoch: r.Epoch(r.Bytes32(msg.RateLimitProof.Epoch)), - ShareX: r.MerkleNode(r.Bytes32(msg.RateLimitProof.ShareX)), - ShareY: r.MerkleNode(r.Bytes32(msg.RateLimitProof.ShareY)), - Nullifier: r.Nullifier(r.Bytes32(msg.RateLimitProof.Nullifier)), - RLNIdentifier: r.RLNIdentifier(r.Bytes32(msg.RateLimitProof.RlnIdentifier)), - } - - return result + membershipIndex, err := rlnRelay.groupManager.MembershipIndex() + if err == nil { + return nil, err + } + + proof, err := rlnRelay.RLN.GenerateProof(input, identityCredentials, membershipIndex, epoch) + if err != nil { + return nil, err + } + + return &pb.RateLimitProof{ + Proof: proof.Proof[:], + MerkleRoot: proof.MerkleRoot[:], + Epoch: proof.Epoch[:], + ShareX: proof.ShareX[:], + ShareY: proof.ShareY[:], + Nullifier: proof.Nullifier[:], + RlnIdentifier: proof.RLNIdentifier[:], + }, nil }