当前位置:   article > 正文

Go语言实现简单区块链_使用 go 语言构造区块链

使用 go 语言构造区块链

        在本文中,我们将使用Go语言来实现一个简单的区块链系统,包括区块生成、交易处理和区块打印、保存区块链等功能。

先决条件

在开始之前,先确保计算机上安装了以下内容:

  1. Go编程语言:您可以从Go官方网站下载并安装Go。
  2. Go开发环境:设置Go开发环境,包括文本编辑器或集成开发环境(IDE)。

区块链基础

        在深入了解代码实现之前,让我们快速了解一些区块链的基本概念。

区块

        区块链由一系列区块组成,每个区块包含一组交易和其他元数据。每个区块都有一个唯一的哈希值,它表示区块的内容。区块还包含前一个区块的哈希值,从而形成一个链条,每个区块都依赖于前一个区块,确保数据的完整性和安全性。

交易

        交易是区块链中的基本操作。交易可以代表各种类型的操作,例如资金转移、资产交换或智能合约执行。交易包含发送者、接收者和交易金额等信息。本文采取简易的交易,不包含任何信息

哈希函数

        哈希函数用于将任意长度的数据转换为固定长度的哈希值。在区块链中,我们使用哈希函数来计算每个区块的哈希值,确保数据未被篡改。常见的哈希函数包括 SHA-256、RIPEMD-160 等。

共识机制

        共识机制确保区块链中的所有节点对交易和区块达成一致。常见的共识机制包括工作量证明(Proof-of-Work)和权益证明(Proof-of-Stake)。在本教程中,我们控制区块生成时间来生成新区快,不涉及共识机制,读者可根据实际进行功能拓展。

代码实现

        让我们开始实现我们的区块链系统。我们将使用 Go 语言来编写代码。

区块结构

        首先,我们定义一个 Block 结构来表示区块。每个区块包含索引、时间戳、交易列表、前一个区块的哈希值和当前区块的哈希值。

  1. // Block 表示区块结构
  2. type Block struct {
  3. Index int // 区块索引
  4. Timestamp string // 时间戳
  5. Txs []string // 交易
  6. PrevHash string // 前一个区块的哈希值
  7. Hash string // 当前区块的哈希值
  8. }

定义全局变量 

  1. // 先把新区块放入一个map中,最后一起存入redis
  2. var blockMap = make(map[string]string)
  3. var blockchain []Block

         blockMap 是一个用于临时存储新区块的 map,稍后我们将把这些新区块保存到本地文件中。

初始化区块链

        我们需要一个函数来初始化区块链。如果这是第一次运行程序,我们将创建一个创世区块(genesis block)。如果之前已经运行过,我们将从文件中加载现有区块链。

  1. func initBlock() {
  2. fmt.Println("初始化")
  3. file, err := os.OpenFile("Block.txt", os.O_RDWR|os.O_CREATE, 0766)
  4. if err != nil {
  5. fmt.Println("文件打开失败")
  6. return
  7. }
  8. defer file.Close()
  9. scanner := bufio.NewScanner(file)
  10. for scanner.Scan() {
  11. line := scanner.Text()
  12. parts := strings.Split(line, ":")
  13. if len(parts) != 2 {
  14. continue
  15. }
  16. value := parts[1]
  17. oldBlock := Block{}
  18. err := json.Unmarshal([]byte(value), &oldBlock)
  19. if err != nil {
  20. continue
  21. }
  22. blockchain = append(blockchain, oldBlock)
  23. }
  24. if len(blockchain) == 0 {
  25. genesisBlock := CreateGenesisBlock()
  26. blockchain = append(blockchain, genesisBlock)
  27. }
  28. }

创建创世区块

        创世区块是区块链中的第一个区块。它没有前一个区块的哈希值,因此我们将其设置为空字符串。

  1. // 创建创世区块
  2. func CreateGenesisBlock() Block {
  3. // 创建创世区块
  4. genesisBlock := Block{0, time.Now().String(), []string{}, "", ""}
  5. genesisBlock.Hash = calculateHash(genesisBlock)
  6. // 将新区块保存到临时的map中
  7. blockJSON, _ := json.Marshal(genesisBlock)
  8. blockMap[genesisBlock.Hash] = string(blockJSON)
  9. return genesisBlock
  10. }

        在上面的代码中,我们首先初始化创世区块的属性,包括索引(0)、当前时间戳、空的交易列表、空的前一个区块哈希值和空的当前区块哈希值。

        接下来,我们调用 calculateHash 函数来计算创世区块的哈希值。这个函数将使用 SHA-256 哈希函数对区块的内容进行哈希计算。我们稍后将详细讨论这个函数。

        计算出哈希值后,我们将创世区块转换为 JSON 格式并保存到 blockMap 中。blockMap 是一个用于临时存储新区块的 map,稍后我们将把这些新区块保存到文件中。

        最后,我们返回创世区块。

生成区块

        现在我们已经有了创世区块,让我们来看看如何生成新的区块。我们将创建一个名为 generateBlock 的函数,它接受前一个区块和交易列表作为参数。

  1. // 生成新的区块
  2. func generateBlock(oldBlock Block, txs []string) Block {
  3. var newBlock Block
  4. newBlock.Index = oldBlock.Index + 1
  5. newBlock.Timestamp = time.Now().String()
  6. newBlock.Txs = txs
  7. newBlock.PrevHash = oldBlock.Hash
  8. newBlock.Hash = calculateHash(newBlock)
  9. return newBlock
  10. }

        在这个函数中,我们首先初始化新区块的属性。索引设置为前一个区块的索引加 1,时间戳设置为当前时间,交易列表设置为传入的交易列表,前一个区块的哈希值设置为传入的前一个区块的哈希值。然后,我们再次调用 calculateHash 函数来计算新区块的哈希值。

计算哈希值

        现在让我们来看看 calculateHash 函数是如何计算区块的哈希值的。

  1. // 计算区块的哈希值
  2. func calculateHash(block Block) string {
  3. // 计算哈希值
  4. record := strconv.Itoa(block.Index) + block.Timestamp + fmt.Sprint(block.Txs) + block.PrevHash
  5. h := sha256.New()
  6. h.Write([]byte(record))
  7. hashed := h.Sum(nil)
  8. // 将[]byte转换成16进制字符串
  9. return hex.EncodeToString(hashed)
  10. }

         在这个函数中,我们首先将区块的索引、时间戳、交易列表和前一个区块的哈希值连接起来,形成一个字符串。然后,我们使用 SHA-256 哈希函数来计算这个字符串的哈希值。

   sha256.New() 函数创建一个新的 SHA-256 哈希对象。h.Write([]byte(record)) 将输入字符串转换为字节数组并写入哈希对象。h.Sum(nil) 计算最终的哈希值并返回字节数组。最后,我们使用 hex.EncodeToString(hashed) 将字节数组转换为 16 进制字符串,这就是区块的哈希值。

生成交易

        在区块链系统中,交易是基本的操作单元。让我们创建一个简单的函数来生成随机交易。

  1. // 随机生成交易
  2. func generateTx() string {
  3. return fmt.Sprintf("Tx%d", rand.Intn(1000))
  4. }

        在这个函数中,我们使用 rand.Intn(1000) 生成一个随机数,并将其格式化为 "Tx" 开头的字符串。这个函数可以根据需要生成多个随机交易。

打包交易放入区块

        让我们继续讨论 productBlock 函数。这个函数负责打包交易生成区块并将其添加到区块链中。

  1. // 生成区块
  2. func productBlock(timeTX int, txPool []string, blockchain []Block) {
  3. for {
  4. // 生成随机交易并放入交易池
  5. for i := 0; i < 10; i++ {
  6. tx := generateTx()
  7. txPool = append(txPool, tx)
  8. }
  9. // 从交易池中取出交易打包到区块中
  10. var blockTxs []string
  11. if len(txPool) > 8 {
  12. blockTxs = txPool[:8]
  13. txPool = txPool[8:]
  14. } else {
  15. blockTxs = txPool
  16. txPool = []string{}
  17. }
  18. // 生成新区块并添加到区块链
  19. newBlock := generateBlock(blockchain[len(blockchain)-1], blockTxs)
  20. blockchain = append(blockchain, newBlock)
  21. // 将新区块保存到临时的map中
  22. blockJSON, _ := json.Marshal(newBlock)
  23. blockMap[newBlock.Hash] = string(blockJSON)
  24. // 等待指定的时间间隔
  25. time.Sleep(time.Duration(timeTX) * time.Second)
  26. }
  27. }

        在这个函数中,我们首先使用 generateTx 函数生成随机交易并将其放入交易池 txPool 中。我们生成 10 个随机交易,但您可以根据需要调整这个数字。

        然后,我们从交易池中取出交易打包到区块中。我们限制每个区块最多包含 8 个交易,因此如果交易池中有超过 8 个交易,我们只取前 8 个,并将剩余的交易留在交易池中供下一个区块使用。

        接下来,我们调用 generateBlock 函数来生成新区块。这个函数接受前一个区块和交易列表作为参数,并返回一个新的区块。我们将新区块添加到区块链 blockchain 中。

然后,我们将新区块转换为 JSON 格式并保存到 blockMap 中。blockMap 是一个用于临时存储新区块的 map,稍后我们将把这些新区块保存到文件中。我们使用 time.Sleep 函数等待指定的时间间隔,以模拟区块的生成间隔。

        这个函数将一直运行,不断生成新的区块并将其添加到区块链中,可以根据需要调整区块生成的 时间间隔。

打印区块链

        让我们继续讨论 printBlock 和 printBlockchain 函数。这些函数负责打印单个区块和整个区块链的信息。

  1. // 打印区块
  2. func printBlock(block Block) {
  3. fmt.Printf("Index:%d\n", block.Index)
  4. fmt.Printf("Timestamp:%s\n", block.Timestamp)
  5. fmt.Printf("Txs:%v\n", block.Txs)
  6. fmt.Printf("PrevHash:%s\n", block.PrevHash)
  7. fmt.Printf("Hash:%s\n", block.Hash)
  8. fmt.Println()
  9. }

    printBlock 函数接受一个 Block 类型的参数,并使用 fmt.Printf 函数打印区块的各个属性,包括索引、时间戳、交易列表、前一个区块的哈希值和当前区块的哈希值。每个属性打印在一行,并以换行符结束。

  1. // 打印区块链
  2. func printBlockchain(blockMap map[string]string) {
  3. var blocks []Block
  4. for _, value := range blockMap {
  5. block := Block{}
  6. err := json.Unmarshal([]byte(value), &block)
  7. if err != nil {
  8. continue
  9. }
  10. blocks = append(blocks, block)
  11. }
  12. // 对区块按照索引排序
  13. sort.Slice(blocks, func(i, j int) bool {
  14. return blocks[i].Index < blocks[j].Index
  15. })
  16. for _, block := range blocks {
  17. printBlock(block)
  18. }
  19. }

   printBlockchain 函数负责打印整个区块链的信息。它首先创建一个空的 Block 切片 blocks 来存储从 blockMap 中解码的区块。blockMap 是一个用于临时存储新区块的 map,每个区块的哈希值作为键,对应的 JSON 格式的区块作为值。

        我们使用 json.Unmarshal 函数将 JSON 格式的区块解码为 Block 类型。如果解码失败,我们将跳过该区块并继续处理下一个。

        然后,我们使用 sort.Slice 对区块按照索引排序。sort.Slice 函数接受一个排序函数,在这个函数中,我们比较两个区块的索引,如果第一个区块的索引小于第二个区块,则返回 true,表示第一个区块应该排在前面。

        最后,我们遍历排序后的区块切片,并调用 printBlock 函数打印每个区块的信息。

保存区块链

        让我们继续讨论 saveBlock 函数。这个函数负责将区块保存到本地文件中。

  1. // 保存区块到本地Block.txt文件下
  2. func saveBlock(blockmap map[string]string) {
  3. //打开文件,没有则创建
  4. file, err := os.OpenFile("Block.txt", os.O_RDWR|os.O_CREATE, 0766)
  5. if err != nil {
  6. fmt.Println("文件打开失败")
  7. return
  8. }
  9. defer file.Close()
  10. for key, value := range blockmap {
  11. fmt.Println("key:", key, "value:", value)
  12. //写入文件
  13. _, err := file.WriteString(key + ":" + value + "\n")
  14. if err != nil {
  15. return
  16. }
  17. }
  18. }

        在这个函数中,我们首先使用 os.OpenFile 函数打开或创建名为 "Block.txt" 的文件。我们使用 os.O_RDWR|os.O_CREATE 标志来指定文件可以读写,如果文件不存在则创建。

        然后,我们使用 defer file.Close() 确保文件在函数结束时关闭。

        接下来,我们遍历 blockMap 中的所有键值对。blockMap 是一个用于临时存储新区块的 map,每个区块的哈希值作为键,对应的 JSON 格式的区块作为值。

        对于每个键值对,我们打印键和值,并使用 file.WriteString 将它们写入文件。我们使用 key + ":" + value + "\n" 来确保每个区块以 "键:值" 的格式写入新的一行。

如果写入文件时发生错误,我们将返回并终止函数。

        这个函数确保新区块被保存到本地文件中,以便下次程序运行时可以从文件中加载现有区块链。

完整代码

        以下是本教程中讨论的所有代码的完整版本:

  1. // Package Block
  2. // -*- coding: utf-8 -*-
  3. // Time : 2024/4/12 20:13
  4. // Author : blue
  5. // File : main.go
  6. // Software: Goland
  7. package main
  8. import (
  9. "bufio"
  10. "crypto/sha256"
  11. "encoding/hex"
  12. "encoding/json"
  13. "fmt"
  14. "math/rand"
  15. "os"
  16. "sort"
  17. "strconv"
  18. "strings"
  19. "time"
  20. )
  21. // Block 表示区块结构
  22. type Block struct {
  23. Index int // 区块索引
  24. Timestamp string // 时间戳
  25. Txs []string // 交易
  26. PrevHash string // 前一个区块的哈希值
  27. Hash string // 当前区块的哈希值
  28. }
  29. // 先把新区块放入一个map中,最后一起存入redis
  30. var blockMap = make(map[string]string)
  31. var blockchain []Block
  32. func main() {
  33. initBlock()
  34. // 交易池
  35. txPool := []string{}
  36. loop := true
  37. for {
  38. fmt.Println("------区块链系统demo------")
  39. fmt.Println("-----1. 添加区块-----")
  40. fmt.Println("-----2. 打印区块-----")
  41. fmt.Println("-----3. 保存区块-----")
  42. fmt.Println("-----4. 退出-----")
  43. fmt.Println("请输入您的选择:")
  44. var choice int
  45. fmt.Scanln(&choice)
  46. switch choice {
  47. case 1:
  48. fmt.Println("请输入生成区块的间隔:")
  49. timeTX := 0
  50. fmt.Scanln(&timeTX)
  51. go productBlock(timeTX, txPool, blockchain)
  52. case 2:
  53. fmt.Println("打印区块")
  54. printBlockchain(blockMap)
  55. case 3:
  56. fmt.Println("保存区块")
  57. saveBlock(blockMap)
  58. case 4:
  59. fmt.Println("退出")
  60. loop = false
  61. }
  62. if loop == false {
  63. break
  64. }
  65. }
  66. }
  67. // 初始化区块
  68. func initBlock() {
  69. fmt.Println("初始化")
  70. file, err := os.OpenFile("Block.txt", os.O_RDWR|os.O_CREATE, 0766)
  71. if err != nil {
  72. fmt.Println("文件打开失败")
  73. return
  74. }
  75. defer file.Close()
  76. scanner := bufio.NewScanner(file)
  77. for scanner.Scan() {
  78. line := scanner.Text()
  79. parts := strings.Split(line, ":")
  80. if len(parts) != 2 {
  81. continue
  82. }
  83. value := parts[1]
  84. oldBlock := Block{}
  85. err := json.Unmarshal([]byte(value), &oldBlock)
  86. if err != nil {
  87. continue
  88. }
  89. blockchain = append(blockchain, oldBlock)
  90. }
  91. if len(blockchain) == 0 {
  92. genesisBlock := CreateGenesisBlock()
  93. blockchain = append(blockchain, genesisBlock)
  94. }
  95. }
  96. // 打印区块
  97. func printBlock(block Block) {
  98. fmt.Printf("Index:%d\n", block.Index)
  99. fmt.Printf("Timestamp:%s\n", block.Timestamp)
  100. fmt.Printf("Txs:%v\n", block.Txs)
  101. fmt.Printf("PrevHash:%s\n", block.PrevHash)
  102. fmt.Printf("Hash:%s\n", block.Hash)
  103. fmt.Println()
  104. }
  105. // 打印区块链
  106. func printBlockchain(blockMap map[string]string) {
  107. var blocks []Block
  108. for _, value := range blockMap {
  109. block := Block{}
  110. err := json.Unmarshal([]byte(value), &block)
  111. if err != nil {
  112. continue
  113. }
  114. blocks = append(blocks, block)
  115. }
  116. // 对区块按照索引排序
  117. sort.Slice(blocks, func(i, j int) bool {
  118. return blocks[i].Index < blocks[j].Index
  119. })
  120. for _, block := range blocks {
  121. printBlock(block)
  122. }
  123. }
  124. // 保存区块到本地Block.txt文件下
  125. func saveBlock(blockmap map[string]string) {
  126. //打开文件,没有则创建
  127. file, err := os.OpenFile("Block.txt", os.O_RDWR|os.O_CREATE, 0766)
  128. if err != nil {
  129. fmt.Println("文件打开失败")
  130. return
  131. }
  132. defer file.Close()
  133. for key, value := range blockmap {
  134. fmt.Println("key:", key, "value:", value)
  135. //写入文件
  136. _, err := file.WriteString(key + ":" + value + "\n")
  137. if err != nil {
  138. return
  139. }
  140. }
  141. }
  142. // 创建创世区块
  143. func CreateGenesisBlock() Block {
  144. // 创建创世区块
  145. genesisBlock := Block{0, time.Now().String(), []string{}, "", ""}
  146. genesisBlock.Hash = calculateHash(genesisBlock)
  147. // 将新区块保存到临时的map中
  148. blockJSON, _ := json.Marshal(genesisBlock)
  149. blockMap[genesisBlock.Hash] = string(blockJSON)
  150. return genesisBlock
  151. }
  152. // 生成区块
  153. func productBlock(timeTX int, txPool []string, blockchain []Block) {
  154. for {
  155. // 生成随机交易并放入交易池
  156. for i := 0; i < 10; i++ {
  157. tx := generateTx()
  158. txPool = append(txPool, tx)
  159. }
  160. // 从交易池中取出交易打包到区块中
  161. var blockTxs []string
  162. if len(txPool) > 8 {
  163. blockTxs = txPool[:8]
  164. txPool = txPool[8:]
  165. } else {
  166. blockTxs = txPool
  167. txPool = []string{}
  168. }
  169. // 生成新区块并添加到区块链
  170. newBlock := generateBlock(blockchain[len(blockchain)-1], blockTxs)
  171. blockchain = append(blockchain, newBlock)
  172. // 将新区块保存到临时的map中
  173. blockJSON, _ := json.Marshal(newBlock)
  174. blockMap[newBlock.Hash] = string(blockJSON)
  175. // 等待指定的时间间隔
  176. time.Sleep(time.Duration(timeTX) * time.Second)
  177. }
  178. }
  179. // 计算区块的哈希值
  180. func calculateHash(block Block) string {
  181. // 计算哈希值
  182. record := strconv.Itoa(block.Index) + block.Timestamp + fmt.Sprint(block.Txs) + block.PrevHash
  183. h := sha256.New()
  184. h.Write([]byte(record))
  185. hashed := h.Sum(nil)
  186. //hex.EncodeToString(hashed)将[]byte转换成16进制字符串
  187. return hex.EncodeToString(hashed)
  188. }
  189. // 生成新的区块
  190. func generateBlock(oldBlock Block, txs []string) Block {
  191. var newBlock Block
  192. newBlock.Index = oldBlock.Index + 1
  193. newBlock.Timestamp = time.Now().String()
  194. newBlock.Txs = txs
  195. newBlock.PrevHash = oldBlock.Hash
  196. newBlock.Hash = calculateHash(newBlock)
  197. return newBlock
  198. }
  199. // 随机生成交易
  200. func generateTx() string {
  201. return fmt.Sprintf("Tx%d", rand.Intn(1000))
  202. }

总结

        在本教程中,我们使用 Go 语言实现了一个简单的区块链系统。我们讨论了区块链的基本概念,包括区块、交易和哈希函数。我们还实现了生成区块、交易和计算哈希值所需的函数。最后,我们创建了一个主函数来组合所有内容并运行区块链系统。

        虽然这个实现是基本的,但它为理解区块链技术提供了很好的基础。您可以在此基础上继续构建,添加更多的功能,例如共识机制、智能合约和去中心化网络。

        感谢阅读,希望这篇教程对您有所帮助!

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号