Skip to main content

hiero_sdk/key/
key_list.rs

1use hiero_sdk_proto::services;
2
3use crate::protobuf::{
4    FromProtobuf,
5    ToProtobuf,
6};
7use crate::Key;
8
9// note: it appears keylists "just" implement the APIs of arrays in their language, which means, uh...
10// todo: Copy over the _entire_ `Vec` API?.
11/// A list of keys with an optional threshold.
12#[derive(Clone, Eq, PartialEq, Hash, Debug, Default)]
13pub struct KeyList {
14    // todo: better doc comment?
15    /// The list of keys.
16    pub keys: Vec<Key>,
17
18    /// If [`Some`]: The minimum number of keys that must sign.
19    pub threshold: Option<u32>,
20}
21
22impl std::ops::Deref for KeyList {
23    type Target = Vec<Key>;
24
25    fn deref(&self) -> &Self::Target {
26        &self.keys
27    }
28}
29
30impl std::ops::DerefMut for KeyList {
31    fn deref_mut(&mut self) -> &mut Self::Target {
32        &mut self.keys
33    }
34}
35
36impl KeyList {
37    /// Create a new empty key list.
38    #[must_use]
39    pub fn new() -> Self {
40        Self::default()
41    }
42
43    /// Returns `true` if this keylist is empty.
44    #[must_use]
45    pub fn is_empty(&self) -> bool {
46        self.keys.is_empty()
47    }
48
49    /// Removes and returns the element at position index within the key list, shifting all elements after it to the left.
50    ///
51    /// # Panics
52    /// Panics if index is out of bounds.
53    pub fn remove(&mut self, index: usize) -> Key {
54        self.keys.remove(index)
55    }
56
57    // why not `ToProtobuf`? because `ToProtobuf` should return a `KeyList`.
58    pub(crate) fn to_protobuf_key(&self) -> services::key::Key {
59        let key_list = services::KeyList { keys: self.keys.to_protobuf() };
60
61        if let Some(threshold) = self.threshold {
62            return services::key::Key::ThresholdKey(services::ThresholdKey {
63                threshold,
64                keys: Some(key_list),
65            });
66        };
67
68        services::key::Key::KeyList(key_list)
69    }
70}
71
72impl ToProtobuf for KeyList {
73    type Protobuf = services::KeyList;
74
75    fn to_protobuf(&self) -> Self::Protobuf {
76        services::KeyList { keys: self.keys.to_protobuf() }
77    }
78}
79
80impl FromIterator<Key> for KeyList {
81    fn from_iter<T: IntoIterator<Item = Key>>(iter: T) -> Self {
82        Self { keys: Vec::from_iter(iter), threshold: None }
83    }
84}
85
86impl From<Vec<Key>> for KeyList {
87    fn from(value: Vec<Key>) -> Self {
88        Self { keys: value, threshold: None }
89    }
90}
91
92impl<T: Into<Key>, const N: usize> From<[T; N]> for KeyList {
93    fn from(value: [T; N]) -> Self {
94        value.into_iter().map(Into::into).collect()
95    }
96}
97
98impl FromProtobuf<services::KeyList> for KeyList {
99    fn from_protobuf(pb: services::KeyList) -> crate::Result<Self>
100    where
101        Self: Sized,
102    {
103        Vec::from_protobuf(pb.keys).map(Self::from)
104    }
105}
106
107impl FromProtobuf<services::ThresholdKey> for KeyList {
108    fn from_protobuf(pb: services::ThresholdKey) -> crate::Result<Self>
109    where
110        Self: Sized,
111    {
112        let keys = Vec::from_protobuf(pb.keys.unwrap_or_default().keys)?;
113        Ok(Self { keys, threshold: Some(pb.threshold) })
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use assert_matches::assert_matches;
120    use hiero_sdk_proto::services;
121
122    use crate::protobuf::{
123        FromProtobuf,
124        ToProtobuf,
125    };
126    use crate::{
127        KeyList,
128        PrivateKey,
129        PublicKey,
130    };
131
132    fn keys() -> [PublicKey; 3] {
133        let key1 = PrivateKey::from_str_ed25519(
134        "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e10").unwrap()
135    .public_key();
136
137        let key2 = PrivateKey::from_str_ed25519(
138        "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e11").unwrap()
139    .public_key();
140
141        let key3 = PrivateKey::from_str_ed25519(
142        "302e020100300506032b657004220420db484b828e64b2d8f12ce3c0a0e93a0b8cce7af1bb8f39c97732394482538e12").unwrap()
143    .public_key();
144
145        [key1, key2, key3]
146    }
147
148    #[test]
149    fn from_protobuf() {
150        let key_list_pb =
151            services::KeyList { keys: keys().iter().map(|it| it.to_protobuf()).collect() };
152
153        let key_list = KeyList::from_protobuf(key_list_pb).unwrap();
154
155        assert!(keys().iter().all(|it| key_list.contains(&crate::Key::Single(*it))));
156    }
157
158    #[test]
159    fn to_protobuf_key() {
160        let key_list = KeyList::from(keys());
161
162        let proto_key = key_list.to_protobuf_key();
163
164        let proto_key_list = assert_matches!(proto_key, services::key::Key::KeyList(it) => it);
165
166        for (actual, expected) in proto_key_list.keys.iter().zip(keys()) {
167            assert_eq!(actual, &expected.to_protobuf());
168        }
169    }
170
171    #[test]
172    fn to_protobuf() {
173        let key_list = KeyList::from(keys());
174
175        let proto_key_list = key_list.to_protobuf();
176
177        for (actual, expected) in proto_key_list.keys.iter().zip(keys()) {
178            assert_eq!(actual, &expected.to_protobuf());
179        }
180    }
181
182    #[test]
183    fn len() {
184        let key_list = KeyList::from(keys());
185        let empty_key_list = KeyList::new();
186
187        assert_eq!(key_list.len(), 3);
188        assert!(!key_list.is_empty());
189        assert_eq!(empty_key_list.len(), 0);
190        assert!(empty_key_list.is_empty());
191    }
192
193    #[test]
194    fn contains() {
195        // Given / When
196
197        let key_list = KeyList::from(keys());
198        let empty_key_list = KeyList::new();
199
200        assert!(keys().iter().all(|it| key_list.contains(&crate::Key::Single(*it))));
201        assert!(!keys().iter().any(|it| empty_key_list.contains(&crate::Key::Single(*it))));
202    }
203
204    #[test]
205    fn push() {
206        let [a, b, c] = keys();
207
208        let mut key_list = KeyList::from([a, b]);
209
210        key_list.push(c.into());
211
212        assert_eq!(key_list.len(), 3);
213
214        assert!(key_list.contains(&c.into()));
215    }
216
217    #[test]
218    fn remove() {
219        let keys = keys();
220        let mut key_list = KeyList::from(keys);
221
222        let _ = key_list.remove(0);
223
224        assert_eq!(key_list.len(), 2);
225
226        assert!(!key_list.contains(&keys[0].into()));
227        assert!(key_list.contains(&keys[1].into()));
228        assert!(key_list.contains(&keys[2].into()));
229    }
230
231    #[test]
232    fn clear() {
233        let mut key_list = KeyList::from(keys());
234
235        key_list.clear();
236
237        assert!(key_list.is_empty());
238    }
239}