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, NoteFile, NoteHeader, NoteTag};
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();
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 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 self.import_note(note_file).await?;
139 }
140
141 self.store.update_note_transport_cursor(rcursor).await?;
143
144 Ok(())
145 }
146}
147
148pub(crate) async fn init_note_transport_cursor(store: Arc<dyn Store>) -> Result<(), ClientError> {
150 let setting = NOTE_TRANSPORT_CURSOR_STORE_SETTING;
151 if store.get_setting(setting.into()).await?.is_none() {
152 let initial_cursor = 0u64.to_be_bytes().to_vec();
153 store.set_setting(setting.into(), initial_cursor).await?;
154 }
155 Ok(())
156}
157
158#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)]
163pub struct NoteTransportCursor(u64);
164
165pub struct NoteTransportUpdate {
167 pub cursor: NoteTransportCursor,
169 pub notes: Vec<Note>,
171}
172
173impl NoteTransportCursor {
174 pub fn new(value: u64) -> Self {
175 Self(value)
176 }
177
178 pub fn init() -> Self {
179 Self::new(0)
180 }
181
182 pub fn value(&self) -> u64 {
183 self.0
184 }
185}
186
187impl From<u64> for NoteTransportCursor {
188 fn from(value: u64) -> Self {
189 Self::new(value)
190 }
191}
192
193#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
195#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
196pub trait NoteTransportClient: Send + Sync {
197 async fn send_note(
199 &self,
200 header: NoteHeader,
201 details: Vec<u8>,
202 ) -> Result<(), NoteTransportError>;
203
204 async fn fetch_notes(
209 &self,
210 tag: &[NoteTag],
211 cursor: NoteTransportCursor,
212 ) -> Result<(Vec<NoteInfo>, NoteTransportCursor), NoteTransportError>;
213
214 async fn stream_notes(
216 &self,
217 tag: NoteTag,
218 cursor: NoteTransportCursor,
219 ) -> Result<Box<dyn NoteStream>, NoteTransportError>;
220}
221
222pub trait NoteStream:
224 Stream<Item = Result<Vec<NoteInfo>, NoteTransportError>> + Send + Unpin
225{
226}
227
228#[derive(Debug, Clone)]
230pub struct NoteInfo {
231 pub header: NoteHeader,
233 pub details_bytes: Vec<u8>,
235}
236
237impl Serializable for NoteInfo {
241 fn write_into<W: ByteWriter>(&self, target: &mut W) {
242 self.header.write_into(target);
243 self.details_bytes.write_into(target);
244 }
245}
246
247impl Deserializable for NoteInfo {
248 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
249 let header = NoteHeader::read_from(source)?;
250 let details_bytes = Vec::<u8>::read_from(source)?;
251 Ok(NoteInfo { header, details_bytes })
252 }
253}
254
255impl Serializable for NoteTransportCursor {
256 fn write_into<W: ByteWriter>(&self, target: &mut W) {
257 self.0.write_into(target);
258 }
259}
260
261impl Deserializable for NoteTransportCursor {
262 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
263 let value = u64::read_from(source)?;
264 Ok(Self::new(value))
265 }
266}
267
268fn rejoin_note(header: &NoteHeader, details_bytes: &[u8]) -> Result<Note, DeserializationError> {
269 let mut reader = SliceReader::new(details_bytes);
270 let details = NoteDetails::read_from(&mut reader)?;
271 let metadata = *header.metadata();
272 Ok(Note::new(details.assets().clone(), metadata, details.recipient().clone()))
273}