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, AccountType};
77    #[test]
78    fn test_as_word_layout() {
79        let id = AccountId::dummy([1u8; 15], AccountIdVersion::Version1, AccountType::Private);
80        let key = AccountIdKey::from(id);
81        let word = key.as_word();
82
83        assert_eq!(word[0], ZERO);
84        assert_eq!(word[1], ZERO);
85        assert_eq!(word[2], id.suffix());
86        assert_eq!(word[3], id.prefix().as_felt());
87    }
88
89    #[test]
90    fn test_roundtrip_word_conversion() {
91        let id = AccountId::dummy([1u8; 15], AccountIdVersion::Version1, AccountType::Private);
92
93        let key = AccountIdKey::from(id);
94        let recovered =
95            AccountIdKey::try_from_word(key.as_word()).expect("valid account id conversion");
96
97        assert_eq!(id, recovered);
98    }
99
100    #[test]
101    fn test_leaf_index_consistency() {
102        let id = AccountId::dummy([1u8; 15], AccountIdVersion::Version1, AccountType::Private);
103        let key = AccountIdKey::from(id);
104
105        let idx1 = key.to_leaf_index();
106        let idx2 = key.to_leaf_index();
107
108        assert_eq!(idx1, idx2);
109    }
110
111    #[test]
112    fn test_from_conversion() {
113        let id = AccountId::dummy([1u8; 15], AccountIdVersion::Version1, AccountType::Private);
114        let key: AccountIdKey = id.into();
115
116        assert_eq!(key.account_id(), id);
117    }
118
119    #[test]
120    fn test_multiple_roundtrips() {
121        for _ in 0..100 {
122            let id = AccountId::dummy([1u8; 15], AccountIdVersion::Version1, AccountType::Private);
123            let key = AccountIdKey::from(id);
124
125            let recovered =
126                AccountIdKey::try_from_word(key.as_word()).expect("valid account id conversion");
127
128            assert_eq!(id, recovered);
129        }
130    }
131}