miden_client/note_transport/
mod.rs1pub mod errors;
2pub mod generated;
3#[cfg(feature = "tonic")]
4pub mod grpc;
5
6use alloc::boxed::Box;
7use alloc::collections::BTreeMap;
8use alloc::sync::Arc;
9use alloc::vec::Vec;
10
11use futures::Stream;
12use miden_lib::utils::Serializable;
13use miden_objects::address::Address;
14use miden_objects::note::{Note, NoteDetails, NoteHeader, NoteId, NoteInclusionProof, NoteTag};
15use miden_tx::auth::TransactionAuthenticator;
16use miden_tx::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, SliceReader};
17
18pub use self::errors::NoteTransportError;
19use crate::store::{InputNoteRecord, Store};
20use crate::{Client, ClientError};
21
22pub const NOTE_TRANSPORT_DEFAULT_ENDPOINT: &str = "http://localhost:57292";
23pub const NOTE_TRANSPORT_CURSOR_STORE_SETTING: &str = "note_transport_cursor";
24
25impl<AUTH> Client<AUTH> {
27 pub fn is_note_transport_enabled(&self) -> bool {
29 self.note_transport_api.is_some()
30 }
31
32 pub(crate) fn get_note_transport_api(
36 &self,
37 ) -> Result<Arc<dyn NoteTransportClient>, NoteTransportError> {
38 self.note_transport_api.clone().ok_or(NoteTransportError::Disabled)
39 }
40
41 pub async fn send_private_note(
47 &mut self,
48 note: Note,
49 _address: &Address,
50 ) -> Result<(), ClientError> {
51 let api = self.get_note_transport_api()?;
52
53 let header = *note.header();
54 let details = NoteDetails::from(note);
55 let details_bytes = details.to_bytes();
56 api.send_note(header, details_bytes).await?;
59
60 Ok(())
61 }
62}
63
64impl<AUTH> Client<AUTH>
65where
66 AUTH: TransactionAuthenticator + Sync + 'static,
67{
68 pub async fn fetch_private_notes(&mut self) -> Result<(), ClientError> {
81 let note_tags = self.store.get_unique_note_tags().await?;
83 let cursor = self.store.get_note_transport_cursor().await?;
85
86 let update = self.fetch_note_transport_update(cursor, note_tags).await?;
87
88 self.store.apply_note_transport_update(update).await?;
89
90 Ok(())
91 }
92
93 pub async fn fetch_all_private_notes(&mut self) -> Result<(), ClientError> {
99 let note_tags = self.store.get_unique_note_tags().await?;
100
101 let update =
102 self.fetch_note_transport_update(NoteTransportCursor::init(), note_tags).await?;
103
104 self.store.apply_note_transport_update(update).await?;
105
106 Ok(())
107 }
108
109 pub(crate) async fn fetch_note_transport_update<I>(
114 &mut self,
115 cursor: NoteTransportCursor,
116 tags: I,
117 ) -> Result<NoteTransportUpdate, ClientError>
118 where
119 I: IntoIterator<Item = NoteTag>,
120 {
121 let mut tnotes = BTreeMap::new();
122 let (note_infos, rcursor) = self
124 .get_note_transport_api()?
125 .fetch_notes(&tags.into_iter().collect::<Vec<_>>(), cursor)
126 .await?;
127 for note_info in ¬e_infos {
128 let tnote = rejoin_note(¬e_info.header, ¬e_info.details_bytes)?;
132 tnotes.insert(tnote.id(), tnote);
133 }
134
135 let ids = tnotes.keys().copied().collect::<Vec<_>>();
137 let nnotes = self.rpc_api.get_notes_by_id(&ids).await?;
138
139 let mut note_updates = Vec::with_capacity(tnotes.len());
141 for nnote in nnotes {
142 if let Some(tnote) = tnotes.get(&nnote.id()) {
143 let opt_input_note_record = self
145 .combine_transport_note_and_proof(
146 tnote.clone(),
147 nnote.id(),
148 nnote.inclusion_proof().clone(),
149 )
150 .await;
151 if let Some(input_note_record) = opt_input_note_record {
152 tnotes.remove(&nnote.id());
153 note_updates.push(input_note_record);
154 }
155 }
156 }
157
158 for (_id, tnote) in tnotes {
160 let input_note_record = InputNoteRecord::from(tnote);
161 note_updates.push(input_note_record);
162 }
163
164 let update = NoteTransportUpdate { note_updates, cursor: rcursor };
165
166 Ok(update)
167 }
168
169 async fn combine_transport_note_and_proof(
171 &self,
172 tnote: Note,
173 note_id: NoteId,
174 inclusion_proof: NoteInclusionProof,
175 ) -> Option<InputNoteRecord> {
176 let previous_note = self.get_input_note(note_id).await.ok()?;
178 self.import_note_record_by_proof(previous_note, tnote, inclusion_proof)
180 .await
181 .ok()?
182 }
183}
184
185pub(crate) async fn init_note_transport_cursor(store: Arc<dyn Store>) -> Result<(), ClientError> {
187 let setting = NOTE_TRANSPORT_CURSOR_STORE_SETTING;
188 if store.get_setting(setting.into()).await?.is_none() {
189 let initial_cursor = 0u64.to_be_bytes().to_vec();
190 store.set_setting(setting.into(), initial_cursor).await?;
191 }
192 Ok(())
193}
194
195#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)]
200pub struct NoteTransportCursor(u64);
201
202pub struct NoteTransportUpdate {
204 pub cursor: NoteTransportCursor,
206 pub note_updates: Vec<InputNoteRecord>,
208}
209
210impl NoteTransportCursor {
211 pub fn new(value: u64) -> Self {
212 Self(value)
213 }
214
215 pub fn init() -> Self {
216 Self::new(0)
217 }
218
219 pub fn value(&self) -> u64 {
220 self.0
221 }
222}
223
224impl From<u64> for NoteTransportCursor {
225 fn from(value: u64) -> Self {
226 Self::new(value)
227 }
228}
229
230#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
232#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
233pub trait NoteTransportClient: Send + Sync {
234 async fn send_note(
236 &self,
237 header: NoteHeader,
238 details: Vec<u8>,
239 ) -> Result<(), NoteTransportError>;
240
241 async fn fetch_notes(
246 &self,
247 tag: &[NoteTag],
248 cursor: NoteTransportCursor,
249 ) -> Result<(Vec<NoteInfo>, NoteTransportCursor), NoteTransportError>;
250
251 async fn stream_notes(
253 &self,
254 tag: NoteTag,
255 cursor: NoteTransportCursor,
256 ) -> Result<Box<dyn NoteStream>, NoteTransportError>;
257}
258
259pub trait NoteStream:
261 Stream<Item = Result<Vec<NoteInfo>, NoteTransportError>> + Send + Unpin
262{
263}
264
265#[derive(Debug, Clone)]
267pub struct NoteInfo {
268 pub header: NoteHeader,
270 pub details_bytes: Vec<u8>,
272}
273
274impl Serializable for NoteInfo {
278 fn write_into<W: ByteWriter>(&self, target: &mut W) {
279 self.header.write_into(target);
280 self.details_bytes.write_into(target);
281 }
282}
283
284impl Deserializable for NoteInfo {
285 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
286 let header = NoteHeader::read_from(source)?;
287 let details_bytes = Vec::<u8>::read_from(source)?;
288 Ok(NoteInfo { header, details_bytes })
289 }
290}
291
292impl Serializable for NoteTransportCursor {
293 fn write_into<W: ByteWriter>(&self, target: &mut W) {
294 self.0.write_into(target);
295 }
296}
297
298impl Deserializable for NoteTransportCursor {
299 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
300 let value = u64::read_from(source)?;
301 Ok(Self::new(value))
302 }
303}
304
305fn rejoin_note(header: &NoteHeader, details_bytes: &[u8]) -> Result<Note, DeserializationError> {
306 let mut reader = SliceReader::new(details_bytes);
307 let details = NoteDetails::read_from(&mut reader)?;
308 let metadata = *header.metadata();
309 Ok(Note::new(details.assets().clone(), metadata, details.recipient().clone()))
310}