[易学易懂系列|rustlang语言|零基础|快速入门|(27)|实战4:从零实现BTC区块链]
项目实战
实战4:从零实现BTC区块链
我们今天来开发我们的BTC区块链系统。
简单来说,从数据结构的角度上来说,区块链,就是区块组成的链。
以下就是BTC区块链典型的结构:
那最小单元就是区块:block。
这个block包含两部分:区块头,区块体。
我们先忽略Merkle树,先简化所有数据结构,只保留最基本的数据结构。
那区块头,就包含:区块高度,时间截;前一个区块地址;工作证明;
区块体,就包含交易数据,我们用一个vector来存储。
代码如下 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 1///交易结构体
2#[derive(Clone, Hash, Serialize, Deserialize, Debug)]
3pub struct Transaction {
4 sender: String, //发送者
5 recipient: String, //接收者
6 amount: i64, //交易数量
7}
8/// 区块结构体
9#[derive(Clone, Hash, Serialize, Deserialize, Debug)]
10pub struct Block {
11 pub index: u64, //区块高度
12 timestamp: DateTime<Utc>, //时间截
13 pub transactions: Vec<Transaction>, //交易集合
14 pub proof: u64, //证明
15 pub previous_hash: String, //上一个区块哈希地址
16}
17//区块链结构体
18#[derive(Default)]
19pub struct Blockchain {
20 pub chain: Vec<Block>, //区块链帐本
21 current_transactions: Vec<Transaction>, //当前交易集合
22 pub nodes: HashSet<String>, //节点集合
23}
24
25
26
现在我们创建了一个基本的区块数据结构,现在我们来让矿工来创建区块吧。
怎么让不同的矿工,积极地创建区块呢?
我们引入一个机制叫:POW共识机制。
什么叫POW?简单来说,就是大家根据给定的一个数值proof,进行hash计算,谁最先算出来的结果值符合某个条件,就拥有创建新的区块,并把这个区块连接到原来的区块链上的权力。
比如,困难程度为5,那有个矿工用proof数据进行SHA哈希计算,出如下结果:
0x0000010000000000000000000000000000000000000000000000000000000000
这个结果,前面的0(除了0x外)是5个,则这就是结果值。
如果,没有计算出上面的结果值,矿工将proof自增1,再进行SHA哈希计算,直到计算出这个符合条件的结果值为止。
而那个给定的数据值proof,也要放在区块头,这个值在每次创建新区块的时候由矿工产生并写入区块头。
当然,如果 两个节点都算出结果并加入了新区块,这时,会产生链的分叉,这时如何决定冲突呢?
我们用最长链原则,即给定周期内,哪个节点拥有的链最长,就用哪个。
所以我们的共识机制是:POW+最长链原则
这个共识机制核心 代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 1impl Blockchain {
2 //创建创世区块
3 pub fn new() -> Blockchain {
4 let mut blockchain = Blockchain {
5 chain: vec![],
6 current_transactions: vec![],
7 nodes: HashSet::new(),
8 };
9 blockchain.new_block(100, Some("1"));
10 blockchain
11 }
12 /// Create a new Block in the Blockchain
13 ///
14 /// :param proof: The proof given by the Proof of Work algorithm
15 /// :param previous_hash: (Optional) hash of previous Block
16 /// :return: New Bloc
17 /// 创建新区块
18 pub fn new_block(&mut self, proof: u64, previous_hash: Option<&str>) -> Block {
19 let block = Block {
20 index: (self.chain.len() + 1) as u64,
21 timestamp: Utc::now(),
22 transactions: self.current_transactions.drain(0..).collect(),
23 proof,
24 previous_hash: previous_hash.unwrap_or("0").to_string(),
25 };
26
27 self.chain.push(block.clone());
28 block
29 }
30 /// Creates a new transaction to go into the next mined Block
31 ///
32 /// :param sender: Address of the śender
33 /// :param recipient: Address of the recipient
34 /// :param amount: Amount
35 /// :return: The index of the Block that will hold this transaction
36 /// 发起一个新交易,将写入下一个区块
37 pub fn new_transaction(&mut self, sender: &str, recipient: &str, amount: i64) -> u64 {
38 self.current_transactions.push(Transaction {
39 sender: sender.to_string(),
40 recipient: recipient.to_string(),
41 amount,
42 });
43 self.last_block().unwrap().index + 1
44 }
45 /// Simple Proof of Work Algorithm:
46 /// - Find a number p' such that hash(pp') contains 4 leading zeroes,
47 /// where p is the previous proof, and p' is the new proof
48 /// POW工作量证明共识机制算法
49 pub fn proof_of_work(last_block: &Block) -> u64 {
50 let mut proof = 0;
51 let last_proof = last_block.proof;
52 let last_hash = &last_block.previous_hash;
53 while !Self::valid_proof(last_proof, proof, last_hash) {
54 proof += 1;
55 }
56 proof
57 }
58 /// Validates the Proof: Does hash(last_proof, proof, last_hash) containt 4 leading zeroes
59 //验证工作证明数字
60 fn valid_proof(last_proof: u64, proof: u64, last_hash: &String) -> bool {
61 let guess = format!("{}{}{}", last_proof, proof, last_hash);
62 let guess_hash = hex_digest(Algorithm::SHA256, guess.as_bytes());
63 guess_hash.ends_with("00000") //困难度为5
64 }
65
66 /// Creates a SHA-256 hash of a Block
67 ///
68 /// :param block: Block
69 /// :return hash for the block
70 /// 创建一个区块 的哈希值,基SHA-256算法
71 pub fn hash(block: &Block) -> String {
72 let serialized = serde_json::to_string(&block).unwrap();
73 hex_digest(Algorithm::SHA256, serialized.as_bytes())
74 }
75 /// Returns the last Block in the chain
76 /// 返回最后一个区块
77 pub fn last_block(&self) -> Option<&Block> {
78 self.chain.last()
79 }
80
81 /// Add a new node to the list of nodes
82 ///
83 /// :param address: Address of the node. Eg. 'http://192.168.0.5:5000'
84 ///
85 /// 节点注册,即新节点加入区块链网络,注册地址参数为节点服务器地址,如:'http://192.168.0.5:5000‘
86 pub fn register_node(&mut self, address: &str) {
87 let parsed_url = urlparse(address);
88 self.nodes.insert(parsed_url.netloc);
89 }
90
91 /// Determine if a given blockchain is valid
92 /// 链的验证
93 fn valid_chain(&self, chain: &[Block]) -> bool {
94 let mut last_block = &chain[0];
95 let mut current_index: usize = 1;
96 while current_index < chain.len() {
97 let block = &chain[current_index];
98 println!("{:?}", last_block);
99 println!("{:?}", block);
100 println!("-----------");
101 if block.previous_hash != Blockchain::hash(last_block) {
102 return false;
103 }
104 if !Blockchain::valid_proof(last_block.proof, block.proof, &last_block.previous_hash) {
105 return false;
106 }
107
108 last_block = block;
109 current_index += 1;
110 }
111 true
112 }
113
114 /// This is our Consensus Algorithm, it resolves conflicts
115 /// by replacing our chain with the longest one in the network.
116 ///
117 /// :return True if our chain was replaced and false otherwise
118 /// 解决冲突的机制,即共识机制,最长链原则处理逻辑,即共识机制为(POw+最长链原则)
119 pub fn resolve_conflicts(&mut self) -> bool {
120 let mut max_length = self.chain.len();
121 let mut new_chain: Option<Vec<Block>> = None;
122
123 // Grab and verify the chains from all the nodes in our network
124 for node in &self.nodes {
125 let mut response = reqwest::get(&format!("http://{}/chain", node)).unwrap();
126 if response.status().is_success() {
127 let node_chain: Chain = response.json().unwrap();
128 if node_chain.length > max_length && self.valid_chain(&node_chain.chain) {
129 max_length = node_chain.length;
130 new_chain = Some(node_chain.chain);
131 }
132 }
133 }
134 // Replace our chain if we discovered a new, valid chain longer than ours
135 match new_chain {
136 Some(x) => {
137 self.chain = x;
138 true
139 }
140 None => false,
141 }
142 }
143}
144
145
以上代码,我们放在当前工程目录下的src/blockchain.rs,完整代码如下 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176 1use crate::api::Chain;
2use chrono::{DateTime, Utc};
3use crypto_hash::{hex_digest, Algorithm};
4use reqwest;
5use serde::{Deserialize, Serialize};
6use std::collections::HashSet;
7use urlparse::urlparse;
8///交易结构体
9#[derive(Clone, Hash, Serialize, Deserialize, Debug)]
10pub struct Transaction {
11 sender: String, //发送者
12 recipient: String, //接收者
13 amount: i64, //交易数量
14}
15/// 区块结构体
16#[derive(Clone, Hash, Serialize, Deserialize, Debug)]
17pub struct Block {
18 pub index: u64, //区块高度
19 timestamp: DateTime<Utc>, //时间截
20 pub transactions: Vec<Transaction>, //交易
21 pub proof: u64, //证明
22 pub previous_hash: String, //上一个区块哈希地址
23}
24//区块链结构体
25#[derive(Default)]
26pub struct Blockchain {
27 pub chain: Vec<Block>, //区块链帐本
28 current_transactions: Vec<Transaction>, //交易集合
29 pub nodes: HashSet<String>, //节点集合
30}
31
32impl Blockchain {
33 //创建创世区块
34 pub fn new() -> Blockchain {
35 let mut blockchain = Blockchain {
36 chain: vec![],
37 current_transactions: vec![],
38 nodes: HashSet::new(),
39 };
40 blockchain.new_block(100, Some("1"));
41 blockchain
42 }
43 /// Create a new Block in the Blockchain
44 ///
45 /// :param proof: The proof given by the Proof of Work algorithm
46 /// :param previous_hash: (Optional) hash of previous Block
47 /// :return: New Bloc
48 /// 创建新区块
49 pub fn new_block(&mut self, proof: u64, previous_hash: Option<&str>) -> Block {
50 let block = Block {
51 index: (self.chain.len() + 1) as u64,
52 timestamp: Utc::now(),
53 transactions: self.current_transactions.drain(0..).collect(),
54 proof,
55 previous_hash: previous_hash.unwrap_or("0").to_string(),
56 };
57
58 self.chain.push(block.clone());
59 block
60 }
61 /// Creates a new transaction to go into the next mined Block
62 ///
63 /// :param sender: Address of the śender
64 /// :param recipient: Address of the recipient
65 /// :param amount: Amount
66 /// :return: The index of the Block that will hold this transaction
67 /// 发起一个新交易,将写入下一个区块
68 pub fn new_transaction(&mut self, sender: &str, recipient: &str, amount: i64) -> u64 {
69 self.current_transactions.push(Transaction {
70 sender: sender.to_string(),
71 recipient: recipient.to_string(),
72 amount,
73 });
74 self.last_block().unwrap().index + 1
75 }
76 /// Simple Proof of Work Algorithm:
77 /// - Find a number p' such that hash(pp') contains 4 leading zeroes,
78 /// where p is the previous proof, and p' is the new proof
79 /// POW工作量证明共识机制算法
80 pub fn proof_of_work(last_block: &Block) -> u64 {
81 let mut proof = 0;
82 let last_proof = last_block.proof;
83 let last_hash = &last_block.previous_hash;
84 while !Self::valid_proof(last_proof, proof, last_hash) {
85 proof += 1;
86 }
87 proof
88 }
89 /// Validates the Proof: Does hash(last_proof, proof, last_hash) containt 4 leading zeroes
90 //验证工作证明数字
91 fn valid_proof(last_proof: u64, proof: u64, last_hash: &String) -> bool {
92 let guess = format!("{}{}{}", last_proof, proof, last_hash);
93 let guess_hash = hex_digest(Algorithm::SHA256, guess.as_bytes());
94 guess_hash.ends_with("00000") //困难度为5
95 }
96
97 /// Creates a SHA-256 hash of a Block
98 ///
99 /// :param block: Block
100 /// :return hash for the block
101 /// 创建一个区块 的哈希值,基SHA-256算法
102 pub fn hash(block: &Block) -> String {
103 let serialized = serde_json::to_string(&block).unwrap();
104 hex_digest(Algorithm::SHA256, serialized.as_bytes())
105 }
106 /// Returns the last Block in the chain
107 /// 返回最后一个区块
108 pub fn last_block(&self) -> Option<&Block> {
109 self.chain.last()
110 }
111
112 /// Add a new node to the list of nodes
113 ///
114 /// :param address: Address of the node. Eg. 'http://192.168.0.5:5000'
115 ///
116 /// 节点注册,即新节点加入区块链网络,注册地址参数为节点服务器地址,如:'http://192.168.0.5:5000‘
117 pub fn register_node(&mut self, address: &str) {
118 let parsed_url = urlparse(address);
119 self.nodes.insert(parsed_url.netloc);
120 }
121
122 /// Determine if a given blockchain is valid
123 /// 链的验证
124 fn valid_chain(&self, chain: &[Block]) -> bool {
125 let mut last_block = &chain[0];
126 let mut current_index: usize = 1;
127 while current_index < chain.len() {
128 let block = &chain[current_index];
129 println!("{:?}", last_block);
130 println!("{:?}", block);
131 println!("-----------");
132 if block.previous_hash != Blockchain::hash(last_block) {
133 return false;
134 }
135 if !Blockchain::valid_proof(last_block.proof, block.proof, &last_block.previous_hash) {
136 return false;
137 }
138
139 last_block = block;
140 current_index += 1;
141 }
142 true
143 }
144
145 /// This is our Consensus Algorithm, it resolves conflicts
146 /// by replacing our chain with the longest one in the network.
147 ///
148 /// :return True if our chain was replaced and false otherwise
149 /// 最长链原则处理逻辑,即共识机制为(POw+最长链原则)
150 pub fn resolve_conflicts(&mut self) -> bool {
151 let mut max_length = self.chain.len();
152 let mut new_chain: Option<Vec<Block>> = None;
153
154 // Grab and verify the chains from all the nodes in our network
155 for node in &self.nodes {
156 let mut response = reqwest::get(&format!("http://{}/chain", node)).unwrap();
157 if response.status().is_success() {
158 let node_chain: Chain = response.json().unwrap();
159 if node_chain.length > max_length && self.valid_chain(&node_chain.chain) {
160 max_length = node_chain.length;
161 new_chain = Some(node_chain.chain);
162 }
163 }
164 }
165 // Replace our chain if we discovered a new, valid chain longer than ours
166 match new_chain {
167 Some(x) => {
168 self.chain = x;
169 true
170 }
171 None => false,
172 }
173 }
174}
175
176
现在 我们向外界提供一些可用的API。
我们新建一个文件:src/api.rs,代码如下 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 1use crate::blockchain::{Block, Blockchain, Transaction};
2
3use actix_web::{web, HttpRequest, HttpResponse};
4use serde::{Deserialize, Serialize};
5use std::sync::Mutex;
6///返回消息体
7#[derive(Serialize, Deserialize)]
8pub struct MessageResponse {
9 message: String,
10}
11//交易请求信息
12#[derive(Serialize, Deserialize)]
13pub struct TransactionRequest {
14 sender: String,
15 recipient: String,
16 amount: i64,
17}
18///挖矿响应消息
19#[derive(Serialize)]
20pub struct MiningRespose {
21 message: String,
22 index: u64,
23 transactions: Vec<Transaction>,
24 proof: u64,
25 previous_hash: String,
26}
27///链结构体,代表现在网络上的最长链
28#[derive(Serialize, Deserialize)]
29pub struct Chain {
30 pub chain: Vec<Block>,
31 pub length: usize,
32}
33///节点注册请求信息
34#[derive(Deserialize)]
35pub struct RegisterRequest {
36 nodes: Vec<String>,
37}
38///节点注册响应信息
39#[derive(Serialize)]
40pub struct RegisterResponse {
41 message: String,
42 total_nodes: Vec<String>,
43}
44//解决冲突响应信息
45#[derive(Serialize)]
46pub struct ResolveResponse {
47 message: String,
48 chain: Vec<Block>,
49}
50///发起新交易
51pub fn new_transaction(
52 state: web::Data<Mutex<Blockchain>>,
53 req: web::Json<TransactionRequest>,
54) -> HttpResponse {
55 let sender = req.sender.to_owned();
56 let recipient = req.recipient.to_owned();
57 let index = state
58 .lock()
59 .unwrap()
60 .new_transaction(&sender, &recipient, req.amount);
61 HttpResponse::Created().json(MessageResponse {
62 message: format! {"Transaction will be added to Block {}", index},
63 })
64}
65///矿工挖矿
66pub fn mine(
67 node_identifier: web::Data<String>,
68 state: web::Data<Mutex<Blockchain>>,
69 _req: HttpRequest,
70) -> HttpResponse {
71 let (proof, previous_hash) = {
72 let blockchain = state.lock().unwrap();
73 let last_block = blockchain.last_block().unwrap();
74 let proof = Blockchain::proof_of_work(&last_block);
75 let previous_hash = Blockchain::hash(last_block);
76 (proof, previous_hash)
77 };
78 let mut blockchain = state.lock().unwrap();
79 blockchain.new_transaction("0", &*node_identifier, 1);
80 let block = blockchain.new_block(proof, Some(&previous_hash));
81 HttpResponse::Ok().json(MiningRespose {
82 message: "New Block Forged".to_string(),
83 index: block.index,
84 transactions: block.transactions,
85 proof,
86 previous_hash,
87 })
88}
89///当前最新链的信息
90pub fn chain(state: web::Data<Mutex<Blockchain>>, _req: HttpRequest) -> HttpResponse {
91 let length = state.lock().unwrap().chain.len();
92 HttpResponse::Ok().json(Chain {
93 chain: state.lock().unwrap().chain.clone(),
94 length,
95 })
96}
97///节点注册
98pub fn register_node(
99 state: web::Data<Mutex<Blockchain>>,
100 req: web::Json<RegisterRequest>,
101) -> HttpResponse {
102 if req.nodes.is_empty() {
103 return HttpResponse::BadRequest().json(MessageResponse {
104 message: "Error: Please supply a valid list of nodes".to_string(),
105 });
106 }
107 let mut blockchain = state.lock().unwrap();
108 for node in req.nodes.iter() {
109 blockchain.register_node(node)
110 }
111 HttpResponse::Created().json(RegisterResponse {
112 message: "New nodes have been added".to_string(),
113 total_nodes: blockchain.nodes.iter().cloned().collect(),
114 })
115}
116///跟网络上其他节点达成共识,即解决冲突
117pub fn resolve_nodes(state: web::Data<Mutex<Blockchain>>, _req: HttpRequest) -> HttpResponse {
118 let mut blockchain = state.lock().unwrap();
119 let replaced = blockchain.resolve_conflicts();
120 let message = if replaced {
121 "Our chain was replaced"
122 } else {
123 "Our chain is authorative"
124 };
125 HttpResponse::Ok().json(ResolveResponse {
126 message: message.to_string(),
127 chain: blockchain.chain.clone(),
128 })
129}
130
131
当然,我们要用到一些好用的库,在我们的Cargo.toml文件,我们加入依赖,完整代码如下:
1
2
3
4
5
6
7
8
9
10 1[dependencies]
2chrono = { version = "0.4.6", features = ["serde"] }
3crypto-hash = "0.3.3"
4serde = { version = "1.0.90", features = ["derive"] }
5serde_json = "1.0"
6actix-web = "1.0"
7uuid = { version = "0.7", features = ["v4"] }
8urlparse = "0.7.3"
9reqwest = "=0.9.17"
10
最后我们的主程序 src/main.rs如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 1pub mod api;
2pub mod blockchain;
3
4use actix_web::{web, App, HttpServer};
5use std::env;
6use std::sync::Mutex;
7use uuid::Uuid;
8
9fn main() {
10 let args: Vec<String> = env::args().collect();
11 let port = match args.as_slice() {
12 [_, key, value] => {
13 if key == "--p" {
14 value
15 } else {
16 panic!("Illegal arguments passed to the program.");
17 }
18 }
19 _ => "5000",
20 };
21 // TODO: make chain shared across threads
22 let sharedchain = web::Data::new(Mutex::new(blockchain::Blockchain::new()));
23 let node_identifier = web::Data::new(Uuid::new_v4().to_simple().to_string());
24
25 HttpServer::new(move || {
26 App::new()
27 .register_data(sharedchain.clone())
28 .register_data(node_identifier.clone())
29 .data(web::JsonConfig::default().limit(4096))
30 .service(web::resource("/mine").route(web::get().to(api::mine)))
31 .service(web::resource("/transactions/new").route(web::post().to(api::new_transaction)))
32 .service(web::resource("/chain").route(web::get().to(api::chain)))
33 .service(web::resource("/nodes/register").route(web::post().to(api::register_node)))
34 .service(web::resource("/nodes/resolve").route(web::get().to(api::resolve_nodes)))
35 })
36 .bind(format!("127.0.0.1:{}", port))
37 .unwrap()
38 .run();
39}
40
41
然后我们可以用以下命令来调用 :
挖矿:
1
2 1curl http://localhost:5000/mine
2
创建新交易:
1
2 1curl -H "Content-Type: application/json" --request POST --data '{"sender":"e79fcabd1d70433191701d17c4d13112", "recipient":"some-other-address", "amount":5}' http://localhost:5000/transactions/new
2
查看整条链信息:
1
2
3 1curl http://localhost:5000/chain
2
3
注册节点:
1
2
3 1curl -H "Content-Type: application/json" --request POST --data '{"nodes":["http://localhost:5001"]}' http://localhost:5000/nodes/register
2
3
与其他节点达成共识(共识机制):
1
2
3 1curl http://localhost:5000/nodes/resolve
2
3
以上,希望对你有用。
1
2
3 1如果遇到什么问题,欢迎加入:rust新手群,在这里我可以提供一些简单的帮助,加微信:360369487,注明:博客园+rust
2
3
https://asymmetric.github.io/2018/02/11/blockchain-rust/
https://jeiwan.net/posts/building-blockchain-in-go-part-1/
https://hackernoon.com/learn-blockchains-by-building-one-117428612f46
https://medium.com/@vanflymen/learn-blockchains-by-building-one-117428612f46?