Changeset View
Changeset View
Standalone View
Standalone View
src/rpc/rawtransaction.cpp
Show All 30 Lines | |||||
#include <txmempool.h> | #include <txmempool.h> | ||||
#include <uint256.h> | #include <uint256.h> | ||||
#include <util/error.h> | #include <util/error.h> | ||||
#include <util/strencodings.h> | #include <util/strencodings.h> | ||||
#include <validation.h> | #include <validation.h> | ||||
#include <validationinterface.h> | #include <validationinterface.h> | ||||
#include <cstdint> | #include <cstdint> | ||||
#include <numeric> | |||||
#include <univalue.h> | #include <univalue.h> | ||||
static void TxToJSON(const CTransaction &tx, const BlockHash &hashBlock, | static void TxToJSON(const CTransaction &tx, const BlockHash &hashBlock, | ||||
UniValue &entry) { | UniValue &entry) { | ||||
// Call into TxToUniv() in bitcoin-common to decode the transaction hex. | // Call into TxToUniv() in bitcoin-common to decode the transaction hex. | ||||
// | // | ||||
// Blockchain contextual information (confirmations and blocktime) is not | // Blockchain contextual information (confirmations and blocktime) is not | ||||
▲ Show 20 Lines • Show All 1,844 Lines • ▼ Show 20 Lines | UniValue analyzepsbt(const Config &config, const JSONRPCRequest &request) { | ||||
// Unserialize the transaction | // Unserialize the transaction | ||||
PartiallySignedTransaction psbtx; | PartiallySignedTransaction psbtx; | ||||
std::string error; | std::string error; | ||||
if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) { | if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) { | ||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, | ||||
strprintf("TX decode failed %s", error)); | strprintf("TX decode failed %s", error)); | ||||
} | } | ||||
// Go through each input and build status | PSBTAnalysis psbta = AnalyzePSBT(psbtx); | ||||
UniValue result(UniValue::VOBJ); | UniValue result(UniValue::VOBJ); | ||||
UniValue inputs_result(UniValue::VARR); | UniValue inputs_result(UniValue::VARR); | ||||
bool calc_fee = true; | for (const auto &input : psbta.inputs) { | ||||
bool all_final = true; | |||||
bool only_missing_sigs = true; | |||||
bool only_missing_final = false; | |||||
Amount in_amt{Amount::zero()}; | |||||
for (size_t i = 0; i < psbtx.tx->vin.size(); ++i) { | |||||
PSBTInput &input = psbtx.inputs[i]; | |||||
UniValue input_univ(UniValue::VOBJ); | UniValue input_univ(UniValue::VOBJ); | ||||
UniValue missing(UniValue::VOBJ); | UniValue missing(UniValue::VOBJ); | ||||
// Check for a UTXO | input_univ.pushKV("has_utxo", input.has_utxo); | ||||
CTxOut utxo; | input_univ.pushKV("is_final", input.is_final); | ||||
if (psbtx.GetInputUTXO(utxo, i)) { | input_univ.pushKV("next", PSBTRoleName(input.next)); | ||||
in_amt += utxo.nValue; | |||||
input_univ.pushKV("has_utxo", true); | if (!input.missing_pubkeys.empty()) { | ||||
} else { | |||||
input_univ.pushKV("has_utxo", false); | |||||
input_univ.pushKV("is_final", false); | |||||
input_univ.pushKV("next", "updater"); | |||||
calc_fee = false; | |||||
} | |||||
// Check if it is final | |||||
if (!utxo.IsNull() && !PSBTInputSigned(input)) { | |||||
input_univ.pushKV("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) { | |||||
if (!outdata.missing_pubkeys.empty()) { | |||||
// Missing pubkeys | |||||
UniValue missing_pubkeys_univ(UniValue::VARR); | UniValue missing_pubkeys_univ(UniValue::VARR); | ||||
for (const CKeyID &pubkey : outdata.missing_pubkeys) { | for (const CKeyID &pubkey : input.missing_pubkeys) { | ||||
missing_pubkeys_univ.push_back(HexStr(pubkey)); | missing_pubkeys_univ.push_back(HexStr(pubkey)); | ||||
} | } | ||||
missing.pushKV("pubkeys", missing_pubkeys_univ); | missing.pushKV("pubkeys", missing_pubkeys_univ); | ||||
} | } | ||||
if (!outdata.missing_redeem_script.IsNull()) { | if (!input.missing_redeem_script.IsNull()) { | ||||
// Missing redeemScript | missing.pushKV("redeemscript", HexStr(input.missing_redeem_script)); | ||||
missing.pushKV("redeemscript", | |||||
HexStr(outdata.missing_redeem_script)); | |||||
} | } | ||||
if (!outdata.missing_sigs.empty()) { | if (!input.missing_sigs.empty()) { | ||||
// Missing sigs | |||||
UniValue missing_sigs_univ(UniValue::VARR); | UniValue missing_sigs_univ(UniValue::VARR); | ||||
for (const CKeyID &pubkey : outdata.missing_sigs) { | for (const CKeyID &pubkey : input.missing_sigs) { | ||||
missing_sigs_univ.push_back(HexStr(pubkey)); | missing_sigs_univ.push_back(HexStr(pubkey)); | ||||
} | } | ||||
missing.pushKV("signatures", missing_sigs_univ); | missing.pushKV("signatures", missing_sigs_univ); | ||||
} | } | ||||
if (!missing.getKeys().empty()) { | |||||
input_univ.pushKV("missing", missing); | input_univ.pushKV("missing", missing); | ||||
// 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_univ.pushKV("next", "signer"); | |||||
} else { | |||||
only_missing_sigs = false; | |||||
input_univ.pushKV("next", "updater"); | |||||
} | |||||
} else { | |||||
only_missing_final = true; | |||||
input_univ.pushKV("next", "finalizer"); | |||||
} | |||||
} else if (!utxo.IsNull()) { | |||||
input_univ.pushKV("is_final", true); | |||||
} | } | ||||
inputs_result.push_back(input_univ); | inputs_result.push_back(input_univ); | ||||
} | } | ||||
result.pushKV("inputs", inputs_result); | result.pushKV("inputs", inputs_result); | ||||
if (all_final) { | if (psbta.estimated_vsize != nullopt) { | ||||
only_missing_sigs = false; | result.pushKV("estimated_vsize", (int)*psbta.estimated_vsize); | ||||
result.pushKV("next", "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; | |||||
// 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]; | |||||
if (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, | |||||
SigHashType().withForkId(), nullptr, true)) { | |||||
mtx.vin[i].scriptSig = input.final_script_sig; | |||||
CTxOut newUtxo; | |||||
if (!psbtx.GetInputUTXO(newUtxo, i)) { | |||||
success = false; | |||||
break; | |||||
} | } | ||||
view.AddCoin(psbtx.tx->vin[i].prevout, Coin(newUtxo, 1, false), | if (psbta.estimated_feerate != nullopt) { | ||||
true); | result.pushKV("estimated_feerate", | ||||
} else { | ValueFromAmount(psbta.estimated_feerate->GetFeePerK())); | ||||
success = false; | |||||
break; | |||||
} | } | ||||
if (psbta.fee != nullopt) { | |||||
result.pushKV("fee", ValueFromAmount(*psbta.fee)); | |||||
} | } | ||||
result.pushKV("next", PSBTRoleName(psbta.next)); | |||||
if (success) { | |||||
CTransaction ctx = CTransaction(mtx); | |||||
size_t size = ctx.GetTotalSize(); | |||||
result.pushKV("estimated_vsize", uint64_t(size)); | |||||
// Estimate fee rate | |||||
CFeeRate feerate(fee, size); | |||||
result.pushKV("estimated_feerate", feerate.ToString()); | |||||
} | |||||
result.pushKV("fee", ValueFromAmount(fee)); | |||||
if (only_missing_sigs) { | |||||
result.pushKV("next", "signer"); | |||||
} else if (only_missing_final) { | |||||
result.pushKV("next", "finalizer"); | |||||
} else if (all_final) { | |||||
result.pushKV("next", "extractor"); | |||||
} else { | |||||
result.pushKV("next", "updater"); | |||||
} | |||||
} else { | |||||
result.pushKV("next", "updater"); | |||||
} | |||||
return result; | return result; | ||||
} | } | ||||
// clang-format off | // clang-format off | ||||
static const CRPCCommand commands[] = { | static const CRPCCommand commands[] = { | ||||
// category name actor (function) argNames | // category name actor (function) argNames | ||||
// ------------------- ------------------------ ---------------------- ---------- | // ------------------- ------------------------ ---------------------- ---------- | ||||
{ "rawtransactions", "getrawtransaction", getrawtransaction, {"txid","verbose","blockhash"} }, | { "rawtransactions", "getrawtransaction", getrawtransaction, {"txid","verbose","blockhash"} }, | ||||
Show All 25 Lines |