From 04833f559fd442c4efa103cb9bcdfba993165ce0 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Fri, 23 Aug 2024 09:42:01 -0400 Subject: [PATCH] refactor_: remove mailservers code --- appdatabase/node_config_test.go | 113 +-- cmd/statusd/main.go | 16 +- config/cli/mailserver-enabled.json | 8 - eth-node/bridge/geth/envelope_event.go | 3 - eth-node/bridge/geth/mailserver_response.go | 19 - eth-node/types/mailserver.go | 29 - eth-node/types/whisper.go | 3 - mailserver/README.md | 43 - mailserver/cleaner.go | 85 -- mailserver/cleaner_test.go | 160 ---- mailserver/db_key.go | 53 -- mailserver/db_key_test.go | 20 - mailserver/limiter.go | 88 -- mailserver/limiter_test.go | 109 --- mailserver/mailserver.go | 948 ------------------- mailserver/mailserver_db.go | 42 - mailserver/mailserver_db_leveldb.go | 242 ----- mailserver/mailserver_db_leveldb_test.go | 47 - mailserver/mailserver_db_postgres.go | 309 ------ mailserver/mailserver_db_postgres_test.go | 129 --- mailserver/mailserver_test.go | 655 ------------- mailserver/metrics.go | 84 -- mailserver/migrations/bindata.go | 319 ------- mailserver/request.go | 51 - node/get_status_node.go | 1 - node/status_node_services.go | 20 +- nodecfg/node_config.go | 36 +- params/cluster.go | 1 - params/config.go | 37 - params/config_test.go | 3 - peers/cotopicpool.go | 3 +- peers/peerpool.go | 13 +- peers/peerpool_test.go | 11 +- protocol/messenger.go | 18 - protocol/messenger_builder_test.go | 1 - protocol/messenger_messages_tracking_test.go | 11 - protocol/transport/envelopes_monitor.go | 72 +- protocol/transport/envelopes_monitor_test.go | 1 - services/ext/api.go | 162 +--- services/ext/api_test.go | 119 --- services/ext/mailrequests.go | 136 --- services/ext/mailrequests_test.go | 85 -- services/ext/mailservers/cache.go | 145 --- services/ext/mailservers/cache_test.go | 81 -- services/ext/mailservers/connmanager.go | 271 ------ services/ext/mailservers/connmanager_test.go | 400 -------- services/ext/mailservers/connmonitor.go | 85 -- services/ext/mailservers/connmonitor_test.go | 81 -- services/ext/mailservers/peerstore.go | 63 -- services/ext/mailservers/peerstore_test.go | 59 -- services/ext/mailservers/utils.go | 54 -- services/ext/mailservers/utils_test.go | 58 -- services/ext/service.go | 30 +- services/ext/signal.go | 10 - services/wakuext/api.go | 2 +- services/wakuext/service.go | 8 +- services/wakuv2ext/api.go | 2 +- services/wakuv2ext/service.go | 8 +- signal/events_shhext.go | 26 - waku/common/events.go | 17 - waku/common/protocol.go | 13 - waku/mailserver.go | 179 ---- waku/v0/peer.go | 60 -- waku/v1/peer.go | 60 -- waku/waku.go | 48 - waku/waku_version_test.go | 72 -- 66 files changed, 126 insertions(+), 6011 deletions(-) delete mode 100644 config/cli/mailserver-enabled.json delete mode 100644 eth-node/bridge/geth/mailserver_response.go delete mode 100644 mailserver/README.md delete mode 100644 mailserver/cleaner.go delete mode 100644 mailserver/cleaner_test.go delete mode 100644 mailserver/db_key.go delete mode 100644 mailserver/db_key_test.go delete mode 100644 mailserver/limiter.go delete mode 100644 mailserver/limiter_test.go delete mode 100644 mailserver/mailserver.go delete mode 100644 mailserver/mailserver_db.go delete mode 100644 mailserver/mailserver_db_leveldb.go delete mode 100644 mailserver/mailserver_db_leveldb_test.go delete mode 100644 mailserver/mailserver_db_postgres.go delete mode 100644 mailserver/mailserver_db_postgres_test.go delete mode 100644 mailserver/mailserver_test.go delete mode 100644 mailserver/metrics.go delete mode 100644 mailserver/migrations/bindata.go delete mode 100644 mailserver/request.go delete mode 100644 services/ext/mailrequests.go delete mode 100644 services/ext/mailrequests_test.go delete mode 100644 services/ext/mailservers/cache.go delete mode 100644 services/ext/mailservers/cache_test.go delete mode 100644 services/ext/mailservers/connmanager.go delete mode 100644 services/ext/mailservers/connmanager_test.go delete mode 100644 services/ext/mailservers/connmonitor.go delete mode 100644 services/ext/mailservers/connmonitor_test.go delete mode 100644 services/ext/mailservers/peerstore.go delete mode 100644 services/ext/mailservers/peerstore_test.go delete mode 100644 services/ext/mailservers/utils.go delete mode 100644 services/ext/mailservers/utils_test.go delete mode 100644 waku/mailserver.go diff --git a/appdatabase/node_config_test.go b/appdatabase/node_config_test.go index 75d8dffac..bedb2b4aa 100644 --- a/appdatabase/node_config_test.go +++ b/appdatabase/node_config_test.go @@ -124,51 +124,50 @@ func randomNodeConfig() *params.NodeConfig { privK, _ := crypto.GenerateKey() return ¶ms.NodeConfig{ - NetworkID: uint64(int64(randomInt(math.MaxInt64))), - DataDir: randomString(), - KeyStoreDir: randomString(), - NodeKey: randomString(), - NoDiscovery: randomBool(), - Rendezvous: randomBool(), - ListenAddr: randomString(), - AdvertiseAddr: randomString(), - Name: randomString(), - Version: randomString(), - APIModules: randomString(), - TLSEnabled: randomBool(), - MaxPeers: randomInt(math.MaxInt64), - MaxPendingPeers: randomInt(math.MaxInt64), - EnableStatusService: randomBool(), - BridgeConfig: params.BridgeConfig{Enabled: randomBool()}, - WalletConfig: params.WalletConfig{Enabled: randomBool()}, - LocalNotificationsConfig: params.LocalNotificationsConfig{Enabled: randomBool()}, - BrowsersConfig: params.BrowsersConfig{Enabled: randomBool()}, - PermissionsConfig: params.PermissionsConfig{Enabled: randomBool()}, - MailserversConfig: params.MailserversConfig{Enabled: randomBool()}, - Web3ProviderConfig: params.Web3ProviderConfig{Enabled: randomBool()}, - ConnectorConfig: params.ConnectorConfig{Enabled: randomBool()}, - SwarmConfig: params.SwarmConfig{Enabled: randomBool()}, - MailServerRegistryAddress: randomString(), - HTTPEnabled: randomBool(), - HTTPHost: randomString(), - HTTPPort: randomInt(math.MaxInt64), - HTTPVirtualHosts: randomStringSlice(), - HTTPCors: randomStringSlice(), - WSEnabled: false, // NOTE: leaving ws field idle since we are moving away from the storing the whole config - WSHost: "", - WSPort: 0, - IPCEnabled: randomBool(), - IPCFile: randomString(), - LogEnabled: randomBool(), - LogMobileSystem: randomBool(), - LogDir: randomString(), - LogFile: randomString(), - LogLevel: randomString(), - LogMaxBackups: randomInt(math.MaxInt64), - LogMaxSize: randomInt(math.MaxInt64), - LogCompressRotated: randomBool(), - LogToStderr: randomBool(), - UpstreamConfig: params.UpstreamRPCConfig{Enabled: randomBool(), URL: randomString()}, + NetworkID: uint64(int64(randomInt(math.MaxInt64))), + DataDir: randomString(), + KeyStoreDir: randomString(), + NodeKey: randomString(), + NoDiscovery: randomBool(), + Rendezvous: randomBool(), + ListenAddr: randomString(), + AdvertiseAddr: randomString(), + Name: randomString(), + Version: randomString(), + APIModules: randomString(), + TLSEnabled: randomBool(), + MaxPeers: randomInt(math.MaxInt64), + MaxPendingPeers: randomInt(math.MaxInt64), + EnableStatusService: randomBool(), + BridgeConfig: params.BridgeConfig{Enabled: randomBool()}, + WalletConfig: params.WalletConfig{Enabled: randomBool()}, + LocalNotificationsConfig: params.LocalNotificationsConfig{Enabled: randomBool()}, + BrowsersConfig: params.BrowsersConfig{Enabled: randomBool()}, + PermissionsConfig: params.PermissionsConfig{Enabled: randomBool()}, + MailserversConfig: params.MailserversConfig{Enabled: randomBool()}, + Web3ProviderConfig: params.Web3ProviderConfig{Enabled: randomBool()}, + ConnectorConfig: params.ConnectorConfig{Enabled: randomBool()}, + SwarmConfig: params.SwarmConfig{Enabled: randomBool()}, + HTTPEnabled: randomBool(), + HTTPHost: randomString(), + HTTPPort: randomInt(math.MaxInt64), + HTTPVirtualHosts: randomStringSlice(), + HTTPCors: randomStringSlice(), + WSEnabled: false, // NOTE: leaving ws field idle since we are moving away from the storing the whole config + WSHost: "", + WSPort: 0, + IPCEnabled: randomBool(), + IPCFile: randomString(), + LogEnabled: randomBool(), + LogMobileSystem: randomBool(), + LogDir: randomString(), + LogFile: randomString(), + LogLevel: randomString(), + LogMaxBackups: randomInt(math.MaxInt64), + LogMaxSize: randomInt(math.MaxInt64), + LogCompressRotated: randomBool(), + LogToStderr: randomBool(), + UpstreamConfig: params.UpstreamRPCConfig{Enabled: randomBool(), URL: randomString()}, ClusterConfig: params.ClusterConfig{ Enabled: randomBool(), Fleet: randomString(), @@ -227,23 +226,13 @@ func randomNodeConfig() *params.NodeConfig { AutoUpdate: randomBool(), }, WakuConfig: params.WakuConfig{ - Enabled: randomBool(), - LightClient: randomBool(), - FullNode: randomBool(), - EnableMailServer: randomBool(), - DataDir: randomString(), - MinimumPoW: randomFloat(math.MaxInt64), - MailServerPassword: randomString(), - MailServerRateLimit: randomInt(math.MaxInt64), - MailServerDataRetention: randomInt(math.MaxInt64), - TTL: randomInt(math.MaxInt64), - MaxMessageSize: uint32(randomInt(math.MaxInt64)), - DatabaseConfig: params.DatabaseConfig{ - PGConfig: params.PGConfig{ - Enabled: randomBool(), - URI: randomString(), - }, - }, + Enabled: randomBool(), + LightClient: randomBool(), + FullNode: randomBool(), + DataDir: randomString(), + MinimumPoW: randomFloat(math.MaxInt64), + TTL: randomInt(math.MaxInt64), + MaxMessageSize: uint32(randomInt(math.MaxInt64)), EnableRateLimiter: randomBool(), PacketRateLimitIP: int64(randomInt(math.MaxInt64)), PacketRateLimitPeerID: int64(randomInt(math.MaxInt64)), diff --git a/cmd/statusd/main.go b/cmd/statusd/main.go index 6bc248dd4..99cd07243 100644 --- a/cmd/statusd/main.go +++ b/cmd/statusd/main.go @@ -54,10 +54,9 @@ var ( torrentClientPort = flag.Int("torrent-client-port", 9025, "Port for BitTorrent protocol connections") version = flag.Bool("version", false, "Print version and dump configuration") - dataDir = flag.String("dir", getDefaultDataDir(), "Directory used by node to store data") - register = flag.Bool("register", false, "Register and make the node discoverable by other nodes") - mailserver = flag.Bool("mailserver", false, "Enable Mail Server with default configuration") - networkID = flag.Int( + dataDir = flag.String("dir", getDefaultDataDir(), "Directory used by node to store data") + register = flag.Bool("register", false, "Register and make the node discoverable by other nodes") + networkID = flag.Int( "network-id", params.GoerliNetworkID, fmt.Sprintf( @@ -118,9 +117,6 @@ func main() { } opts := []params.Option{params.WithFleet(*fleet)} - if *mailserver { - opts = append(opts, params.WithMailserver()) - } config, err := params.NewNodeConfigWithDefaultsAndFiles( *dataDir, @@ -140,9 +136,7 @@ func main() { config.ListenAddr = *listenAddr } - if *register && *mailserver { - config.RegisterTopics = append(config.RegisterTopics, params.MailServerDiscv5Topic) - } else if *register { + if *register { config.RegisterTopics = append(config.RegisterTopics, params.WhisperDiscv5Topic) } @@ -251,7 +245,6 @@ func main() { identity, gethbridge.NewNodeBridge(backend.StatusNode().GethNode(), backend.StatusNode().WakuService(), backend.StatusNode().WakuV2Service()), installationID.String(), - nil, config.Version, options..., ) @@ -324,7 +317,6 @@ func main() { identity, gethbridge.NewNodeBridge(backend.StatusNode().GethNode(), backend.StatusNode().WakuService(), backend.StatusNode().WakuV2Service()), installationID.String(), - nil, config.Version, options..., ) diff --git a/config/cli/mailserver-enabled.json b/config/cli/mailserver-enabled.json deleted file mode 100644 index b572bcb43..000000000 --- a/config/cli/mailserver-enabled.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "WhisperConfig": { - "Enabled": true, - "EnableNTPSync": true, - "EnableMailServer": true, - "MailServerPassword": "status-offline-inbox" - } -} diff --git a/eth-node/bridge/geth/envelope_event.go b/eth-node/bridge/geth/envelope_event.go index cb879bb35..cb255f26f 100644 --- a/eth-node/bridge/geth/envelope_event.go +++ b/eth-node/bridge/geth/envelope_event.go @@ -2,7 +2,6 @@ package gethbridge import ( "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/waku" wakucommon "github.com/status-im/status-go/waku/common" wakuv2common "github.com/status-im/status-go/wakuv2/common" @@ -21,8 +20,6 @@ func NewWakuEnvelopeEventWrapper(envelopeEvent *wakucommon.EnvelopeEvent) *types for index := range data { wrappedData[index] = *NewWakuEnvelopeErrorWrapper(&data[index]) } - case *waku.MailServerResponse: - wrappedData = NewWakuMailServerResponseWrapper(data) } return &types.EnvelopeEvent{ Event: types.EventType(envelopeEvent.Event), diff --git a/eth-node/bridge/geth/mailserver_response.go b/eth-node/bridge/geth/mailserver_response.go deleted file mode 100644 index e875dbf38..000000000 --- a/eth-node/bridge/geth/mailserver_response.go +++ /dev/null @@ -1,19 +0,0 @@ -package gethbridge - -import ( - "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/waku" -) - -// NewWakuMailServerResponseWrapper returns a types.MailServerResponse object that mimics Geth's MailServerResponse -func NewWakuMailServerResponseWrapper(mailServerResponse *waku.MailServerResponse) *types.MailServerResponse { - if mailServerResponse == nil { - panic("mailServerResponse should not be nil") - } - - return &types.MailServerResponse{ - LastEnvelopeHash: types.Hash(mailServerResponse.LastEnvelopeHash), - Cursor: mailServerResponse.Cursor, - Error: mailServerResponse.Error, - } -} diff --git a/eth-node/types/mailserver.go b/eth-node/types/mailserver.go index 2ae7877ce..676298f8f 100644 --- a/eth-node/types/mailserver.go +++ b/eth-node/types/mailserver.go @@ -53,32 +53,3 @@ func (r *MessagesRequest) SetDefaults(now time.Time) { } } } - -// MailServerResponse is the response payload sent by the mailserver. -type MailServerResponse struct { - LastEnvelopeHash Hash - Cursor []byte - Error error -} - -// SyncMailRequest contains details which envelopes should be synced -// between Mail Servers. -type SyncMailRequest struct { - // Lower is a lower bound of time range for which messages are requested. - Lower uint32 - // Upper is a lower bound of time range for which messages are requested. - Upper uint32 - // Bloom is a bloom filter to filter envelopes. - Bloom []byte - // Limit is the max number of envelopes to return. - Limit uint32 - // Cursor is used for pagination of the results. - Cursor []byte -} - -// SyncEventResponse is a response from the Mail Server -// form which the peer received envelopes. -type SyncEventResponse struct { - Cursor []byte - Error string -} diff --git a/eth-node/types/whisper.go b/eth-node/types/whisper.go index b4d69651e..18013d0ff 100644 --- a/eth-node/types/whisper.go +++ b/eth-node/types/whisper.go @@ -43,7 +43,4 @@ type Whisper interface { GetFilter(id string) Filter Unsubscribe(id string) error UnsubscribeMany(ids []string) error - - // SyncMessages can be sent between two Mail Servers and syncs envelopes between them. - SyncMessages(peerID []byte, req SyncMailRequest) error } diff --git a/mailserver/README.md b/mailserver/README.md deleted file mode 100644 index 9b589163f..000000000 --- a/mailserver/README.md +++ /dev/null @@ -1,43 +0,0 @@ -MailServer -========== - -This document is meant to collect various information about our MailServer implementation. - -## Syncing between mail servers - -It might happen that one mail server is behind other due to various reasons like a machine being down for a few minutes etc. - -There is an option to fix such a mail server: -1. SSH to a machine where this broken mail server runs, -2. Add a mail server from which you want to sync: -``` -# sudo might be not needed in your setup -$ echo '{"jsonrpc":"2.0","method":"admin_addPeer", "params": ["enode://c42f368a23fa98ee546fd247220759062323249ef657d26d357a777443aec04db1b29a3a22ef3e7c548e18493ddaf51a31b0aed6079bd6ebe5ae838fcfaf3a49@206.189.243.162:30504"], "id":1}' | \ - sudo socat -d -d - UNIX-CONNECT:/docker/statusd-mail/data/geth.ipc -``` -3. Mark it as a trusted peer: -``` -# sudo might be not needed in your setup -$ echo '{"jsonrpc":"2.0","method":"shh_markTrustedPeer", "params": ["enode://c42f368a23fa98ee546fd247220759062323249ef657d26d357a777443aec04db1b29a3a22ef3e7c548e18493ddaf51a31b0aed6079bd6ebe5ae838fcfaf3a49@206.189.243.162:30504"], "id":1}' | \ - sudo socat -d -d - UNIX-CONNECT:/docker/statusd-mail/data/geth.ipc -``` -4. Finally, trigger the sync command: -``` -# sudo might be not needed in your setup -$ echo '{"jsonrpc":"2.0","method":"shhext_syncMessages","params":[{"mailServerPeer":"enode://c42f368a23fa98ee546fd247220759062323249ef657d26d357a777443aec04db1b29a3a22ef3e7c548e18493ddaf51a31b0aed6079bd6ebe5ae838fcfaf3a49@206.189.243.162:30504", "to": 1550479953, "from": 1550393583, "limit": 1000}],"id":1}' | \ - sudo socat -d -d - UNIX-CONNECT:/docker/statusd-mail/data/geth.ipc -``` - -You can add `"followCursor": true` if you want it to automatically download messages until the cursor is empty meaning all data was synced. - -### Debugging - -To verify that your mail server received any responses, watch logs and seek for logs like this: -``` -INFO [02-18|09:08:54.257] received sync response count=217 final=false err= cursor=[] -``` - -And it should finish with: -``` -INFO [02-18|09:08:54.431] received sync response count=0 final=true err= cursor=[] -``` diff --git a/mailserver/cleaner.go b/mailserver/cleaner.go deleted file mode 100644 index 5afb2d560..000000000 --- a/mailserver/cleaner.go +++ /dev/null @@ -1,85 +0,0 @@ -package mailserver - -import ( - "sync" - "time" - - "github.com/ethereum/go-ethereum/log" -) - -const ( - dbCleanerBatchSize = 1000 - dbCleanerPeriod = time.Hour -) - -// dbCleaner removes old messages from a db. -type dbCleaner struct { - sync.RWMutex - - db DB - batchSize int - retention time.Duration - - period time.Duration - cancel chan struct{} -} - -// newDBCleaner returns a new cleaner for db. -func newDBCleaner(db DB, retention time.Duration) *dbCleaner { - return &dbCleaner{ - db: db, - retention: retention, - - batchSize: dbCleanerBatchSize, - period: dbCleanerPeriod, - } -} - -// Start starts a loop that cleans up old messages. -func (c *dbCleaner) Start() { - log.Info("Starting cleaning envelopes", "period", c.period, "retention", c.retention) - - cancel := make(chan struct{}) - - c.Lock() - c.cancel = cancel - c.Unlock() - - go c.schedule(c.period, cancel) -} - -// Stops stops the cleaning loop. -func (c *dbCleaner) Stop() { - c.Lock() - defer c.Unlock() - - if c.cancel == nil { - return - } - close(c.cancel) - c.cancel = nil -} - -func (c *dbCleaner) schedule(period time.Duration, cancel <-chan struct{}) { - t := time.NewTicker(period) - defer t.Stop() - - for { - select { - case <-t.C: - count, err := c.PruneEntriesOlderThan(time.Now().Add(-c.retention)) - if err != nil { - log.Error("failed to prune data", "err", err) - } - log.Info("Prunned some some messages successfully", "count", count) - case <-cancel: - return - } - } -} - -// PruneEntriesOlderThan removes messages sent between lower and upper timestamps -// and returns how many have been removed. -func (c *dbCleaner) PruneEntriesOlderThan(t time.Time) (int, error) { - return c.db.Prune(t, c.batchSize) -} diff --git a/mailserver/cleaner_test.go b/mailserver/cleaner_test.go deleted file mode 100644 index 61dadedf4..000000000 --- a/mailserver/cleaner_test.go +++ /dev/null @@ -1,160 +0,0 @@ -package mailserver - -import ( - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/require" - "github.com/syndtr/goleveldb/leveldb" - "github.com/syndtr/goleveldb/leveldb/storage" - - "github.com/ethereum/go-ethereum/rlp" - - "github.com/status-im/status-go/eth-node/types" - waku "github.com/status-im/status-go/waku/common" -) - -func TestCleaner(t *testing.T) { - now := time.Now() - server := setupTestServer(t) - defer server.Close() - cleaner := newDBCleaner(server.ms.db, time.Hour) - - archiveEnvelope(t, now.Add(-10*time.Second), server) - archiveEnvelope(t, now.Add(-3*time.Second), server) - archiveEnvelope(t, now.Add(-1*time.Second), server) - - testMessagesCount(t, 3, server) - - testPrune(t, now.Add(-5*time.Second), 1, cleaner) - testPrune(t, now.Add(-2*time.Second), 1, cleaner) - testPrune(t, now, 1, cleaner) - - testMessagesCount(t, 0, server) -} - -func TestCleanerSchedule(t *testing.T) { - now := time.Now() - server := setupTestServer(t) - defer server.Close() - - cleaner := newDBCleaner(server.ms.db, time.Hour) - cleaner.period = time.Millisecond * 10 - cleaner.Start() - defer cleaner.Stop() - - archiveEnvelope(t, now.Add(-3*time.Hour), server) - archiveEnvelope(t, now.Add(-2*time.Hour), server) - archiveEnvelope(t, now.Add(-1*time.Minute), server) - - time.Sleep(time.Millisecond * 50) - - testMessagesCount(t, 1, server) -} - -func benchmarkCleanerPrune(b *testing.B, messages int, batchSize int) { - t := &testing.T{} - now := time.Now() - sentTime := now.Add(-10 * time.Second) - server := setupTestServer(t) - defer server.Close() - - cleaner := newDBCleaner(server.ms.db, time.Hour) - cleaner.batchSize = batchSize - - for i := 0; i < messages; i++ { - archiveEnvelope(t, sentTime, server) - } - - for i := 0; i < b.N; i++ { - testPrune(t, now, 0, cleaner) - } -} - -func BenchmarkCleanerPruneM100_000_B100_000(b *testing.B) { - benchmarkCleanerPrune(b, 100000, 100000) -} - -func BenchmarkCleanerPruneM100_000_B10_000(b *testing.B) { - benchmarkCleanerPrune(b, 100000, 10000) -} - -func BenchmarkCleanerPruneM100_000_B1000(b *testing.B) { - benchmarkCleanerPrune(b, 100000, 1000) -} - -func BenchmarkCleanerPruneM100_000_B100(b *testing.B) { - benchmarkCleanerPrune(b, 100000, 100) -} - -func setupTestServer(t *testing.T) *WakuMailServer { - var s WakuMailServer - db, _ := leveldb.Open(storage.NewMemStorage(), nil) - - s.ms = &mailServer{ - db: &LevelDB{ - ldb: db, - done: make(chan struct{}), - }, - adapter: &wakuAdapter{}, - } - s.minRequestPoW = powRequirement - return &s -} - -func archiveEnvelope(t *testing.T, sentTime time.Time, server *WakuMailServer) *waku.Envelope { - env, err := generateEnvelope(sentTime) - require.NoError(t, err) - server.Archive(env) - - return env -} - -func testPrune(t *testing.T, u time.Time, expected int, c *dbCleaner) { - n, err := c.PruneEntriesOlderThan(u) - require.NoError(t, err) - require.Equal(t, expected, n) -} - -func testMessagesCount(t *testing.T, expected int, s *WakuMailServer) { - count := countMessages(t, s.ms.db) - require.Equal(t, expected, count, fmt.Sprintf("expected %d message, got: %d", expected, count)) -} - -func countMessages(t *testing.T, db DB) int { - var ( - count int - zero types.Hash - emptyTopic types.TopicType - ) - - now := time.Now() - kl := NewDBKey(uint32(0), emptyTopic, zero) - ku := NewDBKey(uint32(now.Unix()), emptyTopic, zero) - - query := CursorQuery{ - start: kl.raw, - end: ku.raw, - } - - i, _ := db.BuildIterator(query) - defer func() { _ = i.Release() }() - - for i.Next() { - var env waku.Envelope - value, err := i.GetEnvelopeByBloomFilter(query.bloom) - if err != nil { - t.Fatal(err) - } - - err = rlp.DecodeBytes(value, &env) - if err != nil { - t.Fatal(err) - } - - count++ - } - - return count -} diff --git a/mailserver/db_key.go b/mailserver/db_key.go deleted file mode 100644 index 33b40f657..000000000 --- a/mailserver/db_key.go +++ /dev/null @@ -1,53 +0,0 @@ -package mailserver - -import ( - "encoding/binary" - "errors" - - "github.com/status-im/status-go/eth-node/types" -) - -const ( - // DBKeyLength is a size of the envelope key. - DBKeyLength = types.HashLength + timestampLength + types.TopicLength - CursorLength = types.HashLength + timestampLength -) - -var ( - // ErrInvalidByteSize is returned when DBKey can't be created - // from a byte slice because it has invalid length. - ErrInvalidByteSize = errors.New("byte slice has invalid length") -) - -// DBKey key to be stored in a db. -type DBKey struct { - raw []byte -} - -// Bytes returns a bytes representation of the DBKey. -func (k *DBKey) Bytes() []byte { - return k.raw -} - -func (k *DBKey) Topic() types.TopicType { - return types.BytesToTopic(k.raw[timestampLength+types.HashLength:]) -} - -func (k *DBKey) EnvelopeHash() types.Hash { - return types.BytesToHash(k.raw[timestampLength : types.HashLength+timestampLength]) -} - -func (k *DBKey) Cursor() []byte { - // We don't use the whole cursor for backward compatibility (also it's not needed) - return k.raw[:CursorLength] -} - -// NewDBKey creates a new DBKey with the given values. -func NewDBKey(timestamp uint32, topic types.TopicType, h types.Hash) *DBKey { - var k DBKey - k.raw = make([]byte, DBKeyLength) - binary.BigEndian.PutUint32(k.raw, timestamp) - copy(k.raw[timestampLength:], h[:]) - copy(k.raw[timestampLength+types.HashLength:], topic[:]) - return &k -} diff --git a/mailserver/db_key_test.go b/mailserver/db_key_test.go deleted file mode 100644 index c681a7147..000000000 --- a/mailserver/db_key_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package mailserver - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/status-im/status-go/eth-node/types" -) - -func TestNewDBKey(t *testing.T) { - topic := types.BytesToTopic([]byte{0x01, 0x02, 0x03, 0x04}) - - hash := types.BytesToHash([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32}) - dbKey := NewDBKey(0xabcdef12, topic, hash) - expected := []byte{0xab, 0xcd, 0xef, 0x12, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32, 0x01, 0x02, 0x03, 0x04} - require.Equal(t, expected, dbKey.Bytes()) - require.Equal(t, topic, dbKey.Topic()) - require.Equal(t, hash, dbKey.EnvelopeHash()) -} diff --git a/mailserver/limiter.go b/mailserver/limiter.go deleted file mode 100644 index 64310b610..000000000 --- a/mailserver/limiter.go +++ /dev/null @@ -1,88 +0,0 @@ -package mailserver - -import ( - "sync" - "time" -) - -type rateLimiter struct { - sync.RWMutex - - lifespan time.Duration // duration of the limit - db map[string]time.Time - - period time.Duration - cancel chan struct{} -} - -func newRateLimiter(duration time.Duration) *rateLimiter { - return &rateLimiter{ - lifespan: duration, - db: make(map[string]time.Time), - period: time.Second, - } -} - -func (l *rateLimiter) Start() { - cancel := make(chan struct{}) - - l.Lock() - l.cancel = cancel - l.Unlock() - - go l.cleanUp(l.period, cancel) -} - -func (l *rateLimiter) Stop() { - l.Lock() - defer l.Unlock() - - if l.cancel == nil { - return - } - close(l.cancel) - l.cancel = nil -} - -func (l *rateLimiter) Add(id string) { - l.Lock() - l.db[id] = time.Now() - l.Unlock() -} - -func (l *rateLimiter) IsAllowed(id string) bool { - l.RLock() - defer l.RUnlock() - - if lastRequestTime, ok := l.db[id]; ok { - return lastRequestTime.Add(l.lifespan).Before(time.Now()) - } - - return true -} - -func (l *rateLimiter) cleanUp(period time.Duration, cancel <-chan struct{}) { - t := time.NewTicker(period) - defer t.Stop() - - for { - select { - case <-t.C: - l.deleteExpired() - case <-cancel: - return - } - } -} - -func (l *rateLimiter) deleteExpired() { - l.Lock() - defer l.Unlock() - - now := time.Now() - for id, lastRequestTime := range l.db { - if lastRequestTime.Add(l.lifespan).Before(now) { - delete(l.db, id) - } - } -} diff --git a/mailserver/limiter_test.go b/mailserver/limiter_test.go deleted file mode 100644 index 2da3c7946..000000000 --- a/mailserver/limiter_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package mailserver - -import ( - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestIsAllowed(t *testing.T) { - peerID := "peerID" - testCases := []struct { - t time.Duration - shouldBeAllowed bool - db func() map[string]time.Time - info string - }{ - { - t: 5 * time.Millisecond, - shouldBeAllowed: true, - db: func() map[string]time.Time { - return make(map[string]time.Time) - }, - info: "Expecting limiter.isAllowed to allow with an empty db", - }, - { - t: 5 * time.Millisecond, - shouldBeAllowed: true, - db: func() map[string]time.Time { - db := make(map[string]time.Time) - db[peerID] = time.Now().Add(time.Duration(-10) * time.Millisecond) - return db - }, - info: "Expecting limiter.isAllowed to allow with an expired peer on its db", - }, - { - t: 5 * time.Millisecond, - shouldBeAllowed: false, - db: func() map[string]time.Time { - db := make(map[string]time.Time) - db[peerID] = time.Now().Add(time.Duration(-1) * time.Millisecond) - return db - }, - info: "Expecting limiter.isAllowed to not allow with a non expired peer on its db", - }, - } - - for _, tc := range testCases { - t.Run(tc.info, func(*testing.T) { - l := newRateLimiter(tc.t) - l.db = tc.db() - assert.Equal(t, tc.shouldBeAllowed, l.IsAllowed(peerID)) - }) - } -} - -func TestRemoveExpiredRateLimits(t *testing.T) { - peer := "peer" - l := newRateLimiter(time.Duration(5) * time.Second) - for i := 0; i < 10; i++ { - peerID := fmt.Sprintf("%s%d", peer, i) - l.db[peerID] = time.Now().Add(time.Duration(i*(-2)) * time.Second) - } - - l.deleteExpired() - assert.Equal(t, 3, len(l.db)) - - for i := 0; i < 3; i++ { - peerID := fmt.Sprintf("%s%d", peer, i) - _, ok := l.db[peerID] - assert.True(t, ok, fmt.Sprintf("Non expired peer '%s' should exist, but it doesn't", peerID)) - } - for i := 3; i < 10; i++ { - peerID := fmt.Sprintf("%s%d", peer, i) - _, ok := l.db[peerID] - assert.False(t, ok, fmt.Sprintf("Expired peer '%s' should not exist, but it does", peerID)) - } -} - -func TestCleaningUpExpiredRateLimits(t *testing.T) { - l := newRateLimiter(5 * time.Second) - l.period = time.Millisecond * 10 - l.Start() - defer l.Stop() - - l.db["peer01"] = time.Now().Add(-1 * time.Second) - l.db["peer02"] = time.Now().Add(-2 * time.Second) - l.db["peer03"] = time.Now().Add(-10 * time.Second) - - time.Sleep(time.Millisecond * 20) - - _, ok := l.db["peer01"] - assert.True(t, ok) - _, ok = l.db["peer02"] - assert.True(t, ok) - _, ok = l.db["peer03"] - assert.False(t, ok) -} - -func TestAddingLimts(t *testing.T) { - peerID := "peerAdding" - l := newRateLimiter(time.Duration(5) * time.Second) - pre := time.Now() - l.Add(peerID) - post := time.Now() - assert.True(t, l.db[peerID].After(pre)) - assert.True(t, l.db[peerID].Before(post)) -} diff --git a/mailserver/mailserver.go b/mailserver/mailserver.go deleted file mode 100644 index eff7d8ca0..000000000 --- a/mailserver/mailserver.go +++ /dev/null @@ -1,948 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package mailserver - -import ( - "crypto/ecdsa" - "encoding/binary" - "errors" - "fmt" - "math/rand" - "sync" - "time" - - prom "github.com/prometheus/client_golang/prometheus" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/rlp" - gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" - "github.com/status-im/status-go/eth-node/crypto" - "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/params" - "github.com/status-im/status-go/waku" - wakucommon "github.com/status-im/status-go/waku/common" -) - -const ( - maxQueryRange = 24 * time.Hour - maxQueryLimit = 1000 - // When we default the upper limit, we want to extend the range a bit - // to accommodate for envelopes with slightly higher timestamp, in seconds - whisperTTLSafeThreshold = 60 -) - -var ( - errDirectoryNotProvided = errors.New("data directory not provided") - errDecryptionMethodNotProvided = errors.New("decryption method is not provided") -) - -const ( - timestampLength = 4 - requestLimitLength = 4 - requestTimeRangeLength = timestampLength * 2 - processRequestTimeout = time.Minute -) - -type Config struct { - // DataDir points to a directory where mailserver's data is stored. - DataDir string - // Password is used to create a symmetric key to decrypt requests. - Password string - // AsymKey is an asymmetric key to decrypt requests. - AsymKey string - // MininumPoW is a minimum PoW for requests. - MinimumPoW float64 - // RateLimit is a maximum number of requests per second from a peer. - RateLimit int - // DataRetention specifies a number of days an envelope should be stored for. - DataRetention int - PostgresEnabled bool - PostgresURI string -} - -// -------------- -// WakuMailServer -// -------------- - -type WakuMailServer struct { - ms *mailServer - shh *waku.Waku - minRequestPoW float64 - - symFilter *wakucommon.Filter - asymFilter *wakucommon.Filter -} - -func (s *WakuMailServer) Init(waku *waku.Waku, cfg *params.WakuConfig) error { - s.shh = waku - s.minRequestPoW = cfg.MinimumPoW - - config := Config{ - DataDir: cfg.DataDir, - Password: cfg.MailServerPassword, - MinimumPoW: cfg.MinimumPoW, - DataRetention: cfg.MailServerDataRetention, - RateLimit: cfg.MailServerRateLimit, - PostgresEnabled: cfg.DatabaseConfig.PGConfig.Enabled, - PostgresURI: cfg.DatabaseConfig.PGConfig.URI, - } - var err error - s.ms, err = newMailServer( - config, - &wakuAdapter{}, - &wakuService{Waku: waku}, - ) - if err != nil { - return err - } - - if err := s.setupDecryptor(config.Password, config.AsymKey); err != nil { - return err - } - - return nil -} - -func (s *WakuMailServer) Close() { - s.ms.Close() -} - -func (s *WakuMailServer) Archive(env *wakucommon.Envelope) { - s.ms.Archive(gethbridge.NewWakuEnvelope(env)) -} - -func (s *WakuMailServer) Deliver(peerID []byte, req wakucommon.MessagesRequest) { - s.ms.DeliverMail(types.BytesToHash(peerID), types.BytesToHash(req.ID), MessagesRequestPayload{ - Lower: req.From, - Upper: req.To, - Bloom: req.Bloom, - Topics: req.Topics, - Limit: req.Limit, - Cursor: req.Cursor, - Batch: true, - }) -} - -// DEPRECATED; user Deliver instead -func (s *WakuMailServer) DeliverMail(peerID []byte, req *wakucommon.Envelope) { - payload, err := s.decodeRequest(peerID, req) - if err != nil { - deliveryFailuresCounter.WithLabelValues("validation").Inc() - log.Error( - "[mailserver:DeliverMail] request failed validaton", - "peerID", types.BytesToHash(peerID), - "requestID", req.Hash().String(), - "err", err, - ) - s.ms.sendHistoricMessageErrorResponse(types.BytesToHash(peerID), types.Hash(req.Hash()), err) - return - } - - s.ms.DeliverMail(types.BytesToHash(peerID), types.Hash(req.Hash()), payload) -} - -// bloomFromReceivedMessage for a given whisper.ReceivedMessage it extracts the -// used bloom filter. -func (s *WakuMailServer) bloomFromReceivedMessage(msg *wakucommon.ReceivedMessage) ([]byte, error) { - payloadSize := len(msg.Payload) - - if payloadSize < 8 { - return nil, errors.New("Undersized p2p request") - } else if payloadSize == 8 { - return wakucommon.MakeFullNodeBloom(), nil - } else if payloadSize < 8+wakucommon.BloomFilterSize { - return nil, errors.New("Undersized bloom filter in p2p request") - } - - return msg.Payload[8 : 8+wakucommon.BloomFilterSize], nil -} - -func (s *WakuMailServer) decompositeRequest(peerID []byte, request *wakucommon.Envelope) (MessagesRequestPayload, error) { - var ( - payload MessagesRequestPayload - err error - ) - - if s.minRequestPoW > 0.0 && request.PoW() < s.minRequestPoW { - return payload, fmt.Errorf("PoW() is too low") - } - - decrypted := s.openEnvelope(request) - if decrypted == nil { - return payload, fmt.Errorf("failed to decrypt p2p request") - } - - if err := checkMsgSignature(decrypted.Src, peerID); err != nil { - return payload, err - } - - payload.Bloom, err = s.bloomFromReceivedMessage(decrypted) - if err != nil { - return payload, err - } - - payload.Lower = binary.BigEndian.Uint32(decrypted.Payload[:4]) - payload.Upper = binary.BigEndian.Uint32(decrypted.Payload[4:8]) - - if payload.Upper < payload.Lower { - err := fmt.Errorf("query range is invalid: from > to (%d > %d)", payload.Lower, payload.Upper) - return payload, err - } - - lowerTime := time.Unix(int64(payload.Lower), 0) - upperTime := time.Unix(int64(payload.Upper), 0) - if upperTime.Sub(lowerTime) > maxQueryRange { - err := fmt.Errorf("query range too big for peer %s", string(peerID)) - return payload, err - } - - if len(decrypted.Payload) >= requestTimeRangeLength+wakucommon.BloomFilterSize+requestLimitLength { - payload.Limit = binary.BigEndian.Uint32(decrypted.Payload[requestTimeRangeLength+wakucommon.BloomFilterSize:]) - } - - if len(decrypted.Payload) == requestTimeRangeLength+wakucommon.BloomFilterSize+requestLimitLength+DBKeyLength { - payload.Cursor = decrypted.Payload[requestTimeRangeLength+wakucommon.BloomFilterSize+requestLimitLength:] - } - - return payload, nil -} - -func (s *WakuMailServer) setupDecryptor(password, asymKey string) error { - s.symFilter = nil - s.asymFilter = nil - - if password != "" { - keyID, err := s.shh.AddSymKeyFromPassword(password) - if err != nil { - return fmt.Errorf("create symmetric key: %v", err) - } - - symKey, err := s.shh.GetSymKey(keyID) - if err != nil { - return fmt.Errorf("save symmetric key: %v", err) - } - - s.symFilter = &wakucommon.Filter{KeySym: symKey} - } - - if asymKey != "" { - keyAsym, err := crypto.HexToECDSA(asymKey) - if err != nil { - return err - } - s.asymFilter = &wakucommon.Filter{KeyAsym: keyAsym} - } - - return nil -} - -// openEnvelope tries to decrypt an envelope, first based on asymetric key (if -// provided) and second on the symetric key (if provided) -func (s *WakuMailServer) openEnvelope(request *wakucommon.Envelope) *wakucommon.ReceivedMessage { - if s.asymFilter != nil { - if d := request.Open(s.asymFilter); d != nil { - return d - } - } - if s.symFilter != nil { - if d := request.Open(s.symFilter); d != nil { - return d - } - } - return nil -} - -func (s *WakuMailServer) decodeRequest(peerID []byte, request *wakucommon.Envelope) (MessagesRequestPayload, error) { - var payload MessagesRequestPayload - - if s.minRequestPoW > 0.0 && request.PoW() < s.minRequestPoW { - return payload, errors.New("PoW too low") - } - - decrypted := s.openEnvelope(request) - if decrypted == nil { - log.Warn("Failed to decrypt p2p request") - return payload, errors.New("failed to decrypt p2p request") - } - - if err := checkMsgSignature(decrypted.Src, peerID); err != nil { - log.Warn("Check message signature failed", "err", err.Error()) - return payload, fmt.Errorf("check message signature failed: %v", err) - } - - if err := rlp.DecodeBytes(decrypted.Payload, &payload); err != nil { - return payload, fmt.Errorf("failed to decode data: %v", err) - } - - if payload.Upper == 0 { - payload.Upper = uint32(time.Now().Unix() + whisperTTLSafeThreshold) - } - - if payload.Upper < payload.Lower { - log.Error("Query range is invalid: lower > upper", "lower", payload.Lower, "upper", payload.Upper) - return payload, errors.New("query range is invalid: lower > upper") - } - - return payload, nil -} - -// ------- -// adapter -// ------- - -type adapter interface { - CreateRequestFailedPayload(reqID types.Hash, err error) []byte - CreateRequestCompletedPayload(reqID, lastEnvelopeHash types.Hash, cursor []byte) []byte - CreateSyncResponse(envelopes []types.Envelope, cursor []byte, final bool, err string) interface{} - CreateRawSyncResponse(envelopes []rlp.RawValue, cursor []byte, final bool, err string) interface{} -} - -// ----------- -// wakuAdapter -// ----------- - -type wakuAdapter struct{} - -var _ adapter = (*wakuAdapter)(nil) - -func (wakuAdapter) CreateRequestFailedPayload(reqID types.Hash, err error) []byte { - return waku.CreateMailServerRequestFailedPayload(common.Hash(reqID), err) -} - -func (wakuAdapter) CreateRequestCompletedPayload(reqID, lastEnvelopeHash types.Hash, cursor []byte) []byte { - return waku.CreateMailServerRequestCompletedPayload(common.Hash(reqID), common.Hash(lastEnvelopeHash), cursor) -} - -func (wakuAdapter) CreateSyncResponse(_ []types.Envelope, _ []byte, _ bool, _ string) interface{} { - return nil -} - -func (wakuAdapter) CreateRawSyncResponse(_ []rlp.RawValue, _ []byte, _ bool, _ string) interface{} { - return nil -} - -// ------- -// service -// ------- - -type service interface { - SendHistoricMessageResponse(peerID []byte, payload []byte) error - SendRawP2PDirect(peerID []byte, envelopes ...rlp.RawValue) error - MaxMessageSize() uint32 - SendRawSyncResponse(peerID []byte, data interface{}) error // optional - SendSyncResponse(peerID []byte, data interface{}) error // optional -} - -// ----------- -// wakuService -// ----------- - -type wakuService struct { - *waku.Waku -} - -func (s *wakuService) SendRawSyncResponse(peerID []byte, data interface{}) error { - return errors.New("syncing mailservers is not support by Waku") -} - -func (s *wakuService) SendSyncResponse(peerID []byte, data interface{}) error { - return errors.New("syncing mailservers is not support by Waku") -} - -// ---------- -// mailServer -// ---------- - -type mailServer struct { - adapter adapter - service service - db DB - cleaner *dbCleaner // removes old envelopes - muRateLimiter sync.RWMutex - rateLimiter *rateLimiter -} - -func newMailServer(cfg Config, adapter adapter, service service) (*mailServer, error) { - if len(cfg.DataDir) == 0 { - return nil, errDirectoryNotProvided - } - - // TODO: move out - if len(cfg.Password) == 0 && len(cfg.AsymKey) == 0 { - return nil, errDecryptionMethodNotProvided - } - - s := mailServer{ - adapter: adapter, - service: service, - } - - if cfg.RateLimit > 0 { - s.setupRateLimiter(time.Duration(cfg.RateLimit) * time.Second) - } - - // Open database in the last step in order not to init with error - // and leave the database open by accident. - if cfg.PostgresEnabled { - log.Info("Connecting to postgres database") - database, err := NewPostgresDB(cfg.PostgresURI) - if err != nil { - return nil, fmt.Errorf("open DB: %s", err) - } - s.db = database - log.Info("Connected to postgres database") - } else { - // Defaults to LevelDB - database, err := NewLevelDB(cfg.DataDir) - if err != nil { - return nil, fmt.Errorf("open DB: %s", err) - } - s.db = database - } - - if cfg.DataRetention > 0 { - // MailServerDataRetention is a number of days. - s.setupCleaner(time.Duration(cfg.DataRetention) * time.Hour * 24) - } - - return &s, nil -} - -// setupRateLimiter in case limit is bigger than 0 it will setup an automated -// limit db cleanup. -func (s *mailServer) setupRateLimiter(limit time.Duration) { - s.rateLimiter = newRateLimiter(limit) - s.rateLimiter.Start() -} - -func (s *mailServer) setupCleaner(retention time.Duration) { - s.cleaner = newDBCleaner(s.db, retention) - s.cleaner.Start() -} - -func (s *mailServer) Archive(env types.Envelope) { - err := s.db.SaveEnvelope(env) - if err != nil { - log.Error("Could not save envelope", "hash", env.Hash().String()) - } -} - -func (s *mailServer) DeliverMail(peerID, reqID types.Hash, req MessagesRequestPayload) { - timer := prom.NewTimer(mailDeliveryDuration) - defer timer.ObserveDuration() - - deliveryAttemptsCounter.Inc() - log.Info( - "[mailserver:DeliverMail] delivering mail", - "peerID", peerID.String(), - "requestID", reqID.String(), - ) - - req.SetDefaults() - - log.Info( - "[mailserver:DeliverMail] processing request", - "peerID", peerID.String(), - "requestID", reqID.String(), - "lower", req.Lower, - "upper", req.Upper, - "bloom", req.Bloom, - "topics", req.Topics, - "limit", req.Limit, - "cursor", req.Cursor, - "batch", req.Batch, - ) - - if err := req.Validate(); err != nil { - syncFailuresCounter.WithLabelValues("req_invalid").Inc() - log.Error( - "[mailserver:DeliverMail] request invalid", - "peerID", peerID.String(), - "requestID", reqID.String(), - "err", err, - ) - s.sendHistoricMessageErrorResponse(peerID, reqID, fmt.Errorf("request is invalid: %v", err)) - return - } - - if s.exceedsPeerRequests(peerID) { - deliveryFailuresCounter.WithLabelValues("peer_req_limit").Inc() - log.Error( - "[mailserver:DeliverMail] peer exceeded the limit", - "peerID", peerID.String(), - "requestID", reqID.String(), - ) - s.sendHistoricMessageErrorResponse(peerID, reqID, fmt.Errorf("rate limit exceeded")) - return - } - - if req.Batch { - requestsBatchedCounter.Inc() - } - - iter, err := s.createIterator(req) - if err != nil { - log.Error( - "[mailserver:DeliverMail] request failed", - "peerID", peerID.String(), - "requestID", reqID.String(), - "err", err, - ) - return - } - defer func() { _ = iter.Release() }() - - bundles := make(chan []rlp.RawValue, 5) - errCh := make(chan error) - cancelProcessing := make(chan struct{}) - - go func() { - counter := 0 - for bundle := range bundles { - if err := s.sendRawEnvelopes(peerID, bundle, req.Batch); err != nil { - close(cancelProcessing) - errCh <- err - break - } - counter++ - } - close(errCh) - log.Info( - "[mailserver:DeliverMail] finished sending bundles", - "peerID", peerID, - "requestID", reqID.String(), - "counter", counter, - ) - }() - - nextPageCursor, lastEnvelopeHash := s.processRequestInBundles( - iter, - req.Bloom, - req.Topics, - int(req.Limit), - processRequestTimeout, - reqID.String(), - bundles, - cancelProcessing, - ) - - // Wait for the goroutine to finish the work. It may return an error. - if err := <-errCh; err != nil { - deliveryFailuresCounter.WithLabelValues("process").Inc() - log.Error( - "[mailserver:DeliverMail] error while processing", - "err", err, - "peerID", peerID, - "requestID", reqID, - ) - s.sendHistoricMessageErrorResponse(peerID, reqID, err) - return - } - - // Processing of the request could be finished earlier due to iterator error. - if err := iter.Error(); err != nil { - deliveryFailuresCounter.WithLabelValues("iterator").Inc() - log.Error( - "[mailserver:DeliverMail] iterator failed", - "err", err, - "peerID", peerID, - "requestID", reqID, - ) - s.sendHistoricMessageErrorResponse(peerID, reqID, err) - return - } - - log.Info( - "[mailserver:DeliverMail] sending historic message response", - "peerID", peerID, - "requestID", reqID, - "last", lastEnvelopeHash, - "next", nextPageCursor, - ) - - s.sendHistoricMessageResponse(peerID, reqID, lastEnvelopeHash, nextPageCursor) -} - -func (s *mailServer) SyncMail(peerID types.Hash, req MessagesRequestPayload) error { - log.Info("Started syncing envelopes", "peer", peerID.String(), "req", req) - - requestID := fmt.Sprintf("%d-%d", time.Now().UnixNano(), rand.Intn(1000)) // nolint: gosec - - syncAttemptsCounter.Inc() - - // Check rate limiting for a requesting peer. - if s.exceedsPeerRequests(peerID) { - syncFailuresCounter.WithLabelValues("req_per_sec_limit").Inc() - log.Error("Peer exceeded request per seconds limit", "peerID", peerID.String()) - return fmt.Errorf("requests per seconds limit exceeded") - } - - req.SetDefaults() - - if err := req.Validate(); err != nil { - syncFailuresCounter.WithLabelValues("req_invalid").Inc() - return fmt.Errorf("request is invalid: %v", err) - } - - iter, err := s.createIterator(req) - if err != nil { - syncFailuresCounter.WithLabelValues("iterator").Inc() - return err - } - defer func() { _ = iter.Release() }() - - bundles := make(chan []rlp.RawValue, 5) - errCh := make(chan error) - cancelProcessing := make(chan struct{}) - - go func() { - for bundle := range bundles { - resp := s.adapter.CreateRawSyncResponse(bundle, nil, false, "") - if err := s.service.SendRawSyncResponse(peerID.Bytes(), resp); err != nil { - close(cancelProcessing) - errCh <- fmt.Errorf("failed to send sync response: %v", err) - break - } - } - close(errCh) - }() - - nextCursor, _ := s.processRequestInBundles( - iter, - req.Bloom, - req.Topics, - int(req.Limit), - processRequestTimeout, - requestID, - bundles, - cancelProcessing, - ) - - // Wait for the goroutine to finish the work. It may return an error. - if err := <-errCh; err != nil { - syncFailuresCounter.WithLabelValues("routine").Inc() - _ = s.service.SendSyncResponse( - peerID.Bytes(), - s.adapter.CreateSyncResponse(nil, nil, false, "failed to send a response"), - ) - return err - } - - // Processing of the request could be finished earlier due to iterator error. - if err := iter.Error(); err != nil { - syncFailuresCounter.WithLabelValues("iterator").Inc() - _ = s.service.SendSyncResponse( - peerID.Bytes(), - s.adapter.CreateSyncResponse(nil, nil, false, "failed to process all envelopes"), - ) - return fmt.Errorf("LevelDB iterator failed: %v", err) - } - - log.Info("Finished syncing envelopes", "peer", peerID.String()) - - err = s.service.SendSyncResponse( - peerID.Bytes(), - s.adapter.CreateSyncResponse(nil, nextCursor, true, ""), - ) - if err != nil { - syncFailuresCounter.WithLabelValues("response_send").Inc() - return fmt.Errorf("failed to send the final sync response: %v", err) - } - - return nil -} - -// Close the mailserver and its associated db connection. -func (s *mailServer) Close() { - if s.db != nil { - if err := s.db.Close(); err != nil { - log.Error("closing database failed", "err", err) - } - } - if s.rateLimiter != nil { - s.rateLimiter.Stop() - } - if s.cleaner != nil { - s.cleaner.Stop() - } -} - -func (s *mailServer) exceedsPeerRequests(peerID types.Hash) bool { - s.muRateLimiter.RLock() - defer s.muRateLimiter.RUnlock() - - if s.rateLimiter == nil { - return false - } - - if s.rateLimiter.IsAllowed(peerID.String()) { - s.rateLimiter.Add(peerID.String()) - return false - } - - log.Info("peerID exceeded the number of requests per second", "peerID", peerID.String()) - return true -} - -func (s *mailServer) createIterator(req MessagesRequestPayload) (Iterator, error) { - var ( - emptyHash types.Hash - emptyTopic types.TopicType - ku, kl *DBKey - ) - - ku = NewDBKey(req.Upper+1, emptyTopic, emptyHash) - kl = NewDBKey(req.Lower, emptyTopic, emptyHash) - - query := CursorQuery{ - start: kl.Bytes(), - end: ku.Bytes(), - cursor: req.Cursor, - topics: req.Topics, - bloom: req.Bloom, - limit: req.Limit, - } - return s.db.BuildIterator(query) -} - -func (s *mailServer) processRequestInBundles( - iter Iterator, - bloom []byte, - topics [][]byte, - limit int, - timeout time.Duration, - requestID string, - output chan<- []rlp.RawValue, - cancel <-chan struct{}, -) ([]byte, types.Hash) { - timer := prom.NewTimer(requestsInBundlesDuration) - defer timer.ObserveDuration() - - var ( - bundle []rlp.RawValue - bundleSize uint32 - batches [][]rlp.RawValue - processedEnvelopes int - processedEnvelopesSize int64 - nextCursor []byte - lastEnvelopeHash types.Hash - ) - - log.Info( - "[mailserver:processRequestInBundles] processing request", - "requestID", requestID, - "limit", limit, - ) - - var topicsMap map[types.TopicType]bool - - if len(topics) != 0 { - topicsMap = make(map[types.TopicType]bool) - for _, t := range topics { - topicsMap[types.BytesToTopic(t)] = true - } - } - - // We iterate over the envelopes. - // We collect envelopes in batches. - // If there still room and we haven't reached the limit - // append and continue. - // Otherwise publish what you have so far, reset the bundle to the - // current envelope, and leave if we hit the limit - for iter.Next() { - var rawValue []byte - var err error - if len(topicsMap) != 0 { - rawValue, err = iter.GetEnvelopeByTopicsMap(topicsMap) - - } else if len(bloom) != 0 { - rawValue, err = iter.GetEnvelopeByBloomFilter(bloom) - } else { - err = errors.New("either topics or bloom must be specified") - } - if err != nil { - log.Error( - "[mailserver:processRequestInBundles]Failed to get envelope from iterator", - "err", err, - "requestID", requestID, - ) - continue - } - - if rawValue == nil { - continue - } - - key, err := iter.DBKey() - if err != nil { - log.Error( - "[mailserver:processRequestInBundles] failed getting key", - "requestID", requestID, - ) - break - - } - - // TODO(adam): this is invalid code. If the limit is 1000, - // it will only send 999 items and send a cursor. - lastEnvelopeHash = key.EnvelopeHash() - processedEnvelopes++ - envelopeSize := uint32(len(rawValue)) - limitReached := processedEnvelopes >= limit - newSize := bundleSize + envelopeSize - - // If we still have some room for messages, add and continue - if !limitReached && newSize < s.service.MaxMessageSize() { - bundle = append(bundle, rawValue) - bundleSize = newSize - continue - } - - // Publish if anything is in the bundle (there should always be - // something unless limit = 1) - if len(bundle) != 0 { - batches = append(batches, bundle) - processedEnvelopesSize += int64(bundleSize) - } - - // Reset the bundle with the current envelope - bundle = []rlp.RawValue{rawValue} - bundleSize = envelopeSize - - // Leave if we reached the limit - if limitReached { - nextCursor = key.Cursor() - break - } - } - - if len(bundle) > 0 { - batches = append(batches, bundle) - processedEnvelopesSize += int64(bundleSize) - } - - log.Info( - "[mailserver:processRequestInBundles] publishing envelopes", - "requestID", requestID, - "batchesCount", len(batches), - "envelopeCount", processedEnvelopes, - "processedEnvelopesSize", processedEnvelopesSize, - "cursor", nextCursor, - ) - - // Publish -batchLoop: - for _, batch := range batches { - select { - case output <- batch: - // It might happen that during producing the batches, - // the connection with the peer goes down and - // the consumer of `output` channel exits prematurely. - // In such a case, we should stop pushing batches and exit. - case <-cancel: - log.Info( - "[mailserver:processRequestInBundles] failed to push all batches", - "requestID", requestID, - ) - break batchLoop - case <-time.After(timeout): - log.Error( - "[mailserver:processRequestInBundles] timed out pushing a batch", - "requestID", requestID, - ) - break batchLoop - } - } - - envelopesCounter.Inc() - sentEnvelopeBatchSizeMeter.Observe(float64(processedEnvelopesSize)) - - log.Info( - "[mailserver:processRequestInBundles] envelopes published", - "requestID", requestID, - ) - close(output) - - return nextCursor, lastEnvelopeHash -} - -func (s *mailServer) sendRawEnvelopes(peerID types.Hash, envelopes []rlp.RawValue, batch bool) error { - timer := prom.NewTimer(sendRawEnvelopeDuration) - defer timer.ObserveDuration() - - if batch { - return s.service.SendRawP2PDirect(peerID.Bytes(), envelopes...) - } - - for _, env := range envelopes { - if err := s.service.SendRawP2PDirect(peerID.Bytes(), env); err != nil { - return err - } - } - - return nil -} - -func (s *mailServer) sendHistoricMessageResponse(peerID, reqID, lastEnvelopeHash types.Hash, cursor []byte) { - payload := s.adapter.CreateRequestCompletedPayload(reqID, lastEnvelopeHash, cursor) - err := s.service.SendHistoricMessageResponse(peerID.Bytes(), payload) - if err != nil { - deliveryFailuresCounter.WithLabelValues("historic_msg_resp").Inc() - log.Error( - "[mailserver:DeliverMail] error sending historic message response", - "err", err, - "peerID", peerID, - "requestID", reqID, - ) - } -} - -func (s *mailServer) sendHistoricMessageErrorResponse(peerID, reqID types.Hash, errorToReport error) { - payload := s.adapter.CreateRequestFailedPayload(reqID, errorToReport) - err := s.service.SendHistoricMessageResponse(peerID.Bytes(), payload) - // if we can't report an error, probably something is wrong with p2p connection, - // so we just print a log entry to document this sad fact - if err != nil { - log.Error("Error while reporting error response", "err", err, "peerID", peerID.String()) - } -} - -func extractBloomFromEncodedEnvelope(rawValue rlp.RawValue) ([]byte, error) { - var envelope wakucommon.Envelope - decodeErr := rlp.DecodeBytes(rawValue, &envelope) - if decodeErr != nil { - return nil, decodeErr - } - return envelope.Bloom(), nil -} - -// checkMsgSignature returns an error in case the message is not correctly signed. -func checkMsgSignature(reqSrc *ecdsa.PublicKey, id []byte) error { - src := crypto.FromECDSAPub(reqSrc) - if len(src)-len(id) == 1 { - src = src[1:] - } - - // if you want to check the signature, you can do it here. e.g.: - // if !bytes.Equal(peerID, src) { - if src == nil { - return errors.New("wrong signature of p2p request") - } - - return nil -} diff --git a/mailserver/mailserver_db.go b/mailserver/mailserver_db.go deleted file mode 100644 index 19ffcbbc2..000000000 --- a/mailserver/mailserver_db.go +++ /dev/null @@ -1,42 +0,0 @@ -package mailserver - -import ( - "time" - - "github.com/status-im/status-go/eth-node/types" -) - -// every this many seconds check real envelopes count -const envelopeCountCheckInterval = 60 - -// DB is an interface to abstract interactions with the db so that the mailserver -// is agnostic to the underlying technology used -type DB interface { - Close() error - // SaveEnvelope stores an envelope - SaveEnvelope(types.Envelope) error - // GetEnvelope returns an rlp encoded envelope from the datastore - GetEnvelope(*DBKey) ([]byte, error) - // Prune removes envelopes older than time - Prune(time.Time, int) (int, error) - // BuildIterator returns an iterator over envelopes - BuildIterator(query CursorQuery) (Iterator, error) -} - -type Iterator interface { - Next() bool - DBKey() (*DBKey, error) - Release() error - Error() error - GetEnvelopeByBloomFilter(bloom []byte) ([]byte, error) - GetEnvelopeByTopicsMap(topics map[types.TopicType]bool) ([]byte, error) -} - -type CursorQuery struct { - start []byte - end []byte - cursor []byte - limit uint32 - bloom []byte - topics [][]byte -} diff --git a/mailserver/mailserver_db_leveldb.go b/mailserver/mailserver_db_leveldb.go deleted file mode 100644 index 4c481d87d..000000000 --- a/mailserver/mailserver_db_leveldb.go +++ /dev/null @@ -1,242 +0,0 @@ -package mailserver - -import ( - "fmt" - "time" - - "github.com/syndtr/goleveldb/leveldb" - "github.com/syndtr/goleveldb/leveldb/errors" - "github.com/syndtr/goleveldb/leveldb/iterator" - "github.com/syndtr/goleveldb/leveldb/util" - - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/rlp" - - "github.com/status-im/status-go/eth-node/types" - waku "github.com/status-im/status-go/waku/common" -) - -type LevelDB struct { - // We can't embed as there are some state problems with go-routines - ldb *leveldb.DB - name string - done chan struct{} -} - -type LevelDBIterator struct { - iterator.Iterator -} - -func (i *LevelDBIterator) DBKey() (*DBKey, error) { - return &DBKey{ - raw: i.Key(), - }, nil -} - -func (i *LevelDBIterator) GetEnvelopeByTopicsMap(topics map[types.TopicType]bool) ([]byte, error) { - rawValue := make([]byte, len(i.Value())) - copy(rawValue, i.Value()) - - key, err := i.DBKey() - if err != nil { - return nil, err - } - - if !topics[key.Topic()] { - return nil, nil - } - - return rawValue, nil -} - -func (i *LevelDBIterator) GetEnvelopeByBloomFilter(bloom []byte) ([]byte, error) { - var envelopeBloom []byte - rawValue := make([]byte, len(i.Value())) - copy(rawValue, i.Value()) - - key, err := i.DBKey() - if err != nil { - return nil, err - } - - if len(key.Bytes()) != DBKeyLength { - var err error - envelopeBloom, err = extractBloomFromEncodedEnvelope(rawValue) - if err != nil { - return nil, err - } - } else { - envelopeBloom = types.TopicToBloom(key.Topic()) - } - if !types.BloomFilterMatch(bloom, envelopeBloom) { - return nil, nil - } - return rawValue, nil -} - -func (i *LevelDBIterator) Release() error { - i.Iterator.Release() - return nil -} - -func NewLevelDB(dataDir string) (*LevelDB, error) { - // Open opens an existing leveldb database - db, err := leveldb.OpenFile(dataDir, nil) - if _, corrupted := err.(*errors.ErrCorrupted); corrupted { - log.Info("database is corrupted trying to recover", "path", dataDir) - db, err = leveldb.RecoverFile(dataDir, nil) - } - - instance := LevelDB{ - ldb: db, - name: dataDir, // name is used for metrics labels - done: make(chan struct{}), - } - - // initialize the metric value - instance.updateArchivedEnvelopesCount() - // checking count on every insert is inefficient - go func() { - for { - select { - case <-instance.done: - return - case <-time.After(time.Second * envelopeCountCheckInterval): - instance.updateArchivedEnvelopesCount() - } - } - }() - return &instance, err -} - -// GetEnvelope get an envelope by its key -func (db *LevelDB) GetEnvelope(key *DBKey) ([]byte, error) { - defer recoverLevelDBPanics("GetEnvelope") - return db.ldb.Get(key.Bytes(), nil) -} - -func (db *LevelDB) updateArchivedEnvelopesCount() { - if count, err := db.envelopesCount(); err != nil { - log.Warn("db query for envelopes count failed", "err", err) - } else { - archivedEnvelopesGauge.WithLabelValues(db.name).Set(float64(count)) - } -} - -// Build iterator returns an iterator given a start/end and a cursor -func (db *LevelDB) BuildIterator(query CursorQuery) (Iterator, error) { - defer recoverLevelDBPanics("BuildIterator") - - i := db.ldb.NewIterator(&util.Range{Start: query.start, Limit: query.end}, nil) - - envelopeQueriesCounter.WithLabelValues("unknown", "unknown").Inc() - // seek to the end as we want to return envelopes in a descending order - if len(query.cursor) == CursorLength { - i.Seek(query.cursor) - } - return &LevelDBIterator{i}, nil -} - -// Prune removes envelopes older than time -func (db *LevelDB) Prune(t time.Time, batchSize int) (int, error) { - defer recoverLevelDBPanics("Prune") - - var zero types.Hash - var emptyTopic types.TopicType - kl := NewDBKey(0, emptyTopic, zero) - ku := NewDBKey(uint32(t.Unix()), emptyTopic, zero) - query := CursorQuery{ - start: kl.Bytes(), - end: ku.Bytes(), - } - i, err := db.BuildIterator(query) - if err != nil { - return 0, err - } - defer func() { _ = i.Release() }() - - batch := leveldb.Batch{} - removed := 0 - - for i.Next() { - dbKey, err := i.DBKey() - if err != nil { - return 0, err - } - - batch.Delete(dbKey.Bytes()) - - if batch.Len() == batchSize { - if err := db.ldb.Write(&batch, nil); err != nil { - return removed, err - } - - removed = removed + batch.Len() - batch.Reset() - } - } - - if batch.Len() > 0 { - if err := db.ldb.Write(&batch, nil); err != nil { - return removed, err - } - - removed = removed + batch.Len() - } - - return removed, nil -} - -func (db *LevelDB) envelopesCount() (int, error) { - defer recoverLevelDBPanics("envelopesCount") - iterator, err := db.BuildIterator(CursorQuery{}) - if err != nil { - return 0, err - } - // LevelDB does not have API for getting a count - var count int - for iterator.Next() { - count++ - } - return count, nil -} - -// SaveEnvelope stores an envelope in leveldb and increments the metrics -func (db *LevelDB) SaveEnvelope(env types.Envelope) error { - defer recoverLevelDBPanics("SaveEnvelope") - - key := NewDBKey(env.Expiry()-env.TTL(), env.Topic(), env.Hash()) - rawEnvelope, err := rlp.EncodeToBytes(env.Unwrap()) - if err != nil { - log.Error(fmt.Sprintf("rlp.EncodeToBytes failed: %s", err)) - archivedErrorsCounter.WithLabelValues(db.name).Inc() - return err - } - - if err = db.ldb.Put(key.Bytes(), rawEnvelope, nil); err != nil { - log.Error(fmt.Sprintf("Writing to DB failed: %s", err)) - archivedErrorsCounter.WithLabelValues(db.name).Inc() - } - archivedEnvelopesGauge.WithLabelValues(db.name).Inc() - archivedEnvelopeSizeMeter.WithLabelValues(db.name).Observe( - float64(waku.EnvelopeHeaderLength + env.Size())) - return err -} - -func (db *LevelDB) Close() error { - select { - case <-db.done: - default: - close(db.done) - } - return db.ldb.Close() -} - -func recoverLevelDBPanics(calleMethodName string) { - // Recover from possible goleveldb panics - if r := recover(); r != nil { - if errString, ok := r.(string); ok { - log.Error(fmt.Sprintf("recovered from panic in %s: %s", calleMethodName, errString)) - } - } -} diff --git a/mailserver/mailserver_db_leveldb_test.go b/mailserver/mailserver_db_leveldb_test.go deleted file mode 100644 index 83ff9329c..000000000 --- a/mailserver/mailserver_db_leveldb_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package mailserver - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/ethereum/go-ethereum/rlp" - "github.com/status-im/status-go/eth-node/types" - waku "github.com/status-im/status-go/waku/common" -) - -func TestLevelDB_BuildIteratorWithTopic(t *testing.T) { - topic := []byte{0x01, 0x02, 0x03, 0x04} - - db, err := NewLevelDB(t.TempDir()) - require.NoError(t, err) - - envelope, err := newTestEnvelope(topic) - require.NoError(t, err) - err = db.SaveEnvelope(envelope) - require.NoError(t, err) - - iter, err := db.BuildIterator(CursorQuery{ - start: NewDBKey(uint32(time.Now().Add(-time.Hour).Unix()), types.BytesToTopic(topic), types.Hash{}).Bytes(), - end: NewDBKey(uint32(time.Now().Add(time.Second).Unix()), types.BytesToTopic(topic), types.Hash{}).Bytes(), - topics: [][]byte{topic}, - limit: 10, - }) - topicsMap := make(map[types.TopicType]bool) - topicsMap[types.BytesToTopic(topic)] = true - require.NoError(t, err) - hasNext := iter.Next() - require.True(t, hasNext) - rawValue, err := iter.GetEnvelopeByTopicsMap(topicsMap) - require.NoError(t, err) - require.NotEmpty(t, rawValue) - var receivedEnvelope waku.Envelope - err = rlp.DecodeBytes(rawValue, &receivedEnvelope) - require.NoError(t, err) - require.EqualValues(t, waku.BytesToTopic(topic), receivedEnvelope.Topic) - - err = iter.Release() - require.NoError(t, err) - require.NoError(t, iter.Error()) -} diff --git a/mailserver/mailserver_db_postgres.go b/mailserver/mailserver_db_postgres.go deleted file mode 100644 index d17c28f97..000000000 --- a/mailserver/mailserver_db_postgres.go +++ /dev/null @@ -1,309 +0,0 @@ -package mailserver - -import ( - "database/sql" - "errors" - "fmt" - "time" - - "github.com/lib/pq" - - // Import postgres driver - _ "github.com/lib/pq" - "github.com/status-im/migrate/v4" - "github.com/status-im/migrate/v4/database/postgres" - bindata "github.com/status-im/migrate/v4/source/go_bindata" - - "github.com/status-im/status-go/mailserver/migrations" - - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/rlp" - - "github.com/status-im/status-go/eth-node/types" - waku "github.com/status-im/status-go/waku/common" -) - -type PostgresDB struct { - db *sql.DB - name string - done chan struct{} -} - -func NewPostgresDB(uri string) (*PostgresDB, error) { - db, err := sql.Open("postgres", uri) - if err != nil { - return nil, err - } - - instance := &PostgresDB{ - db: db, - done: make(chan struct{}), - } - if err := instance.setup(); err != nil { - return nil, err - } - - // name is used for metrics labels - if name, err := instance.getDBName(uri); err == nil { - instance.name = name - } - - // initialize the metric value - instance.updateArchivedEnvelopesCount() - // checking count on every insert is inefficient - go func() { - for { - select { - case <-instance.done: - return - case <-time.After(time.Second * envelopeCountCheckInterval): - instance.updateArchivedEnvelopesCount() - } - } - }() - return instance, nil -} - -type postgresIterator struct { - *sql.Rows -} - -func (i *PostgresDB) getDBName(uri string) (string, error) { - query := "SELECT current_database()" - var dbName string - return dbName, i.db.QueryRow(query).Scan(&dbName) -} - -func (i *PostgresDB) envelopesCount() (int, error) { - query := "SELECT count(*) FROM envelopes" - var count int - return count, i.db.QueryRow(query).Scan(&count) -} - -func (i *PostgresDB) updateArchivedEnvelopesCount() { - if count, err := i.envelopesCount(); err != nil { - log.Warn("db query for envelopes count failed", "err", err) - } else { - archivedEnvelopesGauge.WithLabelValues(i.name).Set(float64(count)) - } -} - -func (i *postgresIterator) DBKey() (*DBKey, error) { - var value []byte - var id []byte - if err := i.Scan(&id, &value); err != nil { - return nil, err - } - return &DBKey{raw: id}, nil -} - -func (i *postgresIterator) Error() error { - return i.Err() -} - -func (i *postgresIterator) Release() error { - return i.Close() -} - -func (i *postgresIterator) GetEnvelopeByBloomFilter(bloom []byte) ([]byte, error) { - var value []byte - var id []byte - if err := i.Scan(&id, &value); err != nil { - return nil, err - } - - return value, nil -} - -func (i *postgresIterator) GetEnvelopeByTopicsMap(topics map[types.TopicType]bool) ([]byte, error) { - var value []byte - var id []byte - if err := i.Scan(&id, &value); err != nil { - return nil, err - } - - return value, nil -} - -func (i *PostgresDB) BuildIterator(query CursorQuery) (Iterator, error) { - var args []interface{} - - stmtString := "SELECT id, data FROM envelopes" - - var historyRange string - if len(query.cursor) > 0 { - args = append(args, query.start, query.cursor) - // If we have a cursor, we don't want to include that envelope in the result set - stmtString += " " + "WHERE id >= $1 AND id < $2" - historyRange = "partial" //nolint: goconst - } else { - args = append(args, query.start, query.end) - stmtString += " " + "WHERE id >= $1 AND id <= $2" - historyRange = "full" //nolint: goconst - } - - var filterRange string - if len(query.topics) > 0 { - args = append(args, pq.Array(query.topics)) - stmtString += " " + "AND topic = any($3)" - filterRange = "partial" //nolint: goconst - } else { - stmtString += " " + fmt.Sprintf("AND bloom & b'%s'::bit(512) = bloom", toBitString(query.bloom)) - filterRange = "full" //nolint: goconst - } - - // Positional argument depends on the fact whether the query uses topics or bloom filter. - // If topic is used, the list of topics is passed as an argument to the query. - // If bloom filter is used, it is included into the query statement. - args = append(args, query.limit) - stmtString += " " + fmt.Sprintf("ORDER BY ID DESC LIMIT $%d", len(args)) - - stmt, err := i.db.Prepare(stmtString) - if err != nil { - return nil, err - } - - envelopeQueriesCounter.WithLabelValues(filterRange, historyRange).Inc() - rows, err := stmt.Query(args...) - if err != nil { - return nil, err - } - - return &postgresIterator{rows}, nil -} - -func (i *PostgresDB) setup() error { - resources := bindata.Resource( - migrations.AssetNames(), - migrations.Asset, - ) - - source, err := bindata.WithInstance(resources) - if err != nil { - return err - } - - driver, err := postgres.WithInstance(i.db, &postgres.Config{}) - if err != nil { - return err - } - - m, err := migrate.NewWithInstance( - "go-bindata", - source, - "postgres", - driver) - if err != nil { - return err - } - - if err = m.Up(); err != migrate.ErrNoChange { - return err - } - - return nil -} - -func (i *PostgresDB) Close() error { - select { - case <-i.done: - default: - close(i.done) - } - return i.db.Close() -} - -func (i *PostgresDB) GetEnvelope(key *DBKey) ([]byte, error) { - statement := `SELECT data FROM envelopes WHERE id = $1` - - stmt, err := i.db.Prepare(statement) - if err != nil { - return nil, err - } - defer stmt.Close() - - var envelope []byte - - if err = stmt.QueryRow(key.Bytes()).Scan(&envelope); err != nil { - return nil, err - } - - return envelope, nil -} - -func (i *PostgresDB) Prune(t time.Time, batch int) (int, error) { - var zero types.Hash - var emptyTopic types.TopicType - kl := NewDBKey(0, emptyTopic, zero) - ku := NewDBKey(uint32(t.Unix()), emptyTopic, zero) - statement := "DELETE FROM envelopes WHERE id BETWEEN $1 AND $2" - - stmt, err := i.db.Prepare(statement) - if err != nil { - return 0, err - } - defer stmt.Close() - - result, err := stmt.Exec(kl.Bytes(), ku.Bytes()) - if err != nil { - return 0, err - } - rows, err := result.RowsAffected() - if err != nil { - return 0, err - } - return int(rows), nil -} - -func (i *PostgresDB) SaveEnvelope(env types.Envelope) error { - topic := env.Topic() - key := NewDBKey(env.Expiry()-env.TTL(), topic, env.Hash()) - rawEnvelope, err := rlp.EncodeToBytes(env.Unwrap()) - if err != nil { - log.Error(fmt.Sprintf("rlp.EncodeToBytes failed: %s", err)) - archivedErrorsCounter.WithLabelValues(i.name).Inc() - return err - } - if rawEnvelope == nil { - archivedErrorsCounter.WithLabelValues(i.name).Inc() - return errors.New("failed to encode envelope to bytes") - } - - statement := "INSERT INTO envelopes (id, data, topic, bloom) VALUES ($1, $2, $3, B'" - statement += toBitString(env.Bloom()) - statement += "'::bit(512)) ON CONFLICT (id) DO NOTHING;" - stmt, err := i.db.Prepare(statement) - if err != nil { - return err - } - defer stmt.Close() - - _, err = stmt.Exec( - key.Bytes(), - rawEnvelope, - topicToByte(topic), - ) - - if err != nil { - archivedErrorsCounter.WithLabelValues(i.name).Inc() - return err - } - - archivedEnvelopesGauge.WithLabelValues(i.name).Inc() - archivedEnvelopeSizeMeter.WithLabelValues(i.name).Observe( - float64(waku.EnvelopeHeaderLength + env.Size())) - - return nil -} - -func topicToByte(t types.TopicType) []byte { - return []byte{t[0], t[1], t[2], t[3]} -} - -func toBitString(bloom []byte) string { - val := "" - for _, n := range bloom { - val += fmt.Sprintf("%08b", n) - } - return val -} diff --git a/mailserver/mailserver_db_postgres_test.go b/mailserver/mailserver_db_postgres_test.go deleted file mode 100644 index 780442e79..000000000 --- a/mailserver/mailserver_db_postgres_test.go +++ /dev/null @@ -1,129 +0,0 @@ -// In order to run these tests, you must run a PostgreSQL database. -// -// Using Docker: -// docker run -e POSTGRES_HOST_AUTH_METHOD=trust -d -p 5432:5432 postgres:9.6-alpine -// - -package mailserver - -import ( - "testing" - "time" - - "github.com/stretchr/testify/suite" - - "github.com/ethereum/go-ethereum/rlp" - - gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" - "github.com/status-im/status-go/eth-node/crypto" - "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/postgres" - waku "github.com/status-im/status-go/waku/common" -) - -func TestMailServerPostgresDBSuite(t *testing.T) { - suite.Run(t, new(MailServerPostgresDBSuite)) -} - -type MailServerPostgresDBSuite struct { - suite.Suite -} - -func (s *MailServerPostgresDBSuite) SetupSuite() { - // ResetDefaultTestPostgresDB Required to completely reset the Postgres DB - err := postgres.ResetDefaultTestPostgresDB() - s.NoError(err) -} - -func (s *MailServerPostgresDBSuite) TestPostgresDB_BuildIteratorWithBloomFilter() { - topic := []byte{0xaa, 0xbb, 0xcc, 0xdd} - - db, err := NewPostgresDB(postgres.DefaultTestURI) - s.NoError(err) - defer db.Close() - - envelope, err := newTestEnvelope(topic) - s.NoError(err) - err = db.SaveEnvelope(envelope) - s.NoError(err) - - iter, err := db.BuildIterator(CursorQuery{ - start: NewDBKey(uint32(time.Now().Add(-time.Hour).Unix()), types.BytesToTopic(topic), types.Hash{}).Bytes(), - end: NewDBKey(uint32(time.Now().Add(time.Second).Unix()), types.BytesToTopic(topic), types.Hash{}).Bytes(), - bloom: types.TopicToBloom(types.BytesToTopic(topic)), - limit: 10, - }) - s.NoError(err) - hasNext := iter.Next() - s.True(hasNext) - rawValue, err := iter.GetEnvelopeByBloomFilter(nil) - s.NoError(err) - s.NotEmpty(rawValue) - var receivedEnvelope waku.Envelope - err = rlp.DecodeBytes(rawValue, &receivedEnvelope) - s.NoError(err) - s.EqualValues(waku.BytesToTopic(topic), receivedEnvelope.Topic) - - err = iter.Release() - s.NoError(err) - s.NoError(iter.Error()) -} - -func (s *MailServerPostgresDBSuite) TestPostgresDB_BuildIteratorWithTopic() { - topic := []byte{0x01, 0x02, 0x03, 0x04} - - db, err := NewPostgresDB(postgres.DefaultTestURI) - s.NoError(err) - defer db.Close() - - envelope, err := newTestEnvelope(topic) - s.NoError(err) - err = db.SaveEnvelope(envelope) - s.NoError(err) - - iter, err := db.BuildIterator(CursorQuery{ - start: NewDBKey(uint32(time.Now().Add(-time.Hour).Unix()), types.BytesToTopic(topic), types.Hash{}).Bytes(), - end: NewDBKey(uint32(time.Now().Add(time.Second).Unix()), types.BytesToTopic(topic), types.Hash{}).Bytes(), - topics: [][]byte{topic}, - limit: 10, - }) - s.NoError(err) - hasNext := iter.Next() - s.True(hasNext) - rawValue, err := iter.GetEnvelopeByBloomFilter(nil) - s.NoError(err) - s.NotEmpty(rawValue) - var receivedEnvelope waku.Envelope - err = rlp.DecodeBytes(rawValue, &receivedEnvelope) - s.NoError(err) - s.EqualValues(waku.BytesToTopic(topic), receivedEnvelope.Topic) - - err = iter.Release() - s.NoError(err) - s.NoError(iter.Error()) -} - -func newTestEnvelope(topic []byte) (types.Envelope, error) { - privateKey, err := crypto.GenerateKey() - if err != nil { - return nil, err - } - params := waku.MessageParams{ - TTL: 10, - PoW: 2.0, - Payload: []byte("hello world"), - WorkTime: 1, - Topic: waku.BytesToTopic(topic), - Dst: &privateKey.PublicKey, - } - message, err := waku.NewSentMessage(¶ms) - if err != nil { - return nil, err - } - now := time.Now() - envelope, err := message.Wrap(¶ms, now) - if err != nil { - return nil, err - } - return gethbridge.NewWakuEnvelope(envelope), nil -} diff --git a/mailserver/mailserver_test.go b/mailserver/mailserver_test.go deleted file mode 100644 index 9ed2e2337..000000000 --- a/mailserver/mailserver_test.go +++ /dev/null @@ -1,655 +0,0 @@ -// Copyright 2017 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package mailserver - -import ( - "crypto/ecdsa" - "encoding/binary" - "errors" - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/suite" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/rlp" - - "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/params" - waku "github.com/status-im/status-go/waku" - wakucommon "github.com/status-im/status-go/waku/common" -) - -const powRequirement = 0.00001 - -var keyID string -var seed = time.Now().Unix() -var testPayload = []byte("test payload") - -type ServerTestParams struct { - topic types.TopicType - birth uint32 - low uint32 - upp uint32 - limit uint32 - key *ecdsa.PrivateKey -} - -func TestMailserverSuite(t *testing.T) { - suite.Run(t, new(MailserverSuite)) -} - -type MailserverSuite struct { - suite.Suite - server *WakuMailServer - shh *waku.Waku - config *params.WakuConfig - dataDir string -} - -func (s *MailserverSuite) SetupTest() { - s.server = &WakuMailServer{} - s.shh = waku.New(&waku.DefaultConfig, nil) - s.shh.RegisterMailServer(s.server) - - tmpDir := s.T().TempDir() - s.dataDir = tmpDir - - s.config = ¶ms.WakuConfig{ - DataDir: tmpDir, - MailServerPassword: "testpassword", - } -} - -func (s *MailserverSuite) TestInit() { - testCases := []struct { - config params.WakuConfig - expectedError error - info string - }{ - { - config: params.WakuConfig{DataDir: ""}, - expectedError: errDirectoryNotProvided, - info: "config with empty DataDir", - }, - { - config: params.WakuConfig{ - DataDir: s.config.DataDir, - MailServerPassword: "pwd", - }, - expectedError: nil, - info: "config with correct DataDir and Password", - }, - { - config: params.WakuConfig{ - DataDir: s.config.DataDir, - MailServerPassword: "pwd", - MailServerRateLimit: 5, - }, - expectedError: nil, - info: "config with rate limit", - }, - } - - for _, testCase := range testCases { - // to satisfy gosec: C601 checks - tc := testCase - s.T().Run(tc.info, func(*testing.T) { - mailServer := &WakuMailServer{} - shh := waku.New(&waku.DefaultConfig, nil) - shh.RegisterMailServer(mailServer) - - err := mailServer.Init(shh, &tc.config) - s.Require().Equal(tc.expectedError, err) - if err == nil { - defer mailServer.Close() - } - - // db should be open only if there was no error - if tc.expectedError == nil { - s.NotNil(mailServer.ms.db) - } else { - s.Nil(mailServer.ms) - } - - if tc.config.MailServerRateLimit > 0 { - s.NotNil(mailServer.ms.rateLimiter) - } - }) - } -} - -func (s *MailserverSuite) TestArchive() { - config := *s.config - - err := s.server.Init(s.shh, &config) - s.Require().NoError(err) - defer s.server.Close() - - env, err := generateEnvelope(time.Now()) - s.NoError(err) - rawEnvelope, err := rlp.EncodeToBytes(env) - s.NoError(err) - - s.server.Archive(env) - key := NewDBKey(env.Expiry-env.TTL, types.TopicType(env.Topic), types.Hash(env.Hash())) - archivedEnvelope, err := s.server.ms.db.GetEnvelope(key) - s.NoError(err) - - s.Equal(rawEnvelope, archivedEnvelope) -} - -func (s *MailserverSuite) TestManageLimits() { - err := s.server.Init(s.shh, s.config) - s.NoError(err) - s.server.ms.rateLimiter = newRateLimiter(time.Duration(5) * time.Millisecond) - s.False(s.server.ms.exceedsPeerRequests(types.BytesToHash([]byte("peerID")))) - s.Equal(1, len(s.server.ms.rateLimiter.db)) - firstSaved := s.server.ms.rateLimiter.db["peerID"] - - // second call when limit is not accomplished does not store a new limit - s.True(s.server.ms.exceedsPeerRequests(types.BytesToHash([]byte("peerID")))) - s.Equal(1, len(s.server.ms.rateLimiter.db)) - s.Equal(firstSaved, s.server.ms.rateLimiter.db["peerID"]) -} - -func (s *MailserverSuite) TestDBKey() { - var h types.Hash - var emptyTopic types.TopicType - i := uint32(time.Now().Unix()) - k := NewDBKey(i, emptyTopic, h) - s.Equal(len(k.Bytes()), DBKeyLength, "wrong DB key length") - s.Equal(byte(i%0x100), k.Bytes()[3], "raw representation should be big endian") - s.Equal(byte(i/0x1000000), k.Bytes()[0], "big endian expected") -} - -func (s *MailserverSuite) TestRequestPaginationLimit() { - s.setupServer(s.server) - defer s.server.Close() - - var ( - sentEnvelopes []*wakucommon.Envelope - sentHashes []common.Hash - receivedHashes []common.Hash - archiveKeys []string - ) - - now := time.Now() - count := uint32(10) - - for i := count; i > 0; i-- { - sentTime := now.Add(time.Duration(-i) * time.Second) - env, err := generateEnvelope(sentTime) - s.NoError(err) - s.server.Archive(env) - key := NewDBKey(env.Expiry-env.TTL, types.TopicType(env.Topic), types.Hash(env.Hash())) - archiveKeys = append(archiveKeys, fmt.Sprintf("%x", key.Cursor())) - sentEnvelopes = append(sentEnvelopes, env) - sentHashes = append(sentHashes, env.Hash()) - } - - reqLimit := uint32(6) - peerID, request, err := s.prepareRequest(sentEnvelopes, reqLimit) - s.NoError(err) - payload, err := s.server.decompositeRequest(peerID, request) - s.NoError(err) - s.Nil(payload.Cursor) - s.Equal(reqLimit, payload.Limit) - - receivedHashes, cursor, _ := processRequestAndCollectHashes(s.server, payload) - - // 10 envelopes sent - s.Equal(count, uint32(len(sentEnvelopes))) - // 6 envelopes received - s.Len(receivedHashes, int(payload.Limit)) - // the 6 envelopes received should be in forward order - s.Equal(sentHashes[:payload.Limit], receivedHashes) - // cursor should be the key of the last envelope of the last page - s.Equal(archiveKeys[payload.Limit-1], fmt.Sprintf("%x", cursor)) - - // second page - payload.Cursor = cursor - receivedHashes, cursor, _ = processRequestAndCollectHashes(s.server, payload) - - // 4 envelopes received - s.Equal(int(count-payload.Limit), len(receivedHashes)) - // cursor is nil because there are no other pages - s.Nil(cursor) -} - -func (s *MailserverSuite) TestMailServer() { - s.setupServer(s.server) - defer s.server.Close() - - env, err := generateEnvelope(time.Now()) - s.NoError(err) - - s.server.Archive(env) - - testCases := []struct { - params *ServerTestParams - expect bool - isOK bool - info string - }{ - { - params: s.defaultServerParams(env), - expect: true, - isOK: true, - info: "Processing a request where from and to are equal to an existing register, should provide results", - }, - { - params: func() *ServerTestParams { - params := s.defaultServerParams(env) - params.low = params.birth + 1 - params.upp = params.birth + 1 - - return params - }(), - expect: false, - isOK: true, - info: "Processing a request where from and to are greater than any existing register, should not provide results", - }, - { - params: func() *ServerTestParams { - params := s.defaultServerParams(env) - params.upp = params.birth + 1 - params.topic[0] = 0xFF - - return params - }(), - expect: false, - isOK: true, - info: "Processing a request where to is greater than any existing register and with a specific topic, should not provide results", - }, - { - params: func() *ServerTestParams { - params := s.defaultServerParams(env) - params.low = params.birth - params.upp = params.birth - 1 - - return params - }(), - isOK: false, - info: "Processing a request where to is lower than from should fail", - }, - { - params: func() *ServerTestParams { - params := s.defaultServerParams(env) - params.low = 0 - params.upp = params.birth + 24 - - return params - }(), - isOK: false, - info: "Processing a request where difference between from and to is > 24 should fail", - }, - } - for _, testCase := range testCases { - // to satisfy gosec: C601 checks - tc := testCase - s.T().Run(tc.info, func(*testing.T) { - request := s.createRequest(tc.params) - src := crypto.FromECDSAPub(&tc.params.key.PublicKey) - payload, err := s.server.decompositeRequest(src, request) - s.Equal(tc.isOK, err == nil) - if err == nil { - s.Equal(tc.params.low, payload.Lower) - s.Equal(tc.params.upp, payload.Upper) - s.Equal(tc.params.limit, payload.Limit) - s.Equal(types.TopicToBloom(tc.params.topic), payload.Bloom) - s.Equal(tc.expect, s.messageExists(env, tc.params.low, tc.params.upp, payload.Bloom, tc.params.limit)) - - src[0]++ - _, err = s.server.decompositeRequest(src, request) - s.True(err == nil) - } - }) - } -} - -func (s *MailserverSuite) TestDecodeRequest() { - s.setupServer(s.server) - defer s.server.Close() - - payload := MessagesRequestPayload{ - Lower: 50, - Upper: 100, - Bloom: []byte{0x01}, - Topics: [][]byte{}, - Limit: 10, - Cursor: []byte{}, - Batch: true, - } - data, err := rlp.EncodeToBytes(payload) - s.Require().NoError(err) - - id, err := s.shh.NewKeyPair() - s.Require().NoError(err) - srcKey, err := s.shh.GetPrivateKey(id) - s.Require().NoError(err) - - env := s.createEnvelope(types.TopicType{0x01}, data, srcKey) - - decodedPayload, err := s.server.decodeRequest(nil, env) - s.Require().NoError(err) - s.Equal(payload, decodedPayload) -} - -func (s *MailserverSuite) TestDecodeRequestNoUpper() { - s.setupServer(s.server) - defer s.server.Close() - - payload := MessagesRequestPayload{ - Lower: 50, - Bloom: []byte{0x01}, - Limit: 10, - Cursor: []byte{}, - Batch: true, - } - data, err := rlp.EncodeToBytes(payload) - s.Require().NoError(err) - - id, err := s.shh.NewKeyPair() - s.Require().NoError(err) - srcKey, err := s.shh.GetPrivateKey(id) - s.Require().NoError(err) - - env := s.createEnvelope(types.TopicType{0x01}, data, srcKey) - - decodedPayload, err := s.server.decodeRequest(nil, env) - s.Require().NoError(err) - s.NotEqual(0, decodedPayload.Upper) -} - -func (s *MailserverSuite) TestProcessRequestDeadlockHandling() { - s.setupServer(s.server) - defer s.server.Close() - - var archievedEnvelopes []*wakucommon.Envelope - - now := time.Now() - count := uint32(10) - - // Archieve some envelopes. - for i := count; i > 0; i-- { - sentTime := now.Add(time.Duration(-i) * time.Second) - env, err := generateEnvelope(sentTime) - s.NoError(err) - s.server.Archive(env) - archievedEnvelopes = append(archievedEnvelopes, env) - } - - // Prepare a request. - peerID, request, err := s.prepareRequest(archievedEnvelopes, 5) - s.NoError(err) - payload, err := s.server.decompositeRequest(peerID, request) - s.NoError(err) - - testCases := []struct { - Name string - Timeout time.Duration - Verify func( - Iterator, - time.Duration, // processRequestInBundles timeout - chan []rlp.RawValue, - ) - }{ - { - Name: "finish processing using `done` channel", - Timeout: time.Second * 5, - Verify: func( - iter Iterator, - timeout time.Duration, - bundles chan []rlp.RawValue, - ) { - done := make(chan struct{}) - processFinished := make(chan struct{}) - - go func() { - s.server.ms.processRequestInBundles(iter, payload.Bloom, payload.Topics, int(payload.Limit), timeout, "req-01", bundles, done) - close(processFinished) - }() - go close(done) - - select { - case <-processFinished: - case <-time.After(time.Second): - s.FailNow("waiting for processing finish timed out") - } - }, - }, - { - Name: "finish processing due to timeout", - Timeout: time.Second, - Verify: func( - iter Iterator, - timeout time.Duration, - bundles chan []rlp.RawValue, - ) { - done := make(chan struct{}) // won't be closed because we test timeout of `processRequestInBundles()` - processFinished := make(chan struct{}) - - go func() { - s.server.ms.processRequestInBundles(iter, payload.Bloom, payload.Topics, int(payload.Limit), time.Second, "req-01", bundles, done) - close(processFinished) - }() - - select { - case <-processFinished: - case <-time.After(time.Second * 5): - s.FailNow("waiting for processing finish timed out") - } - }, - }, - } - - for _, tc := range testCases { - s.T().Run(tc.Name, func(t *testing.T) { - iter, err := s.server.ms.createIterator(payload) - s.Require().NoError(err) - - defer func() { _ = iter.Release() }() - - // Nothing reads from this unbuffered channel which simulates a situation - // when a connection between a peer and mail server was dropped. - bundles := make(chan []rlp.RawValue) - - tc.Verify(iter, tc.Timeout, bundles) - }) - } -} - -func (s *MailserverSuite) messageExists(envelope *wakucommon.Envelope, low, upp uint32, bloom []byte, limit uint32) bool { - receivedHashes, _, _ := processRequestAndCollectHashes(s.server, MessagesRequestPayload{ - Lower: low, - Upper: upp, - Bloom: bloom, - Limit: limit, - }) - for _, hash := range receivedHashes { - if hash == envelope.Hash() { - return true - } - } - return false -} - -func (s *MailserverSuite) setupServer(server *WakuMailServer) { - const password = "password_for_this_test" - - s.shh = waku.New(&waku.DefaultConfig, nil) - s.shh.RegisterMailServer(server) - - err := server.Init(s.shh, ¶ms.WakuConfig{ - DataDir: s.dataDir, - MailServerPassword: password, - MinimumPoW: powRequirement, - }) - if err != nil { - s.T().Fatal(err) - } - - keyID, err = s.shh.AddSymKeyFromPassword(password) - if err != nil { - s.T().Fatalf("failed to create symmetric key for mail request: %s", err) - } -} - -func (s *MailserverSuite) prepareRequest(envelopes []*wakucommon.Envelope, limit uint32) ( - []byte, *wakucommon.Envelope, error, -) { - if len(envelopes) == 0 { - return nil, nil, errors.New("envelopes is empty") - } - - now := time.Now() - - params := s.defaultServerParams(envelopes[0]) - params.low = uint32(now.Add(time.Duration(-len(envelopes)) * time.Second).Unix()) - params.upp = uint32(now.Unix()) - params.limit = limit - - request := s.createRequest(params) - peerID := crypto.FromECDSAPub(¶ms.key.PublicKey) - - return peerID, request, nil -} - -func (s *MailserverSuite) defaultServerParams(env *wakucommon.Envelope) *ServerTestParams { - id, err := s.shh.NewKeyPair() - if err != nil { - s.T().Fatalf("failed to generate new key pair with seed %d: %s.", seed, err) - } - testPeerID, err := s.shh.GetPrivateKey(id) - if err != nil { - s.T().Fatalf("failed to retrieve new key pair with seed %d: %s.", seed, err) - } - birth := env.Expiry - env.TTL - - return &ServerTestParams{ - topic: types.TopicType(env.Topic), - birth: birth, - low: birth - 1, - upp: birth + 1, - limit: 0, - key: testPeerID, - } -} - -func (s *MailserverSuite) createRequest(p *ServerTestParams) *wakucommon.Envelope { - bloom := types.TopicToBloom(p.topic) - data := make([]byte, 8) - binary.BigEndian.PutUint32(data, p.low) - binary.BigEndian.PutUint32(data[4:], p.upp) - data = append(data, bloom...) - - if p.limit != 0 { - limitData := make([]byte, 4) - binary.BigEndian.PutUint32(limitData, p.limit) - data = append(data, limitData...) - } - - return s.createEnvelope(p.topic, data, p.key) -} - -func (s *MailserverSuite) createEnvelope(topic types.TopicType, data []byte, srcKey *ecdsa.PrivateKey) *wakucommon.Envelope { - key, err := s.shh.GetSymKey(keyID) - if err != nil { - s.T().Fatalf("failed to retrieve sym key with seed %d: %s.", seed, err) - } - - params := &wakucommon.MessageParams{ - KeySym: key, - Topic: wakucommon.TopicType(topic), - Payload: data, - PoW: powRequirement * 2, - WorkTime: 2, - Src: srcKey, - } - - msg, err := wakucommon.NewSentMessage(params) - if err != nil { - s.T().Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - - env, err := msg.Wrap(params, time.Now()) - if err != nil { - s.T().Fatalf("failed to wrap with seed %d: %s.", seed, err) - } - return env -} - -func generateEnvelopeWithKeys(sentTime time.Time, keySym []byte, keyAsym *ecdsa.PublicKey) (*wakucommon.Envelope, error) { - params := &wakucommon.MessageParams{ - Topic: wakucommon.TopicType{0x1F, 0x7E, 0xA1, 0x7F}, - Payload: testPayload, - PoW: powRequirement, - WorkTime: 2, - } - - if len(keySym) > 0 { - params.KeySym = keySym - } else if keyAsym != nil { - params.Dst = keyAsym - } - - msg, err := wakucommon.NewSentMessage(params) - if err != nil { - return nil, fmt.Errorf("failed to create new message with seed %d: %s", seed, err) - } - env, err := msg.Wrap(params, sentTime) - if err != nil { - return nil, fmt.Errorf("failed to wrap with seed %d: %s", seed, err) - } - - return env, nil -} - -func generateEnvelope(sentTime time.Time) (*wakucommon.Envelope, error) { - h := crypto.Keccak256Hash([]byte("test sample data")) - return generateEnvelopeWithKeys(sentTime, h[:], nil) -} - -func processRequestAndCollectHashes(server *WakuMailServer, payload MessagesRequestPayload) ([]common.Hash, []byte, types.Hash) { - iter, _ := server.ms.createIterator(payload) - defer func() { _ = iter.Release() }() - bundles := make(chan []rlp.RawValue, 10) - done := make(chan struct{}) - - var hashes []common.Hash - go func() { - for bundle := range bundles { - for _, rawEnvelope := range bundle { - var env *wakucommon.Envelope - if err := rlp.DecodeBytes(rawEnvelope, &env); err != nil { - panic(err) - } - hashes = append(hashes, env.Hash()) - } - } - close(done) - }() - - cursor, lastHash := server.ms.processRequestInBundles(iter, payload.Bloom, payload.Topics, int(payload.Limit), time.Minute, "req-01", bundles, done) - - <-done - - return hashes, cursor, lastHash -} diff --git a/mailserver/metrics.go b/mailserver/metrics.go deleted file mode 100644 index a7e713114..000000000 --- a/mailserver/metrics.go +++ /dev/null @@ -1,84 +0,0 @@ -package mailserver - -import prom "github.com/prometheus/client_golang/prometheus" - -// By default the /metrics endpoint is not available. -// It is exposed only if -metrics flag is set. - -var ( - envelopesCounter = prom.NewCounter(prom.CounterOpts{ - Name: "mailserver_envelopes_total", - Help: "Number of envelopes processed.", - }) - deliveryFailuresCounter = prom.NewCounterVec(prom.CounterOpts{ - Name: "mailserver_delivery_failures_total", - Help: "Number of requests that failed processing.", - }, []string{"type"}) - deliveryAttemptsCounter = prom.NewCounter(prom.CounterOpts{ - Name: "mailserver_delivery_attempts_total", - Help: "Number of Whisper envelopes processed.", - }) - requestsBatchedCounter = prom.NewCounter(prom.CounterOpts{ - Name: "mailserver_requests_batched_total", - Help: "Number of processed batched requests.", - }) - requestsInBundlesDuration = prom.NewHistogram(prom.HistogramOpts{ - Name: "mailserver_requests_bundle_process_duration_seconds", - Help: "The time it took to process message bundles.", - }) - syncFailuresCounter = prom.NewCounterVec(prom.CounterOpts{ - Name: "mailserver_sync_failures_total", - Help: "Number of failures processing a sync requests.", - }, []string{"type"}) - syncAttemptsCounter = prom.NewCounter(prom.CounterOpts{ - Name: "mailserver_sync_attempts_total", - Help: "Number of attempts are processing a sync requests.", - }) - sendRawEnvelopeDuration = prom.NewHistogram(prom.HistogramOpts{ - Name: "mailserver_send_raw_envelope_duration_seconds", - Help: "The time it took to send a Whisper envelope.", - }) - sentEnvelopeBatchSizeMeter = prom.NewHistogram(prom.HistogramOpts{ - Name: "mailserver_sent_envelope_batch_size_bytes", - Help: "Size of processed Whisper envelopes in bytes.", - Buckets: prom.ExponentialBuckets(1024, 4, 10), - }) - mailDeliveryDuration = prom.NewHistogram(prom.HistogramOpts{ - Name: "mailserver_delivery_duration_seconds", - Help: "Time it takes to deliver messages to a Whisper peer.", - }) - archivedErrorsCounter = prom.NewCounterVec(prom.CounterOpts{ - Name: "mailserver_archived_envelopes_failures_total", - Help: "Number of failures storing a Whisper envelope.", - }, []string{"db"}) - archivedEnvelopesGauge = prom.NewGaugeVec(prom.GaugeOpts{ - Name: "mailserver_archived_envelopes_total", - Help: "Number of envelopes saved in the DB.", - }, []string{"db"}) - archivedEnvelopeSizeMeter = prom.NewHistogramVec(prom.HistogramOpts{ - Name: "mailserver_archived_envelope_size_bytes", - Help: "Size of envelopes saved.", - Buckets: prom.ExponentialBuckets(1024, 2, 11), - }, []string{"db"}) - envelopeQueriesCounter = prom.NewCounterVec(prom.CounterOpts{ - Name: "mailserver_envelope_queries_total", - Help: "Number of queries for envelopes in the DB.", - }, []string{"filter", "history"}) -) - -func init() { - prom.MustRegister(envelopesCounter) - prom.MustRegister(deliveryFailuresCounter) - prom.MustRegister(deliveryAttemptsCounter) - prom.MustRegister(requestsBatchedCounter) - prom.MustRegister(requestsInBundlesDuration) - prom.MustRegister(syncFailuresCounter) - prom.MustRegister(syncAttemptsCounter) - prom.MustRegister(sendRawEnvelopeDuration) - prom.MustRegister(sentEnvelopeBatchSizeMeter) - prom.MustRegister(mailDeliveryDuration) - prom.MustRegister(archivedErrorsCounter) - prom.MustRegister(archivedEnvelopesGauge) - prom.MustRegister(archivedEnvelopeSizeMeter) - prom.MustRegister(envelopeQueriesCounter) -} diff --git a/mailserver/migrations/bindata.go b/mailserver/migrations/bindata.go deleted file mode 100644 index 227f32bb4..000000000 --- a/mailserver/migrations/bindata.go +++ /dev/null @@ -1,319 +0,0 @@ -// Code generated by go-bindata. DO NOT EDIT. -// sources: -// 1557732988_initialize_db.down.sql (72B) -// 1557732988_initialize_db.up.sql (278B) -// static.go (198B) - -package migrations - -import ( - "bytes" - "compress/gzip" - "crypto/sha256" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" -) - -func bindataRead(data []byte, name string) ([]byte, error) { - gz, err := gzip.NewReader(bytes.NewBuffer(data)) - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - - var buf bytes.Buffer - _, err = io.Copy(&buf, gz) - clErr := gz.Close() - - if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) - } - if clErr != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -type asset struct { - bytes []byte - info os.FileInfo - digest [sha256.Size]byte -} - -type bindataFileInfo struct { - name string - size int64 - mode os.FileMode - modTime time.Time -} - -func (fi bindataFileInfo) Name() string { - return fi.name -} -func (fi bindataFileInfo) Size() int64 { - return fi.size -} -func (fi bindataFileInfo) Mode() os.FileMode { - return fi.mode -} -func (fi bindataFileInfo) ModTime() time.Time { - return fi.modTime -} -func (fi bindataFileInfo) IsDir() bool { - return false -} -func (fi bindataFileInfo) Sys() interface{} { - return nil -} - -var __1557732988_initialize_dbDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\xf0\xf4\x73\x71\x8d\x50\xc8\x4c\x89\x4f\xca\xc9\xcf\xcf\x8d\xcf\x4c\xa9\xb0\xe6\x42\x95\x28\xc9\x2f\xc8\x4c\x46\x92\x08\x71\x74\xf2\x71\x55\x48\xcd\x2b\x4b\xcd\xc9\x2f\x48\x2d\xb6\xe6\x02\x04\x00\x00\xff\xff\x6b\x93\xaa\x08\x48\x00\x00\x00") - -func _1557732988_initialize_dbDownSqlBytes() ([]byte, error) { - return bindataRead( - __1557732988_initialize_dbDownSql, - "1557732988_initialize_db.down.sql", - ) -} - -func _1557732988_initialize_dbDownSql() (*asset, error) { - bytes, err := _1557732988_initialize_dbDownSqlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "1557732988_initialize_db.down.sql", size: 72, mode: os.FileMode(0664), modTime: time.Unix(1700000000, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x77, 0x40, 0x78, 0xb7, 0x71, 0x3c, 0x20, 0x3b, 0xc9, 0xb, 0x2f, 0x49, 0xe4, 0xff, 0x1c, 0x84, 0x54, 0xa1, 0x30, 0xe3, 0x90, 0xf8, 0x73, 0xda, 0xb0, 0x2a, 0xea, 0x8e, 0xf1, 0x82, 0xe7, 0xd2}} - return a, nil -} - -var __1557732988_initialize_dbUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x0e\x72\x75\x0c\x71\x55\x08\x71\x74\xf2\x71\x55\x48\xcd\x2b\x4b\xcd\xc9\x2f\x48\x2d\x56\xd0\xc8\x4c\x51\x70\x8a\x0c\x71\x75\x54\xf0\xf3\x0f\x51\xf0\x0b\xf5\xf1\x51\x08\xf5\xf3\x0c\x0c\x75\xd5\x51\x48\x49\x2c\x49\x44\x93\xd3\x51\x28\xc9\x2f\xc8\x4c\xc6\x10\x4d\xca\xc9\xcf\xcf\x55\x70\xf2\x0c\xd1\x30\x35\x34\xd2\x84\x4b\x68\x5a\x73\x71\x41\xed\xf5\xf4\x73\x71\x8d\x50\xc8\x4c\x89\x07\x2b\x8d\xcf\x4c\xa9\x50\xf0\xf7\x43\x73\x87\x8b\x6b\xb0\x33\xd4\x2c\x4d\x6b\x0c\x8d\x60\x9b\xf1\x69\x04\x2b\x40\xd7\x88\x5d\x97\x06\x4c\x2d\x20\x00\x00\xff\xff\x0b\x7d\x91\x3e\x16\x01\x00\x00") - -func _1557732988_initialize_dbUpSqlBytes() ([]byte, error) { - return bindataRead( - __1557732988_initialize_dbUpSql, - "1557732988_initialize_db.up.sql", - ) -} - -func _1557732988_initialize_dbUpSql() (*asset, error) { - bytes, err := _1557732988_initialize_dbUpSqlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "1557732988_initialize_db.up.sql", size: 278, mode: os.FileMode(0664), modTime: time.Unix(1700000000, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf5, 0x85, 0x41, 0x7a, 0xba, 0x4f, 0xa3, 0x43, 0xc0, 0x63, 0xfa, 0x2c, 0xd1, 0xc5, 0xbb, 0x20, 0xa0, 0x64, 0xa8, 0x3b, 0x65, 0x82, 0xa2, 0x14, 0x28, 0x18, 0x7c, 0x8b, 0x3a, 0x7a, 0xfd, 0xe0}} - return a, nil -} - -var _staticGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\xcc\x31\x4a\x06\x41\x0c\xc5\xf1\x7e\x4e\xf1\x4a\x05\x67\xa2\x95\x20\xd8\x8b\x28\x08\x7a\x81\xec\x6e\xc8\x17\xd6\x99\x2c\x99\xe8\xf9\x6d\x56\xc4\xd7\x3d\xfe\xf0\x23\xc2\x1b\xaf\x3b\xab\x60\x26\xa7\xad\x90\xbe\xc8\x36\x7f\xdf\xd5\xf3\xfb\x0d\x9e\x3e\x5e\x5f\xae\x11\x32\xfd\x2b\x56\x99\x08\xd3\x4b\xc2\x46\x3a\xf2\x22\x58\x6c\x70\x98\xcc\x72\xfc\x93\x4a\x21\x52\x7f\x50\x19\x12\x9c\x02\xf5\xba\xd8\xd8\x38\x19\xb5\xfb\x96\xd6\xe5\xf1\xee\xfe\xf6\x1c\xea\xb1\x2b\xba\x69\x70\x9a\x8f\x89\xea\x68\x8d\x5a\xa3\xce\xf6\x39\x25\xbe\x25\xe8\x2f\xd3\x49\x35\x75\xb4\xf2\x13\x00\x00\xff\xff\x9a\xab\xca\x11\xc6\x00\x00\x00") - -func staticGoBytes() ([]byte, error) { - return bindataRead( - _staticGo, - "static.go", - ) -} - -func staticGo() (*asset, error) { - bytes, err := staticGoBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "static.go", size: 198, mode: os.FileMode(0664), modTime: time.Unix(1700000000, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb, 0xd2, 0xfd, 0xbf, 0xe5, 0xff, 0xcb, 0x54, 0xec, 0x41, 0x23, 0x7b, 0xc0, 0xeb, 0x55, 0xb8, 0x69, 0xd7, 0x57, 0xf1, 0x83, 0x13, 0x88, 0x55, 0xd9, 0x73, 0xdc, 0x93, 0xee, 0x23, 0xe3, 0xe9}} - return a, nil -} - -// Asset loads and returns the asset for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func Asset(name string) ([]byte, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[canonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) - } - return a.bytes, nil - } - return nil, fmt.Errorf("Asset %s not found", name) -} - -// AssetString returns the asset contents as a string (instead of a []byte). -func AssetString(name string) (string, error) { - data, err := Asset(name) - return string(data), err -} - -// MustAsset is like Asset but panics when Asset would return an error. -// It simplifies safe initialization of global variables. -func MustAsset(name string) []byte { - a, err := Asset(name) - if err != nil { - panic("asset: Asset(" + name + "): " + err.Error()) - } - - return a -} - -// MustAssetString is like AssetString but panics when Asset would return an -// error. It simplifies safe initialization of global variables. -func MustAssetString(name string) string { - return string(MustAsset(name)) -} - -// AssetInfo loads and returns the asset info for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func AssetInfo(name string) (os.FileInfo, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[canonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) - } - return a.info, nil - } - return nil, fmt.Errorf("AssetInfo %s not found", name) -} - -// AssetDigest returns the digest of the file with the given name. It returns an -// error if the asset could not be found or the digest could not be loaded. -func AssetDigest(name string) ([sha256.Size]byte, error) { - canonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[canonicalName]; ok { - a, err := f() - if err != nil { - return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err) - } - return a.digest, nil - } - return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name) -} - -// Digests returns a map of all known files and their checksums. -func Digests() (map[string][sha256.Size]byte, error) { - mp := make(map[string][sha256.Size]byte, len(_bindata)) - for name := range _bindata { - a, err := _bindata[name]() - if err != nil { - return nil, err - } - mp[name] = a.digest - } - return mp, nil -} - -// AssetNames returns the names of the assets. -func AssetNames() []string { - names := make([]string, 0, len(_bindata)) - for name := range _bindata { - names = append(names, name) - } - return names -} - -// _bindata is a table, holding each asset generator, mapped to its name. -var _bindata = map[string]func() (*asset, error){ - "1557732988_initialize_db.down.sql": _1557732988_initialize_dbDownSql, - - "1557732988_initialize_db.up.sql": _1557732988_initialize_dbUpSql, - - "static.go": staticGo, -} - -// AssetDir returns the file names below a certain -// directory embedded in the file by go-bindata. -// For example if you run go-bindata on data/... and data contains the -// following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png -// then AssetDir("data") would return []string{"foo.txt", "img"}, -// AssetDir("data/img") would return []string{"a.png", "b.png"}, -// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and -// AssetDir("") will return []string{"data"}. -func AssetDir(name string) ([]string, error) { - node := _bintree - if len(name) != 0 { - canonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(canonicalName, "/") - for _, p := range pathList { - node = node.Children[p] - if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - } - } - if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - rv := make([]string, 0, len(node.Children)) - for childName := range node.Children { - rv = append(rv, childName) - } - return rv, nil -} - -type bintree struct { - Func func() (*asset, error) - Children map[string]*bintree -} - -var _bintree = &bintree{nil, map[string]*bintree{ - "1557732988_initialize_db.down.sql": &bintree{_1557732988_initialize_dbDownSql, map[string]*bintree{}}, - "1557732988_initialize_db.up.sql": &bintree{_1557732988_initialize_dbUpSql, map[string]*bintree{}}, - "static.go": &bintree{staticGo, map[string]*bintree{}}, -}} - -// RestoreAsset restores an asset under the given directory. -func RestoreAsset(dir, name string) error { - data, err := Asset(name) - if err != nil { - return err - } - info, err := AssetInfo(name) - if err != nil { - return err - } - err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) - if err != nil { - return err - } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) - if err != nil { - return err - } - return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) -} - -// RestoreAssets restores an asset under the given directory recursively. -func RestoreAssets(dir, name string) error { - children, err := AssetDir(name) - // File - if err != nil { - return RestoreAsset(dir, name) - } - // Dir - for _, child := range children { - err = RestoreAssets(dir, filepath.Join(name, child)) - if err != nil { - return err - } - } - return nil -} - -func _filePath(dir, name string) string { - canonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) -} diff --git a/mailserver/request.go b/mailserver/request.go deleted file mode 100644 index 4ac3e8b15..000000000 --- a/mailserver/request.go +++ /dev/null @@ -1,51 +0,0 @@ -package mailserver - -import ( - "errors" - "time" -) - -const ( - maxMessagesRequestPayloadLimit = 1000 -) - -// MessagesRequestPayload is a payload sent to the Mail Server. -type MessagesRequestPayload struct { - // Lower is a lower bound of time range for which messages are requested. - Lower uint32 - // Upper is a lower bound of time range for which messages are requested. - Upper uint32 - // Bloom is a bloom filter to filter envelopes. - Bloom []byte - // Topics is a list of topics to filter envelopes. - Topics [][]byte - // Limit is the max number of envelopes to return. - Limit uint32 - // Cursor is used for pagination of the results. - Cursor []byte - // Batch set to true indicates that the client supports batched response. - Batch bool -} - -func (r *MessagesRequestPayload) SetDefaults() { - if r.Limit == 0 { - r.Limit = maxQueryLimit - } - - if r.Upper == 0 { - r.Upper = uint32(time.Now().Unix() + whisperTTLSafeThreshold) - } -} - -func (r MessagesRequestPayload) Validate() error { - if r.Upper < r.Lower { - return errors.New("query range is invalid: lower > upper") - } - if len(r.Bloom) == 0 && len(r.Topics) == 0 { - return errors.New("bloom filter and topics is empty") - } - if r.Limit > maxMessagesRequestPayloadLimit { - return errors.New("limit exceeds the maximum allowed value") - } - return nil -} diff --git a/node/get_status_node.go b/node/get_status_node.go index 8bbe25a86..b18e69ff0 100644 --- a/node/get_status_node.go +++ b/node/get_status_node.go @@ -432,7 +432,6 @@ func (n *StatusNode) startDiscovery() error { options := peers.NewDefaultOptions() // TODO(dshulyak) consider adding a flag to define this behaviour options.AllowStop = len(n.config.RegisterTopics) == 0 - options.TrustedMailServers = parseNodesToNodeID(n.config.ClusterConfig.TrustedMailServers) n.peerPool = peers.NewPeerPool( n.discovery, diff --git a/node/status_node_services.go b/node/status_node_services.go index 41aa52420..6bfc73eba 100644 --- a/node/status_node_services.go +++ b/node/status_node_services.go @@ -26,7 +26,6 @@ import ( "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/logutils" - "github.com/status-im/status-go/mailserver" "github.com/status-im/status-go/multiaccounts/accounts" "github.com/status-im/status-go/multiaccounts/settings" "github.com/status-im/status-go/params" @@ -297,13 +296,6 @@ func (b *StatusNode) wakuService(wakuCfg *params.WakuConfig, clusterCfg *params. w.SetTimeSource(timesource.Now) } - // enable mail service - if wakuCfg.EnableMailServer { - if err := registerWakuMailServer(w, wakuCfg); err != nil { - return nil, fmt.Errorf("failed to register WakuMailServer: %v", err) - } - } - if wakuCfg.LightClient { emptyBloomFilter := make([]byte, 64) if err := w.SetBloomFilter(emptyBloomFilter); err != nil { @@ -400,10 +392,7 @@ func setSettingsNotifier(db *accounts.Database, feed *event.Feed) { } func wakuRateLimiter(wakuCfg *params.WakuConfig, clusterCfg *params.ClusterConfig) *wakucommon.PeerRateLimiter { - enodes := append( - parseNodes(clusterCfg.StaticNodes), - parseNodes(clusterCfg.TrustedMailServers)..., - ) + enodes := parseNodes(clusterCfg.StaticNodes) var ( ips []string peerIDs []enode.ID @@ -606,13 +595,6 @@ func (b *StatusNode) peerService() *peer.Service { return b.peerSrvc } -func registerWakuMailServer(wakuService *waku.Waku, config *params.WakuConfig) (err error) { - var mailServer mailserver.WakuMailServer - wakuService.RegisterMailServer(&mailServer) - - return mailServer.Init(wakuService, config) -} - func appendIf(condition bool, services []common.StatusService, service common.StatusService) []common.StatusService { if !condition { return services diff --git a/nodecfg/node_config.go b/nodecfg/node_config.go index a2e8a40f8..c11541410 100644 --- a/nodecfg/node_config.go +++ b/nodecfg/node_config.go @@ -13,7 +13,6 @@ import ( const StaticNodes = "static" const BootNodes = "boot" -const TrustedMailServers = "trusted_mailserver" const PushNotificationsServers = "pushnotification" const RendezvousNodes = "rendezvous" const DiscV5BootstrapNodes = "discV5boot" @@ -39,12 +38,12 @@ func insertNodeConfig(tx *sql.Tx, c *params.NodeConfig) error { max_peers, max_pending_peers, enable_status_service, enable_ntp_sync, bridge_enabled, wallet_enabled, local_notifications_enabled, browser_enabled, permissions_enabled, mailservers_enabled, - swarm_enabled, mailserver_registry_address, web3provider_enabled, connector_enabled, + swarm_enabled, web3provider_enabled, connector_enabled, synthetic_id ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, - ?, ?, ?, ?, ?, ?, 'id' + ?, ?, ?, ?, ?, 'id' )`, c.NetworkID, c.DataDir, c.KeyStoreDir, c.NodeKey, c.NoDiscovery, c.Rendezvous, c.ListenAddr, c.AdvertiseAddr, c.Name, c.Version, c.APIModules, @@ -52,7 +51,7 @@ func insertNodeConfig(tx *sql.Tx, c *params.NodeConfig) error { c.EnableStatusService, true, c.BridgeConfig.Enabled, c.WalletConfig.Enabled, c.LocalNotificationsConfig.Enabled, c.BrowsersConfig.Enabled, c.PermissionsConfig.Enabled, c.MailserversConfig.Enabled, - c.SwarmConfig.Enabled, c.MailServerRegistryAddress, c.Web3ProviderConfig.Enabled, + c.SwarmConfig.Enabled, c.Web3ProviderConfig.Enabled, c.ConnectorConfig.Enabled, ) return err @@ -264,12 +263,12 @@ func insertWakuV2ConfigPostMigration(tx *sql.Tx, c *params.NodeConfig) error { func insertWakuV1Config(tx *sql.Tx, c *params.NodeConfig) error { _, err := tx.Exec(` INSERT OR REPLACE INTO waku_config ( - enabled, light_client, full_node, enable_mailserver, data_dir, minimum_pow, mailserver_password, mailserver_rate_limit, mailserver_data_retention, + enabled, light_client, full_node, data_dir, minimum_pow, ttl, max_message_size, enable_rate_limiter, packet_rate_limit_ip, packet_rate_limit_peer_id, bytes_rate_limit_ip, bytes_rate_limit_peer_id, rate_limit_tolerance, bloom_filter_mode, enable_confirmations, synthetic_id - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'id')`, - c.WakuConfig.Enabled, c.WakuConfig.LightClient, c.WakuConfig.FullNode, c.WakuConfig.EnableMailServer, c.WakuConfig.DataDir, c.WakuConfig.MinimumPoW, - c.WakuConfig.MailServerPassword, c.WakuConfig.MailServerRateLimit, c.WakuConfig.MailServerDataRetention, c.WakuConfig.TTL, c.WakuConfig.MaxMessageSize, + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'id')`, + c.WakuConfig.Enabled, c.WakuConfig.LightClient, c.WakuConfig.FullNode, c.WakuConfig.DataDir, c.WakuConfig.MinimumPoW, + c.WakuConfig.TTL, c.WakuConfig.MaxMessageSize, c.WakuConfig.EnableRateLimiter, c.WakuConfig.PacketRateLimitIP, c.WakuConfig.PacketRateLimitPeerID, c.WakuConfig.BytesRateLimitIP, c.WakuConfig.BytesRateLimitPeerID, c.WakuConfig.RateLimitTolerance, c.WakuConfig.BloomFilterMode, c.WakuConfig.EnableConfirmations, ) @@ -277,10 +276,6 @@ func insertWakuV1Config(tx *sql.Tx, c *params.NodeConfig) error { return err } - if _, err := tx.Exec(`INSERT OR REPLACE INTO waku_config_db_pg (enabled, uri, synthetic_id) VALUES (?, ?, 'id')`, c.WakuConfig.DatabaseConfig.PGConfig.Enabled, c.WakuConfig.DatabaseConfig.PGConfig.URI); err != nil { - return err - } - if _, err := tx.Exec(`DELETE FROM waku_softblacklisted_peers WHERE synthetic_id = 'id'`); err != nil { return err } @@ -311,7 +306,6 @@ func insertClusterConfigNodes(tx *sql.Tx, c *params.NodeConfig) error { nodeMap := make(map[string][]string) nodeMap[StaticNodes] = c.ClusterConfig.StaticNodes nodeMap[BootNodes] = c.ClusterConfig.BootNodes - nodeMap[TrustedMailServers] = c.ClusterConfig.TrustedMailServers nodeMap[PushNotificationsServers] = c.ClusterConfig.PushNotificationsServers nodeMap[RendezvousNodes] = c.ClusterConfig.RendezvousNodes nodeMap[DiscV5BootstrapNodes] = c.ClusterConfig.DiscV5BootstrapNodes @@ -445,14 +439,14 @@ func loadNodeConfig(tx *sql.Tx) (*params.NodeConfig, error) { listen_addr, advertise_addr, name, version, api_modules, tls_enabled, max_peers, max_pending_peers, enable_status_service, bridge_enabled, wallet_enabled, local_notifications_enabled, browser_enabled, permissions_enabled, mailservers_enabled, swarm_enabled, - mailserver_registry_address, web3provider_enabled, connector_enabled FROM node_config + web3provider_enabled, connector_enabled FROM node_config WHERE synthetic_id = 'id' `).Scan( &nodecfg.NetworkID, &nodecfg.DataDir, &nodecfg.KeyStoreDir, &nodecfg.NodeKey, &nodecfg.NoDiscovery, &nodecfg.Rendezvous, &nodecfg.ListenAddr, &nodecfg.AdvertiseAddr, &nodecfg.Name, &nodecfg.Version, &nodecfg.APIModules, &nodecfg.TLSEnabled, &nodecfg.MaxPeers, &nodecfg.MaxPendingPeers, &nodecfg.EnableStatusService, &nodecfg.BridgeConfig.Enabled, &nodecfg.WalletConfig.Enabled, &nodecfg.LocalNotificationsConfig.Enabled, &nodecfg.BrowsersConfig.Enabled, &nodecfg.PermissionsConfig.Enabled, &nodecfg.MailserversConfig.Enabled, &nodecfg.SwarmConfig.Enabled, - &nodecfg.MailServerRegistryAddress, &nodecfg.Web3ProviderConfig.Enabled, &nodecfg.ConnectorConfig.Enabled, + &nodecfg.Web3ProviderConfig.Enabled, &nodecfg.ConnectorConfig.Enabled, ) if err != nil && err != sql.ErrNoRows { return nil, err @@ -535,7 +529,6 @@ func loadNodeConfig(tx *sql.Tx) (*params.NodeConfig, error) { nodeMap := make(map[string]*[]string) nodeMap[StaticNodes] = &nodecfg.ClusterConfig.StaticNodes nodeMap[BootNodes] = &nodecfg.ClusterConfig.BootNodes - nodeMap[TrustedMailServers] = &nodecfg.ClusterConfig.TrustedMailServers nodeMap[PushNotificationsServers] = &nodecfg.ClusterConfig.PushNotificationsServers nodeMap[RendezvousNodes] = &nodecfg.ClusterConfig.RendezvousNodes nodeMap[WakuNodes] = &nodecfg.ClusterConfig.WakuNodes @@ -709,13 +702,13 @@ func loadNodeConfig(tx *sql.Tx) (*params.NodeConfig, error) { } err = tx.QueryRow(` - SELECT enabled, light_client, full_node, enable_mailserver, data_dir, minimum_pow, mailserver_password, mailserver_rate_limit, mailserver_data_retention, + SELECT enabled, light_client, full_node, data_dir, minimum_pow, ttl, max_message_size, enable_rate_limiter, packet_rate_limit_ip, packet_rate_limit_peer_id, bytes_rate_limit_ip, bytes_rate_limit_peer_id, rate_limit_tolerance, bloom_filter_mode, enable_confirmations FROM waku_config WHERE synthetic_id = 'id' `).Scan( - &nodecfg.WakuConfig.Enabled, &nodecfg.WakuConfig.LightClient, &nodecfg.WakuConfig.FullNode, &nodecfg.WakuConfig.EnableMailServer, &nodecfg.WakuConfig.DataDir, &nodecfg.WakuConfig.MinimumPoW, - &nodecfg.WakuConfig.MailServerPassword, &nodecfg.WakuConfig.MailServerRateLimit, &nodecfg.WakuConfig.MailServerDataRetention, &nodecfg.WakuConfig.TTL, &nodecfg.WakuConfig.MaxMessageSize, + &nodecfg.WakuConfig.Enabled, &nodecfg.WakuConfig.LightClient, &nodecfg.WakuConfig.FullNode, &nodecfg.WakuConfig.DataDir, &nodecfg.WakuConfig.MinimumPoW, + &nodecfg.WakuConfig.TTL, &nodecfg.WakuConfig.MaxMessageSize, &nodecfg.WakuConfig.EnableRateLimiter, &nodecfg.WakuConfig.PacketRateLimitIP, &nodecfg.WakuConfig.PacketRateLimitPeerID, &nodecfg.WakuConfig.BytesRateLimitIP, &nodecfg.WakuConfig.BytesRateLimitPeerID, &nodecfg.WakuConfig.RateLimitTolerance, &nodecfg.WakuConfig.BloomFilterMode, &nodecfg.WakuConfig.EnableConfirmations, ) @@ -723,11 +716,6 @@ func loadNodeConfig(tx *sql.Tx) (*params.NodeConfig, error) { return nil, err } - err = tx.QueryRow("SELECT enabled, uri FROM waku_config_db_pg WHERE synthetic_id = 'id'").Scan(&nodecfg.WakuConfig.DatabaseConfig.PGConfig.Enabled, &nodecfg.WakuConfig.DatabaseConfig.PGConfig.URI) - if err != nil && err != sql.ErrNoRows { - return nil, err - } - rows, err = tx.Query(`SELECT peer_id FROM waku_softblacklisted_peers WHERE synthetic_id = 'id' ORDER BY peer_id ASC`) if err != nil && err != sql.ErrNoRows { return nil, err diff --git a/params/cluster.go b/params/cluster.go index e0b143f6b..03aa3dc69 100644 --- a/params/cluster.go +++ b/params/cluster.go @@ -14,7 +14,6 @@ const ( type Cluster struct { StaticNodes []string `json:"staticnodes"` BootNodes []string `json:"bootnodes"` - MailServers []string `json:"mailservers"` // list of trusted mail servers RendezvousNodes []string `json:"rendezvousnodes"` } diff --git a/params/config.go b/params/config.go index ef59a65c2..40fa75255 100644 --- a/params/config.go +++ b/params/config.go @@ -80,9 +80,6 @@ type WakuConfig struct { // FullNode should be true if waku should always acta as a full node FullNode bool - // EnableMailServer is mode when node is capable of delivering expired messages on demand - EnableMailServer bool - // DataDir is the file system folder Waku should use for any data storage needs. // For instance, MailServer will use this directory to store its data. DataDir string @@ -91,16 +88,6 @@ type WakuConfig struct { // We enforce a minimum as a bland spam prevention mechanism. MinimumPoW float64 - // MailServerPassword for symmetric encryption of waku message history requests. - // (if no account file selected, then this password is used for symmetric encryption). - MailServerPassword string - - // MailServerRateLimit minimum time between queries to mail server per peer. - MailServerRateLimit int - - // MailServerDataRetention is a number of days data should be stored by MailServer. - MailServerDataRetention int - // TTL time to live for messages, in seconds TTL int @@ -108,9 +95,6 @@ type WakuConfig struct { // not only the size of envelopes sent in that packet. MaxMessageSize uint32 - // DatabaseConfig is configuration for which data store we use. - DatabaseConfig DatabaseConfig - // EnableRateLimiter set to true enables IP and peer ID rate limiting. EnableRateLimiter bool @@ -258,9 +242,6 @@ type ClusterConfig struct { // Deprecated: won't be used at all in wakuv2 BootNodes []string - // TrustedMailServers is a list of verified and trusted Mail Server nodes. - TrustedMailServers []string - // PushNotificationsServers is a list of default push notification servers. PushNotificationsServers []string @@ -526,9 +507,6 @@ type NodeConfig struct { // discoverable peers with the discovery limits. RequireTopics map[discv5.Topic]Limits `json:"RequireTopics"` - // MailServerRegistryAddress is the MailServerRegistry contract address - MailServerRegistryAddress string - // PushNotificationServerConfig is the config for the push notification server PushNotificationServerConfig PushNotificationServerConfig `json:"PushNotificationServerConfig"` @@ -769,13 +747,6 @@ func WithLES() Option { } } -// WithMailserver enables MailServer. -func WithMailserver() Option { - return func(c *NodeConfig) error { - return loadConfigFromAsset("../config/cli/mailserver-enabled.json", c) - } -} - func WithDiscV5BootstrapNodes(nodes []string) Option { return func(c *NodeConfig) error { c.ClusterConfig.DiscV5BootstrapNodes = nodes @@ -1079,14 +1050,6 @@ func (c *NodeConfig) Validate() error { return fmt.Errorf("both Waku and WakuV2 are enabled and use the same data dir") } - // Waku's data directory must be relative to the main data directory - // if EnableMailServer is true. - if c.WakuConfig.Enabled && c.WakuConfig.EnableMailServer { - if !strings.HasPrefix(c.WakuConfig.DataDir, c.DataDir) { - return fmt.Errorf("WakuConfig.DataDir must start with DataDir fragment") - } - } - if !c.NoDiscovery && len(c.ClusterConfig.BootNodes) == 0 { // No point in running discovery if we don't have bootnodes. // In case we do have bootnodes, NoDiscovery should be true. diff --git a/params/config_test.go b/params/config_test.go index c04f96145..26f456dc5 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -23,7 +23,6 @@ func TestNewNodeConfigWithDefaults(t *testing.T) { params.GoerliNetworkID, params.WithFleet(params.FleetProd), params.WithLES(), - params.WithMailserver(), ) require.NoError(t, err) assert.Equal(t, "/some/data/path", c.DataDir) @@ -31,8 +30,6 @@ func TestNewNodeConfigWithDefaults(t *testing.T) { // assert Whisper assert.Equal(t, true, c.WakuConfig.Enabled) assert.Equal(t, "/some/data/path/waku", c.WakuConfig.DataDir) - // assert MailServer - assert.Equal(t, false, c.WakuConfig.EnableMailServer) // assert cluster assert.Equal(t, false, c.NoDiscovery) assert.Equal(t, params.FleetProd, c.ClusterConfig.Fleet) diff --git a/peers/cotopicpool.go b/peers/cotopicpool.go index 87fe4eef8..6adaa049a 100644 --- a/peers/cotopicpool.go +++ b/peers/cotopicpool.go @@ -55,8 +55,7 @@ var sendEnodeDiscovered = signal.SendEnodeDiscovered // ConfirmAdded calls base TopicPool ConfirmAdded method and sends a signal // confirming the enode has been discovered. func (t *cacheOnlyTopicPool) ConfirmAdded(server *p2p.Server, nodeID enode.ID) { - trusted := t.verifier.VerifyNode(context.TODO(), nodeID) - if trusted { + if t.verifier == nil || t.verifier.VerifyNode(context.TODO(), nodeID) { // add to cache only if trusted t.TopicPool.ConfirmAdded(server, nodeID) sendEnodeDiscovered(nodeID.String(), string(t.topic)) diff --git a/peers/peerpool.go b/peers/peerpool.go index 7f7c4b6df..827364297 100644 --- a/peers/peerpool.go +++ b/peers/peerpool.go @@ -14,7 +14,6 @@ import ( "github.com/status-im/status-go/discovery" "github.com/status-im/status-go/params" - "github.com/status-im/status-go/peers/verifier" "github.com/status-im/status-go/signal" ) @@ -55,8 +54,6 @@ type Options struct { // TopicStopSearchDelay time stopSearch will be waiting for max cached peers to be // filled before really stopping the search. TopicStopSearchDelay time.Duration - // TrustedMailServers is a list of trusted nodes. - TrustedMailServers []enode.ID } // NewDefaultOptions returns a struct with default Options. @@ -155,11 +152,7 @@ func (p *PeerPool) Start(server *p2p.Server) error { var topicPool TopicPoolInterface t := newTopicPool(p.discovery, topic, limits, p.opts.SlowSync, p.opts.FastSync, p.cache) if topic == MailServerDiscoveryTopic { - v, err := p.initVerifier() - if err != nil { - return err - } - topicPool = newCacheOnlyTopicPool(t, v) + topicPool = newCacheOnlyTopicPool(t, nil) } else { topicPool = t } @@ -175,10 +168,6 @@ func (p *PeerPool) Start(server *p2p.Server) error { return nil } -func (p *PeerPool) initVerifier() (v Verifier, err error) { - return verifier.NewLocalVerifier(p.opts.TrustedMailServers), nil -} - func (p *PeerPool) startDiscovery() error { if p.discovery.Running() { return nil diff --git a/peers/peerpool_test.go b/peers/peerpool_test.go index 526b6dd82..f45b5f02e 100644 --- a/peers/peerpool_test.go +++ b/peers/peerpool_test.go @@ -128,7 +128,7 @@ func (s *PeerPoolSimulationSuite) TestPeerPoolCacheEthV5() { config := map[discv5.Topic]params.Limits{ topic: params.NewLimits(1, 1), } - peerPoolOpts := &Options{100 * time.Millisecond, 100 * time.Millisecond, 0, true, 100 * time.Millisecond, nil} + peerPoolOpts := &Options{100 * time.Millisecond, 100 * time.Millisecond, 0, true, 100 * time.Millisecond} cache, err := newInMemoryCache() s.Require().NoError(err) peerPool := NewPeerPool(s.discovery[1], config, cache, peerPoolOpts) @@ -177,7 +177,7 @@ func TestPeerPoolMaxPeersOverflow(t *testing.T) { defer func() { assert.NoError(t, discovery.Stop()) }() require.True(t, discovery.Running()) - poolOpts := &Options{DefaultFastSync, DefaultSlowSync, 0, true, 100 * time.Millisecond, nil} + poolOpts := &Options{DefaultFastSync, DefaultSlowSync, 0, true, 100 * time.Millisecond} pool := NewPeerPool(discovery, nil, nil, poolOpts) require.NoError(t, pool.Start(peer)) require.Equal(t, signal.EventDiscoveryStarted, <-signals) @@ -230,7 +230,7 @@ func TestPeerPoolDiscV5Timeout(t *testing.T) { require.True(t, discovery.Running()) // start PeerPool - poolOpts := &Options{DefaultFastSync, DefaultSlowSync, time.Millisecond * 100, true, 100 * time.Millisecond, nil} + poolOpts := &Options{DefaultFastSync, DefaultSlowSync, time.Millisecond * 100, true, 100 * time.Millisecond} pool := NewPeerPool(discovery, nil, nil, poolOpts) require.NoError(t, pool.Start(server)) require.Equal(t, signal.EventDiscoveryStarted, <-signals) @@ -277,7 +277,7 @@ func TestPeerPoolNotAllowedStopping(t *testing.T) { require.True(t, discovery.Running()) // start PeerPool - poolOpts := &Options{DefaultFastSync, DefaultSlowSync, time.Millisecond * 100, false, 100 * time.Millisecond, nil} + poolOpts := &Options{DefaultFastSync, DefaultSlowSync, time.Millisecond * 100, false, 100 * time.Millisecond} pool := NewPeerPool(discovery, nil, nil, poolOpts) require.NoError(t, pool.Start(server)) @@ -294,7 +294,7 @@ func (s *PeerPoolSimulationSuite) TestUpdateTopicLimits() { config := map[discv5.Topic]params.Limits{ topic: params.NewLimits(1, 1), } - peerPoolOpts := &Options{100 * time.Millisecond, 100 * time.Millisecond, 0, true, 100 * time.Millisecond, nil} + peerPoolOpts := &Options{100 * time.Millisecond, 100 * time.Millisecond, 0, true, 100 * time.Millisecond} cache, err := newInMemoryCache() s.Require().NoError(err) peerPool := NewPeerPool(s.discovery[1], config, cache, peerPoolOpts) @@ -374,7 +374,6 @@ func (s *PeerPoolSimulationSuite) TestMailServerPeersDiscovery() { 0, true, 100 * time.Millisecond, - []enode.ID{s.peers[0].Self().ID()}, } peerPool := NewPeerPool(s.discovery[1], config, cache, peerPoolOpts) s.Require().NoError(peerPool.Start(s.peers[1])) diff --git a/protocol/messenger.go b/protocol/messenger.go index 0ec7c88ab..22d718baa 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -64,7 +64,6 @@ import ( "github.com/status-im/status-go/server" "github.com/status-im/status-go/services/browsers" ensservice "github.com/status-im/status-go/services/ens" - "github.com/status-im/status-go/services/ext/mailservers" localnotifications "github.com/status-im/status-go/services/local-notifications" mailserversDB "github.com/status-im/status-go/services/mailservers" "github.com/status-im/status-go/services/wallet" @@ -98,12 +97,9 @@ var messageCacheIntervalMs uint64 = 1000 * 60 * 60 * 48 // layers. // It needs to expose an interface to manage installations // because installations are managed by the user. -// Similarly, it needs to expose an interface to manage -// mailservers because they can also be managed by the user. type Messenger struct { node types.Node server *p2p.Server - peerStore *mailservers.PeerStore config *config identity *ecdsa.PrivateKey persistence *sqlitePersistence @@ -291,24 +287,11 @@ func (interceptor EnvelopeEventsInterceptor) EnvelopeExpired(identifiers [][]byt interceptor.EnvelopeEventsHandler.EnvelopeExpired(identifiers, err) } -// MailServerRequestCompleted triggered when the mailserver sends a message to notify that the request has been completed -func (interceptor EnvelopeEventsInterceptor) MailServerRequestCompleted(requestID types.Hash, lastEnvelopeHash types.Hash, cursor []byte, err error) { - //we don't track mailserver requests in Messenger, so just redirect to handler - interceptor.EnvelopeEventsHandler.MailServerRequestCompleted(requestID, lastEnvelopeHash, cursor, err) -} - -// MailServerRequestExpired triggered when the mailserver request expires -func (interceptor EnvelopeEventsInterceptor) MailServerRequestExpired(hash types.Hash) { - //we don't track mailserver requests in Messenger, so just redirect to handler - interceptor.EnvelopeEventsHandler.MailServerRequestExpired(hash) -} - func NewMessenger( nodeName string, identity *ecdsa.PrivateKey, node types.Node, installationID string, - peerStore *mailservers.PeerStore, version string, opts ...Option, ) (*Messenger, error) { @@ -594,7 +577,6 @@ func NewMessenger( peersyncing: peersyncing.New(peersyncing.Config{Database: database, Timesource: transp}), peersyncingOffers: make(map[string]uint64), peersyncingRequests: make(map[string]uint64), - peerStore: peerStore, mvdsStatusChangeEvent: make(chan datasyncnode.PeerStatusChangeEvent, 5), verificationDatabase: verification.NewPersistence(database), mailserverCycle: mailserverCycle{ diff --git a/protocol/messenger_builder_test.go b/protocol/messenger_builder_test.go index 7829b9134..afb9048f0 100644 --- a/protocol/messenger_builder_test.go +++ b/protocol/messenger_builder_test.go @@ -91,7 +91,6 @@ func newTestMessenger(waku types.Waku, config testMessengerConfig) (*Messenger, config.privateKey, &testNode{shh: waku}, uuid.New().String(), - nil, "testVersion", options..., ) diff --git a/protocol/messenger_messages_tracking_test.go b/protocol/messenger_messages_tracking_test.go index 11a07c894..89ed36561 100644 --- a/protocol/messenger_messages_tracking_test.go +++ b/protocol/messenger_messages_tracking_test.go @@ -35,16 +35,6 @@ func (h EnvelopeSignalHandlerMock) EnvelopeExpired(identifiers [][]byte, err err signal.SendEnvelopeExpired(identifiers, err) } -// MailServerRequestCompleted triggered when the mailserver sends a message to notify that the request has been completed -func (h EnvelopeSignalHandlerMock) MailServerRequestCompleted(requestID types.Hash, lastEnvelopeHash types.Hash, cursor []byte, err error) { - signal.SendMailServerRequestCompleted(requestID, lastEnvelopeHash, cursor, err) -} - -// MailServerRequestExpired triggered when the mailserver request expires -func (h EnvelopeSignalHandlerMock) MailServerRequestExpired(hash types.Hash) { - signal.SendMailServerRequestExpired(hash) -} - type EnvelopeEventsInterceptorMock struct { EnvelopeEventsInterceptor @@ -127,7 +117,6 @@ func (s *MessengerMessagesTrackingSuite) newMessenger(waku types.Waku, logger *z EnvelopeEventsHandler: EnvelopeSignalHandlerMock{}, MaxAttempts: 1, AwaitOnlyMailServerConfirmations: false, - IsMailserver: func(peer types.EnodeID) bool { return false }, Logger: s.logger, } diff --git a/protocol/transport/envelopes_monitor.go b/protocol/transport/envelopes_monitor.go index 1750ef014..bde09987b 100644 --- a/protocol/transport/envelopes_monitor.go +++ b/protocol/transport/envelopes_monitor.go @@ -28,7 +28,6 @@ type EnvelopesMonitorConfig struct { EnvelopeEventsHandler EnvelopeEventsHandler MaxAttempts int AwaitOnlyMailServerConfirmations bool - IsMailserver func(types.EnodeID) bool Logger *zap.Logger } @@ -36,8 +35,6 @@ type EnvelopesMonitorConfig struct { type EnvelopeEventsHandler interface { EnvelopeSent([][]byte) EnvelopeExpired([][]byte, error) - MailServerRequestCompleted(types.Hash, types.Hash, []byte, error) - MailServerRequestExpired(types.Hash) } // NewEnvelopesMonitor returns a pointer to an instance of the EnvelopesMonitor. @@ -59,7 +56,6 @@ func NewEnvelopesMonitor(w types.Waku, config EnvelopesMonitorConfig) *Envelopes handler: config.EnvelopeEventsHandler, awaitOnlyMailServerConfirmations: config.AwaitOnlyMailServerConfirmations, maxAttempts: config.MaxAttempts, - isMailserver: config.IsMailserver, logger: logger.With(zap.Namespace("EnvelopesMonitor")), // key is envelope hash (event.Hash) @@ -200,42 +196,46 @@ func (m *EnvelopesMonitor) handleEvent(event types.EnvelopeEvent) { func (m *EnvelopesMonitor) handleEventEnvelopeSent(event types.EnvelopeEvent) { // Mailserver confirmations for WakuV2 are disabled - if (m.w == nil || m.w.Version() < 2) && m.awaitOnlyMailServerConfirmations { - if !m.isMailserver(event.Peer) { + // Perhaps code might be reused? + + /* + if (m.w == nil || m.w.Version() < 2) && m.awaitOnlyMailServerConfirmations { + if !m.isMailserver(event.Peer) { + return + } + } + + m.mu.Lock() + defer m.mu.Unlock() + + confirmationExpected := event.Batch != (types.Hash{}) + + envelope, ok := m.envelopes[event.Hash] + + // If confirmations are not expected, we keep track of the envelope + // being sent + if !ok && !confirmationExpected { + m.envelopes[event.Hash] = &monitoredEnvelope{envelopeHashID: event.Hash, state: EnvelopeSent} return } - } - m.mu.Lock() - defer m.mu.Unlock() - - confirmationExpected := event.Batch != (types.Hash{}) - - envelope, ok := m.envelopes[event.Hash] - - // If confirmations are not expected, we keep track of the envelope - // being sent - if !ok && !confirmationExpected { - m.envelopes[event.Hash] = &monitoredEnvelope{envelopeHashID: event.Hash, state: EnvelopeSent} - return - } - - // if message was already confirmed - skip it - if envelope.state == EnvelopeSent { - return - } - m.logger.Debug("envelope is sent", zap.String("hash", event.Hash.String()), zap.String("peer", event.Peer.String())) - if confirmationExpected { - if _, ok := m.batches[event.Batch]; !ok { - m.batches[event.Batch] = map[types.Hash]struct{}{} + // if message was already confirmed - skip it + if envelope.state == EnvelopeSent { + return } - m.batches[event.Batch][event.Hash] = struct{}{} - m.logger.Debug("waiting for a confirmation", zap.String("batch", event.Batch.String())) - } else { - m.logger.Debug("confirmation not expected, marking as sent") - envelope.state = EnvelopeSent - m.processMessageIDs(envelope.messageIDs) - } + m.logger.Debug("envelope is sent", zap.String("hash", event.Hash.String()), zap.String("peer", event.Peer.String())) + if confirmationExpected { + if _, ok := m.batches[event.Batch]; !ok { + m.batches[event.Batch] = map[types.Hash]struct{}{} + } + m.batches[event.Batch][event.Hash] = struct{}{} + m.logger.Debug("waiting for a confirmation", zap.String("batch", event.Batch.String())) + } else { + m.logger.Debug("confirmation not expected, marking as sent") + envelope.state = EnvelopeSent + m.processMessageIDs(envelope.messageIDs) + } + */ } func (m *EnvelopesMonitor) handleAcknowledgedBatch(event types.EnvelopeEvent) { diff --git a/protocol/transport/envelopes_monitor_test.go b/protocol/transport/envelopes_monitor_test.go index 336d4dc8c..1e9c8be5d 100644 --- a/protocol/transport/envelopes_monitor_test.go +++ b/protocol/transport/envelopes_monitor_test.go @@ -55,7 +55,6 @@ func (s *EnvelopesMonitorSuite) SetupTest() { EnvelopeEventsHandler: s.eventsHandlerMock, MaxAttempts: 6, AwaitOnlyMailServerConfirmations: false, - IsMailserver: func(types.EnodeID) bool { return false }, Logger: zap.NewNop(), }, ) diff --git a/services/ext/api.go b/services/ext/api.go index 3554281af..a8e35e218 100644 --- a/services/ext/api.go +++ b/services/ext/api.go @@ -3,9 +3,7 @@ package ext import ( "context" "crypto/ecdsa" - "encoding/hex" "errors" - "fmt" "math/big" "time" @@ -20,14 +18,12 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p/enode" - "github.com/ethereum/go-ethereum/rlp" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/images" - "github.com/status-im/status-go/mailserver" multiaccountscommon "github.com/status-im/status-go/multiaccounts/common" "github.com/status-im/status-go/multiaccounts/settings" "github.com/status-im/status-go/protocol" @@ -44,12 +40,6 @@ import ( "github.com/status-im/status-go/protocol/transport" "github.com/status-im/status-go/protocol/urls" "github.com/status-im/status-go/protocol/verification" - "github.com/status-im/status-go/services/ext/mailservers" -) - -const ( - // defaultRequestTimeout is the default request timeout in seconds - defaultRequestTimeout = 10 ) var ( @@ -69,73 +59,11 @@ var ( // PAYLOADS // ----- -// MessagesRequest is a RequestMessages() request payload. -type MessagesRequest struct { - // MailServerPeer is MailServer's enode address. - MailServerPeer string `json:"mailServerPeer"` - - // From is a lower bound of time range (optional). - // Default is 24 hours back from now. - From uint32 `json:"from"` - - // To is a upper bound of time range (optional). - // Default is now. - To uint32 `json:"to"` - - // Limit determines the number of messages sent by the mail server - // for the current paginated request - Limit uint32 `json:"limit"` - - // Cursor is used as starting point for paginated requests - Cursor string `json:"cursor"` - - // StoreCursor is used as starting point for WAKUV2 paginatedRequests - StoreCursor *StoreRequestCursor `json:"storeCursor"` - - // Topic is a regular Whisper topic. - // DEPRECATED - Topic types.TopicType `json:"topic"` - - // Topics is a list of Whisper topics. - Topics []types.TopicType `json:"topics"` - - // SymKeyID is an ID of a symmetric key to authenticate to MailServer. - // It's derived from MailServer password. - SymKeyID string `json:"symKeyID"` - - // Timeout is the time to live of the request specified in seconds. - // Default is 10 seconds - Timeout time.Duration `json:"timeout"` - - // Force ensures that requests will bypass enforced delay. - Force bool `json:"force"` -} - type StoreRequestCursor struct { Digest []byte `json:"digest"` ReceivedTime float64 `json:"receivedTime"` } -func (r *MessagesRequest) SetDefaults(now time.Time) { - // set From and To defaults - if r.To == 0 { - r.To = uint32(now.UTC().Unix()) - } - - if r.From == 0 { - oneDay := uint32(86400) // -24 hours - if r.To < oneDay { - r.From = 0 - } else { - r.From = r.To - oneDay - } - } - - if r.Timeout == 0 { - r.Timeout = defaultRequestTimeout - } -} - // MessagesResponse is a response for requestMessages2 method. type MessagesResponse struct { // Cursor from the response can be used to retrieve more messages @@ -153,17 +81,15 @@ type MessagesResponse struct { // PublicAPI extends whisper public API. type PublicAPI struct { - service *Service - eventSub mailservers.EnvelopeEventSubscriber - log log.Logger + service *Service + log log.Logger } // NewPublicAPI returns instance of the public API. -func NewPublicAPI(s *Service, eventSub mailservers.EnvelopeEventSubscriber) *PublicAPI { +func NewPublicAPI(s *Service) *PublicAPI { return &PublicAPI{ - service: s, - eventSub: eventSub, - log: log.New("package", "status-go/services/sshext.PublicAPI"), + service: s, + log: log.New("package", "status-go/services/sshext.PublicAPI"), } } @@ -175,33 +101,6 @@ type RetryConfig struct { MaxRetries int } -func WaitForExpiredOrCompleted(requestID types.Hash, events chan types.EnvelopeEvent, timeout time.Duration) (*types.MailServerResponse, error) { - expired := fmt.Errorf("request %x expired", requestID) - after := time.NewTimer(timeout) - defer after.Stop() - for { - var ev types.EnvelopeEvent - select { - case ev = <-events: - case <-after.C: - return nil, expired - } - if ev.Hash != requestID { - continue - } - switch ev.Event { - case types.EventMailServerRequestCompleted: - data, ok := ev.Data.(*types.MailServerResponse) - if ok { - return data, nil - } - return nil, errors.New("invalid event data type") - case types.EventMailServerRequestExpired: - return nil, expired - } - } -} - type Author struct { PublicKey types.HexBytes `json:"publicKey"` Alias string `json:"alias"` @@ -1862,57 +1761,6 @@ func (api *PublicAPI) DeleteCommunityMemberMessages(request *requests.DeleteComm return api.service.messenger.DeleteCommunityMemberMessages(request) } -// ----- -// HELPER -// ----- - -// MakeMessagesRequestPayload makes a specific payload for MailServer -// to request historic messages. -// DEPRECATED -func MakeMessagesRequestPayload(r MessagesRequest) ([]byte, error) { - cursor, err := hex.DecodeString(r.Cursor) - if err != nil { - return nil, fmt.Errorf("invalid cursor: %v", err) - } - - if len(cursor) > 0 && len(cursor) != mailserver.CursorLength { - return nil, fmt.Errorf("invalid cursor size: expected %d but got %d", mailserver.CursorLength, len(cursor)) - } - - payload := mailserver.MessagesRequestPayload{ - Lower: r.From, - Upper: r.To, - // We need to pass bloom filter for - // backward compatibility - Bloom: createBloomFilter(r), - Topics: topicsToByteArray(r.Topics), - Limit: r.Limit, - Cursor: cursor, - // Client must tell the MailServer if it supports batch responses. - // This can be removed in the future. - Batch: true, - } - - return rlp.EncodeToBytes(payload) -} - -func topicsToByteArray(topics []types.TopicType) [][]byte { - - var response [][]byte - for idx := range topics { - response = append(response, topics[idx][:]) - } - - return response -} - -func createBloomFilter(r MessagesRequest) []byte { - if len(r.Topics) > 0 { - return topicsToBloom(r.Topics...) - } - return types.TopicToBloom(r.Topic) -} - func topicsToBloom(topics ...types.TopicType) []byte { i := new(big.Int) for _, topic := range topics { diff --git a/services/ext/api_test.go b/services/ext/api_test.go index 6d46c2ca9..8afd18593 100644 --- a/services/ext/api_test.go +++ b/services/ext/api_test.go @@ -1,102 +1,13 @@ package ext import ( - "encoding/hex" - "fmt" "testing" - "time" "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/mailserver" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) -func TestMessagesRequest_setDefaults(t *testing.T) { - daysAgo := func(now time.Time, days int) uint32 { - return uint32(now.UTC().Add(-24 * time.Hour * time.Duration(days)).Unix()) - } - - tnow := time.Now() - now := uint32(tnow.UTC().Unix()) - yesterday := daysAgo(tnow, 1) - - scenarios := []struct { - given *MessagesRequest - expected *MessagesRequest - }{ - { - &MessagesRequest{From: 0, To: 0}, - &MessagesRequest{From: yesterday, To: now, Timeout: defaultRequestTimeout}, - }, - { - &MessagesRequest{From: 1, To: 0}, - &MessagesRequest{From: uint32(1), To: now, Timeout: defaultRequestTimeout}, - }, - { - &MessagesRequest{From: 0, To: yesterday}, - &MessagesRequest{From: daysAgo(tnow, 2), To: yesterday, Timeout: defaultRequestTimeout}, - }, - // 100 - 1 day would be invalid, so we set From to 0 - { - &MessagesRequest{From: 0, To: 100}, - &MessagesRequest{From: 0, To: 100, Timeout: defaultRequestTimeout}, - }, - // set Timeout - { - &MessagesRequest{From: 0, To: 0, Timeout: 100}, - &MessagesRequest{From: yesterday, To: now, Timeout: 100}, - }, - } - - for i, s := range scenarios { - t.Run(fmt.Sprintf("Scenario %d", i), func(t *testing.T) { - s.given.SetDefaults(tnow) - require.Equal(t, s.expected, s.given) - }) - } -} - -func TestMakeMessagesRequestPayload(t *testing.T) { - var emptyTopic types.TopicType - testCases := []struct { - Name string - Req MessagesRequest - Err string - }{ - { - Name: "empty cursor", - Req: MessagesRequest{Cursor: ""}, - Err: "", - }, - { - Name: "invalid cursor size", - Req: MessagesRequest{Cursor: hex.EncodeToString([]byte{0x01, 0x02, 0x03})}, - Err: fmt.Sprintf("invalid cursor size: expected %d but got 3", mailserver.CursorLength), - }, - { - Name: "valid cursor", - Req: MessagesRequest{ - Cursor: hex.EncodeToString(mailserver.NewDBKey(123, emptyTopic, types.Hash{}).Cursor()), - }, - Err: "", - }, - } - - for _, tc := range testCases { - t.Run(tc.Name, func(t *testing.T) { - _, err := MakeMessagesRequestPayload(tc.Req) - if tc.Err == "" { - require.NoError(t, err) - } else { - require.EqualError(t, err, tc.Err) - } - }) - } -} - func TestTopicsToBloom(t *testing.T) { t1 := stringToTopic("t1") b1 := types.TopicToBloom(t1) @@ -121,36 +32,6 @@ func TestTopicsToBloom(t *testing.T) { assert.True(t, types.BloomFilterMatch(reqBloom, b3)) } -func TestCreateBloomFilter(t *testing.T) { - t1 := stringToTopic("t1") - t2 := stringToTopic("t2") - - req := MessagesRequest{Topic: t1} - bloom := createBloomFilter(req) - assert.Equal(t, topicsToBloom(t1), bloom) - - req = MessagesRequest{Topics: []types.TopicType{t1, t2}} - bloom = createBloomFilter(req) - assert.Equal(t, topicsToBloom(t1, t2), bloom) -} - func stringToTopic(s string) types.TopicType { return types.BytesToTopic([]byte(s)) } - -func TestExpiredOrCompleted(t *testing.T) { - timeout := time.Millisecond - events := make(chan types.EnvelopeEvent) - errors := make(chan error, 1) - hash := types.Hash{1} - go func() { - _, err := WaitForExpiredOrCompleted(hash, events, timeout) - errors <- err - }() - select { - case <-time.After(time.Second): - require.FailNow(t, "timed out waiting for waitForExpiredOrCompleted to complete") - case err := <-errors: - require.EqualError(t, err, fmt.Sprintf("request %x expired", hash)) - } -} diff --git a/services/ext/mailrequests.go b/services/ext/mailrequests.go deleted file mode 100644 index e62c1f0c1..000000000 --- a/services/ext/mailrequests.go +++ /dev/null @@ -1,136 +0,0 @@ -package ext - -import ( - "sync" - - "github.com/ethereum/go-ethereum/log" - - "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/services/ext/mailservers" -) - -// EnvelopeState in local tracker -type EnvelopeState int - -const ( - // NotRegistered returned if asked hash wasn't registered in the tracker. - NotRegistered EnvelopeState = -1 - // MailServerRequestSent is set when p2p request is sent to the mailserver - MailServerRequestSent -) - -// MailRequestMonitor is responsible for monitoring history request to mailservers. -type MailRequestMonitor struct { - eventSub mailservers.EnvelopeEventSubscriber - handler EnvelopeEventsHandler - - mu sync.Mutex - cache map[types.Hash]EnvelopeState - - requestsRegistry *RequestsRegistry - - wg sync.WaitGroup - quit chan struct{} -} - -func NewMailRequestMonitor(eventSub mailservers.EnvelopeEventSubscriber, h EnvelopeEventsHandler, reg *RequestsRegistry) *MailRequestMonitor { - return &MailRequestMonitor{ - eventSub: eventSub, - handler: h, - cache: make(map[types.Hash]EnvelopeState), - requestsRegistry: reg, - } -} - -// Start processing events. -func (m *MailRequestMonitor) Start() { - m.quit = make(chan struct{}) - m.wg.Add(1) - go func() { - m.handleEnvelopeEvents() - m.wg.Done() - }() -} - -// Stop process events. -func (m *MailRequestMonitor) Stop() { - close(m.quit) - m.wg.Wait() -} - -func (m *MailRequestMonitor) GetState(hash types.Hash) EnvelopeState { - m.mu.Lock() - defer m.mu.Unlock() - state, exist := m.cache[hash] - if !exist { - return NotRegistered - } - return state -} - -// handleEnvelopeEvents processes whisper envelope events -func (m *MailRequestMonitor) handleEnvelopeEvents() { - events := make(chan types.EnvelopeEvent, 100) // must be buffered to prevent blocking whisper - sub := m.eventSub.SubscribeEnvelopeEvents(events) - defer sub.Unsubscribe() - for { - select { - case <-m.quit: - return - case event := <-events: - m.handleEvent(event) - } - } -} - -// handleEvent based on type of the event either triggers -// confirmation handler or removes hash from MailRequestMonitor -func (m *MailRequestMonitor) handleEvent(event types.EnvelopeEvent) { - handlers := map[types.EventType]func(types.EnvelopeEvent){ - types.EventMailServerRequestSent: m.handleRequestSent, - types.EventMailServerRequestCompleted: m.handleEventMailServerRequestCompleted, - types.EventMailServerRequestExpired: m.handleEventMailServerRequestExpired, - } - - if handler, ok := handlers[event.Event]; ok { - handler(event) - } -} - -func (m *MailRequestMonitor) handleRequestSent(event types.EnvelopeEvent) { - m.mu.Lock() - defer m.mu.Unlock() - m.cache[event.Hash] = MailServerRequestSent -} - -func (m *MailRequestMonitor) handleEventMailServerRequestCompleted(event types.EnvelopeEvent) { - m.mu.Lock() - defer m.mu.Unlock() - m.requestsRegistry.Unregister(event.Hash) - state, ok := m.cache[event.Hash] - if !ok || state != MailServerRequestSent { - return - } - log.Debug("mailserver response received", "hash", event.Hash) - delete(m.cache, event.Hash) - if m.handler != nil { - if resp, ok := event.Data.(*types.MailServerResponse); ok { - m.handler.MailServerRequestCompleted(event.Hash, resp.LastEnvelopeHash, resp.Cursor, resp.Error) - } - } -} - -func (m *MailRequestMonitor) handleEventMailServerRequestExpired(event types.EnvelopeEvent) { - m.mu.Lock() - defer m.mu.Unlock() - m.requestsRegistry.Unregister(event.Hash) - state, ok := m.cache[event.Hash] - if !ok || state != MailServerRequestSent { - return - } - log.Debug("mailserver response expired", "hash", event.Hash) - delete(m.cache, event.Hash) - if m.handler != nil { - m.handler.MailServerRequestExpired(event.Hash) - } -} diff --git a/services/ext/mailrequests_test.go b/services/ext/mailrequests_test.go deleted file mode 100644 index f7063a7f5..000000000 --- a/services/ext/mailrequests_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package ext - -import ( - "errors" - "testing" - "time" - - "github.com/stretchr/testify/suite" - - "github.com/status-im/status-go/eth-node/types" -) - -var ( - testHash = types.Hash{0x01} -) - -func TestMailRequestMonitorSuite(t *testing.T) { - suite.Run(t, new(MailRequestMonitorSuite)) -} - -type MailRequestMonitorSuite struct { - suite.Suite - - monitor *MailRequestMonitor -} - -func (s *MailRequestMonitorSuite) SetupTest() { - s.monitor = &MailRequestMonitor{ - cache: map[types.Hash]EnvelopeState{}, - requestsRegistry: NewRequestsRegistry(0), - } -} - -func (s *MailRequestMonitorSuite) TestRequestCompleted() { - mock := NewHandlerMock(1) - s.monitor.handler = mock - s.monitor.cache[testHash] = MailServerRequestSent - s.monitor.handleEvent(types.EnvelopeEvent{ - Event: types.EventMailServerRequestCompleted, - Hash: testHash, - Data: &types.MailServerResponse{}, - }) - select { - case requestID := <-mock.requestsCompleted: - s.Equal(testHash, requestID) - s.NotContains(s.monitor.cache, testHash) - case <-time.After(10 * time.Second): - s.Fail("timed out while waiting for a request to be completed") - } -} - -func (s *MailRequestMonitorSuite) TestRequestFailed() { - mock := NewHandlerMock(1) - s.monitor.handler = mock - s.monitor.cache[testHash] = MailServerRequestSent - s.monitor.handleEvent(types.EnvelopeEvent{ - Event: types.EventMailServerRequestCompleted, - Hash: testHash, - Data: &types.MailServerResponse{Error: errors.New("test error")}, - }) - select { - case requestID := <-mock.requestsFailed: - s.Equal(testHash, requestID) - s.NotContains(s.monitor.cache, testHash) - case <-time.After(10 * time.Second): - s.Fail("timed out while waiting for a request to be failed") - } -} - -func (s *MailRequestMonitorSuite) TestRequestExpiration() { - mock := NewHandlerMock(1) - s.monitor.handler = mock - s.monitor.cache[testHash] = MailServerRequestSent - s.monitor.handleEvent(types.EnvelopeEvent{ - Event: types.EventMailServerRequestExpired, - Hash: testHash, - }) - select { - case requestID := <-mock.requestsExpired: - s.Equal(testHash, requestID) - s.NotContains(s.monitor.cache, testHash) - case <-time.After(10 * time.Second): - s.Fail("timed out while waiting for request expiration") - } -} diff --git a/services/ext/mailservers/cache.go b/services/ext/mailservers/cache.go deleted file mode 100644 index ae59681d7..000000000 --- a/services/ext/mailservers/cache.go +++ /dev/null @@ -1,145 +0,0 @@ -package mailservers - -import ( - "encoding/json" - "time" - - "github.com/syndtr/goleveldb/leveldb" - "github.com/syndtr/goleveldb/leveldb/iterator" - "github.com/syndtr/goleveldb/leveldb/util" - - "github.com/ethereum/go-ethereum/p2p/enode" - - "github.com/status-im/status-go/db" - "github.com/status-im/status-go/eth-node/types" -) - -// NewPeerRecord returns instance of the peer record. -func NewPeerRecord(node *enode.Node) PeerRecord { - return PeerRecord{node: node} -} - -// PeerRecord is set data associated with each peer that is stored on disk. -// PeerRecord stored with a enode as a key in leveldb, and body marshalled as json. -type PeerRecord struct { - node *enode.Node - - // last time it was used. - LastUsed time.Time -} - -// Encode encodes PeerRecords to bytes. -func (r PeerRecord) Encode() ([]byte, error) { - return json.Marshal(r) -} - -// ID returns enode identity of the node. -func (r PeerRecord) ID() enode.ID { - return r.node.ID() -} - -// Node returs pointer to original object. -// enode.Node doensn't allow modification on the object. -func (r PeerRecord) Node() *enode.Node { - return r.node -} - -// EncodeKey returns bytes that will should be used as a key in persistent storage. -func (r PeerRecord) EncodeKey() ([]byte, error) { - return r.Node().MarshalText() -} - -// NewCache returns pointer to a Cache instance. -func NewCache(db *leveldb.DB) *Cache { - return &Cache{db: db} -} - -// Cache is wrapper for operations on disk with leveldb. -type Cache struct { - db *leveldb.DB -} - -// Replace deletes old and adds new records in the persistent cache. -func (c *Cache) Replace(nodes []*enode.Node) error { - batch := new(leveldb.Batch) - iter := createPeersIterator(c.db) - defer iter.Release() - newNodes := nodesToMap(nodes) - for iter.Next() { - record, err := unmarshalKeyValue(keyWithoutPrefix(iter.Key()), iter.Value()) - if err != nil { - return err - } - if _, exist := newNodes[types.EnodeID(record.ID())]; exist { - delete(newNodes, types.EnodeID(record.ID())) - } else { - batch.Delete(iter.Key()) - } - } - for _, n := range newNodes { - enodeKey, err := n.MarshalText() - if err != nil { - return err - } - // we put nil as default value doesn't have any state associated with them. - batch.Put(db.Key(db.MailserversCache, enodeKey), nil) - } - return c.db.Write(batch, nil) -} - -// LoadAll loads all records from persistent database. -func (c *Cache) LoadAll() (rst []PeerRecord, err error) { - iter := createPeersIterator(c.db) - for iter.Next() { - record, err := unmarshalKeyValue(keyWithoutPrefix(iter.Key()), iter.Value()) - if err != nil { - return nil, err - } - rst = append(rst, record) - } - return rst, nil -} - -// UpdateRecord updates single record. -func (c *Cache) UpdateRecord(record PeerRecord) error { - enodeKey, err := record.EncodeKey() - if err != nil { - return err - } - value, err := record.Encode() - if err != nil { - return err - } - return c.db.Put(db.Key(db.MailserversCache, enodeKey), value, nil) -} - -func unmarshalKeyValue(key, value []byte) (record PeerRecord, err error) { - enodeKey := key - node := new(enode.Node) - err = node.UnmarshalText(enodeKey) - if err != nil { - return record, err - } - record = PeerRecord{node: node} - if len(value) != 0 { - err = json.Unmarshal(value, &record) - } - return record, err -} - -func nodesToMap(nodes []*enode.Node) map[types.EnodeID]*enode.Node { - rst := map[types.EnodeID]*enode.Node{} - for _, n := range nodes { - rst[types.EnodeID(n.ID())] = n - } - return rst -} - -func createPeersIterator(level *leveldb.DB) iterator.Iterator { - return level.NewIterator(util.BytesPrefix([]byte{byte(db.MailserversCache)}), nil) -} - -// keyWithoutPrefix removes first byte from key. -func keyWithoutPrefix(key []byte) []byte { - return key[1:] -} diff --git a/services/ext/mailservers/cache_test.go b/services/ext/mailservers/cache_test.go deleted file mode 100644 index f25cd1cff..000000000 --- a/services/ext/mailservers/cache_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package mailservers - -import ( - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/require" - "github.com/syndtr/goleveldb/leveldb" - "github.com/syndtr/goleveldb/leveldb/storage" - - "github.com/ethereum/go-ethereum/p2p/enode" -) - -func newInMemCache(t *testing.T) *Cache { - db, err := leveldb.Open(storage.NewMemStorage(), nil) - require.NoError(t, err) - return NewCache(db) -} - -func containsNode(nodes []*enode.Node, node *enode.Node) error { - for _, n := range nodes { - if n.ID() == node.ID() { - return nil - } - } - return fmt.Errorf("nodes %+s do not contain %s", nodes, node) -} - -func TestReplaceRecords(t *testing.T) { - nodesNumber := 3 - cache := newInMemCache(t) - nodes := make([]*enode.Node, nodesNumber) - // First round is a sanity check that records were written. - fillWithRandomNodes(t, nodes) - require.NoError(t, cache.Replace(nodes)) - records, err := cache.LoadAll() - require.NoError(t, err) - require.Len(t, records, nodesNumber) - for i := range records { - require.NoError(t, containsNode(nodes, records[i].Node())) - } - // Replace all nodes and verify that length is the same and loaded records are found. - fillWithRandomNodes(t, nodes) - require.NoError(t, cache.Replace(nodes)) - records, err = cache.LoadAll() - require.NoError(t, err) - require.Len(t, records, nodesNumber) - for i := range records { - require.NoError(t, containsNode(nodes, records[i].Node())) - } -} - -func TestUsedRecord(t *testing.T) { - cache := newInMemCache(t) - node, err := RandomNode() - require.NoError(t, err) - record := PeerRecord{node: node} - require.NoError(t, cache.UpdateRecord(record)) - record.LastUsed = time.Now() - require.NoError(t, cache.UpdateRecord(record)) - records, err := cache.LoadAll() - require.NoError(t, err) - require.Len(t, records, 1) - require.True(t, record.LastUsed.Equal(records[0].LastUsed)) -} - -func TestTimestampPreservedOnReplace(t *testing.T) { - cache := newInMemCache(t) - node, err := RandomNode() - require.NoError(t, err) - record := PeerRecord{node: node, LastUsed: time.Now()} - require.NoError(t, cache.UpdateRecord(record)) - require.NoError(t, cache.Replace([]*enode.Node{node})) - records, err := cache.LoadAll() - require.NoError(t, err) - require.Len(t, records, 1) - require.Equal(t, node.ID(), records[0].Node().ID()) - require.False(t, records[0].LastUsed.IsZero(), "timestamp should be preserved and not equal to zero") - -} diff --git a/services/ext/mailservers/connmanager.go b/services/ext/mailservers/connmanager.go deleted file mode 100644 index f4c5bf3fe..000000000 --- a/services/ext/mailservers/connmanager.go +++ /dev/null @@ -1,271 +0,0 @@ -package mailservers - -import ( - "sync" - "time" - - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/enode" - - "github.com/status-im/status-go/eth-node/types" -) - -const ( - peerEventsBuffer = 10 // sufficient buffer to avoid blocking a p2p feed. - whisperEventsBuffer = 20 // sufficient buffer to avod blocking a eventSub envelopes feed. -) - -// PeerAdderRemover is an interface for adding or removing peers. -type PeerAdderRemover interface { - AddPeer(node *enode.Node) - RemovePeer(node *enode.Node) -} - -// PeerEventsSubscriber interface to subscribe for p2p.PeerEvent's. -type PeerEventsSubscriber interface { - SubscribeEvents(chan *p2p.PeerEvent) event.Subscription -} - -// EnvelopeEventSubscriber interface to subscribe for types.EnvelopeEvent's. -type EnvelopeEventSubscriber interface { - SubscribeEnvelopeEvents(chan<- types.EnvelopeEvent) types.Subscription -} - -type p2pServer interface { - PeerAdderRemover - PeerEventsSubscriber -} - -// NewConnectionManager creates an instance of ConnectionManager. -func NewConnectionManager(server p2pServer, eventSub EnvelopeEventSubscriber, target, maxFailures int, timeout time.Duration) *ConnectionManager { - return &ConnectionManager{ - server: server, - eventSub: eventSub, - connectedTarget: target, - maxFailures: maxFailures, - notifications: make(chan []*enode.Node), - timeoutWaitAdded: timeout, - } -} - -// ConnectionManager manages keeps target of peers connected. -type ConnectionManager struct { - wg sync.WaitGroup - quit chan struct{} - - server p2pServer - eventSub EnvelopeEventSubscriber - - notifications chan []*enode.Node - connectedTarget int - timeoutWaitAdded time.Duration - maxFailures int -} - -// Notify sends a non-blocking notification about new nodes. -func (ps *ConnectionManager) Notify(nodes []*enode.Node) { - ps.wg.Add(1) - go func() { - select { - case ps.notifications <- nodes: - case <-ps.quit: - } - ps.wg.Done() - }() - -} - -// Start subscribes to a p2p server and handles new peers and state updates for those peers. -func (ps *ConnectionManager) Start() { - ps.quit = make(chan struct{}) - ps.wg.Add(1) - go func() { - state := newInternalState(ps.server, ps.connectedTarget, ps.timeoutWaitAdded) - events := make(chan *p2p.PeerEvent, peerEventsBuffer) - sub := ps.server.SubscribeEvents(events) - whisperEvents := make(chan types.EnvelopeEvent, whisperEventsBuffer) - whisperSub := ps.eventSub.SubscribeEnvelopeEvents(whisperEvents) - requests := map[types.Hash]struct{}{} - failuresPerServer := map[types.EnodeID]int{} - - defer sub.Unsubscribe() - defer whisperSub.Unsubscribe() - defer ps.wg.Done() - for { - select { - case <-ps.quit: - return - case err := <-sub.Err(): - log.Error("retry after error subscribing to p2p events", "error", err) - return - case err := <-whisperSub.Err(): - log.Error("retry after error suscribing to eventSub events", "error", err) - return - case newNodes := <-ps.notifications: - state.processReplacement(newNodes, events) - case ev := <-events: - processPeerEvent(state, ev) - case ev := <-whisperEvents: - // TODO treat failed requests the same way as expired - switch ev.Event { - case types.EventMailServerRequestSent: - requests[ev.Hash] = struct{}{} - case types.EventMailServerRequestCompleted: - // reset failures count on first success - failuresPerServer[ev.Peer] = 0 - delete(requests, ev.Hash) - case types.EventMailServerRequestExpired: - _, exist := requests[ev.Hash] - if !exist { - continue - } - failuresPerServer[ev.Peer]++ - log.Debug("request to a mail server expired, disconnect a peer", "address", ev.Peer) - if failuresPerServer[ev.Peer] >= ps.maxFailures { - state.nodeDisconnected(ev.Peer) - } - } - } - } - }() -} - -// Stop gracefully closes all background goroutines and waits until they finish. -func (ps *ConnectionManager) Stop() { - if ps.quit == nil { - return - } - select { - case <-ps.quit: - return - default: - } - close(ps.quit) - ps.wg.Wait() - ps.quit = nil -} - -func (state *internalState) processReplacement(newNodes []*enode.Node, events <-chan *p2p.PeerEvent) { - replacement := map[types.EnodeID]*enode.Node{} - for _, n := range newNodes { - replacement[types.EnodeID(n.ID())] = n - } - state.replaceNodes(replacement) - if state.ReachedTarget() { - log.Debug("already connected with required target", "target", state.target) - return - } - if state.timeout != 0 { - log.Debug("waiting defined timeout to establish connections", - "timeout", state.timeout, "target", state.target) - timer := time.NewTimer(state.timeout) - waitForConnections(state, timer.C, events) - timer.Stop() - } -} - -func newInternalState(srv PeerAdderRemover, target int, timeout time.Duration) *internalState { - return &internalState{ - options: options{target: target, timeout: timeout}, - srv: srv, - connected: map[types.EnodeID]struct{}{}, - currentNodes: map[types.EnodeID]*enode.Node{}, - } -} - -type options struct { - target int - timeout time.Duration -} - -type internalState struct { - options - srv PeerAdderRemover - - connected map[types.EnodeID]struct{} - currentNodes map[types.EnodeID]*enode.Node -} - -func (state *internalState) ReachedTarget() bool { - return len(state.connected) >= state.target -} - -func (state *internalState) replaceNodes(new map[types.EnodeID]*enode.Node) { - for nid, n := range state.currentNodes { - if _, exist := new[nid]; !exist { - delete(state.connected, nid) - state.srv.RemovePeer(n) - } - } - if !state.ReachedTarget() { - for _, n := range new { - state.srv.AddPeer(n) - } - } - state.currentNodes = new -} - -func (state *internalState) nodeAdded(peer types.EnodeID) { - n, exist := state.currentNodes[peer] - if !exist { - return - } - if state.ReachedTarget() { - state.srv.RemovePeer(n) - } else { - state.connected[types.EnodeID(n.ID())] = struct{}{} - } -} - -func (state *internalState) nodeDisconnected(peer types.EnodeID) { - n, exist := state.currentNodes[peer] // unrelated event - if !exist { - return - } - _, exist = state.connected[peer] // check if already disconnected - if !exist { - return - } - if len(state.currentNodes) == 1 { // keep node connected if we don't have another choice - return - } - state.srv.RemovePeer(n) // remove peer permanently, otherwise p2p.Server will try to reconnect - delete(state.connected, peer) - if !state.ReachedTarget() { // try to connect with any other selected (but not connected) node - for nid, n := range state.currentNodes { - _, exist := state.connected[nid] - if exist || peer == nid { - continue - } - state.srv.AddPeer(n) - } - } -} - -func processPeerEvent(state *internalState, ev *p2p.PeerEvent) { - switch ev.Type { - case p2p.PeerEventTypeAdd: - log.Debug("connected to a mailserver", "address", ev.Peer) - state.nodeAdded(types.EnodeID(ev.Peer)) - case p2p.PeerEventTypeDrop: - log.Debug("mailserver disconnected", "address", ev.Peer) - state.nodeDisconnected(types.EnodeID(ev.Peer)) - } -} - -func waitForConnections(state *internalState, timeout <-chan time.Time, events <-chan *p2p.PeerEvent) { - for { - select { - case ev := <-events: - processPeerEvent(state, ev) - if state.ReachedTarget() { - return - } - case <-timeout: - return - } - } - -} diff --git a/services/ext/mailservers/connmanager_test.go b/services/ext/mailservers/connmanager_test.go deleted file mode 100644 index 1b93ff1d6..000000000 --- a/services/ext/mailservers/connmanager_test.go +++ /dev/null @@ -1,400 +0,0 @@ -package mailservers - -import ( - "fmt" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/enode" - - "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/t/utils" -) - -type fakePeerEvents struct { - mu sync.Mutex - nodes map[types.EnodeID]struct{} - input chan *p2p.PeerEvent -} - -func (f *fakePeerEvents) Nodes() []types.EnodeID { - f.mu.Lock() - rst := make([]types.EnodeID, 0, len(f.nodes)) - for n := range f.nodes { - rst = append(rst, n) - } - f.mu.Unlock() - return rst -} - -func (f *fakePeerEvents) AddPeer(node *enode.Node) { - f.mu.Lock() - f.nodes[types.EnodeID(node.ID())] = struct{}{} - f.mu.Unlock() - if f.input == nil { - return - } - f.input <- &p2p.PeerEvent{ - Peer: node.ID(), - Type: p2p.PeerEventTypeAdd, - } -} - -func (f *fakePeerEvents) RemovePeer(node *enode.Node) { - f.mu.Lock() - delete(f.nodes, types.EnodeID(node.ID())) - f.mu.Unlock() - if f.input == nil { - return - } - f.input <- &p2p.PeerEvent{ - Peer: node.ID(), - Type: p2p.PeerEventTypeDrop, - } -} - -func newFakePeerAdderRemover() *fakePeerEvents { - return &fakePeerEvents{nodes: map[types.EnodeID]struct{}{}} -} - -func (f *fakePeerEvents) SubscribeEvents(output chan *p2p.PeerEvent) event.Subscription { - return event.NewSubscription(func(quit <-chan struct{}) error { - for { - select { - case <-quit: - return nil - case ev := <-f.input: - // will block the same way as in any feed - output <- ev - } - } - }) -} - -func newFakeServer() *fakePeerEvents { - srv := newFakePeerAdderRemover() - srv.input = make(chan *p2p.PeerEvent, 20) - return srv -} - -type fakeEnvelopeEvents struct { - input chan types.EnvelopeEvent -} - -func (f *fakeEnvelopeEvents) SubscribeEnvelopeEvents(output chan<- types.EnvelopeEvent) types.Subscription { - return event.NewSubscription(func(quit <-chan struct{}) error { - for { - select { - case <-quit: - return nil - case ev := <-f.input: - // will block the same way as in any feed - output <- ev - } - } - }) -} - -func newFakeEnvelopesEvents() *fakeEnvelopeEvents { - return &fakeEnvelopeEvents{ - input: make(chan types.EnvelopeEvent), - } -} - -func fillWithRandomNodes(t *testing.T, nodes []*enode.Node) { - var err error - for i := range nodes { - nodes[i], err = RandomNode() - require.NoError(t, err) - } -} - -func getMapWithRandomNodes(t *testing.T, n int) map[types.EnodeID]*enode.Node { - nodes := make([]*enode.Node, n) - fillWithRandomNodes(t, nodes) - return nodesToMap(nodes) -} - -func mergeOldIntoNew(old, new map[types.EnodeID]*enode.Node) { - for n := range old { - new[n] = old[n] - } -} - -func TestReplaceNodes(t *testing.T) { - type testCase struct { - description string - old map[types.EnodeID]*enode.Node - new map[types.EnodeID]*enode.Node - target int - } - for _, tc := range []testCase{ - { - "InitialReplace", - getMapWithRandomNodes(t, 0), - getMapWithRandomNodes(t, 3), - 2, - }, - { - "FullReplace", - getMapWithRandomNodes(t, 3), - getMapWithRandomNodes(t, 3), - 2, - }, - } { - t.Run(tc.description, func(t *testing.T) { - peers := newFakePeerAdderRemover() - state := newInternalState(peers, tc.target, 0) - state.replaceNodes(tc.old) - require.Len(t, peers.nodes, len(tc.old)) - for n := range peers.nodes { - require.Contains(t, tc.old, n) - } - state.replaceNodes(tc.new) - require.Len(t, peers.nodes, len(tc.new)) - for n := range peers.nodes { - require.Contains(t, tc.new, n) - } - }) - } -} - -func TestPartialReplaceNodesBelowTarget(t *testing.T) { - peers := newFakePeerAdderRemover() - old := getMapWithRandomNodes(t, 1) - new := getMapWithRandomNodes(t, 2) - state := newInternalState(peers, 2, 0) - state.replaceNodes(old) - mergeOldIntoNew(old, new) - state.replaceNodes(new) - require.Len(t, peers.nodes, len(new)) -} - -func TestPartialReplaceNodesAboveTarget(t *testing.T) { - peers := newFakePeerAdderRemover() - initial, err := RandomNode() - require.NoError(t, err) - old := nodesToMap([]*enode.Node{initial}) - new := getMapWithRandomNodes(t, 2) - state := newInternalState(peers, 1, 0) - state.replaceNodes(old) - state.nodeAdded(types.EnodeID(initial.ID())) - mergeOldIntoNew(old, new) - state.replaceNodes(new) - require.Len(t, peers.nodes, 1) -} - -func TestConnectionManagerAddDrop(t *testing.T) { - server := newFakeServer() - whisper := newFakeEnvelopesEvents() - target := 1 - connmanager := NewConnectionManager(server, whisper, target, 1, 0) - connmanager.Start() - defer connmanager.Stop() - nodes := []*enode.Node{} - for _, n := range getMapWithRandomNodes(t, 3) { - nodes = append(nodes, n) - } - // Send 3 random nodes to connection manager. - connmanager.Notify(nodes) - var initial enode.ID - // Wait till connection manager establishes connection with 1 peer. - require.NoError(t, utils.Eventually(func() error { - nodes := server.Nodes() - if len(nodes) != target { - return fmt.Errorf("unexpected number of connected servers: %d", len(nodes)) - } - initial = enode.ID(nodes[0]) - return nil - }, time.Second, 100*time.Millisecond)) - // Send an event that peer was dropped. - select { - case server.input <- &p2p.PeerEvent{Peer: initial, Type: p2p.PeerEventTypeDrop}: - case <-time.After(time.Second): - require.FailNow(t, "can't send a drop event") - } - // Connection manager should establish connection with any other peer from initial list. - require.NoError(t, utils.Eventually(func() error { - nodes := server.Nodes() - if len(nodes) != target { - return fmt.Errorf("unexpected number of connected servers: %d", len(nodes)) - } - if enode.ID(nodes[0]) == initial { - return fmt.Errorf("connected node wasn't changed from %s", initial) - } - return nil - }, time.Second, 100*time.Millisecond)) -} - -func TestConnectionManagerReplace(t *testing.T) { - server := newFakeServer() - whisper := newFakeEnvelopesEvents() - target := 1 - connmanager := NewConnectionManager(server, whisper, target, 1, 0) - connmanager.Start() - defer connmanager.Stop() - nodes := []*enode.Node{} - for _, n := range getMapWithRandomNodes(t, 3) { - nodes = append(nodes, n) - } - // Send a single node to connection manager. - connmanager.Notify(nodes[:1]) - // Wait until this node will get connected. - require.NoError(t, utils.Eventually(func() error { - connected := server.Nodes() - if len(connected) != target { - return fmt.Errorf("unexpected number of connected servers: %d", len(connected)) - } - if types.EnodeID(nodes[0].ID()) != connected[0] { - return fmt.Errorf("connected with a wrong peer. expected %s, got %s", nodes[0].ID(), connected[0]) - } - return nil - }, time.Second, 100*time.Millisecond)) - // Replace previously sent node with 2 different nodes. - connmanager.Notify(nodes[1:]) - // Wait until connection manager replaces node connected in the first round. - require.NoError(t, utils.Eventually(func() error { - connected := server.Nodes() - if len(connected) != target { - return fmt.Errorf("unexpected number of connected servers: %d", len(connected)) - } - switch enode.ID(connected[0]) { - case nodes[1].ID(): - case nodes[2].ID(): - default: - return fmt.Errorf("connected with unexpected peer. got %s, expected %+v", connected[0], nodes[1:]) - } - return nil - }, time.Second, 100*time.Millisecond)) -} - -func setupTestConnectionAfterExpiry(t *testing.T, server *fakePeerEvents, whisperMock *fakeEnvelopeEvents, target, maxFailures int, hash types.Hash) (*ConnectionManager, types.EnodeID) { - connmanager := NewConnectionManager(server, whisperMock, target, maxFailures, 0) - connmanager.Start() - nodes := []*enode.Node{} - for _, n := range getMapWithRandomNodes(t, 2) { - nodes = append(nodes, n) - } - // Send two random nodes to connection manager. - connmanager.Notify(nodes) - var initial types.EnodeID - // Wait until connection manager establishes connection with one node. - require.NoError(t, utils.Eventually(func() error { - nodes := server.Nodes() - if len(nodes) != target { - return fmt.Errorf("unexpected number of connected servers: %d", len(nodes)) - } - initial = nodes[0] - return nil - }, time.Second, 100*time.Millisecond)) - // Send event that history request for connected peer was sent. - select { - case whisperMock.input <- types.EnvelopeEvent{ - Event: types.EventMailServerRequestSent, Peer: initial, Hash: hash}: - case <-time.After(time.Second): - require.FailNow(t, "can't send a 'sent' event") - } - return connmanager, initial -} - -func TestConnectionChangedAfterExpiry(t *testing.T) { - server := newFakeServer() - whisperMock := newFakeEnvelopesEvents() - target := 1 - maxFailures := 1 - hash := types.Hash{1} - connmanager, initial := setupTestConnectionAfterExpiry(t, server, whisperMock, target, maxFailures, hash) - defer connmanager.Stop() - - // And eventually expired. - select { - case whisperMock.input <- types.EnvelopeEvent{ - Event: types.EventMailServerRequestExpired, Peer: initial, Hash: hash}: - case <-time.After(time.Second): - require.FailNow(t, "can't send an 'expiry' event") - } - require.NoError(t, utils.Eventually(func() error { - nodes := server.Nodes() - if len(nodes) != target { - return fmt.Errorf("unexpected number of connected servers: %d", len(nodes)) - } - if nodes[0] == initial { - return fmt.Errorf("connected node wasn't changed from %s", initial) - } - return nil - }, time.Second, 100*time.Millisecond)) -} - -func TestConnectionChangedAfterSecondExpiry(t *testing.T) { - server := newFakeServer() - whisperMock := newFakeEnvelopesEvents() - target := 1 - maxFailures := 2 - hash := types.Hash{1} - connmanager, initial := setupTestConnectionAfterExpiry(t, server, whisperMock, target, maxFailures, hash) - defer connmanager.Stop() - - // First expired is sent. Nothing should happen. - select { - case whisperMock.input <- types.EnvelopeEvent{ - Event: types.EventMailServerRequestExpired, Peer: initial, Hash: hash}: - case <-time.After(time.Second): - require.FailNow(t, "can't send an 'expiry' event") - } - - // we use 'eventually' as 'consistently' because this function will retry for a given timeout while error is received - require.EqualError(t, utils.Eventually(func() error { - nodes := server.Nodes() - if len(nodes) != target { - return fmt.Errorf("unexpected number of connected servers: %d", len(nodes)) - } - if nodes[0] == initial { - return fmt.Errorf("connected node wasn't changed from %s", initial) - } - return nil - }, time.Second, 100*time.Millisecond), fmt.Sprintf("connected node wasn't changed from %s", initial)) - - // second expiry event - select { - case whisperMock.input <- types.EnvelopeEvent{ - Event: types.EventMailServerRequestExpired, Peer: initial, Hash: hash}: - case <-time.After(time.Second): - require.FailNow(t, "can't send an 'expiry' event") - } - require.NoError(t, utils.Eventually(func() error { - nodes := server.Nodes() - if len(nodes) != target { - return fmt.Errorf("unexpected number of connected servers: %d", len(nodes)) - } - if nodes[0] == initial { - return fmt.Errorf("connected node wasn't changed from %s", initial) - } - return nil - }, time.Second, 100*time.Millisecond)) -} - -func TestProcessReplacementWaitsForConnections(t *testing.T) { - srv := newFakePeerAdderRemover() - target := 1 - timeout := time.Second - nodes := make([]*enode.Node, 2) - fillWithRandomNodes(t, nodes) - events := make(chan *p2p.PeerEvent) - state := newInternalState(srv, target, timeout) - state.currentNodes = nodesToMap(nodes) - go func() { - select { - case events <- &p2p.PeerEvent{Peer: nodes[0].ID(), Type: p2p.PeerEventTypeAdd}: - case <-time.After(time.Second): - assert.FailNow(t, "can't send a drop event") - } - }() - state.processReplacement(nodes, events) - require.Len(t, state.connected, 1) -} diff --git a/services/ext/mailservers/connmonitor.go b/services/ext/mailservers/connmonitor.go deleted file mode 100644 index 8d9bd6f40..000000000 --- a/services/ext/mailservers/connmonitor.go +++ /dev/null @@ -1,85 +0,0 @@ -package mailservers - -import ( - "sync" - "time" - - "github.com/ethereum/go-ethereum/log" - - "github.com/status-im/status-go/eth-node/types" -) - -// NewLastUsedConnectionMonitor returns pointer to the instance of LastUsedConnectionMonitor. -func NewLastUsedConnectionMonitor(ps *PeerStore, cache *Cache, eventSub EnvelopeEventSubscriber) *LastUsedConnectionMonitor { - return &LastUsedConnectionMonitor{ - ps: ps, - cache: cache, - eventSub: eventSub, - } -} - -// LastUsedConnectionMonitor watches relevant events and reflects it in cache. -type LastUsedConnectionMonitor struct { - ps *PeerStore - cache *Cache - - eventSub EnvelopeEventSubscriber - - quit chan struct{} - wg sync.WaitGroup -} - -// Start spins a separate goroutine to watch connections. -func (mon *LastUsedConnectionMonitor) Start() { - mon.quit = make(chan struct{}) - mon.wg.Add(1) - go func() { - events := make(chan types.EnvelopeEvent, whisperEventsBuffer) - sub := mon.eventSub.SubscribeEnvelopeEvents(events) - defer sub.Unsubscribe() - defer mon.wg.Done() - for { - select { - case <-mon.quit: - return - case err := <-sub.Err(): - log.Error("retry after error suscribing to eventSub events", "error", err) - return - case ev := <-events: - node := mon.ps.Get(ev.Peer) - if node == nil { - continue - } - if ev.Event == types.EventMailServerRequestCompleted { - err := mon.updateRecord(ev.Peer) - if err != nil { - log.Error("unable to update storage", "peer", ev.Peer, "error", err) - } - } - } - } - }() -} - -func (mon *LastUsedConnectionMonitor) updateRecord(nodeID types.EnodeID) error { - node := mon.ps.Get(nodeID) - if node == nil { - return nil - } - return mon.cache.UpdateRecord(PeerRecord{node: node, LastUsed: time.Now()}) -} - -// Stop closes channel to signal a quit and waits until all goroutines are stoppped. -func (mon *LastUsedConnectionMonitor) Stop() { - if mon.quit == nil { - return - } - select { - case <-mon.quit: - return - default: - } - close(mon.quit) - mon.wg.Wait() - mon.quit = nil -} diff --git a/services/ext/mailservers/connmonitor_test.go b/services/ext/mailservers/connmonitor_test.go deleted file mode 100644 index b96256260..000000000 --- a/services/ext/mailservers/connmonitor_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package mailservers - -import ( - "fmt" - "sort" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/ethereum/go-ethereum/p2p/enode" - - "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/t/utils" -) - -func TestUsedConnectionPersisted(t *testing.T) { - nodes := make([]*enode.Node, 2) - fillWithRandomNodes(t, nodes) - - cache := newInMemCache(t) - store := NewPeerStore(cache) - require.NoError(t, store.Update(nodes)) - whisperMock := newFakeEnvelopesEvents() - monitor := NewLastUsedConnectionMonitor(store, cache, whisperMock) - monitor.Start() - - // Send a confirmation that we received history from one of the peers. - select { - case whisperMock.input <- types.EnvelopeEvent{ - Event: types.EventMailServerRequestCompleted, Peer: types.EnodeID(nodes[0].ID())}: - case <-time.After(time.Second): - require.FailNow(t, "can't send a 'completed' event") - } - - // Wait until records will be updated in the cache. - require.NoError(t, utils.Eventually(func() error { - records, err := cache.LoadAll() - if err != nil { - return err - } - if lth := len(records); lth != 2 { - return fmt.Errorf("unexpected length of all records stored in the cache. expected %d got %d", 2, lth) - } - var used bool - for _, r := range records { - if r.Node().ID() == nodes[0].ID() { - used = !r.LastUsed.IsZero() - } - } - if !used { - return fmt.Errorf("record %s is not marked as used", types.EnodeID(nodes[0].ID())) - } - return nil - }, time.Second, 100*time.Millisecond)) - - // Use different peer, first will be marked as unused. - select { - case whisperMock.input <- types.EnvelopeEvent{ - Event: types.EventMailServerRequestCompleted, Peer: types.EnodeID(nodes[1].ID())}: - case <-time.After(time.Second): - require.FailNow(t, "can't send a 'completed' event") - } - - require.NoError(t, utils.Eventually(func() error { - records, err := cache.LoadAll() - if err != nil { - return err - } - if lth := len(records); lth != 2 { - return fmt.Errorf("unexpected length of all records stored in the cache. expected %d got %d", 2, lth) - } - sort.Slice(records, func(i, j int) bool { - return records[i].LastUsed.After(records[j].LastUsed) - }) - if records[0].Node().ID() != nodes[1].ID() { - return fmt.Errorf("record wasn't updated after previous event") - } - return nil - }, time.Second, 100*time.Millisecond)) -} diff --git a/services/ext/mailservers/peerstore.go b/services/ext/mailservers/peerstore.go deleted file mode 100644 index 3f1a01030..000000000 --- a/services/ext/mailservers/peerstore.go +++ /dev/null @@ -1,63 +0,0 @@ -package mailservers - -import ( - "errors" - "sync" - - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/enode" - - "github.com/status-im/status-go/eth-node/types" -) - -var ( - // ErrNoConnected returned when mail servers are not connected. - ErrNoConnected = errors.New("no connected mail servers") -) - -// PeersProvider is an interface for requesting list of peers. -type PeersProvider interface { - Peers() []*p2p.Peer -} - -// NewPeerStore returns an instance of PeerStore. -func NewPeerStore(cache *Cache) *PeerStore { - return &PeerStore{ - nodes: map[types.EnodeID]*enode.Node{}, - cache: cache, - } -} - -// PeerStore stores list of selected mail servers and keeps N of them connected. -type PeerStore struct { - mu sync.RWMutex - nodes map[types.EnodeID]*enode.Node - - cache *Cache -} - -// Exist confirms that peers was added to a store. -func (ps *PeerStore) Exist(nodeID types.EnodeID) bool { - ps.mu.RLock() - defer ps.mu.RUnlock() - _, exist := ps.nodes[nodeID] - return exist -} - -// Get returns instance of the node with requested ID or nil if ID is not found. -func (ps *PeerStore) Get(nodeID types.EnodeID) *enode.Node { - ps.mu.RLock() - defer ps.mu.RUnlock() - return ps.nodes[nodeID] -} - -// Update updates peers locally. -func (ps *PeerStore) Update(nodes []*enode.Node) error { - ps.mu.Lock() - ps.nodes = map[types.EnodeID]*enode.Node{} - for _, n := range nodes { - ps.nodes[types.EnodeID(n.ID())] = n - } - ps.mu.Unlock() - return ps.cache.Replace(nodes) -} diff --git a/services/ext/mailservers/peerstore_test.go b/services/ext/mailservers/peerstore_test.go deleted file mode 100644 index e08b21f50..000000000 --- a/services/ext/mailservers/peerstore_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package mailservers - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/enode" - - "github.com/status-im/status-go/eth-node/types" -) - -func RandomNode() (*enode.Node, error) { - pkey, err := crypto.GenerateKey() - if err != nil { - return nil, err - } - return enode.NewV4(&pkey.PublicKey, nil, 0, 0), nil -} - -func TestUpdateResetsInternalStorage(t *testing.T) { - store := NewPeerStore(newInMemCache(t)) - r1, err := RandomNode() - require.NoError(t, err) - r2, err := RandomNode() - require.NoError(t, err) - require.NoError(t, store.Update([]*enode.Node{r1, r2})) - require.True(t, store.Exist(types.EnodeID(r1.ID()))) - require.True(t, store.Exist(types.EnodeID(r2.ID()))) - require.NoError(t, store.Update([]*enode.Node{r2})) - require.False(t, store.Exist(types.EnodeID(r1.ID()))) - require.True(t, store.Exist(types.EnodeID(r2.ID()))) -} - -func TestGetNodeByID(t *testing.T) { - store := NewPeerStore(newInMemCache(t)) - r1, err := RandomNode() - require.NoError(t, err) - require.NoError(t, store.Update([]*enode.Node{r1})) - require.Equal(t, r1, store.Get(types.EnodeID(r1.ID()))) - require.Nil(t, store.Get(types.EnodeID{1})) -} - -type fakePeerProvider struct { - peers []*p2p.Peer -} - -func (f fakePeerProvider) Peers() []*p2p.Peer { - return f.peers -} - -func TestNoConnected(t *testing.T) { - provider := fakePeerProvider{} - store := NewPeerStore(newInMemCache(t)) - _, err := GetFirstConnected(provider, store) - require.EqualError(t, ErrNoConnected, err.Error()) -} diff --git a/services/ext/mailservers/utils.go b/services/ext/mailservers/utils.go deleted file mode 100644 index dfbcda046..000000000 --- a/services/ext/mailservers/utils.go +++ /dev/null @@ -1,54 +0,0 @@ -package mailservers - -import ( - "sort" - - "github.com/ethereum/go-ethereum/p2p/enode" - - "github.com/status-im/status-go/eth-node/types" -) - -// GetFirstConnected returns first connected peer that is also added to a peer store. -// Raises ErrNoConnected if no peers are added to a peer store. -func GetFirstConnected(provider PeersProvider, store *PeerStore) (*enode.Node, error) { - peers := provider.Peers() - for _, p := range peers { - if store.Exist(types.EnodeID(p.ID())) { - return p.Node(), nil - } - } - return nil, ErrNoConnected -} - -// NodesNotifee interface to be notified when new nodes are received. -type NodesNotifee interface { - Notify([]*enode.Node) -} - -// EnsureUsedRecordsAddedFirst checks if any nodes were marked as connected before app went offline. -func EnsureUsedRecordsAddedFirst(ps *PeerStore, conn NodesNotifee) error { - records, err := ps.cache.LoadAll() - if err != nil { - return err - } - if len(records) == 0 { - return nil - } - sort.Slice(records, func(i, j int) bool { - return records[i].LastUsed.After(records[j].LastUsed) - }) - all := recordsToNodes(records) - if !records[0].LastUsed.IsZero() { - conn.Notify(all[:1]) - } - conn.Notify(all) - return nil -} - -func recordsToNodes(records []PeerRecord) []*enode.Node { - nodes := make([]*enode.Node, len(records)) - for i := range records { - nodes[i] = records[i].Node() - } - return nodes -} diff --git a/services/ext/mailservers/utils_test.go b/services/ext/mailservers/utils_test.go deleted file mode 100644 index 7a9109cc2..000000000 --- a/services/ext/mailservers/utils_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package mailservers - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/enode" - - "github.com/status-im/status-go/eth-node/types" -) - -func TestGetFirstConnected(t *testing.T) { - numPeers := 3 - nodes := make([]*enode.Node, numPeers) - peers := make([]*p2p.Peer, numPeers) - nodesMap := getMapWithRandomNodes(t, numPeers) - i := 0 - for _, node := range nodesMap { - nodes[i] = node - peers[i] = p2p.NewPeer(node.ID(), node.ID().String(), nil) - i++ - } - store := NewPeerStore(newInMemCache(t)) - provider := fakePeerProvider{peers} - _, err := GetFirstConnected(provider, store) - require.EqualError(t, ErrNoConnected, err.Error()) - require.NoError(t, store.Update(nodes)) - node, err := GetFirstConnected(provider, store) - require.NoError(t, err) - require.Contains(t, nodesMap, types.EnodeID(node.ID())) -} - -type trackingNodeNotifee struct { - calls [][]*enode.Node -} - -func (t *trackingNodeNotifee) Notify(nodes []*enode.Node) { - t.calls = append(t.calls, nodes) -} - -func TestEnsureNewRecordsAddedFirst(t *testing.T) { - notifee := new(trackingNodeNotifee) - store := NewPeerStore(newInMemCache(t)) - nodes := make([]*enode.Node, 3) - fillWithRandomNodes(t, nodes) - require.NoError(t, store.Update(nodes)) - record := NewPeerRecord(nodes[0]) - record.LastUsed = time.Now() - require.NoError(t, store.cache.UpdateRecord(record)) - require.NoError(t, EnsureUsedRecordsAddedFirst(store, notifee)) - require.Len(t, notifee.calls, 2) - require.Len(t, notifee.calls[0], 1) - require.Equal(t, nodes[0].ID(), notifee.calls[0][0].ID()) - require.Len(t, notifee.calls[1], 3) -} diff --git a/services/ext/service.go b/services/ext/service.go index 95d1b49c7..88913e322 100644 --- a/services/ext/service.go +++ b/services/ext/service.go @@ -49,7 +49,6 @@ import ( "github.com/status-im/status-go/server" "github.com/status-im/status-go/services/browsers" "github.com/status-im/status-go/services/communitytokens" - "github.com/status-im/status-go/services/ext/mailservers" mailserversDB "github.com/status-im/status-go/services/mailservers" "github.com/status-im/status-go/services/wallet" "github.com/status-im/status-go/services/wallet/collectibles" @@ -65,8 +64,6 @@ const providerID = "community" type EnvelopeEventsHandler interface { EnvelopeSent([][]byte) EnvelopeExpired([][]byte, error) - MailServerRequestCompleted(types.Hash, types.Hash, []byte, error) - MailServerRequestExpired(types.Hash) } // Service is a service that provides some additional API to whisper-based protocols like Whisper or Waku. @@ -78,9 +75,7 @@ type Service struct { n types.Node rpcClient *rpc.Client config params.NodeConfig - mailMonitor *MailRequestMonitor server *p2p.Server - peerStore *mailservers.PeerStore accountsDB *accounts.Database multiAccountsDB *multiaccounts.Database account *multiaccounts.Account @@ -94,18 +89,12 @@ func New( n types.Node, rpcClient *rpc.Client, ldb *leveldb.DB, - mailMonitor *MailRequestMonitor, - eventSub mailservers.EnvelopeEventSubscriber, ) *Service { - cache := mailservers.NewCache(ldb) - peerStore := mailservers.NewPeerStore(cache) return &Service{ - storage: db.NewLevelDBStorage(ldb), - n: n, - rpcClient: rpcClient, - config: config, - mailMonitor: mailMonitor, - peerStore: peerStore, + storage: db.NewLevelDBStorage(ldb), + n: n, + rpcClient: rpcClient, + config: config, } } @@ -117,9 +106,6 @@ func (s *Service) NodeID() *ecdsa.PrivateKey { } func (s *Service) GetPeer(rawURL string) (*enode.Node, error) { - if len(rawURL) == 0 { - return mailservers.GetFirstConnected(s.server, s.peerStore) - } return enode.ParseV4(rawURL) } @@ -150,11 +136,8 @@ func (s *Service) InitProtocol(nodeName string, identity *ecdsa.PrivateKey, appD envelopesMonitorConfig := &transport.EnvelopesMonitorConfig{ MaxAttempts: s.config.ShhextConfig.MaxMessageDeliveryAttempts, AwaitOnlyMailServerConfirmations: s.config.ShhextConfig.MailServerConfirmations, - IsMailserver: func(peer types.EnodeID) bool { - return s.peerStore.Exist(peer) - }, - EnvelopeEventsHandler: EnvelopeSignalHandler{}, - Logger: logger, + EnvelopeEventsHandler: EnvelopeSignalHandler{}, + Logger: logger, } s.accountsDB, err = accounts.NewDB(appDb) if err != nil { @@ -173,7 +156,6 @@ func (s *Service) InitProtocol(nodeName string, identity *ecdsa.PrivateKey, appD identity, s.n, s.config.ShhextConfig.InstallationID, - s.peerStore, params.Version, options..., ) diff --git a/services/ext/signal.go b/services/ext/signal.go index 9b0903754..9fa13f9f0 100644 --- a/services/ext/signal.go +++ b/services/ext/signal.go @@ -22,16 +22,6 @@ func (h EnvelopeSignalHandler) EnvelopeExpired(identifiers [][]byte, err error) signal.SendEnvelopeExpired(identifiers, err) } -// MailServerRequestCompleted triggered when the mailserver sends a message to notify that the request has been completed -func (h EnvelopeSignalHandler) MailServerRequestCompleted(requestID types.Hash, lastEnvelopeHash types.Hash, cursor []byte, err error) { - signal.SendMailServerRequestCompleted(requestID, lastEnvelopeHash, cursor, err) -} - -// MailServerRequestExpired triggered when the mailserver request expires -func (h EnvelopeSignalHandler) MailServerRequestExpired(hash types.Hash) { - signal.SendMailServerRequestExpired(hash) -} - // PublisherSignalHandler sends signals on protocol events type PublisherSignalHandler struct{} diff --git a/services/wakuext/api.go b/services/wakuext/api.go index a021fdddc..002495bac 100644 --- a/services/wakuext/api.go +++ b/services/wakuext/api.go @@ -17,7 +17,7 @@ type PublicAPI struct { // NewPublicAPI returns instance of the public API. func NewPublicAPI(s *Service) *PublicAPI { return &PublicAPI{ - PublicAPI: ext.NewPublicAPI(s.Service, s.w), + PublicAPI: ext.NewPublicAPI(s.Service), service: s, publicAPI: s.w.PublicWakuAPI(), log: log.New("package", "status-go/services/wakuext.PublicAPI"), diff --git a/services/wakuext/service.go b/services/wakuext/service.go index cd1ff9316..4096940f3 100644 --- a/services/wakuext/service.go +++ b/services/wakuext/service.go @@ -21,14 +21,8 @@ func New(config params.NodeConfig, n types.Node, rpcClient *rpc.Client, handler if err != nil { panic(err) } - delay := ext.DefaultRequestsDelay - if config.ShhextConfig.RequestsDelay != 0 { - delay = config.ShhextConfig.RequestsDelay - } - requestsRegistry := ext.NewRequestsRegistry(delay) - mailMonitor := ext.NewMailRequestMonitor(w, handler, requestsRegistry) return &Service{ - Service: ext.New(config, n, rpcClient, ldb, mailMonitor, w), + Service: ext.New(config, n, rpcClient, ldb), w: w, } } diff --git a/services/wakuv2ext/api.go b/services/wakuv2ext/api.go index e01a9d0d8..05ca60c34 100644 --- a/services/wakuv2ext/api.go +++ b/services/wakuv2ext/api.go @@ -17,7 +17,7 @@ type PublicAPI struct { // NewPublicAPI returns instance of the public API. func NewPublicAPI(s *Service) *PublicAPI { return &PublicAPI{ - PublicAPI: ext.NewPublicAPI(s.Service, s.w), + PublicAPI: ext.NewPublicAPI(s.Service), service: s, publicAPI: s.w.PublicWakuAPI(), log: log.New("package", "status-go/services/wakuext.PublicAPI"), diff --git a/services/wakuv2ext/service.go b/services/wakuv2ext/service.go index b57be687f..6fc8302c5 100644 --- a/services/wakuv2ext/service.go +++ b/services/wakuv2ext/service.go @@ -20,14 +20,8 @@ func New(config params.NodeConfig, n types.Node, rpcClient *rpc.Client, handler if err != nil { panic(err) } - delay := ext.DefaultRequestsDelay - if config.ShhextConfig.RequestsDelay != 0 { - delay = config.ShhextConfig.RequestsDelay - } - requestsRegistry := ext.NewRequestsRegistry(delay) - mailMonitor := ext.NewMailRequestMonitor(w, handler, requestsRegistry) return &Service{ - Service: ext.New(config, n, rpcClient, ldb, mailMonitor, w), + Service: ext.New(config, n, rpcClient, ldb), w: w, } } diff --git a/signal/events_shhext.go b/signal/events_shhext.go index 6dd70861e..5df823329 100644 --- a/signal/events_shhext.go +++ b/signal/events_shhext.go @@ -20,12 +20,6 @@ const ( // to any peer EventEnvelopeExpired = "envelope.expired" - // EventMailServerRequestCompleted is triggered when whisper receives a message ack from the mailserver - EventMailServerRequestCompleted = "mailserver.request.completed" - - // EventMailServerRequestExpired is triggered when request TTL ends - EventMailServerRequestExpired = "mailserver.request.expired" - // EventEnodeDiscovered is tiggered when enode has been discovered. EventEnodeDiscovered = "enode.discovered" @@ -165,26 +159,6 @@ func SendUpdateAvailable(available bool, latestVersion string, url string) { send(EventUpdateAvailable, UpdateAvailableSignal{Available: available, Version: latestVersion, URL: url}) } -// SendMailServerRequestCompleted triggered when mail server response has been received -func SendMailServerRequestCompleted(requestID types.Hash, lastEnvelopeHash types.Hash, cursor []byte, err error) { - errorMsg := "" - if err != nil { - errorMsg = err.Error() - } - sig := MailServerResponseSignal{ - RequestID: requestID, - LastEnvelopeHash: lastEnvelopeHash, - Cursor: hex.EncodeToString(cursor), - ErrorMsg: errorMsg, - } - send(EventMailServerRequestCompleted, sig) -} - -// SendMailServerRequestExpired triggered when mail server request expires -func SendMailServerRequestExpired(hash types.Hash) { - send(EventMailServerRequestExpired, EnvelopeSignal{Hash: hash}) -} - // EnodeDiscoveredSignal includes enode address and topic type EnodeDiscoveredSignal struct { Enode string `json:"enode"` diff --git a/waku/common/events.go b/waku/common/events.go index cebb2f08a..7707464f1 100644 --- a/waku/common/events.go +++ b/waku/common/events.go @@ -43,23 +43,6 @@ const ( // EventEnvelopeAvailable fires when envelop is available for filters EventEnvelopeAvailable EventType = "envelope.available" - - // EventMailServerRequestSent fires when such request is sent. - EventMailServerRequestSent EventType = "mailserver.request.sent" - - // EventMailServerRequestCompleted fires after mailserver sends all the requested messages - EventMailServerRequestCompleted EventType = "mailserver.request.completed" - - // EventMailServerRequestExpired fires after mailserver the request TTL ends. - // This event is independent and concurrent to EventMailServerRequestCompleted. - // Request should be considered as expired only if expiry event was received first. - EventMailServerRequestExpired EventType = "mailserver.request.expired" - - // EventMailServerEnvelopeArchived fires after an envelope has been archived - EventMailServerEnvelopeArchived EventType = "mailserver.envelope.archived" - - // EventMailServerSyncFinished fires when the sync of messages is finished. - EventMailServerSyncFinished EventType = "mailserver.sync.finished" ) // EnvelopeEvent represents an envelope event. diff --git a/waku/common/protocol.go b/waku/common/protocol.go index 0bedb0267..31540a75f 100644 --- a/waku/common/protocol.go +++ b/waku/common/protocol.go @@ -31,8 +31,6 @@ type Peer interface { // SetRWWriter sets the socket to read/write SetRWWriter(p2p.MsgReadWriter) - RequestHistoricMessages(*Envelope) error - SendHistoricMessageResponse([]byte) error SendP2PMessages([]*Envelope) error SendRawP2PDirect([]rlp.RawValue) error @@ -61,8 +59,6 @@ type WakuHost interface { MaxMessageSize() uint32 // LightClientMode returns whether the host is running in light client mode LightClientMode() bool - // Mailserver returns whether the host is running a mailserver - Mailserver() bool // LightClientModeConnectionRestricted indicates that connection to light client in light client mode not allowed LightClientModeConnectionRestricted() bool // ConfirmationsEnabled returns true if message confirmations are enabled. @@ -92,14 +88,5 @@ type WakuHost interface { // OnMessagesResponse handles when the peer receive a message response // from a mailserver OnMessagesResponse(MessagesResponse, Peer) error - // OnMessagesRequest handles when the peer receive a message request - // this only works if the peer is a mailserver - OnMessagesRequest(MessagesRequest, Peer) error - // OnDeprecatedMessagesRequest handles when the peer receive a message request - // using the *Envelope format. Currently the only production client (status-mobile) - // is exclusively using this one. - OnDeprecatedMessagesRequest(*Envelope, Peer) error - OnBatchAcknowledged(common.Hash, Peer) error - OnP2PRequestCompleted([]byte, Peer) error } diff --git a/waku/mailserver.go b/waku/mailserver.go deleted file mode 100644 index 8ccc3851c..000000000 --- a/waku/mailserver.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2019 The Waku Library Authors. -// -// The Waku library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The Waku library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty off -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the Waku library. If not, see . -// -// This software uses the go-ethereum library, which is licensed -// under the GNU Lesser General Public Library, version 3 or any later. - -package waku - -import ( - "bytes" - "errors" - "fmt" - - "github.com/status-im/status-go/waku/common" - - gethcommon "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/p2p/enode" -) - -const ( - mailServerFailedPayloadPrefix = "ERROR=" - cursorSize = 36 -) - -// MailServer represents a mail server, capable of -// archiving the old messages for subsequent delivery -// to the peers. Any implementation must ensure that both -// functions are thread-safe. Also, they must return ASAP. -// DeliverMail should use p2pMessageCode for delivery, -// in order to bypass the expiry checks. -type MailServer interface { - Archive(env *common.Envelope) - DeliverMail(peerID []byte, request *common.Envelope) // DEPRECATED; use Deliver() - Deliver(peerID []byte, request common.MessagesRequest) -} - -// MailServerResponse is the response payload sent by the mailserver. -type MailServerResponse struct { - LastEnvelopeHash gethcommon.Hash - Cursor []byte - Error error -} - -func invalidResponseSizeError(size int) error { - return fmt.Errorf("unexpected payload size: %d", size) -} - -// CreateMailServerRequestCompletedPayload creates a payload representing -// a successful request to mailserver -func CreateMailServerRequestCompletedPayload(requestID, lastEnvelopeHash gethcommon.Hash, cursor []byte) []byte { - payload := make([]byte, len(requestID)) - copy(payload, requestID[:]) - payload = append(payload, lastEnvelopeHash[:]...) - payload = append(payload, cursor...) - return payload -} - -// CreateMailServerRequestFailedPayload creates a payload representing -// a failed request to a mailserver -func CreateMailServerRequestFailedPayload(requestID gethcommon.Hash, err error) []byte { - payload := []byte(mailServerFailedPayloadPrefix) - payload = append(payload, requestID[:]...) - payload = append(payload, []byte(err.Error())...) - return payload -} - -// CreateMailServerEvent returns EnvelopeEvent with correct data -// if payload corresponds to any of the know mailserver events: -// * request completed successfully -// * request failed -// If the payload is unknown/unparseable, it returns `nil` -func CreateMailServerEvent(nodeID enode.ID, payload []byte) (*common.EnvelopeEvent, error) { - if len(payload) < gethcommon.HashLength { - return nil, invalidResponseSizeError(len(payload)) - } - - event, err := tryCreateMailServerRequestFailedEvent(nodeID, payload) - if err != nil { - return nil, err - } else if event != nil { - return event, nil - } - - return tryCreateMailServerRequestCompletedEvent(nodeID, payload) -} - -func tryCreateMailServerRequestFailedEvent(nodeID enode.ID, payload []byte) (*common.EnvelopeEvent, error) { - if len(payload) < gethcommon.HashLength+len(mailServerFailedPayloadPrefix) { - return nil, nil - } - - prefix, remainder := extractPrefix(payload, len(mailServerFailedPayloadPrefix)) - - if !bytes.Equal(prefix, []byte(mailServerFailedPayloadPrefix)) { - return nil, nil - } - - var ( - requestID gethcommon.Hash - errorMsg string - ) - - requestID, remainder = extractHash(remainder) - errorMsg = string(remainder) - - event := common.EnvelopeEvent{ - Peer: nodeID, - Hash: requestID, - Event: common.EventMailServerRequestCompleted, - Data: &MailServerResponse{ - Error: errors.New(errorMsg), - }, - } - - return &event, nil - -} - -func tryCreateMailServerRequestCompletedEvent(nodeID enode.ID, payload []byte) (*common.EnvelopeEvent, error) { - // check if payload is - // - requestID or - // - requestID + lastEnvelopeHash or - // - requestID + lastEnvelopeHash + cursor - // requestID is the hash of the request envelope. - // lastEnvelopeHash is the last envelope sent by the mail server - // cursor is the db key, 36 bytes: 4 for the timestamp + 32 for the envelope hash. - if len(payload) > gethcommon.HashLength*2+cursorSize { - return nil, invalidResponseSizeError(len(payload)) - } - - var ( - requestID gethcommon.Hash - lastEnvelopeHash gethcommon.Hash - cursor []byte - ) - - requestID, remainder := extractHash(payload) - - if len(remainder) >= gethcommon.HashLength { - lastEnvelopeHash, remainder = extractHash(remainder) - } - - if len(remainder) >= cursorSize { - cursor = remainder - } - - event := common.EnvelopeEvent{ - Peer: nodeID, - Hash: requestID, - Event: common.EventMailServerRequestCompleted, - Data: &MailServerResponse{ - LastEnvelopeHash: lastEnvelopeHash, - Cursor: cursor, - }, - } - - return &event, nil -} - -func extractHash(payload []byte) (gethcommon.Hash, []byte) { - prefix, remainder := extractPrefix(payload, gethcommon.HashLength) - return gethcommon.BytesToHash(prefix), remainder -} - -func extractPrefix(payload []byte, size int) ([]byte, []byte) { - return payload[:size], payload[size:] -} diff --git a/waku/v0/peer.go b/waku/v0/peer.go index 8ae8d68ca..0933cb05c 100644 --- a/waku/v0/peer.go +++ b/waku/v0/peer.go @@ -4,7 +4,6 @@ import ( "bytes" "errors" "fmt" - "io" "io/ioutil" "math" "net" @@ -227,16 +226,6 @@ func (p *Peer) handlePacket(packet p2p.Msg) error { p.logger.Warn("failed to decode direct message, peer will be disconnected", zap.String("peerID", types.EncodeHex(p.ID())), zap.Error(err)) return err } - case p2pRequestCode: - if err := p.handleP2PRequestCode(packet); err != nil { - p.logger.Warn("failed to decode p2p request message, peer will be disconnected", zap.String("peerID", types.EncodeHex(p.ID())), zap.Error(err)) - return err - } - case p2pRequestCompleteCode: - if err := p.handleP2PRequestCompleteCode(packet); err != nil { - p.logger.Warn("failed to decode p2p request complete message, peer will be disconnected", zap.String("peerID", types.EncodeHex(p.ID())), zap.Error(err)) - return err - } default: // New message common might be implemented in the future versions of Waku. // For forward compatibility, just ignore. @@ -289,43 +278,6 @@ func (p *Peer) handleMessageResponseCode(packet p2p.Msg) error { return p.host.OnMessagesResponse(response, p) } -func (p *Peer) handleP2PRequestCode(packet p2p.Msg) error { - // Must be processed if mail server is implemented. Otherwise ignore. - if !p.host.Mailserver() { - return nil - } - - // Read all data as we will try to decode it possibly twice. - data, err := ioutil.ReadAll(packet.Payload) - if err != nil { - return fmt.Errorf("invalid p2p request messages: %v", err) - } - r := bytes.NewReader(data) - packet.Payload = r - - var requestDeprecated common.Envelope - errDepReq := packet.Decode(&requestDeprecated) - if errDepReq == nil { - return p.host.OnDeprecatedMessagesRequest(&requestDeprecated, p) - } - p.logger.Info("failed to decode p2p request message (deprecated)", zap.String("peerID", types.EncodeHex(p.ID())), zap.Error(errDepReq)) - - // As we failed to decode the request, let's set the offset - // to the beginning and try decode it again. - if _, err := r.Seek(0, io.SeekStart); err != nil { - return fmt.Errorf("invalid p2p request message: %v", err) - } - - var request common.MessagesRequest - errReq := packet.Decode(&request) - if errReq == nil { - return p.host.OnMessagesRequest(request, p) - } - p.logger.Info("failed to decode p2p request message", zap.String("peerID", types.EncodeHex(p.ID())), zap.Error(errReq)) - - return errors.New("invalid p2p request message") -} - func (p *Peer) handleBatchAcknowledgeCode(packet p2p.Msg) error { var batchHash gethcommon.Hash if err := packet.Decode(&batchHash); err != nil { @@ -368,18 +320,6 @@ func (p *Peer) handleP2PMessageCode(packet p2p.Msg) error { return p.host.OnNewP2PEnvelopes(envelopes) } -func (p *Peer) handleP2PRequestCompleteCode(packet p2p.Msg) error { - if !p.trusted { - return nil - } - - var payload []byte - if err := packet.Decode(&payload); err != nil { - return fmt.Errorf("invalid p2p request complete message: %v", err) - } - return p.host.OnP2PRequestCompleted(payload, p) -} - // sendConfirmation sends messageResponseCode and batchAcknowledgedCode messages. func (p *Peer) sendConfirmation(data []byte, envelopeErrors []common.EnvelopeError) (err error) { batchHash := crypto.Keccak256Hash(data) diff --git a/waku/v1/peer.go b/waku/v1/peer.go index 87fd7eeba..cb3944323 100644 --- a/waku/v1/peer.go +++ b/waku/v1/peer.go @@ -4,7 +4,6 @@ import ( "bytes" "errors" "fmt" - "io" "io/ioutil" "math" "net" @@ -259,16 +258,6 @@ func (p *Peer) handlePacket(packet p2p.Msg) error { p.logger.Warn("failed to decode direct message, peer will be disconnected", zap.String("peerID", types.EncodeHex(p.ID())), zap.Error(err)) return err } - case p2pRequestCode: - if err := p.handleP2PRequestCode(packet); err != nil { - p.logger.Warn("failed to decode p2p request message, peer will be disconnected", zap.String("peerID", types.EncodeHex(p.ID())), zap.Error(err)) - return err - } - case p2pRequestCompleteCode: - if err := p.handleP2PRequestCompleteCode(packet); err != nil { - p.logger.Warn("failed to decode p2p request complete message, peer will be disconnected", zap.String("peerID", types.EncodeHex(p.ID())), zap.Error(err)) - return err - } default: // New message common might be implemented in the future versions of Waku. // For forward compatibility, just ignore. @@ -321,43 +310,6 @@ func (p *Peer) handleMessageResponseCode(packet p2p.Msg) error { return p.host.OnMessagesResponse(response, p) } -func (p *Peer) handleP2PRequestCode(packet p2p.Msg) error { - // Must be processed if mail server is implemented. Otherwise ignore. - if !p.host.Mailserver() { - return nil - } - - // Read all data as we will try to decode it possibly twice. - data, err := ioutil.ReadAll(packet.Payload) - if err != nil { - return fmt.Errorf("invalid p2p request messages: %v", err) - } - r := bytes.NewReader(data) - packet.Payload = r - - var requestDeprecated common.Envelope - errDepReq := packet.Decode(&requestDeprecated) - if errDepReq == nil { - return p.host.OnDeprecatedMessagesRequest(&requestDeprecated, p) - } - p.logger.Info("failed to decode p2p request message (deprecated)", zap.String("peerID", types.EncodeHex(p.ID())), zap.Error(errDepReq)) - - // As we failed to decode the request, let's set the offset - // to the beginning and try decode it again. - if _, err := r.Seek(0, io.SeekStart); err != nil { - return fmt.Errorf("invalid p2p request message: %v", err) - } - - var request common.MessagesRequest - errReq := packet.Decode(&request) - if errReq == nil { - return p.host.OnMessagesRequest(request, p) - } - p.logger.Info("failed to decode p2p request message", zap.String("peerID", types.EncodeHex(p.ID())), zap.Error(errReq)) - - return errors.New("invalid p2p request message") -} - func (p *Peer) handleBatchAcknowledgeCode(packet p2p.Msg) error { var batchHash gethcommon.Hash if err := packet.Decode(&batchHash); err != nil { @@ -400,18 +352,6 @@ func (p *Peer) handleP2PMessageCode(packet p2p.Msg) error { return p.host.OnNewP2PEnvelopes(envelopes) } -func (p *Peer) handleP2PRequestCompleteCode(packet p2p.Msg) error { - if !p.trusted { - return nil - } - - var payload []byte - if err := packet.Decode(&payload); err != nil { - return fmt.Errorf("invalid p2p request complete message: %v", err) - } - return p.host.OnP2PRequestCompleted(payload, p) -} - // sendConfirmation sends messageResponseCode and batchAcknowledgedCode messages. func (p *Peer) sendConfirmation(data []byte, envelopeErrors []common.EnvelopeError) (err error) { batchHash := crypto.Keccak256Hash(data) diff --git a/waku/waku.go b/waku/waku.go index 218cfdf40..7277cfc6b 100644 --- a/waku/waku.go +++ b/waku/waku.go @@ -102,8 +102,6 @@ type Waku struct { settings settings // Holds configuration settings that can be dynamically changed settingsMu sync.RWMutex // Mutex to sync the settings access - mailServer MailServer - rateLimiter *common.PeerRateLimiter envelopeFeed event.Feed @@ -484,12 +482,6 @@ func (w *Waku) Protocols() []p2p.Protocol { return w.protocols } -// RegisterMailServer registers MailServer interface. -// MailServer will process all the incoming messages with p2pRequestCode. -func (w *Waku) RegisterMailServer(server MailServer) { - w.mailServer = server -} - // SetRateLimiter registers a rate limiter. func (w *Waku) RegisterRateLimiter(r *common.PeerRateLimiter) { w.rateLimiter = r @@ -640,14 +632,6 @@ func (w *Waku) AllowP2PMessagesFromPeer(peerID []byte) error { return nil } -func (w *Waku) SendHistoricMessageResponse(peerID []byte, payload []byte) error { - peer, err := w.getPeer(peerID) - if err != nil { - return err - } - return peer.SendHistoricMessageResponse(payload) -} - // SendP2PMessage sends a peer-to-peer message to a specific peer. // It sends one or more envelopes in a single batch. func (w *Waku) SendP2PMessages(peerID []byte, envelopes ...*common.Envelope) error { @@ -1126,30 +1110,6 @@ func (w *Waku) OnNewP2PEnvelopes(envelopes []*common.Envelope) error { return nil } -func (w *Waku) Mailserver() bool { - return w.mailServer != nil -} - -func (w *Waku) OnMessagesRequest(request common.MessagesRequest, p common.Peer) error { - w.mailServer.Deliver(p.ID(), request) - return nil -} - -func (w *Waku) OnDeprecatedMessagesRequest(request *common.Envelope, p common.Peer) error { - w.mailServer.DeliverMail(p.ID(), request) - return nil -} - -func (w *Waku) OnP2PRequestCompleted(payload []byte, p common.Peer) error { - msEvent, err := CreateMailServerEvent(p.EnodeID(), payload) - if err != nil { - return fmt.Errorf("invalid p2p request complete payload: %v", err) - } - - w.postP2P(*msEvent) - return nil -} - func (w *Waku) OnMessagesResponse(response common.MessagesResponse, p common.Peer) error { w.envelopeFeed.Send(common.EnvelopeEvent{ Batch: response.Hash, @@ -1338,14 +1298,6 @@ func (w *Waku) addAndBridge(envelope *common.Envelope, isP2P bool, bridged bool) common.EnvelopesCachedCounter.WithLabelValues("miss").Inc() common.EnvelopesSizeMeter.Observe(float64(envelope.Size())) w.postEvent(envelope, isP2P) // notify the local node about the new message - if w.mailServer != nil { - w.mailServer.Archive(envelope) - w.envelopeFeed.Send(common.EnvelopeEvent{ - Topic: envelope.Topic, - Hash: envelope.Hash(), - Event: common.EventMailServerEnvelopeArchived, - }) - } // Bridge only envelopes that are not p2p messages. // In particular, if a node is a lightweight node, // it should not bridge any envelopes. diff --git a/waku/waku_version_test.go b/waku/waku_version_test.go index 1b32040f4..43f94310a 100644 --- a/waku/waku_version_test.go +++ b/waku/waku_version_test.go @@ -323,23 +323,6 @@ func (s *WakuTestSuite) TestWakuTimeDesyncEnvelopeIgnored() { } } -type MockMailserver struct { - deliverMail func([]byte, *common.Envelope) -} - -func (*MockMailserver) Archive(e *common.Envelope) { -} - -func (*MockMailserver) Deliver(peerID []byte, r common.MessagesRequest) { -} - -func (m *MockMailserver) DeliverMail(peerID []byte, e *common.Envelope) { - - if m.deliverMail != nil { - m.deliverMail(peerID, e) - } -} - func (s *WakuTestSuite) TestRateLimiterIntegration() { conf := &Config{ MinimumAcceptedPoW: 0, @@ -370,61 +353,6 @@ func (s *WakuTestSuite) TestRateLimiterIntegration() { } } -func (s *WakuTestSuite) TestMailserverCompletionEvent() { - w1 := New(nil, nil) - s.Require().NoError(w1.Start()) - defer func() { handleError(s.T(), w1.Stop()) }() - - rw1, rw2 := p2p.MsgPipe() - errorc := make(chan error, 1) - go func() { - err := w1.HandlePeer(s.newPeer(w1, p2p.NewPeer(enode.ID{}, "1", []p2p.Cap{}), rw1, nil, s.stats), rw1) - errorc <- err - }() - - w2 := New(nil, nil) - s.Require().NoError(w2.Start()) - defer func() { handleError(s.T(), w2.Stop()) }() - - peer2 := s.newPeer(w2, p2p.NewPeer(enode.ID{1}, "1", nil), rw2, nil, s.stats) - peer2.SetPeerTrusted(true) - - events := make(chan common.EnvelopeEvent) - sub := w1.SubscribeEnvelopeEvents(events) - defer sub.Unsubscribe() - - envelopes := []*common.Envelope{{Data: []byte{1}}, {Data: []byte{2}}} - s.Require().NoError(peer2.Start()) - // Set peer trusted, we know the peer has been added as handshake was successful - w1.getPeers()[0].SetPeerTrusted(true) - - s.Require().NoError(peer2.SendP2PMessages(envelopes)) - s.Require().NoError(peer2.SendHistoricMessageResponse(make([]byte, 100))) - s.Require().NoError(rw2.Close()) - - // Wait for all messages to be read - err := <-errorc - s.Require().EqualError(err, "p2p: read or write on closed message pipe") - - after := time.After(2 * time.Second) - count := 0 - for { - select { - case <-after: - s.Require().FailNow("timed out waiting for all events") - case ev := <-events: - switch ev.Event { - case common.EventEnvelopeAvailable: - count++ - case common.EventMailServerRequestCompleted: - s.Require().Equal(count, len(envelopes), - "all envelope.avaiable events mut be recevied before request is compelted") - return - } - } - } -} - // two generic waku node handshake func (s *WakuTestSuite) TestPeerHandshakeWithTwoFullNode() { rw1, rw2 := p2p.MsgPipe()