BIP Draft: Anti-Fee-Sniping with LockTime #2266

pull nervana21 wants to merge 2 commits into bitcoin:master from nervana21:anti-fee-snipe changing 2 files +506 −0
  1. nervana21 commented at 8:42 PM on August 21, 2026: contributor

    Specifies the nLockTime anti-fee-sniping behavior that Bitcoin Core and Electrum already implement. BIP326 assumes these rules as the baseline and uses nSequence instead for some taproot spends. The nLockTime path itself was never specified.

    Discussed on bitcoindev https://gnusha.org/pi/bitcoindev/wWGWjMw9TI22vp4VzCm4xJJS3IGA1UhndMKynUkB04BqeoRjhe-QbDbJU-GMQq0nnOnXuB__u8KcdIcxU5i8Cy9pbTbtJ7Hi583FzLVojek=@pm.me/

  2. nervana21 force-pushed on Aug 21, 2026
  3. nervana21 force-pushed on Aug 21, 2026
  4. nervana21 force-pushed on Aug 21, 2026
  5. in bip-anti-fee-sniping-with-locktime.md:18 in 0af9cb51e9
      13 | +
      14 | +This BIP specifies wallet behavior that discourages fee sniping by setting nLockTime. Bitcoin Core already implements this behavior. BIP326 assumes these nLockTime rules as the baseline and uses nSequence instead for some taproot spends.
      15 | +
      16 | +## Motivation
      17 | +
      18 | +Anti-fee-sniping behavior has existed in Bitcoin Core since [2014](https://github.com/bitcoin/bitcoin/commit/ba7fcc8de06602576ab6a5911879d3d8df80d36a) and in Electrum since [2017](https://github.com/spesmilo/electrum/commit/de85b56e0aeac52463530dab3e54f1a35128ee3b). BIP326 assumes this nLockTime behavior but never defined it. This BIP specifies those rules.
    


    murchandamus commented at 8:30 PM on September 21, 2026:

    While it is good to describe why the BIP is being written, the Motivation section should also describe what issue the proposal addresses and how adopting the proposed idea improves the situation. Some of the information below seems like it should be moved into the Motivation section.


    nervana21 commented at 11:45 PM on September 29, 2026:

    Taken. I've moved the fee-sniping description into Motivation and used your wording as below. Thanks!

  6. in bip-anti-fee-sniping-with-locktime.md:24 in 0af9cb51e9
      19 | +
      20 | +## Background
      21 | +
      22 | +### Fee sniping
      23 | +
      24 | +Fee sniping is an incentive-misaligned miner strategy. The miner orphans the best block to capture fees from its transactions and from the mempool instead of extending the tip. With nLockTime anti-fee-sniping, many of those transactions cannot enter the remined first block and must wait for the second, which weakens the attack and favors extending the chain instead.
    


    murchandamus commented at 8:33 PM on September 21, 2026:

    I think this paragraph belongs in the Motivation, and I have a few suggestions regarding the phrasing. How about:

    Fee sniping is an incentive-misaligned miner strategy. The miner orphans the best block to capture fees from its transactions and from the mempool instead of extending the tip. With nLockTime anti-fee-sniping, new transactions submitted to the mempool cannot be included in the remined first block and must wait for the second, which reduces the potential fee revenue of the attack and incentivizes extending the chain instead.
    

    nervana21 commented at 11:46 PM on September 29, 2026:

    Done. Thanks!

  7. in bip-anti-fee-sniping-with-locktime.md:42 in 0af9cb51e9
      37 | +
      38 | +If the tip is not current, such as during initial block download, or when the current time minus the tip timestamp is greater than 8 hours (28_800 seconds), set nLockTime to 0 instead. A tip-relative locktime while far behind the network does not help against fee sniping and can fingerprint the wallet.
      39 | +
      40 | +Do not apply this BIP when the user or a higher-level protocol sets nLockTime, or when any input already has a preset nSequence (for example a PSBT this wallet only funds, or a contract template). Leave those fields unmodified. Presigned transactions meant to stay valid across a wide height range should document their own locktime policy.
      41 | +
      42 | +A child's nLockTime does not protect an unconfirmed parent. Finality is checked per transaction.
    


    murchandamus commented at 8:40 PM on September 21, 2026:

    I don’t understand why this paragraph is in the specification. This seems descriptive rather than prescriptive.


    nervana21 commented at 12:17 AM on September 30, 2026:

    Okay, I've moved it to Rationale under "Unconfirmed parents and child transactions". I've also clarified that package wide parent floors are out of scope. This BIP only floors replacements of the same transaction.

  8. in bip-anti-fee-sniping-with-locktime.md:9 in 0af9cb51e9 outdated
       0 | @@ -0,0 +1,304 @@
       1 | +```
       2 | +  BIP: ?
       3 | +  Layer: Applications
       4 | +  Title: Anti-Fee-Sniping with LockTime
       5 | +  Authors: nervana21 <nervana21@pm.me>
       6 | +  Status: Draft
       7 | +  Type: Informational
       8 | +  Assigned: ?
       9 | +  License: CC0-1.0
    


    murchandamus commented at 8:44 PM on September 21, 2026:

    Please add a link to the discussion on the mailing list. Since this proposal supersedes BIP326 in one aspect, please add the corresponding “Replaces” header:

      License: CC0-1.0
      Discussion: link to mailing list thread
      Replaces: 326
    

    nervana21 commented at 12:35 AM on September 30, 2026:

    Okay, I've added the mailing list discussion link.

    IIUC correctly, "Replaces: 326" would mean that this BIP "succeeds, supersedes, or renders obsolete" BIP326, but isn't BIP326 is still needed to refer to the nSequence-based anti-fee-sniping?

  9. in bip-anti-fee-sniping-with-locktime.md:50 in 0af9cb51e9
      45 | +
      46 | +### Pseudocode
      47 | +
      48 | +```
      49 | +def apply_nlocktime_anti_fee_sniping(transaction):
      50 | +    if transaction.has_preset_nlocktime() or any(input.has_preset_nsequence() for input in transaction.inputs):
    


    murchandamus commented at 8:47 PM on September 21, 2026:

    Should this also check that the locktime of any child transaction is equal or higher than the locktime of every parent transaction? I.e., if a transaction had two parent transactions that were locked to 100 and 110, the child should not have a locktime lower than 110.


    nervana21 commented at 12:17 AM on September 30, 2026:

    Done as above.

  10. in bip-anti-fee-sniping-with-locktime.md:7 in 0af9cb51e9
       0 | @@ -0,0 +1,304 @@
       1 | +```
       2 | +  BIP: ?
       3 | +  Layer: Applications
       4 | +  Title: Anti-Fee-Sniping with LockTime
       5 | +  Authors: nervana21 <nervana21@pm.me>
       6 | +  Status: Draft
       7 | +  Type: Informational
    


    murchandamus commented at 8:55 PM on September 21, 2026:

    I think this should be a Specification BIP

      Type: Specification
    
  11. in bip-anti-fee-sniping-with-locktime.md:256 in 0af9cb51e9
     251 | +
     252 | +## Rationale
     253 | +
     254 | +### Fee bumping and replacements
     255 | +
     256 | +Re-applying the 10% privacy branch when fee-bumping can set a lower nLockTime on the replacement than on the transaction being replaced. That fingerprints Core or Electrum style wallets ([bitcoin#26526](https://github.com/bitcoin/bitcoin/issues/26526)). Changing locktime type across a replacement (for example height-based to time-based) is related ([bitcoin#35628](https://github.com/bitcoin/bitcoin/issues/35628)). Implementations SHOULD document replacement locktime policy, including locktime type. When re-running the privacy branch on a height-based previous locktime, floor at that height. Do not go older unless that policy is intentional and documented.
    


    murchandamus commented at 9:00 PM on September 21, 2026:

    The Rationale section describes related work, engages with criticism, which alternate designs were considered, and why decisions should be made. The implementation instructions in this section belong in the Specification section.


    nervana21 commented at 12:38 AM on September 30, 2026:

    Done.

  12. in bip-anti-fee-sniping-with-locktime.md:53 in 0af9cb51e9
      48 | +```
      49 | +def apply_nlocktime_anti_fee_sniping(transaction):
      50 | +    if transaction.has_preset_nlocktime() or any(input.has_preset_nsequence() for input in transaction.inputs):
      51 | +        return
      52 | +    for input in transaction.inputs:
      53 | +        input.nsequence = 2**32 - 2  # Conventional
    


    murchandamus commented at 9:19 PM on September 21, 2026:

    This sets all nSequence fields to non-final, but the above rules specify that at least one input has to use a non-final sequence. Perhaps this option should be recommended in the text?


    nervana21 commented at 12:40 AM on September 30, 2026:

    Done.

  13. in bip-anti-fee-sniping-with-locktime.md:34 in 0af9cb51e9
      29 | +
      30 | +If every input has nSequence equal to 0xffffffff, nLockTime is ignored.
      31 | +
      32 | +## Specification
      33 | +
      34 | +Set nLockTime to the current tip block height. With probability 10%, subtract a uniform random integer in 0..99 from that height (clamped at 0). The random branch improves privacy when signing is delayed (for example in high-latency mix networks).
    


    murchandamus commented at 9:20 PM on September 21, 2026:

    The sentence starting with “The random branch improves…” should be in the Rationale. The Specification section should focus on describing the exact syntax and semantics of the proposed feature.


    murchandamus commented at 9:38 PM on September 21, 2026:

    “current tip block height” feels imprecise, especially given that you define below that anti-fee-sniping is used if the node’s view of the chaintip is within eight hours of wall-time.


    nervana21 commented at 12:41 AM on September 30, 2026:

    Done.


    nervana21 commented at 12:44 AM on September 30, 2026:

    Updated.

  14. in bip-anti-fee-sniping-with-locktime.md:270 in 0af9cb51e9
     265 | +
     266 | +## Backward Compatibility
     267 | +
     268 | +This BIP requires no consensus changes and wallets may adopt it unilaterally.
     269 | +
     270 | +Hardware signers and PSBT cosigners should accept tip-relative anti-fee-sniping nLockTime values and should not require nLockTime=0 by default. Wallet software that funds PSBTs should apply this BIP when locktime and sequences are still wallet-controlled. Creator-chosen values stay untouched.
    


    murchandamus commented at 9:21 PM on September 21, 2026:

    If these are rules for implementers to follow, these should be in the Specification section.


    nervana21 commented at 2:22 PM on September 30, 2026:

    Done.

  15. in bip-anti-fee-sniping-with-locktime.md:40 in 0af9cb51e9
      35 | +
      36 | +Ensure at least one input has nSequence below 0xffffffff so nLockTime takes effect. The pseudocode sets every wallet-controlled input to `2**32 - 2` as one conventional choice.
      37 | +
      38 | +If the tip is not current, such as during initial block download, or when the current time minus the tip timestamp is greater than 8 hours (28_800 seconds), set nLockTime to 0 instead. A tip-relative locktime while far behind the network does not help against fee sniping and can fingerprint the wallet.
      39 | +
      40 | +Do not apply this BIP when the user or a higher-level protocol sets nLockTime, or when any input already has a preset nSequence (for example a PSBT this wallet only funds, or a contract template). Leave those fields unmodified. Presigned transactions meant to stay valid across a wide height range should document their own locktime policy.
    


    murchandamus commented at 9:24 PM on September 21, 2026:

    The Specification should describe how the anti-fee-sniping should work, when it is applied. Perhaps you could extract an “Applicability” section or otherwise separate the information about when to use anti-fee-sniping from how to implement anti-fee-sniping.


    nervana21 commented at 2:25 PM on September 30, 2026:

    Done.

  16. in bip-anti-fee-sniping-with-locktime.md:28 in 0af9cb51e9
      23 | +
      24 | +Fee sniping is an incentive-misaligned miner strategy. The miner orphans the best block to capture fees from its transactions and from the mempool instead of extending the tip. With nLockTime anti-fee-sniping, many of those transactions cannot enter the remined first block and must wait for the second, which weakens the attack and favors extending the chain instead.
      25 | +
      26 | +### Absolute locktime
      27 | +
      28 | +nLockTime is an absolute lock. Setting aside nSequence effects, a transaction with an nLockTime set to block height N may only be included in block height N + 1 or greater. A transaction with an nLockTime set to 0 is always final. nLockTime values less than 500_000_000 are block heights. The 10% privacy branch uses an older height, which can still enter a remine.
    


    murchandamus commented at 9:35 PM on September 21, 2026:

    “A transaction with an nLockTime set to 0 is always final.” Is that true? I think that a transaction with a locktime of 0 can included in any block, but I thought that finality was orthogonal, in the sense that it was indicated by whether all nSequence fields are set to the maximal value.

    How about:

    nLockTime is an absolute lock. A non-final transaction with an nLockTime set to block height N may only be included in block height N + 1 or greater. A transaction with max value in all nSequence fields is final. nLockTime values less than 500_000_000 are block heights. The 10% privacy branch uses an older height, which can still enter a remine.
    

    Regarding or with an nLockTime set to 0 is


    nervana21 commented at 12:52 AM on September 30, 2026:

    Good catch. Updated.

  17. murchandamus commented at 9:41 PM on September 21, 2026: member

    Thanks for your submission. I like the idea to document Anti-Fee-Sniping as a BIP. This document is a good start, but it still feels a bit rough on the edges.

    In some places you give advice that could be interpreted as implementation instructions in other sections than the Specification. Please make it clear whether these are implementation instructions or optional suggestions. If they are normative, they should appear in the Specification. If they are optional suggestions they should be more clearly introduced as such.

    It’s a bit unclear whether you are going for an Informational BIP that describes a design issue, or a Specification BIP that tries to describe a feature in a way that it can be implemented. At times you describe behavior exhibited by existing implementations, but then you also seem to try to comprehensively describe how to implement the feature. If you are going for an Informational BIP, you may focus on the benefit of anti-fee-sniping, suggest that wallet implementers lock transactions to the chaintip and cut the specification back to some general implementation advice. If you are going for a Specification BIP, you would double-down on your Specification and Pseudocode by making sure that the Specification is comprehensive and precise enough that projects can implement the feature from reading the Specification section.

  18. nervana21 commented at 10:04 PM on September 21, 2026: contributor

    Thanks for the feedback @murchandamus! I will address your concerns and post an updated draft asap

  19. murchandamus added the label New BIP on Sep 21, 2026
  20. murchandamus added the label PR Author action required on Sep 21, 2026
  21. nervana21 commented at 11:36 PM on September 29, 2026: contributor

    If you are going for a Specification BIP, you would double-down on your Specification and Pseudocode by making sure that the Specification is comprehensive and precise enough that projects can implement the feature from reading the Specification section.

    Ok, I think it's best to be a Specification as opposed to Informational BIP. I've updated the style to reflect that everywhere appropriate.

  22. doc: Allow nervana in typos word list eba3b85cdf
  23. nervana21 force-pushed on Sep 30, 2026
  24. doc: Anti-fee-sniping with locktime 456b1a3720
  25. nervana21 force-pushed on Sep 30, 2026
  26. nervana21 commented at 5:20 PM on September 30, 2026: contributor

    Rebased off master and updated draft to address detailed feedback from @murchandamus


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bips. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-10-12 00:10 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me