Skip to content

fix(afs)!: preserve input timelock requirements#65

Merged
evanlinjin merged 2 commits into
bitcoindevkit:masterfrom
aagbotemi:fix/afs-locktime
May 16, 2026
Merged

fix(afs)!: preserve input timelock requirements#65
evanlinjin merged 2 commits into
bitcoindevkit:masterfrom
aagbotemi:fix/afs-locktime

Conversation

@aagbotemi

@aagbotemi aagbotemi commented May 12, 2026

Copy link
Copy Markdown
Collaborator

Description

Fixes consensus-validity issues in BIP-326 anti-fee-sniping.

Closes #62
Closes #63
Closes #64

Bugs

  • The nLockTime strategy could overwrite tx.lock_time with a value lower than the accumulated input CLTV.
  • The nSequence strategy zeroed tx.lock_time, also breaking accumulated CLTV.
  • The nSequence strategy could select a Taproot input that already had a relative-timelock (CSV) requirement and overwrite its sequence.

Fixes

  • The locktime path only updates tx.lock_time when the AFS-proposed value still satisfies the existing requirement.
  • The sequence path leaves tx.lock_time alone and filters CSV-bearing Taproot inputs out of the candidate pool.

Breaking changes

  • PsbtParams::enable_anti_fee_sniping: bool + fallback_locktimeanti_fee_sniping: Option<absolute::Height> + min_locktime. The tip height is now required when AFS is enabled, and time-based tips are unrepresentable.
  • apply_anti_fee_sniping is now pub(crate); rbf_enabled param removed (derived from tx.is_explicitly_rbf()).
  • New AntiFeeSnipingError type, wrapped under CreatePsbtError::AntiFeeSniping.

@evanlinjin evanlinjin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this.

PR description is misleading - none of these bugs were flagged in the post-merge review. It also says this "fixes two consensus-validity issues", however this PR attempts to close 3 issues?

Comment thread src/utils.rs Outdated
Comment thread src/selection.rs
Comment thread src/utils.rs Outdated
Comment thread src/utils.rs

@evanlinjin evanlinjin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two versions of the random_probability and random_range methods. The std versions add no meaningful value imo - I would just remove them.

@aagbotemi

Copy link
Copy Markdown
Collaborator Author

Thank you for the review

@aagbotemi
aagbotemi requested a review from evanlinjin May 13, 2026 13:29
evanlinjin

This comment was marked as outdated.

Comment thread src/utils.rs Outdated
@aagbotemi
aagbotemi force-pushed the fix/afs-locktime branch 2 times, most recently from f3d7e60 to 3b72e6d Compare May 14, 2026 09:59
@aagbotemi
aagbotemi requested a review from evanlinjin May 14, 2026 10:02
Comment thread src/selection.rs
@aagbotemi
aagbotemi force-pushed the fix/afs-locktime branch 2 times, most recently from 3b72e6d to b0b0eb1 Compare May 14, 2026 13:56

@evanlinjin evanlinjin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make apply_anti_fee_sniping explicitly private!

Comment thread src/utils.rs Outdated
Comment thread src/utils.rs Outdated
Comment thread src/utils.rs Outdated
Comment thread src/utils.rs Outdated
Comment thread src/utils.rs Outdated
Comment thread src/utils.rs Outdated
@aagbotemi
aagbotemi force-pushed the fix/afs-locktime branch 3 times, most recently from 15943a8 to adf472f Compare May 14, 2026 15:35
Comment thread examples/anti_fee_sniping.rs
@evanlinjin

Copy link
Copy Markdown
Member

We are lacking commit messages, PR description has factual errors, missing ! to signal breaking change.

I'll push it forward!

@evanlinjin evanlinjin changed the title fix(afs): preserve accumulated locktime fix(afs)!: preserve input timelock requirements May 15, 2026
@evanlinjin
evanlinjin force-pushed the fix/afs-locktime branch 2 times, most recently from b8671c3 to c83079a Compare May 15, 2026 18:59
@evanlinjin
evanlinjin requested a review from nymius May 15, 2026 20:01
@evanlinjin

Copy link
Copy Markdown
Member

@aagbotemi I pushed a Claude-generated test commit on top, let me know what you think.

Comment thread src/selection.rs Outdated
@evanlinjin
evanlinjin force-pushed the fix/afs-locktime branch 3 times, most recently from 059752f to 2fcd3c5 Compare May 16, 2026 12:31
aagbotemi and others added 2 commits May 16, 2026 12:41
Anti-fee-sniping could produce consensus-invalid transactions when
inputs had timelock requirements:

- The nLockTime strategy could overwrite `tx.lock_time` with a value
  lower than the accumulated input CLTV.
- The nSequence strategy zeroed `tx.lock_time`, also breaking
  accumulated CLTV.
- The nSequence strategy could select a Taproot input that already
  had a relative-timelock (CSV) requirement and overwrite its
  sequence.

The locktime path now only updates `tx.lock_time` when the proposed
AFS value still satisfies the existing requirement. The sequence
path leaves `tx.lock_time` alone and filters CSV-bearing Taproot
inputs out of the candidate pool.

API changes:

- `PsbtParams::enable_anti_fee_sniping: bool` and `fallback_locktime`
  are replaced with `anti_fee_sniping: Option<absolute::Height>` and
  `min_locktime`. The tip height is now required when AFS is enabled
  and time-based tips are unrepresentable.
- `apply_anti_fee_sniping` is now `pub(crate)`; the `rbf_enabled`
  parameter is removed (derived from `tx.is_explicitly_rbf()`).
- New `AntiFeeSnipingError` type, wrapped under
  `CreatePsbtError::AntiFeeSniping`.

Closes bitcoindevkit#62
Closes bitcoindevkit#63
Closes bitcoindevkit#64
Three new tests, each verified to fail on the pre-fix algorithm:

- `test_anti_fee_sniping_preserves_input_cltv` — input CLTV above tip
  must not be lowered.
- `test_anti_fee_sniping_skips_taproot_csv_input` — a Taproot input
  carrying a CSV requirement must be excluded from the nSequence
  candidate pool; a regular Taproot input remains to ensure the
  sequence path is still reachable.
- `test_anti_fee_sniping_rejects_time_based_locktime` — a time-based
  CLTV must surface `AntiFeeSnipingError::UnsupportedLockTime`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@evanlinjin evanlinjin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 5df9bb1

@evanlinjin
evanlinjin merged commit 8b51d07 into bitcoindevkit:master May 16, 2026
7 checks passed
@ValuedMammal ValuedMammal mentioned this pull request Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants