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_protocol::address::Address;
12use miden_protocol::note::{Note, NoteDetails, NoteFile, NoteHeader, NoteTag};
13use miden_protocol::utils::Serializable;
14use miden_tx::auth::TransactionAuthenticator;
15use miden_tx::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, SliceReader};
16
17pub use self::errors::NoteTransportError;
18use crate::{Client, ClientError};
19
20pub const NOTE_TRANSPORT_DEFAULT_ENDPOINT: &str = "https://transport.miden.io";
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(crate) fn get_note_transport_api(
34 &self,
35 ) -> Result<Arc<dyn NoteTransportClient>, NoteTransportError> {
36 self.note_transport_api.clone().ok_or(NoteTransportError::Disabled)
37 }
38
39 pub async fn send_private_note(
45 &mut self,
46 note: Note,
47 _address: &Address,
48 ) -> Result<(), ClientError> {
49 let api = self.get_note_transport_api()?;
50
51 let header = note.header().clone();
52 let details = NoteDetails::from(note);
53 let details_bytes = details.to_bytes();
54 api.send_note(header, details_bytes).await?;
57
58 Ok(())
59 }
60}
61
62impl<AUTH> Client<AUTH>
63where
64 AUTH: TransactionAuthenticator + Sync + 'static,
65{
66 pub async fn fetch_private_notes(&mut self) -> Result<(), ClientError> {
79 let note_tags = self.store.get_unique_note_tags().await?;
81 let cursor = self.store.get_note_transport_cursor().await?;
83
84 self.fetch_transport_notes(cursor, note_tags).await?;
85
86 Ok(())
87 }
88
89 pub async fn fetch_all_private_notes(&mut self) -> Result<(), ClientError> {
95 let note_tags = self.store.get_unique_note_tags().await?;
96
97 self.fetch_transport_notes(NoteTransportCursor::init(), note_tags).await?;
98
99 Ok(())
100 }
101
102 pub(crate) async fn fetch_transport_notes<I>(
107 &mut self,
108 cursor: NoteTransportCursor,
109 tags: I,
110 ) -> Result<(), ClientError>
111 where
112 I: IntoIterator<Item = NoteTag>,
113 {
114 let mut notes = Vec::new();
115 let (note_infos, rcursor) = self
117 .get_note_transport_api()?
118 .fetch_notes(&tags.into_iter().collect::<Vec<_>>(), cursor)
119 .await?;
120 for note_info in ¬e_infos {
121 let note = rejoin_note(¬e_info.header, ¬e_info.details_bytes)?;
125 notes.push(note);
126 }
127
128 let sync_height = self.get_sync_height().await?;
129 let mut note_requests = Vec::with_capacity(notes.len());
131 for note in notes {
132 let tag = note.metadata().tag();
133 let note_file = NoteFile::NoteDetails {
134 details: note.into(),
135 after_block_num: sync_height,
136 tag: Some(tag),
137 };
138 note_requests.push(note_file);
139 }
140 self.import_notes(¬e_requests).await?;
141
142 self.store.update_note_transport_cursor(rcursor).await?;
144
145 Ok(())
146 }
147}
148
149#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)]
154pub struct NoteTransportCursor(u64);
155
156pub struct NoteTransportUpdate {
158 pub cursor: NoteTransportCursor,
160 pub notes: Vec<Note>,
162}
163
164impl NoteTransportCursor {
165 pub fn new(value: u64) -> Self {
166 Self(value)
167 }
168
169 pub fn init() -> Self {
170 Self::new(0)
171 }
172
173 pub fn value(&self) -> u64 {
174 self.0
175 }
176}
177
178impl From<u64> for NoteTransportCursor {
179 fn from(value: u64) -> Self {
180 Self::new(value)
181 }
182}
183
184#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
186#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
187pub trait NoteTransportClient: Send + Sync {
188 async fn send_note(
190 &self,
191 header: NoteHeader,
192 details: Vec<u8>,
193 ) -> Result<(), NoteTransportError>;
194
195 async fn fetch_notes(
200 &self,
201 tag: &[NoteTag],
202 cursor: NoteTransportCursor,
203 ) -> Result<(Vec<NoteInfo>, NoteTransportCursor), NoteTransportError>;
204
205 async fn stream_notes(
207 &self,
208 tag: NoteTag,
209 cursor: NoteTransportCursor,
210 ) -> Result<Box<dyn NoteStream>, NoteTransportError>;
211}
212
213pub trait NoteStream:
215 Stream<Item = Result<Vec<NoteInfo>, NoteTransportError>> + Send + Unpin
216{
217}
218
219#[derive(Debug, Clone)]
221pub struct NoteInfo {
222 pub header: NoteHeader,
224 pub details_bytes: Vec<u8>,
226}
227
228impl Serializable for NoteInfo {
232 fn write_into<W: ByteWriter>(&self, target: &mut W) {
233 self.header.write_into(target);
234 self.details_bytes.write_into(target);
235 }
236}
237
238impl Deserializable for NoteInfo {
239 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
240 let header = NoteHeader::read_from(source)?;
241 let details_bytes = Vec::<u8>::read_from(source)?;
242 Ok(NoteInfo { header, details_bytes })
243 }
244}
245
246impl Serializable for NoteTransportCursor {
247 fn write_into<W: ByteWriter>(&self, target: &mut W) {
248 self.0.write_into(target);
249 }
250}
251
252impl Deserializable for NoteTransportCursor {
253 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
254 let value = u64::read_from(source)?;
255 Ok(Self::new(value))
256 }
257}
258
259fn rejoin_note(header: &NoteHeader, details_bytes: &[u8]) -> Result<Note, DeserializationError> {
260 let mut reader = SliceReader::new(details_bytes);
261 let details = NoteDetails::read_from(&mut reader)?;
262 let metadata = header.metadata().clone();
263 Ok(Note::new(details.assets().clone(), metadata, details.recipient().clone()))
264}