简易区块链的搭建(2)——工作量证明

1.原理与补充知识

1.big.Int 的一些常见方法和属性:

  • SetInt64(x int64):将一个 int64 类型的整数赋值给 big.Int

  • SetString(s string, base int):将一个字符串表示的整数按照指定的进制转换为 big.Int

  • SetBytes(x )(s string):讲一个字节型变量转换成big.Init
  • Add(x, y *big.Int) *big.Int:将两个 big.Int 相加,并返回结果。

  • Sub(x, y *big.Int) *big.Int:将一个 big.Int 减去另一个 big.Int,并返回结果。

  • Mul(x, y *big.Int) *big.Int:将两个 big.Int 相乘,并返回结果。

  • Div(x, y, z *big.Int) *big.Int:将一个 big.Int 除以另一个 big.Int,并返回商。

  • Mod(x, y, z *big.Int) *big.Int:将一个 big.Int 对另一个 big.Int 取模,并返回结果。

  • Cmp(y *big.Int) int:将 big.Int 与另一个 big.Int 比较,返回 -1、0 或 1,分别表示小于、等于或大于。

2.Target,nonce和PoW

target本质上是一个和SHA256生成哈希值一样大的整数

特殊的是这个数前几位都是0

我们要求,通过反复枚举nonce,生成一个小于target的数字。这样新产生的区块是成立的(即前面0个数大于等于)

这一过程被称为:工作量证明(PoW算法)

3.SHA256的充足性

sha256生成的哈希值256二进制位,有大约是1.157920892×10^77种可能

而我们要求一个区块链中的区块哈希值不能相同,这个算法产生的值重复的概率几乎为零

哪怕是满足target限制的hash值也是

2.源代码

package main
​
import (
    "bytes"
    "crypto/sha256"
    "encoding/binary"
    "fmt"
    "log"
    "math"
    "math/big"
    "time"
)
​
type Block struct {
    Timestamp int64
    Hash      []byte
    PrevHash  []byte
    Target    []byte 
    Nonce     int64  
    Data      []byte
}
​
const (
    Difficulty = 12
)
​
func (b *Block) GetTarget() []byte {
    target := big.NewInt(1)
    target.Lsh(target, uint(256-Difficulty))
    return target.Bytes()
}
​
func (b *Block) GetBase4Nonce(nonce int64) []byte {
    data := bytes.Join([][]byte{
        ToHexInt(b.Timestamp),
        b.PrevHash,
        ToHexInt(int64(nonce)),
        b.Target,
        b.Data,
    },
        []byte{},
    )
    return data
}
​
func (b *Block) FindNonce() int64 {
    var intHash big.Int
    var intTarget big.Int
    var hash [32]byte
    var nonce int64
    nonce = 0
    intTarget.SetBytes(b.Target)
​
    for nonce < math.MaxInt64 {
        data := b.GetBase4Nonce(nonce)
        hash = sha256.Sum256(data)
        intHash.SetBytes(hash[:])
        if intHash.Cmp(&intTarget) == -1 {
            break
        } else {
            nonce++
        }
    }
    return nonce
}
​
func (b *Block) ValidatePoW() bool {
    var intHash big.Int
    var intTarget big.Int
    var hash [32]byte
    intTarget.SetBytes(b.Target)
    data := b.GetBase4Nonce(b.Nonce)
    hash = sha256.Sum256(data)
    intHash.SetBytes(hash[:])
    if intHash.Cmp(&intTarget) == -1 {
        return true
    }
    return false
}
​
​
func Handle(err error) {
    if err != nil {
        log.Panic(err)
    }
}
​

func ToHexInt(num int64) []byte {
    buff := new(bytes.Buffer)
    err := binary.Write(buff, binary.BigEndian, num)
    Handle(err)
    return buff.Bytes()
}
​
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
}
​
func (b *Block) SetHash() {
    information := bytes.Join([][]byte{ToHexInt(b.Timestamp), b.PrevHash, b.Target, ToHexInt(b.Nonce), b.Data}, []byte{})
    hash := sha256.Sum256(information)
    b.Hash = hash[:]
}
​
func CreateBlock(prevhash, data []byte) *Block {
    block := Block{time.Now().Unix(), []byte{}, prevhash, []byte{}, 0, data}
    block.Target = block.GetTarget()
    block.Nonce = block.FindNonce()
    block.SetHash()
    return &block
}
​
func GenesisBlock() *Block {
    genesisWords := "Hello, blockchain!"
    return CreateBlock([]byte{}, []byte(genesisWords))
}
​
func main() {
    chain := CreateBlockChain()
    time.Sleep(time.Second)
    chain.AddBlock("After genesis, I have something to say.")
    time.Sleep(time.Second)
    chain.AddBlock("Leo Cao is awesome!")
    time.Sleep(time.Second)
    chain.AddBlock("I can't wait to follow his github!")
    time.Sleep(time.Second)
​
    for _, block := range chain.Blocks {
        fmt.Printf("Timestamp: %d\n", block.Timestamp)
        fmt.Printf("hash: %x\n", block.Hash)
        fmt.Printf("Previous hash: %x\n", block.PrevHash)
        fmt.Printf("nonce: %d\n", block.Nonce)
        fmt.Printf("data: %s\n", block.Data)
        fmt.Println("Proof of Work validation:", block.ValidatePoW())
    }
}

3.分块分析

1.导入包作用

import (
    "bytes"
    "crypto/sha256"
    "encoding/binary"
    "fmt"
    "log"
    "math"
​
    //提供了基本函数 π e登常量 等等计算功能
    //加密货币中的加密算法、共识机制和地址生成算法都可能涉及到数学运算
    "math/big"
    //这个包提供了大数运算的支持
    //加密货币中的椭圆曲线加密算法中经常需要处理大数
    //区块链中的一些计算可能涉及到非常大的数字,比如计算工作量证明的难度目标值
    "time"
)

2.区块包含元素作用

type Block struct {
    Timestamp int64
    Hash      []byte
    PrevHash  []byte
    Target    []byte
    //Target 字段通常用于表示工作量证明(Proof of Work,PoW)算法中的难度目标值。
    //在PoW算法中,矿工需要不断尝试不同的Nonce值,使得区块的哈希值小于或等于Target,以此来满足网络的难度要求
    //区块头中,Target字段表示了当前区块的目标哈希值,矿工需要通过不断调整Nonce值来寻找满足条件的哈希值,从而完成区块的挖掘工作
    Nonce int64
    //Nonce 字段是一个整数值,用于在PoW算法中尝试寻找有效哈希值的过程中进行递增或变化。
    //矿工通过调整Nonce值来产生不同的区块哈希,以尝试满足难度目标。
    Data []byte
}

3.难度

const (
    Difficulty = 12 //设置难度
)

difficulty 反映了矿工找到下一个有效区块的难易程度,难度随区块头目标Hash值(target)的变动而变动,target值越小,难度越大。

4.生成Target

// 在区块结构体(Block)中获取难度目标(Target)的字节数组表示。
func (b *Block) GetTarget() []byte {
    //区块头目标Hash值(target)  target值越小,难度越大
    target := big.NewInt(1)
    //NewInt 大整数对象是一种能够存储任意精度整数的数据类型,用于处理超出普通整数范围的大数字计算
    target.Lsh(target, uint(256-Difficulty))
    //Lsh 方法是左移操作,用于将 target 左移 256-Difficulty 位
    return target.Bytes()
}

如果我把区块链比作 设置挖矿目标和挖矿两个部分,那么这个GetTarget函数实现的是那个部分?

显然是设置难度

5.所有数据连成字节

// 将区块的时间戳(Timestamp)、前一个区块的哈希值(PrevHash)、给定的 nonce、挖矿目标值(Target)、以及区块数据(Data)连接成字节返回
func (b *Block) GetBase4Nonce(nonce int64) []byte {
    //(b *Block) 是一个方法接收器(receiver),它定义了一个方法 GetBase4Nonce() 与 Block 结构体相关联
    //指针类型接收器 意味着这个方法可以在 Block 类型的实例上被调用,并且可以修改这个实例的状态
    data := bytes.Join([][]byte{
        ToHexInt(b.Timestamp),
        b.PrevHash,
        ToHexInt(int64(nonce)),
        b.Target,
        b.Data,
    }, //字节切片被放置在一个外部的切片 [][]byte{} 中,以便稍后使用 bytes.Join 函数将它们连接起来,形成一个单独的字节切片
        []byte{},
    )
    return data
}

6.nonce枚举产生合法区块

func (b *Block) FindNonce() int64 {
    var intHash big.Int //哈希值
    //big.Int 是 Go 语言标准库 math/big 中的一个结构体类型,用于表示任意精度的整数
    var intTarget big.Int //要求挖矿大小target的值
    var hash [32]byte
    var nonce int64 //nonce变量,试图通过枚举来实现区块的创建PoW验证合法
    nonce = 0
    intTarget.SetBytes(b.Target)
​
    for nonce < math.MaxInt64 {
        // 将赋值语句放在函数体内部
        data := b.GetBase4Nonce(nonce)
        //调用函数, 传入nonce 返回一个包含所有数据的切片
        hash = sha256.Sum256(data)         //使用SHA-256 计算data切片的 哈希值
        intHash.SetBytes(hash[:])          //将 hash 数组转换为 big.Int 类型,并赋值给 intHash,准备与目标值进行比较
        if intHash.Cmp(&intTarget) == -1 { //big.Int 类型的方法 Cmp() 来比较两个大整数 intHash 和 intTarget 的大小
            break
        } else {
            nonce++
        }
    }
    return nonce
} //这个函数在创建block节点中用到
​

7.判断节点是否合法

// 通过hash和target的比较 判断节点是否合法
func (b *Block) ValidatePoW() bool {
    var intHash big.Int
    var intTarget big.Int
    var hash [32]byte
    intTarget.SetBytes(b.Target)
    //将字节数组 b.Target 的值设置给 intTarget 变量
    data := b.GetBase4Nonce(b.Nonce)
    hash = sha256.Sum256(data)
    intHash.SetBytes(hash[:])
    if intHash.Cmp(&intTarget) == -1 {
        return true
    }
    return false
}

8.处理错误

// 用于处理错误的通用函数 Handle(err error) 作用是在出现错误时,记录错误信息并终止程序的执行
func Handle(err error) {
    if err != nil {
        log.Panic(err)
    }
}

运行结果

go run main.go
​
​
Timestamp: 1711788684
hash: d21ec302d0aa322a933947b2020419e054627dbb0bf074d56deb42b862abedcb
Previous hash: 
nonce: 14124
data: Hello, blockchain!
Proof of Work validation: true
Timestamp: 1711788685
hash: 6f3c44e96d073df10d86ec1f2c609795cffea2d23220e42cec19c8bceedeb96d
Previous hash: d21ec302d0aa322a933947b2020419e054627dbb0bf074d56deb42b862abedcb
nonce: 270
data: This is the second blockchain. 
Proof of Work validation: true
Timestamp: 1711788686
hash: 84b859767f63568e9779ce3bee36d93b28a734b4611bca087b30040fdf158140
Previous hash: 6f3c44e96d073df10d86ec1f2c609795cffea2d23220e42cec19c8bceedeb96d
nonce: 463
data: We added Pow in it
Proof of Work validation: true
Timestamp: 1711788687
hash: 5ac063f26d47122350219560d39c8fff1f0187206244c5e75e1f7ddc0f4d9104
Previous hash: 84b859767f63568e9779ce3bee36d93b28a734b4611bca087b30040fdf158140
nonce: 2248
data: We learn Hash in a new way,more deeply.
Proof of Work validation: true
Timestamp: 1711788688
hash: 33b8604d40d2ac8f097fcb4ed8a76ef72bd37ae2959cec623ba880d1d6489cd5
Previous hash: 5ac063f26d47122350219560d39c8fff1f0187206244c5e75e1f7ddc0f4d9104
nonce: 246
data: Waiting forward to the next Line!
Proof of Work validation: true
​
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值