From 54856504a2982bcac9b514cf1ca6445c54fa19e8 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Fri, 1 Jan 2021 18:35:28 +0800 Subject: [PATCH] feat: add simple contract --- __crypto/simple_contract/Cargo.lock | 393 +++++++++++++++++++++++++ __crypto/simple_contract/Cargo.toml | 18 ++ __crypto/simple_contract/src/credit.rs | 77 +++++ __crypto/simple_contract/src/main.rs | 31 ++ __crypto/simple_contract/src/tx.rs | 76 +++++ __crypto/simple_contract/src/util.rs | 89 ++++++ 6 files changed, 684 insertions(+) create mode 100644 __crypto/simple_contract/Cargo.lock create mode 100644 __crypto/simple_contract/Cargo.toml create mode 100644 __crypto/simple_contract/src/credit.rs create mode 100644 __crypto/simple_contract/src/main.rs create mode 100644 __crypto/simple_contract/src/tx.rs create mode 100644 __crypto/simple_contract/src/util.rs diff --git a/__crypto/simple_contract/Cargo.lock b/__crypto/simple_contract/Cargo.lock new file mode 100644 index 0000000..b5f73a4 --- /dev/null +++ b/__crypto/simple_contract/Cargo.lock @@ -0,0 +1,393 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "bs58" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cc" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dae9c4b8fedcae85592ba623c4fd08cfdab3e3b72d6df780c6ead964a69bfff" + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + +[[package]] +name = "hex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "libc" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +dependencies = [ + "autocfg", + "libc", + "rand_chacha", + "rand_core 0.4.2", + "rand_hc", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg", + "rand_xorshift", + "winapi", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +dependencies = [ + "autocfg", + "rand_core 0.3.1", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +dependencies = [ + "libc", + "rand_core 0.4.2", + "winapi", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +dependencies = [ + "autocfg", + "rand_core 0.4.2", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "ripemd160" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad5112e0dbbb87577bfbc56c42450235e3012ce336e29c5befd7807bd626da4a" +dependencies = [ + "block-buffer", + "digest", + "opaque-debug", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "secp256k1" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2932dc07acd2066ff2e3921a4419606b220ba6cd03a9935123856cc534877056" +dependencies = [ + "rand", + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab2c26f0d3552a0f12e639ae8a64afc2e3db9c52fe32f5fc6c289d38519f220" +dependencies = [ + "cc", +] + +[[package]] +name = "serde" +version = "1.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", +] + +[[package]] +name = "simple_contract" +version = "0.1.0" +dependencies = [ + "bs58", + "digest", + "hex", + "rand", + "ripemd160", + "secp256k1", + "serde", + "serde_json", + "sha2", +] + +[[package]] +name = "syn" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9802ddde94170d186eeee5005b798d9c159fa970403f1be19976d0cfb939b72" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/__crypto/simple_contract/Cargo.toml b/__crypto/simple_contract/Cargo.toml new file mode 100644 index 0000000..5a77c42 --- /dev/null +++ b/__crypto/simple_contract/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "simple_contract" +version = "0.1.0" +authors = ["Hatter Jiang "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +hex = "0.4" +bs58 = "0.3.0" +secp256k1 = { version = "0.17.2", features = ["rand"] } +rand = "0.6" +digest = "0.8.1" +ripemd160 = "0.8.0" +sha2 = "0.8.1" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" diff --git a/__crypto/simple_contract/src/credit.rs b/__crypto/simple_contract/src/credit.rs new file mode 100644 index 0000000..5588d22 --- /dev/null +++ b/__crypto/simple_contract/src/credit.rs @@ -0,0 +1,77 @@ +use std::{collections::HashMap, fs, fs::File}; +use serde::{Serialize, Deserialize}; +use crate::{tx::Transaction, util::{SimpleError, XResult}}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct CreditContract { + pub credit_limit: u32, + pub issue_amount: u32, + pub admin: String, + pub credit: HashMap, +} + +impl CreditContract { + pub fn new(tx: &Transaction, name: &str, credit_limit: u32) -> XResult { + let c = Self { + credit_limit, + issue_amount: 0, + admin: tx.sender.clone().unwrap(), + credit: HashMap::new(), + }; + Self::save(name, &c)?; + Ok(c) + } + + pub fn issue(&mut self, tx: &Transaction, receiver: &str, credit: u32) -> XResult<()> { + if &self.admin != tx.sender.as_ref().ok_or_else(|| SimpleError::new("Sender is not provided".into()))? { + return Err(SimpleError::new(format!("Current user is not admin, {} vs {:?}", self.admin, tx.sender)).into()) + } + if self.issue_amount + credit > self.credit_limit { + return Err(SimpleError::new(format!("Issue too much credit, current: {}, issue: {}, limit: {}", self.issue_amount, credit, self.credit_limit)).into()); + } + match self.credit.get_mut(receiver) { + None => { self.credit.insert(receiver.to_owned(), credit); }, + Some(cr) => *cr += credit, + } + self.issue_amount += credit; + Ok(()) + } + + pub fn transfer(&mut self, tx: &Transaction, receiver: &str, credit: u32) -> XResult<()> { + match self.credit.get_mut(tx.sender.as_ref().ok_or_else(|| SimpleError::new("Sender is not provided".into()))?) { + None => return Err(SimpleError::new(format!("Have not enough credit: {:?}", tx.sender)).into()), + Some(cr) => { + if *cr >= credit { + *cr -= credit; + match self.credit.get_mut(receiver) { + None => { self.credit.insert(receiver.to_owned(), credit); }, + Some(receiver_credit) => *receiver_credit += credit, + } + } else { + return Err(SimpleError::new(format!("Have not enough credit: {:?}", tx.sender)).into()); + } + }, + } + Ok(()) + } + + pub fn get(&self, _tx: &Transaction, account: &str) -> u32 { + match self.credit.get(account) { + None => 0, + Some(receiver_credit) => *receiver_credit, + } + } + + pub fn load(name: &str) -> XResult { + let json = fs::read_to_string(name)?; + serde_json::from_str(&json).map_err(|e| e.into()) + } + + pub fn save(name: &str, c: &CreditContract) -> XResult<()> { + if let Ok(_) = File::open(name) { + return Err(SimpleError::new(format!("File exists: {}", name)).into()); + } + fs::write(name, serde_json::to_string(c)?.as_bytes())?; + Ok(()) + } +} diff --git a/__crypto/simple_contract/src/main.rs b/__crypto/simple_contract/src/main.rs new file mode 100644 index 0000000..ec7103c --- /dev/null +++ b/__crypto/simple_contract/src/main.rs @@ -0,0 +1,31 @@ +pub mod util; +pub mod tx; +pub mod credit; + +use std::str::FromStr; +use secp256k1::{Message, Secp256k1, Signature}; +use util::*; + +fn main() -> XResult<()> { + let (pri_key, pub_key) = make_key_pair(); + println!("{:?}", pri_key); + println!("{:?}", pub_key); + println!("{:?}", JsonKeyPair::from(pri_key, pub_key).to_json()); + println!("{:?}", JsonKeyPair::from(pri_key, pub_key).to_key_pair()); + println!("{}", make_btc_address(&pub_key)); + + let p256k1 = Secp256k1::new(); + let s =calc_sha256("hello".as_bytes()); + let message = Message::from_slice(&s)?; + + let sign = p256k1.sign(&message, &pri_key); + let sign_hex = format!("{}", sign); + println!("{}", sign_hex); + + let sig = Signature::from_str(&sign_hex)?; + + let result = p256k1.verify(&message, &sig, &pub_key); + println!("{:?}", result); + + Ok(()) +} diff --git a/__crypto/simple_contract/src/tx.rs b/__crypto/simple_contract/src/tx.rs new file mode 100644 index 0000000..24fa4b3 --- /dev/null +++ b/__crypto/simple_contract/src/tx.rs @@ -0,0 +1,76 @@ +use secp256k1::{Message, PublicKey, Secp256k1, SecretKey, Signature}; +use serde::{Serialize, Deserialize}; +use std::str::FromStr; +use crate::util::{SimpleError, XResult, calc_sha256, make_btc_address}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Transaction { + pub body: String, + pub sender: Option, + pub signature: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct TransactionBody { + pub timestamp: u64, + pub nonce: String, + pub contract: String, + pub action: String, + pub parameters: String, +} + +impl Transaction { + pub fn from_json(transaction_json: &str) -> XResult { + serde_json::from_str(transaction_json).map_err(|e| e.into()) + } + + pub fn from_transaction_body(transaction_body: &TransactionBody) -> XResult { + Ok(Self { + body: serde_json::to_string(transaction_body)?, + sender: None, + signature: None, + }) + } + + pub fn parse_body(&self) -> XResult { + serde_json::from_str(&self.body).map_err(|e| e.into()) + } + + pub fn is_signed(&self) -> bool { + self.signature.is_some() + } + + pub fn sign(&mut self, sender: &str, priv_key: &SecretKey) -> XResult<()> { + if self.signature.is_some() { + Err(SimpleError::new("Transaction is signed!".into()).into()) + } else { + let message = self.get_body_message()?; + let sign = Secp256k1::new().sign(&message, priv_key); + self.sender = Some(sender.into()); + self.signature = Some(format!("{}", sign)); + Ok(()) + } + } + + pub fn verify(&self, pub_key: &PublicKey) -> XResult<()> { + match (&self.sender, &self.signature) { + (None, None) | (None, Some(_)) | (Some(_), None) => { + Err(SimpleError::new("Transaction has no sender or not signed!".into()).into()) + }, + (Some(sender), Some(sign_hex)) => { + let address = make_btc_address(pub_key); + if &address != sender { + return Err(SimpleError::new("".into()).into()); + } + let message = self.get_body_message()?; + let sig = Signature::from_str(sign_hex)?; + Secp256k1::new().verify(&message, &sig, pub_key).map_err(|e| e.into()) + } + } + } + + fn get_body_message(&self) -> XResult { + let digest= calc_sha256(self.body.as_bytes()); + Message::from_slice(&digest).map_err(|e| e.into()) + } +} diff --git a/__crypto/simple_contract/src/util.rs b/__crypto/simple_contract/src/util.rs new file mode 100644 index 0000000..0f2d79e --- /dev/null +++ b/__crypto/simple_contract/src/util.rs @@ -0,0 +1,89 @@ +use rand::rngs::OsRng; +use serde::{Deserialize, Serialize}; +use secp256k1::{Secp256k1, SecretKey, key::PublicKey}; +use sha2::Sha256; +use ripemd160::Ripemd160; +use digest::{ Input, FixedOutput }; +use std::{fmt::Display, str::FromStr}; +use std::error::Error; + +pub type XResult = Result>; + +#[derive(Debug)] +pub struct SimpleError { + pub message: String, +} + +impl SimpleError { + pub fn new(message: String) -> Self { + Self { message } + } +} + +impl Display for SimpleError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "SimpleErorr, message: {}", self.message) + } +} + +impl Error for SimpleError {} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct JsonKeyPair { + pub pri_key: String, + pub pub_key: String, +} + +impl JsonKeyPair { + pub fn from(pri_key: SecretKey, pub_key: PublicKey) -> Self { + JsonKeyPair { + pri_key: format!("{}", pri_key), + pub_key: format!("{}", pub_key), + } + } + + pub fn to_json(&self) -> XResult { + serde_json::to_string(self).map_err(|e| e.into()) + } + + pub fn to_key_pair(&self) -> XResult<(SecretKey, PublicKey)> { + let pri_key = SecretKey::from_str(&self.pri_key)?; + let pub_key = PublicKey::from_str(&self.pub_key)?; + Ok((pri_key, pub_key)) + } +} + +pub fn make_key_pair() -> (SecretKey, PublicKey) { + let secp = Secp256k1::new(); + let mut rng = OsRng::new().expect("OsRng"); + let (secret_key, public_key) = secp.generate_keypair(&mut rng); + (secret_key, public_key) +} + +pub fn make_btc_address(public_key: &PublicKey) -> String { + let public_key_bytes = public_key.serialize_uncompressed().to_vec(); + let riphemd160_sha256_pub_key = calc_ripemd160(&calc_sha256(&public_key_bytes)); + let mut btc_addr = Vec::::with_capacity(25); + btc_addr.push(0x00 as u8); + btc_addr.extend_from_slice(&riphemd160_sha256_pub_key); + let checksum = &calc_sha256(&calc_sha256(&btc_addr))[0..4]; + btc_addr.extend_from_slice(checksum); + bs58::encode(&btc_addr).into_string() +} + +#[inline] +pub fn calc_sha256(i: &[u8]) -> Vec { + calc_hash(Sha256::default(), i) +} + +#[inline] +fn calc_ripemd160(i: &[u8]) -> Vec { + calc_hash(Ripemd160::default(), i) +} + +#[inline] +fn calc_hash(mut hasher: T, i: &[u8]) -> Vec where T: Input + FixedOutput { + hasher.input(&i); + hasher.fixed_result().to_vec() +}