[build] Move AnalyzePSBT from psbt.cpp to node/psbt.cpp
psbt.cpp definitions except for AnalyzePSBT are used by the wallet and need to be linked into the wallet binary. AnalyzePSBT is an exception in that it is not used by the wallet, and depends on node classes like CCoinsViewCache, and on node global variables like nBytesPerSigOp. So AnalyzePSBT is more at home in libbitcoin_server than libbitcoin_common, and in any case needs to be defined in a separate object file than other PSBT utilities, to avoid dragging link dependencies on node functions and global variables into the wallet.
This commit is contained in:
parent
fd509bd1f7
commit
4d074e84a2
6 changed files with 180 additions and 156 deletions
|
@ -156,6 +156,7 @@ BITCOIN_CORE_H = \
|
||||||
netbase.h \
|
netbase.h \
|
||||||
netmessagemaker.h \
|
netmessagemaker.h \
|
||||||
node/coin.h \
|
node/coin.h \
|
||||||
|
node/psbt.h \
|
||||||
node/transaction.h \
|
node/transaction.h \
|
||||||
noui.h \
|
noui.h \
|
||||||
optional.h \
|
optional.h \
|
||||||
|
@ -272,6 +273,7 @@ libbitcoin_server_a_SOURCES = \
|
||||||
net.cpp \
|
net.cpp \
|
||||||
net_processing.cpp \
|
net_processing.cpp \
|
||||||
node/coin.cpp \
|
node/coin.cpp \
|
||||||
|
node/psbt.cpp \
|
||||||
node/transaction.cpp \
|
node/transaction.cpp \
|
||||||
noui.cpp \
|
noui.cpp \
|
||||||
policy/fees.cpp \
|
policy/fees.cpp \
|
||||||
|
|
134
src/node/psbt.cpp
Normal file
134
src/node/psbt.cpp
Normal file
|
@ -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;
|
||||||
|
}
|
43
src/node/psbt.h
Normal file
43
src/node/psbt.h
Normal file
|
@ -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
|
123
src/psbt.cpp
123
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 DecodeBase64PSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error)
|
||||||
{
|
{
|
||||||
bool invalid;
|
bool invalid;
|
||||||
|
|
33
src/psbt.h
33
src/psbt.h
|
@ -557,31 +557,6 @@ enum class PSBTRole {
|
||||||
EXTRACTOR
|
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);
|
std::string PSBTRoleName(PSBTRole role);
|
||||||
|
|
||||||
/** Checks whether a PSBTInput is already signed. */
|
/** 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);
|
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
|
//! Decode a base64ed PSBT into a PartiallySignedTransaction
|
||||||
NODISCARD bool DecodeBase64PSBT(PartiallySignedTransaction& decoded_psbt, const std::string& base64_psbt, std::string& error);
|
NODISCARD bool DecodeBase64PSBT(PartiallySignedTransaction& decoded_psbt, const std::string& base64_psbt, std::string& error);
|
||||||
//! Decode a raw (binary blob) PSBT into a PartiallySignedTransaction
|
//! Decode a raw (binary blob) PSBT into a PartiallySignedTransaction
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <key_io.h>
|
#include <key_io.h>
|
||||||
#include <keystore.h>
|
#include <keystore.h>
|
||||||
#include <merkleblock.h>
|
#include <merkleblock.h>
|
||||||
|
#include <node/psbt.h>
|
||||||
#include <node/transaction.h>
|
#include <node/transaction.h>
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
#include <policy/rbf.h>
|
#include <policy/rbf.h>
|
||||||
|
|
Loading…
Reference in a new issue