|
|
@@ -83,11 +83,11 @@ type wallet struct {
|
|
|
accounts []accounts.Account // List of derive accounts pinned on the hardware wallet
|
|
|
paths map[common.Address]accounts.DerivationPath // Known derivation paths for signing operations
|
|
|
|
|
|
- deriveNextPath accounts.DerivationPath // Next derivation path for account auto-discovery
|
|
|
- deriveNextAddr common.Address // Next derived account address for auto-discovery
|
|
|
- deriveChain ethereum.ChainStateReader // Blockchain state reader to discover used account with
|
|
|
- deriveReq chan chan struct{} // Channel to request a self-derivation on
|
|
|
- deriveQuit chan chan error // Channel to terminate the self-deriver with
|
|
|
+ deriveNextPaths []accounts.DerivationPath // Next derivation paths for account auto-discovery (multiple bases supported)
|
|
|
+ deriveNextAddrs []common.Address // Next derived account addresses for auto-discovery (multiple bases supported)
|
|
|
+ deriveChain ethereum.ChainStateReader // Blockchain state reader to discover used account with
|
|
|
+ deriveReq chan chan struct{} // Channel to request a self-derivation on
|
|
|
+ deriveQuit chan chan error // Channel to terminate the self-deriver with
|
|
|
|
|
|
healthQuit chan chan error
|
|
|
|
|
|
@@ -339,57 +339,62 @@ func (w *wallet) selfDerive() {
|
|
|
accs []accounts.Account
|
|
|
paths []accounts.DerivationPath
|
|
|
|
|
|
- nextAddr = w.deriveNextAddr
|
|
|
- nextPath = w.deriveNextPath
|
|
|
+ nextPaths = append([]accounts.DerivationPath{}, w.deriveNextPaths...)
|
|
|
+ nextAddrs = append([]common.Address{}, w.deriveNextAddrs...)
|
|
|
|
|
|
context = context.Background()
|
|
|
)
|
|
|
- for empty := false; !empty; {
|
|
|
- // Retrieve the next derived Ethereum account
|
|
|
- if nextAddr == (common.Address{}) {
|
|
|
- if nextAddr, err = w.driver.Derive(nextPath); err != nil {
|
|
|
- w.log.Warn("USB wallet account derivation failed", "err", err)
|
|
|
+ for i := 0; i < len(nextAddrs); i++ {
|
|
|
+ for empty := false; !empty; {
|
|
|
+ // Retrieve the next derived Ethereum account
|
|
|
+ if nextAddrs[i] == (common.Address{}) {
|
|
|
+ if nextAddrs[i], err = w.driver.Derive(nextPaths[i]); err != nil {
|
|
|
+ w.log.Warn("USB wallet account derivation failed", "err", err)
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Check the account's status against the current chain state
|
|
|
+ var (
|
|
|
+ balance *big.Int
|
|
|
+ nonce uint64
|
|
|
+ )
|
|
|
+ balance, err = w.deriveChain.BalanceAt(context, nextAddrs[i], nil)
|
|
|
+ if err != nil {
|
|
|
+ w.log.Warn("USB wallet balance retrieval failed", "err", err)
|
|
|
break
|
|
|
}
|
|
|
- }
|
|
|
- // Check the account's status against the current chain state
|
|
|
- var (
|
|
|
- balance *big.Int
|
|
|
- nonce uint64
|
|
|
- )
|
|
|
- balance, err = w.deriveChain.BalanceAt(context, nextAddr, nil)
|
|
|
- if err != nil {
|
|
|
- w.log.Warn("USB wallet balance retrieval failed", "err", err)
|
|
|
- break
|
|
|
- }
|
|
|
- nonce, err = w.deriveChain.NonceAt(context, nextAddr, nil)
|
|
|
- if err != nil {
|
|
|
- w.log.Warn("USB wallet nonce retrieval failed", "err", err)
|
|
|
- break
|
|
|
- }
|
|
|
- // If the next account is empty, stop self-derivation, but add it nonetheless
|
|
|
- if balance.Sign() == 0 && nonce == 0 {
|
|
|
- empty = true
|
|
|
- }
|
|
|
- // We've just self-derived a new account, start tracking it locally
|
|
|
- path := make(accounts.DerivationPath, len(nextPath))
|
|
|
- copy(path[:], nextPath[:])
|
|
|
- paths = append(paths, path)
|
|
|
-
|
|
|
- account := accounts.Account{
|
|
|
- Address: nextAddr,
|
|
|
- URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)},
|
|
|
- }
|
|
|
- accs = append(accs, account)
|
|
|
+ nonce, err = w.deriveChain.NonceAt(context, nextAddrs[i], nil)
|
|
|
+ if err != nil {
|
|
|
+ w.log.Warn("USB wallet nonce retrieval failed", "err", err)
|
|
|
+ break
|
|
|
+ }
|
|
|
+ // If the next account is empty, stop self-derivation, but add for the last base path
|
|
|
+ if balance.Sign() == 0 && nonce == 0 {
|
|
|
+ empty = true
|
|
|
+ if i < len(nextAddrs)-1 {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // We've just self-derived a new account, start tracking it locally
|
|
|
+ path := make(accounts.DerivationPath, len(nextPaths[i]))
|
|
|
+ copy(path[:], nextPaths[i][:])
|
|
|
+ paths = append(paths, path)
|
|
|
+
|
|
|
+ account := accounts.Account{
|
|
|
+ Address: nextAddrs[i],
|
|
|
+ URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)},
|
|
|
+ }
|
|
|
+ accs = append(accs, account)
|
|
|
|
|
|
- // Display a log message to the user for new (or previously empty accounts)
|
|
|
- if _, known := w.paths[nextAddr]; !known || (!empty && nextAddr == w.deriveNextAddr) {
|
|
|
- w.log.Info("USB wallet discovered new account", "address", nextAddr, "path", path, "balance", balance, "nonce", nonce)
|
|
|
- }
|
|
|
- // Fetch the next potential account
|
|
|
- if !empty {
|
|
|
- nextAddr = common.Address{}
|
|
|
- nextPath[len(nextPath)-1]++
|
|
|
+ // Display a log message to the user for new (or previously empty accounts)
|
|
|
+ if _, known := w.paths[nextAddrs[i]]; !known || (!empty && nextAddrs[i] == w.deriveNextAddrs[i]) {
|
|
|
+ w.log.Info("USB wallet discovered new account", "address", nextAddrs[i], "path", path, "balance", balance, "nonce", nonce)
|
|
|
+ }
|
|
|
+ // Fetch the next potential account
|
|
|
+ if !empty {
|
|
|
+ nextAddrs[i] = common.Address{}
|
|
|
+ nextPaths[i][len(nextPaths[i])-1]++
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
// Self derivation complete, release device lock
|
|
|
@@ -406,8 +411,8 @@ func (w *wallet) selfDerive() {
|
|
|
}
|
|
|
// Shift the self-derivation forward
|
|
|
// TODO(karalabe): don't overwrite changes from wallet.SelfDerive
|
|
|
- w.deriveNextAddr = nextAddr
|
|
|
- w.deriveNextPath = nextPath
|
|
|
+ w.deriveNextAddrs = nextAddrs
|
|
|
+ w.deriveNextPaths = nextPaths
|
|
|
w.stateLock.Unlock()
|
|
|
|
|
|
// Notify the user of termination and loop after a bit of time (to avoid trashing)
|
|
|
@@ -479,18 +484,30 @@ func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
|
|
|
return account, nil
|
|
|
}
|
|
|
|
|
|
-// SelfDerive implements accounts.Wallet, trying to discover accounts that the
|
|
|
-// user used previously (based on the chain state), but ones that he/she did not
|
|
|
-// explicitly pin to the wallet manually. To avoid chain head monitoring, self
|
|
|
-// derivation only runs during account listing (and even then throttled).
|
|
|
-func (w *wallet) SelfDerive(base accounts.DerivationPath, chain ethereum.ChainStateReader) {
|
|
|
+// SelfDerive sets a base account derivation path from which the wallet attempts
|
|
|
+// to discover non zero accounts and automatically add them to list of tracked
|
|
|
+// accounts.
|
|
|
+//
|
|
|
+// Note, self derivaton will increment the last component of the specified path
|
|
|
+// opposed to decending into a child path to allow discovering accounts starting
|
|
|
+// from non zero components.
|
|
|
+//
|
|
|
+// Some hardware wallets switched derivation paths through their evolution, so
|
|
|
+// this method supports providing multiple bases to discover old user accounts
|
|
|
+// too. Only the last base will be used to derive the next empty account.
|
|
|
+//
|
|
|
+// You can disable automatic account discovery by calling SelfDerive with a nil
|
|
|
+// chain state reader.
|
|
|
+func (w *wallet) SelfDerive(bases []accounts.DerivationPath, chain ethereum.ChainStateReader) {
|
|
|
w.stateLock.Lock()
|
|
|
defer w.stateLock.Unlock()
|
|
|
|
|
|
- w.deriveNextPath = make(accounts.DerivationPath, len(base))
|
|
|
- copy(w.deriveNextPath[:], base[:])
|
|
|
-
|
|
|
- w.deriveNextAddr = common.Address{}
|
|
|
+ w.deriveNextPaths = make([]accounts.DerivationPath, len(bases))
|
|
|
+ for i, base := range bases {
|
|
|
+ w.deriveNextPaths[i] = make(accounts.DerivationPath, len(base))
|
|
|
+ copy(w.deriveNextPaths[i][:], base[:])
|
|
|
+ }
|
|
|
+ w.deriveNextAddrs = make([]common.Address, len(bases))
|
|
|
w.deriveChain = chain
|
|
|
}
|
|
|
|