Open the KeypairType union and the util-crypto surface to dilithium65/dilithium87 #1
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
First of the
commonchanges; #2, #3 and #4 all build on it. Context, sizes and sources of truth are in quantus/extension#1.The union is closed
packages/util-crypto/src/types.ts:Four string literals, and that type is referenced throughout
keyring,ui-keyring, and bothextension-baseandextension-ui. Adding'dilithium65' | 'dilithium87'is a one-line change that then surfaces every non-exhaustiveswitchin the codebase as a type error — which is the point, and is how the rest of the work gets found. Expect the compiler to be the task list.Two types, not one
Resist collapsing this to a single
'dilithium'. The parameter set is not an implementation detail:Dilithium87=0,Dilithium65=1)…/0'for 87,…/1'for 65)quantus-apps:quantus_sdk/lib/src/extensions/dilithium_scheme_extension.dartis the settled version of these mappings and matchesquantus-cli. Copy its conventions, including the storage namesml-dsa-65/ml-dsa-87, so a wallet exported from one tool imports into the other.New accounts are ML-DSA-65. ML-DSA-87 is legacy — accounts created before the scheme was recorded — so it must be supported but never chosen.
Also in util-crypto
signatureVerifyneeds to recognise the new prefix bytes. Note it cannot recover a signer from a Quantus signature the way it does elsewhere: verification needs the public key, which for Quantus travels inside the signature assig ‖ pk. Verifying against an address means hashing the embedded public key with Poseidon2 and comparing — which is exactly what the runtime'sVerify::verifydoes, so mirror it rather than inventing something.packages/networksat prefix 189, with the genesis hash and token metadata.ui-settingsbuilds its prefix dropdown from this list and the extension'sutil/chains.tsbuilds its known-chain list fromselectableNetworks, so both light up from this one entry.mnemonicToMiniSecretis the Substrate variant and is the default reach in this codebase; using it for Quantus produces keys that are wrong but entirely well-formed, with no error anywhere.Done. Branch
quantus-keypair-types, one commit. Full build green, 3026 tests pass, lint clean.The union, and the worklist it produced
KeypairTypenow carriesdilithium65anddilithium87. As predicted, the compiler enumerated the rest — nine sites across three files, and they are exactly the fourTYPE_*maps #2 describes pluskeyFromPath(#4) andPairFromSeedinkeyring.ts.Rather than stub each one, there is a single narrowing helper,
assertSubstrateType(type, what)inutil-crypto/src/dilithium.ts. Call sites read:It throws rather than degrading. Every one of these paths produces key material or an address, and a wrong-but-plausible value means funds at an address nobody controls, discovered later and unrecoverably. Each call site disappears as #2 and #4 fill in the real arms — grep for
assertSubstrateTypeto see what is left.keyFromPaththrows for the same reason, and it is worth being explicit about why it is not simply a no-op: silently ignoring a derivation path hands the caller the parent key under a child's name.Network registration
Quantus is at prefix 189, appended to the registry rather than merged —
@substrate/ss58-registrydoes not carry it and nothing occupies 185..195, so there is nothing to override, and appending means a registry bump cannot silently drop it.standardAccount: 'ml-dsa'neededKnownSubstratewidened, since upstream's type is a closed union of the curves Substrate ships. It is load-bearing rather than cosmetic: a missingstandardAccountmakestoExpandedmark the networkisIgnored.Data provenance, since guessing any of it would be silent and wrong:
quantus_sdkAppConstants.ss58prefix, andquantus-clichain/node/src/chain_spec.rsandAppConstants— two independent sources0xfb5487c0…626fbaconfig.toml, read from the node withchain_getBlockHash(0)on 2026-09-09, its launch day0xa5aa9e5c…c8d3b4fThat config also records the trap: the staging chain is
0xb0e90ed9f291a9194f79…and telemetry calls it "Quantus Staging Mainnet", so a name-based guess would have picked the wrong chain.Testnets are first-class here
I initially followed upstream's rule and left Heisenberg out —
knownGenesisstates outright that it "does not and should not contain any testnets", anddefaults.spec.tsenforces it. That was wrong for this fork, and @grenade corrected it mid-change: our tools have to reach testnets, and where the forked source is arbitrarily broken for that we fix the fork.So
isIgnoredno longer follows fromisTestnet. But not by deleting the rule: doing that exposed all fifteen of upstream's testnets too, which moved the tail of the sorted list fromZEROtoZERO Alphavilleand would have surprised anyone readingavailableNetworksfor reasons unrelated to Quantus. It is an opt-in allowlist,exposedTestnet, one network at a time.isTestnetis still reported honestly — we stop deciding for the consumer, we do not stop telling them.Result:
Three upstream tests now assert different rules
Rewritten rather than deleted, each saying what this fork believes instead:
genesisHash— the extension'schains.tstakesgenesisHash[0]— so sharing a prefix costs nothing there.Downstream effects worth knowing
selectableNetworks.@quantus/cryptois not wired in yet. #2 is whereTYPE_FROM_SEED/TYPE_ADDRESS/TYPE_SIGNATURE/TYPE_PREFIXstart calling it, and it should read sizes fromext_mldsa_sizesrather than hardcoding 1952/5261/7219 — see quantus/wasm#1.@quantus/cryptowill meet this repo's dual ESM/CJS build at #2. That is the one place it could bite; flagged there.Checked against
main(7f23d0a60). One criterion is unmet, and it is better settled in #6 than here.Met:
KeypairTypeincludesdilithium65anddilithium87(util-crypto/src/types.ts).ml-dsa-65andml-dsa-87.dilithiumPairFromMnemonic, notmnemonicToMiniSecret.Unmet:
signatureVerifydoes not recognise ML-DSA.util-crypto/src/signature/verify.tsis still upstream's. It only accepts 65- and 66-byte signatures and throws "Unknown crypto type" for Quantus ones.dilithiumVerifymirrors the runtime instead (Poseidon2 account check, then verify). It is a separate function because an ML-DSA verification needs a FIPS-204 context, andsignatureVerify(message, signature, address)has nowhere to put one.Plan: resolve this in #6. When the classical schemes go,
signatureVerifyeither takes a context and routes todilithiumVerify, or is removed in favour of it; either way nothing will silently call a verifier that cannot succeed. Closing this issue once that lands.Closing as done, and superseded by #6. This issue asked to open the union to
dilithium65 | dilithium87. #6 went further and closed it to only those two, so the classical types are gone rather than kept alongside.The rest of this issue shipped in the
quantus.2andquantus.3forks:…/0'for 87 and…/1'for 65Now on
mainat0480bfcbc.