Files

5.3 KiB

Warning

Code in this repository is not audited and may contain serious security holes. Use at your own risk.

Safe + Magnetar

This repository brings Magnetar post-quantum threshold signatures to the Safe smart account on the Lux C-Chain EVM. Magnetar is a public-DKG MPC threshold hash-based signature, FIPS 205 byte-equal (FIPS 205 (SLH-DSA) — a Magnetar MPC ceremony signature is byte-equal to a single-party SLH-DSA signature).

Unlike safe-frost — which ships a self-contained secp256k1 Schnorr verifier in Solidity — Magnetar verification is performed by the on-chain magnetarVerify precompile at 0x012207 (part of the Lux LP-4200 unified PQCrypto block). The Solidity in this repository is a thin, decomplected adapter: it frames the precompile's exact calldata, staticcalls it under a strict success-word check, and binds the verified key to a Safe owner.

safe-magnetar supports the same three use cases as safe-frost:

  • as a Safe signer (an EIP-1271 owner — MagnetarSigner)
  • as a Safe co-signer (a transaction guard — SafeMagnetarCoSigner)
  • as an ERC-4337 account (MagnetarAccount)

Precompile

Field Value
Address 0x012207
Verify ABI (raw wire format) mode:uint8(1) ‖ pkLen:uint16(2) ‖ pubkey ‖ msgLen:uint16(2)=32 ‖ message(32) ‖ signature
Domain-separation context lux-evm-precompile-magnetar-v1
Return value 32-byte word: bytes32(1) iff valid, bytes32(0) otherwise (never reverts on a cryptographic false)
Reference implementation github.com/luxfi/precompile/magnetar

The "message" for every Safe use is the 32-byte Safe-transaction hash (or the ERC-4337 userOpHash), which is EIP-712-bound to the Safe address, chain id and nonce — so a signature authorises exactly one Safe / chain / nonce. The precompile additionally binds the domain-separation context above, so a Magnetar signature cannot be replayed against any other precompile in the PQCrypto block.

Important

The precompile reads its calldata as a raw packed layout, not an ABI-encoded function call — there is no 4-byte selector. Magnetar.sol frames the bytes exactly as the Go Run() parses them. Sending an ABI-encoded call (with a selector) would misalign every field.

Contracts

  • contracts/Magnetar.sol — the verifier library. One function, verify(...), that frames a single mode byte (FIPS 205 parameter set; 0x00 = SHA2-128s), the public key / signature and the 32-byte hash into the precompile's wire format and strict-staticcalls 0x012207. Holds no state and enforces no policy (decomplected: dispatch only).
  • contracts/SafeMagnetarSigner.sol — an EIP-1271 validator that makes a single Magnetar key a first-class Safe owner. Commit the key once via addOwnerWithThreshold; the Safe's checkNSignatures validates this owner's slice through the v = 0 contract-signature path. M-of-N thresholds mixing ECDSA, secp256r1 and Magnetar owners work natively.
  • contracts/SafeMagnetarCoSigner.sol — a Safe transaction guard requiring every execTransaction to additionally carry a valid Magnetar co-signature.
  • contracts/MagnetarAccount.sol — an ERC-4337 account authorised by a committed Magnetar key.

Key binding

Magnetar signatures are a standard FIPS 205 SLH-DSA signature (7856 bytes for SHA2-128s). An owner / account therefore commits a keccak256 of the FIPS 205 group public key at construction (immutable, so the validators are stateless view functions and side-step the Safe's state-changing-validator guard). At verify time the supplied key is re-hashed against the commitment, closing the "attacker supplies an arbitrary key" attack via second-preimage resistance.

Signature payload

The contract-signature / userOp signature payload is abi.encode(bytes pubkey, bytes magnetarSignature) — a standard ABI (bytes, bytes) tuple of the full Magnetar public key and signature.

Testing

forge test

The tests prove verify() against a real known-answer-test (KAT) vector — 7856-byte SLH-DSA-SHA2-128s signature produced by a genuine Magnetar ceremony. The KAT was generated by lux/precompile/cmd/safekatdump and independently proven to verify against the real Go precompile by lux/precompile/cmd/safekatverify, so the bytes the Solidity frames are known to be accepted on-chain.

Because the precompile is a luxd-native Go precompile (absent from a stock Foundry EVM), the tests install a mock at 0x012207 via vm.etch whose sole job is to assert calldata-framing conformance: it returns the success word iff the incoming calldata is byte-for-byte identical to the proven KAT input. Any drift in the wire format (field order, length-prefix widths, an accidental ABI selector) changes the calldata and the test fails. The headline test is Magnetar.t.sol::test_CalldataFramingMatchesPrecompileABI.

The fixture lives at tests/kat.json.

Integration with the Lux ecosystem

This package is part of the Lux blockchain ecosystem.