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::store::Store;
19use crate::{Client, ClientError};
20
21pub const NOTE_TRANSPORT_DEFAULT_ENDPOINT: &str = "https://transport.miden.io";
22pub const NOTE_TRANSPORT_CURSOR_STORE_SETTING: &str = "note_transport_cursor";
23
24impl<AUTH> Client<AUTH> {
26 pub fn is_note_transport_enabled(&self) -> bool {
28 self.note_transport_api.is_some()
29 }
30
31 pub(crate) fn get_note_transport_api(
35 &self,
36 ) -> Result<Arc<dyn NoteTransportClient>, NoteTransportError> {
37 self.note_transport_api.clone().ok_or(NoteTransportError::Disabled)
38 }
39
40 pub async fn send_private_note(
46 &mut self,
47 note: Note,
48 _address: &Address,
49 ) -> Result<(), ClientError> {
50 let api = self.get_note_transport_api()?;
51
52 let header = note.header().clone();
53 let details = NoteDetails::from(note);
54 let details_bytes = details.to_bytes();
55 api.send_note(header, details_bytes).await?;
58
59 Ok(())
60 }
61}
62
63impl<AUTH> Client<AUTH>
64where
65 AUTH: TransactionAuthenticator + Sync + 'static,
66{
67 pub async fn fetch_private_notes(&mut self) -> Result<(), ClientError> {
80 let note_tags = self.store.get_unique_note_tags().await?;
82 let cursor = self.store.get_note_transport_cursor().await?;
84
85 self.fetch_transport_notes(cursor, note_tags).await?;
86
87 Ok(())
88 }
89
90 pub async fn fetch_all_private_notes(&mut self) -> Result<(), ClientError> {
96 let note_tags = self.store.get_unique_note_tags().await?;
97
98 self.fetch_transport_notes(NoteTransportCursor::init(), note_tags).await?;
99
100 Ok(())
101 }
102
103 pub(crate) async fn fetch_transport_notes<I>(
108 &mut self,
109 cursor: NoteTransportCursor,
110 tags: I,
111 ) -> Result<(), ClientError>
112 where
113 I: IntoIterator<Item = NoteTag>,
114 {
115 let mut notes = Vec::new();
116 let (note_infos, rcursor) = self
118 .get_note_transport_api()?
119 .fetch_notes(&tags.into_iter().collect::<Vec<_>>(), cursor)
120 .await?;
121 for note_info in ¬e_infos {
122 let note = rejoin_note(¬e_info.header, ¬e_info.details_bytes)?;
126 notes.push(note);
127 }
128
129 let sync_height = self.get_sync_height().await?;
130 let mut note_requests = Vec::with_capacity(notes.len());
132 for note in notes {
133 let tag = note.metadata().tag();
134 let note_file = NoteFile::NoteDetails {
135 details: note.into(),
136 after_block_num: sync_height,
137 tag: Some(tag),
138 };
139 note_requests.push(note_file);
140 }
141 self.import_notes(¬e_requests).await?;
142
143 self.store.update_note_transport_cursor(rcursor).await?;
145
146 Ok(())
147 }
148}
149
150pub(crate) async fn init_note_transport_cursor(store: Arc<dyn Store>) -> Result<(), ClientError> {
152 let setting = NOTE_TRANSPORT_CURSOR_STORE_SETTING;
153 if store.get_setting(setting.into()).await?.is_none() {
154 let initial_cursor = 0u64.to_be_bytes().to_vec();
155 store.set_setting(setting.into(), initial_cursor).await?;
156 }
157 Ok(())
158}
159
160#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)]
165pub struct NoteTransportCursor(u64);
166
167pub struct NoteTransportUpdate {
169 pub cursor: NoteTransportCursor,
171 pub notes: Vec<Note>,
173}
174
175impl NoteTransportCursor {
176 pub fn new(value: u64) -> Self {
177 Self(value)
178 }
179
180 pub fn init() -> Self {
181 Self::new(0)
182 }
183
184 pub fn value(&self) -> u64 {
185 self.0
186 }
187}
188
189impl From<u64> for NoteTransportCursor {
190 fn from(value: u64) -> Self {
191 Self::new(value)
192 }
193}
194
195#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
197#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
198pub trait NoteTransportClient: Send + Sync {
199 async fn send_note(
201 &self,
202 header: NoteHeader,
203 details: Vec<u8>,
204 ) -> Result<(), NoteTransportError>;
205
206 async fn fetch_notes(
211 &self,
212 tag: &[NoteTag],
213 cursor: NoteTransportCursor,
214 ) -> Result<(Vec<NoteInfo>, NoteTransportCursor), NoteTransportError>;
215
216 async fn stream_notes(
218 &self,
219 tag: NoteTag,
220 cursor: NoteTransportCursor,
221 ) -> Result<Box<dyn NoteStream>, NoteTransportError>;
222}
223
224pub trait NoteStream:
226 Stream<Item = Result<Vec<NoteInfo>, NoteTransportError>> + Send + Unpin
227{
228}
229
230#[derive(Debug, Clone)]
232pub struct NoteInfo {
233 pub header: NoteHeader,
235 pub details_bytes: Vec<u8>,
237}
238
239impl Serializable for NoteInfo {
243 fn write_into<W: ByteWriter>(&self, target: &mut W) {
244 self.header.write_into(target);
245 self.details_bytes.write_into(target);
246 }
247}
248
249impl Deserializable for NoteInfo {
250 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
251 let header = NoteHeader::read_from(source)?;
252 let details_bytes = Vec::<u8>::read_from(source)?;
253 Ok(NoteInfo { header, details_bytes })
254 }
255}
256
257impl Serializable for NoteTransportCursor {
258 fn write_into<W: ByteWriter>(&self, target: &mut W) {
259 self.0.write_into(target);
260 }
261}
262
263impl Deserializable for NoteTransportCursor {
264 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
265 let value = u64::read_from(source)?;
266 Ok(Self::new(value))
267 }
268}
269
270fn rejoin_note(header: &NoteHeader, details_bytes: &[u8]) -> Result<Note, DeserializationError> {
271 let mut reader = SliceReader::new(details_bytes);
272 let details = NoteDetails::read_from(&mut reader)?;
273 let metadata = header.metadata().clone();
274 Ok(Note::new(details.assets().clone(), metadata, details.recipient().clone()))
275}