1use core::{fmt, num::TryFromIntError};
2
3use miden_crypto::Felt;
4
5use super::{
6 AccountId, ByteReader, ByteWriter, Deserializable, DeserializationError, NoteError, NoteType,
7 Serializable,
8};
9
10const NETWORK_EXECUTION: u8 = 0;
13const LOCAL_EXECUTION: u8 = 1;
14
15const LOCAL_EXECUTION_WITH_ALL_NOTE_TYPES_ALLOWED: u32 = 0xc000_0000;
17const PUBLIC_USECASE: u32 = 0x8000_0000;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30#[repr(u8)]
31pub enum NoteExecutionMode {
32 Network = NETWORK_EXECUTION,
33 Local = LOCAL_EXECUTION,
34}
35
36#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
66pub struct NoteTag(u32);
67
68impl NoteTag {
69 pub(crate) const MAX_USE_CASE_ID_EXPONENT: u8 = 14;
75
76 pub fn from_account_id(
95 account_id: AccountId,
96 execution: NoteExecutionMode,
97 ) -> Result<Self, NoteError> {
98 match execution {
99 NoteExecutionMode::Local => {
100 let prefix_id: u64 = account_id.prefix().into();
101
102 let high_bits = prefix_id >> 34;
105
106 let high_bits = high_bits as u32;
109
110 let high_bits = high_bits & 0xffff0000;
114
115 Ok(Self(high_bits | LOCAL_EXECUTION_WITH_ALL_NOTE_TYPES_ALLOWED))
117 },
118 NoteExecutionMode::Network => {
119 if !account_id.is_network() {
120 Err(NoteError::NetworkExecutionRequiresNetworkAccount)
121 } else {
122 let prefix_id: u64 = account_id.prefix().into();
123
124 let high_bits = prefix_id >> 34;
127
128 Ok(Self(high_bits as u32))
133 }
134 },
135 }
136 }
137
138 pub fn for_public_use_case(
149 use_case_id: u16,
150 payload: u16,
151 execution: NoteExecutionMode,
152 ) -> Result<Self, NoteError> {
153 if (use_case_id >> 14) != 0 {
154 return Err(NoteError::NoteTagUseCaseTooLarge(use_case_id));
155 }
156
157 let execution_bits = match execution {
158 NoteExecutionMode::Local => PUBLIC_USECASE, NoteExecutionMode::Network => 0x40000000, };
161
162 let use_case_bits = (use_case_id as u32) << 16;
163 let payload_bits = payload as u32;
164
165 Ok(Self(execution_bits | use_case_bits | payload_bits))
166 }
167
168 pub fn for_local_use_case(use_case_id: u16, payload: u16) -> Result<Self, NoteError> {
179 if (use_case_id >> NoteTag::MAX_USE_CASE_ID_EXPONENT) != 0 {
180 return Err(NoteError::NoteTagUseCaseTooLarge(use_case_id));
181 }
182
183 let execution_bits = LOCAL_EXECUTION_WITH_ALL_NOTE_TYPES_ALLOWED;
184 let use_case_bits = (use_case_id as u32) << 16;
185 let payload_bits = payload as u32;
186
187 Ok(Self(execution_bits | use_case_bits | payload_bits))
188 }
189
190 pub fn is_single_target(&self) -> bool {
197 let first_2_bit = self.0 >> 30;
198 first_2_bit == 0b00
199 }
200
201 pub fn execution_mode(&self) -> NoteExecutionMode {
206 let first_bit = self.0 >> 31;
207
208 if first_bit == (LOCAL_EXECUTION as u32) {
209 NoteExecutionMode::Local
210 } else {
211 NoteExecutionMode::Network
212 }
213 }
214
215 pub fn inner(&self) -> u32 {
217 self.0
218 }
219
220 pub fn validate(&self, note_type: NoteType) -> Result<Self, NoteError> {
226 if self.execution_mode() == NoteExecutionMode::Network && note_type != NoteType::Public {
227 return Err(NoteError::NetworkExecutionRequiresPublicNote(note_type));
228 }
229
230 let is_public_use_case = (self.0 & 0xc0000000) == PUBLIC_USECASE;
231 if is_public_use_case && note_type != NoteType::Public {
232 Err(NoteError::PublicUseCaseRequiresPublicNote(note_type))
233 } else {
234 Ok(*self)
235 }
236 }
237}
238
239impl fmt::Display for NoteTag {
240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241 write!(f, "{}", self.0)
242 }
243}
244
245impl From<u32> for NoteTag {
249 fn from(value: u32) -> Self {
250 Self(value)
251 }
252}
253
254impl TryFrom<u64> for NoteTag {
255 type Error = TryFromIntError;
256
257 fn try_from(value: u64) -> Result<Self, Self::Error> {
258 Ok(Self(value.try_into()?))
259 }
260}
261
262impl TryFrom<Felt> for NoteTag {
263 type Error = TryFromIntError;
264
265 fn try_from(value: Felt) -> Result<Self, Self::Error> {
266 Ok(Self(value.as_int().try_into()?))
267 }
268}
269
270impl From<NoteTag> for u32 {
274 fn from(value: NoteTag) -> Self {
275 value.0
276 }
277}
278
279impl From<NoteTag> for u64 {
280 fn from(value: NoteTag) -> Self {
281 value.0 as u64
282 }
283}
284
285impl From<NoteTag> for Felt {
286 fn from(value: NoteTag) -> Self {
287 Felt::from(value.0)
288 }
289}
290
291impl Serializable for NoteTag {
295 fn write_into<W: ByteWriter>(&self, target: &mut W) {
296 self.0.write_into(target);
297 }
298}
299
300impl Deserializable for NoteTag {
301 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
302 let tag = u32::read_from(source)?;
303 Ok(Self(tag))
304 }
305}
306
307#[cfg(test)]
311mod tests {
312 use assert_matches::assert_matches;
313
314 use super::{NoteExecutionMode, NoteTag};
315 use crate::{
316 NoteError,
317 account::AccountId,
318 note::NoteType,
319 testing::account_id::{
320 ACCOUNT_ID_NETWORK_FUNGIBLE_FAUCET, ACCOUNT_ID_NETWORK_NON_FUNGIBLE_FAUCET,
321 ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET, ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET,
322 ACCOUNT_ID_PRIVATE_SENDER, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET,
323 ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1, ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2,
324 ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_3, ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET,
325 ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET_1,
326 ACCOUNT_ID_REGULAR_NETWORK_ACCOUNT_IMMUTABLE_CODE,
327 ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE,
328 ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE,
329 ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2,
330 ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE,
331 ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE_ON_CHAIN_2, ACCOUNT_ID_SENDER,
332 },
333 };
334
335 #[test]
336 fn test_from_account_id() {
337 let private_accounts = [
338 AccountId::try_from(ACCOUNT_ID_SENDER).unwrap(),
339 AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER).unwrap(),
340 AccountId::try_from(ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE).unwrap(),
341 AccountId::try_from(ACCOUNT_ID_PRIVATE_FUNGIBLE_FAUCET).unwrap(),
342 AccountId::try_from(ACCOUNT_ID_PRIVATE_NON_FUNGIBLE_FAUCET).unwrap(),
343 ];
344 let public_accounts = [
345 AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE).unwrap(),
346 AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE_2).unwrap(),
347 AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE).unwrap(),
348 AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE_ON_CHAIN_2)
349 .unwrap(),
350 AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET).unwrap(),
351 AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_1).unwrap(),
352 AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2).unwrap(),
353 AccountId::try_from(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_3).unwrap(),
354 AccountId::try_from(ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET).unwrap(),
355 AccountId::try_from(ACCOUNT_ID_PUBLIC_NON_FUNGIBLE_FAUCET_1).unwrap(),
356 ];
357 let network_accounts = [
358 AccountId::try_from(ACCOUNT_ID_REGULAR_NETWORK_ACCOUNT_IMMUTABLE_CODE).unwrap(),
359 AccountId::try_from(ACCOUNT_ID_NETWORK_FUNGIBLE_FAUCET).unwrap(),
360 AccountId::try_from(ACCOUNT_ID_NETWORK_NON_FUNGIBLE_FAUCET).unwrap(),
361 ];
362
363 for account_id in private_accounts.into_iter().chain(public_accounts) {
364 assert_matches!(
365 NoteTag::from_account_id(account_id, NoteExecutionMode::Network).unwrap_err(),
366 NoteError::NetworkExecutionRequiresNetworkAccount,
367 "tag generation must fail if network execution is attempted with private or public account ID"
368 )
369 }
370
371 for account_id in network_accounts {
372 let tag = NoteTag::from_account_id(account_id, NoteExecutionMode::Network)
373 .expect("tag generation must work with network execution and network account ID");
374 assert!(tag.is_single_target());
375 assert_eq!(tag.execution_mode(), NoteExecutionMode::Network);
376
377 tag.validate(NoteType::Public)
378 .expect("network execution should require notes to be public");
379 assert_matches!(
380 tag.validate(NoteType::Private),
381 Err(NoteError::NetworkExecutionRequiresPublicNote(NoteType::Private))
382 );
383 assert_matches!(
384 tag.validate(NoteType::Encrypted),
385 Err(NoteError::NetworkExecutionRequiresPublicNote(NoteType::Encrypted))
386 );
387 }
388
389 for account_id in private_accounts {
390 let tag = NoteTag::from_account_id(account_id, NoteExecutionMode::Local)
391 .expect("tag generation must work with local execution and private account ID");
392 assert!(!tag.is_single_target());
393 assert_eq!(tag.execution_mode(), NoteExecutionMode::Local);
394
395 tag.validate(NoteType::Public)
396 .expect("local execution should support public notes");
397 tag.validate(NoteType::Private)
398 .expect("local execution should support private notes");
399 tag.validate(NoteType::Encrypted)
400 .expect("local execution should support encrypted notes");
401 }
402
403 for account_id in public_accounts {
404 let tag = NoteTag::from_account_id(account_id, NoteExecutionMode::Local)
405 .expect("Tag generation must work with local execution and public account ID");
406 assert!(!tag.is_single_target());
407 assert_eq!(tag.execution_mode(), NoteExecutionMode::Local);
408
409 tag.validate(NoteType::Public)
410 .expect("local execution should support public notes");
411 tag.validate(NoteType::Private)
412 .expect("local execution should support private notes");
413 tag.validate(NoteType::Encrypted)
414 .expect("local execution should support encrypted notes");
415 }
416
417 for account_id in network_accounts {
418 let tag = NoteTag::from_account_id(account_id, NoteExecutionMode::Local)
419 .expect("Tag generation must work with local execution and network account ID");
420 assert!(!tag.is_single_target());
421 assert_eq!(tag.execution_mode(), NoteExecutionMode::Local);
422
423 tag.validate(NoteType::Public)
424 .expect("local execution should support public notes");
425 tag.validate(NoteType::Private)
426 .expect("local execution should support private notes");
427 tag.validate(NoteType::Encrypted)
428 .expect("local execution should support encrypted notes");
429 }
430 }
431
432 #[test]
433 fn test_from_account_id_values() {
434 const PRIVATE_ACCOUNT_INT: u128 = ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE
438 | 0x0055_0000_0000_0000_0000_0000_0000_0000;
439 let private_account_id = AccountId::try_from(PRIVATE_ACCOUNT_INT).unwrap();
440
441 let expected_private_local_tag = NoteTag(0b11110011_00010101_00000000_00000000);
443
444 const PUBLIC_ACCOUNT_INT: u128 = ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE
448 | 0x0055_ccaa_0000_0000_0000_0000_0000_0000;
449 let public_account_id = AccountId::try_from(PUBLIC_ACCOUNT_INT).unwrap();
450
451 let expected_public_local_tag = NoteTag(0b11101010_10010101_00000000_00000000);
453
454 const NETWORK_ACCOUNT_INT: u128 = ACCOUNT_ID_REGULAR_NETWORK_ACCOUNT_IMMUTABLE_CODE
459 | 0x00cc_77cc_0000_0000_0000_0000_0000_0000;
460 let network_account_id = AccountId::try_from(NETWORK_ACCOUNT_INT).unwrap();
461
462 let expected_network_local_tag = NoteTag(0b11101010_10110011_00000000_00000000);
464
465 let expected_network_network_tag = NoteTag(0b00101010_10110011_00011101_11110011);
467
468 assert_matches!(
472 NoteTag::from_account_id(private_account_id, NoteExecutionMode::Network),
473 Err(NoteError::NetworkExecutionRequiresNetworkAccount)
474 );
475 assert_matches!(
476 NoteTag::from_account_id(public_account_id, NoteExecutionMode::Network),
477 Err(NoteError::NetworkExecutionRequiresNetworkAccount)
478 );
479
480 assert_eq!(
484 NoteTag::from_account_id(private_account_id, NoteExecutionMode::Local).unwrap(),
485 expected_private_local_tag,
486 );
487 assert_eq!(
488 NoteTag::from_account_id(public_account_id, NoteExecutionMode::Local).unwrap(),
489 expected_public_local_tag,
490 );
491 assert_eq!(
492 NoteTag::from_account_id(network_account_id, NoteExecutionMode::Local).unwrap(),
493 expected_network_local_tag,
494 );
495
496 assert_eq!(
500 NoteTag::from_account_id(network_account_id, NoteExecutionMode::Network).unwrap(),
501 expected_network_network_tag,
502 );
503 }
504
505 #[test]
506 fn test_for_public_use_case() {
507 let tag = NoteTag::for_public_use_case(0b0, 0b0, NoteExecutionMode::Network).unwrap();
510 assert_eq!(tag, NoteTag(0b01000000_00000000_00000000_00000000));
511
512 tag.validate(NoteType::Public).unwrap();
513
514 assert_matches!(
515 tag.validate(NoteType::Private).unwrap_err(),
516 NoteError::NetworkExecutionRequiresPublicNote(NoteType::Private)
517 );
518 assert_matches!(
519 tag.validate(NoteType::Encrypted).unwrap_err(),
520 NoteError::NetworkExecutionRequiresPublicNote(NoteType::Encrypted)
521 );
522
523 let tag = NoteTag::for_public_use_case(0b1, 0b0, NoteExecutionMode::Network).unwrap();
524 assert_eq!(tag, NoteTag(0b01000000_00000001_00000000_00000000));
525
526 let tag = NoteTag::for_public_use_case(0b0, 0b1, NoteExecutionMode::Network).unwrap();
527 assert_eq!(tag, NoteTag(0b01000000_00000000_00000000_00000001));
528
529 let tag = NoteTag::for_public_use_case(1 << 13, 0b0, NoteExecutionMode::Network).unwrap();
530 assert_eq!(tag, NoteTag(0b01100000_00000000_00000000_00000000));
531
532 let tag = NoteTag::for_public_use_case(0b0, 0b0, NoteExecutionMode::Local).unwrap();
535 assert_eq!(tag, NoteTag(0b10000000_00000000_00000000_00000000));
536
537 tag.validate(NoteType::Public).unwrap();
538 assert_matches!(
539 tag.validate(NoteType::Private).unwrap_err(),
540 NoteError::PublicUseCaseRequiresPublicNote(NoteType::Private)
541 );
542 assert_matches!(
543 tag.validate(NoteType::Encrypted).unwrap_err(),
544 NoteError::PublicUseCaseRequiresPublicNote(NoteType::Encrypted)
545 );
546
547 let tag = NoteTag::for_public_use_case(0b0, 0b1, NoteExecutionMode::Local).unwrap();
548 assert_eq!(tag, NoteTag(0b10000000_00000000_00000000_00000001));
549
550 let tag = NoteTag::for_public_use_case(0b1, 0b0, NoteExecutionMode::Local).unwrap();
551 assert_eq!(tag, NoteTag(0b10000000_00000001_00000000_00000000));
552
553 let tag = NoteTag::for_public_use_case(1 << 13, 0b0, NoteExecutionMode::Local).unwrap();
554 assert_eq!(tag, NoteTag(0b10100000_00000000_00000000_00000000));
555
556 assert_matches!(
557 NoteTag::for_public_use_case(1 << 15, 0b0, NoteExecutionMode::Local).unwrap_err(),
558 NoteError::NoteTagUseCaseTooLarge(use_case) if use_case == 1 << 15
559 );
560 assert_matches!(
561 NoteTag::for_public_use_case(1 << 14, 0b0, NoteExecutionMode::Local).unwrap_err(),
562 NoteError::NoteTagUseCaseTooLarge(use_case) if use_case == 1 << 14
563 );
564 }
565
566 #[test]
567 fn test_for_private_use_case() {
568 let tag = NoteTag::for_local_use_case(0b0, 0b0).unwrap();
569 assert_eq!(tag, NoteTag(0b11000000_00000000_00000000_00000000));
570
571 tag.validate(NoteType::Public)
572 .expect("local execution should support public notes");
573 tag.validate(NoteType::Private)
574 .expect("local execution should support private notes");
575 tag.validate(NoteType::Encrypted)
576 .expect("local execution should support encrypted notes");
577
578 let tag = NoteTag::for_local_use_case(0b0, 0b1).unwrap();
579 assert_eq!(tag, NoteTag(0b11000000_00000000_00000000_00000001));
580
581 let tag = NoteTag::for_local_use_case(0b1, 0b0).unwrap();
582 assert_eq!(tag, NoteTag(0b11000000_00000001_00000000_00000000));
583
584 let tag = NoteTag::for_local_use_case(1 << 13, 0b0).unwrap();
585 assert_eq!(tag, NoteTag(0b11100000_00000000_00000000_00000000));
586
587 assert_matches!(
588 NoteTag::for_local_use_case(1 << 15, 0b0).unwrap_err(),
589 NoteError::NoteTagUseCaseTooLarge(use_case) if use_case == 1 << 15
590 );
591 assert_matches!(
592 NoteTag::for_local_use_case(1 << 14, 0b0).unwrap_err(),
593 NoteError::NoteTagUseCaseTooLarge(use_case) if use_case == 1 << 14
594 );
595 }
596}