当前位置:   article > 正文

Go语言实现简单区块链——增加POW机制_区块链 工作量证明机制

区块链 工作量证明机制

探索区块链:引入工作量证明机制

        在我的上一篇文章中,我们介绍了区块链的基本概念和简单实现。那篇文章的重点在于如何通过Go语言构建一个简单的区块链结构,以及如何进行区块的基本操作。然而,现实世界中的区块链系统如比特币和以太坊,采用了一种被称为“工作量证明”(Proof of Work, PoW)的机制来增加网络安全性和稳定性。今天,我们将在之前的基础上引入这一机制,使我们的模拟区块链系统更接近实际应用。

上篇文章地址:Go语言实现简单区块链-CSDN博客

        上个版本保存和读取函数并不能很好的完成工作,用json进行序列化在保存会好很多。此外,在主函数进行了一点修改,不会在每次打开都去创建创世区块了,而是创建一个全局的区块链对象。

工作量证明机制简介

        工作量证明是一种经济措施,用于防止网络服务的滥用(例如,拒绝服务攻击和其他服务滥用)。在区块链中,这种机制用于确认交易并产生新的区块。矿工需要解决一个数学难题,这个难题足够复杂,需要计算资源去解决,但验证结果却相对容易。

代码实现

       本次实现的代码思路与上次略有不同,主要包括以下几个部分:

区块结构体

       首先是基本的区块结构体,包含时间戳、数据、前一个区块的哈希值、当前区块的哈希值、随机数Nonce以及交易数据。

  1. type Block struct {
  2. Timestamp int64 // 时间戳
  3. Data []byte // 数据
  4. PrevBlockHash []byte // 前一个区块的哈希
  5. Hash []byte // 当前区块的哈希
  6. Nonce int // 随机数
  7. TXs []string // 交易
  8. }
工作量证明结构体

       工作量证明结构体ProofOfWork包含一个目标值target,这个目标值定义了有效哈希的条件。有效的哈希必须小于这个目标值。难度targetBits定义了这个条件的严格程度。

  1. type ProofOfWork struct {
  2. block *Block
  3. target *big.Int
  4. }
创世区块的创建
  1. // NewGenesisBlock 生成创世区块
  2. func NewGenesisBlock() *Block {
  3. //创建一个创世区块
  4. return NewBlock("Genesis Block", []byte{})
  5. }
  6. // NewBlockchain 创建一个新的区块链
  7. func NewBlockchain() *Blockchain {
  8. //创建一个包含创世区块的区块链
  9. return &Blockchain{[]*Block{NewGenesisBlock()}}
  10. }
生成并添加新区块

       新区块的生成现在包括了运行工作量证明算法来找到合适的Nonce,使得区块的哈希值满足特定的条件。

  1. // NewBlock 生成新区块
  2. func NewBlock(data string, prevBlockHash []byte) *Block {
  3. block := &Block{Timestamp: time.Now().Unix(), Data: []byte(data), PrevBlockHash: prevBlockHash, Hash: []byte{}, Nonce: 0}
  4. //创建工作量证明
  5. pow := NewProofOfWork(block)
  6. //执行工作量证明得到nonce和hash值
  7. nonce, hash := pow.Run()
  8. //设置区块的hash值,nonce值,交易
  9. block.TXs = GetTXs()
  10. block.Hash = hash[:]
  11. block.Nonce = nonce
  12. return block
  13. }
  14. // AddBlock 用于向区块链中添加新的区块
  15. func (bc *Blockchain) AddBlock(data string) {
  16. // 获取前一个区块
  17. prevBlock := bc.blocks[len(bc.blocks)-1]
  18. // 生成新区块
  19. newBlock := NewBlock(data, prevBlock.Hash)
  20. //添加前验证工作量证明
  21. if !NewProofOfWork(newBlock).Validate() {
  22. fmt.Println("工作量证明验证失败")
  23. return
  24. }
  25. fmt.Printf("区块%s工作量证明验证成功\n", newBlock.Data)
  26. // 将新区块添加到区块链中
  27. bc.blocks = append(bc.blocks, newBlock)
  28. }
工作量证明相关函数

       Run方法寻找有效的Nonce,Validate方法验证区块的工作量证明是否有效,prepareData方法将block各个字段和nonce(可以加上TXs等字段)作为输入,计算得到hash值作为输出,IntToHex 将一个int64类型的数据转换为一个字节数组,NewProofOfWork 用于创建一个新的工作量证明

  1. // Run 用于执行工作量证明,返回满足条件的nonce和hash值
  2. func (pow *ProofOfWork) Run() (int, []byte) {
  3. var hashInt big.Int
  4. var hash [32]byte
  5. nonce := 0
  6. fmt.Printf("Mining the block containing \"%s\"\n", pow.block.Data)
  7. // 不断计算hash值,直到找到满足条件的hash值
  8. for nonce < maxNonce {
  9. data := pow.prepareData(nonce)
  10. hash = sha256.Sum256(data)
  11. fmt.Printf("\r%x", hash)
  12. hashInt.SetBytes(hash[:])
  13. // 检查hashInt是否小于pow.target
  14. // 如果是,则工作完成;否则,nonce加1,继续尝试
  15. if hashInt.Cmp(pow.target) == -1 {
  16. break
  17. } else {
  18. nonce++
  19. }
  20. }
  21. fmt.Print("\n\n")
  22. return nonce, hash[:]
  23. }
  24. // Validate 用于验证工作量证明
  25. func (pow *ProofOfWork) Validate() bool {
  26. var hashInt big.Int
  27. // 计算hash值
  28. data := pow.prepareData(pow.block.Nonce)
  29. hash := sha256.Sum256(data)
  30. // 将hash值转换为一个大整数
  31. hashInt.SetBytes(hash[:])
  32. // 检查hashInt是否小于pow.target
  33. isValid := hashInt.Cmp(pow.target) == -1
  34. return isValid
  35. }
  36. // prepareData方法将block各个字段和nonce(可以加上TXs等字段)作为输入,计算得到hash值作为输出
  37. func (pow *ProofOfWork) prepareData(nonce int) []byte {
  38. data := bytes.Join(
  39. [][]byte{
  40. pow.block.PrevBlockHash,
  41. pow.block.Data,
  42. IntToHex(pow.block.Timestamp),
  43. IntToHex(int64(targetBits)),
  44. IntToHex(int64(nonce)),
  45. },
  46. []byte{},
  47. )
  48. return data
  49. }
  50. // IntToHex 将一个int64类型的数据转换为一个字节数组
  51. func IntToHex(num int64) []byte {
  52. buff := new(bytes.Buffer)
  53. err := binary.Write(buff, binary.BigEndian, num)
  54. if err != nil {
  55. panic(err)
  56. }
  57. return buff.Bytes()
  58. }
  59. // NewProofOfWork 用于创建一个新的工作量证明
  60. func NewProofOfWork(b *Block) *ProofOfWork {
  61. // 创建一个初始值为1的target
  62. target := big.NewInt(1)
  63. // 将target左移256-targetBits位
  64. target.Lsh(target, uint(256-targetBits))
  65. // 创建一个工作量证明
  66. pow := &ProofOfWork{b, target}
  67. return pow
  68. }
文件的保存和读取

       这两个函数负责将区块链的数据保存到本地文件(Block.txt)以及从该文件中读取区块链数据。SaveBlockToLocal 方法遍历区块链中的所有区块,并将每个区块的信息(如前一个区块哈希、数据、时间戳、当前哈希、随机数Nonce和交易数据)格式化后写入到文件中。ReadBlockFromLocal 方法则打开同一个文件,逐行读取并解析每个区块的数据,重建区块链的内存表示。这两个函数是区块链数据持久化和恢复过程中关键的组成部分,确保了区块链的状态可以在系统重启后被恢复。

  1. // SaveBlockToLocal 保存区块到本地
  2. func (bc *Blockchain) SaveBlockToLocal() {
  3. // 打开文件,没有则创建
  4. file, err := os.OpenFile("Block.txt", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0766)
  5. if err != nil {
  6. fmt.Println("文件打开失败", err)
  7. return
  8. }
  9. defer file.Close()
  10. // 写入文件
  11. for _, block := range bc.blocks {
  12. // 写入前先序列化区块数据
  13. blockData, err := json.Marshal(block)
  14. if err != nil {
  15. fmt.Println("序列化区块失败", err)
  16. continue
  17. }
  18. file.Write(blockData)
  19. file.WriteString("\n") // 在每个区块后添加换行符以分隔区块
  20. }
  21. }
  22. // ReadBlockFromLocal 从本地读取区块
  23. func (bc *Blockchain) ReadBlockFromLocal() {
  24. // 打开文件
  25. file, err := os.OpenFile("Block.txt", os.O_RDONLY, 0766)
  26. info, err := file.Stat()
  27. if err != nil {
  28. fmt.Println("获取文件信息失败", err)
  29. return
  30. }
  31. if info.Size() == 0 {
  32. bc.blocks = NewBlockchain().blocks
  33. fmt.Println("区块链为空,创建一个新的区块链")
  34. return
  35. }
  36. // 使用 bufio.Scanner 逐行读取
  37. scanner := bufio.NewScanner(file)
  38. for scanner.Scan() {
  39. var block Block
  40. // 解析区块
  41. err := json.Unmarshal([]byte(scanner.Text()), &block)
  42. if err != nil {
  43. fmt.Println("解析区块失败", err)
  44. continue
  45. }
  46. bc.blocks = append(bc.blocks, &block)
  47. }
  48. if err := scanner.Err(); err != nil {
  49. fmt.Println("读取文件时发生错误:", err)
  50. }
  51. }
 打印,交易及主函数
  1. const (
  2. targetBits = 8 // targetBits 表示哈希值的前导0的个数
  3. maxNonce = 10000000 // maxNonce 表示随机数的最大值
  4. )
  5. func main() {
  6. // 创建一个新的区块链
  7. bc := Blockchain{}
  8. //Block初始化,有则打开,无则创建
  9. bc.ReadBlockFromLocal()
  10. fmt.Println("区块链初始化成功", bc.blocks)
  11. loop := true
  12. for {
  13. fmt.Println("------区块链系统demo------")
  14. fmt.Println("-----1. 添加区块-----")
  15. fmt.Println("-----2. 打印区块-----")
  16. fmt.Println("-----3. 保存区块-----")
  17. fmt.Println("-----4. 退出-----")
  18. fmt.Println("请输入您的选择:")
  19. var choice int
  20. _, err := fmt.Scanln(&choice)
  21. if err != nil {
  22. return
  23. }
  24. switch choice {
  25. case 1:
  26. fmt.Println("请输入生成区块的个数:")
  27. n := 0
  28. _, err := fmt.Scanln(&n)
  29. if err != nil {
  30. return
  31. }
  32. for i := 0; i < n; i++ {
  33. bc.AddBlock("Send 1 BTC to Ivan")
  34. }
  35. case 2:
  36. fmt.Println("打印区块")
  37. PrintBlockChain(&bc)
  38. case 3:
  39. fmt.Println("保存区块")
  40. bc.SaveBlockToLocal()
  41. case 4:
  42. fmt.Println("退出")
  43. loop = false
  44. }
  45. if loop == false {
  46. break
  47. }
  48. }
  49. }
  50. // PrintBlockChain 打印区块链
  51. func PrintBlockChain(bc *Blockchain) {
  52. for _, block := range bc.blocks {
  53. fmt.Printf("PrevBlockHash: %x\n", block.PrevBlockHash)
  54. fmt.Printf("Data: %s\n", block.Data)
  55. fmt.Printf("Hash: %x\n", block.Hash)
  56. fmt.Printf("Nonce: %d\n", block.Nonce)
  57. fmt.Printf("TXs: %s\n", block.TXs)
  58. fmt.Println()
  59. }
  60. }
  61. // GetTXs 生成交易
  62. func GetTXs() []string {
  63. //随机生成交易
  64. var txs []string
  65. for i := 0; i < 5; i++ {
  66. txs = append(txs, fmt.Sprintf("TX%d", rand.Intn(100)))
  67. }
  68. return txs
  69. }

完整代码

       怕有些兄弟不太能运行成功代码,特把源代码放在下边

  1. // -*- coding: utf-8 -*-
  2. // Time : 2024/4/14 23:48
  3. // Author : blue
  4. // File : proofwork.go
  5. // Software: Goland
  6. package main
  7. import (
  8. "bufio"
  9. "bytes"
  10. "crypto/sha256"
  11. "encoding/binary"
  12. "encoding/json"
  13. "fmt"
  14. "math/big"
  15. "math/rand"
  16. "os"
  17. "time"
  18. )
  19. // ProofOfWork 表示工作量证明结构
  20. type ProofOfWork struct {
  21. block *Block
  22. target *big.Int
  23. }
  24. // Block 表示区块结构
  25. type Block struct {
  26. PrevBlockHash []byte `json:"PrevBlockHash"` //前一个区块的哈希值
  27. Data []byte `json:"Data"` // 数据
  28. Timestamp int64 `json:"Timestamp"` // 时间戳
  29. Hash []byte `json:"Hash"` // 当前区块的哈希值
  30. Nonce int `json:"Nonce"` // 随机数
  31. TXs []string `json:"TXs"` // 交易
  32. }
  33. // 区块链结构
  34. type Blockchain struct {
  35. blocks []*Block
  36. }
  37. // targetBits 表示哈希值的前导0的个数
  38. const (
  39. targetBits = 8 // targetBits 表示哈希值的前导0的个数
  40. maxNonce = 10000000 // maxNonce 表示随机数的最大值
  41. )
  42. func main() {
  43. // 创建一个新的区块链
  44. bc := Blockchain{}
  45. //Block初始化,有则打开,无则创建
  46. bc.ReadBlockFromLocal()
  47. fmt.Println("区块链初始化成功", bc.blocks)
  48. loop := true
  49. for {
  50. fmt.Println("------区块链系统demo------")
  51. fmt.Println("-----1. 添加区块-----")
  52. fmt.Println("-----2. 打印区块-----")
  53. fmt.Println("-----3. 保存区块-----")
  54. fmt.Println("-----4. 退出-----")
  55. fmt.Println("请输入您的选择:")
  56. var choice int
  57. _, err := fmt.Scanln(&choice)
  58. if err != nil {
  59. return
  60. }
  61. switch choice {
  62. case 1:
  63. fmt.Println("请输入生成区块的个数:")
  64. n := 0
  65. _, err := fmt.Scanln(&n)
  66. if err != nil {
  67. return
  68. }
  69. for i := 0; i < n; i++ {
  70. bc.AddBlock("Send 1 BTC to Ivan")
  71. }
  72. case 2:
  73. fmt.Println("打印区块")
  74. PrintBlockChain(&bc)
  75. case 3:
  76. fmt.Println("保存区块")
  77. bc.SaveBlockToLocal()
  78. case 4:
  79. fmt.Println("退出")
  80. loop = false
  81. }
  82. if loop == false {
  83. break
  84. }
  85. }
  86. }
  87. // PrintBlockChain 打印区块链
  88. func PrintBlockChain(bc *Blockchain) {
  89. for _, block := range bc.blocks {
  90. fmt.Printf("PrevBlockHash: %x\n", block.PrevBlockHash)
  91. fmt.Printf("Data: %s\n", block.Data)
  92. fmt.Printf("Hash: %x\n", block.Hash)
  93. fmt.Printf("Nonce: %d\n", block.Nonce)
  94. fmt.Printf("TXs: %s\n", block.TXs)
  95. fmt.Println()
  96. }
  97. }
  98. // NewGenesisBlock 生成创世区块
  99. func NewGenesisBlock() *Block {
  100. //创建一个创世区块
  101. return NewBlock("Genesis Block", []byte{})
  102. }
  103. // NewBlockchain 创建一个新的区块链
  104. func NewBlockchain() *Blockchain {
  105. //创建一个包含创世区块的区块链
  106. return &Blockchain{[]*Block{NewGenesisBlock()}}
  107. }
  108. // AddBlock 用于向区块链中添加新的区块
  109. func (bc *Blockchain) AddBlock(data string) {
  110. // 获取前一个区块
  111. prevBlock := bc.blocks[len(bc.blocks)-1]
  112. // 生成新区块
  113. newBlock := NewBlock(data, prevBlock.Hash)
  114. //添加前验证工作量证明
  115. if !NewProofOfWork(newBlock).Validate() {
  116. fmt.Println("工作量证明验证失败")
  117. return
  118. }
  119. fmt.Printf("区块%s工作量证明验证成功\n", newBlock.Data)
  120. // 将新区块添加到区块链中
  121. bc.blocks = append(bc.blocks, newBlock)
  122. }
  123. // NewBlock 生成新区块
  124. func NewBlock(data string, prevBlockHash []byte) *Block {
  125. block := &Block{Timestamp: time.Now().Unix(), Data: []byte(data), PrevBlockHash: prevBlockHash, Hash: []byte{}, Nonce: 0}
  126. //创建工作量证明
  127. pow := NewProofOfWork(block)
  128. //执行工作量证明得到nonce和hash值
  129. nonce, hash := pow.Run()
  130. //设置区块的hash值,nonce值,交易
  131. block.TXs = GetTXs()
  132. block.Hash = hash[:]
  133. block.Nonce = nonce
  134. return block
  135. }
  136. // GetTXs 生成交易
  137. func GetTXs() []string {
  138. //随机生成交易
  139. var txs []string
  140. for i := 0; i < 5; i++ {
  141. txs = append(txs, fmt.Sprintf("TX%d", rand.Intn(100)))
  142. }
  143. return txs
  144. }
  145. // Run 用于执行工作量证明,返回满足条件的nonce和hash值
  146. func (pow *ProofOfWork) Run() (int, []byte) {
  147. var hashInt big.Int
  148. var hash [32]byte
  149. nonce := 0
  150. fmt.Printf("Mining the block containing \"%s\"\n", pow.block.Data)
  151. // 不断计算hash值,直到找到满足条件的hash值
  152. for nonce < maxNonce {
  153. data := pow.prepareData(nonce)
  154. hash = sha256.Sum256(data)
  155. fmt.Printf("\r%x", hash)
  156. hashInt.SetBytes(hash[:])
  157. // 检查hashInt是否小于pow.target
  158. // 如果是,则工作完成;否则,nonce加1,继续尝试
  159. if hashInt.Cmp(pow.target) == -1 {
  160. break
  161. } else {
  162. nonce++
  163. }
  164. }
  165. fmt.Print("\n\n")
  166. return nonce, hash[:]
  167. }
  168. // prepareData方法将block各个字段和nonce(可以加上TXs等字段)作为输入,计算得到hash值作为输出:
  169. func (pow *ProofOfWork) prepareData(nonce int) []byte {
  170. data := bytes.Join(
  171. [][]byte{
  172. pow.block.PrevBlockHash,
  173. pow.block.Data,
  174. IntToHex(pow.block.Timestamp),
  175. IntToHex(int64(targetBits)),
  176. IntToHex(int64(nonce)),
  177. },
  178. []byte{},
  179. )
  180. return data
  181. }
  182. // Validate 用于验证工作量证明
  183. func (pow *ProofOfWork) Validate() bool {
  184. var hashInt big.Int
  185. // 计算hash值
  186. data := pow.prepareData(pow.block.Nonce)
  187. hash := sha256.Sum256(data)
  188. // 将hash值转换为一个大整数
  189. hashInt.SetBytes(hash[:])
  190. // 检查hashInt是否小于pow.target
  191. isValid := hashInt.Cmp(pow.target) == -1
  192. return isValid
  193. }
  194. // IntToHex 将一个int64类型的数据转换为一个字节数组
  195. func IntToHex(num int64) []byte {
  196. buff := new(bytes.Buffer)
  197. err := binary.Write(buff, binary.BigEndian, num)
  198. if err != nil {
  199. panic(err)
  200. }
  201. return buff.Bytes()
  202. }
  203. // NewProofOfWork 用于创建一个新的工作量证明
  204. func NewProofOfWork(b *Block) *ProofOfWork {
  205. // 创建一个初始值为1的target
  206. target := big.NewInt(1)
  207. // 将target左移256-targetBits位
  208. target.Lsh(target, uint(256-targetBits))
  209. // 创建一个工作量证明
  210. pow := &ProofOfWork{b, target}
  211. return pow
  212. }
  213. func (bc *Blockchain) SaveBlockToLocal() {
  214. // 打开文件,没有则创建
  215. file, err := os.OpenFile("Block.txt", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0766)
  216. if err != nil {
  217. fmt.Println("文件打开失败", err)
  218. return
  219. }
  220. defer file.Close()
  221. // 写入文件
  222. for _, block := range bc.blocks {
  223. // 写入前先序列化区块数据
  224. blockData, err := json.Marshal(block)
  225. if err != nil {
  226. fmt.Println("序列化区块失败", err)
  227. continue
  228. }
  229. file.Write(blockData)
  230. file.WriteString("\n") // 在每个区块后添加换行符以分隔区块
  231. }
  232. }
  233. func (bc *Blockchain) ReadBlockFromLocal() {
  234. // 打开文件
  235. file, err := os.OpenFile("Block.txt", os.O_RDONLY, 0766)
  236. info, err := file.Stat()
  237. if err != nil {
  238. fmt.Println("获取文件信息失败", err)
  239. return
  240. }
  241. if info.Size() == 0 {
  242. bc.blocks = NewBlockchain().blocks
  243. fmt.Println("区块链为空,创建一个新的区块链")
  244. return
  245. }
  246. // 使用 bufio.Scanner 逐行读取
  247. scanner := bufio.NewScanner(file)
  248. for scanner.Scan() {
  249. var block Block
  250. // 解析区块
  251. err := json.Unmarshal([]byte(scanner.Text()), &block)
  252. if err != nil {
  253. fmt.Println("解析区块失败", err)
  254. continue
  255. }
  256. bc.blocks = append(bc.blocks, &block)
  257. }
  258. if err := scanner.Err(); err != nil {
  259. fmt.Println("读取文件时发生错误:", err)
  260. }
  261. }

结语

通过引入工作量证明机制,我们的区块链模拟系统更加完善和接近现实中的区块链网络。这不仅增强了系统的安全性,也为后续的扩展和应用打下了坚实基础。在实际应用中,工作量证明可能会被更高效的共识机制取代,但其核心理念——通过消耗资源来保证网络安全性,仍然值得理解和学习。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/931556
推荐阅读
相关标签
  

闽ICP备14008679号