lazy_static! {
static ref BASE64_MAP: [i32; 256] = create_base64_map();
static ref BASE58_MAP: [i32; 256] = create_base58_map();
}
use ripemd160::{Digest, Ripemd160};
const BASE58CHARS: &str = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
const BASE64CHARS: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
pub fn create_base58_map() -> [i32; 256] {
let mut base58_mapi: [i32; 256] = [-1; 256];
let mut i = 1;
BASE58CHARS.chars().for_each(|c| {
base58_mapi[c as usize] = i;
i += 1;
});
base58_mapi
}
pub fn create_base64_map() -> [i32; 256] {
let mut base64_mapi: [i32; 256] = [-1; 256];
let mut i = 1;
BASE64CHARS.chars().for_each(|c| {
base64_mapi[c as usize] = i;
i += 1;
});
base64_mapi['=' as usize] = 0;
base64_mapi
}
pub fn is_negative(bignum: &[u8]) -> bool {
let last_digit = bignum[bignum.len() - 1];
(last_digit & 0x80) != 0
}
pub fn negate(bignum: &[u8]) -> Vec<u8> {
let mut carry = 1;
let mut result: Vec<u8> = Vec::with_capacity(bignum.len());
for part in bignum {
let x = !part + carry;
result.push(x);
carry = x.checked_shr(8).unwrap_or(0);
}
result
}
#[allow(dead_code)]
pub fn decimal_to_binary(size: usize, s: &str) -> Vec<u8> {
let mut result: Vec<u8> = vec![0; size];
s.chars().for_each(|c| {
if !c.is_digit(10) {
panic!("String contains non-decimals");
} else {
let mut carry: u16 = c as u16 - '0' as u16;
for j in 0..size {
let x: u16 = (result[j] as u16) * 10 + carry;
result[j] = x as u8;
carry = x.checked_shr(8).unwrap_or(0);
}
if carry != 0 {
panic!("number is out of range")
}
}
});
result
}
#[allow(dead_code)]
pub fn signed_decimal_to_binary(size: usize, s: &str) -> Vec<u8> {
if s.is_empty() {
return decimal_to_binary(size, "0");
}
let is_neg: bool = s.chars().next().unwrap_or('0') == '-';
if is_neg {
let result = negate(&decimal_to_binary(size, &s[1..]));
if is_negative(&result) {
result
} else {
panic!("Invalid number? should be negative");
}
} else {
let result = decimal_to_binary(size, s);
if is_negative(&result) {
panic!("number overflow: should be positive");
} else {
result
}
}
}
#[allow(dead_code)]
pub fn binary_to_decimal(bignum: &[u8], min_digits: usize) -> String {
let mut result: Vec<char> = std::iter::repeat('0').take(min_digits).collect();
for i in (0..bignum.len()).rev() {
let mut carry = bignum[i];
for j in 0..result.len() {
let mut x: u16 = (result[j] as u16 - '0' as u16).checked_shl(8).unwrap_or(0);
x += carry as u16;
result[j] = (b'0' + (x % 10) as u8) as char;
carry = (x / 10) as u8 | 0;
}
while carry > 0 {
result.push((b'0' + carry % 10) as char);
carry = (carry / 10) | 0;
}
}
result.reverse();
result.into_iter().collect::<String>()
}
#[allow(dead_code)]
pub fn signed_binary_to_decimal(bignum: &[u8], min_digits: usize) -> String {
if is_negative(bignum) {
"-".to_owned() + binary_to_decimal(&negate(bignum), min_digits).as_str()
} else {
binary_to_decimal(&bignum, min_digits)
}
}
#[allow(dead_code)]
pub fn base58_to_binary(_size: usize, s: &str) -> Vec<u8> {
match bs58::decode(s).into_vec() {
Ok(vec) => vec,
Err(err) => panic!("{}", err),
}
}
#[allow(dead_code)]
pub fn binary_to_base58(bignum: &[u8], _min_digits: usize) -> String {
bs58::encode(bignum).into_string()
}
#[allow(dead_code)]
pub fn base64_to_binary(s: &str) -> Vec<u8> {
match base64::decode(s) {
Ok(vec) => vec,
Err(err) => panic!("{}", err),
}
}
#[allow(dead_code)]
pub fn binary_to_base64(bignum: &[u8]) -> String {
base64::encode(bignum)
}
#[allow(dead_code)]
pub fn digest_suffix_ripemd160(data: &[u8], suffix: &str) -> Vec<u8> {
let mut hasher = Ripemd160::new();
let vec: Vec<u8> = suffix.chars().map(|c| c as u8).collect();
let combined = [&data[..], &vec[..]].concat();
hasher.input(combined);
hasher.result().to_vec()
}
#[allow(dead_code)]
fn ripemd160(data: &[u8]) -> Vec<u8> {
let mut hasher = Ripemd160::new();
hasher.input(data);
hasher.result().to_vec()
}
#[allow(dead_code)]
fn is_invalid_digest(block1: &[u8], block2: &[u8], offset: usize) -> bool {
block1[0] != block2[offset]
|| block1[1] != block2[offset + 1]
|| block1[2] != block2[offset + 2]
|| block1[3] != block2[offset + 3]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn base58_mapping() {
assert_eq!(BASE58_MAP[12], -1);
assert_eq!(BASE58_MAP['2' as usize], 2);
assert_eq!(BASE58_MAP['A' as usize], 10);
}
#[test]
fn base64_mapping() {
assert_eq!(BASE64_MAP[12], -1);
assert_eq!(BASE64_MAP['A' as usize], 1);
assert_eq!(BASE64_MAP['2' as usize], 55);
assert_eq!(BASE64_MAP['=' as usize], 0);
}
#[test]
fn base58_to_binary_test() {
let str = "8";
let bin = base58_to_binary(str.len(), str);
assert_eq!(bin[0], 7);
let str2 = "5imfbmmHC83VRxLRTcvovviAc6LPpyszcDuKtkwka9e9Jg37Hp";
let bin2 = base58_to_binary(str2.len(), str2);
assert_eq!(bin2.len(), 37);
assert_eq!(bin2[0], 2);
assert_eq!(bin2[36], 0x87);
}
#[test]
fn base64_to_binary_test() {
let str = "TVqQ";
let bin = base64_to_binary(str);
assert_eq!(bin, vec![0x4D, 0x5a, 0x90]);
let str2 = "tsU=";
let bin2 = base64_to_binary(str2);
assert_eq!(bin2, vec![0xb6, 0xc5]);
}
#[test]
fn binary_to_base58_test() {
let bignum: Vec<u8> = vec![7];
let str = binary_to_base58(&bignum, 1);
assert_eq!(str, "8");
let str2 = "5imfbmmHC83VRxLRTcvovviAc6LPpyszcDuKtkwka9e9Jg37Hp";
let bin2 = base58_to_binary(str2.len(), str2);
assert_eq!(binary_to_base58(&bin2, 1), str2);
let bignum3: Vec<u8> = vec![
0x03, 0xf0, 0x39, 0xfd, 0xcd, 0xb7, 0x28, 0xef, 0xbb, 0xdd, 0xf4, 0xee, 0x45, 0x24,
0x19, 0xa9, 0x88, 0x49, 0x7d, 0xeb, 0xb7, 0xbd, 0x1b, 0x42, 0x64, 0x4c, 0x5f, 0xa6,
0x6e, 0x9a, 0xf8, 0xc8, 0xb6, 0xc6, 0x0a, 0x20, 0xd4,
];
let str = binary_to_base58(&bignum3, 1);
assert_eq!(str, "8f2o2LLQ3phteqyazxirQZnQzQFpnjLnXiUFEJcsSYhnjWNvSX");
}
#[test]
fn negate_test() {
let bignum: Vec<u8> = vec![0x3, 0x4];
let b2 = negate(&bignum);
assert!(is_negative(&b2));
let result = negate(&b2);
assert_eq!(result.len(), 2);
assert_eq!(result[0], bignum[0]);
assert_eq!(result[1], bignum[1]);
}
#[test]
fn decimal_to_binary_test() {
let s = "12345";
let bignum = decimal_to_binary(8, s);
assert_eq!(bignum.len(), 8);
assert_eq!(bignum[0], 0x39);
assert_eq!(bignum[1], 0x30);
assert_eq!(bignum[2], 0x0);
}
#[test]
fn signed_decimal_to_binary_test() {
let s = "12345";
assert_eq!(signed_decimal_to_binary(8, s), decimal_to_binary(8, s));
let bignum = signed_decimal_to_binary(8, "-12345");
assert!(is_negative(&bignum));
assert_eq!(negate(&bignum), decimal_to_binary(8, s));
}
#[test]
fn binary_to_decimal_test() {
let bignum = vec![0x39, 0x30, 0];
let str = binary_to_decimal(&bignum, 3);
assert_eq!("12345", str);
assert_eq!("00012345", binary_to_decimal(&bignum, 8));
}
#[test]
fn signed_binary_to_decimal_test() {
let bignum = signed_decimal_to_binary(2, "-12345");
assert_eq!("-12345", signed_binary_to_decimal(&bignum, 2));
assert_eq!("-00012345", signed_binary_to_decimal(&bignum, 8));
let bignum2 = decimal_to_binary(8, "12345");
assert_eq!("00012345", signed_binary_to_decimal(&bignum2, 8));
}
#[test]
fn digest_suffix_ripemd160_test() {
let bignum: Vec<u8> = vec![0x49, 0x61, 0x6e];
let suffix = "-Kry";
let digest_act = vec![
0xbe, 0x64, 0x6f, 0x38, 0x8a, 0xf0, 0x5e, 0x6e, 0xaa, 0x4b, 0xcd, 0x0f, 0xaa, 0xcb,
0x2a, 0x1d, 0x68, 0x58, 0x43, 0x51,
];
let digest = digest_suffix_ripemd160(&bignum, suffix);
assert_eq!(digest, digest_act);
}
}