本文 mirror リンク:使用 golang 从零开始搭建基于 UTXO 模型的区块链(二、项目重构 + PoW)
前言#
前の章では、ブロックとは何か、そしてブロックとブロックチェーンの関係について学びました。この章では、プロジェクトを再構築し、ブロックのヘッダー情報を拡張し、ブロックがどのようにコンセンサスメカニズムによって合法的にブロックチェーンに追加されるかを説明します。
プロジェクト再構築#
前の章では、すべてのコードを main.go に書きましたが、これは明らかにプロジェクトの構築を続ける上で不利です。main.go は私たちのブロックチェーンシステムを最終的に起動するためだけに使用したいので、設計したブロックとブロックチェーンを他のフォルダーに移植する必要があります。再構築の部分についてはあまり詳しく説明しません。
構造:
utils パッケージは、いくつかのツールメソッドを配置するためのもので、必要に応じて使用します;constcoe はグローバル定数を配置します;blockchain はブロックチェーン実装の主体を配置します。
以前の int64 を byte に変換する関数を utils に移動しました
package utils
import (
"encoding/binary"
)
func Int64ToByte(num int64) []byte {
var buf = make([]byte, 8)
binary.BigEndian.PutUint64(buf, uint64(num))
return buf
}
constcoe にはまず難易度係数を保存します。これは後で詳しく説明します。
package constcoe
const (
Difficulty = 12
)
block.go と blockchain.go には、以前書いた構造体と関数を入れます。
//block.go
package blockchain
import (
"bytes"
"crypto/sha256"
"lighteningchain/utils"
"time"
)
type Block struct {
Timestamp int64
Hash []byte
PrevHash []byte
Data []byte
}
func (b *Block) SetHash() {
information := bytes.Join([][]byte{utils.Int64ToByte(b.Timestamp), b.PrevHash, b.Data}, []byte{})
hash := sha256.Sum256(information)
b.Hash = hash[:]
}
func CreateBlock(prevhash, data []byte) *Block {
block := Block{time.Now().Unix(), []byte{}, prevhash, data}
block.SetHash()
return &block
}
func GenesisBlock() *Block {
genesisWords := "HelloWorld"
return CreateBlock([]byte{}, []byte(genesisWords))
}
//blockchain.go
package blockchain
type BlockChain struct {
Blocks []*Block
}
func (bc *BlockChain) AddBlock(data string) {
newBlock := CreateBlock(bc.Blocks[len(bc.Blocks)-1].Hash, []byte(data))
bc.Blocks = append(bc.Blocks, newBlock)
}
func CreateBlockChain() *BlockChain {
blockchain := BlockChain{}
blockchain.Blocks = append(blockchain.Blocks, GenesisBlock())
return &blockchain
}
main.go には、これだけが残っています。
package main
import (
"fmt"
"lighteningchain/blockchain"
"time"
)
func main() {
blockchain := blockchain.CreateBlockChain()
time.Sleep(time.Second)
blockchain.AddBlock("This is first Block after Genesis")
time.Sleep(time.Second)
blockchain.AddBlock("This is second!")
time.Sleep(time.Second)
blockchain.AddBlock("Awesome!")
time.Sleep(time.Second)
for num, block := range blockchain.Blocks {
fmt.Printf("number:%d Timestamp: %d\n", num, block.Timestamp)
fmt.Printf("number:%d hash: %x\n", num, block.Hash)
fmt.Printf("number:%d Previous hash: %x\n", num, block.PrevHash)
fmt.Printf("number:%d data: %s\n", num, block.Data)
}
}
試してみてください、実行成功!プロジェクトの再構築が完了しました。これで今後の開発がずっと楽になります。
ブロックチェーンのコンセンサスメカニズム#
「コンセンサスメカニズム」とは、特定のノードの投票を通じて、非常に短い時間内に取引の検証と確認を完了することを指します。ある取引について、利害関係のない複数のノードが合意に達することができれば、ネットワーク全体もそれに合意できると考えられます。もう少しわかりやすく言えば、中国のある微博の大 V、アメリカの仮想通貨プレイヤー、アフリカの留学生、そしてヨーロッパの旅行者が互いに知らないとしても、彼らが一致してあなたが良い人だと考えているなら、基本的にあなたは悪い人ではないと断定できます。
pow コンセンサスメカニズムは以下の図を参照してください。
次に、私たちのライトニングチェーンを使ってこのコンセンサスメカニズムを実現します。
Nonce の追加#
nonce は上の図で探しているランダムな数です。これはあなたの作業量を証明する最も重要な部分です。まず、ブロックにヘッダー情報を追加します。
type Block struct {
Timestamp int64
Hash []byte //ブロックのハッシュ値はそのIDです
PrevHash []byte
Data []byte
Nonce int64
Target []byte
}
次に、いくつかの関数でエラーが発生しますが、後で修正します。
POW の実装#
proofofwork.go では、まず target を取得する関数を実装します。この関数は、今後分散システムで target を繰り返し取得するのに便利です。
func (b *Block) GetTarget() []byte {
target := big.NewInt(1)
target.Lsh(target, uint(256-constcoe.Difficulty))
return target.Bytes()
}
Lsh 関数は左にビットシフトします。difficulty が小さいほど、シフトするビット数が多くなり、目標難易度値が大きくなり、ハッシュの取得値が落ちる範囲が広がり、条件を満たす nonce を見つけやすくなります。
次に、nonce を見つける計算を行います。
func (b *Block) GetDataBaseNonce(nonce int64) []byte {
data := bytes.Join([][]byte{
utils.Int64ToByte(b.Timestamp),
b.PrevHash,
utils.Int64ToByte(nonce),
b.Target,
b.Data,
},
[]byte{},
)
return data
}
func (b *Block) FindNonce() int64 {
var intHash big.Int
var intTarget big.Int
intTarget.SetBytes(b.Target)
var hash [32]byte
var nonce int64
nonce = 0
for nonce < math.MaxInt64 {
data := b.GetDataBaseNonce(nonce)
hash = sha256.Sum256(data)
intHash.SetBytes(hash[:])
if intHash.Cmp(&intTarget) == -1 {
break
} else {
nonce++
}
}
return nonce
}
見ると、神秘的な nonce は 0 から始まる整数に過ぎず、試行を重ねるごとに、毎回失敗するたびに nonce が 1 ずつ増加し、現在の nonce から得られるブロックハッシュが目標難易度値より小さくなるまで続きます。
では、ブロックチェーンはこの分散システムであなたのシステムが計算したものが正しいとどうやって知るのでしょうか?次に、検証関数を書く必要があります。
func (b *Block) ValidatePoW() bool {
var intHash big.Int
var intTarget big.Int
var hash [32]byte
intTarget.SetBytes(b.Target)
data := b.GetDataBaseNonce(b.Nonce)
hash = sha256.Sum256(data)
intHash.SetBytes(hash[:])
if intHash.Cmp(&intTarget) == -1 {
return true
}
return false
}
pow の実装が完了しました。次に、block.go に戻り、少し修正を加えます。
func (b *Block) SetHash() {
information := bytes.Join([][]byte{utils.Int64ToByte(b.Timestamp),
b.PrevHash, b.Target, utils.Int64ToByte(b.Nonce), b.Data}, []byte{})
hash := sha256.Sum256(information) //ソフトウェアパッケージsha256はFIPS 180-4で定義されたSHA224およびSHA256ハッシュアルゴリズムを実装しています。
b.Hash = hash[:]
}
func CreateBlock(prevhash []byte, data []byte) *Block {
block := Block{time.Now().Unix(), []byte{},
prevhash, data, 0, []byte{}}
block.Target = block.GetTarget()
block.Nonce = block.FindNonce()
block.SetHash() //すべてのデータを追加した後にハッシュを計算します
return &block
}
すべて完了しました!
デバッグ#
main.go を開き、pow が成功したかどうかを検証するために 1 行出力を追加します。
package main
import (
"fmt"
"lighteningchain/blockchain"
"time"
)
func main() {
blockchain := blockchain.CreateBlockChain()
time.Sleep(time.Second)
blockchain.AddBlock("This is first Block after Genesis")
time.Sleep(time.Second)
blockchain.AddBlock("This is second!")
time.Sleep(time.Second)
blockchain.AddBlock("Awesome!")
time.Sleep(time.Second)
for num, block := range blockchain.Blocks {
fmt.Printf("number:%d Timestamp: %d\n", num, block.Timestamp)
fmt.Printf("number:%d hash: %x\n", num, block.Hash)
fmt.Printf("number:%d Previous hash: %x\n", num, block.PrevHash)
fmt.Printf("number:%d data: %s\n", num, block.Data)
fmt.Printf("number:%d nonce:%d\n", num, block.Nonce)
fmt.Println("POW validation:", block.ValidatePoW())
}
}
実行をクリックすると、出力が得られます。
number:0 Timestamp: 1677654426
number:0 hash: 51c810ee37b56f26baaf27ad8c8c271c1e383dcf75c6b8baaca059a9e621ac67
number:0 Previous hash:
number:0 data: HelloWorld!
number:0 nonce:14014
POW validation: true
number:1 Timestamp: 1677654427
number:1 hash: 059131a889810a8484bc072d0bcd7ecba3011a509ab6bc460c7a892357621f82
number:1 Previous hash: 51c810ee37b56f26baaf27ad8c8c271c1e383dcf75c6b8baaca059a9e621ac67
number:1 data: This is first Block after Genesis
number:1 nonce:1143
POW validation: true
number:2 Timestamp: 1677654428
number:2 hash: 055263bd8eea37b526e45b097b1f837c108ab2fc88f26bbf567a4fa9598cadb9
number:2 Previous hash: 059131a889810a8484bc072d0bcd7ecba3011a509ab6bc460c7a892357621f82
number:2 data: This is second!
number:2 nonce:10091
POW validation: true
number:3 Timestamp: 1677654429
number:3 hash: d0b5a049c2780c01e2e66cc23934267c528df80a3bcc69180a3f2231cf08d87f
number:3 Previous hash: 055263bd8eea37b526e45b097b1f837c108ab2fc88f26bbf567a4fa9598cadb9
number:3 data: Awesome!
number:3 nonce:592
POW validation: true
成功しました!
まとめ#
この章では、PoW コンセンサスメカニズムについて説明しました。nonce と目標難易度値、そして pow の実装を重点的に理解する必要があります。次の章では、ブロック内のデータ情報の保存方法と UTXO モデルを実装します。
また、現在のブロックチェーンの主流のコンセンサスは PoW から PoS に変わりましたので、後で時間があれば改良します。