golang重写区块链——0.2 加入工作量证明pow

    比特币使用的共识机制为工作量证明机制,此机制已经经历了数十年的验证了,虽然原理简单粗暴,但是不得不承认它的安全性与可靠性。下面将实现的是区块链中的pow机制:

pow包

package pow

import (
	"fmt"
	"crypto/sha256"
	"strconv"
	"bytes"
	"math/big"
	"go_code/A_golang_blockchain/block"
	"math"
	"time"
)
//在实际的比特币区块链中,加入一个区块是非常困难的事情,其中运用得到的就是工作量证明

//创建一个工作量证明的结构体
type ProofOfWork struct {
	block *block.Block //要证明的区块
	target *big.Int //难度值
}
//声明一个挖矿难度
const targetBits = 10

//实例化一个工作量证明
func NewProofOfWork(b *block.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,
			[]byte(strconv.FormatInt(pow.block.Timestamp,10)),
			[]byte(strconv.FormatInt(targetBits,10)),
			[]byte(strconv.FormatInt(int64(nonce),10)),
		},
		[]byte{},
	)
	return data
}

//进行工作量证明,证明成功会返回随机数和区块哈希
func (pow *ProofOfWork) Run() (int,[]byte) {
	nonce := 0
	var hash [32]byte
	var hashInt big.Int
	for nonce < math.MaxInt64 {
		data := pow.prepareData(nonce)
		hash = sha256.Sum256(data)
		hashInt.SetBytes(hash[:])

		//把哈希后的数据与难度值进行比较
		if hashInt.Cmp(pow.target) == -1 {
			fmt.Printf("工作量证明成功 hash= %x  nonce = %v\n",hash,nonce)
			break
		}else{
			nonce ++
		}
	}
	fmt.Println()

	return nonce,hash[:]
}

//实例化一个区块
func NewBlock(data string,prevBlockHash []byte) *block.Block {
	block := &block.Block{time.Now().Unix(),[]byte(data),prevBlockHash,[]byte{},0}
	// block.SetHash()

	pow := NewProofOfWork(block)
	nonce,hash := pow.Run()
	block.Hash = hash
	block.Nonce = nonce
	return block
}

//其他节点验证nonce是否正确
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
}

上一章节的代码有些许的改变:

    在block包里面的实例化区块的函数我把它移到了pow包里了,防止串包报错。然后就是结构体Block增加了随机数Nonce字段,更改后的block包如下:

package block

import (

)
/*
	区块
*/

//区块的结构体
type Block struct {
	Timestamp		int64
	Data			[]byte
	PrevBlockHash	[]byte
	Hash 			[]byte
	Nonce			int
}

//计算本区块哈希(已经用不上了,求Hash字段在pow里面进行)
// func(b *Block) SetHash() {
// 	timestamp := []byte(strconv.FormatInt(b.Timestamp,10))
// 	//要进行哈希的区块头
// 	headers := bytes.Join([][]byte{timestamp,b.Data,b.PrevBlockHash},[]byte{})
// 	hash := sha256.Sum256(headers)
// 	b.Hash = hash[:]
// }

// //实例化一个区块(被移到了pow包了,避免串包)
// func NewBlock(data string,prevBlockHash []byte) *Block {
// 	block := &Block{time.Now().Unix(),[]byte(data),prevBlockHash,[]byte{},0}
// 	// block.SetHash()

// 	return block
// }

Blockchain包里面的改变就是把调用的实例化区块函数路径改变了一下:

package blockchain

import (
	"go_code/A_golang_blockchain/block"
	"go_code/A_golang_blockchain/pow"
)
/*
	区块链实现
*/

//区块链
type Blockchain struct {
	Blocks []*block.Block
}

//把区块添加进区块链
func (bc *Blockchain) AddBlock(data string) {
	//求出前一区块
	prevBlock := bc.Blocks[len(bc.Blocks)-1]
	newBlock := pow.NewBlock(data,prevBlock.Hash)
	bc.Blocks = append(bc.Blocks,newBlock)
}

//创建创世区块
func GenesisBlock() *block.Block {
	return pow.NewBlock("创世区块",[]byte{})
}
//实例化一个区块链,默认存储了创世区块
func NewBlockchain() *Blockchain {
	return &Blockchain{[]*block.Block{GenesisBlock()}}
}

main包:

package main

import (
	"strconv"
	"go_code/A_golang_blockchain/blockchain"
	"go_code/A_golang_blockchain/pow"
	"fmt"
)

func main() {
	//先创建一条区块链
	bc := blockchain.NewBlockchain()

	//加入区块到区块链中
	bc.AddBlock("区块01")
	bc.AddBlock("区块02")

	//打印出区块链中各个区块的信息,并验证各个区块是否合格
	for _,b := range bc.Blocks {

		fmt.Printf("时间戳:%v\n",b.Timestamp)
		fmt.Printf("Data:%s\n",b.Data)
		fmt.Printf("上一区块哈希:%x\n",b.PrevBlockHash)
		fmt.Printf("Hash:%x\n",b.Hash)
		fmt.Printf("Nonce:%v\n",b.Nonce)
		//验证当前区块的pow
		pow := pow.NewProofOfWork(b)
		boolen := pow.Validate()
		fmt.Printf("POW is %s\n",strconv.FormatBool(boolen))
		fmt.Println()
	}

}

运行结果:

猜你喜欢

转载自blog.csdn.net/zyj0813/article/details/82052977