1. 程式人生 > >Go語言學習(三) 簡單比特幣挖礦類實現

Go語言學習(三) 簡單比特幣挖礦類實現

//utils.go
package main

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

func IntToHex(num int64)[]byte{
	buff:=new(bytes.Buffer)  //開闢記憶體,儲存位元組集
	err:=binary.Write(buff,binary.BigEndian,num)//num轉化位元組集寫入
	if err!=nil{
		log.Panic(err)
	}
	return buff.Bytes() //返回位元組集合
}
//block.go
package main

import (
	"time"
)

//定義區塊
type Block struct{
	Timestamp int64      //時間線,1970年1月1日00.00.00
	Data []byte    //交易資料
	PrevBlockHash []byte  //上一塊資料的雜湊
	Hash []byte   //當前塊資料的雜湊
	Nonce int   //工作量證明
}

/*//設定結構體物件雜湊
func (block *Block)SetHash(){
	//處理當前的時間,轉化為10進位制的字串,再轉化為位元組集合
	timestamp:=[]byte(strconv.FormatInt(block.Timestamp,10))
	//疊加要雜湊的資料
	headers:=bytes.Join([][]byte{block.PrevBlockHash,block.Data,timestamp},[]byte{})
	//計算出雜湊地址
	hash:=sha256.Sum256(headers)
	block.Hash=hash[:]//設定雜湊
}*/
//建立一個區塊
func NewBlock(data string, prevBlockHash []byte) *Block{
	//block是一個指標,取得一個物件初始化之後的地址
	block:=&Block{time.Now().Unix(),[]byte(data),prevBlockHash,[]byte{},0}
	pow:=NewProofOfWork(block)//挖礦附加這個區塊
	nonce,hash:=pow.Run()//開始挖礦
	block.Hash=hash[:]
	block.Nonce=nonce
	//block.SetHash()//設定當前雜湊
	return block
}
//建立創世區塊,
func NewGenesisBlock() *Block{
	return NewBlock("hello, welcome to my second BlockChain",[]byte{})

}
//blockchain.go
package main

type BlockChain struct{
	blocks []*Block    //一個數組,每個元素都是指標,儲存block區塊的地址
}
//增加一個區塊
func (blocks *BlockChain)AddBlock(data string ){
	prevBlock:=blocks.blocks[len(blocks.blocks)-1]    //取出最後一個區塊
	newBlock:=NewBlock(data,prevBlock.Hash)       //建立一個區塊
	blocks.blocks=append(blocks.blocks,newBlock)    //區塊鏈插入新的區塊
}
//建立一個區塊鏈
func NewBlockchain ()*BlockChain{
	return &BlockChain{[]*Block{NewGenesisBlock()}}
}
//proofofwork.go
package main

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

var(
	maxNonce=math.MaxInt64 //最大的64位整數
)
const targetBits=24//對比的位數

type ProofOfWork struct{
	block *Block  //區塊
	target * big.Int  //儲存計算雜湊對比的特定整數
}

//建立一個工作量證明的挖礦物件
func NewProofOfWork(block *Block)*ProofOfWork{
	target:=big.NewInt(1)     //初始化目標整數
	target.Lsh(target,uint(256-targetBits))  //資料轉換
	pow:=&ProofOfWork{block,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)),//儲存工作量的nonce
		},[]byte{},
		)
	return data
}
//挖礦執行
func (pow * ProofOfWork) Run()(int,[]byte){
	var hashInt big.Int
	var hash [32]byte
	nonce:=0
	fmt.Printf("當前挖礦計算的區塊資料%s",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.Println("\n\n")
	return nonce,hash[:]//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
}
//test_pow.go
package main

import (
	"crypto/sha256"
	"fmt"
	"strconv"
	"time"
)

func mainx(){
	flag:=0
	start:=time.Now()   //當前時間
	for i:=0;i<10000000000;i++{   //迴圈挖礦
		data:=sha256.Sum256([]byte(strconv.Itoa(i)))   //計算雜湊
		fmt.Printf("%10d,%x\n",i,data)
		fmt.Printf("%s\n",string(data[len(data)-2:]))
		if string(data[len(data)-2:])=="00"{  //位數的雜湊匹配
			usedtime:=time.Since(start)
			fmt.Printf("挖礦成功,用時%d ms\n",usedtime)
			flag=1
			break
		}
	}
	if flag==0{
		println("挖礦失敗")
	}
}

//main.go
package main

import (
	"fmt"
	"strconv"
)

func main(){
	fmt.Println("hello game start")
	bc:=NewBlockchain()   //建立區塊鏈
	bc.AddBlock("小明1 pay 小紅 10")
	bc.AddBlock("小明2 pay 小紅 20")
	bc.AddBlock("小明3 pay 小紅 30")

	for _,block:=range bc.blocks{
		fmt.Printf("上一塊雜湊%x\n",block.PrevBlockHash)
		fmt.Printf("資料: %s\n",block.Data)
		fmt.Printf("當前雜湊%x\n",block.Hash)
		pow:=NewProofOfWork(block)//校驗工作量
		fmt.Printf("pow %s\n",strconv.FormatBool(pow.Validate()))
		fmt.Println()
	}


}