1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "lowercase")]
8pub enum ShieldedPool {
9 Sapling,
11 Orchard,
13}
14
15impl std::fmt::Display for ShieldedPool {
16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 match self {
18 ShieldedPool::Sapling => write!(f, "sapling"),
19 ShieldedPool::Orchard => write!(f, "orchard"),
20 }
21 }
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
26#[serde(rename_all = "lowercase")]
27pub enum TxDirection {
28 In,
30 Out,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36#[serde(rename_all = "camelCase")]
37pub struct ZecTransaction {
38 pub txid: String,
40 pub height: u64,
42 pub time: i64,
44 pub amount_zat: String,
46 pub direction: TxDirection,
48 pub memo: Option<String>,
50 pub key_id: String,
52 pub pool: ShieldedPool,
54}
55
56impl ZecTransaction {
57 pub fn amount_zec(&self) -> f64 {
59 self.amount_zat
60 .parse::<i64>()
61 .map(|z| z as f64 / 100_000_000.0)
62 .unwrap_or(0.0)
63 }
64
65 pub fn amount_zatoshis(&self) -> i64 {
67 self.amount_zat.parse().unwrap_or(0)
68 }
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct ScanRequest {
74 pub viewing_key: String,
76 pub key_id: String,
78 pub compact_blocks: Vec<CompactBlock>,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84#[serde(rename_all = "camelCase")]
85pub struct CompactBlock {
86 pub proto_version: u32,
88 pub height: u64,
90 pub hash: String,
92 pub prev_hash: String,
94 pub time: u32,
96 #[serde(default)]
98 pub vtx: Vec<CompactTx>,
99 #[serde(default)]
101 pub chain_metadata: Option<ChainMetadata>,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
106#[serde(rename_all = "camelCase")]
107pub struct CompactTx {
108 pub index: u64,
110 pub txid: String,
112 #[serde(default)]
114 pub fee: Option<u32>,
115 #[serde(default)]
117 pub spends: Vec<CompactSaplingSpend>,
118 #[serde(default)]
120 pub outputs: Vec<CompactSaplingOutput>,
121 #[serde(default)]
123 pub actions: Vec<CompactOrchardAction>,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct CompactSaplingSpend {
129 pub nf: String,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
135#[serde(rename_all = "camelCase")]
136pub struct CompactSaplingOutput {
137 pub cmu: String,
139 pub ephemeral_key: String,
141 pub ciphertext: String,
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
147#[serde(rename_all = "camelCase")]
148pub struct CompactOrchardAction {
149 pub nf: String,
151 pub cmx: String,
153 pub ephemeral_key: String,
155 pub ciphertext: String,
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
161#[serde(rename_all = "camelCase")]
162pub struct ChainMetadata {
163 pub sapling_commitment_tree_size: u32,
165 #[serde(default)]
167 pub orchard_commitment_tree_size: Option<u32>,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
172#[serde(rename_all = "camelCase")]
173pub struct ScanSummary {
174 pub transactions: Vec<ZecTransaction>,
176 pub blocks_scanned: usize,
178 pub start_height: u64,
180 pub end_height: u64,
182 pub sapling_count: usize,
184 pub orchard_count: usize,
186}
187
188impl ScanSummary {
189 pub fn from_transactions(txs: Vec<ZecTransaction>, start: u64, end: u64) -> Self {
191 let sapling_count = txs.iter().filter(|t| t.pool == ShieldedPool::Sapling).count();
192 let orchard_count = txs.iter().filter(|t| t.pool == ShieldedPool::Orchard).count();
193 Self {
194 blocks_scanned: (end - start + 1) as usize,
195 start_height: start,
196 end_height: end,
197 sapling_count,
198 orchard_count,
199 transactions: txs,
200 }
201 }
202}