mirror of https://github.com/status-im/go-waku.git
refactor: static RLN relay
This commit is contained in:
parent
6796936f5e
commit
be09f3f550
2
go.mod
2
go.mod
|
@ -38,7 +38,7 @@ require (
|
||||||
github.com/go-chi/chi/v5 v5.0.0
|
github.com/go-chi/chi/v5 v5.0.0
|
||||||
github.com/lib/pq v1.10.4
|
github.com/lib/pq v1.10.4
|
||||||
github.com/waku-org/go-noise v0.0.4
|
github.com/waku-org/go-noise v0.0.4
|
||||||
github.com/waku-org/go-zerokit-rln v0.1.9
|
github.com/waku-org/go-zerokit-rln v0.1.10
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -1568,6 +1568,8 @@ github.com/waku-org/go-noise v0.0.4 h1:ZfQDcCw8pazm89EBl5SXY7GGAnzDQb9AHFXlw3Ktb
|
||||||
github.com/waku-org/go-noise v0.0.4/go.mod h1:+PWRfs2eSOVwKrPcQlfhwDngSh3faL/1QoxvoqggEKc=
|
github.com/waku-org/go-noise v0.0.4/go.mod h1:+PWRfs2eSOVwKrPcQlfhwDngSh3faL/1QoxvoqggEKc=
|
||||||
github.com/waku-org/go-zerokit-rln v0.1.9 h1:eMp43ThdVC8qcr4l398x3oR98RGdzAkMR/1hDlpjciU=
|
github.com/waku-org/go-zerokit-rln v0.1.9 h1:eMp43ThdVC8qcr4l398x3oR98RGdzAkMR/1hDlpjciU=
|
||||||
github.com/waku-org/go-zerokit-rln v0.1.9/go.mod h1:MUW+wB6Yj7UBMdZrhko7oHfUZeY2wchggXYjpUiMoac=
|
github.com/waku-org/go-zerokit-rln v0.1.9/go.mod h1:MUW+wB6Yj7UBMdZrhko7oHfUZeY2wchggXYjpUiMoac=
|
||||||
|
github.com/waku-org/go-zerokit-rln v0.1.10 h1:H2aLZvIIr00ro8VzHjn+R1v8FA1rBwQTt5VKRkcFnTs=
|
||||||
|
github.com/waku-org/go-zerokit-rln v0.1.10/go.mod h1:MUW+wB6Yj7UBMdZrhko7oHfUZeY2wchggXYjpUiMoac=
|
||||||
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230331231302-258cacb91327 h1:Q5XQqo+PEmvrybT8D7BEsKCwIYDi80s+00Q49cfm9Gs=
|
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230331231302-258cacb91327 h1:Q5XQqo+PEmvrybT8D7BEsKCwIYDi80s+00Q49cfm9Gs=
|
||||||
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230331231302-258cacb91327/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48=
|
github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230331231302-258cacb91327/go.mod h1:KYykqtdApHVYZ3G0spwMnoxc5jH5eI3jyO9SwsSfi48=
|
||||||
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230331223149-f90e66aebb0d h1:Kcg85Y2xGU6hqZ/kMfkLQF2jAog8vt+tw1/VNidzNtE=
|
github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230331223149-f90e66aebb0d h1:Kcg85Y2xGU6hqZ/kMfkLQF2jAog8vt+tw1/VNidzNtE=
|
||||||
|
|
|
@ -4,10 +4,12 @@
|
||||||
package node
|
package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/rln"
|
"github.com/waku-org/go-waku/waku/v2/protocol/rln"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager/static"
|
||||||
r "github.com/waku-org/go-zerokit-rln/rln"
|
r "github.com/waku-org/go-zerokit-rln/rln"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -40,13 +42,24 @@ func (w *WakuNode) mountRlnRelay(ctx context.Context) error {
|
||||||
if !w.opts.rlnRelayDynamic {
|
if !w.opts.rlnRelayDynamic {
|
||||||
w.log.Info("setting up waku-rln-relay in off-chain mode")
|
w.log.Info("setting up waku-rln-relay in off-chain mode")
|
||||||
// set up rln relay inputs
|
// set up rln relay inputs
|
||||||
groupKeys, memKeyPair, memIndex, err := rln.StaticSetup(w.opts.rlnRelayMemIndex)
|
groupKeys, idCredential, err := static.Setup(w.opts.rlnRelayMemIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// mount rlnrelay in off-chain mode with a static group of users
|
// rlnrelay in off-chain mode with a static group of user
|
||||||
rlnRelay, err := rln.RlnRelayStatic(ctx, w.Relay(), groupKeys, memKeyPair, memIndex, w.opts.rlnRelayPubsubTopic, w.opts.rlnRelayContentTopic, w.opts.rlnSpamHandler, w.timesource, w.log)
|
|
||||||
|
groupManager, err := static.NewStaticGroupManager(groupKeys, idCredential, w.opts.rlnRelayMemIndex, w.log)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rlnRelay, err := rln.New(w.Relay(), groupManager, w.opts.rlnRelayPubsubTopic, w.opts.rlnRelayContentTopic, w.opts.rlnSpamHandler, w.timesource, w.log)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rlnRelay.Start(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -60,21 +73,25 @@ func (w *WakuNode) mountRlnRelay(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedRoot := r.STATIC_GROUP_MERKLE_ROOT
|
expectedRoot, err := static.ToBytes32LE(r.STATIC_GROUP_MERKLE_ROOT)
|
||||||
if hex.EncodeToString(root[:]) != expectedRoot {
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(expectedRoot[:], root[:]) {
|
||||||
return errors.New("root mismatch: something went wrong not in Merkle tree construction")
|
return errors.New("root mismatch: something went wrong not in Merkle tree construction")
|
||||||
}
|
}
|
||||||
|
|
||||||
w.log.Info("the calculated root", zap.String("root", hex.EncodeToString(root[:])))
|
w.log.Debug("the calculated root", zap.String("root", hex.EncodeToString(root[:])))
|
||||||
} else {
|
} else {
|
||||||
w.log.Info("setting up waku-rln-relay in on-chain mode")
|
w.log.Info("setting up waku-rln-relay in on-chain mode")
|
||||||
|
|
||||||
// check if the peer has provided its rln credentials
|
/*// check if the peer has provided its rln credentials
|
||||||
var memKeyPair *r.MembershipKeyPair
|
var memKeyPair *r.IdentityCredential
|
||||||
if w.opts.rlnRelayIDCommitment != nil && w.opts.rlnRelayIDKey != nil {
|
if w.opts.rlnRelayIDCommitment != nil && w.opts.rlnRelayIDKey != nil {
|
||||||
memKeyPair = &r.MembershipKeyPair{
|
memKeyPair = &r.IdentityCredential{
|
||||||
IDCommitment: *w.opts.rlnRelayIDCommitment,
|
IDCommitment: *w.opts.rlnRelayIDCommitment,
|
||||||
IDKey: *w.opts.rlnRelayIDKey,
|
IDSecretHash: *w.opts.rlnRelayIDKey,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +100,7 @@ func (w *WakuNode) mountRlnRelay(ctx context.Context) error {
|
||||||
w.rlnRelay, err = rln.RlnRelayDynamic(ctx, w.Relay(), w.opts.rlnETHClientAddress, w.opts.rlnETHPrivateKey, w.opts.rlnMembershipContractAddress, memKeyPair, w.opts.rlnRelayMemIndex, w.opts.rlnRelayPubsubTopic, w.opts.rlnRelayContentTopic, w.opts.rlnSpamHandler, w.opts.rlnRegistrationHandler, w.timesource, w.log)
|
w.rlnRelay, err = rln.RlnRelayDynamic(ctx, w.Relay(), w.opts.rlnETHClientAddress, w.opts.rlnETHPrivateKey, w.opts.rlnMembershipContractAddress, memKeyPair, w.opts.rlnRelayMemIndex, w.opts.rlnRelayPubsubTopic, w.opts.rlnRelayContentTopic, w.opts.rlnSpamHandler, w.opts.rlnRegistrationHandler, w.timesource, w.log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
w.log.Info("mounted waku RLN relay", zap.String("pubsubTopic", w.opts.rlnRelayPubsubTopic), zap.String("contentTopic", w.opts.rlnRelayContentTopic))
|
w.log.Info("mounted waku RLN relay", zap.String("pubsubTopic", w.opts.rlnRelayPubsubTopic), zap.String("contentTopic", w.opts.rlnRelayContentTopic))
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
package group_manager
|
|
@ -0,0 +1,87 @@
|
||||||
|
package static
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/waku-org/go-zerokit-rln/rln"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StaticGroupManager struct {
|
||||||
|
rln *rln.RLN
|
||||||
|
log *zap.Logger
|
||||||
|
|
||||||
|
group []rln.IDCommitment
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStaticGroupManager(
|
||||||
|
group []rln.IDCommitment,
|
||||||
|
memKeyPair rln.IdentityCredential,
|
||||||
|
memIndex 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)] {
|
||||||
|
return nil, errors.New("peer's IDCommitment does not match commitment in group")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &StaticGroupManager{
|
||||||
|
log: log.Named("rln-static"),
|
||||||
|
group: group,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gm *StaticGroupManager) Start(ctx context.Context, rln *rln.RLN) error {
|
||||||
|
gm.log.Info("mounting rln-relay in off-chain/static mode")
|
||||||
|
|
||||||
|
gm.rln = rln
|
||||||
|
|
||||||
|
// add members to the Merkle tree
|
||||||
|
for _, member := range gm.group {
|
||||||
|
if err := rln.InsertMember(member); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gm.group = nil // Deleting group to release memory
|
||||||
|
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -48,7 +48,7 @@ func RlnRelayStatic(
|
||||||
contentTopic: contentTopic,
|
contentTopic: contentTopic,
|
||||||
log: log,
|
log: log,
|
||||||
timesource: timesource,
|
timesource: timesource,
|
||||||
nullifierLog: make(map[r.Epoch][]r.ProofMetadata),
|
nullifierLog: make(map[r.Nullifier][]r.ProofMetadata),
|
||||||
}
|
}
|
||||||
|
|
||||||
root, err := rlnPeer.RLN.GetMerkleRoot()
|
root, err := rlnPeer.RLN.GetMerkleRoot()
|
||||||
|
@ -114,7 +114,7 @@ func RlnRelayDynamic(
|
||||||
contentTopic: contentTopic,
|
contentTopic: contentTopic,
|
||||||
log: log,
|
log: log,
|
||||||
timesource: timesource,
|
timesource: timesource,
|
||||||
nullifierLog: make(map[r.Epoch][]r.ProofMetadata),
|
nullifierLog: make(map[r.Nullifier][]r.ProofMetadata),
|
||||||
registrationHandler: registrationHandler,
|
registrationHandler: registrationHandler,
|
||||||
lastIndexLoaded: -1,
|
lastIndexLoaded: -1,
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,21 +3,18 @@ package rln
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
|
||||||
"errors"
|
"errors"
|
||||||
"math"
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||||
"github.com/libp2p/go-libp2p/core/peer"
|
"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/pb"
|
||||||
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
||||||
"github.com/waku-org/go-waku/waku/v2/timesource"
|
"github.com/waku-org/go-waku/waku/v2/timesource"
|
||||||
"github.com/waku-org/go-waku/waku/v2/utils"
|
|
||||||
r "github.com/waku-org/go-zerokit-rln/rln"
|
r "github.com/waku-org/go-zerokit-rln/rln"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
proto "google.golang.org/protobuf/proto"
|
proto "google.golang.org/protobuf/proto"
|
||||||
|
@ -29,101 +26,129 @@ const MAX_CLOCK_GAP_SECONDS = 20
|
||||||
// maximum allowed gap between the epochs of messages' RateLimitProofs
|
// maximum allowed gap between the epochs of messages' RateLimitProofs
|
||||||
const MAX_EPOCH_GAP = int64(MAX_CLOCK_GAP_SECONDS / r.EPOCH_UNIT_SECONDS)
|
const MAX_EPOCH_GAP = int64(MAX_CLOCK_GAP_SECONDS / r.EPOCH_UNIT_SECONDS)
|
||||||
|
|
||||||
type RegistrationHandler = func(tx *types.Transaction)
|
// Acceptable roots for merkle root validation of incoming messages
|
||||||
|
|
||||||
const AcceptableRootWindowSize = 5
|
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",
|
||||||
|
Version: "0.1",
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
Stop()
|
||||||
|
}
|
||||||
|
|
||||||
type WakuRLNRelay struct {
|
type WakuRLNRelay struct {
|
||||||
ctx context.Context
|
|
||||||
timesource timesource.Timesource
|
timesource timesource.Timesource
|
||||||
|
|
||||||
membershipKeyPair *r.IdentityCredential
|
groupManager GroupManager
|
||||||
|
|
||||||
// membershipIndex denotes the index of a leaf in the Merkle tree
|
// pubsubTopic is the topic for which rln relay is mounted
|
||||||
// that contains the pk of the current peer
|
pubsubTopic string
|
||||||
// this index is used to retrieve the peer's authentication path
|
contentTopic string
|
||||||
membershipIndex r.MembershipIndex
|
relay *relay.WakuRelay
|
||||||
membershipContractAddress common.Address
|
spamHandler SpamHandler
|
||||||
ethClientAddress string
|
|
||||||
// ethAccountPrivateKey is required for signing transactions
|
|
||||||
// TODO may need to erase this ethAccountPrivateKey when is not used
|
|
||||||
// TODO may need to make ethAccountPrivateKey mandatory
|
|
||||||
ethAccountPrivateKey *ecdsa.PrivateKey
|
|
||||||
ethClient *ethclient.Client
|
|
||||||
|
|
||||||
RLN *r.RLN
|
RLN *r.RLN
|
||||||
// pubsubTopic is the topic for which rln relay is mounted
|
|
||||||
pubsubTopic string
|
|
||||||
contentTopic string
|
|
||||||
lastIndexLoaded int64
|
|
||||||
|
|
||||||
validMerkleRoots []r.MerkleNode
|
validMerkleRoots []r.MerkleNode
|
||||||
|
|
||||||
// the log of nullifiers and Shamir shares of the past messages grouped per epoch
|
// the log of nullifiers and Shamir shares of the past messages grouped per epoch
|
||||||
nullifierLogLock sync.RWMutex
|
nullifierLogLock sync.RWMutex
|
||||||
nullifierLog map[r.Epoch][]r.ProofMetadata
|
nullifierLog map[r.Nullifier][]r.ProofMetadata
|
||||||
|
|
||||||
registrationHandler RegistrationHandler
|
log *zap.Logger
|
||||||
log *zap.Logger
|
}
|
||||||
|
|
||||||
|
func New(
|
||||||
|
relay *relay.WakuRelay,
|
||||||
|
groupManager GroupManager,
|
||||||
|
pubsubTopic string,
|
||||||
|
contentTopic string,
|
||||||
|
spamHandler SpamHandler,
|
||||||
|
timesource timesource.Timesource,
|
||||||
|
log *zap.Logger) (*WakuRLNRelay, error) {
|
||||||
|
rlnInstance, err := r.NewRLN()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the WakuRLNRelay
|
||||||
|
rlnPeer := &WakuRLNRelay{
|
||||||
|
RLN: rlnInstance,
|
||||||
|
groupManager: groupManager,
|
||||||
|
pubsubTopic: pubsubTopic,
|
||||||
|
contentTopic: contentTopic,
|
||||||
|
relay: relay,
|
||||||
|
spamHandler: spamHandler,
|
||||||
|
log: log,
|
||||||
|
timesource: timesource,
|
||||||
|
nullifierLog: make(map[r.MerkleNode][]r.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)
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("rln relay topic validator mounted", zap.String("pubsubTopic", rln.pubsubTopic), zap.String("contentTopic", rln.contentTopic))
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rln *WakuRLNRelay) Stop() {
|
func (rln *WakuRLNRelay) Stop() {
|
||||||
if rln.ethClient != nil {
|
rln.groupManager.Stop()
|
||||||
rln.ethClient.Close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func StaticSetup(rlnRelayMemIndex r.MembershipIndex) ([]r.IDCommitment, r.IdentityCredential, r.MembershipIndex, error) {
|
func (rln *WakuRLNRelay) HasDuplicate(proofMD r.ProofMetadata) (bool, error) {
|
||||||
// static group
|
|
||||||
groupKeys := r.STATIC_GROUP_KEYS
|
|
||||||
groupSize := r.STATIC_GROUP_SIZE
|
|
||||||
|
|
||||||
// validate the user-supplied membership index
|
|
||||||
if rlnRelayMemIndex >= r.MembershipIndex(groupSize) {
|
|
||||||
return nil, r.IdentityCredential{}, 0, errors.New("wrong membership index")
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepare the outputs from the static group keys
|
|
||||||
|
|
||||||
// create a sequence of MembershipKeyPairs from the group keys (group keys are in string format)
|
|
||||||
groupKeyPairs, err := toMembershipKeyPairs(groupKeys)
|
|
||||||
if err != nil {
|
|
||||||
return nil, r.IdentityCredential{}, 0, errors.New("invalid data on group keypairs")
|
|
||||||
}
|
|
||||||
|
|
||||||
// extract id commitment keys
|
|
||||||
var groupOpt []r.IDCommitment
|
|
||||||
for _, c := range groupKeyPairs {
|
|
||||||
groupOpt = append(groupOpt, c.IDCommitment)
|
|
||||||
}
|
|
||||||
|
|
||||||
// user selected membership key pair
|
|
||||||
memKeyPairOpt := groupKeyPairs[rlnRelayMemIndex]
|
|
||||||
memIndexOpt := rlnRelayMemIndex
|
|
||||||
|
|
||||||
return groupOpt, memKeyPairOpt, memIndexOpt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rln *WakuRLNRelay) HasDuplicate(msg *pb.WakuMessage) (bool, error) {
|
|
||||||
// returns true if there is another message in the `nullifierLog` of the `rlnPeer` with the same
|
// 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
|
// epoch and nullifier as `msg`'s epoch and nullifier but different Shamir secret shares
|
||||||
// otherwise, returns false
|
// otherwise, returns false
|
||||||
|
|
||||||
if msg == nil {
|
|
||||||
return false, errors.New("nil message")
|
|
||||||
}
|
|
||||||
|
|
||||||
msgProof := ToRateLimitProof(msg)
|
|
||||||
|
|
||||||
// extract the proof metadata of the supplied `msg`
|
|
||||||
proofMD := r.ProofMetadata{
|
|
||||||
Nullifier: msgProof.Nullifier,
|
|
||||||
ShareX: msgProof.ShareX,
|
|
||||||
ShareY: msgProof.ShareY,
|
|
||||||
}
|
|
||||||
|
|
||||||
rln.nullifierLogLock.RLock()
|
rln.nullifierLogLock.RLock()
|
||||||
proofs, ok := rln.nullifierLog[msgProof.Epoch]
|
proofs, ok := rln.nullifierLog[proofMD.ExternalNullifier]
|
||||||
rln.nullifierLogLock.RUnlock()
|
rln.nullifierLogLock.RUnlock()
|
||||||
|
|
||||||
// check if the epoch exists
|
// check if the epoch exists
|
||||||
|
@ -150,42 +175,28 @@ func (rln *WakuRLNRelay) HasDuplicate(msg *pb.WakuMessage) (bool, error) {
|
||||||
return matched, nil
|
return matched, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rln *WakuRLNRelay) updateLog(msg *pb.WakuMessage) (bool, error) {
|
func (rln *WakuRLNRelay) updateLog(proofMD r.ProofMetadata) (bool, error) {
|
||||||
// extracts the `ProofMetadata` of the supplied messages `msg` and
|
|
||||||
// saves it in the `nullifierLog` of the `rlnPeer`
|
|
||||||
|
|
||||||
if msg == nil {
|
|
||||||
return false, errors.New("nil message")
|
|
||||||
}
|
|
||||||
|
|
||||||
msgProof := ToRateLimitProof(msg)
|
|
||||||
|
|
||||||
proofMD := r.ProofMetadata{
|
|
||||||
Nullifier: msgProof.Nullifier,
|
|
||||||
ShareX: msgProof.ShareX,
|
|
||||||
ShareY: msgProof.ShareY,
|
|
||||||
}
|
|
||||||
|
|
||||||
rln.nullifierLogLock.Lock()
|
rln.nullifierLogLock.Lock()
|
||||||
defer rln.nullifierLogLock.Unlock()
|
defer rln.nullifierLogLock.Unlock()
|
||||||
proofs, ok := rln.nullifierLog[msgProof.Epoch]
|
proofs, ok := rln.nullifierLog[proofMD.ExternalNullifier]
|
||||||
|
|
||||||
// check if the epoch exists
|
// check if the epoch exists
|
||||||
if !ok {
|
if !ok {
|
||||||
rln.nullifierLog[msgProof.Epoch] = []r.ProofMetadata{proofMD}
|
rln.nullifierLog[proofMD.ExternalNullifier] = []r.ProofMetadata{proofMD}
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if an identical record exists
|
// check if an identical record exists
|
||||||
for _, p := range proofs {
|
for _, p := range proofs {
|
||||||
if p.Equals(proofMD) {
|
if p.Equals(proofMD) {
|
||||||
|
// TODO: slashing logic
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add proofMD to the log
|
// add proofMD to the log
|
||||||
proofs = append(proofs, proofMD)
|
proofs = append(proofs, proofMD)
|
||||||
rln.nullifierLog[msgProof.Epoch] = proofs
|
rln.nullifierLog[proofMD.ExternalNullifier] = proofs
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -218,6 +229,12 @@ func (rln *WakuRLNRelay) ValidateMessage(msg *pb.WakuMessage, optionalTime *time
|
||||||
return MessageValidationResult_Invalid, nil
|
return MessageValidationResult_Invalid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proofMD, err := r.ExtractMetadata(*msgProof)
|
||||||
|
if err != nil {
|
||||||
|
rln.log.Debug("could not extract metadata", zap.Error(err))
|
||||||
|
return MessageValidationResult_Invalid, nil
|
||||||
|
}
|
||||||
|
|
||||||
// calculate the gaps and validate the epoch
|
// calculate the gaps and validate the epoch
|
||||||
gap := r.Diff(epoch, msgProof.Epoch)
|
gap := r.Diff(epoch, msgProof.Epoch)
|
||||||
if int64(math.Abs(float64(gap))) > MAX_EPOCH_GAP {
|
if int64(math.Abs(float64(gap))) > MAX_EPOCH_GAP {
|
||||||
|
@ -231,7 +248,7 @@ func (rln *WakuRLNRelay) ValidateMessage(msg *pb.WakuMessage, optionalTime *time
|
||||||
contentTopicBytes := []byte(msg.ContentTopic)
|
contentTopicBytes := []byte(msg.ContentTopic)
|
||||||
input := append(msg.Payload, contentTopicBytes...)
|
input := append(msg.Payload, contentTopicBytes...)
|
||||||
|
|
||||||
valid, err := rln.RLN.Verify(input, *msgProof, rln.validMerkleRoots...)
|
valid, err := rln.groupManager.VerifyProof(input, msgProof, rln.validMerkleRoots...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rln.log.Debug("could not verify proof", zap.Error(err))
|
rln.log.Debug("could not verify proof", zap.Error(err))
|
||||||
return MessageValidationResult_Invalid, nil
|
return MessageValidationResult_Invalid, nil
|
||||||
|
@ -244,7 +261,7 @@ func (rln *WakuRLNRelay) ValidateMessage(msg *pb.WakuMessage, optionalTime *time
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if double messaging has happened
|
// check if double messaging has happened
|
||||||
hasDup, err := rln.HasDuplicate(msg)
|
hasDup, err := rln.HasDuplicate(proofMD)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rln.log.Debug("validation error", zap.Error(err))
|
rln.log.Debug("validation error", zap.Error(err))
|
||||||
return MessageValidationResult_Unknown, err
|
return MessageValidationResult_Unknown, err
|
||||||
|
@ -258,7 +275,7 @@ func (rln *WakuRLNRelay) ValidateMessage(msg *pb.WakuMessage, optionalTime *time
|
||||||
// insert the message to the log
|
// insert the message to the log
|
||||||
// the result of `updateLog` is discarded because message insertion is guaranteed by the implementation i.e.,
|
// the result of `updateLog` is discarded because message insertion is guaranteed by the implementation i.e.,
|
||||||
// it will never error out
|
// it will never error out
|
||||||
_, err = rln.updateLog(msg)
|
_, err = rln.updateLog(proofMD)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return MessageValidationResult_Unknown, err
|
return MessageValidationResult_Unknown, err
|
||||||
}
|
}
|
||||||
|
@ -276,13 +293,9 @@ func (rln *WakuRLNRelay) AppendRLNProof(msg *pb.WakuMessage, senderEpochTime tim
|
||||||
return errors.New("nil message")
|
return errors.New("nil message")
|
||||||
}
|
}
|
||||||
|
|
||||||
if rln.membershipKeyPair == nil {
|
|
||||||
return errors.New("No keypair setup")
|
|
||||||
}
|
|
||||||
|
|
||||||
input := toRLNSignal(msg)
|
input := toRLNSignal(msg)
|
||||||
|
|
||||||
proof, err := rln.RLN.GenerateProof(input, *rln.membershipKeyPair, rln.membershipIndex, r.CalcEpoch(senderEpochTime))
|
proof, err := rln.groupManager.GenerateProof(input, r.CalcEpoch(senderEpochTime))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -300,19 +313,7 @@ func (rln *WakuRLNRelay) AppendRLNProof(msg *pb.WakuMessage, senderEpochTime tim
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *WakuRLNRelay) MembershipKeyPair() *r.IdentityCredential {
|
func (r *WakuRLNRelay) insertMember(pubkey r.IDCommitment) error { // TODO: move to group manager? #########################################################
|
||||||
return r.membershipKeyPair
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *WakuRLNRelay) MembershipIndex() r.MembershipIndex {
|
|
||||||
return r.membershipIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *WakuRLNRelay) MembershipContractAddress() common.Address {
|
|
||||||
return r.membershipContractAddress
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *WakuRLNRelay) insertMember(pubkey r.IDCommitment) error {
|
|
||||||
r.log.Debug("a new key is added", zap.Binary("pubkey", pubkey[:]))
|
r.log.Debug("a new key is added", zap.Binary("pubkey", pubkey[:]))
|
||||||
// assuming all the members arrive in order
|
// assuming all the members arrive in order
|
||||||
err := r.RLN.InsertMember(pubkey)
|
err := r.RLN.InsertMember(pubkey)
|
||||||
|
@ -331,8 +332,6 @@ func (r *WakuRLNRelay) insertMember(pubkey r.IDCommitment) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
type SpamHandler = func(message *pb.WakuMessage) error
|
|
||||||
|
|
||||||
// this function sets a validator for the waku messages published on the supplied pubsubTopic and contentTopic
|
// 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
|
// 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/
|
// the message validation logic is according to https://rfc.vac.dev/spec/17/
|
||||||
|
@ -413,27 +412,6 @@ func (r *WakuRLNRelay) addValidator(
|
||||||
return relay.PubSub().RegisterTopicValidator(pubsubTopic, validator)
|
return relay.PubSub().RegisterTopicValidator(pubsubTopic, validator)
|
||||||
}
|
}
|
||||||
|
|
||||||
func toMembershipKeyPairs(groupKeys [][]string) ([]r.IdentityCredential, error) {
|
|
||||||
// groupKeys is sequence of membership key tuples in the form of (identity key, identity commitment) all in the hexadecimal format
|
|
||||||
// the ToMembershipKeyPairs proc populates a sequence of MembershipKeyPairs using the supplied groupKeys
|
|
||||||
|
|
||||||
groupKeyPairs := []r.IdentityCredential{}
|
|
||||||
for _, pair := range groupKeys {
|
|
||||||
idKey, err := utils.DecodeHexString(pair[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
idCommitment, err := utils.DecodeHexString(pair[1])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
groupKeyPairs = append(groupKeyPairs, r.IdentityCredential{IDSecretHash: r.IDSecretHash(r.Bytes32(idKey)), IDCommitment: r.IDCommitment(r.Bytes32(idCommitment))})
|
|
||||||
}
|
|
||||||
|
|
||||||
return groupKeyPairs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func toRLNSignal(wakuMessage *pb.WakuMessage) []byte {
|
func toRLNSignal(wakuMessage *pb.WakuMessage) []byte {
|
||||||
if wakuMessage == nil {
|
if wakuMessage == nil {
|
||||||
return []byte{}
|
return []byte{}
|
||||||
|
|
Loading…
Reference in New Issue