Skip to content
Bitcoin Roots

v30.3-roots.1 · Commit 3: [PATCH 03/68] feat(policy): preserve Roots relay and network controls on Core 30.3

src/validation.cpp

Before: src/validation.cpp · After: src/validation.cpp

File metadata
index 21ccf045fbfcd200ec1dea6ad867a65eec02dfbc..89daf215e6c9aec6905aa611c1c360fc980e9921 100644

Download original patch · Release notes

Diff rows 401–800 of 802. Line numbers refer to the original before/after files.

+ Added− RemovedUnmarked lines are context.

Scroll the code area horizontally to read long lines. The two line-number columns refer to before and after.

src/validation.cpp CHANGED
@@ -926,29 +1079,63 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) (continued)
1117
+ }
1118
+ if (dust_penalty > 0) {
1119
+ m_subpackage.m_changeset->m_to_add.modify(ws.m_tx_handle, [&](CTxMemPoolEntry& e) {
1120
+ e.UpdateModifiedFee(-dust_penalty);
1121
+ });
1122
+ ws.m_modified_fees = SaturatingAdd(ws.m_modified_fees, -dust_penalty);
1123
+ }
1124
+ }
1125
+
942
1126
  // Enforces 0-fee for dust transactions, no incentive to be mined alone
943
- if (m_pool.m_opts.require_standard) {
1127
+ if (m_pool.m_opts.require_standard && !ignore_rejects.count("dust")) {
944
1128
  if (!PreCheckEphemeralTx(*ptx, m_pool.m_opts.dust_relay_feerate, ws.m_base_fees, ws.m_modified_fees, state)) {
945
1129
  return false; // state filled in by PreCheckEphemeralTx
946
1130
  }
947
1131
  }
948
1132
 
949
- if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST)
950
- return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-too-many-sigops",
1133
+ // To avoid rejecting low-sigop bare-multisig transactions, the sigops
1134
+ // are counted a second time more accurately.
1135
+ if ((nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST) || (nBytesPerSigOpStrict && GetAccurateTransactionSigOpCost(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS) > ws.m_vsize * WITNESS_SCALE_FACTOR / nBytesPerSigOpStrict)) {
1136
+ MaybeRejectDbg(TxValidationResult::TX_NOT_STANDARD, "bad-txns-too-many-sigops",
951
1137
  strprintf("%d", nSigOpsCost));
1138
+ }
952
1139
 
953
1140
  // No individual transactions are allowed below the min relay feerate except from disconnected blocks.
954
1141
  // This requirement, unlike CheckFeeRate, cannot be bypassed using m_package_feerates because,
@@ -956,7 +1143,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
956
1143
  // method of ensuring the tx remains bumped. For example, the fee-bumping child could disappear
957
1144
  // due to a replacement.
958
1145
  // The only exception is TRUC transactions.
959
- if (!bypass_limits && ws.m_ptx->version != TRUC_VERSION && ws.m_modified_fees < m_pool.m_opts.min_relay_feerate.GetFee(ws.m_vsize)) {
1146
+ 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)) {
960
1147
  // Even though this is a fee-related failure, this result is TX_MEMPOOL_POLICY, not
961
1148
  // TX_RECONSIDERABLE, because it cannot be bypassed using package validation.
962
1149
  return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "min relay fee not met",
@@ -965,16 +1152,20 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
965
1152
  // No individual transactions are allowed below the mempool min feerate except from disconnected
966
1153
  // blocks and transactions in a package. Package transactions will be checked using package
967
1154
  // feerate later.
968
- if (!bypass_limits && !args.m_package_feerates && !CheckFeeRate(ws.m_vsize, ws.m_modified_fees, state)) return false;
1155
+ if (!args.m_package_feerates && !CheckFeeRate(ws.m_vsize, ws.m_modified_fees, state, args.m_ignore_rejects)) return false;
969
1156
 
970
- ws.m_iters_conflicting = m_pool.GetIterSet(ws.m_conflicts);
1157
+ std::set<Txid> conflicts_as_a_set;
1158
+ std::transform(ws.m_conflicts_incl_policy.begin(), ws.m_conflicts_incl_policy.end(),
1159
+ std::inserter(conflicts_as_a_set, conflicts_as_a_set.end()),
1160
+ [](const std::pair<Txid, bool>& pair){ return pair.first; });
1161
+ ws.m_iters_conflicting = m_pool.GetIterSet(conflicts_as_a_set);
971
1162
 
972
1163
  // Note that these modifications are only applicable to single transaction scenarios;
973
1164
  // carve-outs are disabled for multi-transaction evaluations.
974
1165
  CTxMemPool::Limits maybe_rbf_limits = m_pool.m_opts.limits;
975
1166
 
976
1167
  // Calculate in-mempool ancestors, up to a limit.
977
- if (ws.m_conflicts.size() == 1 && args.m_allow_carveouts) {
1168
+ if (ws.m_conflicts_incl_policy.size() == 1 && args.m_allow_carveouts) {
978
1169
  // In general, when we receive an RBF transaction with mempool conflicts, we want to know whether we
979
1170
  // would meet the chain limits after the conflicts have been removed. However, there isn't a practical
980
1171
  // way to do this short of calculating the ancestor and descendant sets with an overlay cache of
@@ -1009,7 +1200,13 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
1009
1200
  maybe_rbf_limits.descendant_size_vbytes += conflict->GetSizeWithDescendants();
1010
1201
  }
1011
1202
 
1012
- if (auto ancestors{m_subpackage.m_changeset->CalculateMemPoolAncestors(ws.m_tx_handle, maybe_rbf_limits)}) {
1203
+ CTxMemPool::Limits limits;
1204
+ if (ignore_rejects.count("too-long-mempool-chain")) {
1205
+ limits = CTxMemPool::Limits::NoLimits();
1206
+ } else {
1207
+ limits = maybe_rbf_limits;
1208
+ }
1209
+ if (auto ancestors{m_subpackage.m_changeset->CalculateMemPoolAncestors(ws.m_tx_handle, limits)}) {
1013
1210
  ws.m_ancestors = std::move(*ancestors);
1014
1211
  } else {
1015
1212
  // If CalculateMemPoolAncestors fails second time, we want the original error string.
@@ -1038,7 +1235,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
1038
1235
  .descendant_count = maybe_rbf_limits.descendant_count + 1,
1039
1236
  .descendant_size_vbytes = maybe_rbf_limits.descendant_size_vbytes + EXTRA_DESCENDANT_TX_SIZE_LIMIT,
1040
1237
  };
1041
- if (ws.m_vsize > EXTRA_DESCENDANT_TX_SIZE_LIMIT || ws.m_ptx->version == TRUC_VERSION) {
1238
+ if (ws.m_vsize > EXTRA_DESCENDANT_TX_SIZE_LIMIT || (ws.m_ptx->version == TRUC_VERSION && m_pool.m_opts.truc_policy == TRUCPolicy::Enforce)) {
1042
1239
  return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too-long-mempool-chain", error_message);
1043
1240
  }
1044
1241
  if (auto ancestors_retry{m_subpackage.m_changeset->CalculateMemPoolAncestors(ws.m_tx_handle, cpfp_carve_out_limits)}) {
@@ -1051,8 +1248,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
1051
1248
  // Even though just checking direct mempool parents for inheritance would be sufficient, we
1052
1249
  // check using the full ancestor set here because it's more convenient to use what we have
1053
1250
  // already calculated.
1054
- if (!args.m_bypass_limits) {
1055
- if (const auto err{SingleTRUCChecks(ws.m_ptx, ws.m_ancestors, ws.m_conflicts, ws.m_vsize)}) {
1251
+ if (m_pool.m_opts.truc_policy == TRUCPolicy::Enforce && !ignore_rejects.count("truc")) {
1252
+ if (const auto err{SingleTRUCChecks(ws.m_ptx, "truc-", reason, ignore_rejects, ws.m_ancestors, conflicts_as_a_set, ws.m_vsize)}) {
1056
1253
  // Single transaction contexts only.
1057
1254
  if (args.m_allow_sibling_eviction && err->second != nullptr) {
1058
1255
  // We should only be considering where replacement is considered valid as well.
@@ -1060,7 +1257,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
1060
1257
 
1061
1258
  // Potential sibling eviction. Add the sibling to our list of mempool conflicts to be
1062
1259
  // included in RBF checks.
1063
- ws.m_conflicts.insert(err->second->GetHash());
1260
+ ws.m_conflicts_incl_policy.emplace(err->second->GetHash(), false);
1261
+ conflicts_as_a_set.insert(err->second->GetHash());
1064
1262
  // Adding the sibling to m_iters_conflicting here means that it doesn't count towards
1065
1263
  // RBF Carve Out above. This is correct, since removing to-be-replaced transactions from
1066
1264
  // the descendant count is done separately in SingleTRUCChecks for TRUC transactions.
@@ -1071,7 +1269,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
1071
1269
  // (which is normally done in PreChecks). However, the only way a TRUC transaction can
1072
1270
  // have a non-TRUC and non-BIP125 descendant is due to a reorg.
1073
1271
  } else {
1074
- return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "TRUC-violation", err->first);
1272
+ return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, reason, err->first);
1075
1273
  }
1076
1274
  }
1077
1275
  }
@@ -1080,18 +1278,22 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
1080
1278
  // that we have the set of all ancestors we can detect this
1081
1279
  // pathological case by making sure ws.m_conflicts and ws.m_ancestors don't
1082
1280
  // intersect.
1083
- if (const auto err_string{EntriesAndTxidsDisjoint(ws.m_ancestors, ws.m_conflicts, hash)}) {
1281
+ bool has_policy_conflict{false};
1282
+ if (const auto err_string{EntriesAndTxidsDisjoint(ws.m_ancestors, ws.m_conflicts_incl_policy, hash, &has_policy_conflict)}) {
1084
1283
  // We classify this as a consensus error because a transaction depending on something it
1085
1284
  // conflicts with would be inconsistent.
1086
1285
  return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-spends-conflicting-tx", *err_string);
1087
1286
  }
1287
+ if (has_policy_conflict) {
1288
+ MaybeReject(TxValidationResult::TX_MEMPOOL_POLICY, "txn-spk-reused-chained");
1289
+ }
1088
1290
 
1089
1291
  // We want to detect conflicts in any tx in a package to trigger package RBF logic
1090
- m_subpackage.m_rbf |= !ws.m_conflicts.empty();
1292
+ m_subpackage.m_rbf |= !ws.m_conflicts_incl_policy.empty();
1091
1293
  return true;
1092
1294
  }
1093
1295
 
1094
- bool MemPoolAccept::ReplacementChecks(Workspace& ws)
1296
+ bool MemPoolAccept::ReplacementChecks(ATMPArgs& args, Workspace& ws)
1095
1297
  {
1096
1298
  AssertLockHeld(cs_main);
1097
1299
  AssertLockHeld(m_pool.cs);
@@ -1110,27 +1312,31 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
1110
1312
  // guarantee that this is incentive-compatible for miners, because it is possible for a
1111
1313
  // descendant transaction of a direct conflict to pay a higher feerate than the transaction that
1112
1314
  // might replace them, under these rules.
1315
+ if (!args.m_ignore_rejects.count("insufficient fee")) {
1113
1316
  if (const auto err_string{PaysMoreThanConflicts(ws.m_iters_conflicting, newFeeRate, hash)}) {
1114
1317
  // This fee-related failure is TX_RECONSIDERABLE because validating in a package may change
1115
1318
  // the result.
1116
1319
  return state.Invalid(TxValidationResult::TX_RECONSIDERABLE,
1117
1320
  strprintf("insufficient fee%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string);
1118
1321
  }
1322
+ } // ignore_rejects
1119
1323
 
1120
1324
  CTxMemPool::setEntries all_conflicts;
1121
1325
 
1122
1326
  // Calculate all conflicting entries and enforce Rule #5.
1123
- if (const auto err_string{GetEntriesForConflicts(tx, m_pool, ws.m_iters_conflicting, all_conflicts)}) {
1327
+ if (const auto err_string{GetEntriesForConflicts(tx, m_pool, ws.m_iters_conflicting, all_conflicts, args.m_ignore_rejects)}) {
1124
1328
  return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
1125
1329
  strprintf("too many potential replacements%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string);
1126
1330
  }
1127
1331
  // Enforce Rule #2.
1332
+ if (!args.m_ignore_rejects.count("replacement-adds-unconfirmed")) {
1128
1333
  if (const auto err_string{HasNoNewUnconfirmed(tx, m_pool, all_conflicts)}) {
1129
1334
  // Sibling eviction is only done for TRUC transactions, which cannot have multiple ancestors.
1130
1335
  Assume(!ws.m_sibling_eviction);
1131
1336
  return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
1132
1337
  strprintf("replacement-adds-unconfirmed%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string);
1133
1338
  }
1339
+ } // ignore_rejects
1134
1340
 
1135
1341
  // Check if it's economically rational to mine this transaction rather than the ones it
1136
1342
  // replaces and pays for its own relay fees. Enforce Rules #3 and #4.
@@ -1138,12 +1344,14 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
1138
1344
  m_subpackage.m_conflicting_fees += it->GetModifiedFee();
1139
1345
  m_subpackage.m_conflicting_size += it->GetTxSize();
1140
1346
  }
1347
+ if (!args.m_ignore_rejects.count("insufficient fee")) {
1141
1348
  if (const auto err_string{PaysForRBF(m_subpackage.m_conflicting_fees, ws.m_modified_fees, ws.m_vsize,
1142
1349
  m_pool.m_opts.incremental_relay_feerate, hash)}) {
1143
1350
  // Result may change in a package context
1144
1351
  return state.Invalid(TxValidationResult::TX_RECONSIDERABLE,
1145
1352
  strprintf("insufficient fee%s", ws.m_sibling_eviction ? " (including sibling eviction)" : ""), *err_string);
1146
1353
  }
1354
+ } // ignore_rejects
1147
1355
 
1148
1356
  // Add all the to-be-removed transactions to the changeset.
1149
1357
  for (auto it : all_conflicts) {
@@ -1152,7 +1360,7 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
1152
1360
  return true;
1153
1361
  }
1154
1362
 
1155
- bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txns,
1363
+ bool MemPoolAccept::PackageMempoolChecks(const ATMPArgs& args, const std::vector<CTransactionRef>& txns,
1156
1364
  std::vector<Workspace>& workspaces,
1157
1365
  const int64_t total_vsize,
1158
1366
  PackageValidationState& package_state)
@@ -1165,7 +1373,13 @@ bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txn
1165
1373
 
1166
1374
  assert(txns.size() == workspaces.size());
1167
1375
 
1168
- auto result = m_pool.CheckPackageLimits(txns, total_vsize);
1376
+ util::Result<void> result = [&]() EXCLUSIVE_LOCKS_REQUIRED(m_pool.cs) {
1377
+ if (args.m_ignore_rejects.count("package-mempool-limits")) {
1378
+ return util::Result<void>();
1379
+ } else {
1380
+ return m_pool.CheckPackageLimits(txns, total_vsize);
1381
+ }
1382
+ }();
1169
1383
  if (!result) {
1170
1384
  // This is a package-wide error, separate from an individual transaction error.
1171
1385
  return package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-mempool-limits", util::ErrorString(result).original);
@@ -1251,6 +1465,77 @@ bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txn
1251
1465
  return true;
1252
1466
  }
1253
1467
 
1468
+ unsigned int PolicyScriptVerifyFlags(const ignore_rejects_type& ignore_rejects)
1469
+ {
1470
+ if (ignore_rejects.empty()) {
1471
+ return STANDARD_SCRIPT_VERIFY_FLAGS;
1472
+ }
1473
+ if (ignore_rejects.count("non-mandatory-script-verify-flag")) {
1474
+ return MANDATORY_SCRIPT_VERIFY_FLAGS;
1475
+ }
1476
+
1477
+ unsigned int flags = STANDARD_SCRIPT_VERIFY_FLAGS;
1478
+ if (ignore_rejects.count("non-mandatory-script-verify-flag-upgradable")) {
1479
+ constexpr unsigned int upgradable_policy_flags =
1480
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS |
1481
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM |
1482
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION |
1483
+ SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS |
1484
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE;
1485
+ flags &= ~upgradable_policy_flags;
1486
+ } else {
1487
+ if (ignore_rejects.count("non-mandatory-script-verify-flag-upgradable-nops")) {
1488
+ flags &= ~SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS;
1489
+ }
1490
+ if (ignore_rejects.count("non-mandatory-script-verify-flag-upgradable-witness_program")) {
1491
+ flags &= ~SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM;
1492
+ }
1493
+ if (ignore_rejects.count("non-mandatory-script-verify-flag-upgradable-taproot_version")) {
1494
+ flags &= ~SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION;
1495
+ }
1496
+ if (ignore_rejects.count("non-mandatory-script-verify-flag-upgradable-op_success")) {
1497
+ flags &= ~SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS;
1498
+ }
1499
+ if (ignore_rejects.count("non-mandatory-script-verify-flag-upgradable-pubkeytype")) {
1500
+ flags &= ~SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE;
1501
+ }
1502
+ }
1503
+ if (ignore_rejects.count("non-mandatory-script-verify-flag-bip62")) {
1504
+ constexpr unsigned int bip62_policy_flags =
1505
+ SCRIPT_VERIFY_LOW_S |
1506
+ SCRIPT_VERIFY_SIGPUSHONLY | // NOTE: not actually set ever
1507
+ SCRIPT_VERIFY_MINIMALDATA;
1508
+ flags &= ~bip62_policy_flags;
1509
+ } else {
1510
+ if (ignore_rejects.count("non-mandatory-script-verify-flag-low_s")) {
1511
+ flags &= ~SCRIPT_VERIFY_LOW_S;
1512
+ }
1513
+ if (ignore_rejects.count("non-mandatory-script-verify-flag-minimaldata")) {
1514
+ flags &= ~SCRIPT_VERIFY_MINIMALDATA;
1515
+ }
1516
+ if (ignore_rejects.count("non-mandatory-script-verify-flag-cleanstack")) {
1517
+ flags &= ~SCRIPT_VERIFY_CLEANSTACK;
1518
+ }
1519
+ }
1520
+ if (ignore_rejects.count("non-mandatory-script-verify-flag-strictenc")) {
1521
+ flags &= ~SCRIPT_VERIFY_STRICTENC;
1522
+ }
1523
+ if (ignore_rejects.count("non-mandatory-script-verify-flag-minimalif")) {
1524
+ flags &= ~SCRIPT_VERIFY_MINIMALIF;
1525
+ }
1526
+ if (ignore_rejects.count("non-mandatory-script-verify-flag-nullfail")) {
1527
+ flags &= ~SCRIPT_VERIFY_NULLFAIL;
1528
+ }
1529
+ if (ignore_rejects.count("non-mandatory-script-verify-flag-witness_pubkeytype")) {
1530
+ flags &= ~SCRIPT_VERIFY_WITNESS_PUBKEYTYPE;
1531
+ }
1532
+ if (ignore_rejects.count("non-mandatory-script-verify-flag-const_scriptcode")) {
1533
+ flags &= ~SCRIPT_VERIFY_CONST_SCRIPTCODE;
1534
+ }
1535
+ flags |= MANDATORY_SCRIPT_VERIFY_FLAGS; // for safety
1536
+ return flags;
1537
+ }
1538
+
1254
1539
  bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws)
1255
1540
  {
1256
1541
  AssertLockHeld(cs_main);
@@ -1258,7 +1543,7 @@ bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws)
1258
1543
  const CTransaction& tx = *ws.m_ptx;
1259
1544
  TxValidationState& state = ws.m_state;
1260
1545
 
1261
- constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
1546
+ const unsigned int scriptVerifyFlags = PolicyScriptVerifyFlags(args.m_ignore_rejects);
1262
1547
 
1263
1548
  // Check input scripts and signatures.
1264
1549
  // This is done last to help prevent CPU exhaustion denial-of-service attacks.
@@ -1300,7 +1585,7 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws)
1300
1585
  unsigned int currentBlockScriptVerifyFlags{GetBlockScriptFlags(*m_active_chainstate.m_chain.Tip(), m_active_chainstate.m_chainman)};
1301
1586
  if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags,
1302
1587
  ws.m_precomputed_txdata, m_active_chainstate.CoinsTip(), GetValidationCache())) {
1303
- LogPrintf("BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s\n", hash.ToString(), state.ToString());
1588
+ LogError("BUG! PLEASE REPORT THIS! CheckInputScripts failed against latest-block but not STANDARD flags %s, %s", hash.ToString(), state.ToString());
1304
1589
  return Assume(false);
1305
1590
  }
1306
1591
 
@@ -1425,7 +1710,7 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
1425
1710
  const CTransaction& tx = *ws.m_ptx;
1426
1711
  const auto tx_info = NewMempoolTransactionInfo(ws.m_ptx, ws.m_base_fees,
1427
1712
  ws.m_vsize, (*iter)->GetHeight(),
1428
- args.m_bypass_limits, args.m_package_submission,
1713
+ args.m_ignore_rejects, args.m_package_submission,
1429
1714
  IsCurrentForFeeEstimation(m_active_chainstate),
1430
1715
  m_pool.HasNoInputsOf(tx));
1431
1716
  m_pool.m_opts.signals->TransactionAddedToMempool(tx_info, m_pool.GetAndIncrementSequence());
@@ -1465,7 +1750,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
1465
1750
  }
1466
1751
  }
1467
1752
 
1468
- if (m_subpackage.m_rbf && !ReplacementChecks(ws)) {
1753
+ if (m_subpackage.m_rbf && !ReplacementChecks(args, ws)) {
1469
1754
  if (ws.m_state.GetResult() == TxValidationResult::TX_RECONSIDERABLE) {
1470
1755
  // Failed for incentives-based fee reasons. Provide the effective feerate and which tx was included.
1471
1756
  return MempoolAcceptResult::FeeFailure(ws.m_state, CFeeRate(ws.m_modified_fees, ws.m_vsize), single_wtxid);
@@ -1489,7 +1774,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
1489
1774
  FinalizeSubpackage(args);
1490
1775
 
1491
1776
  // Limit the mempool, if appropriate.
1492
- if (!args.m_package_submission && !args.m_bypass_limits) {
1777
+ if (!args.m_package_submission && !args.m_ignore_rejects.count(rejectmsg_mempoolfull)) {
1493
1778
  LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip());
1494
1779
  // If mempool contents change, then the m_view cache is dirty. Given this isn't a package
1495
1780
  // submission, we won't be using the cache anymore, but clear it anyway for clarity.
@@ -1508,7 +1793,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
1508
1793
  Assume(iter.has_value());
1509
1794
  const auto tx_info = NewMempoolTransactionInfo(ws.m_ptx, ws.m_base_fees,
1510
1795
  ws.m_vsize, (*iter)->GetHeight(),
1511
- args.m_bypass_limits, args.m_package_submission,
1796
+ args.m_ignore_rejects, args.m_package_submission,
1512
1797
  IsCurrentForFeeEstimation(m_active_chainstate),
1513
1798
  m_pool.HasNoInputsOf(tx));
1514
1799
  m_pool.m_opts.signals->TransactionAddedToMempool(tx_info, m_pool.GetAndIncrementSequence());
@@ -1575,12 +1860,14 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
1575
1860
 
1576
1861
  // At this point we have all in-mempool ancestors, and we know every transaction's vsize.
1577
1862
  // Run the TRUC checks on the package.
1863
+ if (m_pool.m_opts.truc_policy == TRUCPolicy::Enforce) {
1864
+ std::string reason;
1578
1865
  for (Workspace& ws : workspaces) {
1579
- if (auto err{PackageTRUCChecks(ws.m_ptx, ws.m_vsize, txns, ws.m_ancestors)}) {
1580
- package_state.Invalid(PackageValidationResult::PCKG_POLICY, "TRUC-violation", err.value());
1866
+ if (auto err{PackageTRUCChecks(ws.m_ptx, ws.m_vsize, "truc-", reason, args.m_ignore_rejects, txns, ws.m_ancestors)}) {
1867
+ package_state.Invalid(PackageValidationResult::PCKG_POLICY, reason, err.value());
1581
1868
  return PackageMempoolAcceptResult(package_state, {});
1582
1869
  }
1583
- }
1870
+ }}
1584
1871
 
1585
1872
  // Transactions must meet two minimum feerates: the mempool minimum fee and min relay fee.
1586
1873
  // For transactions consisting of exactly one child and its parents, it suffices to use the
@@ -1602,7 +1889,8 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
1602
1889
  [](const auto& ws) { return ws.m_ptx->GetWitnessHash(); });
1603
1890
  TxValidationState placeholder_state;
1604
1891
  if (args.m_package_feerates &&
1605
- !CheckFeeRate(m_subpackage.m_total_vsize, m_subpackage.m_total_modified_fees, placeholder_state)) {
1892
+ (!args.m_ignore_rejects.count("package-fee-too-low")) &&
1893
+ !CheckFeeRate(m_subpackage.m_total_vsize, m_subpackage.m_total_modified_fees, placeholder_state, empty_ignore_rejects)) {
1606
1894
  package_state.Invalid(PackageValidationResult::PCKG_TX, "transaction failed");
1607
1895
  return PackageMempoolAcceptResult(package_state, {{workspaces.back().m_ptx->GetWitnessHash(),
1608
1896
  MempoolAcceptResult::FeeFailure(placeholder_state, CFeeRate(m_subpackage.m_total_modified_fees, m_subpackage.m_total_vsize), all_package_wtxids)}});
@@ -1610,12 +1898,12 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
1610
1898
 
1611
1899
  // Apply package mempool ancestor/descendant limits. Skip if there is only one transaction,
1612
1900
  // because it's unnecessary.
1613
- if (txns.size() > 1 && !PackageMempoolChecks(txns, workspaces, m_subpackage.m_total_vsize, package_state)) {
1901
+ if (txns.size() > 1 && !PackageMempoolChecks(args, txns, workspaces, m_subpackage.m_total_vsize, package_state)) {
1614
1902
  return PackageMempoolAcceptResult(package_state, std::move(results));
1615
1903
  }
1616
1904
 
1617
1905
  // Now that we've bounded the resulting possible ancestry count, check package for dust spends
1618
- if (m_pool.m_opts.require_standard) {
1906
+ if (m_pool.m_opts.require_standard && !(args.m_ignore_rejects.count("dust") || args.m_ignore_rejects.count("unspent-dust") || args.m_ignore_rejects.count("missing-ephemeral-spends"))) {
1619
1907
  TxValidationState child_state;
1620
1908
  Wtxid child_wtxid;
1621
1909
  if (!CheckEphemeralSpends(txns, m_pool.m_opts.dust_relay_feerate, m_pool, child_state, child_wtxid)) {
@@ -1865,7 +2153,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
1865
2153
  } // anon namespace
1866
2154
 
1867
2155
  MempoolAcceptResult AcceptToMemoryPool(Chainstate& active_chainstate, const CTransactionRef& tx,
1868
- int64_t accept_time, bool bypass_limits, bool test_accept)
2156
+ int64_t accept_time, const ignore_rejects_type& ignore_rejects, bool test_accept)
1869
2157
  {
1870
2158
  AssertLockHeld(::cs_main);
1871
2159
  const CChainParams& chainparams{active_chainstate.m_chainman.GetParams()};
@@ -1873,7 +2161,7 @@ MempoolAcceptResult AcceptToMemoryPool(Chainstate& active_chainstate, const CTra
1873
2161
  CTxMemPool& pool{*active_chainstate.GetMempool()};
1874
2162
 
1875
2163
  std::vector<COutPoint> coins_to_uncache;
1876
- auto args = MemPoolAccept::ATMPArgs::SingleAccept(chainparams, accept_time, bypass_limits, coins_to_uncache, test_accept);
2164
+ auto args = MemPoolAccept::ATMPArgs::SingleAccept(chainparams, accept_time, ignore_rejects, coins_to_uncache, test_accept);
1877
2165
  MempoolAcceptResult result = MemPoolAccept(pool, active_chainstate).AcceptSingleTransaction(tx, args);
1878
2166
  if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
1879
2167
  // Remove coins that were not present in the coins cache before calling
@@ -1895,7 +2183,7 @@ MempoolAcceptResult AcceptToMemoryPool(Chainstate& active_chainstate, const CTra
1895
2183
  }
1896
2184
 
1897
2185
  PackageMempoolAcceptResult ProcessNewPackage(Chainstate& active_chainstate, CTxMemPool& pool,
1898
- const Package& package, bool test_accept, const std::optional<CFeeRate>& client_maxfeerate)
2186
+ const Package& package, bool test_accept, const std::optional<CFeeRate>& client_maxfeerate, const ignore_rejects_type& ignore_rejects)
1899
2187
  {
1900
2188
  AssertLockHeld(cs_main);
1901
2189
  assert(!package.empty());
@@ -1906,10 +2194,10 @@ PackageMempoolAcceptResult ProcessNewPackage(Chainstate& active_chainstate, CTxM
1906
2194
  auto result = [&]() EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
1907
2195
  AssertLockHeld(cs_main);
1908
2196
  if (test_accept) {
1909
- auto args = MemPoolAccept::ATMPArgs::PackageTestAccept(chainparams, GetTime(), coins_to_uncache);
2197
+ auto args = MemPoolAccept::ATMPArgs::PackageTestAccept(chainparams, GetTime(), ignore_rejects, coins_to_uncache);
1910
2198
  return MemPoolAccept(pool, active_chainstate).AcceptMultipleTransactions(package, args);
1911
2199
  } else {
1912
- auto args = MemPoolAccept::ATMPArgs::PackageChildWithParents(chainparams, GetTime(), coins_to_uncache, client_maxfeerate);
2200
+ auto args = MemPoolAccept::ATMPArgs::PackageChildWithParents(chainparams, GetTime(), coins_to_uncache, client_maxfeerate, ignore_rejects);
1913
2201
  return MemPoolAccept(pool, active_chainstate).AcceptPackage(package, args);