1extern crate alloc;
6
7use crate::{BaseField, CurvePoint, ScalarField, SecKey};
8use alloc::{string::String, vec, vec::Vec};
9use ark_ec::{short_weierstrass::Affine, AffineRepr, CurveGroup};
10use ark_ff::{BigInteger, PrimeField, Zero};
11use bs58;
12use core::{
13 fmt,
14 ops::{Mul, Neg},
15};
16use o1_utils::FieldHelpers;
17use sha2::{Digest, Sha256};
18use thiserror::Error;
19
20#[derive(Error, Debug, Clone, PartialEq, Eq)]
22pub enum PubKeyError {
23 #[error("invalid address length")]
25 AddressLength,
26 #[error("invalid address base58")]
28 AddressBase58,
29 #[error("invalid raw address bytes length")]
31 AddressRawByteLength,
32 #[error("invalid address checksum")]
34 AddressChecksum,
35 #[error("invalid address version")]
37 AddressVersion,
38 #[error("invalid x-coordinate bytes")]
40 XCoordinateBytes,
41 #[error("invalid x-coordinate")]
43 XCoordinate,
44 #[error("point not on curve")]
46 YCoordinateBytes,
47 #[error("invalid y-coordinate bytes")]
49 YCoordinateParityBytes,
50 #[error("invalid y-coordinate parity bytes")]
52 YCoordinateParity,
53 #[error("invalid y-coordinate parity")]
55 NonCurvePoint,
56 #[error("invalid public key hex")]
58 Hex,
59 #[error("invalid secret key")]
61 SecKey,
62}
63pub type Result<T> = core::result::Result<T, PubKeyError>;
65
66pub const MINA_ADDRESS_LEN: usize = 55;
68const MINA_ADDRESS_RAW_LEN: usize = 40;
69
70#[derive(Clone, Debug, PartialEq, Eq)]
72pub struct PubKey(CurvePoint);
73
74impl PubKey {
75 pub fn from_point_unsafe(point: CurvePoint) -> Self {
78 Self(point)
79 }
80
81 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
86 if bytes.len() != BaseField::size_in_bytes() * 2 {
87 return Err(PubKeyError::YCoordinateBytes);
88 }
89 let x = BaseField::from_bytes(&bytes[0..BaseField::size_in_bytes()])
90 .map_err(|_| PubKeyError::XCoordinateBytes)?;
91 let y = BaseField::from_bytes(&bytes[BaseField::size_in_bytes()..])
92 .map_err(|_| PubKeyError::YCoordinateBytes)?;
93 let pt = CurvePoint::get_point_from_x_unchecked(x, y.0.is_odd())
94 .ok_or(PubKeyError::XCoordinate)?;
95 if pt.y != y {
96 return Err(PubKeyError::NonCurvePoint);
97 }
98
99 let public = Affine {
100 x,
101 y,
102 infinity: pt.infinity,
103 };
104 if !public.is_on_curve() {
105 return Err(PubKeyError::NonCurvePoint);
106 }
107
108 Ok(PubKey::from_point_unsafe(public))
110 }
111
112 pub fn from_hex(public_hex: &str) -> Result<Self> {
118 let bytes: Vec<u8> = hex::decode(public_hex).map_err(|_| PubKeyError::Hex)?;
119 PubKey::from_bytes(&bytes)
120 }
121
122 pub fn from_secret_key(secret_key: SecKey) -> Result<Self> {
124 if secret_key.clone().into_scalar() == ScalarField::zero() {
125 return Err(PubKeyError::SecKey);
126 }
127 let pt = CurvePoint::generator()
128 .mul(secret_key.into_scalar())
129 .into_affine();
130 if !pt.is_on_curve() {
131 return Err(PubKeyError::NonCurvePoint);
132 }
133 Ok(PubKey::from_point_unsafe(pt))
134 }
135
136 pub fn from_address(address: &str) -> Result<Self> {
142 if address.len() != MINA_ADDRESS_LEN {
143 return Err(PubKeyError::AddressLength);
144 }
145
146 let bytes = bs58::decode(address)
147 .into_vec()
148 .map_err(|_| PubKeyError::AddressBase58)?;
149
150 if bytes.len() != MINA_ADDRESS_RAW_LEN {
151 return Err(PubKeyError::AddressRawByteLength);
152 }
153
154 let (raw, checksum) = (&bytes[..bytes.len() - 4], &bytes[bytes.len() - 4..]);
155 let hash = Sha256::digest(&Sha256::digest(raw)[..]);
156 if checksum != &hash[..4] {
157 return Err(PubKeyError::AddressChecksum);
158 }
159
160 let (version, x_bytes, y_parity) = (
161 &raw[..3],
162 &raw[3..bytes.len() - 5],
163 raw[bytes.len() - 5] == 0x01,
164 );
165 if version != [0xcb, 0x01, 0x01] {
166 return Err(PubKeyError::AddressVersion);
167 }
168
169 let x = BaseField::from_bytes(x_bytes).map_err(|_| PubKeyError::XCoordinateBytes)?;
170 let mut pt =
171 CurvePoint::get_point_from_x_unchecked(x, y_parity).ok_or(PubKeyError::XCoordinate)?;
172
173 if pt.y.into_bigint().is_even() == y_parity {
174 pt.y = pt.y.neg();
175 }
176
177 if !pt.is_on_curve() {
178 return Err(PubKeyError::NonCurvePoint);
179 }
180
181 Ok(PubKey::from_point_unsafe(pt))
183 }
184
185 pub fn point(&self) -> &CurvePoint {
187 &self.0
188 }
189
190 pub fn into_point(self) -> CurvePoint {
192 self.0
193 }
194
195 pub fn into_compressed(&self) -> CompressedPubKey {
197 let point = self.0;
198 CompressedPubKey {
199 x: point.x,
200 is_odd: point.y.into_bigint().is_odd(),
201 }
202 }
203
204 pub fn into_address(&self) -> String {
206 let point = self.point();
207 into_address(&point.x, point.y.into_bigint().is_odd())
208 }
209
210 pub fn to_bytes(&self) -> Vec<u8> {
212 let point = self.point();
213 [point.x.to_bytes(), point.y.to_bytes()].concat()
214 }
215
216 pub fn to_hex(&self) -> String {
218 let point = self.point();
219 point.x.to_hex() + point.y.to_hex().as_str()
220 }
221}
222
223impl fmt::Display for PubKey {
224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225 write!(f, "{}", self.to_hex())
226 }
227}
228
229#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
231pub struct CompressedPubKey {
232 pub x: BaseField,
234
235 pub is_odd: bool,
237}
238
239fn into_address(x: &BaseField, is_odd: bool) -> String {
240 let mut raw: Vec<u8> = vec![
241 0xcb, 0x01, 0x01, ];
245
246 raw.extend(x.to_bytes());
248
249 raw.push(u8::from(is_odd));
251
252 let hash = Sha256::digest(&Sha256::digest(&raw[..])[..]);
254 raw.extend(&hash[..4]);
255
256 bs58::encode(raw).into_string()
258}
259
260impl CompressedPubKey {
261 pub fn into_address(&self) -> String {
263 into_address(&self.x, self.is_odd)
264 }
265
266 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
271 let x = BaseField::from_bytes(&bytes[0..BaseField::size_in_bytes()])
272 .map_err(|_| PubKeyError::XCoordinateBytes)?;
273 let parity_bytes = &bytes[BaseField::size_in_bytes()..];
274 if parity_bytes.len() != 1 {
275 return Err(PubKeyError::YCoordinateParityBytes);
276 }
277 let is_odd = if parity_bytes[0] == 0x01 {
278 true } else if parity_bytes[0] == 0x00 {
280 false } else {
282 return Err(PubKeyError::YCoordinateParity);
283 };
284 let public =
285 CurvePoint::get_point_from_x_unchecked(x, is_odd).ok_or(PubKeyError::XCoordinate)?;
286 if !public.is_on_curve() {
287 return Err(PubKeyError::NonCurvePoint);
288 }
289
290 Ok(Self { x, is_odd })
292 }
293
294 pub fn from_hex(public_hex: &str) -> Result<Self> {
300 let bytes: Vec<u8> = hex::decode(public_hex).map_err(|_| PubKeyError::Hex)?;
301 Self::from_bytes(&bytes)
302 }
303
304 pub fn from_secret_key(sec_key: SecKey) -> Self {
306 let public = PubKey::from_point_unsafe(
309 CurvePoint::generator()
310 .mul(sec_key.into_scalar())
311 .into_affine(),
312 );
313 public.into_compressed()
314 }
315
316 pub fn from_address(address: &str) -> Result<Self> {
323 Ok(PubKey::from_address(address)?.into_compressed())
324 }
325
326 pub fn empty() -> Self {
330 Self {
331 x: BaseField::zero(),
332 is_odd: false,
333 }
334 }
335
336 pub fn to_bytes(&self) -> Vec<u8> {
338 let x_bytes = self.x.to_bytes();
339 let is_odd_bytes = vec![if self.is_odd { 0x01u8 } else { 0x00u8 }];
340 [x_bytes, is_odd_bytes].concat()
341 }
342
343 pub fn to_hex(&self) -> String {
345 hex::encode(self.to_bytes())
346 }
347}