base58check_encode/
lib.rs

1//! encode Vec<u8> objects into a Base58Check encoded `String`
2//! This crate is a direct port of the npm package `noble-base58check`
3//! Details here: <https://github.com/pas1ko/noble-base58check/blob/master/base.ts>
4//! 
5//! This crate does NOT decode Base58Check encoded strings back to Vec<u8>
6//! 
7
8use std::error::Error;
9use sha2::{Digest, Sha256};
10
11mod base58check_config;
12use base58check_config::Base58CheckConfig;
13
14#[cfg(test)]
15mod tests;
16
17
18/// Encodes a Vec<u8> into a Base58Check encoded string, without checksumming the source Vec<u8>
19pub fn encode_b58c_plain(source: Vec<u8>) -> Result<String, Box<dyn Error>> {
20
21    let b_config = Base58CheckConfig::default();
22
23    if source.len() <= 0 { return Ok("".to_string()); }
24
25    let mut zeroes: usize = 0;
26    let mut length: usize = 0;
27    let mut pbegin: usize = 0;
28
29    let pend = source.len();
30
31    while pbegin != source.len() && source[pbegin] == 0 {
32        pbegin += 1;
33        zeroes += 1;
34    }
35
36    let size = ((pend as f64 - pbegin as f64) * b_config.i_factor + 1.0) as u64 >> 0;
37
38    let mut b58: Vec<u8> = vec![0; size as usize]; // https://stackoverflow.com/questions/29530011/creating-a-vector-of-zeros-for-a-specific-size
39    while pbegin != pend {
40        let mut carry = source[pbegin] as u64;
41        
42        let mut it1 = (size - 1) as i64;
43        let mut i = 0 as usize;
44
45        while (carry != 0 || i < length) && it1 != -1 {
46
47            carry += (256.0 * b58[it1 as usize] as f64) as u64 >> 0;
48            let carry_mod_base = (carry as f64 % b_config.base as f64) as u64 >> 0;
49            b58[it1 as usize] = carry_mod_base as u8;
50            carry = ((&carry / b_config.base) as f64) as u64 >> 0;
51
52            it1 = &it1 - 1;
53            i = &i + 1;
54        }
55
56        if carry != 0 {
57            panic!("carry is non-zero: c{} b{} i{} l{} b{} e{}", &carry, b58[it1 as usize], &i, &length, &pbegin, &pend);
58        }
59        length = i.to_owned();
60        pbegin += 1;
61    }
62
63    let mut it2 = (size as u64) - (length as u64);
64    
65
66    while it2 != size && b58[it2 as usize] == 0 {
67        it2 += 1;
68    }
69    let mut str_leader = (format!("{}", b_config.leader)).as_str().repeat(zeroes);
70
71    while it2 < size {
72        it2 += 1;
73
74        let str_slice = format!("{}", b_config.alphabet_vec[b58[it2 as usize - 1 as usize] as usize]); 
75
76        str_leader = format!("{}{}", &str_leader, &str_slice);
77    }    
78
79    Ok(str_leader)
80}
81
82fn double_sha256(payload: &[u8]) -> Vec<u8> {
83    let hasher = Sha256::new().chain_update(&payload);
84    let output: Vec<_> = hasher.finalize().into_iter().collect();
85
86    let hasher = Sha256::new().chain_update(&output);
87    hasher.finalize().into_iter().collect()
88}
89
90/// Converts a Vec<u8> into a checksummed base58check encoded string
91pub fn encode(payload: Vec<u8>) -> Result<String, Box<dyn Error>> {
92    let payload_u8a: &[u8] = &*payload;
93    let checksum = double_sha256(payload_u8a);
94
95    let mut the_buffer: Vec<u8> = vec![0; payload.len() + 4];
96    
97    let xx = payload_u8a.len();
98 
99
100    
101    let ii = 0;
102    for i in ii..xx {
103        the_buffer[i] = payload_u8a[i];
104    }
105
106    the_buffer[ xx + 0] = checksum[0];
107    the_buffer[ xx + 1] = checksum[1];
108    the_buffer[ xx + 2] = checksum[2];
109    the_buffer[ xx + 3] = checksum[3];
110
111    encode_b58c_plain(the_buffer)
112}
113
114