mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-10 10:52:31 -05:00
![fanquake](/assets/img/avatar_default.png)
6c7a17a8e0
psbt: support externally provided preimages for Miniscript satisfaction (Antoine Poinsot)840a396029
qa: add a "smart" Miniscript fuzz target (Antoine Poinsot)17e3547241
qa: add a fuzz target generating random nodes from a binary encoding (Antoine Poinsot)611e12502a
qa: functional test Miniscript signing with key and timelocks (Antoine Poinsot)d57b7f2021
refactor: make descriptors in Miniscript functional test more readable (Antoine Poinsot)0a8fc9e200
wallet: check solvability using descriptor in AvailableCoins (Antoine Poinsot)560e62b1e2
script/sign: signing support for Miniscripts with hash preimage challenges (Antoine Poinsot)a2f81b6a8f
script/sign: signing support for Miniscript with timelocks (Antoine Poinsot)61c6d1a844
script/sign: basic signing support for Miniscript descriptors (Antoine Poinsot)4242c1c521
Align 'e' property of or_d and andor with website spec (Pieter Wuille)f5deb41780
Various additional explanations of the satisfaction logic from Pieter (Pieter Wuille)22c5b00345
miniscript: satisfaction support (Antoine Poinsot) Pull request description: This makes the Miniscript descriptors solvable. Note this introduces signing support for much more complex scripts than the wallet was previously able to solve, and the whole tooling isn't provided for a complete Miniscript integration in the wallet. Particularly, the PSBT<->Miniscript integration isn't entirely covered in this PR. ACKs for top commit: achow101: ACK6c7a17a8e0
sipa: utACK6c7a17a8e0
(to the extent that it's not my own code). Tree-SHA512: a71ec002aaf66bd429012caa338fc58384067bcd2f453a46e21d381ed1bacc8e57afb9db57c0fb4bf40de43b30808815e9ebc0ae1fbd9e61df0e7b91a17771cc
533 lines
20 KiB
C++
533 lines
20 KiB
C++
// Copyright (c) 2009-2022 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <psbt.h>
|
|
|
|
#include <policy/policy.h>
|
|
#include <util/check.h>
|
|
#include <util/strencodings.h>
|
|
|
|
|
|
PartiallySignedTransaction::PartiallySignedTransaction(const CMutableTransaction& tx) : tx(tx)
|
|
{
|
|
inputs.resize(tx.vin.size());
|
|
outputs.resize(tx.vout.size());
|
|
}
|
|
|
|
bool PartiallySignedTransaction::IsNull() const
|
|
{
|
|
return !tx && inputs.empty() && outputs.empty() && unknown.empty();
|
|
}
|
|
|
|
bool PartiallySignedTransaction::Merge(const PartiallySignedTransaction& psbt)
|
|
{
|
|
// Prohibited to merge two PSBTs over different transactions
|
|
if (tx->GetHash() != psbt.tx->GetHash()) {
|
|
return false;
|
|
}
|
|
|
|
for (unsigned int i = 0; i < inputs.size(); ++i) {
|
|
inputs[i].Merge(psbt.inputs[i]);
|
|
}
|
|
for (unsigned int i = 0; i < outputs.size(); ++i) {
|
|
outputs[i].Merge(psbt.outputs[i]);
|
|
}
|
|
for (auto& xpub_pair : psbt.m_xpubs) {
|
|
if (m_xpubs.count(xpub_pair.first) == 0) {
|
|
m_xpubs[xpub_pair.first] = xpub_pair.second;
|
|
} else {
|
|
m_xpubs[xpub_pair.first].insert(xpub_pair.second.begin(), xpub_pair.second.end());
|
|
}
|
|
}
|
|
unknown.insert(psbt.unknown.begin(), psbt.unknown.end());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool PartiallySignedTransaction::AddInput(const CTxIn& txin, PSBTInput& psbtin)
|
|
{
|
|
if (std::find(tx->vin.begin(), tx->vin.end(), txin) != tx->vin.end()) {
|
|
return false;
|
|
}
|
|
tx->vin.push_back(txin);
|
|
psbtin.partial_sigs.clear();
|
|
psbtin.final_script_sig.clear();
|
|
psbtin.final_script_witness.SetNull();
|
|
inputs.push_back(psbtin);
|
|
return true;
|
|
}
|
|
|
|
bool PartiallySignedTransaction::AddOutput(const CTxOut& txout, const PSBTOutput& psbtout)
|
|
{
|
|
tx->vout.push_back(txout);
|
|
outputs.push_back(psbtout);
|
|
return true;
|
|
}
|
|
|
|
bool PartiallySignedTransaction::GetInputUTXO(CTxOut& utxo, int input_index) const
|
|
{
|
|
const PSBTInput& input = inputs[input_index];
|
|
uint32_t prevout_index = tx->vin[input_index].prevout.n;
|
|
if (input.non_witness_utxo) {
|
|
if (prevout_index >= input.non_witness_utxo->vout.size()) {
|
|
return false;
|
|
}
|
|
if (input.non_witness_utxo->GetHash() != tx->vin[input_index].prevout.hash) {
|
|
return false;
|
|
}
|
|
utxo = input.non_witness_utxo->vout[prevout_index];
|
|
} else if (!input.witness_utxo.IsNull()) {
|
|
utxo = input.witness_utxo;
|
|
} else {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PSBTInput::IsNull() const
|
|
{
|
|
return !non_witness_utxo && witness_utxo.IsNull() && partial_sigs.empty() && unknown.empty() && hd_keypaths.empty() && redeem_script.empty() && witness_script.empty();
|
|
}
|
|
|
|
void PSBTInput::FillSignatureData(SignatureData& sigdata) const
|
|
{
|
|
if (!final_script_sig.empty()) {
|
|
sigdata.scriptSig = final_script_sig;
|
|
sigdata.complete = true;
|
|
}
|
|
if (!final_script_witness.IsNull()) {
|
|
sigdata.scriptWitness = final_script_witness;
|
|
sigdata.complete = true;
|
|
}
|
|
if (sigdata.complete) {
|
|
return;
|
|
}
|
|
|
|
sigdata.signatures.insert(partial_sigs.begin(), partial_sigs.end());
|
|
if (!redeem_script.empty()) {
|
|
sigdata.redeem_script = redeem_script;
|
|
}
|
|
if (!witness_script.empty()) {
|
|
sigdata.witness_script = witness_script;
|
|
}
|
|
for (const auto& key_pair : hd_keypaths) {
|
|
sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair);
|
|
}
|
|
if (!m_tap_key_sig.empty()) {
|
|
sigdata.taproot_key_path_sig = m_tap_key_sig;
|
|
}
|
|
for (const auto& [pubkey_leaf, sig] : m_tap_script_sigs) {
|
|
sigdata.taproot_script_sigs.emplace(pubkey_leaf, sig);
|
|
}
|
|
if (!m_tap_internal_key.IsNull()) {
|
|
sigdata.tr_spenddata.internal_key = m_tap_internal_key;
|
|
}
|
|
if (!m_tap_merkle_root.IsNull()) {
|
|
sigdata.tr_spenddata.merkle_root = m_tap_merkle_root;
|
|
}
|
|
for (const auto& [leaf_script, control_block] : m_tap_scripts) {
|
|
sigdata.tr_spenddata.scripts.emplace(leaf_script, control_block);
|
|
}
|
|
for (const auto& [pubkey, leaf_origin] : m_tap_bip32_paths) {
|
|
sigdata.taproot_misc_pubkeys.emplace(pubkey, leaf_origin);
|
|
}
|
|
for (const auto& [hash, preimage] : ripemd160_preimages) {
|
|
sigdata.ripemd160_preimages.emplace(std::vector<unsigned char>(hash.begin(), hash.end()), preimage);
|
|
}
|
|
for (const auto& [hash, preimage] : sha256_preimages) {
|
|
sigdata.sha256_preimages.emplace(std::vector<unsigned char>(hash.begin(), hash.end()), preimage);
|
|
}
|
|
for (const auto& [hash, preimage] : hash160_preimages) {
|
|
sigdata.hash160_preimages.emplace(std::vector<unsigned char>(hash.begin(), hash.end()), preimage);
|
|
}
|
|
for (const auto& [hash, preimage] : hash256_preimages) {
|
|
sigdata.hash256_preimages.emplace(std::vector<unsigned char>(hash.begin(), hash.end()), preimage);
|
|
}
|
|
}
|
|
|
|
void PSBTInput::FromSignatureData(const SignatureData& sigdata)
|
|
{
|
|
if (sigdata.complete) {
|
|
partial_sigs.clear();
|
|
hd_keypaths.clear();
|
|
redeem_script.clear();
|
|
witness_script.clear();
|
|
|
|
if (!sigdata.scriptSig.empty()) {
|
|
final_script_sig = sigdata.scriptSig;
|
|
}
|
|
if (!sigdata.scriptWitness.IsNull()) {
|
|
final_script_witness = sigdata.scriptWitness;
|
|
}
|
|
return;
|
|
}
|
|
|
|
partial_sigs.insert(sigdata.signatures.begin(), sigdata.signatures.end());
|
|
if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
|
|
redeem_script = sigdata.redeem_script;
|
|
}
|
|
if (witness_script.empty() && !sigdata.witness_script.empty()) {
|
|
witness_script = sigdata.witness_script;
|
|
}
|
|
for (const auto& entry : sigdata.misc_pubkeys) {
|
|
hd_keypaths.emplace(entry.second);
|
|
}
|
|
if (!sigdata.taproot_key_path_sig.empty()) {
|
|
m_tap_key_sig = sigdata.taproot_key_path_sig;
|
|
}
|
|
for (const auto& [pubkey_leaf, sig] : sigdata.taproot_script_sigs) {
|
|
m_tap_script_sigs.emplace(pubkey_leaf, sig);
|
|
}
|
|
if (!sigdata.tr_spenddata.internal_key.IsNull()) {
|
|
m_tap_internal_key = sigdata.tr_spenddata.internal_key;
|
|
}
|
|
if (!sigdata.tr_spenddata.merkle_root.IsNull()) {
|
|
m_tap_merkle_root = sigdata.tr_spenddata.merkle_root;
|
|
}
|
|
for (const auto& [leaf_script, control_block] : sigdata.tr_spenddata.scripts) {
|
|
m_tap_scripts.emplace(leaf_script, control_block);
|
|
}
|
|
for (const auto& [pubkey, leaf_origin] : sigdata.taproot_misc_pubkeys) {
|
|
m_tap_bip32_paths.emplace(pubkey, leaf_origin);
|
|
}
|
|
}
|
|
|
|
void PSBTInput::Merge(const PSBTInput& input)
|
|
{
|
|
if (!non_witness_utxo && input.non_witness_utxo) non_witness_utxo = input.non_witness_utxo;
|
|
if (witness_utxo.IsNull() && !input.witness_utxo.IsNull()) {
|
|
witness_utxo = input.witness_utxo;
|
|
}
|
|
|
|
partial_sigs.insert(input.partial_sigs.begin(), input.partial_sigs.end());
|
|
ripemd160_preimages.insert(input.ripemd160_preimages.begin(), input.ripemd160_preimages.end());
|
|
sha256_preimages.insert(input.sha256_preimages.begin(), input.sha256_preimages.end());
|
|
hash160_preimages.insert(input.hash160_preimages.begin(), input.hash160_preimages.end());
|
|
hash256_preimages.insert(input.hash256_preimages.begin(), input.hash256_preimages.end());
|
|
hd_keypaths.insert(input.hd_keypaths.begin(), input.hd_keypaths.end());
|
|
unknown.insert(input.unknown.begin(), input.unknown.end());
|
|
m_tap_script_sigs.insert(input.m_tap_script_sigs.begin(), input.m_tap_script_sigs.end());
|
|
m_tap_scripts.insert(input.m_tap_scripts.begin(), input.m_tap_scripts.end());
|
|
m_tap_bip32_paths.insert(input.m_tap_bip32_paths.begin(), input.m_tap_bip32_paths.end());
|
|
|
|
if (redeem_script.empty() && !input.redeem_script.empty()) redeem_script = input.redeem_script;
|
|
if (witness_script.empty() && !input.witness_script.empty()) witness_script = input.witness_script;
|
|
if (final_script_sig.empty() && !input.final_script_sig.empty()) final_script_sig = input.final_script_sig;
|
|
if (final_script_witness.IsNull() && !input.final_script_witness.IsNull()) final_script_witness = input.final_script_witness;
|
|
if (m_tap_key_sig.empty() && !input.m_tap_key_sig.empty()) m_tap_key_sig = input.m_tap_key_sig;
|
|
if (m_tap_internal_key.IsNull() && !input.m_tap_internal_key.IsNull()) m_tap_internal_key = input.m_tap_internal_key;
|
|
if (m_tap_merkle_root.IsNull() && !input.m_tap_merkle_root.IsNull()) m_tap_merkle_root = input.m_tap_merkle_root;
|
|
}
|
|
|
|
void PSBTOutput::FillSignatureData(SignatureData& sigdata) const
|
|
{
|
|
if (!redeem_script.empty()) {
|
|
sigdata.redeem_script = redeem_script;
|
|
}
|
|
if (!witness_script.empty()) {
|
|
sigdata.witness_script = witness_script;
|
|
}
|
|
for (const auto& key_pair : hd_keypaths) {
|
|
sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair);
|
|
}
|
|
if (!m_tap_tree.empty() && m_tap_internal_key.IsFullyValid()) {
|
|
TaprootBuilder builder;
|
|
for (const auto& [depth, leaf_ver, script] : m_tap_tree) {
|
|
builder.Add((int)depth, script, (int)leaf_ver, /*track=*/true);
|
|
}
|
|
assert(builder.IsComplete());
|
|
builder.Finalize(m_tap_internal_key);
|
|
TaprootSpendData spenddata = builder.GetSpendData();
|
|
|
|
sigdata.tr_spenddata.internal_key = m_tap_internal_key;
|
|
sigdata.tr_spenddata.Merge(spenddata);
|
|
}
|
|
for (const auto& [pubkey, leaf_origin] : m_tap_bip32_paths) {
|
|
sigdata.taproot_misc_pubkeys.emplace(pubkey, leaf_origin);
|
|
}
|
|
}
|
|
|
|
void PSBTOutput::FromSignatureData(const SignatureData& sigdata)
|
|
{
|
|
if (redeem_script.empty() && !sigdata.redeem_script.empty()) {
|
|
redeem_script = sigdata.redeem_script;
|
|
}
|
|
if (witness_script.empty() && !sigdata.witness_script.empty()) {
|
|
witness_script = sigdata.witness_script;
|
|
}
|
|
for (const auto& entry : sigdata.misc_pubkeys) {
|
|
hd_keypaths.emplace(entry.second);
|
|
}
|
|
if (!sigdata.tr_spenddata.internal_key.IsNull()) {
|
|
m_tap_internal_key = sigdata.tr_spenddata.internal_key;
|
|
}
|
|
if (sigdata.tr_builder.has_value() && sigdata.tr_builder->HasScripts()) {
|
|
m_tap_tree = sigdata.tr_builder->GetTreeTuples();
|
|
}
|
|
for (const auto& [pubkey, leaf_origin] : sigdata.taproot_misc_pubkeys) {
|
|
m_tap_bip32_paths.emplace(pubkey, leaf_origin);
|
|
}
|
|
}
|
|
|
|
bool PSBTOutput::IsNull() const
|
|
{
|
|
return redeem_script.empty() && witness_script.empty() && hd_keypaths.empty() && unknown.empty();
|
|
}
|
|
|
|
void PSBTOutput::Merge(const PSBTOutput& output)
|
|
{
|
|
hd_keypaths.insert(output.hd_keypaths.begin(), output.hd_keypaths.end());
|
|
unknown.insert(output.unknown.begin(), output.unknown.end());
|
|
m_tap_bip32_paths.insert(output.m_tap_bip32_paths.begin(), output.m_tap_bip32_paths.end());
|
|
|
|
if (redeem_script.empty() && !output.redeem_script.empty()) redeem_script = output.redeem_script;
|
|
if (witness_script.empty() && !output.witness_script.empty()) witness_script = output.witness_script;
|
|
if (m_tap_internal_key.IsNull() && !output.m_tap_internal_key.IsNull()) m_tap_internal_key = output.m_tap_internal_key;
|
|
if (m_tap_tree.empty() && !output.m_tap_tree.empty()) m_tap_tree = output.m_tap_tree;
|
|
}
|
|
|
|
bool PSBTInputSigned(const PSBTInput& input)
|
|
{
|
|
return !input.final_script_sig.empty() || !input.final_script_witness.IsNull();
|
|
}
|
|
|
|
bool PSBTInputSignedAndVerified(const PartiallySignedTransaction psbt, unsigned int input_index, const PrecomputedTransactionData* txdata)
|
|
{
|
|
CTxOut utxo;
|
|
assert(psbt.inputs.size() >= input_index);
|
|
const PSBTInput& input = psbt.inputs[input_index];
|
|
|
|
if (input.non_witness_utxo) {
|
|
// If we're taking our information from a non-witness UTXO, verify that it matches the prevout.
|
|
COutPoint prevout = psbt.tx->vin[input_index].prevout;
|
|
if (prevout.n >= input.non_witness_utxo->vout.size()) {
|
|
return false;
|
|
}
|
|
if (input.non_witness_utxo->GetHash() != prevout.hash) {
|
|
return false;
|
|
}
|
|
utxo = input.non_witness_utxo->vout[prevout.n];
|
|
} else if (!input.witness_utxo.IsNull()) {
|
|
utxo = input.witness_utxo;
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
if (txdata) {
|
|
return VerifyScript(input.final_script_sig, utxo.scriptPubKey, &input.final_script_witness, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker{&(*psbt.tx), input_index, utxo.nValue, *txdata, MissingDataBehavior::FAIL});
|
|
} else {
|
|
return VerifyScript(input.final_script_sig, utxo.scriptPubKey, &input.final_script_witness, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker{&(*psbt.tx), input_index, utxo.nValue, MissingDataBehavior::FAIL});
|
|
}
|
|
}
|
|
|
|
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt) {
|
|
size_t count = 0;
|
|
for (const auto& input : psbt.inputs) {
|
|
if (!PSBTInputSigned(input)) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
void UpdatePSBTOutput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index)
|
|
{
|
|
CMutableTransaction& tx = *Assert(psbt.tx);
|
|
const CTxOut& out = tx.vout.at(index);
|
|
PSBTOutput& psbt_out = psbt.outputs.at(index);
|
|
|
|
// Fill a SignatureData with output info
|
|
SignatureData sigdata;
|
|
psbt_out.FillSignatureData(sigdata);
|
|
|
|
// Construct a would-be spend of this output, to update sigdata with.
|
|
// Note that ProduceSignature is used to fill in metadata (not actual signatures),
|
|
// so provider does not need to provide any private keys (it can be a HidingSigningProvider).
|
|
MutableTransactionSignatureCreator creator(tx, /*input_idx=*/0, out.nValue, SIGHASH_ALL);
|
|
ProduceSignature(provider, creator, out.scriptPubKey, sigdata);
|
|
|
|
// Put redeem_script, witness_script, key paths, into PSBTOutput.
|
|
psbt_out.FromSignatureData(sigdata);
|
|
}
|
|
|
|
PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction& psbt)
|
|
{
|
|
const CMutableTransaction& tx = *psbt.tx;
|
|
bool have_all_spent_outputs = true;
|
|
std::vector<CTxOut> utxos(tx.vin.size());
|
|
for (size_t idx = 0; idx < tx.vin.size(); ++idx) {
|
|
if (!psbt.GetInputUTXO(utxos[idx], idx)) have_all_spent_outputs = false;
|
|
}
|
|
PrecomputedTransactionData txdata;
|
|
if (have_all_spent_outputs) {
|
|
txdata.Init(tx, std::move(utxos), true);
|
|
} else {
|
|
txdata.Init(tx, {}, true);
|
|
}
|
|
return txdata;
|
|
}
|
|
|
|
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash, SignatureData* out_sigdata, bool finalize)
|
|
{
|
|
PSBTInput& input = psbt.inputs.at(index);
|
|
const CMutableTransaction& tx = *psbt.tx;
|
|
|
|
if (PSBTInputSignedAndVerified(psbt, index, txdata)) {
|
|
return true;
|
|
}
|
|
|
|
// Fill SignatureData with input info
|
|
SignatureData sigdata;
|
|
input.FillSignatureData(sigdata);
|
|
|
|
// Get UTXO
|
|
bool require_witness_sig = false;
|
|
CTxOut utxo;
|
|
|
|
if (input.non_witness_utxo) {
|
|
// If we're taking our information from a non-witness UTXO, verify that it matches the prevout.
|
|
COutPoint prevout = tx.vin[index].prevout;
|
|
if (prevout.n >= input.non_witness_utxo->vout.size()) {
|
|
return false;
|
|
}
|
|
if (input.non_witness_utxo->GetHash() != prevout.hash) {
|
|
return false;
|
|
}
|
|
utxo = input.non_witness_utxo->vout[prevout.n];
|
|
} else if (!input.witness_utxo.IsNull()) {
|
|
utxo = input.witness_utxo;
|
|
// When we're taking our information from a witness UTXO, we can't verify it is actually data from
|
|
// the output being spent. This is safe in case a witness signature is produced (which includes this
|
|
// information directly in the hash), but not for non-witness signatures. Remember that we require
|
|
// a witness signature in this situation.
|
|
require_witness_sig = true;
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
sigdata.witness = false;
|
|
bool sig_complete;
|
|
if (txdata == nullptr) {
|
|
sig_complete = ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, utxo.scriptPubKey, sigdata);
|
|
} else {
|
|
MutableTransactionSignatureCreator creator(tx, index, utxo.nValue, txdata, sighash);
|
|
sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
|
|
}
|
|
// Verify that a witness signature was produced in case one was required.
|
|
if (require_witness_sig && !sigdata.witness) return false;
|
|
|
|
// If we are not finalizing, set sigdata.complete to false to not set the scriptWitness
|
|
if (!finalize && sigdata.complete) sigdata.complete = false;
|
|
|
|
input.FromSignatureData(sigdata);
|
|
|
|
// If we have a witness signature, put a witness UTXO.
|
|
if (sigdata.witness) {
|
|
input.witness_utxo = utxo;
|
|
// We can remove the non_witness_utxo if and only if there are no non-segwit or segwit v0
|
|
// inputs in this transaction. Since this requires inspecting the entire transaction, this
|
|
// is something for the caller to deal with (i.e. FillPSBT).
|
|
}
|
|
|
|
// Fill in the missing info
|
|
if (out_sigdata) {
|
|
out_sigdata->missing_pubkeys = sigdata.missing_pubkeys;
|
|
out_sigdata->missing_sigs = sigdata.missing_sigs;
|
|
out_sigdata->missing_redeem_script = sigdata.missing_redeem_script;
|
|
out_sigdata->missing_witness_script = sigdata.missing_witness_script;
|
|
}
|
|
|
|
return sig_complete;
|
|
}
|
|
|
|
bool FinalizePSBT(PartiallySignedTransaction& psbtx)
|
|
{
|
|
// Finalize input signatures -- in case we have partial signatures that add up to a complete
|
|
// signature, but have not combined them yet (e.g. because the combiner that created this
|
|
// PartiallySignedTransaction did not understand them), this will combine them into a final
|
|
// script.
|
|
bool complete = true;
|
|
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
|
|
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
|
|
complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, SIGHASH_ALL, nullptr, true);
|
|
}
|
|
|
|
return complete;
|
|
}
|
|
|
|
bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransaction& result)
|
|
{
|
|
// It's not safe to extract a PSBT that isn't finalized, and there's no easy way to check
|
|
// whether a PSBT is finalized without finalizing it, so we just do this.
|
|
if (!FinalizePSBT(psbtx)) {
|
|
return false;
|
|
}
|
|
|
|
result = *psbtx.tx;
|
|
for (unsigned int i = 0; i < result.vin.size(); ++i) {
|
|
result.vin[i].scriptSig = psbtx.inputs[i].final_script_sig;
|
|
result.vin[i].scriptWitness = psbtx.inputs[i].final_script_witness;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector<PartiallySignedTransaction>& psbtxs)
|
|
{
|
|
out = psbtxs[0]; // Copy the first one
|
|
|
|
// Merge
|
|
for (auto it = std::next(psbtxs.begin()); it != psbtxs.end(); ++it) {
|
|
if (!out.Merge(*it)) {
|
|
return TransactionError::PSBT_MISMATCH;
|
|
}
|
|
}
|
|
return TransactionError::OK;
|
|
}
|
|
|
|
std::string PSBTRoleName(PSBTRole role) {
|
|
switch (role) {
|
|
case PSBTRole::CREATOR: return "creator";
|
|
case PSBTRole::UPDATER: return "updater";
|
|
case PSBTRole::SIGNER: return "signer";
|
|
case PSBTRole::FINALIZER: return "finalizer";
|
|
case PSBTRole::EXTRACTOR: return "extractor";
|
|
// no default case, so the compiler can warn about missing cases
|
|
}
|
|
assert(false);
|
|
}
|
|
|
|
bool DecodeBase64PSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error)
|
|
{
|
|
auto tx_data = DecodeBase64(base64_tx);
|
|
if (!tx_data) {
|
|
error = "invalid base64";
|
|
return false;
|
|
}
|
|
return DecodeRawPSBT(psbt, MakeByteSpan(*tx_data), error);
|
|
}
|
|
|
|
bool DecodeRawPSBT(PartiallySignedTransaction& psbt, Span<const std::byte> tx_data, std::string& error)
|
|
{
|
|
CDataStream ss_data(tx_data, SER_NETWORK, PROTOCOL_VERSION);
|
|
try {
|
|
ss_data >> psbt;
|
|
if (!ss_data.empty()) {
|
|
error = "extra data after PSBT";
|
|
return false;
|
|
}
|
|
} catch (const std::exception& e) {
|
|
error = e.what();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
uint32_t PartiallySignedTransaction::GetVersion() const
|
|
{
|
|
if (m_version != std::nullopt) {
|
|
return *m_version;
|
|
}
|
|
return 0;
|
|
}
|