diff --git a/api/geth_backend.go b/api/geth_backend.go index 91c85fcdf..7975c6808 100644 --- a/api/geth_backend.go +++ b/api/geth_backend.go @@ -2651,7 +2651,7 @@ func (b *GethStatusBackend) injectAccountsIntoWakuService(w types.WakuKeyManager } if st != nil { - if err := st.InitProtocol(b.statusNode.GethNode().Config().Name, identity, b.appDB, b.walletDB, b.statusNode.HTTPServer(), b.multiaccountsDB, acc, b.accountManager, b.statusNode.RPCClient(), b.statusNode.WalletService(), b.statusNode.CommunityTokensService(), b.statusNode.WakuV2Service(), logutils.ZapLogger()); err != nil { + if err := st.InitProtocol(b.statusNode.GethNode().Config().Name, identity, b.appDB, b.walletDB, b.statusNode.HTTPServer(), b.multiaccountsDB, acc, b.accountManager, b.statusNode.RPCClient(), b.statusNode.WalletService(), b.statusNode.CommunityTokensService(), b.statusNode.WakuV2Service(), logutils.ZapLogger(), b.statusNode.AccountsFeed()); err != nil { return err } // Set initial connection state diff --git a/node/get_status_node.go b/node/get_status_node.go index 055942254..17f5f5e13 100644 --- a/node/get_status_node.go +++ b/node/get_status_node.go @@ -133,7 +133,8 @@ type StatusNode struct { connectorSrvc *connector.Service appGeneralSrvc *appgeneral.Service - walletFeed event.Feed + accountsFeed event.Feed + walletFeed event.Feed } // New makes new instance of StatusNode. diff --git a/node/status_node_services.go b/node/status_node_services.go index e207525ed..d45ab1fde 100644 --- a/node/status_node_services.go +++ b/node/status_node_services.go @@ -74,7 +74,6 @@ var ( ) func (b *StatusNode) initServices(config *params.NodeConfig, mediaServer *server.MediaServer) error { - accountsFeed := &event.Feed{} settingsFeed := &event.Feed{} accDB, err := accounts.NewDB(b.appDB) if err != nil { @@ -97,7 +96,7 @@ func (b *StatusNode) initServices(config *params.NodeConfig, mediaServer *server services = append(services, b.CommunityTokensService()) services = append(services, b.stickersService(accDB)) services = append(services, b.updatesService()) - services = appendIf(b.appDB != nil && b.multiaccountsDB != nil, services, b.accountsService(accountsFeed, accDB, mediaServer)) + services = appendIf(b.appDB != nil && b.multiaccountsDB != nil, services, b.accountsService(&b.accountsFeed, accDB, mediaServer)) services = appendIf(config.BrowsersConfig.Enabled, services, b.browsersService()) services = appendIf(config.PermissionsConfig.Enabled, services, b.permissionsService()) services = appendIf(config.MailserversConfig.Enabled, services, b.mailserversService()) @@ -109,7 +108,7 @@ func (b *StatusNode) initServices(config *params.NodeConfig, mediaServer *server // Wallet Service is used by wakuExtSrvc/wakuV2ExtSrvc // Keep this initialization before the other two if config.WalletConfig.Enabled { - walletService := b.walletService(accDB, b.appDB, accountsFeed, settingsFeed, &b.walletFeed, config.WalletConfig.StatusProxyStageName) + walletService := b.walletService(accDB, b.appDB, &b.accountsFeed, settingsFeed, &b.walletFeed, config.WalletConfig.StatusProxyStageName) services = append(services, walletService) } @@ -575,6 +574,10 @@ func (b *StatusNode) WalletService() *wallet.Service { return b.walletSrvc } +func (b *StatusNode) AccountsFeed() *event.Feed { + return &b.accountsFeed +} + func (b *StatusNode) SetWalletCommunityInfoProvider(provider thirdparty.CommunityInfoProvider) { if b.walletSrvc != nil { b.walletSrvc.SetWalletCommunityInfoProvider(provider) diff --git a/protocol/messenger_backup_test.go b/protocol/messenger_backup_test.go index d4e03e936..bba579d9a 100644 --- a/protocol/messenger_backup_test.go +++ b/protocol/messenger_backup_test.go @@ -6,9 +6,13 @@ import ( "fmt" "reflect" "testing" + "time" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/event" v1protocol "github.com/status-im/status-go/protocol/v1" "github.com/status-im/status-go/protocol/wakusync" + "github.com/status-im/status-go/services/accounts/accountsevent" "github.com/stretchr/testify/suite" @@ -697,8 +701,12 @@ func (s *MessengerBackupSuite) TestBackupKeypairs() { s.Require().True(accounts.SameKeypairs(seedKp, dbSeedKp1)) // Create bob2 - bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil) + accountsFeed := &event.Feed{} + bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, []Option{WithAccountsFeed(accountsFeed)}) s.Require().NoError(err) + s.Require().NotNil(bob2.config.accountsFeed) + ch := make(chan accountsevent.Event, 20) + sub := bob2.config.accountsFeed.Subscribe(ch) defer TearDownMessenger(&s.Suite, bob2) // Backup @@ -728,6 +736,37 @@ func (s *MessengerBackupSuite) TestBackupKeypairs() { dbSeedKp2, err := bob2.settings.GetKeypairByKeyUID(seedKp.KeyUID) s.Require().NoError(err) s.Require().True(accounts.SameKeypairsWithDifferentSyncedFrom(seedKp, dbSeedKp2, false, accounts.SyncedFromBackup, accounts.AccountNonOperable)) + + // Check whether accounts added event is sent + expectedAddresses := [][]common.Address{} + profileKpWalletAddresses := []common.Address{} + seedKpAddresses := []common.Address{} + for _, acc := range dbProfileKp2.Accounts { + if acc.Chat { + continue + } + profileKpWalletAddresses = append(profileKpWalletAddresses, common.Address(acc.Address)) + } + expectedAddresses = append(expectedAddresses, profileKpWalletAddresses) + + for _, acc := range dbSeedKp2.Accounts { + seedKpAddresses = append(seedKpAddresses, common.Address(acc.Address)) + } + expectedAddresses = append(expectedAddresses, seedKpAddresses) + + for i := 0; i < len(expectedAddresses); i++ { + select { + case <-time.After(1 * time.Second): + s.Fail("Timed out waiting for accountsevent") + case event := <-ch: + switch event.Type { + case accountsevent.EventTypeAdded: + s.Require().Len(event.Accounts, len(expectedAddresses[i])) + s.Require().True(reflect.DeepEqual(expectedAddresses[i], event.Accounts)) + } + } + } + sub.Unsubscribe() } func (s *MessengerBackupSuite) TestBackupKeycards() { @@ -811,8 +850,12 @@ func (s *MessengerBackupSuite) TestBackupWatchOnlyAccounts() { s.Require().True(haveSameElements(woAccounts, dbWoAccounts1, accounts.SameAccounts)) // Create bob2 - bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil) + accountsFeed := &event.Feed{} + bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, []Option{WithAccountsFeed(accountsFeed)}) s.Require().NoError(err) + s.Require().NotNil(bob2.config.accountsFeed) + ch := make(chan accountsevent.Event, 20) + sub := bob2.config.accountsFeed.Subscribe(ch) defer TearDownMessenger(&s.Suite, bob2) // Backup @@ -838,6 +881,19 @@ func (s *MessengerBackupSuite) TestBackupWatchOnlyAccounts() { s.Require().NoError(err) s.Require().Equal(len(woAccounts), len(dbWoAccounts2)) s.Require().True(haveSameElements(woAccounts, dbWoAccounts2, accounts.SameAccounts)) + + // Check whether accounts added event is sent + select { + case <-time.After(1 * time.Second): + s.Fail("Timed out waiting for accountsevent") + case event := <-ch: + switch event.Type { + case accountsevent.EventTypeAdded: + s.Require().Len(event.Accounts, 1) + s.Require().Equal(common.Address(dbWoAccounts2[0].Address), event.Accounts[0]) + } + } + sub.Unsubscribe() } func (s *MessengerBackupSuite) TestBackupChats() { diff --git a/protocol/messenger_config.go b/protocol/messenger_config.go index d98d9edea..92950191e 100644 --- a/protocol/messenger_config.go +++ b/protocol/messenger_config.go @@ -5,6 +5,8 @@ import ( "encoding/json" "time" + "github.com/ethereum/go-ethereum/event" + "github.com/status-im/status-go/account" "github.com/status-im/status-go/rpc" "github.com/status-im/status-go/server" @@ -120,6 +122,8 @@ type config struct { messageResendMaxCount int communityManagerOptions []communities.ManagerOption + + accountsFeed *event.Feed } func messengerDefaultConfig() config { @@ -414,3 +418,10 @@ func WithAccountManager(accountManager account.Manager) Option { return nil } } + +func WithAccountsFeed(feed *event.Feed) Option { + return func(c *config) error { + c.accountsFeed = feed + return nil + } +} diff --git a/protocol/messenger_handler.go b/protocol/messenger_handler.go index 160e226aa..d8d2fd783 100644 --- a/protocol/messenger_handler.go +++ b/protocol/messenger_handler.go @@ -9,6 +9,9 @@ import ( "sync" "time" + gethcommon "github.com/ethereum/go-ethereum/common" + + "github.com/status-im/status-go/services/accounts/accountsevent" "github.com/status-im/status-go/services/browsers" "github.com/status-im/status-go/signal" @@ -3376,6 +3379,18 @@ func (m *Messenger) handleSyncWatchOnlyAccount(message *protobuf.SyncAccount, fr return nil, err } + if m.config.accountsFeed != nil { + var eventType accountsevent.EventType + if acc.Removed { + eventType = accountsevent.EventTypeRemoved + } else { + eventType = accountsevent.EventTypeAdded + } + m.config.accountsFeed.Send(accountsevent.Event{ + Type: eventType, + Accounts: []gethcommon.Address{gethcommon.Address(acc.Address)}, + }) + } return acc, nil } @@ -3676,6 +3691,40 @@ func (m *Messenger) handleSyncKeypair(message *protobuf.SyncKeypair, fromLocalPa if err != nil { return nil, err } + + if m.config.accountsFeed != nil { + addedAddresses := []gethcommon.Address{} + removedAddresses := []gethcommon.Address{} + if dbKeypair.Removed { + for _, acc := range dbKeypair.Accounts { + removedAddresses = append(removedAddresses, gethcommon.Address(acc.Address)) + } + } else { + for _, acc := range dbKeypair.Accounts { + if acc.Chat { + continue + } + if acc.Removed { + removedAddresses = append(removedAddresses, gethcommon.Address(acc.Address)) + } else { + addedAddresses = append(addedAddresses, gethcommon.Address(acc.Address)) + } + } + } + if len(addedAddresses) > 0 { + m.config.accountsFeed.Send(accountsevent.Event{ + Type: accountsevent.EventTypeAdded, + Accounts: addedAddresses, + }) + } + if len(removedAddresses) > 0 { + m.config.accountsFeed.Send(accountsevent.Event{ + Type: accountsevent.EventTypeRemoved, + Accounts: removedAddresses, + }) + } + } + return dbKeypair, nil } diff --git a/services/ext/service.go b/services/ext/service.go index 95d1b49c7..bfb1e1f9c 100644 --- a/services/ext/service.go +++ b/services/ext/service.go @@ -19,6 +19,7 @@ import ( commongethtypes "github.com/ethereum/go-ethereum/common" gethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" @@ -123,7 +124,7 @@ func (s *Service) GetPeer(rawURL string) (*enode.Node, error) { return enode.ParseV4(rawURL) } -func (s *Service) InitProtocol(nodeName string, identity *ecdsa.PrivateKey, appDb, walletDb *sql.DB, httpServer *server.MediaServer, multiAccountDb *multiaccounts.Database, acc *multiaccounts.Account, accountManager *account.GethManager, rpcClient *rpc.Client, walletService *wallet.Service, communityTokensService *communitytokens.Service, wakuService *wakuv2.Waku, logger *zap.Logger) error { +func (s *Service) InitProtocol(nodeName string, identity *ecdsa.PrivateKey, appDb, walletDb *sql.DB, httpServer *server.MediaServer, multiAccountDb *multiaccounts.Database, acc *multiaccounts.Account, accountManager *account.GethManager, rpcClient *rpc.Client, walletService *wallet.Service, communityTokensService *communitytokens.Service, wakuService *wakuv2.Waku, logger *zap.Logger, accountsFeed *event.Feed) error { var err error if !s.config.ShhextConfig.PFSEnabled { return nil @@ -163,7 +164,7 @@ func (s *Service) InitProtocol(nodeName string, identity *ecdsa.PrivateKey, appD s.multiAccountsDB = multiAccountDb s.account = acc - options, err := buildMessengerOptions(s.config, identity, appDb, walletDb, httpServer, s.rpcClient, s.multiAccountsDB, acc, envelopesMonitorConfig, s.accountsDB, walletService, communityTokensService, wakuService, logger, &MessengerSignalsHandler{}, accountManager) + options, err := buildMessengerOptions(s.config, identity, appDb, walletDb, httpServer, s.rpcClient, s.multiAccountsDB, acc, envelopesMonitorConfig, s.accountsDB, walletService, communityTokensService, wakuService, logger, &MessengerSignalsHandler{}, accountManager, accountsFeed) if err != nil { return err } @@ -397,6 +398,7 @@ func buildMessengerOptions( logger *zap.Logger, messengerSignalsHandler protocol.MessengerSignalsHandler, accountManager account.Manager, + accountsFeed *event.Feed, ) ([]protocol.Option, error) { options := []protocol.Option{ protocol.WithCustomLogger(logger), @@ -420,6 +422,7 @@ func buildMessengerOptions( protocol.WithCommunityTokensService(communityTokensService), protocol.WithWakuService(wakuService), protocol.WithAccountManager(accountManager), + protocol.WithAccountsFeed(accountsFeed), } if config.ShhextConfig.DataSyncEnabled { diff --git a/services/wakuext/api_test.go b/services/wakuext/api_test.go index e3b059878..4f79a242c 100644 --- a/services/wakuext/api_test.go +++ b/services/wakuext/api_test.go @@ -17,6 +17,7 @@ import ( "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/storage" + "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/status-im/status-go/appdatabase" @@ -136,7 +137,9 @@ func TestInitProtocol(t *testing.T) { defer func() { require.NoError(t, cleanupWalletDB()) }() require.NoError(t, err) - err = service.InitProtocol("Test", privateKey, appDB, walletDB, nil, multiAccounts, acc, nil, nil, nil, nil, nil, zap.NewNop()) + accountsFeed := &event.Feed{} + + err = service.InitProtocol("Test", privateKey, appDB, walletDB, nil, multiAccounts, acc, nil, nil, nil, nil, nil, zap.NewNop(), accountsFeed) require.NoError(t, err) } @@ -207,7 +210,9 @@ func (s *ShhExtSuite) createAndAddNode() { walletDB, err := helpers.SetupTestMemorySQLDB(&walletdatabase.DbInitializer{}) s.Require().NoError(err) - err = service.InitProtocol("Test", privateKey, appDB, walletDB, nil, multiAccounts, acc, nil, nil, nil, nil, nil, zap.NewNop()) + accountsFeed := &event.Feed{} + + err = service.InitProtocol("Test", privateKey, appDB, walletDB, nil, multiAccounts, acc, nil, nil, nil, nil, nil, zap.NewNop(), accountsFeed) s.NoError(err) stack.RegisterLifecycle(service)