Skip to content

Commit 2cd0d08

Browse files
authored
feat!: add walletXPub support to keypair management (#7413)
- `AddKeypairStoredToKeycard` renamed to `AddKeypairStoredToColdWallet` - Introduced walletXPub and coldWallet parameters to keypair creation and related functions. Affected API functions are `AddKeypairViaSeedPhrase` and `AddKeypairStoredToKeycard` - Updated tests and documentation to reflect the new walletXPub, coldWallet integration. - Affected code updated.
1 parent a57f2d9 commit 2cd0d08

24 files changed

Lines changed: 370 additions & 187 deletions

internal/accounts-management/README.md

Lines changed: 115 additions & 95 deletions
Large diffs are not rendered by default.

internal/accounts-management/common/const.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const PathEIP1581Chat = PathEIP1581Root + "/0'/0"
99
const PathEIP1581Encryption = PathEIP1581Root + "/1'/0"
1010

1111
const WalletPath = "m/44'"
12+
const PathWalletXPub = "m/44'/60'/0'"
1213
const PathWalletRoot = "m/44'/60'/0'/0"
1314
const PathDefaultWalletAccount = PathWalletRoot + "/0"
1415
const CustomWalletPath1 = PathWalletRoot + "/1"

internal/accounts-management/generator/types.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ func (a *Account) PublicKeyHex() string {
5252
return types.EncodeHex(crypto.FromECDSAPub(&a.privateKey.PublicKey))
5353
}
5454

55+
func (a *Account) ExtendedPublicKey() string {
56+
if a.extendedKey == nil {
57+
return ""
58+
}
59+
xpub, err := a.extendedKey.Neuter()
60+
if err != nil {
61+
return ""
62+
}
63+
return xpub.String()
64+
}
65+
5566
func (a *Account) Address() types.Address {
5667
if a.privateKey == nil {
5768
return types.Address{}

internal/accounts-management/manager_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ func (s *ManagerTestSuite) createAndStoreProfileKeypair() *types.Keypair {
290290
Path: common.PathDefaultWalletAccount,
291291
}
292292

293-
keypair, err := s.accManager.CreateKeypairFromMnemonicAndStore(s.mnemonic, s.password, "kp-name", walletAccount, true, 0)
293+
keypair, err := s.accManager.CreateKeypairFromMnemonicAndStore(s.mnemonic, s.password, "kp-name", types.ColdWalletTypeNone, walletAccount, true, 0)
294294
s.Require().NoError(err)
295295
s.Require().NotEmpty(s.mnemonic)
296296
s.Require().NotNil(keypair)

internal/accounts-management/persistence_operations.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
// If it's a profile keypair, it also sets the chat account.
1616
// Passed `walletAccount` is used to generate address using its path and set other details accordingly.
1717
func (m *AccountsManager) CreateKeypairFromMnemonicAndStore(mnemonic string, password string, keypairName string,
18-
walletAccount *types.AccountCreationDetails, profile bool, clock uint64) (keypair *types.Keypair, err error) {
18+
coldWallet types.ColdWalletType, walletAccount *types.AccountCreationDetails, profile bool, clock uint64) (keypair *types.Keypair, err error) {
1919

2020
if walletAccount == nil {
2121
err = ErrKeypairDoesNotHaveWalletAccount
@@ -75,8 +75,14 @@ func (m *AccountsManager) CreateKeypairFromMnemonicAndStore(mnemonic string, pas
7575
m.setKeystore(keystore)
7676
}
7777

78+
walletXPub, err := generator.DeriveExtendedPublicKeyAtPath(mnemonic, "", common.PathWalletXPub)
79+
if err != nil {
80+
return
81+
}
82+
7883
// prepare keypair
79-
keypair, err = m.prepareKeypair(masterAccount, derivedAccounts, keypairName, walletAccount, keypairType, profile, clock)
84+
keypair, err = m.prepareKeypair(masterAccount, derivedAccounts, keypairName, walletAccount, keypairType, profile,
85+
walletXPub, coldWallet, clock)
8086
if err != nil {
8187
return
8288
}
@@ -105,7 +111,7 @@ func (m *AccountsManager) CreateKeypairFromMnemonicAndStore(mnemonic string, pas
105111
return
106112
}
107113

108-
func (m *AccountsManager) AddKeypairStoredToKeycard(keyUID string, masterAddress string, name string, xpub string,
114+
func (m *AccountsManager) AddKeypairStoredToColdWallet(keyUID string, masterAddress string, name string, walletXPub string,
109115
coldWallet types.ColdWalletType, walletAccounts []*types.Account, clock uint64) (keypair *types.Keypair, err error) {
110116

111117
if len(walletAccounts) == 0 {
@@ -161,7 +167,7 @@ func (m *AccountsManager) AddKeypairStoredToKeycard(keyUID string, masterAddress
161167
LastUsedDerivationIndex: 0,
162168
Clock: clock,
163169
Accounts: walletAccounts,
164-
XPub: xpub,
170+
XPub: walletXPub,
165171
ColdWallet: coldWallet,
166172
}
167173

@@ -172,7 +178,8 @@ func (m *AccountsManager) AddKeypairStoredToKeycard(keyUID string, masterAddress
172178
}
173179

174180
func (m *AccountsManager) prepareKeypair(account *generator.Account, derivedAccounts map[string]*generator.Account, keypairName string,
175-
walletAccount *types.AccountCreationDetails, keypairType types.KeypairType, profile bool, clock uint64) (*types.Keypair, error) {
181+
walletAccount *types.AccountCreationDetails, keypairType types.KeypairType, profile bool, walletXPub string, coldWallet types.ColdWalletType,
182+
clock uint64) (*types.Keypair, error) {
176183
// set up keypair
177184
keypair := &types.Keypair{
178185
Name: keypairName,
@@ -181,6 +188,8 @@ func (m *AccountsManager) prepareKeypair(account *generator.Account, derivedAcco
181188
DerivedFrom: account.Address().Hex(),
182189
LastUsedDerivationIndex: 0,
183190
Clock: clock,
191+
XPub: walletXPub,
192+
ColdWallet: coldWallet,
184193
}
185194

186195
// add chat account
@@ -279,7 +288,8 @@ func (m *AccountsManager) CreateKeypairFromPrivateKeyAndStore(privateKey string,
279288
}
280289

281290
// prepare keypair
282-
keypair, err = m.prepareKeypair(masterAccount, nil, keypairName, walletAccount, types.KeypairTypeKey, false, clock)
291+
keypair, err = m.prepareKeypair(masterAccount, nil, keypairName, walletAccount, types.KeypairTypeKey, false, "",
292+
types.ColdWalletTypeNone, clock)
283293
if err != nil {
284294
return nil, err
285295
}

pkg/backend/geth_backend.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,14 +1419,17 @@ func (b *StatusBackend) prepareWalletAccount(request *requests.CreateAccount) *a
14191419
}
14201420

14211421
func (b *StatusBackend) prepareKeypair(request *requests.CreateAccount, keyUID string, masterAddress string,
1422-
derivedAddresses map[string]generator.AccountInfo, restoreAccount bool) (keypair *accsmanagementtypes.Keypair, err error) {
1422+
derivedAddresses map[string]generator.AccountInfo, restoreAccount bool, walletXPub string,
1423+
coldWallet accsmanagementtypes.ColdWalletType) (keypair *accsmanagementtypes.Keypair, err error) {
14231424
// set up keypair
14241425
keypair = &accsmanagementtypes.Keypair{
14251426
Name: request.DisplayName,
14261427
KeyUID: keyUID,
14271428
Type: accsmanagementtypes.KeypairTypeProfile,
14281429
DerivedFrom: masterAddress,
14291430
LastUsedDerivationIndex: 0,
1431+
XPub: walletXPub,
1432+
ColdWallet: coldWallet,
14301433
}
14311434

14321435
// add chat account
@@ -1644,11 +1647,15 @@ func (b *StatusBackend) StartNodeWithChatKeyOrMnemonic(
16441647
common.PathDefaultWalletAccount: {},
16451648
}
16461649
keypairToStoreDirectly *accsmanagementtypes.Keypair
1650+
walletXPub string
1651+
coldWallet accsmanagementtypes.ColdWalletType
16471652
)
16481653

16491654
if keycardData != nil { // means that the keycard is already set, details already on it
16501655
keyUID = keycardData.KeyUID
16511656
masterAddress = keycardData.Address
1657+
walletXPub = keycardData.WalletXPub
1658+
coldWallet = keycardData.ColdWallet
16521659

16531660
derivedAddresses[common.PathWalletRoot] = generator.AccountInfo{
16541661
AccountPublicInfo: generator.AccountPublicInfo{
@@ -1698,7 +1705,13 @@ func (b *StatusBackend) StartNodeWithChatKeyOrMnemonic(
16981705
common.PathDefaultWalletAccount,
16991706
common.PathEIP1581Encryption,
17001707
}
1701-
_, derivedAddresses, err = b.generateDerivedAddresses(genMasterAcc, derivationPaths)
1708+
_, generatedDerivedAddresses, err := b.generateDerivedAddresses(genMasterAcc, derivationPaths)
1709+
if err != nil {
1710+
return nil, err
1711+
}
1712+
derivedAddresses = generatedDerivedAddresses
1713+
1714+
walletXPub, err = generator.DeriveExtendedPublicKeyAtPath(mnemonic, "", common.PathWalletRoot)
17021715
if err != nil {
17031716
return nil, err
17041717
}
@@ -1710,6 +1723,7 @@ func (b *StatusBackend) StartNodeWithChatKeyOrMnemonic(
17101723
return nil, err
17111724
}
17121725

1726+
coldWallet = accsmanagementtypes.ColdWalletTypeStatusKeycard
17131727
chatPrivateKey = genChatAccount.PrivateKey()
17141728
chatPublicKey = types.Hex2Bytes(genChatAccount.PublicKeyHex())
17151729

@@ -1753,14 +1767,15 @@ func (b *StatusBackend) StartNodeWithChatKeyOrMnemonic(
17531767
return nil, errors.Wrap(err, "failed to prepare for keycard")
17541768
}
17551769

1756-
keypairToStoreDirectly, err = b.prepareKeypair(request, keyUID, masterAddress, derivedAddresses, restoreAccount)
1770+
keypairToStoreDirectly, err = b.prepareKeypair(request, keyUID, masterAddress, derivedAddresses, restoreAccount,
1771+
walletXPub, coldWallet)
17571772
if err != nil {
17581773
return nil, errors.Wrap(err, "failed to prepare keypair")
17591774
}
17601775
} else {
17611776
walletAccount := b.prepareWalletAccount(request)
1762-
_, err := b.accountsManager.CreateKeypairFromMnemonicAndStore(mnemonic, request.Password,
1763-
request.DisplayName, walletAccount, true, 0)
1777+
_, err := b.accountsManager.CreateKeypairFromMnemonicAndStore(mnemonic, request.Password, request.DisplayName,
1778+
coldWallet, walletAccount, true, 0)
17641779
if err != nil {
17651780
return nil, err
17661781
}

pkg/backend/test_helpers.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func setupTestContext(t *testing.T, password string, storeProfile bool, storeMul
5151
require.NoError(t, err)
5252

5353
accountsPaths := []string{accscommon.PathWalletRoot, accscommon.PathEIP1581Chat, accscommon.PathDefaultWalletAccount}
54-
derivedAccs, err := generator.DeriveChildrenFromAccount(genMasterAcc, append([]string{accscommon.PathWalletRoot}, accountsPaths...))
54+
derivedAccs, err := generator.DeriveChildrenFromAccount(genMasterAcc, accountsPaths)
5555
require.NoError(t, err)
5656

5757
data.profileKeypair = &accsmanagementtypes.Keypair{
@@ -167,6 +167,7 @@ func setupTestContext(t *testing.T, password string, storeProfile bool, storeMul
167167
require.NoError(t, err)
168168

169169
keypair, err := data.backend.AccountsManager().CreateKeypairFromMnemonicAndStore(data.mnemonic, password, "Test Keypair",
170+
accsmanagementtypes.ColdWalletTypeNone,
170171
&accsmanagementtypes.AccountCreationDetails{
171172
Path: accscommon.PathDefaultWalletAccount,
172173
}, true, 0)

protocol/accounts_manager_interface.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ type AccountsManager interface {
1717
MakePrivateKeyKeypairFullyOperable(privateKey string, password string, clock uint64) (string, error)
1818
SaveOrUpdateKeycard(keycard *types.Keycard, password string, clock uint64) error
1919
AddAccounts(keyUID string, accounts []*types.Account, password string) error
20-
CreateKeypairFromMnemonicAndStore(mnemonic string, password string, keypairName string,
20+
CreateKeypairFromMnemonicAndStore(mnemonic string, password string, keypairName string, coldWallet types.ColdWalletType,
2121
walletAccount *types.AccountCreationDetails, profile bool, clock uint64) (keypair *types.Keypair, err error)
22-
AddKeypairStoredToKeycard(keyUID string, masterAddress string, name string,
23-
xpub string, coldWallet types.ColdWalletType, walletAccounts []*types.Account, clock uint64) (keypair *types.Keypair, err error)
22+
AddKeypairStoredToColdWallet(keyUID string, masterAddress string, name string, walletXPub string,
23+
coldWallet types.ColdWalletType, walletAccounts []*types.Account, clock uint64) (keypair *types.Keypair, err error)
2424
CreateKeypairFromPrivateKeyAndStore(privateKey string, password string, keypairName string,
2525
walletAccount *types.AccountCreationDetails, clock uint64) (keypair *types.Keypair, err error)
2626
MigrateNonProfileKeycardKeypairToApp(mnemonic string, password string, clock uint64) (string, error)

protocol/messenger_wallet.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,11 @@ func (m *Messenger) UpdateKeypair(keypair *accsmanagementtypes.Keypair) error {
123123
return m.resolveAndSyncKeypairOrJustWalletAccount(keypair.KeyUID, types.Address{}, keypair.Clock, m.dispatchMessage)
124124
}
125125

126-
func (m *Messenger) AddKeypairViaSeedPhrase(mnemonic string, password string, name string,
126+
func (m *Messenger) AddKeypairViaSeedPhrase(mnemonic string, password string, name string, coldWallet accsmanagementtypes.ColdWalletType,
127127
walletAccount *accsmanagementtypes.AccountCreationDetails) (*accsmanagementtypes.Keypair, error) {
128128
clock, _ := m.getLastClockWithRelatedChat()
129129

130-
keypair, err := m.accountsManager.CreateKeypairFromMnemonicAndStore(mnemonic, password, name, walletAccount, false, clock)
130+
keypair, err := m.accountsManager.CreateKeypairFromMnemonicAndStore(mnemonic, password, name, coldWallet, walletAccount, false, clock)
131131
if err != nil {
132132
return nil, err
133133
}
@@ -136,11 +136,11 @@ func (m *Messenger) AddKeypairViaSeedPhrase(mnemonic string, password string, na
136136
m.resolveAndSyncKeypairOrJustWalletAccount(keypair.KeyUID, types.Address{}, keypair.Clock, m.dispatchMessage)
137137
}
138138

139-
func (m *Messenger) AddKeypairStoredToKeycard(keyUID string, masterAddress string, name string,
140-
xpub string, coldWallet accsmanagementtypes.ColdWalletType, walletAccounts []*accsmanagementtypes.Account) (*accsmanagementtypes.Keypair, error) {
139+
func (m *Messenger) AddKeypairStoredToColdWallet(keyUID string, masterAddress string, name string,
140+
walletXPub string, coldWallet accsmanagementtypes.ColdWalletType, walletAccounts []*accsmanagementtypes.Account) (*accsmanagementtypes.Keypair, error) {
141141
clock, _ := m.getLastClockWithRelatedChat()
142142

143-
keypair, err := m.accountsManager.AddKeypairStoredToKeycard(keyUID, masterAddress, name, xpub, coldWallet, walletAccounts, clock)
143+
keypair, err := m.accountsManager.AddKeypairStoredToColdWallet(keyUID, masterAddress, name, walletXPub, coldWallet, walletAccounts, clock)
144144
if err != nil {
145145
return nil, err
146146
}

protocol/requests/restore_account.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package requests
22

33
import (
44
"errors"
5+
6+
accsmanagementtypes "github.com/status-im/status-go/internal/accounts-management/types"
57
)
68

79
var (
@@ -52,14 +54,16 @@ func (c *RestoreAccount) Validate(restoreViaKeycard bool) error {
5254
}
5355

5456
type KeycardData struct {
55-
KeyUID string `json:"keyUID"`
56-
Address string `json:"address"`
57-
WhisperPrivateKey string `json:"whisperPrivateKey"`
58-
WhisperPublicKey string `json:"whisperPublicKey"`
59-
WhisperAddress string `json:"whisperAddress"`
60-
WalletPublicKey string `json:"walletPublicKey"`
61-
WalletAddress string `json:"walletAddress"`
62-
WalletRootAddress string `json:"walletRootAddress"`
63-
Eip1581Address string `json:"eip1581Address"`
64-
EncryptionPublicKey string `json:"encryptionPublicKey"`
57+
KeyUID string `json:"keyUID"`
58+
Address string `json:"address"`
59+
WhisperPrivateKey string `json:"whisperPrivateKey"`
60+
WhisperPublicKey string `json:"whisperPublicKey"`
61+
WhisperAddress string `json:"whisperAddress"`
62+
WalletPublicKey string `json:"walletPublicKey"`
63+
WalletAddress string `json:"walletAddress"`
64+
WalletRootAddress string `json:"walletRootAddress"`
65+
Eip1581Address string `json:"eip1581Address"`
66+
EncryptionPublicKey string `json:"encryptionPublicKey"`
67+
WalletXPub string `json:"walletXPub"`
68+
ColdWallet accsmanagementtypes.ColdWalletType `json:"coldWallet"`
6569
}

0 commit comments

Comments
 (0)