区块链第四章 工作量证明2

项目结构
在这里插入图片描述
main.go

package main

import "core"

func main(){
    
    
	bc := core.NewBlockchain()  //创建新的链条
	defer bc.Db.Close() //main方法结束后才关db

	cli := core.CLI{
    
    bc}
	cli.Run()
}

block.go

package core

import (
	"bytes"
	"encoding/gob"
	"log"
	"time"
)

type Block struct{
    
    
	Timestamp 		int64 // 区块链创建时间戳
	Data      		[]byte //区块包含的数据
	PrevBlockHash 	[]byte //前一个区块的哈希值
	Hash 	  		[]byte //区块自身的哈希值,用于校验区块数据有效
	Nonce			int    //用于证明工作量
}
//Serialize()转换区块
func (b *Block) Serialize() []byte{
    
    
	var result bytes.Buffer
	encoder := gob.NewEncoder(&result) //gob可以把一个go语言当中的数据结构转换成字节数组

	err := encoder.Encode(b)
	if err!=nil {
    
    
		log.Panic(err)
	}

	return result.Bytes()
}

//NewBLock创建并返回一个区块
func NewBlock(data string,prevBlockHash []byte) *Block{
    
    
	//block := &Block{time.Now().Unix(),[]byte(data),prevBlockHash,[]byte{}}
	block := &Block{
    
    time.Now().Unix(),[]byte(data),prevBlockHash,[]byte{
    
    },0}
	pow := NewProofOfWork(block)
	nonce, hash := pow.Run()

	block.Hash = hash[:]
	block.Nonce = nonce
	return block
}

func NewGenesisBlock() *Block{
    
    
	return NewBlock("Genesis Block",[]byte{
    
    })
}

func DeserializeBlock(d []byte) *Block {
    
    
	var block Block
	decoder := gob.NewDecoder(bytes.NewReader(d))
	err := decoder.Decode(&block)
	if err !=nil {
    
    
		log.Panic(err)
	}

	return &block
}


blockchain.go

package core

import (
	"fmt"
	"log"

	"github.com/boltdb/bolt" //特殊的数据存储结构,用文件来存储
)
const dbFile = "blockchain.db"
const blocksBucket = "blocks"
//BlockChain内置一系列Blocks
type Blockchain struct{
    
    
	tip []byte
	Db *bolt.DB
}
//BlockchainIterator 用来遍历 blockchain的blocks
type BlockChainIterator struct {
    
    
	currentHash []byte
	Db 			*bolt.DB
}

//AddBlock 把提供的数据保存到区块放进区块链里
func (bc *Blockchain)AddBlock(data string){
    
    
	var lastHash []byte
	err := bc.Db.View(func(tx *bolt.Tx) error{
    
    
		b := tx.Bucket([]byte(blocksBucket))
		lastHash = b.Get([]byte("l"))

		return nil
	})
	if err != nil {
    
    
		log.Panic(err)
	}
	newBlock := NewBlock(data, lastHash)
	err = bc.Db.Update(func(tx *bolt.Tx) error {
    
    
		b := tx.Bucket([]byte(blocksBucket))
		err := b.Put(newBlock.Hash, newBlock.Serialize())
		if err != nil {
    
    
			log.Panic(err)
		}

		err = b.Put([]byte("l"),newBlock.Hash)
		if err != nil {
    
    
			log.Panic(err)
		}

		bc.tip = newBlock.Hash

		return nil
	})

}

//遍历...
func (bc *Blockchain) Iterator() * BlockChainIterator{
    
    
	bci := &BlockChainIterator{
    
    bc.tip,bc.Db}

	return bci
}

func (i *BlockChainIterator) Next() *Block{
    
    
	var block *Block

	err := i.Db.View(func(tx *bolt.Tx) error {
    
    
		b := tx.Bucket([]byte(blocksBucket))
		encodedBlock := b.Get(i.currentHash)
		block = DeserializeBlock(encodedBlock)

		return nil
	})
	if err != nil{
    
    
		log.Panic(err)
	}
	i.currentHash = block.PrevBlockHash

	return block
}
//NewBlockchain创造一个新的创世区块链
func NewBlockchain() *Blockchain {
    
    
	var tip []byte
	db, err := bolt.Open(dbFile,0600,nil) //打开某一个硬盘上的文件dbFile
	if err !=nil {
    
     //如果打开失败则退出
		log.Panic(err)
	}
	err = db.Update(func(tx *bolt.Tx)error{
    
     //向数据库里更新数据
		b := tx.Bucket([]byte(blocksBucket))

		if b == nil {
    
     //第一次找是空的,判断如果是空的则执行下面语句
			fmt.Println(("No existing blockchain found. Creating a new one...")) //第一次是空的,找不到,输出“创建一个吧”
			genesis :=  NewGenesisBlock() //创建创世纪块

			b,err := tx.CreateBucket([]byte(blocksBucket))  //创建一个桶
			if err !=nil {
    
     //如果创建失败则退出
				log.Panic(err)
			}
			err = b.Put(genesis.Hash,genesis.Serialize()) //put(key,value),其中key和value都是字节数组类型,genesis.Hash本区块的哈希值,genesis.Serialize()区块产生的字节数组
			if err != nil{
    
    
				log.Panic(err)
			}

			err = b.Put([]byte("l"), genesis.Hash)//把哈希放到key“l”中
			if err != nil {
    
    
				log.Panic(err)
			}
			tip = genesis.Hash
		}else{
    
     //不是第一次创建时
			tip = b.Get([]byte("l")) //把领头的哈希找出来就行
		}

		return nil
	})
if err != nil {
    
    
	log.Panic(err)
}

bc := Blockchain{
    
    tip,db}  //把区块链返出来放进区块链中

return &bc
}



cli.go

package core

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"

)

type CLI struct {
    
    
	Bc *Blockchain
}
//解析命令行的参数并执行命令
func (cli *CLI) printUsage(){
    
    
	fmt.Println("Usage:")
	fmt.Println("  addblock -data BLOCK_DATA - add a block to the blockchain")
	fmt.Println("  printchain - print all the blocks of the blockchain")
}
func(cli *CLI) validateArgs(){
    
    
	if len(os.Args)<2{
    
     //如果给的命令字数不足显示提示的参数printUsage()
		cli.printUsage()
		os.Exit(1)
	}
}

func (cli *CLI) addBlock(data string){
    
    
	cli.Bc.AddBlock(data)
	fmt.Println("Success!")
}
func (cli *CLI) printChain() {
    
    
	bci := cli.Bc.Iterator()

	for{
    
    
		block := bci.Next()

		fmt.Printf("Prev. hash: %x\n",block.PrevBlockHash)
		fmt.Printf("Data: %s\n", block.Data)
		fmt.Printf("Hash: %x\n",block.Hash)
		pow := NewProofOfWork(block)
		fmt.Printf("PoW: %s\n",strconv.FormatBool(pow.Validate()))
		if len(block.PrevBlockHash) == 0 {
    
    
			break
		}
	}
}

//解析命令行参数并执行命令
func (cli *CLI) Run(){
    
    
	cli.validateArgs()
	
	addBlockCmd := flag.NewFlagSet("addblock",flag.ExitOnError)
	printChainCmd := flag.NewFlagSet("printchain",flag.ExitOnError)
	
	addBlockData := addBlockCmd.String("data","","Block data")

	switch os.Args[1] {
    
     //匹配输入的命令,用第一个词匹配,
	case "addblock": //如果输入的第一个词是addblock,执行输入的第二个词
		err := addBlockCmd.Parse(os.Args[2:])
		if err !=nil {
    
    
			log.Panic(err)
		}
	case "printchain"://如果输入的第一个词是printchain,则执行输入的第二个词
		err := printChainCmd.Parse(os.Args[2:])
		if err !=nil {
    
    
			log.Panic(err)
		}
	default:
		cli.printUsage()
		os.Exit(1)
	}

	if addBlockCmd.Parsed() {
    
    
		if *addBlockData == "" {
    
    
			addBlockCmd.Usage()
			os.Exit(1)
		}
		cli.addBlock(*addBlockData) //第一个词是addblock,如果输入的值不是空值,就增加区块
	}

	if printChainCmd.Parsed() {
    
    
		cli.printChain() //第一个词是printchain,如果输入的值不是空值,就打印整条链
	}
}

proofofwork.go

package core

import (
	"bytes"
	"crypto/sha256"
	"fmt"
	"math"
	"math/big"
)

var (
	maxNonce = math.MaxInt64
)

const targetBits = 8

type ProofOfWork struct{
    
    
	block *Block
	target *big.Int
}

//新建一个NewProofOfWork的函数,并返回一个ProofOfWork
func NewProofOfWork(b *Block) *ProofOfWork{
    
    
	target := big.NewInt(1)
	target.Lsh(target,uint(256-targetBits)) //对比特位进行移位操作

	pow:= &ProofOfWork{
    
    b,target}

	return pow
}
func (pow *ProofOfWork) prepareData(nonce int) []byte{
    
    
	data := bytes.Join(
		[][]byte{
    
    
			pow.block.PrevBlockHash,
			pow.block.Data,
			IntToHex(pow.block.Timestamp),
			IntToHex(int64(targetBits)),
			IntToHex(int64(nonce)),
		},
		[]byte{
    
    },
	)

	return data
}
//运行一个pow
func (pow *ProofOfWork) Run() (int,[]byte){
    
    
	var hashInt big.Int
	var hash [32]byte
	nonce := 0

	fmt.Printf("Mining the block containing \"%s\"\n",pow.block.Data)
	for nonce< maxNonce {
    
    
		data := pow.prepareData(nonce)

		hash = sha256.Sum256(data)
		fmt.Printf("\r%x",hash)
		hashInt.SetBytes(hash[:])

		if hashInt.Cmp(pow.target) == -1 {
    
    
			break
		} else{
    
    
			nonce++
		}
	}
	fmt.Print("\n\n")

	return nonce,hash[:]
}

func  (pow *ProofOfWork) Validate() bool {
    
    
	var hashInt big.Int

	data := pow.prepareData(pow.block.Nonce)
	hash := sha256.Sum256(data)
	hashInt.SetBytes(hash[:])

	isValid := hashInt.Cmp(pow.target) == -1

	return isValid

}

utils.go

package core

import (
	"bytes"
	"encoding/binary"
	"log"
)

func IntToHex(num int64) []byte {
    
    
	buff := new(bytes.Buffer)
	err := binary.Write(buff,binary.BigEndian,num)
	if err !=nil{
    
    
		log.Panic(err)
	}

	return buff.Bytes()
}
//func DataToHash(data []byte) []byte {
    
    
//	hash :=sha256.Sum256(data)
//	return hash[:]
//}

在这里插入图片描述
在这里插入图片描述


这次是把上次的工作量证明改改,由默认的语句改成由用户输入,改动最大的blockchain.go和cli.go,blockchain.go主要是改了区块链的存储方式,把以前的数组改成了bucket(桶),用了bolt这个文件?数据库?就可以用key-value的方式存储字符数组了,然后cli.go主要负责处理用户输入。
而且printchain的结果很有意思,不是从创世区块开始的,而是从最新的开始,大概像往桶里放东西和取东西?


财富密码
在这里插入图片描述


今天喝了炭烧,肚子就开始咕嘟嘟了,不亏是你,拉稀酸奶。蔫蔫的,要支愣起来呀狗酱!

猜你喜欢

转载自blog.csdn.net/weixin_43820665/article/details/109058903