Skip to content

Commit 2773f9b

Browse files
committed
backend/account: add ConnectKeystore() api call
This waits for a the account's keystore to be connected. Useful in watch-only when an action requires the keystore, like displaying an address or signing a transaction. The backend will invoke this wherever the keystore is needed. A frontend call to invoke this is added as well for the cases where one wants to prompt the user to connect the keystore before the backend actually needs it, like: - when verifying a receive address, the user should be prompted before the dialog with the full address and QR code appears - when clicking 'Review' in the send screen, the user should be prompted before the review dialog appears Maybe there are more instances like this, which will be fixed in future commits. The minimum Go versio is bumped to 1.20 as `context.WithCancelFunc()` was introduced there.
1 parent dc48289 commit 2773f9b

File tree

17 files changed

+354
-41
lines changed

17 files changed

+354
-41
lines changed

backend/accounts.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"math"
2121
"sort"
2222
"strings"
23+
"time"
2324

2425
"github.com/btcsuite/btcd/btcutil/hdkeychain"
2526
"github.com/digitalbitbox/bitbox-wallet-app/backend/accounts"
@@ -551,7 +552,13 @@ func (backend *Backend) createAndAddAccount(coin coinpkg.Coin, persistedConfig *
551552
Config: persistedConfig,
552553
DBFolder: backend.arguments.CacheDirectoryPath(),
553554
NotesFolder: backend.arguments.NotesDirectoryPath(),
554-
Keystore: backend.keystore,
555+
ConnectKeystore: func() (keystore.Keystore, error) {
556+
accountRootFingerprint, err := persistedConfig.SigningConfigurations.RootFingerprint()
557+
if err != nil {
558+
return nil, err
559+
}
560+
return backend.connectKeystore.connect(backend.Keystore(), accountRootFingerprint, 20*time.Minute)
561+
},
555562
OnEvent: func(event accountsTypes.Event) {
556563
backend.events <- AccountEvent{
557564
Type: "account", Code: persistedConfig.Code,

backend/accounts/baseaccount.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ type AccountConfig struct {
4545
DBFolder string
4646
// NotesFolder is the folder where the transaction notes are stored. Full path.
4747
NotesFolder string
48-
Keystore keystore.Keystore
48+
ConnectKeystore func() (keystore.Keystore, error)
4949
OnEvent func(types.Event)
5050
RateUpdater *rates.RateUpdater
5151
GetNotifier func(signing.Configurations) Notifier

backend/accounts/baseaccount_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ func TestBaseAccount(t *testing.T) {
6565
},
6666
DBFolder: test.TstTempDir("baseaccount_test_dbfolder"),
6767
NotesFolder: test.TstTempDir("baseaccount_test_notesfolder"),
68-
Keystore: nil,
6968
OnEvent: func(event types.Event) { events <- event },
7069
RateUpdater: nil,
7170
GetNotifier: nil,

backend/backend.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,10 @@ type Backend struct {
164164
accounts accountsList
165165
// keystore is nil if no keystore is connected.
166166
keystore keystore.Keystore
167-
aopp AOPP
167+
168+
connectKeystore connectKeystore
169+
170+
aopp AOPP
168171

169172
// makeBtcAccount creates a BTC account. In production this is `btc.NewAccount`, but can be
170173
// overridden in unit tests for mocking.
@@ -505,7 +508,11 @@ func (backend *Backend) Keystore() keystore.Keystore {
505508
func (backend *Backend) registerKeystore(keystore keystore.Keystore) {
506509
defer backend.accountsAndKeystoreLock.Lock()()
507510
// Only for logging, if there is an error we continue anyway.
508-
fingerprint, _ := keystore.RootFingerprint()
511+
fingerprint, err := keystore.RootFingerprint()
512+
if err != nil {
513+
backend.log.WithError(err).Error("could not retrieve keystore fingerprint")
514+
return
515+
}
509516
log := backend.log.WithField("rootFingerprint", fingerprint)
510517
log.Info("registering keystore")
511518
backend.keystore = keystore
@@ -515,19 +522,10 @@ func (backend *Backend) registerKeystore(keystore keystore.Keystore) {
515522
})
516523

517524
belongsToKeystore := func(account *config.Account) bool {
518-
fingerprint, err := keystore.RootFingerprint()
519-
if err != nil {
520-
log.WithError(err).Error("Could not retrieve root fingerprint")
521-
return false
522-
}
523525
return account.SigningConfigurations.ContainsRootFingerprint(fingerprint)
524526
}
525527

526528
persistKeystore := func(accountsConfig *config.AccountsConfig) error {
527-
fingerprint, err := keystore.RootFingerprint()
528-
if err != nil {
529-
return errp.WithMessage(err, "could not retrieve root fingerprint")
530-
}
531529
keystoreName, err := keystore.Name()
532530
if err != nil {
533531
return errp.WithMessage(err, "could not retrieve keystore name")
@@ -538,7 +536,7 @@ func (backend *Backend) registerKeystore(keystore keystore.Keystore) {
538536
return nil
539537
}
540538

541-
err := backend.config.ModifyAccountsConfig(func(accountsConfig *config.AccountsConfig) error {
539+
err = backend.config.ModifyAccountsConfig(func(accountsConfig *config.AccountsConfig) error {
542540
// Persist keystore with its name in the config.
543541
if err := persistKeystore(accountsConfig); err != nil {
544542
log.WithError(err).Error("Could not persist keystore")
@@ -559,6 +557,8 @@ func (backend *Backend) registerKeystore(keystore keystore.Keystore) {
559557
backend.initAccounts(false)
560558

561559
backend.aoppKeystoreRegistered()
560+
561+
backend.connectKeystore.onConnect(backend.keystore)
562562
}
563563

564564
// DeregisterKeystore removes the registered keystore.

backend/coins/btc/account.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,12 @@ func (account *Account) VerifyAddress(addressID string) (bool, error) {
721721
return false, errp.New("account must be initialized")
722722
}
723723
account.Synchronizer.WaitSynchronized()
724+
725+
keystore, err := account.Config().ConnectKeystore()
726+
if err != nil {
727+
return false, err
728+
}
729+
724730
scriptHashHex := blockchain.ScriptHashHex(addressID)
725731
var address *addresses.AccountAddress
726732
for _, subacc := range account.subaccounts {
@@ -732,19 +738,23 @@ func (account *Account) VerifyAddress(addressID string) (bool, error) {
732738
if address == nil {
733739
return false, errp.New("unknown address not found")
734740
}
735-
canVerifyAddress, _, err := account.Config().Keystore.CanVerifyAddress(account.Coin())
741+
canVerifyAddress, _, err := keystore.CanVerifyAddress(account.Coin())
736742
if err != nil {
737743
return false, err
738744
}
739745
if canVerifyAddress {
740-
return true, account.Config().Keystore.VerifyAddress(address.Configuration, account.Coin())
746+
return true, keystore.VerifyAddress(address.Configuration, account.Coin())
741747
}
742748
return false, nil
743749
}
744750

745751
// CanVerifyAddresses wraps Keystores().CanVerifyAddresses(), see that function for documentation.
746752
func (account *Account) CanVerifyAddresses() (bool, bool, error) {
747-
return account.Config().Keystore.CanVerifyAddress(account.Coin())
753+
keystore, err := account.Config().ConnectKeystore()
754+
if err != nil {
755+
return false, false, err
756+
}
757+
return keystore.CanVerifyAddress(account.Coin())
748758
}
749759

750760
type byValue struct {
@@ -799,7 +809,11 @@ func (account *Account) VerifyExtendedPublicKey(signingConfigIndex int) (bool, e
799809
return false, errp.New("account not initialized")
800810
}
801811

802-
keystore := account.Config().Keystore
812+
keystore, err := account.Config().ConnectKeystore()
813+
if err != nil {
814+
return false, err
815+
}
816+
803817
if keystore.CanVerifyExtendedPublicKey() {
804818
return true, keystore.VerifyExtendedPublicKey(
805819
account.Coin(),

backend/coins/btc/account_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ func TestAccount(t *testing.T) {
7373
SigningConfigurations: signingConfigurations,
7474
},
7575
DBFolder: dbFolder,
76-
Keystore: nil,
7776
OnEvent: func(accountsTypes.Event) {},
7877
RateUpdater: nil,
7978
GetNotifier: func(signing.Configurations) accounts.Notifier { return nil },

backend/coins/btc/handlers/handlers.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ func NewHandlers(
6969
handleFunc("/has-secure-output", handlers.ensureAccountInitialized(handlers.getHasSecureOutput)).Methods("GET")
7070
handleFunc("/propose-tx-note", handlers.ensureAccountInitialized(handlers.postProposeTxNote)).Methods("POST")
7171
handleFunc("/notes/tx", handlers.ensureAccountInitialized(handlers.postSetTxNote)).Methods("POST")
72+
handleFunc("/connect-keystore", handlers.ensureAccountInitialized(handlers.postConnectKeystore)).Methods("POST")
7273
return handlers
7374
}
7475

@@ -584,3 +585,12 @@ func (handlers *Handlers) postSetTxNote(r *http.Request) (interface{}, error) {
584585

585586
return nil, handlers.account.SetTxNote(args.InternalTxID, args.Note)
586587
}
588+
589+
func (handlers *Handlers) postConnectKeystore(r *http.Request) (interface{}, error) {
590+
type response struct {
591+
Success bool `json:"success"`
592+
}
593+
594+
_, err := handlers.account.Config().ConnectKeystore()
595+
return response{Success: err == nil}, nil
596+
}

backend/coins/btc/sign.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,11 @@ func (account *Account) signTransaction(
6363
FormatUnit: account.coin.formatUnit,
6464
}
6565

66-
if err := account.Config().Keystore.SignTransaction(proposedTransaction); err != nil {
66+
keystore, err := account.Config().ConnectKeystore()
67+
if err != nil {
68+
return err
69+
}
70+
if err := keystore.SignTransaction(proposedTransaction); err != nil {
6771
return err
6872
}
6973

backend/coins/eth/account.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -642,8 +642,13 @@ func (account *Account) SendTx() error {
642642
return errp.New("No active tx proposal")
643643
}
644644

645+
keystore, err := account.Config().ConnectKeystore()
646+
if err != nil {
647+
return err
648+
}
649+
645650
account.log.Info("Signing and sending transaction")
646-
if err := account.Config().Keystore.SignTransaction(txProposal); err != nil {
651+
if err := keystore.SignTransaction(txProposal); err != nil {
647652
return err
648653
}
649654
// By experience, at least with the Etherscan backend, this can succeed and still the
@@ -802,17 +807,26 @@ func (account *Account) VerifyAddress(addressID string) (bool, error) {
802807
if !account.isInitialized() {
803808
return false, errp.New("account must be initialized")
804809
}
805-
canVerifyAddress, _, err := account.Config().Keystore.CanVerifyAddress(account.Coin())
810+
keystore, err := account.Config().ConnectKeystore()
811+
if err != nil {
812+
return false, err
813+
}
814+
canVerifyAddress, _, err := keystore.CanVerifyAddress(account.Coin())
806815
if err != nil {
807816
return false, err
808817
}
809818
if canVerifyAddress {
810-
return true, account.Config().Keystore.VerifyAddress(account.signingConfiguration, account.Coin())
819+
return true, keystore.VerifyAddress(account.signingConfiguration, account.Coin())
811820
}
812821
return false, nil
813822
}
814823

815824
// CanVerifyAddresses implements accounts.Interface.
816825
func (account *Account) CanVerifyAddresses() (bool, bool, error) {
817-
return account.Config().Keystore.CanVerifyAddress(account.Coin())
826+
keystore, err := account.Config().ConnectKeystore()
827+
if err != nil {
828+
return false, false, err
829+
}
830+
831+
return keystore.CanVerifyAddress(account.Coin())
818832
}

backend/coins/eth/account_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ func newAccount(t *testing.T) *Account {
8888
SigningConfigurations: signingConfigurations,
8989
},
9090
DBFolder: dbFolder,
91-
Keystore: nil,
9291
OnEvent: func(accountsTypes.Event) {},
9392
RateUpdater: nil,
9493
GetNotifier: func(signing.Configurations) accounts.Notifier { return nil },

0 commit comments

Comments
 (0)