当前位置:首页 » 《休闲阅读》 » 正文

基于bolt数据库实现简单的区块链 day(2)_初心魏的博客

25 人参与  2022年02月17日 08:02  分类 : 《休闲阅读》  评论

点击全文阅读


基于bolt数据库实现简单的区块链 day(2)

  • 一、blot数据库
    • 1.1 安装Boltdb数据库
    • 1.2 存储数据
    • 1.3 读取数据
  • 二、基于bolt数据库实现简单的区块链
    • 2.1区块链结构体
    • 2.2 创建带有创世块的区块链
  • 三、gob包
    • 3.1 介绍
    • 3.2 编码解码实例
    • 3.3 block文件
    • 3.4 blockChain文件
    • 3.5 proofOfWork文件
  • 四、迭代器
    • 4.1 迭代器的数据结构
    • 4.2 创建迭代器函数
    • 4.3 迭代函数
    • 4.4 使用迭代器进行迭代
    • 4.5 结果
  • 五、命令行
    • 5.1 获取命令行
    • 5.2 通过命令行得到要运行的方法
    • 5.3 执行命令行

一、blot数据库

1.1 安装Boltdb数据库

  • 他会下载到gopath下的路径
go get github.com/boltdb/bolt
  • 我下载好的路径就是
C:\Users\Administrator\go\src\github.com\boltdb\bolt

1.2 存储数据

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

func main(){
  //打开数据库,打开不了则创建数据库
	db, err := bolt.Open("BitCoin.db",0600,nil)
	if err != nil {
		log.Panicln("打开数据库出错")
	}
	updateErr := db.Update(func(tx *bolt.Tx) error {
		//打开Bucket
		bucket := tx.Bucket([]byte("BitCoin"))
		if bucket == nil{
			fmt.Println("当前桶不存在")
			bucket, err = tx.CreateBucket([]byte("BitCoin"))
			if err != nil {
				log.Panicln("创建桶失败")
			}
		}
		bucket.Put([]byte("name"), []byte("jack"))
		bucket.Put([]byte("age"), []byte("12"))
		bucket.Put([]byte("address"), []byte("西安"))
		//返回Nil,以便数据库进行操作
		return nil
	})
	if updateErr != nil{
		log.Panicln("往数据库存储数据失败")
	}
}

1.3 读取数据

db.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte("BitCoin"))
		if bucket == nil{
			log.Panicln("查找桶失败")
		}
		name := string(bucket.Get([]byte("name")))
		age := string(bucket.Get([]byte("age")))
		address := string(bucket.Get([]byte("address")))
		fmt.Println("name:",name, "age:",age, "address:",address)
		return nil
	})

二、基于bolt数据库实现简单的区块链

2.1区块链结构体

  • 替换原来的指针数组
  • Block的hash作为key
  • Block节点的二进制流作为value
  • 需要一个常量记录最后一个节点的hash值,不然无法创建新节点时记录上一节点的哈希值
// BlockChain 区块链结构体
//将区块链存储在数据库中
type BlockChain struct {
	db *bolt.DB
	//存储最后一个区块的hash
	lastBlockHash []byte
}

2.2 创建带有创世块的区块链

// CreateBlockChain 创建带有一个创世块的区块链
func CreateBlockChain() *BlockChain{
	var blockChain *BlockChain
	//创世块
	genesisBlock := GenesisBlock()
	db, err := bolt.Open("BlockCoin", 0600, nil)
	if err != nil{
		log.Panicln("打开数据库出错")
	}
	db.Update(func(tx *bolt.Tx) error {
		//取到bucket
		bucket := tx.Bucket([]byte("BlockCoin"))
		if bucket == nil{
			bucket, err := tx.CreateBucket([]byte("BlockCoin"))
			if err != nil{
				log.Panicln("创建bucket出错")
			}
			//存储创世块的信息
			bucket.Put([]byte(genesisBlock.Hash), genesisBlock.blockToByte())
			//存储最后一个区块的hash信息
			lashBlockHash = genesisBlock.Hash
		}else {
			//存储创世块的信息
			bucket.Put([]byte(genesisBlock.Hash), genesisBlock.blockToByte())
			//存储最后一个区块的hash信息
			lashBlockHash = genesisBlock.Hash
		}
		blockChain.db = db
		blockChain.lastBlockHash = lashBlockHash
		return nil
	})
	return blockChain
}

三、gob包

3.1 介绍

golang自带的一个数据结构编码/解码的工具。

3.2 编码解码实例

type Teacher struct {
	Name string
	Age int
	Address string
}
func main(){
    var mrsLi Teacher = Teacher{
		Name: "jack",
		Age: 59,
		Address: "xian",
	}

	//1.编码的数据放到buffer中
	var buffer bytes.Buffer
    //2.定义一个编码器
	encoder := gob.NewEncoder(&buffer)
	//3.使用编码器进行编码
	err := encoder.Encode(&mrsLi)
	if err != nil{
		fmt.Println("编码失败")
	}
	fmt.Println("编码后的李老师:",buffer.Bytes())
	//1.定义一个解码器
	decoder := gob.NewDecoder(bytes.NewReader(buffer.Bytes()))
	//2.定义解码后承接的变量
	var mrsWu Teacher
	err1 := decoder.Decode(&mrsWu)
	if err1 != nil{
		fmt.Println("解码出错")
	}
	fmt.Println(mrsWu.Age,mrsWu.Address,mrsWu.Name)
}

3.3 block文件

package main

import (
	"bytes"
	"crypto/sha256"
	"encoding/binary"
	"encoding/gob"
	"fmt"
	"log"
	"time"
)

// Block 区块结构体
type Block struct{
	//1.区块版本号
	Version uint64
	//2.前区块Hash
	PreBlockHash []byte
	//3.merkel根
	MerkelRoot []byte
	//4.时间戳
	TimeStamp uint64
	//5.难度值
	Difficulty uint64
	//6.随机数,也就是挖矿要找的数据
	Nonce uint64
	//a.当前区块的Hash值,在现实比特币中没有这个
	Hash []byte
	Data []byte
}
// Uint64ConvertByte 将uint64类型转换为[]byte{}类型
func Uint64ConvertByte(data uint64)[]byte{
	var buffer bytes.Buffer
	err := binary.Write(&buffer, binary.BigEndian,data)
	if err != nil {
		log.Panicln(err)
	}
	return buffer.Bytes()
}
// CreateBlock 创建block
func CreateBlock(preBlockHash []byte, data string) *Block{
	block := Block{
		Version: 00,
		PreBlockHash: preBlockHash,
		MerkelRoot: []byte{},
		TimeStamp: uint64(time.Now().Unix()),
		Difficulty: 0,//随便填一个
		Nonce: 0,//随便填一个
		Data: []byte(data),
		Hash: []byte{},
	}
	//进行挖矿,得到挖矿成功后的hash和随机值
	pow := CreatePOW(&block)
	//挖矿模拟,不断改变随机数,计算hash,直到找到合适的hash
	hash,nonce := pow.Run()
	//根据挖矿结果对区块数据不断进行更新
	block.Hash = hash
	block.Nonce = nonce
	//block.SetHash()
	return &block
}
// GenesisBlock 创建创世块,即第一个区块
func GenesisBlock() *Block{
	genesisBlock := CreateBlock([]byte{},"第一个创世块,牛逼")
	return genesisBlock
}
// SetHash 计算当前区块的hash
func (block *Block)SetHash(){
	var blockInfo []byte
	//...的作用是把data数组打散,一个个装进preBlockHash切片中
	//blockInfo = append(blockInfo, Uint64ConvertByte(block.Version)...)
	//blockInfo = append(blockInfo, block.PreBlockHash...)
	//blockInfo = append(blockInfo, block.MerkelRoot...)
	//blockInfo = append(blockInfo, Uint64ConvertByte(block.TimeStamp)...)
	//blockInfo = append(blockInfo, Uint64ConvertByte(block.Difficulty)...)
	//blockInfo = append(blockInfo, Uint64ConvertByte(block.Nonce)...)
	//blockInfo = append(blockInfo, block.Data...)
	// 创建一个二维数组
	tem := [][]byte{
		Uint64ConvertByte(block.Version),
		block.PreBlockHash,
		block.MerkelRoot,
		Uint64ConvertByte(block.TimeStamp),
		Uint64ConvertByte(block.Difficulty),
		Uint64ConvertByte(block.Nonce),
		block.Data,
	}
	//将二维byte数组连接成一维byte数组
	bytes.Join(tem, []byte{})
	//hash是一个字节数组
	Hash := sha256.Sum256(blockInfo)
	//block.Hash作为Hash的切片
	block.Hash = Hash[:]
}
func (block *Block) blockToByte()[]byte{
	return []byte{}
}
func (block *Block)Serialize() []byte{
	//1.编码的数据放到buffer中
	var buffer bytes.Buffer
	//2.定义一个编码器
	encoder := gob.NewEncoder(&buffer)
	//3.使用编码器进行编码
	err := encoder.Encode(block)
	if err != nil{
		fmt.Println("编码失败")
	}
	return buffer.Bytes()
}
func (block *Block) DeSerialize(buffer []byte) *Block{
	//1.定义一个解码器
	decoder := gob.NewDecoder(bytes.NewReader(buffer))
	//2.定义解码后承接的变量
	err1 := decoder.Decode(block)
	if err1 != nil{
		fmt.Println("解码出错")
	}
	return block
}

3.4 blockChain文件

package main

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

// BlockChain 区块链结构体
//将区块链存储在数据库中
type BlockChain struct {
	db *bolt.DB
	//存储最后一个区块的hash
	lastBlockHash []byte
}
var  lashBlockHash  []byte
// CreateBlockChain 创建带有一个创世块的区块链
func CreateBlockChain() *BlockChain{
	var blockChain *BlockChain = &BlockChain{}
	//创世块
	genesisBlock := GenesisBlock()
	db, err := bolt.Open("BlockCoin", 0600, nil)
	if err != nil{
		log.Panicln("打开数据库出错")
	}
	db.Update(func(tx *bolt.Tx) error {
		//取到bucket
		bucket := tx.Bucket([]byte("BlockCoin"))
		if bucket == nil{
			bucket, err := tx.CreateBucket([]byte("BlockCoin"))
			if err != nil{
				log.Panicln("创建bucket出错")
			}
			//存储创世块的信息
			bucket.Put([]byte(genesisBlock.Hash), genesisBlock.Serialize())
			//存储最后一个区块的hash信息
			lashBlockHash = genesisBlock.Hash
		}else {
			//存储创世块的信息
			bucket.Put([]byte(genesisBlock.Hash), genesisBlock.Serialize())
			//存储最后一个区块的hash信息
			lashBlockHash = genesisBlock.Hash
		}
		//试着输出一下genesisBlock的信息
		//fmt.Println(genesisBlock.Serialize())
		blockChain.db = db
		blockChain.lastBlockHash = lashBlockHash
		return nil
	})
	return blockChain
}


// AddBlock 当前区块的前一区块哈希值字段从区块链中获取
func (blockChain *BlockChain)AddBlock(data string){
	db := blockChain.db
	lastBlockChain := blockChain.lastBlockHash
	block := CreateBlock(lastBlockChain, data)
	//更新区块链条最后一个区块的hash信息
	blockChain.lastBlockHash = block.Hash
	fmt.Printf("当前区块的hash%d:\n",block.Hash)
	fmt.Println("当前区块的数据:",string(block.Data))
	fmt.Println("当前区块的随机值:",block.Nonce)
	db.Update(func(tx *bolt.Tx) error {
		//取到bucket
		bucket := tx.Bucket([]byte("BlockCoin"))
		if bucket == nil {
			bucket, err := tx.CreateBucket([]byte("BlockCoin"))
			if err != nil {
				log.Panicln("创建bucket出错")
			}
			//存储区块的信息
			bucket.Put([]byte(block.Hash), block.Serialize())
		} else {
			//存储区块的信息
			bucket.Put([]byte(block.Hash), block.Serialize())
		}
		return nil
	})
}

3.5 proofOfWork文件

package main

import (
	"bytes"
	"crypto/sha256"
	"math/big"
)

// ProofOfWork 挖矿模块
// ProofOfWork 工作量证明机制
type ProofOfWork struct {
	block *Block
	target *big.Int
}
//CreatePOW 创建ProofOfWork
func CreatePOW(block *Block) *ProofOfWork{
	pow := ProofOfWork{
		block: block,
	}
	target :=  "0000100000000000000000000000000000000000000000000000000000000000"
	bigNum := big.Int{}
	//res是指针类型的
	res, _ := bigNum.SetString(target, 16)
	pow.target = res
    return &pow
}
//Run 返回一个Hash值和随机数
func (pow *ProofOfWork) Run()([]byte, uint64){
	tmpBigInt := &big.Int{}
	//与给定的目标哈希值进行比较,小于则挖矿成功
	var nonce uint64 = 0
	var hash [32]byte
	for{
		block := pow.block
		tem := [][]byte{
			Uint64ConvertByte(block.Version),
			block.PreBlockHash,
			block.MerkelRoot,
			Uint64ConvertByte(block.TimeStamp),
			Uint64ConvertByte(block.Difficulty),
			Uint64ConvertByte(nonce),
			block.Data,
		}
		// blockInfo 拼装好的数据
		blockInfo := bytes.Join(tem, []byte(""))
		hash = sha256.Sum256(blockInfo)
		tmpBigInt.SetBytes(hash[:])
		res := tmpBigInt.Cmp(pow.target)
		if res == -1{
			break
		}
		nonce ++
	}
   return hash[:],nonce
}

四、迭代器

4.1 迭代器的数据结构

type BlockChainIterator struct {
	DB *bolt.DB
	//当前hash的指针
	CurrentHashPtr []byte
}

4.2 创建迭代器函数

  • 迭代器函数是属于区块链的函数
func (blockChain *BlockChain)  NewBlockChainIterator() *BlockChainIterator{
	//如何判断blockChain是否存在??
	blockChainIterator := BlockChainIterator{
		 DB: blockChain.db,
		 CurrentHashPtr: blockChain.lastBlockHash,
	}
	return &blockChainIterator
}

4.3 迭代函数

  • 迭代函数是属于迭代器结构体
// nextBlock 进行迭代: 1.返回一个Block 2.迭代指针往前移
func (blockChainIterator *BlockChainIterator)nextBlock() *Block{
	var block *Block
	db := blockChainIterator.DB
	db.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte("BlockCoin"))
		if bucket == nil{
			log.Panicln("迭代时出错")
		}
		currentHash := bucket.Get(blockChainIterator.CurrentHashPtr)
		//得到Block对象
		block = DeSerialize(currentHash)
		//指针往前移动
		blockChainIterator.CurrentHashPtr = block.PreBlockHash
		return nil
	})
	return block
}

4.4 使用迭代器进行迭代

func main() {
	//创建第一个区块链
	firstBlockChain := CreateBlockChain()
	//fmt.Println(firstBlockChain.lastBlockHash)
	firstBlockChain.AddBlock("jack向sheep转了50比特币")
	firstBlockChain.AddBlock("jack向sheep转了80比特币")
	//得到迭代器
	 iterator := firstBlockChain.NewBlockChainIterator()
	//进行迭代
	for  {
		block := iterator.nextBlock()
		fmt.Println("-------------------------------")
		fmt.Printf("当前区块的hash:%x\n",block.Hash)
		fmt.Printf("上一区块的hash:%x\n",block.PreBlockHash)
		fmt.Printf("当前区块的数据:%s\n",string(block.Data))
		//判断是否迭代完毕
		if len(iterator.CurrentHashPtr) == 0{
			fmt.Println("迭代结束")
			break
		}
	}
}

4.5 结果

当前区块的hash[0 0 11 253 51 145 39 252 74 72 27 243 138 170 24 86 207 130 33 24 47 50 34 172 2 182 220 230 77 77 38 33]:
当前区块的数据: jack向sheep转了50比特币
当前区块的随机值: 1784998
当前区块的hash[0 0 0 30 7 7 134 66 43 201 134 147 176 39 52 67 42 89 53 28 162 81 219 229 62 109 158 31 180 75 191 110]:
当前区块的数据: jack向sheep转了80比特币
当前区块的随机值: 1897998
-------------------------------
当前区块的hash:0000001e070786422bc98693b02734432a59351ca251dbe53e6d9e1fb44bbf6e
上一区块的hash:00000bfd339127fc4a481bf38aaa1856cf8221182f3222ac02b6dce64d4d2621
当前区块的数据:jack向sheep转了80比特币
-------------------------------
当前区块的hash:00000bfd339127fc4a481bf38aaa1856cf8221182f3222ac02b6dce64d4d2621
上一区块的hash:00000dac365228f51b7ca6063757ea3aa0458612a47048e7779f47edd2a6a6bc
当前区块的数据:jack向sheep转了50比特币
-------------------------------
当前区块的hash:00000dac365228f51b7ca6063757ea3aa0458612a47048e7779f47edd2a6a6bc
上一区块的hash:
当前区块的数据:第一个创世块,牛逼
迭代结束

Process finished with the exit code 0

五、命令行

5.1 获取命令行

list := os.Args

5.2 通过命令行得到要运行的方法

  • 比如go cmd addBlock --data “添加新的交易”,通过参数得知需要执行添加区块的功能。
type Cmd struct {
	bChain *BlockChain
}
const Usage = `addBlock --data 添加区块
printBlock 输出区块`

func (cmd *Cmd) Run(){
	list := os.Args
	if len(list) < 2{
		fmt.Println(Usage)
	}else{
		switch list[1] {
		case "addBlock":
			fmt.Println("添加区块")
			fmt.Println(list[2], list[3])
			//cmd.bChain.AddBlock()
		case "printBlock":fmt.Println("输出区块")
		default:
			fmt.Println(Usage)
		}
	}
}

5.3 执行命令行

go build *.go
./cmd addBlock --data "添加一笔新的交易"

点击全文阅读


本文链接:http://zhangshiyu.com/post/34847.html

区块  迭代  创建  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1