wallet: prevent input signing for transactions on watch-only accounts
Watch-only accounts don't have any type of private key information stored, so we avoid populating input signatures in those cases.
This commit is contained in:
parent
2301069644
commit
f5845dfb42
4 changed files with 97 additions and 6 deletions
|
@ -405,6 +405,29 @@ func (m *Manager) watchOnly() bool {
|
|||
return m.watchingOnly
|
||||
}
|
||||
|
||||
// IsWatchOnlyAccount determines if the account with the given key scope is set
|
||||
// up as watch-only.
|
||||
func (m *Manager) IsWatchOnlyAccount(ns walletdb.ReadBucket, keyScope KeyScope,
|
||||
account uint32) (bool, error) {
|
||||
|
||||
if m.WatchOnly() {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Assume the default imported account has no private keys.
|
||||
//
|
||||
// TODO: Actually check whether it does.
|
||||
if account == ImportedAddrAccount {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
scopedMgr, err := m.FetchScopedKeyManager(keyScope)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return scopedMgr.IsWatchOnlyAccount(ns, account)
|
||||
}
|
||||
|
||||
// lock performs a best try effort to remove and zero all secret keys associated
|
||||
// with the address manager.
|
||||
//
|
||||
|
|
|
@ -2186,6 +2186,22 @@ func (s *ScopedKeyManager) ForEachInternalActiveAddress(ns walletdb.ReadBucket,
|
|||
return nil
|
||||
}
|
||||
|
||||
// IsWatchOnlyAccount determines if the given account belonging to this scoped
|
||||
// manager is set up as watch-only.
|
||||
func (s *ScopedKeyManager) IsWatchOnlyAccount(ns walletdb.ReadBucket,
|
||||
account uint32) (bool, error) {
|
||||
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
|
||||
acctInfo, err := s.loadAccountInfo(ns, account)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return acctInfo.acctKeyPriv == nil, nil
|
||||
}
|
||||
|
||||
// cloneKeyWithVersion clones an extended key to use the version corresponding
|
||||
// to the manager's key scope. This should only be used for non-watch-only
|
||||
// accounts as they are stored within the database using the legacy BIP-0044
|
||||
|
|
|
@ -163,14 +163,36 @@ func (w *Wallet) txToOutputs(outputs []*wire.TxOut, keyScope *waddrmgr.KeyScope,
|
|||
return tx, nil
|
||||
}
|
||||
|
||||
err = tx.AddAllInputScripts(secretSource{w.Manager, addrmgrNs})
|
||||
// Before committing the transaction, we'll sign our inputs. If the
|
||||
// inputs are part of a watch-only account, there's no private key
|
||||
// information stored, so we'll skip signing such.
|
||||
var watchOnly bool
|
||||
if keyScope == nil {
|
||||
// If a key scope wasn't specified, then coin selection was
|
||||
// performed from the default wallet accounts (NP2WKH, P2WKH),
|
||||
// so any key scope provided doesn't impact the result of this
|
||||
// call.
|
||||
watchOnly, err = w.Manager.IsWatchOnlyAccount(
|
||||
addrmgrNs, waddrmgr.KeyScopeBIP0084, account,
|
||||
)
|
||||
} else {
|
||||
watchOnly, err = w.Manager.IsWatchOnlyAccount(
|
||||
addrmgrNs, *keyScope, account,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !watchOnly {
|
||||
err = tx.AddAllInputScripts(secretSource{w.Manager, addrmgrNs})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = validateMsgTx(tx.Tx, tx.PrevScripts, tx.PrevInputValues)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
err = validateMsgTx(tx.Tx, tx.PrevScripts, tx.PrevInputValues)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := dbtx.Commit(); err != nil {
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||
"github.com/btcsuite/btcwallet/wallet/txauthor"
|
||||
"github.com/btcsuite/btcwallet/wallet/txrules"
|
||||
"github.com/btcsuite/btcwallet/walletdb"
|
||||
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||
)
|
||||
|
||||
|
@ -313,8 +314,37 @@ func (w *Wallet) FinalizePsbt(keyScope *waddrmgr.KeyScope, account uint32,
|
|||
}
|
||||
}
|
||||
|
||||
// Finally, we'll sign the input as is, and populate the input
|
||||
// with the witness and sigScript (if needed).
|
||||
// Finally, if the input doesn't belong to a watch-only account,
|
||||
// then we'll sign it as is, and populate the input with the
|
||||
// witness and sigScript (if needed).
|
||||
watchOnly := false
|
||||
err = walletdb.View(w.db, func(tx walletdb.ReadTx) error {
|
||||
ns := tx.ReadBucket(waddrmgrNamespaceKey)
|
||||
var err error
|
||||
if keyScope == nil {
|
||||
// If a key scope wasn't specified, then coin
|
||||
// selection was performed from the default
|
||||
// wallet accounts (NP2WKH, P2WKH), so any key
|
||||
// scope provided doesn't impact the result of
|
||||
// this call.
|
||||
watchOnly, err = w.Manager.IsWatchOnlyAccount(
|
||||
ns, waddrmgr.KeyScopeBIP0084, account,
|
||||
)
|
||||
} else {
|
||||
watchOnly, err = w.Manager.IsWatchOnlyAccount(
|
||||
ns, *keyScope, account,
|
||||
)
|
||||
}
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to determine if account is "+
|
||||
"watch-only: %v", err)
|
||||
}
|
||||
if watchOnly {
|
||||
continue
|
||||
}
|
||||
|
||||
witness, sigScript, err := w.ComputeInputScript(
|
||||
tx, signOutput, idx, sigHashes, in.SighashType, nil,
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue