Changeset View
Changeset View
Standalone View
Standalone View
src/psbt.cpp
// Copyright (c) 2009-2018 The Bitcoin Core developers | // Copyright (c) 2009-2018 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include <coins.h> | |||||
#include <consensus/tx_verify.h> | |||||
#include <policy/policy.h> | |||||
#include <psbt.h> | #include <psbt.h> | ||||
#include <util/strencodings.h> | #include <util/strencodings.h> | ||||
#include <numeric> | |||||
PartiallySignedTransaction::PartiallySignedTransaction( | PartiallySignedTransaction::PartiallySignedTransaction( | ||||
const CMutableTransaction &txIn) | const CMutableTransaction &txIn) | ||||
: tx(txIn) { | : tx(txIn) { | ||||
inputs.resize(txIn.vin.size()); | inputs.resize(txIn.vin.size()); | ||||
outputs.resize(txIn.vout.size()); | outputs.resize(txIn.vout.size()); | ||||
} | } | ||||
bool PartiallySignedTransaction::IsNull() const { | bool PartiallySignedTransaction::IsNull() const { | ||||
▲ Show 20 Lines • Show All 147 Lines • ▼ Show 20 Lines | void PSBTOutput::Merge(const PSBTOutput &output) { | ||||
hd_keypaths.insert(output.hd_keypaths.begin(), output.hd_keypaths.end()); | hd_keypaths.insert(output.hd_keypaths.begin(), output.hd_keypaths.end()); | ||||
unknown.insert(output.unknown.begin(), output.unknown.end()); | unknown.insert(output.unknown.begin(), output.unknown.end()); | ||||
if (redeem_script.empty() && !output.redeem_script.empty()) { | if (redeem_script.empty() && !output.redeem_script.empty()) { | ||||
redeem_script = output.redeem_script; | redeem_script = output.redeem_script; | ||||
} | } | ||||
} | } | ||||
bool PSBTInputSigned(PSBTInput &input) { | bool PSBTInputSigned(const PSBTInput &input) { | ||||
return !input.final_script_sig.empty(); | return !input.final_script_sig.empty(); | ||||
} | } | ||||
bool SignPSBTInput(const SigningProvider &provider, | bool SignPSBTInput(const SigningProvider &provider, | ||||
PartiallySignedTransaction &psbt, int index, | PartiallySignedTransaction &psbt, int index, | ||||
SigHashType sighash, SignatureData *out_sigdata, | SigHashType sighash, SignatureData *out_sigdata, | ||||
bool use_dummy) { | bool use_dummy) { | ||||
PSBTInput &input = psbt.inputs.at(index); | PSBTInput &input = psbt.inputs.at(index); | ||||
▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | CombinePSBTs(PartiallySignedTransaction &out, | ||||
} | } | ||||
if (!out.IsSane()) { | if (!out.IsSane()) { | ||||
return TransactionError::INVALID_PSBT; | return TransactionError::INVALID_PSBT; | ||||
} | } | ||||
return TransactionError::OK; | return TransactionError::OK; | ||||
} | } | ||||
std::string PSBTRoleName(PSBTRole role) { | |||||
switch (role) { | |||||
case PSBTRole::UPDATER: | |||||
return "updater"; | |||||
case PSBTRole::SIGNER: | |||||
return "signer"; | |||||
case PSBTRole::FINALIZER: | |||||
return "finalizer"; | |||||
case PSBTRole::EXTRACTOR: | |||||
return "extractor"; | |||||
} | |||||
} | |||||
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; | |||||
Amount in_amt{Amount::zero()}; | |||||
result.inputs.resize(psbtx.tx->vin.size()); | |||||
for (size_t 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, | |||||
SigHashType().withForkId(), &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_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_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 | |||||
Amount out_amt = std::accumulate( | |||||
psbtx.tx->vout.begin(), psbtx.tx->vout.end(), Amount::zero(), | |||||
[](Amount a, const CTxOut &b) { return a += b.nValue; }); | |||||
// Get the fee | |||||
Amount 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 (size_t i = 0; i < psbtx.tx->vin.size(); ++i) { | |||||
PSBTInput &input = psbtx.inputs[i]; | |||||
CTxOut newUtxo; | |||||
if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, | |||||
SigHashType().withForkId(), nullptr, true) || | |||||
!psbtx.GetInputUTXO(newUtxo, i)) { | |||||
success = false; | |||||
break; | |||||
} else { | |||||
mtx.vin[i].scriptSig = input.final_script_sig; | |||||
view.AddCoin(psbtx.tx->vin[i].prevout, Coin(newUtxo, 1, false), | |||||
true); | |||||
} | |||||
} | |||||
if (success) { | |||||
CTransaction ctx = CTransaction(mtx); | |||||
size_t size = ctx.GetTotalSize(); | |||||
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, | bool DecodeBase64PSBT(PartiallySignedTransaction &psbt, | ||||
const std::string &base64_tx, std::string &error) { | const std::string &base64_tx, std::string &error) { | ||||
bool invalid; | bool invalid; | ||||
std::string tx_data = DecodeBase64(base64_tx, &invalid); | std::string tx_data = DecodeBase64(base64_tx, &invalid); | ||||
if (invalid) { | if (invalid) { | ||||
error = "invalid base64"; | error = "invalid base64"; | ||||
return false; | return false; | ||||
} | } | ||||
Show All 19 Lines |