mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-05-21 17:42:37 +02:00
Merge truc_opts-27+knots
This commit is contained in:
commit
52ebfb386c
@ -662,6 +662,7 @@ void SetupServerArgs(ArgsManager& argsman)
|
||||
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
||||
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);
|
||||
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);
|
||||
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)", "reject"), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
||||
argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY,
|
||||
OptionsCategory::NODE_RELAY);
|
||||
argsman.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kvB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",
|
||||
@ -760,6 +761,10 @@ static bool AppInitServers(NodeContext& node)
|
||||
// Parameter interaction based on rules
|
||||
void InitParameterInteraction(ArgsManager& args)
|
||||
{
|
||||
if (args.GetBoolArg("-acceptnonstdtxn", DEFAULT_ACCEPT_NON_STD_TXN) && (!args.IsArgSet("-mempooltruc")) && DEFAULT_MEMPOOL_TRUC_POLICY == TRUCPolicy::Reject) {
|
||||
args.SoftSetArg("-mempooltruc", "enforce");
|
||||
}
|
||||
|
||||
// when specifying an explicit binding address, you want to listen on it
|
||||
// even when -connect or -proxy is specified
|
||||
if (args.IsArgSet("-bind")) {
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <optional>
|
||||
|
||||
enum class RBFPolicy { Never, OptIn, Always };
|
||||
enum class TRUCPolicy { Reject, Accept, Enforce };
|
||||
|
||||
/** Default for -maxmempool, maximum megabytes of mempool memory usage */
|
||||
static constexpr unsigned int DEFAULT_MAX_MEMPOOL_SIZE_MB{300};
|
||||
@ -23,6 +24,8 @@ static constexpr unsigned int DEFAULT_BLOCKSONLY_MAX_MEMPOOL_SIZE_MB{5};
|
||||
static constexpr unsigned int DEFAULT_MEMPOOL_EXPIRY_HOURS{336};
|
||||
/** Default for -mempoolreplacement; must update docs in init.cpp manually */
|
||||
static constexpr RBFPolicy DEFAULT_MEMPOOL_RBF_POLICY{RBFPolicy::OptIn};
|
||||
/** Default for -mempooltruc; must update docs in init.cpp manually */
|
||||
static constexpr TRUCPolicy DEFAULT_MEMPOOL_TRUC_POLICY{TRUCPolicy::Reject};
|
||||
/** Whether to fall back to legacy V1 serialization when writing mempool.dat */
|
||||
static constexpr bool DEFAULT_PERSIST_V1_DAT{false};
|
||||
/** Default for -acceptnonstdtxn */
|
||||
@ -56,6 +59,7 @@ struct MemPoolOptions {
|
||||
bool permit_bare_multisig{DEFAULT_PERMIT_BAREMULTISIG};
|
||||
bool require_standard{true};
|
||||
RBFPolicy rbf_policy{DEFAULT_MEMPOOL_RBF_POLICY};
|
||||
TRUCPolicy truc_policy{DEFAULT_MEMPOOL_TRUC_POLICY};
|
||||
bool persist_v1_dat{DEFAULT_PERSIST_V1_DAT};
|
||||
MemPoolLimits limits{};
|
||||
};
|
||||
|
@ -143,6 +143,34 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& argsman, const CChainP
|
||||
}
|
||||
}
|
||||
|
||||
if (argsman.IsArgSet("-mempooltruc")) {
|
||||
std::optional<bool> accept_flag, enforce_flag;
|
||||
if (argsman.GetBoolArg("-mempooltruc", false)) {
|
||||
enforce_flag = true;
|
||||
}
|
||||
for (auto& opt : SplitString(argsman.GetArg("-mempooltruc", ""), ",+")) {
|
||||
if (opt == "optin" || opt == "enforce") {
|
||||
enforce_flag = true;
|
||||
} else if (opt == "-optin" || opt == "-enforce") {
|
||||
enforce_flag = false;
|
||||
} else if (opt == "accept") {
|
||||
accept_flag = true;
|
||||
} else if (opt == "reject" || opt == "0") {
|
||||
accept_flag = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (accept_flag && !*accept_flag) { // reject
|
||||
mempool_opts.truc_policy = TRUCPolicy::Reject;
|
||||
} else if (enforce_flag && *enforce_flag) { // enforce
|
||||
mempool_opts.truc_policy = TRUCPolicy::Enforce;
|
||||
} else if ((!accept_flag) && !enforce_flag) {
|
||||
// nothing specified, leave at default
|
||||
} else { // accept or -enforce
|
||||
mempool_opts.truc_policy = TRUCPolicy::Accept;
|
||||
}
|
||||
}
|
||||
|
||||
mempool_opts.persist_v1_dat = argsman.GetBoolArg("-persistmempoolv1", mempool_opts.persist_v1_dat);
|
||||
|
||||
ApplyArgsManOptions(argsman, mempool_opts.limits);
|
||||
|
@ -141,7 +141,7 @@ bool IsStandard(const CScript& scriptPubKey, const std::optional<unsigned>& max_
|
||||
// Changing the default transaction version requires a two step process: first
|
||||
// adapting relay policy by bumping TX_MAX_STANDARD_VERSION, and then later
|
||||
// allowing the new transaction version in the wallet/RPC.
|
||||
static constexpr decltype(CTransaction::nVersion) TX_MAX_STANDARD_VERSION{2};
|
||||
static constexpr decltype(CTransaction::nVersion) TX_MAX_STANDARD_VERSION{3};
|
||||
|
||||
/**
|
||||
* Check for standard transaction types
|
||||
|
@ -891,6 +891,11 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool, const std::optional<MempoolHi
|
||||
case RBFPolicy::OptIn : ret.pushKV("rbf_policy", "optin"); break;
|
||||
case RBFPolicy::Always: ret.pushKV("rbf_policy", "always"); break;
|
||||
}
|
||||
switch (pool.m_truc_policy) {
|
||||
case TRUCPolicy::Reject : ret.pushKV("truc_policy", "reject"); break;
|
||||
case TRUCPolicy::Accept : ret.pushKV("truc_policy", "accept"); break;
|
||||
case TRUCPolicy::Enforce: ret.pushKV("truc_policy", "enforce"); break;
|
||||
}
|
||||
|
||||
if (histogram_floors) {
|
||||
const MempoolHistogramFeeRates& floors{histogram_floors.value()};
|
||||
@ -975,6 +980,7 @@ static RPCHelpMan getmempoolinfo()
|
||||
{RPCResult::Type::NUM, "unbroadcastcount", "Current number of transactions that haven't passed initial broadcast yet"},
|
||||
{RPCResult::Type::BOOL, "fullrbf", "True if the mempool accepts RBF without replaceability signaling inspection"},
|
||||
{RPCResult::Type::STR, "rbf_policy", "Policy used for replacing conflicting transactions by fee (one of: never, optin, always)"},
|
||||
{RPCResult::Type::STR, "truc_policy", "Behaviour for transactions requesting limits (one of: reject, accept, enforce)"},
|
||||
{RPCResult::Type::OBJ_DYN, "fee_histogram", /*optional=*/true, "",
|
||||
{
|
||||
{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",
|
||||
|
@ -406,6 +406,7 @@ CTxMemPool::CTxMemPool(const Options& opts)
|
||||
m_max_datacarrier_bytes{opts.max_datacarrier_bytes},
|
||||
m_require_standard{opts.require_standard},
|
||||
m_rbf_policy{opts.rbf_policy},
|
||||
m_truc_policy{opts.truc_policy},
|
||||
m_persist_v1_dat{opts.persist_v1_dat},
|
||||
m_limits{opts.limits}
|
||||
{
|
||||
|
@ -448,6 +448,7 @@ public:
|
||||
const std::optional<unsigned> m_max_datacarrier_bytes;
|
||||
const bool m_require_standard;
|
||||
const RBFPolicy m_rbf_policy;
|
||||
const TRUCPolicy m_truc_policy;
|
||||
const bool m_persist_v1_dat;
|
||||
|
||||
const Limits m_limits;
|
||||
|
@ -738,6 +738,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
||||
if (tx.IsCoinBase())
|
||||
return state.Invalid(TxValidationResult::TX_CONSENSUS, "coinbase");
|
||||
|
||||
if (tx.nVersion == 3 && m_pool.m_truc_policy == TRUCPolicy::Reject) {
|
||||
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "version");
|
||||
}
|
||||
|
||||
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
|
||||
std::string reason;
|
||||
if (m_pool.m_require_standard && !IsStandardTx(tx, m_pool.m_max_datacarrier_bytes, m_pool.m_permit_bare_multisig, m_pool.m_dust_relay_feerate, reason, ignore_rejects)) {
|
||||
@ -906,7 +910,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
||||
// method of ensuring the tx remains bumped. For example, the fee-bumping child could disappear
|
||||
// due to a replacement.
|
||||
// The only exception is v3 transactions.
|
||||
if (ws.m_ptx->nVersion != 3 && ws.m_modified_fees < m_pool.m_min_relay_feerate.GetFee(ws.m_vsize) && !args.m_ignore_rejects.count(rejectmsg_mempoolfull)) {
|
||||
if ((ws.m_ptx->nVersion != 3 || m_pool.m_truc_policy != TRUCPolicy::Enforce) && ws.m_modified_fees < m_pool.m_min_relay_feerate.GetFee(ws.m_vsize) && !args.m_ignore_rejects.count(rejectmsg_mempoolfull)) {
|
||||
// Even though this is a fee-related failure, this result is TX_MEMPOOL_POLICY, not
|
||||
// TX_RECONSIDERABLE, because it cannot be bypassed using package validation.
|
||||
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "min relay fee not met",
|
||||
@ -986,7 +990,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
||||
.descendant_size_vbytes = maybe_rbf_limits.descendant_size_vbytes + EXTRA_DESCENDANT_TX_SIZE_LIMIT,
|
||||
};
|
||||
const auto error_message{util::ErrorString(ancestors).original};
|
||||
if (ws.m_vsize > EXTRA_DESCENDANT_TX_SIZE_LIMIT || ws.m_ptx->nVersion == 3) {
|
||||
if (ws.m_vsize > EXTRA_DESCENDANT_TX_SIZE_LIMIT || (ws.m_ptx->nVersion == 3 && m_pool.m_truc_policy == TRUCPolicy::Enforce)) {
|
||||
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too-long-mempool-chain", error_message);
|
||||
}
|
||||
ancestors = m_pool.CalculateMemPoolAncestors(*entry, cpfp_carve_out_limits);
|
||||
@ -997,6 +1001,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
||||
// Even though just checking direct mempool parents for inheritance would be sufficient, we
|
||||
// check using the full ancestor set here because it's more convenient to use what we have
|
||||
// already calculated.
|
||||
if (m_pool.m_truc_policy == TRUCPolicy::Enforce) {
|
||||
if (const auto err{SingleV3Checks(ws.m_ptx, "truc-", reason, ignore_rejects, ws.m_ancestors, ws.m_conflicts, ws.m_vsize)}) {
|
||||
// Disabled within package validation.
|
||||
if (err->second != nullptr && args.m_allow_replacement) {
|
||||
@ -1014,7 +1019,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
||||
} else {
|
||||
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, reason, err->first);
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
// A transaction that spends outputs that would be replaced by it is invalid. Now
|
||||
// that we have the set of all ancestors we can detect this
|
||||
@ -1391,13 +1396,14 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
|
||||
|
||||
// At this point we have all in-mempool ancestors, and we know every transaction's vsize.
|
||||
// Run the v3 checks on the package.
|
||||
if (m_pool.m_truc_policy == TRUCPolicy::Enforce) {
|
||||
std::string reason;
|
||||
for (Workspace& ws : workspaces) {
|
||||
if (auto err{PackageV3Checks(ws.m_ptx, ws.m_vsize, "truc-", reason, args.m_ignore_rejects, txns, ws.m_ancestors)}) {
|
||||
package_state.Invalid(PackageValidationResult::PCKG_POLICY, reason, err.value());
|
||||
return PackageMempoolAcceptResult(package_state, {});
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
// Transactions must meet two minimum feerates: the mempool minimum fee and min relay fee.
|
||||
// For transactions consisting of exactly one child and its parents, it suffices to use the
|
||||
|
@ -33,6 +33,8 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
||||
"-limitancestorsize=101",
|
||||
"-limitdescendantcount=200",
|
||||
"-limitdescendantsize=101",
|
||||
"-mempooltruc=accept",
|
||||
"-paytxfee=0.00001", # this test confuses the fee estimator into nearly 1 BTC fees
|
||||
],
|
||||
# second node has default mempool parameters
|
||||
[
|
||||
@ -97,8 +99,10 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
||||
|
||||
self.log.info("Running test opt-in...")
|
||||
self.test_opt_in(fullrbf=False)
|
||||
self.test_opt_in(fullrbf=False, use_truc=True)
|
||||
self.nodes[0], self.nodes[-1] = self.nodes[-1], self.nodes[0]
|
||||
self.test_opt_in(fullrbf=True)
|
||||
self.test_opt_in(fullrbf=True, use_truc=True)
|
||||
self.nodes[0], self.nodes[-1] = self.nodes[-1], self.nodes[0]
|
||||
|
||||
self.log.info("Running test RPC...")
|
||||
@ -511,7 +515,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
||||
self.generate(normal_node, 1)
|
||||
self.wallet.rescan_utxos()
|
||||
|
||||
def test_opt_in(self, fullrbf):
|
||||
def test_opt_in(self, fullrbf, use_truc=False):
|
||||
"""Replacing should only work if orig tx opted in"""
|
||||
tx0_outpoint = self.make_utxo(self.nodes[0], int(1.1 * COIN))
|
||||
|
||||
@ -570,14 +574,23 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
||||
# opt-in on one of the inputs
|
||||
# Transaction should be replaceable on either input
|
||||
|
||||
self.generate(self.nodes[0], 1) # clean mempool so parent txs don't trigger BIP125
|
||||
if use_truc:
|
||||
kwargs = {'sequence': SEQUENCE_FINAL, 'version': 3}
|
||||
else:
|
||||
kwargs = {'sequence': [SEQUENCE_FINAL, 0xfffffffd]}
|
||||
|
||||
tx3a_txid = self.wallet.send_self_transfer_multi(
|
||||
from_node=self.nodes[0],
|
||||
utxos_to_spend=[tx1a_utxo, tx2a_utxo],
|
||||
sequence=[SEQUENCE_FINAL, 0xfffffffd],
|
||||
fee_per_output=int(0.1 * COIN),
|
||||
**kwargs
|
||||
)["txid"]
|
||||
|
||||
# This transaction is shown as replaceable
|
||||
if use_truc:
|
||||
assert_equal(self.nodes[0].getmempoolentry(tx3a_txid)['bip125-replaceable'], False)
|
||||
else:
|
||||
assert_equal(self.nodes[0].getmempoolentry(tx3a_txid)['bip125-replaceable'], True)
|
||||
|
||||
self.wallet.send_self_transfer(
|
||||
|
Loading…
Reference in New Issue
Block a user