Skip to content

Commit

Permalink
Implement FillPSBT in DescriptorScriptPubKeyMan
Browse files Browse the repository at this point in the history
Summary:
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.

Backport of Core [[bitcoin/bitcoin#16528 | PR16528]] [30/43] : bitcoin/bitcoin@72a9540

Test Plan:
  ninja all check-all

Reviewers: #bitcoin_abc, majcosta

Reviewed By: #bitcoin_abc, majcosta

Differential Revision: https://reviews.bitcoinabc.org/D8451
  • Loading branch information
achow101 authored and deadalnix committed Nov 19, 2020
1 parent c480b04 commit 69c82b8
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 46 deletions.
69 changes: 67 additions & 2 deletions src/wallet/scriptpubkeyman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2052,10 +2052,75 @@ DescriptorScriptPubKeyMan::SignMessage(const std::string &message,
}

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<FlatSigningProvider> keys =
std::make_unique<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 = std::make_unique<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 (size_t 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 *
Expand Down
49 changes: 5 additions & 44 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2811,50 +2811,11 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction &psbtx,
}

// 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 (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<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;
}
}

Expand Down

0 comments on commit 69c82b8

Please sign in to comment.