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::sync::Arc;
8use alloc::vec::Vec;
9
10use futures::Stream;
11use miden_lib::utils::Serializable;
12use miden_objects::address::Address;
13use miden_objects::note::{Note, NoteDetails, NoteHeader, NoteTag};
14use miden_tx::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, SliceReader};
15
16pub use self::errors::NoteTransportError;
17use crate::store::{InputNoteRecord, Store};
18use crate::{Client, ClientError};
19
20pub const NOTE_TRANSPORT_DEFAULT_ENDPOINT: &str = "http://localhost:57292";
21pub const NOTE_TRANSPORT_CURSOR_STORE_SETTING: &str = "note_transport_cursor";
22
23impl<AUTH> Client<AUTH> {
25 pub fn is_note_transport_enabled(&self) -> bool {
27 self.note_transport_api.is_some()
28 }
29
30 pub async fn send_private_note(
36 &mut self,
37 note: Note,
38 _address: &Address,
39 ) -> Result<(), ClientError> {
40 let api = self.get_note_transport_api()?;
41
42 let header = *note.header();
43 let details = NoteDetails::from(note);
44 let details_bytes = details.to_bytes();
45 api.send_note(header, details_bytes).await?;
48
49 Ok(())
50 }
51
52 pub async fn fetch_private_notes(&mut self) -> Result<(), ClientError> {
65 let api = self.get_note_transport_api()?;
66
67 let note_tags = self.store.get_unique_note_tags().await?;
69 let cursor = self.store.get_note_transport_cursor().await?;
71
72 let update = NoteTransport::new(api).fetch_notes(cursor, note_tags).await?;
73
74 self.store.apply_note_transport_update(update).await?;
75
76 Ok(())
77 }
78
79 pub async fn fetch_all_private_notes(&mut self) -> Result<(), ClientError> {
85 let api = self.get_note_transport_api()?;
86
87 let note_tags = self.store.get_unique_note_tags().await?;
88
89 let update = NoteTransport::new(api)
90 .fetch_notes(NoteTransportCursor::init(), note_tags)
91 .await?;
92
93 self.store.apply_note_transport_update(update).await?;
94
95 Ok(())
96 }
97
98 pub(crate) fn get_note_transport_api(
100 &self,
101 ) -> Result<Arc<dyn NoteTransportClient>, NoteTransportError> {
102 self.note_transport_api.clone().ok_or(NoteTransportError::Disabled)
103 }
104}
105
106pub(crate) async fn init_note_transport_cursor(store: Arc<dyn Store>) -> Result<(), ClientError> {
108 let setting = NOTE_TRANSPORT_CURSOR_STORE_SETTING;
109 if store.get_setting(setting.into()).await?.is_none() {
110 let initial_cursor = 0u64.to_be_bytes().to_vec();
111 store.set_setting(setting.into(), initial_cursor).await?;
112 }
113 Ok(())
114}
115
116pub struct NoteTransport {
118 api: Arc<dyn NoteTransportClient>,
119}
120
121#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)]
126pub struct NoteTransportCursor(u64);
127
128pub struct NoteTransportUpdate {
130 pub cursor: NoteTransportCursor,
132 pub note_updates: Vec<InputNoteRecord>,
134}
135
136impl NoteTransport {
137 pub fn new(api: Arc<dyn NoteTransportClient>) -> Self {
138 Self { api }
139 }
140
141 pub(crate) async fn fetch_notes<I>(
145 &mut self,
146 cursor: NoteTransportCursor,
147 tags: I,
148 ) -> Result<NoteTransportUpdate, ClientError>
149 where
150 I: IntoIterator<Item = NoteTag>,
151 {
152 let mut note_updates = vec![];
153 let (note_infos, rcursor) =
155 self.api.fetch_notes(&tags.into_iter().collect::<Vec<_>>(), cursor).await?;
156 for note_info in ¬e_infos {
157 let note = rejoin_note(¬e_info.header, ¬e_info.details_bytes)?;
161 let input_note = InputNoteRecord::from(note);
162 note_updates.push(input_note);
163 }
164
165 let update = NoteTransportUpdate { note_updates, cursor: rcursor };
166
167 Ok(update)
168 }
169}
170
171impl NoteTransportCursor {
172 pub fn new(value: u64) -> Self {
173 Self(value)
174 }
175
176 pub fn init() -> Self {
177 Self::new(0)
178 }
179
180 pub fn value(&self) -> u64 {
181 self.0
182 }
183}
184
185impl From<u64> for NoteTransportCursor {
186 fn from(value: u64) -> Self {
187 Self::new(value)
188 }
189}
190
191#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
193#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
194pub trait NoteTransportClient: Send + Sync {
195 async fn send_note(
197 &self,
198 header: NoteHeader,
199 details: Vec<u8>,
200 ) -> Result<(), NoteTransportError>;
201
202 async fn fetch_notes(
207 &self,
208 tag: &[NoteTag],
209 cursor: NoteTransportCursor,
210 ) -> Result<(Vec<NoteInfo>, NoteTransportCursor), NoteTransportError>;
211
212 async fn stream_notes(
214 &self,
215 tag: NoteTag,
216 cursor: NoteTransportCursor,
217 ) -> Result<Box<dyn NoteStream>, NoteTransportError>;
218}
219
220pub trait NoteStream:
222 Stream<Item = Result<Vec<NoteInfo>, NoteTransportError>> + Send + Unpin
223{
224}
225
226#[derive(Debug, Clone)]
228pub struct NoteInfo {
229 pub header: NoteHeader,
231 pub details_bytes: Vec<u8>,
233}
234
235impl Serializable for NoteInfo {
239 fn write_into<W: ByteWriter>(&self, target: &mut W) {
240 self.header.write_into(target);
241 self.details_bytes.write_into(target);
242 }
243}
244
245impl Deserializable for NoteInfo {
246 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
247 let header = NoteHeader::read_from(source)?;
248 let details_bytes = Vec::<u8>::read_from(source)?;
249 Ok(NoteInfo { header, details_bytes })
250 }
251}
252
253impl Serializable for NoteTransportCursor {
254 fn write_into<W: ByteWriter>(&self, target: &mut W) {
255 self.0.write_into(target);
256 }
257}
258
259impl Deserializable for NoteTransportCursor {
260 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
261 let value = u64::read_from(source)?;
262 Ok(Self::new(value))
263 }
264}
265
266fn rejoin_note(header: &NoteHeader, details_bytes: &[u8]) -> Result<Note, DeserializationError> {
267 let mut reader = SliceReader::new(details_bytes);
268 let details = NoteDetails::read_from(&mut reader)?;
269 let metadata = *header.metadata();
270 Ok(Note::new(details.assets().clone(), metadata, details.recipient().clone()))
271}