Skip to content

Pure-Solidity ML-DSA-65 verifier: run PQValidatorModule on any EVM chain #19

Description

@shaibuafeez

Hi MultiVM team 👋

I built a pure-Solidity implementation of IMLDSAVerifier so PQValidatorModule can run on EVM chains that don't have Arbitrum Stylus (Base, Optimism, Polygon, Ethereum L2s, etc.).

It's a true drop-in

  • Byte-identical IMLDSAVerifier interface (verify(bytes, bytes32, bytes) → bool), so the verifier address swaps in with zero changes to PQValidatorModule (it already takes the verifier via constructor).
  • Implements ML-DSA-65 (FIPS-204) fully in Solidity: hand-rolled Keccak-f[1600] → SHAKE-128/256, the NTT, and FIPS-204 Algorithm 8.

Proven, not asserted

  • Cross-validated against the same @noble/post-quantum vector your Rust Stylus verifier uses (js-noble-vectors.json) — JS + Rust + Solidity all agree on one vector.
  • I drove your unmodified PQValidatorModule against it with that vector: a real ML-DSA UserOp validates end-to-end through validateUserOp, no mocks. (Your test suite currently vm.mockCalls the verifier, since the Stylus verifier can't execute inside the EVM — this runs the validation pipeline against real on-chain ML-DSA math.)
  • Live on Base Sepolia: 0xe42C6eF5b71834930faC33780bE730F7112a3a6B — returns true for a real signature, false for a wrong message.
  • 72 tests (Solidity + TS), CI, threat model, deploy scripts.

Honest tradeoffs

  • Full verify is ~163M gas: fine for L2s and free off-chain via eth_call, too heavy for an L1 transaction.
  • There's also an optimistic / fraud-proof path (submit ~295K), but that one is a documented PoC (step-linkage still in progress), so the full verifier is the path for anything real. Unaudited — flagging that clearly.

Repo

https://github.com/shaibuafeez/pq-mldsa-verifier

This also lines up with EIP-8141's model (accounts verifying PQ signatures by calling a verifier contract, no protocol change required).

Happy to open a PR adding the verifier + the real integration test to this repo if that's useful — wanted to check the preferred shape first rather than drop a large unsolicited diff.

Thanks for the post-quantum work; the clean interface made this straightforward to build against.

@shaibuafeez

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions