├── .gitignore
├── Cargo.toml
├── README.md
├── Screenshot20220105125717.png
├── merkle_tree.jpg
└── src
├── block.rs
├── blockchain.rs
├── cli.rs
├── errors.rs
├── main.rs
├── server.rs
├── transaction.rs
├── tx.rs
├── utxoset.rs
└── wallets.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | .idea
3 | Cargo.lock
4 | data
5 |
6 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "blockchain-rust"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | sha2 = "0.10.6"
10 | rust-crypto = "^0.2"
11 | bincode = "1.3"
12 | failure = "0.1"
13 | sled = "0.34"
14 | log = "0.4"
15 | env_logger = "0.10.0"
16 | clap = "4.0.29"
17 | bitcoincash-addr = "0.5.2"
18 | rand = "0.8.5"
19 | merkle-cbt = "0.3.2"
20 | serde = {version = "1.0", features = ["derive"] }
21 | serde_json = "1.0"
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | a simple blockchain demo for learning
2 |
3 |
4 | # blockchain-rust -
5 |
6 |
7 | a simple blockchain demo for learning
8 |
9 |
10 | ## usage
11 |
12 | - Create wallet:
13 | ```sh
14 | cargo run createwallet
15 | ```
16 | - Create blockchain:
17 | ```
18 | cargo run create
19 | ```
20 | - send coins (if `-m` is specified, the block will be mined immediately in the same node):
21 | ```
22 | cargo run send -m
23 | ```
24 |
25 |
--------------------------------------------------------------------------------
/Screenshot20220105125717.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/behrouz-rfa/blockchain-rust/75ce1573eb2e4cec393d901fb1cf8a9d41168ca4/Screenshot20220105125717.png
--------------------------------------------------------------------------------
/merkle_tree.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/behrouz-rfa/blockchain-rust/75ce1573eb2e4cec393d901fb1cf8a9d41168ca4/merkle_tree.jpg
--------------------------------------------------------------------------------
/src/block.rs:
--------------------------------------------------------------------------------
1 | //! Block implement of blockchain
2 |
3 | use super::*;
4 | use crate::transaction::Transaction;
5 | use bincode::serialize;
6 | use crypto::digest::Digest;
7 | use crypto::sha2::Sha256;
8 | use merkle_cbt::merkle_tree::Merge;
9 | use merkle_cbt::merkle_tree::CBMT;
10 | use serde::{Deserialize, Serialize};
11 | use std::time::SystemTime;
12 | use log::info;
13 |
14 | const TARGET_HEXS: usize = 4;
15 |
16 | /// Block keeps block headers
17 | #[derive(Serialize, Deserialize, Debug, Clone)]
18 | pub struct Block {
19 | timestamp: u128,
20 | transactions: Vec,
21 | prev_block_hash: String,
22 | hash: String,
23 | nonce: i32,
24 | height: i32,
25 | }
26 |
27 | impl Block {
28 | pub fn get_hash(&self) -> String {
29 | self.hash.clone()
30 | }
31 |
32 | pub fn get_prev_hash(&self) -> String {
33 | self.prev_block_hash.clone()
34 | }
35 |
36 | pub fn get_transaction(&self) -> &Vec {
37 | &self.transactions
38 | }
39 |
40 | pub fn get_height(&self) -> i32 {
41 | self.height
42 | }
43 |
44 | /// NewBlock creates and returns Block
45 | pub fn new_block(
46 | transactions: Vec,
47 | prev_block_hash: String,
48 | height: i32,
49 | ) -> Result {
50 | let timestamp = SystemTime::now()
51 | .duration_since(SystemTime::UNIX_EPOCH)?
52 | .as_millis();
53 | let mut block = Block {
54 | timestamp,
55 | transactions,
56 | prev_block_hash,
57 | hash: String::new(),
58 | nonce: 0,
59 | height,
60 | };
61 | block.run_proof_of_work()?;
62 | Ok(block)
63 | }
64 |
65 | /// NewGenesisBlock creates and returns genesis Block
66 | pub fn new_genesis_block(coinbase: Transaction) -> Block {
67 | Block::new_block(vec![coinbase], String::new(), 0).unwrap()
68 | }
69 |
70 | /// Run performs a proof-of-work
71 | fn run_proof_of_work(&mut self) -> Result<()> {
72 | info!("Mining the block");
73 | while !self.validate()? {
74 | self.nonce += 1;
75 | }
76 | let data = self.prepare_hash_data()?;
77 | let mut hasher = Sha256::new();
78 | hasher.input(&data[..]);
79 | self.hash = hasher.result_str();
80 | Ok(())
81 | }
82 |
83 | /// HashTransactions returns a hash of the transactions in the block
84 | fn hash_transactions(&self) -> Result> {
85 | let mut transactions = Vec::new();
86 | for tx in &self.transactions {
87 | transactions.push(tx.hash()?.as_bytes().to_owned());
88 | }
89 | let tree = CBMT::, MergeVu8>::build_merkle_tree(&transactions);
90 |
91 | Ok(tree.root())
92 | }
93 |
94 | fn prepare_hash_data(&self) -> Result> {
95 | let content = (
96 | self.prev_block_hash.clone(),
97 | self.hash_transactions()?,
98 | self.timestamp,
99 | TARGET_HEXS,
100 | self.nonce,
101 | );
102 | let bytes = serialize(&content)?;
103 | Ok(bytes)
104 | }
105 |
106 | /// Validate validates block's PoW
107 | fn validate(&self) -> Result {
108 | let data = self.prepare_hash_data()?;
109 | let mut hasher = Sha256::new();
110 | hasher.input(&data[..]);
111 | let mut vec1: Vec = Vec::new();
112 | vec1.resize(TARGET_HEXS, '0' as u8);
113 | Ok(&hasher.result_str()[0..TARGET_HEXS] == String::from_utf8(vec1)?)
114 | }
115 | }
116 |
117 | struct MergeVu8 {}
118 |
119 | impl Merge for MergeVu8 {
120 | type Item = Vec;
121 | fn merge(left: &Self::Item, right: &Self::Item) -> Self::Item {
122 | let mut hasher = Sha256::new();
123 | let mut data: Vec = left.clone();
124 | data.append(&mut right.clone());
125 | hasher.input(&data);
126 | let mut re: [u8; 32] = [0; 32];
127 | hasher.result(&mut re);
128 | re.to_vec()
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/blockchain.rs:
--------------------------------------------------------------------------------
1 | //! Blockchain
2 |
3 | use super::*;
4 | use crate::block::*;
5 | use crate::transaction::*;
6 | use bincode::{deserialize, serialize};
7 | use failure::format_err;
8 | use sled;
9 | use std::collections::HashMap;
10 | use log::{debug, info};
11 |
12 | const GENESIS_COINBASE_DATA: &str =
13 | "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks";
14 |
15 | /// Blockchain implements interactions with a DB
16 | #[derive(Debug)]
17 | pub struct Blockchain {
18 | pub tip: String,
19 | pub db: sled::Db,
20 | }
21 |
22 | /// BlockchainIterator is used to iterate over blockchain blocks
23 | pub struct BlockchainIterator<'a> {
24 | current_hash: String,
25 | bc: &'a Blockchain,
26 | }
27 |
28 | impl Blockchain {
29 | /// NewBlockchain creates a new Blockchain db
30 | pub fn new() -> Result {
31 | info!("open blockchain");
32 |
33 | let db = sled::open("data/blocks")?;
34 | let hash = match db.get("LAST")? {
35 | Some(l) => l.to_vec(),
36 | None => Vec::new(),
37 | };
38 | info!("Found block database");
39 | let lasthash = if hash.is_empty() {
40 | String::new()
41 | } else {
42 | String::from_utf8(hash.to_vec())?
43 | };
44 | Ok(Blockchain { tip: lasthash, db })
45 | }
46 |
47 | /// CreateBlockchain creates a new blockchain DB
48 | pub fn create_blockchain(address: String) -> Result {
49 | info!("Creating new blockchain");
50 |
51 | std::fs::remove_dir_all("data/blocks").ok();
52 | let db = sled::open("data/blocks")?;
53 | debug!("Creating new block database");
54 | let cbtx = Transaction::new_coinbase(address, String::from(GENESIS_COINBASE_DATA))?;
55 | let genesis: Block = Block::new_genesis_block(cbtx);
56 | db.insert(genesis.get_hash(), serialize(&genesis)?)?;
57 | db.insert("LAST", genesis.get_hash().as_bytes())?;
58 | let bc = Blockchain {
59 | tip: genesis.get_hash(),
60 | db,
61 | };
62 | bc.db.flush()?;
63 | Ok(bc)
64 | }
65 |
66 | /// MineBlock mines a new block with the provided transactions
67 | pub fn mine_block(&mut self, transactions: Vec) -> Result {
68 | info!("mine a new block");
69 |
70 | for tx in &transactions {
71 | if !self.verify_transacton(tx)? {
72 | return Err(format_err!("ERROR: Invalid transaction"));
73 | }
74 | }
75 |
76 | let lasthash = self.db.get("LAST")?.unwrap();
77 |
78 | let newblock = Block::new_block(
79 | transactions,
80 | String::from_utf8(lasthash.to_vec())?,
81 | self.get_best_height()? + 1,
82 | )?;
83 | self.db.insert(newblock.get_hash(), serialize(&newblock)?)?;
84 | self.db.insert("LAST", newblock.get_hash().as_bytes())?;
85 | self.db.flush()?;
86 |
87 | self.tip = newblock.get_hash();
88 | Ok(newblock)
89 | }
90 |
91 | /// Iterator returns a BlockchainIterat
92 | pub fn iter(&self) -> BlockchainIterator {
93 | BlockchainIterator {
94 | current_hash: self.tip.clone(),
95 | bc: &self,
96 | }
97 | }
98 |
99 | /// FindUTXO finds and returns all unspent transaction outputs
100 | pub fn find_UTXO(&self) -> HashMap {
101 | let mut utxos: HashMap = HashMap::new();
102 | let mut spend_txos: HashMap> = HashMap::new();
103 |
104 | for block in self.iter() {
105 | for tx in block.get_transaction() {
106 | for index in 0..tx.vout.len() {
107 | if let Some(ids) = spend_txos.get(&tx.id) {
108 | if ids.contains(&(index as i32)) {
109 | continue;
110 | }
111 | }
112 |
113 | match utxos.get_mut(&tx.id) {
114 | Some(v) => {
115 | v.outputs.push(tx.vout[index].clone());
116 | }
117 | None => {
118 | utxos.insert(
119 | tx.id.clone(),
120 | TXOutputs {
121 | outputs: vec![tx.vout[index].clone()],
122 | },
123 | );
124 | }
125 | }
126 | }
127 |
128 | if !tx.is_coinbase() {
129 | for i in &tx.vin {
130 | match spend_txos.get_mut(&i.txid) {
131 | Some(v) => {
132 | v.push(i.vout);
133 | }
134 | None => {
135 | spend_txos.insert(i.txid.clone(), vec![i.vout]);
136 | }
137 | }
138 | }
139 | }
140 | }
141 | }
142 |
143 | utxos
144 | }
145 |
146 | /// FindTransaction finds a transaction by its ID
147 | pub fn find_transacton(&self, id: &str) -> Result {
148 | for b in self.iter() {
149 | for tx in b.get_transaction() {
150 | if tx.id == id {
151 | return Ok(tx.clone());
152 | }
153 | }
154 | }
155 | Err(format_err!("Transaction is not found"))
156 | }
157 |
158 | fn get_prev_TXs(&self, tx: &Transaction) -> Result> {
159 | let mut prev_TXs = HashMap::new();
160 | for vin in &tx.vin {
161 | let prev_TX = self.find_transacton(&vin.txid)?;
162 | prev_TXs.insert(prev_TX.id.clone(), prev_TX);
163 | }
164 | Ok(prev_TXs)
165 | }
166 |
167 | /// SignTransaction signs inputs of a Transaction
168 | pub fn sign_transacton(&self, tx: &mut Transaction, private_key: &[u8]) -> Result<()> {
169 | let prev_TXs = self.get_prev_TXs(tx)?;
170 | tx.sign(private_key, prev_TXs)?;
171 | Ok(())
172 | }
173 |
174 | /// VerifyTransaction verifies transaction input signatures
175 | pub fn verify_transacton(&self, tx: &Transaction) -> Result {
176 | if tx.is_coinbase() {
177 | return Ok(true);
178 | }
179 | let prev_TXs = self.get_prev_TXs(tx)?;
180 | tx.verify(prev_TXs)
181 | }
182 |
183 | /// AddBlock saves the block into the blockchain
184 | pub fn add_block(&mut self, block: Block) -> Result<()> {
185 | let data = serialize(&block)?;
186 | if let Some(_) = self.db.get(block.get_hash())? {
187 | return Ok(());
188 | }
189 | self.db.insert(block.get_hash(), data)?;
190 |
191 | let lastheight = self.get_best_height()?;
192 | if block.get_height() > lastheight {
193 | self.db.insert("LAST", block.get_hash().as_bytes())?;
194 | self.tip = block.get_hash();
195 | self.db.flush()?;
196 | }
197 | Ok(())
198 | }
199 |
200 | // GetBlock finds a block by its hash and returns it
201 | pub fn get_block(&self, block_hash: &str) -> Result {
202 | let data = self.db.get(block_hash)?.unwrap();
203 | let block = deserialize(&data.to_vec())?;
204 | Ok(block)
205 | }
206 |
207 | /// GetBestHeight returns the height of the latest block
208 | pub fn get_best_height(&self) -> Result {
209 | let lasthash = if let Some(h) = self.db.get("LAST")? {
210 | h
211 | } else {
212 | return Ok(-1);
213 | };
214 | let last_data = self.db.get(lasthash)?.unwrap();
215 | let last_block: Block = deserialize(&last_data.to_vec())?;
216 | Ok(last_block.get_height())
217 | }
218 |
219 | /// GetBlockHashes returns a list of hashes of all the blocks in the chain
220 | pub fn get_block_hashs(&self) -> Vec {
221 | let mut list = Vec::new();
222 | for b in self.iter() {
223 | list.push(b.get_hash());
224 | }
225 | list
226 | }
227 | }
228 |
229 | impl<'a> Iterator for BlockchainIterator<'a> {
230 | type Item = Block;
231 |
232 | fn next(&mut self) -> Option {
233 | if let Ok(encoded_block) = self.bc.db.get(&self.current_hash) {
234 | return match encoded_block {
235 | Some(b) => {
236 | if let Ok(block) = deserialize::(&b) {
237 | self.current_hash = block.get_prev_hash();
238 | Some(block)
239 | } else {
240 | None
241 | }
242 | }
243 | None => None,
244 | };
245 | }
246 | None
247 | }
248 | }
249 |
--------------------------------------------------------------------------------
/src/cli.rs:
--------------------------------------------------------------------------------
1 | use std::process::exit;
2 | use bitcoincash_addr::Address;
3 | use clap::{arg, Command};
4 | use crate::blockchain::Blockchain;
5 | use crate::errors::Result;
6 | use crate::server::Server;
7 | use crate::transaction::Transaction;
8 | use crate::utxoset::UTXOSet;
9 | use crate::wallets::{Wallet, Wallets};
10 |
11 | pub struct Cli {}
12 |
13 | impl Cli {
14 | pub fn new() -> Result {
15 | Ok(Cli {})
16 | }
17 | pub fn run(&mut self) -> Result<()> {
18 | let matches = Command::new("blockchain-rust-demo")
19 | .version("0.1")
20 | .author("behrouz.r.fa@gmail.com")
21 | .about("blockchain in rust: a simple blockchain for learning")
22 | .subcommand(Command::new("printchain").about("print all the chain blocks"))
23 | .subcommand(Command::new("createwallet").about("create a wallet"))
24 | .subcommand(Command::new("listaddresses").about("list all addresses"))
25 | .subcommand(Command::new("reindex").about("reindex UTXO"))
26 | .subcommand(Command::new("getbalance")
27 | .about("get balance in the blochain")
28 | .arg(arg!("'The Address it get balance for'"))
29 | ).subcommand(Command::new("startnode")
30 | .about("start the node server")
31 | .arg(arg!("'the port server bind to locally'"))
32 | )
33 | .subcommand(Command::new("create").about("Create new blochain")
34 | .arg(arg!("'The address to send gensis block reqward to' "))
35 | )
36 |
37 | .subcommand(
38 | Command::new("send")
39 | .about("send in the blockchain")
40 | .arg(arg!(" 'Source wallet address'"))
41 | .arg(arg!(" 'Destination wallet address'"))
42 | .arg(arg!(" 'Destination wallet address'"))
43 | .arg(arg!(-m --mine " 'the from address mine immediately'")),
44 | )
45 | .subcommand(
46 | Command::new("startminer")
47 | .about("start the minner server")
48 | .arg(arg!(" 'the port server bind to locally'"))
49 | .arg(arg!(" 'wallet address'")),
50 |
51 | )
52 | .get_matches();
53 |
54 | if let Some(ref matches) = matches.subcommand_matches("startminer") {
55 | let port = if let Some(port) = matches.get_one::("PORT") {
56 | port
57 | } else {
58 | println!("PORT not supply!: usage");
59 | exit(1)
60 | };
61 |
62 | let address = if let Some(address) = matches.get_one::("ADDRESS") {
63 | address
64 | } else {
65 | println!("ADDRESS not supply!: usage");
66 | exit(1)
67 | };
68 | let bc = Blockchain::new()?;
69 | let utxo_set = UTXOSet { blockchain: bc };
70 | let server = Server::new(port, address, utxo_set)?;
71 | server.start_server()?;
72 | }
73 |
74 |
75 | if let Some(ref matches) = matches.subcommand_matches("startnode") {
76 | if let Some(port) = matches.get_one::("PORT") {
77 | let bc = Blockchain::new()?;
78 | let utxo_set = UTXOSet { blockchain: bc };
79 | let server = Server::new(port, "", utxo_set)?;
80 | server.start_server()?;
81 | }
82 | }
83 |
84 | if let Some(_) = matches.subcommand_matches("createwallet") {
85 | println!("address: {}", cmd_create_wallet()?);
86 | }
87 | if let Some(_) = matches.subcommand_matches("reindex") {
88 | let count = cmd_reindex()?;
89 | println!("Done! There are {} transactions in the UTXO set.", count);
90 | }
91 |
92 | if let Some(_) = matches.subcommand_matches("listaddresses") {
93 | cmd_list_address()?;
94 | }
95 |
96 | if let Some(ref matches) = matches.subcommand_matches("create") {
97 | if let Some(address) = matches.get_one::("ADDRESS") {
98 | cmd_create_blockchain(address)?;
99 | }
100 |
101 | }
102 |
103 |
104 | if let Some(ref matches) = matches.subcommand_matches("getbalance") {
105 | if let Some(address) = matches.get_one::("ADDRESS") {
106 | let balance = cmd_get_balance(address)?;
107 | println!("Balance: {}\n", balance);
108 | }
109 | }
110 |
111 | if let Some(ref matches) = matches.subcommand_matches("send") {
112 | let from = if let Some(address) = matches.get_one::("FROM") {
113 | address
114 | } else {
115 | println!("from not supply!: usage");
116 | exit(1)
117 | };
118 |
119 | let to = if let Some(address) = matches.get_one::("TO") {
120 | address
121 | } else {
122 | println!("from not supply!: usage");
123 | exit(1)
124 | };
125 |
126 | let amount: i32 = if let Some(amount) = matches.get_one::("AMOUNT") {
127 | amount.parse()?
128 | } else {
129 | println!("from not supply!: usage");
130 | exit(1)
131 | };
132 |
133 | if matches.contains_id("mine") {
134 | cmd_send(from, to, amount, true)?;
135 | } else {
136 | cmd_send(from, to, amount, false)?;
137 | }
138 |
139 |
140 | /*else {
141 | println!("Not printing testing lists...");
142 | }*/
143 | }
144 |
145 | if let Some(_) = matches.subcommand_matches("printchain") {
146 | cmd_print_chain()?;
147 | }
148 |
149 | Ok(())
150 | }
151 | }
152 |
153 | fn cmd_send(from: &str, to: &str, amount: i32, mine_now: bool) -> Result<()> {
154 | let bc = Blockchain::new()?;
155 | let mut utxo_set = UTXOSet { blockchain: bc };
156 | let wallets = Wallets::new()?;
157 | let wallet = wallets.get_wallet(from).unwrap();
158 | let tx = Transaction::new_UTXO(wallet, to, amount, &utxo_set)?;
159 | if mine_now {
160 | let cbtx = Transaction::new_coinbase(from.to_string(), String::from("reward!"))?;
161 | let new_block = utxo_set.blockchain.mine_block(vec![cbtx, tx])?;
162 |
163 | utxo_set.update(&new_block)?;
164 | } else {
165 | Server::send_transaction(&tx, utxo_set)?;
166 | }
167 |
168 | println!("success!");
169 | Ok(())
170 | }
171 |
172 | fn cmd_create_wallet() -> Result {
173 | let mut ws = Wallets::new()?;
174 | let address = ws.create_wallet();
175 | ws.save_all()?;
176 | Ok(address)
177 | }
178 |
179 | fn cmd_reindex() -> Result {
180 | let bc = Blockchain::new()?;
181 | let utxo_set = UTXOSet { blockchain: bc };
182 | utxo_set.reindex()?;
183 | utxo_set.count_transactions()
184 | }
185 |
186 | fn cmd_create_blockchain(address: &str) -> Result<()> {
187 | let address = String::from(address);
188 | let bc = Blockchain::create_blockchain(address)?;
189 |
190 | let utxo_set = UTXOSet { blockchain: bc };
191 | utxo_set.reindex()?;
192 | println!("create blockchain");
193 | Ok(())
194 | }
195 |
196 | fn cmd_get_balance(address: &str) -> Result {
197 | let pub_key_hash = Address::decode(address).unwrap().body;
198 | let bc = Blockchain::new()?;
199 | let utxo_set = UTXOSet { blockchain: bc };
200 | let utxos = utxo_set.find_UTXO(&pub_key_hash)?;
201 |
202 | let mut balance = 0;
203 | for out in utxos.outputs {
204 | balance += out.value;
205 | }
206 | Ok(balance)
207 | }
208 |
209 | fn cmd_print_chain() -> Result<()> {
210 | let bc = Blockchain::new()?;
211 | for b in bc.iter() {
212 | println!("{:#?}", b);
213 | }
214 | Ok(())
215 | }
216 |
217 | fn cmd_list_address() -> Result<()> {
218 | let ws = Wallets::new()?;
219 | let addresses = ws.get_all_addresses();
220 | println!("addresses: ");
221 | for ad in addresses {
222 | println!("{}", ad);
223 | }
224 | Ok(())
225 | }
--------------------------------------------------------------------------------
/src/errors.rs:
--------------------------------------------------------------------------------
1 |
2 |
3 | pub type Result = std::result::Result;
4 |
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | use crate::cli::Cli;
2 | use crate::errors::Result;
3 |
4 | mod block;
5 |
6 | mod errors;
7 | mod blockchain;
8 | mod cli;
9 | mod transaction;
10 | mod wallets;
11 | mod tx;
12 | mod utxoset;
13 | mod server;
14 |
15 |
16 | fn main()->Result<()> {
17 | let mut cli = Cli::new()?;
18 | cli.run()?;
19 |
20 | Ok(())
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/server.rs:
--------------------------------------------------------------------------------
1 | //! server of Blockchain
2 |
3 | use super::*;
4 | use crate::block::*;
5 | use crate::transaction::*;
6 | use crate::utxoset::*;
7 | use bincode::{deserialize, serialize};
8 | use failure::format_err;
9 | use serde::{Deserialize, Serialize};
10 | use std::collections::{HashMap, HashSet};
11 | use std::io::prelude::*;
12 | use std::net::{TcpListener, TcpStream};
13 | use std::sync::*;
14 | use std::thread;
15 | use std::time::Duration;
16 | use log::{debug, info};
17 |
18 | #[derive(Serialize, Deserialize, Debug, Clone)]
19 | enum Message {
20 | Addr(Vec),
21 | Version(Versionmsg),
22 | Tx(Txmsg),
23 | GetData(GetDatamsg),
24 | GetBlock(GetBlocksmsg),
25 | Inv(Invmsg),
26 | Block(Blockmsg),
27 | }
28 |
29 | #[derive(Serialize, Deserialize, Debug, Clone)]
30 | struct Blockmsg {
31 | addr_from: String,
32 | block: Block,
33 | }
34 |
35 | #[derive(Serialize, Deserialize, Debug, Clone)]
36 | struct GetBlocksmsg {
37 | addr_from: String,
38 | }
39 |
40 | #[derive(Serialize, Deserialize, Debug, Clone)]
41 | struct GetDatamsg {
42 | addr_from: String,
43 | kind: String,
44 | id: String,
45 | }
46 |
47 | #[derive(Serialize, Deserialize, Debug, Clone)]
48 | struct Invmsg {
49 | addr_from: String,
50 | kind: String,
51 | items: Vec,
52 | }
53 |
54 | #[derive(Serialize, Deserialize, Debug, Clone)]
55 | struct Txmsg {
56 | addr_from: String,
57 | transaction: Transaction,
58 | }
59 |
60 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
61 | struct Versionmsg {
62 | addr_from: String,
63 | version: i32,
64 | best_height: i32,
65 | }
66 |
67 | pub struct Server {
68 | node_address: String,
69 | mining_address: String,
70 | inner: Arc>,
71 | }
72 |
73 | struct ServerInner {
74 | known_nodes: HashSet,
75 | utxo: UTXOSet,
76 | blocks_in_transit: Vec,
77 | mempool: HashMap,
78 | }
79 |
80 | const KNOWN_NODE1: &str = "localhost:3000";
81 | const CMD_LEN: usize = 12;
82 | const VERSION: i32 = 1;
83 |
84 | impl Server {
85 | pub fn new(port: &str, miner_address: &str, utxo: UTXOSet) -> Result {
86 | let mut node_set = HashSet::new();
87 | node_set.insert(String::from(KNOWN_NODE1));
88 | Ok(Server {
89 | node_address: String::from("localhost:") + port,
90 | mining_address: miner_address.to_string(),
91 | inner: Arc::new(Mutex::new(ServerInner {
92 | known_nodes: node_set,
93 | utxo,
94 | blocks_in_transit: Vec::new(),
95 | mempool: HashMap::new(),
96 | })),
97 | })
98 | }
99 |
100 | pub fn start_server(&self) -> Result<()> {
101 | let server1 = Server {
102 | node_address: self.node_address.clone(),
103 | mining_address: self.mining_address.clone(),
104 | inner: Arc::clone(&self.inner),
105 | };
106 | info!(
107 | "Start server at {}, minning address: {}",
108 | &self.node_address, &self.mining_address
109 | );
110 |
111 | thread::spawn(move || {
112 | thread::sleep(Duration::from_millis(1000));
113 | if server1.get_best_height()? == -1 {
114 | server1.request_blocks()
115 | } else {
116 | server1.send_version(KNOWN_NODE1)
117 | }
118 | });
119 |
120 | let listener = TcpListener::bind(&self.node_address).unwrap();
121 | info!("Server listen...");
122 |
123 | for stream in listener.incoming() {
124 | let stream = stream?;
125 | let server1 = Server {
126 | node_address: self.node_address.clone(),
127 | mining_address: self.mining_address.clone(),
128 | inner: Arc::clone(&self.inner),
129 | };
130 | thread::spawn(move || server1.handle_connection(stream));
131 | }
132 |
133 | Ok(())
134 | }
135 |
136 | pub fn send_transaction(tx: &Transaction, utxoset: UTXOSet) -> Result<()> {
137 | let server = Server::new("7000", "", utxoset)?;
138 | server.send_tx(KNOWN_NODE1, tx)?;
139 | Ok(())
140 | }
141 |
142 | /* ------------------- inner halp functions ----------------------------------*/
143 |
144 | fn remove_node(&self, addr: &str) {
145 | self.inner.lock().unwrap().known_nodes.remove(addr);
146 | }
147 |
148 | fn add_nodes(&self, addr: &str) {
149 | self.inner
150 | .lock()
151 | .unwrap()
152 | .known_nodes
153 | .insert(String::from(addr));
154 | }
155 |
156 | fn get_known_nodes(&self) -> HashSet {
157 | self.inner.lock().unwrap().known_nodes.clone()
158 | }
159 |
160 | fn node_is_known(&self, addr: &str) -> bool {
161 | self.inner.lock().unwrap().known_nodes.get(addr).is_some()
162 | }
163 |
164 | fn replace_in_transit(&self, hashs: Vec) {
165 | let bit = &mut self.inner.lock().unwrap().blocks_in_transit;
166 | bit.clone_from(&hashs);
167 | }
168 |
169 | fn get_in_transit(&self) -> Vec {
170 | self.inner.lock().unwrap().blocks_in_transit.clone()
171 | }
172 |
173 | fn get_mempool_tx(&self, addr: &str) -> Option {
174 | match self.inner.lock().unwrap().mempool.get(addr) {
175 | Some(tx) => Some(tx.clone()),
176 | None => None,
177 | }
178 | }
179 |
180 | fn get_mempool(&self) -> HashMap {
181 | self.inner.lock().unwrap().mempool.clone()
182 | }
183 |
184 | fn insert_mempool(&self, tx: Transaction) {
185 | self.inner.lock().unwrap().mempool.insert(tx.id.clone(), tx);
186 | }
187 |
188 | fn clear_mempool(&self) {
189 | self.inner.lock().unwrap().mempool.clear()
190 | }
191 |
192 | fn get_best_height(&self) -> Result {
193 | self.inner.lock().unwrap().utxo.blockchain.get_best_height()
194 | }
195 |
196 | fn get_block_hashs(&self) -> Vec {
197 | self.inner.lock().unwrap().utxo.blockchain.get_block_hashs()
198 | }
199 |
200 | fn get_block(&self, block_hash: &str) -> Result {
201 | self.inner
202 | .lock()
203 | .unwrap()
204 | .utxo
205 | .blockchain
206 | .get_block(block_hash)
207 | }
208 |
209 | fn verify_tx(&self, tx: &Transaction) -> Result {
210 | self.inner
211 | .lock()
212 | .unwrap()
213 | .utxo
214 | .blockchain
215 | .verify_transacton(tx)
216 | }
217 |
218 | fn add_block(&self, block: Block) -> Result<()> {
219 | self.inner.lock().unwrap().utxo.blockchain.add_block(block)
220 | }
221 |
222 | fn mine_block(&self, txs: Vec) -> Result {
223 | self.inner.lock().unwrap().utxo.blockchain.mine_block(txs)
224 | }
225 |
226 | fn utxo_reindex(&self) -> Result<()> {
227 | self.inner.lock().unwrap().utxo.reindex()
228 | }
229 |
230 | /* -----------------------------------------------------*/
231 |
232 | fn send_data(&self, addr: &str, data: &[u8]) -> Result<()> {
233 | if addr == &self.node_address {
234 | return Ok(());
235 | }
236 | let mut stream = match TcpStream::connect(addr) {
237 | Ok(s) => s,
238 | Err(_) => {
239 | self.remove_node(addr);
240 | return Ok(());
241 | }
242 | };
243 |
244 | stream.write(data)?;
245 |
246 | info!("data send successfully");
247 | Ok(())
248 | }
249 |
250 | fn request_blocks(&self) -> Result<()> {
251 | for node in self.get_known_nodes() {
252 | self.send_get_blocks(&node)?
253 | }
254 | Ok(())
255 | }
256 |
257 | fn send_block(&self, addr: &str, b: &Block) -> Result<()> {
258 | info!("send block data to: {} block hash: {}", addr, b.get_hash());
259 | let data = Blockmsg {
260 | addr_from: self.node_address.clone(),
261 | block: b.clone(),
262 | };
263 | let data = serialize(&(cmd_to_bytes("block"), data))?;
264 | self.send_data(addr, &data)
265 | }
266 |
267 | fn send_addr(&self, addr: &str) -> Result<()> {
268 | info!("send address info to: {}", addr);
269 | let nodes = self.get_known_nodes();
270 | let data = serialize(&(cmd_to_bytes("addr"), nodes))?;
271 | self.send_data(addr, &data)
272 | }
273 |
274 | fn send_inv(&self, addr: &str, kind: &str, items: Vec) -> Result<()> {
275 | info!(
276 | "send inv message to: {} kind: {} data: {:?}",
277 | addr, kind, items
278 | );
279 | let data = Invmsg {
280 | addr_from: self.node_address.clone(),
281 | kind: kind.to_string(),
282 | items,
283 | };
284 | let data = serialize(&(cmd_to_bytes("inv"), data))?;
285 | self.send_data(addr, &data)
286 | }
287 |
288 | fn send_get_blocks(&self, addr: &str) -> Result<()> {
289 | info!("send get blocks message to: {}", addr);
290 | let data = GetBlocksmsg {
291 | addr_from: self.node_address.clone(),
292 | };
293 | let data = serialize(&(cmd_to_bytes("getblocks"), data))?;
294 | self.send_data(addr, &data)
295 | }
296 |
297 | fn send_get_data(&self, addr: &str, kind: &str, id: &str) -> Result<()> {
298 | info!(
299 | "send get data message to: {} kind: {} id: {}",
300 | addr, kind, id
301 | );
302 | let data = GetDatamsg {
303 | addr_from: self.node_address.clone(),
304 | kind: kind.to_string(),
305 | id: id.to_string(),
306 | };
307 | let data = serialize(&(cmd_to_bytes("getdata"), data))?;
308 | self.send_data(addr, &data)
309 | }
310 |
311 | pub fn send_tx(&self, addr: &str, tx: &Transaction) -> Result<()> {
312 | info!("send tx to: {} txid: {}", addr, &tx.id);
313 | let data = Txmsg {
314 | addr_from: self.node_address.clone(),
315 | transaction: tx.clone(),
316 | };
317 | let data = serialize(&(cmd_to_bytes("tx"), data))?;
318 | self.send_data(addr, &data)
319 | }
320 |
321 | fn send_version(&self, addr: &str) -> Result<()> {
322 | info!("send version info to: {}", addr);
323 | let data = Versionmsg {
324 | addr_from: self.node_address.clone(),
325 | best_height: self.get_best_height()?,
326 | version: VERSION,
327 | };
328 | let data = serialize(&(cmd_to_bytes("version"), data))?;
329 | self.send_data(addr, &data)
330 | }
331 |
332 | fn handle_version(&self, msg: Versionmsg) -> Result<()> {
333 | info!("receive version msg: {:#?}", msg);
334 | let my_best_height = self.get_best_height()?;
335 | if my_best_height < msg.best_height {
336 | self.send_get_blocks(&msg.addr_from)?;
337 | } else if my_best_height > msg.best_height {
338 | self.send_version(&msg.addr_from)?;
339 | }
340 |
341 | self.send_addr(&msg.addr_from)?;
342 |
343 | if !self.node_is_known(&msg.addr_from) {
344 | self.add_nodes(&msg.addr_from);
345 | }
346 | Ok(())
347 | }
348 |
349 | fn handle_addr(&self, msg: Vec) -> Result<()> {
350 | info!("receive address msg: {:#?}", msg);
351 | for node in msg {
352 | self.add_nodes(&node);
353 | }
354 | //self.request_blocks()?;
355 | Ok(())
356 | }
357 |
358 | fn handle_block(&self, msg: Blockmsg) -> Result<()> {
359 | info!(
360 | "receive block msg: {}, {}",
361 | msg.addr_from,
362 | msg.block.get_hash()
363 | );
364 | self.add_block(msg.block)?;
365 |
366 | let mut in_transit = self.get_in_transit();
367 | if in_transit.len() > 0 {
368 | let block_hash = &in_transit[0];
369 | self.send_get_data(&msg.addr_from, "block", block_hash)?;
370 | in_transit.remove(0);
371 | self.replace_in_transit(in_transit);
372 | } else {
373 | self.utxo_reindex()?;
374 | }
375 |
376 | Ok(())
377 | }
378 |
379 | fn handle_inv(&self, msg: Invmsg) -> Result<()> {
380 | info!("receive inv msg: {:#?}", msg);
381 | if msg.kind == "block" {
382 | let block_hash = &msg.items[0];
383 | self.send_get_data(&msg.addr_from, "block", block_hash)?;
384 |
385 | let mut new_in_transit = Vec::new();
386 | for b in &msg.items {
387 | if b != block_hash {
388 | new_in_transit.push(b.clone());
389 | }
390 | }
391 | self.replace_in_transit(new_in_transit);
392 | } else if msg.kind == "tx" {
393 | let txid = &msg.items[0];
394 | match self.get_mempool_tx(txid) {
395 | Some(tx) => {
396 | if tx.id.is_empty() {
397 | self.send_get_data(&msg.addr_from, "tx", txid)?
398 | }
399 | }
400 | None => self.send_get_data(&msg.addr_from, "tx", txid)?,
401 | }
402 | }
403 | Ok(())
404 | }
405 |
406 | fn handle_get_blocks(&self, msg: GetBlocksmsg) -> Result<()> {
407 | info!("receive get blocks msg: {:#?}", msg);
408 | let block_hashs = self.get_block_hashs();
409 | self.send_inv(&msg.addr_from, "block", block_hashs)?;
410 | Ok(())
411 | }
412 |
413 | fn handle_get_data(&self, msg: GetDatamsg) -> Result<()> {
414 | info!("receive get data msg: {:#?}", msg);
415 | if msg.kind == "block" {
416 | let block = self.get_block(&msg.id)?;
417 | self.send_block(&msg.addr_from, &block)?;
418 | } else if msg.kind == "tx" {
419 | let tx = self.get_mempool_tx(&msg.id).unwrap();
420 | self.send_tx(&msg.addr_from, &tx)?;
421 | }
422 | Ok(())
423 | }
424 |
425 | fn handle_tx(&self, msg: Txmsg) -> Result<()> {
426 | info!("receive tx msg: {} {}", msg.addr_from, &msg.transaction.id);
427 | self.insert_mempool(msg.transaction.clone());
428 |
429 | let known_nodes = self.get_known_nodes();
430 | if self.node_address == KNOWN_NODE1 {
431 | for node in known_nodes {
432 | if node != self.node_address && node != msg.addr_from {
433 | self.send_inv(&node, "tx", vec![msg.transaction.id.clone()])?;
434 | }
435 | }
436 | } else {
437 | let mut mempool = self.get_mempool();
438 | debug!("Current mempool: {:#?}", &mempool);
439 | if mempool.len() >= 1 && !self.mining_address.is_empty() {
440 | loop {
441 | let mut txs = Vec::new();
442 |
443 | for (_, tx) in &mempool {
444 | if self.verify_tx(tx)? {
445 | txs.push(tx.clone());
446 | }
447 | }
448 |
449 | if txs.is_empty() {
450 | return Ok(());
451 | }
452 |
453 | let cbtx =
454 | Transaction::new_coinbase(self.mining_address.clone(), String::new())?;
455 | txs.push(cbtx);
456 |
457 | for tx in &txs {
458 | mempool.remove(&tx.id);
459 | }
460 |
461 | let new_block = self.mine_block(txs)?;
462 | self.utxo_reindex()?;
463 |
464 | for node in self.get_known_nodes() {
465 | if node != self.node_address {
466 | self.send_inv(&node, "block", vec![new_block.get_hash()])?;
467 | }
468 | }
469 |
470 | if mempool.len() == 0 {
471 | break;
472 | }
473 | }
474 | self.clear_mempool();
475 | }
476 | }
477 |
478 | Ok(())
479 | }
480 |
481 | fn handle_connection(&self, mut stream: TcpStream) -> Result<()> {
482 | let mut buffer = Vec::new();
483 | let count = stream.read_to_end(&mut buffer)?;
484 | info!("Accept request: length {}", count);
485 |
486 | let cmd = bytes_to_cmd(&buffer)?;
487 |
488 | match cmd {
489 | Message::Addr(data) => self.handle_addr(data)?,
490 | Message::Block(data) => self.handle_block(data)?,
491 | Message::Inv(data) => self.handle_inv(data)?,
492 | Message::GetBlock(data) => self.handle_get_blocks(data)?,
493 | Message::GetData(data) => self.handle_get_data(data)?,
494 | Message::Tx(data) => self.handle_tx(data)?,
495 | Message::Version(data) => self.handle_version(data)?,
496 | }
497 |
498 | Ok(())
499 | }
500 | }
501 |
502 | fn cmd_to_bytes(cmd: &str) -> [u8; CMD_LEN] {
503 | let mut data = [0; CMD_LEN];
504 | for (i, d) in cmd.as_bytes().iter().enumerate() {
505 | data[i] = *d;
506 | }
507 | data
508 | }
509 |
510 | fn bytes_to_cmd(bytes: &[u8]) -> Result {
511 | let mut cmd = Vec::new();
512 | let cmd_bytes = &bytes[..CMD_LEN];
513 | let data = &bytes[CMD_LEN..];
514 | for b in cmd_bytes {
515 | if 0 as u8 != *b {
516 | cmd.push(*b);
517 | }
518 | }
519 | info!("cmd: {}", String::from_utf8(cmd.clone())?);
520 |
521 | if cmd == "addr".as_bytes() {
522 | let data: Vec = deserialize(data)?;
523 | Ok(Message::Addr(data))
524 | } else if cmd == "block".as_bytes() {
525 | let data: Blockmsg = deserialize(data)?;
526 | Ok(Message::Block(data))
527 | } else if cmd == "inv".as_bytes() {
528 | let data: Invmsg = deserialize(data)?;
529 | Ok(Message::Inv(data))
530 | } else if cmd == "getblocks".as_bytes() {
531 | let data: GetBlocksmsg = deserialize(data)?;
532 | Ok(Message::GetBlock(data))
533 | } else if cmd == "getdata".as_bytes() {
534 | let data: GetDatamsg = deserialize(data)?;
535 | Ok(Message::GetData(data))
536 | } else if cmd == "tx".as_bytes() {
537 | let data: Txmsg = deserialize(data)?;
538 | Ok(Message::Tx(data))
539 | } else if cmd == "version".as_bytes() {
540 | let data: Versionmsg = deserialize(data)?;
541 | Ok(Message::Version(data))
542 | } else {
543 | Err(format_err!("Unknown command in the server"))
544 | }
545 | }
546 |
547 | #[cfg(test)]
548 | mod test {
549 | use super::*;
550 | use crate::blockchain::*;
551 | use crate::wallets::*;
552 |
553 | #[test]
554 | fn test_cmd() {
555 | let mut ws = Wallets::new().unwrap();
556 | let wa1 = ws.create_wallet();
557 | let bc = Blockchain::create_blockchain(wa1).unwrap();
558 | let utxo_set = UTXOSet { blockchain: bc };
559 | let server = Server::new("7878", "localhost:3001", utxo_set).unwrap();
560 |
561 | let vmsg = Versionmsg {
562 | addr_from: server.node_address.clone(),
563 | best_height: server.get_best_height().unwrap(),
564 | version: VERSION,
565 | };
566 | let data = serialize(&(cmd_to_bytes("version"), vmsg.clone())).unwrap();
567 | if let Message::Version(v) = bytes_to_cmd(&data).unwrap() {
568 | assert_eq!(v, vmsg);
569 | } else {
570 | panic!("wrong!");
571 | }
572 | }
573 | }
574 |
--------------------------------------------------------------------------------
/src/transaction.rs:
--------------------------------------------------------------------------------
1 | //! transaction implement
2 |
3 | use super::*;
4 | use crate::utxoset::*;
5 | use crate::wallets::*;
6 | use bincode::serialize;
7 | use bitcoincash_addr::Address;
8 | use crypto::digest::Digest;
9 | use crypto::ed25519;
10 | use crypto::sha2::Sha256;
11 | use failure::format_err;
12 | use rand::{Rng, RngCore};
13 | use serde::{Deserialize, Serialize};
14 | use std::collections::HashMap;
15 | use log::{debug, error, info};
16 | use rand::rngs::OsRng;
17 |
18 | const SUBSIDY: i32 = 10;
19 |
20 | /// TXInput represents a transaction input
21 | #[derive(Serialize, Deserialize, Debug, Clone)]
22 | pub struct TXInput {
23 | pub txid: String,
24 | pub vout: i32,
25 | pub signature: Vec,
26 | pub pub_key: Vec,
27 | }
28 |
29 | /// TXOutput represents a transaction output
30 | #[derive(Serialize, Deserialize, Debug, Clone)]
31 | pub struct TXOutput {
32 | pub value: i32,
33 | pub pub_key_hash: Vec,
34 | }
35 |
36 | // TXOutputs collects TXOutput
37 | #[derive(Serialize, Deserialize, Debug, Clone)]
38 | pub struct TXOutputs {
39 | pub outputs: Vec,
40 | }
41 |
42 | /// Transaction represents a Bitcoin transaction
43 | #[derive(Serialize, Deserialize, Debug, Clone)]
44 | pub struct Transaction {
45 | pub id: String,
46 | pub vin: Vec,
47 | pub vout: Vec,
48 | }
49 |
50 | impl Transaction {
51 | /// NewUTXOTransaction creates a new transaction
52 | pub fn new_UTXO(wallet: &Wallet, to: &str, amount: i32, utxo: &UTXOSet) -> Result {
53 | info!(
54 | "new UTXO Transaction from: {} to: {}",
55 | wallet.get_address(),
56 | to
57 | );
58 | let mut vin = Vec::new();
59 |
60 | let mut pub_key_hash = wallet.public_key.clone();
61 | hash_pub_key(&mut pub_key_hash);
62 |
63 | let acc_v = utxo.find_spendable_outputs(&pub_key_hash, amount)?;
64 |
65 | if acc_v.0 < amount {
66 | error!("Not Enough balance");
67 | return Err(format_err!(
68 | "Not Enough balance: current balance {}",
69 | acc_v.0
70 | ));
71 | }
72 |
73 | for tx in acc_v.1 {
74 | for out in tx.1 {
75 | let input = TXInput {
76 | txid: tx.0.clone(),
77 | vout: out,
78 | signature: Vec::new(),
79 | pub_key: wallet.public_key.clone(),
80 | };
81 | vin.push(input);
82 | }
83 | }
84 |
85 | let mut vout = vec![TXOutput::new(amount, to.to_string())?];
86 | if acc_v.0 > amount {
87 | vout.push(TXOutput::new(acc_v.0 - amount, wallet.get_address())?)
88 | }
89 |
90 | let mut tx = Transaction {
91 | id: String::new(),
92 | vin,
93 | vout,
94 | };
95 | tx.id = tx.hash()?;
96 | utxo.blockchain
97 | .sign_transacton(&mut tx, &wallet.secret_key)?;
98 | Ok(tx)
99 | }
100 |
101 | /// NewCoinbaseTX creates a new coinbase transaction
102 | pub fn new_coinbase(to: String, mut data: String) -> Result {
103 | info!("new coinbase Transaction to: {}", to);
104 | let mut key: [u8; 32] = [0; 32];
105 | if data.is_empty() {
106 | let mut rand = OsRng::default();
107 | rand.fill_bytes(&mut key);
108 | data = format!("Reward to '{}'", to);
109 | }
110 | let mut pub_key = Vec::from(data.as_bytes());
111 | pub_key.append(&mut Vec::from(key));
112 |
113 | let mut tx = Transaction {
114 | id: String::new(),
115 | vin: vec![TXInput {
116 | txid: String::new(),
117 | vout: -1,
118 | signature: Vec::new(),
119 | pub_key,
120 | }],
121 | vout: vec![TXOutput::new(SUBSIDY, to)?],
122 | };
123 | tx.id = tx.hash()?;
124 | Ok(tx)
125 | }
126 |
127 | /// IsCoinbase checks whether the transaction is coinbase
128 | pub fn is_coinbase(&self) -> bool {
129 | self.vin.len() == 1 && self.vin[0].txid.is_empty() && self.vin[0].vout == -1
130 | }
131 |
132 | /// Verify verifies signatures of Transaction inputs
133 | pub fn verify(&self, prev_TXs: HashMap) -> Result {
134 | if self.is_coinbase() {
135 | return Ok(true);
136 | }
137 |
138 | for vin in &self.vin {
139 | if prev_TXs.get(&vin.txid).unwrap().id.is_empty() {
140 | return Err(format_err!("ERROR: Previous transaction is not correct"));
141 | }
142 | }
143 |
144 | let mut tx_copy = self.trim_copy();
145 |
146 | for in_id in 0..self.vin.len() {
147 | let prev_Tx = prev_TXs.get(&self.vin[in_id].txid).unwrap();
148 | tx_copy.vin[in_id].signature.clear();
149 | tx_copy.vin[in_id].pub_key = prev_Tx.vout[self.vin[in_id].vout as usize]
150 | .pub_key_hash
151 | .clone();
152 | tx_copy.id = tx_copy.hash()?;
153 | tx_copy.vin[in_id].pub_key = Vec::new();
154 |
155 | if !ed25519::verify(
156 | &tx_copy.id.as_bytes(),
157 | &self.vin[in_id].pub_key,
158 | &self.vin[in_id].signature,
159 | ) {
160 | return Ok(false);
161 | }
162 | }
163 |
164 | Ok(true)
165 | }
166 |
167 | /// Sign signs each input of a Transaction
168 | pub fn sign(
169 | &mut self,
170 | private_key: &[u8],
171 | prev_TXs: HashMap,
172 | ) -> Result<()> {
173 | if self.is_coinbase() {
174 | return Ok(());
175 | }
176 |
177 | for vin in &self.vin {
178 | if prev_TXs.get(&vin.txid).unwrap().id.is_empty() {
179 | return Err(format_err!("ERROR: Previous transaction is not correct"));
180 | }
181 | }
182 |
183 | let mut tx_copy = self.trim_copy();
184 |
185 | for in_id in 0..tx_copy.vin.len() {
186 | let prev_Tx = prev_TXs.get(&tx_copy.vin[in_id].txid).unwrap();
187 | tx_copy.vin[in_id].signature.clear();
188 | tx_copy.vin[in_id].pub_key = prev_Tx.vout[tx_copy.vin[in_id].vout as usize]
189 | .pub_key_hash
190 | .clone();
191 | tx_copy.id = tx_copy.hash()?;
192 | tx_copy.vin[in_id].pub_key = Vec::new();
193 | let signature = ed25519::signature(tx_copy.id.as_bytes(), private_key);
194 | self.vin[in_id].signature = signature.to_vec();
195 | }
196 |
197 | Ok(())
198 | }
199 |
200 | /// Hash returns the hash of the Transaction
201 | pub fn hash(&self) -> Result {
202 | let mut copy = self.clone();
203 | copy.id = String::new();
204 | let data = serialize(©)?;
205 | let mut hasher = Sha256::new();
206 | hasher.input(&data[..]);
207 | Ok(hasher.result_str())
208 | }
209 |
210 | /// TrimmedCopy creates a trimmed copy of Transaction to be used in signing
211 | fn trim_copy(&self) -> Transaction {
212 | let mut vin = Vec::new();
213 | let mut vout = Vec::new();
214 |
215 | for v in &self.vin {
216 | vin.push(TXInput {
217 | txid: v.txid.clone(),
218 | vout: v.vout.clone(),
219 | signature: Vec::new(),
220 | pub_key: Vec::new(),
221 | })
222 | }
223 |
224 | for v in &self.vout {
225 | vout.push(TXOutput {
226 | value: v.value,
227 | pub_key_hash: v.pub_key_hash.clone(),
228 | })
229 | }
230 |
231 | Transaction {
232 | id: self.id.clone(),
233 | vin,
234 | vout,
235 | }
236 | }
237 | }
238 |
239 | impl TXOutput {
240 | /// IsLockedWithKey checks if the output can be used by the owner of the pubkey
241 | pub fn is_locked_with_key(&self, pub_key_hash: &[u8]) -> bool {
242 | self.pub_key_hash == pub_key_hash
243 | }
244 | /// Lock signs the output
245 | fn lock(&mut self, address: &str) -> Result<()> {
246 | let pub_key_hash = Address::decode(address).unwrap().body;
247 | debug!("lock: {}", address);
248 | self.pub_key_hash = pub_key_hash;
249 | Ok(())
250 | }
251 |
252 | pub fn new(value: i32, address: String) -> Result {
253 | let mut txo = TXOutput {
254 | value,
255 | pub_key_hash: Vec::new(),
256 | };
257 | txo.lock(&address)?;
258 | Ok(txo)
259 | }
260 | }
261 |
262 | #[cfg(test)]
263 | mod test {
264 | use super::*;
265 |
266 | #[test]
267 | fn test_signature() {
268 | let mut ws = Wallets::new().unwrap();
269 | let wa1 = ws.create_wallet();
270 | let w = ws.get_wallet(&wa1).unwrap().clone();
271 | ws.save_all().unwrap();
272 | drop(ws);
273 |
274 | let data = String::from("test");
275 | let tx = Transaction::new_coinbase(wa1, data).unwrap();
276 | assert!(tx.is_coinbase());
277 |
278 | let signature = ed25519::signature(tx.id.as_bytes(), &w.secret_key);
279 | assert!(ed25519::verify(tx.id.as_bytes(), &w.public_key, &signature));
280 | }
281 | }
282 |
--------------------------------------------------------------------------------
/src/tx.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 | use bitcoincash_addr::Address;
3 | use failure::format_err;
4 | use log::debug;
5 | use serde::{Serialize, Deserialize};
6 | use crate::transaction::{Transaction};
7 | use crate::errors::Result;
8 | use crate::wallets::hash_pub_key;
9 | // TXOutputs collects TXOutput
10 | #[derive(Serialize, Deserialize, Debug, Clone)]
11 | pub struct TXOutputs {
12 | pub outputs: Vec,
13 | }
14 |
15 | /// TXInput represents a transaction input
16 | #[derive(Serialize, Deserialize, Debug, Clone)]
17 | pub struct TXInput {
18 | pub txid: String,
19 | pub vout: i32,
20 | pub signature: Vec,
21 | pub pub_key: Vec,
22 | }
23 |
24 | /// TXOutput represents a transaction output
25 | #[derive(Serialize, Deserialize, Debug, Clone)]
26 | pub struct TXOutput {
27 | pub value: i32,
28 | pub pub_key_hash: Vec,
29 | }
30 | impl TXInput {
31 | /// CanUnlockOutputWith checks whether the address initiated the transaction
32 | pub fn can_unlock_output_with(&self, unlocking_data: &[u8]) -> bool {
33 | let mut pubkeyhash = self.pub_key.clone();
34 | hash_pub_key(&mut pubkeyhash);
35 | pubkeyhash == unlocking_data
36 | }
37 |
38 |
39 |
40 | }
41 |
42 | impl TXOutput {
43 | /// CanBeUnlockedWith checks if the output can be unlocked with the provided data
44 | pub fn can_be_unlock_with(&self, unlocking_data: &[u8]) -> bool {
45 | self.pub_key_hash == unlocking_data
46 | }
47 |
48 | /// Lock signs the output
49 | fn lock(&mut self, address: &str) -> Result<()> {
50 | let pub_key_hash = Address::decode(address).unwrap().body;
51 | debug!("lock: {}", address);
52 | self.pub_key_hash = pub_key_hash;
53 | Ok(())
54 | }
55 |
56 | pub fn new(value: i32, address: String) -> Result {
57 | let mut txo = TXOutput {
58 | value,
59 | pub_key_hash: Vec::new(),
60 | };
61 | txo.lock(&address)?;
62 | Ok(txo)
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/utxoset.rs:
--------------------------------------------------------------------------------
1 | //! unspend transaction output set
2 |
3 | use super::*;
4 | use crate::block::*;
5 | use crate::blockchain::*;
6 | use crate::transaction::*;
7 | use bincode::{deserialize, serialize};
8 | use sled;
9 | use std::collections::HashMap;
10 |
11 | /// UTXOSet represents UTXO set
12 | pub struct UTXOSet {
13 | pub blockchain: Blockchain,
14 | }
15 |
16 | impl UTXOSet {
17 | /// FindUnspentTransactions returns a list of transactions containing unspent outputs
18 | pub fn find_spendable_outputs(
19 | &self,
20 | pub_key_hash: &[u8],
21 | amount: i32,
22 | ) -> Result<(i32, HashMap>)> {
23 | let mut unspent_outputs: HashMap> = HashMap::new();
24 | let mut accumulated = 0;
25 |
26 | let db = sled::open("data/utxos")?;
27 | for kv in db.iter() {
28 | let (k, v) = kv?;
29 | let txid = String::from_utf8(k.to_vec())?;
30 | let outs: TXOutputs = deserialize(&v.to_vec())?;
31 |
32 | for out_idx in 0..outs.outputs.len() {
33 | if outs.outputs[out_idx].is_locked_with_key(pub_key_hash) && accumulated < amount {
34 | accumulated += outs.outputs[out_idx].value;
35 | match unspent_outputs.get_mut(&txid) {
36 | Some(v) => v.push(out_idx as i32),
37 | None => {
38 | unspent_outputs.insert(txid.clone(), vec![out_idx as i32]);
39 | }
40 | }
41 | }
42 | }
43 | }
44 |
45 | Ok((accumulated, unspent_outputs))
46 | }
47 |
48 | /// FindUTXO finds UTXO for a public key hash
49 | pub fn find_UTXO(&self, pub_key_hash: &[u8]) -> Result {
50 | let mut utxos = TXOutputs {
51 | outputs: Vec::new(),
52 | };
53 | let db = sled::open("data/utxos")?;
54 |
55 | for kv in db.iter() {
56 | let (_, v) = kv?;
57 | let outs: TXOutputs = deserialize(&v.to_vec())?;
58 |
59 | for out in outs.outputs {
60 | if out.is_locked_with_key(pub_key_hash) {
61 | utxos.outputs.push(out.clone())
62 | }
63 | }
64 | }
65 |
66 | Ok(utxos)
67 | }
68 |
69 | /// CountTransactions returns the number of transactions in the UTXO set
70 | pub fn count_transactions(&self) -> Result {
71 | let mut counter = 0;
72 | let db = sled::open("data/utxos")?;
73 | for kv in db.iter() {
74 | kv?;
75 | counter += 1;
76 | }
77 | Ok(counter)
78 | }
79 |
80 | /// Reindex rebuilds the UTXO set
81 | pub fn reindex(&self) -> Result<()> {
82 | std::fs::remove_dir_all("data/utxos").ok();
83 | let db = sled::open("data/utxos")?;
84 |
85 | let utxos = self.blockchain.find_UTXO();
86 |
87 | for (txid, outs) in utxos {
88 | db.insert(txid.as_bytes(), serialize(&outs)?)?;
89 | }
90 |
91 | Ok(())
92 | }
93 |
94 | /// Update updates the UTXO set with transactions from the Block
95 | ///
96 | /// The Block is considered to be the tip of a blockchain
97 | pub fn update(&self, block: &Block) -> Result<()> {
98 | let db = sled::open("data/utxos")?;
99 |
100 | for tx in block.get_transaction() {
101 | if !tx.is_coinbase() {
102 | for vin in &tx.vin {
103 | let mut update_outputs = TXOutputs {
104 | outputs: Vec::new(),
105 | };
106 | let outs: TXOutputs = deserialize(&db.get(&vin.txid)?.unwrap().to_vec())?;
107 | for out_idx in 0..outs.outputs.len() {
108 | if out_idx != vin.vout as usize {
109 | update_outputs.outputs.push(outs.outputs[out_idx].clone());
110 | }
111 | }
112 |
113 | if update_outputs.outputs.is_empty() {
114 | db.remove(&vin.txid)?;
115 | } else {
116 | db.insert(vin.txid.as_bytes(), serialize(&update_outputs)?)?;
117 | }
118 | }
119 | }
120 |
121 | let mut new_outputs = TXOutputs {
122 | outputs: Vec::new(),
123 | };
124 | for out in &tx.vout {
125 | new_outputs.outputs.push(out.clone());
126 | }
127 |
128 | db.insert(tx.id.as_bytes(), serialize(&new_outputs)?)?;
129 | }
130 | Ok(())
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/wallets.rs:
--------------------------------------------------------------------------------
1 | //! bitcoin wallet
2 |
3 | use super::*;
4 | use bincode::{deserialize, serialize};
5 | use bitcoincash_addr::*;
6 | use crypto::digest::Digest;
7 | use crypto::ed25519;
8 | use crypto::ripemd160::Ripemd160;
9 | use crypto::sha2::Sha256;
10 | use rand::{Rng, RngCore};
11 | use serde::{Deserialize, Serialize};
12 | use sled;
13 | use std::collections::HashMap;
14 | use log::info;
15 | use rand::rngs::OsRng;
16 |
17 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
18 | pub struct Wallet {
19 | pub secret_key: Vec,
20 | pub public_key: Vec,
21 | }
22 |
23 | impl Wallet {
24 | /// NewWallet creates and returns a Wallet
25 | fn new() -> Self {
26 | let mut key: [u8; 32] = [0; 32];
27 | let mut rand = OsRng::default();
28 | rand.fill_bytes(&mut key);
29 | let (secret_key, public_key) = ed25519::keypair(&key);
30 | let secret_key = secret_key.to_vec();
31 | let public_key = public_key.to_vec();
32 | Wallet {
33 | secret_key,
34 | public_key,
35 | }
36 | }
37 |
38 | /// GetAddress returns wallet address
39 | pub fn get_address(&self) -> String {
40 | let mut pub_hash: Vec = self.public_key.clone();
41 | hash_pub_key(&mut pub_hash);
42 | let address = Address {
43 | body: pub_hash,
44 | scheme: Scheme::Base58,
45 | hash_type: HashType::Script,
46 | ..Default::default()
47 | };
48 | address.encode().unwrap()
49 | }
50 | }
51 |
52 | /// HashPubKey hashes public key
53 | pub fn hash_pub_key(pubKey: &mut Vec) {
54 | let mut hasher1 = Sha256::new();
55 | hasher1.input(pubKey);
56 | hasher1.result(pubKey);
57 | let mut hasher2 = Ripemd160::new();
58 | hasher2.input(pubKey);
59 | pubKey.resize(20, 0);
60 | hasher2.result(pubKey);
61 | }
62 |
63 | pub struct Wallets {
64 | wallets: HashMap,
65 | }
66 |
67 | impl Wallets {
68 | /// NewWallets creates Wallets and fills it from a file if it exists
69 | pub fn new() -> Result {
70 | let mut wlt = Wallets {
71 | wallets: HashMap::::new(),
72 | };
73 | let db = sled::open("data/wallets")?;
74 |
75 | for item in db.into_iter() {
76 | let i = item?;
77 | let address = String::from_utf8(i.0.to_vec())?;
78 | let wallet = deserialize(&i.1.to_vec())?;
79 | wlt.wallets.insert(address, wallet);
80 | }
81 | drop(db);
82 | Ok(wlt)
83 | }
84 |
85 | /// CreateWallet adds a Wallet to Wallets
86 | pub fn create_wallet(&mut self) -> String {
87 | let wallet = Wallet::new();
88 | let address = wallet.get_address();
89 | self.wallets.insert(address.clone(), wallet);
90 | info!("create wallet: {}", address);
91 | address
92 | }
93 |
94 | /// GetAddresses returns an array of addresses stored in the wallet file
95 | pub fn get_all_addresses(&self) -> Vec {
96 | let mut addresses = Vec::::new();
97 | for (address, _) in &self.wallets {
98 | addresses.push(address.clone());
99 | }
100 | addresses
101 | }
102 |
103 | /// GetWallet returns a Wallet by its address
104 | pub fn get_wallet(&self, address: &str) -> Option<&Wallet> {
105 | self.wallets.get(address)
106 | }
107 |
108 | /// SaveToFile saves wallets to a file
109 | pub fn save_all(&self) -> Result<()> {
110 | let db = sled::open("data/wallets")?;
111 |
112 | for (address, wallet) in &self.wallets {
113 | let data = serialize(wallet)?;
114 | db.insert(address, data)?;
115 | }
116 |
117 | db.flush()?;
118 | drop(db);
119 | Ok(())
120 | }
121 | }
122 |
123 | #[cfg(test)]
124 | mod test {
125 | use super::*;
126 |
127 | #[test]
128 | fn test_create_wallet_and_hash() {
129 | let w1 = Wallet::new();
130 | let w2 = Wallet::new();
131 | assert_ne!(w1, w2);
132 | assert_ne!(w1.get_address(), w2.get_address());
133 |
134 | let mut p2 = w2.public_key.clone();
135 | hash_pub_key(&mut p2);
136 | assert_eq!(p2.len(), 20);
137 | let pub_key_hash = Address::decode(&w2.get_address()).unwrap().body;
138 | assert_eq!(pub_key_hash, p2);
139 | }
140 |
141 | #[test]
142 | fn test_wallets() {
143 | let mut ws = Wallets::new().unwrap();
144 | let wa1 = ws.create_wallet();
145 | let w1 = ws.get_wallet(&wa1).unwrap().clone();
146 | ws.save_all().unwrap();
147 |
148 | let ws2 = Wallets::new().unwrap();
149 | let w2 = ws2.get_wallet(&wa1).unwrap();
150 | assert_eq!(&w1, w2);
151 | }
152 |
153 | #[test]
154 | #[should_panic]
155 | fn test_wallets_not_exist() {
156 | let w3 = Wallet::new();
157 | let ws2 = Wallets::new().unwrap();
158 | ws2.get_wallet(&w3.get_address()).unwrap();
159 | }
160 |
161 | #[test]
162 | fn test_signature() {
163 | let w = Wallet::new();
164 | let signature = ed25519::signature("test".as_bytes(), &w.secret_key);
165 | assert!(ed25519::verify(
166 | "test".as_bytes(),
167 | &w.public_key,
168 | &signature
169 | ));
170 | }
171 | }
172 |
--------------------------------------------------------------------------------