創建區塊鏈實現之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\n", pos, "err occur%\n:",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\n", 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\n", 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!\n", 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\n", 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\n", block.Data)
        fmt.Println("Version:",block.Version)

        fmt.Printf("PrevHash:%x\n",block.PrevBlockHash)
        fmt.Printf("Hash:%x\n",block.TimeStamp)
        fmt.Printf("TimeStamp:%d\n",block.TimeStamp)
        fmt.Printf("MerKel:%x\n",block.MerKelRoot)
        fmt.Printf("Nonce:%d\n",block.Nonce)
        //
        pow := NewProofOfWork(block)
        fmt.Printf("IsvALID:%v\n",pow.IsValid())
        if len(block.PrevBlockHash) == 0{
            break
        }
    }
}

main.go

package main

func main(){
    bc := NewBlockChain()
    cli := CLI{bc}
    cli.Run()
}

實現的效果如下:

創建區塊鏈實現之v3本地持久化(bolt數據庫的序列化和反序列化)和命令行參數

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章