All projects

Project

Complete, for now

Quantum Circuit Optimization for ECDSA

ECDSA is the piece of math that decides who owns every Bitcoin: it stops anyone else from spending your coins, and it guards much of the modern web too. A big enough quantum computer could break it. ECDSA.fail is an open competition to find the most efficient quantum circuit for that attack, ranked on a public leaderboard; the leaner the leading circuit, the smaller the machine an attack needs. I reproduced the best known circuit, took it apart, and concluded it could not be improved without starting over.

I was wrong. Three weeks later the leaderboard had moved 25 times, none of them the rewrite I said was needed. Going back to find out why led to what this project measures: the search cost behind every one of those improvements, which I could not find measured anywhere.

RustQuantum computingCryptographyOptimization

At a glance

Three numbers

The three that carry the project.

25

improvements I said were impossible

The number of times the leaderboard moved in the three weeks after I concluded it could not be improved.

830

slow checks per usable submission

The official checker takes about twenty seconds per candidate. My filter answers in milliseconds first, so only about 830 candidates per success need the slow check, down from about 87,000.

3

findings sent to the competition repository

Including a bug in the competition itself that silently makes experiments look like they had no effect.

The problem

How close is quantum to breaking ECDSA?

When you buy something online, install an app update, or send cryptocurrency, a piece of math quietly checks that the action came from its owner and not an impostor. That check is called a digital signature, and the version nearly everything relies on is ECDSA. A large, fault-tolerant quantum computer running Shor's algorithm could one day forge those signatures, spending coins it does not own and impersonating sites it does not run. The real question is how big a machine it would take.

The math behind ECDSA lives on an elliptic curve: a smooth arc drawn by one short equation, with a rule for “adding” two points on it. Most people have to see one to believe it is that simple; there is a live one here you can drag points on. Bitcoin uses a particular curve named secp256k1, and the whole attack comes down to running that point-adding rule as a reversible quantum circuit. The leaner that circuit, the smaller the machine an attack needs, and the sooner the threat becomes real.

What interests me is the mismatch. One short equation, one rule for adding two points on it, and that is the whole thing standing between a Bitcoin wallet and anyone who wants to spend from it. Hundreds of billions of dollars sit on a curve you can draw by hand.

ECDSA.fail, an Eigen Labs project built on Google Quantum AI's 2026 result, is an open competition around exactly that circuit: anyone can download the current best design, try to make it leaner, and submit. Every entry is checked against the same correctness tests, scored by the same formula, and ranked on a public leaderboard.

No break today

This is forward-looking resource estimation, not a present-day break. No wallet or website is at risk today, and no machine that could run the circuit exists: every run in the competition is an ordinary computer following the circuit one test input at a time. Why that works is on the how-it-works page. The work measures how urgently the world needs to move to post-quantum cryptography.

The rules

One number to beat

Submissions are scored on a single metric, and judged by a strict correctness suite.

Score

average Toffoli count × peak qubit width

Lower wins. Fewer Toffoli gates mean fewer costly “magic states,” and fewer qubits mean less hardware, so the product tracks how hard the attack is to build.

New to these terms? Here is what a Toffoli gate and a qubit actually are

There are no loopholes in the scoring. A circuit only counts if it gets all 9,024 test cases right, cleans up every scratch qubit it used, leaves no trace behind, and gives back exactly what it started with when run in reverse. A win that comes from skipping any of that makes the run fail rather than makes it faster.

The bar to clear

CircuitToffoliQubitsScore
Challenge starting circuit3.94M2,7151.07 × 10¹⁰
Google, low-qubit point2.7M1,1753.2 × 10⁹
Google, low-gate point2.1M1,4253.0 × 10⁹

Those are published reference points, and the Google rows are what the challenge measures its progress against. The community circuit was already below them when I joined in July, and it keeps moving: the one I reproduced scored about 1.49 × 10⁹ in August, and the construction that replaced it on August 23 was at about 1.15 × 10⁹ when I last measured it, about 2.6 times under Google's best published point.

A moving target

The leaderboard moved six times on August 23 alone, and the construction changed underneath it that morning. Every score here was measured on a stated day, on a stated version of the circuit, and the frontier has moved since.

What happened

I said it could not be improved

I got the circuit running on my laptop and took it apart to see where the cost goes. Almost all of it, about 95%, is two divisions, which are brutally expensive to do reversibly. I checked the published methods and none beats the one already in the circuit. Then I worked through eleven other ideas for making it cheaper, and every one was blocked. So I wrote up what I believed: nothing here improves without replacing the whole division engine, and that is a research project, not an afternoon.

Over the next three weeks the leaderboard improved 25 times. Not one was the rewrite I said was required.

I left the conclusion up, marked wrong at the top, and went back through all eleven claims. Seven held. Four did not. I learned the most from the failure where every measurement was right and the conclusion still was not. I found the circuit locked to a fixed number of qubits, could not move it, and called that a hard limit of the design. It was a setting. Part of that count is a shared pool that quietly expands to fill whatever limit you set.

What I actually had was a list of things I tried that did not work. I wrote it up as proof that nothing would.

The real finding

The test rewrites itself every time you edit the circuit

What I had missed was in the competition's rules the whole time.

The competition builds its 9,024 test cases by hashing the circuit itself, so changing the circuit changes the test. Imagine a teacher who writes a new pop quiz every time you revise your essay: you cannot fix what you got wrong, because tomorrow's quiz asks about something else. So a circuit does not pass or fail. It has a failure rate, and shipping one means hunting for a version that happens to ace a fresh quiz.

I could not find that rate measured anywhere, so I measured it over 199 full benchmark runs. On a typical attempt about twenty things go wrong, putting the chance that nothing does at roughly 1 in 850 million. At the 183 runs an hour my laptop manages, that is on the order of 500 years of computing per usable result. Read it as an order of magnitude; my uncertainty spans a factor of about 150.

That was the circuit the benchmark shipped through August. It has since been replaced, and on the new one the same search costs about 107 hours rather than 500 years. Two things changed at once, which is why those numbers are not a like-for-like improvement. The filter I built rejects most failing versions in milliseconds instead of the checker's twenty seconds each, and the new figure counts only the failures the filter can see, where the old one counted every way a version can fail.

Then how did 25 improvements get in? The search cost does not block a competitor, it charges them, and the price has been going up. Most improvements came early, while the circuit was simple enough that a working version was cheap to find. As the circuit got more aggressive the searching got expensive, and you can see that in the leader slowing down: eight improvements in one day in late July, three by the start of August.

I also measured the rate on an older version, from before eight of those improvements, and it came out statistically identical. Every one lowered the score everybody competes on and none moved this, because the score does not count the search.

So lowering the score is the easy half. Every improvement is then paid for in searching, and that price appears nowhere on the leaderboard, even as it slowed the leader down.

The competition

Who I am competing with

I assumed the leaderboard was a crowd of people. It is one bot, improving the circuit every 0.88 days. The bot ships its own search code inside its submissions, so I could read how the search works.

There is no exotic trick. It tests a candidate on 512 cases, then 2,048, then 8,192, then the full 9,024, dropping the candidate the moment it fails a rung. Almost everything fails, so almost everything is discarded cheaply, making the search about 7 times cheaper than running the full test every time. It also had a head start: it ran hardest while clean results were cheap, and its pace has fallen as the search got expensive.

Reading a competitor's search code told me where I stand. I am not going to out-grind a bot with a laptop. What I can do is measure what it is not looking at.

The tool I built

Faster, checked against the real thing, and worth half what I predicted

Measuring the failure rate 199 times meant 199 full benchmark runs at 110 seconds each, and 59 of those seconds go to rebuilding a circuit that is not changing. So I wrote a stripped-down version that builds once, patches only the last part of the circuit for each attempt, and reuses the hashing work. It runs 9.2 times faster. The speedup earns nothing in the competition, because the score counts the circuit's gates and qubits, not how fast the benchmark runs. What it buys is measurements.

It matches the official benchmark on all 199 attempts, down to the exact count of things that went wrong: 3,230, counted each way. I required the count to match, not just pass or fail, because a tool that gets the count right can rank near misses.

My prediction for it missed. Before building it I predicted it would remove about 4 of the twenty things that go wrong on a typical attempt, which would have brought a day of searching closer to affording a success. It removes 2.2. I had assumed the speedup would come from simulating faster; it came entirely from not rebuilding, and the simulation underneath is a floor I never touched. So this tool does not make the search practical. It makes the cost measurable, which is what everything else here rests on.

What it cannot do

It checks only one of the two ways an attempt can fail, so a result it likes is a candidate, not a winner; the candidates outnumber the real successes about 45 to one. Calling a hit a clean result would be the kind of mistake the rest of this project exists to catch, and the tool's own documentation says so at the top.

A trap

The bug that fakes a null result

This bug cost me a week, and anyone running experiments against the same benchmark will hit it.

The benchmark script runs the build with elevated permissions. For safety, that wipes the environment it runs in, including the setting that picks which test to run. So every experiment silently measured the default instead of what I asked for, and handed back an identical file each time. On screen that looks exactly like a clean, honest result: my change had no effect. It was intermittent too, because the permission cache expires partway through a long run, so some trials were real and some were not.

What caught it was a rule I picked up from another contestant's notes: a null result only counts if the output actually changed. If you claim your change did nothing, check that the machine noticed you made one. I now apply that to everything, and I reported the bug to the competition so it does not eat anyone else's week.

The last idea

46,134 gates that never do anything

Out of the circuit's roughly 1.3 million expensive operations, 46,134 never fire on any of the 9,024 tests. That is 3.35% of the score doing nothing, and the margin needed to take the lead is thin enough that proving one single gate truly dead would be a submission.

The catch is the one from before: the test is built from the circuit, so “this gate never fired on today's test” is a fact that destroys itself the moment you act on it. I needed a proof good for every possible test. I tried three ways to get one, and all three are now closed. For each I built a positive control, a case where the answer should be yes, so that a negative result meant my tools worked rather than that they were broken. Two of those controls caught real bugs in my own code before I believed the answer.

  • Pay for it less often. The prize is huge: 76.7% of the score is charged on tests where the gate never fires. The prize is also unreachable. Which tests charge you and which gates actually fire are set independently of each other, so lining them up is about as likely as flipping a coin more than two thousand times and getting heads every time. The category is empty for a structural reason, not because the search failed.
  • Watch it and see. I built a sampler to observe which gates stay quiet. It over-reports, and the reason is the useful part: watching tells you when something fires, never why it is quiet. A gate held silent by a rule the math guarantees looks identical to one that got lucky, and no amount of extra watching separates them.
  • Prove it from the form. A gate can never fire if its two switches are opposites, so I checked all 1.3 million for that relationship. Not one pair is related at all: not equal, not opposite, not even sharing a single ingredient.

The three failures share one cause. All three inspect the shape of a value, and this circuit is built out of division, which scrambles everything into a form with no shape left to inspect. Those gates are quiet because of what their inputs can be, not because of how they are written. The only route I can see is a proof about the division algorithm itself, which is a real research problem.

Contributed back

What went to the competition

Three of these went to the competition repository upstream rather than staying in my copy: the permissions bug that fakes null results, the dead-gate work with its controls, and a correction to the project's own documented failure rate, which was out of date.

None of them lowers the score, so none of them moves me up the leaderboard. The permissions bug is the one that matters to other people, because it can silently cost a week of experiments the way it cost me one.

Updates

Project notes

These get technical. The terms they use, Toffoli gates, modular inversion, the floor, are explained in plain language on How the circuit works.

July 9, 2026

Baseline reproduced and validated on my own machine

I got the challenge harness building and running on my ThinkPad, which meant fixing three real system problems along the way: sandbox networking, sudo credential caching, and home directory permissions. Then I reproduced the current community circuit and ran the full validation suite locally.

It passed everything: all 9,024 test points correct, every ancilla clean, no phase leakage, and the forward-then-reverse identity holds. Score: 1,320,763 average Toffoli gates × 1,152 qubits, about 1.52 × 10⁹. That score is the community’s accumulated work, which I reproduced and validated; my contribution so far is getting it to build, validate, and score cleanly on my own hardware.

July 10, 2026

Matching the frontier, then mapping what I thought was its floor

Superseded: the conclusion below is wrong, and the August 2 note explains how. Kept as written.

I ran the validated baseline through the competition’s submission tool. It scored cleanly and came back at exactly the current frontier, because it was the community circuit unchanged. A submission only moves the leaderboard if it lowers the score, so matching it was the starting line.

So I profiled where the cost lives. About 95 percent of it is the two divisions a reversible point addition requires. Then I tested every lever I could reach and found each one blocked: register widths at their mathematical bound, a swap I hoped to shorten that provably cannot be, dead-gate tables already saturated, a disabled squaring schedule that made things worse. The one lever left saves at most 6 to 9 thousand gates out of 1.3 million, at real risk to correctness.

So I concluded this implementation was at its floor, and that only a different division architecture could go lower. The circuit is the community’s work, not mine; my part is the profiling and the analysis.

July 11, 2026

Checking whether the floor is the circuit’s or the problem’s

Partly superseded: the literature survey here stands; the word "floor" does not.

If this circuit was at its limit, was the whole problem? Point addition needs two divisions and those are 95 percent of the cost, so one addition costs about twice the best division. I read the reversible division literature to find a cheaper one. The method already in the circuit runs about 629 thousand expensive gates; every alternative I found is worse. The closest competitors land near 1.7 and 2.35 million, and a 2026 method that saves qubits pushes the gate count up by two orders of magnitude.

I also checked the one fully disclosed competing circuit. Its per-addition number looks better until you notice it includes a 65,536-entry lookup table and is solving a different problem shape, so a bare single addition here still comes out ahead.

The survey itself held up. What I got wrong was the sentence after it: I turned "no cheaper division exists in the literature" into "this circuit cannot be improved," and those are not the same claim.

August 2, 2026

Upstream improved it 25 times, so I went back through my own claims

In the three weeks after I called the circuit finished, the leaderboard moved 25 times, and none of those was the rewrite I had said was necessary. I was wrong.

I kept the original document exactly as written, marked it wrong at the top, and audited all eleven of my claims one at a time. Seven stand. Four failed. The failure I learned most from had every measurement correct: I found the circuit used a fixed number of qubits, could not move it, and called that a hard limit of the design. It was a configuration setting, and part of that count is a shared pool that expands to fill whatever limit you set.

I was scoring every idea on the number the competition rewards, and there is a second cost behind every one of those 25 improvements that I had never measured. That is the next note.

August 2, 2026

The search cost behind every improvement

The competition generates its 9,024 test cases by hashing the circuit itself, so changing the circuit changes the test. A circuit has no fixed pass or fail; it has a failure rate, and shipping means searching for a version that happens to pass a freshly generated test.

I could not find that rate measured anywhere, so I ran 199 complete benchmarks. A random attempt is clean about 1 in 850 million times, which on my laptop is roughly 500 years of computing per usable result. Read it as an order of magnitude; my uncertainty spans a factor of about 150.

The stronger result is that this rate has not moved. I measured it on an older version, from before eight accepted improvements, and the difference is statistically indistinguishable from zero. Eight changes that all lowered the competition score left this one untouched, because the rate is not part of the score. Lowering the score is the easy half. Paying for the search afterward is the hard half.

August 3, 2026

Three ways to prove a gate is dead, all closed

46,134 gates never fire on any of the 9,024 tests, which is 3.35 percent of the score doing nothing, and the winning margin is thin enough that proving a single one truly dead would be a submission.

But the test comes from the circuit, so "never fired on this test" is a certificate that destroys itself when you use it. I tried three ways to get one that survives: charge the gate on fewer tests, observe it long enough to be sure, and prove from the algebra that its two switches can never agree. All three are closed. Each has a positive control, a case where the answer should be yes, so a negative result means the instrument works rather than that it is broken. Two of those controls caught real bugs in my code before I believed the answer.

They failed for one shared reason. All three reason about the form of a value, and this circuit is built from division, which leaves no form to reason about. What would actually work is a proof about the division algorithm’s own loop, which is research-scale, and now the only route I can see.

Three findings went to the competition repository upstream: the permissions trap, the dead-gate work with its controls, and a correction to the project’s own stale failure-rate number. None of them lowers the score.

August 23, 2026

The circuit was replaced, and two more of my conclusions were wrong

The benchmark stopped shipping the circuit I had spent a month measuring. A fixed-depth ping-pong division replaced it: 908,801 average Toffoli gates on 1,273 qubits, scoring 1,156,903,673 against 1,486,468,554. The old construction sits in the tree behind a switch, and everything I published before this measures a circuit that no longer runs.

So I built a filter for the new one. Submitting means testing millions of candidate versions of the circuit, and the official checker takes about twenty seconds per candidate, simulating it shot by shot. The ways a candidate fails are ordinary arithmetic underneath, so a plain program can predict most of them without simulating anything. Mine answers in milliseconds, catches about 65 percent of the failures of the kind it can see, and never faults a candidate that would have passed. Only the candidates it cannot fault need the twenty-second check: about 830 per success, down from roughly 87,000.

It immediately caught a number I had published. I had measured the search cost from eight samples, put the average through an exponential, and reported 25 to 37 days, not viable on my hardware. From sixty samples it is about 107 hours, with an interval running from 43 to 267. Six to eight times off, in the direction that abandons a plan I could have afforded.

The second wrong conclusion lasted twenty minutes. I priced Toom-3 for the squaring step, found it capped by the cost of its recombination, and wrote that the square was bounded. A submission then landed a second level of Karatsuba and took 2,650 gates out of it. Karatsuba recombines with additions alone, so the test I used to rule one method out was the test that would have picked the one I never tried. Three solver notes also went to the competition repository, through its own notes system, one of them that retraction; those notes are separate from the three findings filed earlier as an issue and two pull requests.

August 24, 2026

I searched for a submission, and the frontier moved past my target

A submission is two things: an improvement, and a candidate version of the improved circuit that happens to pass the test its own edit generated. Making candidates is possible because the rules leave a free dial. The last few operations of the circuit cancel themselves out, so changing them changes nothing the circuit computes, but it does re-roll the test. I had the improvement. A checkpoint in the interleaving schedule was mistuned by 33 rounds, worth 322 expensive gates at unchanged width, or 0.038 percent of the score. Two independent measurements of the saving agreed. Then I started making candidates.

Seven hours, about 1.8 million candidates, 6.2 percent of the search one success needs. 413 got past the filter and 263 of those held up against the real scorer. None passed all 9,024 tests, which is what 6 percent of a search predicts.

A candidate that passes everything averages 2.9 × 10⁷ tries, about 86 hours on this machine. The leaderboard drifts about 1.14 percent a day, so a target has to be worth more than roughly 5 percent to still be worth having when its search finishes. Mine was worth 0.038 percent. The longest quiet stretch that day was 6.5 hours, and when it ended the frontier was already below my target, so a winning candidate would have been out of date on arrival.

The competitors publish how they do it: H200 GPU pods, coordinated fleets of agents, and a ladder that drops a candidate after 256 of the 9,024 tests. Together that is two to three orders of magnitude more throughput than one laptop. Better filtering does not close the gap, because the number of candidates a success takes is set by the circuit, not by the filter. A smaller improvement of mine, a swap worth 0.016 percent, went to the competition repository as a note instead, for anyone with the throughput to spend it.

Verify

Check my work

The benchmark is public, so the score on this page is one anyone can reproduce.

Reproduce the score

The competition ships a command-line tool that fetches the benchmark for you, which is the supported way in: a plain git clone can hand you a different layout than the harness expects. Once it is set up, one command builds, validates and scores the circuit. It should pass all 9,024 test cases. On the morning of August 23 the new construction reported 908,801 average Toffoli gates × 1,273 qubits, about 1.16 × 10⁹; by that evening the competition's circuit was at 911,390 × 1,267, about 1.15 × 10⁹. The circuit I measured through August reported 1,288,101 × 1,154, about 1.49 × 10⁹. The number moves whenever the competition accepts another improvement:

terminal

curl -fsSL https://api.ecdsa.fail/install.sh | sh
ecdsafail login <api-key>
ecdsafail clone
./benchmark.sh

On a fresh Ubuntu machine you may hit the three system obstacles I did; the fixes are on the setup page. My own runs are logged in results.tsv in my working copy, and the live standings are on the competition leaderboard.

Status

Where this stands

Complete, for now. I have not lowered the score, which is the number the competition ranks you by. What I did produce is a measurement of what every improvement costs, corrections to three conclusions of my own, and three findings filed in the competition repository upstream.

The benchmark replaced that circuit on August 23, and the measurements above are labeled with the construction they were taken on. I built a filter for the new one, which brings the search for a valid submission down to about 107 hours on my machine.

I have made several attempts at a submission and none has landed. The leaderboard keeps changing, so the scores here are a snapshot of the days they were taken rather than a final state. I will keep trying, and keep reading what the accepted submissions change.

The circuit is the community's work, and a large amount of another contestant's code ships alongside it. The measurement and the analysis are mine, and the repository marks which is which.