mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-22 15:04:44 +01:00
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.
This commit is contained in:
parent
84b4978c02
commit
72a9540df9
2 changed files with 68 additions and 48 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue