A sovereign CometBFT blockchain where every payment is authorised by a
Winternitz one-time signature (WOTS+) instead of an ECDSA key. The wallet
signs each payment with a hash-chain one-time key derived from its seed; only
sha256 stands between an attacker and the funds, and the spent key is burned
on use.
Shor's algorithm breaks ECDSA: every signed payment exposes the public key, and every exposed key is a future forged spend. SealStore replaces ECDSA with WOTS+ — a hash-based signature scheme that is secure against quantum adversaries (a quantum computer merely halves hash strength via Grover, it doesn't break it). Each payment is a single transaction carrying a WOTS+ signature; the account rotates to a fresh WOTS+ root afterward, so nothing reusable is ever exposed.
A WOTS+ key is a set of keyed, masked hash chains. Each chain step is
F(K, r_i, x) = sha256(K || (x XOR r_i)) — a per-position random mask r_i and
a public key K — matching the inner one-time signature of NIST FIPS 205
(SPHINCS+), which is more robust than classic WOTS's plain iterated hash
(see References below). The
wallet derives the k-th key deterministically from its seed; its root (the
hash of all the chains' public values) is what the chain stores as the account's
auth hash P. To pay, the wallet signs the hash of the payment body and the
next root with the key whose root equals the account's current P, and carries
the next root to rotate to. The signature binds the rotation target too, so
it cannot be swapped out.
- WOTS+ parameters are tunable (
DigestLen,HashLen,ChunkBits); the defaults sign a 32-byte digest over 16-byte internal chains in 8-bit chunks: 32 message chunks + 2 checksum chunks = 34 chains, a ~544-byte signature. - Verification needs no public key. The chain rebuilds each chain's public
value from the signature and confirms they hash to the stored root
P— so the signature both proves knowledge of the key and pins the exact payment body (a miner cannot swap in a different payment).
The leading byte is the type tag, byte strings are uvarint-length-prefixed, and
hashes are raw 32-byte arrays. Full spec:
docs/wots-signed-payments.md.
| WOTS+ (this chain) | ECDSA payment (typical) | |
|---|---|---|
| Byte footprint | ~544 B in 1 tx (signature dominated by 34×16-B chains) | ~110 B in 1 tx — Bitcoin P2WPKH or Ethereum (65 B r‖s‖v) |
| Computation | ~34 chains × avg ~127 hashes to verify (~4k sha256, ~µs) — no curve math | 1 ECDSA verify (double scalar multiplication, ~50–100 µs) |
| Post-quantum | Resistant (hash-based); Grover halves strength only | Broken by Shor's algorithm |
| Blockchain ops | 1 tx; single signed payment; P rotates to next WOTS+ root |
1 tx; two balance writes |
- You pay: a much larger signature and one-time keys (each account must rotate to a fresh root every spend).
- You gain: quantum-resistant signatures with only stdlib
sha256, a single signed transaction (no two-phase commit–reveal), and no curve math.
- Go 1.25+
cometbft(pure Go):go install github.com/cometbft/cometbft/cmd/cometbft@v0.40.0
# 1. build the ABCI app + CLI
go build -o sealstore ./cmd/sealstore && go build -o sealstore-cli ./cmd/sealstore-cli
# 2. create wallets and copy their addresses (balances start at genesis)
./sealstore-cli wallet my-secret
# → address: 9f86d08fb044…
# 3. init the node (do init once), then pre-credit accounts in the genesis
cometbft init --home /tmp/cometbft-home
# edit /tmp/cometbft-home/config/genesis.json and add an app_state:
# "app_state": {
# "accounts": [
# {"address": "<64 hex from step 2>", "balance": 1000000}
# ]
# }
# Each account is created keyed by its address with P seeded from the address
# itself — spendable by exactly the wallet that printed it.
# 4. start the app + node
./sealstore -kv-home $HOME/.kvstore
cometbft node --home /tmp/cometbft-home --proxy_app=unix:///tmp/example.sockThe node RPC is then at localhost:26657.
One user (quick path):
./sealstore-cli health
./sealstore-cli wallets # list wallets (address)
./sealstore-cli wallet # create a wallet (random seed)
./sealstore-cli transfer addr1 addr2 1 2 # a single WOTS+-signed payment
./sealstore-cli account addr1 # → balance, seq, PPayments are authorised by a WOTS+ signature — no ECDSA keys (see
docs/wots-signed-payments.md). Accounts hold
{balance, seq, P}; spending signs with the current key and rotates P to the
next WOTS+ root, burning the spent one-time key.
Note (breaking change): SealStore uses WOTS+ (NIST FIPS 205 / SPHINCS+ inner one-time signature), not classic WOTS. WOTS+ keys are derived under the domain string
"sealstore-wots+-v2", so roots/addresses from older schemes are invalid — an upgrade invalidates all previously issued addresses.
The address is the first WOTS root. A public address is exactly 64 hex
chars — WOTSRoot(seed, 0), the first root of the wallet's key chain. The
address is the account id. Accounts come into existence in two ways:
pre-credited in the genesis app_state, or lazily when they receive their first
payment — either way the account is keyed by the address with auth hash P
seeded from the address bytes (so P equals the address until the first spend
rotates it). Anything that isn't a 64-hex address is rejected — the chain never
invents a hash, since funds sent to a hash whose seed nobody knows would be
locked forever.
Funds move one way: a single SignPaymentTx. The wallet fetches the
account's seq, builds the payment body (seq + 1), and signs the hash of the
body plus next_root with the key at index seq (whose root equals the
stored P), carrying next_root = WOTSRoot(seed, seq+1) to rotate to. Because
next_root is inside the signed digest, it is authenticated by the signature —
an attacker cannot substitute a garbage root to lock the account. The chain
verifies the signature against P, applies the transfers, then sets P = next_root and advances seq.
Wallets store only the seed locally (~/.sealstore-cli/wallets/, one file
per address); every WOTS+ key is derived deterministically from it and never
leaves your machine. Commands identify a wallet by its full address:
./sealstore-cli wallet - # create a wallet (random seed)
# → address: 9f86d08fb044… # put this in genesis to fund it
./sealstore-cli wallets # list wallets (address)Transfer signs and submits a single payment:
./sealstore-cli transfer <from-address> <to-address> 10 1
# → amount 10, fee 1
./sealstore-cli account <to-address> # balance=10 seq=0 P=<its address>
./sealstore-cli account <from-address> # balance, seq=1, P=<rotated to next root>Replaying a transaction (stale seq), reusing a spent key, or substituting the
payee all fail — the signature binds the exact body and P has rotated.
go test ./...- NIST FIPS 205 (SLH-DSA / SPHINCS+), Stateless Hash-Based Digital Signature Standard, published 2024-08-13. WOTS+ is the inner one-time signature component of SLH-DSA/SPHINCS+. https://doi.org/10.6028/NIST.FIPS.205
- A. Hülsing, "W-OTS+ – Shorter Signatures for Hash-Based Signature
Schemes", the paper introducing the WOTS+ construction (keyed
Fand per-position random masks). https://eprint.iacr.org/2017/965