From 32c5052686858636aa691edc4e52e8fb876a0f5c Mon Sep 17 00:00:00 2001 From: Shoaib Ahmed Date: Tue, 3 Nov 2026 18:49:15 +0400 Subject: [PATCH] fix(zeronym): correct the non-finalized nullifier comment `in finalized state: false` is the non-finalized best chain, so that reply stays AlreadyKnown. A missing prevout stays retryable. Co-authored-by: Cursor --- zeronym/hub/src/chain.rs | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/zeronym/hub/src/chain.rs b/zeronym/hub/src/chain.rs index 589226df..66ad08c0 100644 --- a/zeronym/hub/src/chain.rs +++ b/zeronym/hub/src/chain.rs @@ -890,9 +890,11 @@ fn classify_publish_error(message: &str) -> Publish { // Zebra's spelling of a spent note (`orchard double-spend: duplicate // nullifier: ...`), which lacks the `bad txns` prefix and would // otherwise match the already-known pattern "duplicate". Spent in a - // block, or twice in these bytes: permanent. A conflict with a - // mempool transaction (`in finalized state: false`) can clear, and a - // missing prevout (`possible double-spend`) stays retryable. + // block, or twice in these bytes: permanent. `in finalized state: + // false` is the non-finalized best chain, so that string falls through + // to `AlreadyKnown`. A mempool-versus-mempool conflict does not contain + // `duplicate`. A missing prevout (`possible double-spend`) stays + // retryable. Publish::Rejected { reason: message.to_string(), } @@ -1082,7 +1084,7 @@ mod tests { } #[test] - fn zebras_double_spend_is_a_rejection_not_already_known() { + fn zebras_double_spend_is_a_rejection_except_on_the_non_finalized_chain() { // zebra-consensus's spellings, bare and as the mempool wraps them. for message in [ "orchard double-spend: duplicate nullifier: Nullifier(..)", @@ -1094,17 +1096,20 @@ mod tests { "{message:?}" ); } - // A conflict with a mempool transaction can clear, and a missing - // prevout is a parent that has not arrived yet. - for message in [ - "orchard double-spend: duplicate nullifier: Nullifier(..), in finalized state: false", - "missing transparent output: possible double-spend of OutPoint(..)", - ] { - assert!( - !matches!(classify_publish_error(message), Publish::Rejected { .. }), - "{message:?}" - ); - } + // `in finalized state: false` is the non-finalized best chain. The arm + // still returns AlreadyKnown. A missing prevout stays retryable. + assert_eq!( + classify_publish_error( + "orchard double-spend: duplicate nullifier: Nullifier(..), in finalized state: false" + ), + Publish::AlreadyKnown + ); + assert!(matches!( + classify_publish_error( + "missing transparent output: possible double-spend of OutPoint(..)" + ), + Publish::Retryable { .. } + )); } #[test]