AI Bridge Precompile (aivmbridge) - C->A Inference On-Ramp at 0x0300...0004
The C-Chain EVM precompile that on-ramps a contract to A-Chain large-model inference via two consensus-safe ops - SubmitInferenceIntent (Pattern A) and VerifyInferenceReceipt (Pattern B)
LP-5301: AI Bridge Precompile (aivmbridge) — C→A Inference On-Ramp at 0x0300…0004
Companion to LP-5300. LP-5300 specifies the Thinking Chains protocol (A-Chain quorum settlement, the bridge law, the wire spec, subsampled cognitive consensus). This LP specifies the C-Chain EVM precompile that is the entry point for Patterns A and B — exactly as LP-9010 / LP-9999 spec the DEX precompile against the D-Chain VM (LP-032).
Abstract
LP-5301 specifies the AI Bridge Precompile (aivmbridge) at address
0x0300000000000000000000000000000000000004, the C-Chain entry point that lets a
smart contract request inference from a large model running on the A-Chain
(AIVM) and later consume the result with native-opcode fork-safety. It exposes
exactly two ops, both consensus-safe by construction:
submitInferenceIntent(Pattern A) — write a committed C-side outbox intent and return a deterministicintent_id. No A query, no A mutation.verifyInferenceReceipt(Pattern B) — verify a committed A receipt + Merkle proof against areceipt_rootC already holds committed, match a pending intent, and return the canonical output. No A query.
The precompile is a leaf library: it never reaches up into the node chain
manager, and the C-Chain StateRoot is derivable without observing any live A
process — the bridge law of LP-5300.
It is the AI analogue of the DEX on-ramp, and its AChainClient is the exact
shape of precompile/dex/dchain_client.go (DChainClient), adapted to inference
and made proof/receipt-oriented (never a live-query surface).
Mining scope (normative)
This precompile verifies cognitive task receipts; it does not make C-Chain a Proof-of-AI authority. Under LP-5200, only PoAI consensus on A-Chain may admit useful mining work, validate that its bound computation was actually performed, consume the global work nullifier, and authorize subsidy. Z-Chain batches and settles that finalized transition through the activated Plonky3-derived P3Q proof path; it does not admit or validate raw mining work. Lux Quasar and the activated PQ validator policy secure the corresponding A-Chain and Z-Chain roots. If a receipt carries a mining payout, the destination verifies that Z-settled, finalized A-Chain receipt and releases only its bound amount to its bound recipient.
Neither this precompile nor the 0x0300…0000 mining slot may accept a raw miner
signature, TEE quote, Freivalds opening, quorum output, or destination-local
spent key as an alternative to A-Chain finality. Any legacy direct
verify-and-mint selector MUST remain disabled or be replaced by receipt-only
settlement before activation.
Until the exact PoAI AIR, A-to-Z state-transition binding, recursive policy, and P3Q verifier are audited and activated, this precompile MAY consume a directly authenticated A-Chain receipt root but MUST NOT describe that path as succinct Z-Chain proof settlement.
Motivation
A contract cannot call an inference server during Run() without forking
consensus (see LP-5300
§Motivation). The DEX faced the identical problem with the moving order book and
solved it with an on-ramp precompile that submits intents and verifies committed
receipts (LP-9010, LP-9999).
This LP provides the same on-ramp for cognition. It is deliberately a separate
precompile from:
- the deterministic in-consensus inference precompile
0x0300…0003([LP-0303],precompile/inference/module.go) — Tier 1, small models, run by every validator; left byte-identically unchanged; - the model registry precompile
0x0300…0002(precompile/modelregistry/module.go) — governance model adoption; - the AI-mining precompile
0x0300…0000.
Keeping the on-ramp in its own slot keeps each concern orthogonal and lets a node that does not run a local A-Chain leave the on-ramp closed (every selector reverts cleanly) without affecting the other AI precompiles.
Specification
Precompile Address
0x0300000000000000000000000000000000000004
The AI reserved range is 0x0300…0000 .. 0x0300…00FF (the significant byte 0x03
is at the front; the slot is the last byte). Slot map:
| Slot | Address | Precompile |
|---|---|---|
0x00 | 0x0300…0000 | A-Chain mining-receipt settlement (no raw work verification) |
0x02 | 0x0300…0002 | Model Registry (governance model adoption) |
0x03 | 0x0300…0003 | Deterministic in-consensus inference (Tier 1) |
0x04 | 0x0300…0004 | AI Bridge (this LP) |
Cite:
precompile/aivmbridge/module.go(ContractAddress),precompile/inference/module.go(0x0300…0003),precompile/modelregistry/module.go(0x0300…0002).
Method Selectors
submitInferenceIntent selector = 0x10000000
verifyInferenceReceipt selector = 0x11000000
Chosen in the AI range, disjoint from the inference precompile's
SelectorGenerate = 0x01000000. Each is the first 4 bytes of the calldata.
Cite:
precompile/aivmbridge/bridge.go(SelectorSubmitInferenceIntent,SelectorVerifyInferenceReceipt).
Interface Definition
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2026 Lux Industries Inc.
pragma solidity ^0.8.24;
/// @title IAIBridge - C->A inference on-ramp precompile (LP-5301)
/// @notice Precompile at 0x0300000000000000000000000000000000000004.
/// @dev Pattern A submits a committed intent; Pattern B verifies a committed
/// A-Chain receipt. Neither op observes live A-Chain state (the bridge law,
/// LP-5300). Both ops mutate the C outbox, so neither is callable in a
/// static (read-only) context.
interface IAIBridge {
/// @notice Pattern A — record a committed C-side inference INTENT for the
/// A-Chain to import under its own consensus, and return the
/// deterministic intent id.
/// @param modelSpecHash Weight-commitment of the model (must be non-zero;
/// must be a governance-adopted model — see Model Registry).
/// @param promptHash Commitment to the prompt/input (must be non-zero).
/// @param n Fan-out: number of independent provider executions,
/// in [1, 256].
/// @param threshold M-of-N agreement required, in [1, n] (the A-Chain
/// further requires floor(n/2)+1 <= threshold).
/// @param fee Reward offered, 256-bit (funds the A escrow + burn).
/// @param routing Opaque routing hint; carried ONLY in the transport
/// ZAP nudge, NOT consensus state, NOT in the intent_id preimage.
/// @return intentID keccak over the pinned preimage (LP-5300 wire spec).
function submitInferenceIntent(
bytes32 modelSpecHash,
bytes32 promptHash,
uint16 n,
uint16 threshold,
uint256 fee,
bytes32 routing
) external returns (bytes32 intentID);
/// @notice Pattern B — verify a committed A-Chain receipt against a
/// receipt_root C already holds committed, settle the matching
/// pending intent, and return the canonical output.
/// @param receiptBytes Canonical 355-byte AInferenceReceipt encoding.
/// @param proofBytes Merkle inclusion proof (DecodeProof wire frame).
/// @return intentID The settled intent id.
/// @return canonicalOutputHash The agreed output digest C now trusts.
/// @return status Always 2 (Completed) on success.
function verifyInferenceReceipt(bytes calldata receiptBytes, bytes calldata proofBytes)
external
returns (bytes32 intentID, bytes32 canonicalOutputHash, uint8 status);
}
The Solidity interface is the intended ABI surface. The precompile decodes a tight fixed-width frame internally (no Solidity-ABI offset ambiguity); see the calldata layouts below. Cite:
precompile/aivmbridge/bridge.go.
Calldata Layout (normative)
submitInferenceIntent — after the 4-byte selector, a fixed 6-word
(192-byte) frame:
[0:32] modelSpecHash (bytes32)
[32:64] promptHash (bytes32)
[64:96] n (uint16, right-aligned; high 30 bytes MUST be zero)
[96:128] threshold (uint16, right-aligned; high 30 bytes MUST be zero)
[128:160] fee (uint256)
[160:192] routing (bytes32, opaque; read for the notifier, NOT stored on-state)
-> returns bytes32 intentID
verifyInferenceReceipt — after the 4-byte selector, a tight length-prefixed
frame:
[0:2] u16be receiptLen
[2:4] u16be proofLen
[4:4+rl] receipt bytes (canonical 355-byte AInferenceReceipt encoding)
[..] proof bytes (ReceiptRoot(32) | u64be Index(8) | u16be pathLen(2) | pathLen*32)
-> returns (bytes32 intentID, bytes32 canonicalOutputHash, uint8 status) // 96 bytes
Both frames are exact-length: the precompile rejects a short frame AND a frame
with trailing junk (ErrInputTooShort / ErrInputOversized). Numeric words reject
dirty high bytes (ErrDirtyWord). This is the calldata-hardening discipline.
Cite:
precompile/aivmbridge/bridge.go(submitIntentArgsLen,verifyReceiptframing,readUint16Word),proof.go(DecodeProof).
AChainClient — the node-local A-Chain on-ramp
The precompile resolves through an AChainClient, the EXACT shape of
dex.DChainClient, adapted to inference. This is not a backend swap. There is
exactly one valid target: the co-located A-Chain this node runs.
type AChainClient interface {
Brand() string // white-label identity; MUST be non-empty
AChainID() [32]byte // committed C<->A rail peer id; MUST be non-zero
// Pattern A: write a committed C outbox intent; return its deterministic id.
// MUST NOT call into or mutate aivm.
SubmitInferenceIntent(ctx, store IntentStore, in InferenceIntent) ([32]byte, error)
// Pattern B: verify a committed receipt against a committed receipt root.
// MUST NOT live-query aivm; receipt + proof arrive as calldata.
VerifyInferenceReceipt(ctx, vs ReceiptVerifierState, r AInferenceReceipt, p AInferenceProof) (VerifiedAInferenceReceipt, error)
}
There is deliberately no GetInferenceReceipt(id) method — that name invites a
live mutable A query, exactly the fork-unsafe shape a prior build shipped.
Verification takes the receipt + proof as calldata, so the C side never reaches
into A to fetch anything.
Cite:
precompile/aivmbridge/achain_client.go(AChainClient,VerifiedAInferenceReceipt, the naming note); templateprecompile/dex/dchain_client.go(DChainClient, the CONSENSUS-SAFETY RULE).
Install Discipline (fail-secure, install-once)
The host binary calls InstallAChainClient exactly once at boot, BEFORE the
VM serves precompiles. A node without its local A on-ramp leaves the default
achainUnavailable client in place; Pattern A then reverts cleanly
(ErrAChainUnavailable) and Pattern B still works (it is pure C-state
verification, no client needed). Install-time sanity (fail-secure):
cMUST be non-nil (a nil client would NPE the bridge).c.Brand()MUST be non-empty (white-label / log discipline).c.AChainID()MUST be non-zero (an unscoped peer mints cross-rail-aliasable intent ids — the rail binding is load-bearing for replay isolation).- Install is once: a second call errors rather than swapping the live client,
so a misconfigured plugin cannot race a late re-resolution against in-flight
Run()(TOCTOU). The client lives behind anatomic.Pointerfor race-free reads.
Cite:
precompile/aivmbridge/achain_client.go(InstallAChainClient,installed,aChainClient,currentAChainClient); mirror ofdex.InstallDChainClient.
Gas Costs
| Operation | Gas | Notes |
|---|---|---|
submitInferenceIntent | 40,000 | write-class: derive id, replay/calldata checks, write outbox slots (dex native-intent tier) |
verifyInferenceReceipt (base) | 30,000 | decode + recompute receipt_hash + committed-root lookup + match/consume intent |
verifyInferenceReceipt (per proof node) | +1,000 / node | the Merkle walk; pathLen hard-capped by MaxProofDepth = 64 |
RequiredGas is a pure function of the selector (and, for verify, the proof depth
declared in calldata). It NEVER depends on any A-Chain observation. A malformed or
short frame falls back to the base verify gas.
Cite:
precompile/aivmbridge/gas.go(GasSubmitInferenceIntent,GasVerifyInferenceReceiptBase,GasVerifyPerProofNode,verifyGas),module.go(RequiredGas).
Revert Discipline
geth-canonical, mirroring the inference + dex paths:
- Out-of-gas →
(nil, 0, err): charged gas consumed. - Decode / state / client error →
(nil, remainingGas, err): only the gas already charged for this call is consumed; the leftover is returned. There is NO all-gas-burn beyond the charged amount.
Every error reverts cleanly and mutates NOTHING (no partial consume). Fail-secure is the rule: when in doubt, deny.
Cite:
precompile/aivmbridge/bridge.go(revert discipline header),errors.go(the full error surface).
Effects on C State
The precompile owns one auditable storage region under aivmbridge.v1.* at its
own address:
| Pot | Key | Meaning |
|---|---|---|
| outbox | intent_id -> packed OutboxIntent | the committed Pattern-A record (binds model/prompt/requester/chain/fan-out) |
| consumed | intent_id -> blockNumber+1 | Pattern-B double-consume guard |
| receipt-root checkpoint | receipt_root -> committedHeight+1 | the A→C atomic-import seam's landing slot (root authenticity) |
Pattern A appends to the outbox. Pattern B reads the checkpoint + outbox and marks an intent consumed. The receipt-root checkpoint is written by the A→C atomic boundary / Warp handler at block accept (the same shape as dex's accepted-atomic-op flush, in the A→C direction); this package ships the C-side store + verification, and the authenticity of a committed root rests on that import handler — clearly marked, never papered over.
Cite:
precompile/aivmbridge/state.go(stateNamespace, the three prefixes,OutboxStatus,OutboxIntent, the seam-boundary note).
Rationale
- Why a separate precompile. Orthogonal concern (large-model on-ramp) at its own slot, disjoint from Tier-1 deterministic inference and the model registry. A node can run Tier 1 without the A on-ramp, or vice versa.
- Why proof/receipt-oriented, not query. A live query forks consensus. The
on-ramp records intents and verifies committed receipts — both deterministic.
This is the whole reason the
AChainClienthas noGet…(id)method. - Why a tight fixed-width frame instead of full Solidity ABI decode. Exact, length-checked frames remove offset ambiguity and make the calldata-hardening (reject short/oversized/dirty) total and cheap.
- Why install-once. A live client swap is a TOCTOU against concurrent
Run()threads; install-once + atomic publish removes the race.
Backwards Compatibility
The precompile is purely additive: a new address in the AI range, no change to any
existing ABI or to the Tier-1 inference precompile (0x0300…0003), the model
registry (0x0300…0002), or the AI-mining precompile (0x0300…0000). Contracts
opt in by calling the new address; existing tooling (Foundry, Hardhat) interacts
with it as a normal precompile.
Test Cases
The package ships an extensive harness (golden wire vectors, cross-module seam equality, and an adversarial red-team suite). Representative cases:
wire_test.go — intent_id / receipt encoding golden vectors (byte-for-byte)
decode_test.go — receipt/proof decode: reject short, oversized, bad version
achain_submit_test.go — Pattern A: deterministic id, replay revert, calldata bounds
achain_verify_test.go — Pattern B: 8-step verify, bind-mismatch, double-consume
crossmodule_test.go — TestCrossModuleMerkleSeam: leaf/node hashing == chains/aivm
install_test.go — install-once, nil/empty-brand/zero-rail rejection
gas_test.go — RequiredGas == verifyGas(pathLen); OOG burns charged gas
zap_test.go — routing hint is transport-only, never on-state
redteam_test.go — the 12 attack vectors (live read, forged proof, replay, ...)
Illustrative Solidity (consumer side):
// Pattern A: request a 5-of-... judgment from an adopted model.
bytes32 intentID = IAIBridge(0x0300...0004).submitInferenceIntent(
modelSpecHash, promptHash, /*n*/ 5, /*threshold*/ 3, /*fee*/ 1 ether, routing
);
// ...later, after A settles and the A->C boundary commits the receipt_root...
// Pattern B: verify the certified receipt and act on the canonical output.
(bytes32 settled, bytes32 outputHash, uint8 status) =
IAIBridge(0x0300...0004).verifyInferenceReceipt(receiptBytes, proofBytes);
require(status == 2 && settled == intentID, "not settled");
// outputHash is now trusted C-side; the contract chooses what to do with it.
Reference Implementation
Location: github.com/luxfi/node/precompile/aivmbridge/
precompile/aivmbridge/
├── module.go # precompile registration, address 0x0300...0004, RequiredGas
├── bridge.go # Run() body: selector routing, calldata hardening, Pattern A/B
├── intent.go # InferenceIntent + DeriveIntentID (pinned wire)
├── receipt.go # AInferenceReceipt + Encode/Decode + ReceiptHash (355 bytes)
├── proof.go # AInferenceProof + VerifyMerkle + DecodeProof
├── verify.go # verifyInferenceReceipt — the 8-step Pattern-B core
├── state.go # C outbox + consumed set + receipt-root checkpoint
├── achain_client.go # AChainClient interface + install-once + achainUnavailable
├── native_achain_client.go # the staged native on-ramp (test/integration)
├── gas.go # GasSubmitInferenceIntent / Verify base + per-node
└── errors.go # the fail-secure error surface
The A-Chain settlement side this precompile bridges to is specified in
LP-5300 and
implemented in github.com/luxfi/chains/aivm/.
Security Considerations
The precompile-level attack surface (a subset of the LP-5300 §Security twelve vectors, focused on the EVM entry point). Each maps to a code citation.
- Live A read. No live-read method exists; Pattern B verifies committed C
state + the calldata proof only.
achain_client.go,verify.go. - Direct A mutation. Leaf library; cannot reach the chain manager; Pattern A
writes only C outbox.
achain_client.go,state.go. - Intent replay. Deterministic injective
intent_id;OutboxPendingguard (ErrIntentReplay).intent.go,state.go,bridge.go. - Receipt replay / double-consume.
OutboxConsumedguard, CEI order (ErrIntentConsumed).verify.go. - Wrong model / prompt. Bound-field equality check
(
ErrReceiptBindMismatch).verify.gostep 7. - Pending → credit. Only
Completed+ non-zero output is actionable (ErrReceiptNotCompleted,ErrZeroOutput).verify.gosteps 1–2. - Forged proof. Root must be C-committed (
ErrReceiptRootNotCommitted); leaf recomputed from receipt bytes; index high-bit aliasing rejected.verify.gostep 3–4,proof.go. - ZAP-without-proof. Routing hint is transport-only, NOT consensus state, NOT
in the
intent_idpreimage.bridge.go,state.go,zap_test.go. - Install TOCTOU. Install-once
CompareAndSwap+ atomic publish; second install errors.achain_client.go. - Calldata. Exact-length frames (reject short AND oversized); dirty-high-byte
rejection; fan-out bounds (
MaxFanout = 256).bridge.go,errors.go. - Read-only abuse. Both ops mutate the C outbox, so a static call reverts
(
ErrReadOnly).bridge.go. - Missing atomic capability. Without the cross-chain atomic capability the
bridge cannot mint a rail-scoped id and reverts (
ErrNoAtomicState) rather than guess a peer; a zeroc_tx_hash(broken host wiring) also reverts (ErrZeroTxHash).bridge.go.
The off-chain attack surface (beacon grinding, slash-grief, recursion runaway) and the value-conservation argument live with the settlement engine in LP-5300 §Security.
Related LPs
- LP-5200 — A-Chain-only mining authority and destination receipt settlement.
- LP-5300 — the Thinking Chains protocol this precompile is the C-Chain entry point for.
- LP-5000 — A-Chain core.
- LP-9010 — the on-ramp precompile pattern this mirrors.
- LP-9999 — the receipt-verify-and-settle pattern this mirrors.
- LP-032 — the D-Chain VM (the "matches/settles" split).
- Zoo "Beluga L3" ZIP — first deployment; Hanzo "Cognitive Sidecar / Hanzo Engine provider" HIP — the provider (operator) side; Zoo "Thinking Chains" paper — the research framing. (Cross-referenced in LP-5300 §Related Work.)
Copyright
Copyright (C) 2026, Lux Partners Limited. All rights reserved.
Licensed under the MIT License.