cherry_ingest/
svm.rs

1use anyhow::{anyhow, Context, Result};
2use serde::{Deserialize, 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(Deserialize, Serialize, Default, Debug, Clone, Copy)]
215#[serde(default)]
216#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
217pub struct Fields {
218    pub instruction: InstructionFields,
219    pub transaction: TransactionFields,
220    pub log: LogFields,
221    pub balance: BalanceFields,
222    pub token_balance: TokenBalanceFields,
223    pub reward: RewardFields,
224    pub block: BlockFields,
225}
226
227impl Fields {
228    pub fn all() -> Self {
229        Self {
230            instruction: InstructionFields::all(),
231            transaction: TransactionFields::all(),
232            log: LogFields::all(),
233            balance: BalanceFields::all(),
234            token_balance: TokenBalanceFields::all(),
235            reward: RewardFields::all(),
236            block: BlockFields::all(),
237        }
238    }
239}
240
241#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize)]
242#[serde(default)]
243#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
244pub struct InstructionFields {
245    pub block_slot: bool,
246    pub block_hash: bool,
247    pub transaction_index: bool,
248    pub instruction_address: bool,
249    pub program_id: bool,
250    pub a0: bool,
251    pub a1: bool,
252    pub a2: bool,
253    pub a3: bool,
254    pub a4: bool,
255    pub a5: bool,
256    pub a6: bool,
257    pub a7: bool,
258    pub a8: bool,
259    pub a9: bool,
260    pub rest_of_accounts: bool,
261    pub data: bool,
262    pub d1: bool,
263    pub d2: bool,
264    pub d4: bool,
265    pub d8: bool,
266    pub error: bool,
267    pub compute_units_consumed: bool,
268    pub is_committed: bool,
269    pub has_dropped_log_messages: bool,
270}
271
272impl InstructionFields {
273    pub fn all() -> Self {
274        InstructionFields {
275            block_slot: true,
276            block_hash: true,
277            transaction_index: true,
278            instruction_address: true,
279            program_id: true,
280            a0: true,
281            a1: true,
282            a2: true,
283            a3: true,
284            a4: true,
285            a5: true,
286            a6: true,
287            a7: true,
288            a8: true,
289            a9: true,
290            rest_of_accounts: true,
291            data: true,
292            d1: true,
293            d2: true,
294            d4: true,
295            d8: true,
296            error: true,
297            compute_units_consumed: true,
298            is_committed: true,
299            has_dropped_log_messages: true,
300        }
301    }
302}
303
304#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize)]
305#[serde(default)]
306#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
307pub struct TransactionFields {
308    pub block_slot: bool,
309    pub block_hash: bool,
310    pub transaction_index: bool,
311    pub signature: bool,
312    pub version: bool,
313    pub account_keys: bool,
314    pub address_table_lookups: bool,
315    pub num_readonly_signed_accounts: bool,
316    pub num_readonly_unsigned_accounts: bool,
317    pub num_required_signatures: bool,
318    pub recent_blockhash: bool,
319    pub signatures: bool,
320    pub err: bool,
321    pub fee: bool,
322    pub compute_units_consumed: bool,
323    pub loaded_readonly_addresses: bool,
324    pub loaded_writable_addresses: bool,
325    pub fee_payer: bool,
326    pub has_dropped_log_messages: bool,
327}
328
329impl TransactionFields {
330    pub fn all() -> Self {
331        TransactionFields {
332            block_slot: true,
333            block_hash: true,
334            transaction_index: true,
335            signature: true,
336            version: true,
337            account_keys: true,
338            address_table_lookups: true,
339            num_readonly_signed_accounts: true,
340            num_readonly_unsigned_accounts: true,
341            num_required_signatures: true,
342            recent_blockhash: true,
343            signatures: true,
344            err: true,
345            fee: true,
346            compute_units_consumed: true,
347            loaded_readonly_addresses: true,
348            loaded_writable_addresses: true,
349            fee_payer: true,
350            has_dropped_log_messages: true,
351        }
352    }
353}
354
355#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize)]
356#[serde(default)]
357#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
358pub struct LogFields {
359    pub block_slot: bool,
360    pub block_hash: bool,
361    pub transaction_index: bool,
362    pub log_index: bool,
363    pub instruction_address: bool,
364    pub program_id: bool,
365    pub kind: bool,
366    pub message: bool,
367}
368
369impl LogFields {
370    pub fn all() -> Self {
371        LogFields {
372            block_slot: true,
373            block_hash: true,
374            transaction_index: true,
375            log_index: true,
376            instruction_address: true,
377            program_id: true,
378            kind: true,
379            message: true,
380        }
381    }
382}
383
384#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize)]
385#[serde(default)]
386#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
387pub struct BalanceFields {
388    pub block_slot: bool,
389    pub block_hash: bool,
390    pub transaction_index: bool,
391    pub account: bool,
392    pub pre: bool,
393    pub post: bool,
394}
395
396impl BalanceFields {
397    pub fn all() -> Self {
398        BalanceFields {
399            block_slot: true,
400            block_hash: true,
401            transaction_index: true,
402            account: true,
403            pre: true,
404            post: true,
405        }
406    }
407}
408
409#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize)]
410#[serde(default)]
411#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
412pub struct TokenBalanceFields {
413    pub block_slot: bool,
414    pub block_hash: bool,
415    pub transaction_index: bool,
416    pub account: bool,
417    pub pre_mint: bool,
418    pub post_mint: bool,
419    pub pre_decimals: bool,
420    pub post_decimals: bool,
421    pub pre_program_id: bool,
422    pub post_program_id: bool,
423    pub pre_owner: bool,
424    pub post_owner: bool,
425    pub pre_amount: bool,
426    pub post_amount: bool,
427}
428
429impl TokenBalanceFields {
430    pub fn all() -> Self {
431        TokenBalanceFields {
432            block_slot: true,
433            block_hash: true,
434            transaction_index: true,
435            account: true,
436            pre_mint: true,
437            post_mint: true,
438            pre_decimals: true,
439            post_decimals: true,
440            pre_program_id: true,
441            post_program_id: true,
442            pre_owner: true,
443            post_owner: true,
444            pre_amount: true,
445            post_amount: true,
446        }
447    }
448}
449
450#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize)]
451#[serde(default)]
452#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
453pub struct RewardFields {
454    pub block_slot: bool,
455    pub block_hash: bool,
456    pub pubkey: bool,
457    pub lamports: bool,
458    pub post_balance: bool,
459    pub reward_type: bool,
460    pub commission: bool,
461}
462
463impl RewardFields {
464    pub fn all() -> Self {
465        RewardFields {
466            block_slot: true,
467            block_hash: true,
468            pubkey: true,
469            lamports: true,
470            post_balance: true,
471            reward_type: true,
472            commission: true,
473        }
474    }
475}
476
477#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize)]
478#[serde(default)]
479#[cfg_attr(feature = "pyo3", derive(pyo3::FromPyObject))]
480pub struct BlockFields {
481    pub slot: bool,
482    pub hash: bool,
483    pub parent_slot: bool,
484    pub parent_hash: bool,
485    pub height: bool,
486    pub timestamp: bool,
487}
488
489impl BlockFields {
490    pub fn all() -> Self {
491        BlockFields {
492            slot: true,
493            hash: true,
494            parent_slot: true,
495            parent_hash: true,
496            height: true,
497            timestamp: true,
498        }
499    }
500}