@@ -36,6 +36,15 @@ import (
36
36
"github.com/ethereum/go-ethereum/params"
37
37
)
38
38
39
+ // Set the `watch` setting on new accounts to this default value.
40
+ // For now we keep it unset, and have users opt-in to watching specific accounts.
41
+ //
42
+ // We set it to `nil` not `false` so that we reserve the possibility to default all accounts to
43
+ // watch-only for accounts where the user hasn't made an active decision (e.g. turn on watch-only
44
+ // for all accounts of a keystore if the user did not activate/deactivate watch-only manually on any
45
+ // of them).
46
+ var defaultWatch * bool = nil
47
+
39
48
// hardenedKeystart is the BIP44 offset to make a keypath element hardened.
40
49
const hardenedKeystart uint32 = hdkeychain .HardenedKeyStart
41
50
@@ -478,6 +487,46 @@ func (backend *Backend) RenameAccount(accountCode accountsTypes.Code, name strin
478
487
return nil
479
488
}
480
489
490
+ // AccountSetWatch sets the account's persisted watch flag to `watch`. Set to `true` if the account
491
+ // should be loaded even if its keystore is not connected.
492
+ // If `watch` is set to `false`, the account is unloaded and the frontend notified.
493
+ func (backend * Backend ) AccountSetWatch (accountCode accountsTypes.Code , watch bool ) error {
494
+ err := backend .config .ModifyAccountsConfig (func (accountsConfig * config.AccountsConfig ) error {
495
+ acct := accountsConfig .Lookup (accountCode )
496
+ if acct == nil {
497
+ return errp .Newf ("Could not find account %s" , accountCode )
498
+ }
499
+ acct .Watch = & watch
500
+ return nil
501
+ })
502
+ if err != nil {
503
+ return err
504
+ }
505
+ defer backend .accountsAndKeystoreLock .Lock ()()
506
+
507
+ // ETH tokens inherit the Watch-flag from the parent ETH account.
508
+ // If the watch status of an ETH account was changed, we update it for its tokens as well.
509
+ //
510
+ // This ensures that removing a keystore after setting an ETH account including tokens to
511
+ // watchonly results in the account *and* the tokens remaining loaded.
512
+ if acct := backend .accounts .lookup (accountCode ); acct != nil {
513
+ if acct .Config ().Config .CoinCode == coinpkg .CodeETH {
514
+ for _ , erc20TokenCode := range acct .Config ().Config .ActiveTokens {
515
+ erc20AccountCode := Erc20AccountCode (accountCode , erc20TokenCode )
516
+ if tokenAcct := backend .accounts .lookup (erc20AccountCode ); tokenAcct != nil {
517
+ tokenAcct .Config ().Config .Watch = & watch
518
+ }
519
+ }
520
+ }
521
+ }
522
+
523
+ if ! watch {
524
+ backend .initAccounts (false )
525
+ backend .emitAccountsStatusChanged ()
526
+ }
527
+ return nil
528
+ }
529
+
481
530
// addAccount adds the given account to the backend.
482
531
// The accountsAndKeystoreLock must be held when calling this function.
483
532
func (backend * Backend ) addAccount (account accounts.Interface ) {
@@ -493,6 +542,10 @@ func (backend *Backend) addAccount(account accounts.Interface) {
493
542
494
543
// The accountsAndKeystoreLock must be held when calling this function.
495
544
func (backend * Backend ) createAndAddAccount (coin coinpkg.Coin , persistedConfig * config.Account ) {
545
+ if backend .accounts .lookup (persistedConfig .Code ) != nil {
546
+ // Do not create/load account if it is already loaded.
547
+ return
548
+ }
496
549
var account accounts.Interface
497
550
accountConfig := & accounts.AccountConfig {
498
551
Config : persistedConfig ,
@@ -549,9 +602,15 @@ func (backend *Backend) createAndAddAccount(coin coinpkg.Coin, persistedConfig *
549
602
tokenName = fmt .Sprintf ("%s %d" , tokenName , accountNumber + 1 )
550
603
}
551
604
605
+ var watchToken * bool
606
+ if persistedConfig .Watch != nil {
607
+ wCopy := * persistedConfig .Watch
608
+ watchToken = & wCopy
609
+ }
552
610
erc20Config := & config.Account {
553
611
Inactive : persistedConfig .Inactive ,
554
612
HiddenBecauseUnused : persistedConfig .HiddenBecauseUnused ,
613
+ Watch : watchToken ,
555
614
CoinCode : erc20CoinCode ,
556
615
Name : tokenName ,
557
616
Code : erc20AccountCode ,
@@ -655,6 +714,7 @@ func (backend *Backend) persistBTCAccountConfig(
655
714
if keystore .SupportsUnifiedAccounts () {
656
715
return backend .persistAccount (config.Account {
657
716
HiddenBecauseUnused : hiddenBecauseUnused ,
717
+ Watch : defaultWatch ,
658
718
CoinCode : coin .Code (),
659
719
Name : name ,
660
720
Code : code ,
@@ -674,6 +734,7 @@ func (backend *Backend) persistBTCAccountConfig(
674
734
675
735
err := backend .persistAccount (config.Account {
676
736
HiddenBecauseUnused : hiddenBecauseUnused ,
737
+ Watch : defaultWatch ,
677
738
CoinCode : coin .Code (),
678
739
Name : suffixedName ,
679
740
Code : splitAccountCode (code , cfg .ScriptType ()),
@@ -730,6 +791,7 @@ func (backend *Backend) persistETHAccountConfig(
730
791
731
792
return backend .persistAccount (config.Account {
732
793
HiddenBecauseUnused : hiddenBecauseUnused ,
794
+ Watch : defaultWatch ,
733
795
CoinCode : coin .Code (),
734
796
Name : name ,
735
797
Code : code ,
@@ -740,39 +802,56 @@ func (backend *Backend) persistETHAccountConfig(
740
802
741
803
// The accountsAndKeystoreLock must be held when calling this function.
742
804
func (backend * Backend ) initPersistedAccounts () {
743
- if backend .keystore == nil {
744
- return
745
- }
746
- // Only load accounts which belong to connected keystores.
747
- rootFingerprint , err := backend .keystore .RootFingerprint ()
748
- if err != nil {
749
- backend .log .WithError (err ).Error ("Could not retrieve root fingerprint" )
750
- return
751
- }
752
- keystoreConnected := func (account * config.Account ) bool {
805
+ // Only load accounts which belong to connected keystores or for which watchonly is enabled.
806
+ keystoreConnectedOrWatch := func (account * config.Account ) bool {
807
+ if account .IsWatch () {
808
+ return true
809
+ }
810
+
811
+ if backend .keystore == nil {
812
+ return false
813
+ }
814
+ rootFingerprint , err := backend .keystore .RootFingerprint ()
815
+ if err != nil {
816
+ backend .log .WithError (err ).Error ("Could not retrieve root fingerprint" )
817
+ return false
818
+ }
819
+
753
820
return account .SigningConfigurations .ContainsRootFingerprint (rootFingerprint )
754
821
}
755
822
756
823
persistedAccounts := backend .config .AccountsConfig ()
824
+
825
+ // In this loop, we add all accounts that match the filter, except for the ones whose signing
826
+ // configuration is not supported by the connected keystore. The latter can happen for example
827
+ // if a user connects a BitBox02 Multi edition first, which persists some altcoin accounts, and
828
+ // then connects a BitBox02 BTC-only with the same seed. In that case, the unsupported accounts
829
+ // will not be loaded, unless they have been marked as watch-only.
757
830
outer:
758
- for _ , account := range backend .filterAccounts (& persistedAccounts , keystoreConnected ) {
831
+ for _ , account := range backend .filterAccounts (& persistedAccounts , keystoreConnectedOrWatch ) {
759
832
account := account
760
833
coin , err := backend .Coin (account .CoinCode )
761
834
if err != nil {
762
835
backend .log .Errorf ("skipping persisted account %s/%s, could not find coin" ,
763
836
account .CoinCode , account .Code )
764
837
continue
765
838
}
766
- switch coin .(type ) {
767
- case * btc.Coin :
768
- for _ , cfg := range account .SigningConfigurations {
769
- if ! backend .keystore .SupportsAccount (coin , cfg .ScriptType ()) {
770
- continue outer
839
+
840
+ // Watch-only accounts are loaded regardless, and if later e.g. a BitBox02 BTC-only is
841
+ // inserted with the same seed as a Multi, we will need to catch that mismatch when the
842
+ // keystore will be used to e.g. display an Ethereum address etc.
843
+ if backend .keystore != nil && ! account .IsWatch () {
844
+ switch coin .(type ) {
845
+ case * btc.Coin :
846
+ for _ , cfg := range account .SigningConfigurations {
847
+ if ! backend .keystore .SupportsAccount (coin , cfg .ScriptType ()) {
848
+ continue outer
849
+ }
850
+ }
851
+ default :
852
+ if ! backend .keystore .SupportsAccount (coin , nil ) {
853
+ continue
771
854
}
772
- }
773
- default :
774
- if ! backend .keystore .SupportsAccount (coin , nil ) {
775
- continue
776
855
}
777
856
}
778
857
@@ -898,9 +977,10 @@ func (backend *Backend) updatePersistedAccounts(
898
977
}
899
978
900
979
// The accountsAndKeystoreLock must be held when calling this function.
901
- func (backend * Backend ) initAccounts () {
980
+ // if force is true, all accounts are uninitialized first, even if they are watch-only.
981
+ func (backend * Backend ) initAccounts (force bool ) {
902
982
// Since initAccounts replaces all previous accounts, we need to properly close them first.
903
- backend .uninitAccounts ()
983
+ backend .uninitAccounts (force )
904
984
905
985
backend .initPersistedAccounts ()
906
986
@@ -920,19 +1000,26 @@ func (backend *Backend) ReinitializeAccounts() {
920
1000
defer backend .accountsAndKeystoreLock .Lock ()()
921
1001
922
1002
backend .log .Info ("Reinitializing accounts" )
923
- backend .initAccounts ()
1003
+ backend .initAccounts (true )
924
1004
}
925
1005
926
1006
// The accountsAndKeystoreLock must be held when calling this function.
927
- func (backend * Backend ) uninitAccounts () {
1007
+ // if force is true, all accounts are uninitialized, even if they are watch-only.
1008
+ func (backend * Backend ) uninitAccounts (force bool ) {
1009
+ keep := []accounts.Interface {}
928
1010
for _ , account := range backend .accounts {
929
1011
account := account
1012
+ if ! force && account .Config ().Config .IsWatch () {
1013
+ // Do not uninit/remove account that is being watched.
1014
+ keep = append (keep , account )
1015
+ continue
1016
+ }
930
1017
if backend .onAccountUninit != nil {
931
1018
backend .onAccountUninit (account )
932
1019
}
933
1020
account .Close ()
934
1021
}
935
- backend .accounts = []accounts. Interface {}
1022
+ backend .accounts = keep
936
1023
}
937
1024
938
1025
// maybeAddHiddenUnusedAccounts adds a hidden account for scanning to facilitate accounts discovery.
0 commit comments