Your Transaction Is Competing Against a Graph, Not a List

You hit send. The fee estimator said 12 sat/vbyte, which felt fine, and now you're watching a block explorer refresh while three transactions submitted after yours have already confirmed. Your wallet said the fee was reasonable. Your wallet was telling you half a story.

The other half is a dependency problem that most people building on Bitcoin have never seriously reckoned with.

Miners don't pick transactions the way a bouncer works a queue. They're solving a constrained optimization problem on a directed acyclic graph of who owes what to whom, under a hard deadline, with real money on the line.

Why Fee Rate Alone Is a Lie Your Wallet Tells You

Bitcoin blocks have a weight limit: 4 million weight units. A miner maximizing revenue must fill that space with transactions that collectively pay the most in fees. The obvious approach: sort by fee rate (satoshis per virtual byte), grab from the top, stop when the block is full.

Clean. Logical. Incomplete.

The problem is that transactions have parents. Consider a simple chain: Alice sends 1 BTC to Bob. Bob, before that transaction confirms, immediately sends 0.5 BTC onward to Carol. Carol's transaction cannot be valid without Alice-to-Bob being confirmed first, no exceptions, because the protocol enforces input validity. If a miner wants Carol's transaction, they're forced to take Alice's too.

Now suppose Alice-to-Bob pays 5 sat/vbyte and Carol's transaction pays 50 sat/vbyte. Sorted by individual fee rate, Carol looks like a star. But you can't have Carol without Alice, and Alice is mediocre. The miner has to evaluate the package together: combined fees divided by combined weight. That blended rate is what actually competes for block space.

This is the dependency graph. Every unconfirmed transaction spending an unconfirmed output is a node with edges. Miners are, in effect, running a modified knapsack problem on a directed graph. It's NP-hard in the general case, which is worth sitting with for a moment.

The Algorithm Bitcoin Core Actually Uses

Bitcoin Core's `getblocktemplate` builds what it calls ancestor sets. For each mempool transaction, it calculates the total fee and total weight of that transaction plus every unconfirmed ancestor it depends on. The sorting metric is ancestor fee rate: total ancestor package fees divided by total ancestor package weight.

Here's a worked example.

Transaction A (Alice to Bob): 250 vbytes, 1,250 sat fee. Fee rate: 5 sat/vbyte. Transaction B (Bob to Carol): 200 vbytes, 10,000 sat fee. Fee rate: 50 sat/vbyte. B depends on A.

Evaluated alone, B looks excellent. But the ancestor package for B is A plus B together: 450 vbytes, 11,250 sat total. Ancestor fee rate: 25 sat/vbyte.

Meanwhile, Transaction C is a standalone transfer, 200 vbytes, 8,000 sat fee, 40 sat/vbyte, no parents.

Sorted by ancestor fee rate, C at 40 beats B's package at 25. The miner running the default algorithm picks C first, even though B individually outpriced C. If you're a wallet developer who thought a high child fee would always accelerate a stuck parent, now you understand why it works through child-pays-for-parent (CPFP) and also why the blended rate is the number that matters, not the child's rate read in isolation.

Child-Pays-for-Parent Is a Feature, Not a Hack

CPFP is formalized precisely because of ancestor-set logic. Stuck transaction? Spend one of its unconfirmed outputs with a new transaction carrying a fee high enough to lift the entire package's ancestor fee rate above the current inclusion threshold.

A concrete scenario: you send 0.1 BTC to a hardware wallet, paying 2 sat/vbyte. Blocks are clearing at 15 sat/vbyte. Your transaction isn't moving. But you control the destination address, so you create a second transaction spending that unconfirmed output, with a fee generous enough that the combined package (your original 250 vbytes at 500 sat, plus the new 200 vbytes at 3,500 sat) hits an ancestor fee rate of 18 sat/vbyte. Now miners have a reason to grab both.

Think of it like a water main and a branch line: you can't pressurize the branch until the main is live, but you can increase the pressure differential enough that clearing both becomes the path of least resistance for whoever controls the pump.

This is why wallets expose "bump fee" features, and why Lightning Network channel opens can be accelerated from the receiving side even when the sender has gone dark.

What the Algorithm Gets Wrong (and Knows It)

The ancestor-set approach is a greedy heuristic. Fast enough to run in milliseconds on a mempool with hundreds of thousands of transactions, which matters because a miner needs a fresh block template ready the instant a new block arrives. Perfect optimization isn't available at that timescale. Nothing is.

The greedy approach can leave fees on the table in complex graph structures. Take a diamond dependency: transaction A is the parent of both B and C, and D requires both B and C. The ancestor-set logic handles this, but as graph depth and branching increase, the heuristic's approximation quality degrades against what an exhaustive search would find.

Bitcoin Core's mempool enforces a limit of 25 ancestor transactions and 25 descendant transactions per chain to keep computation tractable. A transaction that would create a 26th ancestor is rejected from the mempool outright. Importantly, that's a policy setting in the software, not a consensus rule. Other node implementations can and do differ.

Then there's Replace-by-Fee. If a transaction signals replaceability (via sequence number), a competing transaction spending the same inputs with a higher fee can evict it from the mempool entirely. This interacts with the dependency graph in ways that aren't obvious: replacing a parent can orphan its children, forcing wallets to rebroadcast. Some mining pools have experimented with full-RBF policies allowing replacement of any transaction regardless of opt-in signaling, which shifts the strategic calculus for anyone relying on zero-confirmation acceptance.

What Miners Actually Optimize For

A large mining pool doesn't just run Bitcoin Core's default template builder and call it done. Sophisticated operations run custom block-building software trying to squeeze more fee revenue from complex mempool states.

The gains are real but bounded. Analysis of historical mempools suggests the greedy ancestor-set algorithm typically captures somewhere between 95% and 99% of theoretically optimal revenue per block. Custom optimizers are chasing the remaining one to five percent. At scale, with blocks routinely collecting several tenths of a bitcoin in fees, that slice justifies engineering time. For smaller operations, the marginal gain rarely does.

Acceleration services, where a user pays an out-of-band fee to a pool to prioritize their transaction, work by the pool manually inserting a transaction into their template above what its fee rate would normally earn. The pool captures a payment that bypasses the public mempool entirely. It isn't protocol manipulation, it's a private market running alongside the public one, and by most measures one of the more transparent corners of the fee ecosystem, since the economic incentive is straightforward and the counterparties are identifiable.

The Part That Changes How You Set Fees

Spending only confirmed outputs? Your ancestor fee rate equals your own fee rate. The sat/vbyte estimate your wallet shows is accurate. You're a leaf node. Simple.

Spending an unconfirmed output? You're now inside someone else's ancestor set. Your transaction's effective competitiveness depends on your parent's fee rate too. A wallet that doesn't surface the parent's fee rate when you're spending unconfirmed outputs is hiding information you need, and that's a design failure, not a minor omission.

Building a service that batches transactions or constructs payment chains? The dependency graph is your infrastructure, full stop. Every output you create that might be spent before it confirms is a graph edge you're responsible for managing. Batching ten payments into one transaction reduces fee footprint and eliminates graph complexity entirely, which is why high-volume exchanges do it obsessively.

So here's the question worth asking before you ship your next fee estimation logic: do you actually know which graph you're a node in?

The mempool isn't a waiting room with a single orderly line. It's a weighted directed graph, and miners are running a timed auction on every node simultaneously. The pipe either has enough pressure or it doesn't.