diff options
Diffstat (limited to 'src/rpc/rawtransaction.cpp')
-rw-r--r-- | src/rpc/rawtransaction.cpp | 481 |
1 files changed, 60 insertions, 421 deletions
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 5dcd04323e..b3926786db 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -15,12 +15,15 @@ #include <key_io.h> #include <keystore.h> #include <merkleblock.h> +#include <node/coin.h> +#include <node/psbt.h> #include <node/transaction.h> #include <policy/policy.h> #include <policy/rbf.h> +#include <policy/settings.h> #include <primitives/transaction.h> #include <psbt.h> -#include <rpc/rawtransaction.h> +#include <rpc/rawtransaction_util.h> #include <rpc/server.h> #include <rpc/util.h> #include <script/script.h> @@ -61,8 +64,8 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry.pushKV("blockhash", hashBlock.GetHex()); CBlockIndex* pindex = LookupBlockIndex(hashBlock); if (pindex) { - if (chainActive.Contains(pindex)) { - entry.pushKV("confirmations", 1 + chainActive.Height() - pindex->nHeight); + if (::ChainActive().Contains(pindex)) { + entry.pushKV("confirmations", 1 + ::ChainActive().Height() - pindex->nHeight); entry.pushKV("time", pindex->GetBlockTime()); entry.pushKV("blocktime", pindex->GetBlockTime()); } @@ -181,7 +184,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) if (!blockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found"); } - in_active_chain = chainActive.Contains(blockindex); + in_active_chain = ::ChainActive().Contains(blockindex); } bool f_txindex_ready = false; @@ -271,7 +274,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request) for (const auto& tx : setTxids) { const Coin& coin = AccessByTxid(*pcoinsTip, tx); if (!coin.IsSpent()) { - pblockindex = chainActive[coin.nHeight]; + pblockindex = ::ChainActive()[coin.nHeight]; break; } } @@ -345,7 +348,7 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request) LOCK(cs_main); const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash()); - if (!pindex || !chainActive.Contains(pindex) || pindex->nTx == 0) { + if (!pindex || !::ChainActive().Contains(pindex) || pindex->nTx == 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); } @@ -359,119 +362,6 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request) return res; } -CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, const UniValue& rbf) -{ - if (inputs_in.isNull() || outputs_in.isNull()) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); - - UniValue inputs = inputs_in.get_array(); - const bool outputs_is_obj = outputs_in.isObject(); - UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array(); - - CMutableTransaction rawTx; - - if (!locktime.isNull()) { - int64_t nLockTime = locktime.get_int64(); - if (nLockTime < 0 || nLockTime > LOCKTIME_MAX) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range"); - rawTx.nLockTime = nLockTime; - } - - bool rbfOptIn = rbf.isTrue(); - - for (unsigned int idx = 0; idx < inputs.size(); idx++) { - const UniValue& input = inputs[idx]; - const UniValue& o = input.get_obj(); - - uint256 txid = ParseHashO(o, "txid"); - - const UniValue& vout_v = find_value(o, "vout"); - if (!vout_v.isNum()) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); - int nOutput = vout_v.get_int(); - if (nOutput < 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); - - uint32_t nSequence; - if (rbfOptIn) { - nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */ - } else if (rawTx.nLockTime) { - nSequence = CTxIn::SEQUENCE_FINAL - 1; - } else { - nSequence = CTxIn::SEQUENCE_FINAL; - } - - // set the sequence number if passed in the parameters object - const UniValue& sequenceObj = find_value(o, "sequence"); - if (sequenceObj.isNum()) { - int64_t seqNr64 = sequenceObj.get_int64(); - if (seqNr64 < 0 || seqNr64 > CTxIn::SEQUENCE_FINAL) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range"); - } else { - nSequence = (uint32_t)seqNr64; - } - } - - CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); - - rawTx.vin.push_back(in); - } - - if (!outputs_is_obj) { - // Translate array of key-value pairs into dict - UniValue outputs_dict = UniValue(UniValue::VOBJ); - for (size_t i = 0; i < outputs.size(); ++i) { - const UniValue& output = outputs[i]; - if (!output.isObject()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair not an object as expected"); - } - if (output.size() != 1) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair must contain exactly one key"); - } - outputs_dict.pushKVs(output); - } - outputs = std::move(outputs_dict); - } - - // Duplicate checking - std::set<CTxDestination> destinations; - bool has_data{false}; - - for (const std::string& name_ : outputs.getKeys()) { - if (name_ == "data") { - if (has_data) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: data"); - } - has_data = true; - std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data"); - - CTxOut out(0, CScript() << OP_RETURN << data); - rawTx.vout.push_back(out); - } else { - CTxDestination destination = DecodeDestination(name_); - if (!IsValidDestination(destination)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_); - } - - if (!destinations.insert(destination).second) { - throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); - } - - CScript scriptPubKey = GetScriptForDestination(destination); - CAmount nAmount = AmountFromValue(outputs[name_]); - - CTxOut out(nAmount, scriptPubKey); - rawTx.vout.push_back(out); - } - } - - if (!rbf.isNull() && rawTx.vin.size() > 0 && rbfOptIn != SignalsOptInRBF(CTransaction(rawTx))) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option"); - } - - return rawTx; -} - static UniValue createrawtransaction(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) { @@ -682,7 +572,7 @@ static UniValue decodescript(const JSONRPCRequest& request) if (type.isStr() && type.get_str() != "scripthash") { // P2SH cannot be wrapped in a P2SH. If this script is already a P2SH, // don't return the address for a P2SH of the P2SH. - r.pushKV("p2sh", EncodeDestination(CScriptID(script))); + r.pushKV("p2sh", EncodeDestination(ScriptHash(script))); // P2SH and witness programs cannot be wrapped in P2WSH, if this script // is a witness program, don't return addresses for a segwit programs. if (type.get_str() == "pubkey" || type.get_str() == "pubkeyhash" || type.get_str() == "multisig" || type.get_str() == "nonstandard") { @@ -709,7 +599,7 @@ static UniValue decodescript(const JSONRPCRequest& request) segwitScr = GetScriptForDestination(WitnessV0ScriptHash(script)); } ScriptPubKeyToUniv(segwitScr, sr, /* fIncludeHex */ true); - sr.pushKV("p2sh-segwit", EncodeDestination(CScriptID(segwitScr))); + sr.pushKV("p2sh-segwit", EncodeDestination(ScriptHash(segwitScr))); r.pushKV("segwit", sr); } } @@ -717,23 +607,6 @@ static UniValue decodescript(const JSONRPCRequest& request) return r; } -/** Pushes a JSON object for script verification or signing errors to vErrorsRet. */ -static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage) -{ - UniValue entry(UniValue::VOBJ); - entry.pushKV("txid", txin.prevout.hash.ToString()); - entry.pushKV("vout", (uint64_t)txin.prevout.n); - UniValue witness(UniValue::VARR); - for (unsigned int i = 0; i < txin.scriptWitness.stack.size(); i++) { - witness.push_back(HexStr(txin.scriptWitness.stack[i].begin(), txin.scriptWitness.stack[i].end())); - } - entry.pushKV("witness", witness); - entry.pushKV("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())); - entry.pushKV("sequence", (uint64_t)txin.nSequence); - entry.pushKV("error", strMessage); - vErrorsRet.push_back(entry); -} - static UniValue combinerawtransaction(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) @@ -818,152 +691,6 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request) return EncodeHexTx(CTransaction(mergedTx)); } -// TODO(https://github.com/bitcoin/bitcoin/pull/10973#discussion_r267084237): -// This function is called from both wallet and node rpcs -// (signrawtransactionwithwallet and signrawtransactionwithkey). It should be -// moved to a util file so wallet code doesn't need to link against node code. -// Also the dependency on interfaces::Chain should be removed, so -// signrawtransactionwithkey doesn't need access to a Chain instance. -UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, const UniValue& prevTxsUnival, CBasicKeyStore *keystore, bool is_temp_keystore, const UniValue& hashType) -{ - // Fetch previous transactions (inputs): - std::map<COutPoint, Coin> coins; - for (const CTxIn& txin : mtx.vin) { - coins[txin.prevout]; // Create empty map entry keyed by prevout. - } - chain.findCoins(coins); - - // Add previous txouts given in the RPC call: - if (!prevTxsUnival.isNull()) { - UniValue prevTxs = prevTxsUnival.get_array(); - for (unsigned int idx = 0; idx < prevTxs.size(); ++idx) { - const UniValue& p = prevTxs[idx]; - if (!p.isObject()) { - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}"); - } - - UniValue prevOut = p.get_obj(); - - RPCTypeCheckObj(prevOut, - { - {"txid", UniValueType(UniValue::VSTR)}, - {"vout", UniValueType(UniValue::VNUM)}, - {"scriptPubKey", UniValueType(UniValue::VSTR)}, - }); - - uint256 txid = ParseHashO(prevOut, "txid"); - - int nOut = find_value(prevOut, "vout").get_int(); - if (nOut < 0) { - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); - } - - COutPoint out(txid, nOut); - std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey")); - CScript scriptPubKey(pkData.begin(), pkData.end()); - - { - auto coin = coins.find(out); - if (coin != coins.end() && !coin->second.IsSpent() && coin->second.out.scriptPubKey != scriptPubKey) { - std::string err("Previous output scriptPubKey mismatch:\n"); - err = err + ScriptToAsmStr(coin->second.out.scriptPubKey) + "\nvs:\n"+ - ScriptToAsmStr(scriptPubKey); - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); - } - Coin newcoin; - newcoin.out.scriptPubKey = scriptPubKey; - newcoin.out.nValue = MAX_MONEY; - if (prevOut.exists("amount")) { - newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount")); - } - newcoin.nHeight = 1; - coins[out] = std::move(newcoin); - } - - // if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed - if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) { - RPCTypeCheckObj(prevOut, - { - {"redeemScript", UniValueType(UniValue::VSTR)}, - {"witnessScript", UniValueType(UniValue::VSTR)}, - }, true); - UniValue rs = find_value(prevOut, "redeemScript"); - if (!rs.isNull()) { - std::vector<unsigned char> rsData(ParseHexV(rs, "redeemScript")); - CScript redeemScript(rsData.begin(), rsData.end()); - keystore->AddCScript(redeemScript); - // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH). - // This is only for compatibility, it is encouraged to use the explicit witnessScript field instead. - keystore->AddCScript(GetScriptForWitness(redeemScript)); - } - UniValue ws = find_value(prevOut, "witnessScript"); - if (!ws.isNull()) { - std::vector<unsigned char> wsData(ParseHexV(ws, "witnessScript")); - CScript witnessScript(wsData.begin(), wsData.end()); - keystore->AddCScript(witnessScript); - // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH). - keystore->AddCScript(GetScriptForWitness(witnessScript)); - } - } - } - } - - int nHashType = ParseSighashString(hashType); - - bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); - - // Script verification errors - UniValue vErrors(UniValue::VARR); - - // Use CTransaction for the constant parts of the - // transaction to avoid rehashing. - const CTransaction txConst(mtx); - // Sign what we can: - for (unsigned int i = 0; i < mtx.vin.size(); i++) { - CTxIn& txin = mtx.vin[i]; - auto coin = coins.find(txin.prevout); - if (coin == coins.end() || coin->second.IsSpent()) { - TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); - continue; - } - const CScript& prevPubKey = coin->second.out.scriptPubKey; - const CAmount& amount = coin->second.out.nValue; - - SignatureData sigdata = DataFromTransaction(mtx, i, coin->second.out); - // Only sign SIGHASH_SINGLE if there's a corresponding output: - if (!fHashSingle || (i < mtx.vout.size())) { - ProduceSignature(*keystore, MutableTransactionSignatureCreator(&mtx, i, amount, nHashType), prevPubKey, sigdata); - } - - UpdateInput(txin, sigdata); - - // amount must be specified for valid segwit signature - if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) { - throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coin->second.out.ToString())); - } - - ScriptError serror = SCRIPT_ERR_OK; - if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) { - if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) { - // Unable to sign input and verification failed (possible attempt to partially sign). - TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid stack size (possibly missing key)"); - } else { - TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); - } - } - } - bool fComplete = vErrors.empty(); - - UniValue result(UniValue::VOBJ); - result.pushKV("hex", EncodeHexTx(CTransaction(mtx))); - result.pushKV("complete", fComplete); - if (!vErrors.empty()) { - result.pushKV("errors", vErrors); - } - - return result; -} - static UniValue signrawtransactionwithkey(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) @@ -1044,7 +771,14 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request) keystore.AddKey(key); } - return SignTransaction(*g_rpc_interfaces->chain, mtx, request.params[2], &keystore, true, request.params[3]); + // Fetch previous transactions (inputs): + std::map<COutPoint, Coin> coins; + for (const CTxIn& txin : mtx.vin) { + coins[txin.prevout]; // Create empty map entry keyed by prevout. + } + FindCoins(coins); + + return SignTransaction(mtx, request.params[2], &keystore, coins, true, request.params[3]); } static UniValue sendrawtransaction(const JSONRPCRequest& request) @@ -1054,7 +788,9 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request) "\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n", { {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"}, - {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"}, + {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE), + "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + + "/kB.\nSet to 0 to accept any fee rate.\n"}, }, RPCResult{ "\"hex\" (string) The transaction hash in hex\n" @@ -1090,15 +826,13 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request) // TODO: temporary migration code for old clients. Remove in v0.20 if (request.params[1].isBool()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Second argument must be numeric (maxfeerate) and no longer supports a boolean. To allow a transaction with high fees, set maxfeerate to 0."); - } else if (request.params[1].isNum()) { + } else if (!request.params[1].isNull()) { size_t weight = GetTransactionWeight(*tx); CFeeRate fr(AmountFromValue(request.params[1])); // the +3/4 part rounds the value up, and is the same formula used when // calculating the fee for a transaction // (see GetVirtualTransactionSize) max_raw_tx_fee = fr.GetFee((weight+3)/4); - } else if (!request.params[1].isNull()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "second argument (maxfeerate) must be numeric"); } uint256 txid; @@ -1172,15 +906,13 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) // TODO: temporary migration code for old clients. Remove in v0.20 if (request.params[1].isBool()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Second argument must be numeric (maxfeerate) and no longer supports a boolean. To allow a transaction with high fees, set maxfeerate to 0."); - } else if (request.params[1].isNum()) { + } else if (!request.params[1].isNull()) { size_t weight = GetTransactionWeight(*tx); CFeeRate fr(AmountFromValue(request.params[1])); // the +3/4 part rounds the value up, and is the same formula used when // calculating the fee for a transaction // (see GetVirtualTransactionSize) max_raw_tx_fee = fr.GetFee((weight+3)/4); - } else if (!request.params[1].isNull()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "second argument (maxfeerate) must be numeric"); } UniValue result(UniValue::VARR); @@ -1549,7 +1281,6 @@ UniValue combinepsbt(const JSONRPCRequest& request) throw JSONRPCTransactionError(error); } - UniValue result(UniValue::VOBJ); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << merged_psbt; return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size()); @@ -1944,148 +1675,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; } |