1use core::str::FromStr;
20
21#[cfg(feature = "async")]
22use darkfi_serial::async_trait;
23use darkfi_serial::{SerialDecodable, SerialEncodable};
24use halo2_gadgets::ecc::chip::FixedPoint;
25use pasta_curves::{
26 arithmetic::CurveAffine,
27 group::{
28 ff::{Field, PrimeField},
29 Curve, Group, GroupEncoding,
30 },
31 pallas,
32};
33use rand_core::{CryptoRng, RngCore};
34
35use super::{constants::NullifierK, util::fp_mod_fv};
36use crate::error::ContractError;
37
38#[derive(Copy, Clone, PartialEq, Eq, Debug, SerialEncodable, SerialDecodable)]
40pub struct Keypair {
41 pub secret: SecretKey,
42 pub public: PublicKey,
43}
44
45impl Keypair {
46 pub fn new(secret: SecretKey) -> Self {
48 Self { secret, public: PublicKey::from_secret(secret) }
49 }
50
51 pub fn random(rng: &mut (impl CryptoRng + RngCore)) -> Self {
53 Self::new(SecretKey::random(rng))
54 }
55}
56
57impl Default for Keypair {
58 fn default() -> Self {
60 let secret = SecretKey::from(pallas::Base::from(42));
61 let public = PublicKey::from_secret(secret);
62 Self { secret, public }
63 }
64}
65
66#[derive(Copy, Clone, PartialEq, Eq, Debug, SerialEncodable, SerialDecodable)]
68pub struct SecretKey(pallas::Base);
69
70impl SecretKey {
71 pub fn inner(&self) -> pallas::Base {
73 self.0
74 }
75
76 pub fn random(rng: &mut (impl CryptoRng + RngCore)) -> Self {
78 Self(pallas::Base::random(rng))
79 }
80
81 pub fn from_bytes(bytes: [u8; 32]) -> Result<Self, ContractError> {
84 match pallas::Base::from_repr(bytes).into() {
85 Some(k) => Ok(Self(k)),
86 None => Err(ContractError::IoError("Could not convert bytes to SecretKey".to_string())),
87 }
88 }
89}
90
91impl From<pallas::Base> for SecretKey {
92 fn from(x: pallas::Base) -> Self {
93 Self(x)
94 }
95}
96
97impl FromStr for SecretKey {
98 type Err = ContractError;
99
100 fn from_str(enc: &str) -> Result<Self, Self::Err> {
102 let decoded = bs58::decode(enc).into_vec()?;
103 if decoded.len() != 32 {
104 return Err(Self::Err::IoError(
105 "Failed decoding SecretKey from bytes, len is not 32".to_string(),
106 ))
107 }
108
109 Self::from_bytes(decoded.try_into().unwrap())
110 }
111}
112
113impl core::fmt::Display for SecretKey {
114 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
115 let disp: String = bs58::encode(self.0.to_repr()).into_string();
116 write!(f, "{disp}")
117 }
118}
119
120#[derive(Copy, Clone, PartialEq, Eq, Debug, SerialEncodable, SerialDecodable)]
122pub struct PublicKey(pallas::Point);
123
124impl PublicKey {
125 pub fn inner(&self) -> pallas::Point {
127 self.0
128 }
129
130 pub fn from_secret(s: SecretKey) -> Self {
132 let p = NullifierK.generator() * fp_mod_fv(s.inner());
133 Self(p)
134 }
135
136 pub fn from_bytes(bytes: [u8; 32]) -> Result<Self, ContractError> {
139 match <subtle::CtOption<pallas::Point> as Into<Option<pallas::Point>>>::into(
140 pallas::Point::from_bytes(&bytes),
141 ) {
142 Some(k) => {
143 if bool::from(k.is_identity()) {
144 return Err(ContractError::IoError(
145 "Could not convert bytes to PublicKey".to_string(),
146 ))
147 }
148
149 Ok(Self(k))
150 }
151 None => Err(ContractError::IoError("Could not convert bytes to PublicKey".to_string())),
152 }
153 }
154
155 pub fn to_bytes(&self) -> [u8; 32] {
157 self.0.to_bytes()
158 }
159
160 pub fn x(&self) -> pallas::Base {
162 *self.0.to_affine().coordinates().unwrap().x()
163 }
164
165 pub fn y(&self) -> pallas::Base {
167 *self.0.to_affine().coordinates().unwrap().y()
168 }
169
170 pub fn xy(&self) -> (pallas::Base, pallas::Base) {
172 let coords = self.0.to_affine().coordinates().unwrap();
173 (*coords.x(), *coords.y())
174 }
175}
176
177impl TryFrom<pallas::Point> for PublicKey {
178 type Error = ContractError;
179
180 fn try_from(x: pallas::Point) -> Result<Self, Self::Error> {
181 if bool::from(x.is_identity()) {
182 return Err(ContractError::IoError(
183 "Could not convert identity point to PublicKey".to_string(),
184 ))
185 }
186
187 Ok(Self(x))
188 }
189}
190
191impl FromStr for PublicKey {
192 type Err = ContractError;
193
194 fn from_str(enc: &str) -> Result<Self, Self::Err> {
196 let decoded = bs58::decode(enc).into_vec()?;
197 if decoded.len() != 32 {
198 return Err(Self::Err::IoError(
199 "Failed decoding PublicKey from bytes, len is not 32".to_string(),
200 ))
201 }
202
203 Self::from_bytes(decoded.try_into().unwrap())
204 }
205}
206
207impl core::fmt::Display for PublicKey {
208 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
209 let disp: String = bs58::encode(self.0.to_bytes()).into_string();
210 write!(f, "{disp}")
211 }
212}
213
214#[derive(Copy, Clone, Eq, PartialEq, Debug)]
215pub enum Network {
216 Mainnet,
217 Testnet,
218}
219
220impl Network {
221 pub fn is_testnet(self) -> bool {
222 self == Network::Testnet
223 }
224}
225
226#[derive(Copy, Clone, Eq, PartialEq, Debug)]
227pub enum AddressPrefix {
228 MainnetStandard = 0x39,
229 TestnetStandard = 0xaf,
230}
231
232impl AddressPrefix {
233 pub fn network(&self) -> Network {
234 match self {
235 Self::MainnetStandard => Network::Mainnet,
236 Self::TestnetStandard => Network::Testnet,
237 }
238 }
239}
240
241impl TryFrom<u8> for AddressPrefix {
242 type Error = ContractError;
243
244 fn try_from(value: u8) -> Result<Self, Self::Error> {
245 match value {
246 0x39 => Ok(Self::MainnetStandard),
247 0xaf => Ok(Self::TestnetStandard),
248 _ => Err(ContractError::IoError("Invalid address type".to_string())),
249 }
250 }
251}
252
253#[derive(Copy, Clone, Debug, Eq, PartialEq)]
255pub struct StandardAddress {
256 network: Network,
257 spending_key: PublicKey,
258}
259
260impl StandardAddress {
261 pub fn prefix(&self) -> AddressPrefix {
262 match self.network {
263 Network::Mainnet => AddressPrefix::MainnetStandard,
264 Network::Testnet => AddressPrefix::TestnetStandard,
265 }
266 }
267
268 pub fn public_key(&self) -> &PublicKey {
269 &self.spending_key
270 }
271
272 pub fn from_public(network: Network, public_key: PublicKey) -> Self {
273 Self { network, spending_key: public_key }
274 }
275}
276
277impl From<StandardAddress> for Address {
278 fn from(v: StandardAddress) -> Self {
279 Address::Standard(v)
280 }
281}
282
283const ADDR_CHECKSUM_LEN: usize = 4;
285
286const STANDARD_ADDR_LEN: usize = 1 + 32 + ADDR_CHECKSUM_LEN;
288
289#[derive(Copy, Clone, Debug, Eq, PartialEq)]
291pub enum Address {
292 Standard(StandardAddress),
293}
294
295impl Address {
296 pub fn network(&self) -> Network {
297 match self {
298 Self::Standard(addr) => addr.network,
299 }
300 }
301
302 pub fn public_key(&self) -> &PublicKey {
303 match self {
304 Self::Standard(addr) => addr.public_key(),
305 }
306 }
307}
308
309impl FromStr for Address {
310 type Err = ContractError;
311
312 fn from_str(enc: &str) -> Result<Self, Self::Err> {
313 let dec = bs58::decode(enc).with_check(None).into_vec()?;
314 if dec.is_empty() {
315 return Err(ContractError::IoError("Empty address".to_string()))
316 }
317
318 let r_addrtype = AddressPrefix::try_from(dec[0])?;
319 match r_addrtype {
320 AddressPrefix::MainnetStandard | AddressPrefix::TestnetStandard => {
321 if dec.len() != STANDARD_ADDR_LEN {
325 return Err(Self::Err::IoError("Invalid address length".to_string()))
326 }
327
328 let r_spending_key = PublicKey::from_bytes(
329 dec[1..STANDARD_ADDR_LEN - ADDR_CHECKSUM_LEN].try_into().unwrap(),
330 )?;
331 let r_checksum = &dec[STANDARD_ADDR_LEN - ADDR_CHECKSUM_LEN..];
332
333 let checksum = blake3::hash(&dec[..STANDARD_ADDR_LEN - ADDR_CHECKSUM_LEN]);
334 if r_checksum != &checksum.as_bytes()[..ADDR_CHECKSUM_LEN] {
335 return Err(Self::Err::IoError("Invalid address checksum".to_string()))
336 }
337
338 let addr =
339 StandardAddress { network: r_addrtype.network(), spending_key: r_spending_key };
340
341 Ok(Self::Standard(addr))
342 }
343 }
344 }
345}
346
347impl core::fmt::Display for Address {
348 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
349 let payload = match self {
350 Self::Standard(addr) => {
351 let mut payload = Vec::with_capacity(STANDARD_ADDR_LEN);
352 payload.push(addr.prefix() as u8);
353 payload.extend_from_slice(&addr.spending_key.to_bytes());
354 let checksum = blake3::hash(&payload);
355 payload.extend_from_slice(&checksum.as_bytes()[..ADDR_CHECKSUM_LEN]);
356 payload
357 }
358 };
359
360 write!(f, "{}", bs58::encode(payload).with_check().into_string())
361 }
362}
363
364#[cfg(test)]
365mod tests {
366 use super::*;
367
368 use rand::rngs::OsRng;
369
370 #[test]
371 fn test_standard_address_encoding() {
372 let s_kp = Keypair::random(&mut OsRng);
373
374 let s_addr = StandardAddress { network: Network::Mainnet, spending_key: s_kp.public };
375
376 let addr: Address = s_addr.into();
377 let encoded = addr.to_string();
378 let decoded = Address::from_str(&encoded).unwrap();
379
380 assert_eq!(addr, decoded);
381
382 println!("{encoded}");
383 }
384}