7 Commits

Author SHA1 Message Date
f61cfd4ed5 chore: let the tier-1 harness send a configurable amount
`QUANTUS_AMOUNT`, in plancks. The hardcoded 0.001 HEI was right for proving a
transfer lands and useless for funding an account to sign *from*: one ML-DSA
extrinsic costs about 0.01 HEI in fees, so an account funded with the default
could not pay for its own first transaction.

Needed for the manual verification pass, which has to fund a fresh account
created in the extension — the only funded dev account (crystal_alice) is
ML-DSA-87, and the import-seed screen always imports as ML-DSA-65, so the
funded account cannot be brought in through the UI at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-16 11:47:24 +03:00
4f759c4094 test: the tier-1 cases that prove the plumbing, not the happy path
`cases.mjs`, against Heisenberg at spec 148. All five pass:

  PASS  the empty signing context is rejected — 1010: bad signature
  PASS  a payload over 256 bytes is hashed before signing — 279 -> blake2 32
  PASS  a batch_all over 256 bytes dispatches — block 1050692, funded 50 HEI
  PASS  an ML-DSA-65 account signs and is accepted — block 1050693
  PASS  an account exported to JSON and re-imported still signs — block 1050694

The first is the one worth having. Every other failure mode here surfaces as a
decode error somewhere; a signature under the wrong FIPS 204 context is
well-formed, verifies against its own key, and is refused only by the runtime —
so nothing local can tell it from a correct one.

**`submitAndInclude` does not watch the nonce**, and the reason is a mistake this
harness made first. `system_accountNextIndex` counts pending pool transactions,
so it advances the moment a transaction is *accepted* — before it has done
anything. Waiting on it and then reading a balance shows the state from before
the dispatch. Two `batch_all` calls were recorded as reverted on that basis when
both had funded their target with exactly what they were asked for; the accounts
were sitting there holding 50 HEI each while the run said they held nothing. It
now looks for the extrinsic's own bytes in the blocks that arrive, which also
exercises the decoder against real chain data — every Quantus block opens with a
bare-v5 timestamp inherent that @polkadot/api cannot read.

`submit.mjs` moves onto the same shared `chain.mjs` and now *asserts* the
recipient's balance moved rather than printing it, which it could not do before:
reading a balance needs storage addressing, which @quantus/codec only gained for
this (quantus/wasm#4).

Refs #7

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
2026-09-15 15:15:51 +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
Tarik Gul
a1c87fa383 Set headers to 2026 (#1609) 2026-03-12 19:27:39 +02:00
Tarik Gul
73cedbb824 2025 (#1493)
* 2025

* Enable ci execution of diff

* Update extra files

* fix webpack config header
2025-01-03 02:15:31 +02:00
Tarik Gul
1fab0828a7 Add CI script to check for diffs in src vs build for store release (#1429)
* Add CI script for diffs

* Cleanup

* Update github actions

* simplify

* echo

* cleanup

* fix

* "$RUNNER_OS" == "Linux"

* hack
2024-07-31 20:16:24 +03:00