LatticeScan

Open specification

This is the standard behind every number on this site. It is open: anyone may implement it, extend it, or assess against it. Verify any LatticeScan document yourself with the reference verifier, no dependencies beyond Node.js 24:

curl -sO https://latticescan.com/spec/verify-pqs.mjs
node verify-pqs.mjs any-document-you-were-handed.json
LS_API_KEY=your-key node verify-pqs.mjs https://latticescan.com/api/quantum-solvency

Source: verify-pqs.mjs · specification as Markdown · assessor public key · how signatures work

Proof of Quantum Solvency

Specification, version 1.0 Status: Published for public comment. Stable: an implementation that conforms today will conform to any 1.x revision. Date: 22 September 2026 Maintainer: LatticeScan (LSCC LLC), hello@latticescan.com Licence: This specification is released under CC BY 4.0. Anyone may implement it, extend it, or assess against it, with attribution.


0. Why this document exists

Proof of Reserves shows that the coins exist. It does not show whether the coins will still be their owner's once a cryptographically relevant quantum computer can recover a private key from a public one. On a blockchain there is no key rotation and no revocation, so a public key that has been revealed is a permanent target and cannot be un-revealed.

Several parties now assert facts about this risk: exchanges say their reserves are safe, custodians say client holdings are hash-protected, ETF prospectuses say the risk depends on the custodian's key practices. None of those assertions can be checked by the party making them, because a holder cannot credibly attest to itself, and no attestation criteria exist for an auditor to apply.

This specification defines, in terms precise enough for an independent party to compute and a third party to reproduce from public data:

  1. what it means for a balance to be quantum-exposed (Section 3);
  2. how to measure the quantum solvency of a set of addresses (Section 4);
  3. how to rank exposure by practical risk (Section 5);
  4. how to check a holder's published key policy (Section 6);
  5. how a result is signed, anchored, and verified so that nobody has to trust the assessor or the holder (Sections 7 to 9);
  6. what an assessor must disclose about coverage, sources and limitations (Section 10).

The normative words MUST, MUST NOT, SHOULD and MAY are used as in RFC 2119.

1. Scope

This version covers Bitcoin (mainnet, native units of BTC) and Ethereum (mainnet, native units of ETH, externally-owned and contract accounts). Tokens, Layer 2 networks, other chains and extended public keys (xpub, zpub) are out of scope for 1.0 and reserved for later versions. Nothing in this specification requires access to a holder's systems, keys or private records; every input is public chain data plus the holder's own published address set and policy.

2. Definitions

  • Holder. The party whose balances are measured: an exchange, custodian, fund, treasury, protocol or sovereign.
  • Address set. The list of addresses being measured. For a Proof of Quantum Solvency it MUST come from the holder's own publication (a proof-of-reserves file, a published catalogue, a signed statement), never from third-party wallet labels or clustering heuristics.
  • Assessor. The independent party that computes and signs the result. An assessor MUST NOT hold custody of, or a financial interest in, the balances it measures.
  • Snapshot. One measurement of an address set at one point in time, read from public chain data.
  • Cryptographically relevant quantum computer (CRQC). A quantum computer able to recover a secp256k1 private key from its public key. None exists at the date of this version; see Section 10.

3. Exposure

A balance is exposed when the public key that controls it is already recoverable from public chain data. Exposure is a property of the key, not a prediction about hardware.

3.1 Bitcoin

Classify each address by its output type, then apply the rule:

Output typeAddress formExposure rule
Pay-to-public-key (P2PK), bare multisigNo standard address; legacy base58 form for known casesExposed. The public key is in the output.
Pay-to-taproot (P2TR)bc1p…Exposed. The x-only public key is placed directly in the output (BIP-341), whether or not the output has been spent.
Pay-to-public-key-hash (P2PKH)1…Exposed if the address has ever been spent from (the key is published in the spending input). Otherwise hash-protected.
Pay-to-witness-public-key-hash (P2WPKH)bc1q…, 42 charactersSame rule as P2PKH.
Pay-to-script-hash (P2SH)3…Exposed if the address has ever been spent from. Exposure of an unspent P2SH depends on the redeem script; an assessor MUST treat a spent P2SH as exposed and MUST disclose that redeem-script contents were not evaluated.
Pay-to-witness-script-hash (P2WSH)bc1q…, 62 charactersSame rule as P2SH.

"Spent from" means at least one transaction input references an output paid to the address. An address with any spent output is exposed for all of its balance, including outputs funded after the spend, because the key is the same.

Hash-protected means only a hash of the key is on-chain and the address has never been spent from. It is not exposed at rest. It becomes exposed at its first spend, and during the interval between broadcast and confirmation the key is visible in the mempool; an assessor SHOULD state this "on-spend" window.

3.2 Ethereum

Account typeDetectionExposure rule
Externally-owned account (EOA)eth_getCode returns 0xExposed if the account has sent at least one transaction (eth_getTransactionCount > 0): the public key is recoverable from any ECDSA signature. Otherwise not exposed.
Delegated EOA (EIP-7702)Code begins 0xef0100Treated as an EOA.
Contract accountAny other codeNot classified. Exposure depends on the signer(s) that control the contract, which are outside the account. An assessor MUST report contract balances separately and MUST NOT count them as protected.

3.3 Balances

Balance is the confirmed on-chain balance at the snapshot, in native units, excluding unconfirmed transactions. Bitcoin balances MUST be computed in satoshis and Ethereum balances in wei before conversion, so that comparisons (Section 6) are exact.

4. Quantum solvency

For an address set S on one chain, at one snapshot:

  • Total = sum of balances of all readable addresses in S.
  • Exposed balance = sum of balances of exposed addresses in S.
  • Exposed share = Exposed balance ÷ Total, as a percentage.
  • Quantum solvency = 100% − Exposed share.

Quantum solvency is the share of the holder's on-chain value that is not behind a revealed key. It is balance-weighted: one large exposed key matters more than many dust addresses. Chains MUST NOT be summed with each other; report each unit separately.

Unreadable addresses (Section 10.2) MUST be excluded from Total and reported by count. They MUST NOT be assumed safe.

5. Practical risk

Exposure is binary, but an attacker must recover each key separately and the cost scales per key. A total spread across many small keys is a weaker target than the same total behind one key. An assessor MUST therefore report, in addition to the exposed share:

5.1 Tiers. Each exposed address is assigned a tier by the balance behind it:

TierBitcoinEthereum
High concentration≥ 100 BTC≥ 1,000 ETH
Material≥ 10 BTC≥ 100 ETH
Moderate≥ 1 BTC≥ 10 ETH
Lowbelow 1 BTCbelow 10 ETH

5.2 Concentration. The number of exposed keys at or above the Material floor, the balance they hold in total, and the balance of the single largest exposed key.

The tiers are a ranking, not an estimate of when any key could be broken. They exist so that the result answers the question critics of headline exposure figures rightly ask: what would actually be worth attacking, and what should be moved first.

6. Policy attestation

A holder MAY publish a key policy. An assessor MAY check it against the snapshot and report whether it holds. Version 1.0 defines two policy rules:

6.1 Maximum per address. No single address holds more than N units. Compared in smallest units (satoshis, wei). A dust tolerance of 0.001 units is applied above the cap, so that a change output of a few hundred satoshis does not register as a breach. The tolerance MUST be stated in the attestation.

6.2 Hash-protected only. Every address holding funds at rest is hash-protected under Section 3. An address holding less than the dust tolerance is treated as emptied: it is reported as informational, not as a breach, since the policy concerns where funds are held.

An attestation MUST list every address checked, the finding per rule, and a single overall verdict: the policy holds at this snapshot, or the number of findings against it. It MUST NOT omit failing addresses.

7. The signed result

Every result an assessor publishes MUST be a single JSON document carrying a signature member of this form:

"signature": {
  "algorithm":        "ML-DSA-65",
  "keyId":            "<hex identifier of the assessor's public key>",
  "publicKey":        "<URL where the public key is published, PEM>",
  "canonicalization": "<the exact rule used to serialize the signed bytes>",
  "signedAt":         "<ISO 8601 UTC>",
  "value":            "<base64 signature>"
}

7.1 Algorithm. ML-DSA-65 as specified in NIST FIPS 204. The point of a post-quantum signature is that the proof of the measurement is itself not breakable by the machine the measurement warns about. An assessor MAY additionally sign with a classical algorithm; it MUST NOT sign only with one.

7.2 Canonicalization. The signed bytes are the UTF-8 encoding of the document with its signature member removed, serialized as canonical JSON: object keys sorted lexicographically, array order preserved, members whose value is undefined omitted, no insignificant whitespace. The exact rule MUST be stated in the canonicalization field so a verifier needs nothing outside the document.

7.3 Key publication. The assessor's public key MUST be published at a stable HTTPS URL on the assessor's own domain, and the keyId MUST identify it uniquely. The private key MUST exist only in the assessor's signing environment; it MUST NOT be distributed in any client-side or downloadable software.

7.4 Document types. This version defines five document types, identified by the type member:

typeContents
latticescan.exposureOne measurement of an ad-hoc address set (Sections 3 to 5) with a migration plan and method statement.
latticescan.attestationA policy attestation (Section 6) over a named holder's published address set, carrying the underlying exposure measurement unsigned inside it; the attestation is signed as a whole.
latticescan.index-snapshotOne holder's entry in an index: totals, exposure by class, tiers, concentration, the largest exposed keys, coverage (Section 10.1), and an anchor (Section 8).
latticescan.quantum-solvency-indexThe ranked set of snapshots served together, signed as one document.
latticescan.evidenceTransport and on-chain findings mapped to named regulatory clauses.

Implementers other than LatticeScan SHOULD use their own reverse-domain prefix for type and keep the field structure.

8. Anchoring

A snapshot that will be relied on later (an index entry, an attestation, a published letter) SHOULD be anchored: its SHA-256, computed over the canonical bytes of the document with any anchor member removed, is committed to the Bitcoin blockchain via OpenTimestamps. The document then carries:

"anchor": {
  "method":          "opentimestamps",
  "sha256":          "<hex>",
  "proof":           "<base64 .ots proof>",
  "stampedAt":       "<ISO 8601 UTC>",
  "bitcoinAttested": true | false,
  "upgradedAt":      "<ISO 8601 UTC, once Bitcoin-attested>"
}

Anchoring proves the document existed at a point in time, on the chain it measures, with no party to trust. A proof is first a calendar promise (bitcoinAttested: false) and upgrades within hours to a Bitcoin block attestation; a verifier MAY accept a calendar promise as provisional and MUST report which state it verified.

8.1 What the anchor covers. The hash is computed over the document as it was committed, with the anchor member removed. Members a server adds when serving (signature, servedAt, quantumSolvencyPct) are not part of the anchored document and MUST be removed before hashing. A verifier MUST hash the document after one JSON round trip, so that numbers are in their shortest round-trip form (RFC 8785 §3.2.2.3).

8.2 Exact balances. Because a sum of floating-point values can differ in its last digit between serializers, every balance in an anchored document MUST be rounded to the smallest unit of its chain (satoshi for BTC, micro-ether for ETH) before the document is committed and anchored. This guarantees the anchored bytes survive any conforming transport.

9. Verification

A conforming verifier, given a document and the assessor's public key, MUST:

  1. Confirm the signature.algorithm is ML-DSA-65 and the keyId matches the supplied key.
  2. Remove the signature member, serialize per the stated canonicalization, and verify the signature over those bytes. Any change of one byte MUST fail.
  3. If an anchor is present, remove it, recompute the SHA-256 of the canonical bytes, and confirm it equals anchor.sha256. It SHOULD verify the OpenTimestamps proof against Bitcoin and report whether it is block-attested.
  4. For an index snapshot or attestation, recompute Total, Exposed balance, Exposed share and Quantum solvency (Section 4) and the tier totals (Section 5) from the per-address items in the document, and confirm they equal the stated values within rounding.

A verifier that can reach a public explorer MAY additionally re-read a sample of addresses and confirm the exposure classification (Section 3); balances will drift with time and MUST NOT be expected to match exactly.

The reference verifier published with this specification (verify-pqs.mjs) performs steps 1 to 4 with no dependencies beyond Node.js 24.

10. Disclosure

Every published result MUST state:

10.1 Coverage. How many addresses the holder published, how many were targeted, how many were read, and, where the holder declared per-address balances, the share of the declared balance the read set covers. An assessor MAY read the largest declared holdings first and stop at a coverage target of no less than 99.5% of declared balance; if it does, the target and the achieved coverage MUST be shown.

10.2 Unreadable addresses. The count of addresses the public data source could not return at snapshot time. These are excluded from every total and MUST NOT be presumed safe.

10.3 Sources. The explorer or node used for each chain, and the holder's publication (URL, date, and block height if declared) from which the address set was taken.

10.4 Limitations. At minimum: that P2SH and P2WSH exposure depends on the redeem script; that bare P2PK is recognised only for known addresses; that contract accounts are not classified; that extended public keys are unsupported; that balances exclude unconfirmed transactions; and that exposure is not a prediction. No CRQC exists at the date of this version; NIST IR 8547 (Initial Public Draft) sets classical elliptic-curve signatures to be deprecated after 2030 and disallowed after 2035, and no production post-quantum signature scheme exists yet for the balances measured.

10.5 Independence. That the assessor holds none of the balances measured and has no financial relationship with the holder beyond any disclosed assessment fee.

11. Conformance

An implementation conforms to this specification if it: classifies exposure exactly per Section 3; computes quantum solvency per Section 4 and tiers per Section 5; where it attests a policy, does so per Section 6; signs per Section 7 and, for relied-on documents, anchors per Section 8; produces documents a Section 9 verifier accepts; and discloses everything in Section 10.

An assessor conforms if, in addition, it satisfies the independence requirement in Section 2 and Section 10.5.

12. Change control

Revisions within 1.x add fields, chains, policy rules or document types; they never change the meaning of a field already defined. A 2.0 will be issued for any breaking change, with at least ninety days' notice. Proposed changes are accepted at hello@latticescan.com and considered in public.

13. References

  • NIST FIPS 204, Module-Lattice-Based Digital Signature Standard (ML-DSA), August 2024.
  • NIST IR 8547 (Initial Public Draft), Transition to Post-Quantum Cryptography Standards, November 2024.
  • BIP-341, Taproot: SegWit version 1 spending rules.
  • BIP-360, Pay-to-Merkle-Root (Draft).
  • EIP-7702, Set EOA account code.
  • OpenTimestamps, opentimestamps.org.
  • RFC 2119, Key words for use in RFCs to indicate requirement levels.
  • RFC 8785, JSON Canonicalization Scheme (the canonicalization in Section 7.2 is a strict subset).

Reference implementation: latticescan.com. Live index: latticescan.com/quantum-solvency. Verifier: latticescan.com/spec/verify-pqs.mjs.

Appendix A. Ownership commitments (informative in 1.0, normative from 1.1)

A commitment is a holder's dated, signed declaration of which post-quantum public key belongs to an existing classical address. It exists so that, once the classical key can be recovered by an adversary, the earliest verified commitment identifies the rightful owner. It moves nothing on-chain.

A.1 Statement

The signed text is exactly, with line breaks and no trailing newline:

LatticeScan Proof of Quantum Solvency registry (pqs-registry-v1)
I control the <chain> address <address>.
My post-quantum public key (ML-DSA-65) has key id <keyId>.
Signed by the key that controls the address above.

<chain> is bitcoin or ethereum; <address> is the address as the holder wrote it (Ethereum addresses are compared case-insensitively); <keyId> is the first 16 hex characters of the SHA-256 of the DER-encoded SubjectPublicKeyInfo of the ML-DSA-65 public key (FIPS 204).

A.2 Proof of control

  • Bitcoin: the legacy signed-message scheme (double-SHA-256 of the length-prefixed magic `Bitcoin Signed Message:

and the statement; 65-byte compact recoverable signature, base64). The verifier recovers the public key and derives P2PKH, P2WPKH and P2SH-P2WPKH addresses from it; the commitment is valid if one equals <address>`.

  • Taproot (P2TR): a BIP-322 "simple" signature by the key path: the base64 witness of the to_sign transaction that spends the to_spend transaction built from the statement, verified as a BIP-341 key-path spend (sighash type SIGHASH_DEFAULT or SIGHASH_ALL) against the x-only key in <address> under BIP-340. Script-path witnesses are not accepted in this revision. signature_scheme is bip322-simple.
  • Ethereum: EIP-191 personal_sign over the statement; the recovered key's keccak-256 address must equal <address>.

A.3 Record and anchor

The canonical record is the JSON object with keys sorted: address, address_type, chain, classical_signature, committed_at, pq_algorithm, pq_key_id, pq_public_key, signature_scheme, statement. Its SHA-256 is anchored per Section 8. A record whose signature fails A.2, whose statement differs from A.1, or whose hash differs from its anchor is invalid and MUST be reported as such, never silently dropped.

A.4 Precedence

Between competing claims for one address, the valid commitment with the earliest Bitcoin-attested anchor takes precedence. A holder may supersede their own commitment by publishing a later one; the earlier one remains on record.

A.5 Sealed commitments, for addresses whose key is not yet revealed

Both proof schemes in A.2 produce a signature from which the signer's public key can be recovered, and the record is public. For an address that has been spent from (Section 3.1) or an account that has sent a transaction (Section 3.2) this reveals nothing new. For an address or account whose key is still hash-protected, publishing the signature would expose it.

A registry MUST read the address on-chain before issuing a statement. It MUST NOT record an ordinary commitment for an address whose key is not yet revealed, and MUST refuse rather than record when it cannot read the chain to tell. For such an address it MAY record a sealed commitment:

  • The holder signs the A.1 statement exactly as in A.2 and keeps the signature. The seal is the SHA-256, in lowercase hex, of the 65 signature bytes (the base64-decoded Bitcoin signature, or the hex-decoded Ethereum r || s || v). Only the seal is sent to the registry.
  • The canonical record is as in A.3 with seal in place of classical_signature, and with signature_scheme set to bitcoin-signmessage-sealed or eip-191-sealed. Its SHA-256 is anchored per Section 8. The record keeps this form after it is opened, so the anchor made when it was sealed remains valid.
  • A sealed commitment cannot be checked by anyone and has no standing until it is opened: the holder publishes the signature, its SHA-256 MUST equal the seal, and it MUST verify under A.2. An opened commitment takes the date of its sealed anchor for the purposes of A.4.
  • A registry MUST present an unopened sealed commitment as unverified. Anyone can file a seal for any address; one that was not computed from a valid signature can never be opened, because the signature would have to be chosen after its hash was fixed.
  • Opening publishes the signature. A registry SHOULD warn a holder who opens a seal for an address whose key is still not revealed on-chain.
  • A sealed commitment is accepted only for address forms whose control a signed message can later prove (P2PKH, P2WPKH, P2SH-P2WPKH, Ethereum accounts).

Appendix B. Extensions in 1.1 (informative in 1.0)

The rules below are implemented by the reference implementation and will become normative in revision 1.1. An assessor MAY report them today; when it does, it MUST label them as this appendix defines them.

B.1 Contract wallets with a readable signer set

Section 3.2 leaves contract accounts unclassified. Where a contract exposes an owner list and a threshold through the Safe interface (getOwners(), selector 0xa0e67e2b; getThreshold(), selector 0xe75235b8), an assessor MAY classify it as a contract wallet: exposed if at least threshold of its owners are themselves exposed under Section 3.2 (an owner that has sent a transaction, or a delegated EOA), otherwise not exposed. An owner that is itself a contract is counted as not exposed and the count of such owners MUST be disclosed. Other contract shapes remain unclassified.

B.2 Disclosure freshness

Each reserve carries the date of the holder's publication the address list was read from. A publication read from a live endpoint at snapshot time is live; otherwise the tier is fresh (7 days or less), recent (35 days or less), stale (180 days or less) or old. A holder that has stopped publishing addresses is withdrawn, and the assessor MUST keep the last address-bearing publication on record and say so.

B.3 Custody as observed

The spending inputs of an address reveal what controls it: a single public key (indistinguishable on-chain from a threshold-signature or MPC wallet), an m-of-n multisig script, a Taproot key path (one signature, possibly aggregated), a Taproot script path, or another script. An address that has never spent has not revealed its control. An assessor reporting custody MUST state how many of the published addresses, and how much of the balance, the observation covers.

B.4 Repeated nonces

For every ECDSA or Schnorr signature read from an address's spends, the r value is recorded against the signing public key. Two signatures by the same key over different transactions with the same r reveal the private key by elementary algebra, with no quantum computer. An assessor that finds such a pair MUST report the address, the transaction ids and the balance affected, and MUST say whether the same key is proven (definite) or only likely, as inside a multisig set where the signer of each signature is not attributed (probable). A clean check is reported as the number of signatures and keys examined and whether the read was truncated.

B.5 Time to migrate

The cost of moving an exposed Bitcoin balance to fresh addresses is estimated as: each unspent output spent once into one fresh output of the same script family, in transactions of at most 400 inputs, using virtual sizes per input type as published in the reference implementation (lib/migration.ts: P2WPKH 68, P2SH-P2WPKH 91, P2PKH 148 vbytes, and so on) plus 31 vbytes per fresh output and 10.5 per transaction, and current fee-rate recommendations, expressed as a fee in BTC and as a share of one day's block space at one million virtual bytes per block. Signing ceremonies, custody procedures and withdrawal queues are excluded and MUST be stated as excluded. For Ethereum the estimate is one transaction per exposed account, with the note that a fresh account reveals its key again at its first send.