1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
use core::{fmt, num::TryFromIntError};
use miden_crypto::Felt;
use super::{
AccountId, ByteReader, ByteWriter, Deserializable, DeserializationError, NoteError,
NoteExecutionMode, NoteType, Serializable,
};
// NOTE TAG
// ================================================================================================
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct NoteTag(u32);
impl NoteTag {
// CONSTRUCTORS
// --------------------------------------------------------------------------------------------
/// Returns a new [NoteTag] instantiated from the specified account ID.
///
/// The tag is constructed as follows:
/// - For local execution, the two most significant bits are set to 0b00, the following 16 bits
/// are set to the 16 most significant bits of the account ID, and the remaining 14 bits are
/// set to 0.
/// - For network execution, the two most significant bits are set to 0b10 and the remaining
/// bits are set to the 30 most significant bits of the account ID.
pub fn from_account_id(
account_id: AccountId,
execution: NoteExecutionMode,
) -> Result<Self, NoteError> {
match execution {
NoteExecutionMode::Local => {
let id: u64 = account_id.into();
// select the 16 high bits of the account id
let high_bits = id & 0xffff000000000000;
// set bits (30,14] with the account id data
// set bits (32,30] as `0b00` identifying the note as intended for local execution
Ok(Self((high_bits >> 34) as u32))
},
NoteExecutionMode::Network => {
if !account_id.is_on_chain() {
Err(NoteError::NetworkExecutionRequiresOnChainAccount)
} else {
let id: u64 = account_id.into();
// select the 30 high bits of the account id
let high_bits = id & 0xfffffffc00000000;
// set bits (30,0] with the account id data
let tag = (high_bits >> 34) as u32;
// set bits (32,30] as `0b10` identifying the note as intended for network
// execution
Ok(Self(tag | 0x80000000))
}
},
}
}
// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------
/// Returns true if the note is intended for execution by a specific account.
///
/// A note is intended for execution by a single account if either the first two bits are zeros
/// or the first 3 bits are 0b100.
pub fn is_single_target(&self) -> bool {
let first_2_bit = self.0 >> 30;
let first_3_bits = self.0 >> 29;
first_2_bit == 0b00 || first_3_bits == 0b100
}
/// Returns note execution mode defined by this tag.
///
/// If the most significant bit of the tag is 0 or the 3 most significant bits are equal to
/// 0b101, the note is intended for local execution; otherwise, the note is intended for
/// network execution.
pub fn execution_mode(&self) -> NoteExecutionMode {
let first_bit = self.0 >> 31;
let first_3_bits = self.0 >> 29;
if first_bit == 0 || first_3_bits == 0b101 {
NoteExecutionMode::Local
} else {
NoteExecutionMode::Network
}
}
/// Returns the inner u32 value of this tag.
pub fn inner(&self) -> u32 {
self.0
}
// UTILITY METHODS
// --------------------------------------------------------------------------------------------
/// Returns an error if this tag is not consistent with the specified note type, and self
/// otherwise.
///
/// The tag and the note type are consistent if they satisfy the following rules:
/// - For off-chain notes, the most significant bit of the tag is 0.
/// - For public notes, the second most significant bit of the tag is 0.
/// - For encrypted notes, two most significant bits of the tag is 00.
pub fn validate(&self, note_type: NoteType) -> Result<Self, NoteError> {
let tag_mask = note_type as u32;
if (self.0 >> 30) & tag_mask != 0 {
Err(NoteError::InconsistentNoteTag(note_type, self.0 as u64))
} else {
Ok(*self)
}
}
}
impl fmt::Display for NoteTag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
// CONVERSIONS INTO NOTE TAG
// ================================================================================================
impl From<u32> for NoteTag {
fn from(value: u32) -> Self {
Self(value)
}
}
impl TryFrom<u64> for NoteTag {
type Error = TryFromIntError;
fn try_from(value: u64) -> Result<Self, Self::Error> {
Ok(Self(value.try_into()?))
}
}
impl TryFrom<Felt> for NoteTag {
type Error = TryFromIntError;
fn try_from(value: Felt) -> Result<Self, Self::Error> {
Ok(Self(value.as_int().try_into()?))
}
}
// CONVERSIONS FROM NOTE TAG
// ================================================================================================
impl From<NoteTag> for u32 {
fn from(value: NoteTag) -> Self {
value.0
}
}
impl From<NoteTag> for u64 {
fn from(value: NoteTag) -> Self {
value.0 as u64
}
}
impl From<NoteTag> for Felt {
fn from(value: NoteTag) -> Self {
Felt::from(value.0)
}
}
// SERIALIZATION
// ================================================================================================
impl Serializable for NoteTag {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.0.write_into(target);
}
}
impl Deserializable for NoteTag {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let tag = u32::read_from(source)?;
Ok(Self(tag))
}
}