]> Untitled Git - bdk/commitdiff
fix(chain): Unconfirmed coinbase txs should never be canonical
author志宇 <hello@evanlinjin.me>
Fri, 13 Jun 2025 03:07:37 +0000 (13:07 +1000)
committer志宇 <hello@evanlinjin.me>
Thu, 10 Jul 2025 03:04:24 +0000 (03:04 +0000)
The logic in `CanonicalIter` will consider txs that are anchored to
blocks not in the best chain since they still can appear in the mempool.

However, coinbase txs can never be unconfirmed - which the old logic
failed to exclude.

crates/chain/src/canonical_iter.rs

index 58f266f89102db49eab588acef62f1643442433a..204ead4511f3586ce7f048ae4e7ad7dd2f679f23 100644 (file)
@@ -230,6 +230,10 @@ impl<A: Anchor, C: ChainOracle> Iterator for CanonicalIter<'_, A, C> {
             }
 
             if let Some((txid, tx, last_seen)) = self.unprocessed_seen_txs.next() {
+                debug_assert!(
+                    !tx.is_coinbase(),
+                    "Coinbase txs must not have `last_seen` (in mempool) value"
+                );
                 if !self.is_canonicalized(txid) {
                     let observed_in = ObservedIn::Mempool(last_seen);
                     self.mark_canonical(txid, tx, CanonicalReason::from_observed_in(observed_in));
@@ -238,7 +242,7 @@ impl<A: Anchor, C: ChainOracle> Iterator for CanonicalIter<'_, A, C> {
             }
 
             if let Some((txid, tx, height)) = self.unprocessed_leftover_txs.pop_front() {
-                if !self.is_canonicalized(txid) {
+                if !self.is_canonicalized(txid) && !tx.is_coinbase() {
                     let observed_in = ObservedIn::Block(height);
                     self.mark_canonical(txid, tx, CanonicalReason::from_observed_in(observed_in));
                 }