1use binprot::{BinProtRead, BinProtWrite};
4use binprot_derive::{BinProtRead, BinProtWrite};
5use derive_more::From;
6use malloc_size_of_derive::MallocSizeOf;
7use serde::{Deserialize, Serialize};
8use std::{fmt, marker::PhantomData, str::FromStr};
9
10pub fn encode(b: &[u8], v: u8) -> String {
12 bs58::encode(&b).with_check_version(v).into_string()
13}
14
15pub fn decode(s: &str, v: u8) -> Result<Vec<u8>, bs58::decode::Error> {
20 bs58::decode(s).with_check(Some(v)).into_vec()
21}
22
23pub trait ToBase58Check: Sized {
25 type Error;
26
27 fn to_base58check(&self) -> Result<String, Self::Error>;
29}
30
31pub trait FromBase58Check: Sized {
33 type Error;
34
35 fn from_base58check<T: AsRef<str>>(b58: T) -> Result<Self, Self::Error>;
37}
38
39pub trait Base58CheckVersion {
40 const VERSION_BYTE: u8;
41}
42
43#[derive(Debug, thiserror::Error)]
44pub enum ToBase58CheckError {
45 #[error("Error writing the value to binprot: {0}")]
46 Binprot(#[from] std::io::Error),
47}
48
49impl<T> ToBase58Check for T
50where
51 T: BinProtWrite + Base58CheckVersion,
52{
53 type Error = ToBase58CheckError;
54
55 fn to_base58check(&self) -> Result<String, Self::Error> {
56 let mut binprot = Vec::new();
57 self.binprot_write(&mut binprot)?;
58 Ok(encode(&binprot, Self::VERSION_BYTE))
59 }
60}
61
62#[derive(Debug, thiserror::Error)]
63pub enum FromBase58CheckError {
64 #[error("Error reading the value from binprot: {0}")]
65 Binprot(#[from] binprot::Error),
66 #[error("Error converting base58check to the value: {0}")]
67 Base58(#[from] bs58::decode::Error),
68}
69
70impl<T> FromBase58Check for T
71where
72 T: BinProtRead + Base58CheckVersion,
73{
74 type Error = FromBase58CheckError;
75
76 fn from_base58check<S: AsRef<str>>(b58: S) -> Result<Self, Self::Error> {
77 let binprot = decode(b58.as_ref(), Self::VERSION_BYTE)?;
78 let binable = T::binprot_read(&mut &binprot[..])?;
79 Ok(binable)
80 }
81}
82
83#[derive(PartialEq, Eq, PartialOrd, Ord, MallocSizeOf)]
86pub struct Base58CheckOfBinProt<T, U, const V: u8>(T, PhantomData<U>);
87
88impl<T, U, const V: u8> Default for Base58CheckOfBinProt<T, U, V>
89where
90 T: Default,
91{
92 fn default() -> Self {
93 Self(T::default(), Default::default())
94 }
95}
96
97impl<T, U, const V: u8> Clone for Base58CheckOfBinProt<T, U, V>
98where
99 T: Clone,
100{
101 fn clone(&self) -> Self {
102 Self(self.0.clone(), Default::default())
103 }
104}
105
106impl<T, U, const V: u8> From<T> for Base58CheckOfBinProt<T, U, V> {
107 fn from(source: T) -> Self {
108 Base58CheckOfBinProt(source, Default::default())
109 }
110}
111
112impl<T, U, const V: u8> std::ops::Deref for Base58CheckOfBinProt<T, U, V> {
113 type Target = T;
114
115 fn deref(&self) -> &Self::Target {
116 &self.0
117 }
118}
119
120impl<T, U, const V: u8> Base58CheckOfBinProt<T, U, V> {
121 pub fn into_inner(self) -> T {
122 self.0
123 }
124
125 pub fn inner(&self) -> &T {
126 &self.0
127 }
128}
129
130impl<T, U, const V: u8> BinProtRead for Base58CheckOfBinProt<T, U, V>
131where
132 T: BinProtRead,
133{
134 fn binprot_read<R: std::io::Read + ?Sized>(r: &mut R) -> Result<Self, binprot::Error>
135 where
136 Self: Sized,
137 {
138 T::binprot_read(r).map(|v| Self(v, Default::default()))
139 }
140}
141
142impl<T, U, const V: u8> BinProtWrite for Base58CheckOfBinProt<T, U, V>
143where
144 T: BinProtWrite,
145{
146 fn binprot_write<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
147 self.0.binprot_write(w)
148 }
149}
150
151impl<T, U, const V: u8> fmt::Display for Base58CheckOfBinProt<T, U, V>
152where
153 T: Clone,
154 U: From<T> + BinProtWrite,
155{
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 let mut binprot = Vec::new();
158 let from = U::from(self.0.clone());
159 from.binprot_write(&mut binprot).map_err(|e| {
160 serde::ser::Error::custom(format!("Failed to convert to base58check: {e}"))
161 })?;
162 let encoded = encode(&binprot, V);
163
164 write!(f, "{}", encoded)
165 }
166}
167
168impl<T, U, const V: u8> fmt::Debug for Base58CheckOfBinProt<T, U, V>
169where
170 T: Serialize + Clone,
171 U: From<T> + BinProtWrite,
172{
173 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174 write!(f, "{}", self)
175 }
176}
177
178impl<T, U, const V: u8> FromStr for Base58CheckOfBinProt<T, U, V>
179where
180 U: BinProtRead,
181 T: From<U>,
182{
183 type Err = FromBase58CheckError;
184
185 fn from_str(s: &str) -> Result<Self, Self::Err> {
186 let binprot = decode(s, V)?;
187 let binable = U::binprot_read(&mut &binprot[1..])?;
188 Ok(T::from(binable).into())
189 }
190}
191
192impl<T, U, const V: u8> Serialize for Base58CheckOfBinProt<T, U, V>
193where
194 T: Clone + Serialize,
195 U: From<T> + BinProtWrite + std::fmt::Debug,
196{
197 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
198 where
199 S: serde::Serializer,
200 {
201 if serializer.is_human_readable() {
202 serializer.serialize_str(&self.to_string())
203 } else {
204 self.0.serialize(serializer)
205 }
206 }
207}
208
209impl<'de, T, U, const V: u8> serde::Deserialize<'de> for Base58CheckOfBinProt<T, U, V>
210where
211 T: From<U> + Deserialize<'de>,
212 U: BinProtRead,
213{
214 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
215 where
216 D: serde::Deserializer<'de>,
217 {
218 if deserializer.is_human_readable() {
219 let b58: String = Deserialize::deserialize(deserializer)?;
220 Ok(b58.parse().map_err(serde::de::Error::custom)?)
221 } else {
222 T::deserialize(deserializer).map(|v| Self(v, Default::default()))
223 }
224 }
225}
226
227#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, From, BinProtRead, BinProtWrite)]
230pub struct Base58CheckOfBytes<T, const V: u8>(T);
231
232impl<T, const V: u8> Base58CheckOfBytes<T, V> {
233 pub fn into_inner(self) -> T {
234 self.0
235 }
236}
237
238impl<T, const V: u8> std::ops::Deref for Base58CheckOfBytes<T, V> {
239 type Target = T;
240
241 fn deref(&self) -> &Self::Target {
242 &self.0
243 }
244}
245
246impl<T, const V: u8> Serialize for Base58CheckOfBytes<T, V>
247where
248 T: Serialize + AsRef<[u8]> + std::fmt::Debug,
249{
250 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
251 where
252 S: serde::Serializer,
253 {
254 if serializer.is_human_readable() {
255 let encoded = encode(self.0.as_ref(), V);
256 serializer.serialize_str(&encoded)
257 } else {
258 self.0.serialize(serializer)
259 }
260 }
261}
262
263impl<'de, T, const V: u8> serde::Deserialize<'de> for Base58CheckOfBytes<T, V>
264where
265 T: for<'a> From<&'a [u8]> + Deserialize<'de>,
266{
267 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
268 where
269 D: serde::Deserializer<'de>,
270 {
271 if deserializer.is_human_readable() {
272 let b58: String = Deserialize::deserialize(deserializer)?;
273 let bytes = decode(&b58, V).map_err(|e| {
274 serde::de::Error::custom(format!("Failed to construct from base58check: {e}"))
275 })?;
276 Ok(T::from(&bytes[1..]))
277 } else {
278 T::deserialize(deserializer)
279 }
280 .map(Self)
281 }
282}
283
284impl<T: mina_hasher::Hashable, U, const V: u8> mina_hasher::Hashable
285 for Base58CheckOfBinProt<T, U, V>
286{
287 type D = T::D;
288
289 fn to_roinput(&self) -> mina_hasher::ROInput {
290 self.0.to_roinput()
291 }
292
293 fn domain_string(domain_param: Self::D) -> Option<String> {
294 T::domain_string(domain_param)
295 }
296}
297
298impl<T: mina_hasher::Hashable, const V: u8> mina_hasher::Hashable for Base58CheckOfBytes<T, V> {
299 type D = T::D;
300
301 fn to_roinput(&self) -> mina_hasher::ROInput {
302 self.0.to_roinput()
303 }
304
305 fn domain_string(domain_param: Self::D) -> Option<String> {
306 T::domain_string(domain_param)
307 }
308}
309
310#[cfg(test)]
311mod tests {
312 use super::ToBase58Check;
313 use binprot::BinProtRead;
314 use binprot_derive::{BinProtRead, BinProtWrite};
315 use serde::{Deserialize, Serialize};
316
317 use crate::{
318 b58::{Base58CheckOfBinProt, Base58CheckVersion},
319 bigint::BigInt,
320 versioned::Versioned,
321 };
322
323 macro_rules! base58tests {
324 ($($name:ident($bytes:expr, $version:expr, $b58:expr $(,)? )),* $(,)?) => {
325 $(
326 #[test]
327 fn $name() {
328 let bytes = hex::decode($bytes).unwrap();
329
330 let encoded = super::encode(&bytes, $version);
331 assert_eq!(&encoded, $b58);
332
333 let decoded = super::decode($b58, $version).unwrap();
334 assert_eq!(decoded[0], $version);
335 assert_eq!(&decoded[1..], &bytes);
336 }
337 )*
338 };
339 }
340
341 base58tests!(
342 genesis_state_hash(
343 "01fc630629c6a1a237a3dc1d95fd54fbf9cca062486e9f57852ebc64e4042ceb3d",
344 0x10,
345 "3NLx3eBDTvYmP27bUmYANzmhjL5rGe36nGW6N5XhGcuStF6Zv7ZD"
346 ),
347 prev_state_hash(
348 "019b4f7c30bcf6c883c388097db490bfeeae5a1d36eb4b593af65e3511db4fc432",
349 0x10,
350 "3NLDHxUsL6Ehujf6x2AT6CXrpRjeY1rw9e93QJfJUAD3P6HVVtcA"
351 ),
352 state_hash(
353 "018d67aadd018581a812623915b13d5c3a6da7dfe8a195172d9bbd206810bc2329",
354 0x10,
355 "3NL7AkynW6hbDrhHTAht1GLG563Fo9fdcEQk1zEyy5XedC6aZTeB",
356 ),
357 ledger_hash(
358 "01636f5b2d67278e17bc4343c7c23fb4991f8cf0bbbfd8558615b124d5d6254801",
359 0x05,
360 "jwrPvAMUNo3EKT2puUk5Fxz6B7apRAoKNTGpAA49t3TRSfzvdrL"
361 ),
362 address(
363 "01013c2b5b48c22dc8b8c9d2c9d76a2ceaaf02beabb364301726c3f8e989653af51300",
364 0xcb,
365 "B62qkUHaJUHERZuCHQhXCQ8xsGBqyYSgjQsKnKN5HhSJecakuJ4pYyk"
366 )
367 );
368
369 fn bigint(s: &str) -> BigInt {
370 let bytes = hex::decode(s).unwrap();
371 BigInt::binprot_read(&mut &bytes[..]).unwrap()
372 }
373
374 #[test]
375 fn binable_base58check() {
376 #[derive(Clone, BinProtRead, BinProtWrite)]
377 struct Binable(BigInt);
378
379 impl Base58CheckVersion for Binable {
380 const VERSION_BYTE: u8 = 0x10;
381 }
382
383 let b = Binable(bigint(
384 "fc630629c6a1a237a3dc1d95fd54fbf9cca062486e9f57852ebc64e4042ceb3d",
385 ));
386 let b58c = b.to_base58check().unwrap();
387 assert_eq!(&b58c, "a24htZ9FGiBmD2D9vBdX4uN7aeghG7K1852UEVrsgACMBHnL58")
388 }
389
390 #[test]
391 fn serde_as_base58check() {
392 #[derive(Clone, Debug, BinProtRead, BinProtWrite, Serialize, Deserialize, PartialEq)]
393 struct BinableV1(BigInt);
394
395 impl Base58CheckVersion for BinableV1 {
396 const VERSION_BYTE: u8 = 0x10;
397 }
398
399 #[derive(Clone, Debug, BinProtRead, BinProtWrite, Serialize, Deserialize, PartialEq)]
400 struct BinableV2(BigInt);
401
402 impl From<BinableV2> for Versioned<BinableV1, 1> {
403 fn from(v2: BinableV2) -> Self {
404 BinableV1(v2.0).into()
405 }
406 }
407
408 impl From<Versioned<BinableV1, 1>> for BinableV2 {
409 fn from(v1: Versioned<BinableV1, 1>) -> Self {
410 BinableV2(v1.into_inner().0)
411 }
412 }
413
414 #[derive(Debug, Serialize, Deserialize, PartialEq)]
415 struct Foo {
416 b: Base58CheckOfBinProt<BinableV2, Versioned<BinableV1, 1>, 0x10>,
417 }
418
419 let b = Foo {
420 b: BinableV2(bigint(
421 "fc630629c6a1a237a3dc1d95fd54fbf9cca062486e9f57852ebc64e4042ceb3d",
422 ))
423 .into(),
424 };
425 assert_eq!(
426 &serde_json::to_string(&b).unwrap(),
427 r#"{"b":"3NLx3eBDTvYmP27bUmYANzmhjL5rGe36nGW6N5XhGcuStF6Zv7ZD"}"#
428 );
429 assert_eq!(
430 serde_json::from_str::<Foo>(
431 r#"{"b":"3NLx3eBDTvYmP27bUmYANzmhjL5rGe36nGW6N5XhGcuStF6Zv7ZD"}"#
432 )
433 .unwrap(),
434 b
435 );
436 }
437}