赞
踩
在我的上一篇文章中,我们介绍了区块链的基本概念和简单实现。那篇文章的重点在于如何通过Go语言构建一个简单的区块链结构,以及如何进行区块的基本操作。然而,现实世界中的区块链系统如比特币和以太坊,采用了一种被称为“工作量证明”(Proof of Work, PoW)的机制来增加网络安全性和稳定性。今天,我们将在之前的基础上引入这一机制,使我们的模拟区块链系统更接近实际应用。
上篇文章地址:Go语言实现简单区块链-CSDN博客
上个版本保存和读取函数并不能很好的完成工作,用json进行序列化在保存会好很多。此外,在主函数进行了一点修改,不会在每次打开都去创建创世区块了,而是创建一个全局的区块链对象。
工作量证明是一种经济措施,用于防止网络服务的滥用(例如,拒绝服务攻击和其他服务滥用)。在区块链中,这种机制用于确认交易并产生新的区块。矿工需要解决一个数学难题,这个难题足够复杂,需要计算资源去解决,但验证结果却相对容易。
本次实现的代码思路与上次略有不同,主要包括以下几个部分:
首先是基本的区块结构体,包含时间戳、数据、前一个区块的哈希值、当前区块的哈希值、随机数Nonce以及交易数据。
- type Block struct {
- Timestamp int64 // 时间戳
- Data []byte // 数据
- PrevBlockHash []byte // 前一个区块的哈希
- Hash []byte // 当前区块的哈希
- Nonce int // 随机数
- TXs []string // 交易
- }
工作量证明结构体ProofOfWork
包含一个目标值target
,这个目标值定义了有效哈希的条件。有效的哈希必须小于这个目标值。难度targetBits
定义了这个条件的严格程度。
- type ProofOfWork struct {
- block *Block
- target *big.Int
- }
- // NewGenesisBlock 生成创世区块
- func NewGenesisBlock() *Block {
- //创建一个创世区块
- return NewBlock("Genesis Block", []byte{})
- }
-
- // NewBlockchain 创建一个新的区块链
- func NewBlockchain() *Blockchain {
- //创建一个包含创世区块的区块链
- return &Blockchain{[]*Block{NewGenesisBlock()}}
- }
新区块的生成现在包括了运行工作量证明算法来找到合适的Nonce,使得区块的哈希值满足特定的条件。
- // NewBlock 生成新区块
- func NewBlock(data string, prevBlockHash []byte) *Block {
- block := &Block{Timestamp: time.Now().Unix(), Data: []byte(data), PrevBlockHash: prevBlockHash, Hash: []byte{}, Nonce: 0}
- //创建工作量证明
- pow := NewProofOfWork(block)
- //执行工作量证明得到nonce和hash值
- nonce, hash := pow.Run()
- //设置区块的hash值,nonce值,交易
- block.TXs = GetTXs()
- block.Hash = hash[:]
- block.Nonce = nonce
- return block
- }
-
- // AddBlock 用于向区块链中添加新的区块
- func (bc *Blockchain) AddBlock(data string) {
- // 获取前一个区块
- prevBlock := bc.blocks[len(bc.blocks)-1]
- // 生成新区块
- newBlock := NewBlock(data, prevBlock.Hash)
- //添加前验证工作量证明
- if !NewProofOfWork(newBlock).Validate() {
- fmt.Println("工作量证明验证失败")
- return
- }
- fmt.Printf("区块%s工作量证明验证成功\n", newBlock.Data)
- // 将新区块添加到区块链中
- bc.blocks = append(bc.blocks, newBlock)
- }
Run方法寻找有效的Nonce,Validate方法验证区块的工作量证明是否有效,prepareData方法将block各个字段和nonce(可以加上TXs等字段)作为输入,计算得到hash值作为输出,IntToHex 将一个int64类型的数据转换为一个字节数组,NewProofOfWork 用于创建一个新的工作量证明
- // Run 用于执行工作量证明,返回满足条件的nonce和hash值
- 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)
- // 不断计算hash值,直到找到满足条件的hash值
- for nonce < maxNonce {
- data := pow.prepareData(nonce)
- hash = sha256.Sum256(data)
- fmt.Printf("\r%x", hash)
- hashInt.SetBytes(hash[:])
- // 检查hashInt是否小于pow.target
- // 如果是,则工作完成;否则,nonce加1,继续尝试
- if hashInt.Cmp(pow.target) == -1 {
- break
- } else {
- nonce++
- }
- }
- fmt.Print("\n\n")
-
- return nonce, hash[:]
- }
-
- // Validate 用于验证工作量证明
- func (pow *ProofOfWork) Validate() bool {
- var hashInt big.Int
- // 计算hash值
- data := pow.prepareData(pow.block.Nonce)
-
- hash := sha256.Sum256(data)
- // 将hash值转换为一个大整数
- hashInt.SetBytes(hash[:])
- // 检查hashInt是否小于pow.target
- isValid := hashInt.Cmp(pow.target) == -1
-
- return isValid
- }
-
-
- // prepareData方法将block各个字段和nonce(可以加上TXs等字段)作为输入,计算得到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
- }
-
- // IntToHex 将一个int64类型的数据转换为一个字节数组
- func IntToHex(num int64) []byte {
- buff := new(bytes.Buffer)
- err := binary.Write(buff, binary.BigEndian, num)
- if err != nil {
- panic(err)
- }
- return buff.Bytes()
- }
-
- // NewProofOfWork 用于创建一个新的工作量证明
- func NewProofOfWork(b *Block) *ProofOfWork {
- // 创建一个初始值为1的target
- target := big.NewInt(1)
- // 将target左移256-targetBits位
- target.Lsh(target, uint(256-targetBits))
- // 创建一个工作量证明
- pow := &ProofOfWork{b, target}
- return pow
- }
这两个函数负责将区块链的数据保存到本地文件(Block.txt
)以及从该文件中读取区块链数据。SaveBlockToLocal
方法遍历区块链中的所有区块,并将每个区块的信息(如前一个区块哈希、数据、时间戳、当前哈希、随机数Nonce和交易数据)格式化后写入到文件中。ReadBlockFromLocal
方法则打开同一个文件,逐行读取并解析每个区块的数据,重建区块链的内存表示。这两个函数是区块链数据持久化和恢复过程中关键的组成部分,确保了区块链的状态可以在系统重启后被恢复。
- // SaveBlockToLocal 保存区块到本地
- func (bc *Blockchain) SaveBlockToLocal() {
- // 打开文件,没有则创建
- file, err := os.OpenFile("Block.txt", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0766)
- if err != nil {
- fmt.Println("文件打开失败", err)
- return
- }
- defer file.Close()
-
- // 写入文件
- for _, block := range bc.blocks {
- // 写入前先序列化区块数据
- blockData, err := json.Marshal(block)
- if err != nil {
- fmt.Println("序列化区块失败", err)
- continue
- }
- file.Write(blockData)
- file.WriteString("\n") // 在每个区块后添加换行符以分隔区块
- }
- }
- // ReadBlockFromLocal 从本地读取区块
- func (bc *Blockchain) ReadBlockFromLocal() {
- // 打开文件
- file, err := os.OpenFile("Block.txt", os.O_RDONLY, 0766)
- info, err := file.Stat()
- if err != nil {
- fmt.Println("获取文件信息失败", err)
- return
- }
- if info.Size() == 0 {
- bc.blocks = NewBlockchain().blocks
- fmt.Println("区块链为空,创建一个新的区块链")
- return
- }
- // 使用 bufio.Scanner 逐行读取
- scanner := bufio.NewScanner(file)
- for scanner.Scan() {
- var block Block
- // 解析区块
- err := json.Unmarshal([]byte(scanner.Text()), &block)
- if err != nil {
- fmt.Println("解析区块失败", err)
- continue
- }
- bc.blocks = append(bc.blocks, &block)
- }
-
- if err := scanner.Err(); err != nil {
- fmt.Println("读取文件时发生错误:", err)
- }
- }
- const (
- targetBits = 8 // targetBits 表示哈希值的前导0的个数
- maxNonce = 10000000 // maxNonce 表示随机数的最大值
- )
-
- func main() {
- // 创建一个新的区块链
- bc := Blockchain{}
- //Block初始化,有则打开,无则创建
- bc.ReadBlockFromLocal()
- fmt.Println("区块链初始化成功", bc.blocks)
- loop := true
- for {
- fmt.Println("------区块链系统demo------")
- fmt.Println("-----1. 添加区块-----")
- fmt.Println("-----2. 打印区块-----")
- fmt.Println("-----3. 保存区块-----")
- fmt.Println("-----4. 退出-----")
- fmt.Println("请输入您的选择:")
- var choice int
- _, err := fmt.Scanln(&choice)
- if err != nil {
- return
- }
- switch choice {
- case 1:
- fmt.Println("请输入生成区块的个数:")
- n := 0
- _, err := fmt.Scanln(&n)
- if err != nil {
- return
- }
- for i := 0; i < n; i++ {
- bc.AddBlock("Send 1 BTC to Ivan")
- }
- case 2:
- fmt.Println("打印区块")
- PrintBlockChain(&bc)
- case 3:
- fmt.Println("保存区块")
- bc.SaveBlockToLocal()
- case 4:
- fmt.Println("退出")
- loop = false
- }
-
- if loop == false {
- break
- }
- }
-
- }
- // PrintBlockChain 打印区块链
- func PrintBlockChain(bc *Blockchain) {
- 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("Nonce: %d\n", block.Nonce)
- fmt.Printf("TXs: %s\n", block.TXs)
- fmt.Println()
- }
- }
-
- // GetTXs 生成交易
- func GetTXs() []string {
- //随机生成交易
- var txs []string
- for i := 0; i < 5; i++ {
- txs = append(txs, fmt.Sprintf("TX%d", rand.Intn(100)))
- }
- return txs
- }
怕有些兄弟不太能运行成功代码,特把源代码放在下边
- // -*- coding: utf-8 -*-
- // Time : 2024/4/14 23:48
- // Author : blue
- // File : proofwork.go
- // Software: Goland
- package main
-
- import (
- "bufio"
- "bytes"
- "crypto/sha256"
- "encoding/binary"
- "encoding/json"
- "fmt"
- "math/big"
- "math/rand"
- "os"
- "time"
- )
-
- // ProofOfWork 表示工作量证明结构
- type ProofOfWork struct {
- block *Block
- target *big.Int
- }
-
- // Block 表示区块结构
- type Block struct {
- PrevBlockHash []byte `json:"PrevBlockHash"` //前一个区块的哈希值
- Data []byte `json:"Data"` // 数据
- Timestamp int64 `json:"Timestamp"` // 时间戳
- Hash []byte `json:"Hash"` // 当前区块的哈希值
- Nonce int `json:"Nonce"` // 随机数
- TXs []string `json:"TXs"` // 交易
- }
-
- // 区块链结构
- type Blockchain struct {
- blocks []*Block
- }
-
- // targetBits 表示哈希值的前导0的个数
- const (
- targetBits = 8 // targetBits 表示哈希值的前导0的个数
- maxNonce = 10000000 // maxNonce 表示随机数的最大值
- )
-
- func main() {
- // 创建一个新的区块链
- bc := Blockchain{}
- //Block初始化,有则打开,无则创建
- bc.ReadBlockFromLocal()
- fmt.Println("区块链初始化成功", bc.blocks)
- loop := true
- for {
- fmt.Println("------区块链系统demo------")
- fmt.Println("-----1. 添加区块-----")
- fmt.Println("-----2. 打印区块-----")
- fmt.Println("-----3. 保存区块-----")
- fmt.Println("-----4. 退出-----")
- fmt.Println("请输入您的选择:")
- var choice int
- _, err := fmt.Scanln(&choice)
- if err != nil {
- return
- }
- switch choice {
- case 1:
- fmt.Println("请输入生成区块的个数:")
- n := 0
- _, err := fmt.Scanln(&n)
- if err != nil {
- return
- }
- for i := 0; i < n; i++ {
- bc.AddBlock("Send 1 BTC to Ivan")
- }
- case 2:
- fmt.Println("打印区块")
- PrintBlockChain(&bc)
- case 3:
- fmt.Println("保存区块")
- bc.SaveBlockToLocal()
- case 4:
- fmt.Println("退出")
- loop = false
- }
-
- if loop == false {
- break
- }
- }
-
- }
-
- // PrintBlockChain 打印区块链
- func PrintBlockChain(bc *Blockchain) {
- 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("Nonce: %d\n", block.Nonce)
- fmt.Printf("TXs: %s\n", block.TXs)
- fmt.Println()
- }
- }
-
- // NewGenesisBlock 生成创世区块
- func NewGenesisBlock() *Block {
- //创建一个创世区块
- return NewBlock("Genesis Block", []byte{})
- }
-
- // NewBlockchain 创建一个新的区块链
- func NewBlockchain() *Blockchain {
- //创建一个包含创世区块的区块链
- return &Blockchain{[]*Block{NewGenesisBlock()}}
- }
-
- // AddBlock 用于向区块链中添加新的区块
- func (bc *Blockchain) AddBlock(data string) {
- // 获取前一个区块
- prevBlock := bc.blocks[len(bc.blocks)-1]
- // 生成新区块
- newBlock := NewBlock(data, prevBlock.Hash)
- //添加前验证工作量证明
- if !NewProofOfWork(newBlock).Validate() {
- fmt.Println("工作量证明验证失败")
- return
- }
- fmt.Printf("区块%s工作量证明验证成功\n", newBlock.Data)
- // 将新区块添加到区块链中
- bc.blocks = append(bc.blocks, newBlock)
- }
-
- // NewBlock 生成新区块
- func NewBlock(data string, prevBlockHash []byte) *Block {
- block := &Block{Timestamp: time.Now().Unix(), Data: []byte(data), PrevBlockHash: prevBlockHash, Hash: []byte{}, Nonce: 0}
- //创建工作量证明
- pow := NewProofOfWork(block)
- //执行工作量证明得到nonce和hash值
- nonce, hash := pow.Run()
- //设置区块的hash值,nonce值,交易
- block.TXs = GetTXs()
- block.Hash = hash[:]
- block.Nonce = nonce
- return block
- }
-
- // GetTXs 生成交易
- func GetTXs() []string {
- //随机生成交易
- var txs []string
- for i := 0; i < 5; i++ {
- txs = append(txs, fmt.Sprintf("TX%d", rand.Intn(100)))
- }
- return txs
- }
-
- // Run 用于执行工作量证明,返回满足条件的nonce和hash值
- 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)
- // 不断计算hash值,直到找到满足条件的hash值
- for nonce < maxNonce {
- data := pow.prepareData(nonce)
- hash = sha256.Sum256(data)
- fmt.Printf("\r%x", hash)
- hashInt.SetBytes(hash[:])
- // 检查hashInt是否小于pow.target
- // 如果是,则工作完成;否则,nonce加1,继续尝试
- if hashInt.Cmp(pow.target) == -1 {
- break
- } else {
- nonce++
- }
- }
- fmt.Print("\n\n")
-
- return nonce, hash[:]
- }
-
- // prepareData方法将block各个字段和nonce(可以加上TXs等字段)作为输入,计算得到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
- }
-
- // Validate 用于验证工作量证明
- func (pow *ProofOfWork) Validate() bool {
- var hashInt big.Int
- // 计算hash值
- data := pow.prepareData(pow.block.Nonce)
-
- hash := sha256.Sum256(data)
- // 将hash值转换为一个大整数
- hashInt.SetBytes(hash[:])
- // 检查hashInt是否小于pow.target
- isValid := hashInt.Cmp(pow.target) == -1
-
- return isValid
- }
-
- // IntToHex 将一个int64类型的数据转换为一个字节数组
- func IntToHex(num int64) []byte {
- buff := new(bytes.Buffer)
- err := binary.Write(buff, binary.BigEndian, num)
- if err != nil {
- panic(err)
- }
- return buff.Bytes()
- }
-
- // NewProofOfWork 用于创建一个新的工作量证明
- func NewProofOfWork(b *Block) *ProofOfWork {
- // 创建一个初始值为1的target
- target := big.NewInt(1)
- // 将target左移256-targetBits位
- target.Lsh(target, uint(256-targetBits))
- // 创建一个工作量证明
- pow := &ProofOfWork{b, target}
- return pow
- }
-
- func (bc *Blockchain) SaveBlockToLocal() {
- // 打开文件,没有则创建
- file, err := os.OpenFile("Block.txt", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0766)
- if err != nil {
- fmt.Println("文件打开失败", err)
- return
- }
- defer file.Close()
-
- // 写入文件
- for _, block := range bc.blocks {
- // 写入前先序列化区块数据
- blockData, err := json.Marshal(block)
- if err != nil {
- fmt.Println("序列化区块失败", err)
- continue
- }
- file.Write(blockData)
- file.WriteString("\n") // 在每个区块后添加换行符以分隔区块
- }
- }
- func (bc *Blockchain) ReadBlockFromLocal() {
- // 打开文件
- file, err := os.OpenFile("Block.txt", os.O_RDONLY, 0766)
- info, err := file.Stat()
- if err != nil {
- fmt.Println("获取文件信息失败", err)
- return
- }
- if info.Size() == 0 {
- bc.blocks = NewBlockchain().blocks
- fmt.Println("区块链为空,创建一个新的区块链")
- return
- }
- // 使用 bufio.Scanner 逐行读取
- scanner := bufio.NewScanner(file)
- for scanner.Scan() {
- var block Block
- // 解析区块
- err := json.Unmarshal([]byte(scanner.Text()), &block)
- if err != nil {
- fmt.Println("解析区块失败", err)
- continue
- }
- bc.blocks = append(bc.blocks, &block)
- }
-
- if err := scanner.Err(); err != nil {
- fmt.Println("读取文件时发生错误:", err)
- }
- }
通过引入工作量证明机制,我们的区块链模拟系统更加完善和接近现实中的区块链网络。这不仅增强了系统的安全性,也为后续的扩展和应用打下了坚实基础。在实际应用中,工作量证明可能会被更高效的共识机制取代,但其核心理念——通过消耗资源来保证网络安全性,仍然值得理解和学习。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。