feat: use protected topics for communities
refactor: associate chats to pubsub topics and populate these depending if the chat belongs to a community or not refactor: add pubsub topic to mailserver batches chore: ensure default relay messages continue working as they should refactor: mailserver functions should be aware of pubsub topics fix: use []byte for communityIDs
This commit is contained in:
parent
8764170149
commit
f9ec588c4e
|
@ -0,0 +1,24 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS pubsubtopic_signing_key (
|
||||||
|
topic VARCHAR NOT NULL,
|
||||||
|
priv_key BLOB NULL,
|
||||||
|
pub_key BLOB NOT NULL,
|
||||||
|
PRIMARY KEY (topic)
|
||||||
|
) WITHOUT ROWID;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS mailserver_topics_new (
|
||||||
|
topic VARCHAR NOT NULL DEFAULT "",
|
||||||
|
pubsub_topic VARCHAR NOT NULL DEFAULT "/waku/2/default-waku/proto",
|
||||||
|
chat_ids VARCHAR,
|
||||||
|
last_request INTEGER DEFAULT 1,
|
||||||
|
discovery BOOLEAN DEFAULT FALSE,
|
||||||
|
negotiated BOOLEAN DEFAULT FALSE,
|
||||||
|
PRIMARY KEY(topic, pubsub_topic)
|
||||||
|
) WITHOUT ROWID;
|
||||||
|
|
||||||
|
INSERT INTO mailserver_topics_new
|
||||||
|
SELECT topic, "/waku/2/default-waku/proto", chat_ids, last_request, discovery, negotiated
|
||||||
|
FROM mailserver_topics;
|
||||||
|
|
||||||
|
DROP TABLE mailserver_topics;
|
||||||
|
|
||||||
|
ALTER TABLE mailserver_topics_new RENAME TO mailserver_topics;
|
|
@ -53,10 +53,11 @@ func (w *gethPublicWakuV2APIWrapper) NewMessageFilter(req types.Criteria) (strin
|
||||||
}
|
}
|
||||||
|
|
||||||
criteria := wakuv2.Criteria{
|
criteria := wakuv2.Criteria{
|
||||||
SymKeyID: req.SymKeyID,
|
SymKeyID: req.SymKeyID,
|
||||||
PrivateKeyID: req.PrivateKeyID,
|
PrivateKeyID: req.PrivateKeyID,
|
||||||
Sig: req.Sig,
|
Sig: req.Sig,
|
||||||
Topics: topics,
|
PubsubTopic: req.PubsubTopic,
|
||||||
|
ContentTopics: topics,
|
||||||
}
|
}
|
||||||
return w.api.NewMessageFilter(criteria)
|
return w.api.NewMessageFilter(criteria)
|
||||||
}
|
}
|
||||||
|
@ -72,13 +73,14 @@ func (w *gethPublicWakuV2APIWrapper) GetFilterMessages(id string) ([]*types.Mess
|
||||||
wrappedMsgs := make([]*types.Message, len(msgs))
|
wrappedMsgs := make([]*types.Message, len(msgs))
|
||||||
for index, msg := range msgs {
|
for index, msg := range msgs {
|
||||||
wrappedMsgs[index] = &types.Message{
|
wrappedMsgs[index] = &types.Message{
|
||||||
Sig: msg.Sig,
|
Sig: msg.Sig,
|
||||||
Timestamp: msg.Timestamp,
|
Timestamp: msg.Timestamp,
|
||||||
Topic: types.TopicType(msg.Topic),
|
PubsubTopic: msg.PubsubTopic,
|
||||||
Payload: msg.Payload,
|
Topic: types.TopicType(msg.ContentTopic),
|
||||||
Padding: msg.Padding,
|
Payload: msg.Payload,
|
||||||
Hash: msg.Hash,
|
Padding: msg.Padding,
|
||||||
Dst: msg.Dst,
|
Hash: msg.Hash,
|
||||||
|
Dst: msg.Dst,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return wrappedMsgs, nil
|
return wrappedMsgs, nil
|
||||||
|
@ -88,14 +90,15 @@ func (w *gethPublicWakuV2APIWrapper) GetFilterMessages(id string) ([]*types.Mess
|
||||||
// returns the hash of the message in case of success.
|
// returns the hash of the message in case of success.
|
||||||
func (w *gethPublicWakuV2APIWrapper) Post(ctx context.Context, req types.NewMessage) ([]byte, error) {
|
func (w *gethPublicWakuV2APIWrapper) Post(ctx context.Context, req types.NewMessage) ([]byte, error) {
|
||||||
msg := wakuv2.NewMessage{
|
msg := wakuv2.NewMessage{
|
||||||
SymKeyID: req.SymKeyID,
|
SymKeyID: req.SymKeyID,
|
||||||
PublicKey: req.PublicKey,
|
PublicKey: req.PublicKey,
|
||||||
Sig: req.SigID, // Sig is really a SigID
|
Sig: req.SigID, // Sig is really a SigID
|
||||||
Topic: wakucommon.TopicType(req.Topic),
|
PubsubTopic: req.PubsubTopic,
|
||||||
Payload: req.Payload,
|
ContentTopic: wakucommon.TopicType(req.Topic),
|
||||||
Padding: req.Padding,
|
Payload: req.Payload,
|
||||||
TargetPeer: req.TargetPeer,
|
Padding: req.Padding,
|
||||||
Ephemeral: req.Ephemeral,
|
TargetPeer: req.TargetPeer,
|
||||||
|
Ephemeral: req.Ephemeral,
|
||||||
}
|
}
|
||||||
return w.api.Post(ctx, msg)
|
return w.api.Post(ctx, msg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,16 @@ func (w *gethWakuWrapper) AddStorePeer(address string) (peer.ID, error) {
|
||||||
return "", errors.New("not available in WakuV1")
|
return "", errors.New("not available in WakuV1")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SubscribeToPubsubTopic function only added for compatibility with waku V2
|
||||||
|
func (w *gethWakuWrapper) SubscribeToPubsubTopic(topic string, optPublicKey *ecdsa.PublicKey) error {
|
||||||
|
// not available in WakuV1
|
||||||
|
return errors.New("not available in WakuV1")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *gethWakuWrapper) StorePubsubTopicKey(topic string, privKey *ecdsa.PrivateKey) error {
|
||||||
|
return errors.New("not available in WakuV1")
|
||||||
|
}
|
||||||
|
|
||||||
// AddRelayPeer function only added for compatibility with waku V2
|
// AddRelayPeer function only added for compatibility with waku V2
|
||||||
func (w *gethWakuWrapper) AddRelayPeer(address string) (peer.ID, error) {
|
func (w *gethWakuWrapper) AddRelayPeer(address string) (peer.ID, error) {
|
||||||
return "", errors.New("not available in WakuV1")
|
return "", errors.New("not available in WakuV1")
|
||||||
|
@ -231,7 +241,7 @@ func (w *gethWakuWrapper) SendMessagesRequest(peerID []byte, r types.MessagesReq
|
||||||
Limit: r.Limit,
|
Limit: r.Limit,
|
||||||
Cursor: r.Cursor,
|
Cursor: r.Cursor,
|
||||||
Bloom: r.Bloom,
|
Bloom: r.Bloom,
|
||||||
Topics: r.Topics,
|
Topics: r.ContentTopics,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,7 @@ func (w *gethWakuV2Wrapper) Subscribe(opts *types.SubscriptionOptions) (string,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err := w.createFilterWrapper("", keyAsym, keySym, opts.PoW, opts.Topics)
|
f, err := w.createFilterWrapper("", keyAsym, keySym, opts.PoW, opts.PubsubTopic, opts.Topics)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -160,12 +160,13 @@ func (w *gethWakuV2Wrapper) UnsubscribeMany(ids []string) error {
|
||||||
return w.waku.UnsubscribeMany(ids)
|
return w.waku.UnsubscribeMany(ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *gethWakuV2Wrapper) createFilterWrapper(id string, keyAsym *ecdsa.PrivateKey, keySym []byte, pow float64, topics [][]byte) (types.Filter, error) {
|
func (w *gethWakuV2Wrapper) createFilterWrapper(id string, keyAsym *ecdsa.PrivateKey, keySym []byte, pow float64, pubsubTopic string, topics [][]byte) (types.Filter, error) {
|
||||||
return NewWakuV2FilterWrapper(&wakucommon.Filter{
|
return NewWakuV2FilterWrapper(&wakucommon.Filter{
|
||||||
KeyAsym: keyAsym,
|
KeyAsym: keyAsym,
|
||||||
KeySym: keySym,
|
KeySym: keySym,
|
||||||
Topics: topics,
|
Topics: topics,
|
||||||
Messages: wakucommon.NewMemoryMessageStore(),
|
PubsubTopic: pubsubTopic,
|
||||||
|
Messages: wakucommon.NewMemoryMessageStore(),
|
||||||
}, id), nil
|
}, id), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,12 +195,12 @@ func (w *gethWakuV2Wrapper) RequestStoreMessages(ctx context.Context, peerID []b
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
var topics []wakucommon.TopicType
|
var contentTopics []wakucommon.TopicType
|
||||||
for _, topic := range r.Topics {
|
for _, topic := range r.ContentTopics {
|
||||||
topics = append(topics, wakucommon.BytesToTopic(topic))
|
contentTopics = append(contentTopics, wakucommon.BytesToTopic(topic))
|
||||||
}
|
}
|
||||||
|
|
||||||
pbCursor, err := w.waku.Query(ctx, peer, topics, uint64(r.From), uint64(r.To), options)
|
pbCursor, err := w.waku.Query(ctx, peer, r.PubsubTopic, contentTopics, uint64(r.From), uint64(r.To), options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -229,6 +230,15 @@ func (w *gethWakuV2Wrapper) StopDiscV5() error {
|
||||||
return w.waku.StopDiscV5()
|
return w.waku.StopDiscV5()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Subscribe to a pubsub topic, passing an optional public key if the pubsub topic is protected
|
||||||
|
func (w *gethWakuV2Wrapper) SubscribeToPubsubTopic(topic string, optPublicKey *ecdsa.PublicKey) error {
|
||||||
|
return w.waku.SubscribeToPubsubTopic(topic, optPublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *gethWakuV2Wrapper) StorePubsubTopicKey(topic string, privKey *ecdsa.PrivateKey) error {
|
||||||
|
return w.waku.StorePubsubTopicKey(topic, privKey)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *gethWakuV2Wrapper) AddStorePeer(address string) (peer.ID, error) {
|
func (w *gethWakuV2Wrapper) AddStorePeer(address string) (peer.ID, error) {
|
||||||
return w.waku.AddStorePeer(address)
|
return w.waku.AddStorePeer(address)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,10 +28,11 @@ type MessagesRequest struct {
|
||||||
StoreCursor *StoreRequestCursor `json:"storeCursor"`
|
StoreCursor *StoreRequestCursor `json:"storeCursor"`
|
||||||
// Bloom is a filter to match requested messages.
|
// Bloom is a filter to match requested messages.
|
||||||
Bloom []byte `json:"bloom"`
|
Bloom []byte `json:"bloom"`
|
||||||
|
// PubsubTopic is the gossipsub topic on which the message was broadcasted
|
||||||
// Topics is a list of topics. A returned message should
|
PubsubTopic string
|
||||||
|
// ContentTopics is a list of topics. A returned message should
|
||||||
// belong to one of the topics from the list.
|
// belong to one of the topics from the list.
|
||||||
Topics [][]byte `json:"topics"`
|
ContentTopics [][]byte `json:"topics"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type StoreRequestCursor struct {
|
type StoreRequestCursor struct {
|
||||||
|
|
|
@ -6,17 +6,18 @@ import (
|
||||||
|
|
||||||
// NewMessage represents a new whisper message that is posted through the RPC.
|
// NewMessage represents a new whisper message that is posted through the RPC.
|
||||||
type NewMessage struct {
|
type NewMessage struct {
|
||||||
SymKeyID string `json:"symKeyID"`
|
SymKeyID string `json:"symKeyID"`
|
||||||
PublicKey []byte `json:"pubKey"`
|
PublicKey []byte `json:"pubKey"`
|
||||||
SigID string `json:"sig"`
|
SigID string `json:"sig"`
|
||||||
TTL uint32 `json:"ttl"`
|
TTL uint32 `json:"ttl"`
|
||||||
Topic TopicType `json:"topic"`
|
PubsubTopic string `json:"pubsubTopic"`
|
||||||
Payload []byte `json:"payload"`
|
Topic TopicType `json:"topic"`
|
||||||
Padding []byte `json:"padding"`
|
Payload []byte `json:"payload"`
|
||||||
PowTime uint32 `json:"powTime"`
|
Padding []byte `json:"padding"`
|
||||||
PowTarget float64 `json:"powTarget"`
|
PowTime uint32 `json:"powTime"`
|
||||||
TargetPeer string `json:"targetPeer"`
|
PowTarget float64 `json:"powTarget"`
|
||||||
Ephemeral bool `json:"ephemeral"`
|
TargetPeer string `json:"targetPeer"`
|
||||||
|
Ephemeral bool `json:"ephemeral"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message is the RPC representation of a whisper message.
|
// Message is the RPC representation of a whisper message.
|
||||||
|
@ -24,6 +25,7 @@ type Message struct {
|
||||||
Sig []byte `json:"sig,omitempty"`
|
Sig []byte `json:"sig,omitempty"`
|
||||||
TTL uint32 `json:"ttl"`
|
TTL uint32 `json:"ttl"`
|
||||||
Timestamp uint32 `json:"timestamp"`
|
Timestamp uint32 `json:"timestamp"`
|
||||||
|
PubsubTopic string `json:"pubsubTopic"`
|
||||||
Topic TopicType `json:"topic"`
|
Topic TopicType `json:"topic"`
|
||||||
Payload []byte `json:"payload"`
|
Payload []byte `json:"payload"`
|
||||||
Padding []byte `json:"padding"`
|
Padding []byte `json:"padding"`
|
||||||
|
@ -40,6 +42,7 @@ type Criteria struct {
|
||||||
PrivateKeyID string `json:"privateKeyID"`
|
PrivateKeyID string `json:"privateKeyID"`
|
||||||
Sig []byte `json:"sig"`
|
Sig []byte `json:"sig"`
|
||||||
MinPow float64 `json:"minPow"`
|
MinPow float64 `json:"minPow"`
|
||||||
|
PubsubTopic string `json:"pubsubTopic"`
|
||||||
Topics []TopicType `json:"topics"`
|
Topics []TopicType `json:"topics"`
|
||||||
AllowP2P bool `json:"allowP2P"`
|
AllowP2P bool `json:"allowP2P"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,5 +6,6 @@ type SubscriptionOptions struct {
|
||||||
PrivateKeyID string
|
PrivateKeyID string
|
||||||
SymKeyID string
|
SymKeyID string
|
||||||
PoW float64
|
PoW float64
|
||||||
|
PubsubTopic string
|
||||||
Topics [][]byte
|
Topics [][]byte
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,10 @@ type Waku interface {
|
||||||
|
|
||||||
StopDiscV5() error
|
StopDiscV5() error
|
||||||
|
|
||||||
|
SubscribeToPubsubTopic(topic string, optPublicKey *ecdsa.PublicKey) error
|
||||||
|
|
||||||
|
StorePubsubTopicKey(topic string, privKey *ecdsa.PrivateKey) error
|
||||||
|
|
||||||
AddStorePeer(address string) (peer.ID, error)
|
AddStorePeer(address string) (peer.ID, error)
|
||||||
|
|
||||||
AddRelayPeer(address string) (peer.ID, error)
|
AddRelayPeer(address string) (peer.ID, error)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -336,7 +337,7 @@ func (s *MessageSender) sendCommunity(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to decompress pubkey")
|
return nil, errors.Wrap(err, "failed to decompress pubkey")
|
||||||
}
|
}
|
||||||
hash, newMessage, err = s.dispatchCommunityMessage(ctx, pubkey, payload, messageIDs)
|
hash, newMessage, err = s.dispatchCommunityMessage(ctx, pubkey, payload, messageIDs, rawMessage.PubsubTopic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("failed to send a community message", zap.Error(err))
|
s.logger.Error("failed to send a community message", zap.Error(err))
|
||||||
return nil, errors.Wrap(err, "failed to send a message spec")
|
return nil, errors.Wrap(err, "failed to send a message spec")
|
||||||
|
@ -524,10 +525,11 @@ func (s *MessageSender) EncodeAbridgedMembershipUpdate(
|
||||||
func (s *MessageSender) dispatchCommunityChatMessage(ctx context.Context, rawMessage *RawMessage, wrappedMessage []byte) ([]byte, *types.NewMessage, error) {
|
func (s *MessageSender) dispatchCommunityChatMessage(ctx context.Context, rawMessage *RawMessage, wrappedMessage []byte) ([]byte, *types.NewMessage, error) {
|
||||||
|
|
||||||
newMessage := &types.NewMessage{
|
newMessage := &types.NewMessage{
|
||||||
TTL: whisperTTL,
|
TTL: whisperTTL,
|
||||||
Payload: wrappedMessage,
|
Payload: wrappedMessage,
|
||||||
PowTarget: calculatePoW(wrappedMessage),
|
PowTarget: calculatePoW(wrappedMessage),
|
||||||
PowTime: whisperPoWTime,
|
PowTime: whisperPoWTime,
|
||||||
|
PubsubTopic: rawMessage.PubsubTopic,
|
||||||
}
|
}
|
||||||
|
|
||||||
if rawMessage.BeforeDispatch != nil {
|
if rawMessage.BeforeDispatch != nil {
|
||||||
|
@ -586,6 +588,7 @@ func (s *MessageSender) SendPublic(
|
||||||
}
|
}
|
||||||
|
|
||||||
newMessage.Ephemeral = rawMessage.Ephemeral
|
newMessage.Ephemeral = rawMessage.Ephemeral
|
||||||
|
newMessage.PubsubTopic = rawMessage.PubsubTopic
|
||||||
|
|
||||||
messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage)
|
messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage)
|
||||||
rawMessage.ID = types.EncodeHex(messageID)
|
rawMessage.ID = types.EncodeHex(messageID)
|
||||||
|
@ -786,6 +789,7 @@ func (s *MessageSender) handleErrDeviceNotFound(ctx context.Context, publicKey *
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessageSender) wrapMessageV1(rawMessage *RawMessage) ([]byte, error) {
|
func (s *MessageSender) wrapMessageV1(rawMessage *RawMessage) ([]byte, error) {
|
||||||
|
fmt.Println("wrapMessageV1: pubsubTopic: ", rawMessage.PubsubTopic, " message type", rawMessage.MessageType.String())
|
||||||
wrappedMessage, err := v1protocol.WrapMessageV1(rawMessage.Payload, rawMessage.MessageType, rawMessage.Sender)
|
wrappedMessage, err := v1protocol.WrapMessageV1(rawMessage.Payload, rawMessage.MessageType, rawMessage.Sender)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to wrap message")
|
return nil, errors.Wrap(err, "failed to wrap message")
|
||||||
|
@ -856,10 +860,11 @@ func (s *MessageSender) sendDataSync(ctx context.Context, publicKey *ecdsa.Publi
|
||||||
// sendPrivateRawMessage sends a message not wrapped in an encryption layer
|
// sendPrivateRawMessage sends a message not wrapped in an encryption layer
|
||||||
func (s *MessageSender) sendPrivateRawMessage(ctx context.Context, rawMessage *RawMessage, publicKey *ecdsa.PublicKey, payload []byte, messageIDs [][]byte) ([]byte, *types.NewMessage, error) {
|
func (s *MessageSender) sendPrivateRawMessage(ctx context.Context, rawMessage *RawMessage, publicKey *ecdsa.PublicKey, payload []byte, messageIDs [][]byte) ([]byte, *types.NewMessage, error) {
|
||||||
newMessage := &types.NewMessage{
|
newMessage := &types.NewMessage{
|
||||||
TTL: whisperTTL,
|
TTL: whisperTTL,
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
PowTarget: calculatePoW(payload),
|
PowTarget: calculatePoW(payload),
|
||||||
PowTime: whisperPoWTime,
|
PowTime: whisperPoWTime,
|
||||||
|
PubsubTopic: rawMessage.PubsubTopic,
|
||||||
}
|
}
|
||||||
var hash []byte
|
var hash []byte
|
||||||
var err error
|
var err error
|
||||||
|
@ -878,12 +883,13 @@ func (s *MessageSender) sendPrivateRawMessage(ctx context.Context, rawMessage *R
|
||||||
|
|
||||||
// sendCommunityMessage sends a message not wrapped in an encryption layer
|
// sendCommunityMessage sends a message not wrapped in an encryption layer
|
||||||
// to a community
|
// to a community
|
||||||
func (s *MessageSender) dispatchCommunityMessage(ctx context.Context, publicKey *ecdsa.PublicKey, payload []byte, messageIDs [][]byte) ([]byte, *types.NewMessage, error) {
|
func (s *MessageSender) dispatchCommunityMessage(ctx context.Context, publicKey *ecdsa.PublicKey, payload []byte, messageIDs [][]byte, pubsubTopic string) ([]byte, *types.NewMessage, error) {
|
||||||
newMessage := &types.NewMessage{
|
newMessage := &types.NewMessage{
|
||||||
TTL: whisperTTL,
|
TTL: whisperTTL,
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
PowTarget: calculatePoW(payload),
|
PowTarget: calculatePoW(payload),
|
||||||
PowTime: whisperPoWTime,
|
PowTime: whisperPoWTime,
|
||||||
|
PubsubTopic: pubsubTopic,
|
||||||
}
|
}
|
||||||
|
|
||||||
hash, err := s.transport.SendCommunityMessage(ctx, newMessage, publicKey)
|
hash, err := s.transport.SendCommunityMessage(ctx, newMessage, publicKey)
|
||||||
|
|
|
@ -36,4 +36,5 @@ type RawMessage struct {
|
||||||
Ephemeral bool
|
Ephemeral bool
|
||||||
BeforeDispatch func(*RawMessage) error
|
BeforeDispatch func(*RawMessage) error
|
||||||
HashRatchetGroupID []byte
|
HashRatchetGroupID []byte
|
||||||
|
PubsubTopic string
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/status-im/status-go/eth-node/crypto"
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
|
@ -19,6 +20,7 @@ import (
|
||||||
community_token "github.com/status-im/status-go/protocol/communities/token"
|
community_token "github.com/status-im/status-go/protocol/communities/token"
|
||||||
"github.com/status-im/status-go/protocol/protobuf"
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
"github.com/status-im/status-go/protocol/requests"
|
"github.com/status-im/status-go/protocol/requests"
|
||||||
|
"github.com/status-im/status-go/protocol/transport"
|
||||||
"github.com/status-im/status-go/protocol/v1"
|
"github.com/status-im/status-go/protocol/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1194,13 +1196,26 @@ func (o *Community) MemberUpdateChannelID() string {
|
||||||
return o.IDString() + "-memberUpdate"
|
return o.IDString() + "-memberUpdate"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Community) DefaultFilters() []string {
|
func (o *Community) PubsubTopic() string {
|
||||||
|
return transport.GetPubsubTopic(o.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Community) DefaultFilters() []transport.FiltersToInitialize {
|
||||||
cID := o.IDString()
|
cID := o.IDString()
|
||||||
uncompressedPubKey := common.PubkeyToHex(o.config.ID)[2:]
|
uncompressedPubKey := common.PubkeyToHex(o.config.ID)[2:]
|
||||||
updatesChannelID := o.StatusUpdatesChannelID()
|
updatesChannelID := o.StatusUpdatesChannelID()
|
||||||
mlChannelID := o.MagnetlinkMessageChannelID()
|
mlChannelID := o.MagnetlinkMessageChannelID()
|
||||||
memberUpdateChannelID := o.MemberUpdateChannelID()
|
memberUpdateChannelID := o.MemberUpdateChannelID()
|
||||||
return []string{cID, uncompressedPubKey, updatesChannelID, mlChannelID, memberUpdateChannelID}
|
|
||||||
|
communityPubsubTopic := o.PubsubTopic()
|
||||||
|
|
||||||
|
return []transport.FiltersToInitialize{
|
||||||
|
{ChatID: cID, PubsubTopic: relay.DefaultWakuTopic}, // TODO: verify if this goes into default topic
|
||||||
|
{ChatID: uncompressedPubKey, PubsubTopic: communityPubsubTopic},
|
||||||
|
{ChatID: updatesChannelID, PubsubTopic: communityPubsubTopic},
|
||||||
|
{ChatID: mlChannelID, PubsubTopic: communityPubsubTopic},
|
||||||
|
{ChatID: memberUpdateChannelID, PubsubTopic: communityPubsubTopic},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Community) PrivateKey() *ecdsa.PrivateKey {
|
func (o *Community) PrivateKey() *ecdsa.PrivateKey {
|
||||||
|
|
|
@ -3369,7 +3369,7 @@ func (m *Manager) GetCommunityChatsTopics(communityID types.HexBytes) ([]types.T
|
||||||
|
|
||||||
topics := []types.TopicType{}
|
topics := []types.TopicType{}
|
||||||
for _, filter := range filters {
|
for _, filter := range filters {
|
||||||
topics = append(topics, filter.Topic)
|
topics = append(topics, filter.ContentTopic)
|
||||||
}
|
}
|
||||||
|
|
||||||
return topics, nil
|
return topics, nil
|
||||||
|
@ -3412,7 +3412,7 @@ func (m *Manager) GetHistoryArchivePartitionStartTimestamp(communityID types.Hex
|
||||||
topics := []types.TopicType{}
|
topics := []types.TopicType{}
|
||||||
|
|
||||||
for _, filter := range filters {
|
for _, filter := range filters {
|
||||||
topics = append(topics, filter.Topic)
|
topics = append(topics, filter.ContentTopic)
|
||||||
}
|
}
|
||||||
|
|
||||||
lastArchiveEndDateTimestamp, err := m.GetLastMessageArchiveEndDate(communityID)
|
lastArchiveEndDateTimestamp, err := m.GetLastMessageArchiveEndDate(communityID)
|
||||||
|
|
|
@ -25,14 +25,14 @@ func (ckd *CommunitiesKeyDistributorImpl) Distribute(community *communities.Comm
|
||||||
return communities.ErrNotControlNode
|
return communities.ErrNotControlNode
|
||||||
}
|
}
|
||||||
|
|
||||||
err := ckd.distributeKey(community.ID(), community.ID(), &keyActions.CommunityKeyAction)
|
err := ckd.distributeKey(community, community.ID(), &keyActions.CommunityKeyAction)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for channelID := range keyActions.ChannelKeysActions {
|
for channelID := range keyActions.ChannelKeysActions {
|
||||||
keyAction := keyActions.ChannelKeysActions[channelID]
|
keyAction := keyActions.ChannelKeysActions[channelID]
|
||||||
err := ckd.distributeKey(community.ID(), []byte(community.IDString()+channelID), &keyAction)
|
err := ckd.distributeKey(community, []byte(community.IDString()+channelID), &keyAction)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ func (ckd *CommunitiesKeyDistributorImpl) Rekey(community *communities.Community
|
||||||
return communities.ErrNotControlNode
|
return communities.ErrNotControlNode
|
||||||
}
|
}
|
||||||
|
|
||||||
err := ckd.distributeKey(community.ID(), community.ID(), &communities.EncryptionKeyAction{
|
err := ckd.distributeKey(community, community.ID(), &communities.EncryptionKeyAction{
|
||||||
ActionType: communities.EncryptionKeyRekey,
|
ActionType: communities.EncryptionKeyRekey,
|
||||||
Members: community.Members(),
|
Members: community.Members(),
|
||||||
})
|
})
|
||||||
|
@ -55,7 +55,7 @@ func (ckd *CommunitiesKeyDistributorImpl) Rekey(community *communities.Community
|
||||||
}
|
}
|
||||||
|
|
||||||
for channelID, channel := range community.Chats() {
|
for channelID, channel := range community.Chats() {
|
||||||
err := ckd.distributeKey(community.ID(), []byte(community.IDString()+channelID), &communities.EncryptionKeyAction{
|
err := ckd.distributeKey(community, []byte(community.IDString()+channelID), &communities.EncryptionKeyAction{
|
||||||
ActionType: communities.EncryptionKeyRekey,
|
ActionType: communities.EncryptionKeyRekey,
|
||||||
Members: channel.Members,
|
Members: channel.Members,
|
||||||
})
|
})
|
||||||
|
@ -67,7 +67,7 @@ func (ckd *CommunitiesKeyDistributorImpl) Rekey(community *communities.Community
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ckd *CommunitiesKeyDistributorImpl) distributeKey(communityID, hashRatchetGroupID []byte, keyAction *communities.EncryptionKeyAction) error {
|
func (ckd *CommunitiesKeyDistributorImpl) distributeKey(community *communities.Community, hashRatchetGroupID []byte, keyAction *communities.EncryptionKeyAction) error {
|
||||||
pubkeys := make([]*ecdsa.PublicKey, len(keyAction.Members))
|
pubkeys := make([]*ecdsa.PublicKey, len(keyAction.Members))
|
||||||
i := 0
|
i := 0
|
||||||
for hex := range keyAction.Members {
|
for hex := range keyAction.Members {
|
||||||
|
@ -82,19 +82,19 @@ func (ckd *CommunitiesKeyDistributorImpl) distributeKey(communityID, hashRatchet
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ckd.sendKeyExchangeMessage(communityID, hashRatchetGroupID, pubkeys, common.KeyExMsgReuse)
|
err = ckd.sendKeyExchangeMessage(community, hashRatchetGroupID, pubkeys, common.KeyExMsgReuse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case communities.EncryptionKeyRekey:
|
case communities.EncryptionKeyRekey:
|
||||||
err := ckd.sendKeyExchangeMessage(communityID, hashRatchetGroupID, pubkeys, common.KeyExMsgRekey)
|
err := ckd.sendKeyExchangeMessage(community, hashRatchetGroupID, pubkeys, common.KeyExMsgRekey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case communities.EncryptionKeySendToMembers:
|
case communities.EncryptionKeySendToMembers:
|
||||||
err := ckd.sendKeyExchangeMessage(communityID, hashRatchetGroupID, pubkeys, common.KeyExMsgReuse)
|
err := ckd.sendKeyExchangeMessage(community, hashRatchetGroupID, pubkeys, common.KeyExMsgReuse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -103,14 +103,15 @@ func (ckd *CommunitiesKeyDistributorImpl) distributeKey(communityID, hashRatchet
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ckd *CommunitiesKeyDistributorImpl) sendKeyExchangeMessage(communityID, hashRatchetGroupID []byte, pubkeys []*ecdsa.PublicKey, msgType common.CommKeyExMsgType) error {
|
func (ckd *CommunitiesKeyDistributorImpl) sendKeyExchangeMessage(community *communities.Community, hashRatchetGroupID []byte, pubkeys []*ecdsa.PublicKey, msgType common.CommKeyExMsgType) error {
|
||||||
rawMessage := common.RawMessage{
|
rawMessage := common.RawMessage{
|
||||||
SkipProtocolLayer: false,
|
SkipProtocolLayer: false,
|
||||||
CommunityID: communityID,
|
CommunityID: community.ID(),
|
||||||
CommunityKeyExMsgType: msgType,
|
CommunityKeyExMsgType: msgType,
|
||||||
Recipients: pubkeys,
|
Recipients: pubkeys,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE,
|
MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE,
|
||||||
HashRatchetGroupID: hashRatchetGroupID,
|
HashRatchetGroupID: hashRatchetGroupID,
|
||||||
|
PubsubTopic: community.PubsubTopic(), // TODO: confirm if it should be sent in community pubsub topic
|
||||||
}
|
}
|
||||||
_, err := ckd.sender.SendCommunityMessage(context.Background(), rawMessage)
|
_, err := ckd.sender.SendCommunityMessage(context.Background(), rawMessage)
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
|
|
||||||
|
@ -988,6 +989,7 @@ func (m *Messenger) publishContactCode() error {
|
||||||
}
|
}
|
||||||
for _, community := range joinedCommunities {
|
for _, community := range joinedCommunities {
|
||||||
rawMessage.LocalChatID = community.MemberUpdateChannelID()
|
rawMessage.LocalChatID = community.MemberUpdateChannelID()
|
||||||
|
rawMessage.PubsubTopic = community.PubsubTopic()
|
||||||
_, err = m.sender.SendPublic(ctx, rawMessage.LocalChatID, rawMessage)
|
_, err = m.sender.SendPublic(ctx, rawMessage.LocalChatID, rawMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1586,7 +1588,7 @@ func (m *Messenger) Init() error {
|
||||||
logger := m.logger.With(zap.String("site", "Init"))
|
logger := m.logger.With(zap.String("site", "Init"))
|
||||||
|
|
||||||
var (
|
var (
|
||||||
publicChatIDs []string
|
filtersToInit []transport.FiltersToInitialize
|
||||||
publicKeys []*ecdsa.PublicKey
|
publicKeys []*ecdsa.PublicKey
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1596,7 +1598,7 @@ func (m *Messenger) Init() error {
|
||||||
}
|
}
|
||||||
for _, org := range joinedCommunities {
|
for _, org := range joinedCommunities {
|
||||||
// the org advertise on the public topic derived by the pk
|
// the org advertise on the public topic derived by the pk
|
||||||
publicChatIDs = append(publicChatIDs, org.DefaultFilters()...)
|
filtersToInit = append(filtersToInit, org.DefaultFilters()...)
|
||||||
|
|
||||||
// This is for status-go versions that didn't have `CommunitySettings`
|
// This is for status-go versions that didn't have `CommunitySettings`
|
||||||
// We need to ensure communities that existed before community settings
|
// We need to ensure communities that existed before community settings
|
||||||
|
@ -1644,21 +1646,24 @@ func (m *Messenger) Init() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, org := range spectatedCommunities {
|
for _, org := range spectatedCommunities {
|
||||||
publicChatIDs = append(publicChatIDs, org.DefaultFilters()...)
|
filtersToInit = append(filtersToInit, org.DefaultFilters()...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init filters for the communities we control
|
// Init filters for the communities we control
|
||||||
var controlledCommunitiesPks []*ecdsa.PrivateKey
|
var communityFiltersToInitialize []transport.CommunityFilterToInitialize
|
||||||
controlledCommunities, err := m.communitiesManager.ControlledCommunities()
|
controlledCommunities, err := m.communitiesManager.ControlledCommunities()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range controlledCommunities {
|
for _, c := range controlledCommunities {
|
||||||
controlledCommunitiesPks = append(controlledCommunitiesPks, c.PrivateKey())
|
communityFiltersToInitialize = append(communityFiltersToInitialize, transport.CommunityFilterToInitialize{
|
||||||
|
CommunityID: c.ID(),
|
||||||
|
PrivKey: c.PrivateKey(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = m.transport.InitCommunityFilters(controlledCommunitiesPks)
|
_, err = m.transport.InitCommunityFilters(communityFiltersToInitialize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1688,10 +1693,13 @@ func (m *Messenger) Init() error {
|
||||||
|
|
||||||
switch chat.ChatType {
|
switch chat.ChatType {
|
||||||
case ChatTypePublic, ChatTypeProfile:
|
case ChatTypePublic, ChatTypeProfile:
|
||||||
publicChatIDs = append(publicChatIDs, chat.ID)
|
filtersToInit = append(filtersToInit, transport.FiltersToInitialize{ChatID: chat.ID, PubsubTopic: relay.DefaultWakuTopic})
|
||||||
case ChatTypeCommunityChat:
|
case ChatTypeCommunityChat:
|
||||||
// TODO not public chat now
|
communityID, err := hexutil.Decode(chat.CommunityID)
|
||||||
publicChatIDs = append(publicChatIDs, chat.ID)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
filtersToInit = append(filtersToInit, transport.FiltersToInitialize{ChatID: chat.ID, PubsubTopic: transport.GetPubsubTopic(communityID)})
|
||||||
case ChatTypeOneToOne:
|
case ChatTypeOneToOne:
|
||||||
pk, err := chat.PublicKey()
|
pk, err := chat.PublicKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1766,7 +1774,7 @@ func (m *Messenger) Init() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = m.transport.InitFilters(publicChatIDs, publicKeys)
|
_, err = m.transport.InitFilters(filtersToInit, publicKeys)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1935,6 +1943,7 @@ func (m *Messenger) reSendRawMessage(ctx context.Context, messageID string) erro
|
||||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||||
LocalChatID: chat.ID,
|
LocalChatID: chat.ID,
|
||||||
Payload: message.Payload,
|
Payload: message.Payload,
|
||||||
|
PubsubTopic: message.PubsubTopic,
|
||||||
MessageType: message.MessageType,
|
MessageType: message.MessageType,
|
||||||
Recipients: message.Recipients,
|
Recipients: message.Recipients,
|
||||||
ResendAutomatically: message.ResendAutomatically,
|
ResendAutomatically: message.ResendAutomatically,
|
||||||
|
@ -2047,6 +2056,13 @@ func (m *Messenger) dispatchMessage(ctx context.Context, rawMessage common.RawMe
|
||||||
return rawMessage, err
|
return rawMessage, err
|
||||||
}
|
}
|
||||||
case ChatTypeCommunityChat:
|
case ChatTypeCommunityChat:
|
||||||
|
communityID, err := hexutil.Decode(chat.CommunityID)
|
||||||
|
if err != nil {
|
||||||
|
return rawMessage, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rawMessage.PubsubTopic = transport.GetPubsubTopic(communityID)
|
||||||
|
|
||||||
// TODO: add grant
|
// TODO: add grant
|
||||||
canPost, err := m.communitiesManager.CanPost(&m.identity.PublicKey, chat.CommunityID, chat.CommunityChatID(), nil)
|
canPost, err := m.communitiesManager.CanPost(&m.identity.PublicKey, chat.CommunityID, chat.CommunityChatID(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -3423,7 +3439,7 @@ func (m *Messenger) handleImportedMessages(messagesToHandle map[transport.Filter
|
||||||
}
|
}
|
||||||
|
|
||||||
messageState.CurrentMessageState.Message = protoMessage
|
messageState.CurrentMessageState.Message = protoMessage
|
||||||
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, messageState.CurrentMessageState.Message)
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.ContentTopic, filter.ChatID, msg.Type, messageState.CurrentMessageState.Message)
|
||||||
err = m.HandleImportedChatMessage(messageState)
|
err = m.HandleImportedChatMessage(messageState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warn("failed to handle ChatMessage", zap.Error(err))
|
logger.Warn("failed to handle ChatMessage", zap.Error(err))
|
||||||
|
|
|
@ -415,7 +415,7 @@ func (m *Messenger) deactivateChat(chatID string, deactivationClock uint64, shou
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err := m.mailserversDatabase.ResetLastRequest(filter.Topic.String())
|
err := m.mailserversDatabase.ResetLastRequest(filter.PubsubTopic, filter.ContentTopic.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,7 @@ func (m *Messenger) publishCommunityEvents(msg *communities.CommunityEventsMessa
|
||||||
// we don't want to wrap in an encryption layer message
|
// we don't want to wrap in an encryption layer message
|
||||||
SkipProtocolLayer: true,
|
SkipProtocolLayer: true,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_EVENTS_MESSAGE,
|
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_EVENTS_MESSAGE,
|
||||||
|
PubsubTopic: transport.GetPubsubTopic(msg.CommunityID), // TODO: confirm if it should be sent in community pubsub topic
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: resend in case of failure?
|
// TODO: resend in case of failure?
|
||||||
|
@ -129,6 +130,7 @@ func (m *Messenger) publishCommunityEventsRejected(community *communities.Commun
|
||||||
// we don't want to wrap in an encryption layer message
|
// we don't want to wrap in an encryption layer message
|
||||||
SkipProtocolLayer: true,
|
SkipProtocolLayer: true,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_EVENTS_MESSAGE_REJECTED,
|
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_EVENTS_MESSAGE_REJECTED,
|
||||||
|
PubsubTopic: community.PubsubTopic(), // TODO: confirm if it should be sent in community pubsub topic
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: resend in case of failure?
|
// TODO: resend in case of failure?
|
||||||
|
@ -568,16 +570,17 @@ func (m *Messenger) CuratedCommunities() (*communities.KnownCommunitiesResponse,
|
||||||
func (m *Messenger) initCommunityChats(community *communities.Community) ([]*Chat, error) {
|
func (m *Messenger) initCommunityChats(community *communities.Community) ([]*Chat, error) {
|
||||||
logger := m.logger.Named("initCommunityChats")
|
logger := m.logger.Named("initCommunityChats")
|
||||||
|
|
||||||
chatIDs := community.DefaultFilters()
|
publicFiltersToInit := community.DefaultFilters()
|
||||||
|
|
||||||
chats := CreateCommunityChats(community, m.getTimesource())
|
chats := CreateCommunityChats(community, m.getTimesource())
|
||||||
|
|
||||||
for _, chat := range chats {
|
for _, chat := range chats {
|
||||||
chatIDs = append(chatIDs, chat.ID)
|
publicFiltersToInit = append(publicFiltersToInit, transport.FiltersToInitialize{ChatID: chat.ID, PubsubTopic: community.PubsubTopic()})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load transport filters
|
// Load transport filters
|
||||||
filters, err := m.transport.InitPublicFilters(chatIDs)
|
filters, err := m.transport.InitPublicFilters(publicFiltersToInit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Debug("m.transport.InitPublicFilters error", zap.Error(err))
|
logger.Debug("m.transport.InitPublicFilters error", zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -585,7 +588,11 @@ func (m *Messenger) initCommunityChats(community *communities.Community) ([]*Cha
|
||||||
|
|
||||||
if community.IsControlNode() {
|
if community.IsControlNode() {
|
||||||
// Init the community filter so we can receive messages on the community
|
// Init the community filter so we can receive messages on the community
|
||||||
communityFilters, err := m.transport.InitCommunityFilters([]*ecdsa.PrivateKey{community.PrivateKey()})
|
communityFilters, err := m.transport.InitCommunityFilters([]transport.CommunityFilterToInitialize{{
|
||||||
|
CommunityID: community.ID(),
|
||||||
|
PrivKey: community.PrivateKey(),
|
||||||
|
}})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -657,6 +664,16 @@ func (m *Messenger) JoinCommunity(ctx context.Context, communityID types.HexByte
|
||||||
return mr, nil
|
return mr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Messenger) subscribeToCommunityShard(communityID []byte) error {
|
||||||
|
// TODO: store private key and topic
|
||||||
|
// TODO: determine pubsub topic and public key for community
|
||||||
|
// TODO: this should probably be moved completely to transport once pubsub topic logic is implemented
|
||||||
|
|
||||||
|
pubsubTopic := transport.GetPubsubTopic(communityID)
|
||||||
|
var communityPubKey *ecdsa.PublicKey
|
||||||
|
return m.transport.SubscribeToPubsubTopic(pubsubTopic, communityPubKey)
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Messenger) joinCommunity(ctx context.Context, communityID types.HexBytes, forceJoin bool) (*MessengerResponse, error) {
|
func (m *Messenger) joinCommunity(ctx context.Context, communityID types.HexBytes, forceJoin bool) (*MessengerResponse, error) {
|
||||||
logger := m.logger.Named("joinCommunity")
|
logger := m.logger.Named("joinCommunity")
|
||||||
|
|
||||||
|
@ -679,6 +696,10 @@ func (m *Messenger) joinCommunity(ctx context.Context, communityID types.HexByte
|
||||||
if _, err = m.initCommunitySettings(communityID); err != nil {
|
if _, err = m.initCommunitySettings(communityID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = m.subscribeToCommunityShard(communityID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
communitySettings, err := m.communitiesManager.GetCommunitySettingsByID(communityID)
|
communitySettings, err := m.communitiesManager.GetCommunitySettingsByID(communityID)
|
||||||
|
@ -730,6 +751,10 @@ func (m *Messenger) SpectateCommunity(communityID types.HexBytes) (*MessengerRes
|
||||||
|
|
||||||
response.AddCommunity(community)
|
response.AddCommunity(community)
|
||||||
|
|
||||||
|
if err = m.subscribeToCommunityShard(community.ID()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1003,6 +1028,7 @@ func (m *Messenger) RequestToJoinCommunity(request *requests.RequestToJoinCommun
|
||||||
CommunityID: community.ID(),
|
CommunityID: community.ID(),
|
||||||
SkipProtocolLayer: true,
|
SkipProtocolLayer: true,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_REQUEST_TO_JOIN,
|
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_REQUEST_TO_JOIN,
|
||||||
|
PubsubTopic: community.PubsubTopic(), // TODO: confirm if it should be sent in community pubsub topic
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = m.sender.SendCommunityMessage(context.Background(), rawMessage)
|
_, err = m.sender.SendCommunityMessage(context.Background(), rawMessage)
|
||||||
|
@ -1146,6 +1172,7 @@ func (m *Messenger) EditSharedAddressesForCommunity(request *requests.EditShared
|
||||||
CommunityID: community.ID(),
|
CommunityID: community.ID(),
|
||||||
SkipProtocolLayer: true,
|
SkipProtocolLayer: true,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_EDIT_SHARED_ADDRESSES,
|
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_EDIT_SHARED_ADDRESSES,
|
||||||
|
PubsubTopic: community.PubsubTopic(),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = m.sender.SendCommunityMessage(context.Background(), rawMessage)
|
_, err = m.sender.SendCommunityMessage(context.Background(), rawMessage)
|
||||||
|
@ -1301,6 +1328,7 @@ func (m *Messenger) CancelRequestToJoinCommunity(request *requests.CancelRequest
|
||||||
CommunityID: community.ID(),
|
CommunityID: community.ID(),
|
||||||
SkipProtocolLayer: true,
|
SkipProtocolLayer: true,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_CANCEL_REQUEST_TO_JOIN,
|
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_CANCEL_REQUEST_TO_JOIN,
|
||||||
|
PubsubTopic: community.PubsubTopic(), // TODO: confirm if it should be send in community pubsub topic
|
||||||
}
|
}
|
||||||
_, err = m.sender.SendCommunityMessage(context.Background(), rawMessage)
|
_, err = m.sender.SendCommunityMessage(context.Background(), rawMessage)
|
||||||
|
|
||||||
|
@ -1406,6 +1434,7 @@ func (m *Messenger) AcceptRequestToJoinCommunity(request *requests.AcceptRequest
|
||||||
Sender: community.PrivateKey(),
|
Sender: community.PrivateKey(),
|
||||||
SkipProtocolLayer: true,
|
SkipProtocolLayer: true,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_REQUEST_TO_JOIN_RESPONSE,
|
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_REQUEST_TO_JOIN_RESPONSE,
|
||||||
|
PubsubTopic: community.PubsubTopic(), // TODO: confirm if it should be sent in community pubsub topic
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = m.sender.SendPrivate(context.Background(), pk, rawMessage)
|
_, err = m.sender.SendPrivate(context.Background(), pk, rawMessage)
|
||||||
|
@ -1611,6 +1640,7 @@ func (m *Messenger) LeaveCommunity(communityID types.HexBytes) (*MessengerRespon
|
||||||
CommunityID: communityID,
|
CommunityID: communityID,
|
||||||
SkipProtocolLayer: true,
|
SkipProtocolLayer: true,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_REQUEST_TO_LEAVE,
|
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_REQUEST_TO_LEAVE,
|
||||||
|
PubsubTopic: transport.GetPubsubTopic(communityID), // TODO: confirm if it should be sent in the community pubsub topic
|
||||||
}
|
}
|
||||||
_, err = m.sender.SendCommunityMessage(context.Background(), rawMessage)
|
_, err = m.sender.SendCommunityMessage(context.Background(), rawMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1757,16 +1787,17 @@ func (m *Messenger) CreateCommunityChat(communityID types.HexBytes, c *protobuf.
|
||||||
response.CommunityChanges = []*communities.CommunityChanges{changes}
|
response.CommunityChanges = []*communities.CommunityChanges{changes}
|
||||||
|
|
||||||
var chats []*Chat
|
var chats []*Chat
|
||||||
var chatIDs []string
|
var publicFiltersToInit []transport.FiltersToInitialize
|
||||||
for chatID, chat := range changes.ChatsAdded {
|
for chatID, chat := range changes.ChatsAdded {
|
||||||
c := CreateCommunityChat(changes.Community.IDString(), chatID, chat, m.getTimesource())
|
c := CreateCommunityChat(changes.Community.IDString(), chatID, chat, m.getTimesource())
|
||||||
chats = append(chats, c)
|
chats = append(chats, c)
|
||||||
chatIDs = append(chatIDs, c.ID)
|
publicFiltersToInit = append(publicFiltersToInit, transport.FiltersToInitialize{ChatID: c.ID, PubsubTopic: changes.Community.PubsubTopic()})
|
||||||
|
|
||||||
response.AddChat(c)
|
response.AddChat(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load filters
|
// Load filters
|
||||||
filters, err := m.transport.InitPublicFilters(chatIDs)
|
filters, err := m.transport.InitPublicFilters(publicFiltersToInit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1798,16 +1829,16 @@ func (m *Messenger) EditCommunityChat(communityID types.HexBytes, chatID string,
|
||||||
response.CommunityChanges = []*communities.CommunityChanges{changes}
|
response.CommunityChanges = []*communities.CommunityChanges{changes}
|
||||||
|
|
||||||
var chats []*Chat
|
var chats []*Chat
|
||||||
var chatIDs []string
|
var publicFiltersToInit []transport.FiltersToInitialize
|
||||||
for chatID, change := range changes.ChatsModified {
|
for chatID, change := range changes.ChatsModified {
|
||||||
c := CreateCommunityChat(community.IDString(), chatID, change.ChatModified, m.getTimesource())
|
c := CreateCommunityChat(community.IDString(), chatID, change.ChatModified, m.getTimesource())
|
||||||
chats = append(chats, c)
|
chats = append(chats, c)
|
||||||
chatIDs = append(chatIDs, c.ID)
|
publicFiltersToInit = append(publicFiltersToInit, transport.FiltersToInitialize{ChatID: c.ID, PubsubTopic: community.PubsubTopic()})
|
||||||
response.AddChat(c)
|
response.AddChat(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load filters
|
// Load filters
|
||||||
filters, err := m.transport.InitPublicFilters(chatIDs)
|
filters, err := m.transport.InitPublicFilters(publicFiltersToInit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1862,8 +1893,15 @@ func (m *Messenger) CreateCommunity(request *requests.CreateCommunity, createDef
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = m.subscribeToCommunityShard(community.ID()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Init the community filter so we can receive messages on the community
|
// Init the community filter so we can receive messages on the community
|
||||||
_, err = m.transport.InitCommunityFilters([]*ecdsa.PrivateKey{community.PrivateKey()})
|
_, err = m.transport.InitCommunityFilters([]transport.CommunityFilterToInitialize{{
|
||||||
|
CommunityID: community.ID(),
|
||||||
|
PrivKey: community.PrivateKey(),
|
||||||
|
}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -2359,11 +2397,19 @@ func (m *Messenger) requestCommunityInfoFromMailserver(communityID string, waitF
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id, err := hexutil.Decode(communityID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
//If filter wasn't installed we create it and remember for deinstalling after
|
//If filter wasn't installed we create it and remember for deinstalling after
|
||||||
//response received
|
//response received
|
||||||
filter := m.transport.FilterByChatID(communityID)
|
filter := m.transport.FilterByChatID(communityID)
|
||||||
if filter == nil {
|
if filter == nil {
|
||||||
filters, err := m.transport.InitPublicFilters([]string{communityID})
|
filters, err := m.transport.InitPublicFilters([]transport.FiltersToInitialize{{
|
||||||
|
ChatID: communityID,
|
||||||
|
PubsubTopic: transport.GetPubsubTopic(id),
|
||||||
|
}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Can't install filter for community: %v", err)
|
return nil, fmt.Errorf("Can't install filter for community: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2380,9 +2426,9 @@ func (m *Messenger) requestCommunityInfoFromMailserver(communityID string, waitF
|
||||||
to := uint32(m.transport.GetCurrentTime() / 1000)
|
to := uint32(m.transport.GetCurrentTime() / 1000)
|
||||||
from := to - oneMonthInSeconds
|
from := to - oneMonthInSeconds
|
||||||
|
|
||||||
_, err := m.performMailserverRequest(func() (*MessengerResponse, error) {
|
_, err = m.performMailserverRequest(func() (*MessengerResponse, error) {
|
||||||
|
|
||||||
batch := MailserverBatch{From: from, To: to, Topics: []types.TopicType{filter.Topic}}
|
batch := MailserverBatch{From: from, To: to, Topics: []types.TopicType{filter.ContentTopic}}
|
||||||
m.logger.Info("Requesting historic")
|
m.logger.Info("Requesting historic")
|
||||||
err := m.processMailserverBatch(batch)
|
err := m.processMailserverBatch(batch)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -2446,11 +2492,19 @@ func (m *Messenger) requestCommunitiesFromMailserver(communityIDs []string) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id, err := hexutil.Decode(communityID)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
//If filter wasn't installed we create it and remember for deinstalling after
|
//If filter wasn't installed we create it and remember for deinstalling after
|
||||||
//response received
|
//response received
|
||||||
filter := m.transport.FilterByChatID(communityID)
|
filter := m.transport.FilterByChatID(communityID)
|
||||||
if filter == nil {
|
if filter == nil {
|
||||||
filters, err := m.transport.InitPublicFilters([]string{communityID})
|
filters, err := m.transport.InitPublicFilters([]transport.FiltersToInitialize{{
|
||||||
|
ChatID: communityID,
|
||||||
|
PubsubTopic: transport.GetPubsubTopic(id),
|
||||||
|
}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.logger.Error("Can't install filter for community", zap.Error(err))
|
m.logger.Error("Can't install filter for community", zap.Error(err))
|
||||||
continue
|
continue
|
||||||
|
@ -2465,7 +2519,7 @@ func (m *Messenger) requestCommunitiesFromMailserver(communityIDs []string) {
|
||||||
//we don't remember filter id associated with community because it was already installed
|
//we don't remember filter id associated with community because it was already installed
|
||||||
m.requestedCommunities[communityID] = nil
|
m.requestedCommunities[communityID] = nil
|
||||||
}
|
}
|
||||||
topics = append(topics, filter.Topic)
|
topics = append(topics, filter.ContentTopic)
|
||||||
}
|
}
|
||||||
|
|
||||||
to := uint32(m.transport.GetCurrentTime() / 1000)
|
to := uint32(m.transport.GetCurrentTime() / 1000)
|
||||||
|
@ -2602,7 +2656,7 @@ func (m *Messenger) handleCommunityResponse(state *ReceivedMessageState, communi
|
||||||
// Update relevant chats names and add new ones
|
// Update relevant chats names and add new ones
|
||||||
// Currently removal is not supported
|
// Currently removal is not supported
|
||||||
chats := CreateCommunityChats(community, state.Timesource)
|
chats := CreateCommunityChats(community, state.Timesource)
|
||||||
var chatIDs []string
|
var publicFiltersToInit []transport.FiltersToInitialize
|
||||||
for i, chat := range chats {
|
for i, chat := range chats {
|
||||||
|
|
||||||
oldChat, ok := state.AllChats.Load(chat.ID)
|
oldChat, ok := state.AllChats.Load(chat.ID)
|
||||||
|
@ -2611,7 +2665,10 @@ func (m *Messenger) handleCommunityResponse(state *ReceivedMessageState, communi
|
||||||
state.AllChats.Store(chat.ID, chats[i])
|
state.AllChats.Store(chat.ID, chats[i])
|
||||||
|
|
||||||
state.Response.AddChat(chat)
|
state.Response.AddChat(chat)
|
||||||
chatIDs = append(chatIDs, chat.ID)
|
publicFiltersToInit = append(publicFiltersToInit, transport.FiltersToInitialize{
|
||||||
|
ChatID: chat.ID,
|
||||||
|
PubsubTopic: community.PubsubTopic(),
|
||||||
|
})
|
||||||
// Update name, currently is the only field is mutable
|
// Update name, currently is the only field is mutable
|
||||||
} else if oldChat.Name != chat.Name ||
|
} else if oldChat.Name != chat.Name ||
|
||||||
oldChat.Description != chat.Description ||
|
oldChat.Description != chat.Description ||
|
||||||
|
@ -2636,7 +2693,7 @@ func (m *Messenger) handleCommunityResponse(state *ReceivedMessageState, communi
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load transport filters
|
// Load transport filters
|
||||||
filters, err := m.transport.InitPublicFilters(chatIDs)
|
filters, err := m.transport.InitPublicFilters(publicFiltersToInit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -2953,7 +3010,7 @@ func (m *Messenger) InitHistoryArchiveTasks(communities []*communities.Community
|
||||||
topics := []types.TopicType{}
|
topics := []types.TopicType{}
|
||||||
|
|
||||||
for _, filter := range filters {
|
for _, filter := range filters {
|
||||||
topics = append(topics, filter.Topic)
|
topics = append(topics, filter.ContentTopic)
|
||||||
}
|
}
|
||||||
|
|
||||||
// First we need to know the timestamp of the latest waku message
|
// First we need to know the timestamp of the latest waku message
|
||||||
|
@ -3199,6 +3256,7 @@ func (m *Messenger) dispatchMagnetlinkMessage(communityID string) error {
|
||||||
Payload: encodedMessage,
|
Payload: encodedMessage,
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_MESSAGE_ARCHIVE_MAGNETLINK,
|
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_MESSAGE_ARCHIVE_MAGNETLINK,
|
||||||
SkipGroupMessageWrap: true,
|
SkipGroupMessageWrap: true,
|
||||||
|
PubsubTopic: community.PubsubTopic(),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = m.sender.SendPublic(context.Background(), chatID, rawMessage)
|
_, err = m.sender.SendPublic(context.Background(), chatID, rawMessage)
|
||||||
|
@ -4128,7 +4186,10 @@ func (m *Messenger) RequestImportDiscordCommunity(request *requests.ImportDiscor
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init the community filter so we can receive messages on the community
|
// Init the community filter so we can receive messages on the community
|
||||||
_, err = m.transport.InitCommunityFilters([]*ecdsa.PrivateKey{discordCommunity.PrivateKey()})
|
_, err = m.transport.InitCommunityFilters([]transport.CommunityFilterToInitialize{{
|
||||||
|
CommunityID: discordCommunity.ID(),
|
||||||
|
PrivKey: discordCommunity.PrivateKey(),
|
||||||
|
}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.cleanUpImport(communityID)
|
m.cleanUpImport(communityID)
|
||||||
importProgress.AddTaskError(discord.InitCommunityTask, discord.Error(err.Error()))
|
importProgress.AddTaskError(discord.InitCommunityTask, discord.Error(err.Error()))
|
||||||
|
@ -4307,7 +4368,7 @@ func (m *Messenger) pinMessagesToWakuMessages(pinMessages []*common.PinMessage,
|
||||||
wakuMessage := &types.Message{
|
wakuMessage := &types.Message{
|
||||||
Sig: crypto.FromECDSAPub(&c.PrivateKey().PublicKey),
|
Sig: crypto.FromECDSAPub(&c.PrivateKey().PublicKey),
|
||||||
Timestamp: uint32(msg.WhisperTimestamp / 1000),
|
Timestamp: uint32(msg.WhisperTimestamp / 1000),
|
||||||
Topic: filter.Topic,
|
Topic: filter.ContentTopic,
|
||||||
Payload: wrappedPayload,
|
Payload: wrappedPayload,
|
||||||
Padding: []byte{1},
|
Padding: []byte{1},
|
||||||
Hash: hash[:],
|
Hash: hash[:],
|
||||||
|
@ -4346,7 +4407,7 @@ func (m *Messenger) chatMessagesToWakuMessages(chatMessages []*common.Message, c
|
||||||
wakuMessage := &types.Message{
|
wakuMessage := &types.Message{
|
||||||
Sig: crypto.FromECDSAPub(&c.PrivateKey().PublicKey),
|
Sig: crypto.FromECDSAPub(&c.PrivateKey().PublicKey),
|
||||||
Timestamp: uint32(msg.WhisperTimestamp / 1000),
|
Timestamp: uint32(msg.WhisperTimestamp / 1000),
|
||||||
Topic: filter.Topic,
|
Topic: filter.ContentTopic,
|
||||||
Payload: wrappedPayload,
|
Payload: wrappedPayload,
|
||||||
Padding: []byte{1},
|
Padding: []byte{1},
|
||||||
Hash: hash[:],
|
Hash: hash[:],
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/status-im/status-go/deprecation"
|
"github.com/status-im/status-go/deprecation"
|
||||||
|
@ -468,7 +469,7 @@ func (m *Messenger) addContact(ctx context.Context, pubKey, ensName, nickname, d
|
||||||
if !deprecation.ChatProfileDeprecated {
|
if !deprecation.ChatProfileDeprecated {
|
||||||
response.AddChat(profileChat)
|
response.AddChat(profileChat)
|
||||||
|
|
||||||
_, err = m.transport.InitFilters([]string{profileChat.ID}, []*ecdsa.PublicKey{publicKey})
|
_, err = m.transport.InitFilters([]transport.FiltersToInitialize{{ChatID: profileChat.ID, PubsubTopic: relay.DefaultWakuTopic}}, []*ecdsa.PublicKey{publicKey})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/status-im/status-go/connection"
|
"github.com/status-im/status-go/connection"
|
||||||
|
@ -217,19 +218,19 @@ func (m *Messenger) filtersForChat(chatID string) ([]*transport.Filter, error) {
|
||||||
return filters, nil
|
return filters, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Messenger) topicsForChat(chatID string) ([]types.TopicType, error) {
|
func (m *Messenger) topicsForChat(chatID string) (string, []types.TopicType, error) {
|
||||||
filters, err := m.filtersForChat(chatID)
|
filters, err := m.filtersForChat(chatID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var topics []types.TopicType
|
var contentTopics []types.TopicType
|
||||||
|
|
||||||
for _, filter := range filters {
|
for _, filter := range filters {
|
||||||
topics = append(topics, filter.Topic)
|
contentTopics = append(contentTopics, filter.ContentTopic)
|
||||||
}
|
}
|
||||||
|
|
||||||
return topics, nil
|
return filters[0].PubsubTopic, contentTopics, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assume is a public chat for now
|
// Assume is a public chat for now
|
||||||
|
@ -250,7 +251,7 @@ func (m *Messenger) syncBackup() error {
|
||||||
|
|
||||||
to := m.calculateMailserverTo()
|
to := m.calculateMailserverTo()
|
||||||
from := uint32(m.getTimesource().GetCurrentTime()/1000) - oneMonthInSeconds
|
from := uint32(m.getTimesource().GetCurrentTime()/1000) - oneMonthInSeconds
|
||||||
batch := MailserverBatch{From: from, To: to, Topics: []types.TopicType{filter.Topic}}
|
batch := MailserverBatch{From: from, To: to, PubsubTopic: relay.DefaultWakuTopic, Topics: []types.TopicType{filter.ContentTopic}}
|
||||||
err := m.processMailserverBatch(batch)
|
err := m.processMailserverBatch(batch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -346,7 +347,7 @@ func (m *Messenger) syncFiltersFrom(filters []*transport.Filter, lastRequest uin
|
||||||
|
|
||||||
topicsData := make(map[string]mailservers.MailserverTopic)
|
topicsData := make(map[string]mailservers.MailserverTopic)
|
||||||
for _, topic := range topicInfo {
|
for _, topic := range topicInfo {
|
||||||
topicsData[topic.Topic] = topic
|
topicsData[fmt.Sprintf("%s-%s", topic.PubsubTopic, topic.ContentTopic)] = topic
|
||||||
}
|
}
|
||||||
|
|
||||||
batches := make(map[int]MailserverBatch)
|
batches := make(map[int]MailserverBatch)
|
||||||
|
@ -371,74 +372,88 @@ func (m *Messenger) syncFiltersFrom(filters []*transport.Filter, lastRequest uin
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contentTopicsPerPubsubTopic := make(map[string]map[string]*transport.Filter)
|
||||||
for _, filter := range filters {
|
for _, filter := range filters {
|
||||||
if !filter.Listen || filter.Ephemeral {
|
if !filter.Listen || filter.Ephemeral {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var chatID string
|
contentTopics, ok := contentTopicsPerPubsubTopic[filter.PubsubTopic]
|
||||||
// If the filter has an identity, we use it as a chatID, otherwise is a public chat/community chat filter
|
|
||||||
if len(filter.Identity) != 0 {
|
|
||||||
chatID = filter.Identity
|
|
||||||
} else {
|
|
||||||
chatID = filter.ChatID
|
|
||||||
}
|
|
||||||
|
|
||||||
topicData, ok := topicsData[filter.Topic.String()]
|
|
||||||
var capToDefaultSyncPeriod = true
|
|
||||||
if !ok {
|
if !ok {
|
||||||
if lastRequest == 0 {
|
contentTopics = make(map[string]*transport.Filter)
|
||||||
lastRequest = defaultPeriodFromNow
|
|
||||||
}
|
|
||||||
topicData = mailservers.MailserverTopic{
|
|
||||||
Topic: filter.Topic.String(),
|
|
||||||
LastRequest: int(defaultPeriodFromNow),
|
|
||||||
}
|
|
||||||
} else if lastRequest != 0 {
|
|
||||||
topicData.LastRequest = int(lastRequest)
|
|
||||||
capToDefaultSyncPeriod = false
|
|
||||||
}
|
}
|
||||||
|
contentTopics[filter.ContentTopic.String()] = filter
|
||||||
|
contentTopicsPerPubsubTopic[filter.PubsubTopic] = contentTopics
|
||||||
|
}
|
||||||
|
|
||||||
batchID := topicData.LastRequest
|
for pubsubTopic, contentTopics := range contentTopicsPerPubsubTopic {
|
||||||
|
for _, filter := range contentTopics {
|
||||||
if currentBatch < len(prioritizedBatches) {
|
var chatID string
|
||||||
batch, ok := batches[currentBatch]
|
// If the filter has an identity, we use it as a chatID, otherwise is a public chat/community chat filter
|
||||||
if ok {
|
if len(filter.Identity) != 0 {
|
||||||
prevTopicData, ok := topicsData[batch.Topics[0].String()]
|
chatID = filter.Identity
|
||||||
if (!ok && topicData.LastRequest != int(defaultPeriodFromNow)) ||
|
} else {
|
||||||
(ok && prevTopicData.LastRequest != topicData.LastRequest) {
|
chatID = filter.ChatID
|
||||||
currentBatch++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
topicData, ok := topicsData[filter.PubsubTopic+filter.ContentTopic.String()]
|
||||||
|
var capToDefaultSyncPeriod = true
|
||||||
|
if !ok {
|
||||||
|
if lastRequest == 0 {
|
||||||
|
lastRequest = defaultPeriodFromNow
|
||||||
|
}
|
||||||
|
topicData = mailservers.MailserverTopic{
|
||||||
|
PubsubTopic: filter.PubsubTopic,
|
||||||
|
ContentTopic: filter.ContentTopic.String(),
|
||||||
|
LastRequest: int(defaultPeriodFromNow),
|
||||||
|
}
|
||||||
|
} else if lastRequest != 0 {
|
||||||
|
topicData.LastRequest = int(lastRequest)
|
||||||
|
capToDefaultSyncPeriod = false
|
||||||
|
}
|
||||||
|
|
||||||
|
batchID := topicData.LastRequest
|
||||||
|
|
||||||
if currentBatch < len(prioritizedBatches) {
|
if currentBatch < len(prioritizedBatches) {
|
||||||
batchID = currentBatch
|
batch, ok := batches[currentBatch]
|
||||||
currentBatchCap := prioritizedBatches[currentBatch] - 1
|
if ok {
|
||||||
if currentBatchCap == 0 {
|
prevTopicData, ok := topicsData[batch.PubsubTopic+batch.Topics[0].String()]
|
||||||
currentBatch++
|
if (!ok && topicData.LastRequest != int(defaultPeriodFromNow)) ||
|
||||||
} else {
|
(ok && prevTopicData.LastRequest != topicData.LastRequest) {
|
||||||
prioritizedBatches[currentBatch] = currentBatchCap
|
currentBatch++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if currentBatch < len(prioritizedBatches) {
|
||||||
|
batchID = currentBatch
|
||||||
|
currentBatchCap := prioritizedBatches[currentBatch] - 1
|
||||||
|
if currentBatchCap == 0 {
|
||||||
|
currentBatch++
|
||||||
|
} else {
|
||||||
|
prioritizedBatches[currentBatch] = currentBatchCap
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
batch, ok := batches[batchID]
|
batch, ok := batches[batchID]
|
||||||
if !ok {
|
if !ok {
|
||||||
from := uint32(topicData.LastRequest)
|
from := uint32(topicData.LastRequest)
|
||||||
if capToDefaultSyncPeriod {
|
if capToDefaultSyncPeriod {
|
||||||
from, err = m.capToDefaultSyncPeriod(uint32(topicData.LastRequest))
|
from, err = m.capToDefaultSyncPeriod(uint32(topicData.LastRequest))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
batch = MailserverBatch{From: from, To: to}
|
||||||
}
|
}
|
||||||
batch = MailserverBatch{From: from, To: to}
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.ChatIDs = append(batch.ChatIDs, chatID)
|
batch.ChatIDs = append(batch.ChatIDs, chatID)
|
||||||
batch.Topics = append(batch.Topics, filter.Topic)
|
batch.PubsubTopic = pubsubTopic
|
||||||
batches[batchID] = batch
|
batch.Topics = append(batch.Topics, filter.ContentTopic)
|
||||||
// Set last request to the new `to`
|
batches[batchID] = batch
|
||||||
topicData.LastRequest = int(to)
|
// Set last request to the new `to`
|
||||||
syncedTopics = append(syncedTopics, topicData)
|
topicData.LastRequest = int(to)
|
||||||
|
syncedTopics = append(syncedTopics, topicData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.config.messengerSignalsHandler != nil {
|
if m.config.messengerSignalsHandler != nil {
|
||||||
|
@ -460,10 +475,11 @@ func (m *Messenger) syncFiltersFrom(filters []*transport.Filter, lastRequest uin
|
||||||
batch := batches[k]
|
batch := batches[k]
|
||||||
|
|
||||||
dayBatch := MailserverBatch{
|
dayBatch := MailserverBatch{
|
||||||
To: batch.To,
|
To: batch.To,
|
||||||
Cursor: batch.Cursor,
|
Cursor: batch.Cursor,
|
||||||
Topics: batch.Topics,
|
PubsubTopic: batch.PubsubTopic,
|
||||||
ChatIDs: batch.ChatIDs,
|
Topics: batch.Topics,
|
||||||
|
ChatIDs: batch.ChatIDs,
|
||||||
}
|
}
|
||||||
|
|
||||||
from := batch.To - 86400
|
from := batch.To - 86400
|
||||||
|
@ -586,9 +602,10 @@ func (m *Messenger) calculateGapForChat(chat *Chat, from uint32) (*common.Messag
|
||||||
}
|
}
|
||||||
|
|
||||||
type work struct {
|
type work struct {
|
||||||
topics []types.TopicType
|
pubsubTopic string
|
||||||
cursor []byte
|
contentTopics []types.TopicType
|
||||||
storeCursor *types.StoreRequestCursor
|
cursor []byte
|
||||||
|
storeCursor *types.StoreRequestCursor
|
||||||
}
|
}
|
||||||
|
|
||||||
type messageRequester interface {
|
type messageRequester interface {
|
||||||
|
@ -598,7 +615,8 @@ type messageRequester interface {
|
||||||
from, to uint32,
|
from, to uint32,
|
||||||
previousCursor []byte,
|
previousCursor []byte,
|
||||||
previousStoreCursor *types.StoreRequestCursor,
|
previousStoreCursor *types.StoreRequestCursor,
|
||||||
topics []types.TopicType,
|
pubsubTopic string,
|
||||||
|
contentTopics []types.TopicType,
|
||||||
waitForResponse bool,
|
waitForResponse bool,
|
||||||
) (cursor []byte, storeCursor *types.StoreRequestCursor, err error)
|
) (cursor []byte, storeCursor *types.StoreRequestCursor, err error)
|
||||||
}
|
}
|
||||||
|
@ -645,7 +663,8 @@ func processMailserverBatch(ctx context.Context, messageRequester messageRequest
|
||||||
default:
|
default:
|
||||||
logger.Debug("processBatch producer - creating work")
|
logger.Debug("processBatch producer - creating work")
|
||||||
workCh <- work{
|
workCh <- work{
|
||||||
topics: batch.Topics[i:j],
|
pubsubTopic: batch.PubsubTopic,
|
||||||
|
contentTopics: batch.Topics[i:j],
|
||||||
}
|
}
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
@ -685,7 +704,7 @@ loop:
|
||||||
}()
|
}()
|
||||||
|
|
||||||
queryCtx, queryCancel := context.WithTimeout(ctx, mailserverRequestTimeout)
|
queryCtx, queryCancel := context.WithTimeout(ctx, mailserverRequestTimeout)
|
||||||
cursor, storeCursor, err := messageRequester.SendMessagesRequestForTopics(queryCtx, mailserverID, batch.From, batch.To, w.cursor, w.storeCursor, w.topics, true)
|
cursor, storeCursor, err := messageRequester.SendMessagesRequestForTopics(queryCtx, mailserverID, batch.From, batch.To, w.cursor, w.storeCursor, w.pubsubTopic, w.contentTopics, true)
|
||||||
|
|
||||||
queryCancel()
|
queryCancel()
|
||||||
|
|
||||||
|
@ -700,9 +719,10 @@ loop:
|
||||||
|
|
||||||
workWg.Add(1)
|
workWg.Add(1)
|
||||||
workCh <- work{
|
workCh <- work{
|
||||||
topics: w.topics,
|
pubsubTopic: w.pubsubTopic,
|
||||||
cursor: cursor,
|
contentTopics: w.contentTopics,
|
||||||
storeCursor: storeCursor,
|
cursor: cursor,
|
||||||
|
storeCursor: storeCursor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}(w)
|
}(w)
|
||||||
|
@ -737,45 +757,18 @@ func (m *Messenger) processMailserverBatch(batch MailserverBatch) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type MailserverBatch struct {
|
type MailserverBatch struct {
|
||||||
From uint32
|
From uint32
|
||||||
To uint32
|
To uint32
|
||||||
Cursor string
|
Cursor string
|
||||||
Topics []types.TopicType
|
PubsubTopic string
|
||||||
ChatIDs []string
|
Topics []types.TopicType
|
||||||
}
|
ChatIDs []string
|
||||||
|
|
||||||
func (m *Messenger) RequestHistoricMessagesForFilter(
|
|
||||||
ctx context.Context,
|
|
||||||
from, to uint32,
|
|
||||||
cursor []byte,
|
|
||||||
previousStoreCursor *types.StoreRequestCursor,
|
|
||||||
filter *transport.Filter,
|
|
||||||
waitForResponse bool,
|
|
||||||
) ([]byte, *types.StoreRequestCursor, error) {
|
|
||||||
|
|
||||||
activeMailserverID, err := m.activeMailserverID()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if activeMailserverID == nil {
|
|
||||||
m.cycleMailservers()
|
|
||||||
activeMailserverID, err = m.activeMailserverID()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if activeMailserverID == nil {
|
|
||||||
return nil, nil, errors.New("no mailserver selected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.transport.SendMessagesRequestForFilter(ctx, activeMailserverID, from, to, cursor, previousStoreCursor, filter, waitForResponse)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Messenger) SyncChatFromSyncedFrom(chatID string) (uint32, error) {
|
func (m *Messenger) SyncChatFromSyncedFrom(chatID string) (uint32, error) {
|
||||||
var from uint32
|
var from uint32
|
||||||
_, err := m.performMailserverRequest(func() (*MessengerResponse, error) {
|
_, err := m.performMailserverRequest(func() (*MessengerResponse, error) {
|
||||||
topics, err := m.topicsForChat(chatID)
|
pubsubTopic, topics, err := m.topicsForChat(chatID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -789,11 +782,13 @@ func (m *Messenger) SyncChatFromSyncedFrom(chatID string) (uint32, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
batch := MailserverBatch{
|
batch := MailserverBatch{
|
||||||
ChatIDs: []string{chatID},
|
ChatIDs: []string{chatID},
|
||||||
To: chat.SyncedFrom,
|
To: chat.SyncedFrom,
|
||||||
From: chat.SyncedFrom - defaultSyncPeriod,
|
From: chat.SyncedFrom - defaultSyncPeriod,
|
||||||
Topics: topics,
|
PubsubTopic: pubsubTopic,
|
||||||
|
Topics: topics,
|
||||||
}
|
}
|
||||||
if m.config.messengerSignalsHandler != nil {
|
if m.config.messengerSignalsHandler != nil {
|
||||||
m.config.messengerSignalsHandler.HistoryRequestStarted(1)
|
m.config.messengerSignalsHandler.HistoryRequestStarted(1)
|
||||||
|
@ -835,7 +830,7 @@ func (m *Messenger) FillGaps(chatID string, messageIDs []string) error {
|
||||||
return errors.New("chat not existing")
|
return errors.New("chat not existing")
|
||||||
}
|
}
|
||||||
|
|
||||||
topics, err := m.topicsForChat(chatID)
|
pubsubTopic, topics, err := m.topicsForChat(chatID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -857,10 +852,11 @@ func (m *Messenger) FillGaps(chatID string, messageIDs []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
batch := MailserverBatch{
|
batch := MailserverBatch{
|
||||||
ChatIDs: []string{chatID},
|
ChatIDs: []string{chatID},
|
||||||
To: highestTo,
|
To: highestTo,
|
||||||
From: lowestFrom,
|
From: lowestFrom,
|
||||||
Topics: topics,
|
PubsubTopic: pubsubTopic,
|
||||||
|
Topics: topics,
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.config.messengerSignalsHandler != nil {
|
if m.config.messengerSignalsHandler != nil {
|
||||||
|
|
|
@ -41,12 +41,13 @@ func (t *mockTransport) SendMessagesRequestForTopics(
|
||||||
from, to uint32,
|
from, to uint32,
|
||||||
previousCursor []byte,
|
previousCursor []byte,
|
||||||
previousStoreCursor *types.StoreRequestCursor,
|
previousStoreCursor *types.StoreRequestCursor,
|
||||||
topics []types.TopicType,
|
pubsubTopic string,
|
||||||
|
contentTopics []types.TopicType,
|
||||||
waitForResponse bool,
|
waitForResponse bool,
|
||||||
) (cursor []byte, storeCursor *types.StoreRequestCursor, err error) {
|
) (cursor []byte, storeCursor *types.StoreRequestCursor, err error) {
|
||||||
var response queryResponse
|
var response queryResponse
|
||||||
if previousCursor == nil {
|
if previousCursor == nil {
|
||||||
initialResponse := getInitialResponseKey(topics)
|
initialResponse := getInitialResponseKey(contentTopics)
|
||||||
response = t.queryResponses[initialResponse]
|
response = t.queryResponses[initialResponse]
|
||||||
} else {
|
} else {
|
||||||
response = t.queryResponses[hex.EncodeToString(previousCursor)]
|
response = t.queryResponses[hex.EncodeToString(previousCursor)]
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
|
||||||
"github.com/libp2p/go-libp2p/core/peer"
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
|
@ -33,3 +35,12 @@ func (m *Messenger) Peers() map[string]types.WakuV2Peer {
|
||||||
func (m *Messenger) ListenAddresses() ([]string, error) {
|
func (m *Messenger) ListenAddresses() ([]string, error) {
|
||||||
return m.transport.ListenAddresses()
|
return m.transport.ListenAddresses()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Subscribe to a pubsub topic, passing an optional public key if the pubsub topic is protected
|
||||||
|
func (m *Messenger) SubscribeToPubsubTopic(topic string, optPublicKey *ecdsa.PublicKey) error {
|
||||||
|
return m.transport.SubscribeToPubsubTopic(topic, optPublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Messenger) StorePubsubTopicKey(topic string, privKey *ecdsa.PrivateKey) error {
|
||||||
|
return m.transport.StorePubsubTopicKey(topic, privKey)
|
||||||
|
}
|
||||||
|
|
|
@ -83,6 +83,7 @@ func (m *Messenger) sendUserStatus(ctx context.Context, status UserStatus) error
|
||||||
}
|
}
|
||||||
for _, community := range joinedCommunities {
|
for _, community := range joinedCommunities {
|
||||||
rawMessage.LocalChatID = community.StatusUpdatesChannelID()
|
rawMessage.LocalChatID = community.StatusUpdatesChannelID()
|
||||||
|
rawMessage.PubsubTopic = community.PubsubTopic()
|
||||||
_, err = m.sender.SendPublic(ctx, rawMessage.LocalChatID, rawMessage)
|
_, err = m.sender.SendPublic(ctx, rawMessage.LocalChatID, rawMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -171,6 +172,7 @@ func (m *Messenger) sendCurrentUserStatusToCommunity(ctx context.Context, commun
|
||||||
MessageType: protobuf.ApplicationMetadataMessage_STATUS_UPDATE,
|
MessageType: protobuf.ApplicationMetadataMessage_STATUS_UPDATE,
|
||||||
ResendAutomatically: true,
|
ResendAutomatically: true,
|
||||||
Ephemeral: statusUpdate.StatusType == protobuf.StatusUpdate_AUTOMATIC,
|
Ephemeral: statusUpdate.StatusType == protobuf.StatusUpdate_AUTOMATIC,
|
||||||
|
PubsubTopic: community.PubsubTopic(),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = m.sender.SendPublic(ctx, rawMessage.LocalChatID, rawMessage)
|
_, err = m.sender.SendPublic(ctx, rawMessage.LocalChatID, rawMessage)
|
||||||
|
|
|
@ -15,8 +15,10 @@ type Filter struct {
|
||||||
// Identity is the public key of the other recipient for non-public filters.
|
// Identity is the public key of the other recipient for non-public filters.
|
||||||
// It's encoded using encoding/hex.
|
// It's encoded using encoding/hex.
|
||||||
Identity string `json:"identity"`
|
Identity string `json:"identity"`
|
||||||
// Topic is the whisper topic
|
// PubsubTopic is the waku2 pubsub topic
|
||||||
Topic types.TopicType `json:"topic"`
|
PubsubTopic string `json:"pubsubTopic"`
|
||||||
|
// ContentTopic is the waku topic
|
||||||
|
ContentTopic types.TopicType `json:"topic"`
|
||||||
// Discovery is whether this is a discovery topic
|
// Discovery is whether this is a discovery topic
|
||||||
Discovery bool `json:"discovery"`
|
Discovery bool `json:"discovery"`
|
||||||
// Negotiated tells us whether is a negotiated topic
|
// Negotiated tells us whether is a negotiated topic
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
|
@ -74,7 +75,7 @@ func NewFiltersManager(persistence KeysPersistence, service FiltersService, priv
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FiltersManager) Init(
|
func (f *FiltersManager) Init(
|
||||||
chatIDs []string,
|
filtersToInit []FiltersToInitialize,
|
||||||
publicKeys []*ecdsa.PublicKey,
|
publicKeys []*ecdsa.PublicKey,
|
||||||
) ([]*Filter, error) {
|
) ([]*Filter, error) {
|
||||||
|
|
||||||
|
@ -97,8 +98,8 @@ func (f *FiltersManager) Init(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add public, one-to-one and negotiated filters.
|
// Add public, one-to-one and negotiated filters.
|
||||||
for _, chatID := range chatIDs {
|
for _, fi := range filtersToInit {
|
||||||
_, err := f.LoadPublic(chatID)
|
_, err := f.LoadPublic(fi.ChatID, fi.PubsubTopic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -121,11 +122,16 @@ func (f *FiltersManager) Init(
|
||||||
return allFilters, nil
|
return allFilters, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FiltersManager) InitPublicFilters(chatIDs []string) ([]*Filter, error) {
|
type FiltersToInitialize struct {
|
||||||
|
ChatID string
|
||||||
|
PubsubTopic string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FiltersManager) InitPublicFilters(publicFiltersToInit []FiltersToInitialize) ([]*Filter, error) {
|
||||||
var filters []*Filter
|
var filters []*Filter
|
||||||
// Add public, one-to-one and negotiated filters.
|
// Add public, one-to-one and negotiated filters.
|
||||||
for _, chatID := range chatIDs {
|
for _, pf := range publicFiltersToInit {
|
||||||
f, err := f.LoadPublic(chatID)
|
f, err := f.LoadPublic(pf.ChatID, pf.PubsubTopic) // TODO: pubsubtopic
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -134,15 +140,20 @@ func (f *FiltersManager) InitPublicFilters(chatIDs []string) ([]*Filter, error)
|
||||||
return filters, nil
|
return filters, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FiltersManager) InitCommunityFilters(pks []*ecdsa.PrivateKey) ([]*Filter, error) {
|
type CommunityFilterToInitialize struct {
|
||||||
|
CommunityID []byte
|
||||||
|
PrivKey *ecdsa.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FiltersManager) InitCommunityFilters(communityFiltersToInitialize []CommunityFilterToInitialize) ([]*Filter, error) {
|
||||||
var filters []*Filter
|
var filters []*Filter
|
||||||
f.mutex.Lock()
|
f.mutex.Lock()
|
||||||
defer f.mutex.Unlock()
|
defer f.mutex.Unlock()
|
||||||
|
|
||||||
for _, pk := range pks {
|
for _, cf := range communityFiltersToInitialize {
|
||||||
|
pubsubTopic := GetPubsubTopic(cf.CommunityID)
|
||||||
identityStr := PublicKeyToStr(&pk.PublicKey)
|
identityStr := PublicKeyToStr(&cf.PrivKey.PublicKey)
|
||||||
rawFilter, err := f.addAsymmetric(identityStr, pk, true)
|
rawFilter, err := f.addAsymmetric(identityStr, pubsubTopic, cf.PrivKey, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.logger.Debug("could not register community filter", zap.Error(err))
|
f.logger.Debug("could not register community filter", zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -150,12 +161,13 @@ func (f *FiltersManager) InitCommunityFilters(pks []*ecdsa.PrivateKey) ([]*Filte
|
||||||
}
|
}
|
||||||
filterID := identityStr + "-admin"
|
filterID := identityStr + "-admin"
|
||||||
filter := &Filter{
|
filter := &Filter{
|
||||||
ChatID: filterID,
|
ChatID: filterID,
|
||||||
FilterID: rawFilter.FilterID,
|
FilterID: rawFilter.FilterID,
|
||||||
Topic: rawFilter.Topic,
|
PubsubTopic: pubsubTopic,
|
||||||
Identity: identityStr,
|
ContentTopic: rawFilter.Topic,
|
||||||
Listen: true,
|
Identity: identityStr,
|
||||||
OneToOne: true,
|
Listen: true,
|
||||||
|
OneToOne: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
f.filters[filterID] = filter
|
f.filters[filterID] = filter
|
||||||
|
@ -170,8 +182,8 @@ func (f *FiltersManager) InitCommunityFilters(pks []*ecdsa.PrivateKey) ([]*Filte
|
||||||
// DEPRECATED
|
// DEPRECATED
|
||||||
func (f *FiltersManager) InitWithFilters(filters []*Filter) ([]*Filter, error) {
|
func (f *FiltersManager) InitWithFilters(filters []*Filter) ([]*Filter, error) {
|
||||||
var (
|
var (
|
||||||
chatIDs []string
|
filtersToInit []FiltersToInitialize
|
||||||
publicKeys []*ecdsa.PublicKey
|
publicKeys []*ecdsa.PublicKey
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, filter := range filters {
|
for _, filter := range filters {
|
||||||
|
@ -182,11 +194,11 @@ func (f *FiltersManager) InitWithFilters(filters []*Filter) ([]*Filter, error) {
|
||||||
}
|
}
|
||||||
publicKeys = append(publicKeys, publicKey)
|
publicKeys = append(publicKeys, publicKey)
|
||||||
} else if filter.ChatID != "" {
|
} else if filter.ChatID != "" {
|
||||||
chatIDs = append(chatIDs, filter.ChatID)
|
filtersToInit = append(filtersToInit, FiltersToInitialize{ChatID: filter.ChatID, PubsubTopic: filter.PubsubTopic})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return f.Init(chatIDs, publicKeys)
|
return f.Init(filtersToInit, publicKeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FiltersManager) Reset(ctx context.Context) error {
|
func (f *FiltersManager) Reset(ctx context.Context) error {
|
||||||
|
@ -234,7 +246,7 @@ func (f *FiltersManager) FilterByTopic(topic []byte) *Filter {
|
||||||
f.mutex.Lock()
|
f.mutex.Lock()
|
||||||
defer f.mutex.Unlock()
|
defer f.mutex.Unlock()
|
||||||
for _, f := range f.filters {
|
for _, f := range f.filters {
|
||||||
if bytes.Equal(types.TopicTypeToByteArray(f.Topic), topic) {
|
if bytes.Equal(types.TopicTypeToByteArray(f.ContentTopic), topic) {
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -318,6 +330,8 @@ func (f *FiltersManager) RemoveNoListenFilters() error {
|
||||||
|
|
||||||
// Remove remove all the filters associated with a chat/identity
|
// Remove remove all the filters associated with a chat/identity
|
||||||
func (f *FiltersManager) RemoveFilterByChatID(chatID string) (*Filter, error) {
|
func (f *FiltersManager) RemoveFilterByChatID(chatID string) (*Filter, error) {
|
||||||
|
// TODO: remove subscriptions from waku2 if required. Might need to be implemented in transport
|
||||||
|
|
||||||
f.mutex.Lock()
|
f.mutex.Lock()
|
||||||
filter, ok := f.filters[chatID]
|
filter, ok := f.filters[chatID]
|
||||||
f.mutex.Unlock()
|
f.mutex.Unlock()
|
||||||
|
@ -354,21 +368,24 @@ func (f *FiltersManager) LoadPersonal(publicKey *ecdsa.PublicKey, identity *ecds
|
||||||
return f.filters[chatID], nil
|
return f.filters[chatID], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pubsubTopic := relay.DefaultWakuTopic
|
||||||
|
|
||||||
// We set up a filter so we can publish,
|
// We set up a filter so we can publish,
|
||||||
// but we discard envelopes if listen is false.
|
// but we discard envelopes if listen is false.
|
||||||
filter, err := f.addAsymmetric(chatID, identity, listen)
|
filter, err := f.addAsymmetric(chatID, pubsubTopic, identity, listen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.logger.Debug("could not register personal topic filter", zap.Error(err))
|
f.logger.Debug("could not register personal topic filter", zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
chat := &Filter{
|
chat := &Filter{
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
FilterID: filter.FilterID,
|
FilterID: filter.FilterID,
|
||||||
Topic: filter.Topic,
|
ContentTopic: filter.Topic,
|
||||||
Identity: PublicKeyToStr(publicKey),
|
PubsubTopic: pubsubTopic,
|
||||||
Listen: listen,
|
Identity: PublicKeyToStr(publicKey),
|
||||||
OneToOne: true,
|
Listen: listen,
|
||||||
|
OneToOne: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
f.filters[chatID] = chat
|
f.filters[chatID] = chat
|
||||||
|
@ -392,22 +409,25 @@ func (f *FiltersManager) loadPartitioned(publicKey *ecdsa.PublicKey, identity *e
|
||||||
return f.filters[chatID], nil
|
return f.filters[chatID], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pubsubTopic := relay.DefaultWakuTopic
|
||||||
|
|
||||||
// We set up a filter so we can publish,
|
// We set up a filter so we can publish,
|
||||||
// but we discard envelopes if listen is false.
|
// but we discard envelopes if listen is false.
|
||||||
filter, err := f.addAsymmetric(chatID, identity, listen)
|
filter, err := f.addAsymmetric(chatID, pubsubTopic, identity, listen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.logger.Debug("could not register partitioned topic", zap.String("chatID", chatID), zap.Error(err))
|
f.logger.Debug("could not register partitioned topic", zap.String("chatID", chatID), zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
chat := &Filter{
|
chat := &Filter{
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
FilterID: filter.FilterID,
|
FilterID: filter.FilterID,
|
||||||
Topic: filter.Topic,
|
ContentTopic: filter.Topic,
|
||||||
Identity: PublicKeyToStr(publicKey),
|
PubsubTopic: pubsubTopic,
|
||||||
Listen: listen,
|
Identity: PublicKeyToStr(publicKey),
|
||||||
Ephemeral: ephemeral,
|
Listen: listen,
|
||||||
OneToOne: true,
|
Ephemeral: ephemeral,
|
||||||
|
OneToOne: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
f.filters[chatID] = chat
|
f.filters[chatID] = chat
|
||||||
|
@ -428,22 +448,24 @@ func (f *FiltersManager) LoadNegotiated(secret types.NegotiatedSecret) (*Filter,
|
||||||
return f.filters[chatID], nil
|
return f.filters[chatID], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pubsubTopic := relay.DefaultWakuTopic
|
||||||
keyString := hex.EncodeToString(secret.Key)
|
keyString := hex.EncodeToString(secret.Key)
|
||||||
filter, err := f.addSymmetric(keyString)
|
filter, err := f.addSymmetric(keyString, pubsubTopic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.logger.Debug("could not register negotiated topic", zap.Error(err))
|
f.logger.Debug("could not register negotiated topic", zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
chat := &Filter{
|
chat := &Filter{
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
Topic: filter.Topic,
|
ContentTopic: filter.Topic,
|
||||||
SymKeyID: filter.SymKeyID,
|
PubsubTopic: pubsubTopic,
|
||||||
FilterID: filter.FilterID,
|
SymKeyID: filter.SymKeyID,
|
||||||
Identity: PublicKeyToStr(secret.PublicKey),
|
FilterID: filter.FilterID,
|
||||||
Negotiated: true,
|
Identity: PublicKeyToStr(secret.PublicKey),
|
||||||
Listen: true,
|
Negotiated: true,
|
||||||
OneToOne: true,
|
Listen: true,
|
||||||
|
OneToOne: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
f.filters[chat.ChatID] = chat
|
f.filters[chat.ChatID] = chat
|
||||||
|
@ -478,25 +500,26 @@ func (f *FiltersManager) LoadDiscovery() ([]*Filter, error) {
|
||||||
|
|
||||||
// Load personal discovery
|
// Load personal discovery
|
||||||
personalDiscoveryChat := &Filter{
|
personalDiscoveryChat := &Filter{
|
||||||
ChatID: personalDiscoveryTopic,
|
ChatID: personalDiscoveryTopic,
|
||||||
Identity: identityStr,
|
Identity: identityStr,
|
||||||
Discovery: true,
|
PubsubTopic: relay.DefaultWakuTopic,
|
||||||
Listen: true,
|
Discovery: true,
|
||||||
OneToOne: true,
|
Listen: true,
|
||||||
|
OneToOne: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
discoveryResponse, err := f.addAsymmetric(personalDiscoveryChat.ChatID, f.privateKey, true)
|
discoveryResponse, err := f.addAsymmetric(personalDiscoveryChat.ChatID, personalDiscoveryChat.PubsubTopic, f.privateKey, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.logger.Debug("could not register discovery topic", zap.String("chatID", personalDiscoveryChat.ChatID), zap.Error(err))
|
f.logger.Debug("could not register discovery topic", zap.String("chatID", personalDiscoveryChat.ChatID), zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
personalDiscoveryChat.Topic = discoveryResponse.Topic
|
personalDiscoveryChat.ContentTopic = discoveryResponse.Topic
|
||||||
personalDiscoveryChat.FilterID = discoveryResponse.FilterID
|
personalDiscoveryChat.FilterID = discoveryResponse.FilterID
|
||||||
|
|
||||||
f.filters[personalDiscoveryChat.ChatID] = personalDiscoveryChat
|
f.filters[personalDiscoveryChat.ChatID] = personalDiscoveryChat
|
||||||
|
|
||||||
f.logger.Debug("registering filter for", zap.String("chatID", personalDiscoveryChat.ChatID), zap.String("type", "discovery"), zap.String("topic", personalDiscoveryChat.Topic.String()))
|
f.logger.Debug("registering filter for", zap.String("chatID", personalDiscoveryChat.ChatID), zap.String("type", "discovery"), zap.String("topic", personalDiscoveryChat.ContentTopic.String()))
|
||||||
|
|
||||||
return []*Filter{personalDiscoveryChat}, nil
|
return []*Filter{personalDiscoveryChat}, nil
|
||||||
}
|
}
|
||||||
|
@ -508,7 +531,7 @@ func (f *FiltersManager) PersonalTopicFilter() *Filter {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadPublic adds a filter for a public chat.
|
// LoadPublic adds a filter for a public chat.
|
||||||
func (f *FiltersManager) LoadPublic(chatID string) (*Filter, error) {
|
func (f *FiltersManager) LoadPublic(chatID string, pubsubTopic string) (*Filter, error) {
|
||||||
f.mutex.Lock()
|
f.mutex.Lock()
|
||||||
defer f.mutex.Unlock()
|
defer f.mutex.Unlock()
|
||||||
|
|
||||||
|
@ -516,19 +539,20 @@ func (f *FiltersManager) LoadPublic(chatID string) (*Filter, error) {
|
||||||
return chat, nil
|
return chat, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
filterAndTopic, err := f.addSymmetric(chatID)
|
filterAndTopic, err := f.addSymmetric(chatID, pubsubTopic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.logger.Debug("could not register public chat topic", zap.String("chatID", chatID), zap.Error(err))
|
f.logger.Debug("could not register public chat topic", zap.String("chatID", chatID), zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
chat := &Filter{
|
chat := &Filter{
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
FilterID: filterAndTopic.FilterID,
|
FilterID: filterAndTopic.FilterID,
|
||||||
SymKeyID: filterAndTopic.SymKeyID,
|
SymKeyID: filterAndTopic.SymKeyID,
|
||||||
Topic: filterAndTopic.Topic,
|
ContentTopic: filterAndTopic.Topic,
|
||||||
Listen: true,
|
PubsubTopic: pubsubTopic,
|
||||||
OneToOne: false,
|
Listen: true,
|
||||||
|
OneToOne: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
f.filters[chatID] = chat
|
f.filters[chatID] = chat
|
||||||
|
@ -549,19 +573,22 @@ func (f *FiltersManager) LoadContactCode(pubKey *ecdsa.PublicKey) (*Filter, erro
|
||||||
return f.filters[chatID], nil
|
return f.filters[chatID], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
contactCodeFilter, err := f.addSymmetric(chatID)
|
pubsubTopic := relay.DefaultWakuTopic
|
||||||
|
|
||||||
|
contactCodeFilter, err := f.addSymmetric(chatID, pubsubTopic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.logger.Debug("could not register contact code topic", zap.String("chatID", chatID), zap.Error(err))
|
f.logger.Debug("could not register contact code topic", zap.String("chatID", chatID), zap.Error(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
chat := &Filter{
|
chat := &Filter{
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
FilterID: contactCodeFilter.FilterID,
|
FilterID: contactCodeFilter.FilterID,
|
||||||
Topic: contactCodeFilter.Topic,
|
ContentTopic: contactCodeFilter.Topic,
|
||||||
SymKeyID: contactCodeFilter.SymKeyID,
|
SymKeyID: contactCodeFilter.SymKeyID,
|
||||||
Identity: PublicKeyToStr(pubKey),
|
Identity: PublicKeyToStr(pubKey),
|
||||||
Listen: true,
|
PubsubTopic: pubsubTopic,
|
||||||
|
Listen: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
f.filters[chatID] = chat
|
f.filters[chatID] = chat
|
||||||
|
@ -572,7 +599,7 @@ func (f *FiltersManager) LoadContactCode(pubKey *ecdsa.PublicKey) (*Filter, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// addSymmetric adds a symmetric key filter
|
// addSymmetric adds a symmetric key filter
|
||||||
func (f *FiltersManager) addSymmetric(chatID string) (*RawFilter, error) {
|
func (f *FiltersManager) addSymmetric(chatID string, pubsubTopic string) (*RawFilter, error) {
|
||||||
var symKeyID string
|
var symKeyID string
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -602,9 +629,10 @@ func (f *FiltersManager) addSymmetric(chatID string) (*RawFilter, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := f.service.Subscribe(&types.SubscriptionOptions{
|
id, err := f.service.Subscribe(&types.SubscriptionOptions{
|
||||||
SymKeyID: symKeyID,
|
SymKeyID: symKeyID,
|
||||||
PoW: minPow,
|
PoW: minPow,
|
||||||
Topics: topics,
|
Topics: topics,
|
||||||
|
PubsubTopic: pubsubTopic,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -619,7 +647,7 @@ func (f *FiltersManager) addSymmetric(chatID string) (*RawFilter, error) {
|
||||||
|
|
||||||
// addAsymmetricFilter adds a filter with our private key
|
// addAsymmetricFilter adds a filter with our private key
|
||||||
// and set minPow according to the listen parameter.
|
// and set minPow according to the listen parameter.
|
||||||
func (f *FiltersManager) addAsymmetric(chatID string, identity *ecdsa.PrivateKey, listen bool) (*RawFilter, error) {
|
func (f *FiltersManager) addAsymmetric(chatID string, pubsubTopic string, identity *ecdsa.PrivateKey, listen bool) (*RawFilter, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
pow = 1.0 // use PoW high enough to discard all messages for the filter
|
pow = 1.0 // use PoW high enough to discard all messages for the filter
|
||||||
|
@ -641,6 +669,7 @@ func (f *FiltersManager) addAsymmetric(chatID string, identity *ecdsa.PrivateKey
|
||||||
PrivateKeyID: privateKeyID,
|
PrivateKeyID: privateKeyID,
|
||||||
PoW: pow,
|
PoW: pow,
|
||||||
Topics: topics,
|
Topics: topics,
|
||||||
|
PubsubTopic: pubsubTopic,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/libp2p/go-libp2p/core/peer"
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
@ -136,12 +137,12 @@ func NewTransport(
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) InitFilters(chatIDs []string, publicKeys []*ecdsa.PublicKey) ([]*Filter, error) {
|
func (t *Transport) InitFilters(chatIDs []FiltersToInitialize, publicKeys []*ecdsa.PublicKey) ([]*Filter, error) {
|
||||||
return t.filters.Init(chatIDs, publicKeys)
|
return t.filters.Init(chatIDs, publicKeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) InitPublicFilters(chatIDs []string) ([]*Filter, error) {
|
func (t *Transport) InitPublicFilters(filtersToInit []FiltersToInitialize) ([]*Filter, error) {
|
||||||
return t.filters.InitPublicFilters(chatIDs)
|
return t.filters.InitPublicFilters(filtersToInit)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) Filters() []*Filter {
|
func (t *Transport) Filters() []*Filter {
|
||||||
|
@ -164,8 +165,8 @@ func (t *Transport) LoadFilters(filters []*Filter) ([]*Filter, error) {
|
||||||
return t.filters.InitWithFilters(filters)
|
return t.filters.InitWithFilters(filters)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) InitCommunityFilters(pks []*ecdsa.PrivateKey) ([]*Filter, error) {
|
func (t *Transport) InitCommunityFilters(communityFiltersToInitialize []CommunityFilterToInitialize) ([]*Filter, error) {
|
||||||
return t.filters.InitCommunityFilters(pks)
|
return t.filters.InitCommunityFilters(communityFiltersToInitialize)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) RemoveFilters(filters []*Filter) error {
|
func (t *Transport) RemoveFilters(filters []*Filter) error {
|
||||||
|
@ -189,7 +190,7 @@ func (t *Transport) ProcessNegotiatedSecret(secret types.NegotiatedSecret) (*Fil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) JoinPublic(chatID string) (*Filter, error) {
|
func (t *Transport) JoinPublic(chatID string) (*Filter, error) {
|
||||||
return t.filters.LoadPublic(chatID)
|
return t.filters.LoadPublic(chatID, relay.DefaultWakuTopic)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) LeavePublic(chatID string) error {
|
func (t *Transport) LeavePublic(chatID string) error {
|
||||||
|
@ -279,13 +280,13 @@ func (t *Transport) SendPublic(ctx context.Context, newMessage *types.NewMessage
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
filter, err := t.filters.LoadPublic(chatName)
|
filter, err := t.filters.LoadPublic(chatName, newMessage.PubsubTopic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newMessage.SymKeyID = filter.SymKeyID
|
newMessage.SymKeyID = filter.SymKeyID
|
||||||
newMessage.Topic = filter.Topic
|
newMessage.Topic = filter.ContentTopic
|
||||||
|
|
||||||
return t.api.Post(ctx, *newMessage)
|
return t.api.Post(ctx, *newMessage)
|
||||||
}
|
}
|
||||||
|
@ -304,7 +305,7 @@ func (t *Transport) SendPrivateWithSharedSecret(ctx context.Context, newMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
newMessage.SymKeyID = filter.SymKeyID
|
newMessage.SymKeyID = filter.SymKeyID
|
||||||
newMessage.Topic = filter.Topic
|
newMessage.Topic = filter.ContentTopic
|
||||||
newMessage.PublicKey = nil
|
newMessage.PublicKey = nil
|
||||||
|
|
||||||
return t.api.Post(ctx, *newMessage)
|
return t.api.Post(ctx, *newMessage)
|
||||||
|
@ -320,7 +321,7 @@ func (t *Transport) SendPrivateWithPartitioned(ctx context.Context, newMessage *
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newMessage.Topic = filter.Topic
|
newMessage.Topic = filter.ContentTopic
|
||||||
newMessage.PublicKey = crypto.FromECDSAPub(publicKey)
|
newMessage.PublicKey = crypto.FromECDSAPub(publicKey)
|
||||||
|
|
||||||
return t.api.Post(ctx, *newMessage)
|
return t.api.Post(ctx, *newMessage)
|
||||||
|
@ -336,7 +337,7 @@ func (t *Transport) SendPrivateOnPersonalTopic(ctx context.Context, newMessage *
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newMessage.Topic = filter.Topic
|
newMessage.Topic = filter.ContentTopic
|
||||||
newMessage.PublicKey = crypto.FromECDSAPub(publicKey)
|
newMessage.PublicKey = crypto.FromECDSAPub(publicKey)
|
||||||
|
|
||||||
return t.api.Post(ctx, *newMessage)
|
return t.api.Post(ctx, *newMessage)
|
||||||
|
@ -356,15 +357,15 @@ func (t *Transport) SendCommunityMessage(ctx context.Context, newMessage *types.
|
||||||
}
|
}
|
||||||
|
|
||||||
// We load the filter to make sure we can post on it
|
// We load the filter to make sure we can post on it
|
||||||
filter, err := t.filters.LoadPublic(PubkeyToHex(publicKey)[2:])
|
filter, err := t.filters.LoadPublic(PubkeyToHex(publicKey)[2:], newMessage.PubsubTopic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newMessage.Topic = filter.Topic
|
newMessage.Topic = filter.ContentTopic
|
||||||
newMessage.PublicKey = crypto.FromECDSAPub(publicKey)
|
newMessage.PublicKey = crypto.FromECDSAPub(publicKey)
|
||||||
|
|
||||||
t.logger.Debug("SENDING message", zap.Binary("topic", filter.Topic[:]))
|
t.logger.Debug("SENDING message", zap.Binary("topic", filter.ContentTopic[:]))
|
||||||
|
|
||||||
return t.api.Post(ctx, *newMessage)
|
return t.api.Post(ctx, *newMessage)
|
||||||
}
|
}
|
||||||
|
@ -450,7 +451,7 @@ func (t *Transport) createMessagesRequestV1(
|
||||||
topics []types.TopicType,
|
topics []types.TopicType,
|
||||||
waitForResponse bool,
|
waitForResponse bool,
|
||||||
) (cursor []byte, err error) {
|
) (cursor []byte, err error) {
|
||||||
r := createMessagesRequest(from, to, previousCursor, nil, topics)
|
r := createMessagesRequest(from, to, previousCursor, nil, "", topics)
|
||||||
|
|
||||||
events := make(chan types.EnvelopeEvent, 10)
|
events := make(chan types.EnvelopeEvent, 10)
|
||||||
sub := t.waku.SubscribeEnvelopeEvents(events)
|
sub := t.waku.SubscribeEnvelopeEvents(events)
|
||||||
|
@ -481,10 +482,11 @@ func (t *Transport) createMessagesRequestV2(
|
||||||
peerID []byte,
|
peerID []byte,
|
||||||
from, to uint32,
|
from, to uint32,
|
||||||
previousStoreCursor *types.StoreRequestCursor,
|
previousStoreCursor *types.StoreRequestCursor,
|
||||||
topics []types.TopicType,
|
pubsubTopic string,
|
||||||
|
contentTopics []types.TopicType,
|
||||||
waitForResponse bool,
|
waitForResponse bool,
|
||||||
) (storeCursor *types.StoreRequestCursor, err error) {
|
) (storeCursor *types.StoreRequestCursor, err error) {
|
||||||
r := createMessagesRequest(from, to, nil, previousStoreCursor, topics)
|
r := createMessagesRequest(from, to, nil, previousStoreCursor, pubsubTopic, contentTopics)
|
||||||
|
|
||||||
if waitForResponse {
|
if waitForResponse {
|
||||||
resultCh := make(chan struct {
|
resultCh := make(chan struct {
|
||||||
|
@ -524,55 +526,22 @@ func (t *Transport) SendMessagesRequestForTopics(
|
||||||
from, to uint32,
|
from, to uint32,
|
||||||
previousCursor []byte,
|
previousCursor []byte,
|
||||||
previousStoreCursor *types.StoreRequestCursor,
|
previousStoreCursor *types.StoreRequestCursor,
|
||||||
topics []types.TopicType,
|
pubsubTopic string,
|
||||||
|
contentTopics []types.TopicType,
|
||||||
waitForResponse bool,
|
waitForResponse bool,
|
||||||
) (cursor []byte, storeCursor *types.StoreRequestCursor, err error) {
|
) (cursor []byte, storeCursor *types.StoreRequestCursor, err error) {
|
||||||
switch t.waku.Version() {
|
switch t.waku.Version() {
|
||||||
case 2:
|
case 2:
|
||||||
storeCursor, err = t.createMessagesRequestV2(ctx, peerID, from, to, previousStoreCursor, topics, waitForResponse)
|
storeCursor, err = t.createMessagesRequestV2(ctx, peerID, from, to, previousStoreCursor, pubsubTopic, contentTopics, waitForResponse)
|
||||||
case 1:
|
case 1:
|
||||||
cursor, err = t.createMessagesRequestV1(ctx, peerID, from, to, previousCursor, topics, waitForResponse)
|
cursor, err = t.createMessagesRequestV1(ctx, peerID, from, to, previousCursor, contentTopics, waitForResponse)
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("unsupported version %d", t.waku.Version())
|
err = fmt.Errorf("unsupported version %d", t.waku.Version())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequestHistoricMessages requests historic messages for all registered filters.
|
func createMessagesRequest(from, to uint32, cursor []byte, storeCursor *types.StoreRequestCursor, pubsubTopic string, topics []types.TopicType) types.MessagesRequest {
|
||||||
func (t *Transport) SendMessagesRequest(
|
|
||||||
ctx context.Context,
|
|
||||||
peerID []byte,
|
|
||||||
from, to uint32,
|
|
||||||
previousCursor []byte,
|
|
||||||
previousStoreCursor *types.StoreRequestCursor,
|
|
||||||
waitForResponse bool,
|
|
||||||
) (cursor []byte, storeCursor *types.StoreRequestCursor, err error) {
|
|
||||||
|
|
||||||
topics := make([]types.TopicType, len(t.Filters()))
|
|
||||||
for _, f := range t.Filters() {
|
|
||||||
topics = append(topics, f.Topic)
|
|
||||||
}
|
|
||||||
|
|
||||||
return t.SendMessagesRequestForTopics(ctx, peerID, from, to, previousCursor, previousStoreCursor, topics, waitForResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Transport) SendMessagesRequestForFilter(
|
|
||||||
ctx context.Context,
|
|
||||||
peerID []byte,
|
|
||||||
from, to uint32,
|
|
||||||
previousCursor []byte,
|
|
||||||
previousStoreCursor *types.StoreRequestCursor,
|
|
||||||
filter *Filter,
|
|
||||||
waitForResponse bool,
|
|
||||||
) (cursor []byte, storeCursor *types.StoreRequestCursor, err error) {
|
|
||||||
|
|
||||||
topics := make([]types.TopicType, len(t.Filters()))
|
|
||||||
topics = append(topics, filter.Topic)
|
|
||||||
|
|
||||||
return t.SendMessagesRequestForTopics(ctx, peerID, from, to, previousCursor, previousStoreCursor, topics, waitForResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createMessagesRequest(from, to uint32, cursor []byte, storeCursor *types.StoreRequestCursor, topics []types.TopicType) types.MessagesRequest {
|
|
||||||
aUUID := uuid.New()
|
aUUID := uuid.New()
|
||||||
// uuid is 16 bytes, converted to hex it's 32 bytes as expected by types.MessagesRequest
|
// uuid is 16 bytes, converted to hex it's 32 bytes as expected by types.MessagesRequest
|
||||||
id := []byte(hex.EncodeToString(aUUID[:]))
|
id := []byte(hex.EncodeToString(aUUID[:]))
|
||||||
|
@ -581,13 +550,14 @@ func createMessagesRequest(from, to uint32, cursor []byte, storeCursor *types.St
|
||||||
topicBytes = append(topicBytes, topics[idx][:])
|
topicBytes = append(topicBytes, topics[idx][:])
|
||||||
}
|
}
|
||||||
return types.MessagesRequest{
|
return types.MessagesRequest{
|
||||||
ID: id,
|
ID: id,
|
||||||
From: from,
|
From: from,
|
||||||
To: to,
|
To: to,
|
||||||
Limit: 1000,
|
Limit: 1000,
|
||||||
Cursor: cursor,
|
Cursor: cursor,
|
||||||
Topics: topicBytes,
|
PubsubTopic: pubsubTopic,
|
||||||
StoreCursor: storeCursor,
|
ContentTopics: topicBytes,
|
||||||
|
StoreCursor: storeCursor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -689,3 +659,25 @@ func (t *Transport) SubscribeToConnStatusChanges() (*types.ConnStatusSubscriptio
|
||||||
func (t *Transport) ConnectionChanged(state connection.State) {
|
func (t *Transport) ConnectionChanged(state connection.State) {
|
||||||
t.waku.ConnectionChanged(state)
|
t.waku.ConnectionChanged(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Subscribe to a pubsub topic, passing an optional public key if the pubsub topic is protected
|
||||||
|
func (t *Transport) SubscribeToPubsubTopic(topic string, optPublicKey *ecdsa.PublicKey) error {
|
||||||
|
if t.waku.Version() == 2 {
|
||||||
|
return t.waku.SubscribeToPubsubTopic(topic, optPublicKey)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transport) StorePubsubTopicKey(topic string, privKey *ecdsa.PrivateKey) error {
|
||||||
|
return t.waku.StorePubsubTopicKey(topic, privKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPubsubTopic(communityID []byte) string {
|
||||||
|
// TODO: remove hardcoded pubsub topic and use shard
|
||||||
|
result := "/waku/2/status-signed-test-1"
|
||||||
|
if communityID == nil {
|
||||||
|
result = relay.DefaultWakuTopic
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package ext
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -1267,6 +1268,37 @@ func (api *PublicAPI) DisableCommunityHistoryArchiveProtocol() error {
|
||||||
return api.service.messenger.DisableCommunityHistoryArchiveProtocol()
|
return api.service.messenger.DisableCommunityHistoryArchiveProtocol()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *PublicAPI) SubscribeToPubsubTopic(topic string, optPublicKey string) error {
|
||||||
|
var publicKey *ecdsa.PublicKey
|
||||||
|
if optPublicKey != "" {
|
||||||
|
keyBytes, err := hexutil.Decode(optPublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKey, err = crypto.UnmarshalPubkey(keyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.service.messenger.SubscribeToPubsubTopic(topic, publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *PublicAPI) StorePubsubTopicKey(topic string, privKey string) error {
|
||||||
|
keyBytes, err := hexutil.Decode(privKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := crypto.ToECDSA(keyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.service.messenger.StorePubsubTopicKey(topic, p)
|
||||||
|
}
|
||||||
|
|
||||||
func (api *PublicAPI) AddStorePeer(address string) (string, error) {
|
func (api *PublicAPI) AddStorePeer(address string) (string, error) {
|
||||||
peerID, err := api.service.messenger.AddStorePeer(address)
|
peerID, err := api.service.messenger.AddStorePeer(address)
|
||||||
return string(peerID), err
|
return string(peerID), err
|
||||||
|
|
|
@ -51,8 +51,8 @@ func (a *API) GetMailserverTopics(ctx context.Context) ([]MailserverTopic, error
|
||||||
return a.db.Topics()
|
return a.db.Topics()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *API) DeleteMailserverTopic(ctx context.Context, topic string) error {
|
func (a *API) DeleteMailserverTopic(ctx context.Context, pubsubTopic string, topic string) error {
|
||||||
return a.db.DeleteTopic(topic)
|
return a.db.DeleteTopic(pubsubTopic, topic)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *API) AddChatRequestRange(ctx context.Context, req ChatRequestRange) error {
|
func (a *API) AddChatRequestRange(ctx context.Context, req ChatRequestRange) error {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
||||||
|
|
||||||
"github.com/status-im/status-go/appdatabase"
|
"github.com/status-im/status-go/appdatabase"
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
|
@ -58,9 +59,9 @@ func TestTopic(t *testing.T) {
|
||||||
defer close()
|
defer close()
|
||||||
topicA := "0x61000000"
|
topicA := "0x61000000"
|
||||||
topicD := "0x64000000"
|
topicD := "0x64000000"
|
||||||
topic1 := MailserverTopic{Topic: topicA, LastRequest: 1}
|
topic1 := MailserverTopic{PubsubTopic: relay.DefaultWakuTopic, ContentTopic: topicA, LastRequest: 1}
|
||||||
topic2 := MailserverTopic{Topic: "0x6200000", LastRequest: 2}
|
topic2 := MailserverTopic{PubsubTopic: relay.DefaultWakuTopic, ContentTopic: "0x6200000", LastRequest: 2}
|
||||||
topic3 := MailserverTopic{Topic: "0x6300000", LastRequest: 3}
|
topic3 := MailserverTopic{PubsubTopic: relay.DefaultWakuTopic, ContentTopic: "0x6300000", LastRequest: 3}
|
||||||
|
|
||||||
require.NoError(t, db.AddTopic(topic1))
|
require.NoError(t, db.AddTopic(topic1))
|
||||||
require.NoError(t, db.AddTopic(topic2))
|
require.NoError(t, db.AddTopic(topic2))
|
||||||
|
@ -72,12 +73,16 @@ func TestTopic(t *testing.T) {
|
||||||
|
|
||||||
filters := []*transport.Filter{
|
filters := []*transport.Filter{
|
||||||
// Existing topic, is not updated
|
// Existing topic, is not updated
|
||||||
{Topic: types.BytesToTopic([]byte{0x61})},
|
{
|
||||||
|
PubsubTopic: relay.DefaultWakuTopic,
|
||||||
|
ContentTopic: types.BytesToTopic([]byte{0x61}),
|
||||||
|
},
|
||||||
// Non existing topic is not inserted
|
// Non existing topic is not inserted
|
||||||
{
|
{
|
||||||
Discovery: true,
|
Discovery: true,
|
||||||
Negotiated: true,
|
Negotiated: true,
|
||||||
Topic: types.BytesToTopic([]byte{0x64}),
|
PubsubTopic: relay.DefaultWakuTopic,
|
||||||
|
ContentTopic: types.BytesToTopic([]byte{0x64}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,13 +91,13 @@ func TestTopic(t *testing.T) {
|
||||||
topics, err = db.Topics()
|
topics, err = db.Topics()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, topics, 2)
|
require.Len(t, topics, 2)
|
||||||
require.Equal(t, topics[0].Topic, topicA)
|
require.Equal(t, topics[0].ContentTopic, topicA)
|
||||||
require.Equal(t, topics[0].LastRequest, 1)
|
require.Equal(t, topics[0].LastRequest, 1)
|
||||||
|
|
||||||
require.Equal(t, topics[0].Topic, topicA)
|
require.Equal(t, topics[0].ContentTopic, topicA)
|
||||||
require.Equal(t, topics[0].LastRequest, 1)
|
require.Equal(t, topics[0].LastRequest, 1)
|
||||||
|
|
||||||
require.Equal(t, topics[1].Topic, topicD)
|
require.Equal(t, topics[1].ContentTopic, topicD)
|
||||||
require.NotEmpty(t, topics[1].LastRequest)
|
require.NotEmpty(t, topics[1].LastRequest)
|
||||||
require.True(t, topics[1].Negotiated)
|
require.True(t, topics[1].Negotiated)
|
||||||
require.True(t, topics[1].Discovery)
|
require.True(t, topics[1].Discovery)
|
||||||
|
@ -152,9 +157,10 @@ func TestAddGetDeleteMailserverTopics(t *testing.T) {
|
||||||
defer close()
|
defer close()
|
||||||
api := &API{db: db}
|
api := &API{db: db}
|
||||||
testTopic := MailserverTopic{
|
testTopic := MailserverTopic{
|
||||||
Topic: "topic-001",
|
PubsubTopic: relay.DefaultWakuTopic,
|
||||||
ChatIDs: []string{"chatID01", "chatID02"},
|
ContentTopic: "topic-001",
|
||||||
LastRequest: 10,
|
ChatIDs: []string{"chatID01", "chatID02"},
|
||||||
|
LastRequest: 10,
|
||||||
}
|
}
|
||||||
err := api.AddMailserverTopic(context.Background(), testTopic)
|
err := api.AddMailserverTopic(context.Background(), testTopic)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -164,14 +170,14 @@ func TestAddGetDeleteMailserverTopics(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.EqualValues(t, []MailserverTopic{testTopic}, topics)
|
require.EqualValues(t, []MailserverTopic{testTopic}, topics)
|
||||||
|
|
||||||
err = api.DeleteMailserverTopic(context.Background(), testTopic.Topic)
|
err = api.DeleteMailserverTopic(context.Background(), relay.DefaultWakuTopic, testTopic.ContentTopic)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
topics, err = api.GetMailserverTopics(context.Background())
|
topics, err = api.GetMailserverTopics(context.Background())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.EqualValues(t, ([]MailserverTopic)(nil), topics)
|
require.EqualValues(t, ([]MailserverTopic)(nil), topics)
|
||||||
|
|
||||||
// Delete non-existing topic.
|
// Delete non-existing topic.
|
||||||
err = api.DeleteMailserverTopic(context.Background(), "non-existing-topic")
|
err = api.DeleteMailserverTopic(context.Background(), relay.DefaultWakuTopic, "non-existing-topic")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/libp2p/go-libp2p/core/peer"
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
|
|
||||||
"github.com/status-im/status-go/protocol/transport"
|
"github.com/status-im/status-go/protocol/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -83,11 +84,12 @@ type MailserverRequestGap struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type MailserverTopic struct {
|
type MailserverTopic struct {
|
||||||
Topic string `json:"topic"`
|
PubsubTopic string `json:"pubsubTopic"`
|
||||||
Discovery bool `json:"discovery?"`
|
ContentTopic string `json:"topic"`
|
||||||
Negotiated bool `json:"negotiated?"`
|
Discovery bool `json:"discovery?"`
|
||||||
ChatIDs []string `json:"chat-ids"`
|
Negotiated bool `json:"negotiated?"`
|
||||||
LastRequest int `json:"last-request"` // default is 1
|
ChatIDs []string `json:"chat-ids"`
|
||||||
|
LastRequest int `json:"last-request"` // default is 1
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChatRequestRange struct {
|
type ChatRequestRange struct {
|
||||||
|
@ -264,13 +266,15 @@ func (d *Database) AddTopic(topic MailserverTopic) error {
|
||||||
|
|
||||||
chatIDs := sqlStringSlice(topic.ChatIDs)
|
chatIDs := sqlStringSlice(topic.ChatIDs)
|
||||||
_, err := d.db.Exec(`INSERT OR REPLACE INTO mailserver_topics(
|
_, err := d.db.Exec(`INSERT OR REPLACE INTO mailserver_topics(
|
||||||
|
pubsub_topic,
|
||||||
topic,
|
topic,
|
||||||
chat_ids,
|
chat_ids,
|
||||||
last_request,
|
last_request,
|
||||||
discovery,
|
discovery,
|
||||||
negotiated
|
negotiated
|
||||||
) VALUES (?, ?, ?,?,?)`,
|
) VALUES (?, ?, ?, ?, ?, ?)`,
|
||||||
topic.Topic,
|
topic.PubsubTopic,
|
||||||
|
topic.ContentTopic,
|
||||||
chatIDs,
|
chatIDs,
|
||||||
topic.LastRequest,
|
topic.LastRequest,
|
||||||
topic.Discovery,
|
topic.Discovery,
|
||||||
|
@ -296,13 +300,15 @@ func (d *Database) AddTopics(topics []MailserverTopic) (err error) {
|
||||||
for _, topic := range topics {
|
for _, topic := range topics {
|
||||||
chatIDs := sqlStringSlice(topic.ChatIDs)
|
chatIDs := sqlStringSlice(topic.ChatIDs)
|
||||||
_, err = tx.Exec(`INSERT OR REPLACE INTO mailserver_topics(
|
_, err = tx.Exec(`INSERT OR REPLACE INTO mailserver_topics(
|
||||||
|
pubsub_topic,
|
||||||
topic,
|
topic,
|
||||||
chat_ids,
|
chat_ids,
|
||||||
last_request,
|
last_request,
|
||||||
discovery,
|
discovery,
|
||||||
negotiated
|
negotiated
|
||||||
) VALUES (?, ?, ?,?,?)`,
|
) VALUES (?, ?, ?, ?, ?, ?)`,
|
||||||
topic.Topic,
|
topic.PubsubTopic,
|
||||||
|
topic.ContentTopic,
|
||||||
chatIDs,
|
chatIDs,
|
||||||
topic.LastRequest,
|
topic.LastRequest,
|
||||||
topic.Discovery,
|
topic.Discovery,
|
||||||
|
@ -318,7 +324,7 @@ func (d *Database) AddTopics(topics []MailserverTopic) (err error) {
|
||||||
func (d *Database) Topics() ([]MailserverTopic, error) {
|
func (d *Database) Topics() ([]MailserverTopic, error) {
|
||||||
var result []MailserverTopic
|
var result []MailserverTopic
|
||||||
|
|
||||||
rows, err := d.db.Query(`SELECT topic, chat_ids, last_request,discovery,negotiated FROM mailserver_topics`)
|
rows, err := d.db.Query(`SELECT pubsub_topic, topic, chat_ids, last_request,discovery,negotiated FROM mailserver_topics`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -330,7 +336,8 @@ func (d *Database) Topics() ([]MailserverTopic, error) {
|
||||||
chatIDs sqlStringSlice
|
chatIDs sqlStringSlice
|
||||||
)
|
)
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
&t.Topic,
|
&t.PubsubTopic,
|
||||||
|
&t.ContentTopic,
|
||||||
&chatIDs,
|
&chatIDs,
|
||||||
&t.LastRequest,
|
&t.LastRequest,
|
||||||
&t.Discovery,
|
&t.Discovery,
|
||||||
|
@ -345,13 +352,13 @@ func (d *Database) Topics() ([]MailserverTopic, error) {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Database) ResetLastRequest(topic string) error {
|
func (d *Database) ResetLastRequest(pubsubTopic, contentTopic string) error {
|
||||||
_, err := d.db.Exec("UPDATE mailserver_topics SET last_request = 0 WHERE topic = ?", topic)
|
_, err := d.db.Exec("UPDATE mailserver_topics SET last_request = 0 WHERE pubsub_topic = ? AND topic = ?", pubsubTopic, contentTopic)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Database) DeleteTopic(topic string) error {
|
func (d *Database) DeleteTopic(pubsubTopic, contentTopic string) error {
|
||||||
_, err := d.db.Exec(`DELETE FROM mailserver_topics WHERE topic = ?`, topic)
|
_, err := d.db.Exec(`DELETE FROM mailserver_topics WHERE pubsub_topic = ? AND topic = ?`, pubsubTopic, contentTopic)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,16 +382,29 @@ func (d *Database) SetTopics(filters []*transport.Filter) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
topicsArgs := make([]interface{}, 0, len(filters))
|
contentTopicsPerPubsubTopic := make(map[string]map[string]struct{})
|
||||||
for _, filter := range filters {
|
for _, filter := range filters {
|
||||||
topicsArgs = append(topicsArgs, filter.Topic.String())
|
contentTopics, ok := contentTopicsPerPubsubTopic[filter.PubsubTopic]
|
||||||
|
if !ok {
|
||||||
|
contentTopics = make(map[string]struct{})
|
||||||
|
}
|
||||||
|
contentTopics[filter.ContentTopic.String()] = struct{}{}
|
||||||
|
contentTopicsPerPubsubTopic[filter.PubsubTopic] = contentTopics
|
||||||
}
|
}
|
||||||
|
|
||||||
inVector := strings.Repeat("?, ", len(filters)-1) + "?"
|
for pubsubTopic, contentTopics := range contentTopicsPerPubsubTopic {
|
||||||
|
topicsArgs := make([]interface{}, 0, len(contentTopics)+1)
|
||||||
|
topicsArgs = append(topicsArgs, pubsubTopic)
|
||||||
|
for ct := range contentTopics {
|
||||||
|
topicsArgs = append(topicsArgs, ct)
|
||||||
|
}
|
||||||
|
|
||||||
// Delete topics
|
inVector := strings.Repeat("?, ", len(contentTopics)-1) + "?"
|
||||||
query := "DELETE FROM mailserver_topics WHERE topic NOT IN (" + inVector + ")" // nolint: gosec
|
|
||||||
_, err = tx.Exec(query, topicsArgs...)
|
// Delete topics
|
||||||
|
query := "DELETE FROM mailserver_topics WHERE pubsub_topic = ? AND topic NOT IN (" + inVector + ")" // nolint: gosec
|
||||||
|
_, err = tx.Exec(query, topicsArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
// Default to now - 1.day
|
// Default to now - 1.day
|
||||||
lastRequest := (time.Now().Add(-24 * time.Hour)).Unix()
|
lastRequest := (time.Now().Add(-24 * time.Hour)).Unix()
|
||||||
|
@ -392,12 +412,12 @@ func (d *Database) SetTopics(filters []*transport.Filter) (err error) {
|
||||||
for _, filter := range filters {
|
for _, filter := range filters {
|
||||||
// fetch
|
// fetch
|
||||||
var topic string
|
var topic string
|
||||||
err = tx.QueryRow(`SELECT topic FROM mailserver_topics WHERE topic = ?`, filter.Topic.String()).Scan(&topic)
|
err = tx.QueryRow(`SELECT topic FROM mailserver_topics WHERE topic = ? AND pubsub_topic = ?`, filter.ContentTopic.String(), filter.PubsubTopic).Scan(&topic)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return
|
return
|
||||||
} else if err == sql.ErrNoRows {
|
} else if err == sql.ErrNoRows {
|
||||||
// we insert the topic
|
// we insert the topic
|
||||||
_, err = tx.Exec(`INSERT INTO mailserver_topics(topic,last_request,discovery,negotiated) VALUES (?,?,?,?)`, filter.Topic.String(), lastRequest, filter.Discovery, filter.Negotiated)
|
_, err = tx.Exec(`INSERT INTO mailserver_topics(topic,pubsub_topic,last_request,discovery,negotiated) VALUES (?,?,?,?,?)`, filter.ContentTopic.String(), filter.PubsubTopic, lastRequest, filter.Discovery, filter.Negotiated)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
|
@ -42,7 +42,8 @@ func (c *Client) PushReceivedMessages(filter transport.Filter, sshMessage *types
|
||||||
"messageHash": types.EncodeHex(sshMessage.Hash),
|
"messageHash": types.EncodeHex(sshMessage.Hash),
|
||||||
"messageId": message.ID,
|
"messageId": message.ID,
|
||||||
"sentAt": sshMessage.Timestamp,
|
"sentAt": sshMessage.Timestamp,
|
||||||
"topic": filter.Topic.String(),
|
"pubsubTopic": filter.PubsubTopic,
|
||||||
|
"topic": filter.ContentTopic.String(),
|
||||||
"messageType": message.Type.String(),
|
"messageType": message.Type.String(),
|
||||||
"receiverKeyUID": c.keyUID,
|
"receiverKeyUID": c.keyUID,
|
||||||
"nodeName": c.nodeName,
|
"nodeName": c.nodeName,
|
||||||
|
|
|
@ -171,14 +171,15 @@ func (api *PublicWakuAPI) BloomFilter() []byte {
|
||||||
|
|
||||||
// NewMessage represents a new waku message that is posted through the RPC.
|
// NewMessage represents a new waku message that is posted through the RPC.
|
||||||
type NewMessage struct {
|
type NewMessage struct {
|
||||||
SymKeyID string `json:"symKeyID"`
|
SymKeyID string `json:"symKeyID"`
|
||||||
PublicKey []byte `json:"pubKey"`
|
PublicKey []byte `json:"pubKey"`
|
||||||
Sig string `json:"sig"`
|
Sig string `json:"sig"`
|
||||||
Topic common.TopicType `json:"topic"`
|
PubsubTopic string `json:"pubsubTopic"`
|
||||||
Payload []byte `json:"payload"`
|
ContentTopic common.TopicType `json:"topic"`
|
||||||
Padding []byte `json:"padding"`
|
Payload []byte `json:"payload"`
|
||||||
TargetPeer string `json:"targetPeer"`
|
Padding []byte `json:"padding"`
|
||||||
Ephemeral bool `json:"ephemeral"`
|
TargetPeer string `json:"targetPeer"`
|
||||||
|
Ephemeral bool `json:"ephemeral"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post posts a message on the Waku network.
|
// Post posts a message on the Waku network.
|
||||||
|
@ -195,27 +196,22 @@ func (api *PublicWakuAPI) Post(ctx context.Context, req NewMessage) (hexutil.Byt
|
||||||
return nil, ErrSymAsym
|
return nil, ErrSymAsym
|
||||||
}
|
}
|
||||||
|
|
||||||
params := &common.MessageParams{
|
|
||||||
Payload: req.Payload,
|
|
||||||
Padding: req.Padding,
|
|
||||||
Topic: req.Topic,
|
|
||||||
}
|
|
||||||
|
|
||||||
var keyInfo *payload.KeyInfo = new(payload.KeyInfo)
|
var keyInfo *payload.KeyInfo = new(payload.KeyInfo)
|
||||||
|
|
||||||
// Set key that is used to sign the message
|
// Set key that is used to sign the message
|
||||||
if len(req.Sig) > 0 {
|
if len(req.Sig) > 0 {
|
||||||
if params.Src, err = api.w.GetPrivateKey(req.Sig); err != nil {
|
privKey, err := api.w.GetPrivateKey(req.Sig)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
keyInfo.PrivKey = params.Src
|
keyInfo.PrivKey = privKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set symmetric key that is used to encrypt the message
|
// Set symmetric key that is used to encrypt the message
|
||||||
if symKeyGiven {
|
if symKeyGiven {
|
||||||
keyInfo.Kind = payload.Symmetric
|
keyInfo.Kind = payload.Symmetric
|
||||||
|
|
||||||
if params.Topic == (common.TopicType{}) { // topics are mandatory with symmetric encryption
|
if req.ContentTopic == (common.TopicType{}) { // topics are mandatory with symmetric encryption
|
||||||
return nil, ErrNoTopics
|
return nil, ErrNoTopics
|
||||||
}
|
}
|
||||||
if keyInfo.SymKey, err = api.w.GetSymKey(req.SymKeyID); err != nil {
|
if keyInfo.SymKey, err = api.w.GetSymKey(req.SymKeyID); err != nil {
|
||||||
|
@ -251,13 +247,13 @@ func (api *PublicWakuAPI) Post(ctx context.Context, req NewMessage) (hexutil.Byt
|
||||||
wakuMsg := &pb.WakuMessage{
|
wakuMsg := &pb.WakuMessage{
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
Version: version,
|
Version: version,
|
||||||
ContentTopic: req.Topic.ContentTopic(),
|
ContentTopic: req.ContentTopic.ContentTopic(),
|
||||||
Timestamp: api.w.timestamp(),
|
Timestamp: api.w.timestamp(),
|
||||||
Meta: []byte{}, // TODO: empty for now. Once we use Waku Archive v2, we should deprecate the timestamp and use an ULID here
|
Meta: []byte{}, // TODO: empty for now. Once we use Waku Archive v2, we should deprecate the timestamp and use an ULID here
|
||||||
Ephemeral: req.Ephemeral,
|
Ephemeral: req.Ephemeral,
|
||||||
}
|
}
|
||||||
|
|
||||||
hash, err := api.w.Send(wakuMsg)
|
hash, err := api.w.Send(req.PubsubTopic, wakuMsg)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -278,10 +274,11 @@ func (api *PublicWakuAPI) Unsubscribe(ctx context.Context, id string) {
|
||||||
|
|
||||||
// Criteria holds various filter options for inbound messages.
|
// Criteria holds various filter options for inbound messages.
|
||||||
type Criteria struct {
|
type Criteria struct {
|
||||||
SymKeyID string `json:"symKeyID"`
|
SymKeyID string `json:"symKeyID"`
|
||||||
PrivateKeyID string `json:"privateKeyID"`
|
PrivateKeyID string `json:"privateKeyID"`
|
||||||
Sig []byte `json:"sig"`
|
Sig []byte `json:"sig"`
|
||||||
Topics []common.TopicType `json:"topics"`
|
PubsubTopic string `json:"pubsubTopic"`
|
||||||
|
ContentTopics []common.TopicType `json:"topics"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Messages set up a subscription that fires events when messages arrive that match
|
// Messages set up a subscription that fires events when messages arrive that match
|
||||||
|
@ -314,7 +311,9 @@ func (api *PublicWakuAPI) Messages(ctx context.Context, crit Criteria) (*rpc.Sub
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, bt := range crit.Topics {
|
filter.PubsubTopic = crit.PubsubTopic
|
||||||
|
|
||||||
|
for _, bt := range crit.ContentTopics {
|
||||||
filter.Topics = append(filter.Topics, bt[:])
|
filter.Topics = append(filter.Topics, bt[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,23 +377,25 @@ func (api *PublicWakuAPI) Messages(ctx context.Context, crit Criteria) (*rpc.Sub
|
||||||
|
|
||||||
// Message is the RPC representation of a waku message.
|
// Message is the RPC representation of a waku message.
|
||||||
type Message struct {
|
type Message struct {
|
||||||
Sig []byte `json:"sig,omitempty"`
|
Sig []byte `json:"sig,omitempty"`
|
||||||
Timestamp uint32 `json:"timestamp"`
|
Timestamp uint32 `json:"timestamp"`
|
||||||
Topic common.TopicType `json:"topic"`
|
PubsubTopic string `json:"pubsubTopic"`
|
||||||
Payload []byte `json:"payload"`
|
ContentTopic common.TopicType `json:"topic"`
|
||||||
Padding []byte `json:"padding"`
|
Payload []byte `json:"payload"`
|
||||||
Hash []byte `json:"hash"`
|
Padding []byte `json:"padding"`
|
||||||
Dst []byte `json:"recipientPublicKey,omitempty"`
|
Hash []byte `json:"hash"`
|
||||||
|
Dst []byte `json:"recipientPublicKey,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToWakuMessage converts an internal message into an API version.
|
// ToWakuMessage converts an internal message into an API version.
|
||||||
func ToWakuMessage(message *common.ReceivedMessage) *Message {
|
func ToWakuMessage(message *common.ReceivedMessage) *Message {
|
||||||
msg := Message{
|
msg := Message{
|
||||||
Payload: message.Data,
|
Payload: message.Data,
|
||||||
Padding: message.Padding,
|
Padding: message.Padding,
|
||||||
Timestamp: message.Sent,
|
Timestamp: message.Sent,
|
||||||
Hash: message.Hash().Bytes(),
|
Hash: message.Hash().Bytes(),
|
||||||
Topic: message.Topic,
|
PubsubTopic: message.PubsubTopic,
|
||||||
|
ContentTopic: message.ContentTopic,
|
||||||
}
|
}
|
||||||
|
|
||||||
if message.Dst != nil {
|
if message.Dst != nil {
|
||||||
|
@ -494,20 +495,21 @@ func (api *PublicWakuAPI) NewMessageFilter(req Criteria) (string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(req.Topics) > 0 {
|
if len(req.ContentTopics) > 0 {
|
||||||
topics = make([][]byte, len(req.Topics))
|
topics = make([][]byte, len(req.ContentTopics))
|
||||||
for i, topic := range req.Topics {
|
for i, topic := range req.ContentTopics {
|
||||||
topics[i] = make([]byte, common.TopicLength)
|
topics[i] = make([]byte, common.TopicLength)
|
||||||
copy(topics[i], topic[:])
|
copy(topics[i], topic[:])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
f := &common.Filter{
|
f := &common.Filter{
|
||||||
Src: src,
|
Src: src,
|
||||||
KeySym: keySym,
|
KeySym: keySym,
|
||||||
KeyAsym: keyAsym,
|
KeyAsym: keyAsym,
|
||||||
Topics: topics,
|
PubsubTopic: req.PubsubTopic,
|
||||||
Messages: common.NewMemoryMessageStore(),
|
Topics: topics,
|
||||||
|
Messages: common.NewMemoryMessageStore(),
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := api.w.Subscribe(f)
|
id, err := api.w.Subscribe(f)
|
||||||
|
|
|
@ -23,6 +23,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
||||||
|
|
||||||
"github.com/status-im/status-go/wakuv2/common"
|
"github.com/status-im/status-go/wakuv2/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -45,8 +47,8 @@ func TestMultipleTopicCopyInNewMessageFilter(t *testing.T) {
|
||||||
t2 := [4]byte{0xca, 0xfe, 0xde, 0xca}
|
t2 := [4]byte{0xca, 0xfe, 0xde, 0xca}
|
||||||
|
|
||||||
crit := Criteria{
|
crit := Criteria{
|
||||||
SymKeyID: keyID,
|
SymKeyID: keyID,
|
||||||
Topics: []common.TopicType{common.TopicType(t1), common.TopicType(t2)},
|
ContentTopics: []common.TopicType{common.TopicType(t1), common.TopicType(t2)},
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = api.NewMessageFilter(crit)
|
_, err = api.NewMessageFilter(crit)
|
||||||
|
@ -55,7 +57,7 @@ func TestMultipleTopicCopyInNewMessageFilter(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
candidates := w.filters.GetWatchersByTopic(t1)
|
candidates := w.filters.GetWatchersByTopic(relay.DefaultWakuTopic, t1)
|
||||||
for _, f := range candidates {
|
for _, f := range candidates {
|
||||||
if len(f.Topics) == 2 {
|
if len(f.Topics) == 2 {
|
||||||
if bytes.Equal(f.Topics[0], t1[:]) && bytes.Equal(f.Topics[1], t2[:]) {
|
if bytes.Equal(f.Topics[0], t1[:]) && bytes.Equal(f.Topics[1], t2[:]) {
|
||||||
|
|
|
@ -23,6 +23,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/protocol/relay"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
@ -30,22 +32,27 @@ import (
|
||||||
|
|
||||||
// Filter represents a Waku message filter
|
// Filter represents a Waku message filter
|
||||||
type Filter struct {
|
type Filter struct {
|
||||||
Src *ecdsa.PublicKey // Sender of the message
|
Src *ecdsa.PublicKey // Sender of the message
|
||||||
KeyAsym *ecdsa.PrivateKey // Private Key of recipient
|
KeyAsym *ecdsa.PrivateKey // Private Key of recipient
|
||||||
KeySym []byte // Key associated with the Topic
|
KeySym []byte // Key associated with the Topic
|
||||||
Topics [][]byte // Topics to filter messages with
|
PubsubTopic string // Pubsub topic used to filter messages with
|
||||||
SymKeyHash common.Hash // The Keccak256Hash of the symmetric key, needed for optimization
|
Topics [][]byte // ContentTopics to filter messages with
|
||||||
id string // unique identifier
|
SymKeyHash common.Hash // The Keccak256Hash of the symmetric key, needed for optimization
|
||||||
|
id string // unique identifier
|
||||||
|
|
||||||
Messages MessageStore
|
Messages MessageStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FilterSet = map[*Filter]struct{}
|
||||||
|
type ContentTopicToFilter = map[TopicType]FilterSet
|
||||||
|
type PubsubTopicToContentTopic = map[string]ContentTopicToFilter
|
||||||
|
|
||||||
// Filters represents a collection of filters
|
// Filters represents a collection of filters
|
||||||
type Filters struct {
|
type Filters struct {
|
||||||
watchers map[string]*Filter
|
watchers map[string]*Filter
|
||||||
|
|
||||||
topicMatcher map[TopicType]map[*Filter]struct{} // map a topic to the filters that are interested in being notified when a message matches that topic
|
topicMatcher PubsubTopicToContentTopic // map a topic to the filters that are interested in being notified when a message matches that topic
|
||||||
allTopicsMatcher map[*Filter]struct{} // list all the filters that will be notified of a new message, no matter what its topic is
|
allTopicsMatcher map[*Filter]struct{} // list all the filters that will be notified of a new message, no matter what its topic is
|
||||||
|
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
@ -54,7 +61,7 @@ type Filters struct {
|
||||||
func NewFilters() *Filters {
|
func NewFilters() *Filters {
|
||||||
return &Filters{
|
return &Filters{
|
||||||
watchers: make(map[string]*Filter),
|
watchers: make(map[string]*Filter),
|
||||||
topicMatcher: make(map[TopicType]map[*Filter]struct{}),
|
topicMatcher: make(PubsubTopicToContentTopic),
|
||||||
allTopicsMatcher: make(map[*Filter]struct{}),
|
allTopicsMatcher: make(map[*Filter]struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,8 +111,10 @@ func (fs *Filters) AllTopics() []TopicType {
|
||||||
var topics []TopicType
|
var topics []TopicType
|
||||||
fs.mutex.Lock()
|
fs.mutex.Lock()
|
||||||
defer fs.mutex.Unlock()
|
defer fs.mutex.Unlock()
|
||||||
for t := range fs.topicMatcher {
|
for _, topicsPerPubsubTopic := range fs.topicMatcher {
|
||||||
topics = append(topics, t)
|
for t := range topicsPerPubsubTopic {
|
||||||
|
topics = append(topics, t)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return topics
|
return topics
|
||||||
|
@ -115,36 +124,57 @@ func (fs *Filters) AllTopics() []TopicType {
|
||||||
// If the filter's Topics array is empty, it will be tried on every topic.
|
// If the filter's Topics array is empty, it will be tried on every topic.
|
||||||
// Otherwise, it will be tried on the topics specified.
|
// Otherwise, it will be tried on the topics specified.
|
||||||
func (fs *Filters) addTopicMatcher(watcher *Filter) {
|
func (fs *Filters) addTopicMatcher(watcher *Filter) {
|
||||||
if len(watcher.Topics) == 0 {
|
if len(watcher.Topics) == 0 && (watcher.PubsubTopic == relay.DefaultWakuTopic || watcher.PubsubTopic == "") {
|
||||||
fs.allTopicsMatcher[watcher] = struct{}{}
|
fs.allTopicsMatcher[watcher] = struct{}{}
|
||||||
} else {
|
} else {
|
||||||
|
filtersPerContentTopic, ok := fs.topicMatcher[watcher.PubsubTopic]
|
||||||
|
if !ok {
|
||||||
|
filtersPerContentTopic = make(ContentTopicToFilter)
|
||||||
|
}
|
||||||
|
|
||||||
for _, t := range watcher.Topics {
|
for _, t := range watcher.Topics {
|
||||||
topic := BytesToTopic(t)
|
topic := BytesToTopic(t)
|
||||||
if fs.topicMatcher[topic] == nil {
|
if filtersPerContentTopic[topic] == nil {
|
||||||
fs.topicMatcher[topic] = make(map[*Filter]struct{})
|
filtersPerContentTopic[topic] = make(FilterSet)
|
||||||
}
|
}
|
||||||
fs.topicMatcher[topic][watcher] = struct{}{}
|
filtersPerContentTopic[topic][watcher] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fs.topicMatcher[watcher.PubsubTopic] = filtersPerContentTopic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeFromTopicMatchers removes a filter from the topic matchers
|
// removeFromTopicMatchers removes a filter from the topic matchers
|
||||||
func (fs *Filters) removeFromTopicMatchers(watcher *Filter) {
|
func (fs *Filters) removeFromTopicMatchers(watcher *Filter) {
|
||||||
delete(fs.allTopicsMatcher, watcher)
|
delete(fs.allTopicsMatcher, watcher)
|
||||||
|
|
||||||
|
filtersPerContentTopic, ok := fs.topicMatcher[watcher.PubsubTopic]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for _, t := range watcher.Topics {
|
for _, t := range watcher.Topics {
|
||||||
topic := BytesToTopic(t)
|
topic := BytesToTopic(t)
|
||||||
delete(fs.topicMatcher[topic], watcher)
|
delete(filtersPerContentTopic[topic], watcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fs.topicMatcher[watcher.PubsubTopic] = filtersPerContentTopic
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWatchersByTopic returns a slice containing the filters that
|
// GetWatchersByTopic returns a slice containing the filters that
|
||||||
// match a specific topic
|
// match a specific topic
|
||||||
func (fs *Filters) GetWatchersByTopic(topic TopicType) []*Filter {
|
func (fs *Filters) GetWatchersByTopic(pubsubTopic string, contentTopic TopicType) []*Filter {
|
||||||
res := make([]*Filter, 0, len(fs.allTopicsMatcher))
|
res := make([]*Filter, 0, len(fs.allTopicsMatcher))
|
||||||
for watcher := range fs.allTopicsMatcher {
|
for watcher := range fs.allTopicsMatcher {
|
||||||
res = append(res, watcher)
|
res = append(res, watcher)
|
||||||
}
|
}
|
||||||
for watcher := range fs.topicMatcher[topic] {
|
|
||||||
|
filtersPerContentTopic, ok := fs.topicMatcher[pubsubTopic]
|
||||||
|
if !ok {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
for watcher := range filtersPerContentTopic[contentTopic] {
|
||||||
res = append(res, watcher)
|
res = append(res, watcher)
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
@ -166,10 +196,10 @@ func (fs *Filters) NotifyWatchers(recvMessage *ReceivedMessage) bool {
|
||||||
defer fs.mutex.RUnlock()
|
defer fs.mutex.RUnlock()
|
||||||
|
|
||||||
var matched bool
|
var matched bool
|
||||||
candidates := fs.GetWatchersByTopic(recvMessage.Topic)
|
candidates := fs.GetWatchersByTopic(recvMessage.PubsubTopic, recvMessage.ContentTopic)
|
||||||
|
|
||||||
if len(candidates) == 0 {
|
if len(candidates) == 0 {
|
||||||
log.Debug("no filters available for this topic", "message", recvMessage.Hash().Hex(), "topic", recvMessage.Topic.String())
|
log.Debug("no filters available for this topic", "message", recvMessage.Hash().Hex(), "pubsubTopic", recvMessage.PubsubTopic, "contentTopic", recvMessage.ContentTopic.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, watcher := range candidates {
|
for _, watcher := range candidates {
|
||||||
|
|
|
@ -45,10 +45,12 @@ type ReceivedMessage struct {
|
||||||
Padding []byte
|
Padding []byte
|
||||||
Signature []byte
|
Signature []byte
|
||||||
|
|
||||||
Sent uint32 // Time when the message was posted into the network
|
Sent uint32 // Time when the message was posted into the network
|
||||||
Src *ecdsa.PublicKey // Message recipient (identity used to decode the message)
|
Src *ecdsa.PublicKey // Message recipient (identity used to decode the message)
|
||||||
Dst *ecdsa.PublicKey // Message recipient (identity used to decode the message)
|
Dst *ecdsa.PublicKey // Message recipient (identity used to decode the message)
|
||||||
Topic TopicType
|
|
||||||
|
PubsubTopic string
|
||||||
|
ContentTopic TopicType
|
||||||
|
|
||||||
SymKeyHash common.Hash // The Keccak256Hash of the key
|
SymKeyHash common.Hash // The Keccak256Hash of the key
|
||||||
|
|
||||||
|
@ -159,10 +161,11 @@ func NewReceivedMessage(env *protocol.Envelope, msgType MessageType) *ReceivedMe
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ReceivedMessage{
|
return &ReceivedMessage{
|
||||||
Envelope: env,
|
Envelope: env,
|
||||||
MsgType: msgType,
|
MsgType: msgType,
|
||||||
Sent: uint32(env.Message().Timestamp / int64(time.Second)),
|
Sent: uint32(env.Message().Timestamp / int64(time.Second)),
|
||||||
Topic: ct,
|
ContentTopic: ct,
|
||||||
|
PubsubTopic: env.PubsubTopic(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +245,8 @@ func (msg *ReceivedMessage) Open(watcher *Filter) (result *ReceivedMessage) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Topic = ct
|
result.PubsubTopic = watcher.PubsubTopic
|
||||||
|
result.ContentTopic = ct
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
package persistence
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DBStore is a MessageProvider that has a *sql.DB connection
|
||||||
|
type ProtectedTopicsStore struct {
|
||||||
|
db *sql.DB
|
||||||
|
log *zap.Logger
|
||||||
|
|
||||||
|
insertStmt *sql.Stmt
|
||||||
|
fetchPrivKeyStmt *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a new DB store using the db specified via options.
|
||||||
|
// It will create a messages table if it does not exist and
|
||||||
|
// clean up records according to the retention policy used
|
||||||
|
func NewProtectedTopicsStore(log *zap.Logger, db *sql.DB) (*ProtectedTopicsStore, error) {
|
||||||
|
insertStmt, err := db.Prepare("INSERT OR REPLACE INTO pubsubtopic_signing_key (topic, priv_key, pub_key) VALUES (?, ?, ?)")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchPrivKeyStmt, err := db.Prepare("SELECT priv_key FROM pubsubtopic_signing_key WHERE topic = ?")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := new(ProtectedTopicsStore)
|
||||||
|
result.log = log.Named("protected-topics-store")
|
||||||
|
result.db = db
|
||||||
|
result.insertStmt = insertStmt
|
||||||
|
result.fetchPrivKeyStmt = fetchPrivKeyStmt
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ProtectedTopicsStore) Close() error {
|
||||||
|
err := p.insertStmt.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.fetchPrivKeyStmt.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ProtectedTopicsStore) Insert(pubsubTopic string, privKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey) error {
|
||||||
|
var privKeyBytes []byte
|
||||||
|
if privKey != nil {
|
||||||
|
privKeyBytes = crypto.FromECDSA(privKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKeyBytes := crypto.FromECDSAPub(publicKey)
|
||||||
|
|
||||||
|
_, err := p.insertStmt.Exec(pubsubTopic, privKeyBytes, pubKeyBytes)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ProtectedTopicsStore) FetchPrivateKey(topic string) (privKey *ecdsa.PrivateKey, err error) {
|
||||||
|
var privKeyBytes []byte
|
||||||
|
err = p.fetchPrivKeyStmt.QueryRow(topic).Scan(&privKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.ToECDSA(privKeyBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProtectedTopic struct {
|
||||||
|
PubKey *ecdsa.PublicKey
|
||||||
|
Topic string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ProtectedTopicsStore) ProtectedTopics() ([]ProtectedTopic, error) {
|
||||||
|
rows, err := p.db.Query("SELECT pub_key, topic FROM pubsubtopic_signing_key")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []ProtectedTopic
|
||||||
|
for rows.Next() {
|
||||||
|
var pubKeyBytes []byte
|
||||||
|
var topic string
|
||||||
|
err := rows.Scan(&pubKeyBytes, &topic)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pubk, err := crypto.UnmarshalPubkey(pubKeyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, ProtectedTopic{
|
||||||
|
PubKey: pubk,
|
||||||
|
Topic: topic,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
198
wakuv2/waku.go
198
wakuv2/waku.go
|
@ -118,8 +118,9 @@ type Waku struct {
|
||||||
|
|
||||||
bandwidthCounter *metrics.BandwidthCounter
|
bandwidthCounter *metrics.BandwidthCounter
|
||||||
|
|
||||||
sendQueue chan *protocol.Envelope
|
protectedTopicStore *persistence.ProtectedTopicsStore
|
||||||
msgQueue chan *common.ReceivedMessage // Message queue for waku messages that havent been decoded
|
sendQueue chan *protocol.Envelope
|
||||||
|
msgQueue chan *common.ReceivedMessage // Message queue for waku messages that havent been decoded
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
|
@ -305,6 +306,13 @@ func New(nodeKey string, fleet string, cfg *Config, logger *zap.Logger, appDB *s
|
||||||
opts = append(opts, node.WithMessageProvider(dbStore))
|
opts = append(opts, node.WithMessageProvider(dbStore))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if appDB != nil {
|
||||||
|
waku.protectedTopicStore, err = persistence.NewProtectedTopicsStore(logger, appDB)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
waku.settings.Options = opts
|
waku.settings.Options = opts
|
||||||
waku.logger.Info("setup the go-waku node successfully")
|
waku.logger.Info("setup the go-waku node successfully")
|
||||||
|
|
||||||
|
@ -602,34 +610,48 @@ func (w *Waku) runPeerExchangeLoop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Waku) runRelayMsgLoop() {
|
func (w *Waku) subscribeToPubsubTopicWithWakuRelay(topic string, pubkey *ecdsa.PublicKey) error {
|
||||||
defer w.wg.Done()
|
|
||||||
|
|
||||||
if w.settings.LightClient {
|
if w.settings.LightClient {
|
||||||
return
|
return errors.New("only available for full nodes")
|
||||||
}
|
}
|
||||||
|
|
||||||
sub, err := w.node.Relay().Subscribe(w.ctx)
|
if w.node.Relay().IsSubscribed(topic) {
|
||||||
if err != nil {
|
return nil
|
||||||
fmt.Println("Could not subscribe:", err)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
if pubkey != nil {
|
||||||
select {
|
err := w.node.Relay().AddSignedTopicValidator(topic, pubkey)
|
||||||
case <-w.ctx.Done():
|
if err != nil {
|
||||||
sub.Unsubscribe()
|
return err
|
||||||
return
|
|
||||||
case env := <-sub.Ch:
|
|
||||||
envelopeErrors, err := w.OnNewEnvelopes(env, common.RelayedMessageType)
|
|
||||||
if err != nil {
|
|
||||||
w.logger.Error("onNewEnvelope error", zap.Error(err))
|
|
||||||
}
|
|
||||||
// TODO: should these be handled?
|
|
||||||
_ = envelopeErrors
|
|
||||||
_ = err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub, err := w.node.Relay().SubscribeToTopic(context.Background(), topic)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer w.wg.Done()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-w.ctx.Done():
|
||||||
|
sub.Unsubscribe()
|
||||||
|
return
|
||||||
|
case env := <-sub.Ch:
|
||||||
|
envelopeErrors, err := w.OnNewEnvelopes(env, common.RelayedMessageType)
|
||||||
|
if err != nil {
|
||||||
|
w.logger.Error("onNewEnvelope error", zap.Error(err))
|
||||||
|
}
|
||||||
|
// TODO: should these be handled?
|
||||||
|
_ = envelopeErrors
|
||||||
|
_ = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Waku) runFilterSubscriptionLoop(sub *filter.SubscriptionDetails) {
|
func (w *Waku) runFilterSubscriptionLoop(sub *filter.SubscriptionDetails) {
|
||||||
|
@ -680,7 +702,7 @@ func (w *Waku) runFilterMsgLoop() {
|
||||||
err := w.isFilterSubAlive(sub)
|
err := w.isFilterSubAlive(sub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Unsubscribe on light node
|
// Unsubscribe on light node
|
||||||
contentFilter := w.buildContentFilter(f.Topics)
|
contentFilter := w.buildContentFilter(f.PubsubTopic, f.Topics)
|
||||||
// TODO Better return value handling for WakuFilterPushResult
|
// TODO Better return value handling for WakuFilterPushResult
|
||||||
_, err := w.node.FilterLightnode().Unsubscribe(w.ctx, contentFilter, filter.Peer(sub.PeerID))
|
_, err := w.node.FilterLightnode().Unsubscribe(w.ctx, contentFilter, filter.Peer(sub.PeerID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -712,9 +734,9 @@ func (w *Waku) runFilterMsgLoop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (w *Waku) buildContentFilter(topics [][]byte) filter.ContentFilter {
|
func (w *Waku) buildContentFilter(pubsubTopic string, topics [][]byte) filter.ContentFilter {
|
||||||
contentFilter := filter.ContentFilter{
|
contentFilter := filter.ContentFilter{
|
||||||
Topic: relay.DefaultWakuTopic,
|
Topic: pubsubTopic,
|
||||||
}
|
}
|
||||||
for _, topic := range topics {
|
for _, topic := range topics {
|
||||||
contentFilter.ContentTopics = append(contentFilter.ContentTopics, common.BytesToTopic(topic).ContentTopic())
|
contentFilter.ContentTopics = append(contentFilter.ContentTopics, common.BytesToTopic(topic).ContentTopic())
|
||||||
|
@ -1008,6 +1030,9 @@ func (w *Waku) GetSymKey(id string) ([]byte, error) {
|
||||||
// Subscribe installs a new message handler used for filtering, decrypting
|
// Subscribe installs a new message handler used for filtering, decrypting
|
||||||
// and subsequent storing of incoming messages.
|
// and subsequent storing of incoming messages.
|
||||||
func (w *Waku) Subscribe(f *common.Filter) (string, error) {
|
func (w *Waku) Subscribe(f *common.Filter) (string, error) {
|
||||||
|
if f.PubsubTopic == "" {
|
||||||
|
f.PubsubTopic = relay.DefaultWakuTopic
|
||||||
|
}
|
||||||
|
|
||||||
s, err := w.filters.Install(f)
|
s, err := w.filters.Install(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1033,7 +1058,7 @@ func (w *Waku) Subscribe(f *common.Filter) (string, error) {
|
||||||
func (w *Waku) Unsubscribe(ctx context.Context, id string) error {
|
func (w *Waku) Unsubscribe(ctx context.Context, id string) error {
|
||||||
f := w.filters.Get(id)
|
f := w.filters.Get(id)
|
||||||
if f != nil && w.settings.LightClient {
|
if f != nil && w.settings.LightClient {
|
||||||
contentFilter := w.buildContentFilter(f.Topics)
|
contentFilter := w.buildContentFilter(f.PubsubTopic, f.Topics)
|
||||||
if _, err := w.node.FilterLightnode().Unsubscribe(ctx, contentFilter); err != nil {
|
if _, err := w.node.FilterLightnode().Unsubscribe(ctx, contentFilter); err != nil {
|
||||||
return fmt.Errorf("failed to unsubscribe: %w", err)
|
return fmt.Errorf("failed to unsubscribe: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -1069,15 +1094,15 @@ func (w *Waku) broadcast() {
|
||||||
case envelope := <-w.sendQueue:
|
case envelope := <-w.sendQueue:
|
||||||
var err error
|
var err error
|
||||||
if w.settings.LightClient {
|
if w.settings.LightClient {
|
||||||
w.logger.Info("publishing message via lightpush", zap.String("envelopeHash", hexutil.Encode(envelope.Hash())))
|
w.logger.Info("publishing message via lightpush", zap.String("envelopeHash", hexutil.Encode(envelope.Hash())), zap.String("pubsubTopic", envelope.PubsubTopic()))
|
||||||
_, err = w.node.Lightpush().Publish(w.ctx, envelope.Message())
|
_, err = w.node.Lightpush().PublishToTopic(context.Background(), envelope.Message(), envelope.PubsubTopic())
|
||||||
} else {
|
} else {
|
||||||
w.logger.Info("publishing message via relay", zap.String("envelopeHash", hexutil.Encode(envelope.Hash())))
|
w.logger.Info("publishing message via relay", zap.String("envelopeHash", hexutil.Encode(envelope.Hash())), zap.String("pubsubTopic", envelope.PubsubTopic()))
|
||||||
_, err = w.node.Relay().Publish(w.ctx, envelope.Message())
|
_, err = w.node.Relay().PublishToTopic(context.Background(), envelope.Message(), envelope.PubsubTopic())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.logger.Error("could not send message", zap.String("envelopeHash", hexutil.Encode(envelope.Hash())), zap.Error(err))
|
w.logger.Error("could not send message", zap.String("envelopeHash", hexutil.Encode(envelope.Hash())), zap.String("pubsubTopic", envelope.PubsubTopic()), zap.Error(err))
|
||||||
w.envelopeFeed.Send(common.EnvelopeEvent{
|
w.envelopeFeed.Send(common.EnvelopeEvent{
|
||||||
Hash: gethcommon.BytesToHash(envelope.Hash()),
|
Hash: gethcommon.BytesToHash(envelope.Hash()),
|
||||||
Event: common.EventEnvelopeExpired,
|
Event: common.EventEnvelopeExpired,
|
||||||
|
@ -1101,8 +1126,26 @@ func (w *Waku) broadcast() {
|
||||||
|
|
||||||
// Send injects a message into the waku send queue, to be distributed in the
|
// Send injects a message into the waku send queue, to be distributed in the
|
||||||
// network in the coming cycles.
|
// network in the coming cycles.
|
||||||
func (w *Waku) Send(msg *pb.WakuMessage) ([]byte, error) {
|
func (w *Waku) Send(pubsubTopic string, msg *pb.WakuMessage) ([]byte, error) {
|
||||||
envelope := protocol.NewEnvelope(msg, msg.Timestamp, relay.DefaultWakuTopic) // TODO: once sharding is defined, use the correct pubsub topic
|
if pubsubTopic == "" {
|
||||||
|
pubsubTopic = relay.DefaultWakuTopic
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.protectedTopicStore != nil {
|
||||||
|
privKey, err := w.protectedTopicStore.FetchPrivateKey(pubsubTopic)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if privKey != nil {
|
||||||
|
err = relay.SignMessage(privKey, msg, pubsubTopic)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
envelope := protocol.NewEnvelope(msg, msg.Timestamp, pubsubTopic)
|
||||||
|
|
||||||
w.sendQueue <- envelope
|
w.sendQueue <- envelope
|
||||||
|
|
||||||
|
@ -1118,7 +1161,7 @@ func (w *Waku) Send(msg *pb.WakuMessage) ([]byte, error) {
|
||||||
return envelope.Hash(), nil
|
return envelope.Hash(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Waku) query(ctx context.Context, peerID peer.ID, topics []common.TopicType, from uint64, to uint64, opts []store.HistoryRequestOption) (*store.Result, error) {
|
func (w *Waku) query(ctx context.Context, peerID peer.ID, pubsubTopic string, topics []common.TopicType, from uint64, to uint64, opts []store.HistoryRequestOption) (*store.Result, error) {
|
||||||
strTopics := make([]string, len(topics))
|
strTopics := make([]string, len(topics))
|
||||||
for i, t := range topics {
|
for i, t := range topics {
|
||||||
strTopics[i] = t.ContentTopic()
|
strTopics[i] = t.ContentTopic()
|
||||||
|
@ -1130,16 +1173,16 @@ func (w *Waku) query(ctx context.Context, peerID peer.ID, topics []common.TopicT
|
||||||
StartTime: int64(from) * int64(time.Second),
|
StartTime: int64(from) * int64(time.Second),
|
||||||
EndTime: int64(to) * int64(time.Second),
|
EndTime: int64(to) * int64(time.Second),
|
||||||
ContentTopics: strTopics,
|
ContentTopics: strTopics,
|
||||||
Topic: relay.DefaultWakuTopic,
|
Topic: pubsubTopic,
|
||||||
}
|
}
|
||||||
|
|
||||||
return w.node.Store().Query(ctx, query, opts...)
|
return w.node.Store().Query(ctx, query, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Waku) Query(ctx context.Context, peerID peer.ID, topics []common.TopicType, from uint64, to uint64, opts []store.HistoryRequestOption) (cursor *storepb.Index, err error) {
|
func (w *Waku) Query(ctx context.Context, peerID peer.ID, pubsubTopic string, topics []common.TopicType, from uint64, to uint64, opts []store.HistoryRequestOption) (cursor *storepb.Index, err error) {
|
||||||
requestID := protocol.GenerateRequestId()
|
requestID := protocol.GenerateRequestId()
|
||||||
opts = append(opts, store.WithRequestId(requestID))
|
opts = append(opts, store.WithRequestId(requestID))
|
||||||
result, err := w.query(ctx, peerID, topics, from, to, opts)
|
result, err := w.query(ctx, peerID, pubsubTopic, topics, from, to, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.logger.Error("error querying storenode", zap.String("requestID", hexutil.Encode(requestID)), zap.String("peerID", peerID.String()), zap.Error(err))
|
w.logger.Error("error querying storenode", zap.String("requestID", hexutil.Encode(requestID)), zap.String("peerID", peerID.String()), zap.Error(err))
|
||||||
if w.onHistoricMessagesRequestFailed != nil {
|
if w.onHistoricMessagesRequestFailed != nil {
|
||||||
|
@ -1153,8 +1196,8 @@ func (w *Waku) Query(ctx context.Context, peerID peer.ID, topics []common.TopicT
|
||||||
// See https://github.com/vacp2p/rfc/issues/563
|
// See https://github.com/vacp2p/rfc/issues/563
|
||||||
msg.RateLimitProof = nil
|
msg.RateLimitProof = nil
|
||||||
|
|
||||||
envelope := protocol.NewEnvelope(msg, msg.Timestamp, relay.DefaultWakuTopic)
|
envelope := protocol.NewEnvelope(msg, msg.Timestamp, pubsubTopic)
|
||||||
w.logger.Info("received waku2 store message", zap.Any("envelopeHash", hexutil.Encode(envelope.Hash())))
|
w.logger.Info("received waku2 store message", zap.Any("envelopeHash", hexutil.Encode(envelope.Hash())), zap.String("pubsubTopic", pubsubTopic))
|
||||||
_, err = w.OnNewEnvelopes(envelope, common.StoreMessageType)
|
_, err = w.OnNewEnvelopes(envelope, common.StoreMessageType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -1205,7 +1248,7 @@ func (w *Waku) Start() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
w.wg.Add(4)
|
w.wg.Add(3)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer w.wg.Done()
|
defer w.wg.Done()
|
||||||
|
@ -1252,9 +1295,13 @@ func (w *Waku) Start() error {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go w.telemetryBandwidthStats(w.cfg.TelemetryServerURL)
|
go w.telemetryBandwidthStats(w.cfg.TelemetryServerURL)
|
||||||
go w.runFilterMsgLoop()
|
|
||||||
go w.runRelayMsgLoop()
|
|
||||||
go w.runPeerExchangeLoop()
|
go w.runPeerExchangeLoop()
|
||||||
|
go w.runFilterMsgLoop()
|
||||||
|
|
||||||
|
err = w.setupRelaySubscriptions()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
numCPU := runtime.NumCPU()
|
numCPU := runtime.NumCPU()
|
||||||
for i := 0; i < numCPU; i++ {
|
for i := 0; i < numCPU; i++ {
|
||||||
|
@ -1267,12 +1314,54 @@ func (w *Waku) Start() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Waku) setupRelaySubscriptions() error {
|
||||||
|
if w.settings.LightClient {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.protectedTopicStore != nil {
|
||||||
|
protectedTopics, err := w.protectedTopicStore.ProtectedTopics()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pt := range protectedTopics {
|
||||||
|
// Adding subscription to protected topics
|
||||||
|
err = w.subscribeToPubsubTopicWithWakuRelay(pt.Topic, pt.PubKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default Waku Topic (TODO: remove once sharding is added)
|
||||||
|
err := w.subscribeToPubsubTopicWithWakuRelay(relay.DefaultWakuTopic, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Stop implements node.Service, stopping the background data propagation thread
|
// Stop implements node.Service, stopping the background data propagation thread
|
||||||
// of the Waku protocol.
|
// of the Waku protocol.
|
||||||
func (w *Waku) Stop() error {
|
func (w *Waku) Stop() error {
|
||||||
w.cancel()
|
w.cancel()
|
||||||
w.identifyService.Close()
|
|
||||||
|
err := w.identifyService.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
w.node.Stop()
|
w.node.Stop()
|
||||||
|
|
||||||
|
if w.protectedTopicStore != nil {
|
||||||
|
err = w.protectedTopicStore.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
close(w.connectionChanged)
|
close(w.connectionChanged)
|
||||||
w.wg.Wait()
|
w.wg.Wait()
|
||||||
return nil
|
return nil
|
||||||
|
@ -1293,7 +1382,6 @@ func (w *Waku) OnNewEnvelopes(envelope *protocol.Envelope, msgType common.Messag
|
||||||
logger := w.logger.With(zap.String("hash", recvMessage.Hash().Hex()))
|
logger := w.logger.With(zap.String("hash", recvMessage.Hash().Hex()))
|
||||||
|
|
||||||
logger.Debug("received new envelope")
|
logger.Debug("received new envelope")
|
||||||
|
|
||||||
trouble := false
|
trouble := false
|
||||||
|
|
||||||
_, err := w.add(recvMessage)
|
_, err := w.add(recvMessage)
|
||||||
|
@ -1368,14 +1456,14 @@ func (w *Waku) processQueue() {
|
||||||
|
|
||||||
// If not matched we remove it
|
// If not matched we remove it
|
||||||
if !matched {
|
if !matched {
|
||||||
w.logger.Debug("filters did not match", zap.String("hash", e.Hash().String()), zap.String("contentTopic", e.Topic.ContentTopic()))
|
w.logger.Debug("filters did not match", zap.String("hash", e.Hash().String()), zap.String("contentTopic", e.ContentTopic.ContentTopic()))
|
||||||
w.storeMsgIDsMu.Lock()
|
w.storeMsgIDsMu.Lock()
|
||||||
delete(w.storeMsgIDs, e.Hash())
|
delete(w.storeMsgIDs, e.Hash())
|
||||||
w.storeMsgIDsMu.Unlock()
|
w.storeMsgIDsMu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
w.envelopeFeed.Send(common.EnvelopeEvent{
|
w.envelopeFeed.Send(common.EnvelopeEvent{
|
||||||
Topic: e.Topic,
|
Topic: e.ContentTopic,
|
||||||
Hash: e.Hash(),
|
Hash: e.Hash(),
|
||||||
Event: common.EventEnvelopeAvailable,
|
Event: common.EventEnvelopeAvailable,
|
||||||
})
|
})
|
||||||
|
@ -1429,6 +1517,20 @@ func (w *Waku) ListenAddresses() []string {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Waku) SubscribeToPubsubTopic(topic string, pubkey *ecdsa.PublicKey) error {
|
||||||
|
if !w.settings.LightClient {
|
||||||
|
err := w.subscribeToPubsubTopicWithWakuRelay(topic, pubkey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Waku) StorePubsubTopicKey(topic string, privKey *ecdsa.PrivateKey) error {
|
||||||
|
return w.protectedTopicStore.Insert(topic, privKey, &privKey.PublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Waku) StartDiscV5() error {
|
func (w *Waku) StartDiscV5() error {
|
||||||
if w.node.DiscV5() == nil {
|
if w.node.DiscV5() == nil {
|
||||||
return errors.New("discv5 is not setup")
|
return errors.New("discv5 is not setup")
|
||||||
|
@ -1727,7 +1829,7 @@ func (w *Waku) subscribeToFilter(f *common.Filter) error {
|
||||||
peers := w.findFilterPeers()
|
peers := w.findFilterPeers()
|
||||||
|
|
||||||
if len(peers) > 0 {
|
if len(peers) > 0 {
|
||||||
contentFilter := w.buildContentFilter(f.Topics)
|
contentFilter := w.buildContentFilter(f.PubsubTopic, f.Topics)
|
||||||
for i := 0; i < len(peers) && i < w.settings.MinPeersForFilter; i++ {
|
for i := 0; i < len(peers) && i < w.settings.MinPeersForFilter; i++ {
|
||||||
subDetails, err := w.node.FilterLightnode().Subscribe(w.ctx, contentFilter, filter.WithPeer(peers[i]))
|
subDetails, err := w.node.FilterLightnode().Subscribe(w.ctx, contentFilter, filter.WithPeer(peers[i]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/waku-org/go-waku/waku/v2/dnsdisc"
|
"github.com/waku-org/go-waku/waku/v2/dnsdisc"
|
||||||
waku_filter "github.com/waku-org/go-waku/waku/v2/protocol/filter"
|
waku_filter "github.com/waku-org/go-waku/waku/v2/protocol/filter"
|
||||||
"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/store"
|
"github.com/waku-org/go-waku/waku/v2/protocol/store"
|
||||||
|
|
||||||
"github.com/status-im/status-go/protocol/tt"
|
"github.com/status-im/status-go/protocol/tt"
|
||||||
|
@ -143,7 +144,7 @@ func TestBasicWakuV2(t *testing.T) {
|
||||||
msgTimestamp := w.timestamp()
|
msgTimestamp := w.timestamp()
|
||||||
contentTopic := common.BytesToTopic(filter.Topics[0])
|
contentTopic := common.BytesToTopic(filter.Topics[0])
|
||||||
|
|
||||||
_, err = w.Send(&pb.WakuMessage{
|
_, err = w.Send(relay.DefaultWakuTopic, &pb.WakuMessage{
|
||||||
Payload: []byte{1, 2, 3, 4, 5},
|
Payload: []byte{1, 2, 3, 4, 5},
|
||||||
ContentTopic: contentTopic.ContentTopic(),
|
ContentTopic: contentTopic.ContentTopic(),
|
||||||
Version: 0,
|
Version: 0,
|
||||||
|
@ -157,7 +158,7 @@ func TestBasicWakuV2(t *testing.T) {
|
||||||
require.Len(t, messages, 1)
|
require.Len(t, messages, 1)
|
||||||
|
|
||||||
timestampInSeconds := msgTimestamp / int64(time.Second)
|
timestampInSeconds := msgTimestamp / int64(time.Second)
|
||||||
storeResult, err := w.query(context.Background(), storeNode.PeerID, []common.TopicType{contentTopic}, uint64(timestampInSeconds-20), uint64(timestampInSeconds+20), []store.HistoryRequestOption{})
|
storeResult, err := w.query(context.Background(), storeNode.PeerID, relay.DefaultWakuTopic, []common.TopicType{contentTopic}, uint64(timestampInSeconds-20), uint64(timestampInSeconds+20), []store.HistoryRequestOption{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotZero(t, len(storeResult.Messages))
|
require.NotZero(t, len(storeResult.Messages))
|
||||||
|
|
||||||
|
@ -206,7 +207,7 @@ func TestWakuV2Filter(t *testing.T) {
|
||||||
msgTimestamp := w.timestamp()
|
msgTimestamp := w.timestamp()
|
||||||
contentTopic := common.BytesToTopic(filter.Topics[0])
|
contentTopic := common.BytesToTopic(filter.Topics[0])
|
||||||
|
|
||||||
_, err = w.Send(&pb.WakuMessage{
|
_, err = w.Send(relay.DefaultWakuTopic, &pb.WakuMessage{
|
||||||
Payload: []byte{1, 2, 3, 4, 5},
|
Payload: []byte{1, 2, 3, 4, 5},
|
||||||
ContentTopic: contentTopic.ContentTopic(),
|
ContentTopic: contentTopic.ContentTopic(),
|
||||||
Version: 0,
|
Version: 0,
|
||||||
|
|
Loading…
Reference in New Issue