diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -2052,10 +2052,75 @@ } TransactionError -DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction &psbt, +DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction &psbtx, SigHashType sighash_type, bool sign, bool bip32derivs) const { - return TransactionError::INVALID_PSBT; + for (size_t i = 0; i < psbtx.tx->vin.size(); ++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.getRawSigHashType() > 0 && + input.sighash_type != sighash_type) { + return TransactionError::SIGHASH_MISMATCH; + } + + // Get the scriptPubKey to know which SigningProvider to use + CScript script; + if (!input.utxo.IsNull()) { + script = input.utxo.scriptPubKey; + } else { + // There's no UTXO so we can just skip this now + continue; + } + SignatureData sigdata; + input.FillSignatureData(sigdata); + + std::unique_ptr keys = + std::make_unique(); + std::unique_ptr 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 = std::make_unique(); + for (const auto &pk_pair : input.hd_keypaths) { + const CPubKey &pubkey = pk_pair.first; + std::unique_ptr 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 (size_t i = 0; i < psbtx.tx->vout.size(); ++i) { + std::unique_ptr keys = + GetSolvingProvider(psbtx.tx->vout.at(i).scriptPubKey); + if (!keys) { + continue; + } + UpdatePSBTOutput(HidingSigningProvider(keys.get(), true, !bip32derivs), + psbtx, i); + } + + return TransactionError::OK; } const CKeyMetadata * diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2811,50 +2811,11 @@ } // 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 visited_spk_mans; - for (size_t i = 0; i < psbtx.tx->vin.size(); ++i) { - PSBTInput &input = psbtx.inputs.at(i); - - if (PSBTInputSigned(input)) { - continue; - } - - // Get the scriptPubKey to know which ScriptPubKeyMan to use - CScript script; - if (!input.utxo.IsNull()) { - script = input.utxo.scriptPubKey; - } else { - // There's no UTXO so we can just skip this now - continue; - } - SignatureData sigdata; - input.FillSignatureData(sigdata); - std::set 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; } }