创建区块链实现之v3本地持久化(bolt数据库的序列化和反序列化)和命令行参数

在之前的版本上修改,实现命令添加data(记账信息)

block.go

package mainimport ( "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 mainimport ( "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)}//定义迭代器structtype 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 mainimport ( "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 mainimport ( "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 mainimport ( "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 mainimport "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 mainfunc main(){ bc := NewBlockChain() cli := CLI{bc} cli.Run()}

实现的效果如下:

相关文章