1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
use std::sync::{Arc, Mutex};

use axiom_circuit::{
    axiom_codec::HiLo,
    axiom_eth::{
        halo2_base::{gates::RangeChip, safe_types::SafeTypeChip, AssignedValue, Context},
        keccak::promise::{KeccakFixLenCall, KeccakVarLenCall},
        rlc::circuit::builder::RlcCircuitBuilder,
        utils::uint_to_bytes_be,
    },
    subquery::caller::SubqueryCaller,
    utils::{from_hi_lo, to_hi_lo},
};
use ethers::providers::Http;

use crate::{
    subquery::{
        account::{get_account, Account},
        header::{get_header, Header},
        mapping::{get_mapping, SolidityMapping},
        receipt::{get_receipt, Receipt},
        storage::{get_storage, Storage},
        tx::{get_tx, Tx},
    },
    Fr,
};

/// Axiom Circuit API for making both subquery calls (e.g. `get_account`, `get_header`, etc.) and for more general ZK primitives (e.g. `add`, `mul`, etc.).
pub struct AxiomAPI<'a> {
    /// The `halo2-lib` struct used to construct the circuit
    pub builder: &'a mut RlcCircuitBuilder<Fr>,
    /// The main chip for ZK primitives
    pub range: &'a RangeChip<Fr>,
    /// The struct that manages all subquery calls
    subquery_caller: Arc<Mutex<SubqueryCaller<Http, Fr>>>,
}

impl<'a> AxiomAPI<'a> {
    pub fn new(
        builder: &'a mut RlcCircuitBuilder<Fr>,
        range: &'a RangeChip<Fr>,
        subquery_caller: Arc<Mutex<SubqueryCaller<Http, Fr>>>,
    ) -> Self {
        Self {
            builder,
            range,
            subquery_caller,
        }
    }

    /// Returns a thread-safe [SubqueryCaller] object.
    pub fn subquery_caller(&self) -> Arc<Mutex<SubqueryCaller<Http, Fr>>> {
        self.subquery_caller.clone()
    }

    /// Returns a mutable reference to the [Context] of a gate thread. Spawns a new thread for the given phase, if none exists.
    /// * `phase`: The challenge phase (as an index) of the gate thread.
    pub fn ctx(&mut self) -> &mut Context<Fr> {
        self.builder.base.main(0)
    }

    /// Returns an `AssignedValue<Fr>` from a `HiLo<AssignedValue<Fr>>`.
    ///
    /// NOTE: this can fail if the hi-lo pair is greater than the `Fr` modulus. See `check_hi_lo` for what is constrained.
    ///
    /// * `hilo` - The `HiLo<AssignedValue<Fr>>` object to convert.
    pub fn from_hi_lo(&mut self, hilo: HiLo<AssignedValue<Fr>>) -> AssignedValue<Fr> {
        let ctx = self.builder.base.main(0);
        from_hi_lo(ctx, self.range, hilo)
    }

    /// Returns a 256-bit `HiLo<AssignedValue<Fr>>` from a `AssignedValue<Fr>`.
    ///
    /// See `check_hi_lo` for what is constrained.
    ///
    /// * `val` - The `AssignedValue<Fr>` object to convert.
    pub fn to_hi_lo(&mut self, val: AssignedValue<Fr>) -> HiLo<AssignedValue<Fr>> {
        let ctx = self.builder.base.main(0);
        to_hi_lo(ctx, self.range, val)
    }

    /// Decomposes a `AssignedValue<Fr>` into bytes, in big-endian, and returns the bytes.
    ///
    /// * `uint` - The `AssignedValue<Fr>` object to convert.
    /// * `num_bytes` - The number of bytes in `uint`.
    pub fn to_bytes_be(
        &mut self,
        uint: AssignedValue<Fr>,
        num_bytes: usize,
    ) -> Vec<AssignedValue<Fr>> {
        let ctx = self.builder.base.main(0);
        uint_to_bytes_be(ctx, self.range, &uint, num_bytes)
            .iter()
            .map(|x| *x.as_ref())
            .collect()
    }

    /// Returns an [Account] builder given block number and address.
    ///
    /// * `block_number` - The block number as an `AssignedValue<Fr>`.
    /// * `addr` - The address as an `AssignedValue<Fr>`.
    pub fn get_account(
        &mut self,
        block_number: AssignedValue<Fr>,
        addr: AssignedValue<Fr>,
    ) -> Account {
        let ctx = self.builder.base.main(0);
        get_account(ctx, self.subquery_caller.clone(), block_number, addr)
    }

    /// Returns a [Header] builder given block number.
    ///
    /// * `block_number` - The block number as an `AssignedValue<Fr>`.
    pub fn get_header(&mut self, block_number: AssignedValue<Fr>) -> Header {
        let ctx = self.builder.base.main(0);
        get_header(ctx, self.subquery_caller.clone(), block_number)
    }

    /// Returns a [SolidityMapping] builder given block number, address, and mapping slot.
    ///
    /// * `block_number` - The block number as an `AssignedValue<Fr>`.
    /// * `addr` - The address as an `AssignedValue<Fr>`.
    /// * `mapping_slot` - The mapping slot as a `HiLo<AssignedValue<Fr>`.
    pub fn get_mapping(
        &mut self,
        block_number: AssignedValue<Fr>,
        addr: AssignedValue<Fr>,
        mapping_slot: HiLo<AssignedValue<Fr>>,
    ) -> SolidityMapping {
        let ctx = self.builder.base.main(0);
        get_mapping(
            ctx,
            self.subquery_caller.clone(),
            block_number,
            addr,
            mapping_slot,
        )
    }

    /// Returns a [Receipt] builder given block number and transaction index.
    ///
    /// * `block_number` - The block number as an `AssignedValue<Fr>`.
    /// * `tx_idx` - The transaction index as an `AssignedValue<Fr>`.
    pub fn get_receipt(
        &mut self,
        block_number: AssignedValue<Fr>,
        tx_idx: AssignedValue<Fr>,
    ) -> Receipt {
        let ctx = self.builder.base.main(0);
        get_receipt(ctx, self.subquery_caller.clone(), block_number, tx_idx)
    }

    /// Returns a [Storage] builder given block number and address.
    ///
    /// * `block_number` - The block number as an `AssignedValue<Fr>`.
    /// * `addr` - The address as an `AssignedValue<Fr>`.
    pub fn get_storage(
        &mut self,
        block_number: AssignedValue<Fr>,
        addr: AssignedValue<Fr>,
    ) -> Storage {
        let ctx = self.builder.base.main(0);
        get_storage(ctx, self.subquery_caller.clone(), block_number, addr)
    }

    /// Returns a [Tx] builder given block number and transaction index.
    ///
    /// * `block_number` - The block number as an `AssignedValue<Fr>`.
    /// * `tx_idx` - The transaction index as an `AssignedValue<Fr>`.
    pub fn get_tx(&mut self, block_number: AssignedValue<Fr>, tx_idx: AssignedValue<Fr>) -> Tx {
        let ctx = self.builder.base.main(0);
        get_tx(ctx, self.subquery_caller.clone(), block_number, tx_idx)
    }

    pub fn keccak_fix_len(&mut self, bytes: Vec<AssignedValue<Fr>>) -> HiLo<AssignedValue<Fr>> {
        let ctx = self.builder.base.main(0);
        let subquery_caller = self.subquery_caller.clone();
        let mut subquery_caller = subquery_caller.lock().unwrap();

        let safe_type_chip = SafeTypeChip::new(self.range);
        let len = bytes.len();
        let bytes = safe_type_chip.raw_to_fix_len_bytes_vec(ctx, bytes, len);

        subquery_caller.keccak(ctx, KeccakFixLenCall::new(bytes))
    }

    pub fn keccak_var_len(
        &mut self,
        bytes: Vec<AssignedValue<Fr>>,
        len: AssignedValue<Fr>,
        max_len: usize,
    ) -> HiLo<AssignedValue<Fr>> {
        let ctx = self.builder.base.main(0);
        let subquery_caller = self.subquery_caller.clone();
        let mut subquery_caller = subquery_caller.lock().unwrap();

        let safe_type_chip = SafeTypeChip::new(self.range);
        let bytes = safe_type_chip.raw_to_var_len_bytes_vec(ctx, bytes, len, max_len);

        subquery_caller.keccak(ctx, KeccakVarLenCall::new(bytes, 0))
    }

    pub fn keccak_fix_len_unsafe(
        &mut self,
        bytes: Vec<AssignedValue<Fr>>,
    ) -> HiLo<AssignedValue<Fr>> {
        let ctx = self.builder.base.main(0);
        let subquery_caller = self.subquery_caller.clone();
        let mut subquery_caller = subquery_caller.lock().unwrap();

        let len = bytes.len();
        let bytes = SafeTypeChip::unsafe_to_fix_len_bytes_vec(bytes, len);

        subquery_caller.keccak(ctx, KeccakFixLenCall::new(bytes))
    }

    pub fn keccak_var_len_unsafe(
        &mut self,
        bytes: Vec<AssignedValue<Fr>>,
        len: AssignedValue<Fr>,
        max_len: usize,
    ) -> HiLo<AssignedValue<Fr>> {
        let ctx = self.builder.base.main(0);
        let subquery_caller = self.subquery_caller.clone();
        let mut subquery_caller = subquery_caller.lock().unwrap();

        let bytes = SafeTypeChip::unsafe_to_var_len_bytes_vec(bytes, len, max_len);

        subquery_caller.keccak(ctx, KeccakVarLenCall::new(bytes, 0))
    }
}