Tuesday, November 4, 2025

bitcoin core – Would a “Bulk Mud” relay/consensus rule (limiting 100+ sub-1,000-sat outputs, plus a ratio examine) be efficient with out unfavourable results?

I’m exploring a possible resolution to discourage UTXO-bloat patterns with out touching Script or witness semantics. This rule merely flags “bulk mud” transactions that create many very low-value outputs:

  • Threshold: not less than 100 outputs with worth < 1,000 sats
  • Ratio: these “tiny” (sub-1,000 sat) outputs are ≥ 60% of all outputs within the Tx

The purpose isn’t to get rid of all arbitrary information makes use of, however to lift prices for patterns most related to UTXO progress (low cost, “bulk mud” Txs), whereas leaving typical funds, channel opens, and one-off inscriptions unaffected.

Patterns this is able to restrict (not essentially get rid of)

  • Faux pubkeys hiding information (UTXO fan-outs)
  • Bitcoin STAMPS / UTXO-art utilizing many mud UTXOs
  • BRC-20 batch mints that fan out tiny outputs
  • Some batched Ordinal inscriptions that unfold information/state throughout many small UTXOs
  • Mud bombing (monitoring) / UTXO squatting / resource-exhaustion makes an attempt
  • Mass micro-airdrops of sub-1k-sat outputs (collateral impact)

Non-goals / Not coated

  • Single/few-output inscriptions with massive witness information (these wouldn’t set off the “bulk mud” heuristic)
  • Any scheme that merely makes use of ≥1,000 sat per output (economically costlier however nonetheless legitimate)

Why a ratio + depend?

Requiring each (tiny_count ≥ 100) and (tiny_count / total_outputs ≥ 0.6) reduces false positives (e.g., massive custodial batch payouts with a mixture of values). It focuses on transactions which might be largely product of dust-like outputs.

Ask

  • Are there credible, non-spam use-cases that want ≥100 sub-1k-sat outputs with ≥60% tiny ratio in a single tx?
  • Are there coverage pitfalls I’m lacking (e.g., payment market dynamics, odd coinbase behaviors, privateness instruments)?
  • Any prior artwork or measurements you possibly can level to in regards to the prevalence of such transactions?

(with syntax highlighting right here: https://pastebin.com/tYsvDh2R)

RELAY POLICY FILTER sketch

// Place in /coverage/coverage.cpp, and name from inside IsStandardTx() earlier than returning:
//     if (IsBulkDust(tx, motive))
//         return false;   // reject as nonstandard


bool IsBulkDust(const CTransaction& tx, std::string& motive)
{
    static constexpr CAmount MIN_OUTPUT_VALUE_SATS = 1000;   // < 1000 sats counts as "tiny"
    static constexpr int     MAX_TINY_OUTPUTS      = 100;    // >= 100 tiny outputs triggers ratio examine
    static constexpr double  TINY_RATIO_THRESHOLD  = 0.6;    // >= 60% of all outputs tiny = reject
 
    int tiny = 0;
    const int whole = tx.vout.dimension();
 
    // Sanity examine — keep away from division by zero
    if (whole == 0)
        return false;
 
    // Rely any spendable output beneath 1000 sats as "tiny"
    for (const auto& out : tx.vout) {
        if (out.nValue < MIN_OUTPUT_VALUE_SATS)
            ++tiny;
    }
 
    // Threshold + ratio examine
    if (tiny >= MAX_TINY_OUTPUTS && (static_cast(tiny) / whole) >= TINY_RATIO_THRESHOLD)
    {
        motive = strprintf("too-many-tiny-outputs(%d of %d, %.2f%%)", tiny, whole, 100.0 * tiny / whole);
        return true;  // flag as bulk mud
    }
 
    return false;
}

CONSENSUS (soft-fork, hybrid activation) sketch

// Helpers in /consensus/tx_check.cpp; activation/enforcement in /validation.cpp
// Additionally outline deployment in: /consensus/params.h, /chainparams.cpp, /versionbits.*

 
// -----------------------------------------------------------------------
// --- In /consensus/tx_check.cpp (helper solely; no params wanted) ---
// -----------------------------------------------------------------------
 
static constexpr CAmount MIN_OUTPUT_VALUE_SATS = 1000;   // < 1000 sats counts as "tiny"
static constexpr int     MAX_TINY_OUTPUTS      = 100;    // >= 100 tiny outputs triggers ratio examine
static constexpr double  TINY_RATIO_THRESHOLD  = 0.6;    // >= 60% of all outputs tiny = reject
 
bool IsBulkDust(const CTransaction& tx)    // expose through tx_check.h if wanted
{
    int tiny = 0;
    const int whole = tx.vout.dimension();
 
    // Sanity examine — keep away from division by zero
    if (whole == 0)
        return false;
 
    // Rely any spendable output beneath 1000 sats as "tiny"
    for (const auto& out : tx.vout) {
        if (out.nValue < MIN_OUTPUT_VALUE_SATS)
            ++tiny;
    }
 
    // Threshold + ratio examine
    if (tiny >= MAX_TINY_OUTPUTS && ((static_cast(tiny) / whole) >= TINY_RATIO_THRESHOLD))
        return true;
    
    return false;
}
 
 
// -----------------------------------------------------------------------
// --- In /validation.cpp (enforcement with hybrid activation) ---
// -----------------------------------------------------------------------
 
#embrace 
#embrace 
 
// ... inside the suitable validation path (e.g., after primary tx checks),
// with entry to chainparams/params and a tip pointer:
 
const Consensus::Params& params = chainparams.GetConsensus();
 
const bool bulk_dust_active =
    DeploymentActiveAtTip(params, Consensus::DEPLOYMENT_BULK_DUST_LIMIT) ||
    (chainActive.Tip() && chainActive.Tip()->nHeight >= params.BulkDustActivationHeight);
 
if (bulk_dust_active) {
    if (IsBulkDust(tx)) {
        return state.Invalid(TxValidationResult::TX_CONSENSUS, "too-many-tiny-outputs");
    }
}
 
 
// -----------------------------------------------------------------------
// --- In /consensus/params.h ---
// -----------------------------------------------------------------------
 
enum DeploymentPos {
    // ...
    DEPLOYMENT_BULK_DUST_LIMIT,
    MAX_VERSION_BITS_DEPLOYMENTS
};
 
struct Params {
    // ...
    int BulkDustActivationHeight; // top flag-day fallback
};
 
 
// -----------------------------------------------------------------------
// --- In /chainparams.cpp (per-network values; examples solely) ---
// -----------------------------------------------------------------------
 
consensus.vDeployments[Consensus::DEPLOYMENT_BULK_DUST_LIMIT].bit = 12;
consensus.vDeployments[Consensus::DEPLOYMENT_BULK_DUST_LIMIT].nStartTime = 1767225600;  // 2026-01-01 UTC
consensus.vDeployments[Consensus::DEPLOYMENT_BULK_DUST_LIMIT].nTimeout   = 1838160000;  // 2028-04-01 UTC
consensus.vDeployments[Consensus::DEPLOYMENT_BULK_DUST_LIMIT].min_activation_height = 969696;
 
consensus.BulkDustActivationHeight = 1021021;  // flag-day fallback

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Stay Connected

0FansLike
0FollowersFollow
0SubscribersSubscribe
- Advertisement -spot_img

Latest Articles