Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ get_directory_property(precious_variables CACHE_VARIABLES)
#=============================
# Project / Package metadata
#=============================
set(CLIENT_NAME "Bitcoin Core")
set(CLIENT_NAME "Bitcoin DOG Mode")
set(CLIENT_VERSION_MAJOR 31)
set(CLIENT_VERSION_MINOR 1)
set(CLIENT_VERSION_BUILD 0)
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ For an immediately usable, binary version of the Bitcoin DOG Mode software, see
What is Bitcoin DOG Mode?
---------------------

Bitcoin DOG Mode connects to the Bitcoin peer-to-peer network to download and fully
validate blocks and transactions. It also includes a wallet and graphical user
interface, which can be optionally built.
Bitcoin DOG Mode is Bitcoin Core v31.1 with a small relay-policy overlay: larger
standard transactions (3.9M WU), a 1-sat dust floor, and a `/DOGMode/` P2P
user-agent. Consensus rules are unchanged.

Further information about Bitcoin DOG Mode is available in the [doc folder](/doc).
See [doc/policy/dogmode.md](doc/policy/dogmode.md) for the exact knobs,
side effects (`-maxmempool` floor), and non-goals. Release notes:
[doc/release-notes-dogmode.md](doc/release-notes-dogmode.md).

License
-------
Expand Down
41 changes: 41 additions & 0 deletions doc/policy/dogmode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Bitcoin DOG Mode relay policy

DOG Mode is Bitcoin Core v31.1 with a small, explicit relay-policy overlay.
Consensus rules are unchanged. A miner still has to include any transaction this
node forwards.

This document is the policy spec. If a default is not listed here, it is Core's.

## What we change

| Knob | Core v31.1 | DOG Mode | Notes |
| --- | --- | --- | --- |
| `MAX_STANDARD_TX_WEIGHT` | 400,000 WU | 3,900,000 WU | Near a full block. |
| `MAX_PACKAGE_WEIGHT` | 404,000 WU | 3,904,000 WU | Same 4,000 WU headroom Core uses, so a max-size tx can still sit in a package with a tiny child. |
| `DEFAULT_CLUSTER_SIZE_LIMIT_KVB` | 101 | 976 | Forced by the package/cluster `static_assert` chain. |
| `-dustrelayfee` default | 3000 sat/kvB | 0 sat/kvB | 1-sat floor on standard output types. The flag still works; set `3000` to restore Core. |
| P2P user-agent | `/Satoshi:31.1.0/` | `/DOGMode:31.1.0/` | So peers and crawlers can tell the clients apart. |

## What we deliberately do not change

- Consensus, block weight, or subsidy.
- `-minrelaytxfee` / incremental relay fee.
- RBF policy.
- Wallet defaults other than inheriting the new standardness ceiling.
- **Datacarrier / OP_RETURN.** `MAX_OP_RETURN_RELAY` stays 100,000 vB. It is *not* derived from `MAX_STANDARD_TX_WEIGHT`. Raising the tx-weight constant without pinning this would silently lift OP_RETURN to 975,000 vB.

## Operator-visible side effects

`CTxMemPool::Flatten()` requires `-maxmempool >= cluster_size_vbytes * 40`. With a 976 kvB cluster that is **40 MB**. A node given a smaller `-maxmempool` will not start.

`-blocksonly` used to imply `-maxmempool=5`. That default is raised to the same 40 MB floor.

Tests or configs that need a tiny mempool (for example `fill_mempool` at 5 MB) must also pin `-limitclustersize=101`.

## What this release does not include

Preferential peering (`NODE_DOG_RELAY`) is a separate change. Shipping the policy defaults without it means large or dusty transactions still die at the first Core peer. That work belongs in a follow-up once the service-bit question in [PR #2](https://github.com/bitcoindogmode/bitcoin/pull/2) is decided.

## UTXO cost of 1-sat outputs

A 1-sat output is cheap for the creator and permanent for every full node. Measured on regtest against v31.1 (see https://github.com/tialkan/bitcoin-dust-utxo-lab): about 43 bytes of chainstate per output after LevelDB settles. Spending a P2TR key-path input is ~57.5 vB, so a 1-sat output is not economically spendable at the 1 sat/vB relay floor. Treat these as markers, not recovered value.
2 changes: 1 addition & 1 deletion doc/policy/packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ parent of a parent should not be in this package (unless this "grandparent" is a

The following rules are enforced for all packages:

* Packages cannot exceed `MAX_PACKAGE_COUNT=25` count and `MAX_PACKAGE_WEIGHT=404000` total weight
* Packages cannot exceed `MAX_PACKAGE_COUNT=25` count and `MAX_PACKAGE_WEIGHT=3904000` total weight
(#20833)

- *Rationale*: We want package size to be as small as possible to mitigate DoS via package
Expand Down
23 changes: 23 additions & 0 deletions doc/release-notes-dogmode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Bitcoin DOG Mode 31.1 overlay

Based on Bitcoin Core 31.1. Consensus-compatible. Relay policy only.

## Policy defaults

- Standard transaction weight ceiling: **3,900,000 WU** (Core: 400,000).
- Package weight ceiling: **3,904,000 WU** (Core: 404,000).
- Default cluster size: **976 kvB** (Core: 101).
- Default `-dustrelayfee`: **0** (Core: 3000 sat/kvB), i.e. a 1-sat dust floor.
- Default `-datacarriersize`: **100,000 vB**, unchanged. This is pinned on purpose so the weight change does not silently raise OP_RETURN.
- P2P user-agent: `/DOGMode:31.1.0/`.

## Migration

- `-maxmempool` must be at least **40 MB**, or the node will refuse to start (`-maxmempool must be at least 40 MB`).
- `-blocksonly` now implies `-maxmempool=40` rather than `5`.
- To keep a 5 MB mempool for testing, also set `-limitclustersize=101`.
- To restore Core's dust floor: `-dustrelayfee=0.00003000`.

## Not in this tag

Preferential peering is not included. Without it, transactions that only this policy accepts still need a path to a miner (direct submission, or a later peering patch).
2 changes: 1 addition & 1 deletion src/clientversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ using util::Join;
* for both bitcoind and bitcoin-qt, to make it harder for attackers to
* target servers or GUI users specifically.
*/
const std::string UA_NAME("Satoshi");
const std::string UA_NAME("DOGMode");


#include <bitcoin-build-info.h>
Expand Down
12 changes: 10 additions & 2 deletions src/kernel/mempool_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,18 @@

class ValidationSignals;

/** Flatten() requires -maxmempool >= cluster_size_vbytes * this factor. */
static constexpr unsigned int MEMPOOL_CLUSTER_MEMORY_FACTOR{40};
/** Minimum -maxmempool in MB implied by DEFAULT_CLUSTER_SIZE_LIMIT_KVB and Flatten(). */
static constexpr unsigned int MIN_MAX_MEMPOOL_SIZE_MB{
static_cast<unsigned int>((static_cast<uint64_t>(DEFAULT_CLUSTER_SIZE_LIMIT_KVB) * 1'000 * MEMPOOL_CLUSTER_MEMORY_FACTOR + 999'999) / 1'000'000)
};
/** Default for -maxmempool, maximum megabytes of mempool memory usage */
static constexpr unsigned int DEFAULT_MAX_MEMPOOL_SIZE_MB{300};
/** Default for -maxmempool when blocksonly is set */
static constexpr unsigned int DEFAULT_BLOCKSONLY_MAX_MEMPOOL_SIZE_MB{5};
static_assert(DEFAULT_MAX_MEMPOOL_SIZE_MB >= MIN_MAX_MEMPOOL_SIZE_MB);
/** Default for -maxmempool when blocksonly is set.
* Raised from Core's 5 MB so -blocksonly still satisfies Flatten()'s cluster-size floor. */
static constexpr unsigned int DEFAULT_BLOCKSONLY_MAX_MEMPOOL_SIZE_MB{MIN_MAX_MEMPOOL_SIZE_MB};
/** Default for -mempoolexpiry, expiration time for mempool transactions in hours */
static constexpr unsigned int DEFAULT_MEMPOOL_EXPIRY_HOURS{336};
/** Whether to fall back to legacy V1 serialization when writing mempool.dat */
Expand Down
3 changes: 2 additions & 1 deletion src/policy/packages.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ static constexpr uint32_t MAX_PACKAGE_COUNT{25};
to allow for context-less checks. This must allow a superset of sigops
weighted vsize limited transactions to not disallow transactions we would
have otherwise accepted individually. */
static constexpr uint32_t MAX_PACKAGE_WEIGHT = 404'000;
/** 3.9M WU standard tx plus the same 4,000 WU headroom Core v31.1 used (400k vs 404k). */
static constexpr uint32_t MAX_PACKAGE_WEIGHT = 3'904'000;
static_assert(MAX_PACKAGE_WEIGHT >= MAX_STANDARD_TX_WEIGHT);

// Packages are part of a single cluster, so ensure that the package limits are
Expand Down
5 changes: 3 additions & 2 deletions src/policy/policy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
// need a CTxIn of at least 148 bytes to spend:
// so dust is a spendable txout less than
// 182*dustRelayFee/1000 (in satoshis).
// 546 satoshis at the default rate of 3000 sat/kvB.
// 546 satoshis at Bitcoin Core's historical default of 3000 sat/kvB.
// DOG Mode's default dust relay fee is 0, which makes this 0 (1-sat floor in practice).
// A typical spendable segwit P2WPKH txout is 31 bytes big, and will
// need a CTxIn of at least 67 bytes to spend:
// so dust is a spendable txout less than
// 98*dustRelayFee/1000 (in satoshis).
// 294 satoshis at the default rate of 3000 sat/kvB.
// 294 satoshis at Bitcoin Core's historical default of 3000 sat/kvB.
if (txout.scriptPubKey.IsUnspendable())
return 0;

Expand Down
24 changes: 15 additions & 9 deletions src/policy/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ static constexpr unsigned int DEFAULT_COINBASE_OUTPUT_MAX_ADDITIONAL_SIGOPS{400}
static constexpr unsigned int MINIMUM_BLOCK_RESERVED_WEIGHT{2000};
/** Default for -blockmintxfee, which sets the minimum feerate for a transaction in blocks created by mining code **/
static constexpr unsigned int DEFAULT_BLOCK_MIN_TX_FEE{1};
/** The maximum weight for transactions we're willing to relay/mine */
static constexpr int32_t MAX_STANDARD_TX_WEIGHT{400000};
/** The maximum weight for transactions we're willing to relay/mine.
* DOG Mode: 3.9M WU so a single standard tx can fill nearly a whole block. */
static constexpr int32_t MAX_STANDARD_TX_WEIGHT{3'900'000};
/** The minimum non-witness size for transactions we're willing to relay/mine: one larger than 64 */
static constexpr unsigned int MIN_STANDARD_TX_NONWITNESS_SIZE{65};
/** Maximum number of signature check operations in an IsStandard() P2SH script */
Expand All @@ -61,16 +62,18 @@ static constexpr unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE{3600};
static constexpr unsigned int MAX_STANDARD_SCRIPTSIG_SIZE{1650};
/** Min feerate for defining dust.
* Changing the dust limit changes which transactions are
* standard and should be done with care and ideally rarely. It makes sense to
* only increase the dust limit after prior releases were already not creating
* outputs below the new threshold */
static constexpr unsigned int DUST_RELAY_TX_FEE{3000};
* standard and should be done with care and ideally rarely.
* DOG Mode default is 0 sat/kvB, which yields a 1-sat floor on standard
* output types. Operators can restore Core's 3000 sat/kvB with -dustrelayfee. */
static constexpr unsigned int DUST_RELAY_TX_FEE{0};
/** Default for -minrelaytxfee, minimum relay fee for transactions */
static constexpr unsigned int DEFAULT_MIN_RELAY_TX_FEE{100};
/** Maximum number of transactions per cluster (default) */
static constexpr unsigned int DEFAULT_CLUSTER_LIMIT{64};
/** Maximum size of cluster in virtual kilobytes */
static constexpr unsigned int DEFAULT_CLUSTER_SIZE_LIMIT_KVB{101};
/** Maximum size of cluster in virtual kilobytes.
* Must satisfy MAX_PACKAGE_WEIGHT <= this * WITNESS_SCALE_FACTOR * 1000.
* Flatten() also requires -maxmempool >= this * 1000 vB * 40. */
static constexpr unsigned int DEFAULT_CLUSTER_SIZE_LIMIT_KVB{976};
/** Default for -limitancestorcount, max number of in-mempool ancestors */
static constexpr unsigned int DEFAULT_ANCESTOR_LIMIT{25};
/** Default for -limitdescendantcount, max number of in-mempool descendants */
Expand All @@ -79,8 +82,11 @@ static constexpr unsigned int DEFAULT_DESCENDANT_LIMIT{25};
static const bool DEFAULT_ACCEPT_DATACARRIER = true;
/**
* Default setting for -datacarriersize in vbytes.
* Intentionally not derived from MAX_STANDARD_TX_WEIGHT: raising the standard
* tx weight to 3.9M WU would otherwise silently lift this to 975,000 vB.
* This keeps Core v31.1's 100,000 vB default; change it here if that is wanted.
*/
static const unsigned int MAX_OP_RETURN_RELAY = MAX_STANDARD_TX_WEIGHT / WITNESS_SCALE_FACTOR;
static constexpr unsigned int MAX_OP_RETURN_RELAY{100'000};
/**
* An extra transaction can be added to a package, as long as it only has one
* ancestor and is no larger than this. Not really any reason to make this
Expand Down
34 changes: 22 additions & 12 deletions src/test/transaction_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -777,7 +777,8 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)

CheckIsStandard(t);

// Check dust with default relay fee:
// Check dust with Core's historical 3000 sat/kvB relay fee (DOG Mode default is 0):
g_dust = CFeeRate{3000};
CAmount nDustThreshold = 182 * g_dust.GetFeePerK() / 1000;
BOOST_CHECK_EQUAL(nDustThreshold, 546);

Expand Down Expand Up @@ -821,6 +822,9 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
t.vout[0].nValue = 674;
CheckIsStandard(t);
g_dust = CFeeRate{DUST_RELAY_TX_FEE};
BOOST_CHECK_EQUAL(DUST_RELAY_TX_FEE, 0);
t.vout[0].nValue = 1;
CheckIsStandard(t); // 1-sat P2PKH is standard at the DOG Mode default

t.vout[0].scriptPubKey = CScript() << OP_1;
CheckIsNotStandard(t, "scriptpubkey");
Expand Down Expand Up @@ -920,21 +924,23 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
CheckIsStandard(t);
}

// Check tx-size (non-standard if transaction weight is > MAX_STANDARD_TX_WEIGHT)
// Check tx-size (non-standard if transaction weight is > MAX_STANDARD_TX_WEIGHT).
// Pad with empty-scriptSig inputs (41 bytes / 164 WU each) so the tx stays
// datacarrier-standard at the 100k OP_RETURN default.
t.vin.clear();
t.vin.resize(2438); // size per input (empty scriptSig): 41 bytes
t.vout[0].scriptPubKey = CScript() << OP_RETURN << std::vector<unsigned char>(19, 0); // output size: 30 bytes
// tx header: 12 bytes => 48 weight units
// 2438 inputs: 2438*41 = 99958 bytes => 399832 weight units
// 1 output: 30 bytes => 120 weight units
// ======================================
// total: 400000 weight units
BOOST_CHECK_EQUAL(GetTransactionWeight(CTransaction(t)), 400000);
constexpr int32_t kInputBytes{41};
constexpr int32_t kHeaderAndOutputBytes{12 + 30};
const int32_t target_bytes{MAX_STANDARD_TX_WEIGHT / WITNESS_SCALE_FACTOR};
const int32_t n_inputs{(target_bytes - kHeaderAndOutputBytes) / kInputBytes};
t.vin.resize(n_inputs);
const int32_t exact_weight{GetTransactionWeight(CTransaction(t))};
BOOST_CHECK_LE(exact_weight, MAX_STANDARD_TX_WEIGHT);
BOOST_CHECK_GT(exact_weight, MAX_STANDARD_TX_WEIGHT - kInputBytes * WITNESS_SCALE_FACTOR);
CheckIsStandard(t);

// increase output size by one byte, so we end up with 400004 weight units
t.vout[0].scriptPubKey = CScript() << OP_RETURN << std::vector<unsigned char>(20, 0); // output size: 31 bytes
BOOST_CHECK_EQUAL(GetTransactionWeight(CTransaction(t)), 400004);
t.vin.resize(n_inputs + 1);
BOOST_CHECK_GT(GetTransactionWeight(CTransaction(t)), MAX_STANDARD_TX_WEIGHT);
CheckIsNotStandard(t, "tx-size");

// Check bare multisig (standard if policy flag g_bare_multi is set)
Expand All @@ -952,6 +958,10 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
assert(t.vout.size() == 1);
t.vout.insert(t.vout.end(), MAX_DUST_OUTPUTS_PER_TX, {0, t.vout[0].scriptPubKey});

// Dust floors below are Core's historical 3000 sat/kvB values, not the
// DOG Mode default of 0.
g_dust = CFeeRate{3000};

// Check compressed P2PK outputs dust threshold (must have leading 02 or 03)
t.vout[0].scriptPubKey = CScript() << std::vector<unsigned char>(33, 0x02) << OP_CHECKSIG;
t.vout[0].nValue = 576;
Expand Down
14 changes: 10 additions & 4 deletions src/test/txpackage_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,9 @@ BOOST_AUTO_TEST_CASE(package_sanitization_tests)
BOOST_CHECK_EQUAL(state_too_many.GetResult(), PackageValidationResult::PCKG_POLICY);
BOOST_CHECK_EQUAL(state_too_many.GetRejectReason(), "package-too-many-transactions");

// Packages can't have a total weight of more than 404'000WU.
CTransactionRef large_ptx = create_placeholder_tx(150, 150);
// Packages can't have a total weight of more than MAX_PACKAGE_WEIGHT.
// Use a large placeholder so the weight limit trips before MAX_PACKAGE_COUNT.
CTransactionRef large_ptx = create_placeholder_tx(2000, 2000);
Package package_too_large;
auto size_large = GetTransactionWeight(*large_ptx);
size_t total_weight{0};
Expand Down Expand Up @@ -239,8 +240,13 @@ BOOST_AUTO_TEST_CASE(package_validation_tests)
BOOST_CHECK_EQUAL(it_child->second.m_wtxids_fee_calculations.value().front(), tx_child->GetWitnessHash());
}
// A single, giant transaction submitted through ProcessNewPackage fails on single tx policy.
CTransactionRef giant_ptx = create_placeholder_tx(999, 999);
BOOST_CHECK(GetVirtualTransactionSize(*giant_ptx) > DEFAULT_CLUSTER_SIZE_LIMIT_KVB * 1000);
CTransactionRef giant_ptx = create_placeholder_tx(2000, 2000);
size_t giant_n{2000};
while (GetTransactionWeight(*giant_ptx) <= MAX_STANDARD_TX_WEIGHT) {
giant_n += 500;
giant_ptx = create_placeholder_tx(giant_n, giant_n);
}
BOOST_CHECK(GetTransactionWeight(*giant_ptx) > MAX_STANDARD_TX_WEIGHT);
Package package_single_giant{giant_ptx};
auto result_single_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_single_giant, /*test_accept=*/true, /*client_maxfeerate=*/{});
if (auto err_single_large{CheckPackageMempoolAcceptResult(package_single_giant, result_single_large, /*expect_valid=*/false, nullptr)}) {
Expand Down
4 changes: 2 additions & 2 deletions src/test/txvalidation_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ BOOST_FIXTURE_TEST_CASE(ephemeral_tests, RegTestingSetup)
TxValidationState child_state;
Wtxid child_wtxid;

// Arbitrary non-0 feerate for these tests
CFeeRate dustrelay(DUST_RELAY_TX_FEE);
// Arbitrary non-0 feerate for these tests (DOG Mode default dust relay fee is 0)
CFeeRate dustrelay{3000};

// Basic transaction with dust
auto grandparent_tx_1 = make_ephemeral_tx(random_outpoints(1), /*version=*/2);
Expand Down
2 changes: 1 addition & 1 deletion src/txmempool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ CTxMemPool::setEntries CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEnt
static CTxMemPool::Options&& Flatten(CTxMemPool::Options&& opts, bilingual_str& error)
{
opts.check_ratio = std::clamp<int>(opts.check_ratio, 0, 1'000'000);
int64_t cluster_limit_bytes = opts.limits.cluster_size_vbytes * 40;
int64_t cluster_limit_bytes = opts.limits.cluster_size_vbytes * MEMPOOL_CLUSTER_MEMORY_FACTOR;
if (opts.max_size_bytes < 0 || (opts.max_size_bytes > 0 && opts.max_size_bytes < cluster_limit_bytes)) {
error = strprintf(_("-maxmempool must be at least %d MB"), std::ceil(cluster_limit_bytes / 1'000'000.0));
}
Expand Down
16 changes: 6 additions & 10 deletions src/wallet/test/coinselector_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1370,23 +1370,19 @@ BOOST_AUTO_TEST_CASE(check_max_selection_weight)
{
// Scenario 3:

// The actor starts with 1515x 0.033 BTC (49.995 BTC total) unspent outputs
// No results should be returned, because the transaction would be too large

// Perform selection
// The actor starts with two 25 BTC outputs whose signed input size is
// large enough that selecting both exceeds MAX_STANDARD_TX_WEIGHT.
// Target 49.5 BTC requires both coins, so selection must fail.
const int too_heavy_vbytes{MAX_STANDARD_TX_WEIGHT / (2 * WITNESS_SCALE_FACTOR) + 1'000};
const auto result = select_coins(
target, cs_params, cc, [&](CWallet& wallet) {
CoinsResult available_coins;
for (int j = 0; j < 1515; ++j) {
add_coin(available_coins, wallet, CAmount(0.033 * COIN), CFeeRate(0), 144, false, 0, true);
}
add_coin(available_coins, wallet, CAmount(25 * COIN), CFeeRate(0), 144, false, 0, true, too_heavy_vbytes);
add_coin(available_coins, wallet, CAmount(25 * COIN), CFeeRate(0), 144, false, 0, true, too_heavy_vbytes);
return available_coins;
},
m_node);

// No results
// 1515 inputs * 68 bytes = 103,020 bytes
// 103,020 bytes * 4 = 412,080 weight, which is above the MAX_STANDARD_TX_WEIGHT of 400,000
BOOST_CHECK(!result);
}
}
Expand Down
Loading