#block-header #circuit #block-hash #chain #instance #forms #aggregation

bin+lib axiom-core

This contains the ZK circuits that generate proofs for the AxiomV2Core smart contract. These circuits read the RLP encoded block headers for a chain of blocks and verify that the block headers form a chain. They output a Merkle Mountain Range of the block hashes of the chain. This crate also contains aggregation circuits to aggregate multiple circuits for the purpose of proving longer chains.

2 stable releases

2.0.13 Mar 12, 2024
2.0.12 Jan 21, 2024

#8 in #block-header

Download history 9/week @ 2024-09-22 1/week @ 2024-09-29

53 downloads per month

MIT license

1MB
17K SLoC

AxiomV2Core ZK Circuits

Proving and Verifying Key Generation

For instructions on how to generate the exact proving and verifying keys we use in production on Ethereum Mainnet, see here.

Public Instance Formats

Any Snark has an associated Vec<Fr> of public instances. We describe the format for the ones relevant to the AxiomV2Core circuits below.

EthBlockHeaderChainCircuit

pub struct EthBlockHeaderChainInput<F> {
    header_rlp_encodings: Vec<Vec<u8>>,
    num_blocks: u32, // num_blocks in [0, 2 ** max_depth)
    max_depth: usize,
    network: Network,
    _marker: PhantomData<F>,
}

This depends on a max_depth parameter. The public instances are:

  • prev_hash: H256 as two Fr elements in hi-lo format
  • end_hash: H256 as two Fr elements in hi-lo format
  • start_block_number . end_block_number: we assume both numbers are u32 and encode them to a single Fr element as start_block_number * 2^32 + end_block_number
  • merkle_mountain_range: a sequence of max_depth + 1 H256 elements, each encoded as two Fr elements in hi-lo format

Notes:

  • prev_hash is the parent hash of block number start_block_number
  • end_hash is the block hash of block number end_block_number
  • end_block_number - start_block_number is constrained to be <= 2^max_depth
    • This was previously assumed in axiom-eth v0.1.1 but not enforced because the block numbers are public instances, but we now enforce it for safety
  • merkle_mountain_range is ordered from largest peak (depth max_depth) first to smallest peak (depth 0) last

EthBlockHeaderChainIntermediateAggregationCircuit

pub struct EthBlockHeaderChainIntermediateAggregationInput {
    num_blocks: u32,
    snarks: Vec<Snark>,
    pub max_depth: usize,
    pub initial_depth: usize,
}

This circuit takes two EthBlockHeaderChainCircuits and aggregates them. The public instances are:

  • 4 * LIMBS = 12 Fr elements for the two BN254 G1 points representing the accumulator, used by the verifier for a pairing check
  • prev_hash: H256 as two Fr elements in hi-lo format
  • end_hash: H256 as two Fr elements in hi-lo format
  • start_block_number . end_block_number: we assume both numbers are u32 and encode them to a single Fr element as start_block_number * 2^32 + end_block_number
  • merkle_mountain_range: a sequence of 2^{max_depth - initial_depth} + initial_depth H256 elements, each encoded as two Fr elements in hi-lo format

Notes:

  • Same notes as EthBlockHeaderChainCircuit except that merkle_mountain_range is not actually a Merkle mountain range: we recover a Merkle mountain range of length max_depth + 1 by forming a Merkle mountain range from leaves merkle_mountain_range[..2^{max_depth - initial_depth}] and then appending merkle_mountain_range[2^{max_depth - initial_depth}..] to the end of it.
    • The reason is that we want to delay Keccaks

EthBlockHeaderChainRootAggregationCircuit

pub struct EthBlockHeaderChainRootAggregationInput {
    /// See [EthBlockHeaderChainIntermediateAggregationInput]
    pub inner: EthBlockHeaderChainIntermediateAggregationInput,
    /// Succinct verifying key (generator of KZG trusted setup) should match `inner.snarks`
    pub svk: Svk,
    prev_acc_indices: Vec<Vec<usize>>,
}

This circuit takes two EthBlockHeaderChainIntermediateAggregationCircuits and aggregates them. The public instances are:

  • 4 * LIMBS = 12 Fr elements for the two BN254 G1 points representing the accumulator, used by the verifier for a pairing check
  • prev_hash: H256 as two Fr elements in hi-lo format
  • end_hash: H256 as two Fr elements in hi-lo format
  • start_block_number . end_block_number: we assume both numbers are u32 and encode them to a single Fr element as start_block_number * 2^32 + end_block_number
  • merkle_mountain_range: a sequence of max_depth + 1 H256 elements, each encoded as two Fr elements in hi-lo format

Notes:

Passthrough Aggregation Circuit

This is from axiom-eth.

pub struct InputMerkleAggregation {
    pub snarks: Vec<EnhancedSnark>,
}

We will only use this where snarks has length 1 and consists of a single snark. In this case it is an AggregationCircuit that purely passes through the public instances of the single snark in snarks, discarding old accumulators (there is no Merkle root computation because there is only one snark).

We will use this snark on EthBlockHeaderChainRootAggregationCircuit or itself if we want multiple rounds of passthrough aggregation. The public instances are exactly the same as for EthBlockHeaderChainRootAggregationCircuit.

Dependencies

~33–52MB
~1M SLoC