Axiom Query Format
Axiom V2 queries allow users to compute over historic data on Ethereum. These queries consist of the following three pieces:
- Data query: ZK authenticated access to historic block headers, accounts, storage slots, transactions, receipts, and Solidity mapping values from the history of Ethereum.
- Compute query: ZK-proven computation over the data authenticated in the data query.
- Callback: An on-chain callback to invoke with the result of the compute query.
All three of the data, compute, and callback are optional, but a valid query must have at least one of the data or compute queries.
Query format specification
The query is specified by the following fields, of which we will detail the data, compute, and callback details below.
version
(uint8
) -- the version, fixed to beuint8(2)
for Axiom V2.sourceChainId
(uint64
) -- the sourcechainId
caller
(address
) -- the address of the callerdataQueryHash
(bytes32
) -- the Keccak hash of the encoded data querycomputeQuery
(AxiomV2ComputeQuery
) -- the compute querycallback
(AxiomV2Callback
) -- the callbackuserSalt
(bytes32
) -- salt chosen by the userfeeData
(AxiomV2FeeData
) -- fee specification for the fulfillment transaction, consisting of:maxFeePerGas
(uint64
) -- max fee to use on the fulfillment transactioncallbackGasLimit
(uint32
) -- gas limit to allocate for the callbackoverrideAxiomQueryFee
(uint256
) -- optional override to increase the query fee aboveaxiomQueryFee
refundee
(address
) -- address taking refunds
We create a unique identifier for the query via:
queryId = uint256(keccak(targetChainId, caller . userSalt . queryHash . callbackHash . refundee))
where
targetChainId
(uint64
) is thechainId
of the chain the query is sent onqueryHash = keccak(version . sourceChainId . dataQueryHash . encodedComputeQuery)
encodedComputeQuery
(see Compute Query Format)callbackHash = keccak(target . extraData)
The difference between queryId
and queryHash
is that the queryId
has identifiers related to the user, such as the callback and refundee addresses, whereas the queryHash
is an identifier for just the data and compute query.
Query Schema
We also define the query schema via:
querySchema = keccak(k . resultLen . vkeyLen . vkey)
The querySchema
provides a unique identifier for a callback function to distinguish the type of compute query used to generate the query results passed to the callback.
Note that querySchema
is only meaningful when a compute query is supplied: it is set to bytes32(0x0)
if there is no compute query.
Data query format
Each data query consists of the fields:
sourceChainId
(uint64
) -- thechainId
of the source chainsubqueries
(Subquery[]
)
Each subquery has a result given by a single uint256
or bytes32
and is specified by
type
(uint16
) -- a number identifying the subquery typesubqueryData
-- data specifying the subquery which follows a different subquery schema for eachtype
.- This should be of a max size over all subquery types.
We encode the query by:
dataQueryHash
(bytes32
): The Keccak hash ofsourceChainId
concatenated with the array with entries given by:keccak(type . subqueryData)
Each subquery has a result
which is of type uint256
or bytes32
, with smaller datatypes left-padded with 0's. If a user wishes to access multiple fields from e.g. a single account or receipt, they must make multiple subqueries. We hope this does not impose too much overhead, since we will only constrain the Keccak hashes once in the Keccak table.
We have 6 subquery types, corresponding to:
blockHeader
: fields from block headeraccount
: fields from accounts, e.g. nonce, balance, codeHash, storageRootstorage
: slots in account local storagetransaction
: fields from transactions, including indexing into calldata.receipt
: fields from receipts, including indexing into topics and data of logs.solidityNestedMapping
: values from nested mappings of value types
Compute query format
The compute query is specified by AxiomV2ComputeQuery
, which contains:
k
(uint8
) -- degree of the compute circuit, equal to0
if no compute is neededresultLen
(uint16
) --- number of meaningful public outputs of the circuitvkey
(bytes32[]
) -- verification key for the compute circuitcomputeProof
(bytes
) -- user generated proof
We encode the compute query in bytes as
encodedComputeQuery = k . resultLen . vkeyLen . vkey . proofLen . computeProof
when the compute query exists, where
uint8 vkeyLen
is the length ofvkey
asbytes32[]
proofLen
is the length ofcomputeProof
asbytes
.
If there is no compute query, then the encodedComputeQuery = uint8(0) . resultLen
.
Callback format
The callback is specified by AxiomV2Callback
, which contains:
target
(address
) -- equal toaddress(0x0)
if no callback neededextraData
(bytes
) -- additional data sent to the callback. Equal tobytes(0x0)
if no callback is needed.
Query result specification
Query fulfillment requires the successful verification of a ZK proof with public input/outputs consisting of:
sourceChainId
(uint64
) -- the sourcechainId
computeResultsHash
(bytes32
) -- the Keccak hash ofcomputeResults
, specified as:computeResults
(bytes32[]
) -- the result of applying the compute circuit with the data subquery outputs as public inputs- if no compute is needed, this is the first
resultLen
data results.
queryHash
(bytes32
) -- thequeryHash
identifying the query.querySchema
(bytes32
) -- thequerySchema
identifying the query type. This isbytes32(0x0)
is there is no compute query.blockhashMMRKeccak
(bytes32
) -- witness data for reconciling the proof againstAxiomV2Core
aggregateVkeyHash
(bytes32
) -- a hash identifying the aggregation strategy used to generate a ZK proof of the query result.payee
(address
) -- a free public input which is associated to a private witness in the proof to avoid malleability issues
You can read more about our ZK circuits in ZK Circuits for Axiom Queries.