VERIFY_MLDSA65
Frost post-quantum Signature verification
Verify an ML-DSA-65 (FIPS 204) post-quantum signature.
| Address | 0x0000000000000000000000000000000000000014 |
|---|---|
| Name | VERIFY_MLDSA65 |
| Minimum gas | 16,000 |
| Input | 5293 bytes (exact) |
| Output | 32 bytes, always |
| Available since | Osaka (Frost) |
Input
| Offset | Length | Field | Description |
|---|---|---|---|
| 0 | 32 | msg | The message. For a frame transaction this is the 32-byte sig-hash. |
| 32 | 3309 | signature | Standard FIPS 204 ML-DSA-65 signature encoding. |
| 3341 | 1952 | publicKey | Standard FIPS 204 ML-DSA-65 public key encoding. |
Output
A 32-byte word: 1 if the signature is valid, 0 otherwise.
Gas
16,000 (flat, regardless of input)
Priced at ecrecover throughput parity on validator-class hardware: gas = t_verify × (EcrecoverGas / t_ecrecover). Measured 177.7 µs per verify against ecrecover at 33.7 µs / 3000 gas (≈89 Mgas/s) on AMD EPYC Milan, giving parity at 15.9k, rounded up to 16,000 for margin. CONSENSUS-CRITICAL: changing this price requires a fresh chain.
Failure behaviour
- Returns the 32-byte zero word — never empty data, and never an error — when the signature is invalid, when the public key is not canonically encoded, or when the input length is not exactly the expected size.
- Input is not right-padded. A short or long input is a deterministic reject, not a truncation.
- Gas is charged in full on every call, valid or not.
Notes
- Verification is pure ML-DSA with an empty context string, per FIPS 204 — not the pre-hash variant. Any conformant signer interoperates.
- Apple CryptoKit on iOS/macOS supports ML-DSA-65 and ML-DSA-87, so a key generated in a Secure Enclave can sign a Frost transaction directly. This is the parameter set the rotatable v2 account uses for exactly that reason.
- Soundness rests on the caller binding the exact public key bytes. Frost account contracts embed the key in their own code or behind a storage pointer, so the caller cannot be tricked into verifying against an attacker-supplied key.
- Conformance is checked against NIST ACVP signature-verification vectors, differentially against an independent Rust implementation, and against golden vectors produced by a real Apple Secure Enclave key.
Worked example
Generate a signature with any FIPS 204 library and check it against the live chain. This uses dilithium-py, an implementation unrelated to the one the chain runs — if it interoperates, so will yours.
from dilithium_py.ml_dsa import ML_DSA_65
msg = bytes.fromhex("11" * 32) # 32 bytespk, sk = ML_DSA_65.keygen() # pk 1952 Bsig = ML_DSA_65.sign(sk, msg) # sig 3309 B
payload = (msg + sig + pk).hex() # 5293 bytes totalprint(len(msg) + len(sig) + len(pk)) # 5293Call the precompile with that payload via eth_call to 0x…0014. A valid signature returns 0x00…01; flip a single bit anywhere in the signature and it returns 0x00…00. Truncate the input and it still returns 0x00…00 — never empty.
Calling it
Precompiles have no ABI and no function selector: the input is raw bytes at the exact offsets above. Call with staticcall.
// VERIFY_MLDSA65 at 0x14(bool ok, bytes memory out) = address(0x14).staticcall(input);require(ok, "VERIFY_MLDSA65 reverted");