1use hiero_sdk_proto::services;
4
5use crate::contract::DelegateContractId;
6use crate::{
7 ContractId,
8 Error,
9 FromProtobuf,
10 KeyList,
11 PublicKey,
12 ToProtobuf,
13};
14
15#[derive(Eq, PartialEq, Debug, Clone, Hash)]
17#[non_exhaustive]
18pub enum Key {
19 Single(PublicKey),
22
23 ContractId(ContractId),
25
26 DelegateContractId(DelegateContractId),
28
29 KeyList(KeyList),
31}
32
33impl Key {
34 #[must_use]
36 pub fn to_bytes(&self) -> Vec<u8> {
37 ToProtobuf::to_bytes(self)
38 }
39}
40
41impl ToProtobuf for Key {
42 type Protobuf = services::Key;
43
44 fn to_protobuf(&self) -> Self::Protobuf {
45 use services::key::Key::*;
46
47 services::Key {
48 key: Some(match self {
49 Self::Single(key) => {
50 let bytes = key.to_bytes_raw();
51
52 match key.kind() {
53 crate::key::KeyKind::Ed25519 => Ed25519(bytes),
54 crate::key::KeyKind::Ecdsa => EcdsaSecp256k1(bytes),
55 }
56 }
57
58 Self::ContractId(id) => ContractId(id.to_protobuf()),
59 Self::DelegateContractId(id) => DelegatableContractId(id.to_protobuf()),
60 Self::KeyList(key) => key.to_protobuf_key(),
62 }),
63 }
64 }
65}
66
67impl From<PublicKey> for Key {
68 fn from(key: PublicKey) -> Self {
69 Self::Single(key)
70 }
71}
72
73impl From<ContractId> for Key {
74 fn from(id: ContractId) -> Self {
75 Self::ContractId(id)
76 }
77}
78
79impl From<KeyList> for Key {
80 fn from(value: KeyList) -> Self {
81 Self::KeyList(value)
82 }
83}
84
85impl FromProtobuf<services::Key> for Key {
86 fn from_protobuf(pb: services::Key) -> crate::Result<Self>
87 where
88 Self: Sized,
89 {
90 use services::key::Key::*;
91
92 match pb.key {
93 Some(Ed25519(bytes)) => Ok(Self::Single(PublicKey::from_bytes_ed25519(&bytes)?)),
94 Some(ContractId(id)) => Ok(Self::ContractId(crate::ContractId::from_protobuf(id)?)),
95 Some(DelegatableContractId(id)) => {
96 Ok(Self::DelegateContractId(crate::DelegateContractId::from_protobuf(id)?))
97 }
98 Some(Rsa3072(_)) => {
99 Err(Error::from_protobuf("unexpected unsupported RSA-3072 key in Key"))
100 }
101 Some(Ecdsa384(_)) => {
102 Err(Error::from_protobuf("unexpected unsupported ECDSA-384 key in Key"))
103 }
104 Some(ThresholdKey(it)) => Ok(Self::KeyList(crate::KeyList::from_protobuf(it)?)),
105 Some(KeyList(it)) => Ok(Self::KeyList(crate::KeyList::from_protobuf(it)?)),
106 Some(EcdsaSecp256k1(bytes)) => Ok(Self::Single(PublicKey::from_bytes_ecdsa(&bytes)?)),
107 None => Err(Error::from_protobuf("unexpected empty key in Key")),
108 }
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use assert_matches::assert_matches;
115 use hex_literal::hex;
116 use hiero_sdk_proto::services;
117
118 use crate::protobuf::FromProtobuf;
119 use crate::{
120 Key,
121 PublicKey,
122 };
123
124 #[test]
125 fn from_proto_key_ed25519() {
126 const KEY_BYTES: [u8; 32] =
127 hex!("0011223344556677889900112233445566778899001122334455667788990011");
128
129 let key = services::Key { key: Some(services::key::Key::Ed25519(KEY_BYTES.to_vec())) };
130
131 let key = PublicKey::from_protobuf(key).unwrap();
132
133 assert_matches!(key.kind(), crate::key::KeyKind::Ed25519);
134
135 assert_eq!(key.to_bytes_raw(), KEY_BYTES);
136 }
137
138 #[test]
139 fn from_proto_key_ecdsa() {
140 const KEY_BYTES: [u8; 35] =
141 hex!("3a21034e0441201f2bf9c7d9873c2a9dc3fd451f64b7c05e17e4d781d916e3a11dfd99");
142
143 let key = PublicKey::from_alias_bytes(&KEY_BYTES).unwrap().unwrap();
144
145 assert_matches!(key.kind(), crate::key::KeyKind::Ecdsa);
146
147 assert_eq!(Key::from(key).to_bytes(), KEY_BYTES);
148 }
149
150 #[test]
151 fn from_proto_key_key_list() {
152 const KEY_BYTES: [[u8; 32]; 2] = [
153 hex!("0011223344556677889900112233445566778899001122334455667788990011"),
154 hex!("aa11223344556677889900112233445566778899001122334455667788990011"),
155 ];
156
157 let key_list_pb = services::KeyList {
158 keys: KEY_BYTES
159 .iter()
160 .map(|it| services::Key { key: Some(services::key::Key::Ed25519(it.to_vec())) })
161 .collect(),
162 };
163
164 let key_pb = services::Key { key: Some(services::key::Key::KeyList(key_list_pb.clone())) };
165
166 let key = Key::from_protobuf(key_pb).unwrap();
167
168 let key_list = assert_matches!(key, Key::KeyList(it) => it);
169
170 assert_eq!(key_list.len(), KEY_BYTES.len());
171
172 let reencoded =
173 assert_matches!(key_list.to_protobuf_key(), services::key::Key::KeyList(key) => key);
174
175 assert_eq!(reencoded, key_list_pb);
176 }
177
178 #[test]
179 fn from_proto_key_threshold_key() {
180 const KEY_BYTES: [[u8; 32]; 2] = [
181 hex!("0011223344556677889900112233445566778899001122334455667788990011"),
182 hex!("aa11223344556677889900112233445566778899001122334455667788990011"),
183 ];
184
185 let key_list_pb = services::KeyList {
186 keys: KEY_BYTES
187 .iter()
188 .map(|it| services::Key { key: Some(services::key::Key::Ed25519(it.to_vec())) })
189 .collect(),
190 };
191
192 let threshold_key_pb = services::ThresholdKey { threshold: 1, keys: Some(key_list_pb) };
193
194 let key_pb =
195 services::Key { key: Some(services::key::Key::ThresholdKey(threshold_key_pb.clone())) };
196
197 let key = Key::from_protobuf(key_pb).unwrap();
198
199 let threshold_key = assert_matches!(key, Key::KeyList(it) => it);
200
201 assert_eq!(threshold_key.len(), KEY_BYTES.len());
202
203 let reencoded = assert_matches!(threshold_key.to_protobuf_key(), services::key::Key::ThresholdKey(key) => key);
204
205 assert_eq!(reencoded, threshold_key_pb);
206 }
207
208 #[test]
209 fn unsupported_key_fails() {
210 let key = services::Key { key: Some(services::key::Key::Rsa3072(Vec::from([0, 1, 2]))) };
211
212 assert_matches!(Key::from_protobuf(key), Err(crate::Error::FromProtobuf(_)));
213 }
214}