Axiom Query Protocol

How Axiom trustlessly fulfill queries into on-chain data.

The AxiomV1Query contract allows users to query arbitrary block header, account, and storage data from the history of Ethereum. Queries can be submitted on-chain, and query results are verified on-chain with zero-knowledge proofs of validity. We explain how this works below.

Initiating queries

Anyone can initiate a query into Axiom with either on- or off-chain data availability using the following functions in AxiomV1Query:

  • sendQuery -- Request a proof for keccakQueryResponse. This allows the caller to specify a refundee and also provide on-chain data availability for the query in query.

  • sendOffchainQuery-- Request a proof for keccakQueryResponse. This allows the caller to specify a refundee and also provide off-chain data availability for the query in ipfsHash.

Each query request must be accompanied by an on-chain payment which is collected upon fulfillment. The minimum payment allowed is specified by minQueryPrice in AxiomV1Query and is initially set to 0.01 ETH. If a query is not fulfilled by a pre-specified deadline, anyone can process a refund to the pre-specified refundee:

  • collectRefund allows anyone to process a refund for a query specified by keccakQueryResponse.

Fulfilling queries

Queries are fulfilled by submitting a proof that verifies keccakQueryResponse against the cache of block hashes in AxiomV1. We have permissioned fulfillment to the PROVER_ROLE for safety at the moment.

  • fulfillQueryVsMMR allows a prover to supply a proof which proves keccakQueryResponse was correct against the Merkle Mountain range stored in index mmrIdx of AxiomV1.mmrRingBuffer. The prover must also pass some additional witness data in mmrWitness and the ZK proof itself in proof. The prover can collect payment to payee.

  • verifyResultVsMMR allows a prover to prove a keccakQueryResponse without on-chain query request.

Both of these fulfillment functions use the verifier deployed at mmrVerifierAddress to verify a ZK proof of the query result. This proof has public inputs/outputs given by the following, where mmr is a variable length array of bytes32 containing the Merkle Mountain Range that proof is proving into, and mmr[idx] is either bytes32(0) or the Merkle root of 1 << idx block hashes.

  • poseidonBlockResponse as a field element

  • keccakBlockResponse as 2 field elements, in hi-lo form

  • poseidonAccountResponse as a field element

  • keccakAccountResponse as 2 field elements, in hi-lo form

  • poseidonStorageResponse as a field element

  • keccakStorageResponse as 2 field elements, in hi-lo form

  • historicalMMRKeccak which is keccak256(abi.encodePacked(mmr[10:])) as 2 field elements in hi-lo form.

  • recentMMRKeccak which is keccak256(abi.encodePacked(mmr[:10])) as 2 field elements in hi-lo form.

Here, hi-lo form means a uint256 (a << 128) + b is represented as two uint256's a and b, each of which is guaranteed to contain a uint128. The proof checks that

The {poseidon, keccak}{Block, Account, Storage}Response are consistent relative to the Merkle Mountain range of block hashes committed to in historicalMMRKeccak and recentMMRKeccak.

In ZK Circuits for Axiom Queries, we explain how our ZK circuits check this claim.

Upon verification, the smart contract uses the additional witness data in mmrWitness to check historicalMMRKeccak and recentMMRKeccak are consistent with the on-chain cache of block hashes in AxiomV1 by checking:

  • historicalMMRKeccak appears in index mmrIndex of AxiomV1.mmrRingBuffer.

  • recentMMRKeccak is either committed to by an element of AxiomV1.historicalRoots or is an extension of such an element by block hashes accessible to the EVM.

If all checks pass, the fulfilled results keccakQueryResponse and poseidonQueryResponse are stored in verifiedKeccakResults and verifiedPoseidonResults.

Reading verified query results

We support reading block, account, and storage data from verified query results via:

  • areResponsesValid -- Check whether queries into block, account, and storage data have been verified. Each query is specified by:

    • BlockResponse -- The blockNumber and blockHash as well as a Merkle proof proof and leaf location leafIdx in keccakBlockResponse.

    • AccountResponse -- The blockNumber, addr, nonce, balance, storageRoot, and codeHash as well as a Merkle proof proof and leaf location leafIdx in keccakAccountResponse.

    • StorageResponse -- The blockNumber, addr, slot, and value as well as a Merkle proof proof and leaf location leafIdx in keccakStorageResponse.

The raw query results may also be accessed using:

  • isKeccakResultValid -- Check whether a query consisting of keccakBlockResponse, keccakAccountResponse, and keccakStorageResponse has already been verified.

  • isPoseidonResultValid -- Check whether a query consisting of poseidonBlockResponse, poseidonAccountResponse, and poseidonStorageResponse has already been verified.

Last updated