zync_core/client/
zidecar.rs1use super::{
4 zidecar_proto::{
5 zidecar_client::ZidecarClient as GrpcClient, BlockId, BlockRange, Empty, ProofRequest,
6 RawTransaction, TransparentAddressFilter, TxFilter,
7 },
8 CompactAction, CompactBlock, SendResult, SyncStatus, TreeState, Utxo,
9};
10use anyhow::Result;
11use tonic::transport::Channel;
12use tracing::{debug, info};
13
14pub struct ZidecarClient {
15 client: GrpcClient<Channel>,
16}
17
18impl ZidecarClient {
19 pub async fn connect(url: &str) -> Result<Self> {
20 info!("connecting to zidecar at {}", url);
21 let client = GrpcClient::connect(url.to_string()).await?;
22 Ok(Self { client })
23 }
24
25 pub async fn get_header_proof(&mut self) -> Result<(Vec<u8>, u32, u32)> {
27 let request = tonic::Request::new(ProofRequest {
28 from_height: 0,
29 to_height: 0, });
31
32 let response = self.client.get_header_proof(request).await?;
33 let proof = response.into_inner();
34
35 debug!(
36 "received proof: {} -> {} ({} bytes)",
37 proof.from_height,
38 proof.to_height,
39 proof.ligerito_proof.len()
40 );
41
42 Ok((proof.ligerito_proof, proof.from_height, proof.to_height))
43 }
44
45 pub async fn get_tip(&mut self) -> Result<(u32, Vec<u8>)> {
47 let request = tonic::Request::new(Empty {});
48 let response = self.client.get_tip(request).await?;
49 let tip = response.into_inner();
50
51 Ok((tip.height, tip.hash))
52 }
53
54 pub async fn get_compact_blocks(
56 &mut self,
57 start_height: u32,
58 end_height: u32,
59 ) -> Result<Vec<CompactBlock>> {
60 let request = tonic::Request::new(BlockRange {
61 start_height,
62 end_height,
63 });
64
65 let mut stream = self.client.get_compact_blocks(request).await?.into_inner();
66 let mut blocks = Vec::new();
67
68 while let Some(block) = stream.message().await? {
69 let actions: Vec<CompactAction> = block
70 .actions
71 .into_iter()
72 .map(|a| {
73 let mut cmx = [0u8; 32];
74 let mut ek = [0u8; 32];
75 let mut nf = [0u8; 32];
76 if a.cmx.len() == 32 {
77 cmx.copy_from_slice(&a.cmx);
78 }
79 if a.ephemeral_key.len() == 32 {
80 ek.copy_from_slice(&a.ephemeral_key);
81 }
82 if a.nullifier.len() == 32 {
83 nf.copy_from_slice(&a.nullifier);
84 }
85 CompactAction {
86 cmx,
87 ephemeral_key: ek,
88 ciphertext: a.ciphertext,
89 nullifier: nf,
90 }
91 })
92 .collect();
93
94 blocks.push(CompactBlock {
95 height: block.height,
96 hash: block.hash,
97 actions,
98 });
99 }
100
101 Ok(blocks)
102 }
103
104 pub async fn get_sync_status(&mut self) -> Result<SyncStatus> {
106 let request = tonic::Request::new(Empty {});
107 let response = self.client.get_sync_status(request).await?;
108 let status = response.into_inner();
109
110 Ok(SyncStatus {
111 current_height: status.current_height,
112 current_epoch: status.current_epoch,
113 blocks_in_epoch: status.blocks_in_epoch,
114 complete_epochs: status.complete_epochs,
115 epoch_proof_ready: status.epoch_proof_status == 2, blocks_until_ready: status.blocks_until_ready,
117 last_epoch_proof_height: status.last_epoch_proof_height,
118 })
119 }
120
121 pub async fn send_transaction(&mut self, tx_data: Vec<u8>) -> Result<SendResult> {
123 let request = tonic::Request::new(RawTransaction {
124 data: tx_data,
125 height: 0,
126 });
127 let response = self.client.send_transaction(request).await?;
128 let resp = response.into_inner();
129 Ok(SendResult {
130 txid: resp.txid,
131 error_code: resp.error_code,
132 error_message: resp.error_message,
133 })
134 }
135
136 pub async fn get_transaction(&mut self, txid: &[u8; 32]) -> Result<Vec<u8>> {
138 let request = tonic::Request::new(TxFilter {
139 hash: txid.to_vec(),
140 });
141 let response = self.client.get_transaction(request).await?;
142 Ok(response.into_inner().data)
143 }
144
145 pub async fn get_tree_state(&mut self, height: u32) -> Result<TreeState> {
147 let request = tonic::Request::new(BlockId {
148 height,
149 hash: vec![],
150 });
151 let response = self.client.get_tree_state(request).await?;
152 let state = response.into_inner();
153 Ok(TreeState {
154 height: state.height,
155 hash: state.hash,
156 time: state.time,
157 sapling_tree: state.sapling_tree,
158 orchard_tree: state.orchard_tree,
159 })
160 }
161
162 pub async fn get_address_utxos(&mut self, addresses: Vec<String>) -> Result<Vec<Utxo>> {
164 let request = tonic::Request::new(TransparentAddressFilter {
165 addresses,
166 start_height: 0,
167 max_entries: 0,
168 });
169 let response = self.client.get_address_utxos(request).await?;
170 let utxos = response.into_inner().utxos;
171
172 Ok(utxos
173 .into_iter()
174 .map(|u| {
175 let mut txid = [0u8; 32];
176 if u.txid.len() == 32 {
177 txid.copy_from_slice(&u.txid);
178 }
179 Utxo {
180 address: u.address,
181 txid,
182 output_index: u.output_index,
183 script: u.script,
184 value_zat: u.value_zat,
185 height: u.height,
186 }
187 })
188 .collect())
189 }
190
191 pub async fn get_taddress_txids(
193 &mut self,
194 addresses: Vec<String>,
195 start_height: u32,
196 ) -> Result<Vec<[u8; 32]>> {
197 let request = tonic::Request::new(TransparentAddressFilter {
198 addresses,
199 start_height,
200 max_entries: 0,
201 });
202 let response = self.client.get_taddress_txids(request).await?;
203 let txids = response.into_inner().txids;
204
205 Ok(txids
206 .into_iter()
207 .filter_map(|t| {
208 if t.len() == 32 {
209 let mut arr = [0u8; 32];
210 arr.copy_from_slice(&t);
211 Some(arr)
212 } else {
213 None
214 }
215 })
216 .collect())
217 }
218}