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