feat: add simple contract

This commit is contained in:
2021-01-01 18:35:28 +08:00
parent 6386371a69
commit 54856504a2
6 changed files with 684 additions and 0 deletions

393
__crypto/simple_contract/Cargo.lock generated Normal file
View File

@@ -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"

View File

@@ -0,0 +1,18 @@
[package]
name = "simple_contract"
version = "0.1.0"
authors = ["Hatter Jiang <jht5945@gmail.com>"]
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"

View File

@@ -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<String, u32>,
}
impl CreditContract {
pub fn new(tx: &Transaction, name: &str, credit_limit: u32) -> XResult<Self> {
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<Self> {
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(())
}
}

View File

@@ -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(())
}

View File

@@ -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<String>,
pub signature: Option<String>,
}
#[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<Self> {
serde_json::from_str(transaction_json).map_err(|e| e.into())
}
pub fn from_transaction_body(transaction_body: &TransactionBody) -> XResult<Self> {
Ok(Self {
body: serde_json::to_string(transaction_body)?,
sender: None,
signature: None,
})
}
pub fn parse_body(&self) -> XResult<TransactionBody> {
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<Message> {
let digest= calc_sha256(self.body.as_bytes());
Message::from_slice(&digest).map_err(|e| e.into())
}
}

View File

@@ -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<T> = Result<T, Box<dyn Error>>;
#[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<String> {
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::<u8>::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<u8> {
calc_hash(Sha256::default(), i)
}
#[inline]
fn calc_ripemd160(i: &[u8]) -> Vec<u8> {
calc_hash(Ripemd160::default(), i)
}
#[inline]
fn calc_hash<T>(mut hasher: T, i: &[u8]) -> Vec<u8> where T: Input + FixedOutput {
hasher.input(&i);
hasher.fixed_result().to_vec()
}