BIP326: Anti-Fee-Sniping as a Privacy Primitive for Taproot Wallets
Written by Abiodun Awoyemi
Table of Contents
- Introduction to BIP326
- Locktime Fundamentals: nLockTime vs nSequence
- Fee Sniping: The Threat That Locktime Solves
- How LockTime Defends Against Fee Sniping
- The Taproot Connection: How MAST works
- HTLC vs PTLC: Why Off-Chain Protocols Leak
- The Privacy Gap: The Problem Before BIP326
- BIP326: The nLockTime + nSequence Strategy
- Code Implementation & Analysis
- Conclusion
Introduction to BIP326
Since Bitcoin’s introduction by Satoshi Nakamoto, its scripting capabilities have evolved significantly. One of the most important upgrades is Taproot, introduced in BIP341, which allows a transaction output to commit to multiple spending conditions simultaneously using a structure known as a Merkelized Alternative Script Tree (MAST).
These transaction outputs are called SegWit V1 or P2TR (Pay-to-Taproot). The P2TR locking script can be unlocked via two distinct paths: a Key Path Spend, which uses an aggregated public key, or a Script Path Spend, where the spending conditions are organised into a script tree used to compute the Merkle root. It is this Taproot architecture that BIP326 builds upon to address a specific and previously overlooked privacy gap in off-chain protocols.
BIP326 defines how wallets should behave when spending Taproot outputs by leveraging the nSequence field in place of traditional nLockTime for anti-fee-sniping protection. The primary motivation is to improve the privacy and fungibility of off-chain protocols such that when on-chain wallets adopt this behavior, a Taproot spend with an nSequence value becomes indistinguishable from an off-chain settlement transaction that uses timelock, making it impossible for a blockchain analyst to tell the two apart.
In modern Bitcoin scripting, what makes BIP326 particularly powerful is that it requires no consensus change and no protocol upgrade; it can be adopted unilaterally by wallet software. By simply mimicking the nSequence behaviour that off-chain protocols already use, on-chain wallets begin contributing to an anonymity set that grows larger with every wallet that adopts it.
Locktime Fundamentals: nLockTime vs nSequence

Bitcoin supports two types of locktimes that control when a UTXO can be spent.
- Absolute Locktime prevents a transaction from being mined until a specific block height or Unix timestamp has been reached. It is set at the transaction level via the nLockTime field and applies to the entire transaction. Bitcoin interprets nLockTime below 500,000,000 as a block height and at or above 500,000,000 as a Unix timestamp.
- Relative Locktime defines a delay based on when a specific output was mined, rather than a fixed date or block height. Encoded at the input level via the nSequence field, it allows for granular control over each input independently. This functionality was introduced by BIP68, which repurposed the nSequence field to enable these relative constraints. BIP68 requires transaction version 2 and interprets the lower 16 bits of nSequence as either a block-based or time-based relative delay.
The nSequence field is a 32-bit value assigned to each transaction input. Under BIP68, it is parsed as follows:
1. Bit 31 acts as the disable flag; if set, the relative locktime is ignored.
2. Bit 22 determines the unit, distinguishing between block-height (0) and time-based (1) delays.
3. Bits 0–15, the lower 16 bits store the actual duration, allowing for a maximum value of 65,535 blocks or 65,535 intervals of 512 seconds each.
Bits 16–21 and 23–30 are currently undefined and reserved under BIP68.
Off-chain protocols such as Lightning Network channels and CoinSwaps commonly use relative locktime because they allow contracts to remain open indefinitely.
Since absolute locktime are still used and still necessary, BIP326 does not propose replacing nLockTime entirely, because it is backward compatible. Instead, it proposes that Taproot wallets keep using nLockTime but also frequently use nSequence, alternating between the two with a 50% probability.
Fee Sniping: The Threat That Locktime Solves

Fee sniping refers to the process of re-mining the last block in the chain to claim the fees of both the transactions mined in that block and the new high-fee transactions that entered the mempool since it was mined. Instead of extending the current blockchain, a miner ignores the last block and secretly attempts to mine an alternative version of it. If successful, the original block is orphaned and the attacker collects all its fees, including all other high fee transactions that arrived in the mempool after the original block was confirmed.
Fee sniping threaten two fundamental properties of Bitcoin which are:
- Settlement finality: when miners can re-mine past blocks, users can no longer trust that confirmed transactions are irreversible. A transaction with 6 confirmations today could be reversed tomorrow, which undermines the basic promise that Bitcoin’s confirmation model makes to its users.
- Miner incentive alignment: Bitcoin’s security model is built on the assumption that miners are always incentivised to extend the longest chain. Fee sniping breaks that assumption. When re-mining a past block is more profitable than building on the current tip, the economic foundation of the entire network is compromised. In other words, miner incentive alignment is the mechanism Bitcoin relies on, and network security is what collapses when that alignment breaks.
How LockTime Defends Against Fee Sniping
Most wallets set nLockTime to the current block height, ensuring that transactions are only valid in the next block. This prevents miners from reorganizing the blockchain to include high-fee transactions from the mempool into a re-mined past block during fee sniping. By limiting transaction inclusion to the latest block, the fee sniping attack becomes economically unviable. Today, most modern wallet implementations including Bitcoin Core, BDK, and Electrum set anti-fee-sniping locktime by default.
However, while nLockTime effectively prevents fee sniping, it introduces a privacy leak by making on-chain transactions distinguishable from off-chain settlements. BIP326 addresses this by proposing that Taproot wallets alternate between nLockTime and nSequence randomly, using each with equal probability. This provides the same anti-fee-sniping protection while making on-chain and off-chain timelocked transactions indistinguishable, enhancing privacy and fungibility across the network.
The Taproot Connection: How MAST works

The Merkelized Alternative Script Tree (MAST), originally the Merkelized Abstract Syntax Tree, allows a Bitcoin output to commit to multiple spending conditions simultaneously while revealing only what is strictly necessary at spend time. It uses a Merkle tree to summarise the collection of potential spending scripts, avoiding the need to include every script in the transaction while keeping all unused conditions completely hidden on-chain
Think of MAST as a tree where each leaf represents a spending condition or script path. For example, one leaf could be “Alice can spend after 100 blocks,” another could be “Bob and Alice can spend together cooperatively,” and another could be “Bob can spend with a hash preimage”. These leaves are hashed and paired together, working upward until they produce a single Merkle root committed inside the Taproot output. When spent, only the executed leaf is revealed on-chain and all unused script paths remain completely hidden.
In off-chain protocols like Lightning Network, when a channel is force-closed via the timelock path, only that branch is revealed. However, before BIP326, the nSequence value used in that timelock settlement acted as an immediate fingerprint. Regular on-chain wallets were not using nSequence, any nSequence value was an instant indicator of off-chain activity. BIP326 addresses this by formally specifying that all Taproot wallets should randomly alternate between nLockTime and nSequence for anti-fee-sniping protection, making on-chain and off-chain Taproot spends completely indistinguishable.
HTLC vs PTLC: Why Off-Chain Protocols Leak
Hash Time-Locked Contract (HTLC) is a script-based conditional payment constructs that secure payments across multiple hops using a hash lock and a time lock. To claim the funds, the recipient must produce a secret value (the preimage) that hashes to a predetermined hash within a specified timeframe, otherwise the funds are returned to the sender. The critical privacy problem with HTLCs is that the same hash is used across every hop in a payment route, meaning any routing node can correlate the payment end-to-end simply by recognising the same hash value appearing at each step.
Point Time-Locked Contract (PTLC) perform the same function as HTLCs but provide stronger privacy using elliptic curve point operations based on Schnorr Signatures. Instead of a hash preimage, a PTLC requires the recipient to produce a scalar value corresponding to a specific elliptic curve point to claim the funds. PTLCs use a different randomised point per hop, breaking the payment correlation that HTLCs expose and making it impossible for routing nodes to link individual hops into a single payment path.
When a Lightning channel is force-closed and an HTLC must be settled on-chain, the blockchain sees a hash value and preimage. A cooperative close, by contrast, reveals none of this — both parties agree on final balances off-chain and broadcast a simple transaction that looks like any ordinary Taproot spend. When a Lightning channel closes via PTLC cooperatively, the blockchain sees only a regular Taproot script. However, a timelock path closure reveals either OP_CHECKSEQUENCEVERIFY opcode in the script itself or an nSequence value in the transaction input.
Note: At the time of writing, the Lightning Network still uses HTLCs. PTLCs are a planned upgrade dependent on broader Taproot and Schnorr Signature adoption across Lightning implementations. BIP326 lays the groundwork today so that when PTLCs are eventually deployed, the anonymity set will already be large enough to provide meaningful privacy.
The Privacy Gap: The Problem Before BIP326
Before BIP326, off-chain protocols such as Lightning Network relied on Hash Time-Locked Contracts (HTLCs) which is script-based conditional payment constructs that, when forced to settle on-chain, revealed recognisable fingerprints that chain analysts could trivially identify and trace.
In a Lightning Network channel, when one party broadcasts their latest commitment transaction without the agreement of the other party, the HTLC is forced to settle on-chain. The transaction reveals recognizable fingerprints which are traceable. Chain analysts could trivially identify these transactions making them simple to spot and analyze. What was intended to be private has now become public.
Privacy in Bitcoin depends on transactions being indistinguishable from one another, when there is a large pool of transactions, it becomes significantly harder to single out any one of them. HTLCs produced a distinct script pattern such as hash preimage, timelock structure and multi-path constructs, that existed almost nowhere else on-chain, making the anonymity set small and unique. At that point, even users who never broadcast their channel activity are exposed and the moment a channel is forced to close, the entire payment history off-chain can become linkable on-chain, and what was meant to be private off-chain becomes an open record on-chain.
BIP326: The nLockTime + nSequence Strategy
BIP326 proposes that Taproot wallets randomly alternate between nLockTime and nSequence for anti-fee-sniping protection, using each with a 50% probability. This random alternation is the core of the strategy. When on-chain wallets behave the same way off-chain protocols do, the nSequence value that once acted as a fingerprint becomes indistinguishable from a regular wallet transaction.
To further protect transactions that may be delayed after signing, wallets should also apply a random look back with a 10% probability, subtracting a random value between 0 and 99 from the current block height. This ensures delayed transactions blend naturally into the broader transaction landscape without standing out as anomalies.
BIP326 does not replace nLockTime, rather it acknowledges that absolute locktimes remain indispensable. There are specific scenarios where nSequence cannot be used and nLockTime must be the fallback:
- When any input is unconfirmed
- When any input has more than 65,535 confirmations
- When any input is not a Taproot output
- When RBF is not set, because nSequence needs specific values to signal RBF and cannot simultaneously encode both RBF signaling and a relative locktime
In all these cases, the wallet must use nLockTime. nLockTime remains the universal baseline, it is reliable, broadly applicable and backward compatible, while nSequence is the privacy upgrade specifically for eligible Taproot transactions.
Code Implementation & Analysis
The following implementation is written in Rust in the bdk_tx crate. It applies BIP326 anti-fee-sniping protection to a transaction by randomly alternating between nLockTime and nSequence for eligible Taproot inputs. The implementation takes a mutable transaction, a slice of inputs, the current block height, RBF flag, and a random number generator as parameters.
Before any logic runs, a guard clause checks that the transaction version is at least 2, which is a requirement of BIP68. If the version is less than 2, the function returns a CreatePsbtError::UnsupportedVersion error immediately and halts execution.
if tx.version < Version::TWO {
return Err(CreatePsbtError::UnsupportedVersion(tx.version));
}The implementation then filters all inputs to identify eligible Taproot inputs, specifically those whose previous output script is a Pay-to-Taproot (P2TR) script. Non-Taproot inputs are excluded entirely because BIP326 is specifically scoped to Taproot outputs only.
let taproot_inputs: Vec<(usize, &Input)> = tx
.input
.iter()
.enumerate()
.filter_map(|(vin, txin)| {
let input = inputs
.iter()
.find(|input| input.prev_outpoint() == txin.previous_output)?;
if input.prev_txout().script_pubkey.is_p2tr() {
Some((vin, input))
} else {
None
}
})
.collect();
Next, the function evaluates whether any input forces a fallback to nLockTime. Three conditions trigger this fallback, each for a specific reason. An unconfirmed input forces the fallback because confirmations() == 0 means nSequence cannot encode a meaningful relative delay. An input exceeding 65,535 confirmations forces the fallback because nSequence only has 16 bits for block-based delays and the value would overflow. A non-Taproot input forces the fallback because BIP326 is explicitly scoped to Taproot inputs only.
let must_use_locktime = inputs.iter().any(|input| {
let confirmation = input.confirmations(current_height);
confirmation == 0
|| confirmation > MAX_RELATIVE_HEIGHT
|| !input.prev_txout().script_pubkey.is_p2tr()
});If none of these conditions are met, the function uses a 50/50 random coin flip to decide between nLockTime and nSequence. Worth noting here is the !rbf_enabled condition, which forces nLockTime when RBF is disabled. This is because using nSequence for anti-fee-sniping on a non-RBF transaction could accidentally signal RBF, since RBF is indicated by nSequence values below 0xFFFFFFFE.
let use_locktime = !rbf_enabled
|| must_use_locktime
|| taproot_inputs.is_empty()
|| random_probability(rng, FIFTY_PERCENT_PROBABILITY_RANGE);A unique aspect of BIP326 is the 10% lookback rule, with 10% probability, the locktime is set slightly in the past (up to 99 blocks back), rather than exactly at the current block height. This preserves privacy for transactions that are signed but broadcast with a delay. For example, transactions routed through high-latency privacy networks. Without this, a transaction signed offline and broadcast later would stand out because its locktime would be noticeably behind the current chain tip.
For the nLockTime path:
if use_locktime {
let mut locktime = current_height.to_consensus_u32();
if random_probability(rng, TEN_PERCENT_PROBABILITY_RANGE) {
let random_offset = random_range(rng, MAX_RANDOM_OFFSET);
locktime = locktime.saturating_sub(random_offset);
}
let new_locktime = LockTime::from_height(locktime)
.expect("must be valid Height");
tx.lock_time = new_locktime;
}For the nSequence path, the same 10% lookback logic applies, but with an additional .max(MIN_SEQUENCE_VALUE) guard to ensure nSequence never reaches zero, which would disable the relative locktime entirely under BIP68. The saturating_sub call prevents integer underflow when subtracting the random offset from a small confirmation value.
else {
tx.lock_time = LockTime::ZERO;
let random_index = random_range(rng, taproot_inputs.len() as u32);
let (input_index, input) = taproot_inputs[random_index as usize];
let confirmation = input.confirmations(current_height);
let mut sequence_value = confirmation;
if random_probability(rng, TEN_PERCENT_PROBABILITY_RANGE) {
let random_offset = random_range(rng, MAX_RANDOM_OFFSET);
sequence_value = sequence_value
.saturating_sub(random_offset)
.max(MIN_SEQUENCE_VALUE);
}
tx.input[input_index].sequence = Sequence(sequence_value);
}It is also worth noting that BIP326 protection is opt-in by design. In PsbtParams, the enable_anti_fee_sniping flag defaults to false, meaning wallet implementers must explicitly enable it. This reflects two deliberate design decisions working together: it avoids silently modifying transaction behaviour for callers who have not opted in, and it preserves full backward compatibility, ensuring that existing wallets and integrations continue to behave exactly as before without any forced migration.
pub enable_anti_fee_sniping: bool,
// Defaults to falseThe implementation supports both std and no-std environments through conditional compilation.
Conclusion
BIP326, though simple in its mechanism, is profound in its implications. By randomly alternating between nLockTime and nSequence for anti-fee-sniping in Taproot transactions, it closes the last remaining forensic gap that chain analysts could exploit to identify off-chain protocol settlements on-chain. The privacy impact is a network effect, every Taproot wallet that adopts BIP326 enlarges the anonymity set that off-chain protocols like Lightning Network and CoinSwap depend on, improving fungibility and security for the entire ecosystem.
What makes BIP326 significant is that it requires no consensus change, no protocol upgrade, and no coordination between nodes. It is a wallet behaviour standard that any implementation can adopt. The anonymity set BIP326 depends on only grows if wallets act before Taproot usage patterns become entrenched.
BIP326 is not just a wallet policy recommendation, it is a foundational privacy primitive that the entire Bitcoin Layer 2 ecosystem depends on, and its full impact will only be realised as Taproot adoption, PTLC deployment, and wallet implementation converge toward a more private, fungible, and scalable Bitcoin.
References
- https://github.com/bitcoindevkit/bdk-tx
- https://github.com/bitcoin/bips/blob/master/bip-0326.mediawiki
- https://learnmeabitcoin.com/technical/upgrades/taproot
- https://bitcoinops.org/en/topics/htlc
- https://www.bydfi.com/blog/learn/glossary/merkelized-alternative-script-tree-mast
- https://www.wallstreetmojo.com/hashed-timelock-contract
- https://bitcoin.stackexchange.com/questions/120922/what-is-fee-sniping