Skip to content
Merged
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
42 changes: 42 additions & 0 deletions doc/release-notes-dogmode-policy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
$DOG Mode policy changes
========================

This release relaxes several transaction relay policies, so that $DOG Mode
nodes relay and mine a wider range of transactions. Nodes with the old
policies will not relay these transactions, so $DOG Mode nodes additionally
preferentially peer with each other (see below).

Policy
------

- The maximum standard transaction weight has been raised from 400,000 WU
(100 kvB) to 3,900,000 WU (975 kvB), just under the 4,000,000 WU consensus
block weight limit. Transactions larger than 975 kvB remain non-standard.
The mempool cluster size limit default (`-limitclustersize`) is raised
accordingly from 101 kvB to 976 kvB, and the maximum package weight for
package relay from 404,000 WU to 3,904,000 WU.

- Since `MAX_OP_RETURN_RELAY` is defined as one quarter of the maximum
standard transaction weight, the default `-datacarriersize` rises from
100,000 to 975,000 bytes.

- The minimum `-maxmempool` value remains ~5 MB; the sanity check tying it to
the cluster size limit now requires 5x (was 40x) the cluster size limit.

- The dust limit is lowered from 294-546 sats (depending on output type) to a
global 1 sat: only zero-value spendable outputs are now considered dust.
`-dustrelayfee` is retained, but it can only lower the dust limit further
(0 disables it entirely, as before), never raise it above 1 sat.

P2P
---

- Nodes now advertise the `NODE_DOG_MODE` service flag (bit 14) and open up to
4 extra long-lived outbound connections ("dog" connection type) to peers
advertising the same flag, so that $DOG Mode nodes preferentially peer with
each other. This is a port of Libre Relay's preferential peering (which uses
bit 29). Peers connected to via a dog connection that do not advertise
`NODE_DOG_MODE` are disconnected after the version handshake.

- The "dog" connection type is visible in `getpeerinfo`, `bitcoin-cli
-netinfo` and the GUI peers window.
19 changes: 16 additions & 3 deletions src/bench/txorphanage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,13 @@ static void OrphanageSinglePeerEviction(benchmark::Bench& bench)
for (unsigned int i{0}; i < NUM_TINY_TRANSACTIONS; ++i) {
tiny_txs.emplace_back(MakeTransactionBulkedTo(1, TINY_TX_WEIGHT, det_rand));
}
auto large_tx = MakeTransactionBulkedTo(1, MAX_STANDARD_TX_WEIGHT, det_rand);
// Cap the large transaction to the global usage limit. MakeTxOrphanage permits transactions up to
// MAX_STANDARD_TX_WEIGHT, but policy may raise that limit beyond what the orphanage's reserved
// per-peer usage can hold (e.g. $DOG Mode), which would make the transaction impossible to fit and
// break the expected-eviction arithmetic below.
static constexpr node::TxOrphanage::Usage LARGE_TX_WEIGHT{
MAX_STANDARD_TX_WEIGHT < node::DEFAULT_RESERVED_ORPHAN_WEIGHT_PER_PEER ? MAX_STANDARD_TX_WEIGHT : node::DEFAULT_RESERVED_ORPHAN_WEIGHT_PER_PEER};
auto large_tx = MakeTransactionBulkedTo(1, LARGE_TX_WEIGHT, det_rand);
assert(GetTransactionWeight(*large_tx) <= MAX_STANDARD_TX_WEIGHT);

const auto orphanage{node::MakeTxOrphanage(/*max_global_latency_score=*/node::DEFAULT_MAX_ORPHANAGE_LATENCY_SCORE, /*reserved_peer_usage=*/node::DEFAULT_RESERVED_ORPHAN_WEIGHT_PER_PEER)};
Expand Down Expand Up @@ -93,6 +99,13 @@ static void OrphanageSinglePeerEviction(benchmark::Bench& bench)
assert(orphanage->TotalLatencyScore() <= orphanage->MaxGlobalLatencyScore());
assert(orphanage->TotalOrphanUsage() + TINY_TX_WEIGHT > orphanage->MaxGlobalUsage());

// Expected evictions: enough tiny transactions must be evicted to cover the usage deficit after
// adding the large transaction. The large transaction itself raises the announcement count by
// one, so subtract it from the expected number evicted during the trim.
const auto expected_deficit{orphanage->TotalOrphanUsage() + GetTransactionWeight(*large_tx) - orphanage->MaxGlobalUsage()};
assert(expected_deficit > 0 && expected_deficit <= orphanage->TotalOrphanUsage());
const auto expected_evictions{(expected_deficit + TINY_TX_WEIGHT - 1) / TINY_TX_WEIGHT - 1};

bench.epochs(1).epochIterations(1).run([&]() NO_THREAD_SAFETY_ANALYSIS {
// Lastly, add the large transaction.
const auto num_announcements_before_trim{orphanage->CountAnnouncements()};
Expand All @@ -104,7 +117,7 @@ static void OrphanageSinglePeerEviction(benchmark::Bench& bench)

// The number of evictions is the same regardless of the number of peers. In both cases, we can exceed the
// usage limit using 1 maximally-sized transaction.
assert(num_evicted == MAX_STANDARD_TX_WEIGHT / TINY_TX_WEIGHT);
assert(num_evicted == expected_evictions);
});
}
static void OrphanageMultiPeerEviction(benchmark::Bench& bench)
Expand All @@ -117,7 +130,7 @@ static void OrphanageMultiPeerEviction(benchmark::Bench& bench)
// Subtract 4 because BulkTransaction rounds up and we must avoid going over the weight limit early.
static constexpr node::TxOrphanage::Usage LARGE_TX_WEIGHT{TOTAL_USAGE_LIMIT / NUM_UNIQUE_TXNS - 4};
static_assert(LARGE_TX_WEIGHT >= TINY_TX_WEIGHT * 2, "Tx is too small, increase NUM_PEERS");
// The orphanage does not permit any transactions larger than 400'000, so this test will not work if the large tx is much larger.
// The orphanage does not permit any transactions larger than MAX_STANDARD_TX_WEIGHT, so this test will not work if the large tx is much larger.
static_assert(LARGE_TX_WEIGHT <= MAX_STANDARD_TX_WEIGHT, "Tx is too large, decrease NUM_PEERS");

FastRandomContext det_rand{true};
Expand Down
9 changes: 8 additions & 1 deletion src/bitcoin-cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ class NetinfoRequestHandler : public BaseRequestHandler
private:
std::array<std::array<uint16_t, NETWORKS.size() + 1>, 3> m_counts{{{}}}; //!< Peer counts by (in/out/total, networks/total)
uint8_t m_block_relay_peers_count{0};
uint8_t m_dog_peers_count{0};
uint8_t m_manual_peers_count{0};
uint8_t m_details_level{0}; //!< Optional user-supplied arg to set dashboard details level
bool DetailsRequested() const { return m_details_level; }
Expand Down Expand Up @@ -452,6 +453,7 @@ class NetinfoRequestHandler : public BaseRequestHandler
if (conn_type == "manual" || conn_type == "feeler") return conn_type;
if (conn_type == "addr-fetch") return "addr";
if (conn_type == "private-broadcast") return "priv";
if (conn_type == "dog") return "dog";
return "";
}
std::string FormatServices(const UniValue& services)
Expand Down Expand Up @@ -530,6 +532,7 @@ class NetinfoRequestHandler : public BaseRequestHandler
++m_counts.at(2).at(network_id); // total by network
++m_counts.at(2).at(NETWORKS.size()); // total overall
if (conn_type == "block-relay-only") ++m_block_relay_peers_count;
if (conn_type == "dog") ++m_dog_peers_count;
if (conn_type == "manual") ++m_manual_peers_count;
if (m_outbound_only_selected && !is_outbound) continue;
if (DetailsRequested()) {
Expand Down Expand Up @@ -633,6 +636,7 @@ class NetinfoRequestHandler : public BaseRequestHandler
}

result += " total block";
if (m_dog_peers_count) result += " dog";
if (m_manual_peers_count) result += " manual";

const std::array rows{"in", "out", "total"};
Expand All @@ -642,8 +646,9 @@ class NetinfoRequestHandler : public BaseRequestHandler
result += strprintf("%8i", m_counts.at(i).at(n)); // network peers count
}
result += strprintf(" %5i", m_counts.at(i).at(NETWORKS.size())); // total peers count
if (i == 1) { // the outbound row has two extra columns for block relay and manual peer counts
if (i == 1) { // the outbound row has extra columns for block relay, dog mode and manual peer counts
result += strprintf(" %5i", m_block_relay_peers_count);
if (m_dog_peers_count) result += strprintf(" %5i", m_dog_peers_count);
if (m_manual_peers_count) result += strprintf(" %5i", m_manual_peers_count);
}
}
Expand Down Expand Up @@ -704,6 +709,7 @@ class NetinfoRequestHandler : public BaseRequestHandler
" \"feeler\" - short-lived connection for testing addresses\n"
" \"addr\" - address fetch; short-lived connection for requesting addresses\n"
" \"priv\" - private broadcast; short-lived connection for broadcasting our transactions\n"
" \"dog\" - $DOG Mode; long-lived connection to a $DOG Mode peer\n"
" net Network the peer connected through (\"ipv4\", \"ipv6\", \"onion\", \"i2p\", \"cjdns\", or \"npr\" (not publicly routable))\n"
" serv Services offered by the peer\n"
" \"n\" - NETWORK: peer can serve the full block chain\n"
Expand All @@ -712,6 +718,7 @@ class NetinfoRequestHandler : public BaseRequestHandler
" \"c\" - COMPACT_FILTERS: peer can handle basic block filter requests (see BIPs 157 and 158)\n"
" \"l\" - NETWORK_LIMITED: peer limited to serving only the last 288 blocks (~2 days)\n"
" \"2\" - P2P_V2: peer supports version 2 P2P transport protocol, as defined in BIP 324\n"
" \"d\" - DOG_MODE: $DOG Mode peer (preferential peering)\n"
" \"u\" - UNKNOWN: unrecognized bit flag\n"
" v Version of transport protocol used for the connection\n"
" mping Minimum observed ping time, in milliseconds (ms)\n"
Expand Down
5 changes: 3 additions & 2 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)

argsman.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (test networks only; default: %u)", DEFAULT_ACCEPT_NON_STD_TXN), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kvB) used to define cost of relay, used for mempool limiting and replacement policy. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kvB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kvB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. The dust threshold is capped at a global limit of 1 satoshi; this option can only lower it further. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-acceptstalefeeestimates", strprintf("Read fee estimates even if they are stale (%sdefault: %u) fee estimates are considered stale if they are %s hours old", "regtest only; ", DEFAULT_ACCEPT_STALE_FEE_ESTIMATES, Ticks<std::chrono::hours>(MAX_FILE_AGE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
Expand Down Expand Up @@ -860,7 +860,7 @@ namespace { // Variables internal to initialization process only

int nMaxConnections;
int available_fds;
ServiceFlags g_local_services = ServiceFlags(NODE_NETWORK_LIMITED | NODE_WITNESS);
ServiceFlags g_local_services = ServiceFlags(NODE_NETWORK_LIMITED | NODE_WITNESS | NODE_DOG_MODE);
int64_t peer_connect_timeout;
std::set<BlockFilterType> g_enabled_filter_types;

Expand Down Expand Up @@ -2099,6 +2099,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
CConnman::Options connOptions;
connOptions.m_local_services = g_local_services;
connOptions.m_max_automatic_connections = nMaxConnections;
connOptions.m_max_outbound_dog_mode = MAX_DOG_MODE_CONNECTIONS;
connOptions.uiInterface = &uiInterface;
connOptions.m_banman = node.banman.get();
connOptions.m_msgproc = node.peerman.get();
Expand Down
16 changes: 16 additions & 0 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1887,6 +1887,9 @@ bool CConnman::AddConnection(const std::string& address, ConnectionType conn_typ
case ConnectionType::BLOCK_RELAY:
max_connections = m_max_outbound_block_relay;
break;
case ConnectionType::DOG_MODE:
max_connections = m_max_outbound_dog_mode;
break;
// no limit for ADDR_FETCH because -seednode has no limit either
case ConnectionType::ADDR_FETCH:
break;
Expand Down Expand Up @@ -2669,6 +2672,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
// Only connect out to one peer per ipv4/ipv6 network group (/16 for IPv4).
int nOutboundFullRelay = 0;
int nOutboundBlockRelay = 0;
int nOutboundDogMode = 0;
int outbound_privacy_network_peers = 0;
std::set<std::vector<unsigned char>> outbound_ipv46_peer_netgroups;

Expand All @@ -2677,6 +2681,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
for (const CNode* pnode : m_nodes) {
if (pnode->IsFullOutboundConn()) nOutboundFullRelay++;
if (pnode->IsBlockOnlyConn()) nOutboundBlockRelay++;
if (pnode->IsDogModeConn()) nOutboundDogMode++;

// Make sure our persistent outbound slots to ipv4/ipv6 peers belong to different netgroups.
switch (pnode->m_conn_type) {
Expand All @@ -2693,6 +2698,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
case ConnectionType::MANUAL:
case ConnectionType::OUTBOUND_FULL_RELAY:
case ConnectionType::BLOCK_RELAY:
case ConnectionType::DOG_MODE:
const CAddress address{pnode->addr};
if (address.IsTor() || address.IsI2P() || address.IsCJDNS()) {
// Since our addrman-groups for these networks are
Expand Down Expand Up @@ -2721,6 +2727,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
auto now = GetTime<std::chrono::microseconds>();
bool anchor = false;
bool fFeeler = false;
bool dog_mode = false;
std::optional<Network> preferred_net;

// Determine what type of connection to open. Opening
Expand Down Expand Up @@ -2767,6 +2774,9 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
// (similar to how we deal with extra outbound peers).
next_extra_block_relay = now + rng.rand_exp_duration(EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
conn_type = ConnectionType::BLOCK_RELAY;
} else if (nOutboundDogMode < m_max_outbound_dog_mode) {
conn_type = ConnectionType::DOG_MODE;
dog_mode = true;
} else if (now > next_feeler) {
next_feeler = now + rng.rand_exp_duration(FEELER_INTERVAL);
conn_type = ConnectionType::FEELER;
Expand Down Expand Up @@ -2863,6 +2873,12 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
continue;
}

// if we would like to open a $DOG Mode connection, require the
// NODE_DOG_MODE service flag to be set
if (dog_mode && !HasDogModeServiceFlag(addr.nServices)) {
continue;
}

// for non-feelers, require all the services we'll want,
// for feelers, only require they be a full node (only because most
// SPV clients don't have a good address DB available)
Expand Down
20 changes: 19 additions & 1 deletion src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ static const int MAX_OUTBOUND_FULL_RELAY_CONNECTIONS = 8;
static const int MAX_ADDNODE_CONNECTIONS = 8;
/** Maximum number of block-relay-only outgoing connections */
static const int MAX_BLOCK_RELAY_ONLY_CONNECTIONS = 2;
/** Maximum number of automatic $DOG Mode peers */
static const int MAX_DOG_MODE_CONNECTIONS = 4;
/** Maximum number of feeler connections */
static const int MAX_FEELER_CONNECTIONS = 1;
/** Maximum number of private broadcast connections */
Expand Down Expand Up @@ -777,6 +779,7 @@ class CNode
switch (m_conn_type) {
case ConnectionType::OUTBOUND_FULL_RELAY:
case ConnectionType::BLOCK_RELAY:
case ConnectionType::DOG_MODE:
return true;
case ConnectionType::INBOUND:
case ConnectionType::MANUAL:
Expand Down Expand Up @@ -807,6 +810,7 @@ class CNode
case ConnectionType::PRIVATE_BROADCAST:
return false;
case ConnectionType::OUTBOUND_FULL_RELAY:
case ConnectionType::DOG_MODE:
case ConnectionType::MANUAL:
return true;
} // no default case, so the compiler can warn about missing cases
Expand Down Expand Up @@ -835,6 +839,10 @@ class CNode
return m_conn_type == ConnectionType::INBOUND;
}

bool IsDogModeConn() const {
return m_conn_type == ConnectionType::DOG_MODE;
}

bool ExpectServicesFromConn() const {
switch (m_conn_type) {
case ConnectionType::INBOUND:
Expand All @@ -845,6 +853,7 @@ class CNode
case ConnectionType::BLOCK_RELAY:
case ConnectionType::ADDR_FETCH:
case ConnectionType::PRIVATE_BROADCAST:
case ConnectionType::DOG_MODE:
return true;
} // no default case, so the compiler can warn about missing cases

Expand Down Expand Up @@ -1104,6 +1113,7 @@ class CConnman
bool whitelist_forcerelay = DEFAULT_WHITELISTFORCERELAY;
bool whitelist_relay = DEFAULT_WHITELISTRELAY;
bool m_capture_messages = false;
int m_max_outbound_dog_mode = 0;
};

void Init(const Options& connOptions) EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex, !m_total_bytes_sent_mutex)
Expand All @@ -1114,7 +1124,12 @@ class CConnman
m_max_automatic_connections = connOptions.m_max_automatic_connections;
m_max_outbound_full_relay = std::min(MAX_OUTBOUND_FULL_RELAY_CONNECTIONS, m_max_automatic_connections);
m_max_outbound_block_relay = std::min(MAX_BLOCK_RELAY_ONLY_CONNECTIONS, m_max_automatic_connections - m_max_outbound_full_relay);
m_max_automatic_outbound = m_max_outbound_full_relay + m_max_outbound_block_relay + m_max_feeler;
// Clamp dog mode connections against the budget remaining after the
// other outbound slots, like the block-relay counter above, instead
// of trusting the caller-provided value.
m_max_outbound_dog_mode = std::min(connOptions.m_max_outbound_dog_mode,
std::max(0, m_max_automatic_connections - m_max_outbound_full_relay - m_max_outbound_block_relay - m_max_feeler));
m_max_automatic_outbound = m_max_outbound_full_relay + m_max_outbound_block_relay + m_max_feeler + m_max_outbound_dog_mode;
m_max_inbound = std::max(0, m_max_automatic_connections - m_max_automatic_outbound);
m_use_addrman_outgoing = connOptions.m_use_addrman_outgoing;
m_client_interface = connOptions.uiInterface;
Expand Down Expand Up @@ -1679,6 +1694,9 @@ class CConnman
// We do not relay tx or addr messages with these peers
int m_max_outbound_block_relay;

// How many $DOG Mode outbound peers we want
int m_max_outbound_dog_mode;

int m_max_addnode{MAX_ADDNODE_CONNECTIONS};
int m_max_feeler{MAX_FEELER_CONNECTIONS};
int m_max_automatic_outbound;
Expand Down
Loading
Loading