创建区块链实现之v3本地持久化(bolt数据库的序列化和反序列化)和命令行参数
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了创建区块链实现之v3本地持久化(bolt数据库的序列化和反序列化)和命令行参数相关的知识,希望对你有一定的参考价值。
在之前的版本上修改,实现命令添加data(记账信息)
block.go
package main
import (
"time"
"bytes"
"encoding/gob"
)
//定义块结构
type Block struct{
Version int64
PrevBlockHash []byte
Hash []byte
TimeStamp int64
TargetBits int64
Nonce int64
MerKelRoot []byte
Data []byte
}
func (block *Block)Serialize()[]byte{
var buffer bytes.Buffer
encoder := gob.NewEncoder(&buffer)
err := encoder.Encode(block)
CheckErr("Serialize", err)
return buffer.Bytes()
}
func Deserialize(data []byte)*Block{
decoder := gob.NewDecoder(bytes.NewReader(data))
var block Block
err := decoder.Decode(&block)
CheckErr("Deserialize", err)
return &block
}
//设定创建块的方法
func NewBlock(data string, prevBlockHash []byte) *Block{
block := &Block{
Version:1,
PrevBlockHash:prevBlockHash,
//Hash:
TimeStamp:time.Now().Unix(),
TargetBits:10,
Nonce:0,
MerKelRoot:[]byte{},
Data:[]byte(data),
}
//block.SetHash() //设置区块的哈希值--->>> v2中来自工作量证明
pow := NewProofOfWork(block)
nonce, hash := pow.Run()
block.Hash = hash
block.Nonce = nonce
return block
}
// 添加哈希值---->> v2中来自pow
//func (block *Block)SetHash(){
// tmp := [][]byte{
// //实现int类型转换为byte类型的工具函数
// IntToByte(block.Version),
// block.PrevBlockHash,
// IntToByte(block.TimeStamp),
// block.MerKelRoot,
// IntToByte(block.Nonce),
// block.Data,
// }
// //将区块的各个字段链接成一个切片,使用【】byte{}进行链接,目的是避免污染源区块的信息
// data := bytes.Join(tmp,[]byte{})
//
// //对区块进行sha256哈希算法,返回值为[32]byte数组,不是切片
// hash := sha256.Sum256(data)
// block.Hash = hash[:]//由数组转换为切片
//}
// 创世块的创建,它的钱一个去魁岸的哈希值为空
func NewGenesisBlock() *Block{
return NewBlock("Genesis Block!",[]byte{})
}
blockchain.go
package main
import (
"blockChain/v3/boltdb/bolt"
"os"
)
//定义区块链条
type BlockChain struct{
//blocks []*Block
db *bolt.DB
lastHash []byte
}
const dbfile = "blockChainDb.db"
const blockBucket = "blockbucket"
const lasthash = "lastHash"
// 创建区块链,并且添加创世块
func NewBlockChain() *BlockChain{
//return &BlockChain{[]*Block{
// NewGenesisBlock(),
//}}
db, err := bolt.Open(dbfile, 0600, nil)
CheckErr("NewBlockChain1",err)
var lastHash []byte
db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(blockBucket))
if bucket != nil {
//读取lasthash
lastHash = bucket.Get([]byte(lasthash))
}else{
//创建bucket
//写数据
genesis := NewGenesisBlock()
bucket, err :=tx.CreateBucket([]byte(blockBucket))
CheckErr("NewBlockChain1",err)
//1)写入区块
err = bucket.Put(genesis.Hash, genesis.Serialize()) //TODO
CheckErr("NewBlockChain2", err)
//2) 写入lasthash
err = bucket.Put([]byte(lasthash), genesis.Hash)
CheckErr("NewBlockChain3", err)
//3) 给变量赋值
lastHash = genesis.Hash
}
return nil
})
return &BlockChain{db,lastHash}
}
//添加区块
func (bc *BlockChain)AddBlock(data string){
var prevBlockHash []byte
err := bc.db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(blockBucket))
lastHash :=bucket.Get([]byte(lasthash))
prevBlockHash = lastHash
return nil
})
CheckErr("AddBlock1", err)
block :=NewBlock(data, prevBlockHash)
err = bc.db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(blockBucket))
err := bucket.Put(block.Hash, block.Serialize())
CheckErr("AddBlock2", err)
err = bucket.Put([]byte(lasthash), block.Hash)
CheckErr("AddBlock3", err)
bc.lastHash = block.Hash
return nil
})
CheckErr("AddBlock4", err)
}
//定义迭代器struct
type BlockChainIterator struct{
db *bolt.DB
currentHash []byte
}
//获取到迭代器
func (bc *BlockChain)Iterator() *BlockChainIterator {
return &BlockChainIterator{bc.db, bc.lastHash}
}
//迭代器的Next方法
func (it *BlockChainIterator) Next() *Block{
var block *Block
err := it.db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(blockBucket))
if bucket == nil{
os.Exit(1)
}
blockTmp := bucket.Get(it.currentHash)
block = Deserialize(blockTmp)
it.currentHash = block.PrevBlockHash
return nil
})
CheckErr("Next", err)
return block
}
utils.go
package main
import (
"bytes"
"encoding/binary"
"fmt"
"os"
)
func IntToByte(num int64)[]byte{
var buffer bytes.Buffer
err := binary.Write(&buffer, binary.BigEndian, num)
//if err != nil{
// fmt.Println("IntToByte err occur:",err)
// os.Exit(1)
//}
CheckErr("I", err)
return buffer.Bytes()
}
//@CheckErr错误检查服用,pos参数为方法名称字符串, err为具体错误信息
func CheckErr(pos string,err error){
if err != nil{
fmt.Println("current method:%s
", pos, "err occur%
:",err)
os.Exit(1)
}
}
proofofwork.go
package main
import (
"math/big"
"bytes"
"math"
"crypto/sha256"
"fmt"
)
const targetBits = 24 //假定难度值
type ProofOfWork struct{
block *Block
targetBit *big.Int
}
func NewProofOfWork(block *Block) *ProofOfWork{
var IntTarget = big.NewInt(1) // 假定值
//000000000000000000000000000000001 初始值
//100000000000000000000000000000000 十进制
//000000000100000000000000000000000 十进制
//000001000000000000000000000000000 十六进制 目标哈希值
//0000000a0000000000001234560000000 实际值
IntTarget.Lsh(IntTarget, uint(256- targetBits))
return &ProofOfWork{block,IntTarget}
}
func (pow *ProofOfWork)PrepareRawData(nonce int64) []byte{
block := pow.block //获取需要处理的区块
tmp := [][]byte{
//实现int类型转换为byte类型的工具函数
IntToByte(block.Version),
block.PrevBlockHash,
IntToByte(block.TimeStamp),
block.MerKelRoot,
IntToByte(nonce),
IntToByte(targetBits), //添加难度值
block.Data,
}
//将区块的各个字段链接成一个切片,使用【】byte{}进行链接,目的是避免污染源区块的信息
data := bytes.Join(tmp,[]byte{})
return data
}
func (pow *ProofOfWork)Run()(int64, []byte){
var nonce int64
var hash [32]byte
var HashInt big.Int
fmt.Println("Begin Minng ...")
fmt.Printf("target hash : %x
", pow.targetBit.Bytes())
for nonce < math.MaxInt64{
data := pow.PrepareRawData(nonce)
hash = sha256.Sum256(data) //取出来后是字符串
HashInt.SetBytes(hash[:]) //将byte值转换为大的数字
// 比较哈希值
if HashInt.Cmp(pow.targetBit) == -1{
fmt.Printf("Found Hash:%x
", hash)
break
}else{
nonce ++
}
}
return nonce, hash[:]
}
//提供外部校验的方法
func (pow *ProofOfWork)IsValid()bool{
data :=pow.PrepareRawData(pow.block.Nonce)
hash := sha256.Sum256(data)
var IntHash big.Int
IntHash.SetBytes(hash[:])
return IntHash.Cmp(pow.targetBit) == -1
}
cli.go
package main
import (
"os"
"fmt"
"flag"
)
const Usage = `
./blockchain addBlock --data DATA "add a block to blockchain"
./blockchain printchain "print all blocks"
`
type CLI struct{
bc *BlockChain
}
func (cli *CLI)Run(){
if len(os.Args) <2 {
fmt.Println("too few parameters!
", Usage)
os.Exit(1)
}
//添加命令
addBlockCmd := flag.NewFlagSet("addBlock", flag.ExitOnError)
printCmd := flag.NewFlagSet("printchain",flag.ExitOnError)
//获取命令携带的参数
addBlockPara :=addBlockCmd.String("data","", "block info")
switch os.Args[1]{
case "addBlock":
//解析数据
err := addBlockCmd.Parse(os.Args[2:])
CheckErr("addBlock", err)
if addBlockCmd.Parsed(){
if *addBlockPara == "" {
//cli.bc.AddBlock(*addBlockCmdPara)
fmt.Println("data is empty, pls check!")
os.Exit(1)
}
cli.AddBlock(*addBlockPara)
}
case "printchain":
err := printCmd.Parse(os.Args[2:])
CheckErr("printchain", err)
if printCmd.Parsed(){
cli.PrintChain()
}
default:
fmt.Println("invalid cmd
", Usage)
}
}
command.go
package main
import "fmt"
func (cli *CLI)AddBlock(data string){
cli.bc.AddBlock(data)
fmt.Println("AddBlock Succeed!")
}
func (cli *CLI)PrintChain(){
bc := cli.bc
it := bc.Iterator()
//遍历区块
for{
block := it.Next() //取回当前hash执行的block,将hash值前移
fmt.Printf("Data:%s
", block.Data)
fmt.Println("Version:",block.Version)
fmt.Printf("PrevHash:%x
",block.PrevBlockHash)
fmt.Printf("Hash:%x
",block.TimeStamp)
fmt.Printf("TimeStamp:%d
",block.TimeStamp)
fmt.Printf("MerKel:%x
",block.MerKelRoot)
fmt.Printf("Nonce:%d
",block.Nonce)
//
pow := NewProofOfWork(block)
fmt.Printf("IsvALID:%v
",pow.IsValid())
if len(block.PrevBlockHash) == 0{
break
}
}
}
main.go
package main
func main(){
bc := NewBlockChain()
cli := CLI{bc}
cli.Run()
}
实现的效果如下:
以上是关于创建区块链实现之v3本地持久化(bolt数据库的序列化和反序列化)和命令行参数的主要内容,如果未能解决你的问题,请参考以下文章