区块链之bolt数据库持久化与基本功能完善

news2024/9/25 23:15:20

文章目录

    • bolt数据库安装
    • 使用bolt进行持久化存储
      • bolt持久化的基本步骤
      • 创世区块的持久化
      • 新增区块的持久化
    • 完善区块链基本功能
      • 创世区块创建
      • 增加区块
      • 遍历区块链

链接: 区块链项目github地址
项目目前进度:
![在这里插

bolt数据库安装

bolt数据库介绍:

bolt数据库是一个为go语言提供的轻量化数据集,是一个简单、快速的关系型数据库,其中数据
以[]byte的方式进行存储。索引方式为键/值的方式,即通过关键词去索引数据。

bolt项目地址:link
安装步骤:
1.在项目地址下的终端运行

go get github.com/boltdb/bolt/...

2.当前项目的.mod文件加入语句

require (
github.com/boltdb/bolt v1.3.1 // indirect
golang.org/x/sys v0.3.0 // indirect
)

3.修改mod文件后,需要运行

go mod vendor

进行更新

使用bolt进行持久化存储

相较于之前的区块链程序,使用持久化存储即将区块链存放在数据库中进行保存,进行持久化。相较于之前的未持久化的程序,修改的地方有:
1.将创世区块进行持久化
2.将新增区块进行持久化

bolt持久化的基本步骤

1.创建或打开一个数据库,dbName为数据库的名称,0600说明数据库的权限(0600的二进制码为111,000,000,表示管理员有读、写权限,没有执行权限),nil表示默认操作

	db, err := bolt.Open(dbName, 0600, nil)

2.对数据库进行创建数据表的操作,这里bucket即表示数据表的意思

db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(blockTableName))
		if b == nil {
			//数据表不存在
			b, err := tx.CreateBucket([]byte(blockTableName))
			if err != nil {
				log.Panicf("create backet [%s] failed %v\n", blockTableName, err)
			}
		}
		return nil
	})

3.增加与插入数据操作,b.get表示通过键去索引值,得到的值为[]byte的格式,b.put表示存储值,其中,数据必须以键值对的方式进行存储。

bc.DB.Update(func(tx *bolt.Tx) error {
		//获取数据库桶
		b := tx.Bucket([]byte(blockTableName))
		if b != nil {
			blockTypes := b.Get(bc.Tip)
			err = b.Put([]byte("l"), newBlock.Hash)
			if err != nil {
				log.Panicf("save the latest block hash failed %v\n", err)
			}
		}
		return nil
	})

创世区块的持久化

原始方式:

func CreateBlockChainWithGenesisBlock() *BlockChain {
	//生成创世区块
	block := CreateGenesisBlock([]byte("the first block"))
	return &BlockChain{[]*Block{block}}
}
// 生成创世区块
func CreateGenesisBlock(data []byte) *Block {
	return NewBlock(nil, 1, data)
}

持久化方式:

db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(blockTableName))
		if b == nil {
			//数据表不存在
			b, err := tx.CreateBucket([]byte(blockTableName))
			if err != nil {
				log.Panicf("create backet [%s] failed %v\n", blockTableName, err)
			}
			//生成创世区块
			genesisBlock := CreateGenesisBlock([]byte("the first block"))
			//键为区块的哈希,值为区块的序列化
			err = b.Put(genesisBlock.Hash, genesisBlock.Serialize())
			if err != nil {
				log.Panicf("insert the genesis block failed %v\n", err)
			}
			blockHash = genesisBlock.Hash
			//数据库中也存储最新区块的哈希
			err = b.Put([]byte("l"), genesisBlock.Hash)
			if err != nil {
				log.Panicf("save the latest hash of genesis block failed %v\n", err)
			}
		} else {
			//数据表已存在
			blockHash = b.Get([]byte("l"))
		}
		return nil
	})
	if err != nil {
		log.Panicf("create db [%s] failed %v\n", dbName, err)
	}

新增区块的持久化

原始方式:

func (bc *BlockChain) AddBlock(prevBlockHash []byte, height int64, data []byte) {
	var newBlock *Block
	newBlock = NewBlock(prevBlockHash, height, data)
	bc.Blocks = append(bc.Blocks, newBlock)
}

持久化方式:

bc.DB.Update(func(tx *bolt.Tx) error {
		//获取数据库桶
		b := tx.Bucket([]byte(blockTableName))
		if b != nil {
			//获取对应区块
			blockTypes := b.Get(bc.Tip)
			//区块结构反序列化
			latestBlock := DeserializeBlock(blockTypes)
			//新建区块,传入参数:prevBlockHash []byte, height int64, data []byte
			newBlock := NewBlock(latestBlock.Hash, latestBlock.Height+1, data)
			//存入数据库
			bc.Tip = newBlock.Hash
			err := b.Put(newBlock.Hash, newBlock.Serialize())
			if err != nil {
				log.Panicf("insert the new block failed %v\n", err)
			}
			err = b.Put([]byte("l"), newBlock.Hash)
			if err != nil {
				log.Panicf("save the latest block hash failed %v\n", err)
			}
		}
		return nil
	})

完善区块链基本功能

由于区块链不支持删除、修改,所以当前项目区块链的功能有创建创世区块,增加区块,遍历区块链这三个功能,以后计划完善交易信息、挖矿等多个功能。相较于之前功能的完善有:1.持久化,2.新增遍历功能,3.新增迭代器

创世区块创建

通过将结构体进行序列化,从而使得区块可以通过(区块哈希–>区块信息)的方式存储到数据库中,为后期得到前一区块哈希后进行索引前一区块提供便利。

// 初始化区块链
func CreateBlockChainWithGenesisBlock() *BlockChain {
	//存储最新区块链哈希
	var blockHash []byte
	//1.创建或打开一个数据库
	db, err := bolt.Open(dbName, 0600, nil)
	//2.创建一个桶,将创世区块存入数据库中
	db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(blockTableName))
		if b == nil {
			//数据表不存在
			b, err := tx.CreateBucket([]byte(blockTableName))
			if err != nil {
				log.Panicf("create backet [%s] failed %v\n", blockTableName, err)
			}
			//生成创世区块
			genesisBlock := CreateGenesisBlock([]byte("the first block"))
			//键为区块的哈希,值为区块的序列化
			err = b.Put(genesisBlock.Hash, genesisBlock.Serialize())
			if err != nil {
				log.Panicf("insert the genesis block failed %v\n", err)
			}
			blockHash = genesisBlock.Hash
			//数据库中也存储最新区块的哈希
			err = b.Put([]byte("l"), genesisBlock.Hash)
			if err != nil {
				log.Panicf("save the latest hash of genesis block failed %v\n", err)
			}
		}
		return nil
	})
	if err != nil {
		log.Panicf("create db [%s] failed %v\n", dbName, err)
	}
	return &BlockChain{DB: db, Tip: blockHash}
}

Block的序列化代码:

// 区块结构序列化,结构体->字节数组
func (block *Block) Serialize() []byte {
	var buffer bytes.Buffer
	//序列化结构体
	encoder := gob.NewEncoder(&buffer)
	err := encoder.Encode(block)
	if err != nil {
		log.Panicf("serialize the block to byte[] failed %v", err)
	}
	return buffer.Bytes()
}

Block的反序列化代码

// 区块结构反序列化,字节数组->结构体
func DeserializeBlock(blockByte []byte) *Block {
	var block Block
	decoder := gob.NewDecoder(bytes.NewReader(blockByte))
	err := decoder.Decode(&block)
	if err != nil {
		log.Panicf("deserialize the byte[] to block failed %v", err)
	}
	return &block
}

增加区块

func (bc *BlockChain) AddBlock(data []byte) {
	//更新区块数据(insert)
	bc.DB.Update(func(tx *bolt.Tx) error {
		//获取数据库桶
		b := tx.Bucket([]byte(blockTableName))
		if b != nil {
			//获取对应区块
			blockTypes := b.Get(bc.Tip)
			//区块结构反序列化
			latestBlock := DeserializeBlock(blockTypes)
			//新建区块,传入参数:prevBlockHash []byte, height int64, data []byte
			newBlock := NewBlock(latestBlock.Hash, latestBlock.Height+1, data)
			//存入数据库
			bc.Tip = newBlock.Hash
			err := b.Put(newBlock.Hash, newBlock.Serialize())
			if err != nil {
				log.Panicf("insert the new block failed %v\n", err)
			}
			err = b.Put([]byte("l"), newBlock.Hash)
			if err != nil {
				log.Panicf("save the latest block hash failed %v\n", err)
			}
		}
		return nil
	})
}

遍历区块链

这里使用了迭代器对区块链进行优化,改善代码可读性。

func (bc *BlockChain) PrintBlockChain() {
	var curBlock *Block
	var iter *BlockChainIterator = bc.Iterator()
	fmt.Printf("打印区块链完整信息。。。\n")
	//循环读取
	for {
		curBlock = iter.Next()
		fmt.Printf("-------------------------------------------\n")
		fmt.Printf("\tHash : %x\n", curBlock.Hash)
		fmt.Printf("\tPrevBlockHash : %x\n", curBlock.PrevBlockHash)
		fmt.Printf("\tHeight : %x\n", curBlock.Height)
		fmt.Printf("\tData : %v\n", curBlock.Data)
		fmt.Printf("\tNonce : %x\n", curBlock.Nonce)
		fmt.Printf("\tTimeStamp : %x\n", curBlock.TimeStamp)
		//退出条件,由于哈希值的大数特性,需要使用函数进行比较
		var hashInt big.Int
		hashInt.SetBytes(iter.curHash)
		if big.NewInt(0).Cmp(&hashInt) == 0 {
			//遍历到了创世区块
			break
		}
	}

迭代器的实现如下:

// 实现迭代函数next()
func (iter *BlockChainIterator) Next() *Block {
	var curBlock *Block
	err := iter.DB.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(blockTableName))
		if b != nil {
			Block := b.Get(iter.curHash)
			curBlock = DeserializeBlock(Block)
			//更新迭代器当前指向结点哈希
			iter.curHash = curBlock.PrevBlockHash
		}
		return nil
	})
	if err != nil {
		log.Panicf("Iterator the db failed%v\n", err)
	} else {
		return curBlock
	}
	return nil
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/140191.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue3过渡动画实现

文章目录P14Vue3过渡&动画实现过渡动画的使用过渡CSS动画效果同时设置过渡和动画mode和appearanimate.cssgsapgsap实现数字变化认识列表的过渡列表过渡的移动动画列表的交错过渡案例P14Vue3过渡&动画实现 过渡动画的使用 <template><button click"isShow…

进入新组织项目经理如何快速提升自己的影响力?

我们在工作中&#xff0c;经常以“对事不对人”来体现他们的专业性&#xff0c;但是这点并不符合人性。更多时候对人不对事&#xff0c;反倒能提高问题的解决能力。项目经理会发现&#xff0c;很多事情的推进&#xff0c;都建立在和对方的信任的基础上&#xff0c;所以先成为对…

使用Qt开发的linux嵌入式设备监控、管理框架,监测嵌入式设备运行状态,执行远程shell,远程升级,与客户端进行文件传输

linux SPY 简介 使用Qt开发的linux嵌入式设备监控、管理框架 [客户端]&#xff1a;aes_tcp_lib 完整代码下载地址&#xff1a;使用Qt开发的linux嵌入式设备监控、管理框架 开发环境 ubuntu 20Qt 5.12Qt Creator 4.13.1 核心功能 监测嵌入式设备运行状态转发客户端消息,…

划重点!企业在采购管理中应避免的10个错误

一家企业的采购能力在很大程度上取决于其采购管理系统的有效性。当系统运行良好时&#xff0c;那么采购就会相当顺利。如果你使用的是一个低效的系统&#xff0c;那就会导致一大堆常见的采购问题。 无论采购错误的根本原因是什么&#xff0c;任其发展&#xff0c;最终会给企业…

【ZooKeeper】第二章 JavaAPI 操作

【ZooKeeper】第二章 JavaAPI 操作 文章目录【ZooKeeper】第二章 JavaAPI 操作一、Curator 简介二、Curator API1.建立连接2.创建节点3.查询节点4.修改节点5.删除节点6.Watch 事件监听三、分布式锁四、案例&#xff1a;12306售票一、Curator 简介 Curator 是 Apache ZooKeeper…

【云原生进阶之容器】第二章Controller Manager原理2.3节--Reflector分析

1 Reflector组件 1.1 背景 Reflector 是保证 Informer 可靠性的核心组件,在丢失事件,收到异常事件,处理事件失败等多种异常情况下需要考虑的细节很多。单独的listwatcher缺少重新连接和重新同步机制,有可能出现数据不一致问题。其对事件响应是同步的,如果执行复杂的操作会…

把财务分析明白的BI软件有哪些?

有财务分析这一个地狱级的在&#xff0c;什么销售、采购、库存都是渣渣。在财务分析中&#xff0c;指标运算组合可以有多样化的改变&#xff0c;资金来来回回能把人绕晕&#xff0c;一个极不起眼的疏忽都可能导致所有工作推到重来的可怕后果。即使是在擅长做大数据智能可视化分…

MySQL基础命令

MySQL基础命令 创建数据库 Show databases;查看 选择数据库 Use test; 在test数据库中创建表 create table t_stu( id int, name varchar(20) ); 查看表信息 desc t_stu&#xff1b; 添加字段 alter table t_stu add age int; 字段age成功添加 修改字段 alter tab…

springBoot使用rabbitmq并保证消息可靠性

一、理论说明 1.1、数据的丢失问题&#xff0c;可能出现在生产者、MQ、消费者中 1、如下图 1.2、生产者弄丢了数据 1、生产者将数据发送到 RabbitMQ 的时候&#xff0c;可能数据就在半路给搞丢了&#xff0c;因为网络问题啥的&#xff0c;都有可能。此时可以选择用RabbitM…

VS2019打包程序变成带运行环境的安装包

背景 给外行客户写程序的时候&#xff0c;为了避免客户麻烦&#xff0c;我们在写完程序之后&#xff0c;需要把运行环境也打包进安装包中&#xff0c;这样客户就可以一键安装使用。给客户减少麻烦的同时&#xff0c;无疑让我们也有了更多的好评。 步骤 1.写好我们要打包的程…

推荐16个前端必备的实用工具与网站

1. GitHub Desktop 对于新手来说&#xff0c;要记住那么多git命令可能有点困难&#xff0c;建议新手用git可视化工具&#xff0c;会方便很多 2. 图片在线压缩 tinypng 是一个完全免费并且高压缩率的在线压缩图片网站&#xff0c;一般能满足日常大部分压缩图片的需求&#x…

Visual Studio 2022最全安装教程(+背景图设置),一步步教会你如何安装并运行

目录visual studio 2022最全安装教程一、官网下载二、安装启动三、项目测试四、背景图设置 一、官网下载1.点击蓝色链接—->Visual Studio官网&#xff0c;进入之后是这个界面&#xff0c;选择社区版Community下载&#xff08;社区版Community是对个人免费的&#xff0c;一共…

磨金石教育科技技能干货分享|顶级黑白照片长啥样?看后令人震撼

黑白摄影往往在于一些老旧照片中见到&#xff0c;平常的摄影作品往往会强调一些色彩。黑白色调不是常用的色调。但在一些艺术摄影或者创意摄影中&#xff0c;黑白摄影依然是摄影师常用的一种风格。黑白色调往往能够给人一种肃穆、压抑的感觉。那么今天我们就通过一些作品&#…

(一)redis之Nosql数据库简介

一、技术发展 解决功能性的问题&#xff1a;Java、Jsp、RDBMS、Tomcat、HTML、Linux、JDBC、SVN 解决扩展性的问题&#xff1a;Struts、Spring、SpringMVC、Hibernate、Mybatis 解决性能的问题&#xff1a;NoSQL、Java线程、Hadoop、Nginx、MQ、ElasticSearch 1、Web1.0 W…

VMware 基本设置

VMware 基本设置 文章目录VMware 基本设置摘要全局优化内存优先级更新反馈虚拟机系统优化尽可能多的分配资源去掉多余的组件开启3D图形加速和增加显存大小高级设置优化显示关键字&#xff1a; 显示、 内存、 优化、 命令栏、 虚拟机摘要 做Qt开发 久了&#xff0c;跨平台应该…

【BSP视频教程】BSP视频教程第25期:CAN/CANFD/CANopen专题,CAN知识点干货分享, 收发执行过程和错误帧处理(2023-01-03)

视频教程汇总帖&#xff1a;【学以致用&#xff0c;授人以渔】2023视频教程汇总&#xff0c;DSP第10期&#xff0c;ThreadX第5期&#xff0c;BSP驱动第25期&#xff0c;USB实战第5期&#xff0c;GUI实战第3期&#xff08;2023-01-03&#xff09; - STM32F429 - 硬汉嵌入式论坛 …

梦之光芒Monyer (全关解析)

目录 前言: 第0关 描述: 过程: 第1关 描述: 过程: 第2关 描述: 过程: 第3关 描述: 过程: 第4关 描述: 过程: 第5关 描述: 过程: 第6关 描述: 过程: 第7关 描述: 过程: 第8关 描述: 过程: 第9关 描述: 过程: 第10关 描述: 过程: 第11关 描…

spring mvc文档阅读笔记——01

目录标题一、文档地址二、文档目录索引简介&#xff08;一&#xff09;Spring Web MVC1. 常用注解的使用2. 过滤器3. WebMvcConfigurer&#xff08;二&#xff09;RestTemplate&#xff08;三&#xff09;WebSocket三、Spring Web MVC&#xff08;一&#xff09;ControllerRest…

CSS知识点精学3-CSS浮动

目录 结构伪类选择器 伪元素 标准流 浮动 清除浮动 结构伪类选择器 目标&#xff1a;能够使用结构伪类选择器在HTML中定位元素 1.作用&#xff1a;工具元素在HTML中的结构关系查找元素 2.优势&#xff1a;减少对于HTML中类的依赖&#xff0c;有利于保持代码整洁 3.场景…

5G NR标准 第10章 物理层控制信令

为了支持下行链路和上行链路传输信道的传输&#xff0c;需要某些相关联的控制信令。这种控制信令通常被称为L1/L2控制信令&#xff0c;表示相应的信息部分来自物理层&#xff08;层1&#xff09;并且部分来自MAC&#xff08;层2&#xff09;。在本章中&#xff0c;将描述下行链…