1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
extern crate base58;
extern crate crypto;
use std::iter;
use base58::{FromBase58, ToBase58};
use crypto::digest::Digest;
use crypto::sha2::Sha256;
pub use base58::FromBase58Error;
#[derive(Debug, PartialEq)]
pub enum FromBase58CheckError {
InvalidBase58(FromBase58Error),
InvalidChecksum,
}
pub trait ToBase58Check {
fn to_base58check(&self, version: u8) -> String;
}
pub trait FromBase58Check {
fn from_base58check(&self) -> Result<(u8, Vec<u8>), FromBase58CheckError>;
}
impl ToBase58Check for [u8] {
fn to_base58check(&self, version: u8) -> String {
let mut payload: Vec<u8> = iter::once(version).chain(self.iter().map(|x| *x)).collect();
let mut checksum = double_sha256(&payload);
payload.append(&mut checksum[..4].to_vec());
payload.to_base58()
}
}
impl FromBase58Check for str {
fn from_base58check(&self) -> Result<(u8, Vec<u8>), FromBase58CheckError> {
let mut payload: Vec<u8> = match self.from_base58() {
Ok(payload) => payload,
Err(error) => return Err(FromBase58CheckError::InvalidBase58(error)),
};
if payload.len() < 5 {
return Err(FromBase58CheckError::InvalidChecksum)
}
let checksum_index = payload.len() - 4;
let provided_checksum = payload.split_off(checksum_index);
let checksum = double_sha256(&payload)[..4].to_vec();
if checksum != provided_checksum {
return Err(FromBase58CheckError::InvalidChecksum)
}
Ok((payload[0], payload[1..].to_vec()))
}
}
fn double_sha256(payload: &[u8]) -> Vec<u8> {
let mut hasher = Sha256::new();
let mut hash = vec![0; hasher.output_bytes()];
hasher.input(&payload);
hasher.result(&mut hash);
hasher.reset();
hasher.input(&hash);
hasher.result(&mut hash);
hash.to_vec()
}
#[cfg(test)]
mod tests {
use super::{ToBase58Check, FromBase58Check, FromBase58CheckError};
#[test]
fn to_base58check() {
assert_eq!(b"".to_base58check(0), "1Wh4bh");
assert_eq!(b"hello".to_base58check(0), "12L5B5yqsf7vwb");
assert_eq!(b"hello".to_base58check(1), "5b4vP1wunz2H5");
}
#[test]
fn from_base58check() {
assert_eq!("1Wh4bh".from_base58check().unwrap(), (0u8, vec![]));
assert_eq!("12L5B5yqsf7vwb".from_base58check().unwrap(), (0u8, b"hello".to_vec()));
assert_eq!("5b4vP1wunz2H5".from_base58check().unwrap(), (1u8, b"hello".to_vec()));
}
#[test]
fn from_base58check_with_invalid_checksum() {
assert_eq!("1Wh4bc".from_base58check(), Err(FromBase58CheckError::InvalidChecksum));
}
#[test]
#[should_panic]
fn from_base58check_with_invalid_length() {
"Wh4bh".from_base58check().unwrap();
}
}