diff --git a/__crypto/simple_contract/Cargo.lock b/__crypto/simple_contract/Cargo.lock index ea12186..0472123 100644 --- a/__crypto/simple_contract/Cargo.lock +++ b/__crypto/simple_contract/Cargo.lock @@ -1,5 +1,14 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + [[package]] name = "arrayref" version = "0.3.6" @@ -12,6 +21,17 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "0.1.7" @@ -98,6 +118,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + [[package]] name = "cloudabi" version = "0.0.3" @@ -176,6 +211,15 @@ dependencies = [ "wasi", ] +[[package]] +name = "hermit-abi" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" +dependencies = [ + "libc", +] + [[package]] name = "hex" version = "0.4.2" @@ -464,6 +508,7 @@ name = "simple_contract" version = "0.1.0" dependencies = [ "bs58", + "clap", "digest", "hex", "rand", @@ -475,6 +520,12 @@ dependencies = [ "sha2", ] +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "syn" version = "1.0.56" @@ -507,18 +558,39 @@ dependencies = [ "winapi", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "typenum" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" diff --git a/__crypto/simple_contract/Cargo.toml b/__crypto/simple_contract/Cargo.toml index 9c78d79..763b7d2 100644 --- a/__crypto/simple_contract/Cargo.toml +++ b/__crypto/simple_contract/Cargo.toml @@ -17,3 +17,4 @@ sha2 = "0.8.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" rust_util = "0.6" +clap = "2.33" diff --git a/__crypto/simple_contract/src/cmd.rs b/__crypto/simple_contract/src/cmd.rs new file mode 100644 index 0000000..1d669fa --- /dev/null +++ b/__crypto/simple_contract/src/cmd.rs @@ -0,0 +1,8 @@ +use clap::{ArgMatches, App}; +use rust_util::XResult; +pub type CommandError = XResult<()>; +pub trait Command { + fn name(&self) -> &str; + fn subcommand<'a>(&self) -> App<'a, 'a>; + fn run(&self, arg_matches: &ArgMatches, _: &ArgMatches) -> CommandError; +} diff --git a/__crypto/simple_contract/src/cmd_credit_create.rs b/__crypto/simple_contract/src/cmd_credit_create.rs new file mode 100644 index 0000000..ff45c93 --- /dev/null +++ b/__crypto/simple_contract/src/cmd_credit_create.rs @@ -0,0 +1,46 @@ +use std::fs; +use clap::{App, Arg, ArgMatches, SubCommand}; +use rust_util::information; +use crate::{cmd::{Command, CommandError}, engine_credit::CreditContractCreateParameters}; +use crate::engine::ContractEngine; +use crate::tx::{Transaction, TransactionBody}; +use crate::util::JsonKeyPair; + +pub struct CommandImpl; +impl Command for CommandImpl { + fn name(&self) -> &str { "create" } + fn subcommand<'a>(&self) -> App<'a, 'a> { + SubCommand::with_name(self.name()).about("Create credit contract subcommand") + .arg(Arg::with_name("name").long("name").short("n").required(true).takes_value(true).help("Contract name")) + .arg(Arg::with_name("limit").long("limit").short("l").required(true).takes_value(true).help("Contract credit limit")) + .arg(Arg::with_name("key").long("key").short("k").required(true).takes_value(true).help("Key pair")) + } + fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { + let name = sub_arg_matches.value_of("name").unwrap(); + let limit = sub_arg_matches.value_of("limit").unwrap(); + let key = sub_arg_matches.value_of("key").unwrap(); + + let key_json = fs::read_to_string(key)?; + let (pri_key, pub_key) = JsonKeyPair::from_json(&key_json)?.to_key_pair()?; + + let parameters = CreditContractCreateParameters { + name: name.into(), + credit_limit: limit.parse()?, + }; + let transaction_body = TransactionBody { + timestamp: 0, + nonce: "".into(), + contract: "credit".into(), + action: "create".into(), + parameters: serde_json::to_string(¶meters)?.into(), + }; + let tx = Transaction::new(&transaction_body, &pri_key, &pub_key)?; + + information!("Create transaction: {:#?}", tx); + + let engine = ContractEngine::new(); + engine.execute(&tx)?; + + Ok(()) + } +} diff --git a/__crypto/simple_contract/src/cmd_credit_issue.rs b/__crypto/simple_contract/src/cmd_credit_issue.rs new file mode 100644 index 0000000..6260062 --- /dev/null +++ b/__crypto/simple_contract/src/cmd_credit_issue.rs @@ -0,0 +1,49 @@ +use std::fs; +use clap::{App, Arg, ArgMatches, SubCommand}; +use rust_util::information; +use crate::{cmd::{Command, CommandError}, engine_credit::CreditContractIssueParameters}; +use crate::engine::ContractEngine; +use crate::tx::{Transaction, TransactionBody}; +use crate::util::JsonKeyPair; + +pub struct CommandImpl; +impl Command for CommandImpl { + fn name(&self) -> &str { "issue" } + fn subcommand<'a>(&self) -> App<'a, 'a> { + SubCommand::with_name(self.name()).about("Issue credit contract subcommand") + .arg(Arg::with_name("name").long("name").short("n").required(true).takes_value(true).help("Contract name")) + .arg(Arg::with_name("receiver").long("receiver").short("r").required(true).takes_value(true).help("Receiver")) + .arg(Arg::with_name("credit").long("credit").short("c").required(true).takes_value(true).help("Credit")) + .arg(Arg::with_name("key").long("key").short("k").required(true).takes_value(true).help("Key pair")) + } + fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { + let name = sub_arg_matches.value_of("name").unwrap(); + let receiver = sub_arg_matches.value_of("receiver").unwrap(); + let credit: u32 = sub_arg_matches.value_of("credit").unwrap().parse()?; + let key = sub_arg_matches.value_of("key").unwrap(); + + let key_json = fs::read_to_string(key)?; + let (pri_key, pub_key) = JsonKeyPair::from_json(&key_json)?.to_key_pair()?; + + let parameters = CreditContractIssueParameters { + name: name.into(), + receiver: receiver.into(), + credit, + }; + let transaction_body = TransactionBody { + timestamp: 0, + nonce: "".into(), + contract: "credit".into(), + action: "issue".into(), + parameters: serde_json::to_string(¶meters)?.into(), + }; + let tx = Transaction::new(&transaction_body, &pri_key, &pub_key)?; + + information!("Create transaction: {:#?}", tx); + + let engine = ContractEngine::new(); + engine.execute(&tx)?; + + Ok(()) + } +} diff --git a/__crypto/simple_contract/src/cmd_credit_query.rs b/__crypto/simple_contract/src/cmd_credit_query.rs new file mode 100644 index 0000000..3636363 --- /dev/null +++ b/__crypto/simple_contract/src/cmd_credit_query.rs @@ -0,0 +1,46 @@ +use std::fs; +use clap::{App, Arg, ArgMatches, SubCommand}; +use rust_util::information; +use crate::{cmd::{Command, CommandError}, engine_credit::CreditContractQueryParameters}; +use crate::engine::ContractEngine; +use crate::tx::{Transaction, TransactionBody}; +use crate::util::JsonKeyPair; + +pub struct CommandImpl; +impl Command for CommandImpl { + fn name(&self) -> &str { "query" } + fn subcommand<'a>(&self) -> App<'a, 'a> { + SubCommand::with_name(self.name()).about("Query credit contract subcommand") + .arg(Arg::with_name("name").long("name").short("n").required(true).takes_value(true).help("Contract name")) + .arg(Arg::with_name("account").long("account").short("a").required(true).takes_value(true).help("Account")) + .arg(Arg::with_name("key").long("key").short("k").required(true).takes_value(true).help("Key pair")) + } + fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { + let name = sub_arg_matches.value_of("name").unwrap(); + let account = sub_arg_matches.value_of("account").unwrap(); + let key = sub_arg_matches.value_of("key").unwrap(); + + let key_json = fs::read_to_string(key)?; + let (pri_key, pub_key) = JsonKeyPair::from_json(&key_json)?.to_key_pair()?; + + let parameters = CreditContractQueryParameters { + name: name.into(), + account: account.into(), + }; + let transaction_body = TransactionBody { + timestamp: 0, + nonce: "".into(), + contract: "credit".into(), + action: "query".into(), + parameters: serde_json::to_string(¶meters)?.into(), + }; + let tx = Transaction::new(&transaction_body, &pri_key, &pub_key)?; + + information!("Create transaction: {:#?}", tx); + + let engine = ContractEngine::new(); + engine.execute(&tx)?; + + Ok(()) + } +} diff --git a/__crypto/simple_contract/src/cmd_credit_queryall.rs b/__crypto/simple_contract/src/cmd_credit_queryall.rs new file mode 100644 index 0000000..be7c6dd --- /dev/null +++ b/__crypto/simple_contract/src/cmd_credit_queryall.rs @@ -0,0 +1,43 @@ +use std::fs; +use clap::{App, Arg, ArgMatches, SubCommand}; +use rust_util::information; +use crate::{cmd::{Command, CommandError}, engine_credit::CreditContractQueryAllParameters}; +use crate::engine::ContractEngine; +use crate::tx::{Transaction, TransactionBody}; +use crate::util::JsonKeyPair; + +pub struct CommandImpl; +impl Command for CommandImpl { + fn name(&self) -> &str { "queryall" } + fn subcommand<'a>(&self) -> App<'a, 'a> { + SubCommand::with_name(self.name()).about("Queryall credit contract subcommand") + .arg(Arg::with_name("name").long("name").short("n").required(true).takes_value(true).help("Contract name")) + .arg(Arg::with_name("key").long("key").short("k").required(true).takes_value(true).help("Key pair")) + } + fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { + let name = sub_arg_matches.value_of("name").unwrap(); + let key = sub_arg_matches.value_of("key").unwrap(); + + let key_json = fs::read_to_string(key)?; + let (pri_key, pub_key) = JsonKeyPair::from_json(&key_json)?.to_key_pair()?; + + let parameters = CreditContractQueryAllParameters { + name: name.into(), + }; + let transaction_body = TransactionBody { + timestamp: 0, + nonce: "".into(), + contract: "credit".into(), + action: "query_all".into(), + parameters: serde_json::to_string(¶meters)?.into(), + }; + let tx = Transaction::new(&transaction_body, &pri_key, &pub_key)?; + + information!("Create transaction: {:#?}", tx); + + let engine = ContractEngine::new(); + engine.execute(&tx)?; + + Ok(()) + } +} diff --git a/__crypto/simple_contract/src/cmd_credit_transfer.rs b/__crypto/simple_contract/src/cmd_credit_transfer.rs new file mode 100644 index 0000000..edbf05b --- /dev/null +++ b/__crypto/simple_contract/src/cmd_credit_transfer.rs @@ -0,0 +1,49 @@ +use std::fs; +use clap::{App, Arg, ArgMatches, SubCommand}; +use rust_util::information; +use crate::{cmd::{Command, CommandError}, engine_credit::CreditContractTransferParameters}; +use crate::engine::ContractEngine; +use crate::tx::{Transaction, TransactionBody}; +use crate::util::JsonKeyPair; + +pub struct CommandImpl; +impl Command for CommandImpl { + fn name(&self) -> &str { "transfer" } + fn subcommand<'a>(&self) -> App<'a, 'a> { + SubCommand::with_name(self.name()).about("Transfer credit contract subcommand") + .arg(Arg::with_name("name").long("name").short("n").required(true).takes_value(true).help("Contract name")) + .arg(Arg::with_name("receiver").long("receiver").short("r").required(true).takes_value(true).help("Receiver")) + .arg(Arg::with_name("credit").long("credit").short("c").required(true).takes_value(true).help("Credit")) + .arg(Arg::with_name("key").long("key").short("k").required(true).takes_value(true).help("Key pair")) + } + fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { + let name = sub_arg_matches.value_of("name").unwrap(); + let receiver = sub_arg_matches.value_of("receiver").unwrap(); + let credit: u32 = sub_arg_matches.value_of("credit").unwrap().parse()?; + let key = sub_arg_matches.value_of("key").unwrap(); + + let key_json = fs::read_to_string(key)?; + let (pri_key, pub_key) = JsonKeyPair::from_json(&key_json)?.to_key_pair()?; + + let parameters = CreditContractTransferParameters { + name: name.into(), + receiver: receiver.into(), + credit, + }; + let transaction_body = TransactionBody { + timestamp: 0, + nonce: "".into(), + contract: "credit".into(), + action: "transfer".into(), + parameters: serde_json::to_string(¶meters)?.into(), + }; + let tx = Transaction::new(&transaction_body, &pri_key, &pub_key)?; + + information!("Create transaction: {:#?}", tx); + + let engine = ContractEngine::new(); + engine.execute(&tx)?; + + Ok(()) + } +} diff --git a/__crypto/simple_contract/src/cmd_default.rs b/__crypto/simple_contract/src/cmd_default.rs new file mode 100644 index 0000000..a4c0c8e --- /dev/null +++ b/__crypto/simple_contract/src/cmd_default.rs @@ -0,0 +1,15 @@ +use clap::{App, Arg, ArgMatches}; +use rust_util::information; +use crate::cmd::CommandError; +pub struct CommandImpl; +impl CommandImpl { + pub fn process_command<'a>(app: App<'a, 'a>) -> App<'a, 'a> { + app.arg(Arg::with_name("verbose").long("verbose").short("v").multiple(true).help("Show verbose info")) + } + pub fn run(arg_matches: &ArgMatches) -> CommandError { + let verbose_count = arg_matches.occurrences_of("verbose"); + information!("Verbose count: {}", verbose_count); + information!("This is default command cli ..."); + Ok(()) + } +} \ No newline at end of file diff --git a/__crypto/simple_contract/src/cmd_genkey.rs b/__crypto/simple_contract/src/cmd_genkey.rs new file mode 100644 index 0000000..cc85267 --- /dev/null +++ b/__crypto/simple_contract/src/cmd_genkey.rs @@ -0,0 +1,24 @@ +use std::fs; +use std::fs::File; +use clap::{App, Arg, ArgMatches, SubCommand}; +use rust_util::failure; +use crate::{cmd::{Command, CommandError}, util::{JsonKeyPair, make_key_pair}}; +pub struct CommandImpl; +impl Command for CommandImpl { + fn name(&self) -> &str { "genkey" } + fn subcommand<'a>(&self) -> App<'a, 'a> { + SubCommand::with_name(self.name()).about("Generate key pair subcommand") + .arg(Arg::with_name("output").long("output").short("o").required(true).takes_value(true).help("Key pair output")) + } + fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { + let output = sub_arg_matches.value_of("output").unwrap(); + if let Ok(_) = File::open(output) { + failure!("Output file exists: {}", output); + return Ok(()); + } + let (pri_key, pub_key) = make_key_pair(); + let json_key_pair = JsonKeyPair::from(pri_key, pub_key); + fs::write(output, json_key_pair.to_json()?.as_bytes())?; + Ok(()) + } +} diff --git a/__crypto/simple_contract/src/credit.rs b/__crypto/simple_contract/src/credit.rs index 011acf8..2a299a6 100644 --- a/__crypto/simple_contract/src/credit.rs +++ b/__crypto/simple_contract/src/credit.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, fs, fs::File}; -use rust_util::{SimpleError, XResult, simple_error}; +use rust_util::{XResult, information, simple_error}; use serde::{Serialize, Deserialize}; -use crate::{tx::Transaction}; +use crate::tx::Transaction; #[derive(Debug, Serialize, Deserialize)] pub struct CreditContract { @@ -18,9 +18,10 @@ impl CreditContract { name: name.into(), credit_limit, issue_amount: 0, - admin: tx.sender.clone().unwrap(), + admin: tx.sender.clone(), credit: HashMap::new(), }; + save_credit_contract(&c, false)?; Ok(c) } @@ -29,23 +30,29 @@ impl CreditContract { } 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()))? { + if &self.admin != &tx.sender { return simple_error!("Current user is not admin, {} vs {:?}", self.admin, tx.sender); } + if credit == 0 { + return simple_error!("Cannot issue 0 credit!"); + } if self.issue_amount + credit > self.credit_limit { - return simple_error!("Issue too much credit, current: {}, issue: {}, limit: {}", self.issue_amount, credit, self.credit_limit); + return simple_error!("Issue too much credit, current: {}, issue: {}, limit: {}, left: {}", self.issue_amount, credit, self.credit_limit, self.credit_limit - self.issue_amount); } match self.credit.get_mut(receiver) { None => { self.credit.insert(receiver.to_owned(), credit); }, Some(cr) => *cr += credit, } self.issue_amount += credit; - save_credit_contract(self)?; + save_credit_contract(self, true)?; 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()))?) { + if credit == 0 { + return simple_error!("Cannot transfer 0 credit!"); + } + match self.credit.get_mut(&tx.sender) { None => return simple_error!("Have not enough credit: {:?}", tx.sender), Some(cr) => { if *cr >= credit { @@ -59,7 +66,7 @@ impl CreditContract { } }, } - save_credit_contract(self)?; + save_credit_contract(self, true)?; Ok(()) } @@ -80,11 +87,14 @@ fn load_credit_contract(name: &str) -> XResult { serde_json::from_str(&json).map_err(|e| e.into()) } -fn save_credit_contract(c: &CreditContract) -> XResult<()> { +fn save_credit_contract(c: &CreditContract, overwrite: bool) -> XResult<()> { let name = &c.name; - if let Ok(_) = File::open(name) { - return simple_error!("File exists: {}", name); + if !overwrite { + if let Ok(_) = File::open(name) { + return simple_error!("File exists: {}", name); + } } + information!("Write file: {}", name); fs::write(name, serde_json::to_string(c)?.as_bytes())?; Ok(()) } diff --git a/__crypto/simple_contract/src/engine.rs b/__crypto/simple_contract/src/engine.rs new file mode 100644 index 0000000..103c468 --- /dev/null +++ b/__crypto/simple_contract/src/engine.rs @@ -0,0 +1,26 @@ +use rust_util::{XResult, simple_error}; +use crate::{engine_credit::ContractEngineCredit, tx::Transaction}; + +pub struct ContractEngine { +} + +impl ContractEngine { + pub fn new() -> Self { + Self{} + } + + pub fn execute(&self, tx: &Transaction) -> XResult<()> { + tx.verify()?; + + let tx_body = tx.parse_body()?; + + match tx_body.contract.as_str() { + "credit" => { + ContractEngineCredit::new().execute(tx, &tx_body)?; + }, + c => return simple_error!("Unknown cotract: {}", c), + } + + Ok(()) + } +} diff --git a/__crypto/simple_contract/src/engine_credit.rs b/__crypto/simple_contract/src/engine_credit.rs new file mode 100644 index 0000000..1c5f0b7 --- /dev/null +++ b/__crypto/simple_contract/src/engine_credit.rs @@ -0,0 +1,81 @@ +use serde::{Serialize, Deserialize}; +use rust_util::{XResult, debugging, information, simple_error}; +use crate::{credit::CreditContract, tx::{Transaction, TransactionBody}}; + + +#[derive(Debug, Serialize, Deserialize)] +pub struct CreditContractCreateParameters { + pub name: String, + pub credit_limit: u32, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct CreditContractIssueParameters { + pub name: String, + pub receiver: String, + pub credit: u32, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct CreditContractTransferParameters { + pub name: String, + pub receiver: String, + pub credit: u32, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct CreditContractQueryParameters { + pub name: String, + pub account: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct CreditContractQueryAllParameters { + pub name: String, +} + +pub struct ContractEngineCredit { +} + +impl ContractEngineCredit { + pub fn new() -> Self { + Self{} + } + + pub fn execute(&self, tx: &Transaction, tx_body: &TransactionBody) -> XResult<()> { + let action = tx_body.action.as_str(); + + debugging!("Creidt contract, action: {}", action); + match action { + "create" => { + let params: CreditContractCreateParameters = serde_json::from_str(&tx_body.parameters)?; + CreditContract::new(tx, ¶ms.name, params.credit_limit)?; + }, + "issue" => { + let params: CreditContractIssueParameters = serde_json::from_str(&tx_body.parameters)?; + let mut c = CreditContract::load(¶ms.name)?; + c.issue(tx, ¶ms.receiver, params.credit)?; + }, + "transfer" => { + let params: CreditContractTransferParameters = serde_json::from_str(&tx_body.parameters)?; + let mut c = CreditContract::load(¶ms.name)?; + c.transfer(tx, ¶ms.receiver, params.credit)?; + }, + "query" => { + let params: CreditContractQueryParameters = serde_json::from_str(&tx_body.parameters)?; + let c = CreditContract::load(¶ms.name)?; + information!("Query: {}, credit: {}", ¶ms.account, c.query(tx, ¶ms.account)); + }, + "query_all" => { + let params: CreditContractQueryAllParameters = serde_json::from_str(&tx_body.parameters)?; + let c = CreditContract::load(¶ms.name)?; + let map = c.query_all(tx); + map.iter().for_each(|(k , v)| { + information!("Query: {}, credit: {}", k, v); + }); + }, + _ => return simple_error!("Unknown action: {}", action), + } + Ok(()) + } +} diff --git a/__crypto/simple_contract/src/main.rs b/__crypto/simple_contract/src/main.rs index 8c27335..3eca7f2 100644 --- a/__crypto/simple_contract/src/main.rs +++ b/__crypto/simple_contract/src/main.rs @@ -1,32 +1,42 @@ pub mod util; pub mod tx; pub mod credit; +pub mod engine; +pub mod engine_credit; -use std::str::FromStr; -use rust_util::XResult; -use secp256k1::{Message, Secp256k1, Signature}; -use util::*; +mod cmd; +mod cmd_default; +mod cmd_genkey; +mod cmd_credit_create; +mod cmd_credit_issue; +mod cmd_credit_transfer; +mod cmd_credit_query; +mod cmd_credit_queryall; -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)); +use clap::App; +use cmd::{Command, CommandError}; - 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(()) +fn main() -> CommandError { + let commands: Vec> = vec![ + Box::new(cmd_genkey::CommandImpl), + Box::new(cmd_credit_create::CommandImpl), + Box::new(cmd_credit_issue::CommandImpl), + Box::new(cmd_credit_transfer::CommandImpl), + Box::new(cmd_credit_query::CommandImpl), + Box::new(cmd_credit_queryall::CommandImpl), + ]; + let mut app = App::new(env!("CARGO_PKG_NAME")) + .version(env!("CARGO_PKG_VERSION")) + .about(env!("CARGO_PKG_DESCRIPTION")); + app = cmd_default::CommandImpl::process_command(app); + for command in &commands { + app = app.subcommand(command.subcommand()); + } + let matches = app.get_matches(); + for command in &commands { + if let Some(sub_cmd_matches) = matches.subcommand_matches(command.name()) { + return command.run(&matches, sub_cmd_matches); + } + } + cmd_default::CommandImpl::run(&matches) } diff --git a/__crypto/simple_contract/src/tx.rs b/__crypto/simple_contract/src/tx.rs index c11ac7f..87249e8 100644 --- a/__crypto/simple_contract/src/tx.rs +++ b/__crypto/simple_contract/src/tx.rs @@ -7,8 +7,9 @@ use crate::util::{calc_sha256, make_btc_address}; #[derive(Debug, Serialize, Deserialize)] pub struct Transaction { pub body: String, - pub sender: Option, - pub signature: Option, + pub sender: String, + pub public_key: String, + pub signature: String, } #[derive(Debug, Serialize, Deserialize)] @@ -21,57 +22,43 @@ pub struct TransactionBody { } impl Transaction { - pub fn from_json(transaction_json: &str) -> XResult { - serde_json::from_str(transaction_json).map_err(|e| e.into()) + pub fn new(transaction_body: &TransactionBody, priv_key: &SecretKey, pub_key: &PublicKey) -> XResult { + let body = serde_json::to_string(transaction_body)?; + let sender = make_btc_address(&pub_key); + let public_key = format!("{}", pub_key); + + let message = get_body_message(&body)?; + let sign = Secp256k1::new().sign(&message, priv_key); + + Ok(Self{ + body, + sender, + public_key, + signature: format!("{}", sign), + }) } - pub fn from_transaction_body(transaction_body: &TransactionBody) -> XResult { - Ok(Self { - body: serde_json::to_string(transaction_body)?, - sender: None, - signature: None, - }) + pub fn from_json(transaction_json: &str) -> XResult { + serde_json::from_str(transaction_json).map_err(|e| e.into()) } 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() { - simple_error!("Transaction is signed!") - } 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) -> XResult<()> { + let public_key = PublicKey::from_str(&self.public_key)?; + let address = make_btc_address(&public_key); + if &address != &self.sender { + return simple_error!("Assress and sender not match!"); } - } - - pub fn verify(&self, pub_key: &PublicKey) -> XResult<()> { - match (&self.sender, &self.signature) { - (None, None) | (None, Some(_)) | (Some(_), None) => { - simple_error!("Transaction has no sender or not signed!") - }, - (Some(sender), Some(sign_hex)) => { - let address = make_btc_address(pub_key); - if &address != sender { - return simple_error!("Assress and sender not match!"); - } - 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()) + let message = get_body_message(&self.body)?; + let sig = Signature::from_str(&self.signature)?; + Secp256k1::new().verify(&message, &sig, &public_key).map_err(|e| e.into()) } } + +fn get_body_message(body: &str) -> XResult { + let digest= calc_sha256(body.as_bytes()); + Message::from_slice(&digest).map_err(|e| e.into()) +} \ No newline at end of file diff --git a/__crypto/simple_contract/src/util.rs b/__crypto/simple_contract/src/util.rs index 041c7fe..598fd63 100644 --- a/__crypto/simple_contract/src/util.rs +++ b/__crypto/simple_contract/src/util.rs @@ -10,6 +10,7 @@ use std::str::FromStr; #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct JsonKeyPair { + pub identity: String, pub pri_key: String, pub pub_key: String, } @@ -17,13 +18,18 @@ pub struct JsonKeyPair { impl JsonKeyPair { pub fn from(pri_key: SecretKey, pub_key: PublicKey) -> Self { JsonKeyPair { + identity: make_btc_address(&pub_key), 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()) + serde_json::to_string_pretty(self).map_err(|e| e.into()) + } + + pub fn from_json(json: &str) -> XResult { + serde_json::from_str(json).map_err(|e| e.into()) } pub fn to_key_pair(&self) -> XResult<(SecretKey, PublicKey)> {