miden_protocol/block/account_tree/
account_id_key.rs1use miden_crypto::merkle::smt::LeafIndex;
2
3use super::AccountId;
4use crate::Word;
5use crate::account::AccountIdPrefix;
6use crate::crypto::merkle::smt::SMT_DEPTH;
7use crate::errors::AccountIdError;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub struct AccountIdKey(AccountId);
17
18impl AccountIdKey {
19 const KEY_SUFFIX_IDX: usize = 2;
21 const KEY_PREFIX_IDX: usize = 3;
22
23 pub fn new(id: AccountId) -> Self {
25 Self(id)
26 }
27
28 pub fn account_id(&self) -> AccountId {
30 self.0
31 }
32
33 pub fn as_word(&self) -> Word {
38 let mut key = Word::empty();
39
40 key[Self::KEY_SUFFIX_IDX] = self.0.suffix();
41 key[Self::KEY_PREFIX_IDX] = self.0.prefix().as_felt();
42
43 key
44 }
45
46 pub fn try_from_word(word: Word) -> Result<AccountId, AccountIdError> {
50 AccountId::try_from_elements(word[Self::KEY_SUFFIX_IDX], word[Self::KEY_PREFIX_IDX])
51 }
52
53 pub(crate) fn id_prefix_to_smt_key(prefix: AccountIdPrefix) -> Word {
55 let mut key = Word::empty();
56 key[Self::KEY_PREFIX_IDX] = prefix.as_felt();
57 key
58 }
59
60 pub fn to_leaf_index(&self) -> LeafIndex<SMT_DEPTH> {
65 LeafIndex::from(self.as_word())
66 }
67}
68
69impl From<AccountId> for AccountIdKey {
70 fn from(id: AccountId) -> Self {
71 Self(id)
72 }
73}
74
75#[cfg(test)]
79mod tests {
80
81 use miden_core::ZERO;
82
83 use super::{AccountId, *};
84 use crate::account::AccountType;
85 #[test]
86 fn test_as_word_layout() {
87 let id = AccountId::builder()
88 .account_type(AccountType::Private)
89 .build_with_seed([1u8; 32]);
90 let key = AccountIdKey::from(id);
91 let word = key.as_word();
92
93 assert_eq!(word[0], ZERO);
94 assert_eq!(word[1], ZERO);
95 assert_eq!(word[2], id.suffix());
96 assert_eq!(word[3], id.prefix().as_felt());
97 }
98
99 #[test]
100 fn test_roundtrip_word_conversion() {
101 let id = AccountId::builder()
102 .account_type(AccountType::Private)
103 .build_with_seed([1u8; 32]);
104
105 let key = AccountIdKey::from(id);
106 let recovered =
107 AccountIdKey::try_from_word(key.as_word()).expect("valid account id conversion");
108
109 assert_eq!(id, recovered);
110 }
111
112 #[test]
113 fn test_leaf_index_consistency() {
114 let id = AccountId::builder()
115 .account_type(AccountType::Private)
116 .build_with_seed([1u8; 32]);
117 let key = AccountIdKey::from(id);
118
119 let idx1 = key.to_leaf_index();
120 let idx2 = key.to_leaf_index();
121
122 assert_eq!(idx1, idx2);
123 }
124
125 #[test]
126 fn test_from_conversion() {
127 let id = AccountId::builder()
128 .account_type(AccountType::Private)
129 .build_with_seed([1u8; 32]);
130 let key: AccountIdKey = id.into();
131
132 assert_eq!(key.account_id(), id);
133 }
134
135 #[test]
136 fn test_multiple_roundtrips() {
137 for _ in 0..100 {
138 let id = AccountId::builder()
139 .account_type(AccountType::Private)
140 .build_with_seed([1u8; 32]);
141 let key = AccountIdKey::from(id);
142
143 let recovered =
144 AccountIdKey::try_from_word(key.as_word()).expect("valid account id conversion");
145
146 assert_eq!(id, recovered);
147 }
148 }
149}