mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-11-11 14:38:29 +01:00
Merge #15508: Refactor analyzepsbt for use outside RPC code
892eff05f1Add documentation of struct PSBTAnalysis et al (Glenn Willen)ef22fe8c1fRefactor analyzepsbt for use outside RPC code (Glenn Willen)afd20a25f2Move PSBT decoding functions from core_io to psbt.cpp (Glenn Willen) Pull request description: Refactor the analyzepsbt RPC into (1) an AnalyzePSBT function, which returns its output as a new strongly-typed PSBTAnalysis struct, and (2) a thin wrapper which converts the struct into a UniValue for RPC use. ---- As with my previous refactoring PR, I need this because I am creating a dependency on this code from the GUI. Per discussion in #bitcoin-core-dev on IRC, since we don't want to create a dependency on UniValue in anything outside RPC, I introduced some new structs to hold the info we get when analyzing a PSBT. For the field types, I used whatever types are already used internally for this data (e.g. CAmount, CFeeRate, CKeyID), and only convert to int/string etc. in the wrapper. @achow101, maybe take the first look? :-) ACKs for commit 892eff: sipa: utACK892eff05f1achow101: utACK892eff05f1ryanofsky: utACK892eff05f1. Just small cleanups since the last review: removing unneeded include, forward decl, adding const ref Tree-SHA512: eb278b0a82717ebc3eb0c08dc5bb4eefb996a317a6a3a8ecf51cd88110ddbb188ad3482cdd9563e557995e73aca5a282c1f6e352bc598155f1203b7b46fe5dee
This commit is contained in:
@@ -1943,148 +1943,56 @@ UniValue analyzepsbt(const JSONRPCRequest& request)
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error));
|
||||
}
|
||||
|
||||
// Go through each input and build status
|
||||
PSBTAnalysis psbta = AnalyzePSBT(psbtx);
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
UniValue inputs_result(UniValue::VARR);
|
||||
bool calc_fee = true;
|
||||
bool all_final = true;
|
||||
bool only_missing_sigs = true;
|
||||
bool only_missing_final = false;
|
||||
CAmount in_amt = 0;
|
||||
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
|
||||
PSBTInput& input = psbtx.inputs[i];
|
||||
for (const auto& input : psbta.inputs) {
|
||||
UniValue input_univ(UniValue::VOBJ);
|
||||
UniValue missing(UniValue::VOBJ);
|
||||
|
||||
// Check for a UTXO
|
||||
CTxOut utxo;
|
||||
if (psbtx.GetInputUTXO(utxo, i)) {
|
||||
in_amt += utxo.nValue;
|
||||
input_univ.pushKV("has_utxo", true);
|
||||
} else {
|
||||
input_univ.pushKV("has_utxo", false);
|
||||
input_univ.pushKV("is_final", false);
|
||||
input_univ.pushKV("next", "updater");
|
||||
calc_fee = false;
|
||||
}
|
||||
input_univ.pushKV("has_utxo", input.has_utxo);
|
||||
input_univ.pushKV("is_final", input.is_final);
|
||||
input_univ.pushKV("next", PSBTRoleName(input.next));
|
||||
|
||||
// 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, 1, &outdata);
|
||||
|
||||
// Things are missing
|
||||
if (!complete) {
|
||||
if (!outdata.missing_pubkeys.empty()) {
|
||||
// Missing pubkeys
|
||||
UniValue missing_pubkeys_univ(UniValue::VARR);
|
||||
for (const CKeyID& pubkey : outdata.missing_pubkeys) {
|
||||
missing_pubkeys_univ.push_back(HexStr(pubkey));
|
||||
}
|
||||
missing.pushKV("pubkeys", missing_pubkeys_univ);
|
||||
}
|
||||
if (!outdata.missing_redeem_script.IsNull()) {
|
||||
// Missing redeemScript
|
||||
missing.pushKV("redeemscript", HexStr(outdata.missing_redeem_script));
|
||||
}
|
||||
if (!outdata.missing_witness_script.IsNull()) {
|
||||
// Missing witnessScript
|
||||
missing.pushKV("witnessscript", HexStr(outdata.missing_witness_script));
|
||||
}
|
||||
if (!outdata.missing_sigs.empty()) {
|
||||
// Missing sigs
|
||||
UniValue missing_sigs_univ(UniValue::VARR);
|
||||
for (const CKeyID& pubkey : outdata.missing_sigs) {
|
||||
missing_sigs_univ.push_back(HexStr(pubkey));
|
||||
}
|
||||
missing.pushKV("signatures", missing_sigs_univ);
|
||||
}
|
||||
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_witness_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");
|
||||
if (!input.missing_pubkeys.empty()) {
|
||||
UniValue missing_pubkeys_univ(UniValue::VARR);
|
||||
for (const CKeyID& pubkey : input.missing_pubkeys) {
|
||||
missing_pubkeys_univ.push_back(HexStr(pubkey));
|
||||
}
|
||||
} else if (!utxo.IsNull()){
|
||||
input_univ.pushKV("is_final", true);
|
||||
missing.pushKV("pubkeys", missing_pubkeys_univ);
|
||||
}
|
||||
if (!input.missing_redeem_script.IsNull()) {
|
||||
missing.pushKV("redeemscript", HexStr(input.missing_redeem_script));
|
||||
}
|
||||
if (!input.missing_witness_script.IsNull()) {
|
||||
missing.pushKV("witnessscript", HexStr(input.missing_witness_script));
|
||||
}
|
||||
if (!input.missing_sigs.empty()) {
|
||||
UniValue missing_sigs_univ(UniValue::VARR);
|
||||
for (const CKeyID& pubkey : input.missing_sigs) {
|
||||
missing_sigs_univ.push_back(HexStr(pubkey));
|
||||
}
|
||||
missing.pushKV("signatures", missing_sigs_univ);
|
||||
}
|
||||
if (!missing.getKeys().empty()) {
|
||||
input_univ.pushKV("missing", missing);
|
||||
}
|
||||
inputs_result.push_back(input_univ);
|
||||
}
|
||||
result.pushKV("inputs", inputs_result);
|
||||
|
||||
if (all_final) {
|
||||
only_missing_sigs = false;
|
||||
result.pushKV("next", "extractor");
|
||||
if (psbta.estimated_vsize != nullopt) {
|
||||
result.pushKV("estimated_vsize", (int)*psbta.estimated_vsize);
|
||||
}
|
||||
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;
|
||||
|
||||
// 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];
|
||||
if (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, nullptr, true)) {
|
||||
mtx.vin[i].scriptSig = input.final_script_sig;
|
||||
mtx.vin[i].scriptWitness = input.final_script_witness;
|
||||
|
||||
Coin newcoin;
|
||||
if (!psbtx.GetInputUTXO(newcoin.out, i)) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
newcoin.nHeight = 1;
|
||||
view.AddCoin(psbtx.tx->vin[i].prevout, std::move(newcoin), true);
|
||||
} else {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
CTransaction ctx = CTransaction(mtx);
|
||||
size_t size = GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS));
|
||||
result.pushKV("estimated_vsize", (int)size);
|
||||
// Estimate fee rate
|
||||
CFeeRate feerate(fee, size);
|
||||
result.pushKV("estimated_feerate", ValueFromAmount(feerate.GetFeePerK()));
|
||||
}
|
||||
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");
|
||||
if (psbta.estimated_feerate != nullopt) {
|
||||
result.pushKV("estimated_feerate", ValueFromAmount(psbta.estimated_feerate->GetFeePerK()));
|
||||
}
|
||||
if (psbta.fee != nullopt) {
|
||||
result.pushKV("fee", ValueFromAmount(*psbta.fee));
|
||||
}
|
||||
result.pushKV("next", PSBTRoleName(psbta.next));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user