Bitcoin Forum
October 19, 2026, 08:59:03 AM *
News: Latest Bitcoin Core release: 31.1 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: Lessons from deploying post-quantum signatures (ML-DSA-44) on a live 2014 UTXO c  (Read 10 times)
hyperminer (OP)
Newbie
*
Offline

Activity: 134
Merit: 0


View Profile WWW
October 18, 2026, 09:36:06 PM
 #1

In September I hard-forked Blazecoin, a scrypt chain from 2014 (UTXO, now on a Bitcoin Core 28 base), to add a post-quantum output type. It went live at block 4,250,000 on 16 September 2026. Most of what I learned applies to any UTXO chain thinking about this, so here are the parts I would want to know before starting.

1. The signature is the easy part. Everything around it is the work.
The consensus change was one opcode, a new output template and a relaxed push limit for that template only. Then every service that touches an address needed changes:
  • the pool's coinbase builder (a 34-byte output instead of 25)
  • the indexer (a new script type, and post-quantum supply as its own category)
  • address verification on the site
  • the payout service
  • the desktop, web and Android wallets
  • the nightly UTXO consolidation
That half was bigger than the fork itself. Plan for it.

2. Size is the real cost, not CPU
ECDSA P2PKHML-DSA-44
Input size148 B3,782 B
Inputs per 1 MB block~6,700~264
Inputs per 100 KB standard tx~67026
Verify, one core254 µs132 µs
ML-DSA-44 verifies faster than our managed ECDSA, and a full block of it verifies in about 35 ms. But a standard transaction now fits only 26 post-quantum inputs, so our consolidation batches had to drop from 150 inputs to 26. Find every place that assumes "inputs are small" before the fork, not after.

3. ML-DSA has no xpub
There is no public derivation, so you can't hand out an xpub and derive receive addresses watch-only. We derive keys from the HD seed with a tagged hash, cache the expanded public keys, and accept that a locked wallet can recognise its coins but can't top up its address pool. One trap we caught in testing: if receive and change derive from the same seed, they produce identical addresses. Use separate seeds.

4. Make one reference implementation the authority
The daemon is C++ and the wallets are C#. Before writing either, I generated test vectors from a pure-Python FIPS 204 implementation and checked them byte-for-byte against the reference C code. Both codebases then had to match the same vectors (keys, signed transactions, 40 named pass/fail spend cases). Signing is deterministic only; randomised signing is a compile error in the daemon, so there is no RNG in the consensus path. Everything matched on the first run.

5. Choices we made and why
  • No ECDSA hybrid. The point is to stop depending on ECDSA. A hybrid adds about 100 bytes per input and buys nothing once ECDSA falls.
  • Not SLH-DSA first. It rests on the most conservative assumptions, but signatures are ~7.9 KB and signing took ~835 ms each in our tests (10 s for a 12-input transaction). It's reserved as the fallback if lattices break.
  • Not Falcon yet. It's much smaller, but FIPS 206 isn't final, and its floating-point signing is a known implementation hazard.
  • Versioned algorithm IDs, so ML-DSA-65/87 can be added later without another address format.

6. Ship everything before the height
Every node, wallet and service was on the fork build about a day before block 4,250,000. Post-quantum features were switched off by config until activation, and the wallets refused to pay to or spend from post-quantum addresses before the height. When the block arrived, nothing had to be deployed.

7. Your activation clock will be wrong
I forecast the fork block for 05:40 and it arrived at 21:18. The 880 blocks between the fork and the first post-quantum coinbase should have taken about 7 hours and took 50 minutes. Hashrate had been off for stretches, difficulty decayed, and blocks came at 2 seconds during the catch-up until the per-block retarget pulled it back. Don't hang anything time-critical on a predicted fork time; hang it on the height.

8. The fork doesn't migrate anyone
A legacy address is only protected while its public key has never appeared on-chain. Every coin on a spent-from or reused address stays exposed until its owner moves it. The protocol can offer the safe output, but only holders can use it. The best lever we found was making it the default: the wallets now use post-quantum addresses for receive, change and sweeps, and we moved the project's own funds first.

Caveats. This is a small chain. I chose the activation height with about a day's notice, which only works when one operator runs most of the hashrate, so that part doesn't transfer to a large network.

I'm happy to go into any of it: the signature digest, the key derivation, the push-limit change, or the test vectors.

ANN thread with the full specs: https://bitcointalk.org/index.php?topic=5597142.0
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!