status-go/protocol/messenger_sync_saved_addres...

375 lines
11 KiB
Go

package protocol
import (
"context"
"crypto/ecdsa"
"reflect"
"sort"
"testing"
"github.com/stretchr/testify/suite"
"go.uber.org/zap"
"github.com/ethereum/go-ethereum/common"
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/multiaccounts/settings"
"github.com/status-im/status-go/protocol/encryption/multidevice"
"github.com/status-im/status-go/protocol/tt"
"github.com/status-im/status-go/services/wallet"
"github.com/status-im/status-go/waku"
)
func TestMessengerSyncSavedAddressesSuite(t *testing.T) {
suite.Run(t, new(MessengerSyncSavedAddressesSuite))
}
type MessengerSyncSavedAddressesSuite struct {
suite.Suite
main *Messenger // main instance of Messenger paired with `other`
other *Messenger
privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger
// If one wants to send messages between different instances of Messenger,
// a single Waku service should be shared.
shh types.Waku
logger *zap.Logger
}
func (s *MessengerSyncSavedAddressesSuite) SetupTest() {
s.logger = tt.MustCreateTestLogger()
config := waku.DefaultConfig
config.MinimumAcceptedPoW = 0
shh := waku.New(&config, s.logger)
s.shh = gethbridge.NewGethWakuWrapper(shh)
s.Require().NoError(shh.Start())
s.main = s.newMessenger(s.logger.Named("main"))
s.privateKey = s.main.identity
var err error
// Create new device and add main account to
s.other, err = newMessengerWithKey(s.shh, s.main.identity, s.logger.Named("other"), nil)
s.Require().NoError(err)
// Pair devices (main and other)
imOther := &multidevice.InstallationMetadata{
Name: "other-device",
DeviceType: "other-device-type",
}
err = s.other.SetInstallationMetadata(s.other.installationID, imOther)
s.Require().NoError(err)
response, err := s.other.SendPairInstallation(context.Background(), nil)
s.Require().NoError(err)
s.Require().NotNil(response)
// Wait for the message to reach its destination
_, err = WaitOnMessengerResponse(
s.main,
func(r *MessengerResponse) bool { return len(r.Installations) > 0 },
"installation not received",
)
s.Require().NoError(err)
err = s.main.EnableInstallation(s.other.installationID)
s.Require().NoError(err)
}
func (s *MessengerSyncSavedAddressesSuite) TearDownTest() {
TearDownMessenger(&s.Suite, s.main)
}
func (s *MessengerSyncSavedAddressesSuite) newMessenger(logger *zap.Logger) *Messenger {
privateKey, err := crypto.GenerateKey()
s.Require().NoError(err)
messenger, err := newMessengerWithKey(s.shh, privateKey, logger, nil)
s.Require().NoError(err)
return messenger
}
// Helpers duplicate of wallet test. Could not import it from saved_addresses_test.go
func contains[T comparable](container []T, element T, isEqual func(T, T) bool) bool {
for _, e := range container {
if isEqual(e, element) {
return true
}
}
return false
}
func haveSameElements[T comparable](a []T, b []T, isEqual func(T, T) bool) bool {
if len(a) != len(b) {
return false
}
for _, v := range a {
if !contains(b, v, isEqual) {
return false
}
}
return true
}
func savedAddressDataIsEqual(a, b *wallet.SavedAddress) bool {
return a.Address == b.Address && a.IsTest == b.IsTest && a.Name == b.Name &&
a.ENSName == b.ENSName && a.ChainShortNames == b.ChainShortNames && a.ColorID == b.ColorID
}
func (s *MessengerSyncSavedAddressesSuite) TestSyncExistingSavedAddresses() {
var isTestChain1 bool = false
var isTestChain2 bool = true
var testAddress1 = common.Address{1}
// Add saved addresses to main device
sa1 := wallet.SavedAddress{
Address: testAddress1,
Name: "TestC1A1",
IsTest: isTestChain1,
}
sa2 := wallet.SavedAddress{
ENSName: "test.ens.eth",
Name: "TestC2A1",
IsTest: isTestChain2,
}
err := s.main.UpsertSavedAddress(context.Background(), sa1)
s.Require().NoError(err)
err = s.main.UpsertSavedAddress(context.Background(), sa2)
s.Require().NoError(err)
// Wait and check that saved addresses are synced
_, err = WaitOnMessengerResponse(
s.other,
func(r *MessengerResponse) bool {
if len(r.SavedAddresses()) == 2 {
sas := r.SavedAddresses()
s.Require().True(haveSameElements([]*wallet.SavedAddress{&sa1, &sa2}, []*wallet.SavedAddress{sas[0], sas[1]}, savedAddressDataIsEqual))
return true
}
return false
},
"expected to receive two changes",
)
s.Require().NoError(err)
savedAddresses, err := s.other.savedAddressesManager.GetSavedAddresses()
s.Require().NoError(err)
s.Require().Equal(2, len(savedAddresses))
s.Require().True(haveSameElements([]*wallet.SavedAddress{&sa1, &sa2}, savedAddresses, savedAddressDataIsEqual))
}
func (s *MessengerSyncSavedAddressesSuite) TestSyncSavedAddresses() {
var isTestChain1 bool = true
var testAddress1 = common.Address{1}
// Add saved addresses to main device
sa1 := wallet.SavedAddress{
Address: testAddress1,
Name: "TestC1A1",
IsTest: isTestChain1,
}
sa2 := wallet.SavedAddress{
ENSName: "test.ens.eth",
Name: "TestC1A2",
IsTest: isTestChain1,
}
err := s.main.UpsertSavedAddress(context.Background(), sa1)
s.Require().NoError(err)
err = s.main.UpsertSavedAddress(context.Background(), sa2)
s.Require().NoError(err)
// Wait and check that saved addresses are synced
_, err = WaitOnMessengerResponse(
s.other,
func(r *MessengerResponse) bool {
if len(r.SavedAddresses()) == 2 {
sas := r.SavedAddresses()
s.Require().True(haveSameElements([]*wallet.SavedAddress{&sa1, &sa2}, []*wallet.SavedAddress{sas[0], sas[1]}, savedAddressDataIsEqual))
return true
}
return false
},
"expected to receive two changes",
)
s.Require().NoError(err)
savedAddresses, err := s.other.savedAddressesManager.GetSavedAddresses()
s.Require().NoError(err)
s.Require().Equal(2, len(savedAddresses))
s.Require().True(haveSameElements([]*wallet.SavedAddress{&sa1, &sa2}, savedAddresses, savedAddressDataIsEqual))
}
func (s *MessengerSyncSavedAddressesSuite) requireSavedAddressesEqual(a, b []*wallet.SavedAddress) {
sort.Slice(a, func(i, j int) bool {
return a[i].Address.Hex() < a[j].Address.Hex()
})
sort.Slice(b, func(i, j int) bool {
return b[i].Address.Hex() < b[j].Address.Hex()
})
s.Require().True(reflect.DeepEqual(a, b))
}
func (s *MessengerSyncSavedAddressesSuite) testSyncDeletesOfSavedAddresses(testModeMain bool, testModeOther bool) {
sa1 := &wallet.SavedAddress{
Address: common.Address{1},
Name: "TestC1A1",
IsTest: true,
}
sa2 := &wallet.SavedAddress{
Address: common.Address{2},
Name: "TestC1A2",
IsTest: false,
}
err := s.main.settings.SaveSettingField(settings.TestNetworksEnabled, testModeMain)
s.Require().NoError(err)
err = s.other.settings.SaveSettingField(settings.TestNetworksEnabled, testModeOther)
s.Require().NoError(err)
// Add saved addresses to main device
err = s.main.UpsertSavedAddress(context.Background(), *sa1)
s.Require().NoError(err)
err = s.main.UpsertSavedAddress(context.Background(), *sa2)
s.Require().NoError(err)
// Wait and check that saved addresses are synced
{
response, err := WaitOnMessengerResponse(
s.other,
func(r *MessengerResponse) bool {
return len(r.SavedAddresses()) == 2
},
"expected to receive two changes",
)
s.Require().NoError(err)
otherSavedAddresses := response.SavedAddresses()
s.Require().Len(otherSavedAddresses, 2)
// Check that the UpdateClock was bumped
s.Require().GreaterOrEqual(otherSavedAddresses[0].CreatedAt, int64(0))
s.Require().GreaterOrEqual(otherSavedAddresses[1].CreatedAt, int64(0))
s.Require().Greater(otherSavedAddresses[0].UpdateClock, uint64(0))
s.Require().Greater(otherSavedAddresses[1].UpdateClock, uint64(0))
// Reset the UpdateClock to 0 for comparison
otherSavedAddresses[0].CreatedAt = 0
otherSavedAddresses[1].CreatedAt = 0
otherSavedAddresses[0].UpdateClock = 0
otherSavedAddresses[1].UpdateClock = 0
s.requireSavedAddressesEqual([]*wallet.SavedAddress{sa1, sa2}, otherSavedAddresses)
// Ensure the messenger actually has the saved addresses, not just the response
savedAddresses, err := s.other.savedAddressesManager.GetSavedAddresses()
s.Require().NoError(err)
s.Require().Len(savedAddresses, 2)
// Reset the UpdateClock to 0 for comparison
savedAddresses[0].CreatedAt = 0
savedAddresses[1].CreatedAt = 0
savedAddresses[0].UpdateClock = 0
savedAddresses[1].UpdateClock = 0
s.requireSavedAddressesEqual([]*wallet.SavedAddress{sa1, sa2}, savedAddresses)
}
// Delete saved address 1 (test mode = true) and sync with the other device
{
err = s.main.DeleteSavedAddress(context.Background(), sa1.Address, sa1.IsTest)
s.Require().NoError(err)
// Ensure the removal
savedAddresses, err := s.main.savedAddressesManager.GetSavedAddresses()
s.Require().NoError(err)
s.Require().Len(savedAddresses, 1)
sa2.CreatedAt = savedAddresses[0].CreatedAt // force same value
sa2.UpdateClock = savedAddresses[0].UpdateClock // force same value
s.Require().Equal(sa2, savedAddresses[0])
// Wait other device to receive the change
response, err := WaitOnMessengerResponse(
s.other,
func(r *MessengerResponse) bool {
return len(r.SavedAddresses()) == 1
},
"saved address removal wasn't received",
)
s.Require().NoError(err)
// We expect the delete event to report address, ens, isTest
sa := response.SavedAddresses()[0]
s.Require().Equal(sa1.Address, sa.Address)
s.Require().Equal(sa1.IsTest, sa.IsTest)
s.Require().Equal("", sa.Name)
// Ensure the messenger doesn't return the removed address
savedAddresses, err = s.other.savedAddressesManager.GetSavedAddresses()
s.Require().NoError(err)
s.Require().Len(savedAddresses, 1)
savedAddresses[0].CreatedAt = sa2.CreatedAt // force same value
s.Require().Equal(sa2, savedAddresses[0])
}
// Delete saved address 2 (test mode = false) and sync with the other device
{
err = s.main.DeleteSavedAddress(context.Background(), sa2.Address, sa2.IsTest)
s.Require().NoError(err)
// Ensure the removal
savedAddresses, err := s.main.savedAddressesManager.GetSavedAddresses()
s.Require().NoError(err)
s.Require().Len(savedAddresses, 0)
// Wait other device to receive the change
response, err := WaitOnMessengerResponse(
s.other,
func(r *MessengerResponse) bool {
return len(r.SavedAddresses()) == 1
},
"expected to receive one change",
)
s.Require().NoError(err)
sa := response.SavedAddresses()[0]
// We expect the deleted event to report address, ens, isTest
s.Require().Equal(sa2.Address, sa.Address)
s.Require().Equal(sa2.IsTest, sa.IsTest)
s.Require().Equal("", sa.Name)
savedAddresses, err = s.other.savedAddressesManager.GetSavedAddresses()
s.Require().NoError(err)
s.Require().Len(savedAddresses, 0)
}
}
func (s *MessengerSyncSavedAddressesSuite) TestSyncDeletesOfSavedAddresses() {
testCases := []struct {
Name string
TestModeMain bool
TestModeOther bool
}{
{
Name: "same test mode on both devices",
TestModeMain: true,
TestModeOther: true,
},
{
Name: "different test mode on devices",
TestModeMain: true,
TestModeOther: false,
},
}
for _, tc := range testCases {
s.Run(tc.Name, func() {
s.testSyncDeletesOfSavedAddresses(tc.TestModeMain, tc.TestModeOther)
})
}
}