区块链入门:用Go语言理解区块、哈希与链式结构

作者:技术拾光

时间:2026-07-16

浏览:183

本文通过Go语言代码实战,一步步拆解区块数据结构、SHA256哈希生成、创世块创建及链式连接机制,帮助新手直观理解区块链底层逻辑,掌握数据不可篡改的原理。

文章目录

自互联网诞生以来,区块链就被视为一项具有颠覆性潜力的核心技术。虽然很多人提到区块链就会联想到价格波动剧烈的加密货币,但从技术架构和实际应用来看,它的边界远不止于此。加密货币只是区块链生态中的一环,许多由私营机构推动的真实解决方案同样依赖这个底层技术。

区块链入门:用Go语言理解区块、哈希与链式结构

随着Web3技术的发展,越来越多的人想搞清楚区块链到底是怎么工作的。其实,自己动手搭建一个最简单的区块链模型并不难,关键在于理解它的几个核心部件。

这篇文章会借助Go语言(GoLang),带你一步步实现一个基础版区块链。通过代码实操,你就能直观掌握区块(Block)、哈希(Hash)以及链式连接这些核心机制的运行方式。

先搞清楚区块链的基本结构

“区块链”这个名字本身就说明了它的核心特点:数据被打包成一个个“区块”,然后通过哈希值按照时间顺序串联成一条链条。哈希算法会把任意长度的输入数据转换成一个固定长度的加密字符串。因为每个区块都包含了前一个区块的哈希值,这种设计保证了数据一旦写入就很难被篡改——如果链中某个区块的内容被改了,它的哈希值就会变,导致后面所有区块的链接断开,整个链条就被破坏了。

在区块链体系里,区块是存储数据的基本单元。在最简单的模型中,区块通常用来记录交易信息。拿比特币来说,它的交易历史就是通过这种链式结构被永久保存下来的,任何人都可以追溯。

假如我们要搭一条只有三个区块的最简区块链,第一个区块就叫“创世块”(Genesis Block)。因为它是第一个,前面没有其他区块,所以它的“前一个哈希”字段是空的。不过我们仍然可以把时间戳和初始交易数据作为输入,用哈希算法算出一个创世块独有的哈希值。

接下来,创建第二个区块时,把创世块的哈希值写进这个区块的“前一个哈希”字段,这一步就实现了两个区块之间的逻辑连接。然后,把时间戳、交易列表和前一个哈希值一起输入哈希算法,就能算出第二个区块的新哈希。

后面的区块生成都遵循同样的逻辑。理论上,这种链式结构可以无限延伸。在实际应用中,限制主要来自共识机制对区块有效性的验证要求,以及存储容量的限制——比如比特币区块链的数据量现在已经非常大了。

用Go语言动手建一个简易区块链

搭建区块链的第一步是定义数据结构。Go语言支持自定义类型,我们可以用 Block 结构体来描述一个区块。这个结构体包含四个关键字段:时间戳、交易记录、前一个区块的哈希,以及当前区块的哈希。

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 切片,把每条交易数据转换成字符串并追加进去。代码里的 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[:]
}

最后,利用Go标准库 crypto/sha256 里的 Sum256() 函数,对拼接好的输入数据进行哈希运算。返回结果时,使用切片操作 hash[:] 让返回值符合函数要求的字节数组类型。

到这里,构建可链接区块的最小化区块链核心逻辑就完成了。为了方便调试和观察,我们再加几个辅助函数,在程序运行时打印出区块的详细信息。

func printBlockInformation(block *Block) {
    fmt.Printf("time: %s\n", block.timestamp.String())
    fmt.Printf("prevHash: %x\n", block.prevHash)
    fmt.Printf("Hash: %x\n", block.Hash)
    printTransactions(block)
}

func printTransactions(block *Block) {
    fmt.Println("Transactions:")
    for i, transaction := range block.transactions {
        fmt.Printf("%v: %q\n", i, transaction)
    }
}

接下来,在主函数中创建交易数据、生成区块并计算哈希。注意,创建第一个区块(创世块)时,因为没有前置区块,传入的 prevHash 应该是空字节数组。

func main() {
    genesisTransactions := []string{"Izzy sent Will 50 bitcoin", "Will sent Izzy 30 bitcoin"}
    genesisBlock := NewBlock(genesisTransactions, []byte{})
    fmt.Println("--- First Block ---")
    printBlockInformation(genesisBlock)

    block2Transactions := []string{"John sent Izzy 30 bitcoin"}
    block2 := NewBlock(block2Transactions, genesisBlock.Hash)
    fmt.Println("--- Second Block ---")
    printBlockInformation(block2)

    block3Transactions := []string{"Will sent Izzy 45 bitcoin", "Izzy sent Will 10 bitcoin"}
    block3 := NewBlock(block3Transactions, block2.Hash)
    fmt.Println("--- Third Block ---")
    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{"Izzy sent Will 50 bitcoin", "Will sent Izzy 30 bitcoin"}
    genesisBlock := NewBlock(genesisTransactions, []byte{})
    fmt.Println("--- First Block ---")
    printBlockInformation(genesisBlock)

    block2Transactions := []string{"John sent Izzy 30 bitcoin"}
    block2 := NewBlock(block2Transactions, genesisBlock.Hash)
    fmt.Println("--- Second Block ---")
    printBlockInformation(block2)

    block3Transactions := []string{"Will sent Izzy 45 bitcoin", "Izzy sent Will 10 bitcoin"}
    block3 := NewBlock(block3Transactions, block2.Hash)
    fmt.Println("--- Third Block ---")
    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("time: %s\n", block.timestamp.String())
    fmt.Printf("prevHash: %x\n", block.prevHash)
    fmt.Printf("Hash: %x\n", block.Hash)
    printTransactions(block)
}

func printTransactions(block *Block) {
    fmt.Println("Transactions:")
    for i, transaction := range block.transactions {
        fmt.Printf("%v: %q\n", i, transaction)
    }
}

运行这个程序后,你会看到类似下面的输出结果,清晰地展示了区块之间的哈希关联:

$ go run example.go
--- First Block ---
time: 2021-04-05 15:12:18.813294 -0600 MDT m=+0.000074939
prevHash: 
Hash: 43ec51c50d2b9565f221155a29d8b72307247b08eaf6731cca
Transactions:
0: "Izzy sent Will 50 bitcoin" 
1: "Will sent Izzy 30 bitcoin" 
--- Second Block ---
time: 2021-04-05 15:12:18.813477 -0600 MDT m=+0.000257244
prevHash: 43ec51c50d2b9565f221155a29d8b72307247b08eaf6731cca
Hash: fcce5323a35cb67b45fe75866582db00fd32baeb92aac448c7
Transactions:
0: "John sent Izzy 30 bitcoin" 
--- Third Block ---
time: 2021-04-05 15:12:18.813488 -0600 MDT m=+0.000269168
prevHash: fcce5323a35cb67b45fe75866582db00fd32baeb92aac448c7
Hash: fc1d3eee286970d85812b47c3a5bf016ae8c1de4f86b8ace972ffa
Transactions:
0: "Will sent Izzy 45 bitcoin" 
1: "Izzy sent Will 10 bitcoin"

总的来说,虽然这个实现偏向演示性质,但它清晰地展示了如何用Go语言构建最简单的区块链结构。对于想理解区块数据封装、哈希算法应用以及链式引用关系的任何人来说,这都是一个直观又实用的入门案例。最后需要提醒的是:本文内容仅供学习参考,不构成任何投资建议或交易建议。加密货币市场波动较大,价格预测及市场数据请以官方公告、交易所页面及实时行情平台为准,投资者需独立判断、谨慎决策。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›