axiom_query/verify_compute/
types.rs

1use std::iter;
2
3use anyhow::bail;
4use axiom_codec::{types::field_elements::FlattenedSubqueryResult, HiLo};
5use axiom_eth::{
6    halo2_base::AssignedValue,
7    halo2curves::bn256::{Fr, G1Affine},
8    impl_flatten_conversion,
9    snark_verifier_sdk::{halo2::gen_dummy_snark_from_protocol, Snark, SHPLONK},
10    utils::{
11        build_utils::dummy::DummyFrom,
12        component::{
13            circuit::{CoreBuilderOutputParams, CoreBuilderParams},
14            types::LogicalEmpty,
15            ComponentType, ComponentTypeId, LogicalResult,
16        },
17        snark_verifier::NUM_FE_ACCUMULATOR,
18    },
19};
20use getset::{CopyGetters, Getters};
21use serde::{Deserialize, Serialize};
22
23use crate::{
24    components::results::{table::SubqueryResultsTable, types::CircuitOutputResultsRoot},
25    utils::client_circuit::{metadata::AxiomV2CircuitMetadata, vkey::OnchainVerifyingKey},
26};
27
28/// Identifier for the component type of Verify Compute Circuit
29pub struct ComponentTypeVerifyCompute;
30
31/// Configuration parameters for Verify Compute Circuit that determine
32/// the circuit, **independent** of the variable inputs.
33///
34/// Even when `nonempty_compute_query == false` (no compute query),
35/// the `circuit_params.client_metadata` needs to be set to a valid
36/// client circuit configuration.
37#[derive(Clone, Debug, Default, Serialize, Deserialize, Getters, CopyGetters)]
38pub struct CoreParamsVerifyCompute {
39    /// Capacity: max number of subquery results
40    #[getset(get_copy = "pub")]
41    subquery_results_capacity: usize,
42    /// Succinct verifying key should be the generator `g()[0]` of the KZG trusted setup used to generate the vkey.
43    #[getset(get_copy = "pub")]
44    svk: G1Affine, // Svk type doesn't derive Serialize
45    /// Client circuit on-chain vkey
46    #[getset(get = "pub")]
47    client_metadata: AxiomV2CircuitMetadata,
48    /// Length of `preprocessed` in `PlonkProtocol`
49    #[getset(get_copy = "pub")]
50    preprocessed_len: usize,
51}
52
53impl CoreParamsVerifyCompute {
54    pub fn new(
55        subquery_results_capacity: usize,
56        svk: G1Affine,
57        client_metadata: AxiomV2CircuitMetadata,
58        preprocessed_len: usize,
59    ) -> Self {
60        Self { subquery_results_capacity, svk, client_metadata, preprocessed_len }
61    }
62}
63impl CoreBuilderParams for CoreParamsVerifyCompute {
64    /// No component output
65    fn get_output_params(&self) -> CoreBuilderOutputParams {
66        CoreBuilderOutputParams::new(vec![])
67    }
68}
69
70/// Logic inputs to Verify Compute Circuit
71/// Deserialization is specialized to [Fr] for now.
72///
73/// ## Compute Snark
74/// The Verify Compute Circuit should only depend on the number of columns and custom gates / lookup arguments
75/// of `compute_snark`, not on the fixed commitments or domain size `2^k`.
76#[derive(Clone, Debug, Serialize, Deserialize, Getters)]
77pub struct CircuitInputVerifyCompute {
78    /// Chain ID of the chain the EVM data is from
79    pub source_chain_id: u64,
80    /// Used for lookups, length may be padded with dummy subqueries
81    pub subquery_results: CircuitOutputResultsRoot<Fr>,
82    /// If `nonempty_compute_query == false`, then `compute_snark` is a dummy snark.
83    pub nonempty_compute_query: bool,
84    /// The number of user results
85    pub result_len: u16,
86    /// The client snark.
87    ///
88    /// When there is no compute query (`nonempty_compute_query == false`),
89    /// this must be a dummy snark matching `circuit_params.client_metadata` that will still verify.
90    #[getset(get = "pub")]
91    pub(super) compute_snark: Snark,
92}
93
94impl CircuitInputVerifyCompute {
95    /// If `nonempty_compute_query == false`, then `compute_snark` must be a dummy snark that will verify.
96    pub fn new(
97        source_chain_id: u64,
98        subquery_results: CircuitOutputResultsRoot<Fr>,
99        nonempty_compute_query: bool,
100        result_len: u16,
101        compute_snark: Snark,
102    ) -> Self {
103        Self {
104            source_chain_id,
105            subquery_results,
106            nonempty_compute_query,
107            result_len,
108            compute_snark,
109        }
110    }
111}
112
113impl DummyFrom<CoreParamsVerifyCompute> for CircuitInputVerifyCompute {
114    fn dummy_from(core_params: CoreParamsVerifyCompute) -> Self {
115        let subquery_results_capacity = core_params.subquery_results_capacity();
116        let onchain_vk = OnchainVerifyingKey {
117            circuit_metadata: core_params.client_metadata().clone(),
118            transcript_initial_state: Default::default(),
119            preprocessed: vec![G1Affine::default(); core_params.preprocessed_len()],
120        };
121        // k is loaded as witness so it shouldn't matter
122        let k = 7;
123        let protocol = onchain_vk.into_plonk_protocol(k).unwrap();
124        let compute_snark = gen_dummy_snark_from_protocol::<SHPLONK>(protocol);
125        let results = SubqueryResultsTable {
126            rows: vec![FlattenedSubqueryResult::default(); subquery_results_capacity],
127        };
128        let subquery_hashes = vec![HiLo::default(); subquery_results_capacity];
129
130        let subquery_results =
131            CircuitOutputResultsRoot { results, subquery_hashes, num_subqueries: 0 };
132        Self::new(0, subquery_results, true, 0, compute_snark)
133    }
134}
135
136pub(super) const NUM_LOGICAL_INSTANCE_WITHOUT_ACC: usize = 1 + 2 + 2 + 2 + 1 + 1;
137pub(super) const NUM_LOGICAL_INSTANCE: usize =
138    NUM_FE_ACCUMULATOR + NUM_LOGICAL_INSTANCE_WITHOUT_ACC;
139const NUM_BITS_PER_FE: [usize; NUM_LOGICAL_INSTANCE] = get_num_bits_per_fe();
140// 9999 means that the public instance takes a whole witness
141// Accumulators *must* take whole witnesses.
142const fn get_num_bits_per_fe() -> [usize; NUM_LOGICAL_INSTANCE] {
143    let mut bits_per = [9999; NUM_LOGICAL_INSTANCE];
144    bits_per[NUM_FE_ACCUMULATOR] = 64;
145    bits_per[NUM_FE_ACCUMULATOR + 1] = 128;
146    bits_per[NUM_FE_ACCUMULATOR + 2] = 128;
147    bits_per[NUM_FE_ACCUMULATOR + 3] = 128;
148    bits_per[NUM_FE_ACCUMULATOR + 4] = 128;
149    bits_per[NUM_FE_ACCUMULATOR + 5] = 128;
150    bits_per[NUM_FE_ACCUMULATOR + 6] = 128;
151    bits_per
152}
153/// The public instances of the circuit, **excluding** the component owned instances
154/// for output commit and promise commit.
155#[derive(Debug, Clone, PartialEq, Eq)]
156pub struct LogicalPublicInstanceVerifyCompute<T> {
157    pub accumulator: Vec<T>,
158    pub source_chain_id: T,
159    pub compute_results_hash: HiLo<T>,
160    pub query_hash: HiLo<T>,
161    pub query_schema: HiLo<T>,
162    pub results_root_poseidon: T,
163    pub promise_subquery_hashes: T,
164}
165/// [LogicalPublicInstanceVerifyCompute] with `accumulator` removed.
166#[derive(Debug, Clone, PartialEq, Eq)]
167pub struct LogicalPisVerifyComputeWithoutAccumulator<T> {
168    pub source_chain_id: T,
169    pub compute_results_hash: HiLo<T>,
170    pub query_hash: HiLo<T>,
171    pub query_schema: HiLo<T>,
172    pub results_root_poseidon: T,
173    pub promise_subquery_hashes: T,
174}
175
176type F = Fr;
177/// Verify Compute has no virtual table as output
178impl ComponentType<F> for ComponentTypeVerifyCompute {
179    type InputValue = LogicalEmpty<F>;
180    type InputWitness = LogicalEmpty<AssignedValue<F>>;
181    type OutputValue = LogicalEmpty<F>;
182    type OutputWitness = LogicalEmpty<AssignedValue<F>>;
183    type LogicalInput = LogicalEmpty<F>;
184
185    fn get_type_id() -> ComponentTypeId {
186        "axiom-query:ComponentTypeVerifyCompute".to_string()
187    }
188    fn logical_result_to_virtual_rows_impl(
189        _ins: &LogicalResult<F, Self>,
190    ) -> Vec<(Self::InputValue, Self::OutputValue)> {
191        unreachable!()
192    }
193    fn logical_input_to_virtual_rows_impl(_li: &Self::LogicalInput) -> Vec<Self::InputValue> {
194        unreachable!()
195    }
196}
197
198// ============== LogicalPublicInstanceVerifyCompute ==============
199impl<T: Copy> LogicalPublicInstanceVerifyCompute<T> {
200    pub fn flatten(self) -> Vec<T> {
201        iter::empty()
202            .chain(self.accumulator)
203            .chain(Some(self.source_chain_id))
204            .chain(self.compute_results_hash.hi_lo())
205            .chain(self.query_hash.hi_lo())
206            .chain(self.query_schema.hi_lo())
207            .chain([self.results_root_poseidon, self.promise_subquery_hashes])
208            .collect()
209    }
210}
211
212impl<T: Copy> TryFrom<Vec<T>> for LogicalPublicInstanceVerifyCompute<T> {
213    type Error = anyhow::Error;
214
215    fn try_from(mut value: Vec<T>) -> anyhow::Result<Self> {
216        if value.len() != NUM_LOGICAL_INSTANCE {
217            bail!("wrong number of logical public instances")
218        };
219        let accumulator = value.drain(..NUM_FE_ACCUMULATOR).collect();
220        let drained: LogicalPisVerifyComputeWithoutAccumulator<T> = value.try_into().unwrap();
221        Ok(Self {
222            accumulator,
223            source_chain_id: drained.source_chain_id,
224            compute_results_hash: drained.compute_results_hash,
225            query_hash: drained.query_hash,
226            query_schema: drained.query_schema,
227            results_root_poseidon: drained.results_root_poseidon,
228            promise_subquery_hashes: drained.promise_subquery_hashes,
229        })
230    }
231}
232impl<T: Copy> TryFrom<Vec<T>> for LogicalPisVerifyComputeWithoutAccumulator<T> {
233    type Error = anyhow::Error;
234
235    fn try_from(value: Vec<T>) -> anyhow::Result<Self> {
236        if value.len() != NUM_LOGICAL_INSTANCE_WITHOUT_ACC {
237            bail!("wrong number of logical public instances without accumulator")
238        };
239        let source_chain_id = value[0];
240        let compute_results_hash = HiLo::from_hi_lo([value[1], value[2]]);
241        let query_hash = HiLo::from_hi_lo([value[3], value[4]]);
242        let query_schema = HiLo::from_hi_lo([value[5], value[6]]);
243        let results_root_poseidon = value[7];
244        let promise_subquery_hashes = value[8];
245        Ok(Self {
246            source_chain_id,
247            compute_results_hash,
248            query_hash,
249            query_schema,
250            results_root_poseidon,
251            promise_subquery_hashes,
252        })
253    }
254}
255
256impl_flatten_conversion!(LogicalPublicInstanceVerifyCompute, NUM_BITS_PER_FE);