如何用 Go 实现一个简单的区块链示例
互联网出现以来,区块链经常被看作一项影响深远的底层技术。虽然很多人一提到区块链就想到炒币,但其实它的应用范围远不止加密货币。币圈只是区块链生态的一部分,不少实际落地的项目,背后也是由企业或组织在推动。

随着区块链技术不断向前发展,搞懂它的基础原理和机制,对技术从业者来说越来越重要。从入门实现的角度看,搭一个最简单的区块链模型,其实没有想象中那么复杂。
这篇文章会用 2026 年比较热门的编程语言之一 Go(也叫 Golang),来演示怎么手写一个基础版区块链,顺便帮你理解区块、哈希和链式连接这些核心概念。
先搞懂区块链的基本结构
从名字就能看出来,区块链的本质就是“区块”按顺序连成“链”。每个区块里都存着数据,而区块之间通过哈希值串联起来。哈希可以简单理解成:把任意长度的输入数据,通过算法算出一个固定长度的加密输出。因为每个区块都存了前一个区块的哈希,所以一旦某个区块的内容被篡改,后面所有区块的关联关系就会断裂,整条链也就没法信任了。
在区块链里,每个区块都可以存放数据。最简单的做法就是把交易记录直接放进去。比特币就是这么干的,所以你可以在链上一直往前追溯每一笔交易的历史。
假如我们只用三个区块来搭一条最简单的链,那第一个区块就是“创世块”。因为它前面没有其他区块,所以“前一个哈希”字段是空的。不过没关系,我们仍然可以把时间戳和初始交易数据作为输入,通过哈希算法算出一个创世块自己的哈希值。
接下来,在第二个区块里,我们把创世块的哈希值填进“前一个哈希”字段,这一步就完成了区块之间的连接。然后,再把时间戳、交易列表和前一个哈希值一起扔进哈希算法,得到第二个区块的哈希值。
后面的每个区块都重复这个过程。理论上,这条链可以一直延伸下去。现实中,限制链条长度的通常是区块的有效性验证机制和存储能力——比如比特币的区块链体积已经非常大了。
用 Go 写一个简单的区块链
实现区块链的第一步,就是先定义“区块”这个数据结构。Go 语言支持自定义类型,所以我们用结构体(struct)来描述一个区块。这个结构体里包含四个字段,正好对应上面说的核心元素。
type Block struct {
timestamp time.Time
transactions []string
prevHash []byte
Hash []byte
}定义完区块之后,下一步是写一个创建新区块的函数。这个函数接收两个参数:一个字符串数组,用来放交易记录;一个字节数组,表示前一个区块的哈希值。函数内部还会调用后面要实现的 NewHash() 方法,生成当前区块的哈希。
func NewBlock(transactions []string, prevHash []byte) *Block {
currentTime := time.Now()
return &Block {
timestamp: currentTime,
transactions: transactions,
prevHash: prevHash,
Hash: NewHash(currentTime, transactions, prevHash),
}
}NewHash() 函数的作用,就是把时间、交易列表和前一个哈希作为输入,最终算出一个当前区块对应的哈希值。具体实现时,先把这些输入内容拼接成一个统一的字节数组 input。
这里通过 append() 方法,把 prevHash 和时间字符串依次写进 input。然后遍历 transactions,把每条交易也追加到 input 里。代码里的 string(rune(transaction))... 这种写法,其实就是 Go 里对数据进行转换并追加的一种习惯用法。
func NewHash(time time.Time, transactions []string, prevHash []byte) []byte {
input := append(prevHash, time.String()...)
for transaction := range transactions {
input = append(input, string(rune(transaction))...)
}
hash := sha256.Sum256(input)
return hash[:]
}最后,通过 crypto 包里的 sha256.Sum256() 对 input 进行哈希运算,得到新的哈希值。返回结果时,用 hash[:] 这种切片写法,以保证返回的是字节数组格式。
到这里,构建一个能连起来的最小化区块链模型,所需的核心逻辑就已经有了。为了让结果更直观,我们还可以加几个辅助函数,在程序运行时打印区块信息。
func printBlockInformation(block *Block) {
fmt.Printf("ttime: %s
", block.timestamp.String())
fmt.Printf("tprevHash: %x
", block.prevHash)
fmt.Printf("tHash: %x
", block.Hash)
printTransactions(block)
}
func printTransactions(block *Block) {
fmt.Println("tTransactions:")
for i, transaction := range block.transactions {
fmt.Printf(" %v: %q
", i, transaction)
}
}接下来,只需要创建交易数据、生成区块并计算哈希就行。这部分工作可以放在主函数里。需要注意,生成第一个区块(创世块)时,传入的是一个空字节数组,因为它没有前置区块。
func main() {
genesisTransactions := []string{"小明给小红转了50个比特币", "小红给小刚转了30个比特币"}
genesisBlock := NewBlock(genesisTransactions, []byte{})
fmt.Println("--- 第一个区块 ---")
printBlockInformation(genesisBlock)
block2Transactions := []string{"小李给小明转了30个比特币"}
block2 := NewBlock(block2Transactions, genesisBlock.Hash)
fmt.Println("--- 第二个区块 ---")
printBlockInformation(block2)
block3Transactions := []string{"小刚给小明转了45个比特币", "小明给小红转了10个比特币"}
block3 := NewBlock(block3Transactions, block2.Hash)
fmt.Println("--- 第三个区块 ---")
printBlockInformation(block3)
}每次创建新区块时,只要把上一个区块的哈希值和当前对应的交易记录一起传给 NewBlock(),新的链式关系就被建立起来了。
如果把这部分代码汇总到一起,完整的程序如下:
package main
import (
"crypto/sha256"
"fmt"
"time"
)
type Block struct {
timestamp time.Time
transactions []string
prevHash []byte
Hash []byte
}
func main() {
genesisTransactions := []string{"小明给小红转了50个比特币", "小红给小刚转了30个比特币"}
genesisBlock := NewBlock(genesisTransactions, []byte{})
fmt.Println("--- 第一个区块 ---")
printBlockInformation(genesisBlock)
block2Transactions := []string{"小李给小明转了30个比特币"}
block2 := NewBlock(block2Transactions, genesisBlock.Hash)
fmt.Println("--- 第二个区块 ---")
printBlockInformation(block2)
block3Transactions := []string{"小刚给小明转了45个比特币", "小明给小红转了10个比特币"}
block3 := NewBlock(block3Transactions, block2.Hash)
fmt.Println("--- 第三个区块 ---")
printBlockInformation(block3)
}
func NewBlock(transactions []string, prevHash []byte) *Block {
currentTime := time.Now()
return &Block {
timestamp: currentTime,
transactions: transactions,
prevHash: prevHash,
Hash: NewHash(currentTime, transactions, prevHash),
}
}
func NewHash(time time.Time, transactions []string, prevHash []byte) []byte {
input := append(prevHash, time.String()...)
for transaction := range transactions {
input = append(input, string(rune(transaction))...)
}
hash := sha256.Sum256(input)
return hash[:]
}
func printBlockInformation(block *Block) {
fmt.Printf("ttime: %s
", block.timestamp.String())
fmt.Printf("tprevHash: %x
", block.prevHash)
fmt.Printf("tHash: %x
", block.Hash)
printTransactions(block)
}
func printTransactions(block *Block) {
fmt.Println("tTransactions:")
for i, transaction := range block.transactions {
fmt.Printf(" %v: %q
", i, transaction)
}
}如果运行这段程序,你会看到类似下面的输出:
$ go run example.go --- 第一个区块 --- time: 2026-04-05 15:12:18.813294 -0600 MDT m=+0.000074939 prevHash: Hash: 43ec51c50d2b9565f221155a29d8b72307247b08eaf6731cca Transactions: 0: "小明给小红转了50个比特币" 1: "小红给小刚转了30个比特币" --- 第二个区块 --- time: 2026-04-05 15:12:18.813477 -0600 MDT m=+0.000257244 prevHash: 43ec51c50d2b9565f221155a29d8b72307247b08eaf6731cca Hash: fcce5323a35cb67b45fe75866582db00fd32baeb92aac448c7 Transactions: 0: "小李给小明转了30个比特币" --- 第三个区块 --- time: 2026-04-05 15:12:18.813488 -0600 MDT m=+0.000269168 prevHash: fcce5323a35cb67b45fe75866582db00fd32baeb92aac448c7 Hash: fc1d3eee286970d85812b47c3a5bf016ae8c1de4f86b8ace972ffa Transactions: 0: "小刚给小明转了45个比特币" 1: "小明给小红转了10个比特币"
整体来看,这个例子仍然比较基础,更像一个演示性质的小项目,但它已经清楚地展示了如何用 Go 搭建最简单的区块链结构。对于理解区块、哈希生成和链式引用这几个核心概念来说,这是一个直接又实用的入门示例。另外需要提醒一句:本文内容仅供学习参考,不构成任何投资建议、交易建议或收益承诺。加密货币和迷因币价格波动极大,实际市场数据请以官方公告、交易所页面及实时行情平台为准,投资者应独立判断、谨慎决策。