1331 Commits

Author SHA1 Message Date
29033e8bb8 feat: post-quantum only; no derivation, no MetaMask shim, no Ethereum addresses
Some checks failed
Lock Threads / lock (push) Has been cancelled
Moves to the quantus.3 forks of keyring, util-crypto, networks and ui-keyring,
which hold only ML-DSA keys and have deleted the classical primitives
(quantus/common#6). What in the extension depended on them goes too:

- the derive-from-parent flow (Popup/Derive, pri(derivation.*), the
  AccountsContext master, the menu entries): ML-DSA pairs have no //hard/soft
  derivation, and Quantus derives every account from the mnemonic instead
- extension-compat-metamask, which injected Ethereum accounts
- the Ethereum branches in Address and the //m/44'/60' suri suffix
- canDerive and nextDerivationPath, now unused

Restoring a JSON backup of a classical key shows the keyring's reason ("ed25519
keys are not quantum-safe and cannot be held here...") rather than "Invalid
Json file", for single and batch files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-16 21:28:01 +03:00
a4e19a4b1b feat: wormhole balances in the background, from chain state and a chosen observer
A wormhole address's System.Account balance is a receipt total. An exit
credits the exit account and records a nullifier, and nothing debits the
address. So what a wallet account can still spend is worked out deposit by
deposit, in pri(wallets.wormholeBalance):

- Transfer counts come from the chain's Wormhole.TransferCount, read from the
  balance node.
- Deposits come from a blackbeard observer
  (/v1/chains/{chain}/wormhole/deposits, blackbeard/observer#22), matched by
  genesis, never by name. Anything the chain counts that the observer didn't
  return is reported as missing, never folded into a smaller balance.
- Spent status comes from Wormhole.UsedNullifiers, read in whole buckets by
  the first byte of blake2_128(nullifier), at least 16 buckets with random
  decoys, in a shuffled order. A lookup by key would hand the node the
  nullifier, and exits publish theirs.
- A deposit whose nullifier was never computed is reported as unchecked.

Nullifiers are precomputed while the password is available and stored per
wallet account: 128 transfer counts for each of the 40 addresses when a phrase
wallet or account is made. pri(wallets.wormholeUnlock) extends only the
addresses whose deposits outgrew what is stored, to their count plus 128.
Forgetting a wallet removes its nullifiers for every account index along with
the secret and the pairs. They're kept under their own prefix, and the
manifests ask for unlimitedStorage, since a busy mining address alone runs to
megabytes.

@quantus/crypto 0.3.0 provides wormholeNullifiers, pinned there to the chain's
circuit crate.

Specs: a wallet stores a full window whose bytes match a direct derivation; a
raw seed stores none; unlock extends only the addresses past their window and
keeps what was there; forget removes every account's nullifiers.

Live against mainnet and the deployed observer:
- a spent nullifier taken from a public ProofVerified event is found in its
  bucket (240 entries, 158 ms);
- for a mining address the chain counts 41,849 transfers, the observer
  returned 24,702, and 17,147 are reported missing. That is blackbeard/
  observer#23's gap, now being rescanned, surfacing as intended.

Part of quantus/extension#14.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-16 18:22:23 +03:00
c378b36a3a feat: wallets in the popup, one card per secret with a tab per account
The account list shows each wallet as one card. Tabs switch between the
ML-DSA-65, ML-DSA-87 and wormhole accounts its secret unlocks, and a selector
switches between account indices when there is more than one. Pairs a wallet
owns aren't listed again on their own; accounts from JSON or quantus-cli
imports, and those made before wallets, still are. The last tab and index
shown are remembered per wallet in browser storage.

Import and create no longer ask for a key type, and import no longer takes a
derivation path. A recovery phrase or 0x seed previews all three account-0
addresses before anything is saved, so a user can recognise their wallet by
whichever address they know it by. This was the gap behind a wormhole account
"importing" as the wrong address. More accounts come from the wallet's menu
(Add account, password required), which replaces derive-from-parent.

- The wallet menu offers rename, add account, export of the selected signing
  account, and forget wallet, which removes the stored secret and every pair.
- The wormhole tab shows the receive address and says plainly that balance
  and sending aren't in the extension yet (#13, stage B of #14).
- Address no longer shows its hide-from-dapps toggle for an address with no
  keyring account behind it, where it could only fail.
- The name/password step can now show why creating failed, such as a secret
  that is already a wallet.

Verified in Firefox against the built extension:
- The chain node's TEST_MNEMONIC previews its TEST_WORMHOLE_ADDRESS (qzpWh4…)
  and TEST_ADDRESS_HD_0 (qzm5QC…, ML-DSA-87). The latter matches a vector
  these tests never used.
- The zero seed previews crystal_alice with no wormhole tab, and importing it
  twice is refused.
- Storage holds the secret only encrypted.
- Add account refuses a wrong password, then adds account 1, and the selector
  switches to it.
- Forget removes the wallet record and both of its keyring pairs, and leaves
  the other wallet alone.

Part of quantus/extension#14.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-16 16:13:18 +03:00
22eb1c25c8 feat: wallets in the background, one secret shown as every account it unlocks
A wallet holds one secret: a recovery phrase, or a raw 32-byte seed for the
dev accounts. The secret is encrypted with the wallet password (the keyring's
scrypt + xsalsa20-poly1305 JSON encryption) under its own storage prefix,
quantus:wallet. From it the wallet derives, for each account index:

- an ML-DSA-65 and an ML-DSA-87 signing account, at the paths quantus-cli and
  the mobile wallet use. These are ordinary keyring pairs tagged with walletId
  and accountIndex, so signing, dapp injection and JSON export don't change.
  They're named "<wallet> (ML-DSA-65)" and so on, so a dapp's account list can
  tell them apart.
- 20 receive and 20 change wormhole addresses (m/44'/189189189'/n'/b'/k'),
  derived by @quantus/crypto 0.2.0. Only addresses leave WASM. They're derived
  while the password is available, so a balance can later be shown without
  asking for it.

New messages: pri(wallets.subscribe, preview, create, addAccount, rename,
forget).
- The same secret can't become two wallets, because forgetting one would take
  the other's pairs with it.
- A raw seed has one account and no wormhole addresses: wormhole derivation
  starts from the 64-byte BIP39 seed.
- Adding an account needs the password; a wrong one is refused before any
  pair is made.

@quantus/crypto is resolved to 0.2.0 everywhere, so the common forks and the
extension share one copy of the WASM instead of bundling two.

The specs use independent vectors only:
- the chain node's TEST_WORMHOLE_ADDRESS, also pinned by the mobile wallet's
  SDK;
- quantus-cli's ML-DSA-65 and ML-DSA-87 accounts for its dev phrase;
- crystal_alice for the zero seed.

Part of quantus/extension#14.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-16 16:04:13 +03:00
a29a68d48a feat: add the tier 2 harness dapp
quantus/extension#7 tier 2: a page that asks the *extension* to sign, then
assembles and submits the extrinsic itself. `yarn tier2` bundles it; the README
has the run steps and where to get a funded dev account.

Every intermediate value is printed — the call, the extra, the payload that was
signed, the signature that came back, the assembled extrinsic, and the round trip
decoded back out of it. That display is the debugger when a node rejects
something, and it is exactly what a real app hides behind a success toast.

Nothing in it uses @polkadot/api. @quantus/codec produces every byte and the
WebSocket is a JSON-RPC client that decodes nothing.

Also extracts decodeMethod out of Extrinsic.tsx into util/, with tests. It moved
from `registry.createType('Call', data)` to the runtime in the previous commit and
was the one changed path with no coverage — `yarn test` skips .spec.tsx, so as a
component method it would have stayed that way. The tests pin the three cases
that matter more than the happy path: metadata from a different spec version
decodes a call into something plausible and wrong and must be refused; a chain
with no runtime renders hex; and bytes that are not a call must not throw, since
an exception there lands between somebody and their funds.

Verified so far in a browser: the page loads under Firefox with the extension
installed, and @quantus/codec parses Heisenberg's 101,493-byte metadata client
side and reports extrinsic v4 with all twelve signed extensions. The injected
signing round trip is **not** yet verified end to end — driving the extension's
own UI needs a privileged browsing context this tooling cannot script — so the
popup half of tier 2 still wants a human at a keyboard. The background half is
covered by Extension.spec.ts, which runs the full pub(extrinsic.sign) ->
pri(signing.approve.password) path.

Refs #7

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-15 14:57:53 +03:00
be69149957 feat: build and decode extrinsics with @quantus/codec, and prove tier 1
The tier-1 harness now signs a real transfer and gets it into a real block:

    accepted    0xd25bb081e4a87da6a2af79f038f6991e914d732322393096128894c0d644e806
    included    block 1050475 index 1
    decoded     {"Balances":{"transfer_keep_alive":{…,"value":"1000000000"}}}
    nonce       2761 -> 2762

on Heisenberg at spec 148, signed by the forked keyring under QUANTUS_EXTRINSIC.
First end-to-end proof that the fork can spend.

It got there by dropping @polkadot/api entirely. Every byte on the wire is now
produced by @quantus/codec from metadata the node generated by running
Metadata_metadata against the runtime WASM; WsProvider appears only as a
JSON-RPC transport. The api route failed three times over — a 2048-byte cap on
fixed arrays, a preamble byte read as a version so that no Quantus block decodes
at all, and signed extensions *assumed* to be empty rather than read — and
quantus/api#1 has the evidence. The first submission through it was rejected as
`1010: Invalid Transaction: Transaction has a bad signature`; through the codec
the same transfer is accepted.

The harness reads the block back with the same runtime that built the extrinsic,
which also exercises what @polkadot/api cannot do at all: index 0 of every
Quantus block is a timestamp inherent whose preamble byte is 0x05 — bare,
version 5 — beside this signed extrinsic's 0x84, while the metadata declares
version 4.

Also moves the post-quantum chain gate here from @polkadot/networks. Gating the
library list broke @polkadot/api at import, because @polkadot/types-known throws
for a chain it holds upgrade history for but cannot find. What a wallet offers
is the wallet's decision, and this is the wallet.

Refs #7, quantus/api#1, quantus/wasm#3

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-15 14:16:34 +03:00
rob thijssen
bff8eec1e6 build: consume the Quantus forks from the Gitea registry
Wires this repo to the four forked @polkadot packages and @quantus/crypto, and
the extension now builds with post-quantum crypto in it.

Two mechanisms, for the reason established in quantus/ui: @quantus is routed as a
whole scope in .yarnrc.yml, which is safe because we own every name in it, while
the forked @polkadot packages are pinned individually to tarball URLs in
resolutions. Routing the @polkadot scope would send yarn hunting for the dozens
of packages in it that are not forked — api, types, rpc-provider, dev, x-*,
hw-ledger and more — all of which must keep coming from npm.

The forks carry -quantus.N versions, so a resolution left out fails with "No
candidates found" rather than being quietly satisfied from npm by the unforked
package.

buildHierarchy.spec.ts is rewritten rather than deleted. compareByNetwork
resolves a genesis hash through getNetworkMap(), which is built from
selectableNetworks — and this fork only offers chains whose accounts are
post-quantum, so Polkadot and Kusama are no longer in it. Both fixtures resolved
to '' and compared equal, meaning the network-ordering tests passed without
ordering anything. They now use Quantus and Heisenberg, whose display names sort
in the same direction the originals did, so the fixtures keep their shape.

Verified in the built artifacts rather than assumed: background.js carries
QUANTUS_EXTRINSIC, the dilithium keypair types, ext_poseidon_hash, ext_mldsa_sign
and the base64 wasm payload; extension.js carries the Quantus network entry, QTC
and the mainnet genesis hash. The split is right — signing lives in the service
worker, the UI only needs to render addresses.

65 tests pass; `yarn build:chrome` completes.

Refs quantus/extension#2

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-15 11:53:36 +03:00
Alberto Nicolas Penayo
d7c9ce2145 0.64.0 (#1625)
Some checks failed
Lock Threads / lock (push) Has been cancelled
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 12:43:58 -03:00
github-actions[bot]
91f0b7194e [CI Skip] bump/beta 0.63.2-1-x
skip-checks: true
2026-07-31 15:14:13 +00:00
github-actions[bot]
58093e3a50 [CI Skip] bump/beta 0.63.2-0-x
skip-checks: true
2026-07-30 18:24:37 +00:00
github-actions[bot]
987784c81e [CI Skip] release/stable 0.63.1
skip-checks: true
2026-03-31 01:56:50 +00:00
Tarik Gul
b54ff921b4 0.63.1 (#1615)
* 0.63.1

* Update Changelog
2026-03-31 04:54:01 +03:00
github-actions[bot]
77a7f59489 [CI Skip] bump/beta 0.62.7-5-x
skip-checks: true
2026-03-30 20:47:28 +00:00
github-actions[bot]
2198396137 [CI Skip] bump/beta 0.62.7-4-x
skip-checks: true
2026-03-26 22:20:12 +00:00
Tarik Gul
ec26b80ab1 Bump pjs deps (#1614) 2026-03-27 00:17:18 +02:00
github-actions[bot]
81714b2cf5 [CI Skip] bump/beta 0.62.7-3-x
skip-checks: true
2026-03-26 19:48:35 +00:00
github-actions[bot]
a9bbe3efb2 [CI Skip] bump/beta 0.62.7-2-x
skip-checks: true
2026-03-26 19:33:03 +00:00
github-actions[bot]
73fef6ffbf [CI Skip] bump/beta 0.62.7-1-x
skip-checks: true
2026-03-12 18:51:34 +00:00
github-actions[bot]
67faeb9f69 [CI Skip] bump/beta 0.62.7-0-x
skip-checks: true
2026-03-12 17:30:41 +00:00
github-actions[bot]
9288eb12b1 [CI Skip] release/stable 0.62.6
skip-checks: true
2025-11-26 07:49:46 +00:00
Arjun Porwal
4622b6c13d 0.62.6 (#1604) 2025-11-26 13:16:54 +05:30
github-actions[bot]
d1fb181e27 [CI Skip] bump/beta 0.62.6-0-x
skip-checks: true
2025-11-26 07:38:34 +00:00
Arjun Porwal
d6473a5c7a chore: bump deps (#1603) 2025-11-26 13:05:38 +05:30
github-actions[bot]
eeeb336417 [CI Skip] release/stable 0.62.5
skip-checks: true
2025-11-17 13:38:00 +00:00
Arjun Porwal
3f5fa9b043 0.62.5 (#1602) 2025-11-17 08:35:06 -05:00
github-actions[bot]
f64d18828d [CI Skip] bump/beta 0.62.5-0-x
skip-checks: true
2025-11-14 16:42:44 +00:00
github-actions[bot]
f32f39e371 [CI Skip] release/stable 0.62.4
skip-checks: true
2025-11-13 04:48:02 +00:00
Arjun Porwal
81347b8894 0.63.4 (#1600) 2025-11-13 10:15:10 +05:30
github-actions[bot]
e17b36371b [CI Skip] bump/beta 0.62.4-0-x
skip-checks: true
2025-11-13 04:33:12 +00:00
Arjun Porwal
7103f3e809 chore: bump dependecies (#1599) 2025-11-13 10:00:11 +05:30
github-actions[bot]
132381008e [CI Skip] release/stable 0.62.3
skip-checks: true
2025-10-23 12:48:59 +00:00
Arjun Porwal
3822c6d2d5 0.62.3 (#1597) 2025-10-23 18:15:27 +05:30
github-actions[bot]
ed97de2f85 [CI Skip] bump/beta 0.62.3-0-x
skip-checks: true
2025-10-23 12:08:01 +00:00
Arjun Porwal
461c7d7dbe chore: bump deps (#1596) 2025-10-23 09:04:23 -03:00
github-actions[bot]
0cfe2d5c9d [CI Skip] release/stable 0.62.2
skip-checks: true
2025-10-09 04:12:53 +00:00
Arjun Porwal
87544c7864 0.62.2 (#1594) 2025-10-09 09:39:29 +05:30
github-actions[bot]
211fee8528 [CI Skip] bump/beta 0.62.2-0-x
skip-checks: true
2025-10-08 14:33:42 +00:00
github-actions[bot]
18274fe136 [CI Skip] release/stable 0.62.1
skip-checks: true
2025-09-25 12:24:12 +00:00
Arjun Porwal
ec1fdcdd76 0.62.1 (#1592) 2025-09-25 17:50:43 +05:30
github-actions[bot]
3093db76fe [CI Skip] bump/beta 0.61.8-1-x
skip-checks: true
2025-09-25 03:19:41 +00:00
Arjun Porwal
fae45e014b chore: bump deps (#1591) 2025-09-25 08:46:06 +05:30
github-actions[bot]
4f1192528d [CI Skip] bump/beta 0.61.8-0-x
skip-checks: true
2025-09-17 15:55:06 +00:00
github-actions[bot]
592b63f60f [CI Skip] release/stable 0.61.7
skip-checks: true
2025-08-28 17:07:58 +00:00
Arjun Porwal
8a13f3ae75 0.61.7 (#1589)
* 0.61.7

* 0.61.7
2025-08-28 22:34:32 +05:30
github-actions[bot]
997e25554f [CI Skip] bump/beta 0.61.7-0-x
skip-checks: true
2025-08-28 12:06:30 +00:00
Arjun Porwal
77c112b9f6 chore: bump deps (#1588) 2025-08-28 17:33:03 +05:30
github-actions[bot]
e900463d52 [CI Skip] release/stable 0.61.6
skip-checks: true
2025-08-14 18:06:27 +00:00
Arjun Porwal
cf0aba0cc3 0.61.6 (#1583) 2025-08-14 15:02:58 -03:00
github-actions[bot]
216cc4c2d2 [CI Skip] bump/beta 0.61.6-0-x
skip-checks: true
2025-08-14 14:08:17 +00:00
Arjun Porwal
32c37ba096 chore: bump deps (#1582) 2025-08-14 19:34:45 +05:30