Introduction

In January 2015, a single Bitcoin transaction quietly created one of the strangest ongoing experiments in computer science. It funded 160 addresses, numbered 1 to 160, and left them there as bait. Address number nn holds its private key somewhere in the range [2n−1,2n−1][2^{n-1}, 2^{n}-1] — puzzle #1 hides a key that is just the number 1, puzzle #160 hides a key that could be almost any 160-bit number.

Whoever finds the private key for an address can spend the coins inside. No cryptography needs to be broken to win: ECDSA over secp256k1, the curve behind every Bitcoin address, is exactly as strong as always. The only door left ajar is that for the low-numbered puzzles, the range of the secret is small enough to search directly.

That is the whole game: not defeating a cipher, but out-computing a range that gets twice as wide with every step. It turns out that's plenty hard.

Try It: Hunt the Key

The real puzzles search spaces up to 21602^{160} keys. That is unusable here, so this demo shrinks everything down to a toy keyspace of just a few bits — small enough to search in your browser, big enough to feel the pattern.

<p class="hint">Puzzle #<b>n</b> hides its private key somewhere in the range [2^(n-1), 2^n − 1]. Slide n and watch the range — and the search — double in size every step.</p>
<div class="controls">
  <label for="nrange">Puzzle number n:</label>
  <input type="range" id="nrange" min="1" max="16" value="6" step="1">
  <span id="nval" class="nval">6</span>
</div>
<div class="range-box">
  <div class="range-line" id="rangeLine"></div>
  <div class="range-caption" id="rangeCaption"></div>
</div>
<div class="btns">
  <button id="search" type="button">Search</button>
  <button id="reveal" type="button" class="ghost">Reveal key</button>
  <button id="reset" type="button" class="ghost">New secret</button>
</div>
<div class="status" id="status">Ready. Press Search to hunt for the hidden key.</div>
<div class="progress-wrap">
  <div class="progress-bar" id="progressBar"></div>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .8rem; line-height: 1.45; }
.controls { display: flex; align-items: center; gap: .6rem; margin-bottom: .8rem; flex-wrap: wrap; }
.controls label { font-size: .85rem; font-weight: 600; color: #1d3557; }
input[type="range"] { flex: 1 1 160px; max-width: 220px; accent-color: #1d3557; }
.nval { font: 700 15px ui-monospace, monospace; color: #1d3557; min-width: 1.6em; text-align: center; }
.range-box { background: #eef2f5; border: 1px solid #cdd9e3; border-radius: 8px; padding: .6rem .8rem; margin-bottom: .8rem; }
.range-line { font: 700 13px ui-monospace, monospace; color: #1d3557; word-break: break-all; }
.range-caption { font-size: .8rem; color: #555; margin-top: .3rem; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: .7rem; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
         background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
button:disabled { opacity: .5; cursor: default; }
.status { font-size: 1rem; font-weight: 600; margin: .4rem 0; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.progress-wrap { background: #e3e7ea; border-radius: 6px; height: 10px; overflow: hidden; }
.progress-bar { height: 100%; width: 0%; background: linear-gradient(90deg,#1d3557,#e63946); transition: width .05s linear; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));

// Toy model of the real Bitcoin puzzle transaction: puzzle #n's private key k satisfies
// 2^(n-1) <= k < 2^n. Real puzzles reach n = 160; here n stays small enough to brute-force live.
let n = 6;
let secretKey = null;
let searching = false;
let timer = null;

const nrange = document.getElementById('nrange');
const nval = document.getElementById('nval');
const rangeLine = document.getElementById('rangeLine');
const rangeCaption = document.getElementById('rangeCaption');
const searchBtn = document.getElementById('search');
const revealBtn = document.getElementById('reveal');
const resetBtn = document.getElementById('reset');
const statusEl = document.getElementById('status');
const progressBar = document.getElementById('progressBar');

// Inclusive [lo, hi] range and its size for a given puzzle number.
function bounds(nn) {
  const lo = Math.pow(2, nn - 1);
  const hi = Math.pow(2, nn) - 1;
  return { lo, hi, size: hi - lo + 1 };
}

function pickSecret(nn) {
  const { lo, hi } = bounds(nn);
  return lo + Math.floor(Math.random() * (hi - lo + 1));
}

function setStatus(t, cls) {
  statusEl.textContent = t;
  statusEl.className = 'status ' + (cls || '');
}

function renderRange() {
  const { lo, hi, size } = bounds(n);
  rangeLine.textContent = '[' + lo + ' .. ' + hi + ']';
  rangeCaption.textContent = i18nFmt('Puzzle #%n% range has %size% candidate key(s) — this is what brute force must search.', { n: n, size: size });
}

function resetAll() {
  searching = false;
  if (timer) { clearInterval(timer); timer = null; }
  secretKey = pickSecret(n);
  progressBar.style.width = '0%';
  setStatus('Ready. Press Search to hunt for the hidden key.', '');
  searchBtn.disabled = false;
  revealBtn.disabled = false;
}

// Linear brute force in small batches per animation tick, so the doubling is visible, not instant.
function search() {
  if (searching) return;
  searching = true;
  searchBtn.disabled = true;
  revealBtn.disabled = true;
  const { lo, hi, size } = bounds(n);
  let candidate = lo;
  let tries = 0;
  const stepsPerTick = Math.max(1, Math.ceil(size / 400));
  setStatus('Searching candidates one by one...', '');
  timer = setInterval(() => {
    for (let i = 0; i < stepsPerTick && candidate <= hi; i++) {
      tries++;
      if (candidate === secretKey) {
        clearInterval(timer);
        timer = null;
        searching = false;
        progressBar.style.width = '100%';
        setStatus(i18nFmt('Found key %key% after %tries% of %size% candidates checked.', { key: secretKey, tries: tries, size: size }), 'ok');
        searchBtn.disabled = false;
        revealBtn.disabled = false;
        return;
      }
      candidate++;
    }
    progressBar.style.width = Math.min(100, (tries / size) * 100) + '%';
    if (candidate > hi) {
      clearInterval(timer);
      timer = null;
      searching = false;
      searchBtn.disabled = false;
      revealBtn.disabled = false;
    }
  }, 16);
}

function reveal() {
  setStatus(i18nFmt('The hidden key was %key%.', { key: secretKey }), 'ok');
}

nrange.addEventListener('input', () => {
  n = parseInt(nrange.value, 10);
  nval.textContent = String(n);
  renderRange();
  resetAll();
});
searchBtn.addEventListener('click', search);
revealBtn.addEventListener('click', reveal);
resetBtn.addEventListener('click', resetAll);

renderRange();
resetAll();
Result
Console

Pick a puzzle number nn: the hidden key lives somewhere in [2n−1,2n−1][2^{n-1}, 2^{n}-1], a range of exactly 2n−12^{n-1} candidates. Press Search and the computer checks candidates one by one until it hits the secret. Slide nn up by one and watch the candidate count — and the number of tries the search needs on average — double. That doubling, not any weakness in the cryptography, is the entire difficulty of the real puzzle.

The Real Complexity

How hard is puzzle #nn, really?

  • The private key is just an integer kk with 2n−1≤k<2n2^{n-1} \le k < 2^{n}; the matching public key is k⋅Gk \cdot G for the base point GG on secp256k1. Recovering kk from k⋅Gk \cdot G is the elliptic-curve discrete logarithm problem — see elliptic-curve cryptography and the discrete-log problem for the general picture.
  • Naive brute force tries every candidate in the range: up to 2n−12^{n-1} multiplications, checked against the known address. Expected work to find the key is about half the range, so it doubles every time nn increases by one.
  • The best public attack, Pollard's rho method (see Pollard's rho for discrete log), cuts the exponent roughly in half: around 2n−1=2(n−1)/2\sqrt{2^{n-1}} = 2^{(n-1)/2} group operations instead of 2n−12^{n-1}. That is a huge constant-factor win, but the cost is still exponential in nn — every extra bit still multiplies the work, just by 2\sqrt{2} instead of 22.
  • Status: ongoing, not solved. Puzzles #1 through roughly #66 have been claimed over the years (mostly by hobbyists and, for the higher ones, well-resourced GPU/FPGA rigs). As of 2026, about 78 of the 160 puzzles remain unclaimed, holding roughly US $58 million in Bitcoin. The lowest unclaimed target is puzzle #71, whose key sits in [270,271−1][2^{70}, 2^{71}-1] — a range of 2702^{70} candidates, or about 270≈235\sqrt{2^{70}} \approx 2^{35} Pollard-rho steps. That is within reach of serious hardware over time; puzzle #160 is not, not with anything resembling today's computers.

Nobody needs to break ECDSA to win a low puzzle — they just need to out-search a range that keeps doubling. That is P vs NP's cousin from the world of cryptography: brute force is always correct, the only question is whether exponential growth lets you finish.

Where It Matters

The puzzle transaction is a toy, but the arithmetic behind it runs real systems:

  • Choosing key sizes. Security engineers size keys precisely so brute force costs more than the value being protected. A 71-bit range being crackable-with-effort and a 128-bit range being untouchable for the foreseeable future is the same doubling law, just at different points on the curve.
  • Why "almost random" seeds are dangerous. Real Bitcoin wallets have been drained because their private keys came from weak or predictable sources — human-chosen passphrases, flawed random-number generators, reused nonces. Anything that shrinks the effective search range from 22562^{256} down to something a GPU farm can finish is exactly this puzzle transaction, unintentionally.
  • Grinding and vanity addresses. Vanity Bitcoin addresses (ones with a chosen prefix) are generated by brute-force search over a smaller space than the full keyspace — the same exponential trade-off, deliberately dialed down.
  • Benchmarking cryptographic hardware. GPU and FPGA teams use the low, solvable puzzles as real-world speed tests for elliptic-curve point multiplication — a public, verifiable benchmark with a cash prize attached.

Every one of these is the same lesson as the toy demo above: shrink the range, and exponential difficulty becomes finite effort.

Conclusion

The Bitcoin puzzle transaction never asked anyone to break a cipher — it asked them to out-run a keyspace that doubles with every step. That framing is what makes it such a clean, public demonstration of exponential cost: cheap enough at n=40n=40 for a laptop, and utterly out of reach at n=160n=160 for every computer on Earth combined.

As of 2026, roughly 78 addresses and about US$58 million in Bitcoin are still sitting untouched, guarded by nothing more than arithmetic. Puzzle #71 is the current frontier — reachable with enough hardware and patience, unlike the puzzles above it. It is one of the few places where you can watch P vs NP-style intractability play out for real money, in public, one solved address at a time.

Share this article

Pick a channel — or use your device's native share sheet.

Comments

Loading comments...

https://www.kipuhub.com/en/article/bitcoin-puzzle-transaction/Content licensed under CC BY-NC 4.0.