比特币使用的共识机制为工作量证明机制,此机制已经经历了数十年的验证了,虽然原理简单粗暴,但是不得不承认它的安全性与可靠性。下面将实现的是区块链中的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()
}
}
运行结果: