区块链之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
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值