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