diff options
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/node/psbt.cpp | 134 | ||||
-rw-r--r-- | src/node/psbt.h | 43 | ||||
-rw-r--r-- | src/psbt.cpp | 123 | ||||
-rw-r--r-- | src/psbt.h | 33 | ||||
-rw-r--r-- | src/rpc/rawtransaction.cpp | 1 |
6 files changed, 180 insertions, 156 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 17e72a7906..cb0604391e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -156,6 +156,7 @@ BITCOIN_CORE_H = \ netbase.h \ netmessagemaker.h \ node/coin.h \ + node/psbt.h \ node/transaction.h \ noui.h \ optional.h \ @@ -272,6 +273,7 @@ libbitcoin_server_a_SOURCES = \ net.cpp \ net_processing.cpp \ node/coin.cpp \ + node/psbt.cpp \ node/transaction.cpp \ noui.cpp \ policy/fees.cpp \ diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp new file mode 100644 index 0000000000..12559c5a5f --- /dev/null +++ b/src/node/psbt.cpp @@ -0,0 +1,134 @@ +// Copyright (c) 2009-2018 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 <coins.h> +#include <consensus/tx_verify.h> +#include <node/psbt.h> +#include <policy/policy.h> +#include <policy/settings.h> + +#include <numeric> + +PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx) +{ + // Go through each input and build status + PSBTAnalysis result; + + bool calc_fee = true; + bool all_final = true; + bool only_missing_sigs = true; + bool only_missing_final = false; + CAmount in_amt = 0; + + result.inputs.resize(psbtx.tx->vin.size()); + + for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { + PSBTInput& input = psbtx.inputs[i]; + PSBTInputAnalysis& input_analysis = result.inputs[i]; + + // Check for a UTXO + CTxOut utxo; + if (psbtx.GetInputUTXO(utxo, i)) { + in_amt += utxo.nValue; + input_analysis.has_utxo = true; + } else { + input_analysis.has_utxo = false; + input_analysis.is_final = false; + input_analysis.next = PSBTRole::UPDATER; + calc_fee = false; + } + + // Check if it is final + if (!utxo.IsNull() && !PSBTInputSigned(input)) { + input_analysis.is_final = false; + all_final = false; + + // Figure out what is missing + SignatureData outdata; + bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, &outdata); + + // Things are missing + if (!complete) { + input_analysis.missing_pubkeys = outdata.missing_pubkeys; + input_analysis.missing_redeem_script = outdata.missing_redeem_script; + input_analysis.missing_witness_script = outdata.missing_witness_script; + input_analysis.missing_sigs = outdata.missing_sigs; + + // If we are only missing signatures and nothing else, then next is signer + if (outdata.missing_pubkeys.empty() && outdata.missing_redeem_script.IsNull() && outdata.missing_witness_script.IsNull() && !outdata.missing_sigs.empty()) { + input_analysis.next = PSBTRole::SIGNER; + } else { + only_missing_sigs = false; + input_analysis.next = PSBTRole::UPDATER; + } + } else { + only_missing_final = true; + input_analysis.next = PSBTRole::FINALIZER; + } + } else if (!utxo.IsNull()){ + input_analysis.is_final = true; + } + } + + if (all_final) { + only_missing_sigs = false; + result.next = PSBTRole::EXTRACTOR; + } + if (calc_fee) { + // Get the output amount + CAmount out_amt = std::accumulate(psbtx.tx->vout.begin(), psbtx.tx->vout.end(), CAmount(0), + [](CAmount a, const CTxOut& b) { + return a += b.nValue; + } + ); + + // Get the fee + CAmount fee = in_amt - out_amt; + result.fee = fee; + + // Estimate the size + CMutableTransaction mtx(*psbtx.tx); + CCoinsView view_dummy; + CCoinsViewCache view(&view_dummy); + bool success = true; + + for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { + PSBTInput& input = psbtx.inputs[i]; + Coin newcoin; + + if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, nullptr, true) || !psbtx.GetInputUTXO(newcoin.out, i)) { + success = false; + break; + } else { + mtx.vin[i].scriptSig = input.final_script_sig; + mtx.vin[i].scriptWitness = input.final_script_witness; + newcoin.nHeight = 1; + view.AddCoin(psbtx.tx->vin[i].prevout, std::move(newcoin), true); + } + } + + if (success) { + CTransaction ctx = CTransaction(mtx); + size_t size = GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS)); + result.estimated_vsize = size; + // Estimate fee rate + CFeeRate feerate(fee, size); + result.estimated_feerate = feerate; + } + + if (only_missing_sigs) { + result.next = PSBTRole::SIGNER; + } else if (only_missing_final) { + result.next = PSBTRole::FINALIZER; + } else if (all_final) { + result.next = PSBTRole::EXTRACTOR; + } else { + result.next = PSBTRole::UPDATER; + } + } else { + result.next = PSBTRole::UPDATER; + } + + return result; +} diff --git a/src/node/psbt.h b/src/node/psbt.h new file mode 100644 index 0000000000..e04366a20f --- /dev/null +++ b/src/node/psbt.h @@ -0,0 +1,43 @@ +// Copyright (c) 2009-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_NODE_PSBT_H +#define BITCOIN_NODE_PSBT_H + +#include <psbt.h> + +/** + * Holds an analysis of one input from a PSBT + */ +struct PSBTInputAnalysis { + bool has_utxo; //!< Whether we have UTXO information for this input + bool is_final; //!< Whether the input has all required information including signatures + PSBTRole next; //!< Which of the BIP 174 roles needs to handle this input next + + std::vector<CKeyID> missing_pubkeys; //!< Pubkeys whose BIP32 derivation path is missing + std::vector<CKeyID> missing_sigs; //!< Pubkeys whose signatures are missing + uint160 missing_redeem_script; //!< Hash160 of redeem script, if missing + uint256 missing_witness_script; //!< SHA256 of witness script, if missing +}; + +/** + * Holds the results of AnalyzePSBT (miscellaneous information about a PSBT) + */ +struct PSBTAnalysis { + Optional<size_t> estimated_vsize; //!< Estimated weight of the transaction + Optional<CFeeRate> estimated_feerate; //!< Estimated feerate (fee / weight) of the transaction + Optional<CAmount> fee; //!< Amount of fee being paid by the transaction + std::vector<PSBTInputAnalysis> inputs; //!< More information about the individual inputs of the transaction + PSBTRole next; //!< Which of the BIP 174 roles needs to handle the transaction next +}; + +/** + * Provides helpful miscellaneous information about where a PSBT is in the signing workflow. + * + * @param[in] psbtx the PSBT to analyze + * @return A PSBTAnalysis with information about the provided PSBT. + */ +PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx); + +#endif // BITCOIN_NODE_PSBT_H diff --git a/src/psbt.cpp b/src/psbt.cpp index 184129e330..f31f2af0d1 100644 --- a/src/psbt.cpp +++ b/src/psbt.cpp @@ -340,129 +340,6 @@ std::string PSBTRoleName(PSBTRole role) { } } -PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx) -{ - // Go through each input and build status - PSBTAnalysis result; - - bool calc_fee = true; - bool all_final = true; - bool only_missing_sigs = true; - bool only_missing_final = false; - CAmount in_amt = 0; - - result.inputs.resize(psbtx.tx->vin.size()); - - for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { - PSBTInput& input = psbtx.inputs[i]; - PSBTInputAnalysis& input_analysis = result.inputs[i]; - - // Check for a UTXO - CTxOut utxo; - if (psbtx.GetInputUTXO(utxo, i)) { - in_amt += utxo.nValue; - input_analysis.has_utxo = true; - } else { - input_analysis.has_utxo = false; - input_analysis.is_final = false; - input_analysis.next = PSBTRole::UPDATER; - calc_fee = false; - } - - // Check if it is final - if (!utxo.IsNull() && !PSBTInputSigned(input)) { - input_analysis.is_final = false; - all_final = false; - - // Figure out what is missing - SignatureData outdata; - bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, &outdata); - - // Things are missing - if (!complete) { - input_analysis.missing_pubkeys = outdata.missing_pubkeys; - input_analysis.missing_redeem_script = outdata.missing_redeem_script; - input_analysis.missing_witness_script = outdata.missing_witness_script; - input_analysis.missing_sigs = outdata.missing_sigs; - - // If we are only missing signatures and nothing else, then next is signer - if (outdata.missing_pubkeys.empty() && outdata.missing_redeem_script.IsNull() && outdata.missing_witness_script.IsNull() && !outdata.missing_sigs.empty()) { - input_analysis.next = PSBTRole::SIGNER; - } else { - only_missing_sigs = false; - input_analysis.next = PSBTRole::UPDATER; - } - } else { - only_missing_final = true; - input_analysis.next = PSBTRole::FINALIZER; - } - } else if (!utxo.IsNull()){ - input_analysis.is_final = true; - } - } - - if (all_final) { - only_missing_sigs = false; - result.next = PSBTRole::EXTRACTOR; - } - if (calc_fee) { - // Get the output amount - CAmount out_amt = std::accumulate(psbtx.tx->vout.begin(), psbtx.tx->vout.end(), CAmount(0), - [](CAmount a, const CTxOut& b) { - return a += b.nValue; - } - ); - - // Get the fee - CAmount fee = in_amt - out_amt; - result.fee = fee; - - // Estimate the size - CMutableTransaction mtx(*psbtx.tx); - CCoinsView view_dummy; - CCoinsViewCache view(&view_dummy); - bool success = true; - - for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { - PSBTInput& input = psbtx.inputs[i]; - Coin newcoin; - - if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, nullptr, true) || !psbtx.GetInputUTXO(newcoin.out, i)) { - success = false; - break; - } else { - mtx.vin[i].scriptSig = input.final_script_sig; - mtx.vin[i].scriptWitness = input.final_script_witness; - newcoin.nHeight = 1; - view.AddCoin(psbtx.tx->vin[i].prevout, std::move(newcoin), true); - } - } - - if (success) { - CTransaction ctx = CTransaction(mtx); - size_t size = GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS)); - result.estimated_vsize = size; - // Estimate fee rate - CFeeRate feerate(fee, size); - result.estimated_feerate = feerate; - } - - if (only_missing_sigs) { - result.next = PSBTRole::SIGNER; - } else if (only_missing_final) { - result.next = PSBTRole::FINALIZER; - } else if (all_final) { - result.next = PSBTRole::EXTRACTOR; - } else { - result.next = PSBTRole::UPDATER; - } - } else { - result.next = PSBTRole::UPDATER; - } - - return result; -} - bool DecodeBase64PSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error) { bool invalid; diff --git a/src/psbt.h b/src/psbt.h index fcb3337a53..1bc1e91a84 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -557,31 +557,6 @@ enum class PSBTRole { EXTRACTOR }; -/** - * Holds an analysis of one input from a PSBT - */ -struct PSBTInputAnalysis { - bool has_utxo; //!< Whether we have UTXO information for this input - bool is_final; //!< Whether the input has all required information including signatures - PSBTRole next; //!< Which of the BIP 174 roles needs to handle this input next - - std::vector<CKeyID> missing_pubkeys; //!< Pubkeys whose BIP32 derivation path is missing - std::vector<CKeyID> missing_sigs; //!< Pubkeys whose signatures are missing - uint160 missing_redeem_script; //!< Hash160 of redeem script, if missing - uint256 missing_witness_script; //!< SHA256 of witness script, if missing -}; - -/** - * Holds the results of AnalyzePSBT (miscellaneous information about a PSBT) - */ -struct PSBTAnalysis { - Optional<size_t> estimated_vsize; //!< Estimated weight of the transaction - Optional<CFeeRate> estimated_feerate; //!< Estimated feerate (fee / weight) of the transaction - Optional<CAmount> fee; //!< Amount of fee being paid by the transaction - std::vector<PSBTInputAnalysis> inputs; //!< More information about the individual inputs of the transaction - PSBTRole next; //!< Which of the BIP 174 roles needs to handle the transaction next -}; - std::string PSBTRoleName(PSBTRole role); /** Checks whether a PSBTInput is already signed. */ @@ -616,14 +591,6 @@ bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransacti */ NODISCARD TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector<PartiallySignedTransaction>& psbtxs); -/** - * Provides helpful miscellaneous information about where a PSBT is in the signing workflow. - * - * @param[in] psbtx the PSBT to analyze - * @return A PSBTAnalysis with information about the provided PSBT. - */ -PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx); - //! Decode a base64ed PSBT into a PartiallySignedTransaction NODISCARD bool DecodeBase64PSBT(PartiallySignedTransaction& decoded_psbt, const std::string& base64_psbt, std::string& error); //! Decode a raw (binary blob) PSBT into a PartiallySignedTransaction diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 4da952060e..8d15ecc28e 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -15,6 +15,7 @@ #include <key_io.h> #include <keystore.h> #include <merkleblock.h> +#include <node/psbt.h> #include <node/transaction.h> #include <policy/policy.h> #include <policy/rbf.h> |