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

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

基于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作为keyBlock节点的二进制流作为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://outofmemory.cn/langs/995475.html

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

发表评论

登录后才能评论

评论列表(0条)

保存