miden_objects/note/
note_tag.rs

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
10// CONSTANTS
11// ================================================================================================
12const NETWORK_EXECUTION: u8 = 0;
13const LOCAL_EXECUTION: u8 = 1;
14
15// The 2 most significant bits are set to `0b11`.
16const LOCAL_EXECUTION_WITH_ALL_NOTE_TYPES_ALLOWED: u32 = 0xc000_0000;
17// The 2 most significant bits are set to `0b10`.
18const PUBLIC_USECASE: u32 = 0x8000_0000;
19
20/// [super::Note]'s execution mode hints.
21///
22/// The execution hints are _not_ enforced, therefore function only as hints. For example, if a
23/// note's tag is created with the [NoteExecutionMode::Network], further validation is necessary to
24/// check the account_id is known, that the account's state is on-chain, and the account is
25/// controlled by the network.
26///
27/// The goal of the hint is to allow for a network node to quickly filter notes that are not
28/// intended for network execution, and skip the validation steps mentioned above.
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30#[repr(u8)]
31pub enum NoteExecutionMode {
32    Network = NETWORK_EXECUTION,
33    Local = LOCAL_EXECUTION,
34}
35
36// NOTE TAG
37// ================================================================================================
38
39/// [NoteTag]`s are best effort filters for notes registered with the network.
40///
41/// Tags are light-weight values used to speed up queries. The 2 most significant bits of the tags
42/// have the following interpretation:
43///
44/// | Prefix | Execution mode | Target   | Allowed [NoteType] |
45/// | ------ | :------------: | :------: | :----------------: |
46/// | `0b00` | Network        | Specific | [NoteType::Public] |
47/// | `0b01` | Network        | Use case | [NoteType::Public] |
48/// | `0b10` | Local          | Any      | [NoteType::Public] |
49/// | `0b11` | Local          | Any      | Any                |
50///
51/// Where:
52///
53/// - [`NoteExecutionMode`] is set to [`NoteExecutionMode::Network`] to hint a [`Note`](super::Note)
54///   should be consumed by the network. These notes will be further validated and if possible
55///   consumed by it.
56/// - Target describes how to further interpret the bits in the tag. For tags with a specific
57///   target, the rest of the tag is interpreted as a partial [`AccountId`]. For use case values,
58///   the meaning of the rest of the tag is not specified by the protocol and can be used by
59///   applications built on top of the rollup.
60///
61/// The note type is the only value enforced by the protocol. The rationale is that any note
62/// intended to be consumed by the network must be public to have all the details available. The
63/// public note for local execution is intended to allow users to search for notes that can be
64/// consumed right away, without requiring an off-band communication channel.
65#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
66pub struct NoteTag(u32);
67
68impl NoteTag {
69    // CONSTANTS
70    // --------------------------------------------------------------------------------------------
71
72    /// The exponent of the maximum allowed use case id. In other words, 2^exponent is the maximum
73    /// allowed use case id.
74    pub(crate) const MAX_USE_CASE_ID_EXPONENT: u8 = 14;
75
76    // CONSTRUCTORS
77    // --------------------------------------------------------------------------------------------
78
79    /// Returns a new [NoteTag] instantiated from the specified account ID and execution mode.
80    ///
81    /// The tag is constructed as follows:
82    ///
83    /// - For local execution, the two most significant bits are set to `0b11`, which allows for any
84    ///   note type to be used. The following 14 bits are set to the most significant bits of the
85    ///   account ID, and the remaining 16 bits are set to 0.
86    /// - For network execution, the most significant bits are set to `0b00` and the remaining bits
87    ///   are set to the 30 most significant bits of the account ID.
88    ///
89    /// # Errors
90    ///
91    /// Returns an error if:
92    /// - [`NoteExecutionMode::Network`] is provided but the storage mode of the `account_id` is not
93    ///   [`AccountStorageMode::Network`](crate::account::AccountStorageMode::Network).
94    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                // Shift the high bits of the account ID such that they are layed out as:
103                // [34 zero bits | remaining high bits (30 bits)].
104                let high_bits = prefix_id >> 34;
105
106                // This is equivalent to the following layout, interpreted as a u32:
107                // [2 zero bits | remaining high bits (30 bits)].
108                let high_bits = high_bits as u32;
109
110                // Select the upper half of the u32 which then contains the 14 most significant bits
111                // of the account ID, i.e.:
112                // [2 zero bits | remaining high bits (14 bits) | 16 zero bits].
113                let high_bits = high_bits & 0xffff0000;
114
115                // Set the local execution tag in the two most significant bits.
116                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                    // Shift the high bits of the account ID such that they are layed out as:
125                    // [34 zero bits | remaining high bits (30 bits)].
126                    let high_bits = prefix_id >> 34;
127
128                    // This is equivalent to the following layout, interpreted as a u32:
129                    // [2 zero bits | remaining high bits (30 bits)].
130                    // The two most significant zero bits match the tag we need for network
131                    // execution.
132                    Ok(Self(high_bits as u32))
133                }
134            },
135        }
136    }
137
138    /// Returns a new [NoteTag] instantiated for a custom use case which requires a public note.
139    ///
140    /// The public use_case tag requires a [NoteType::Public] note.
141    ///
142    /// The two high bits are set to the `b10` or `b01` depending on the execution hint, the next 14
143    /// bits are set to the `use_case_id`, and the low 16 bits are set to `payload`.
144    ///
145    /// # Errors
146    ///
147    /// - If `use_case_id` is larger than or equal to $2^{14}$.
148    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, // high bits set to `0b10`
159            NoteExecutionMode::Network => 0x40000000,   // high bits set to `0b01`
160        };
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    /// Returns a new [NoteTag] instantiated for a custom local use case.
169    ///
170    /// The local use_case tag is the only tag type that allows for [NoteType::Private] notes.
171    ///
172    /// The two high bits are set to the `b11`, the next 14 bits are set to the `use_case_id`, and
173    /// the low 16 bits are set to `payload`.
174    ///
175    /// # Errors
176    ///
177    /// - If `use_case_id` is larger than or equal to 2^14.
178    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    // PUBLIC ACCESSORS
191    // --------------------------------------------------------------------------------------------
192
193    /// Returns true if the note is intended for execution by a specific account.
194    ///
195    /// A note is intended for execution by a single account if the first two bits are zeros
196    pub fn is_single_target(&self) -> bool {
197        let first_2_bit = self.0 >> 30;
198        first_2_bit == 0b00
199    }
200
201    /// Returns note execution mode defined by this tag.
202    ///
203    /// If the most significant bit of the tag is 0 the note is intended for local execution;
204    /// otherwise, the note is intended for network execution.
205    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    /// Returns the inner u32 value of this tag.
216    pub fn inner(&self) -> u32 {
217        self.0
218    }
219
220    // UTILITY METHODS
221    // --------------------------------------------------------------------------------------------
222
223    /// Returns an error if this tag is not consistent with the specified note type, and self
224    /// otherwise.
225    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
245// CONVERSIONS INTO NOTE TAG
246// ================================================================================================
247
248impl 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
270// CONVERSIONS FROM NOTE TAG
271// ================================================================================================
272
273impl 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
291// SERIALIZATION
292// ================================================================================================
293
294impl 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// TESTS
308// ================================================================================================
309
310#[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        /// Private Account ID with the following bit pattern in the first and second byte:
435        /// 0b11001100_01010101
436        ///   ^^^^^^^^ ^^^^^^  <- 14 bits of the local tag.
437        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        // Expected private tag with LOCAL_EXECUTION_WITH_ALL_NOTE_TYPES_ALLOWED.
442        let expected_private_local_tag = NoteTag(0b11110011_00010101_00000000_00000000);
443
444        /// Public Account ID with the following bit pattern in the first and second byte:
445        /// 0b10101010_01010101_11001100_10101010
446        ///   ^^^^^^^^ ^^^^^^  <- 14 bits of the local tag.
447        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        // Expected public tag with LOCAL_EXECUTION_WITH_ALL_NOTE_TYPES_ALLOWED.
452        let expected_public_local_tag = NoteTag(0b11101010_10010101_00000000_00000000);
453
454        /// Network Account ID with the following bit pattern in the first and second byte:
455        /// 0b10101010_11001100_01110111_11001100
456        ///   ^^^^^^^^ ^^^^^^^^ ^^^^^^^^ ^^^^^^  <- 30 bits of the network tag.
457        ///   ^^^^^^^^ ^^^^^^  <- 14 bits of the local tag.
458        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        // Expected network tag with LOCAL_EXECUTION_WITH_ALL_NOTE_TYPES_ALLOWED.
463        let expected_network_local_tag = NoteTag(0b11101010_10110011_00000000_00000000);
464
465        // Expected network tag with leading 00 tag bits for network execution.
466        let expected_network_network_tag = NoteTag(0b00101010_10110011_00011101_11110011);
467
468        // Public and Private storage modes with NoteExecutionMode::Network should fail.
469        // ----------------------------------------------------------------------------------------
470
471        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        // NoteExecutionMode::Local
481        // ----------------------------------------------------------------------------------------
482
483        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        // NoteExecutionMode::Network
497        // ----------------------------------------------------------------------------------------
498
499        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        // NETWORK
508        // ----------------------------------------------------------------------------------------
509        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        // LOCAL
533        // ----------------------------------------------------------------------------------------
534        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}