用Go语言搭一条迷你区块链:区块、哈希和链式连接
本文用Go语言代码一步步教你搭建一条简易区块链,解释区块结构、SHA256哈希和链式连接原理,适合想理解区块链底层逻辑的开发者或Web3新手。
说到区块链,很多人第一反应就是比特币这类加密货币,价格涨跌让人心跳加速。但其实,区块链本身是一种底层技术,它的应用场景远不止炒币。从供应链溯源到数字身份,很多现实项目都在用它。今天我们就从最基础的结构开始,一步步拆解区块链到底长什么样。
最安全的虚拟币交易平台推荐:
- OKX(欧易交易所)>>>进入官网<<< >>>官方下载<<<
- Binance(币安交易所)>>>进入官网<<< >>>官方下载<<<
Web3这个词越来越火,但背后的区块链技术到底怎么运作?其实自己动手搭一条最简单的区块链,比想象中要简单。只需要搞清楚三个核心概念:区块、哈希和链条。
这篇文章就用Go语言,带你把这条链亲手写出来。代码不复杂,重点是理解每个部分是怎么连起来的。
区块链到底长什么样?先看结构
名字已经告诉你了——区块链,就是由一个个“区块”连成的“链”。每个区块装着一批数据,比如交易记录,然后用一个哈希值(类似身份证号)把前后区块串起来。如果谁想偷偷改掉前面某个区块的数据,它的哈希就会变,后面的链就接不上了,整个链条就断掉。这就是不可篡改的基本原理。
每个区块就像一个装满交易记录的盒子。比特币的整个交易历史,就是靠这些盒子一个接一个连起来,永久保存的。
我们先搭一条只有三个区块的迷你链。第一个区块叫做“创世块”,它前面没有别的区块,所以它的“前一个哈希”字段是空的。但我们仍然可以用时间戳和交易数据,算出一个属于它自己的哈希值。
创建第二个区块时,就把第一个区块的哈希值填进它的“前一个哈希”字段里。这样,两个区块就通过哈希值连起来了。然后,把时间戳、交易列表和这个前一个哈希一起输入哈希算法,算出第二个区块自己的哈希。
后面的区块都按这个逻辑来。链条可以一直往下接,不过实际中会受到共识机制和存储空间的限制,比如比特币的区块链现在已经非常大了。
动手写代码:用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 和时间字符串加进去,然后遍历交易列表,把每一条交易也追加进去。代码里的 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标准库里的 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"总的来说,这是一个非常基础的演示,但它帮你理清了区块链最核心的区块、哈希和链式连接是怎么工作的。如果你是开发者,想入门区块链底层逻辑,这个例子是个不错的起点。需要提醒的是,本文仅用于技术学习,不构成任何投资建议。加密货币市场波动大,价格预测和市场数据请以官方公告和实时行情平台为准,投资需独立判断,谨慎决策。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
- 热门数据榜
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
相关攻略
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程


