Skip to main content

miden_protocol/block/account_tree/
account_id_key.rs

1use miden_crypto::merkle::smt::LeafIndex;
2
3use super::AccountId;
4use crate::Word;
5use crate::crypto::merkle::smt::SMT_DEPTH;
6use crate::errors::AccountIdError;
7
8/// The account ID encoded as a key for use in AccountTree and advice maps in
9/// `TransactionAdviceInputs`.
10///
11/// Canonical word layout:
12///
13/// [0, 0, suffix, prefix]
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub struct AccountIdKey(AccountId);
16
17impl AccountIdKey {
18    // Indices in the word layout where the prefix and suffix are stored.
19    const KEY_SUFFIX_IDX: usize = 2;
20    const KEY_PREFIX_IDX: usize = 3;
21
22    /// Create from AccountId
23    pub fn new(id: AccountId) -> Self {
24        Self(id)
25    }
26
27    /// Returns the underlying AccountId
28    pub fn account_id(&self) -> AccountId {
29        self.0
30    }
31
32    // SMT WORD REPRESENTATION
33    //---------------------------------------------------------------------------------------------------
34
35    /// Returns `[0, 0, suffix, prefix]`
36    pub fn as_word(&self) -> Word {
37        let mut key = Word::empty();
38
39        key[Self::KEY_SUFFIX_IDX] = self.0.suffix();
40        key[Self::KEY_PREFIX_IDX] = self.0.prefix().as_felt();
41
42        key
43    }
44
45    /// Construct from SMT word representation.
46    ///
47    /// Validates structure before converting.
48    pub fn try_from_word(word: Word) -> Result<AccountId, AccountIdError> {
49        AccountId::try_from_elements(word[Self::KEY_SUFFIX_IDX], word[Self::KEY_PREFIX_IDX])
50    }
51
52    // LEAF INDEX
53    //---------------------------------------------------------------------------------------------------
54
55    /// Converts to SMT leaf index used by AccountTree
56    pub fn to_leaf_index(&self) -> LeafIndex<SMT_DEPTH> {
57        LeafIndex::from(self.as_word())
58    }
59}
60
61impl From<AccountId> for AccountIdKey {
62    fn from(id: AccountId) -> Self {
63        Self(id)
64    }
65}
66
67// TESTS
68//---------------------------------------------------------------------------------------------------
69
70#[cfg(test)]
71mod tests {
72
73    use miden_core::ZERO;
74
75    use super::{AccountId, *};
76    use crate::account::{AccountIdVersion, AccountStorageMode, AccountType};
77    #[test]
78    fn test_as_word_layout() {
79        let id = AccountId::dummy(
80            [1u8; 15],
81            AccountIdVersion::Version0,
82            AccountType::RegularAccountImmutableCode,
83            AccountStorageMode::Private,
84        );
85        let key = AccountIdKey::from(id);
86        let word = key.as_word();
87
88        assert_eq!(word[0], ZERO);
89        assert_eq!(word[1], ZERO);
90        assert_eq!(word[2], id.suffix());
91        assert_eq!(word[3], id.prefix().as_felt());
92    }
93
94    #[test]
95    fn test_roundtrip_word_conversion() {
96        let id = AccountId::dummy(
97            [1u8; 15],
98            AccountIdVersion::Version0,
99            AccountType::RegularAccountImmutableCode,
100            AccountStorageMode::Private,
101        );
102
103        let key = AccountIdKey::from(id);
104        let recovered =
105            AccountIdKey::try_from_word(key.as_word()).expect("valid account id conversion");
106
107        assert_eq!(id, recovered);
108    }
109
110    #[test]
111    fn test_leaf_index_consistency() {
112        let id = AccountId::dummy(
113            [1u8; 15],
114            AccountIdVersion::Version0,
115            AccountType::RegularAccountImmutableCode,
116            AccountStorageMode::Private,
117        );
118        let key = AccountIdKey::from(id);
119
120        let idx1 = key.to_leaf_index();
121        let idx2 = key.to_leaf_index();
122
123        assert_eq!(idx1, idx2);
124    }
125
126    #[test]
127    fn test_from_conversion() {
128        let id = AccountId::dummy(
129            [1u8; 15],
130            AccountIdVersion::Version0,
131            AccountType::RegularAccountImmutableCode,
132            AccountStorageMode::Private,
133        );
134        let key: AccountIdKey = id.into();
135
136        assert_eq!(key.account_id(), id);
137    }
138
139    #[test]
140    fn test_multiple_roundtrips() {
141        for _ in 0..100 {
142            let id = AccountId::dummy(
143                [1u8; 15],
144                AccountIdVersion::Version0,
145                AccountType::RegularAccountImmutableCode,
146                AccountStorageMode::Private,
147            );
148            let key = AccountIdKey::from(id);
149
150            let recovered =
151                AccountIdKey::try_from_word(key.as_word()).expect("valid account id conversion");
152
153            assert_eq!(id, recovered);
154        }
155    }
156}