ergo_lib/wallet/
secret_key.rs1use derive_more::From;
4use ergo_chain_types::EcPoint;
5use ergotree_interpreter::sigma_protocol::private_input::DhTupleProverInput;
6use ergotree_interpreter::sigma_protocol::private_input::DlogProverInput;
7use ergotree_interpreter::sigma_protocol::private_input::PrivateInput;
8use ergotree_ir::chain::address::Address;
9use ergotree_ir::ergo_tree::ErgoTree;
10use ergotree_ir::mir::constant::Constant;
11use ergotree_ir::mir::expr::Expr;
12use ergotree_ir::serialization::SigmaSerializable;
13use thiserror::Error;
14
15#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
17#[cfg_attr(feature = "json", serde(untagged))]
18#[derive(PartialEq, Eq, Debug, Clone, From)]
19pub enum SecretKey {
20 DlogSecretKey(DlogProverInput),
23 DhtSecretKey(DhTupleProverInput),
28}
29
30impl SecretKey {
31 pub fn random_dlog() -> SecretKey {
33 SecretKey::DlogSecretKey(DlogProverInput::random())
34 }
35
36 pub fn random_dht() -> SecretKey {
38 SecretKey::DhtSecretKey(DhTupleProverInput::random())
39 }
40
41 pub fn dlog_from_bytes(bytes: &[u8; DlogProverInput::SIZE_BYTES]) -> Option<SecretKey> {
43 DlogProverInput::from_bytes(bytes).map(SecretKey::DlogSecretKey)
44 }
45
46 pub fn dht_from_bytes(bytes: &[u8; DhTupleProverInput::SIZE_BYTES]) -> Option<SecretKey> {
51 DhTupleProverInput::from_bytes(bytes).map(SecretKey::DhtSecretKey)
52 }
53
54 pub fn dht_from_bytes_fields(
58 w_bytes: &[u8; DlogProverInput::SIZE_BYTES],
59 g_bytes: &[u8; EcPoint::GROUP_SIZE],
60 h_bytes: &[u8; EcPoint::GROUP_SIZE],
61 u_bytes: &[u8; EcPoint::GROUP_SIZE],
62 v_bytes: &[u8; EcPoint::GROUP_SIZE],
63 ) -> Option<SecretKey> {
64 DhTupleProverInput::from_bytes_fields(w_bytes, g_bytes, h_bytes, u_bytes, v_bytes)
65 .map(SecretKey::DhtSecretKey)
66 }
67
68 #[allow(clippy::unwrap_used)]
72 pub fn get_address_from_public_image(&self) -> Address {
73 match self {
74 SecretKey::DlogSecretKey(dlog) => Address::P2Pk(dlog.public_image()),
75 SecretKey::DhtSecretKey(dht) => {
76 let expr: Expr = Constant::from(dht.public_image().clone()).into();
77 let ergo_tree: ErgoTree = expr.try_into().unwrap();
78 Address::P2S(ergo_tree.sigma_serialize_bytes().unwrap())
79 }
80 }
81 }
82
83 pub fn to_bytes(&self) -> Vec<u8> {
85 match self {
86 SecretKey::DlogSecretKey(dlog) => dlog.to_bytes().to_vec(),
87 SecretKey::DhtSecretKey(dht) => dht.to_bytes().to_vec(),
88 }
89 }
90
91 pub fn from_bytes(bytes: &[u8]) -> Result<SecretKey, SecretKeyParsingError> {
96 if bytes.len() == DlogProverInput::SIZE_BYTES {
97 let mut dlog_bytes = [0u8; DlogProverInput::SIZE_BYTES];
98 dlog_bytes.copy_from_slice(bytes);
99 Ok(SecretKey::dlog_from_bytes(&dlog_bytes)
100 .ok_or(SecretKeyParsingError::DlogParsingError)?)
101 } else if bytes.len() == DhTupleProverInput::SIZE_BYTES {
102 let mut dht_bytes = [0u8; DhTupleProverInput::SIZE_BYTES];
103 dht_bytes.copy_from_slice(bytes);
104 Ok(SecretKey::DhtSecretKey(
105 DhTupleProverInput::from_bytes(&dht_bytes)
106 .ok_or(SecretKeyParsingError::DhtParsingError)?,
107 ))
108 } else {
109 Err(SecretKeyParsingError::InvalidLength)
110 }
111 }
112}
113
114#[allow(missing_docs)]
116#[derive(Error, Eq, PartialEq, Debug, Clone)]
117pub enum SecretKeyParsingError {
118 #[error("Dlog parsing error")]
119 DlogParsingError,
120 #[error("DHT secret parsing error")]
121 DhtParsingError,
122 #[error("Invalid length, expected either 32(Dlog) or 164(DHT) bytes")]
123 InvalidLength,
124}
125
126impl From<SecretKey> for PrivateInput {
127 fn from(s: SecretKey) -> Self {
128 match s {
129 SecretKey::DlogSecretKey(dlog) => PrivateInput::DlogProverInput(dlog),
130 SecretKey::DhtSecretKey(dht) => PrivateInput::DhTupleProverInput(dht),
131 }
132 }
133}
134
135#[cfg(test)]
136#[allow(clippy::unwrap_used)]
137mod tests {
138 use super::*;
139 use std::convert::TryInto;
140
141 #[test]
142 fn dlog_roundtrip() {
143 let sk = SecretKey::random_dlog();
144 let sk_copy1 =
145 SecretKey::dlog_from_bytes(&sk.to_bytes().as_slice().try_into().unwrap()).unwrap();
146 let sk_copy2 = SecretKey::from_bytes(sk.to_bytes().as_slice()).unwrap();
147 assert_eq!(sk, sk_copy1);
148 assert_eq!(sk, sk_copy2);
149 }
150
151 #[test]
152 fn dht_roundtrip() {
153 let sk = SecretKey::random_dht();
154 let sk_copy1 =
155 SecretKey::dht_from_bytes(&sk.to_bytes().as_slice().try_into().unwrap()).unwrap();
156 let sk_copy2 = SecretKey::from_bytes(sk.to_bytes().as_slice()).unwrap();
157 assert_eq!(sk, sk_copy1);
158 assert_eq!(sk, sk_copy2);
159 }
160}
161
162#[cfg(test)]
163#[allow(clippy::unwrap_used)]
164#[cfg(feature = "json")]
165mod json_tests {
166 use crate::wallet::secret_key::SecretKey;
167 use pretty_assertions::assert_eq;
168
169 #[test]
170 fn json_dlog_roundtrip() {
171 let sk = SecretKey::random_dlog();
172 let sk_json = serde_json::to_string(&sk).unwrap();
173 let sk_copy: SecretKey = serde_json::from_str(&sk_json).unwrap();
175 assert_eq!(sk, sk_copy);
176 }
177
178 #[test]
179 fn json_dht_roundtrip() {
180 let sk = SecretKey::random_dht();
181 let sk_json = serde_json::to_string(&sk).unwrap();
182 let sk_copy: SecretKey = serde_json::from_str(&sk_json).unwrap();
184 assert_eq!(sk, sk_copy);
185 }
186
187 #[test]
188 fn json_dht_golden() {
189 let sk_json = r#"{
190 "secret": "b2a93a9a37b4656c7abf4e259b9c066cd8bf4e02449d5956aaf453a73764bfeb",
191 "g": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
192 "h": "0288a812f57b66b4c68fd9e097c79a6e2847013fa4112a43c45cd41c9ba8c79b69",
193 "u": "0381fff110959bc06d99c5580c462c0196d8434bc5d470fb10a160019276e648c2",
194 "v": "02c6626ad387bb6b2eccc2fdd238c97ee4a81bfded401843bc8bb71f1cc7269924"
195}"#;
196 let sk: SecretKey = serde_json::from_str(sk_json).unwrap();
197 assert!(matches!(sk, SecretKey::DhtSecretKey(_)));
198 let sk_json_copy = serde_json::to_string_pretty(&sk).unwrap();
199 assert_eq!(sk_json, sk_json_copy);
200 }
201
202 #[test]
203 fn json_dlog_golden() {
204 let sk_json = r#""0cd81ce156fed4017520e561e9c492222027751ed0dd71b5a9b3a61da68b5850""#;
205 let sk: SecretKey = serde_json::from_str(sk_json).unwrap();
207 assert!(matches!(sk, SecretKey::DlogSecretKey(_)));
208 let sk_json_copy = serde_json::to_string_pretty(&sk).unwrap();
209 assert_eq!(sk_json, sk_json_copy);
210 }
211}