Axiom Query Format
Detailed specification of the on-chain Axiom V2 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 encoded data querycomputeQuery
(AxiomV2ComputeQuery
) -- the compute querycallback
(AxiomV2Callback
) -- the callbackuserSalt
(bytes32
) -- salt chosen by the usermaxFeePerGas
(uint64
) -- max fee to use on the fulfillment transactioncallbackGasLimit
(uint32
) -- gas limit to allocate for the callbackrefundee
(address
) -- address taking refunds
We create a unique identifier for the query via:
where
queryHash = 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.
We also define the query schema via:
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.T
The difference between queryHash
and querySchema
is that querySchema
specifies a general schema for a query, with unknown variables which may change from instance to instance. queryHash
specifies a specific instance of the query schema, where all unknown variables have numerical values.
Note that querySchema
is only meaningful when a compute query is supplied: it is bytes32(0x0)
if there is no compute query.
Query result specification
We anticipate a ZK proof for each query with public input/outputs consisting of:
sourceChainId
(uint64
) -- the sourcechainId
dataResultsRoot
(bytes32
) -- the Keccak encoded data outputdataResultsPoseidonRoot
(bytes32
) -- the Poseidon form of the data outputcomputeResultsHash
(bytes32
) -- the Keccak hash ofcomputeResults
, specified as:computeResults
(bytes32[]
) -- the result of applying the compute circuit with the inputs fromdataResultsRoot
as public inputsif 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
We define a commitment to the query result via resultHash
defined by
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 encode the query results by:
dataResultsRoot
: The Keccak Merkle root of the padded tree (padding bybytes32(0)
) with even index leaves given bykeccak(type . subqueryData)
and odd index leaves given byresult
.This is the same as the Keccak Merkle root of the padded tree with leaves given by
keccak(keccak(type . subqueryData) . result)
where padding is bykeccak(bytes32(0) . bytes32(0))
dataResultsPoseidonRoot
: The Poseidon Merkle root of the padded tree with leaves given byposeidon(poseidon(type . subqueryData) . result)
with padding by0
subqueryData
is a variable length array of field elements (determined by subquerytype
and in theSolidityNestedMapping
case themappingDepth
). Thereforeposeidon(type . subqueryData)
is a variable length Poseidon. We do this so the result root is independent of future subquery type additions.result
is a fixed length array of field elements, andposeidon(poseidon(type .subqueryData) . result)
refers to Poseidon on the fixed length concatenated array.
We have 6 subquery types, corresponding to:
block_header
: 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.solidity_nested_mapping
: 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
(bytes32[]
) -- user generated proof
We encode the compute query in bytes as
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 = 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.
Last updated