|
|
@ -119,8 +119,8 @@ type Wallet struct { |
|
|
|
session *Session // The secure communication session with the card
|
|
|
|
session *Session // The secure communication session with the card
|
|
|
|
log log.Logger // Contextual logger to tag the base with its id
|
|
|
|
log log.Logger // Contextual logger to tag the base with its id
|
|
|
|
|
|
|
|
|
|
|
|
deriveNextPath accounts.DerivationPath // Next derivation path for account auto-discovery
|
|
|
|
deriveNextPaths []accounts.DerivationPath // Next derivation paths for account auto-discovery (multiple bases supported)
|
|
|
|
deriveNextAddr common.Address // Next derived account address for auto-discovery
|
|
|
|
deriveNextAddrs []common.Address // Next derived account addresses for auto-discovery (multiple bases supported)
|
|
|
|
deriveChain ethereum.ChainStateReader // Blockchain state reader to discover used account with
|
|
|
|
deriveChain ethereum.ChainStateReader // Blockchain state reader to discover used account with
|
|
|
|
deriveReq chan chan struct{} // Channel to request a self-derivation on
|
|
|
|
deriveReq chan chan struct{} // Channel to request a self-derivation on
|
|
|
|
deriveQuit chan chan error // Channel to terminate the self-deriver with
|
|
|
|
deriveQuit chan chan error // Channel to terminate the self-deriver with
|
|
|
@ -390,7 +390,7 @@ func (w *Wallet) Open(passphrase string) error { |
|
|
|
w.deriveReq = make(chan chan struct{}) |
|
|
|
w.deriveReq = make(chan chan struct{}) |
|
|
|
w.deriveQuit = make(chan chan error) |
|
|
|
w.deriveQuit = make(chan chan error) |
|
|
|
|
|
|
|
|
|
|
|
go w.selfDerive(0) |
|
|
|
go w.selfDerive() |
|
|
|
|
|
|
|
|
|
|
|
// Notify anyone listening for wallet events that a new device is accessible
|
|
|
|
// Notify anyone listening for wallet events that a new device is accessible
|
|
|
|
go w.Hub.updateFeed.Send(accounts.WalletEvent{Wallet: w, Kind: accounts.WalletOpened}) |
|
|
|
go w.Hub.updateFeed.Send(accounts.WalletEvent{Wallet: w, Kind: accounts.WalletOpened}) |
|
|
@ -426,9 +426,8 @@ func (w *Wallet) Close() error { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// selfDerive is an account derivation loop that upon request attempts to find
|
|
|
|
// selfDerive is an account derivation loop that upon request attempts to find
|
|
|
|
// new non-zero accounts. maxEmpty specifies the number of empty accounts that
|
|
|
|
// new non-zero accounts.
|
|
|
|
// should be derived once an initial empty account has been found.
|
|
|
|
func (w *Wallet) selfDerive() { |
|
|
|
func (w *Wallet) selfDerive(maxEmpty int) { |
|
|
|
|
|
|
|
w.log.Debug("Smart card wallet self-derivation started") |
|
|
|
w.log.Debug("Smart card wallet self-derivation started") |
|
|
|
defer w.log.Debug("Smart card wallet self-derivation stopped") |
|
|
|
defer w.log.Debug("Smart card wallet self-derivation stopped") |
|
|
|
|
|
|
|
|
|
|
@ -461,56 +460,59 @@ func (w *Wallet) selfDerive(maxEmpty int) { |
|
|
|
paths []accounts.DerivationPath |
|
|
|
paths []accounts.DerivationPath |
|
|
|
nextAcc accounts.Account |
|
|
|
nextAcc accounts.Account |
|
|
|
|
|
|
|
|
|
|
|
nextAddr = w.deriveNextAddr |
|
|
|
nextPaths = append([]accounts.DerivationPath{}, w.deriveNextPaths...) |
|
|
|
nextPath = w.deriveNextPath |
|
|
|
nextAddrs = append([]common.Address{}, w.deriveNextAddrs...) |
|
|
|
|
|
|
|
|
|
|
|
context = context.Background() |
|
|
|
context = context.Background() |
|
|
|
) |
|
|
|
) |
|
|
|
for empty, emptyCount := false, maxEmpty+1; !empty || emptyCount > 0; { |
|
|
|
for i := 0; i < len(nextAddrs); i++ { |
|
|
|
|
|
|
|
for empty := false; !empty; { |
|
|
|
// Retrieve the next derived Ethereum account
|
|
|
|
// Retrieve the next derived Ethereum account
|
|
|
|
if nextAddr == (common.Address{}) { |
|
|
|
if nextAddrs[i] == (common.Address{}) { |
|
|
|
if nextAcc, err = w.session.derive(nextPath); err != nil { |
|
|
|
if nextAcc, err = w.session.derive(nextPaths[i]); err != nil { |
|
|
|
w.log.Warn("Smartcard wallet account derivation failed", "err", err) |
|
|
|
w.log.Warn("Smartcard wallet account derivation failed", "err", err) |
|
|
|
break |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
nextAddr = nextAcc.Address |
|
|
|
nextAddrs[i] = nextAcc.Address |
|
|
|
} |
|
|
|
} |
|
|
|
// Check the account's status against the current chain state
|
|
|
|
// Check the account's status against the current chain state
|
|
|
|
var ( |
|
|
|
var ( |
|
|
|
balance *big.Int |
|
|
|
balance *big.Int |
|
|
|
nonce uint64 |
|
|
|
nonce uint64 |
|
|
|
) |
|
|
|
) |
|
|
|
balance, err = w.deriveChain.BalanceAt(context, nextAddr, nil) |
|
|
|
balance, err = w.deriveChain.BalanceAt(context, nextAddrs[i], nil) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
w.log.Warn("Smartcard wallet balance retrieval failed", "err", err) |
|
|
|
w.log.Warn("Smartcard wallet balance retrieval failed", "err", err) |
|
|
|
break |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
nonce, err = w.deriveChain.NonceAt(context, nextAddr, nil) |
|
|
|
nonce, err = w.deriveChain.NonceAt(context, nextAddrs[i], nil) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
w.log.Warn("Smartcard wallet nonce retrieval failed", "err", err) |
|
|
|
w.log.Warn("Smartcard wallet nonce retrieval failed", "err", err) |
|
|
|
break |
|
|
|
break |
|
|
|
} |
|
|
|
} |
|
|
|
// If the next account is empty and no more empty accounts are
|
|
|
|
// If the next account is empty, stop self-derivation, but add for the last base path
|
|
|
|
// allowed, stop self-derivation. Add the current one nonetheless.
|
|
|
|
|
|
|
|
if balance.Sign() == 0 && nonce == 0 { |
|
|
|
if balance.Sign() == 0 && nonce == 0 { |
|
|
|
empty = true |
|
|
|
empty = true |
|
|
|
emptyCount-- |
|
|
|
if i < len(nextAddrs)-1 { |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
// We've just self-derived a new account, start tracking it locally
|
|
|
|
// We've just self-derived a new account, start tracking it locally
|
|
|
|
path := make(accounts.DerivationPath, len(nextPath)) |
|
|
|
path := make(accounts.DerivationPath, len(nextPaths[i])) |
|
|
|
copy(path[:], nextPath[:]) |
|
|
|
copy(path[:], nextPaths[i][:]) |
|
|
|
paths = append(paths, path) |
|
|
|
paths = append(paths, path) |
|
|
|
|
|
|
|
|
|
|
|
// Display a log message to the user for new (or previously empty accounts)
|
|
|
|
// Display a log message to the user for new (or previously empty accounts)
|
|
|
|
if _, known := pairing.Accounts[nextAddr]; !known || !empty || nextAddr != w.deriveNextAddr { |
|
|
|
if _, known := pairing.Accounts[nextAddrs[i]]; !known || !empty || nextAddrs[i] != w.deriveNextAddrs[i] { |
|
|
|
w.log.Info("Smartcard wallet discovered new account", "address", nextAddr, "path", path, "balance", balance, "nonce", nonce) |
|
|
|
w.log.Info("Smartcard wallet discovered new account", "address", nextAddrs[i], "path", path, "balance", balance, "nonce", nonce) |
|
|
|
} |
|
|
|
} |
|
|
|
pairing.Accounts[nextAddr] = path |
|
|
|
pairing.Accounts[nextAddrs[i]] = path |
|
|
|
|
|
|
|
|
|
|
|
// Fetch the next potential account
|
|
|
|
// Fetch the next potential account
|
|
|
|
if !empty || emptyCount > 0 { |
|
|
|
if !empty { |
|
|
|
nextAddr = common.Address{} |
|
|
|
nextAddrs[i] = common.Address{} |
|
|
|
nextPath[len(nextPath)-1]++ |
|
|
|
nextPaths[i][len(nextPaths[i])-1]++ |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
// If there are new accounts, write them out
|
|
|
|
// If there are new accounts, write them out
|
|
|
@ -518,8 +520,8 @@ func (w *Wallet) selfDerive(maxEmpty int) { |
|
|
|
err = w.Hub.setPairing(w, pairing) |
|
|
|
err = w.Hub.setPairing(w, pairing) |
|
|
|
} |
|
|
|
} |
|
|
|
// Shift the self-derivation forward
|
|
|
|
// Shift the self-derivation forward
|
|
|
|
w.deriveNextAddr = nextAddr |
|
|
|
w.deriveNextAddrs = nextAddrs |
|
|
|
w.deriveNextPath = nextPath |
|
|
|
w.deriveNextPaths = nextPaths |
|
|
|
|
|
|
|
|
|
|
|
// Self derivation complete, release device lock
|
|
|
|
// Self derivation complete, release device lock
|
|
|
|
w.lock.Unlock() |
|
|
|
w.lock.Unlock() |
|
|
@ -592,7 +594,7 @@ func (w *Wallet) Contains(account accounts.Account) bool { |
|
|
|
|
|
|
|
|
|
|
|
// Initialize installs a keypair generated from the provided key into the wallet.
|
|
|
|
// Initialize installs a keypair generated from the provided key into the wallet.
|
|
|
|
func (w *Wallet) Initialize(seed []byte) error { |
|
|
|
func (w *Wallet) Initialize(seed []byte) error { |
|
|
|
go w.selfDerive(0) |
|
|
|
go w.selfDerive() |
|
|
|
// DO NOT lock at this stage, as the initialize
|
|
|
|
// DO NOT lock at this stage, as the initialize
|
|
|
|
// function relies on Status()
|
|
|
|
// function relies on Status()
|
|
|
|
return w.session.initialize(seed) |
|
|
|
return w.session.initialize(seed) |
|
|
@ -629,16 +631,22 @@ func (w *Wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun |
|
|
|
// opposed to decending into a child path to allow discovering accounts starting
|
|
|
|
// opposed to decending into a child path to allow discovering accounts starting
|
|
|
|
// from non zero components.
|
|
|
|
// 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
|
|
|
|
// You can disable automatic account discovery by calling SelfDerive with a nil
|
|
|
|
// chain state reader.
|
|
|
|
// chain state reader.
|
|
|
|
func (w *Wallet) SelfDerive(base accounts.DerivationPath, chain ethereum.ChainStateReader) { |
|
|
|
func (w *Wallet) SelfDerive(bases []accounts.DerivationPath, chain ethereum.ChainStateReader) { |
|
|
|
w.lock.Lock() |
|
|
|
w.lock.Lock() |
|
|
|
defer w.lock.Unlock() |
|
|
|
defer w.lock.Unlock() |
|
|
|
|
|
|
|
|
|
|
|
w.deriveNextPath = make(accounts.DerivationPath, len(base)) |
|
|
|
w.deriveNextPaths = make([]accounts.DerivationPath, len(bases)) |
|
|
|
copy(w.deriveNextPath[:], base[:]) |
|
|
|
for i, base := range bases { |
|
|
|
|
|
|
|
w.deriveNextPaths[i] = make(accounts.DerivationPath, len(base)) |
|
|
|
w.deriveNextAddr = common.Address{} |
|
|
|
copy(w.deriveNextPaths[i][:], base[:]) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
w.deriveNextAddrs = make([]common.Address, len(bases)) |
|
|
|
w.deriveChain = chain |
|
|
|
w.deriveChain = chain |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|