cherry_ingest/
svm.rs

1use anyhow::{anyhow, Context, Result};
2use serde::Serialize;
3
4#[derive(Default, Debug, Clone)]
5#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
6pub struct Query {
7    pub from_block: u64,
8    pub to_block: Option<u64>,
9    pub include_all_blocks: bool,
10    pub fields: Fields,
11    pub instructions: Vec<InstructionRequest>,
12    pub transactions: Vec<TransactionRequest>,
13    pub logs: Vec<LogRequest>,
14    pub balances: Vec<BalanceRequest>,
15    pub token_balances: Vec<TokenBalanceRequest>,
16    pub rewards: Vec<RewardRequest>,
17}
18
19#[derive(Debug, Clone, Copy)]
20pub struct Address(pub [u8; 32]);
21
22#[derive(Debug, Clone, Copy)]
23pub struct D1(pub [u8; 1]);
24
25#[derive(Debug, Clone, Copy)]
26pub struct D2(pub [u8; 2]);
27
28#[derive(Debug, Clone, Copy)]
29pub struct D3(pub [u8; 3]);
30
31#[derive(Debug, Clone, Copy)]
32pub struct D4(pub [u8; 4]);
33
34#[derive(Debug, Clone, Copy)]
35pub struct D8(pub [u8; 8]);
36
37#[cfg(feature = "pyo3")]
38fn extract_base58<const N: usize>(ob: &pyo3::Bound<'_, pyo3::PyAny>) -> pyo3::PyResult<[u8; N]> {
39    use pyo3::types::PyAnyMethods;
40
41    let s: &str = ob.extract()?;
42    let mut out = [0; N];
43
44    bs58::decode(s)
45        .with_alphabet(bs58::Alphabet::BITCOIN)
46        .onto(&mut out)
47        .context("decode base58")?;
48
49    Ok(out)
50}
51
52#[cfg(feature = "pyo3")]
53impl<'py> pyo3::FromPyObject<'py> for Address {
54    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
55        let out = extract_base58(ob)?;
56        Ok(Self(out))
57    }
58}
59
60#[cfg(feature = "pyo3")]
61impl<'py> pyo3::FromPyObject<'py> for D1 {
62    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
63        let out = extract_base58(ob)?;
64        Ok(Self(out))
65    }
66}
67
68#[cfg(feature = "pyo3")]
69impl<'py> pyo3::FromPyObject<'py> for D2 {
70    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
71        let out = extract_base58(ob)?;
72        Ok(Self(out))
73    }
74}
75
76#[cfg(feature = "pyo3")]
77impl<'py> pyo3::FromPyObject<'py> for D3 {
78    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
79        let out = extract_base58(ob)?;
80        Ok(Self(out))
81    }
82}
83
84#[cfg(feature = "pyo3")]
85impl<'py> pyo3::FromPyObject<'py> for D4 {
86    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
87        let out = extract_base58(ob)?;
88        Ok(Self(out))
89    }
90}
91
92#[cfg(feature = "pyo3")]
93impl<'py> pyo3::FromPyObject<'py> for D8 {
94    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
95        let out = extract_base58(ob)?;
96        Ok(Self(out))
97    }
98}
99
100#[derive(Default, Debug, Clone)]
101#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
102pub struct InstructionRequest {
103    pub program_id: Vec<Address>,
104    pub d1: Vec<D1>,
105    pub d2: Vec<D2>,
106    pub d3: Vec<D3>,
107    pub d4: Vec<D4>,
108    pub d8: Vec<D8>,
109    pub a0: Vec<Address>,
110    pub a1: Vec<Address>,
111    pub a2: Vec<Address>,
112    pub a3: Vec<Address>,
113    pub a4: Vec<Address>,
114    pub a5: Vec<Address>,
115    pub a6: Vec<Address>,
116    pub a7: Vec<Address>,
117    pub a8: Vec<Address>,
118    pub a9: Vec<Address>,
119    pub is_committed: bool,
120    pub include_transactions: bool,
121    pub include_transaction_token_balances: bool,
122    pub include_logs: bool,
123    pub include_inner_instructions: bool,
124    pub include_blocks: bool,
125}
126
127#[derive(Default, Debug, Clone)]
128#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
129pub struct TransactionRequest {
130    pub fee_payer: Vec<Address>,
131    pub include_instructions: bool,
132    pub include_logs: bool,
133    pub include_blocks: bool,
134}
135
136#[derive(Default, Debug, Clone)]
137#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
138pub struct LogRequest {
139    pub program_id: Vec<Address>,
140    pub kind: Vec<LogKind>,
141    pub include_transactions: bool,
142    pub include_instructions: bool,
143    pub include_blocks: bool,
144}
145
146#[derive(Debug, Clone, Copy)]
147pub enum LogKind {
148    Log,
149    Data,
150    Other,
151}
152
153impl LogKind {
154    pub fn as_str(&self) -> &str {
155        match self {
156            Self::Log => "log",
157            Self::Data => "data",
158            Self::Other => "other",
159        }
160    }
161
162    pub fn from_str(s: &str) -> Result<Self> {
163        match s {
164            "log" => Ok(Self::Log),
165            "data" => Ok(Self::Data),
166            "other" => Ok(Self::Other),
167            _ => Err(anyhow!("unknown log kind: {}", s)),
168        }
169    }
170}
171
172#[cfg(feature = "pyo3")]
173impl<'py> pyo3::FromPyObject<'py> for LogKind {
174    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
175        use pyo3::types::PyAnyMethods;
176
177        let s: &str = ob.extract().context("extract string")?;
178
179        Ok(Self::from_str(s).context("from str")?)
180    }
181}
182
183#[derive(Default, Debug, Clone)]
184#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
185pub struct BalanceRequest {
186    pub account: Vec<Address>,
187    pub include_transactions: bool,
188    pub include_transaction_instructions: bool,
189    pub include_blocks: bool,
190}
191
192#[derive(Default, Debug, Clone)]
193#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
194pub struct TokenBalanceRequest {
195    pub account: Vec<Address>,
196    pub pre_program_id: Vec<Address>,
197    pub post_program_id: Vec<Address>,
198    pub pre_mint: Vec<Address>,
199    pub post_mint: Vec<Address>,
200    pub pre_owner: Vec<Address>,
201    pub post_owner: Vec<Address>,
202    pub include_transactions: bool,
203    pub include_transaction_instructions: bool,
204    pub include_blocks: bool,
205}
206
207#[derive(Default, Debug, Clone)]
208#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
209pub struct RewardRequest {
210    pub pubkey: Vec<Address>,
211    pub include_blocks: bool,
212}
213
214#[derive(Serialize, Default, Debug, Clone, Copy)]
215#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
216pub struct Fields {
217    pub instruction: InstructionFields,
218    pub transaction: TransactionFields,
219    pub log: LogFields,
220    pub balance: BalanceFields,
221    pub token_balance: TokenBalanceFields,
222    pub reward: RewardFields,
223    pub block: BlockFields,
224}
225
226impl Fields {
227    pub fn all() -> Self {
228        Self {
229            instruction: InstructionFields::all(),
230            transaction: TransactionFields::all(),
231            log: LogFields::all(),
232            balance: BalanceFields::all(),
233            token_balance: TokenBalanceFields::all(),
234            reward: RewardFields::all(),
235            block: BlockFields::all(),
236        }
237    }
238}
239
240#[derive(Default, Debug, Clone, Copy, Serialize)]
241#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
242pub struct InstructionFields {
243    pub block_slot: bool,
244    pub block_hash: bool,
245    pub transaction_index: bool,
246    pub instruction_address: bool,
247    pub program_id: bool,
248    pub a0: bool,
249    pub a1: bool,
250    pub a2: bool,
251    pub a3: bool,
252    pub a4: bool,
253    pub a5: bool,
254    pub a6: bool,
255    pub a7: bool,
256    pub a8: bool,
257    pub a9: bool,
258    pub rest_of_accounts: bool,
259    pub data: bool,
260    pub d1: bool,
261    pub d2: bool,
262    pub d4: bool,
263    pub d8: bool,
264    pub error: bool,
265    pub compute_units_consumed: bool,
266    pub is_committed: bool,
267    pub has_dropped_log_messages: bool,
268}
269
270impl InstructionFields {
271    pub fn all() -> Self {
272        InstructionFields {
273            block_slot: true,
274            block_hash: true,
275            transaction_index: true,
276            instruction_address: true,
277            program_id: true,
278            a0: true,
279            a1: true,
280            a2: true,
281            a3: true,
282            a4: true,
283            a5: true,
284            a6: true,
285            a7: true,
286            a8: true,
287            a9: true,
288            rest_of_accounts: true,
289            data: true,
290            d1: true,
291            d2: true,
292            d4: true,
293            d8: true,
294            error: true,
295            compute_units_consumed: true,
296            is_committed: true,
297            has_dropped_log_messages: true,
298        }
299    }
300}
301
302#[derive(Default, Debug, Clone, Copy, Serialize)]
303#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
304pub struct TransactionFields {
305    pub block_slot: bool,
306    pub block_hash: bool,
307    pub transaction_index: bool,
308    pub signature: bool,
309    pub version: bool,
310    pub account_keys: bool,
311    pub address_table_lookups: bool,
312    pub num_readonly_signed_accounts: bool,
313    pub num_readonly_unsigned_accounts: bool,
314    pub num_required_signatures: bool,
315    pub recent_blockhash: bool,
316    pub signatures: bool,
317    pub err: bool,
318    pub fee: bool,
319    pub compute_units_consumed: bool,
320    pub loaded_readonly_addresses: bool,
321    pub loaded_writable_addresses: bool,
322    pub fee_payer: bool,
323    pub has_dropped_log_messages: bool,
324}
325
326impl TransactionFields {
327    pub fn all() -> Self {
328        TransactionFields {
329            block_slot: true,
330            block_hash: true,
331            transaction_index: true,
332            signature: true,
333            version: true,
334            account_keys: true,
335            address_table_lookups: true,
336            num_readonly_signed_accounts: true,
337            num_readonly_unsigned_accounts: true,
338            num_required_signatures: true,
339            recent_blockhash: true,
340            signatures: true,
341            err: true,
342            fee: true,
343            compute_units_consumed: true,
344            loaded_readonly_addresses: true,
345            loaded_writable_addresses: true,
346            fee_payer: true,
347            has_dropped_log_messages: true,
348        }
349    }
350}
351
352#[derive(Default, Debug, Clone, Copy, Serialize)]
353#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
354pub struct LogFields {
355    pub block_slot: bool,
356    pub block_hash: bool,
357    pub transaction_index: bool,
358    pub log_index: bool,
359    pub instruction_address: bool,
360    pub program_id: bool,
361    pub kind: bool,
362    pub message: bool,
363}
364
365impl LogFields {
366    pub fn all() -> Self {
367        LogFields {
368            block_slot: true,
369            block_hash: true,
370            transaction_index: true,
371            log_index: true,
372            instruction_address: true,
373            program_id: true,
374            kind: true,
375            message: true,
376        }
377    }
378}
379
380#[derive(Default, Debug, Clone, Copy, Serialize)]
381#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
382pub struct BalanceFields {
383    pub block_slot: bool,
384    pub block_hash: bool,
385    pub transaction_index: bool,
386    pub account: bool,
387    pub pre: bool,
388    pub post: bool,
389}
390
391impl BalanceFields {
392    pub fn all() -> Self {
393        BalanceFields {
394            block_slot: true,
395            block_hash: true,
396            transaction_index: true,
397            account: true,
398            pre: true,
399            post: true,
400        }
401    }
402}
403
404#[derive(Default, Debug, Clone, Copy, Serialize)]
405#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
406pub struct TokenBalanceFields {
407    pub block_slot: bool,
408    pub block_hash: bool,
409    pub transaction_index: bool,
410    pub account: bool,
411    pub pre_mint: bool,
412    pub post_mint: bool,
413    pub pre_decimals: bool,
414    pub post_decimals: bool,
415    pub pre_program_id: bool,
416    pub post_program_id: bool,
417    pub pre_owner: bool,
418    pub post_owner: bool,
419    pub pre_amount: bool,
420    pub post_amount: bool,
421}
422
423impl TokenBalanceFields {
424    pub fn all() -> Self {
425        TokenBalanceFields {
426            block_slot: true,
427            block_hash: true,
428            transaction_index: true,
429            account: true,
430            pre_mint: true,
431            post_mint: true,
432            pre_decimals: true,
433            post_decimals: true,
434            pre_program_id: true,
435            post_program_id: true,
436            pre_owner: true,
437            post_owner: true,
438            pre_amount: true,
439            post_amount: true,
440        }
441    }
442}
443
444#[derive(Default, Debug, Clone, Copy, Serialize)]
445#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
446pub struct RewardFields {
447    pub block_slot: bool,
448    pub block_hash: bool,
449    pub pubkey: bool,
450    pub lamports: bool,
451    pub post_balance: bool,
452    pub reward_type: bool,
453    pub commission: bool,
454}
455
456impl RewardFields {
457    pub fn all() -> Self {
458        RewardFields {
459            block_slot: true,
460            block_hash: true,
461            pubkey: true,
462            lamports: true,
463            post_balance: true,
464            reward_type: true,
465            commission: true,
466        }
467    }
468}
469
470#[derive(Default, Debug, Clone, Copy, Serialize)]
471#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
472pub struct BlockFields {
473    pub slot: bool,
474    pub hash: bool,
475    pub parent_slot: bool,
476    pub parent_hash: bool,
477    pub height: bool,
478    pub timestamp: bool,
479}
480
481impl BlockFields {
482    pub fn all() -> Self {
483        BlockFields {
484            slot: true,
485            hash: true,
486            parent_slot: true,
487            parent_hash: true,
488            height: true,
489            timestamp: true,
490        }
491    }
492}