Skip to content

Latest commit

 

History

17 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🔐 SealStore — a WOTS+-signed payments chain (CometBFT)

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.

🤔 What problem it solves

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.

⚙️ How it works

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.

🆚 vs ECDSA-signature payments

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.

📦 Requirements

  • Go 1.25+
  • cometbft (pure Go): go install github.com/cometbft/cometbft/cmd/cometbft@v0.40.0

🚀 Build & run (pure Go, no Docker)

# 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.sock

The node RPC is then at localhost:26657.

💻 Use via the CLI

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, P

💸 WOTS payments (accounts)

Payments 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.

✅ Tests

go test ./...

📚 References

  • 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 F and per-position random masks). https://eprint.iacr.org/2017/965

About

SealStore is a sovereign CometBFT blockchain for sealed, privacy-preserving key/value data, using WOTS-signed transactions with key rotation to support secure applications like private voting, sealed-bid auctions, and verifiable randomness.

Topics

Resources

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages