hip-0104

HIP-104: Contract Auth via Z-Chain Proof. Status Draft. Hanzo's own standard — read this before implementing against it.

HIP-0104: Contract Auth via Z-Chain Proof

Abstract

HIP-0104 defines the contract-side auth surface for verifying PQ signatures and Z-Chain auth proofs from within EVM-compatible contracts under the strict-PQ profile. Four precompiles are pinned in the EVM 0x0301..0x0304 block:

Each precompile returns (bool ok, bytes payload) per the canonical Go function-pointer interface in luxfi/consensus/protocol/auth/precompile.go. Contracts call these precompiles to gate sensitive functions on PQ identity without re-implementing the verifier.

Specification

Canonical references:

and the four PrecompileAddrPQVerify* constants.

lives here, not in consensus).

Precompile 0x0301 — pq_verify_mldsa65

input: bytes
    mldsa_pubkey          []byte
    transcript            [48]byte
    mldsa_signature       []byte

output: (bool ok, bytes payload)
    ok      = true on verify success
    payload empty on success, error-detail bytes on failure

Verifies a FIPS 204 ML-DSA-65 signature. Used by HIP-0087 permits and HIP-0086 envelope introspection where the contract already has the pubkey and signature locally.

Precompile 0x0302 — pq_verify_mldsa87

Same shape as 0x0301; verifies a FIPS 204 ML-DSA-87 signature (NIST PQ Cat 5). Used for high-value contract authorisation (treasury, governance roots).

Precompile 0x0303 — pq_verify_slh_dsa

Same shape as 0x0301; verifies a FIPS 205 SLH-DSA signature. The hash-based backstop for account recovery / breakglass operations.

Precompile 0x0304 — pq_verify_z_auth_proof

input: bytes
    format_byte           uint8    = 0x10  // STARK_FRI_SHA3_PQ
    proof_blob            []byte   // STARK proof bytes per HIP-0078
    public_inputs {
        zchain_root           [48]byte
        account_id            [48]byte
        action_root           [48]byte
        nonce                 uint64
    }

output: (bool ok, bytes payload)
    ok      = true on verify success
    payload encodes account_id (48 B), verified_at_height (u64), flags
            (u64) on success

The heavy verifier suitable for cross-domain authentication: a wallet that lives on Z-Chain proving an action to a contract on the EVM side.

Gas-schedule numbers live in the coreth wiring (core/vm/contracts_pq.go) and are calibrated against the canonical verifier benchmark in luxfi/consensus. Operators MUST re-benchmark on each major release to prevent under-pricing attacks.

Contracts call these via standard EVM staticcall:

function verifyPQ(bytes calldata proof, bytes32 expectedAccount)
    external view returns (bool)
{
    (bool ok, bytes memory out) = address(0x0304).staticcall(proof);
    if (!ok || out.length < 48) return false;
    bytes32 acct;
    assembly { acct := mload(add(out, 0x20)) }
    return acct == expectedAccount;
}

Backwards compatibility

None. Strict-PQ chains reject ecrecover calls at the consensus boundary; contracts that used ecrecover MUST migrate to one of 0x0301..0x0304. Permissive profiles MAY keep ecrecover active during transition.

Reference implementation

luxfi/consensus/protocol/auth/precompile.go. EVM binding: luxfi/coreth/core/vm/contracts_pq.go. Test vectors: luxfi/coreth/core/vm/testdata/precompile_pq_v1.json.

Security considerations

The precompile returns the authenticated AccountID; contracts MUST compare against an expected value, not trust the precompile's success flag alone for authorisation. Gas schedule is calibrated against the luxfi reference verifier benchmark (1.2 M gas for STARK verify, 0.8 M gas for direct ML-DSA-65 verify); operators MUST re-benchmark on each major release to prevent under-pricing attacks. The 48-byte AccountID format prevents collision attacks against EVM-form 20-byte addresses that would otherwise cause auth-skirting.

References

Copyright

CC0.