新建一个cargo项目,增加一个Cargo.toml用来配置整个Rust项目,配置如下。
[workspace]
members=[
"main",
"core",
"utils",
]
在整个项目中有三部分,其中core模块和utils模块都是lib,使用cargo new core --lib
创建,main部分是测试和创建区块链的相关代码。
首先需要编写core模块,在这个模块中主要编写区块的结点数据结构和区块链数据结构,在结点数据结构中包含一个结点的头数据结构,创建一个block.rs文件,用来编写结点的数据结构。
use chrono::prelude::Local;
use utils::coder;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct BlockHeader {
pub time: i64,
// transactionns data merkle root hash
pub tx_hash: String,
pub pre_hash: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct Block {
pub header:BlockHeader,
pub hash:String,
// transactions data
pub data:String,
}
impl Block {
fn set_hash(&mut self){
self.header.time =Local::now().timestamp();
let header =coder::my_serialize(&self.header);
self.hash= coder::get_hash(&header[..]);
}
pub fn new_block(data:String,pre_hash:String)->Block{
let transactions=coder::my_serialize(&data);
let tx_hash=coder::get_hash(&transactions[..]);
let time =Local::now().timestamp();
let mut block=Block{
header:BlockHeader{
time: time,
tx_hash:tx_hash,
pre_hash:pre_hash,
},
hash:"".to_string(),
data:data,
};
block.set_hash();
block
}
}
在编写数据结构时需要让此结构支持序列化和反序列化,(打印、相等对比不是必要的),需要引入serde模块,需要使用时间戳,所以引入chrono模块,修改Cargo.toml文件,序列化和反序列化以及Hash方法都编写在Utils模块中,所以需要引入自己编写的utils模块,如下所示。
[package]
name = "core"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = "0.4"
serde = {version="1.0.136", features=["derive"]}
utils = {path = "../utils"}
在区块链数据结构中应当包含区块结点结构,编写一个blockchain.rs文件,以Vector的方式存储这些结点,如下代码所示
use crate::block;
pub struct BlockChain {
pub blocks: Vec<block::Block>,
}
impl BlockChain {
pub fn add_block(&mut self, data: String) {
let pre_block=&self.blocks[self.blocks.len()-1];
let new_block=block::Block::new_block(data, pre_block.hash.clone());
self.blocks.push(new_block);
}
// 创世区块
pub fn new_genesis_block()->block::Block{
block::Block::new_block("This is genesis block".to_string(),String::from(""))
}
pub fn new_blockchain()->BlockChain{
BlockChain{
blocks:vec![BlockChain::new_genesis_block()]
}
}
}
在core模块中应当暴露出block和blockchain,所以修改lib.rs,如下代码所示。
pub mod block;
pub mod blockchain;
然后编写utils模块,在这个文件中应当提供数据序列化和反序列化的方法,如下代码所示。
// 序列化和反序列化
// 使用第三方crates.io
use bincode;
use serde::{Deserialize, Serialize};
use crypto::digest::Digest;
use crypto::sha3::Sha3;
/**
* 序列化
* 编译时大小不确定,使用范型
* 编译时需要限制范型本身是可以被序列化的,使用where进行限制
*/
pub fn my_serialize<T:?Sized>(value:&T)->Vec<u8>
where T: Serialize,{
let seialized=bincode::serialize(value).unwrap();
seialized
}
/**
* 反序列化
*/
pub fn my_deserialize<'a,T>(bytes:&'a[u8])->T
where T:Deserialize<'a>{
let deserialized=bincode::deserialize(bytes).unwrap();
return deserialized;
}
/**
* 对象转hash
*
*/
pub fn get_hash(value:&[u8])->String{
let mut hasher=Sha3::sha3_256();
hasher.input(value);
hasher.result_str()
}
#[derive(Serialize, Deserialize,Debug,PartialEq,Eq)]
struct Point{
x:i32,
y:i32,
}
#[cfg(test)]
mod tests {
use crate::coder::Point;
use crate::coder::{my_serialize,my_deserialize};
#[test]
fn coders_works() {
let point=Point{x:1,y:2};
let se=my_serialize(&point);
let de=my_deserialize(&se);
assert_eq!(point,de);
}
}
在utils为了完成数据的序列化和反序列化,使用了第三方bincode、serde、rust-crypto模块,如下Cargo.toml配置。
[package]
name = "utils"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bincode = "1.3.3"
serde = {version="1.0.136", features=["derive"]}
rust-crypto = "0.2"
在lib.rs中暴露编写的coder.rs模块。
pub mod coder;
在测试运行的main模块中引入core模块。
[package]
name = "main"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
core={path="../core"}
运行方法:
use core::blockchain;
fn main() {
let mut bc=blockchain::BlockChain::new_blockchain();
bc.add_block("a->b: 5btc".to_string());
bc.add_block("b->c: 10btc".to_string());
for b in bc.blocks{
println!("++++++++++++++++++++++++++++++++++++++++");
println!("{:#?}",b);
println!("")
}
}
运行结果