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