导入所需的包
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 |
|