axiom_codec/encoder/
field_elements.rs

1use std::io;
2
3use ethers_core::types::{Bytes, H256};
4
5use crate::{
6    constants::*,
7    types::field_elements::*,
8    types::native::*,
9    utils::native::{encode_addr_to_field, encode_h256_to_hilo, encode_u256_to_hilo},
10    Field,
11};
12
13pub const NUM_FE_HEADER: usize = 2;
14pub const NUM_FE_ACCOUNT: usize = 3;
15pub const NUM_FE_STORAGE: usize = 4;
16pub const NUM_FE_TX: usize = 3;
17pub const NUM_FE_RECEIPT: usize = 6;
18pub const NUM_FE_SOLIDITY_NESTED_MAPPING: usize =
19    NUM_FE_SOLIDITY_NESTED_MAPPING_WITHOUT_KEYS + MAX_SOLIDITY_MAPPING_KEYS * 2;
20pub const NUM_FE_SOLIDITY_NESTED_MAPPING_WITHOUT_KEYS: usize = 5;
21pub const NUM_FE_SOLIDITY_NESTED_MAPPING_MIN: usize = 7; // assumes >=1 key
22pub const NUM_FE_ANY: [usize; NUM_SUBQUERY_TYPES] = [
23    0,
24    NUM_FE_HEADER,
25    NUM_FE_ACCOUNT,
26    NUM_FE_STORAGE,
27    NUM_FE_TX,
28    NUM_FE_RECEIPT,
29    NUM_FE_SOLIDITY_NESTED_MAPPING,
30];
31
32/// The index of the mapping depth in [`FieldSolidityNestedMappingSubquery`].
33pub const FIELD_SOLIDITY_NESTED_MAPPING_DEPTH_IDX: usize = 4;
34/// The index of the mapping depth in [`SubqueryKey`], where the first index holds the subquery type.
35pub const FIELD_ENCODED_SOLIDITY_NESTED_MAPPING_DEPTH_IDX: usize =
36    FIELD_SOLIDITY_NESTED_MAPPING_DEPTH_IDX + 1;
37
38// The following constants describe how to convert from the field element
39// encoding into the bytes encoding, by specifying the fixed width bytes each field
40// element represented (in big endian order).
41pub const BYTES_PER_FE_HEADER: [usize; NUM_FE_HEADER] = [4, 4];
42pub const BITS_PER_FE_HEADER: [usize; NUM_FE_HEADER] = [32, 32];
43pub const BYTES_PER_FE_ACCOUNT: [usize; NUM_FE_ACCOUNT] = [4, 20, 4];
44pub const BITS_PER_FE_ACCOUNT: [usize; NUM_FE_ACCOUNT] = [32, 160, 32];
45pub const BYTES_PER_FE_STORAGE: [usize; NUM_FE_STORAGE] = [4, 20, 16, 16];
46pub const BITS_PER_FE_STORAGE: [usize; NUM_FE_STORAGE] = [32, 160, 128, 128];
47pub const BYTES_PER_FE_TX: [usize; NUM_FE_TX] = [4, 2, 4];
48pub const BITS_PER_FE_TX: [usize; NUM_FE_TX] = [32, 16, 32];
49pub const BYTES_PER_FE_RECEIPT: [usize; NUM_FE_RECEIPT] = [4, 2, 4, 4, 16, 16];
50pub const BITS_PER_FE_RECEIPT: [usize; NUM_FE_RECEIPT] = [32, 16, 32, 32, 128, 128];
51pub const BYTES_PER_FE_SOLIDITY_NESTED_MAPPING: [usize; NUM_FE_SOLIDITY_NESTED_MAPPING] =
52    bytes_per_fe_solidity_nested_mapping();
53pub const BITS_PER_FE_SOLIDITY_NESTED_MAPPING: [usize; NUM_FE_SOLIDITY_NESTED_MAPPING] =
54    bits_per_fe_solidity_nested_mapping();
55pub const BYTES_PER_FE_ANY: [&[usize]; NUM_SUBQUERY_TYPES] = [
56    &[],
57    &BYTES_PER_FE_HEADER,
58    &BYTES_PER_FE_ACCOUNT,
59    &BYTES_PER_FE_STORAGE,
60    &BYTES_PER_FE_TX,
61    &BYTES_PER_FE_RECEIPT,
62    &BYTES_PER_FE_SOLIDITY_NESTED_MAPPING,
63];
64pub const BYTES_PER_FE_SUBQUERY_OUTPUT: usize = 16;
65
66pub const BITS_PER_FE_SUBQUERY_RESULT: [usize; SUBQUERY_RESULT_LEN] = [128; SUBQUERY_RESULT_LEN];
67pub const SUBQUERY_OUTPUT_BYTES: usize = MAX_SUBQUERY_OUTPUTS * BYTES_PER_FE_SUBQUERY_OUTPUT;
68
69const fn bytes_per_fe_solidity_nested_mapping() -> [usize; NUM_FE_SOLIDITY_NESTED_MAPPING] {
70    let mut bytes_per = [16; MAX_SUBQUERY_INPUTS];
71    bytes_per[0] = 4;
72    bytes_per[1] = 20;
73    bytes_per[2] = 16;
74    bytes_per[3] = 16;
75    bytes_per[4] = 1;
76    bytes_per
77}
78const fn bits_per_fe_solidity_nested_mapping() -> [usize; NUM_FE_SOLIDITY_NESTED_MAPPING] {
79    let mut bits_per = [128; MAX_SUBQUERY_INPUTS];
80    bits_per[0] = 32;
81    bits_per[1] = 160;
82    bits_per[2] = 128;
83    bits_per[3] = 128;
84    bits_per[4] = 8;
85    bits_per
86}
87
88pub const BITS_PER_FE_SUBQUERY_OUTPUT: usize = BYTES_PER_FE_SUBQUERY_OUTPUT * 8;
89
90impl<F: Field> From<H256> for SubqueryOutput<F> {
91    fn from(value: H256) -> Self {
92        let mut output = [F::ZERO; MAX_SUBQUERY_OUTPUTS];
93        let hilo = encode_h256_to_hilo(&value);
94        output[0] = hilo.hi();
95        output[1] = hilo.lo();
96        Self(output)
97    }
98}
99
100impl<F: Field> TryFrom<Bytes> for SubqueryOutput<F> {
101    type Error = io::Error;
102    fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
103        if bytes.len() != 32 {
104            Err(io::Error::new(io::ErrorKind::InvalidData, "result length is not 32"))
105        } else {
106            let result = H256::from_slice(&bytes[..]);
107            let hilo = encode_h256_to_hilo(&result);
108            let mut result = [F::ZERO; MAX_SUBQUERY_OUTPUTS];
109            result[0] = hilo.hi();
110            result[1] = hilo.lo();
111            Ok(Self(result))
112        }
113    }
114}
115
116impl<F: Field> TryFrom<SubqueryResult> for FlattenedSubqueryResult<F> {
117    type Error = io::Error;
118    fn try_from(result: SubqueryResult) -> Result<Self, Self::Error> {
119        let result: FieldSubqueryResult<F> = result.try_into()?;
120        Ok(result.into())
121    }
122}
123
124impl<F: Field> TryFrom<SubqueryResult> for FieldSubqueryResult<F> {
125    type Error = io::Error;
126    fn try_from(result: SubqueryResult) -> Result<Self, Self::Error> {
127        let subquery = result.subquery.try_into()?;
128        let value = result.value.try_into()?;
129        Ok(FieldSubqueryResult { subquery, value })
130    }
131}
132
133impl<F: Field> TryFrom<Subquery> for FieldSubquery<F> {
134    type Error = io::Error;
135    fn try_from(subquery: Subquery) -> Result<Self, Self::Error> {
136        let subquery = AnySubquery::try_from(subquery)?;
137        Ok(subquery.into())
138    }
139}
140
141impl<F: Field> From<AnySubquery> for FieldSubquery<F> {
142    fn from(subquery: AnySubquery) -> Self {
143        match subquery {
144            AnySubquery::Null => FieldSubquery {
145                subquery_type: SubqueryType::Null,
146                encoded_subquery_data: Default::default(),
147            },
148            AnySubquery::Header(subquery) => FieldHeaderSubquery::from(subquery).into(),
149            AnySubquery::Account(subquery) => FieldAccountSubquery::from(subquery).into(),
150            AnySubquery::Storage(subquery) => FieldStorageSubquery::from(subquery).into(),
151            AnySubquery::Transaction(subquery) => FieldTxSubquery::from(subquery).into(),
152            AnySubquery::Receipt(subquery) => FieldReceiptSubquery::from(subquery).into(),
153            AnySubquery::SolidityNestedMapping(subquery) => {
154                FieldSolidityNestedMappingSubquery::from(subquery).into()
155            }
156        }
157    }
158}
159
160impl<F: Field> From<HeaderSubquery> for FieldHeaderSubquery<F> {
161    fn from(subquery: HeaderSubquery) -> Self {
162        Self {
163            block_number: F::from(subquery.block_number as u64),
164            field_idx: F::from(subquery.field_idx as u64),
165        }
166    }
167}
168
169impl<T> FieldHeaderSubquery<T> {
170    pub fn flatten(self) -> [T; NUM_FE_HEADER] {
171        [self.block_number, self.field_idx]
172    }
173}
174
175impl<F: Field> From<FieldHeaderSubquery<F>> for FieldSubquery<F> {
176    fn from(subquery: FieldHeaderSubquery<F>) -> Self {
177        let mut encoded_subquery_data = [F::ZERO; MAX_SUBQUERY_INPUTS];
178        encoded_subquery_data[..NUM_FE_HEADER].copy_from_slice(&subquery.flatten());
179        Self { subquery_type: SubqueryType::Header, encoded_subquery_data }
180    }
181}
182
183impl<F: Field> From<AccountSubquery> for FieldAccountSubquery<F> {
184    fn from(subquery: AccountSubquery) -> Self {
185        Self {
186            block_number: F::from(subquery.block_number as u64),
187            addr: encode_addr_to_field(&subquery.addr),
188            field_idx: F::from(subquery.field_idx as u64),
189        }
190    }
191}
192
193impl<T> FieldAccountSubquery<T> {
194    pub fn flatten(self) -> [T; NUM_FE_ACCOUNT] {
195        [self.block_number, self.addr, self.field_idx]
196    }
197}
198
199impl<F: Field> From<FieldAccountSubquery<F>> for FieldSubquery<F> {
200    fn from(value: FieldAccountSubquery<F>) -> Self {
201        let mut encoded_subquery_data = [F::ZERO; MAX_SUBQUERY_INPUTS];
202        encoded_subquery_data[..NUM_FE_ACCOUNT].copy_from_slice(&value.flatten());
203        Self { subquery_type: SubqueryType::Account, encoded_subquery_data }
204    }
205}
206
207impl<F: Field> From<StorageSubquery> for FieldStorageSubquery<F> {
208    fn from(subquery: StorageSubquery) -> Self {
209        Self {
210            block_number: F::from(subquery.block_number as u64),
211            addr: encode_addr_to_field(&subquery.addr),
212            slot: encode_u256_to_hilo(&subquery.slot),
213        }
214    }
215}
216
217impl<T: Copy> FieldStorageSubquery<T> {
218    pub fn flatten(self) -> [T; NUM_FE_STORAGE] {
219        [self.block_number, self.addr, self.slot.hi(), self.slot.lo()]
220    }
221}
222
223impl<F: Field> From<FieldStorageSubquery<F>> for FieldSubquery<F> {
224    fn from(value: FieldStorageSubquery<F>) -> Self {
225        let mut encoded_subquery_data = [F::ZERO; MAX_SUBQUERY_INPUTS];
226        encoded_subquery_data[..NUM_FE_STORAGE].copy_from_slice(&value.flatten());
227        Self { subquery_type: SubqueryType::Storage, encoded_subquery_data }
228    }
229}
230
231impl<F: Field> From<TxSubquery> for FieldTxSubquery<F> {
232    fn from(subquery: TxSubquery) -> Self {
233        Self {
234            block_number: F::from(subquery.block_number as u64),
235            tx_idx: F::from(subquery.tx_idx as u64),
236            field_or_calldata_idx: F::from(subquery.field_or_calldata_idx as u64),
237        }
238    }
239}
240
241impl<T> FieldTxSubquery<T> {
242    pub fn flatten(self) -> [T; NUM_FE_TX] {
243        [self.block_number, self.tx_idx, self.field_or_calldata_idx]
244    }
245}
246
247impl<F: Field> From<FieldTxSubquery<F>> for FieldSubquery<F> {
248    fn from(value: FieldTxSubquery<F>) -> Self {
249        let mut encoded_subquery_data = [F::ZERO; MAX_SUBQUERY_INPUTS];
250        encoded_subquery_data[..NUM_FE_TX].copy_from_slice(&value.flatten());
251        Self { subquery_type: SubqueryType::Transaction, encoded_subquery_data }
252    }
253}
254
255impl<F: Field> From<ReceiptSubquery> for FieldReceiptSubquery<F> {
256    fn from(subquery: ReceiptSubquery) -> Self {
257        Self {
258            block_number: F::from(subquery.block_number as u64),
259            tx_idx: F::from(subquery.tx_idx as u64),
260            field_or_log_idx: F::from(subquery.field_or_log_idx as u64),
261            topic_or_data_or_address_idx: F::from(subquery.topic_or_data_or_address_idx as u64),
262            event_schema: encode_h256_to_hilo(&subquery.event_schema),
263        }
264    }
265}
266
267impl<T: Copy> FieldReceiptSubquery<T> {
268    pub fn flatten(self) -> [T; NUM_FE_RECEIPT] {
269        [
270            self.block_number,
271            self.tx_idx,
272            self.field_or_log_idx,
273            self.topic_or_data_or_address_idx,
274            self.event_schema.hi(),
275            self.event_schema.lo(),
276        ]
277    }
278}
279
280impl<F: Field> From<FieldReceiptSubquery<F>> for FieldSubquery<F> {
281    fn from(value: FieldReceiptSubquery<F>) -> Self {
282        let mut encoded_subquery_data = [F::ZERO; MAX_SUBQUERY_INPUTS];
283        encoded_subquery_data[..NUM_FE_RECEIPT].copy_from_slice(&value.flatten());
284        Self { subquery_type: SubqueryType::Receipt, encoded_subquery_data }
285    }
286}
287
288impl<F: Field> From<SolidityNestedMappingSubquery> for FieldSolidityNestedMappingSubquery<F> {
289    fn from(mut subquery: SolidityNestedMappingSubquery) -> Self {
290        assert!(subquery.keys.len() <= MAX_SOLIDITY_MAPPING_KEYS);
291        subquery.keys.resize(MAX_SOLIDITY_MAPPING_KEYS, H256::zero());
292
293        Self {
294            block_number: F::from(subquery.block_number as u64),
295            addr: encode_addr_to_field(&subquery.addr),
296            mapping_slot: encode_u256_to_hilo(&subquery.mapping_slot),
297            mapping_depth: F::from(subquery.mapping_depth as u64),
298            keys: subquery
299                .keys
300                .iter()
301                .map(|k| encode_h256_to_hilo(k))
302                .collect::<Vec<_>>()
303                .try_into()
304                .unwrap(),
305        }
306    }
307}
308
309impl<T: Copy> FieldSolidityNestedMappingSubquery<T> {
310    pub fn flatten(self) -> [T; NUM_FE_SOLIDITY_NESTED_MAPPING] {
311        assert_eq!(5 + self.keys.len() * 2, NUM_FE_SOLIDITY_NESTED_MAPPING);
312        let mut result = [self.block_number; NUM_FE_SOLIDITY_NESTED_MAPPING]; // default will be overwritten in all indices so doesn't matter
313        result[0] = self.block_number;
314        result[1] = self.addr;
315        result[2] = self.mapping_slot.hi();
316        result[3] = self.mapping_slot.lo();
317        result[4] = self.mapping_depth;
318        for (i, key) in self.keys.iter().enumerate() {
319            result[5 + i * 2] = key.hi();
320            result[5 + i * 2 + 1] = key.lo();
321        }
322        result
323    }
324}
325
326impl<F: Field> From<FieldSolidityNestedMappingSubquery<F>> for FieldSubquery<F> {
327    fn from(value: FieldSolidityNestedMappingSubquery<F>) -> Self {
328        let mut encoded_subquery_data = [F::ZERO; MAX_SUBQUERY_INPUTS];
329        encoded_subquery_data[..NUM_FE_SOLIDITY_NESTED_MAPPING].copy_from_slice(&value.flatten());
330        Self { subquery_type: SubqueryType::SolidityNestedMapping, encoded_subquery_data }
331    }
332}