Where you are. Module 1 left you holding miniledger v1; this module has been wiring rails onto it. Lesson 3 built RTGS, which settles each payment one by one, irrevocably, in reserves. Lesson 4 priced that immediacy: gross settlement means every payment needs its full value in reserves at the moment it settles, so timing and ordering set the bill. Lesson 5 let banks answer the price by posting less and queueing more, and measured what that trade costs in delay. This lesson is the queue’s failure mode. A queue can arrange itself into a shape no patience will drain - every payment waiting on a payment that, around a loop, waits on it back - and the cure turns out to be not more reserves but a different way of settling. You will build the deadlock out of three perfectly sound banks, watch the FIFO drain fail forever, and dissolve it in one pass that spends nothing.
Three good banks, going nowhere
Monday on the RTGS rail, three banks you know from module 1’s close of business: Alder, Birch and Cedar. Three payments sit queued. ann at Alder is paying bob at Birch 100. ben at Birch is paying cara at Cedar 100. carl at Cedar is paying ada at Alder 100. Every payment is good: each payer’s deposit covers the full amount, every sheet squares, and nothing about any of them would make submit refuse. Each bank holds 10 of reserves. The numbers are stylised - a queue worth 300 against 30 of reserves in the whole system - so the knot shows at sight.
Watch Alder try. ann’s payment needs 100 of reserves and Alder holds 10, so Alder waits for its inflow: carl’s 100, coming from Cedar. Cedar cannot send it - Cedar holds 10 and is waiting on ben’s 100 from Birch. Birch holds 10 and is waiting on Alder. An hour passes; nothing settles. Go looking for the failure and there is none to find: no bank broke a rule, no payment was refused, no sheet is out of square. Every participant is behaving correctly, and the system is stuck.
The idea in one paragraph
Gridlock is the payment system’s deadlock: a set of queued payments in which each can settle only after another in the same set, arranged in a closed cycle, so that no order of settlement can begin. It is not insolvency - every payer can afford its payment and every bank’s sheet squares throughout - it is topology: the gross flows are large, the net flows are exactly zero, and a rail that demands gross liquidity payment by payment finds the whole cycle stuck behind reserves nobody holds. The cure matches the cause. Recognise the cycle, check that every member’s queued inflows equal its queued outflows, and settle the whole set as one simultaneous stroke: deposits move at full value, every reserve position ends exactly where it began, and the queue drains without one new unit of liquidity. The algorithms that run this cure inside real rails are called liquidity-saving mechanisms.
The wait-for cycle
The rigging of the world deserves a look, because it uses a tool you already own. Each payer’s deposit of 100 was funded twice over: a 10 endowment - the honest act from module 1 lesson 10 that creates reserves and deposit together - plus a 90 loan, and module 1 lesson 9 is exactly why the loan half matters: loans create deposits without adding a unit of reserves. So every payer can afford the 100, while every bank holds only 10 of settlement liquidity. Deposits and reserves live on different tiers, and this lesson is what that gap does under stress.
Now run the queue against it. The drain from lesson 5 looks at the head of the queue, checks the paying bank’s reserves cover the amount, and settles only then. Head of the queue: 100 against 10, blocked, and the pass ends. Reorder however you like and run again: every ordering blocks the same way, because whichever payment goes first asks some bank to pay 100 while holding 10, and the only event that would raise its reserves is the settlement of another payment in the same queue. Ten passes or ten thousand: settled, zero.
Draw it instead of running it. Give each queued payment an arrow meaning waits on, pointing at the payment whose settlement would fund it. ann’s payment waits on carl’s; carl’s waits on ben’s; ben’s waits on ann’s. The arrows close into a loop, and the loop is the whole story: there is no first payment, because every candidate depends on another.
Wider than the screen; scroll it sideways.
Dissolving the knot
Stop trying to order the queue and read it whole. For each bank, sum what the queue owes it minus what it owes the queue. Alder: 100 in from Cedar, 100 out to Birch - net zero. Birch: 100 in from Alder, 100 out to Cedar - net zero. Cedar: the same - net zero. The queue that no ordering could start is, in aggregate, a request for nothing to move between banks at all.
So settle it whole: all three payments in one simultaneous stroke. Each payment’s deposit legs post at full value - ann down 100 at Alder, ada up 100, and likewise round the circle - because the payments are real and their full amounts are owed. The reserve legs, summed across the batch, cancel to zero at every bank: each 100 out meets a 100 in, and no reserve position ends a unit from where it began. Value settled, 300; liquidity spent, none. This is module 0’s netting insight arriving where it bites hardest: the cycle’s gross is 300 and its net is zero, and settlement design alone decides which of those two numbers you must fund.
Production rails carry an algorithm for exactly this. A liquidity-saving mechanism, LSM for short, runs against a gross-settlement rail’s queue and hunts for sets of queued payments - pairs, cycles, bigger tangles - that can settle together using far less liquidity than they would need one at a time, sometimes none. On lesson 5’s frontier the LSM is the rare free move: less liquidity for the same delay, paid for in nothing but computation.
One honesty note about what your miniledger will show you. It applies a batch payment by payment, so mid-batch Alder’s reserves dip to -90 before carl’s payment restores them; step through it and you will watch an impossible state. A real LSM never exhibits that state: it computes the end position of the whole cycle first and posts it as one stroke. Simultaneously is precisely the claim that there is no moment in between for anyone to observe. Your batch replays the offset; the end state is identical, and the asserts pin it.
Review
What gridlock is
Three banks: Alder, Birch and Cedar. Each holds ten in reserves. Each has a queued payment of one hundred to the next bank round the circle. Every payment is good. Each payer’s deposit covers it in full, and every sheet squares. But Alder needs one hundred and holds ten, so it waits for its inflow from Cedar. Cedar waits on Birch. Birch waits on Alder. Draw an arrow from each payment to the payment whose settlement would fund it, and the arrows close into a loop. That is gridlock: a closed cycle of payments in which none can settle alone. No ordering fixes it, because a cycle has no starting point. Reordering a cycle just rotates it.
How offsetting dissolves it
Stop trying to order the queue, and read it whole. For each bank, sum what the queue owes it minus what it owes the queue. Alder: one hundred in from Cedar, one hundred out to Birch. Net zero. The same at Birch, and the same at Cedar. The queue that no ordering could start is, in aggregate, a request for nothing to move between banks at all. So settle it whole: all three payments in one simultaneous stroke. Each payment’s deposit legs post at full value, because the payments are real and their full amounts are owed. The reserve legs, summed across the batch, cancel to zero at every bank. Value settled, three hundred. Liquidity spent, none.
Liquidity is not solvency
One distinction to carry out of this lesson. The three banks were never in trouble. Before, during and after the gridlock, every sheet squares. What each bank lacked was reserves at one particular moment, which is liquidity. What none of them lacked was assets to cover liabilities, which is solvency. Gridlock is a liquidity topology problem, not a solvency problem. And the two look identical from the outside: a bank that cannot settle today, and a bank that can never pay, both show you the same missing payment. Confusing the first for the second is how sound banks get called broken in a crisis.
Check yourself
1. Every bank is solvent and every payer’s deposit covers its payment in full. What exactly is the system short of?
Nothing, in aggregate - that is the point. Each bank individually is short of gross settlement liquidity at its own moment: 100 demanded where 10 is held. But the cycle’s net demand is zero at every bank. The shortage is positional, a matter of where and when reserves would need to be, and no ordering can fix it, because the only source of each bank’s inflow is another payment stuck in the same queue.
2. Why can no ordering of the three payments settle the queue? Is the deadlock just bad luck of arrival order?
No ordering works, so it is not luck. Whichever payment goes first requires its bank to pay 100 while holding 10, and the inflow that would fund it sits somewhere in the same set. In graph terms: the wait-for graph is a closed cycle, and a cycle has no starting point - no payment depends on nothing. Reordering a cycle just rotates it.
3. Your offsetting pass asserts every bank’s net is exactly zero before settling anything. Change ann’s payment to 120 and predict what happens.
The assert fires: the nets become Alder -20, Birch +20, Cedar 0, and the cycle no longer closes. Offsetting could still dissolve the closed core of 100 per leg, but the remaining 20 is a genuine net transfer that needs real reserves - and Alder holds 10. An LSM shrinks the liquidity a queue needs down to its true net residuals; it cannot make a genuine net obligation vanish.
4. After the pass, ann’s deposit reads 0 and ada’s reads 100, yet no bank’s reserves moved. Were the payments real?
Entirely. Each payment’s deposit legs posted at full gross value, which is why every payer paid in full and every payee was paid in full. The reserve legs, summed across the simultaneous batch, cancelled to zero at every bank, so the tier 1 movement each payment would have needed alone never happened net. Customers experienced gross settlement; banks experienced no reserve movement at all; both are honest readings of the same batch.
5. Mid-batch, your miniledger drives Alder’s reserves to -90; a real LSM never shows such a state. What does “simultaneously” actually mean?
That there is no observable moment between the payments. The LSM computes the end position of the whole cycle and commits it as one stroke, so no state exists in which one payment has settled and another has not. The -90 is an artefact of replaying the cycle sequentially inside one batch: visible if you step through, gone by the time settle returns, and pinned to gone by the reserves-unchanged asserts.
Do this
Twenty minutes, from module-02-domestic-rails; the script adds the repo root to its own path, so the miniledger import resolves on its own. Open code/gridlock.py. The harness is the hook made executable: it builds the three-bank world - each payer’s 100 funded by a 10 endowment plus a 90 loan, so every bank opens with exactly 10 of reserves - queues the cycle, then proves the deadlock by running ten FIFO drain passes and asserting every one of them settles nothing. Your work is offset_cycle, and the TODO(you) names its two moves. First, recognise the cycle: sum each bank’s queued inflows minus outflows and assert every bank’s net is exactly zero - offsetting only works when the cycle closes. Second, settle the set as one atomic batch: submit every payment, call settle() once, clear the queue, and return the number settled.
python3 code/gridlock.py
The asserts after the call are the spec: three payments settled in one pass, an empty queue, every bank’s reserves exactly where they started, ann at 0 and ada holding her 100, and assert_world green. Done right, the run ends with
offsetting pass: settled 3 payments worth 300, reserves unchanged at 10 per bank
every bank waited to be paid before paying; offsetting the cycle settled all three simultaneously with no new liquidity at all
If your zero-net assert fires on the unmodified queue, you have almost certainly summed inflows and outflows with the same sign: money the queue owes a bank counts plus, money the bank owes the queue counts minus. The completed pass is solutions/gridlock.py; compare once you are green, not before.
What you can now do. You can build a deadlock out of three solvent banks - every payment valid, every sheet square, nothing able to move - and dissolve it without adding a single reserve, by recognising the closed cycle and settling it simultaneously net. You can name the two readings of one queue, gross 300 and net zero, and say which settlement designs must fund which. And you can keep liquidity failure and solvency failure apart under pressure, which is a distinction whole crises turn on. The offsetting pass you wrote ran once, as an emergency repair on a jammed gross rail. Lesson 7 makes the cancellation the whole design: batch rails collect payments all day, cancel everything that offsets, and settle only the differences at fixed cutoffs - netting as the rail itself, not the rescue.