feat_: accounts/saved addresses capacity related api endpoints added

Added endpoints:
- `RemainingAccountCapacity`
- `RemainingKeypairCapacity`
- `RemainingWatchOnlyAccountCapacity`
- `RmaininngCapacityForSavedAddresses`
This commit is contained in:
Sale Djenic 2024-08-01 14:31:25 +02:00 committed by saledjenic
parent 39497c2bff
commit 2129a3f95c
8 changed files with 437 additions and 0 deletions

View File

@ -0,0 +1,8 @@
package constants
const (
MaxNumberOfAccounts = 20
MaxNumberOfKeypairs = 5 // including the profile keypair
MaxNumberOfWatchOnlyAccounts = 3
MaxNumberOfSavedAddresses = 20
)

View File

@ -36,6 +36,14 @@ func (m *Messenger) GetSavedAddresses(ctx context.Context) ([]*wallet.SavedAddre
return m.savedAddressesManager.GetSavedAddresses()
}
func (m *Messenger) GetSavedAddressesPerMode(isTest bool) ([]*wallet.SavedAddress, error) {
return m.savedAddressesManager.GetSavedAddressesPerMode(isTest)
}
func (m *Messenger) RemainingCapacityForSavedAddresses(testnetMode bool) (int, error) {
return m.savedAddressesManager.RemainingCapacityForSavedAddresses(testnetMode)
}
func (m *Messenger) garbageCollectRemovedSavedAddresses() error {
return m.savedAddressesManager.DeleteSoftRemovedSavedAddresses(uint64(time.Now().AddDate(0, 0, -30).Unix()))
}

View File

@ -10,6 +10,7 @@ import (
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/constants"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/multiaccounts/accounts"
walletsettings "github.com/status-im/status-go/multiaccounts/settings_wallet"
@ -781,3 +782,45 @@ func (m *Messenger) resolveAndSyncKeypairOrJustWalletAccount(keyUID string, addr
chat.LastClockValue = clock
return m.saveChat(chat)
}
func (m *Messenger) RemainingAccountCapacity() (int, error) {
accounts, err := m.settings.GetActiveAccounts()
if err != nil {
return 0, err
}
numOfAccountsWithoutChatAccount := 0
if len(accounts) > 0 {
numOfAccountsWithoutChatAccount = len(accounts) - 1
}
remainingCapacity := constants.MaxNumberOfAccounts - numOfAccountsWithoutChatAccount
if remainingCapacity <= 0 {
return 0, errors.New("no more accounts can be added")
}
return remainingCapacity, nil
}
func (m *Messenger) RemainingKeypairCapacity() (int, error) {
keypairs, err := m.settings.GetActiveKeypairs()
if err != nil {
return 0, err
}
remainingCapacity := constants.MaxNumberOfKeypairs - len(keypairs)
if remainingCapacity <= 0 {
return 0, errors.New("no more keypairs can be added")
}
return remainingCapacity, nil
}
func (m *Messenger) RemainingWatchOnlyAccountCapacity() (int, error) {
accounts, err := m.settings.GetActiveWatchOnlyAccounts()
if err != nil {
return 0, err
}
remainingCapacity := constants.MaxNumberOfWatchOnlyAccounts - len(accounts)
if remainingCapacity <= 0 {
return 0, errors.New("no more watch-only accounts can be added")
}
return remainingCapacity, nil
}

View File

@ -0,0 +1,193 @@
package protocol
import (
"testing"
"github.com/status-im/status-go/constants"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/stretchr/testify/suite"
)
func TestWalletSuite(t *testing.T) {
suite.Run(t, new(WalletSuite))
}
type WalletSuite struct {
MessengerBaseTestSuite
}
func (s *WalletSuite) TestRemainingCapacity() {
profileKeypair := accounts.GetProfileKeypairForTest(true, true, true)
seedImportedKeypair := accounts.GetSeedImportedKeypair1ForTest()
woAccounts := accounts.GetWatchOnlyAccountsForTest()
// Empty DB
capacity, err := s.m.RemainingAccountCapacity()
s.Require().NoError(err)
s.Require().Equal(constants.MaxNumberOfAccounts, capacity)
capacity, err = s.m.RemainingKeypairCapacity()
s.Require().NoError(err)
s.Require().Equal(constants.MaxNumberOfKeypairs, capacity)
capacity, err = s.m.RemainingWatchOnlyAccountCapacity()
s.Require().NoError(err)
s.Require().Equal(constants.MaxNumberOfWatchOnlyAccounts, capacity)
// profile keypair with chat account, default wallet account and 2 more derived accounts added
err = s.m.SaveOrUpdateKeypair(profileKeypair)
s.Require().NoError(err)
capacity, err = s.m.RemainingAccountCapacity()
s.Require().NoError(err)
s.Require().Equal(constants.MaxNumberOfAccounts-3, capacity)
capacity, err = s.m.RemainingKeypairCapacity()
s.Require().NoError(err)
s.Require().Equal(constants.MaxNumberOfKeypairs-1, capacity)
capacity, err = s.m.RemainingWatchOnlyAccountCapacity()
s.Require().NoError(err)
s.Require().Equal(constants.MaxNumberOfWatchOnlyAccounts, capacity)
// seed keypair with 2 derived accounts added
err = s.m.SaveOrUpdateKeypair(seedImportedKeypair)
s.Require().NoError(err)
capacity, err = s.m.RemainingAccountCapacity()
s.Require().NoError(err)
s.Require().Equal(constants.MaxNumberOfAccounts-(3+2), capacity)
capacity, err = s.m.RemainingKeypairCapacity()
s.Require().NoError(err)
s.Require().Equal(constants.MaxNumberOfKeypairs-(1+1), capacity)
capacity, err = s.m.RemainingWatchOnlyAccountCapacity()
s.Require().NoError(err)
s.Require().Equal(constants.MaxNumberOfWatchOnlyAccounts, capacity)
// 1 Watch only accounts added
err = s.m.SaveOrUpdateAccount(woAccounts[0])
s.Require().NoError(err)
capacity, err = s.m.RemainingAccountCapacity()
s.Require().NoError(err)
s.Require().Equal(constants.MaxNumberOfAccounts-(3+2+1), capacity)
capacity, err = s.m.RemainingKeypairCapacity()
s.Require().NoError(err)
s.Require().Equal(constants.MaxNumberOfKeypairs-(1+1), capacity)
capacity, err = s.m.RemainingWatchOnlyAccountCapacity()
s.Require().NoError(err)
s.Require().Equal(constants.MaxNumberOfWatchOnlyAccounts-1, capacity)
// try to add 3 more keypairs
seedImportedKeypair2 := accounts.GetSeedImportedKeypair2ForTest()
seedImportedKeypair2.KeyUID = "0000000000000000000000000000000000000000000000000000000000000091"
seedImportedKeypair2.Accounts[0].Address = types.Address{0x91}
seedImportedKeypair2.Accounts[0].KeyUID = seedImportedKeypair2.KeyUID
seedImportedKeypair2.Accounts[1].Address = types.Address{0x92}
seedImportedKeypair2.Accounts[1].KeyUID = seedImportedKeypair2.KeyUID
err = s.m.SaveOrUpdateKeypair(seedImportedKeypair2)
s.Require().NoError(err)
seedImportedKeypair3 := accounts.GetSeedImportedKeypair2ForTest()
seedImportedKeypair3.KeyUID = "0000000000000000000000000000000000000000000000000000000000000093"
seedImportedKeypair3.Accounts[0].Address = types.Address{0x93}
seedImportedKeypair3.Accounts[0].KeyUID = seedImportedKeypair3.KeyUID
seedImportedKeypair3.Accounts[1].Address = types.Address{0x94}
seedImportedKeypair3.Accounts[1].KeyUID = seedImportedKeypair3.KeyUID
err = s.m.SaveOrUpdateKeypair(seedImportedKeypair3)
s.Require().NoError(err)
seedImportedKeypair4 := accounts.GetSeedImportedKeypair2ForTest()
seedImportedKeypair4.KeyUID = "0000000000000000000000000000000000000000000000000000000000000095"
seedImportedKeypair4.Accounts[0].Address = types.Address{0x95}
seedImportedKeypair4.Accounts[0].KeyUID = seedImportedKeypair4.KeyUID
seedImportedKeypair4.Accounts[1].Address = types.Address{0x96}
seedImportedKeypair4.Accounts[1].KeyUID = seedImportedKeypair4.KeyUID
err = s.m.SaveOrUpdateKeypair(seedImportedKeypair4)
s.Require().NoError(err)
// check the capacity after adding 3 more keypairs
capacity, err = s.m.RemainingAccountCapacity()
s.Require().NoError(err)
s.Require().Equal(constants.MaxNumberOfAccounts-(3+2+1+3*2), capacity)
capacity, err = s.m.RemainingKeypairCapacity()
s.Require().Error(err)
s.Require().Equal("no more keypairs can be added", err.Error())
s.Require().Equal(0, capacity)
capacity, err = s.m.RemainingWatchOnlyAccountCapacity()
s.Require().NoError(err)
s.Require().Equal(constants.MaxNumberOfWatchOnlyAccounts-1, capacity)
// add 2 more watch only accounts
err = s.m.SaveOrUpdateAccount(woAccounts[1])
s.Require().NoError(err)
err = s.m.SaveOrUpdateAccount(woAccounts[2])
s.Require().NoError(err)
// check the capacity after adding 8 more watch only accounts
capacity, err = s.m.RemainingAccountCapacity()
s.Require().NoError(err)
s.Require().Equal(constants.MaxNumberOfAccounts-(3+2+3+3*2), capacity)
capacity, err = s.m.RemainingKeypairCapacity()
s.Require().Error(err)
s.Require().Equal("no more keypairs can be added", err.Error())
s.Require().Equal(0, capacity)
capacity, err = s.m.RemainingWatchOnlyAccountCapacity()
s.Require().Error(err)
s.Require().Equal("no more watch-only accounts can be added", err.Error())
s.Require().Equal(0, capacity)
// add 6 accounts more
seedImportedKeypair4.Accounts[0].Address = types.Address{0x81}
err = s.m.SaveOrUpdateAccount(seedImportedKeypair4.Accounts[0])
s.Require().NoError(err)
seedImportedKeypair4.Accounts[0].Address = types.Address{0x82}
err = s.m.SaveOrUpdateAccount(seedImportedKeypair4.Accounts[0])
s.Require().NoError(err)
seedImportedKeypair4.Accounts[0].Address = types.Address{0x83}
err = s.m.SaveOrUpdateAccount(seedImportedKeypair4.Accounts[0])
s.Require().NoError(err)
seedImportedKeypair4.Accounts[0].Address = types.Address{0x84}
err = s.m.SaveOrUpdateAccount(seedImportedKeypair4.Accounts[0])
s.Require().NoError(err)
seedImportedKeypair4.Accounts[0].Address = types.Address{0x85}
err = s.m.SaveOrUpdateAccount(seedImportedKeypair4.Accounts[0])
s.Require().NoError(err)
seedImportedKeypair4.Accounts[0].Address = types.Address{0x86}
err = s.m.SaveOrUpdateAccount(seedImportedKeypair4.Accounts[0])
s.Require().NoError(err)
// check the capacity after adding 8 more watch only accounts
capacity, err = s.m.RemainingAccountCapacity()
s.Require().Error(err)
s.Require().Equal("no more accounts can be added", err.Error())
s.Require().Equal(0, capacity)
capacity, err = s.m.RemainingKeypairCapacity()
s.Require().Error(err)
s.Require().Equal("no more keypairs can be added", err.Error())
s.Require().Equal(0, capacity)
capacity, err = s.m.RemainingWatchOnlyAccountCapacity()
s.Require().Error(err)
s.Require().Equal("no more watch-only accounts can be added", err.Error())
s.Require().Equal(0, capacity)
}

View File

@ -211,6 +211,21 @@ func (api *API) AddKeypair(ctx context.Context, password string, keypair *accoun
return nil
}
// RemainingAccountCapacity returns the number of accounts that can be added.
func (api *API) RemainingAccountCapacity(ctx context.Context) (int, error) {
return (*api.messenger).RemainingAccountCapacity()
}
// RemainingKeypairCapacity returns the number of keypairs that can be added.
func (api *API) RemainingKeypairCapacity(ctx context.Context) (int, error) {
return (*api.messenger).RemainingKeypairCapacity()
}
// RemainingWatchOnlyAccountCapacity returns the number of watch-only accounts that can be added.
func (api *API) RemainingWatchOnlyAccountCapacity(ctx context.Context) (int, error) {
return (*api.messenger).RemainingWatchOnlyAccountCapacity()
}
func (api *API) checkAccountValidity(account *accounts.Account) error {
if len(account.Address) == 0 {
return errors.New("`Address` field of an account must be set")

View File

@ -1126,6 +1126,15 @@ func (api *PublicAPI) GetSavedAddresses(ctx context.Context) ([]*wallet.SavedAdd
return api.service.messenger.GetSavedAddresses(ctx)
}
func (api *PublicAPI) GetSavedAddressesPerMode(ctx context.Context, testnetMode bool) ([]*wallet.SavedAddress, error) {
return api.service.messenger.GetSavedAddressesPerMode(testnetMode)
}
// RemainingCapacityForSavedAddresses returns the number of saved addresses that can be added
func (api *PublicAPI) RemainingCapacityForSavedAddresses(ctx context.Context, testnetMode bool) (int, error) {
return api.service.messenger.RemainingCapacityForSavedAddresses(testnetMode)
}
// PushNotifications server endpoints
func (api *PublicAPI) StartPushNotificationsServer() error {
err := api.service.accountsDB.SaveSettingField(settings.PushNotificationsServerEnabled, true)

View File

@ -3,10 +3,12 @@ package wallet
import (
"database/sql"
"encoding/json"
"errors"
"fmt"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/constants"
multiAccCommon "github.com/status-im/status-go/multiaccounts/common"
)
@ -115,11 +117,28 @@ func (sam *SavedAddressesManager) GetSavedAddresses() ([]*SavedAddress, error) {
return sam.getSavedAddresses("removed != 1")
}
func (sam *SavedAddressesManager) GetSavedAddressesPerMode(testnetMode bool) ([]*SavedAddress, error) {
return sam.getSavedAddresses(fmt.Sprintf("is_test = %t AND removed != 1", testnetMode))
}
// GetRawSavedAddresses provides access to the soft-delete and sync metadata
func (sam *SavedAddressesManager) GetRawSavedAddresses() ([]*SavedAddress, error) {
return sam.getSavedAddresses("")
}
func (sam *SavedAddressesManager) RemainingCapacityForSavedAddresses(testnetMode bool) (int, error) {
savedAddress, err := sam.GetSavedAddressesPerMode(testnetMode)
if err != nil {
return 0, err
}
remainingCapacity := constants.MaxNumberOfSavedAddresses - len(savedAddress)
if remainingCapacity <= 0 {
return 0, errors.New("no more save addresses can be added")
}
return remainingCapacity, nil
}
func (sam *SavedAddressesManager) upsertSavedAddress(sa SavedAddress, tx *sql.Tx) (err error) {
if tx == nil {
tx, err = sam.db.Begin()

View File

@ -394,3 +394,145 @@ func TestSavedAddressesAddSame(t *testing.T) {
require.NoError(t, err)
require.Equal(t, 1, len(rst))
}
func TestSavedAddressesPerTestnetMode(t *testing.T) {
manager, stop := setupTestSavedAddressesDB(t)
defer stop()
addresses := []SavedAddress{
SavedAddress{
Address: common.Address{1},
Name: "addr1",
IsTest: true,
},
SavedAddress{
Address: common.Address{2},
Name: "addr2",
IsTest: false,
},
SavedAddress{
Address: common.Address{3},
Name: "addr3",
IsTest: true,
},
SavedAddress{
Address: common.Address{4},
Name: "addr4",
IsTest: false,
},
SavedAddress{
Address: common.Address{5},
Name: "addr5",
IsTest: true,
Removed: true,
},
SavedAddress{
Address: common.Address{6},
Name: "addr6",
IsTest: false,
Removed: true,
},
SavedAddress{
Address: common.Address{7},
Name: "addr7",
IsTest: true,
},
}
for _, sa := range addresses {
err := manager.upsertSavedAddress(sa, nil)
require.NoError(t, err)
}
res, err := manager.GetSavedAddresses()
require.NoError(t, err)
require.Equal(t, len(res), len(addresses)-2)
res, err = manager.GetSavedAddressesPerMode(true)
require.NoError(t, err)
require.Equal(t, len(res), 3)
res, err = manager.GetSavedAddressesPerMode(false)
require.NoError(t, err)
require.Equal(t, len(res), 2)
}
func TestSavedAddressesCapacity(t *testing.T) {
manager, stop := setupTestSavedAddressesDB(t)
defer stop()
addresses := []SavedAddress{
SavedAddress{
Address: common.Address{1},
Name: "addr1",
IsTest: true,
},
SavedAddress{
Address: common.Address{2},
Name: "addr2",
IsTest: false,
},
SavedAddress{
Address: common.Address{3},
Name: "addr3",
IsTest: true,
},
SavedAddress{
Address: common.Address{4},
Name: "addr4",
IsTest: false,
},
SavedAddress{
Address: common.Address{5},
Name: "addr5",
IsTest: true,
Removed: true,
},
SavedAddress{
Address: common.Address{6},
Name: "addr6",
IsTest: false,
Removed: true,
},
SavedAddress{
Address: common.Address{7},
Name: "addr7",
IsTest: true,
},
}
for _, sa := range addresses {
err := manager.upsertSavedAddress(sa, nil)
require.NoError(t, err)
}
capacity, err := manager.RemainingCapacityForSavedAddresses(true)
require.NoError(t, err)
require.Equal(t, 17, capacity)
capacity, err = manager.RemainingCapacityForSavedAddresses(false)
require.NoError(t, err)
require.Equal(t, 18, capacity)
// add 17 more for testnet and 18 more for mainnet mode
for i := 1; i < 36; i++ {
sa := SavedAddress{
Address: common.Address{byte(i + 8)},
Name: "addr" + strconv.Itoa(i+8),
IsTest: i%2 == 0,
}
err := manager.upsertSavedAddress(sa, nil)
require.NoError(t, err)
}
capacity, err = manager.RemainingCapacityForSavedAddresses(true)
require.Error(t, err)
require.Equal(t, "no more save addresses can be added", err.Error())
require.Equal(t, 0, capacity)
capacity, err = manager.RemainingCapacityForSavedAddresses(false)
require.Error(t, err)
require.Equal(t, "no more save addresses can be added", err.Error())
require.Equal(t, 0, capacity)
}