axiom_query/components/results/
types.rs

1use std::{any::Any, marker::PhantomData};
2
3use anyhow::Result;
4use axiom_codec::{
5    types::{
6        field_elements::{AnySubqueryResult, FlattenedSubqueryResult},
7        native::{SubqueryResult, SubqueryType},
8    },
9    HiLo,
10};
11use axiom_eth::{
12    halo2_base::{
13        gates::{GateInstructions, RangeChip},
14        AssignedValue,
15    },
16    impl_flatten_conversion,
17    rlc::{chip::RlcChip, circuit::builder::RlcContextPair},
18    utils::{
19        build_utils::dummy::DummyFrom,
20        component::{
21            circuit::CoreBuilderInput,
22            promise_loader::{
23                flatten_witness_to_rlc,
24                multi::{ComponentTypeList, RlcAdapter},
25            },
26            types::{Flatten, LogicalEmpty},
27            utils::into_key,
28            ComponentType, ComponentTypeId, LogicalResult, PromiseCallWitness,
29            TypelessLogicalInput,
30        },
31        encode_h256_to_hilo,
32    },
33};
34use ethers_core::types::H256;
35use itertools::Itertools;
36use serde::{Deserialize, Serialize};
37
38use crate::{components::results::circuit::SubqueryDependencies, Field, RawField};
39
40use super::{
41    circuit::CoreParamsResultRoot,
42    table::{join::GroupedSubqueryResults, SubqueryResultsTable},
43};
44
45/// Component type for ResultsRoot component.
46pub struct ComponentTypeResultsRoot<F: Field>(PhantomData<F>);
47
48/// Logic inputs to Data Results and Calculate Subquery Hashes Circuit
49///
50/// Length of `data_query` fixed at compile time. True number of subqueries is `num_subqueries`.
51#[derive(Clone, Debug, Serialize, Deserialize)]
52pub struct CircuitInputResultsRootShard<F: RawField> {
53    /// The **ordered** subqueries to be verified, _with_ claimed result values.
54    /// This table may have been resized to some fixed length, known at compile time.
55    /// The actual subqueries will be the first `num_subqueries` rows of the table.
56    pub subqueries: SubqueryResultsTable<F>,
57    /// The number of true subqueries in the table.
58    pub num_subqueries: F,
59}
60
61impl<F: Field> DummyFrom<CoreParamsResultRoot> for CircuitInputResultsRootShard<F> {
62    fn dummy_from(core_params: CoreParamsResultRoot) -> Self {
63        let subqueries = SubqueryResultsTable {
64            rows: vec![FlattenedSubqueryResult::default(); core_params.capacity],
65        };
66        Self { subqueries, num_subqueries: F::ZERO }
67    }
68}
69
70/// Lengths of `results` and `subquery_hashes` are always equal.
71/// May include padding - `num_subqueries` is the actual number of subqueries.
72#[derive(Clone, Debug, Default, Serialize, Deserialize, Hash)]
73#[serde(rename_all = "camelCase")]
74pub struct LogicOutputResultsRoot {
75    pub results: Vec<SubqueryResult>,
76    pub subquery_hashes: Vec<H256>,
77    pub num_subqueries: usize,
78}
79
80/// Lengths of `results` and `subquery_hashes` must be equal.
81///
82#[derive(Clone, Debug, Default, Serialize, Deserialize)]
83pub struct CircuitOutputResultsRoot<F: RawField> {
84    pub results: SubqueryResultsTable<F>,
85    pub subquery_hashes: Vec<HiLo<F>>,
86    pub num_subqueries: usize,
87}
88
89impl<F: RawField> CircuitOutputResultsRoot<F> {
90    /// Resize itself to `new_len` by repeating the first row. Crash if the table is empty.
91    pub fn resize_with_first(&mut self, new_len: usize) {
92        self.results.rows.resize(new_len, self.results.rows[0]);
93        self.subquery_hashes.resize(new_len, self.subquery_hashes[0]);
94    }
95}
96
97// ==== Public Instances ====
98// 9999 means that the public instance takes a whole witness.
99const NUM_BITS_PER_FE: [usize; 2] = [9999, 9999];
100#[derive(Debug, Clone, PartialEq, Eq, Hash)]
101pub struct LogicalPublicInstanceResultsRoot<T> {
102    pub results_root_poseidon: T,
103    pub commit_subquery_hashes: T,
104}
105
106// All promise calls made by ResultsRoot component are Virtual: they will be managed by MultiPromiseLoader
107// The ResultsRoot component should never to called directly, so it has no virtual table as output.
108impl<F: Field> ComponentType<F> for ComponentTypeResultsRoot<F> {
109    type InputValue = LogicalEmpty<F>;
110    type InputWitness = LogicalEmpty<AssignedValue<F>>;
111    type OutputValue = LogicalEmpty<F>;
112    type OutputWitness = LogicalEmpty<AssignedValue<F>>;
113    type LogicalInput = LogicalEmpty<F>;
114
115    fn get_type_id() -> ComponentTypeId {
116        "axiom-query:ComponentTypeResultsRoot".to_string()
117    }
118
119    fn logical_result_to_virtual_rows_impl(
120        _ins: &LogicalResult<F, Self>,
121    ) -> Vec<(Self::InputValue, Self::OutputValue)> {
122        unreachable!()
123    }
124    fn logical_input_to_virtual_rows_impl(_li: &Self::LogicalInput) -> Vec<Self::InputValue> {
125        unreachable!()
126    }
127}
128
129impl LogicOutputResultsRoot {
130    pub fn new(
131        results: Vec<SubqueryResult>,
132        subquery_hashes: Vec<H256>,
133        num_subqueries: usize,
134    ) -> Self {
135        assert_eq!(results.len(), subquery_hashes.len());
136        Self { results, subquery_hashes, num_subqueries }
137    }
138}
139
140impl<F: Field> TryFrom<LogicOutputResultsRoot> for CircuitOutputResultsRoot<F> {
141    type Error = std::io::Error;
142    fn try_from(output: LogicOutputResultsRoot) -> Result<Self, Self::Error> {
143        let LogicOutputResultsRoot { results, subquery_hashes, num_subqueries } = output;
144        let rows = results
145            .into_iter()
146            .map(FlattenedSubqueryResult::<F>::try_from)
147            .collect::<Result<Vec<_>, _>>()?;
148        let results = SubqueryResultsTable { rows };
149        let subquery_hashes = subquery_hashes
150            .into_iter()
151            .map(|subquery_hash| encode_h256_to_hilo(&subquery_hash))
152            .collect();
153        Ok(Self { results, subquery_hashes, num_subqueries })
154    }
155}
156
157// ============== LogicalPublicInstanceResultsRoot ==============
158impl<T: Copy> TryFrom<Vec<T>> for LogicalPublicInstanceResultsRoot<T> {
159    type Error = anyhow::Error;
160
161    fn try_from(value: Vec<T>) -> anyhow::Result<Self> {
162        let [results_root_poseidon, commit_subquery_hashes] =
163            value.try_into().map_err(|_| anyhow::anyhow!("invalid length"))?;
164        Ok(Self { results_root_poseidon, commit_subquery_hashes })
165    }
166}
167impl<T: Copy> LogicalPublicInstanceResultsRoot<T> {
168    pub fn flatten(&self) -> [T; 2] {
169        [self.results_root_poseidon, self.commit_subquery_hashes]
170    }
171}
172impl_flatten_conversion!(LogicalPublicInstanceResultsRoot, NUM_BITS_PER_FE);
173
174// ======== Types for component implementation ==========
175
176/// RLC adapter for MultiPromiseLoader of results root component.
177pub struct RlcAdapterResultsRoot<F>(PhantomData<F>);
178impl<F: Field> RlcAdapter<F> for RlcAdapterResultsRoot<F> {
179    fn to_rlc(
180        ctx_pair: RlcContextPair<F>,
181        gate: &impl GateInstructions<F>,
182        rlc: &RlcChip<F>,
183        component_type_id: &ComponentTypeId,
184        io_pairs: &[(Flatten<AssignedValue<F>>, Flatten<AssignedValue<F>>)],
185    ) -> Vec<AssignedValue<F>> {
186        let subquery_type = component_type_id_to_subquery_type::<F>(component_type_id).unwrap();
187
188        let subqueries = GroupedSubqueryResults {
189            subquery_type,
190            results: io_pairs
191                .iter()
192                .map(|(i, o)| AnySubqueryResult {
193                    subquery: i.fields.clone(),
194                    value: o.fields.clone(),
195                })
196                .collect_vec(),
197        };
198        subqueries.into_rlc(ctx_pair, gate, rlc).concat()
199    }
200}
201
202/// Virtual component type for MultiPromiseLoader
203pub struct VirtualComponentType<F: Field>(PhantomData<F>);
204
205impl<F: Field> ComponentType<F> for VirtualComponentType<F> {
206    type InputValue = FlattenedSubqueryResult<F>;
207    type InputWitness = FlattenedSubqueryResult<AssignedValue<F>>;
208    type OutputValue = LogicalEmpty<F>;
209    type OutputWitness = LogicalEmpty<AssignedValue<F>>;
210    type LogicalInput = LogicalEmpty<F>;
211
212    fn get_type_id() -> ComponentTypeId {
213        "axiom-query:VirtualComponentTypeResultsRoot".to_string()
214    }
215
216    fn logical_result_to_virtual_rows_impl(
217        _ins: &LogicalResult<F, Self>,
218    ) -> Vec<(Self::InputValue, Self::OutputValue)> {
219        unreachable!()
220    }
221    fn logical_input_to_virtual_rows_impl(_li: &Self::LogicalInput) -> Vec<Self::InputValue> {
222        unreachable!()
223    }
224}
225
226#[derive(Clone, Copy, Debug)]
227pub struct SubqueryResultCall<F: Field>(pub FlattenedSubqueryResult<AssignedValue<F>>);
228
229impl<F: Field> PromiseCallWitness<F> for SubqueryResultCall<F> {
230    fn get_component_type_id(&self) -> ComponentTypeId {
231        VirtualComponentType::<F>::get_type_id()
232    }
233    fn get_capacity(&self) -> usize {
234        // virtual call so no consumption
235        0
236    }
237    fn to_rlc(
238        &self,
239        (_, rlc_ctx): RlcContextPair<F>,
240        _range_chip: &RangeChip<F>,
241        rlc_chip: &RlcChip<F>,
242    ) -> AssignedValue<F> {
243        flatten_witness_to_rlc(rlc_ctx, rlc_chip, &self.0.into())
244    }
245    fn to_typeless_logical_input(&self) -> TypelessLogicalInput {
246        let f_a: Flatten<AssignedValue<F>> = self.0.into();
247        let f_v: Flatten<F> = f_a.into();
248        let l_v: FlattenedSubqueryResult<F> = f_v.try_into().unwrap();
249        into_key(l_v)
250    }
251    fn get_mock_output(&self) -> Flatten<F> {
252        let output_val: <VirtualComponentType<F> as ComponentType<F>>::OutputValue =
253            Default::default();
254        output_val.into()
255    }
256    fn as_any(&self) -> &dyn Any {
257        self
258    }
259}
260
261// ======== Boilerplate conversions ========
262
263/// Mapping from component type id to subquery type.
264pub fn component_type_id_to_subquery_type<F: Field>(
265    type_id: &ComponentTypeId,
266) -> Option<SubqueryType> {
267    // This cannot be static because of <F>
268    let type_ids = SubqueryDependencies::<F>::get_component_type_ids();
269    type_ids
270        .iter()
271        .position(|id| id == type_id)
272        .map(|i| SubqueryType::try_from(i as u16 + 1).unwrap())
273}