miden_node_proto/domain/
note.rs1use miden_protocol::Word;
2use miden_protocol::block::BlockNumber;
3use miden_protocol::crypto::merkle::SparseMerklePath;
4use miden_protocol::note::{
5 Note,
6 NoteAttachment,
7 NoteDetails,
8 NoteId,
9 NoteInclusionProof,
10 NoteMetadata,
11 NoteScript,
12 NoteTag,
13 NoteType,
14 Nullifier,
15};
16use miden_protocol::utils::{Deserializable, Serializable};
17use miden_standards::note::{NetworkAccountTarget, NetworkAccountTargetError};
18use thiserror::Error;
19
20use super::account::NetworkAccountId;
21use crate::errors::{ConversionError, MissingFieldHelper};
22use crate::generated as proto;
23
24impl TryFrom<proto::note::NoteMetadata> for NoteMetadata {
25 type Error = ConversionError;
26
27 fn try_from(value: proto::note::NoteMetadata) -> Result<Self, Self::Error> {
28 let sender = value
29 .sender
30 .ok_or_else(|| proto::note::NoteMetadata::missing_field(stringify!(sender)))?
31 .try_into()?;
32 let note_type = NoteType::try_from(u64::from(value.note_type))?;
33 let tag = NoteTag::new(value.tag);
34
35 let attachment = if value.attachment.is_empty() {
37 NoteAttachment::default()
38 } else {
39 NoteAttachment::read_from_bytes(&value.attachment)
40 .map_err(|err| ConversionError::deserialization_error("NoteAttachment", err))?
41 };
42
43 Ok(NoteMetadata::new(sender, note_type, tag).with_attachment(attachment))
44 }
45}
46
47impl From<Note> for proto::note::NetworkNote {
48 fn from(note: Note) -> Self {
49 Self {
50 metadata: Some(proto::note::NoteMetadata::from(note.metadata().clone())),
51 details: NoteDetails::from(note).to_bytes(),
52 }
53 }
54}
55
56impl From<Note> for proto::note::Note {
57 fn from(note: Note) -> Self {
58 Self {
59 metadata: Some(proto::note::NoteMetadata::from(note.metadata().clone())),
60 details: Some(NoteDetails::from(note).to_bytes()),
61 }
62 }
63}
64
65impl From<NetworkNote> for proto::note::NetworkNote {
66 fn from(note: NetworkNote) -> Self {
67 let note = Note::from(note);
68 Self {
69 metadata: Some(proto::note::NoteMetadata::from(note.metadata().clone())),
70 details: NoteDetails::from(note).to_bytes(),
71 }
72 }
73}
74
75impl From<NoteMetadata> for proto::note::NoteMetadata {
76 fn from(val: NoteMetadata) -> Self {
77 let sender = Some(val.sender().into());
78 let note_type = val.note_type() as u32;
79 let tag = val.tag().as_u32();
80 let attachment = val.attachment().to_bytes();
81
82 proto::note::NoteMetadata { sender, note_type, tag, attachment }
83 }
84}
85
86impl From<Word> for proto::note::NoteId {
87 fn from(digest: Word) -> Self {
88 Self { id: Some(digest.into()) }
89 }
90}
91
92impl TryFrom<proto::note::NoteId> for Word {
93 type Error = ConversionError;
94
95 fn try_from(note_id: proto::note::NoteId) -> Result<Self, Self::Error> {
96 note_id
97 .id
98 .as_ref()
99 .ok_or(proto::note::NoteId::missing_field(stringify!(id)))?
100 .try_into()
101 }
102}
103
104impl From<&NoteId> for proto::note::NoteId {
105 fn from(note_id: &NoteId) -> Self {
106 Self { id: Some(note_id.into()) }
107 }
108}
109
110impl From<(&NoteId, &NoteInclusionProof)> for proto::note::NoteInclusionInBlockProof {
111 fn from((note_id, proof): (&NoteId, &NoteInclusionProof)) -> Self {
112 Self {
113 note_id: Some(note_id.into()),
114 block_num: proof.location().block_num().as_u32(),
115 note_index_in_block: proof.location().node_index_in_block().into(),
116 inclusion_path: Some(proof.note_path().clone().into()),
117 }
118 }
119}
120
121impl TryFrom<&proto::note::NoteInclusionInBlockProof> for (NoteId, NoteInclusionProof) {
122 type Error = ConversionError;
123
124 fn try_from(
125 proof: &proto::note::NoteInclusionInBlockProof,
126 ) -> Result<(NoteId, NoteInclusionProof), Self::Error> {
127 let inclusion_path = SparseMerklePath::try_from(
128 proof
129 .inclusion_path
130 .as_ref()
131 .ok_or(proto::note::NoteInclusionInBlockProof::missing_field(stringify!(
132 inclusion_path
133 )))?
134 .clone(),
135 )?;
136
137 let note_id = Word::try_from(
138 proof
139 .note_id
140 .as_ref()
141 .ok_or(proto::note::NoteInclusionInBlockProof::missing_field(stringify!(note_id)))?
142 .id
143 .as_ref()
144 .ok_or(proto::note::NoteId::missing_field(stringify!(id)))?,
145 )?;
146
147 Ok((
148 NoteId::from_raw(note_id),
149 NoteInclusionProof::new(
150 proof.block_num.into(),
151 proof.note_index_in_block.try_into()?,
152 inclusion_path,
153 )?,
154 ))
155 }
156}
157
158impl TryFrom<proto::note::Note> for Note {
159 type Error = ConversionError;
160
161 fn try_from(proto_note: proto::note::Note) -> Result<Self, Self::Error> {
162 let metadata: NoteMetadata = proto_note
163 .metadata
164 .ok_or(proto::note::Note::missing_field(stringify!(metadata)))?
165 .try_into()?;
166
167 let details = proto_note
168 .details
169 .ok_or(proto::note::Note::missing_field(stringify!(details)))?;
170
171 let note_details = NoteDetails::read_from_bytes(&details)
172 .map_err(|err| ConversionError::deserialization_error("NoteDetails", err))?;
173
174 let (assets, recipient) = note_details.into_parts();
175 Ok(Note::new(assets, metadata, recipient))
176 }
177}
178
179#[derive(Clone, Debug, PartialEq, Eq)]
184pub enum NetworkNote {
185 SingleTarget(SingleTargetNetworkNote),
186}
187
188impl NetworkNote {
189 pub fn inner(&self) -> &Note {
190 match self {
191 NetworkNote::SingleTarget(note) => note.inner(),
192 }
193 }
194
195 pub fn metadata(&self) -> &NoteMetadata {
196 self.inner().metadata()
197 }
198
199 pub fn nullifier(&self) -> Nullifier {
200 self.inner().nullifier()
201 }
202
203 pub fn id(&self) -> NoteId {
204 self.inner().id()
205 }
206}
207
208impl From<NetworkNote> for Note {
209 fn from(value: NetworkNote) -> Self {
210 match value {
211 NetworkNote::SingleTarget(note) => note.into(),
212 }
213 }
214}
215
216impl TryFrom<Note> for NetworkNote {
217 type Error = NetworkNoteError;
218
219 fn try_from(note: Note) -> Result<Self, Self::Error> {
220 SingleTargetNetworkNote::try_from(note).map(NetworkNote::SingleTarget)
221 }
222}
223
224impl TryFrom<proto::note::NetworkNote> for NetworkNote {
225 type Error = ConversionError;
226
227 fn try_from(proto_note: proto::note::NetworkNote) -> Result<Self, Self::Error> {
228 from_proto(proto_note)
229 }
230}
231
232#[derive(Clone, Debug, PartialEq, Eq)]
240pub struct SingleTargetNetworkNote {
241 note: Note,
242 account_target: NetworkAccountTarget,
243}
244
245impl SingleTargetNetworkNote {
246 pub fn inner(&self) -> &Note {
247 &self.note
248 }
249
250 pub fn metadata(&self) -> &NoteMetadata {
251 self.inner().metadata()
252 }
253
254 pub fn nullifier(&self) -> Nullifier {
255 self.inner().nullifier()
256 }
257
258 pub fn id(&self) -> NoteId {
259 self.inner().id()
260 }
261
262 pub fn account_id(&self) -> NetworkAccountId {
264 self.account_target.target_id().try_into().expect("always a network account ID")
265 }
266
267 pub fn can_be_consumed(&self, block_num: BlockNumber) -> Option<bool> {
268 self.account_target.execution_hint().can_be_consumed(block_num)
269 }
270}
271
272impl From<SingleTargetNetworkNote> for Note {
273 fn from(value: SingleTargetNetworkNote) -> Self {
274 value.note
275 }
276}
277
278impl TryFrom<Note> for SingleTargetNetworkNote {
279 type Error = NetworkNoteError;
280
281 fn try_from(note: Note) -> Result<Self, Self::Error> {
282 let attachment = note.metadata().attachment();
284 let account_target = NetworkAccountTarget::try_from(attachment)
285 .map_err(NetworkNoteError::InvalidAttachment)?;
286 Ok(Self { note, account_target })
287 }
288}
289
290impl TryFrom<proto::note::NetworkNote> for SingleTargetNetworkNote {
291 type Error = ConversionError;
292
293 fn try_from(proto_note: proto::note::NetworkNote) -> Result<Self, Self::Error> {
294 from_proto(proto_note)
295 }
296}
297
298fn from_proto<T>(proto_note: proto::note::NetworkNote) -> Result<T, ConversionError>
300where
301 T: TryFrom<Note>,
302 T::Error: Into<ConversionError>,
303{
304 let details = NoteDetails::read_from_bytes(&proto_note.details)
305 .map_err(|err| ConversionError::deserialization_error("NoteDetails", err))?;
306 let (assets, recipient) = details.into_parts();
307 let metadata: NoteMetadata = proto_note
308 .metadata
309 .ok_or_else(|| proto::note::NetworkNote::missing_field(stringify!(metadata)))?
310 .try_into()?;
311 let note = Note::new(assets, metadata, recipient);
312 T::try_from(note).map_err(Into::into)
313}
314
315#[derive(Debug, Error)]
316pub enum NetworkNoteError {
317 #[error("note does not have a valid NetworkAccountTarget attachment: {0}")]
318 InvalidAttachment(#[source] NetworkAccountTargetError),
319}
320
321impl From<NoteScript> for proto::note::NoteScript {
325 fn from(script: NoteScript) -> Self {
326 Self {
327 entrypoint: script.entrypoint().into(),
328 mast: script.mast().to_bytes(),
329 }
330 }
331}