tari_utilities/
encoding.rs

1// Copyright 2020. The Tari Project
2//
3// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
4// following conditions are met:
5//
6// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
7// disclaimer.
8//
9// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
10// following disclaimer in the documentation and/or other materials provided with the distribution.
11//
12// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
13// products derived from this software without specific prior written permission.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
16// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
21// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23//! A trait that handles [base58](https://crates.io/crates/base58-monero) encoding and decoding.
24
25use alloc::string::{String, ToString};
26
27use snafu::prelude::*;
28
29use crate::ByteArray;
30
31/// Trait for encoding/decoding to base58.
32#[deprecated(since = "0.8.0", note = "please use `MBase58` instead")]
33pub trait Base58 {
34    /// Convert from base58 string.
35    fn from_base58(hex: &str) -> Result<Self, Base58Error>
36    where Self: Sized;
37
38    /// Convert to base58 string.
39    fn to_base58(&self) -> String;
40}
41
42/// Errors for trait Base58.
43#[derive(Debug, Snafu)]
44#[allow(missing_docs)]
45pub enum Base58Error {
46    #[snafu(display("Byte array error: `{reason}'"))]
47    ByteArrayError { reason: String },
48    #[snafu(display("Decode error: `{reason}'"))]
49    DecodeError { reason: String },
50}
51
52#[allow(deprecated)]
53impl<T: ByteArray> Base58 for T {
54    fn from_base58(data: &str) -> Result<Self, Base58Error>
55    where Self: Sized {
56        let bytes = base58_monero::decode(data).map_err(|e| Base58Error::DecodeError { reason: e.to_string() })?;
57        Self::from_canonical_bytes(&bytes).map_err(|e| Base58Error::ByteArrayError { reason: e.to_string() })
58    }
59
60    fn to_base58(&self) -> String {
61        base58_monero::encode(self.as_bytes()).expect("base58_monero::encode is infallible")
62    }
63}
64
65/// Trait for encoding/decoding to base58.
66pub trait MBase58 {
67    /// Convert from base58 string.
68    fn from_monero_base58(hex: &str) -> Result<Self, Base58Error>
69    where Self: Sized;
70
71    /// Convert to base58 string.
72    fn to_monero_base58(&self) -> String;
73}
74
75impl<T: ByteArray> crate::encoding::MBase58 for T {
76    fn from_monero_base58(data: &str) -> Result<Self, Base58Error>
77    where Self: Sized {
78        let bytes = base58_monero::decode(data).map_err(|e| Base58Error::DecodeError { reason: e.to_string() })?;
79        Self::from_canonical_bytes(&bytes)
80            .map_err(|e| crate::encoding::Base58Error::ByteArrayError { reason: e.to_string() })
81    }
82
83    fn to_monero_base58(&self) -> String {
84        base58_monero::encode(self.as_bytes()).expect("base58_monero::encode is infallible")
85    }
86}
87
88#[cfg(test)]
89mod test {
90    use alloc::vec::Vec;
91
92    use rand_core::{OsRng, RngCore};
93
94    use super::*;
95
96    #[test]
97    fn decoding() {
98        assert_eq!(Vec::from_monero_base58("111111").unwrap(), vec![0; 4]);
99        assert_eq!(Vec::from_monero_base58("11115Q").unwrap(), vec![0, 0, 0, 255]);
100        assert!(Vec::from_monero_base58("11111O").is_err());
101        assert!(Vec::from_monero_base58("🖖🥴").is_err());
102    }
103
104    #[test]
105    fn encoding() {
106        assert_eq!(vec![0; 4].to_monero_base58(), "111111");
107        assert_eq!(vec![0, 2, 250, 39].to_monero_base58(), "111zzz");
108    }
109
110    #[test]
111    fn inverse_operations() {
112        let mut bytes = vec![0; 10];
113        OsRng.fill_bytes(&mut bytes);
114        assert_eq!(Vec::from_monero_base58(&bytes.to_monero_base58()).unwrap(), bytes);
115    }
116}