mod crypto;
mod rlp;
pub use self::crypto::{keccak256, KECCAK256_BYTES};
pub use self::rlp::{RLPList, WriteRLP};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use chrono::prelude::Utc;
use hex::FromHex;
use std::io::Cursor;
use std::mem::transmute;
static HEX_CHARS: &'static [u8] = b"0123456789abcdef";
const ETH: &'static str = "eth";
const MORDEN: &'static str = "morden";
const ROPSTEN: &'static str = "ropsten";
const RINKEBY: &'static str = "rinkeby";
const ROOTSTOCK_MAINNET: &'static str = "rootstock-main";
const ROOTSTOCK_TESTNET: &'static str = "rootstock-test";
const KOVAN: &'static str = "kovan";
const ETC: &'static str = "etc";
const MAINNET: &'static str = "mainnet";
const ETC_MORDEN: &'static str = "etc-morden";
pub trait ToHex {
fn to_hex(&self) -> String;
}
impl ToHex for [u8] {
fn to_hex(&self) -> String {
let mut v = Vec::with_capacity(self.len() * 2);
for &byte in self.iter() {
v.push(HEX_CHARS[(byte >> 4) as usize]);
v.push(HEX_CHARS[(byte & 0xf) as usize]);
}
unsafe { String::from_utf8_unchecked(v) }
}
}
impl ToHex for u64 {
fn to_hex(&self) -> String {
let bytes: [u8; 8] = unsafe { transmute(self.to_be()) };
bytes.to_hex()
}
}
pub fn to_chain_name(id: u8) -> Option<&'static str> {
match id {
1 => Some(ETH),
2 => Some(MORDEN),
3 => Some(ROPSTEN),
4 => Some(RINKEBY),
30 => Some(ROOTSTOCK_MAINNET),
31 => Some(ROOTSTOCK_TESTNET),
42 => Some(KOVAN),
61 => Some(ETC),
62 => Some(ETC_MORDEN),
_ => None,
}
}
pub fn to_chain_id(name: &str) -> Option<u8> {
match name.to_lowercase().as_str() {
ETH => Some(1),
MORDEN => Some(2),
ROPSTEN => Some(3),
RINKEBY => Some(4),
ROOTSTOCK_MAINNET => Some(30),
ROOTSTOCK_TESTNET => Some(31),
KOVAN => Some(42),
ETC | MAINNET => Some(61),
ETC_MORDEN => Some(62),
_ => None,
}
}
pub fn to_u64(v: &[u8]) -> u64 {
let data = align_bytes(v, 8);
let mut buf = Cursor::new(&data);
buf.read_u64::<BigEndian>().unwrap()
}
pub fn trim_hex(val: &str) -> &str {
if !val.starts_with("0x") {
return val;
}
let (_, s) = val.split_at(2);
s
}
pub fn to_arr<A, T>(slice: &[T]) -> A
where
A: AsMut<[T]> + Default,
T: Clone,
{
let mut arr = Default::default();
<A as AsMut<[T]>>::as_mut(&mut arr).clone_from_slice(slice);
arr
}
pub fn align_bytes(data: &[u8], len: usize) -> Vec<u8> {
if data.len() >= len {
return data.to_vec();
}
let mut v = vec![0u8; len - data.len()];
v.extend_from_slice(data);
v
}
pub fn to_even_str(data: &str) -> String {
if data.len() % 2 == 0 {
return String::from(data);
}
let mut v = String::from("0");
v.push_str(data);
v
}
pub fn trim_bytes(data: &[u8]) -> &[u8] {
let mut n = 0;
for b in data {
if *b != 0u8 {
break;
}
n += 1;
}
&data[n..data.len()]
}
pub fn bytes_count(x: usize) -> u8 {
match x {
_ if x > 0xff => 1 + bytes_count(x >> 8),
_ if x > 0 => 1,
_ => 0,
}
}
pub fn to_bytes(x: u64, len: u8) -> Vec<u8> {
let mut buf = vec![];
match len {
1 => buf.push(x as u8),
2 => buf.write_u16::<BigEndian>(x as u16).unwrap(),
4 => buf.write_u32::<BigEndian>(x as u32).unwrap(),
8 => buf.write_u64::<BigEndian>(x).unwrap(),
_ => (),
}
buf
}
pub fn timestamp() -> String {
str::replace(&Utc::now().to_rfc3339(), ":", "-")
.split('.')
.next()
.unwrap()
.to_string()
}
pub fn to_16bytes(hex: &str) -> [u8; 16] {
to_arr(Vec::from_hex(&hex).unwrap().as_slice())
}
pub fn to_20bytes(hex: &str) -> [u8; 20] {
to_arr(Vec::from_hex(&hex).unwrap().as_slice())
}
pub fn to_32bytes(hex: &str) -> [u8; 32] {
to_arr(Vec::from_hex(&hex).unwrap().as_slice())
}
#[cfg(test)]
pub use self::tests::*;
#[cfg(test)]
mod tests {
use super::*;
use tests::*;
#[test]
fn should_convert_zero_string_into_16bytes() {
assert_eq!(to_16bytes("00000000000000000000000000000000"), [0u8; 16]);
}
#[test]
fn should_convert_address_into_20bytes() {
assert_eq!(
to_20bytes("3f4e0668c20e100d7c2a27d4b177ac65b2875d26"),
[
0x3f, 0x4e, 0x06, 0x68, 0xc2, 0x0e, 0x10, 0x0d, 0x7c, 0x2a, 0x27, 0xd4, 0xb1, 0x77,
0xac, 0x65, 0xb2, 0x87, 0x5d, 0x26,
]
);
}
#[test]
fn should_convert_key_into_32bytes() {
assert_eq!(
to_32bytes("fa384e6fe915747cd13faa1022044b0def5e6bec4238bec53166487a5cca569f",),
[
0xfa, 0x38, 0x4e, 0x6f, 0xe9, 0x15, 0x74, 0x7c, 0xd1, 0x3f, 0xaa, 0x10, 0x22, 0x04,
0x4b, 0x0d, 0xef, 0x5e, 0x6b, 0xec, 0x42, 0x38, 0xbe, 0xc5, 0x31, 0x66, 0x48, 0x7a,
0x5c, 0xca, 0x56, 0x9f,
]
);
}
#[test]
fn should_align_empty_bytes() {
assert_eq!(align_bytes(&[], 8), vec![0, 0, 0, 0, 0, 0, 0, 0]);
}
#[test]
fn should_align_some_zero_bytes() {
assert_eq!(align_bytes(&[0, 0, 0], 8), vec![0, 0, 0, 0, 0, 0, 0, 0]);
}
#[test]
fn should_align_all_zero_bytes() {
assert_eq!(
align_bytes(&[0, 0, 0, 0, 0, 0, 0, 0], 8),
vec![0, 0, 0, 0, 0, 0, 0, 0]
);
}
#[test]
fn should_align_some_bytes() {
assert_eq!(align_bytes(&[0, 1, 2, 3], 8), vec![0, 0, 0, 0, 0, 1, 2, 3]);
}
#[test]
fn should_align_full_bytes() {
assert_eq!(
align_bytes(&[1, 2, 3, 4, 5, 6, 7, 8], 8),
vec![1, 2, 3, 4, 5, 6, 7, 8]
);
}
#[test]
fn should_skip_smaller_length() {
assert_eq!(
align_bytes(&[1, 2, 3, 4, 5, 6, 7, 8], 6),
vec![1, 2, 3, 4, 5, 6, 7, 8]
);
}
#[test]
fn should_align_to_even_str() {
assert_eq!(to_even_str("100"), String::from("0100"));
}
#[test]
fn should_skip_already_even_str() {
assert_eq!(to_even_str("100012"), String::from("100012"));
}
#[test]
fn should_trim_empty_bytes() {
assert_eq!(trim_bytes(&[]), &[] as &[u8]);
}
#[test]
fn should_trim_zero_bytes() {
assert_eq!(trim_bytes(&[0, 0, 0]), &[] as &[u8]);
}
#[test]
fn should_trim_some_bytes() {
assert_eq!(trim_bytes(&[0, 0, 0, 0, 0, 1, 2, 3]), &[1, 2, 3]);
}
#[test]
fn should_trim_hex_prefix() {
assert_eq!("12345", trim_hex("0x12345"))
}
#[test]
fn should_skip_trim_hex_prefix() {
assert_eq!("12345", trim_hex("12345"))
}
#[test]
fn should_count_bytes_for_rlp() {
assert_eq!(bytes_count(0x00), 0);
assert_eq!(bytes_count(0x01), 1);
assert_eq!(bytes_count(0xff), 1);
assert_eq!(bytes_count(0xff01), 2);
assert_eq!(bytes_count(0xffff01), 3);
}
#[test]
fn should_convert_u8_to_bytes() {
assert_eq!([1], to_bytes(1, 1).as_slice());
assert_eq!([2], to_bytes(2, 1).as_slice());
assert_eq!([127], to_bytes(127, 1).as_slice());
assert_eq!([128], to_bytes(128, 1).as_slice());
assert_eq!([255], to_bytes(255, 1).as_slice());
}
#[test]
fn should_convert_u16_to_bytes() {
assert_eq!([0, 1], to_bytes(1, 2).as_slice());
assert_eq!([0, 2], to_bytes(2, 2).as_slice());
assert_eq!([0, 255], to_bytes(255, 2).as_slice());
assert_eq!([1, 0], to_bytes(256, 2).as_slice());
assert_eq!([0x12, 0x34], to_bytes(0x1234, 2).as_slice());
assert_eq!([0xff, 0xff], to_bytes(0xffff, 2).as_slice());
}
#[test]
fn should_convert_u32_to_bytes() {
assert_eq!([0, 0, 0, 1], to_bytes(1, 4).as_slice());
assert_eq!([0x12, 0x34, 0x56, 0x78], to_bytes(0x12345678, 4).as_slice());
assert_eq!([0xff, 0x0, 0x0, 0x0], to_bytes(0xff000000, 4).as_slice());
assert_eq!([0x00, 0xff, 0x0, 0x0], to_bytes(0x00ff0000, 4).as_slice());
}
#[test]
fn should_convert_u64_to_bytes() {
assert_eq!([0, 0, 0, 0, 0, 0, 0, 1], to_bytes(1, 8).as_slice());
assert_eq!(
[0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef],
to_bytes(0x1234567890abcdef, 8).as_slice()
);
assert_eq!(
[0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0],
to_bytes(0xff00000000000000, 8).as_slice()
);
assert_eq!(
[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
to_bytes(0xffffffffffffffff, 8).as_slice()
);
}
#[test]
fn should_generate_timestamp() {
let re = Regex::new(r"^\d{4}-\d{2}-\d{2}[T]\d{2}-\d{2}-\d{2}").unwrap();
assert!(re.is_match(×tamp()));
}
#[test]
fn should_convert_to_chain_id() {
assert_eq!(to_chain_id("eth"), Some(1));
assert_eq!(to_chain_id("morden"), Some(2));
assert_eq!(to_chain_id("ropsten"), Some(3));
assert_eq!(to_chain_id("rinkeby"), Some(4));
assert_eq!(to_chain_id("rootstock-main"), Some(30));
assert_eq!(to_chain_id("rootstock-test"), Some(31));
assert_eq!(to_chain_id("kovan"), Some(42));
assert_eq!(to_chain_id("etc"), Some(61));
assert_eq!(to_chain_id("mainnet"), Some(61));
assert_eq!(to_chain_id("etc-morden"), Some(62));
assert_eq!(to_chain_id("eTc"), Some(61));
assert_eq!(to_chain_id("ecccc"), None);
}
#[test]
fn should_convert_to_chain_name() {
assert_eq!(to_chain_name(1), Some(ETH));
assert_eq!(to_chain_name(2), Some(MORDEN));
assert_eq!(to_chain_name(3), Some(ROPSTEN));
assert_eq!(to_chain_name(4), Some(RINKEBY));
assert_eq!(to_chain_name(30), Some(ROOTSTOCK_MAINNET));
assert_eq!(to_chain_name(31), Some(ROOTSTOCK_TESTNET));
assert_eq!(to_chain_name(42), Some(KOVAN));
assert_eq!(to_chain_name(61), Some(ETC));
assert_eq!(to_chain_name(62), Some(ETC_MORDEN));
assert_eq!(to_chain_name(100), None);
}
}