v31.1-roots.1 · Commit 3: [PATCH 03/17] feat(policy): preserve cluster accounting and repair dynamic dust
src/validation.cpp
Before: src/validation.cpp · After: src/validation.cpp
File metadata
index 5a07852971bb4688ccdd1c4b26f42dbda946ba4b..f94c378f4ee1ed60ecf8b83ac37a12a277d2ecdd 100644
Download original patch · Release notes
Diff rows 1–197 of 197. Line numbers refer to the original before/after files.
+ Added− RemovedUnmarked lines are context.
Scroll the code area horizontally to read long lines. Before is on the left; after is on the right. Unified view fits narrow screens more easily.
src/validation.cpp
CHANGED
|
@@ -839,9 +839,14 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
| 839 |
if (tx.IsCoinBase())
|
| 840 |
return state.Invalid(TxValidationResult::TX_CONSENSUS, "coinbase");
|
| 841 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 842 |
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
|
| 843 |
std::string reason;
|
| 844 |
-
if (m_pool.m_opts.require_standard && !IsStandardTx(tx, m_pool.m_opts, reason)) {
|
|
|
|
| 845 |
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, reason);
|
| 846 |
}
|
| 847 |
|
|
@@ -874,10 +879,27 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
| 874 |
// Transaction conflicts with a mempool tx, but we're not allowing replacements in this context.
|
| 875 |
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "bip125-replacement-disallowed");
|
| 876 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 877 |
ws.m_conflicts.insert(ptxConflicting->GetHash());
|
| 878 |
}
|
| 879 |
}
|
| 880 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 881 |
m_view.SetBackend(m_viewmempool);
|
| 882 |
|
| 883 |
const CCoinsViewCache& coins_cache = m_active_chainstate.CoinsTip();
|
|
@@ -948,6 +970,23 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
| 948 |
if (coinblocks < m_pool.m_opts.minrelaycoinblocks) {
|
| 949 |
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "bad-txns-input-immature-coinblocks");
|
| 950 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 951 |
if (m_pool.m_opts.datacarrier_fullcount || !m_pool.m_opts.accept_non_std_datacarrier) {
|
| 952 |
const auto [standard_bytes, nonstandard_bytes] = DatacarrierBytes(tx, m_view);
|
| 953 |
if (nonstandard_bytes && !m_pool.m_opts.accept_non_std_datacarrier) {
|
|
@@ -958,7 +997,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
| 958 |
}
|
| 959 |
}
|
| 960 |
|
| 961 |
-
if (m_pool.m_opts.require_standard && !AreInputsStandard(tx, m_view, m_pool.m_opts, "bad-txns-input-", reason)) {
|
| 962 |
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, reason);
|
| 963 |
}
|
| 964 |
|
|
@@ -986,7 +1025,51 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
| 986 |
if (!m_subpackage.m_changeset) {
|
| 987 |
m_subpackage.m_changeset = m_pool.GetChangeSet();
|
| 988 |
}
|
| 989 |
-
ws.m_tx_handle = m_subpackage.m_changeset->StageAddition(ptx, ws.m_base_fees, nAcceptTime, m_active_chainstate.m_chain.Height(), entry_sequence, fSpendsCoinbase, nSigOpsCost, lock_points.value());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 990 |
|
| 991 |
// ws.m_modified_fees includes any fee deltas from PrioritiseTransaction
|
| 992 |
ws.m_modified_fees = ws.m_tx_handle->GetModifiedFee();
|
|
@@ -995,14 +1078,13 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
| 995 |
|
| 996 |
// Enforces 0-fee for dust transactions, no incentive to be mined alone
|
| 997 |
if (m_pool.m_opts.require_standard) {
|
| 998 |
-
if (!PreCheckEphemeralTx(*ptx, m_pool.m_opts.dust_relay_feerate, ws.m_base_fees, ws.m_modified_fees, state)) {
|
| 999 |
return false; // state filled in by PreCheckEphemeralTx
|
| 1000 |
}
|
| 1001 |
}
|
| 1002 |
|
| 1003 |
-
if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST ||
|
| 1004 |
-
(nBytesPerSigOpStrict && GetAccurateTransactionSigOpCost(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS) >
|
| 1005 |
-
ws.m_vsize * WITNESS_SCALE_FACTOR / nBytesPerSigOpStrict))
|
| 1006 |
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-too-many-sigops",
|
| 1007 |
strprintf("%d", nSigOpsCost));
|
| 1008 |
|
|
@@ -1015,11 +1097,11 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
| 1015 |
|
| 1016 |
ws.m_parents = m_pool.GetParents(*ws.m_tx_handle);
|
| 1017 |
|
| 1018 |
-
if (!args.m_bypass_limits) {
|
| 1019 |
// Perform the TRUC checks, using the in-mempool parents.
|
| 1020 |
if (const auto err{SingleTRUCChecks(m_pool, ws.m_ptx, ws.m_parents, ws.m_conflicts, ws.m_vsize)}) {
|
| 1021 |
// Single transaction contexts only.
|
| 1022 |
-
if (args.m_allow_sibling_eviction && err->second != nullptr) {
|
| 1023 |
// We should only be considering where replacement is considered valid as well.
|
| 1024 |
Assume(args.m_allow_replacement);
|
| 1025 |
// Potential sibling eviction. Add the sibling to our list of mempool conflicts to be
|
|
@@ -1040,6 +1122,15 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
| 1040 |
}
|
| 1041 |
}
|
| 1042 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1043 |
// We want to detect conflicts in any tx in a package to trigger package RBF logic
|
| 1044 |
m_subpackage.m_rbf |= !ws.m_conflicts.empty();
|
| 1045 |
return true;
|
|
@@ -1436,7 +1527,7 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransactionInternal(const CTransa
|
|
| 1436 |
|
| 1437 |
if (!args.m_bypass_limits && m_pool.m_opts.require_standard) {
|
| 1438 |
Wtxid dummy_wtxid;
|
| 1439 |
-
if (!CheckEphemeralSpends(/*package=*/{ptx}, m_pool.m_opts.dust_relay_feerate, m_pool, ws.m_state, dummy_wtxid)) {
|
| 1440 |
return MempoolAcceptResult::Failure(ws.m_state);
|
| 1441 |
}
|
| 1442 |
}
|
|
@@ -1541,9 +1632,9 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactionsInternal(con
|
|
| 1541 |
}
|
| 1542 |
|
| 1543 |
// At this point we have all in-mempool parents, and we know every transaction's vsize.
|
| 1544 |
-
// Run the TRUC checks on the package.
|
| 1545 |
for (Workspace& ws : workspaces) {
|
| 1546 |
-
if (auto err{PackageTRUCChecks(m_pool, ws.m_ptx, ws.m_vsize, txns, ws.m_parents)}) {
|
| 1547 |
package_state.Invalid(PackageValidationResult::PCKG_POLICY, "TRUC-violation", err.value());
|
| 1548 |
return PackageMempoolAcceptResult(package_state, {});
|
| 1549 |
}
|
|
@@ -1590,7 +1681,7 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactionsInternal(con
|
|
| 1590 |
if (m_pool.m_opts.require_standard) {
|
| 1591 |
TxValidationState child_state;
|
| 1592 |
Wtxid child_wtxid;
|
| 1593 |
-
if (!CheckEphemeralSpends(txns, m_pool.m_opts.dust_relay_feerate, m_pool, child_state, child_wtxid)) {
|
| 1594 |
package_state.Invalid(PackageValidationResult::PCKG_TX, "unspent-dust");
|
| 1595 |
results.emplace(child_wtxid, MempoolAcceptResult::Failure(child_state));
|
| 1596 |
return PackageMempoolAcceptResult(package_state, std::move(results));
|
|
|
|
| 839 |
if (tx.IsCoinBase())
|
| 840 |
return state.Invalid(TxValidationResult::TX_CONSENSUS, "coinbase");
|
| 841 |
|
| 842 |
+
if (tx.version == TRUC_VERSION && m_pool.m_opts.truc_policy == TRUCPolicy::Reject) {
|
| 843 |
+
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "version");
|
| 844 |
+
}
|
| 845 |
+
|
| 846 |
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
|
| 847 |
std::string reason;
|
| 848 |
+
const StandardnessOptions standardness{m_pool.GetStandardnessOptions()};
|
| 849 |
+
if (m_pool.m_opts.require_standard && !IsStandardTx(tx, standardness, reason)) {
|
| 850 |
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, reason);
|
| 851 |
}
|
| 852 |
|
|
|
|
| 879 |
// Transaction conflicts with a mempool tx, but we're not allowing replacements in this context.
|
| 880 |
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "bip125-replacement-disallowed");
|
| 881 |
}
|
| 882 |
+
if ((m_pool.m_opts.rbf_policy == RBFPolicy::Never ||
|
| 883 |
+
(m_pool.m_opts.rbf_policy == RBFPolicy::OptIn && !SignalsOptInRBF(*ptxConflicting) && ptxConflicting->version != TRUC_VERSION))) {
|
| 884 |
+
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-mempool-conflict");
|
| 885 |
+
}
|
| 886 |
ws.m_conflicts.insert(ptxConflicting->GetHash());
|
| 887 |
}
|
| 888 |
}
|
| 889 |
|
| 890 |
+
std::map<CScript, unsigned> script_claims;
|
| 891 |
+
std::set<Txid> script_conflicts;
|
| 892 |
+
if (m_pool.m_opts.reject_spk_reuse) {
|
| 893 |
+
for (const auto& output : tx.vout) {
|
| 894 |
+
if (script_claims.contains(output.scriptPubKey)) {
|
| 895 |
+
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-spk-reused-twinoutputs");
|
| 896 |
+
}
|
| 897 |
+
script_claims[output.scriptPubKey] |= 2;
|
| 898 |
+
const auto conflicts = m_pool.GetScriptConflicts(output.scriptPubKey, true);
|
| 899 |
+
script_conflicts.insert(conflicts.begin(), conflicts.end());
|
| 900 |
+
}
|
| 901 |
+
}
|
| 902 |
+
|
| 903 |
m_view.SetBackend(m_viewmempool);
|
| 904 |
|
| 905 |
const CCoinsViewCache& coins_cache = m_active_chainstate.CoinsTip();
|
|
|
|
| 970 |
if (coinblocks < m_pool.m_opts.minrelaycoinblocks) {
|
| 971 |
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "bad-txns-input-immature-coinblocks");
|
| 972 |
}
|
| 973 |
+
if (m_pool.m_opts.reject_spk_reuse) {
|
| 974 |
+
for (const auto& input : tx.vin) {
|
| 975 |
+
const auto& script = m_view.AccessCoin(input.prevout).out.scriptPubKey;
|
| 976 |
+
if (script_claims[script] & 2) {
|
| 977 |
+
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-spk-reused-change");
|
| 978 |
+
}
|
| 979 |
+
script_claims[script] |= 1;
|
| 980 |
+
const auto conflicts = m_pool.GetScriptConflicts(script, false);
|
| 981 |
+
script_conflicts.insert(conflicts.begin(), conflicts.end());
|
| 982 |
+
}
|
| 983 |
+
{
|
| 984 |
+
if (!args.m_allow_replacement && !script_conflicts.empty()) {
|
| 985 |
+
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "bip125-replacement-disallowed");
|
| 986 |
+
}
|
| 987 |
+
ws.m_conflicts.insert(script_conflicts.begin(), script_conflicts.end());
|
| 988 |
+
}
|
| 989 |
+
}
|
| 990 |
if (m_pool.m_opts.datacarrier_fullcount || !m_pool.m_opts.accept_non_std_datacarrier) {
|
| 991 |
const auto [standard_bytes, nonstandard_bytes] = DatacarrierBytes(tx, m_view);
|
| 992 |
if (nonstandard_bytes && !m_pool.m_opts.accept_non_std_datacarrier) {
|
|
|
|
| 997 |
}
|
| 998 |
}
|
| 999 |
|
| 1000 |
+
if (m_pool.m_opts.require_standard && !AreInputsStandard(tx, m_view, standardness, "bad-txns-input-", reason)) {
|
| 1001 |
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, reason);
|
| 1002 |
}
|
| 1003 |
|
|
|
|
| 1025 |
if (!m_subpackage.m_changeset) {
|
| 1026 |
m_subpackage.m_changeset = m_pool.GetChangeSet();
|
| 1027 |
}
|
| 1028 |
+
if (!bypass_limits) {
|
| 1029 |
+
for (const auto& [script, claims] : script_claims) {
|
| 1030 |
+
if (m_subpackage.m_changeset->HasStagedScriptConflict(script, claims & 2)) {
|
| 1031 |
+
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-spk-reused-package");
|
| 1032 |
+
}
|
| 1033 |
+
}
|
| 1034 |
+
}
|
| 1035 |
+
const int32_t extra_weight{CalculateExtraTxWeight(tx, m_view, m_pool.m_opts.weight_per_data_byte)};
|
| 1036 |
+
// Bound sigop cost before multiplying by the initialized unsigned policy
|
| 1037 |
+
// factor. This is the same local limit checked below, moved ahead of math.
|
| 1038 |
+
if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST) {
|
| 1039 |
+
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-too-many-sigops", strprintf("%d", nSigOpsCost));
|
| 1040 |
+
}
|
| 1041 |
+
const int64_t adjusted_weight{GetSigOpsAdjustedWeight(GetTransactionWeight(tx) + int64_t{extra_weight}, nSigOpsCost, nBytesPerSigOp)};
|
| 1042 |
+
const int64_t policy_vsize{(adjusted_weight + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR};
|
| 1043 |
+
// CFeeRate accepts int32 vsize. Extreme native bytespersigop settings must
|
| 1044 |
+
// fail the graph limit before an implicit narrowing can wrap that argument.
|
| 1045 |
+
if (policy_vsize > INT32_MAX) {
|
| 1046 |
+
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too-large-cluster");
|
| 1047 |
+
}
|
| 1048 |
+
CAmount dust_penalty{0};
|
| 1049 |
+
if (m_pool.m_opts.subdustfeepenalty) {
|
| 1050 |
+
for (const auto& output : tx.vout) {
|
| 1051 |
+
const CAmount threshold{GetDustThreshold(output, standardness.dust_relay_feerate)};
|
| 1052 |
+
if (output.nValue < threshold) dust_penalty = SaturatingAdd(dust_penalty, threshold - output.nValue);
|
| 1053 |
+
}
|
| 1054 |
+
}
|
| 1055 |
+
CAmount delta{0};
|
| 1056 |
+
m_pool.ApplyDelta(hash, delta);
|
| 1057 |
+
const CAmount modified_fee{SaturatingAdd(SaturatingAdd(ws.m_base_fees, delta), -dust_penalty)};
|
| 1058 |
+
ws.m_modified_fees = modified_fee;
|
| 1059 |
+
ws.m_vsize = policy_vsize;
|
| 1060 |
+
// Reject ordinary low-fee transactions before placing extreme deficit/weight
|
| 1061 |
+
// values in the native graph. TRUC sponsorship is checked at package level.
|
| 1062 |
+
const bool sponsored{tx.version == TRUC_VERSION && m_pool.m_opts.truc_policy == TRUCPolicy::Enforce};
|
| 1063 |
+
if (!bypass_limits && !sponsored && modified_fee < m_pool.m_opts.min_relay_feerate.GetFee(policy_vsize)) {
|
| 1064 |
+
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "min relay fee not met", strprintf("%d < %d", modified_fee, m_pool.m_opts.min_relay_feerate.GetFee(policy_vsize)));
|
| 1065 |
+
}
|
| 1066 |
+
if (!m_pool.CanAddModifiedFee(modified_fee)) {
|
| 1067 |
+
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool-fee-overflow");
|
| 1068 |
+
}
|
| 1069 |
+
if (!m_pool.CanAddPolicyWeight(adjusted_weight)) {
|
| 1070 |
+
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too-large-cluster");
|
| 1071 |
+
}
|
| 1072 |
+
ws.m_tx_handle = m_subpackage.m_changeset->StageAddition(ptx, ws.m_base_fees, nAcceptTime, m_active_chainstate.m_chain.Height(), entry_sequence, fSpendsCoinbase, nSigOpsCost, lock_points.value(), extra_weight, dust_penalty, std::move(script_claims));
|
| 1073 |
|
| 1074 |
// ws.m_modified_fees includes any fee deltas from PrioritiseTransaction
|
| 1075 |
ws.m_modified_fees = ws.m_tx_handle->GetModifiedFee();
|
|
|
|
| 1078 |
|
| 1079 |
// Enforces 0-fee for dust transactions, no incentive to be mined alone
|
| 1080 |
if (m_pool.m_opts.require_standard) {
|
| 1081 |
+
if (!PreCheckEphemeralTx(*ptx, standardness.dust_relay_feerate, ws.m_base_fees, ws.m_modified_fees, state)) {
|
| 1082 |
return false; // state filled in by PreCheckEphemeralTx
|
| 1083 |
}
|
| 1084 |
}
|
| 1085 |
|
| 1086 |
+
if (nBytesPerSigOpStrict && GetAccurateTransactionSigOpCost(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS) >
|
| 1087 |
+
ws.m_vsize * WITNESS_SCALE_FACTOR / nBytesPerSigOpStrict)
|
|
|
|
| 1088 |
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-too-many-sigops",
|
| 1089 |
strprintf("%d", nSigOpsCost));
|
| 1090 |
|
|
|
|
| 1097 |
|
| 1098 |
ws.m_parents = m_pool.GetParents(*ws.m_tx_handle);
|
| 1099 |
|
| 1100 |
+
if (!args.m_bypass_limits && m_pool.m_opts.truc_policy == TRUCPolicy::Enforce) {
|
| 1101 |
// Perform the TRUC checks, using the in-mempool parents.
|
| 1102 |
if (const auto err{SingleTRUCChecks(m_pool, ws.m_ptx, ws.m_parents, ws.m_conflicts, ws.m_vsize)}) {
|
| 1103 |
// Single transaction contexts only.
|
| 1104 |
+
if (args.m_allow_sibling_eviction && m_pool.m_opts.rbf_policy != RBFPolicy::Never && err->second != nullptr) {
|
| 1105 |
// We should only be considering where replacement is considered valid as well.
|
| 1106 |
Assume(args.m_allow_replacement);
|
| 1107 |
// Potential sibling eviction. Add the sibling to our list of mempool conflicts to be
|
|
|
|
| 1122 |
}
|
| 1123 |
}
|
| 1124 |
|
| 1125 |
+
if (!script_conflicts.empty()) {
|
| 1126 |
+
const auto ancestors = m_subpackage.m_changeset->CalculateMemPoolAncestors(ws.m_tx_handle);
|
| 1127 |
+
for (const auto& ancestor : ancestors) {
|
| 1128 |
+
if (script_conflicts.contains(ancestor->GetTx().GetHash())) {
|
| 1129 |
+
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-spk-reused-chained");
|
| 1130 |
+
}
|
| 1131 |
+
}
|
| 1132 |
+
}
|
| 1133 |
+
|
| 1134 |
// We want to detect conflicts in any tx in a package to trigger package RBF logic
|
| 1135 |
m_subpackage.m_rbf |= !ws.m_conflicts.empty();
|
| 1136 |
return true;
|
|
|
|
| 1527 |
|
| 1528 |
if (!args.m_bypass_limits && m_pool.m_opts.require_standard) {
|
| 1529 |
Wtxid dummy_wtxid;
|
| 1530 |
+
if (!CheckEphemeralSpends(/*package=*/{ptx}, m_pool.GetDustRelayFee(), m_pool, ws.m_state, dummy_wtxid)) {
|
| 1531 |
return MempoolAcceptResult::Failure(ws.m_state);
|
| 1532 |
}
|
| 1533 |
}
|
|
|
|
| 1632 |
}
|
| 1633 |
|
| 1634 |
// At this point we have all in-mempool parents, and we know every transaction's vsize.
|
| 1635 |
+
// Run the configured TRUC checks on the package.
|
| 1636 |
for (Workspace& ws : workspaces) {
|
| 1637 |
+
if (auto err{m_pool.m_opts.truc_policy == TRUCPolicy::Enforce ? PackageTRUCChecks(m_pool, ws.m_ptx, ws.m_vsize, txns, ws.m_parents) : std::nullopt}) {
|
| 1638 |
package_state.Invalid(PackageValidationResult::PCKG_POLICY, "TRUC-violation", err.value());
|
| 1639 |
return PackageMempoolAcceptResult(package_state, {});
|
| 1640 |
}
|
|
|
|
| 1681 |
if (m_pool.m_opts.require_standard) {
|
| 1682 |
TxValidationState child_state;
|
| 1683 |
Wtxid child_wtxid;
|
| 1684 |
+
if (!CheckEphemeralSpends(txns, m_pool.GetDustRelayFee(), m_pool, child_state, child_wtxid)) {
|
| 1685 |
package_state.Invalid(PackageValidationResult::PCKG_TX, "unspent-dust");
|
| 1686 |
results.emplace(child_wtxid, MempoolAcceptResult::Failure(child_state));
|
| 1687 |
return PackageMempoolAcceptResult(package_state, std::move(results));
|