genesis_core/
base58check.rs

1//! Base58Check-to-text encoding
2
3use std::iter;
4use base58::{FromBase58, ToBase58};
5use crypto::digest::Digest;
6use crypto::sha2::Sha256;
7
8pub use base58::FromBase58Error;
9
10/// Errors that can occur when decoding base58check encoded string.
11#[derive(Debug, PartialEq)]
12pub enum FromBase58CheckError {
13    /// Base58 error.
14    InvalidBase58(FromBase58Error),
15    /// The input had invalid checksum.
16    InvalidChecksum,
17}
18
19/// A trait for converting a value to base58 encoded string.
20pub trait ToBase58Check {
21    /// Converts a value of `self` to a base58 value, returning the owned string.
22    fn to_base58check(&self, version: u8) -> String;
23}
24
25/// A trait for converting base58check encoded values.
26pub trait FromBase58Check {
27    /// Convert a value of `self`, interpreted as base58check encoded data, into the tuple with version and payload as bytes vector.
28    fn from_base58check(&self) -> Result<(u8, Vec<u8>), FromBase58CheckError>;
29}
30
31impl ToBase58Check for [u8] {
32    fn to_base58check(&self, version: u8) -> String {
33        let mut payload: Vec<u8> = iter::once(version).chain(self.iter().map(|x| *x)).collect();
34        let mut checksum = double_sha256(&payload);
35        payload.append(&mut checksum[..4].to_vec());
36        payload.to_base58()
37    }
38}
39
40impl FromBase58Check for str {
41    fn from_base58check(&self) -> Result<(u8, Vec<u8>), FromBase58CheckError> {
42        let mut payload: Vec<u8> = match self.from_base58() {
43            Ok(payload) => payload,
44            Err(error) => return Err(FromBase58CheckError::InvalidBase58(error)),
45        };
46        if payload.len() < 5 {
47            return Err(FromBase58CheckError::InvalidChecksum)
48        }
49        let checksum_index = payload.len() - 4;
50        let provided_checksum = payload.split_off(checksum_index);
51        let checksum = double_sha256(&payload)[..4].to_vec();
52        if checksum != provided_checksum {
53            return Err(FromBase58CheckError::InvalidChecksum)
54        }
55        Ok((payload[0], payload[1..].to_vec()))
56    }
57}
58
59fn double_sha256(payload: &[u8]) -> Vec<u8> {
60    let mut hasher = Sha256::new();
61    let mut hash = vec![0; hasher.output_bytes()];
62    hasher.input(&payload);
63    hasher.result(&mut hash);
64    hasher.reset();
65    hasher.input(&hash);
66    hasher.result(&mut hash);
67    hash.to_vec()
68}
69
70#[cfg(test)]
71mod tests {
72    use super::{ToBase58Check, FromBase58Check};
73
74    #[test]
75    fn to_base58check() {
76        assert_eq!(b"".to_base58check(0), "1Wh4bh");
77        assert_eq!(b"hello".to_base58check(0), "12L5B5yqsf7vwb");
78        assert_eq!(b"hello".to_base58check(1), "5b4vP1wunz2H5");
79    }
80        
81    #[test]
82    fn from_base58check() {
83        assert_eq!(String::from("1Wh4bh").from_base58check().unwrap(), (0u8, vec![]));
84        assert_eq!(String::from("12L5B5yqsf7vwb").from_base58check().unwrap(), (0u8, b"hello".to_vec()));
85        assert_eq!(String::from("5b4vP1wunz2H5").from_base58check().unwrap(), (1u8, b"hello".to_vec()));
86    }
87}