feat: works

This commit is contained in:
2021-01-01 22:16:11 +08:00
parent 601f11efe6
commit 24150ca0f1
16 changed files with 555 additions and 82 deletions

View File

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

View File

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

View File

@@ -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;
}

View File

@@ -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(&parameters)?.into(),
};
let tx = Transaction::new(&transaction_body, &pri_key, &pub_key)?;
information!("Create transaction: {:#?}", tx);
let engine = ContractEngine::new();
engine.execute(&tx)?;
Ok(())
}
}

View File

@@ -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(&parameters)?.into(),
};
let tx = Transaction::new(&transaction_body, &pri_key, &pub_key)?;
information!("Create transaction: {:#?}", tx);
let engine = ContractEngine::new();
engine.execute(&tx)?;
Ok(())
}
}

View File

@@ -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(&parameters)?.into(),
};
let tx = Transaction::new(&transaction_body, &pri_key, &pub_key)?;
information!("Create transaction: {:#?}", tx);
let engine = ContractEngine::new();
engine.execute(&tx)?;
Ok(())
}
}

View File

@@ -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(&parameters)?.into(),
};
let tx = Transaction::new(&transaction_body, &pri_key, &pub_key)?;
information!("Create transaction: {:#?}", tx);
let engine = ContractEngine::new();
engine.execute(&tx)?;
Ok(())
}
}

View File

@@ -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(&parameters)?.into(),
};
let tx = Transaction::new(&transaction_body, &pri_key, &pub_key)?;
information!("Create transaction: {:#?}", tx);
let engine = ContractEngine::new();
engine.execute(&tx)?;
Ok(())
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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, &params.name, params.credit_limit)?;
},
"issue" => {
let params: CreditContractIssueParameters = serde_json::from_str(&tx_body.parameters)?;
let mut c = CreditContract::load(&params.name)?;
c.issue(tx, &params.receiver, params.credit)?;
},
"transfer" => {
let params: CreditContractTransferParameters = serde_json::from_str(&tx_body.parameters)?;
let mut c = CreditContract::load(&params.name)?;
c.transfer(tx, &params.receiver, params.credit)?;
},
"query" => {
let params: CreditContractQueryParameters = serde_json::from_str(&tx_body.parameters)?;
let c = CreditContract::load(&params.name)?;
information!("Query: {}, credit: {}", &params.account, c.query(tx, &params.account));
},
"query_all" => {
let params: CreditContractQueryAllParameters = serde_json::from_str(&tx_body.parameters)?;
let c = CreditContract::load(&params.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(())
}
}

View File

@@ -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<Box<dyn Command>> = 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)
}

View File

@@ -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<String>,
pub signature: Option<String>,
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<Self> {
serde_json::from_str(transaction_json).map_err(|e| e.into())
pub fn new(transaction_body: &TransactionBody, priv_key: &SecretKey, pub_key: &PublicKey) -> XResult<Self> {
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<Self> {
Ok(Self {
body: serde_json::to_string(transaction_body)?,
sender: None,
signature: None,
})
pub fn from_json(transaction_json: &str) -> XResult<Self> {
serde_json::from_str(transaction_json).map_err(|e| e.into())
}
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() {
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, 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 {
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!");
}
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())
}
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(&self) -> XResult<Message> {
let digest= calc_sha256(self.body.as_bytes());
fn get_body_message(body: &str) -> XResult<Message> {
let digest= calc_sha256(body.as_bytes());
Message::from_slice(&digest).map_err(|e| e.into())
}
}

View File

@@ -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<String> {
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<Self> {
serde_json::from_str(json).map_err(|e| e.into())
}
pub fn to_key_pair(&self) -> XResult<(SecretKey, PublicKey)> {