Changeset View
Changeset View
Standalone View
Standalone View
src/node/psbt.cpp
Show All 12 Lines | |||||
#include <numeric> | #include <numeric> | ||||
namespace node { | namespace node { | ||||
PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx) { | PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx) { | ||||
// Go through each input and build status | // Go through each input and build status | ||||
PSBTAnalysis result; | PSBTAnalysis result; | ||||
bool calc_fee = true; | bool calc_fee = true; | ||||
bool all_final = true; | |||||
bool only_missing_sigs = true; | |||||
bool only_missing_final = false; | |||||
Amount in_amt{Amount::zero()}; | Amount in_amt{Amount::zero()}; | ||||
result.inputs.resize(psbtx.tx->vin.size()); | result.inputs.resize(psbtx.tx->vin.size()); | ||||
for (size_t i = 0; i < psbtx.tx->vin.size(); ++i) { | for (size_t i = 0; i < psbtx.tx->vin.size(); ++i) { | ||||
PSBTInput &input = psbtx.inputs[i]; | PSBTInput &input = psbtx.inputs[i]; | ||||
PSBTInputAnalysis &input_analysis = result.inputs[i]; | PSBTInputAnalysis &input_analysis = result.inputs[i]; | ||||
// We set next role here and ratchet backwards as required | |||||
input_analysis.next = PSBTRole::EXTRACTOR; | |||||
// Check for a UTXO | // Check for a UTXO | ||||
CTxOut utxo; | CTxOut utxo; | ||||
if (psbtx.GetInputUTXO(utxo, i)) { | if (psbtx.GetInputUTXO(utxo, i)) { | ||||
if (!MoneyRange(utxo.nValue) || !MoneyRange(in_amt + utxo.nValue)) { | if (!MoneyRange(utxo.nValue) || !MoneyRange(in_amt + utxo.nValue)) { | ||||
result.SetInvalid(strprintf( | result.SetInvalid(strprintf( | ||||
"PSBT is not valid. Input %u has invalid value", i)); | "PSBT is not valid. Input %u has invalid value", i)); | ||||
return result; | return result; | ||||
} | } | ||||
Show All 10 Lines | for (size_t i = 0; i < psbtx.tx->vin.size(); ++i) { | ||||
result.SetInvalid(strprintf( | result.SetInvalid(strprintf( | ||||
"PSBT is not valid. Input %u spends unspendable output", i)); | "PSBT is not valid. Input %u spends unspendable output", i)); | ||||
return result; | return result; | ||||
} | } | ||||
// Check if it is final | // Check if it is final | ||||
if (!utxo.IsNull() && !PSBTInputSigned(input)) { | if (!utxo.IsNull() && !PSBTInputSigned(input)) { | ||||
input_analysis.is_final = false; | input_analysis.is_final = false; | ||||
all_final = false; | |||||
// Figure out what is missing | // Figure out what is missing | ||||
SignatureData outdata; | SignatureData outdata; | ||||
bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, | bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, | ||||
SigHashType().withForkId(), &outdata); | SigHashType().withForkId(), &outdata); | ||||
// Things are missing | // Things are missing | ||||
if (!complete) { | if (!complete) { | ||||
input_analysis.missing_pubkeys = outdata.missing_pubkeys; | input_analysis.missing_pubkeys = outdata.missing_pubkeys; | ||||
input_analysis.missing_redeem_script = | input_analysis.missing_redeem_script = | ||||
outdata.missing_redeem_script; | outdata.missing_redeem_script; | ||||
input_analysis.missing_sigs = outdata.missing_sigs; | input_analysis.missing_sigs = outdata.missing_sigs; | ||||
// If we are only missing signatures and nothing else, then next | // If we are only missing signatures and nothing else, then next | ||||
// is signer | // is signer | ||||
if (outdata.missing_pubkeys.empty() && | if (outdata.missing_pubkeys.empty() && | ||||
outdata.missing_redeem_script.IsNull() && | outdata.missing_redeem_script.IsNull() && | ||||
!outdata.missing_sigs.empty()) { | !outdata.missing_sigs.empty()) { | ||||
input_analysis.next = PSBTRole::SIGNER; | input_analysis.next = PSBTRole::SIGNER; | ||||
} else { | } else { | ||||
only_missing_sigs = false; | |||||
input_analysis.next = PSBTRole::UPDATER; | input_analysis.next = PSBTRole::UPDATER; | ||||
} | } | ||||
} else { | } else { | ||||
only_missing_final = true; | |||||
input_analysis.next = PSBTRole::FINALIZER; | input_analysis.next = PSBTRole::FINALIZER; | ||||
} | } | ||||
} else if (!utxo.IsNull()) { | } else if (!utxo.IsNull()) { | ||||
input_analysis.is_final = true; | input_analysis.is_final = true; | ||||
} | } | ||||
} | } | ||||
if (all_final) { | // Calculate next role for PSBT by grabbing "minumum" PSBTInput next role | ||||
only_missing_sigs = false; | |||||
result.next = PSBTRole::EXTRACTOR; | result.next = PSBTRole::EXTRACTOR; | ||||
for (size_t i = 0; i < psbtx.tx->vin.size(); ++i) { | |||||
PSBTInputAnalysis &input_analysis = result.inputs[i]; | |||||
result.next = std::min(result.next, input_analysis.next); | |||||
} | } | ||||
assert(result.next > PSBTRole::CREATOR); | |||||
if (calc_fee) { | if (calc_fee) { | ||||
// Get the output amount | // Get the output amount | ||||
Amount out_amt = | Amount out_amt = | ||||
std::accumulate(psbtx.tx->vout.begin(), psbtx.tx->vout.end(), | std::accumulate(psbtx.tx->vout.begin(), psbtx.tx->vout.end(), | ||||
Amount::zero(), [](Amount a, const CTxOut &b) { | Amount::zero(), [](Amount a, const CTxOut &b) { | ||||
if (!MoneyRange(a) || !MoneyRange(b.nValue) || | if (!MoneyRange(a) || !MoneyRange(b.nValue) || | ||||
!MoneyRange(a + b.nValue)) { | !MoneyRange(a + b.nValue)) { | ||||
return -1 * SATOSHI; | return -1 * SATOSHI; | ||||
Show All 35 Lines | if (calc_fee) { | ||||
if (success) { | if (success) { | ||||
CTransaction ctx = CTransaction(mtx); | CTransaction ctx = CTransaction(mtx); | ||||
size_t size = ctx.GetTotalSize(); | size_t size = ctx.GetTotalSize(); | ||||
result.estimated_vsize = size; | result.estimated_vsize = size; | ||||
// Estimate fee rate | // Estimate fee rate | ||||
CFeeRate feerate(fee, size); | CFeeRate feerate(fee, size); | ||||
result.estimated_feerate = feerate; | 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; | return result; | ||||
} | } | ||||
} // namespace node | } // namespace node |