Skip to content

Commit 3dbae62

Browse files
committed
Add mempooltruc=reject/accept/enforce option to enable TRUC support
1 parent 9222ee5 commit 3dbae62

File tree

5 files changed

+49
-4
lines changed

5 files changed

+49
-4
lines changed

src/init.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,7 @@ void SetupServerArgs(ArgsManager& argsman)
650650
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
651651
argsman.AddArg("-mempoolfullrbf", strprintf("Accept transaction replace-by-fee without requiring replaceability signaling (default: %u)", (DEFAULT_MEMPOOL_RBF_POLICY == RBFPolicy::Always)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
652652
argsman.AddArg("-mempoolreplacement", strprintf("Set to 0 to disable RBF entirely, \"fee,optin\" to honour RBF opt-out signal, or \"fee,-optin\" to always RBF aka full RBF (default: %s)", "fee,-optin"), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
653+
argsman.AddArg("-mempooltruc", strprintf("Behaviour for transactions requesting TRUC limits: \"reject\" the transactions entirely, \"accept\" them just like any other, or \"enforce\" to impose their requested restrictions (default: %s)", "enforce"), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
653654
argsman.AddArg("-permitbaremultisig", strprintf("Relay transactions creating non-P2SH multisig outputs (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY,
654655
OptionsCategory::NODE_RELAY);
655656
argsman.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kvB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",

src/kernel/mempool_options.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
class ValidationSignals;
1717

1818
enum class RBFPolicy { Never, OptIn, Always };
19+
enum class TRUCPolicy { Reject, Accept, Enforce };
1920

2021
/** Default for -maxmempool, maximum megabytes of mempool memory usage */
2122
static constexpr unsigned int DEFAULT_MAX_MEMPOOL_SIZE_MB{300};
@@ -25,6 +26,8 @@ static constexpr unsigned int DEFAULT_BLOCKSONLY_MAX_MEMPOOL_SIZE_MB{5};
2526
static constexpr unsigned int DEFAULT_MEMPOOL_EXPIRY_HOURS{336};
2627
/** Default for -mempoolreplacement; must update docs in init.cpp manually */
2728
static constexpr RBFPolicy DEFAULT_MEMPOOL_RBF_POLICY{RBFPolicy::Always};
29+
/** Default for -mempooltruc; must update docs in init.cpp manually */
30+
static constexpr TRUCPolicy DEFAULT_MEMPOOL_TRUC_POLICY{TRUCPolicy::Enforce};
2831
/** Whether to fall back to legacy V1 serialization when writing mempool.dat */
2932
static constexpr bool DEFAULT_PERSIST_V1_DAT{false};
3033
/** Default for -acceptnonstdtxn */
@@ -58,6 +61,7 @@ struct MemPoolOptions {
5861
bool permit_bare_multisig{DEFAULT_PERMIT_BAREMULTISIG};
5962
bool require_standard{true};
6063
RBFPolicy rbf_policy{DEFAULT_MEMPOOL_RBF_POLICY};
64+
TRUCPolicy truc_policy{DEFAULT_MEMPOOL_TRUC_POLICY};
6165
bool persist_v1_dat{DEFAULT_PERSIST_V1_DAT};
6266
MemPoolLimits limits{};
6367

src/node/mempool_args.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,34 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& argsman, const CChainP
148148
}
149149
}
150150

151+
if (argsman.IsArgSet("-mempooltruc")) {
152+
std::optional<bool> accept_flag, enforce_flag;
153+
if (argsman.GetBoolArg("-mempooltruc", false)) {
154+
enforce_flag = true;
155+
}
156+
for (auto& opt : util::SplitString(argsman.GetArg("-mempooltruc", ""), ",+")) {
157+
if (opt == "optin" || opt == "enforce") {
158+
enforce_flag = true;
159+
} else if (opt == "-optin" || opt == "-enforce") {
160+
enforce_flag = false;
161+
} else if (opt == "accept") {
162+
accept_flag = true;
163+
} else if (opt == "reject" || opt == "0") {
164+
accept_flag = false;
165+
}
166+
}
167+
168+
if (accept_flag && !*accept_flag) { // reject
169+
mempool_opts.truc_policy = TRUCPolicy::Reject;
170+
} else if (enforce_flag && *enforce_flag) { // enforce
171+
mempool_opts.truc_policy = TRUCPolicy::Enforce;
172+
} else if ((!accept_flag) && !enforce_flag) {
173+
// nothing specified, leave at default
174+
} else { // accept or -enforce
175+
mempool_opts.truc_policy = TRUCPolicy::Accept;
176+
}
177+
}
178+
151179
mempool_opts.persist_v1_dat = argsman.GetBoolArg("-persistmempoolv1", mempool_opts.persist_v1_dat);
152180

153181
ApplyArgsManOptions(argsman, mempool_opts.limits);

src/rpc/mempool.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,11 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool, const std::optional<MempoolHi
732732
case RBFPolicy::OptIn : ret.pushKV("rbf_policy", "optin"); break;
733733
case RBFPolicy::Always: ret.pushKV("rbf_policy", "always"); break;
734734
}
735+
switch (pool.m_opts.truc_policy) {
736+
case TRUCPolicy::Reject : ret.pushKV("truc_policy", "reject"); break;
737+
case TRUCPolicy::Accept : ret.pushKV("truc_policy", "accept"); break;
738+
case TRUCPolicy::Enforce: ret.pushKV("truc_policy", "enforce"); break;
739+
}
735740

736741
if (histogram_floors) {
737742
const MempoolHistogramFeeRates& floors{histogram_floors.value()};
@@ -816,6 +821,7 @@ static RPCHelpMan getmempoolinfo()
816821
{RPCResult::Type::NUM, "unbroadcastcount", "Current number of transactions that haven't passed initial broadcast yet"},
817822
{RPCResult::Type::BOOL, "fullrbf", "True if the mempool accepts RBF without replaceability signaling inspection"},
818823
{RPCResult::Type::STR, "rbf_policy", "Policy used for replacing conflicting transactions by fee (one of: never, optin, always)"},
824+
{RPCResult::Type::STR, "truc_policy", "Behaviour for transactions requesting limits (one of: reject, accept, enforce)"},
819825
{RPCResult::Type::OBJ_DYN, "fee_histogram", /*optional=*/true, "",
820826
{
821827
{RPCResult::Type::OBJ, "<fee_rate_group>", "Fee rate group named by its lower bound (in " + CURRENCY_ATOM + "/vB), identical to the \"from_feerate\" field below",

src/validation.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
809809
if (tx.IsCoinBase())
810810
return state.Invalid(TxValidationResult::TX_CONSENSUS, "coinbase");
811811

812+
if (tx.version == TRUC_VERSION && m_pool.m_opts.truc_policy == TRUCPolicy::Reject) {
813+
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "version");
814+
}
815+
812816
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
813817
std::string reason;
814818
if (m_pool.m_opts.require_standard && !IsStandardTx(tx, m_pool.m_opts.max_datacarrier_bytes, m_pool.m_opts.permit_bare_multisig, m_pool.m_opts.dust_relay_feerate, reason, ignore_rejects)) {
@@ -970,7 +974,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
970974
// method of ensuring the tx remains bumped. For example, the fee-bumping child could disappear
971975
// due to a replacement.
972976
// The only exception is TRUC transactions.
973-
if (ws.m_ptx->version != TRUC_VERSION && ws.m_modified_fees < m_pool.m_opts.min_relay_feerate.GetFee(ws.m_vsize) && !args.m_ignore_rejects.count(rejectmsg_lowfee_relay)) {
977+
if ((ws.m_ptx->version != TRUC_VERSION || m_pool.m_opts.truc_policy != TRUCPolicy::Enforce) && ws.m_modified_fees < m_pool.m_opts.min_relay_feerate.GetFee(ws.m_vsize) && !args.m_ignore_rejects.count(rejectmsg_lowfee_relay)) {
974978
// Even though this is a fee-related failure, this result is TX_MEMPOOL_POLICY, not
975979
// TX_RECONSIDERABLE, because it cannot be bypassed using package validation.
976980
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "min relay fee not met",
@@ -1058,7 +1062,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
10581062
.descendant_count = maybe_rbf_limits.descendant_count + 1,
10591063
.descendant_size_vbytes = maybe_rbf_limits.descendant_size_vbytes + EXTRA_DESCENDANT_TX_SIZE_LIMIT,
10601064
};
1061-
if (ws.m_vsize > EXTRA_DESCENDANT_TX_SIZE_LIMIT || ws.m_ptx->version == TRUC_VERSION) {
1065+
if (ws.m_vsize > EXTRA_DESCENDANT_TX_SIZE_LIMIT || (ws.m_ptx->version == TRUC_VERSION && m_pool.m_opts.truc_policy == TRUCPolicy::Enforce)) {
10621066
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too-long-mempool-chain", error_message);
10631067
}
10641068
if (auto ancestors_retry{m_pool.CalculateMemPoolAncestors(*entry, cpfp_carve_out_limits)}) {
@@ -1071,6 +1075,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
10711075
// Even though just checking direct mempool parents for inheritance would be sufficient, we
10721076
// check using the full ancestor set here because it's more convenient to use what we have
10731077
// already calculated.
1078+
if (m_pool.m_opts.truc_policy == TRUCPolicy::Enforce) {
10741079
if (const auto err{SingleTRUCChecks(ws.m_ptx, "truc-", reason, ignore_rejects, ws.m_ancestors, ws.m_conflicts, ws.m_vsize)}) {
10751080
// Single transaction contexts only.
10761081
if (args.m_allow_sibling_eviction && err->second != nullptr) {
@@ -1092,7 +1097,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
10921097
} else {
10931098
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, reason, err->first);
10941099
}
1095-
}
1100+
}}
10961101

10971102
// A transaction that spends outputs that would be replaced by it is invalid. Now
10981103
// that we have the set of all ancestors we can detect this
@@ -1596,13 +1601,14 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
15961601

15971602
// At this point we have all in-mempool ancestors, and we know every transaction's vsize.
15981603
// Run the TRUC checks on the package.
1604+
if (m_pool.m_opts.truc_policy == TRUCPolicy::Enforce) {
15991605
std::string reason;
16001606
for (Workspace& ws : workspaces) {
16011607
if (auto err{PackageTRUCChecks(ws.m_ptx, ws.m_vsize, "truc-", reason, args.m_ignore_rejects, txns, ws.m_ancestors)}) {
16021608
package_state.Invalid(PackageValidationResult::PCKG_POLICY, reason, err.value());
16031609
return PackageMempoolAcceptResult(package_state, {});
16041610
}
1605-
}
1611+
}}
16061612

16071613
// Transactions must meet two minimum feerates: the mempool minimum fee and min relay fee.
16081614
// For transactions consisting of exactly one child and its parents, it suffices to use the

0 commit comments

Comments
 (0)