创建区块链实现之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数据库的序列化和反序列化)和命令行参数的主要内容,如果未能解决你的问题,请参考以下文章

区块链之bolt数据库持久化与基本功能完善

POS代码实现

POS代码实现

POS代码实现

从0到1简易区块链开发手册V0.3-数据持久化与创世区块

基于Java语言构建区块链—— 持久化 & 命令行