aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Chow <achow101-github@achow101.com>2020-02-11 17:39:43 -0500
committerAndrew Chow <achow101-github@achow101.com>2020-04-23 13:59:48 -0400
commit72a9540df96ffdb94f039b9c14eaacdc7d961196 (patch)
tree38f57b328f20663c5550b2912e95dc27110d7203 /src
parent84b4978c02102171775c77a45f6ec198930f0a88 (diff)
downloadbitcoin-72a9540df96ffdb94f039b9c14eaacdc7d961196.tar.xz
Implement FillPSBT in DescriptorScriptPubKeyMan
FillPSBT will add our own scripts to the PSBT if those inputs are ours. If an input also lists pubkeys that we happen to know the private keys for, we will sign those inputs too.
Diffstat (limited to 'src')
-rw-r--r--src/wallet/scriptpubkeyman.cpp66
-rw-r--r--src/wallet/wallet.cpp50
2 files changed, 68 insertions, 48 deletions
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index b5272be83b..0c1da43b48 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -1974,9 +1974,71 @@ SigningResult DescriptorScriptPubKeyMan::SignMessage(const std::string& message,
return SigningResult::OK;
}
-TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, int sighash_type, bool sign, bool bip32derivs) const
+TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs) const
{
- return TransactionError::INVALID_PSBT;
+ for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
+ const CTxIn& txin = psbtx.tx->vin[i];
+ PSBTInput& input = psbtx.inputs.at(i);
+
+ if (PSBTInputSigned(input)) {
+ continue;
+ }
+
+ // Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
+ if (!input.IsSane()) {
+ return TransactionError::INVALID_PSBT;
+ }
+
+ // Get the Sighash type
+ if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) {
+ return TransactionError::SIGHASH_MISMATCH;
+ }
+
+ // Get the scriptPubKey to know which SigningProvider to use
+ CScript script;
+ if (!input.witness_utxo.IsNull()) {
+ script = input.witness_utxo.scriptPubKey;
+ } else if (input.non_witness_utxo) {
+ if (txin.prevout.n >= input.non_witness_utxo->vout.size()) {
+ return TransactionError::MISSING_INPUTS;
+ }
+ script = input.non_witness_utxo->vout[txin.prevout.n].scriptPubKey;
+ } else {
+ // There's no UTXO so we can just skip this now
+ continue;
+ }
+ SignatureData sigdata;
+ input.FillSignatureData(sigdata);
+
+ std::unique_ptr<FlatSigningProvider> keys = MakeUnique<FlatSigningProvider>();
+ std::unique_ptr<FlatSigningProvider> script_keys = GetSigningProvider(script, sign);
+ if (script_keys) {
+ *keys = Merge(*keys, *script_keys);
+ } else {
+ // Maybe there are pubkeys listed that we can sign for
+ script_keys = MakeUnique<FlatSigningProvider>();
+ for (const auto& pk_pair : input.hd_keypaths) {
+ const CPubKey& pubkey = pk_pair.first;
+ std::unique_ptr<FlatSigningProvider> pk_keys = GetSigningProvider(pubkey);
+ if (pk_keys) {
+ *keys = Merge(*keys, *pk_keys);
+ }
+ }
+ }
+
+ SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, sighash_type);
+ }
+
+ // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
+ for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
+ std::unique_ptr<SigningProvider> keys = GetSolvingProvider(psbtx.tx->vout.at(i).scriptPubKey);
+ if (!keys) {
+ continue;
+ }
+ UpdatePSBTOutput(HidingSigningProvider(keys.get(), true, !bip32derivs), psbtx, i);
+ }
+
+ return TransactionError::OK;
}
const CKeyMetadata* DescriptorScriptPubKeyMan::GetMetadata(const CTxDestination& dest) const
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 062162bdf7..90507a9415 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2489,52 +2489,10 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
}
// Fill in information from ScriptPubKeyMans
- // Because each ScriptPubKeyMan may be able to fill more than one input, we need to keep track of each ScriptPubKeyMan that has filled this psbt.
- // Each iteration, we may fill more inputs than the input that is specified in that iteration.
- // We assume that each input is filled by only one ScriptPubKeyMan
- std::set<uint256> visited_spk_mans;
- for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
- const CTxIn& txin = psbtx.tx->vin[i];
- PSBTInput& input = psbtx.inputs.at(i);
-
- if (PSBTInputSigned(input)) {
- continue;
- }
-
- // Get the scriptPubKey to know which ScriptPubKeyMan to use
- CScript script;
- if (!input.witness_utxo.IsNull()) {
- script = input.witness_utxo.scriptPubKey;
- } else if (input.non_witness_utxo) {
- if (txin.prevout.n >= input.non_witness_utxo->vout.size()) {
- return TransactionError::MISSING_INPUTS;
- }
- script = input.non_witness_utxo->vout[txin.prevout.n].scriptPubKey;
- } else {
- // There's no UTXO so we can just skip this now
- continue;
- }
- SignatureData sigdata;
- input.FillSignatureData(sigdata);
- std::set<ScriptPubKeyMan*> spk_mans = GetScriptPubKeyMans(script, sigdata);
- if (spk_mans.size() == 0) {
- continue;
- }
-
- for (auto& spk_man : spk_mans) {
- // If we've already been signed by this spk_man, skip it
- if (visited_spk_mans.count(spk_man->GetID()) > 0) {
- continue;
- }
-
- // Fill in the information from the spk_man
- TransactionError res = spk_man->FillPSBT(psbtx, sighash_type, sign, bip32derivs);
- if (res != TransactionError::OK) {
- return res;
- }
-
- // Add this spk_man to visited_spk_mans so we can skip it later
- visited_spk_mans.insert(spk_man->GetID());
+ for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) {
+ TransactionError res = spk_man->FillPSBT(psbtx, sighash_type, sign, bip32derivs);
+ if (res != TransactionError::OK) {
+ return res;
}
}