/Users/eugenesiegel/btc/bitcoin/src/rpc/mempool.cpp
| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | // Copyright (c) 2010 Satoshi Nakamoto | 
| 2 |  | // Copyright (c) 2009-2022 The Bitcoin Core developers | 
| 3 |  | // Distributed under the MIT software license, see the accompanying | 
| 4 |  | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | 
| 5 |  |  | 
| 6 |  | #include <rpc/blockchain.h> | 
| 7 |  |  | 
| 8 |  | #include <node/mempool_persist.h> | 
| 9 |  |  | 
| 10 |  | #include <chainparams.h> | 
| 11 |  | #include <consensus/validation.h> | 
| 12 |  | #include <core_io.h> | 
| 13 |  | #include <kernel/mempool_entry.h> | 
| 14 |  | #include <net_processing.h> | 
| 15 |  | #include <node/mempool_persist_args.h> | 
| 16 |  | #include <node/types.h> | 
| 17 |  | #include <policy/rbf.h> | 
| 18 |  | #include <policy/settings.h> | 
| 19 |  | #include <primitives/transaction.h> | 
| 20 |  | #include <rpc/server.h> | 
| 21 |  | #include <rpc/server_util.h> | 
| 22 |  | #include <rpc/util.h> | 
| 23 |  | #include <txmempool.h> | 
| 24 |  | #include <univalue.h> | 
| 25 |  | #include <util/fs.h> | 
| 26 |  | #include <util/moneystr.h> | 
| 27 |  | #include <util/strencodings.h> | 
| 28 |  | #include <util/time.h> | 
| 29 |  | #include <util/vector.h> | 
| 30 |  |  | 
| 31 |  | #include <utility> | 
| 32 |  |  | 
| 33 |  | using node::DumpMempool; | 
| 34 |  |  | 
| 35 |  | using node::DEFAULT_MAX_BURN_AMOUNT; | 
| 36 |  | using node::DEFAULT_MAX_RAW_TX_FEE_RATE; | 
| 37 |  | using node::MempoolPath; | 
| 38 |  | using node::NodeContext; | 
| 39 |  | using node::TransactionError; | 
| 40 |  | using util::ToString; | 
| 41 |  |  | 
| 42 |  | static RPCHelpMan sendrawtransaction() | 
| 43 | 0 | { | 
| 44 | 0 |     return RPCHelpMan{ | 
| 45 | 0 |         "sendrawtransaction", | 
| 46 | 0 |         "Submit a raw transaction (serialized, hex-encoded) to local node and network.\n" | 
| 47 | 0 |         "\nThe transaction will be sent unconditionally to all peers, so using sendrawtransaction\n" | 
| 48 | 0 |         "for manual rebroadcast may degrade privacy by leaking the transaction's origin, as\n" | 
| 49 | 0 |         "nodes will normally not rebroadcast non-wallet transactions already in their mempool.\n" | 
| 50 | 0 |         "\nA specific exception, RPC_TRANSACTION_ALREADY_IN_UTXO_SET, may throw if the transaction cannot be added to the mempool.\n" | 
| 51 | 0 |         "\nRelated RPCs: createrawtransaction, signrawtransactionwithkey\n", | 
| 52 | 0 |         { | 
| 53 | 0 |             {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"}, | 
| 54 | 0 |             {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())}, | 
| 55 | 0 |              "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + | 
| 56 | 0 |                  "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."}, | 
| 57 | 0 |             {"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_BURN_AMOUNT)}, | 
| 58 | 0 |              "Reject transactions with provably unspendable outputs (e.g. 'datacarrier' outputs that use the OP_RETURN opcode) greater than the specified value, expressed in " + CURRENCY_UNIT + ".\n" | 
| 59 | 0 |              "If burning funds through unspendable outputs is desired, increase this value.\n" | 
| 60 | 0 |              "This check is based on heuristics and does not guarantee spendability of outputs.\n"}, | 
| 61 | 0 |         }, | 
| 62 | 0 |         RPCResult{ | 
| 63 | 0 |             RPCResult::Type::STR_HEX, "", "The transaction hash in hex" | 
| 64 | 0 |         }, | 
| 65 | 0 |         RPCExamples{ | 
| 66 | 0 |             "\nCreate a transaction\n" | 
| 67 | 0 |             + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") + | 
| 68 | 0 |             "Sign the transaction, and get back the hex\n" | 
| 69 | 0 |             + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") + | 
| 70 | 0 |             "\nSend the transaction (signed hex)\n" | 
| 71 | 0 |             + HelpExampleCli("sendrawtransaction", "\"signedhex\"") + | 
| 72 | 0 |             "\nAs a JSON-RPC call\n" | 
| 73 | 0 |             + HelpExampleRpc("sendrawtransaction", "\"signedhex\"") | 
| 74 | 0 |                 }, | 
| 75 | 0 |         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue | 
| 76 | 0 |         { | 
| 77 | 0 |             const CAmount max_burn_amount = request.params[2].isNull() ? 0 : AmountFromValue(request.params[2]); | 
| 78 |  | 
 | 
| 79 | 0 |             CMutableTransaction mtx; | 
| 80 | 0 |             if (!DecodeHexTx(mtx, request.params[0].get_str())) { | 
| 81 | 0 |                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed. Make sure the tx has at least one input."); | 
| 82 | 0 |             } | 
| 83 |  |  | 
| 84 | 0 |             for (const auto& out : mtx.vout) { | 
| 85 | 0 |                 if((out.scriptPubKey.IsUnspendable() || !out.scriptPubKey.HasValidOps()) && out.nValue > max_burn_amount) { | 
| 86 | 0 |                     throw JSONRPCTransactionError(TransactionError::MAX_BURN_EXCEEDED); | 
| 87 | 0 |                 } | 
| 88 | 0 |             } | 
| 89 |  |  | 
| 90 | 0 |             CTransactionRef tx(MakeTransactionRef(std::move(mtx))); | 
| 91 |  | 
 | 
| 92 | 0 |             const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>("maxfeerate"))}; | 
| 93 |  | 
 | 
| 94 | 0 |             int64_t virtual_size = GetVirtualTransactionSize(*tx); | 
| 95 | 0 |             CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size); | 
| 96 |  | 
 | 
| 97 | 0 |             std::string err_string; | 
| 98 | 0 |             AssertLockNotHeld(cs_main); | Line | Count | Source |  | 142 | 0 | #define AssertLockNotHeld(cs) AssertLockNotHeldInline(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 99 | 0 |             NodeContext& node = EnsureAnyNodeContext(request.context); | 
| 100 | 0 |             const TransactionError err = BroadcastTransaction(node, tx, err_string, max_raw_tx_fee, /*relay=*/true, /*wait_callback=*/true); | 
| 101 | 0 |             if (TransactionError::OK != err) { | 
| 102 | 0 |                 throw JSONRPCTransactionError(err, err_string); | 
| 103 | 0 |             } | 
| 104 |  |  | 
| 105 | 0 |             return tx->GetHash().GetHex(); | 
| 106 | 0 |         }, | 
| 107 | 0 |     }; | 
| 108 | 0 | } | 
| 109 |  |  | 
| 110 |  | static RPCHelpMan testmempoolaccept() | 
| 111 | 0 | { | 
| 112 | 0 |     return RPCHelpMan{ | 
| 113 | 0 |         "testmempoolaccept", | 
| 114 | 0 |         "Returns result of mempool acceptance tests indicating if raw transaction(s) (serialized, hex-encoded) would be accepted by mempool.\n" | 
| 115 | 0 |         "\nIf multiple transactions are passed in, parents must come before children and package policies apply: the transactions cannot conflict with any mempool transactions or each other.\n" | 
| 116 | 0 |         "\nIf one transaction fails, other transactions may not be fully validated (the 'allowed' key will be blank).\n" | 
| 117 | 0 |         "\nThe maximum number of transactions allowed is " + ToString(MAX_PACKAGE_COUNT) + ".\n" | 
| 118 | 0 |         "\nThis checks if transactions violate the consensus or policy rules.\n" | 
| 119 | 0 |         "\nSee sendrawtransaction call.\n", | 
| 120 | 0 |         { | 
| 121 | 0 |             {"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings of raw transactions.", | 
| 122 | 0 |                 { | 
| 123 | 0 |                     {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""}, | 
| 124 | 0 |                 }, | 
| 125 | 0 |             }, | 
| 126 | 0 |             {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())}, | 
| 127 | 0 |              "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + | 
| 128 | 0 |                  "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."}, | 
| 129 | 0 |         }, | 
| 130 | 0 |         RPCResult{ | 
| 131 | 0 |             RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n" | 
| 132 | 0 |                                       "Returns results for each transaction in the same order they were passed in.\n" | 
| 133 | 0 |                                       "Transactions that cannot be fully validated due to failures in other transactions will not contain an 'allowed' result.\n", | 
| 134 | 0 |             { | 
| 135 | 0 |                 {RPCResult::Type::OBJ, "", "", | 
| 136 | 0 |                 { | 
| 137 | 0 |                     {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"}, | 
| 138 | 0 |                     {RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"}, | 
| 139 | 0 |                     {RPCResult::Type::STR, "package-error", /*optional=*/true, "Package validation error, if any (only possible if rawtxs had more than 1 transaction)."}, | 
| 140 | 0 |                     {RPCResult::Type::BOOL, "allowed", /*optional=*/true, "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate. " | 
| 141 | 0 |                                                        "If not present, the tx was not fully validated due to a failure in another tx in the list."}, | 
| 142 | 0 |                     {RPCResult::Type::NUM, "vsize", /*optional=*/true, "Virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true)"}, | 
| 143 | 0 |                     {RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees (only present if 'allowed' is true)", | 
| 144 | 0 |                     { | 
| 145 | 0 |                         {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT}, | 
| 146 | 0 |                         {RPCResult::Type::STR_AMOUNT, "effective-feerate", /*optional=*/false, "the effective feerate in " + CURRENCY_UNIT + " per KvB. May differ from the base feerate if, for example, there are modified fees from prioritisetransaction or a package feerate was used."}, | 
| 147 | 0 |                         {RPCResult::Type::ARR, "effective-includes", /*optional=*/false, "transactions whose fees and vsizes are included in effective-feerate.", | 
| 148 | 0 |                             {RPCResult{RPCResult::Type::STR_HEX, "", "transaction wtxid in hex"}, | 
| 149 | 0 |                         }}, | 
| 150 | 0 |                     }}, | 
| 151 | 0 |                     {RPCResult::Type::STR, "reject-reason", /*optional=*/true, "Rejection reason (only present when 'allowed' is false)"}, | 
| 152 | 0 |                     {RPCResult::Type::STR, "reject-details", /*optional=*/true, "Rejection details (only present when 'allowed' is false and rejection details exist)"}, | 
| 153 | 0 |                 }}, | 
| 154 | 0 |             } | 
| 155 | 0 |         }, | 
| 156 | 0 |         RPCExamples{ | 
| 157 | 0 |             "\nCreate a transaction\n" | 
| 158 | 0 |             + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") + | 
| 159 | 0 |             "Sign the transaction, and get back the hex\n" | 
| 160 | 0 |             + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") + | 
| 161 | 0 |             "\nTest acceptance of the transaction (signed hex)\n" | 
| 162 | 0 |             + HelpExampleCli("testmempoolaccept", R"('["signedhex"]')") + | 
| 163 | 0 |             "\nAs a JSON-RPC call\n" | 
| 164 | 0 |             + HelpExampleRpc("testmempoolaccept", "[\"signedhex\"]") | 
| 165 | 0 |                 }, | 
| 166 | 0 |         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue | 
| 167 | 0 |         { | 
| 168 | 0 |             const UniValue raw_transactions = request.params[0].get_array(); | 
| 169 | 0 |             if (raw_transactions.size() < 1 || raw_transactions.size() > MAX_PACKAGE_COUNT) { | 
| 170 | 0 |                 throw JSONRPCError(RPC_INVALID_PARAMETER, | 
| 171 | 0 |                                    "Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions."); | 
| 172 | 0 |             } | 
| 173 |  |  | 
| 174 | 0 |             const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>("maxfeerate"))}; | 
| 175 |  | 
 | 
| 176 | 0 |             std::vector<CTransactionRef> txns; | 
| 177 | 0 |             txns.reserve(raw_transactions.size()); | 
| 178 | 0 |             for (const auto& rawtx : raw_transactions.getValues()) { | 
| 179 | 0 |                 CMutableTransaction mtx; | 
| 180 | 0 |                 if (!DecodeHexTx(mtx, rawtx.get_str())) { | 
| 181 | 0 |                     throw JSONRPCError(RPC_DESERIALIZATION_ERROR, | 
| 182 | 0 |                                        "TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input."); | 
| 183 | 0 |                 } | 
| 184 | 0 |                 txns.emplace_back(MakeTransactionRef(std::move(mtx))); | 
| 185 | 0 |             } | 
| 186 |  |  | 
| 187 | 0 |             NodeContext& node = EnsureAnyNodeContext(request.context); | 
| 188 | 0 |             CTxMemPool& mempool = EnsureMemPool(node); | 
| 189 | 0 |             ChainstateManager& chainman = EnsureChainman(node); | 
| 190 | 0 |             Chainstate& chainstate = chainman.ActiveChainstate(); | 
| 191 | 0 |             const PackageMempoolAcceptResult package_result = [&] { | 
| 192 | 0 |                 LOCK(::cs_main); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 193 | 0 |                 if (txns.size() > 1) return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/true, /*client_maxfeerate=*/{}); | 
| 194 | 0 |                 return PackageMempoolAcceptResult(txns[0]->GetWitnessHash(), | 
| 195 | 0 |                                                   chainman.ProcessTransaction(txns[0], /*test_accept=*/true)); | 
| 196 | 0 |             }(); | 
| 197 |  | 
 | 
| 198 | 0 |             UniValue rpc_result(UniValue::VARR); | 
| 199 |  |             // We will check transaction fees while we iterate through txns in order. If any transaction fee | 
| 200 |  |             // exceeds maxfeerate, we will leave the rest of the validation results blank, because it | 
| 201 |  |             // doesn't make sense to return a validation result for a transaction if its ancestor(s) would | 
| 202 |  |             // not be submitted. | 
| 203 | 0 |             bool exit_early{false}; | 
| 204 | 0 |             for (const auto& tx : txns) { | 
| 205 | 0 |                 UniValue result_inner(UniValue::VOBJ); | 
| 206 | 0 |                 result_inner.pushKV("txid", tx->GetHash().GetHex()); | 
| 207 | 0 |                 result_inner.pushKV("wtxid", tx->GetWitnessHash().GetHex()); | 
| 208 | 0 |                 if (package_result.m_state.GetResult() == PackageValidationResult::PCKG_POLICY) { | 
| 209 | 0 |                     result_inner.pushKV("package-error", package_result.m_state.ToString()); | 
| 210 | 0 |                 } | 
| 211 | 0 |                 auto it = package_result.m_tx_results.find(tx->GetWitnessHash()); | 
| 212 | 0 |                 if (exit_early || it == package_result.m_tx_results.end()) { | 
| 213 |  |                     // Validation unfinished. Just return the txid and wtxid. | 
| 214 | 0 |                     rpc_result.push_back(std::move(result_inner)); | 
| 215 | 0 |                     continue; | 
| 216 | 0 |                 } | 
| 217 | 0 |                 const auto& tx_result = it->second; | 
| 218 |  |                 // Package testmempoolaccept doesn't allow transactions to already be in the mempool. | 
| 219 | 0 |                 CHECK_NONFATAL(tx_result.m_result_type != MempoolAcceptResult::ResultType::MEMPOOL_ENTRY); | Line | Count | Source |  | 103 | 0 |     inline_check_non_fatal(condition, __FILE__, __LINE__, __func__, #condition) | 
 | 
| 220 | 0 |                 if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) { | 
| 221 | 0 |                     const CAmount fee = tx_result.m_base_fees.value(); | 
| 222 |  |                     // Check that fee does not exceed maximum fee | 
| 223 | 0 |                     const int64_t virtual_size = tx_result.m_vsize.value(); | 
| 224 | 0 |                     const CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size); | 
| 225 | 0 |                     if (max_raw_tx_fee && fee > max_raw_tx_fee) { | 
| 226 | 0 |                         result_inner.pushKV("allowed", false); | 
| 227 | 0 |                         result_inner.pushKV("reject-reason", "max-fee-exceeded"); | 
| 228 | 0 |                         exit_early = true; | 
| 229 | 0 |                     } else { | 
| 230 |  |                         // Only return the fee and vsize if the transaction would pass ATMP. | 
| 231 |  |                         // These can be used to calculate the feerate. | 
| 232 | 0 |                         result_inner.pushKV("allowed", true); | 
| 233 | 0 |                         result_inner.pushKV("vsize", virtual_size); | 
| 234 | 0 |                         UniValue fees(UniValue::VOBJ); | 
| 235 | 0 |                         fees.pushKV("base", ValueFromAmount(fee)); | 
| 236 | 0 |                         fees.pushKV("effective-feerate", ValueFromAmount(tx_result.m_effective_feerate.value().GetFeePerK())); | 
| 237 | 0 |                         UniValue effective_includes_res(UniValue::VARR); | 
| 238 | 0 |                         for (const auto& wtxid : tx_result.m_wtxids_fee_calculations.value()) { | 
| 239 | 0 |                             effective_includes_res.push_back(wtxid.ToString()); | 
| 240 | 0 |                         } | 
| 241 | 0 |                         fees.pushKV("effective-includes", std::move(effective_includes_res)); | 
| 242 | 0 |                         result_inner.pushKV("fees", std::move(fees)); | 
| 243 | 0 |                     } | 
| 244 | 0 |                 } else { | 
| 245 | 0 |                     result_inner.pushKV("allowed", false); | 
| 246 | 0 |                     const TxValidationState state = tx_result.m_state; | 
| 247 | 0 |                     if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) { | 
| 248 | 0 |                         result_inner.pushKV("reject-reason", "missing-inputs"); | 
| 249 | 0 |                     } else { | 
| 250 | 0 |                         result_inner.pushKV("reject-reason", state.GetRejectReason()); | 
| 251 | 0 |                         result_inner.pushKV("reject-details", state.ToString()); | 
| 252 | 0 |                     } | 
| 253 | 0 |                 } | 
| 254 | 0 |                 rpc_result.push_back(std::move(result_inner)); | 
| 255 | 0 |             } | 
| 256 | 0 |             return rpc_result; | 
| 257 | 0 |         }, | 
| 258 | 0 |     }; | 
| 259 | 0 | } | 
| 260 |  |  | 
| 261 |  | static std::vector<RPCResult> MempoolEntryDescription() | 
| 262 | 0 | { | 
| 263 | 0 |     return { | 
| 264 | 0 |         RPCResult{RPCResult::Type::NUM, "vsize", "virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."}, | 
| 265 | 0 |         RPCResult{RPCResult::Type::NUM, "weight", "transaction weight as defined in BIP 141."}, | 
| 266 | 0 |         RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in seconds since 1 Jan 1970 GMT"}, | 
| 267 | 0 |         RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"}, | 
| 268 | 0 |         RPCResult{RPCResult::Type::NUM, "descendantcount", "number of in-mempool descendant transactions (including this one)"}, | 
| 269 | 0 |         RPCResult{RPCResult::Type::NUM, "descendantsize", "virtual transaction size of in-mempool descendants (including this one)"}, | 
| 270 | 0 |         RPCResult{RPCResult::Type::NUM, "ancestorcount", "number of in-mempool ancestor transactions (including this one)"}, | 
| 271 | 0 |         RPCResult{RPCResult::Type::NUM, "ancestorsize", "virtual transaction size of in-mempool ancestors (including this one)"}, | 
| 272 | 0 |         RPCResult{RPCResult::Type::STR_HEX, "wtxid", "hash of serialized transaction, including witness data"}, | 
| 273 | 0 |         RPCResult{RPCResult::Type::OBJ, "fees", "", | 
| 274 | 0 |             { | 
| 275 | 0 |                 RPCResult{RPCResult::Type::STR_AMOUNT, "base", "transaction fee, denominated in " + CURRENCY_UNIT}, | 
| 276 | 0 |                 RPCResult{RPCResult::Type::STR_AMOUNT, "modified", "transaction fee with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT}, | 
| 277 | 0 |                 RPCResult{RPCResult::Type::STR_AMOUNT, "ancestor", "transaction fees of in-mempool ancestors (including this one) with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT}, | 
| 278 | 0 |                 RPCResult{RPCResult::Type::STR_AMOUNT, "descendant", "transaction fees of in-mempool descendants (including this one) with fee deltas used for mining priority, denominated in " + CURRENCY_UNIT}, | 
| 279 | 0 |             }}, | 
| 280 | 0 |         RPCResult{RPCResult::Type::ARR, "depends", "unconfirmed transactions used as inputs for this transaction", | 
| 281 | 0 |             {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "parent transaction id"}}}, | 
| 282 | 0 |         RPCResult{RPCResult::Type::ARR, "spentby", "unconfirmed transactions spending outputs from this transaction", | 
| 283 | 0 |             {RPCResult{RPCResult::Type::STR_HEX, "transactionid", "child transaction id"}}}, | 
| 284 | 0 |         RPCResult{RPCResult::Type::BOOL, "bip125-replaceable", "Whether this transaction signals BIP125 replaceability or has an unconfirmed ancestor signaling BIP125 replaceability. (DEPRECATED)\n"}, | 
| 285 | 0 |         RPCResult{RPCResult::Type::BOOL, "unbroadcast", "Whether this transaction is currently unbroadcast (initial broadcast not yet acknowledged by any peers)"}, | 
| 286 | 0 |     }; | 
| 287 | 0 | } | 
| 288 |  |  | 
| 289 |  | static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPoolEntry& e) EXCLUSIVE_LOCKS_REQUIRED(pool.cs) | 
| 290 | 0 | { | 
| 291 | 0 |     AssertLockHeld(pool.cs); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 292 |  | 
 | 
| 293 | 0 |     info.pushKV("vsize", (int)e.GetTxSize()); | 
| 294 | 0 |     info.pushKV("weight", (int)e.GetTxWeight()); | 
| 295 | 0 |     info.pushKV("time", count_seconds(e.GetTime())); | 
| 296 | 0 |     info.pushKV("height", (int)e.GetHeight()); | 
| 297 | 0 |     info.pushKV("descendantcount", e.GetCountWithDescendants()); | 
| 298 | 0 |     info.pushKV("descendantsize", e.GetSizeWithDescendants()); | 
| 299 | 0 |     info.pushKV("ancestorcount", e.GetCountWithAncestors()); | 
| 300 | 0 |     info.pushKV("ancestorsize", e.GetSizeWithAncestors()); | 
| 301 | 0 |     info.pushKV("wtxid", e.GetTx().GetWitnessHash().ToString()); | 
| 302 |  | 
 | 
| 303 | 0 |     UniValue fees(UniValue::VOBJ); | 
| 304 | 0 |     fees.pushKV("base", ValueFromAmount(e.GetFee())); | 
| 305 | 0 |     fees.pushKV("modified", ValueFromAmount(e.GetModifiedFee())); | 
| 306 | 0 |     fees.pushKV("ancestor", ValueFromAmount(e.GetModFeesWithAncestors())); | 
| 307 | 0 |     fees.pushKV("descendant", ValueFromAmount(e.GetModFeesWithDescendants())); | 
| 308 | 0 |     info.pushKV("fees", std::move(fees)); | 
| 309 |  | 
 | 
| 310 | 0 |     const CTransaction& tx = e.GetTx(); | 
| 311 | 0 |     std::set<std::string> setDepends; | 
| 312 | 0 |     for (const CTxIn& txin : tx.vin) | 
| 313 | 0 |     { | 
| 314 | 0 |         if (pool.exists(txin.prevout.hash)) | 
| 315 | 0 |             setDepends.insert(txin.prevout.hash.ToString()); | 
| 316 | 0 |     } | 
| 317 |  | 
 | 
| 318 | 0 |     UniValue depends(UniValue::VARR); | 
| 319 | 0 |     for (const std::string& dep : setDepends) | 
| 320 | 0 |     { | 
| 321 | 0 |         depends.push_back(dep); | 
| 322 | 0 |     } | 
| 323 |  | 
 | 
| 324 | 0 |     info.pushKV("depends", std::move(depends)); | 
| 325 |  | 
 | 
| 326 | 0 |     UniValue spent(UniValue::VARR); | 
| 327 | 0 |     for (const CTxMemPoolEntry& child : e.GetMemPoolChildrenConst()) { | 
| 328 | 0 |         spent.push_back(child.GetTx().GetHash().ToString()); | 
| 329 | 0 |     } | 
| 330 |  | 
 | 
| 331 | 0 |     info.pushKV("spentby", std::move(spent)); | 
| 332 |  |  | 
| 333 |  |     // Add opt-in RBF status | 
| 334 | 0 |     bool rbfStatus = false; | 
| 335 | 0 |     RBFTransactionState rbfState = IsRBFOptIn(tx, pool); | 
| 336 | 0 |     if (rbfState == RBFTransactionState::UNKNOWN) { | 
| 337 | 0 |         throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not in mempool"); | 
| 338 | 0 |     } else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125) { | 
| 339 | 0 |         rbfStatus = true; | 
| 340 | 0 |     } | 
| 341 |  |  | 
| 342 | 0 |     info.pushKV("bip125-replaceable", rbfStatus); | 
| 343 | 0 |     info.pushKV("unbroadcast", pool.IsUnbroadcastTx(tx.GetHash())); | 
| 344 | 0 | } | 
| 345 |  |  | 
| 346 |  | UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose, bool include_mempool_sequence) | 
| 347 | 0 | { | 
| 348 | 0 |     if (verbose) { | 
| 349 | 0 |         if (include_mempool_sequence) { | 
| 350 | 0 |             throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbose results cannot contain mempool sequence values."); | 
| 351 | 0 |         } | 
| 352 | 0 |         LOCK(pool.cs); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 353 | 0 |         UniValue o(UniValue::VOBJ); | 
| 354 | 0 |         for (const CTxMemPoolEntry& e : pool.entryAll()) { | 
| 355 | 0 |             UniValue info(UniValue::VOBJ); | 
| 356 | 0 |             entryToJSON(pool, info, e); | 
| 357 |  |             // Mempool has unique entries so there is no advantage in using | 
| 358 |  |             // UniValue::pushKV, which checks if the key already exists in O(N). | 
| 359 |  |             // UniValue::pushKVEnd is used instead which currently is O(1). | 
| 360 | 0 |             o.pushKVEnd(e.GetTx().GetHash().ToString(), std::move(info)); | 
| 361 | 0 |         } | 
| 362 | 0 |         return o; | 
| 363 | 0 |     } else { | 
| 364 | 0 |         UniValue a(UniValue::VARR); | 
| 365 | 0 |         uint64_t mempool_sequence; | 
| 366 | 0 |         { | 
| 367 | 0 |             LOCK(pool.cs); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 368 | 0 |             for (const CTxMemPoolEntry& e : pool.entryAll()) { | 
| 369 | 0 |                 a.push_back(e.GetTx().GetHash().ToString()); | 
| 370 | 0 |             } | 
| 371 | 0 |             mempool_sequence = pool.GetSequence(); | 
| 372 | 0 |         } | 
| 373 | 0 |         if (!include_mempool_sequence) { | 
| 374 | 0 |             return a; | 
| 375 | 0 |         } else { | 
| 376 | 0 |             UniValue o(UniValue::VOBJ); | 
| 377 | 0 |             o.pushKV("txids", std::move(a)); | 
| 378 | 0 |             o.pushKV("mempool_sequence", mempool_sequence); | 
| 379 | 0 |             return o; | 
| 380 | 0 |         } | 
| 381 | 0 |     } | 
| 382 | 0 | } | 
| 383 |  |  | 
| 384 |  | static RPCHelpMan getrawmempool() | 
| 385 | 0 | { | 
| 386 | 0 |     return RPCHelpMan{ | 
| 387 | 0 |         "getrawmempool", | 
| 388 | 0 |         "Returns all transaction ids in memory pool as a json array of string transaction ids.\n" | 
| 389 | 0 |         "\nHint: use getmempoolentry to fetch a specific transaction from the mempool.\n", | 
| 390 | 0 |         { | 
| 391 | 0 |             {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"}, | 
| 392 | 0 |             {"mempool_sequence", RPCArg::Type::BOOL, RPCArg::Default{false}, "If verbose=false, returns a json object with transaction list and mempool sequence number attached."}, | 
| 393 | 0 |         }, | 
| 394 | 0 |         { | 
| 395 | 0 |             RPCResult{"for verbose = false", | 
| 396 | 0 |                 RPCResult::Type::ARR, "", "", | 
| 397 | 0 |                 { | 
| 398 | 0 |                     {RPCResult::Type::STR_HEX, "", "The transaction id"}, | 
| 399 | 0 |                 }}, | 
| 400 | 0 |             RPCResult{"for verbose = true", | 
| 401 | 0 |                 RPCResult::Type::OBJ_DYN, "", "", | 
| 402 | 0 |                 { | 
| 403 | 0 |                     {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()}, | 
| 404 | 0 |                 }}, | 
| 405 | 0 |             RPCResult{"for verbose = false and mempool_sequence = true", | 
| 406 | 0 |                 RPCResult::Type::OBJ, "", "", | 
| 407 | 0 |                 { | 
| 408 | 0 |                     {RPCResult::Type::ARR, "txids", "", | 
| 409 | 0 |                     { | 
| 410 | 0 |                         {RPCResult::Type::STR_HEX, "", "The transaction id"}, | 
| 411 | 0 |                     }}, | 
| 412 | 0 |                     {RPCResult::Type::NUM, "mempool_sequence", "The mempool sequence value."}, | 
| 413 | 0 |                 }}, | 
| 414 | 0 |         }, | 
| 415 | 0 |         RPCExamples{ | 
| 416 | 0 |             HelpExampleCli("getrawmempool", "true") | 
| 417 | 0 |             + HelpExampleRpc("getrawmempool", "true") | 
| 418 | 0 |         }, | 
| 419 | 0 |         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue | 
| 420 | 0 | { | 
| 421 | 0 |     bool fVerbose = false; | 
| 422 | 0 |     if (!request.params[0].isNull()) | 
| 423 | 0 |         fVerbose = request.params[0].get_bool(); | 
| 424 |  | 
 | 
| 425 | 0 |     bool include_mempool_sequence = false; | 
| 426 | 0 |     if (!request.params[1].isNull()) { | 
| 427 | 0 |         include_mempool_sequence = request.params[1].get_bool(); | 
| 428 | 0 |     } | 
| 429 |  | 
 | 
| 430 | 0 |     return MempoolToJSON(EnsureAnyMemPool(request.context), fVerbose, include_mempool_sequence); | 
| 431 | 0 | }, | 
| 432 | 0 |     }; | 
| 433 | 0 | } | 
| 434 |  |  | 
| 435 |  | static RPCHelpMan getmempoolancestors() | 
| 436 | 0 | { | 
| 437 | 0 |     return RPCHelpMan{ | 
| 438 | 0 |         "getmempoolancestors", | 
| 439 | 0 |         "If txid is in the mempool, returns all in-mempool ancestors.\n", | 
| 440 | 0 |         { | 
| 441 | 0 |             {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"}, | 
| 442 | 0 |             {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"}, | 
| 443 | 0 |         }, | 
| 444 | 0 |         { | 
| 445 | 0 |             RPCResult{"for verbose = false", | 
| 446 | 0 |                 RPCResult::Type::ARR, "", "", | 
| 447 | 0 |                 {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool ancestor transaction"}}}, | 
| 448 | 0 |             RPCResult{"for verbose = true", | 
| 449 | 0 |                 RPCResult::Type::OBJ_DYN, "", "", | 
| 450 | 0 |                 { | 
| 451 | 0 |                     {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()}, | 
| 452 | 0 |                 }}, | 
| 453 | 0 |         }, | 
| 454 | 0 |         RPCExamples{ | 
| 455 | 0 |             HelpExampleCli("getmempoolancestors", "\"mytxid\"") | 
| 456 | 0 |             + HelpExampleRpc("getmempoolancestors", "\"mytxid\"") | 
| 457 | 0 |         }, | 
| 458 | 0 |         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue | 
| 459 | 0 | { | 
| 460 | 0 |     bool fVerbose = false; | 
| 461 | 0 |     if (!request.params[1].isNull()) | 
| 462 | 0 |         fVerbose = request.params[1].get_bool(); | 
| 463 |  | 
 | 
| 464 | 0 |     auto txid{Txid::FromUint256(ParseHashV(request.params[0], "txid"))}; | 
| 465 |  | 
 | 
| 466 | 0 |     const CTxMemPool& mempool = EnsureAnyMemPool(request.context); | 
| 467 | 0 |     LOCK(mempool.cs); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 468 |  | 
 | 
| 469 | 0 |     const auto entry{mempool.GetEntry(txid)}; | 
| 470 | 0 |     if (entry == nullptr) { | 
| 471 | 0 |         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); | 
| 472 | 0 |     } | 
| 473 |  |  | 
| 474 | 0 |     auto ancestors{mempool.AssumeCalculateMemPoolAncestors(self.m_name, *entry, CTxMemPool::Limits::NoLimits(), /*fSearchForParents=*/false)}; | 
| 475 |  | 
 | 
| 476 | 0 |     if (!fVerbose) { | 
| 477 | 0 |         UniValue o(UniValue::VARR); | 
| 478 | 0 |         for (CTxMemPool::txiter ancestorIt : ancestors) { | 
| 479 | 0 |             o.push_back(ancestorIt->GetTx().GetHash().ToString()); | 
| 480 | 0 |         } | 
| 481 | 0 |         return o; | 
| 482 | 0 |     } else { | 
| 483 | 0 |         UniValue o(UniValue::VOBJ); | 
| 484 | 0 |         for (CTxMemPool::txiter ancestorIt : ancestors) { | 
| 485 | 0 |             const CTxMemPoolEntry &e = *ancestorIt; | 
| 486 | 0 |             UniValue info(UniValue::VOBJ); | 
| 487 | 0 |             entryToJSON(mempool, info, e); | 
| 488 | 0 |             o.pushKV(e.GetTx().GetHash().ToString(), std::move(info)); | 
| 489 | 0 |         } | 
| 490 | 0 |         return o; | 
| 491 | 0 |     } | 
| 492 | 0 | }, | 
| 493 | 0 |     }; | 
| 494 | 0 | } | 
| 495 |  |  | 
| 496 |  | static RPCHelpMan getmempooldescendants() | 
| 497 | 0 | { | 
| 498 | 0 |     return RPCHelpMan{ | 
| 499 | 0 |         "getmempooldescendants", | 
| 500 | 0 |         "If txid is in the mempool, returns all in-mempool descendants.\n", | 
| 501 | 0 |         { | 
| 502 | 0 |             {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"}, | 
| 503 | 0 |             {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "True for a json object, false for array of transaction ids"}, | 
| 504 | 0 |         }, | 
| 505 | 0 |         { | 
| 506 | 0 |             RPCResult{"for verbose = false", | 
| 507 | 0 |                 RPCResult::Type::ARR, "", "", | 
| 508 | 0 |                 {{RPCResult::Type::STR_HEX, "", "The transaction id of an in-mempool descendant transaction"}}}, | 
| 509 | 0 |             RPCResult{"for verbose = true", | 
| 510 | 0 |                 RPCResult::Type::OBJ_DYN, "", "", | 
| 511 | 0 |                 { | 
| 512 | 0 |                     {RPCResult::Type::OBJ, "transactionid", "", MempoolEntryDescription()}, | 
| 513 | 0 |                 }}, | 
| 514 | 0 |         }, | 
| 515 | 0 |         RPCExamples{ | 
| 516 | 0 |             HelpExampleCli("getmempooldescendants", "\"mytxid\"") | 
| 517 | 0 |             + HelpExampleRpc("getmempooldescendants", "\"mytxid\"") | 
| 518 | 0 |         }, | 
| 519 | 0 |         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue | 
| 520 | 0 | { | 
| 521 | 0 |     bool fVerbose = false; | 
| 522 | 0 |     if (!request.params[1].isNull()) | 
| 523 | 0 |         fVerbose = request.params[1].get_bool(); | 
| 524 |  | 
 | 
| 525 | 0 |     auto txid{Txid::FromUint256(ParseHashV(request.params[0], "txid"))}; | 
| 526 |  | 
 | 
| 527 | 0 |     const CTxMemPool& mempool = EnsureAnyMemPool(request.context); | 
| 528 | 0 |     LOCK(mempool.cs); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 529 |  | 
 | 
| 530 | 0 |     const auto it{mempool.GetIter(txid)}; | 
| 531 | 0 |     if (!it) { | 
| 532 | 0 |         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); | 
| 533 | 0 |     } | 
| 534 |  |  | 
| 535 | 0 |     CTxMemPool::setEntries setDescendants; | 
| 536 | 0 |     mempool.CalculateDescendants(*it, setDescendants); | 
| 537 |  |     // CTxMemPool::CalculateDescendants will include the given tx | 
| 538 | 0 |     setDescendants.erase(*it); | 
| 539 |  | 
 | 
| 540 | 0 |     if (!fVerbose) { | 
| 541 | 0 |         UniValue o(UniValue::VARR); | 
| 542 | 0 |         for (CTxMemPool::txiter descendantIt : setDescendants) { | 
| 543 | 0 |             o.push_back(descendantIt->GetTx().GetHash().ToString()); | 
| 544 | 0 |         } | 
| 545 |  | 
 | 
| 546 | 0 |         return o; | 
| 547 | 0 |     } else { | 
| 548 | 0 |         UniValue o(UniValue::VOBJ); | 
| 549 | 0 |         for (CTxMemPool::txiter descendantIt : setDescendants) { | 
| 550 | 0 |             const CTxMemPoolEntry &e = *descendantIt; | 
| 551 | 0 |             UniValue info(UniValue::VOBJ); | 
| 552 | 0 |             entryToJSON(mempool, info, e); | 
| 553 | 0 |             o.pushKV(e.GetTx().GetHash().ToString(), std::move(info)); | 
| 554 | 0 |         } | 
| 555 | 0 |         return o; | 
| 556 | 0 |     } | 
| 557 | 0 | }, | 
| 558 | 0 |     }; | 
| 559 | 0 | } | 
| 560 |  |  | 
| 561 |  | static RPCHelpMan getmempoolentry() | 
| 562 | 0 | { | 
| 563 | 0 |     return RPCHelpMan{ | 
| 564 | 0 |         "getmempoolentry", | 
| 565 | 0 |         "Returns mempool data for given transaction\n", | 
| 566 | 0 |         { | 
| 567 | 0 |             {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id (must be in mempool)"}, | 
| 568 | 0 |         }, | 
| 569 | 0 |         RPCResult{ | 
| 570 | 0 |             RPCResult::Type::OBJ, "", "", MempoolEntryDescription()}, | 
| 571 | 0 |         RPCExamples{ | 
| 572 | 0 |             HelpExampleCli("getmempoolentry", "\"mytxid\"") | 
| 573 | 0 |             + HelpExampleRpc("getmempoolentry", "\"mytxid\"") | 
| 574 | 0 |         }, | 
| 575 | 0 |         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue | 
| 576 | 0 | { | 
| 577 | 0 |     auto txid{Txid::FromUint256(ParseHashV(request.params[0], "txid"))}; | 
| 578 |  | 
 | 
| 579 | 0 |     const CTxMemPool& mempool = EnsureAnyMemPool(request.context); | 
| 580 | 0 |     LOCK(mempool.cs); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 581 |  | 
 | 
| 582 | 0 |     const auto entry{mempool.GetEntry(txid)}; | 
| 583 | 0 |     if (entry == nullptr) { | 
| 584 | 0 |         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); | 
| 585 | 0 |     } | 
| 586 |  |  | 
| 587 | 0 |     UniValue info(UniValue::VOBJ); | 
| 588 | 0 |     entryToJSON(mempool, info, *entry); | 
| 589 | 0 |     return info; | 
| 590 | 0 | }, | 
| 591 | 0 |     }; | 
| 592 | 0 | } | 
| 593 |  |  | 
| 594 |  | static RPCHelpMan gettxspendingprevout() | 
| 595 | 0 | { | 
| 596 | 0 |     return RPCHelpMan{"gettxspendingprevout", | 
| 597 | 0 |         "Scans the mempool to find transactions spending any of the given outputs", | 
| 598 | 0 |         { | 
| 599 | 0 |             {"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The transaction outputs that we want to check, and within each, the txid (string) vout (numeric).", | 
| 600 | 0 |                 { | 
| 601 | 0 |                     {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", | 
| 602 | 0 |                         { | 
| 603 | 0 |                             {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"}, | 
| 604 | 0 |                             {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"}, | 
| 605 | 0 |                         }, | 
| 606 | 0 |                     }, | 
| 607 | 0 |                 }, | 
| 608 | 0 |             }, | 
| 609 | 0 |         }, | 
| 610 | 0 |         RPCResult{ | 
| 611 | 0 |             RPCResult::Type::ARR, "", "", | 
| 612 | 0 |             { | 
| 613 | 0 |                 {RPCResult::Type::OBJ, "", "", | 
| 614 | 0 |                 { | 
| 615 | 0 |                     {RPCResult::Type::STR_HEX, "txid", "the transaction id of the checked output"}, | 
| 616 | 0 |                     {RPCResult::Type::NUM, "vout", "the vout value of the checked output"}, | 
| 617 | 0 |                     {RPCResult::Type::STR_HEX, "spendingtxid", /*optional=*/true, "the transaction id of the mempool transaction spending this output (omitted if unspent)"}, | 
| 618 | 0 |                 }}, | 
| 619 | 0 |             } | 
| 620 | 0 |         }, | 
| 621 | 0 |         RPCExamples{ | 
| 622 | 0 |             HelpExampleCli("gettxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"") | 
| 623 | 0 |             + HelpExampleRpc("gettxspendingprevout", "\"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":3}]\"") | 
| 624 | 0 |         }, | 
| 625 | 0 |         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue | 
| 626 | 0 |         { | 
| 627 | 0 |             const UniValue& output_params = request.params[0].get_array(); | 
| 628 | 0 |             if (output_params.empty()) { | 
| 629 | 0 |                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, outputs are missing"); | 
| 630 | 0 |             } | 
| 631 |  |  | 
| 632 | 0 |             std::vector<COutPoint> prevouts; | 
| 633 | 0 |             prevouts.reserve(output_params.size()); | 
| 634 |  | 
 | 
| 635 | 0 |             for (unsigned int idx = 0; idx < output_params.size(); idx++) { | 
| 636 | 0 |                 const UniValue& o = output_params[idx].get_obj(); | 
| 637 |  | 
 | 
| 638 | 0 |                 RPCTypeCheckObj(o, | 
| 639 | 0 |                                 { | 
| 640 | 0 |                                     {"txid", UniValueType(UniValue::VSTR)}, | 
| 641 | 0 |                                     {"vout", UniValueType(UniValue::VNUM)}, | 
| 642 | 0 |                                 }, /*fAllowNull=*/false, /*fStrict=*/true); | 
| 643 |  | 
 | 
| 644 | 0 |                 const Txid txid = Txid::FromUint256(ParseHashO(o, "txid")); | 
| 645 | 0 |                 const int nOutput{o.find_value("vout").getInt<int>()}; | 
| 646 | 0 |                 if (nOutput < 0) { | 
| 647 | 0 |                     throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative"); | 
| 648 | 0 |                 } | 
| 649 |  |  | 
| 650 | 0 |                 prevouts.emplace_back(txid, nOutput); | 
| 651 | 0 |             } | 
| 652 |  |  | 
| 653 | 0 |             const CTxMemPool& mempool = EnsureAnyMemPool(request.context); | 
| 654 | 0 |             LOCK(mempool.cs); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 655 |  | 
 | 
| 656 | 0 |             UniValue result{UniValue::VARR}; | 
| 657 |  | 
 | 
| 658 | 0 |             for (const COutPoint& prevout : prevouts) { | 
| 659 | 0 |                 UniValue o(UniValue::VOBJ); | 
| 660 | 0 |                 o.pushKV("txid", prevout.hash.ToString()); | 
| 661 | 0 |                 o.pushKV("vout", (uint64_t)prevout.n); | 
| 662 |  | 
 | 
| 663 | 0 |                 const CTransaction* spendingTx = mempool.GetConflictTx(prevout); | 
| 664 | 0 |                 if (spendingTx != nullptr) { | 
| 665 | 0 |                     o.pushKV("spendingtxid", spendingTx->GetHash().ToString()); | 
| 666 | 0 |                 } | 
| 667 |  | 
 | 
| 668 | 0 |                 result.push_back(std::move(o)); | 
| 669 | 0 |             } | 
| 670 |  | 
 | 
| 671 | 0 |             return result; | 
| 672 | 0 |         }, | 
| 673 | 0 |     }; | 
| 674 | 0 | } | 
| 675 |  |  | 
| 676 |  | UniValue MempoolInfoToJSON(const CTxMemPool& pool) | 
| 677 | 0 | { | 
| 678 |  |     // Make sure this call is atomic in the pool. | 
| 679 | 0 |     LOCK(pool.cs); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 680 | 0 |     UniValue ret(UniValue::VOBJ); | 
| 681 | 0 |     ret.pushKV("loaded", pool.GetLoadTried()); | 
| 682 | 0 |     ret.pushKV("size", (int64_t)pool.size()); | 
| 683 | 0 |     ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize()); | 
| 684 | 0 |     ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage()); | 
| 685 | 0 |     ret.pushKV("total_fee", ValueFromAmount(pool.GetTotalFee())); | 
| 686 | 0 |     ret.pushKV("maxmempool", pool.m_opts.max_size_bytes); | 
| 687 | 0 |     ret.pushKV("mempoolminfee", ValueFromAmount(std::max(pool.GetMinFee(), pool.m_opts.min_relay_feerate).GetFeePerK())); | 
| 688 | 0 |     ret.pushKV("minrelaytxfee", ValueFromAmount(pool.m_opts.min_relay_feerate.GetFeePerK())); | 
| 689 | 0 |     ret.pushKV("incrementalrelayfee", ValueFromAmount(pool.m_opts.incremental_relay_feerate.GetFeePerK())); | 
| 690 | 0 |     ret.pushKV("unbroadcastcount", uint64_t{pool.GetUnbroadcastTxs().size()}); | 
| 691 | 0 |     ret.pushKV("fullrbf", true); | 
| 692 | 0 |     ret.pushKV("permitbaremultisig", pool.m_opts.permit_bare_multisig); | 
| 693 | 0 |     ret.pushKV("maxdatacarriersize", pool.m_opts.max_datacarrier_bytes.value_or(0)); | 
| 694 | 0 |     return ret; | 
| 695 | 0 | } | 
| 696 |  |  | 
| 697 |  | static RPCHelpMan getmempoolinfo() | 
| 698 | 0 | { | 
| 699 | 0 |     return RPCHelpMan{"getmempoolinfo", | 
| 700 | 0 |         "Returns details on the active state of the TX memory pool.", | 
| 701 | 0 |         {}, | 
| 702 | 0 |         RPCResult{ | 
| 703 | 0 |             RPCResult::Type::OBJ, "", "", | 
| 704 | 0 |             { | 
| 705 | 0 |                 {RPCResult::Type::BOOL, "loaded", "True if the initial load attempt of the persisted mempool finished"}, | 
| 706 | 0 |                 {RPCResult::Type::NUM, "size", "Current tx count"}, | 
| 707 | 0 |                 {RPCResult::Type::NUM, "bytes", "Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted"}, | 
| 708 | 0 |                 {RPCResult::Type::NUM, "usage", "Total memory usage for the mempool"}, | 
| 709 | 0 |                 {RPCResult::Type::STR_AMOUNT, "total_fee", "Total fees for the mempool in " + CURRENCY_UNIT + ", ignoring modified fees through prioritisetransaction"}, | 
| 710 | 0 |                 {RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"}, | 
| 711 | 0 |                 {RPCResult::Type::STR_AMOUNT, "mempoolminfee", "Minimum fee rate in " + CURRENCY_UNIT + "/kvB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee"}, | 
| 712 | 0 |                 {RPCResult::Type::STR_AMOUNT, "minrelaytxfee", "Current minimum relay fee for transactions"}, | 
| 713 | 0 |                 {RPCResult::Type::NUM, "incrementalrelayfee", "minimum fee rate increment for mempool limiting or replacement in " + CURRENCY_UNIT + "/kvB"}, | 
| 714 | 0 |                 {RPCResult::Type::NUM, "unbroadcastcount", "Current number of transactions that haven't passed initial broadcast yet"}, | 
| 715 | 0 |                 {RPCResult::Type::BOOL, "fullrbf", "True if the mempool accepts RBF without replaceability signaling inspection (DEPRECATED)"}, | 
| 716 | 0 |                 {RPCResult::Type::BOOL, "permitbaremultisig", "True if the mempool accepts transactions with bare multisig outputs"}, | 
| 717 | 0 |                 {RPCResult::Type::NUM, "maxdatacarriersize", "Maximum number of bytes that can be used by OP_RETURN outputs in the mempool"}, | 
| 718 | 0 |             }}, | 
| 719 | 0 |         RPCExamples{ | 
| 720 | 0 |             HelpExampleCli("getmempoolinfo", "") | 
| 721 | 0 |             + HelpExampleRpc("getmempoolinfo", "") | 
| 722 | 0 |         }, | 
| 723 | 0 |         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue | 
| 724 | 0 | { | 
| 725 | 0 |     return MempoolInfoToJSON(EnsureAnyMemPool(request.context)); | 
| 726 | 0 | }, | 
| 727 | 0 |     }; | 
| 728 | 0 | } | 
| 729 |  |  | 
| 730 |  | static RPCHelpMan importmempool() | 
| 731 | 0 | { | 
| 732 | 0 |     return RPCHelpMan{ | 
| 733 | 0 |         "importmempool", | 
| 734 | 0 |         "Import a mempool.dat file and attempt to add its contents to the mempool.\n" | 
| 735 | 0 |         "Warning: Importing untrusted files is dangerous, especially if metadata from the file is taken over.", | 
| 736 | 0 |         { | 
| 737 | 0 |             {"filepath", RPCArg::Type::STR, RPCArg::Optional::NO, "The mempool file"}, | 
| 738 | 0 |             {"options", | 
| 739 | 0 |              RPCArg::Type::OBJ_NAMED_PARAMS, | 
| 740 | 0 |              RPCArg::Optional::OMITTED, | 
| 741 | 0 |              "", | 
| 742 | 0 |              { | 
| 743 | 0 |                  {"use_current_time", RPCArg::Type::BOOL, RPCArg::Default{true}, | 
| 744 | 0 |                   "Whether to use the current system time or use the entry time metadata from the mempool file.\n" | 
| 745 | 0 |                   "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior."}, | 
| 746 | 0 |                  {"apply_fee_delta_priority", RPCArg::Type::BOOL, RPCArg::Default{false}, | 
| 747 | 0 |                   "Whether to apply the fee delta metadata from the mempool file.\n" | 
| 748 | 0 |                   "It will be added to any existing fee deltas.\n" | 
| 749 | 0 |                   "The fee delta can be set by the prioritisetransaction RPC.\n" | 
| 750 | 0 |                   "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior.\n" | 
| 751 | 0 |                   "Only set this bool if you understand what it does."}, | 
| 752 | 0 |                  {"apply_unbroadcast_set", RPCArg::Type::BOOL, RPCArg::Default{false}, | 
| 753 | 0 |                   "Whether to apply the unbroadcast set metadata from the mempool file.\n" | 
| 754 | 0 |                   "Warning: Importing untrusted metadata may lead to unexpected issues and undesirable behavior."}, | 
| 755 | 0 |              }, | 
| 756 | 0 |              RPCArgOptions{.oneline_description = "options"}}, | 
| 757 | 0 |         }, | 
| 758 | 0 |         RPCResult{RPCResult::Type::OBJ, "", "", std::vector<RPCResult>{}}, | 
| 759 | 0 |         RPCExamples{HelpExampleCli("importmempool", "/path/to/mempool.dat") + HelpExampleRpc("importmempool", "/path/to/mempool.dat")}, | 
| 760 | 0 |         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { | 
| 761 | 0 |             const NodeContext& node{EnsureAnyNodeContext(request.context)}; | 
| 762 |  | 
 | 
| 763 | 0 |             CTxMemPool& mempool{EnsureMemPool(node)}; | 
| 764 | 0 |             ChainstateManager& chainman = EnsureChainman(node); | 
| 765 | 0 |             Chainstate& chainstate = chainman.ActiveChainstate(); | 
| 766 |  | 
 | 
| 767 | 0 |             if (chainman.IsInitialBlockDownload()) { | 
| 768 | 0 |                 throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Can only import the mempool after the block download and sync is done."); | 
| 769 | 0 |             } | 
| 770 |  |  | 
| 771 | 0 |             const fs::path load_path{fs::u8path(request.params[0].get_str())}; | 
| 772 | 0 |             const UniValue& use_current_time{request.params[1]["use_current_time"]}; | 
| 773 | 0 |             const UniValue& apply_fee_delta{request.params[1]["apply_fee_delta_priority"]}; | 
| 774 | 0 |             const UniValue& apply_unbroadcast{request.params[1]["apply_unbroadcast_set"]}; | 
| 775 | 0 |             node::ImportMempoolOptions opts{ | 
| 776 | 0 |                 .use_current_time = use_current_time.isNull() ? true : use_current_time.get_bool(), | 
| 777 | 0 |                 .apply_fee_delta_priority = apply_fee_delta.isNull() ? false : apply_fee_delta.get_bool(), | 
| 778 | 0 |                 .apply_unbroadcast_set = apply_unbroadcast.isNull() ? false : apply_unbroadcast.get_bool(), | 
| 779 | 0 |             }; | 
| 780 |  | 
 | 
| 781 | 0 |             if (!node::LoadMempool(mempool, load_path, chainstate, std::move(opts))) { | 
| 782 | 0 |                 throw JSONRPCError(RPC_MISC_ERROR, "Unable to import mempool file, see debug.log for details."); | 
| 783 | 0 |             } | 
| 784 |  |  | 
| 785 | 0 |             UniValue ret{UniValue::VOBJ}; | 
| 786 | 0 |             return ret; | 
| 787 | 0 |         }, | 
| 788 | 0 |     }; | 
| 789 | 0 | } | 
| 790 |  |  | 
| 791 |  | static RPCHelpMan savemempool() | 
| 792 | 0 | { | 
| 793 | 0 |     return RPCHelpMan{ | 
| 794 | 0 |         "savemempool", | 
| 795 | 0 |         "Dumps the mempool to disk. It will fail until the previous dump is fully loaded.\n", | 
| 796 | 0 |         {}, | 
| 797 | 0 |         RPCResult{ | 
| 798 | 0 |             RPCResult::Type::OBJ, "", "", | 
| 799 | 0 |             { | 
| 800 | 0 |                 {RPCResult::Type::STR, "filename", "the directory and file where the mempool was saved"}, | 
| 801 | 0 |             }}, | 
| 802 | 0 |         RPCExamples{ | 
| 803 | 0 |             HelpExampleCli("savemempool", "") | 
| 804 | 0 |             + HelpExampleRpc("savemempool", "") | 
| 805 | 0 |         }, | 
| 806 | 0 |         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue | 
| 807 | 0 | { | 
| 808 | 0 |     const ArgsManager& args{EnsureAnyArgsman(request.context)}; | 
| 809 | 0 |     const CTxMemPool& mempool = EnsureAnyMemPool(request.context); | 
| 810 |  | 
 | 
| 811 | 0 |     if (!mempool.GetLoadTried()) { | 
| 812 | 0 |         throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet"); | 
| 813 | 0 |     } | 
| 814 |  |  | 
| 815 | 0 |     const fs::path& dump_path = MempoolPath(args); | 
| 816 |  | 
 | 
| 817 | 0 |     if (!DumpMempool(mempool, dump_path)) { | 
| 818 | 0 |         throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk"); | 
| 819 | 0 |     } | 
| 820 |  |  | 
| 821 | 0 |     UniValue ret(UniValue::VOBJ); | 
| 822 | 0 |     ret.pushKV("filename", dump_path.utf8string()); | 
| 823 |  | 
 | 
| 824 | 0 |     return ret; | 
| 825 | 0 | }, | 
| 826 | 0 |     }; | 
| 827 | 0 | } | 
| 828 |  |  | 
| 829 |  | static std::vector<RPCResult> OrphanDescription() | 
| 830 | 0 | { | 
| 831 | 0 |     return { | 
| 832 | 0 |         RPCResult{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"}, | 
| 833 | 0 |         RPCResult{RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"}, | 
| 834 | 0 |         RPCResult{RPCResult::Type::NUM, "bytes", "The serialized transaction size in bytes"}, | 
| 835 | 0 |         RPCResult{RPCResult::Type::NUM, "vsize", "The virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."}, | 
| 836 | 0 |         RPCResult{RPCResult::Type::NUM, "weight", "The transaction weight as defined in BIP 141."}, | 
| 837 | 0 |         RPCResult{RPCResult::Type::ARR, "from", "", | 
| 838 | 0 |         { | 
| 839 | 0 |             RPCResult{RPCResult::Type::NUM, "peer_id", "Peer ID"}, | 
| 840 | 0 |         }}, | 
| 841 | 0 |     }; | 
| 842 | 0 | } | 
| 843 |  |  | 
| 844 |  | static UniValue OrphanToJSON(const node::TxOrphanage::OrphanInfo& orphan) | 
| 845 | 0 | { | 
| 846 | 0 |     UniValue o(UniValue::VOBJ); | 
| 847 | 0 |     o.pushKV("txid", orphan.tx->GetHash().ToString()); | 
| 848 | 0 |     o.pushKV("wtxid", orphan.tx->GetWitnessHash().ToString()); | 
| 849 | 0 |     o.pushKV("bytes", orphan.tx->GetTotalSize()); | 
| 850 | 0 |     o.pushKV("vsize", GetVirtualTransactionSize(*orphan.tx)); | 
| 851 | 0 |     o.pushKV("weight", GetTransactionWeight(*orphan.tx)); | 
| 852 | 0 |     UniValue from(UniValue::VARR); | 
| 853 | 0 |     for (const auto fromPeer: orphan.announcers) { | 
| 854 | 0 |         from.push_back(fromPeer); | 
| 855 | 0 |     } | 
| 856 | 0 |     o.pushKV("from", from); | 
| 857 | 0 |     return o; | 
| 858 | 0 | } | 
| 859 |  |  | 
| 860 |  | static RPCHelpMan getorphantxs() | 
| 861 | 0 | { | 
| 862 | 0 |     return RPCHelpMan{ | 
| 863 | 0 |         "getorphantxs", | 
| 864 | 0 |         "Shows transactions in the tx orphanage.\n" | 
| 865 | 0 |         "\nEXPERIMENTAL warning: this call may be changed in future releases.\n", | 
| 866 | 0 |         { | 
| 867 | 0 |             {"verbosity", RPCArg::Type::NUM, RPCArg::Default{0}, "0 for an array of txids (may contain duplicates), 1 for an array of objects with tx details, and 2 for details from (1) and tx hex", | 
| 868 | 0 |              RPCArgOptions{.skip_type_check = true}}, | 
| 869 | 0 |         }, | 
| 870 | 0 |         { | 
| 871 | 0 |             RPCResult{"for verbose = 0", | 
| 872 | 0 |                 RPCResult::Type::ARR, "", "", | 
| 873 | 0 |                 { | 
| 874 | 0 |                     {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"}, | 
| 875 | 0 |                 }}, | 
| 876 | 0 |             RPCResult{"for verbose = 1", | 
| 877 | 0 |                 RPCResult::Type::ARR, "", "", | 
| 878 | 0 |                 { | 
| 879 | 0 |                     {RPCResult::Type::OBJ, "", "", OrphanDescription()}, | 
| 880 | 0 |                 }}, | 
| 881 | 0 |             RPCResult{"for verbose = 2", | 
| 882 | 0 |                 RPCResult::Type::ARR, "", "", | 
| 883 | 0 |                 { | 
| 884 | 0 |                     {RPCResult::Type::OBJ, "", "", | 
| 885 | 0 |                         Cat<std::vector<RPCResult>>( | 
| 886 | 0 |                             OrphanDescription(), | 
| 887 | 0 |                             {{RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded transaction data"}} | 
| 888 | 0 |                         ) | 
| 889 | 0 |                     }, | 
| 890 | 0 |                 }}, | 
| 891 | 0 |         }, | 
| 892 | 0 |         RPCExamples{ | 
| 893 | 0 |             HelpExampleCli("getorphantxs", "2") | 
| 894 | 0 |             + HelpExampleRpc("getorphantxs", "2") | 
| 895 | 0 |         }, | 
| 896 | 0 |         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue | 
| 897 | 0 |         { | 
| 898 | 0 |             const NodeContext& node = EnsureAnyNodeContext(request.context); | 
| 899 | 0 |             PeerManager& peerman = EnsurePeerman(node); | 
| 900 | 0 |             std::vector<node::TxOrphanage::OrphanInfo> orphanage = peerman.GetOrphanTransactions(); | 
| 901 |  | 
 | 
| 902 | 0 |             int verbosity{ParseVerbosity(request.params[0], /*default_verbosity=*/0, /*allow_bool*/false)}; | 
| 903 |  | 
 | 
| 904 | 0 |             UniValue ret(UniValue::VARR); | 
| 905 |  | 
 | 
| 906 | 0 |             if (verbosity == 0) { | 
| 907 | 0 |                 for (auto const& orphan : orphanage) { | 
| 908 | 0 |                     ret.push_back(orphan.tx->GetHash().ToString()); | 
| 909 | 0 |                 } | 
| 910 | 0 |             } else if (verbosity == 1) { | 
| 911 | 0 |                 for (auto const& orphan : orphanage) { | 
| 912 | 0 |                     ret.push_back(OrphanToJSON(orphan)); | 
| 913 | 0 |                 } | 
| 914 | 0 |             } else if (verbosity == 2) { | 
| 915 | 0 |                 for (auto const& orphan : orphanage) { | 
| 916 | 0 |                     UniValue o{OrphanToJSON(orphan)}; | 
| 917 | 0 |                     o.pushKV("hex", EncodeHexTx(*orphan.tx)); | 
| 918 | 0 |                     ret.push_back(o); | 
| 919 | 0 |                 } | 
| 920 | 0 |             } else { | 
| 921 | 0 |                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid verbosity value " + ToString(verbosity)); | 
| 922 | 0 |             } | 
| 923 |  |  | 
| 924 | 0 |             return ret; | 
| 925 | 0 |         }, | 
| 926 | 0 |     }; | 
| 927 | 0 | } | 
| 928 |  |  | 
| 929 |  | static RPCHelpMan submitpackage() | 
| 930 | 0 | { | 
| 931 | 0 |     return RPCHelpMan{"submitpackage", | 
| 932 | 0 |         "Submit a package of raw transactions (serialized, hex-encoded) to local node.\n" | 
| 933 | 0 |         "The package will be validated according to consensus and mempool policy rules. If any transaction passes, it will be accepted to mempool.\n" | 
| 934 | 0 |         "This RPC is experimental and the interface may be unstable. Refer to doc/policy/packages.md for documentation on package policies.\n" | 
| 935 | 0 |         "Warning: successful submission does not mean the transactions will propagate throughout the network.\n" | 
| 936 | 0 |         , | 
| 937 | 0 |         { | 
| 938 | 0 |             {"package", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of raw transactions.\n" | 
| 939 | 0 |                 "The package must solely consist of a child transaction and all of its unconfirmed parents, if any. None of the parents may depend on each other.\n" | 
| 940 | 0 |                 "The package must be topologically sorted, with the child being the last element in the array.", | 
| 941 | 0 |                 { | 
| 942 | 0 |                     {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""}, | 
| 943 | 0 |                 }, | 
| 944 | 0 |             }, | 
| 945 | 0 |             {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())}, | 
| 946 | 0 |              "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + | 
| 947 | 0 |                  "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."}, | 
| 948 | 0 |             {"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_BURN_AMOUNT)}, | 
| 949 | 0 |              "Reject transactions with provably unspendable outputs (e.g. 'datacarrier' outputs that use the OP_RETURN opcode) greater than the specified value, expressed in " + CURRENCY_UNIT + ".\n" | 
| 950 | 0 |              "If burning funds through unspendable outputs is desired, increase this value.\n" | 
| 951 | 0 |              "This check is based on heuristics and does not guarantee spendability of outputs.\n" | 
| 952 | 0 |             }, | 
| 953 | 0 |         }, | 
| 954 | 0 |         RPCResult{ | 
| 955 | 0 |             RPCResult::Type::OBJ, "", "", | 
| 956 | 0 |             { | 
| 957 | 0 |                 {RPCResult::Type::STR, "package_msg", "The transaction package result message. \"success\" indicates all transactions were accepted into or are already in the mempool."}, | 
| 958 | 0 |                 {RPCResult::Type::OBJ_DYN, "tx-results", "The transaction results keyed by wtxid. An entry is returned for every submitted wtxid.", | 
| 959 | 0 |                 { | 
| 960 | 0 |                     {RPCResult::Type::OBJ, "wtxid", "transaction wtxid", { | 
| 961 | 0 |                         {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"}, | 
| 962 | 0 |                         {RPCResult::Type::STR_HEX, "other-wtxid", /*optional=*/true, "The wtxid of a different transaction with the same txid but different witness found in the mempool. This means the submitted transaction was ignored."}, | 
| 963 | 0 |                         {RPCResult::Type::NUM, "vsize", /*optional=*/true, "Sigops-adjusted virtual transaction size."}, | 
| 964 | 0 |                         {RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees", { | 
| 965 | 0 |                             {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT}, | 
| 966 | 0 |                             {RPCResult::Type::STR_AMOUNT, "effective-feerate", /*optional=*/true, "if the transaction was not already in the mempool, the effective feerate in " + CURRENCY_UNIT + " per KvB. For example, the package feerate and/or feerate with modified fees from prioritisetransaction."}, | 
| 967 | 0 |                             {RPCResult::Type::ARR, "effective-includes", /*optional=*/true, "if effective-feerate is provided, the wtxids of the transactions whose fees and vsizes are included in effective-feerate.", | 
| 968 | 0 |                                 {{RPCResult::Type::STR_HEX, "", "transaction wtxid in hex"}, | 
| 969 | 0 |                             }}, | 
| 970 | 0 |                         }}, | 
| 971 | 0 |                         {RPCResult::Type::STR, "error", /*optional=*/true, "Error string if rejected from mempool, or \"package-not-validated\" when the package aborts before any per-tx processing."}, | 
| 972 | 0 |                     }} | 
| 973 | 0 |                 }}, | 
| 974 | 0 |                 {RPCResult::Type::ARR, "replaced-transactions", /*optional=*/true, "List of txids of replaced transactions", | 
| 975 | 0 |                 { | 
| 976 | 0 |                     {RPCResult::Type::STR_HEX, "", "The transaction id"}, | 
| 977 | 0 |                 }}, | 
| 978 | 0 |             }, | 
| 979 | 0 |         }, | 
| 980 | 0 |         RPCExamples{ | 
| 981 | 0 |             HelpExampleRpc("submitpackage", R"(["raw-parent-tx-1", "raw-parent-tx-2", "raw-child-tx"])") + | 
| 982 | 0 |             HelpExampleCli("submitpackage", R"('["raw-tx-without-unconfirmed-parents"]')") | 
| 983 | 0 |         }, | 
| 984 | 0 |         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue | 
| 985 | 0 |         { | 
| 986 | 0 |             const UniValue raw_transactions = request.params[0].get_array(); | 
| 987 | 0 |             if (raw_transactions.empty() || raw_transactions.size() > MAX_PACKAGE_COUNT) { | 
| 988 | 0 |                 throw JSONRPCError(RPC_INVALID_PARAMETER, | 
| 989 | 0 |                                    "Array must contain between 1 and " + ToString(MAX_PACKAGE_COUNT) + " transactions."); | 
| 990 | 0 |             } | 
| 991 |  |  | 
| 992 |  |             // Fee check needs to be run with chainstate and package context | 
| 993 | 0 |             const CFeeRate max_raw_tx_fee_rate{ParseFeeRate(self.Arg<UniValue>("maxfeerate"))}; | 
| 994 | 0 |             std::optional<CFeeRate> client_maxfeerate{max_raw_tx_fee_rate}; | 
| 995 |  |             // 0-value is special; it's mapped to no sanity check | 
| 996 | 0 |             if (max_raw_tx_fee_rate == CFeeRate(0)) { | 
| 997 | 0 |                 client_maxfeerate = std::nullopt; | 
| 998 | 0 |             } | 
| 999 |  |  | 
| 1000 |  |             // Burn sanity check is run with no context | 
| 1001 | 0 |             const CAmount max_burn_amount = request.params[2].isNull() ? 0 : AmountFromValue(request.params[2]); | 
| 1002 |  | 
 | 
| 1003 | 0 |             std::vector<CTransactionRef> txns; | 
| 1004 | 0 |             txns.reserve(raw_transactions.size()); | 
| 1005 | 0 |             for (const auto& rawtx : raw_transactions.getValues()) { | 
| 1006 | 0 |                 CMutableTransaction mtx; | 
| 1007 | 0 |                 if (!DecodeHexTx(mtx, rawtx.get_str())) { | 
| 1008 | 0 |                     throw JSONRPCError(RPC_DESERIALIZATION_ERROR, | 
| 1009 | 0 |                                        "TX decode failed: " + rawtx.get_str() + " Make sure the tx has at least one input."); | 
| 1010 | 0 |                 } | 
| 1011 |  |  | 
| 1012 | 0 |                 for (const auto& out : mtx.vout) { | 
| 1013 | 0 |                     if((out.scriptPubKey.IsUnspendable() || !out.scriptPubKey.HasValidOps()) && out.nValue > max_burn_amount) { | 
| 1014 | 0 |                         throw JSONRPCTransactionError(TransactionError::MAX_BURN_EXCEEDED); | 
| 1015 | 0 |                     } | 
| 1016 | 0 |                 } | 
| 1017 |  |  | 
| 1018 | 0 |                 txns.emplace_back(MakeTransactionRef(std::move(mtx))); | 
| 1019 | 0 |             } | 
| 1020 | 0 |             CHECK_NONFATAL(!txns.empty()); | Line | Count | Source |  | 103 | 0 |     inline_check_non_fatal(condition, __FILE__, __LINE__, __func__, #condition) | 
 | 
| 1021 | 0 |             if (txns.size() > 1 && !IsChildWithParentsTree(txns)) { | 
| 1022 | 0 |                 throw JSONRPCTransactionError(TransactionError::INVALID_PACKAGE, "package topology disallowed. not child-with-parents or parents depend on each other."); | 
| 1023 | 0 |             } | 
| 1024 |  |  | 
| 1025 | 0 |             NodeContext& node = EnsureAnyNodeContext(request.context); | 
| 1026 | 0 |             CTxMemPool& mempool = EnsureMemPool(node); | 
| 1027 | 0 |             Chainstate& chainstate = EnsureChainman(node).ActiveChainstate(); | 
| 1028 | 0 |             const auto package_result = WITH_LOCK(::cs_main, return ProcessNewPackage(chainstate, mempool, txns, /*test_accept=*/ false, client_maxfeerate)); | Line | Count | Source |  | 290 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) | 
 | 
| 1029 |  | 
 | 
| 1030 | 0 |             std::string package_msg = "success"; | 
| 1031 |  |  | 
| 1032 |  |             // First catch package-wide errors, continue if we can | 
| 1033 | 0 |             switch(package_result.m_state.GetResult()) { | 
| 1034 | 0 |                 case PackageValidationResult::PCKG_RESULT_UNSET: | 
| 1035 | 0 |                 { | 
| 1036 |  |                     // Belt-and-suspenders check; everything should be successful here | 
| 1037 | 0 |                     CHECK_NONFATAL(package_result.m_tx_results.size() == txns.size()); | Line | Count | Source |  | 103 | 0 |     inline_check_non_fatal(condition, __FILE__, __LINE__, __func__, #condition) | 
 | 
| 1038 | 0 |                     for (const auto& tx : txns) { | 
| 1039 | 0 |                         CHECK_NONFATAL(mempool.exists(tx->GetHash())); | Line | Count | Source |  | 103 | 0 |     inline_check_non_fatal(condition, __FILE__, __LINE__, __func__, #condition) | 
 | 
| 1040 | 0 |                     } | 
| 1041 | 0 |                     break; | 
| 1042 | 0 |                 } | 
| 1043 | 0 |                 case PackageValidationResult::PCKG_MEMPOOL_ERROR: | 
| 1044 | 0 |                 { | 
| 1045 |  |                     // This only happens with internal bug; user should stop and report | 
| 1046 | 0 |                     throw JSONRPCTransactionError(TransactionError::MEMPOOL_ERROR, | 
| 1047 | 0 |                         package_result.m_state.GetRejectReason()); | 
| 1048 | 0 |                 } | 
| 1049 | 0 |                 case PackageValidationResult::PCKG_POLICY: | 
| 1050 | 0 |                 case PackageValidationResult::PCKG_TX: | 
| 1051 | 0 |                 { | 
| 1052 |  |                     // Package-wide error we want to return, but we also want to return individual responses | 
| 1053 | 0 |                     package_msg = package_result.m_state.ToString(); | 
| 1054 | 0 |                     CHECK_NONFATAL(package_result.m_tx_results.size() == txns.size() || | Line | Count | Source |  | 103 | 0 |     inline_check_non_fatal(condition, __FILE__, __LINE__, __func__, #condition) | 
 | 
| 1055 | 0 |                             package_result.m_tx_results.empty()); | 
| 1056 | 0 |                     break; | 
| 1057 | 0 |                 } | 
| 1058 | 0 |             } | 
| 1059 |  |  | 
| 1060 | 0 |             size_t num_broadcast{0}; | 
| 1061 | 0 |             for (const auto& tx : txns) { | 
| 1062 |  |                 // We don't want to re-submit the txn for validation in BroadcastTransaction | 
| 1063 | 0 |                 if (!mempool.exists(tx->GetHash())) { | 
| 1064 | 0 |                     continue; | 
| 1065 | 0 |                 } | 
| 1066 |  |  | 
| 1067 |  |                 // We do not expect an error here; we are only broadcasting things already/still in mempool | 
| 1068 | 0 |                 std::string err_string; | 
| 1069 | 0 |                 const auto err = BroadcastTransaction(node, tx, err_string, /*max_tx_fee=*/0, /*relay=*/true, /*wait_callback=*/true); | 
| 1070 | 0 |                 if (err != TransactionError::OK) { | 
| 1071 | 0 |                     throw JSONRPCTransactionError(err, | 
| 1072 | 0 |                         strprintf("transaction broadcast failed: %s (%d transactions were broadcast successfully)",| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 1073 | 0 |                             err_string, num_broadcast)); | 
| 1074 | 0 |                 } | 
| 1075 | 0 |                 num_broadcast++; | 
| 1076 | 0 |             } | 
| 1077 |  |  | 
| 1078 | 0 |             UniValue rpc_result{UniValue::VOBJ}; | 
| 1079 | 0 |             rpc_result.pushKV("package_msg", package_msg); | 
| 1080 | 0 |             UniValue tx_result_map{UniValue::VOBJ}; | 
| 1081 | 0 |             std::set<Txid> replaced_txids; | 
| 1082 | 0 |             for (const auto& tx : txns) { | 
| 1083 | 0 |                 UniValue result_inner{UniValue::VOBJ}; | 
| 1084 | 0 |                 result_inner.pushKV("txid", tx->GetHash().GetHex()); | 
| 1085 | 0 |                 const auto wtxid_hex = tx->GetWitnessHash().GetHex(); | 
| 1086 | 0 |                 auto it = package_result.m_tx_results.find(tx->GetWitnessHash()); | 
| 1087 | 0 |                 if (it == package_result.m_tx_results.end()) { | 
| 1088 |  |                     // No per-tx result for this wtxid | 
| 1089 |  |                     // Current invariant: per-tx results are all-or-none (every member or empty on package abort). | 
| 1090 |  |                     // If any exist yet this one is missing, it's an unexpected partial map. | 
| 1091 | 0 |                     CHECK_NONFATAL(package_result.m_tx_results.empty()); | Line | Count | Source |  | 103 | 0 |     inline_check_non_fatal(condition, __FILE__, __LINE__, __func__, #condition) | 
 | 
| 1092 | 0 |                     result_inner.pushKV("error", "package-not-validated"); | 
| 1093 | 0 |                     tx_result_map.pushKV(wtxid_hex, std::move(result_inner)); | 
| 1094 | 0 |                     continue; | 
| 1095 | 0 |                 } | 
| 1096 | 0 |                 const auto& tx_result = it->second; | 
| 1097 | 0 |                 switch(it->second.m_result_type) { | 
| 1098 | 0 |                 case MempoolAcceptResult::ResultType::DIFFERENT_WITNESS: | 
| 1099 | 0 |                     result_inner.pushKV("other-wtxid", it->second.m_other_wtxid.value().GetHex()); | 
| 1100 | 0 |                     break; | 
| 1101 | 0 |                 case MempoolAcceptResult::ResultType::INVALID: | 
| 1102 | 0 |                     result_inner.pushKV("error", it->second.m_state.ToString()); | 
| 1103 | 0 |                     break; | 
| 1104 | 0 |                 case MempoolAcceptResult::ResultType::VALID: | 
| 1105 | 0 |                 case MempoolAcceptResult::ResultType::MEMPOOL_ENTRY: | 
| 1106 | 0 |                     result_inner.pushKV("vsize", int64_t{it->second.m_vsize.value()}); | 
| 1107 | 0 |                     UniValue fees(UniValue::VOBJ); | 
| 1108 | 0 |                     fees.pushKV("base", ValueFromAmount(it->second.m_base_fees.value())); | 
| 1109 | 0 |                     if (tx_result.m_result_type == MempoolAcceptResult::ResultType::VALID) { | 
| 1110 |  |                         // Effective feerate is not provided for MEMPOOL_ENTRY transactions even | 
| 1111 |  |                         // though modified fees is known, because it is unknown whether package | 
| 1112 |  |                         // feerate was used when it was originally submitted. | 
| 1113 | 0 |                         fees.pushKV("effective-feerate", ValueFromAmount(tx_result.m_effective_feerate.value().GetFeePerK())); | 
| 1114 | 0 |                         UniValue effective_includes_res(UniValue::VARR); | 
| 1115 | 0 |                         for (const auto& wtxid : tx_result.m_wtxids_fee_calculations.value()) { | 
| 1116 | 0 |                             effective_includes_res.push_back(wtxid.ToString()); | 
| 1117 | 0 |                         } | 
| 1118 | 0 |                         fees.pushKV("effective-includes", std::move(effective_includes_res)); | 
| 1119 | 0 |                     } | 
| 1120 | 0 |                     result_inner.pushKV("fees", std::move(fees)); | 
| 1121 | 0 |                     for (const auto& ptx : it->second.m_replaced_transactions) { | 
| 1122 | 0 |                         replaced_txids.insert(ptx->GetHash()); | 
| 1123 | 0 |                     } | 
| 1124 | 0 |                     break; | 
| 1125 | 0 |                 } | 
| 1126 | 0 |                 tx_result_map.pushKV(wtxid_hex, std::move(result_inner)); | 
| 1127 | 0 |             } | 
| 1128 | 0 |             rpc_result.pushKV("tx-results", std::move(tx_result_map)); | 
| 1129 | 0 |             UniValue replaced_list(UniValue::VARR); | 
| 1130 | 0 |             for (const auto& txid : replaced_txids) replaced_list.push_back(txid.ToString()); | 
| 1131 | 0 |             rpc_result.pushKV("replaced-transactions", std::move(replaced_list)); | 
| 1132 | 0 |             return rpc_result; | 
| 1133 | 0 |         }, | 
| 1134 | 0 |     }; | 
| 1135 | 0 | } | 
| 1136 |  |  | 
| 1137 |  | void RegisterMempoolRPCCommands(CRPCTable& t) | 
| 1138 | 51.2k | { | 
| 1139 | 51.2k |     static const CRPCCommand commands[]{ | 
| 1140 | 51.2k |         {"rawtransactions", &sendrawtransaction}, | 
| 1141 | 51.2k |         {"rawtransactions", &testmempoolaccept}, | 
| 1142 | 51.2k |         {"blockchain", &getmempoolancestors}, | 
| 1143 | 51.2k |         {"blockchain", &getmempooldescendants}, | 
| 1144 | 51.2k |         {"blockchain", &getmempoolentry}, | 
| 1145 | 51.2k |         {"blockchain", &gettxspendingprevout}, | 
| 1146 | 51.2k |         {"blockchain", &getmempoolinfo}, | 
| 1147 | 51.2k |         {"blockchain", &getrawmempool}, | 
| 1148 | 51.2k |         {"blockchain", &importmempool}, | 
| 1149 | 51.2k |         {"blockchain", &savemempool}, | 
| 1150 | 51.2k |         {"hidden", &getorphantxs}, | 
| 1151 | 51.2k |         {"rawtransactions", &submitpackage}, | 
| 1152 | 51.2k |     }; | 
| 1153 | 615k |     for (const auto& c : commands) { | 
| 1154 | 615k |         t.appendCommand(c.name, &c); | 
| 1155 | 615k |     } | 
| 1156 | 51.2k | } |