miden_objects/account/account_id/id_anchor.rs
1use crate::{
2 Digest, EMPTY_WORD,
3 block::{BlockHeader, BlockNumber},
4 errors::AccountIdError,
5};
6
7// ACCOUNT ID ANCHOR
8// ================================================================================================
9
10/// The anchor of an [`AccountId`](crate::account::AccountId). See the type's documentation for
11/// details on anchors.
12///
13/// This type is recommended to be created from a reference to a [`BlockHeader`] via the `TryFrom`
14/// impl.
15///
16/// # Constraints
17///
18/// This type enforces the following constraints.
19/// - The `anchor_block_number` % 2^[`BlockNumber::EPOCH_LENGTH_EXPONENT`] must be zero. In other
20/// words, the block number must a multiple of 2^[`BlockNumber::EPOCH_LENGTH_EXPONENT`].
21/// - The epoch derived from the `anchor_block_number` must be strictly less than [`u16::MAX`].
22#[derive(Debug, Clone, Copy)]
23pub struct AccountIdAnchor {
24 epoch: u16,
25 block_commitment: Digest,
26}
27
28impl AccountIdAnchor {
29 // CONSTANTS
30 // --------------------------------------------------------------------------------------------
31
32 /// A "pre-genesis" [`AccountIdAnchor`] which can be used to anchor accounts created in the
33 /// genesis block.
34 ///
35 /// This anchor should only be used for accounts included in the genesis state, but should not
36 /// be used as actual anchors in a running network. The reason is that this anchor has the same
37 /// `epoch` as the genesis block will have (epoch `0`). However, the genesis block will have a
38 /// different block_commitment than this anchor ([`EMPTY_WORD`]) and so any account ID that
39 /// would use this anchor would be rejected as invalid by the transaction kernel.
40 pub const PRE_GENESIS: Self = Self {
41 epoch: 0,
42 block_commitment: Digest::new(EMPTY_WORD),
43 };
44
45 // CONSTRUCTORS
46 // --------------------------------------------------------------------------------------------
47
48 /// Creates a new [`AccountIdAnchor`] from the provided `anchor_block_number` and
49 /// `anchor_block_commitment`.
50 ///
51 /// # Errors
52 ///
53 /// Returns an error if any of the anchor constraints are not met. See the [type
54 /// documentation](AccountIdAnchor) for details.
55 pub fn new(
56 anchor_block_number: BlockNumber,
57 anchor_block_commitment: Digest,
58 ) -> Result<Self, AccountIdError> {
59 if anchor_block_number.as_u32() & 0x0000_ffff != 0 {
60 return Err(AccountIdError::AnchorBlockMustBeEpochBlock);
61 }
62
63 let anchor_epoch = anchor_block_number.block_epoch();
64
65 if anchor_epoch == u16::MAX {
66 return Err(AccountIdError::AnchorEpochMustNotBeU16Max);
67 }
68
69 Ok(Self {
70 epoch: anchor_epoch,
71 block_commitment: anchor_block_commitment,
72 })
73 }
74
75 /// Creates a new [`AccountIdAnchor`] from the provided `anchor_epoch` and
76 /// `anchor_block_commitment` without validation.
77 ///
78 /// # Warning
79 ///
80 /// The caller must ensure validity of the `anchor_epoch`, in particular the correctness of the
81 /// relationship between the `anchor_epoch` and the provided `anchor_block_commitment`.
82 ///
83 /// # Panics
84 ///
85 /// If debug_assertions are enabled (e.g. in debug mode), this function panics if the
86 /// `anchor_epoch` is [`u16::MAX`].
87 pub fn new_unchecked(anchor_epoch: u16, anchor_block_commitment: Digest) -> Self {
88 debug_assert_ne!(anchor_epoch, u16::MAX, "anchor epoch cannot be u16::MAX");
89
90 Self {
91 epoch: anchor_epoch,
92 block_commitment: anchor_block_commitment,
93 }
94 }
95
96 // PUBLIC ACCESSORS
97 // --------------------------------------------------------------------------------------------
98
99 /// Returns the epoch of this anchor.
100 pub fn epoch(self) -> u16 {
101 self.epoch
102 }
103
104 /// Returns the block commitment of this anchor.
105 pub fn block_commitment(self) -> Digest {
106 self.block_commitment
107 }
108}
109
110// CONVERSIONS TO ACCOUNT ID ANCHOR
111// ================================================================================================
112
113impl TryFrom<&BlockHeader> for AccountIdAnchor {
114 type Error = AccountIdError;
115
116 /// Extracts the [`BlockHeader::block_num`] and [`BlockHeader::commitment`] from the provided
117 /// `block_header` and tries to convert it to an [`AccountIdAnchor`].
118 ///
119 /// # Errors
120 ///
121 /// Returns an error if any of the anchor constraints are not met. See the [type
122 /// documentation](AccountIdAnchor) for details.
123 fn try_from(block_header: &BlockHeader) -> Result<Self, Self::Error> {
124 Self::new(block_header.block_num(), block_header.commitment())
125 }
126}