pub mod errors;
pub mod generated;
#[cfg(feature = "tonic")]
pub mod grpc;
use alloc::boxed::Box;
use alloc::sync::Arc;
use alloc::vec::Vec;
use futures::Stream;
use miden_protocol::address::Address;
use miden_protocol::block::BlockNumber;
use miden_protocol::note::{Note, NoteDetails, NoteFile, NoteHeader, NoteTag};
use miden_protocol::utils::serde::Serializable;
use miden_tx::auth::TransactionAuthenticator;
use miden_tx::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
SliceReader,
};
pub use self::errors::NoteTransportError;
use crate::{Client, ClientError};
pub const NOTE_TRANSPORT_TESTNET_ENDPOINT: &str = "https://transport.miden.io";
pub const NOTE_TRANSPORT_DEVNET_ENDPOINT: &str = "https://transport.devnet.miden.io";
pub const NOTE_TRANSPORT_CURSOR_STORE_SETTING: &str = "note_transport_cursor";
impl<AUTH> Client<AUTH> {
pub fn is_note_transport_enabled(&self) -> bool {
self.note_transport_api.is_some()
}
pub(crate) fn get_note_transport_api(
&self,
) -> Result<Arc<dyn NoteTransportClient>, NoteTransportError> {
self.note_transport_api.clone().ok_or(NoteTransportError::Disabled)
}
pub async fn send_private_note(
&mut self,
note: Note,
_address: &Address,
) -> Result<(), ClientError> {
let api = self.get_note_transport_api()?;
let header = note.header().clone();
let details = NoteDetails::from(note);
let details_bytes = details.to_bytes();
api.send_note(header, details_bytes).await?;
Ok(())
}
}
impl<AUTH> Client<AUTH>
where
AUTH: TransactionAuthenticator + Sync + 'static,
{
pub async fn fetch_private_notes(&mut self) -> Result<(), ClientError> {
let note_tags = self.store.get_unique_note_tags().await?;
let cursor = self.store.get_note_transport_cursor().await?;
self.fetch_transport_notes(cursor, note_tags).await?;
Ok(())
}
pub async fn fetch_all_private_notes(&mut self) -> Result<(), ClientError> {
let note_tags = self.store.get_unique_note_tags().await?;
self.fetch_transport_notes(NoteTransportCursor::init(), note_tags).await?;
Ok(())
}
pub(crate) async fn fetch_transport_notes<I>(
&mut self,
cursor: NoteTransportCursor,
tags: I,
) -> Result<(), ClientError>
where
I: IntoIterator<Item = NoteTag>,
{
const NOTE_LOOKBACK_BLOCKS: u32 = 20;
let mut notes = Vec::new();
let (note_infos, rcursor) = self
.get_note_transport_api()?
.fetch_notes(&tags.into_iter().collect::<Vec<_>>(), cursor)
.await?;
for note_info in ¬e_infos {
let note = rejoin_note(¬e_info.header, ¬e_info.details_bytes)?;
notes.push(note);
}
let sync_height = self.get_sync_height().await?;
let after_block_num =
BlockNumber::from(sync_height.as_u32().saturating_sub(NOTE_LOOKBACK_BLOCKS));
let mut note_requests = Vec::with_capacity(notes.len());
for note in notes {
let tag = note.metadata().tag();
let note_file = NoteFile::NoteDetails {
details: note.into(),
after_block_num,
tag: Some(tag),
};
note_requests.push(note_file);
}
self.import_notes(¬e_requests).await?;
self.store.update_note_transport_cursor(rcursor).await?;
Ok(())
}
}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub struct NoteTransportCursor(u64);
pub struct NoteTransportUpdate {
pub cursor: NoteTransportCursor,
pub notes: Vec<Note>,
}
impl NoteTransportCursor {
pub fn new(value: u64) -> Self {
Self(value)
}
pub fn init() -> Self {
Self::new(0)
}
pub fn value(&self) -> u64 {
self.0
}
}
impl From<u64> for NoteTransportCursor {
fn from(value: u64) -> Self {
Self::new(value)
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
pub trait NoteTransportClient: Send + Sync {
async fn send_note(
&self,
header: NoteHeader,
details: Vec<u8>,
) -> Result<(), NoteTransportError>;
async fn fetch_notes(
&self,
tag: &[NoteTag],
cursor: NoteTransportCursor,
) -> Result<(Vec<NoteInfo>, NoteTransportCursor), NoteTransportError>;
async fn stream_notes(
&self,
tag: NoteTag,
cursor: NoteTransportCursor,
) -> Result<Box<dyn NoteStream>, NoteTransportError>;
}
pub trait NoteStream:
Stream<Item = Result<Vec<NoteInfo>, NoteTransportError>> + Send + Unpin
{
}
#[derive(Debug, Clone)]
pub struct NoteInfo {
pub header: NoteHeader,
pub details_bytes: Vec<u8>,
}
impl Serializable for NoteInfo {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.header.write_into(target);
self.details_bytes.write_into(target);
}
}
impl Deserializable for NoteInfo {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let header = NoteHeader::read_from(source)?;
let details_bytes = Vec::<u8>::read_from(source)?;
Ok(NoteInfo { header, details_bytes })
}
}
impl Serializable for NoteTransportCursor {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.0.write_into(target);
}
}
impl Deserializable for NoteTransportCursor {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let value = u64::read_from(source)?;
Ok(Self::new(value))
}
}
fn rejoin_note(header: &NoteHeader, details_bytes: &[u8]) -> Result<Note, DeserializationError> {
let mut reader = SliceReader::new(details_bytes);
let details = NoteDetails::read_from(&mut reader)?;
let metadata = header.metadata().clone();
Ok(Note::new(details.assets().clone(), metadata, details.recipient().clone()))
}