前两部分我们都是将区块链存储在内存中。从这一部分开始,我们将区块链持久化到硬盘中,默认使用KV数据库sled。

数据库结构

KEY

VALUE

tip_hash

区块链中最后加入块的hash值

height

区块链中的高度

blocks:{hash}

区块的hash值,blocks为前缀。

pub const TIP_KEY: &str = "tip_hash";
pub const HEIGHT: &str = "height";
pub const TABLE_OF_BLOCK: &str = "blocks";

Storage Trait

考虑到存储的可扩展性,将来使用其他KV数据库,如:RocksDB。我们定义了storage trait。

pub trait Storage: Send + Sync + 'static {// 获取最后一个块的hash值fn get_tip(&self) -> Result<Option<String>, BlockchainError>;// 获取一个区块fn get_block(&self, key: &str) -> Result<Option<Block>, BlockchainError>;// 获取区块链的高度fn get_height(&self) -> Result<Option<usize>, BlockchainError>;// 以事务的方式更新区块链 fn update_blocks(&self, key: &str, block: &Block, height: usize);// 获取区块的迭代器fn get_block_iter(&self) -> Result<Box<dyn Iterator<Item = Block>>, BlockchainError>;
}
// 定义区块的迭代器
pub struct StorageIterator<T> {data: T
}impl<T> StorageIterator<T> {pub fn new(data: T) -> Self {Self { data }}
}// T泛型需要满足Iterator约束
// T的item类型需要满足能转换成Block
impl<T> Iterator for StorageIterator<T>
whereT: Iterator,T::Item: Into<Block>
{type Item = Block;fn next(&mut self) -> Option<Self::Item> {self.data.next().map(|v| v.into())}
}

Sled的实现

pub struct SledDb {// seld::Dbdb: Db
}impl SledDb {pub fn new(path: impl AsRef<Path>) -> Self {Self {db: sled::open(path).unwrap()}}fn get_full_key(table: &str, key: &str) -> String {format!("{}:{}", table, key)}
}impl Storage for SledDb {......fn update_blocks(&self, key: &str, block: &Block, height: usize) {// 使用事务let _: TransactionResult<(), ()> = self.db.transaction(|db| {let name = Self::get_full_key(TABLE_OF_BLOCK, key);db.insert(name.as_str(), serialize(block).unwrap())?;db.insert(TIP_KEY, serialize(key).unwrap())?;db.insert(HEIGHT, serialize(&height).unwrap())?;db.flush();Ok(())});}fn get_block_iter(&self) -> Result<Box<dyn Iterator<Item = Block>>, BlockchainError> {let prefix = format!("{}:", TABLE_OF_BLOCK);let iter = StorageIterator::new(self.db.scan_prefix(prefix));Ok(Box::new(iter))}
}

修改区块链

// 默认使用sled数据库
pub struct Blockchain<T = SledDb> {storage: T,tip: Arc<RwLock<String>>,height: AtomicUsize,
}impl<T: Storage> Blockchain<T> {pub fn new(storage: T) -> Self {// 如果数据库中有tip值,则加载到内存。// 否则创建一个创世块,并更新到数据库中。if let Ok(Some(tip)) = storage.get_tip() {let height = storage.get_height().unwrap();Self {storage,tip: Arc::new(RwLock::new(tip)),height: AtomicUsize::new(height.unwrap()),}}else {let genesis_block = Block::create_genesis_block(CURR_BITS);let hash = genesis_block.get_hash();storage.update_blocks(&hash, &genesis_block, 0 as usize);Self {storage,tip: Arc::new(RwLock::new(hash)),height: AtomicUsize::new(0),}}}pub fn mine_block(&mut self, data: &str) {let block = Block::new(data, &self.tip.read().unwrap(), CURR_BITS);let hash = block.get_hash();self.height.fetch_add(1, Ordering::Relaxed);self.storage.update_blocks(&hash, &block, self.height.load(Ordering::Relaxed));let mut tip = self.tip.write().unwrap();*tip = hash;}pub fn blocks_info(&self) {let blocks = self.storage.get_block_iter().unwrap();for block in blocks {info!("{:#?}", block);}}
}

验证

RUST_LOG=info cargo run --example gen_bc --quiet
INFO blockchain_rust_part_3::blocks::blockchain: Block {header: BlockHeader {timestamp: 1650259594,prev_hash: "",bits: 8,nonce: 233,},data: "创世区块",hash: "00d76473e80522e336a1078227d10d599190d8ef6877fa1d6fa980d692ef3c18",
}

第一次执行,创建了创世块,且存入到了数据库中。再次执行,取出的区块信息应该与第一次执行时创建的区块一样。大家可以自己验证。

工程结构

完整代码:

https://github.com/Justin02180218/blockchain_rust


更多文章,请关注公众号:coding到灯火阑珊

用Rust实现区块链 - 3 持久化相关推荐

  1. 蚂蚁区块链-CONFIDE-ACM SIGMOD 20

    论文名称:<Confidentiality Support over Financial Grade Consortium Blockchain> 论文地址:https://dl.acm. ...

  2. Web3 开发者必备手册:盘点 5 种最佳区块链编程语言

    一份必备的区块链开发语言列表,帮助 Web3 创业者快速启动并创建一个项目. Rust - 用于区块链开发的最佳编程语言 Rust 是一种相对年轻的编程语言,但已经在区块链行业中证明了它的效率.不久前 ...

  3. 用Go语言建立一个简单的区块链part3:持久化和命令行接口

    持久化和命令行接口 引言 到目前为止,我们已经构建了一个有工作量证明机制的区块链.有了工作量证明,挖矿也就有了着落.虽然目前距离一个有着完整功能的区块链越来越近了,但是它仍然缺少了一些重要的特性.在今 ...

  4. 用 Go 构建一个区块链 -- Part 3: 持久化和命令行接口

    翻译的系列文章我已经放到了 GitHub 上:blockchain-tutorial,后续如有更新都会在 GitHub 上,可能就不在这里同步了.如果想直接运行代码,也可以 clone GitHub ...

  5. 从0到1简易区块链开发手册V0.3-数据持久化与创世区块

    Author: brucefeng Email: brucefeng@brucefeng.com 编程语言:Golang 1.BoltDB简介 Bolt是一个纯粹Key/Value模型的程序.该项目的 ...

  6. 区块链生态系统将崩溃、Rust 超越 Go、无服务器成主导,这十大计算机预测将成真?...

    [编者按]在IT圈有句名言:"活到老,学到老".短短 6 字便道出技术发展速度之快.移动互联网.云计算.大数据.人工智能等技术革新在过去十年推动着时代巨轮不断向前迈步.接下来,我们 ...

  7. 区块链常用序列化分析

    parity开源解析 parity-scale-codec 用于Parity Substrate框架中的类型的SCALE(Simple Concatenated Aggregate Little-En ...

  8. libra区块链技术白皮书-中文部分翻译版

    2019年6月18日facebook发布了libra区块链,目标是做一个全球货币和支付工具.最近花了一些时间来研究这个libra,官网上有白皮书的中文版,但没有技术白皮书的中文版,因此花了一些时间来做 ...

  9. 区块链入门 第七部分 以太坊

    以太坊 以太坊相关导航 以太坊(Ethereum)是一个开源的有智能合约功能的公共区块链平台.通过其专用加密货币以太币(Ether)提供去中心化的虚拟机("以太虚拟机" Ether ...

最新文章

  1. Kibana:分析及可视化日志文件
  2. 类选择器和所作用的标签一起写为什么不起作用? - CSDN博客
  3. CVPR和ICLR双榜公布,最离谱审稿人竟然没读论文!
  4. Google 全球员工围攻 Google!
  5. hello this Word ! I'm coming!
  6. 机器学习中的损失函数
  7. 与时代并肩的数字创新者
  8. Java通过HighCharts导出图表
  9. 普通调幅(AM)与抑制载波双边带调幅(DSB)matlab编程实现
  10. nginx配置 vue打包后的项目 解决刷新页面404问题|nginx配置多端访问
  11. 计算机中年级排名怎么操作,智学网如何查看年级排名 智学网看年级排名方法【详解】...
  12. (转)星巴克其实是靠大数据盈利的!
  13. LaTeX最全的数学符号大全(更新中…… )
  14. 几本开关电源书籍 pdf格式 (来源中国电子开发网)
  15. 360浏览器小号多开使用
  16. mysql之事务 锁(三)
  17. 捋一捋DSP里的重叠保留法
  18. 教你修改电脑mac地址图解教程
  19. 系统扫描修复cmd命令
  20. Serv-U 安装配置以及外网访问使用

热门文章

  1. 永远不要在MySQL中使用UTF-8
  2. 2019_GDUT_新生专题V算法优化 F. Expedition POJ 2431
  3. python批量修改列名_pandas修改DataFrame列名的实现方法
  4. 计算机正确的坐姿教案,礼仪课坐姿教案.doc
  5. Java实现给图片局部打马赛克
  6. python开发项目经历_Python开发工程师岗位项目经历怎么写
  7. [Swift]LeetCode41. 缺失的第一个正数 | First Missing Positive
  8. 如何用Python画一只狗狗——turtle基础
  9. lower_bound,upper_bound的第四个参数的用法
  10. Velocity模板语言(VTL):说明