区块链项目 - 3 区块存储和遍历

区块链项目 - 3 区块存储和遍历,第1张

3.存储和遍历

现在我们想要把区块存储进数据库中并且根据需要遍历某个区块的信息

3.1区块的存储

这里我们采用Go语言自带的数据库,如果在安装Go语言时没有安装,则需要通过如下链接安装

go get github.com/boltdb/bolt

安装成功后才能实现区块的存储和遍历

我们用键值对来实现对区块的存储,key:当前区块的哈希,value:当前区块序列化后的数组。当然遍历时我们就需要进行反序列化以还原区块信息,现在我们一并构建这它们

/Users/xxx/go/src/publicChain/part15-persistence/BLC/Block.go

//SerializeBlock

func (block *Block) Serialize() []byte {

	var result bytes.Buffer
	encoder := gob.NewEncoder(&result)

	err := encoder.Encode(block)
	if err != nil {
		log.Panic(err)
	}
	return result.Bytes()
}

//DeserializeBlock

func DeserializeBlock(blockBytes []byte) *Block {
	var block Block

	decoder := gob.NewDecoder(bytes.NewReader(blockBytes))
	err := decoder.Decode(&block)
	if err != nil {
		log.Panic()
	}
	return &block
}

在此之前,我们会将新产生的区块加入到链上,现在我们重新改写一下:在这里我们将会把新产生的区块存入数据库中的数据表里。事实上,只要一个区块的产生依赖于上一个区块,那这些区块自动就是一条“区块链”,所以并不需要我们再重构一条链,我们只需要通过一定的方式处理这些区块即可。前面所说的把一个个区块加入到“链”上其实就是一种处理方式,现在我们把这种方式改变一下,也就是说直接把区块装入数据库中。下面是实现代码:

/Users/xxx/go/src/publicChain/part15-persistence/BLC/Blockchain.go

const dbName = "blockChain.db"
const blockTableName = "blocks"

type Blockchain struct {
	Tip []byte   //the hash of current block
	DB  *bolt.DB //database
}
//add a new block to blockchain

func (blc *Blockchain) AddBlockToBlockchain(data string) {

	err := blc.DB.Update(func(tx *bolt.Tx) error {
		//get a table

		b := tx.Bucket([]byte(blockTableName))

		//crate a new block

		if b != nil {
			//Get the latest block
			blockBytes := b.Get(blc.Tip)
			//Deserialize
			block := DeserializeBlock(blockBytes)
			//Serialize the block and store it in the database
			newBlock := Newblock(block.Height+1, block.Hash, data)
			err := b.Put(newBlock.Hash, newBlock.Serialize())
			if err != nil {
				log.Panic(err)
			}
			//Update the hash corresponding to l in the database
			err = b.Put([]byte("l"), newBlock.Hash)
			if err != nil {
				log.Panic(err)
			}
			//Update blockchain tip
			blc.Tip = newBlock.Hash
		}
		return nil
	})
	if err != nil {
		log.Panic(err)
	}
}

//1 creat a blockchain with Genesis Block

func CreatBlockchainWithGenesisBlock() *Blockchain {
	//creat a database
	db, err := bolt.Open(dbName, 0600, nil)
	if err != nil {
		log.Fatal(err)
	}

	var blockHash []byte

	err = db.Update(func(tx *bolt.Tx) error {
		//creat a table
		b, err := tx.CreateBucket([]byte(blockTableName))

		if err != nil {
			log.Panic()
		}
		if b != nil {
			genesisBlock := CrateGenesisBlock("Genesis Data......")

			//Store the genesis block into a table
			err := b.Put(genesisBlock.Hash, genesisBlock.Serialize())
			if err != nil {
				log.Panic(err)
			}
			//Store the hash of current block
			err = b.Put([]byte("l"), genesisBlock.Hash)
			if err != nil {
				log.Panic(err)
			}
			blockHash = genesisBlock.Hash
		}
		return nil
	})
	return &Blockchain{blockHash, db}
}
3.2区块的遍历

在上面的代码中,我们已经可以将新产生的区块装入数据库中的数据表中,现在我们遍历打印

/Users/xxx/go/src/publicChain/part16-persistence-iteration/BLC/Blockchain.go

//iterate over all blocks

func (blc *Blockchain) PrintChain() {

	var block *Block

	var currentHash []byte = blc.Tip
	for {
		err := blc.DB.View(func(tx *bolt.Tx) error {

			b := tx.Bucket([]byte(blockTableName))
			if b != nil {
				// Get the byte array of the current block
				blockBytes := b.Get(currentHash)
				//Deserialize
				block = DeserializeBlock(blockBytes)

				fmt.Printf("Height:%d\n", block.Height)
				fmt.Printf("PreBlockHash:%x\n", block.PreBlockHash)
				fmt.Printf("Data:%s\n", block.Data)
				fmt.Printf("Timestamp:%d\n", block.Timestamp)
				fmt.Printf("Hash:%x\n", block.Hash)
				fmt.Printf("Nonce:%d\n", block.Nonce)

			}
			return nil
		})

		fmt.Println()

		if err != nil {
			log.Panic(err)
		}
		var hashInt big.Int
		hashInt.SetBytes(block.PreBlockHash)

		if big.NewInt(0).Cmp(&hashInt) == 0 {
			break
		}
		currentHash = block.PreBlockHash
	}
}

我们在main函数中稍作修改,然后运行,非常棒,我们能够成功的遍历区块并且打印它们

func main() {
	//creat a Genesis Block
	Blockchain := BLC.CreatBlockchainWithGenesisBlock()
	defer Blockchain.DB.Close()

	// new block

	Blockchain.AddBlockToBlockchain("Send 100RMB To Zhangqiang")

	Blockchain.AddBlockToBlockchain("Send 300RMB To Xinyi")

	Blockchain.AddBlockToBlockchain("Send 200RMB To Gaojian")

	Blockchain.AddBlockToBlockchain("Send 500RMB To Xiaochu")

	Blockchain.PrintChain()
}
0000b5430784409081e82c0de2b08267f1a803ccfb066fdc526f223ede70799d
000008339b4489740c7bbb2d57b72cba32744359feab79b9ef0e0ef587cb0e24
0000d68e0482e967652f34a3a26be66920d3444851764a4abdb5a16e76742114
0000e513daca4c4148da99343e036e7d77c673ff6039e7f8420483de0c63db45
0000f6bcd5f300d6ebd557a9a10128baea0a89c79b03e4614a0b9898aece1a40
Height:5
PreBlockHash:0000e513daca4c4148da99343e036e7d77c673ff6039e7f8420483de0c63db45
Data:Send 500RMB To Xiaochu
Timestamp:1645877202
Hash:0000f6bcd5f300d6ebd557a9a10128baea0a89c79b03e4614a0b9898aece1a40
Nonce:74522

Height:4
PreBlockHash:0000d68e0482e967652f34a3a26be66920d3444851764a4abdb5a16e76742114
Data:Send 200RMB To Gaojian
Timestamp:1645877201
Hash:0000e513daca4c4148da99343e036e7d77c673ff6039e7f8420483de0c63db45
Nonce:48490

Height:3
PreBlockHash:000008339b4489740c7bbb2d57b72cba32744359feab79b9ef0e0ef587cb0e24
Data:Send 300RMB To Xinyi
Timestamp:1645877201
Hash:0000d68e0482e967652f34a3a26be66920d3444851764a4abdb5a16e76742114
Nonce:7929

Height:2
PreBlockHash:0000b5430784409081e82c0de2b08267f1a803ccfb066fdc526f223ede70799d
Data:Send 100RMB To Zhangqiang
Timestamp:1645877201
Hash:000008339b4489740c7bbb2d57b72cba32744359feab79b9ef0e0ef587cb0e24
Nonce:38449

Height:1
PreBlockHash:0000000000000000000000000000000000000000
Data:Genesis Data......
Timestamp:1645877201
Hash:0000b5430784409081e82c0de2b08267f1a803ccfb066fdc526f223ede70799d
Nonce:36943
3.3 时间格式格式化输出

更改一下上述PrintChain方法中的时间打印方式

fmt.Printf("Timestamp:%s\n", time.Unix(block.Timestamp, 0).Format("2006-01-02 03:04:05 PM"))

部分输出结果如下,我们看到时间被格式化打印而出了

Height:5
PreBlockHash:0000d41c2f482e9f8885e31065368da0ddbe0b0aa6d4797cab09a8d0c42eb0d2
Data:Send 500RMB To Xiaochu
Timestamp:2022-02-26 08:39:34 PM
Hash:00001c26642bbf01f1974a5067cf0dbc0aa62726a89e26059bc0fa3ff7033459
Nonce:13611
3.4 迭代器优化

现在我们来对迭代器所在部分的代码进行优化。让我们先来分析一下思路:第一步,根据最新区块的哈希找到最新的区块,第二步,根据区块数据寻找上一个区块,层层回溯,具体代码实现如下:

/Users/xxx/go/src/publicChain/part16-persistence-iteration/BLC/Blockchain.go

func (blc *Blockchain) Iterator() *BlockChainIterator {
	return &BlockChainIterator{blc.Tip, blc.DB}
}

type BlockChainIterator struct {
	CurrentHash []byte
	DB          *bolt.DB
}

func (blockChainIterator *BlockChainIterator) Next() *Block {

	var block *Block

	err := blockChainIterator.DB.View(func(tx *bolt.Tx) error {
		//get a table

		b := tx.Bucket([]byte(blockTableName))
		if b != nil {
			currentBlockBytes := b.Get(blockChainIterator.CurrentHash)
			//get current block
			block = DeserializeBlock(currentBlockBytes)

			//update the hash in Iterator
			blockChainIterator.CurrentHash = block.PreBlockHash
		}
		return nil
	})
	if err != nil {
		log.Panic(err)
	}
	return block
}

//iterate over all blocks

func (blc *Blockchain) PrintChain() {
	blockChainIterator := blc.Iterator()

	for {
		block := blockChainIterator.Next()

		fmt.Printf("Height:%d\n", block.Height)
		fmt.Printf("PreBlockHash:%x\n", block.PreBlockHash)
		fmt.Printf("Data:%s\n", block.Data)
		fmt.Printf("Timestamp:%s\n", time.Unix(block.Timestamp, 0).Format("2006-01-02 03:04:05 PM"))
		fmt.Printf("Hash:%x\n", block.Hash)
		fmt.Printf("Nonce:%d\n", block.Nonce)

		fmt.Println()

		var hashInt big.Int
		hashInt.SetBytes(block.PreBlockHash)

		if big.NewInt(0).Cmp(&hashInt) == 0 {
			break
		}
	}
}

上述代码能够正常运行,部分结果如下:

Height:2
PreBlockHash:00004fe9eae1cd4285edcc5d112eb1df4c1cf68a214c612a3afb024ab4f35d43
Data:Send 100RMB To Zhangqiang
Timestamp:2022-02-27 08:54:29 AM
Hash:0000bebc7cb0c274731969cf2ca83c45eb4cb490ed65475cb62fc8f2a2825b6d
Nonce:70176

并且我们可以把上述迭代器部分的代码放到独立文件中

/Users/xxx/go/src/publicChain/part16-persistence-iteration/BLC/BlockchainIterator.go

package BLC

import (
	"github.com/boltdb/bolt"
	"log"
)

type BlockChainIterator struct {
	CurrentHash []byte
	DB          *bolt.DB
}

func (blockChainIterator *BlockChainIterator) Next() *Block {

	var block *Block

	err := blockChainIterator.DB.View(func(tx *bolt.Tx) error {
		//get a table

		b := tx.Bucket([]byte(blockTableName))
		if b != nil {
			currentBlockBytes := b.Get(blockChainIterator.CurrentHash)
			//get current block
			block = DeserializeBlock(currentBlockBytes)

			//update the hash in Iterator
			blockChainIterator.CurrentHash = block.PreBlockHash
		}
		return nil
	})
	if err != nil {
		log.Panic(err)
	}
	return block
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/994081.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存