miden_client/rpc/domain/
note.rs1use alloc::vec::Vec;
2
3use miden_objects::block::{BlockHeader, BlockNumber};
4use miden_objects::crypto::merkle::{MerklePath, SparseMerklePath};
5use miden_objects::note::{
6 Note,
7 NoteDetails,
8 NoteId,
9 NoteInclusionProof,
10 NoteMetadata,
11 NoteScript,
12 NoteTag,
13 NoteType,
14};
15use miden_objects::{Felt, MastForest, MastNodeId, Word};
16use miden_tx::utils::Deserializable;
17
18use super::{MissingFieldHelper, RpcConversionError};
19use crate::rpc::{RpcError, generated as proto};
20
21impl From<NoteId> for proto::note::NoteId {
22 fn from(value: NoteId) -> Self {
23 proto::note::NoteId { id: Some(value.into()) }
24 }
25}
26
27impl TryFrom<proto::note::NoteId> for NoteId {
28 type Error = RpcConversionError;
29
30 fn try_from(value: proto::note::NoteId) -> Result<Self, Self::Error> {
31 Word::try_from(value.id.ok_or(proto::note::NoteId::missing_field(stringify!(id)))?)
32 .map(Into::into)
33 }
34}
35
36impl TryFrom<proto::note::NoteMetadata> for NoteMetadata {
37 type Error = RpcConversionError;
38
39 fn try_from(value: proto::note::NoteMetadata) -> Result<Self, Self::Error> {
40 let sender = value
41 .sender
42 .ok_or_else(|| proto::note::NoteMetadata::missing_field(stringify!(sender)))?
43 .try_into()?;
44 let note_type = NoteType::try_from(u64::from(value.note_type))?;
45 let tag = NoteTag::from(value.tag);
46 let execution_hint = value.execution_hint.try_into()?;
47
48 let aux = Felt::try_from(value.aux).map_err(|_| RpcConversionError::NotAValidFelt)?;
49
50 Ok(NoteMetadata::new(sender, note_type, tag, execution_hint, aux)?)
51 }
52}
53
54impl From<NoteMetadata> for proto::note::NoteMetadata {
55 fn from(value: NoteMetadata) -> Self {
56 proto::note::NoteMetadata {
57 sender: Some(value.sender().into()),
58 note_type: value.note_type() as u32,
59 tag: value.tag().into(),
60 execution_hint: value.execution_hint().into(),
61 aux: value.aux().into(),
62 }
63 }
64}
65
66impl TryFrom<proto::note::NoteInclusionInBlockProof> for NoteInclusionProof {
67 type Error = RpcConversionError;
68
69 fn try_from(value: proto::note::NoteInclusionInBlockProof) -> Result<Self, Self::Error> {
70 Ok(NoteInclusionProof::new(
71 value.block_num.into(),
72 u16::try_from(value.note_index_in_block)
73 .map_err(|_| RpcConversionError::InvalidField("NoteIndexInBlock".into()))?,
74 value
75 .inclusion_path
76 .ok_or_else(|| {
77 proto::note::NoteInclusionInBlockProof::missing_field(stringify!(
78 inclusion_path
79 ))
80 })?
81 .try_into()?,
82 )?)
83 }
84}
85
86#[derive(Debug)]
91pub struct NoteSyncInfo {
92 pub chain_tip: BlockNumber,
94 pub block_header: BlockHeader,
96 pub mmr_path: MerklePath,
103 pub notes: Vec<CommittedNote>,
105}
106
107impl TryFrom<proto::rpc_store::SyncNotesResponse> for NoteSyncInfo {
108 type Error = RpcError;
109
110 fn try_from(value: proto::rpc_store::SyncNotesResponse) -> Result<Self, Self::Error> {
111 let chain_tip = value
112 .pagination_info
113 .ok_or(proto::rpc_store::SyncNotesResponse::missing_field(stringify!(pagination_info)))?
114 .chain_tip;
115
116 let block_header = value
118 .block_header
119 .ok_or(proto::rpc_store::SyncNotesResponse::missing_field(stringify!(block_header)))?
120 .try_into()?;
121
122 let mmr_path = value
123 .mmr_path
124 .ok_or(proto::rpc_store::SyncNotesResponse::missing_field(stringify!(mmr_path)))?
125 .try_into()?;
126
127 let mut notes = vec![];
129 for note in value.notes {
130 let note_id: NoteId = note
131 .note_id
132 .ok_or(proto::rpc_store::SyncNotesResponse::missing_field(stringify!(
133 notes.note_id
134 )))?
135 .try_into()?;
136
137 let inclusion_path = note
138 .inclusion_path
139 .ok_or(proto::rpc_store::SyncNotesResponse::missing_field(stringify!(
140 notes.inclusion_path
141 )))?
142 .try_into()?;
143
144 let metadata = note
145 .metadata
146 .ok_or(proto::rpc_store::SyncNotesResponse::missing_field(stringify!(
147 notes.metadata
148 )))?
149 .try_into()?;
150
151 let committed_note = CommittedNote::new(
152 note_id,
153 u16::try_from(note.note_index_in_block).expect("note index out of range"),
154 inclusion_path,
155 metadata,
156 );
157
158 notes.push(committed_note);
159 }
160
161 Ok(NoteSyncInfo {
162 chain_tip: chain_tip.into(),
163 block_header,
164 mmr_path,
165 notes,
166 })
167 }
168}
169
170#[derive(Debug, Clone)]
175pub struct CommittedNote {
176 note_id: NoteId,
178 note_index: u16,
180 inclusion_path: SparseMerklePath,
182 metadata: NoteMetadata,
184}
185
186impl CommittedNote {
187 pub fn new(
188 note_id: NoteId,
189 note_index: u16,
190 inclusion_path: SparseMerklePath,
191 metadata: NoteMetadata,
192 ) -> Self {
193 Self {
194 note_id,
195 note_index,
196 inclusion_path,
197 metadata,
198 }
199 }
200
201 pub fn note_id(&self) -> &NoteId {
202 &self.note_id
203 }
204
205 pub fn note_index(&self) -> u16 {
206 self.note_index
207 }
208
209 pub fn inclusion_path(&self) -> &SparseMerklePath {
210 &self.inclusion_path
211 }
212
213 pub fn metadata(&self) -> NoteMetadata {
214 self.metadata
215 }
216}
217
218#[allow(clippy::large_enum_variant)]
223pub enum FetchedNote {
224 Private(NoteId, NoteMetadata, NoteInclusionProof),
227 Public(Note, NoteInclusionProof),
229}
230
231impl FetchedNote {
232 pub fn inclusion_proof(&self) -> &NoteInclusionProof {
234 match self {
235 FetchedNote::Private(_, _, inclusion_proof)
236 | FetchedNote::Public(_, inclusion_proof) => inclusion_proof,
237 }
238 }
239
240 pub fn metadata(&self) -> &NoteMetadata {
242 match self {
243 FetchedNote::Private(_, metadata, _) => metadata,
244 FetchedNote::Public(note, _) => note.metadata(),
245 }
246 }
247
248 pub fn id(&self) -> NoteId {
250 match self {
251 FetchedNote::Private(id, ..) => *id,
252 FetchedNote::Public(note, _) => note.id(),
253 }
254 }
255}
256
257impl TryFrom<proto::note::CommittedNote> for FetchedNote {
258 type Error = RpcConversionError;
259
260 fn try_from(value: proto::note::CommittedNote) -> Result<Self, Self::Error> {
261 let inclusion_proof = value.inclusion_proof.ok_or_else(|| {
262 proto::note::CommittedNote::missing_field(stringify!(inclusion_proof))
263 })?;
264
265 let note_id: NoteId = inclusion_proof
266 .note_id
267 .ok_or_else(|| {
268 proto::note::CommittedNote::missing_field(stringify!(inclusion_proof.note_id))
269 })?
270 .try_into()?;
271
272 let inclusion_proof = NoteInclusionProof::try_from(inclusion_proof)?;
273
274 let note = value
275 .note
276 .ok_or_else(|| proto::note::CommittedNote::missing_field(stringify!(note)))?;
277
278 let metadata = note
279 .metadata
280 .ok_or_else(|| proto::note::CommittedNote::missing_field(stringify!(note.metadata)))?
281 .try_into()?;
282
283 if let Some(detail_bytes) = note.details {
284 let details = NoteDetails::read_from_bytes(&detail_bytes)?;
285 let (assets, recipient) = details.into_parts();
286
287 Ok(FetchedNote::Public(Note::new(assets, metadata, recipient), inclusion_proof))
288 } else {
289 Ok(FetchedNote::Private(note_id, metadata, inclusion_proof))
290 }
291 }
292}
293
294impl TryFrom<proto::note::NoteScript> for NoteScript {
298 type Error = RpcConversionError;
299
300 fn try_from(note_script: proto::note::NoteScript) -> Result<Self, Self::Error> {
301 let mast_forest = MastForest::read_from_bytes(¬e_script.mast)?;
302 let entrypoint = MastNodeId::from_u32_safe(note_script.entrypoint, &mast_forest)?;
303 Ok(NoteScript::from_parts(alloc::sync::Arc::new(mast_forest), entrypoint))
304 }
305}