Skip to content

Commit f37de92

Browse files
committed
Implement CWallet::SignTransaction using ScriptPubKeyMan::SignTransaction
1 parent d999dd5 commit f37de92

File tree

2 files changed

+70
-16
lines changed

2 files changed

+70
-16
lines changed

src/wallet/wallet.cpp

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2410,34 +2410,71 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
24102410
return res;
24112411
}
24122412

2413-
bool CWallet::SignTransaction(CMutableTransaction& tx)
2413+
bool CWallet::SignTransaction(CMutableTransaction& tx) const
24142414
{
24152415
AssertLockHeld(cs_wallet);
24162416

2417-
// sign the new tx
2418-
int nIn = 0;
2417+
// Build coins map
2418+
std::map<COutPoint, Coin> coins;
24192419
for (auto& input : tx.vin) {
24202420
std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(input.prevout.hash);
24212421
if(mi == mapWallet.end() || input.prevout.n >= mi->second.tx->vout.size()) {
24222422
return false;
24232423
}
2424-
const CScript& scriptPubKey = mi->second.tx->vout[input.prevout.n].scriptPubKey;
2425-
const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue;
2426-
SignatureData sigdata;
2424+
const CWalletTx& wtx = mi->second;
2425+
coins[input.prevout] = Coin(wtx.tx->vout[input.prevout.n], wtx.m_confirm.block_height, wtx.IsCoinBase());
2426+
}
2427+
std::map<int, std::string> input_errors;
2428+
return SignTransaction(tx, coins, SIGHASH_ALL, input_errors);
2429+
}
2430+
2431+
bool CWallet::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const
2432+
{
2433+
// Sign the tx with ScriptPubKeyMans
2434+
// Because each ScriptPubKeyMan can sign more than one input, we need to keep track of each ScriptPubKeyMan that has signed this transaction.
2435+
// Each iteration, we may sign more txins than the txin that is specified in that iteration.
2436+
// We assume that each input is signed by only one ScriptPubKeyMan.
2437+
std::set<uint256> visited_spk_mans;
2438+
for (unsigned int i = 0; i < tx.vin.size(); i++) {
2439+
// Get the prevout
2440+
CTxIn& txin = tx.vin[i];
2441+
auto coin = coins.find(txin.prevout);
2442+
if (coin == coins.end() || coin->second.IsSpent()) {
2443+
input_errors[i] = "Input not found or already spent";
2444+
continue;
2445+
}
24272446

2428-
std::unique_ptr<SigningProvider> provider = GetSigningProvider(scriptPubKey);
2429-
if (!provider) {
2430-
// We don't know about this scriptpbuKey;
2431-
return false;
2447+
// Check if this input is complete
2448+
SignatureData sigdata = DataFromTransaction(tx, i, coin->second.out);
2449+
if (sigdata.complete) {
2450+
continue;
24322451
}
24332452

2434-
if (!ProduceSignature(*provider, MutableTransactionSignatureCreator(&tx, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) {
2435-
return false;
2453+
// Input needs to be signed, find the right ScriptPubKeyMan
2454+
std::set<ScriptPubKeyMan*> spk_mans = GetScriptPubKeyMans(coin->second.out.scriptPubKey, sigdata);
2455+
if (spk_mans.size() == 0) {
2456+
input_errors[i] = "Unable to sign input, missing keys";
2457+
continue;
2458+
}
2459+
2460+
for (auto& spk_man : spk_mans) {
2461+
// If we've already been signed by this spk_man, skip it
2462+
if (visited_spk_mans.count(spk_man->GetID()) > 0) {
2463+
continue;
2464+
}
2465+
2466+
// Sign the tx.
2467+
// spk_man->SignTransaction will return true if the transaction is complete,
2468+
// so we can exit early and return true if that happens.
2469+
if (spk_man->SignTransaction(tx, coins, sighash, input_errors)) {
2470+
return true;
2471+
}
2472+
2473+
// Add this spk_man to visited_spk_mans so we can skip it later
2474+
visited_spk_mans.insert(spk_man->GetID());
24362475
}
2437-
UpdateInput(input, sigdata);
2438-
nIn++;
24392476
}
2440-
return true;
2477+
return false;
24412478
}
24422479

24432480
bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl)
@@ -4155,6 +4192,17 @@ ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const OutputType& type, bool intern
41554192
return it->second;
41564193
}
41574194

4195+
std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script, SignatureData& sigdata) const
4196+
{
4197+
std::set<ScriptPubKeyMan*> spk_mans;
4198+
for (const auto& spk_man_pair : m_spk_managers) {
4199+
if (spk_man_pair.second->CanProvide(script, sigdata)) {
4200+
spk_mans.insert(spk_man_pair.second.get());
4201+
}
4202+
}
4203+
return spk_mans;
4204+
}
4205+
41584206
ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const CScript& script) const
41594207
{
41604208
SignatureData sigdata;

src/wallet/wallet.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,10 @@ class CWallet final : public WalletStorage, private interfaces::Chain::Notificat
916916
* calling CreateTransaction();
917917
*/
918918
bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
919-
bool SignTransaction(CMutableTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
919+
// Fetch the inputs and sign with SIGHASH_ALL.
920+
bool SignTransaction(CMutableTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
921+
// Sign the tx given the input coins and sighash.
922+
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const;
920923

921924
/**
922925
* Create a new transaction paying the recipients with a set of coins
@@ -1153,6 +1156,9 @@ class CWallet final : public WalletStorage, private interfaces::Chain::Notificat
11531156
//! Get the ScriptPubKeyMan by id
11541157
ScriptPubKeyMan* GetScriptPubKeyMan(const uint256& id) const;
11551158

1159+
//! Get all of the ScriptPubKeyMans for a script given additional information in sigdata (populated by e.g. a psbt)
1160+
std::set<ScriptPubKeyMan*> GetScriptPubKeyMans(const CScript& script, SignatureData& sigdata) const;
1161+
11561162
//! Get the SigningProvider for a script
11571163
std::unique_ptr<SigningProvider> GetSigningProvider(const CScript& script) const;
11581164
std::unique_ptr<SigningProvider> GetSigningProvider(const CScript& script, SignatureData& sigdata) const;

0 commit comments

Comments
 (0)