Repository: bitcoin/bitcoin
File: src/rpc/mempool.cpp (C++)

Code before:

    const fs::path& dump_path = MempoolPath(args);

    if (!DumpMempool(mempool, dump_path)) {
        throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
    }

    UniValue ret(UniValue::VOBJ);
    ret.pushKV("filename", dump_path.utf8string());

    return ret;
},
    };
}

static RPCHelpMan submitpackage()
{
    return RPCHelpMan{"submitpackage",
        "Submit a package of raw transactions (serialized, hex-encoded) to local node.\n"
        "The package will be validated according to consensus and mempool policy rules. If any transaction passes, it will be accepted to mempool.\n"
        "This RPC is experimental and the interface may be unstable. Refer to doc/policy/packages.md for documentation on package policies.\n"
        "Warning: successful submission does not mean the transactions will propagate throughout the network.\n"
        ,
        {
            {"package", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of raw transactions.\n"
                "The package must solely consist of a child and its parents. None of the parents may depend on each other.\n"
                "The package must be topologically sorted, with the child being the last element in the array.",
                {
                    {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
                },
            },
            {"maxfeerate", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK())},
             "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
                 "/kvB.\nFee rates larger than 1BTC/kvB are rejected.\nSet to 0 to accept any fee rate."},
            {"maxburnamount", RPCArg::Type::AMOUNT, RPCArg::Default{FormatMoney(DEFAULT_MAX_BURN_AMOUNT)},
             "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"
             "If burning funds through unspendable outputs is desired, increase this value.\n"
             "This check is based on heuristics and does not guarantee spendability of outputs.\n"
            },
        },
        RPCResult{
            RPCResult::Type::OBJ, "", "",
            {
                {RPCResult::Type::STR, "package_msg", "The transaction package result message. \"success\" indicates all transactions were accepted into or are already in the mempool."},
                {RPCResult::Type::OBJ_DYN, "tx-results", "transaction results keyed by wtxid",
                {
                    {RPCResult::Type::OBJ, "wtxid", "transaction wtxid", {
                        {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
                        {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."},
                        {RPCResult::Type::NUM, "vsize", /*optional=*/true, "Sigops-adjusted virtual transaction size."},
                        {RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees", {

Review comment (refactor):
The orphanage can contain duplicates by txid, so adding wtxid as well could be helpful. This could be a `UniValue::VOBJ` instead, with both fields? That would allow adding more fields in the future (perhaps with different verbosity levels), e.g. telling us the size or originator.

Code after:
    if (!mempool.GetLoadTried()) {
        throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
    }

    const fs::path& dump_path = MempoolPath(args);

    if (!DumpMempool(mempool, dump_path)) {
        throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
    }

    UniValue ret(UniValue::VOBJ);
    ret.pushKV("filename", dump_path.utf8string());

    return ret;
},
    };
}

static std::vector<RPCResult> OrphanDescription()
{
    return {
        RPCResult{RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
        RPCResult{RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
        RPCResult{RPCResult::Type::NUM, "bytes", "The serialized transaction size in bytes"},
        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."},
        RPCResult{RPCResult::Type::NUM, "weight", "The transaction weight as defined in BIP 141."},
        RPCResult{RPCResult::Type::NUM_TIME, "expiration", "The orphan expiration time expressed in " + UNIX_EPOCH_TIME},
        RPCResult{RPCResult::Type::ARR, "from", "",
        {
            RPCResult{RPCResult::Type::NUM, "peer_id", "Peer ID"},
        }},
    };
}

static UniValue OrphanToJSON(const TxOrphanage::OrphanTxBase& orphan)
{
    UniValue o(UniValue::VOBJ);
    o.pushKV("txid", orphan.tx->GetHash().ToString());
    o.pushKV("wtxid", orphan.tx->GetWitnessHash().ToString());
    o.pushKV("bytes", orphan.tx->GetTotalSize());
    o.pushKV("vsize", GetVirtualTransactionSize(*orphan.tx));
    o.pushKV("weight", GetTransactionWeight(*orphan.tx));
    o.pushKV("expiration", int64_t{TicksSinceEpoch<std::chrono::seconds>(orphan.nTimeExpire)});
    UniValue from(UniValue::VARR);
    from.push_back(orphan.fromPeer); // only one fromPeer for now
    o.pushKV("from", from);
    return o;
}

static RPCHelpMan getorphantxs()
{