1extern crate alloc;
6use crate::{BaseField, CurvePoint, ScalarField, SecKey};
7use alloc::{string::String, vec, vec::Vec};
8use ark_ec::{short_weierstrass::Affine, AffineRepr, CurveGroup};
9use ark_ff::{BigInteger, PrimeField, Zero};
10use bs58;
11use core::{
12 fmt,
13 ops::{Mul, Neg},
14};
15use o1_utils::FieldHelpers;
16use sha2::{Digest, Sha256};
17use thiserror::Error;
18
19#[derive(Error, Debug, Clone, PartialEq, Eq)]
21pub enum PubKeyError {
22 #[error("invalid address length")]
24 AddressLength,
25 #[error("invalid address base58")]
27 AddressBase58,
28 #[error("invalid raw address bytes length")]
30 AddressRawByteLength,
31 #[error("invalid address checksum")]
33 AddressChecksum,
34 #[error("invalid address version")]
36 AddressVersion,
37 #[error("invalid x-coordinate bytes")]
39 XCoordinateBytes,
40 #[error("invalid x-coordinate")]
42 XCoordinate,
43 #[error("point not on curve")]
45 YCoordinateBytes,
46 #[error("invalid y-coordinate bytes")]
48 YCoordinateParityBytes,
49 #[error("invalid y-coordinate parity bytes")]
51 YCoordinateParity,
52 #[error("invalid y-coordinate parity")]
54 NonCurvePoint,
55 #[error("invalid public key hex")]
57 Hex,
58 #[error("invalid secret key")]
60 SecKey,
61}
62pub type Result<T> = core::result::Result<T, PubKeyError>;
64
65pub const MINA_ADDRESS_LEN: usize = 55;
67const MINA_ADDRESS_RAW_LEN: usize = 40;
68
69#[derive(Clone, Debug, PartialEq, Eq)]
71pub struct PubKey(CurvePoint);
72
73impl PubKey {
74 pub fn from_point_unsafe(point: CurvePoint) -> Self {
77 Self(point)
78 }
79
80 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
85 if bytes.len() != BaseField::size_in_bytes() * 2 {
86 return Err(PubKeyError::YCoordinateBytes);
87 }
88 let x = BaseField::from_bytes(&bytes[0..BaseField::size_in_bytes()])
89 .map_err(|_| PubKeyError::XCoordinateBytes)?;
90 let y = BaseField::from_bytes(&bytes[BaseField::size_in_bytes()..])
91 .map_err(|_| PubKeyError::YCoordinateBytes)?;
92 let pt = CurvePoint::get_point_from_x_unchecked(x, y.0.is_odd())
93 .ok_or(PubKeyError::XCoordinate)?;
94 if pt.y != y {
95 return Err(PubKeyError::NonCurvePoint);
96 }
97
98 let public = Affine {
99 x,
100 y,
101 infinity: pt.infinity,
102 };
103 if !public.is_on_curve() {
104 return Err(PubKeyError::NonCurvePoint);
105 }
106
107 Ok(PubKey::from_point_unsafe(public))
109 }
110
111 pub fn from_hex(public_hex: &str) -> Result<Self> {
117 let bytes: Vec<u8> = hex::decode(public_hex).map_err(|_| PubKeyError::Hex)?;
118 PubKey::from_bytes(&bytes)
119 }
120
121 pub fn from_secret_key(secret_key: SecKey) -> Result<Self> {
123 if secret_key.clone().into_scalar() == ScalarField::zero() {
124 return Err(PubKeyError::SecKey);
125 }
126 let pt = CurvePoint::generator()
127 .mul(secret_key.into_scalar())
128 .into_affine();
129 if !pt.is_on_curve() {
130 return Err(PubKeyError::NonCurvePoint);
131 }
132 Ok(PubKey::from_point_unsafe(pt))
133 }
134
135 pub fn from_address(address: &str) -> Result<Self> {
141 if address.len() != MINA_ADDRESS_LEN {
142 return Err(PubKeyError::AddressLength);
143 }
144
145 let bytes = bs58::decode(address)
146 .into_vec()
147 .map_err(|_| PubKeyError::AddressBase58)?;
148
149 if bytes.len() != MINA_ADDRESS_RAW_LEN {
150 return Err(PubKeyError::AddressRawByteLength);
151 }
152
153 let (raw, checksum) = (&bytes[..bytes.len() - 4], &bytes[bytes.len() - 4..]);
154 let hash = Sha256::digest(&Sha256::digest(raw)[..]);
155 if checksum != &hash[..4] {
156 return Err(PubKeyError::AddressChecksum);
157 }
158
159 let (version, x_bytes, y_parity) = (
160 &raw[..3],
161 &raw[3..bytes.len() - 5],
162 raw[bytes.len() - 5] == 0x01,
163 );
164 if version != [0xcb, 0x01, 0x01] {
165 return Err(PubKeyError::AddressVersion);
166 }
167
168 let x = BaseField::from_bytes(x_bytes).map_err(|_| PubKeyError::XCoordinateBytes)?;
169 let mut pt =
170 CurvePoint::get_point_from_x_unchecked(x, y_parity).ok_or(PubKeyError::XCoordinate)?;
171
172 if pt.y.into_bigint().is_even() == y_parity {
173 pt.y = pt.y.neg();
174 }
175
176 if !pt.is_on_curve() {
177 return Err(PubKeyError::NonCurvePoint);
178 }
179
180 Ok(PubKey::from_point_unsafe(pt))
182 }
183
184 pub fn point(&self) -> &CurvePoint {
186 &self.0
187 }
188
189 pub fn into_point(self) -> CurvePoint {
191 self.0
192 }
193
194 pub fn into_compressed(&self) -> CompressedPubKey {
196 let point = self.0;
197 CompressedPubKey {
198 x: point.x,
199 is_odd: point.y.into_bigint().is_odd(),
200 }
201 }
202
203 pub fn into_address(&self) -> String {
205 let point = self.point();
206 into_address(&point.x, point.y.into_bigint().is_odd())
207 }
208
209 pub fn to_bytes(&self) -> Vec<u8> {
211 let point = self.point();
212 [point.x.to_bytes(), point.y.to_bytes()].concat()
213 }
214
215 pub fn to_hex(&self) -> String {
217 let point = self.point();
218 point.x.to_hex() + point.y.to_hex().as_str()
219 }
220}
221
222impl fmt::Display for PubKey {
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224 write!(f, "{}", self.to_hex())
225 }
226}
227
228#[derive(Clone, Debug, PartialEq, Eq)]
230pub struct CompressedPubKey {
231 pub x: BaseField,
233
234 pub is_odd: bool,
236}
237
238fn into_address(x: &BaseField, is_odd: bool) -> String {
239 let mut raw: Vec<u8> = vec![
240 0xcb, 0x01, 0x01, ];
244
245 raw.extend(x.to_bytes());
247
248 raw.push(u8::from(is_odd));
250
251 let hash = Sha256::digest(&Sha256::digest(&raw[..])[..]);
253 raw.extend(&hash[..4]);
254
255 bs58::encode(raw).into_string()
257}
258
259impl CompressedPubKey {
260 pub fn into_address(&self) -> String {
262 into_address(&self.x, self.is_odd)
263 }
264
265 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
270 let x = BaseField::from_bytes(&bytes[0..BaseField::size_in_bytes()])
271 .map_err(|_| PubKeyError::XCoordinateBytes)?;
272 let parity_bytes = &bytes[BaseField::size_in_bytes()..];
273 if parity_bytes.len() != 1 {
274 return Err(PubKeyError::YCoordinateParityBytes);
275 }
276 let is_odd = if parity_bytes[0] == 0x01 {
277 true } else if parity_bytes[0] == 0x00 {
279 false } else {
281 return Err(PubKeyError::YCoordinateParity);
282 };
283 let public =
284 CurvePoint::get_point_from_x_unchecked(x, is_odd).ok_or(PubKeyError::XCoordinate)?;
285 if !public.is_on_curve() {
286 return Err(PubKeyError::NonCurvePoint);
287 }
288
289 Ok(Self { x, is_odd })
291 }
292
293 pub fn from_hex(public_hex: &str) -> Result<Self> {
299 let bytes: Vec<u8> = hex::decode(public_hex).map_err(|_| PubKeyError::Hex)?;
300 Self::from_bytes(&bytes)
301 }
302
303 pub fn from_secret_key(sec_key: SecKey) -> Self {
305 let public = PubKey::from_point_unsafe(
307 CurvePoint::generator()
308 .mul(sec_key.into_scalar())
309 .into_affine(),
310 );
311 public.into_compressed()
312 }
313
314 pub fn from_address(address: &str) -> Result<Self> {
320 Ok(PubKey::from_address(address)?.into_compressed())
321 }
322
323 pub fn empty() -> Self {
326 Self {
327 x: BaseField::zero(),
328 is_odd: false,
329 }
330 }
331
332 pub fn to_bytes(&self) -> Vec<u8> {
334 let x_bytes = self.x.to_bytes();
335 let is_odd_bytes = vec![if self.is_odd { 0x01u8 } else { 0x00u8 }];
336 [x_bytes, is_odd_bytes].concat()
337 }
338
339 pub fn to_hex(&self) -> String {
341 hex::encode(self.to_bytes())
342 }
343}