导入所需的包
Cargo.toml
1  | [dependencies]  | 
创建结构体(blockchain.rs)
交易信息
交易结构体需要包含发送者,接收者以及金额,同时需要实现序列化,Debug和Clone(后续创建新块需要)特性
1  | 
  | 
区块头
包含时间戳,前一个区块的hash,难度,默克尔值和nonce。
其中默克尔值是一个hash值,它包含每一笔交易的hash值。
nonce是用于工作量证明的值,得到一个nonce使得区块头的hash前X位为0,其中X为难度
1  | 
  | 
区块体
包含区块头,交易计数以及所有的交易信息
1  | 
  | 
链
包含区块列表当前交易,难度,矿工地址以及奖励
1  | pub struct Chain {  | 
为链创建方法(blockchain.rs)
创建新链
要创建一个全新的区块链,我们需要得到第一笔交易信息,由Root发给第一位矿工。
首先新建一个链,接收矿工地址和难度两个参数,调用generate_new_block()方法生成第一个区块,最后返回一个chain实例
1  | pub fn new(miner_address: String, difficulty: u32) -> Chain {  | 
修改难度和奖励
创建修改难度和奖励的方法
1  | pub fn update_difficulty(&mut self, difficulty: u32) -> bool {  | 
创建新的交易
创建新交易很简单,只需要将发送者和接受者信息传入Transaction结构体,最后将结构体加入到链中的交易信息里即可,返回的bool用于判断是否创建成功。
1  | pub fn new_transaction(&mut self, sender: String, receiver: String, amount: f32) -> bool {  | 
计算当前块的hash值
定义last_hash方法来计算当前块的hash值。
1  | pub fn last_hash(&self) -> String {  | 
创建新的区块
首先生成区块头,传入初始化信息。原视频使用time生成时间戳,这边使用了chrono。
然后产生一笔新的交易,从Root给miner。
创建一个新的区块,将此处的新交易添加到区块的交易中,并且将历史交易记录添加到此区块,此时transaction列表长度即为交易数。
通过get_merkle方法得到默克尔值,通过proof_of_work得到nonce,最后将此block打包到链中
1  | pub fn generate_new_block(&mut self) -> bool {  | 
计算默克尔值
传入当前交易的列表,取出每一笔交易 计算hash后加入merkle列表。
如果默克尔列表中有奇数个值,则复制最后一个值并将其加入默克尔列表。
从默克尔hash列表中 取出前两个交易的hash值,拼接后将调用hash()得到一个hash,重复此操作直到默克尔列表中的hash被合并为一个时pop并返回。
1  | fn get_merkle(curr_trans: Vec<Transaction>) -> String {  | 
工作量证明
接收一个区块头并且计算hash,从hash中取出一定长度的切片并且将其解析,其中长度由difficulty决定。
parse::<u32>()会尝试将其解析为u32类型,这边得到的是一个Result类型,我们的目的是得到一个nonce使得最终区块头的hash值中,前X位都是0,所以在得到ERROR时,我们依然将nonce+1,知道得到所需的hash为止。
最后我们就得到了一个可以将区块头的hash的前X位变成0的nonce,用于证明工作。
1  | pub fn proof_of_work(header: &mut BlockHeader) {  | 
hash计算
hash接受一个可被serde序列化的结构体,将结构体变成字符串,我们调用sha2的方法输出得到一个&[u8]类型的列表,通过hex_to_string 将其转换为一个16进制字符串,这边使用std::fmt::Write实现
1  | pub fn hash<T: serde::Serialize>(item: &T) -> String {  | 
主函数调用(main.rs)
启动初始化
启动此程序时,我们需要初始化数据,开始第一笔交易以创建区块链,
使用io::stdin().read_line读取用户输入,并且将参数传给blockchain::Chain::new创建全新的区块链
1  | let mut miner_address = String::new();  | 
循环内容
打印菜单,使用match匹配用户输入,对其做相应处理,通过对应函数返回的bool来判断执行结果
1  | loop {  | 
效果
1  | 
  | 
完整代码
blockchain.rs
1  | extern crate serde;  | 
main.rs
1  | 
  |