經典共識PoW的原理及實作
一、PoW簡介
PoW(Proof of Work)是作業量證明的簡稱,最早使用在防止拒絕服務攻擊以及過濾垃圾郵件,現在成為區塊鏈公鏈中最常見也是最有效的共識演算法之一,當下最流行的位元幣、以太坊等公鏈目前均使用PoW共識,
? PoW是通過對一個復雜難題的求解,來保證區塊鏈交易打包出塊的公平性,即最先解決難題的礦工,可以獲得記賬權,并將打包好的區塊發送至其他節點驗證上鏈,從而獲得激勵,在保證能夠在不可信的環境下創造可信的同時,運用PoW還可以天然的防御女巫攻擊等針對區塊鏈的攻擊,
? 由于PoW要求所有礦工節點都解決無意義的復雜難題,導致了巨大的資源浪費,因此,最新的以太坊版本考慮使用PoS代替PoW成為其共識演算法,
二、PoW原理及實作
? 在介紹PoW原理之前,需要先了解一些知識,
2.1 區塊頭
? 在區塊鏈中,區塊分為區塊頭和區塊體兩部分,區塊體以默克爾樹的資料結構存盤了交易資料,區塊頭存盤了上一個區塊的Hash、默克爾樹樹根值、時間戳、難度值、區塊高度等等非交易資料資訊,
2.2 哈希計算
? 哈希計算實際上就是使用哈希函式(散列函式)對不同長度的資料都能計算出等長的輸出,而且對資料微小差距都十分敏感,比如"hello world"和"Hello world"的哈希值差距都是巨大的,
2.3 原理及實作
? PoW的原理十分簡單,實際上就是計算一個亂數Nounce,要求這個Nounce和區塊頭拼接后做的哈希值小于我們預設的值,具體實作如下:
? 1.預設一個難度值targetBit,比如我們想PoW計算出的Hash值最前面開始有16個0,則設定該值為16,
? 2.找到判斷是否找出正確Nounce值的臨界值target,在位元幣中,PoW使用的是SHA256,輸出的哈希值都是256位的,比如設定的難度值是16,那么我們最終應該得到的值的形式應該是 0000 0000 0000 0000 xxxx…xxxx(共256位),那么我們可以得知,當我得到的值小于0000 0000 0000 0001 0000…0000(共256位)時,該值合法,因為這個值已經小于了前面有15個0的最小值,那么其前面一定有16個0,
? 所以我們只要將臨界值target先設定為1,二進制表示為0000 0000…0001(共256位),再將這個值左移256-targetBit = 256 - 16 = 240位,即可得到前面有15個0的最小值,即0000 0000 0000 0001 0000…0000(共256位),
? 3.將亂數Nounce置為0,再拼接上區塊頭的內容,計算其哈希值,如果大于了我們的目標值target,則將Nounce加1,再拼接區塊頭的內容計算哈希,重復上述程序,直到計算出小于target的哈希值,此時的Nounce就是最終的解,
?
三、PoW的go語言實作
1.首先定義作業量證明物件
type ProofOfWork struct {
Block *Block //當前要驗證的區塊
target *big.Int //大數存盤
}
2.定義難度值
//256位hash里面至少有16個零
const targetBit = 16
3.初始化作業量證明物件,計算出臨界值target
func NewProofOfWork(block *Block) *ProofOfWork {
//創建一個初始值為1的target
target := big.NewInt(1)
//左移256-targetBit
target = target.Lsh(target, 256-targetBit)
return &ProofOfWork{
block,
target,
}
}
4.拼接Nounce值和區塊頭中的內容
func (proofOfWork *ProofOfWork) prepareData(nonce int) []byte {
join := bytes.Join(
[][]byte{
proofOfWork.Block.PreBlockHash,//前一個區塊的hash
proofOfWork.Block.HashTransactions(),//默克爾樹根
Utils.IntToHex(proofOfWork.Block.TimeStamp),//時間戳
Utils.IntToHex(int64(targetBit)),//難度值
Utils.IntToHex(int64(nonce)),//Nounce
Utils.IntToHex(int64(proofOfWork.Block.Height)),//區塊高度
},
[]byte{},
)
return join
}
5.開始進行作業量證明計算
func (proofOfWork *ProofOfWork) Run() ([]byte, int64) {
nonce := 0
var hashInt big.Int //存盤我們新生成的hash
var hash [32]byte
for {
// 將BLOCK屬性拼接成位元組陣列
dataBytes := proofOfWork.prepareData(nonce)
// 生成hash, sum256回傳32位需要轉換為64位
hash = sha256.Sum256(dataBytes)
// 將hash存盤到hashInt,采取hash[:]將切片轉換為64位
hashInt.SetBytes(hash[:])
fmt.Printf("\r%x", hash)
// 判斷hashInt是否小于Block里面的target
// x < y -1
// x == y 0
// x > y 1
if proofOfWork.target.Cmp(&hashInt) == 1 {
//判斷有效性,如果滿足條件,跳出回圈
break
}
nonce = nonce + 1
}
return hash[:], int64(nonce)
}
6.撰寫當需要驗證Nounce值是否合法時的驗證函式
func (proofOfWork *ProofOfWork) IsValid() bool {
var hashInt big.Int
hashInt.SetBytes(proofOfWork.Block.Hash)
//判斷提供的hash是否小于target
if proofOfWork.target.Cmp(&hashInt) == 1 {
return true
}
return false
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/304604.html
標籤:區塊鏈
下一篇:挖礦的大佬們長點心吧
