use crate::errors::{ProtocolError, Result};
use bigdecimal::BigDecimal;
use nash_mpc::rust_bigint::traits::Converter;
use nash_mpc::rust_bigint::BigInt;
use sha3::{Digest, Keccak256};
use std::str::FromStr;
use std::time::{SystemTime, UNIX_EPOCH};
#[cfg(feature = "num_bigint")]
use num_traits::Num;
pub fn bigint_to_nash_sig(num: BigInt) -> String {
format!("{:0>1024}", num.to_hex())
}
pub fn bigint_to_nash_r(r: BigInt) -> String {
format!("{:0>66}", r.to_hex())
}
pub fn hash_eth_message(msg: &[u8]) -> BigInt {
const ETH_PREFIX: &[u8] = b"\x19Ethereum Signed Message:\n32";
let msg_hash =
Keccak256::digest(&[Ð_PREFIX[..], &Keccak256::digest(&msg).to_vec()].concat());
BigInt::from_bytes(&msg_hash)
}
pub fn hash_neo_message(msg: &[u8]) -> BigInt {
let msg_hash_once = sha2::Sha256::digest(msg);
let msg_hash_twice = sha2::Sha256::digest(&msg_hash_once[..]);
BigInt::from_str_radix(&hex::encode(msg_hash_twice), 16).unwrap()
}
pub fn hash_message(message: &str) -> BigInt {
let hash = sha2::Sha256::digest(message.as_bytes());
BigInt::from_str_radix(&hex::encode(hash), 16).unwrap()
}
pub fn der_encode_sig(r: &BigInt, s: &BigInt) -> Vec<u8> {
let r_bytes = der_encode_sig_value(r);
let s_bytes = der_encode_sig_value(s);
let mut der_bytes = Vec::new();
der_bytes.push(0x30);
der_bytes.push((r_bytes.len() + s_bytes.len()) as u8);
for byte in r_bytes {
der_bytes.push(byte);
}
for byte in s_bytes {
der_bytes.push(byte)
}
der_bytes
}
fn der_encode_sig_value(r_or_s: &BigInt) -> Vec<u8> {
let mut bytes = r_or_s.to_bytes();
if bytes[0] >= 0b1000_0000 {
bytes.insert(0, 0x00);
}
bytes.insert(0, bytes.len() as u8);
bytes.insert(0, 0x02);
bytes
}
pub fn current_time_as_i64() -> i64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as i64
}
pub fn decode_hexstr(hex_str: &str) -> Result<Vec<u8>> {
Ok(hex::decode(hex_str).map_err(|_| ProtocolError("Could not decode hex string"))?)
}
pub fn pad_zeros(str_num: &str, precision: u32) -> Result<String> {
let components: Vec<&str> = str_num.split('.').collect();
match components.len() {
1 => {
let mut zeros = ".".to_string();
for _ in 0..precision {
zeros += "0"
}
Ok(str_num.to_string() + &zeros)
}
2 => {
let mut zeros = "".to_string();
let existing_count = components[1].len();
if existing_count as u32 > precision {
let num_zeros_to_subtract = existing_count as u32 - precision;
let reduced_prec = &str_num[..(str_num.len() - (num_zeros_to_subtract as usize))];
Ok(reduced_prec.to_string())
} else {
for _ in 0..(precision - existing_count as u32) {
zeros += "0"
}
Ok(str_num.to_string() + &zeros)
}
}
_ => Err(ProtocolError(
"String was not a valid number for zero padding",
)),
}
}
pub fn convert_at_price(amount: &str, price: &str) -> Result<String> {
let amount_bd = BigDecimal::from_str(amount)
.map_err(|_| ProtocolError("Could not convert amount to bigdecimal"))?;
let price_bd = BigDecimal::from_str(price)
.map_err(|_| ProtocolError("Could not convert price to bigdecimal"))?;
let convert = amount_bd * price_bd.inverse();
Ok(convert.to_string())
}