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 1–400 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
@@ -106,6 +106,8 @@ const std::vector<std::string> CHECKLEVEL_DOC {
106
106
  "level 4 tries to reconnect the blocks",
107
107
  "each level includes the checks of the previous levels",
108
108
  };
109
+
110
+ SpkReuseModes SpkReuseMode{SRM_ALLOW};
109
111
  /** The number of blocks to keep below the deepest prune lock.
110
112
  * There is nothing special about this number. It is higher than what we
111
113
  * expect to see in regular mainnet reorgs, but not so high that it would
@@ -272,7 +274,39 @@ bool CheckSequenceLocksAtTip(CBlockIndex* tip,
272
274
  // Returns the script flags which should be checked for a given block
273
275
  static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman);
274
276
 
275
- static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache)
277
+ /** Compute accurate total signature operation cost of a transaction.
278
+ * Not consensus-critical, since legacy sigops counting is always used in the protocol.
279
+ */
280
+ int64_t GetAccurateTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, int flags)
281
+ {
282
+ if (tx.IsCoinBase()) {
283
+ return 0;
284
+ }
285
+
286
+ unsigned int nSigOps = 0;
287
+ for (const auto& txin : tx.vin) {
288
+ nSigOps += txin.scriptSig.GetSigOpCount(false);
289
+ }
290
+
291
+ if (flags & SCRIPT_VERIFY_P2SH) {
292
+ nSigOps += GetP2SHSigOpCount(tx, inputs);
293
+ }
294
+
295
+ nSigOps *= WITNESS_SCALE_FACTOR;
296
+
297
+ if (flags & SCRIPT_VERIFY_WITNESS) {
298
+ for (const auto& txin : tx.vin) {
299
+ const Coin& coin = inputs.AccessCoin(txin.prevout);
300
+ assert(!coin.IsSpent());
301
+ const CTxOut &prevout = coin.out;
302
+ nSigOps += CountWitnessSigOps(txin.scriptSig, prevout.scriptPubKey, &txin.scriptWitness, flags);
303
+ }
304
+ }
305
+
306
+ return nSigOps;
307
+ }
308
+
309
+ void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache)
276
310
  EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs)
277
311
  {
278
312
  AssertLockHeld(::cs_main);
@@ -459,7 +493,7 @@ public:
459
493
  struct ATMPArgs {
460
494
  const CChainParams& m_chainparams;
461
495
  const int64_t m_accept_time;
462
- const bool m_bypass_limits;
496
+ const ignore_rejects_type& m_ignore_rejects;
463
497
  /*
464
498
  * Return any outpoints which were not previously present in the coins
465
499
  * cache, but were added as a result of validating the tx for mempool
@@ -494,11 +528,11 @@ public:
494
528
 
495
529
  /** Parameters for single transaction mempool validation. */
496
530
  static ATMPArgs SingleAccept(const CChainParams& chainparams, int64_t accept_time,
497
- bool bypass_limits, std::vector<COutPoint>& coins_to_uncache,
531
+ const ignore_rejects_type& ignore_rejects, std::vector<COutPoint>& coins_to_uncache,
498
532
  bool test_accept) {
499
533
  return ATMPArgs{/* m_chainparams */ chainparams,
500
534
  /* m_accept_time */ accept_time,
501
- /* m_bypass_limits */ bypass_limits,
535
+ /* m_ignore_rejects */ ignore_rejects,
502
536
  /* m_coins_to_uncache */ coins_to_uncache,
503
537
  /* m_test_accept */ test_accept,
504
538
  /* m_allow_replacement */ true,
@@ -512,10 +546,10 @@ public:
512
546
 
513
547
  /** Parameters for test package mempool validation through testmempoolaccept. */
514
548
  static ATMPArgs PackageTestAccept(const CChainParams& chainparams, int64_t accept_time,
515
- std::vector<COutPoint>& coins_to_uncache) {
549
+ const ignore_rejects_type& ignore_rejects, std::vector<COutPoint>& coins_to_uncache) {
516
550
  return ATMPArgs{/* m_chainparams */ chainparams,
517
551
  /* m_accept_time */ accept_time,
518
- /* m_bypass_limits */ false,
552
+ /* m_ignore_rejects */ ignore_rejects,
519
553
  /* m_coins_to_uncache */ coins_to_uncache,
520
554
  /* m_test_accept */ true,
521
555
  /* m_allow_replacement */ false,
@@ -529,10 +563,10 @@ public:
529
563
 
530
564
  /** Parameters for child-with-parents package validation. */
531
565
  static ATMPArgs PackageChildWithParents(const CChainParams& chainparams, int64_t accept_time,
532
- std::vector<COutPoint>& coins_to_uncache, const std::optional<CFeeRate>& client_maxfeerate) {
566
+ std::vector<COutPoint>& coins_to_uncache, const std::optional<CFeeRate>& client_maxfeerate, const ignore_rejects_type& ignore_rejects) {
533
567
  return ATMPArgs{/* m_chainparams */ chainparams,
534
568
  /* m_accept_time */ accept_time,
535
- /* m_bypass_limits */ false,
569
+ /* m_ignore_rejects */ ignore_rejects,
536
570
  /* m_coins_to_uncache */ coins_to_uncache,
537
571
  /* m_test_accept */ false,
538
572
  /* m_allow_replacement */ true,
@@ -548,7 +582,7 @@ public:
548
582
  static ATMPArgs SingleInPackageAccept(const ATMPArgs& package_args) {
549
583
  return ATMPArgs{/* m_chainparams */ package_args.m_chainparams,
550
584
  /* m_accept_time */ package_args.m_accept_time,
551
- /* m_bypass_limits */ false,
585
+ empty_ignore_rejects,
552
586
  /* m_coins_to_uncache */ package_args.m_coins_to_uncache,
553
587
  /* m_test_accept */ package_args.m_test_accept,
554
588
  /* m_allow_replacement */ true,
@@ -565,7 +599,7 @@ public:
565
599
  // mixing up the order of the arguments. Use static functions above instead.
566
600
  ATMPArgs(const CChainParams& chainparams,
567
601
  int64_t accept_time,
568
- bool bypass_limits,
602
+ const ignore_rejects_type& ignore_rejects,
569
603
  std::vector<COutPoint>& coins_to_uncache,
570
604
  bool test_accept,
571
605
  bool allow_replacement,
@@ -576,7 +610,7 @@ public:
576
610
  bool allow_carveouts)
577
611
  : m_chainparams{chainparams},
578
612
  m_accept_time{accept_time},
579
- m_bypass_limits{bypass_limits},
613
+ m_ignore_rejects{ignore_rejects},
580
614
  m_coins_to_uncache{coins_to_uncache},
581
615
  m_test_accept{test_accept},
582
616
  m_allow_replacement{allow_replacement},
@@ -636,7 +670,8 @@ private:
636
670
  explicit Workspace(const CTransactionRef& ptx) : m_ptx(ptx), m_hash(ptx->GetHash()) {}
637
671
  /** Txids of mempool transactions that this transaction directly conflicts with or may
638
672
  * replace via sibling eviction. */
639
- std::set<Txid> m_conflicts;
673
+ /** .second=true is a consensus conflict, and .second=false is a policy conflict. */
674
+ std::map<Txid, bool> m_conflicts_incl_policy;
640
675
  /** Iterators to mempool entries that this transaction directly conflicts with or may
641
676
  * replace via sibling eviction. */
642
677
  CTxMemPool::setEntries m_iters_conflicting;
@@ -670,6 +705,23 @@ private:
670
705
  PrecomputedTransactionData m_precomputed_txdata;
671
706
  };
672
707
 
708
+ static inline bool MaybeReject_(TxValidationResult reason, const std::string& reason_str, const std::string& debug_msg, const ignore_rejects_type& ignore_rejects, TxValidationState& state) {
709
+ if (ignore_rejects.count(reason_str)) {
710
+ return false;
711
+ }
712
+
713
+ state.Invalid(reason, reason_str, debug_msg);
714
+ return true;
715
+ }
716
+
717
+ #define MaybeRejectDbg(reason, reason_str, debug_msg) do { \
718
+ if (MaybeReject_(reason, reason_str, debug_msg, ignore_rejects, state)) { \
719
+ return false; \
720
+ } \
721
+ } while(0)
722
+
723
+ #define MaybeReject(reason, reason_str) MaybeRejectDbg(reason, reason_str, "")
724
+
673
725
  // Run the policy checks on a given transaction, excluding any script checks.
674
726
  // Looks up inputs, calculates feerate, considers replacement, evaluates
675
727
  // package limits, etc. As this function can be invoked for "free" by a peer,
@@ -677,11 +729,11 @@ private:
677
729
  bool PreChecks(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
678
730
 
679
731
  // Run checks for mempool replace-by-fee, only used in AcceptSingleTransaction.
680
- bool ReplacementChecks(Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
732
+ bool ReplacementChecks(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
681
733
 
682
734
  // Enforce package mempool ancestor/descendant limits (distinct from individual
683
735
  // ancestor/descendant limits done in PreChecks) and run Package RBF checks.
684
- bool PackageMempoolChecks(const std::vector<CTransactionRef>& txns,
736
+ bool PackageMempoolChecks(const ATMPArgs& args, const std::vector<CTransactionRef>& txns,
685
737
  std::vector<Workspace>& workspaces,
686
738
  int64_t total_vsize,
687
739
  PackageValidationState& package_state) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
@@ -707,16 +759,16 @@ private:
707
759
  EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
708
760
 
709
761
  // Compare a package's feerate against minimum allowed.
710
- bool CheckFeeRate(size_t package_size, CAmount package_fee, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_pool.cs)
762
+ bool CheckFeeRate(size_t package_size, CAmount package_fee, TxValidationState& state, const ignore_rejects_type& ignore_rejects) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_pool.cs)
711
763
  {
712
764
  AssertLockHeld(::cs_main);
713
765
  AssertLockHeld(m_pool.cs);
714
766
  CAmount mempoolRejectFee = m_pool.GetMinFee().GetFee(package_size);
715
- if (mempoolRejectFee > 0 && package_fee < mempoolRejectFee) {
767
+ if (mempoolRejectFee > 0 && package_fee < mempoolRejectFee && !ignore_rejects.count(rejectmsg_lowfee_mempool)) {
716
768
  return state.Invalid(TxValidationResult::TX_RECONSIDERABLE, "mempool min fee not met", strprintf("%d < %d", package_fee, mempoolRejectFee));
717
769
  }
718
770
 
719
- if (package_fee < m_pool.m_opts.min_relay_feerate.GetFee(package_size)) {
771
+ if (package_fee < m_pool.m_opts.min_relay_feerate.GetFee(package_size) && !ignore_rejects.count(rejectmsg_lowfee_relay)) {
720
772
  return state.Invalid(TxValidationResult::TX_RECONSIDERABLE, "min relay fee not met",
721
773
  strprintf("%d < %d", package_fee, m_pool.m_opts.min_relay_feerate.GetFee(package_size)));
722
774
  }
@@ -800,7 +852,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
800
852
 
801
853
  // Copy/alias what we need out of args
802
854
  const int64_t nAcceptTime = args.m_accept_time;
803
- const bool bypass_limits = args.m_bypass_limits;
855
+ const ignore_rejects_type& ignore_rejects = args.m_ignore_rejects;
804
856
  std::vector<COutPoint>& coins_to_uncache = args.m_coins_to_uncache;
805
857
 
806
858
  // Alias what we need out of ws
@@ -814,21 +866,25 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
814
866
  if (tx.IsCoinBase())
815
867
  return state.Invalid(TxValidationResult::TX_CONSENSUS, "coinbase");
816
868
 
869
+ if (tx.version == TRUC_VERSION && m_pool.m_opts.truc_policy == TRUCPolicy::Reject) {
870
+ return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "version");
871
+ }
872
+
817
873
  // Rather not work on nonstandard transactions (unless -testnet/-regtest)
818
874
  std::string reason;
819
- 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)) {
875
+ if (m_pool.m_opts.require_standard && !IsStandardTx(tx, m_pool.m_opts, reason, ignore_rejects)) {
820
876
  return state.Invalid(TxValidationResult::TX_NOT_STANDARD, reason);
821
877
  }
822
878
 
823
879
  // Transactions smaller than 65 non-witness bytes are not relayed to mitigate CVE-2017-12842.
824
880
  if (::GetSerializeSize(TX_NO_WITNESS(tx)) < MIN_STANDARD_TX_NONWITNESS_SIZE)
825
- return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "tx-size-small");
881
+ MaybeReject(TxValidationResult::TX_NOT_STANDARD, "tx-size-small");
826
882
 
827
883
  // Only accept nLockTime-using transactions that can be mined in the next
828
884
  // block; we don't want our mempool filled up with transactions that can't
829
885
  // be mined yet.
830
886
  if (!CheckFinalTxAtTip(*Assert(m_active_chainstate.m_chain.Tip()), tx)) {
831
- return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-final");
887
+ MaybeReject(TxValidationResult::TX_PREMATURE_SPEND, "non-final");
832
888
  }
833
889
 
834
890
  if (m_pool.exists(tx.GetWitnessHash())) {
@@ -840,16 +896,68 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
840
896
  return state.Invalid(TxValidationResult::TX_CONFLICT, "txn-same-nonwitness-data-in-mempool");
841
897
  }
842
898
 
899
+ auto spk_reuse_mode = SpkReuseMode;
900
+ if (ignore_rejects.count("txn-spk-reused")) {
901
+ spk_reuse_mode = SRM_ALLOW;
902
+ }
903
+ SPKStates_t mapSPK;
904
+
843
905
  // Check for conflicts with in-memory transactions
844
906
  for (const CTxIn &txin : tx.vin)
845
907
  {
846
908
  const CTransaction* ptxConflicting = m_pool.GetConflictTx(txin.prevout);
847
909
  if (ptxConflicting) {
848
910
  if (!args.m_allow_replacement) {
849
- // Transaction conflicts with a mempool tx, but we're not allowing replacements in this context.
911
+ // Transaction conflicts with a mempool tx, but we're not allowing replacements.
850
912
  return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "bip125-replacement-disallowed");
851
913
  }
852
- ws.m_conflicts.insert(ptxConflicting->GetHash());
914
+ if (!ws.m_conflicts_incl_policy.count(ptxConflicting->GetHash()))
915
+ {
916
+ // Transactions that don't explicitly signal replaceability are
917
+ // *not* replaceable with the current logic, even if one of their
918
+ // unconfirmed ancestors signals replaceability. This diverges
919
+ // from BIP125's inherited signaling description (see CVE-2021-31876).
920
+ // Applications relying on first-seen mempool behavior should
921
+ // check all unconfirmed ancestors; otherwise an opt-in ancestor
922
+ // might be replaced, causing removal of this descendant.
923
+ //
924
+ // All TRUC transactions are considered replaceable.
925
+ //
926
+ // Replaceability signaling of the original transactions may be
927
+ // ignored due to node setting.
928
+ bool allow_rbf;
929
+ if (m_pool.m_opts.rbf_policy == RBFPolicy::Always || ignore_rejects.count("txn-mempool-conflict")) {
930
+ allow_rbf = true;
931
+ } else if (m_pool.m_opts.rbf_policy == RBFPolicy::Never) {
932
+ allow_rbf = false;
933
+ } else {
934
+ allow_rbf = SignalsOptInRBF(*ptxConflicting) || ptxConflicting->version == TRUC_VERSION;
935
+ }
936
+ if (!allow_rbf) {
937
+ return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-mempool-conflict");
938
+ }
939
+
940
+ ws.m_conflicts_incl_policy.emplace(ptxConflicting->GetHash(), true);
941
+ }
942
+ }
943
+ }
944
+
945
+ if (spk_reuse_mode != SRM_ALLOW) {
946
+ for (const CTxOut& txout : tx.vout) {
947
+ uint160 hashSPK = ScriptHashkey(txout.scriptPubKey);
948
+ const auto& SPKUsedIn = m_pool.mapUsedSPK.find(hashSPK);
949
+ if (SPKUsedIn != m_pool.mapUsedSPK.end()) {
950
+ if (SPKUsedIn->second.first) {
951
+ ws.m_conflicts_incl_policy.emplace(SPKUsedIn->second.first->GetHash(), false);
952
+ }
953
+ if (SPKUsedIn->second.second) {
954
+ ws.m_conflicts_incl_policy.emplace(SPKUsedIn->second.second->GetHash(), false);
955
+ }
956
+ }
957
+ if (mapSPK.find(hashSPK) != mapSPK.end()) {
958
+ MaybeReject(TxValidationResult::TX_MEMPOOL_POLICY, "txn-spk-reused-twinoutputs");
959
+ }
960
+ mapSPK[hashSPK] = MemPool_SPK_State(mapSPK[hashSPK] | MSS_CREATED);
853
961
  }
854
962
  }
855
963
 
@@ -895,22 +1003,67 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
895
1003
  // Pass in m_view which has all of the relevant inputs cached. Note that, since m_view's
896
1004
  // backend was removed, it no longer pulls coins from the mempool.
897
1005
  const std::optional<LockPoints> lock_points{CalculateLockPointsAtTip(m_active_chainstate.m_chain.Tip(), m_view, tx)};
1006
+ // NOTE: The miner doesn't check this again, so for now it may not be overridden.
898
1007
  if (!lock_points.has_value() || !CheckSequenceLocksAtTip(m_active_chainstate.m_chain.Tip(), *lock_points)) {
899
1008
  return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final");
900
1009
  }
901
1010
 
902
1011
  // The mempool holds txs for the next block, so pass height+1 to CheckTxInputs
903
- if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_chain.Height() + 1, ws.m_base_fees)) {
1012
+ const auto block_height_current = m_active_chainstate.m_chain.Height();
1013
+ const auto block_height_next = block_height_current + 1;
1014
+ if (!Consensus::CheckTxInputs(tx, state, m_view, block_height_next, ws.m_base_fees)) {
904
1015
  return false; // state filled in by CheckTxInputs
905
1016
  }
906
1017
 
907
- if (m_pool.m_opts.require_standard && !AreInputsStandard(tx, m_view)) {
908
- return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
1018
+ if (m_pool.m_opts.minrelaymaturity) {
1019
+ auto max_coin_height = block_height_next - m_pool.m_opts.minrelaymaturity;
1020
+ static_assert(std::is_signed_v<decltype(max_coin_height)>, "Unsigned max_coin_height needs a range check");
1021
+ for (const CTxIn &txin : tx.vin) {
1022
+ const Coin &coin = m_view.AccessCoin(txin.prevout);
1023
+ if (coin.nHeight > max_coin_height) {
1024
+ MaybeReject(TxValidationResult::TX_PREMATURE_SPEND, "bad-txns-input-immature-depth");
1025
+ }
1026
+ }
1027
+ }
1028
+
1029
+ if (spk_reuse_mode != SRM_ALLOW) {
1030
+ for (const CTxIn& txin : tx.vin) {
1031
+ const Coin &coin = m_view.AccessCoin(txin.prevout);
1032
+ uint160 hashSPK = ScriptHashkey(coin.out.scriptPubKey);
1033
+
1034
+ SPKStates_t::iterator mssit = mapSPK.find(hashSPK);
1035
+ if (mssit != mapSPK.end()) {
1036
+ if (mssit->second & MSS_CREATED) {
1037
+ MaybeReject(TxValidationResult::TX_MEMPOOL_POLICY, "txn-spk-reused-change");
1038
+ }
1039
+ }
1040
+ const auto& SPKit = m_pool.mapUsedSPK.find(hashSPK);
1041
+ if (SPKit != m_pool.mapUsedSPK.end()) {
1042
+ if (SPKit->second.second /* Spent */) {
1043
+ ws.m_conflicts_incl_policy.emplace(SPKit->second.second->GetHash(), false);
1044
+ }
1045
+ }
1046
+ mapSPK[hashSPK] = MemPool_SPK_State(mapSPK[hashSPK] | MSS_SPENT);
1047
+ }
1048
+ }
1049
+
1050
+ if (m_pool.m_opts.require_standard && !AreInputsStandard(tx, m_view, m_pool.m_opts, "bad-txns-input-", reason, ignore_rejects)) {
1051
+ return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, reason);
1052
+ }
1053
+
1054
+ if (m_pool.m_opts.datacarrier_fullcount || !m_pool.m_opts.accept_non_std_datacarrier) {
1055
+ const auto dcb = DatacarrierBytes(tx, m_view);
1056
+ if (dcb.second > 0 && !(m_pool.m_opts.accept_non_std_datacarrier || ignore_rejects.count("txn-datacarrier-nonstandard"))) {
1057
+ return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "txn-datacarrier-nonstandard");
1058
+ }
1059
+ if (m_pool.m_opts.datacarrier_fullcount && (!ignore_rejects.count("txn-datacarrier-exceeded")) && dcb.first + dcb.second > m_pool.m_opts.max_datacarrier_bytes.value_or(0)) {
1060
+ return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "txn-datacarrier-exceeded");
1061
+ }
909
1062
  }
910
1063
 
911
1064
  // Check for non-standard witnesses.
912
- if (tx.HasWitness() && m_pool.m_opts.require_standard && !IsWitnessStandard(tx, m_view)) {
913
- return state.Invalid(TxValidationResult::TX_WITNESS_MUTATED, "bad-witness-nonstandard");
1065
+ if (tx.HasWitness() && m_pool.m_opts.require_standard && !IsWitnessStandard(tx, m_view, "bad-witness-", reason, ignore_rejects)) {
1066
+ return state.Invalid(TxValidationResult::TX_WITNESS_MUTATED, reason);
914
1067
  }
915
1068
 
916
1069
  int64_t nSigOpsCost = GetTransactionSigOpCost(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS);
@@ -926,29 +1079,63 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
926
1079
  }
927
1080
  }
928
1081
 
929
- // Set entry_sequence to 0 when bypass_limits is used; this allows txs from a block
1082
+ // Since entries arrive *after* the tip's height, their priority is for the height+1
1083
+ const auto coin_age = GetCoinAge(tx, m_view, block_height_next);
1084
+
1085
+ if (coin_age.inputs_coin_age < m_pool.m_opts.minrelaycoinblocks) {
1086
+ MaybeReject(TxValidationResult::TX_PREMATURE_SPEND, "bad-txns-input-immature-coinblocks");
1087
+ }
1088
+
1089
+ // Set entry_sequence to 0 when rejectmsg_zero_mempool_entry_seq is used; this allows txs from a block
930
1090
  // reorg to be marked earlier than any child txs that were already in the mempool.
931
- const uint64_t entry_sequence = bypass_limits ? 0 : m_pool.GetSequence();
1091
+ const uint64_t entry_sequence = args.m_ignore_rejects.count(rejectmsg_zero_mempool_entry_seq) ? 0 : m_pool.GetSequence();
1092
+ int32_t extra_weight = CalculateExtraTxWeight(*ptx, m_view, ::g_weight_per_data_byte);
932
1093
  if (!m_subpackage.m_changeset) {
933
1094
  m_subpackage.m_changeset = m_pool.GetChangeSet();
934
1095
  }
935
- 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());
1096
+ ws.m_tx_handle = m_subpackage.m_changeset->StageAddition(ptx, ws.m_base_fees, nAcceptTime, block_height_current, entry_sequence, coin_age, fSpendsCoinbase, /*extra_weight=*/ extra_weight, /*sigops_cost=*/ nSigOpsCost, lock_points.value());
1097
+
1098
+ if (spk_reuse_mode != SRM_ALLOW) {
1099
+ m_subpackage.m_changeset->m_to_add.modify(ws.m_tx_handle, [=](CTxMemPoolEntry& e) {
1100
+ e.mapSPK = mapSPK;
1101
+ });
1102
+ }
936
1103
 
937
1104
  // ws.m_modified_fees includes any fee deltas from PrioritiseTransaction
938
1105
  ws.m_modified_fees = ws.m_tx_handle->GetModifiedFee();
939
1106
 
940
1107
  ws.m_vsize = ws.m_tx_handle->GetTxSize();
941
1108
 
1109
+ // Reduce effective fee by dust threshold for each sub-dust output
1110
+ if (m_pool.m_opts.subdustfeepenalty) {
1111
+ CAmount dust_penalty{0};
1112
+ for (const auto& txout : tx.vout) {
1113
+ const CAmount dust_threshold = GetDustThreshold(txout, m_pool.m_opts.dust_relay_feerate);
1114
+ if (txout.nValue < dust_threshold) {
1115
+ dust_penalty = SaturatingAdd(dust_penalty, dust_threshold - txout.nValue);
1116
+ }