From e8da007011c3c7e247c26a1c9984319229bb0afa Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E5=BF=97=E5=AE=87?= Date: Fri, 13 Jun 2025 13:07:37 +1000 Subject: [PATCH] fix(chain): Unconfirmed coinbase txs should never be canonical 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 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/chain/src/canonical_iter.rs b/crates/chain/src/canonical_iter.rs index 58f266f8..204ead45 100644 --- a/crates/chain/src/canonical_iter.rs +++ b/crates/chain/src/canonical_iter.rs @@ -230,6 +230,10 @@ impl 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 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)); } -- 2.49.0