前言
在前一篇“golang-区块链学习01”的基础上,增加我们区块链的工作量证明。
知识点
1、区块链ProofOfWork(工作量证明)概念,因为所有人都想生成区块来获取奖励,为了公平起见,我们规定要想成功生成一个区块必须完成指定难度的任务才行。也就是谁先完成指定难度的任务就将成功生成一个区块。先预留个彩蛋,结合实例的工作量证明将在文末总结。
golang实现简单的工作量证明
1、定义一个工作量难度。比如要求生产的区块的hash值前面五位必须为0。即hash类似:00000xxxxxxxxxxx的样式。
2、在Block的结构中增加一个Nonce变量,通过不断修改Nonce的值,不断计算整个区的hash值,直到满足上面的要求即可。
3、代码实例
创建一个proofofwork.go文件。定义一个工作量证明的结构体
type ProofOfWork struct { block *Block // 即将生成的区块对象 target *big.Int //生成区块的难度}
创建实例化工作量证明结构体.
const targetBits = 20func NewProofOfWork(b *Block) *ProofOfWork { target := big.NewInt(1) //难度:target=10的18次方(即要求计算出的hash值小于这个target) target.Lsh(target, uint(256-targetBits)) pow := &ProofOfWork{b, target} return pow}
计算hash值的算法
func (pow *ProofOfWork) Run() (int, []byte) { var hashInt big.Int var hash [32]byte nonce := 0// 从0自增 fmt.Printf("Mining the block containing \"%s\"\n", pow.block.Data) // 循环从nonce=0一直计算到nonce=2的64次方的值,知道算出符合要求的hash值 for nonce < maxNonce { // 准备计算hash的数据 data := pow.prepareData(nonce) hash = sha256.Sum256(data)// 计算hash fmt.Printf("\r%x", hash) hashInt.SetBytes(hash[:]) // 难度证明 if hashInt.Cmp(pow.target) == -1 { break// 符合 } else { nonce++// 不符合继续计算 } } fmt.Printf("\n\n") return nonce, hash[:] }
准备数据
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}
附件
惯例上码。所有的代码文件清单。
/lession02/src/coin/main.go
package mainimport ( "fmt" "core" "strconv")func main() { fmt.Printf("%d\n",uint(256-20)) bc := core.NewBlockChain() bc.AddBlock("send 1 btc to Ivan") bc.AddBlock("send 2 btc to Ivan") for _, block := range bc.Blocks { fmt.Printf("PrevBlockHash:%x\n", block.PrevBlockHash) fmt.Printf("Data:%s\n", block.Data) fmt.Printf("Hash:%x\n", block.Hash) fmt.Printf("TimeStamp:%d\n", block.TimeStamp) fmt.Printf("Nonce:%d\n", block.Nonce) pow := core.NewProofOfWork(block) fmt.Printf("Pow is %s\n", strconv.FormatBool(pow.Validate())) println() } }
/lession02/src/core/block.go
package coreimport ( "time" "strconv" "bytes" "crypto/sha256")type Block struct { TimeStamp int64 Data []byte PrevBlockHash []byte Hash []byte Nonce int}func NewBlock(data string, prevBlockHash []byte) *Block { block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}, 0} pow := NewProofOfWork(block) block.Nonce, block.Hash = pow.Run() return block } func (b *Block) SetHash() { strTimeStamp := []byte(strconv.FormatInt(b.TimeStamp, 10)) headers := bytes.Join([][]byte{b.PrevBlockHash, b.Data, strTimeStamp}, []byte{}) hash := sha256.Sum256(headers) b.Hash = hash[:] }func NewGenesisBlock() *Block { return NewBlock("Genesis Block", []byte{}) }
/lession02/src/core/blockchain.go
package core type BlockChain struct { Blocks []*Block } func (bc *BlockChain) AddBlock(data string) { preBlock := bc.Blocks[len(bc.Blocks)-1] newBlock := NewBlock(data, preBlock.Hash) bc.Blocks = append(bc.Blocks, newBlock) } func NewBlockChain() *BlockChain { return &BlockChain{[]*Block{NewGenesisBlock()}} }
/lession02/src/core/proofofwork.go
package coreimport ( "math" "math/big" "fmt" "crypto/sha256" "bytes")var ( maxNonce = math.MaxInt64 )const targetBits = 20type ProofOfWork struct { block *Block target *big.Int }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 } 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.Printf("\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 }
/lession02/src/core/utils.go
package coreimport ( "bytes" "encoding/binary" "log" "crypto/sha256")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[:] }
作者:embedsky
链接:https://www.jianshu.com/p/10e50bf14878