Bijective mapping between BIP39 mnemonic seeds and Burning Ship fractal locations, with Argon2-based two-stage pipeline.
Licensed under either of Apache License, Version 2.0 or MIT License at your option.
Protecting a Bitcoin seed phrase is a problem with no good conventional solution. Every existing approach sacrifices at least one desirable property. Great Wall provides all four at once:
- Knowledge-Based Authentication. Your secret lives entirely in your memory — no device, physical vault, or geographic location required.
- Individual Custody. You depend on no one else. The core premise of Bitcoin — full self-custody — is kept intact.
- Non-Obscurity. The method is not a secret trick that fails the moment an attacker learns about it. Nor does it rely on convincing the attacker that the stash doesn't exist or is smaller than it really is.
- Coercion-Resistance. The threat of violence is ineffective as a means to obtain the secret leading to the stash.
In one sentence: it's all in your head (1), in nobody else's (2), the attacker is aware of that (3), and is nevertheless unable to rob it (4).
This sounds like having your pie and eating it too — many times over. It is only possible because the secret knowledge (that is only in your head) is tacit, and the interface for deploying it (to convert it into a key) is gated by an inescapably lengthy computation.
The Burning Ship fractals are indescribable labyrinths. Great Wall converts your secret into exact coordinates on these labyrinths.
Your memory of where those points are is tacit knowledge — the same kind of knowledge that lets you recognize a friend's face but not describe it precisely enough for a stranger to pick them out of a crowd. You can identify your locations by looking at the fractal, but you literally cannot dictate them to someone else. There is no verbal shortcut: the only way to extract the secret is to sit in front of the fractal and point.
Great Wall splits your secret into two halves. The first half is encoded on the standard fractal. But before the second half can be encoded or decoded, the system requires a long, memory-intensive computation using Argon2 — a key-stretching algorithm where each step depends on the previous one (strictly sequential) and requires gigabytes of RAM that cannot be traded for speed. The delay is configurable: hours, days, or weeks. The computation produces a unique key that reshapes the fractal itself — generating a new, private labyrinth that only your first-half secret can unlock.
The second-stage fractal does not exist until this computation finishes. No one — not even a fully cooperative user — can reveal the second-half locations any faster, because there is nothing to point at until Argon2 is done.
This is why coercion fails: the user cannot verbalize their fractal locations (tacit knowledge), and the second-stage fractal cannot be materialized without completing the full Argon2 computation. Coercing the user into giving up the entire secret effectively requires kidnapping them for at least as long as the Argon2 delay — hours, days, or weeks. That is the wall.
Bijective mapping between BIP39 mnemonic seeds and locations in the Burning Ship fractal, using I4F60 fixed-point arithmetic (60 fractional bits, range [-8, +8)).
Supports three size presets:
| Preset | BIP39 Words | Entropy Bits | Points/Stage |
|---|---|---|---|
| mini | 6 | 64 | 1 |
| default | 12 | 128 | 2 |
| large | 24 | 256 | 4 |
- Rust 1.70+ (with cargo)
- Python 3.8+
- numpy >= 1.24
- pygame >= 2.5
cd burning_ship/rust_engine
# Release build (no logs — default)
cargo build --release
# Release build with verbose logging to stderr
cargo build --release --features verboseThis produces burning_ship/rust_engine/target/release/libburning_ship_engine.so.
cd burning_ship
python3 viewer.py
# Optional: enable render cache (recommended for smoother panning)
python3 viewer.py --cache-size 1048576cd burning_ship
# All tests in one go (Rust + bijection + frozen vectors + round-trips + meta tests)
bash run_tests.shOr individually:
cd burning_ship
# Rust unit tests
cd rust_engine && cargo test && cd ..
# Bijection test (1-8 bits)
python3 test_bijection.py
# Frozen vectors, round-trips, cross-mode, and meta tests
python3 test_vectors.py
# Single vector (useful for debugging)
python3 test_vectors.py --vector test_vectors/v0.1.0/vanity2_iter0.json
# Verbose output (show diffs on failure)
python3 test_vectors.py --verbose| Input | Action |
|---|---|
Mouse wheel / + - |
Zoom in/out at cursor |
| Arrow keys / drag | Pan |
R |
Reset view to origin |
1-5 |
Switch color scheme (Classic/Fire/Ice/Rainbow/HiCon) |
P |
Cycle escape-count transform (Identity/Square/Cube/Exp/Sqrt/Cbrt/Log) |
L |
Toggle brightness falloff (sigmoid cave-like dimming) |
Tab |
Focus BIP39 text input |
Enter |
Encode BIP39 seed (when input focused) |
C |
Clear all encoded/decoded points |
D |
Toggle debug mode (show hex fields, verbose info) |
S |
Toggle point-selection mode (click to decode) |
T |
Toggle Stage 1 / Stage 2 |
V |
Toggle area visualization (bisection rectangles) |
< / > |
Navigate bisection steps (when V active) |
W |
Cycle size preset (mini/default/large) |
M |
Toggle manual bit-input mode |
O / I |
Enter bit 0 / bit 1 (manual mode) |
Backspace |
Undo last bit (manual mode) |
X |
Stretch correction mode (click P1, P2) |
Z |
Clear stretch corrections |
F2 |
Random encode (stage 1 + Argon2 + stage 2) |
F5 |
Load session from JSON |
F6 |
Save session to JSON |
Escape / Q |
Quit |
Ctrl+C / Ctrl+V |
Copy / Paste in text fields |
Stage 1 encodes the first half of entropy bits using the canonical Burning Ship formula (o=0, p=0, q=0). The encoded bits are then hashed with Argon2 (configurable profile and iteration count) to derive stage-2 fractal parameters (o, p, q).
Stage 2 encodes the remaining entropy bits using the perturbed formula with the Argon2-derived parameters, producing a different fractal with a different area tree.
Press M to enter manual mode. Use O (bit 0) and I (bit 1) to
manually bisect the area tree one level at a time. The area visualization
updates incrementally (O(1) per keypress via inherited seeds across FFI).
Points auto-commit at 32 bits and advance to the next point.
Press S to enter selection mode, then click points on the fractal.
Each clicked point is validated through the full bisection with
contraction — points that fall in contracted-away dead zones are
rejected with a status bar message. Valid points are decoded to bits.
When a BIP39 mnemonic is decoded, a salt input field and SHA512 button
appear. Enter a salt string and click SHA512 to compute
SHA-512(mnemonic + salt) — the hex digest is copied to the clipboard.
Press F6 to save the current session (encoded points, Argon2
parameters, mnemonic) to a JSON file. Press F5 to load. The size
preset is auto-detected from the entropy bit count.
burning_ship/
viewer.py Main application (event loop, rendering, panel)
constants.py All configuration, colors, size presets
palettes.py Color schemes and escape-count transforms
encoding.py BIP39 ↔ fractal encode/decode
argon2_pipeline.py Argon2 hashing, checkpoints, F2 pipeline
session.py Clipboard helpers, session save/load
text_input.py BIP39 text field keyboard handling
manual_mode.py Manual O/I bit input with incremental encode
bip39.py BIP39 mnemonic ↔ bit conversion (6/12/24 words)
burning_ship_engine.py Python ctypes bridge to Rust
rust_engine/
src/
lib.rs Crate root, log_verbose! macro
fixed.rs I4F60 fixed-point type
fractal.rs Burning Ship iteration
discovery.rs Island discovery (sampling + flood fill)
bisect.rs Bisection tree encode/decode
render_cache.rs FIFO cache for rendering
argon2_hash.rs Argon2 hashing via Rust argon2 crate
ffi.rs C-ABI exports for Python bridge
from burning_ship_engine import encode, decode, DiscoveryParams, Rect
bits = [1, 0, 1, 1, 0, 0, 1, 0]
result = encode(bits)
decoded = decode(result.point_re_raw, result.point_im_raw, len(bits))
assert bits == decodedWith I4F60 (60 fractional bits), the encoder reliably handles 32 bits per point. For a 12-word BIP39 seed (128 entropy bits), use 2 stages of 2 points each (32 bits per point). For 24-word (256 bits), use 2 stages of 4 points.