一直想手写一次区块链来理解,终于补充了。
首先上代码:
// 区块链Demo - Go语言实现
// 本程序演示了区块链的核心概念,包括:
// 1. 区块结构和区块链
// 2. 工作量证明(PoW)算法
// 3. 交易系统
// 4. 默克尔树
// 5. 区块链验证
package main
import (
"bytes" // 用于字节操作
"crypto/sha256" // SHA256哈希算法
"encoding/gob" // Go二进制编码
"encoding/hex" // 十六进制编码
"fmt" // 格式化输出
"log" // 日志记录
"math" // 数学函数
"math/big" // 大整数运算
"strconv" // 字符串转换
"time" // 时间处理
)
// Transaction 表示一个交易
// 每个交易包含一个唯一的ID、输入列表和输出列表
type Transaction struct {
ID []byte // 交易的唯一标识符(哈希值)
Vin []TXInput // 交易输入列表
Vout []TXOutput // 交易输出列表
}
// TXInput 表示交易的输入
// 输入引用之前交易的输出,证明你有权花费这些资金
type TXInput struct {
Txid []byte // 引用的交易ID
Vout int // 引用的输出索引
ScriptSig string // 解锁脚本(这里简化为地址)
}
// TXOutput 表示交易的输出
// 输出指定了资金的接收者和金额
type TXOutput struct {
Value int // 输出的金额
ScriptPubKey string // 锁定脚本(这里简化为接收者地址)
}
// Block 表示区块链中的一个区块
// 每个区块包含多个交易,并通过哈希与前一个区块链接
type Block struct {
Timestamp int64 // 区块创建时间戳
Transactions []*Transaction // 区块中包含的交易列表
PrevBlockHash []byte // 前一个区块的哈希值
Hash []byte // 当前区块的哈希值
Nonce int // 工作量证明的随机数
}
// Blockchain 表示整个区块链
// 区块链是一个按时间顺序排列的区块列表
type Blockchain struct {
blocks []*Block // 区块列表
}
// NewBlock 创建一个新的区块
// 通过工作量证明算法计算区块哈希
func NewBlock(transactions []*Transaction, prevBlockHash []byte) *Block {
// 创建新区块,初始哈希为空
block := &Block{
Timestamp: time.Now().Unix(), // 当前时间戳
Transactions: transactions, // 交易列表
PrevBlockHash: prevBlockHash, // 前一个区块哈希
Hash: []byte{}, // 当前区块哈希(待计算)
Nonce: 0, // 随机数(待计算)
}
// 创建工作量证明对象并运行挖矿算法
pow := NewProofOfWork(block)
nonce, hash := pow.Run()
// 设置计算出的哈希和随机数
block.Hash = hash[:]
block.Nonce = nonce
return block
}
// NewGenesisBlock 创建创世区块
// 创世区块是区块链的第一个区块,没有前一个区块
func NewGenesisBlock(coinbase *Transaction) *Block {
return NewBlock([]*Transaction{coinbase}, []byte{})
}
// NewBlockchain 创建一个新的区块链
// 初始化时创建包含创世区块的区块链
func NewBlockchain() *Blockchain {
return &Blockchain{[]*Block{NewGenesisBlock(NewCoinbaseTX("genesis", ""))}}
}
// AddBlock 向区块链添加新区块
// 新区块的哈希基于前一个区块的哈希计算
func (bc *Blockchain) AddBlock(transactions []*Transaction) {
prevBlock := bc.blocks[len(bc.blocks)-1] // 获取最后一个区块
newBlock := NewBlock(transactions, prevBlock.Hash) // 创建新区块
bc.blocks = append(bc.blocks, newBlock) // 添加到区块链
}
// HashTransactions 计算区块中所有交易的哈希
// 使用默克尔树来高效地计算所有交易的根哈希
func (b *Block) HashTransactions() []byte {
var txHashes [][]byte
// 收集所有交易的ID
for _, tx := range b.Transactions {
txHashes = append(txHashes, tx.ID)
}
// 创建默克尔树并返回根节点哈希
tree := NewMerkleTree(txHashes)
return tree.RootNode.Data
}
// Serialize 序列化区块
// 将区块转换为字节数组,用于存储或传输
func (b *Block) Serialize() []byte {
var result bytes.Buffer
encoder := gob.NewEncoder(&result)
err := encoder.Encode(b)
if err != nil {
log.Panic(err)
}
return result.Bytes()
}
// DeserializeBlock 反序列化区块
// 从字节数组恢复区块对象
func DeserializeBlock(d []byte) *Block {
var block Block
decoder := gob.NewDecoder(bytes.NewReader(d))
err := decoder.Decode(&block)
if err != nil {
log.Panic(err)
}
return &block
}
// SetID 设置交易的ID
// 通过序列化交易内容并计算SHA256哈希来生成交易ID
func (tx *Transaction) SetID() {
var encoded bytes.Buffer
var hash [32]byte
// 序列化交易内容
enc := gob.NewEncoder(&encoded)
err := enc.Encode(tx)
if err != nil {
log.Panic(err)
}
// 计算SHA256哈希
hash = sha256.Sum256(encoded.Bytes())
tx.ID = hash[:]
}
// NewCoinbaseTX 创建coinbase交易
// Coinbase交易是矿工奖励交易,没有输入,只有输出
func NewCoinbaseTX(to, data string) *Transaction {
// 如果没有提供数据,使用默认格式
if data == "" {
data = fmt.Sprintf("Reward to '%s'", to)
}
// 创建coinbase交易的输入(Txid为空,Vout为-1表示coinbase)
txin := TXInput{[]byte{}, -1, data}
// 创建输出(奖励100个币给指定地址)
txout := TXOutput{100, to}
// 创建交易并设置ID
tx := Transaction{nil, []TXInput{txin}, []TXOutput{txout}}
tx.SetID()
return &tx
}
// NewTransaction 创建新交易
// 从指定地址向目标地址转账指定金额
func NewTransaction(from, to string, amount int, bc *Blockchain) *Transaction {
var inputs []TXInput
var outputs []TXOutput
// 查找发送者的可花费输出
acc, validOutputs := bc.FindSpendableOutputs(from, amount)
// 检查余额是否足够
if acc < amount {
log.Panic("ERROR: Not enough funds")
}
// 创建交易输入
for txid, outs := range validOutputs {
txID, err := hex.DecodeString(txid)
if err != nil {
log.Panic(err)
}
for _, out := range outs {
input := TXInput{txID, out, from}
inputs = append(inputs, input)
}
}
// 创建交易输出
outputs = append(outputs, TXOutput{amount, to})
// 如果有找零,创建找零输出
if acc > amount {
outputs = append(outputs, TXOutput{acc - amount, from})
}
// 创建交易并设置ID
tx := Transaction{nil, inputs, outputs}
tx.SetID()
return &tx
}
// FindSpendableOutputs 查找可花费的输出
// 返回指定地址的可用余额和对应的未花费输出
func (bc *Blockchain) FindSpendableOutputs(address string, amount int) (int, map[string][]int) {
unspentOutputs := make(map[string][]int)
unspentTXs := bc.FindUnspentTransactions(address)
accumulated := 0
// 遍历未花费交易,累积金额直到满足需求
Work:
for _, tx := range unspentTXs {
txID := hex.EncodeToString(tx.ID)
for outIdx, out := range tx.Vout {
if out.CanBeUnlockedWith(address) && accumulated < amount {
accumulated += out.Value
unspentOutputs[txID] = append(unspentOutputs[txID], outIdx)
// 如果累积金额足够,停止查找
if accumulated >= amount {
break Work
}
}
}
}
return accumulated, unspentOutputs
}
// FindUnspentTransactions 查找未花费的交易
// 从最新区块开始向前遍历,找出指定地址的所有未花费交易
func (bc *Blockchain) FindUnspentTransactions(address string) []Transaction {
var unspentTXs []Transaction
spentTXOs := make(map[string][]int) // 记录已花费的输出
bci := bc.Iterator()
// 从最新区块开始向前遍历
for {
block := bci.Next()
// 遍历区块中的每个交易
for _, tx := range block.Transactions {
txID := hex.EncodeToString(tx.ID)
Outputs:
// 检查交易输出
for outIdx, out := range tx.Vout {
// 跳过已花费的输出
if spentTXOs[txID] != nil {
for _, spentOutIdx := range spentTXOs[txID] {
if spentOutIdx == outIdx {
continue Outputs
}
}
}
// 如果输出可以被指定地址解锁,添加到未花费列表
if out.CanBeUnlockedWith(address) {
unspentTXs = append(unspentTXs, *tx)
}
}
// 如果不是coinbase交易,记录已花费的输出
if !tx.IsCoinbase() {
for _, in := range tx.Vin {
if in.CanUnlockOutputWith(address) {
inTxID := hex.EncodeToString(in.Txid)
spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Vout)
}
}
}
}
// 如果到达创世区块,停止遍历
if len(block.PrevBlockHash) == 0 {
break
}
}
return unspentTXs
}
// Iterator 创建区块链迭代器
// 用于从最新区块开始向前遍历区块链
func (bc *Blockchain) Iterator() *BlockchainIterator {
bci := &BlockchainIterator{[]byte{}, len(bc.blocks) - 1, bc}
return bci
}
// BlockchainIterator 区块链迭代器
// 用于遍历区块链中的区块
type BlockchainIterator struct {
currentHash []byte // 当前区块哈希
current int // 当前区块索引
bc *Blockchain // 区块链引用
}
// Next 返回下一个区块
// 从最新区块开始向前遍历
func (i *BlockchainIterator) Next() *Block {
block := i.bc.blocks[i.current]
i.currentHash = block.Hash
i.current-- // 向前移动
return block
}
// CanUnlockOutputWith 检查输入是否可以解锁输出
// 验证解锁脚本是否匹配
func (in *TXInput) CanUnlockOutputWith(unlockingData string) bool {
return in.ScriptSig == unlockingData
}
// CanBeUnlockedWith 检查输出是否可以被解锁
// 验证锁定脚本是否匹配
func (out *TXOutput) CanBeUnlockedWith(unlockingData string) bool {
return out.ScriptPubKey == unlockingData
}
// IsCoinbase 检查是否是coinbase交易
// Coinbase交易的特征:只有一个输入,且Txid为空,Vout为-1
func (tx *Transaction) IsCoinbase() bool {
return len(tx.Vin) == 1 && len(tx.Vin[0].Txid) == 0 && tx.Vin[0].Vout == -1
}
// MerkleTree 默克尔树
// 用于高效地验证区块中的交易
type MerkleTree struct {
RootNode *MerkleNode // 根节点
}
// MerkleNode 默克尔树节点
// 每个节点包含左右子节点和数据
type MerkleNode struct {
Left *MerkleNode // 左子节点
Right *MerkleNode // 右子节点
Data []byte // 节点数据(哈希值)
}
// NewMerkleNode 创建默克尔树节点
// 如果是叶子节点,直接计算数据的哈希
// 如果是内部节点,计算左右子节点数据的组合哈希
func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode {
node := MerkleNode{}
if left == nil && right == nil {
// 叶子节点:直接计算数据的哈希
hash := sha256.Sum256(data)
node.Data = hash[:]
} else {
// 内部节点:计算左右子节点数据的组合哈希
prevHashes := append(left.Data, right.Data...)
hash := sha256.Sum256(prevHashes)
node.Data = hash[:]
}
node.Left = left
node.Right = right
return &node
}
// NewMerkleTree 创建默克尔树
// 从交易哈希列表构建完整的默克尔树
func NewMerkleTree(data [][]byte) *MerkleTree {
var nodes []MerkleNode
// 创建叶子节点
for _, datum := range data {
node := NewMerkleNode(nil, nil, datum)
nodes = append(nodes, *node)
}
// 逐层构建树,直到只剩一个根节点
for len(nodes) > 1 {
var level []MerkleNode
// 每两个节点合并为一个父节点
for i := 0; i < len(nodes); i += 2 {
if i+1 == len(nodes) {
// 如果节点数为奇数,最后一个节点直接复制
level = append(level, nodes[i])
continue
}
node := NewMerkleNode(&nodes[i], &nodes[i+1], nil)
level = append(level, *node)
}
nodes = level
}
tree := MerkleTree{&nodes[0]}
return &tree
}
// ProofOfWork 工作量证明
// 用于挖矿和验证区块的有效性
type ProofOfWork struct {
block *Block // 要挖矿的区块
target *big.Int // 目标难度(哈希值必须小于此值)
}
// NewProofOfWork 创建工作量证明
// 设置目标难度为24位(前3个字节为0)
func NewProofOfWork(b *Block) *ProofOfWork {
target := big.NewInt(1)
target.Lsh(target, uint(256-targetBits)) // 左移232位,相当于前24位为0
pow := &ProofOfWork{b, target}
return pow
}
// prepareData 准备数据
// 将区块数据组合成用于哈希计算的字节数组
func (pow *ProofOfWork) prepareData(nonce int) []byte {
data := bytes.Join(
[][]byte{
pow.block.PrevBlockHash, // 前一个区块哈希
pow.block.HashTransactions(), // 交易哈希(默克尔根)
IntToHex(pow.block.Timestamp), // 时间戳
IntToHex(int64(targetBits)), // 目标难度
IntToHex(int64(nonce)), // 随机数
},
[]byte{},
)
return data
}
// Run 运行工作量证明
// 通过不断调整nonce值来寻找有效的哈希
func (pow *ProofOfWork) Run() (int, []byte) {
var hashInt big.Int
var hash [32]byte
nonce := 0
fmt.Printf("Mining the block containing \"%s\"\n", pow.block.Transactions[0].Vout[0].ScriptPubKey)
// 不断尝试不同的nonce值
for nonce < maxNonce {
data := pow.prepareData(nonce)
hash = sha256.Sum256(data)
fmt.Printf("\r%x", hash)
hashInt.SetBytes(hash[:])
// 检查哈希是否小于目标值
if hashInt.Cmp(pow.target) == -1 {
break
} else {
nonce++
}
}
fmt.Print("\n\n")
return nonce, hash[:]
}
// Validate 验证工作量证明
// 验证区块的nonce值是否产生有效的哈希
func (pow *ProofOfWork) Validate() bool {
var hashInt big.Int
data := pow.prepareData(pow.block.Nonce)
hash := sha256.Sum256(data)
hashInt.SetBytes(hash[:])
isValid := hashInt.Cmp(pow.target) == -1
return isValid
}
// IntToHex 将整数转换为十六进制
// 用于将数值转换为字节数组
func IntToHex(n int64) []byte {
return []byte(strconv.FormatInt(n, 16))
}
// 常量定义
const targetBits = 16 // 目标难度位数(前16位必须为0)
const maxNonce = math.MaxInt64 // 最大nonce值
// main 主函数
// 演示区块链的基本功能
func main() {
fmt.Println("=== 区块链Demo演示 ===")
fmt.Println("正在创建区块链...")
// 创建新的区块链(包含创世区块)
bc := NewBlockchain()
// 添加一些简单的coinbase交易来演示
fmt.Println("正在添加区块...")
bc.AddBlock([]*Transaction{NewCoinbaseTX("张三", "奖励给张三")})
bc.AddBlock([]*Transaction{NewCoinbaseTX("李四", "奖励给李四")})
// 打印区块链信息
fmt.Println("\n=== 区块链信息 ===")
for i, block := range bc.blocks {
fmt.Printf("区块 %d:\n", i)
fmt.Printf(" 时间戳: %d\n", block.Timestamp)
fmt.Printf(" 前一个区块哈希: %x\n", block.PrevBlockHash)
fmt.Printf(" 当前区块哈希: %x\n", block.Hash)
fmt.Printf(" 随机数: %d\n", block.Nonce)
fmt.Printf(" 交易数量: %d\n", len(block.Transactions))
// 打印交易详情
for j, tx := range block.Transactions {
fmt.Printf(" 交易 %d: %x\n", j, tx.ID)
if tx.IsCoinbase() {
fmt.Printf(" 类型: Coinbase交易\n")
fmt.Printf(" 接收者: %s\n", tx.Vout[0].ScriptPubKey)
fmt.Printf(" 金额: %d\n", tx.Vout[0].Value)
}
}
fmt.Println()
}
// 验证区块链
fmt.Println("=== 验证区块链 ===")
for i, block := range bc.blocks {
pow := NewProofOfWork(block)
fmt.Printf("区块 %d 验证结果: %t\n", i, pow.Validate())
}
// 演示默克尔树
fmt.Println("\n=== 默克尔树演示 ===")
if len(bc.blocks) > 0 {
block := bc.blocks[0]
txHashes := make([][]byte, len(block.Transactions))
for i, tx := range block.Transactions {
txHashes[i] = tx.ID
}
merkleTree := NewMerkleTree(txHashes)
fmt.Printf("默克尔根哈希: %x\n", merkleTree.RootNode.Data)
}
fmt.Println("\n=== 演示完成 ===")
}
关键代码分析
1. 核心数据结构
交易结构 (Transaction)
type Transaction struct {
ID []byte // 交易哈希ID
Vin []TXInput // 交易输入(资金来源)
Vout []TXOutput // 交易输出(资金去向)
}
区块结构 (Block)
type Block struct {
Timestamp int64 // 时间戳
Transactions []*Transaction // 交易列表
PrevBlockHash []byte // 前一个区块哈希(链式结构)
Hash []byte // 当前区块哈希
Nonce int // 工作量证明随机数
}
2. 关键流程
流程1: 区块链初始化
func NewBlockchain() *Blockchain {
// 创建包含创世区块的区块链
return &Blockchain{[]*Block{NewGenesisBlock(NewCoinbaseTX("genesis", ""))}}
}
流程2: 创建新区块
func NewBlock(transactions []*Transaction, prevBlockHash []byte) *Block {
// 1. 创建区块结构
block := &Block{
Timestamp: time.Now().Unix(),
Transactions: transactions,
PrevBlockHash: prevBlockHash,
Hash: []byte{}, // 待计算
Nonce: 0, // 待计算
}
// 2. 工作量证明挖矿
pow := NewProofOfWork(block)
nonce, hash := pow.Run()
// 3. 设置挖矿结果
block.Hash = hash[:]
block.Nonce = nonce
return block
}
流程3: 工作量证明挖矿
func (pow *ProofOfWork) Run() (int, []byte) {
var hashInt big.Int
var hash [32]byte
nonce := 0
// 不断尝试不同的nonce值
for nonce < maxNonce {
data := pow.prepareData(nonce)
hash = sha256.Sum256(data) // 计算SHA256哈希
hashInt.SetBytes(hash[:])
// 检查是否满足难度要求(前16位为0)
if hashInt.Cmp(pow.target) == -1 {
break // 挖矿成功
} else {
nonce++ // 继续尝试
}
}
return nonce, hash[:]
}
流程4: 添加区块到链
func (bc *Blockchain) AddBlock(transactions []*Transaction) {
prevBlock := bc.blocks[len(bc.blocks)-1] // 获取最后一个区块
newBlock := NewBlock(transactions, prevBlock.Hash) // 创建新区块
bc.blocks = append(bc.blocks, newBlock) // 添加到链
}
3. 关键算法
默克尔树 (Merkle Tree)
func NewMerkleTree(data [][]byte) *MerkleTree {
var nodes []MerkleNode
// 1. 创建叶子节点
for _, datum := range data {
node := NewMerkleNode(nil, nil, datum)
nodes = append(nodes, *node)
}
// 2. 逐层构建树
for len(nodes) > 1 {
var level []MerkleNode
for i := 0; i < len(nodes); i += 2 {
if i+1 == len(nodes) {
level = append(level, nodes[i]) // 奇数节点直接复制
continue
}
node := NewMerkleNode(&nodes[i], &nodes[i+1], nil)
level = append(level, *node)
}
nodes = level
}
return &MerkleTree{&nodes[0]}
}
交易ID生成
func (tx *Transaction) SetID() {
var encoded bytes.Buffer
var hash [32]byte
// 序列化交易内容
enc := gob.NewEncoder(&encoded)
err := enc.Encode(tx)
if err != nil {
log.Panic(err)
}
// 计算SHA256哈希作为交易ID
hash = sha256.Sum256(encoded.Bytes())
tx.ID = hash[:]
}
4. 主程序流程
func main() {
// 1. 创建区块链
bc := NewBlockchain()
// 2. 添加交易区块
bc.AddBlock([]*Transaction{NewCoinbaseTX("张三", "奖励给张三")})
bc.AddBlock([]*Transaction{NewCoinbaseTX("李四", "奖励给李四")})
// 3. 显示区块链信息
for i, block := range bc.blocks {
fmt.Printf("区块 %d:\n", i)
fmt.Printf(" 哈希: %x\n", block.Hash)
fmt.Printf(" 随机数: %d\n", block.Nonce)
}
// 4. 验证区块链
for i, block := range bc.blocks {
pow := NewProofOfWork(block)
fmt.Printf("区块 %d 验证结果: %t\n", i, pow.Validate())
}
}
5. 关键技术点
- 链式结构: 每个区块包含前一个区块的哈希
- 工作量证明: 通过调整nonce找到满足难度的哈希
- 默克尔树: 高效验证区块中的交易
- SHA256哈希: 确保数据完整性
- 序列化: 使用gob编码存储和传输数据
执行结果分析
# go run main.go
=== 区块链Demo演示 ===
正在创建区块链...
Mining the block containing "genesis"
0000967996bf72383203b5bc2d6819b0a0834b4070a1ef427ed5c0a603aaaf93
正在添加区块...
Mining the block containing "张三"
00003b2999cf8addbb30b10ee798abed8119160eab4b72bea4d7b094895e88a6
Mining the block containing "李四"
00006f757d2d31b5f98388f25c328b2813304ebd0c38f1069af8e72eea19467c
=== 区块链信息 ===
区块 0:
时间戳: 1761630912
前一个区块哈希:
当前区块哈希: 0000967996bf72383203b5bc2d6819b0a0834b4070a1ef427ed5c0a603aaaf93
随机数: 26477
交易数量: 1
交易 0: f110075914d97d4ec56b67e3ee5c29a7ae7c5f2876d3f1a80414603f28b5c34d
类型: Coinbase交易
接收者: genesis
金额: 100
区块 1:
时间戳: 1761630917
前一个区块哈希: 0000967996bf72383203b5bc2d6819b0a0834b4070a1ef427ed5c0a603aaaf93
当前区块哈希: 00003b2999cf8addbb30b10ee798abed8119160eab4b72bea4d7b094895e88a6
随机数: 27602
交易数量: 1
交易 0: 5ee6dcf4f2a1402d7bdb1e92004113d75331f426bb49532796cc715bdce809b9
类型: Coinbase交易
接收者: 张三
金额: 100
区块 2:
时间戳: 1761630923
前一个区块哈希: 00003b2999cf8addbb30b10ee798abed8119160eab4b72bea4d7b094895e88a6
当前区块哈希: 00006f757d2d31b5f98388f25c328b2813304ebd0c38f1069af8e72eea19467c
随机数: 6834
交易数量: 1
交易 0: e61d4fb8f92dbcf5c775e090fee8ba7f50859bdd80d75204563e2b523a1516bd
类型: Coinbase交易
接收者: 李四
金额: 100
=== 验证区块链 ===
区块 0 验证结果: true
区块 1 验证结果: true
区块 2 验证结果: true
=== 默克尔树演示 ===
默克尔根哈希: 2c548dd8f363a5048d3f1fe5b1afd0feefbf3a1fcf8a7c0eb32945534021c3be
1. 程序启动阶段
=== 区块链Demo演示 ===
正在创建区块链...
程序开始运行,创建新的区块链。此时会创建创世区块(第一个区块)
2. 挖矿过程
Mining the block containing "genesis"
0000967996bf72383203b5bc2d6819b0a0834b4070a1ef427ed5c0a603aaaf93
Mining the block containing "张三"
00003b2999cf8addbb30b10ee798abed8119160eab4b72bea4d7b094895e88a6
Mining the block containing "李四"
00006f757d2d31b5f98388f25c328b2813304ebd0c38f1069af8e72eea19467c
挖矿过程解释:
– Mining the block containing “xxx” – 正在挖矿包含指定内容的区块
– 后面的一串十六进制数字是挖矿成功后的区块哈希值
– 注意所有哈希都以 0000 开头,说明满足了16位难度要求
– 挖矿就是不断尝试nonce值,直到找到满足条件的哈希
3. 区块链结构信息
区块 0 (创世区块)
区块 0:
时间戳: 1761630912 # Unix时间戳
前一个区块哈希: # 空(创世区块没有前一个区块)
当前区块哈希: 0000967996bf72383203b5bc2d6819b0a0834b4070a1ef427ed5c0a603aaaf93
随机数: 26477 # 挖矿成功的nonce值
交易数量: 1
交易 0: f110075914d97d4ec56b67e3ee5c29a7ae7c5f2876d3f1a80414603f28b5c34d
类型: Coinbase交易 # 矿工奖励交易
接收者: genesis # 接收者地址
金额: 100 # 奖励金额
区块 1 (张三的区块)
区块 1:
时间戳: 1761630917 # 比区块0晚5秒
前一个区块哈希: 0000967996bf72383203b5bc2d6819b0a0834b4070a1ef427ed5c0a603aaaf93 # 区块0的哈希
当前区块哈希: 00003b2999cf8addbb30b10ee798abed8119160eab4b72bea4d7b094895e88a6
随机数: 27602 # 不同的nonce值
交易数量: 1
交易 0: 5ee6dcf4f2a1402d7bdb1e92004113d75331f426bb49532796cc715bdce809b9
类型: Coinbase交易
接收者: 张三 # 张三获得奖励
金额: 100
区块 2 (李四的区块)
区块 2:
时间戳: 1761630923 # 比区块1晚6秒
前一个区块哈希: 00003b2999cf8addbb30b10ee798abed8119160eab4b72bea4d7b094895e88a6 # 区块1的哈希
当前区块哈希: 00006f757d2d31b5f98388f25c328b2813304ebd0c38f1069af8e72eea19467c
随机数: 6834 # 不同的nonce值
交易数量: 1
交易 0: e61d4fb8f92dbcf5c775e090fee8ba7f50859bdd80d75204563e2b523a1516bd
类型: Coinbase交易
接收者: 李四 # 李四获得奖励
金额: 100
4. 区块链链接关系
区块0 → 区块1 → 区块2
| | |
| | └─ 前一个区块哈希 = 区块1的哈希
| └─ 前一个区块哈希 = 区块0的哈希
└─ 创世区块(没有前一个区块)
5. 验证结果
=== 验证区块链 ===
区块 0 验证结果: true
区块 1 验证结果: true
区块 2 验证结果: true
验证含义:
– 每个区块的哈希值都满足工作量证明要求
– 区块链的完整性得到确认
– 没有发现篡改或错误
6. 默克尔树演示
=== 默克尔树演示 ===
默克尔根哈希: 2c548dd8f363a5048d3f1fe5b1afd0feefbf3a1fcf8a7c0eb32945534021c3be
默克尔树作用:
– 将区块中的所有交易组织成树状结构
– 根哈希代表整个区块的交易摘要
– 用于快速验证交易是否被篡改
7. 关键观察点
- 哈希链式结构: 每个区块都包含前一个区块的哈希
- 时间递增: 时间戳依次递增,体现区块的时间顺序
- 挖矿难度: 所有哈希都以 0000 开头,满足16位难度
- 交易完整性: 每个区块都包含有效的coinbase交易
- 验证通过: 所有区块都通过了工作量证明验证
时序图
区块链Demo时序图
时间轴: 程序启动 → 创建区块链 → 挖矿过程 → 验证结果
参与者: Main函数 | Blockchain | Block | ProofOfWork | Transaction
Main函数:
│
├─ 1. 启动程序
│ │
│ └─ 输出: "=== 区块链Demo演示 ==="
│
├─ 2. 创建区块链
│ │
│ ├─ 调用: NewBlockchain()
│ │ │
│ │ ├─ 创建创世区块
│ │ │ │
│ │ │ ├─ 创建Coinbase交易: genesis
│ │ │ │
│ │ │ ├─ 调用: NewBlock(transactions, nil)
│ │ │ │ │
│ │ │ │ ├─ 设置区块基本信息
│ │ │ │ │ │
│ │ │ │ │ ├─ Timestamp: 当前时间
│ │ │ │ │ │
│ │ │ │ │ ├─ Transactions: [coinbase]
│ │ │ │ │ │
│ │ │ │ │ ├─ PrevBlockHash: [] (空)
│ │ │ │ │ │
│ │ │ │ │ └─ Hash: [] (待计算)
│ │ │ │ │
│ │ │ │ ├─ 创建工作量证明: NewProofOfWork(block)
│ │ │ │ │ │
│ │ │ │ │ ├─ 设置目标难度: 16位 (0000...)
│ │ │ │ │ │
│ │ │ │ │ └─ 开始挖矿: pow.Run()
│ │ │ │ │ │
│ │ │ │ │ ├─ nonce = 0
│ │ │ │ │ │
│ │ │ │ │ ├─ 循环: for nonce < maxNonce
│ │ │ │ │ │ │
│ │ │ │ │ │ ├─ 准备数据: prepareData(nonce)
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ ├─ 组合: PrevBlockHash + Transactions + Timestamp + TargetBits + Nonce
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ └─ 返回: 字节数组
│ │ │ │ │ │ │
│ │ │ │ │ │ ├─ 计算哈希: SHA256(data)
│ │ │ │ │ │ │
│ │ │ │ │ │ ├─ 检查难度: hash < target?
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ ├─ 是: 挖矿成功! → 返回 (nonce, hash)
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ └─ 否: nonce++ → 继续循环
│ │ │ │ │ │ │
│ │ │ │ │ │ └─ 输出: 挖矿过程中的哈希值
│ │ │ │ │ │
│ │ │ │ │ └─ 输出: "Mining the block containing 'genesis'"
│ │ │ │ │
│ │ │ │ ├─ 设置区块哈希: block.Hash = hash
│ │ │ │ │
│ │ │ │ └─ 设置随机数: block.Nonce = nonce
│ │ │ │
│ │ │ └─ 返回: 完整的创世区块
│ │ │
│ │ └─ 创建区块链: Blockchain{blocks: [genesisBlock]}
│ │
│ └─ 输出: "正在创建区块链..."
│
├─ 3. 添加第一个区块 (张三)
│ │
│ ├─ 输出: "正在添加区块..."
│ │
│ ├─ 创建Coinbase交易: NewCoinbaseTX("张三", "奖励给张三")
│ │ │
│ │ ├─ 创建交易输入: TXInput{Txid: [], Vout: -1, ScriptSig: "奖励给张三"}
│ │ │
│ │ ├─ 创建交易输出: TXOutput{Value: 100, ScriptPubKey: "张三"}
│ │ │
│ │ ├─ 创建交易: Transaction{Vin: [input], Vout: [output]}
│ │ │
│ │ ├─ 计算交易ID: tx.SetID()
│ │ │ │
│ │ │ ├─ 序列化交易: gob.Encode(tx)
│ │ │ │
│ │ │ ├─ 计算哈希: SHA256(serialized)
│ │ │ │
│ │ │ └─ 设置ID: tx.ID = hash
│ │ │
│ │ └─ 返回: 完整的coinbase交易
│ │
│ ├─ 调用: bc.AddBlock([coinbaseTX])
│ │ │
│ │ ├─ 获取最后一个区块: bc.blocks[len-1]
│ │ │
│ │ ├─ 创建新区块: NewBlock(transactions, prevBlock.Hash)
│ │ │ │
│ │ │ ├─ 设置区块信息
│ │ │ │ │
│ │ │ │ ├─ Timestamp: 当前时间
│ │ │ │ │
│ │ │ │ ├─ Transactions: [张三的coinbase]
│ │ │ │ │
│ │ │ │ ├─ PrevBlockHash: 创世区块的哈希
│ │ │ │ │
│ │ │ │ └─ Hash: [] (待计算)
│ │ │ │
│ │ │ ├─ 工作量证明挖矿
│ │ │ │ │
│ │ │ │ ├─ 输出: "Mining the block containing '张三'"
│ │ │ │ │
│ │ │ │ ├─ 挖矿循环 (同创世区块)
│ │ │ │ │
│ │ │ │ └─ 输出: 挖矿过程中的哈希值
│ │ │ │
│ │ │ ├─ 设置哈希和nonce
│ │ │ │
│ │ │ └─ 返回: 完整的区块1
│ │ │
│ │ └─ 添加到链: bc.blocks.append(newBlock)
│ │
│ └─ 输出: 挖矿成功哈希
│
├─ 4. 添加第二个区块 (李四)
│ │
│ ├─ 创建Coinbase交易: NewCoinbaseTX("李四", "奖励给李四")
│ │ │
│ │ ├─ 创建交易输入/输出
│ │ │
│ │ ├─ 计算交易ID
│ │ │
│ │ └─ 返回: 完整的coinbase交易
│ │
│ ├─ 调用: bc.AddBlock([coinbaseTX])
│ │ │
│ │ ├─ 获取最后一个区块: 区块1
│ │ │
│ │ ├─ 创建新区块: NewBlock(transactions, 区块1.Hash)
│ │ │ │
│ │ │ ├─ 设置区块信息
│ │ │ │ │
│ │ │ │ ├─ PrevBlockHash: 区块1的哈希
│ │ │ │ │
│ │ │ │ └─ 其他信息...
│ │ │ │
│ │ │ ├─ 工作量证明挖矿
│ │ │ │ │
│ │ │ │ ├─ 输出: "Mining the block containing '李四'"
│ │ │ │ │
│ │ │ │ ├─ 挖矿循环
│ │ │ │ │
│ │ │ │ └─ 输出: 挖矿过程中的哈希值
│ │ │ │
│ │ │ └─ 返回: 完整的区块2
│ │ │
│ │ └─ 添加到链: bc.blocks.append(newBlock)
│ │
│ └─ 输出: 挖矿成功哈希
│
├─ 5. 显示区块链信息
│ │
│ ├─ 输出: "=== 区块链信息 ==="
│ │
│ ├─ 遍历所有区块: for i, block := range bc.blocks
│ │ │
│ │ ├─ 输出区块基本信息
│ │ │ │
│ │ │ ├─ 区块编号: i
│ │ │ │
│ │ │ ├─ 时间戳: block.Timestamp
│ │ │ │
│ │ │ ├─ 前一个区块哈希: block.PrevBlockHash
│ │ │ │
│ │ │ ├─ 当前区块哈希: block.Hash
│ │ │ │
│ │ │ ├─ 随机数: block.Nonce
│ │ │ │
│ │ │ └─ 交易数量: len(block.Transactions)
│ │ │
│ │ ├─ 遍历区块中的交易: for j, tx := range block.Transactions
│ │ │ │
│ │ │ ├─ 输出交易ID: tx.ID
│ │ │ │
│ │ │ ├─ 检查交易类型: tx.IsCoinbase()
│ │ │ │ │
│ │ │ │ ├─ 是coinbase: 输出类型、接收者、金额
│ │ │ │ │
│ │ │ │ └─ 否: 输出普通交易信息
│ │ │ │
│ │ └─ 输出空行分隔
│ │
│ └─ 完成区块信息显示
│
├─ 6. 验证区块链
│ │
│ ├─ 输出: "=== 验证区块链 ==="
│ │
│ ├─ 遍历所有区块: for i, block := range bc.blocks
│ │ │
│ │ ├─ 创建工作量证明: NewProofOfWork(block)
│ │ │
│ │ ├─ 验证区块: pow.Validate()
│ │ │ │
│ │ │ ├─ 准备数据: prepareData(block.Nonce)
│ │ │ │
│ │ │ ├─ 计算哈希: SHA256(data)
│ │ │ │
│ │ │ ├─ 检查难度: hash < target?
│ │ │ │ │
│ │ │ │ ├─ 是: 返回 true
│ │ │ │ │
│ │ │ │ └─ 否: 返回 false
│ │ │ │
│ │ └─ 输出: "区块 i 验证结果: true/false"
│ │
│ └─ 完成验证
│
├─ 7. 默克尔树演示
│ │
│ ├─ 输出: "=== 默克尔树演示 ==="
│ │
│ ├─ 获取第一个区块: bc.blocks[0]
│ │
│ ├─ 收集交易哈希: for tx := range block.Transactions
│ │ │
│ │ └─ txHashes.append(tx.ID)
│ │
│ ├─ 创建默克尔树: NewMerkleTree(txHashes)
│ │ │
│ │ ├─ 创建叶子节点: for each txHash
│ │ │ │
│ │ │ └─ NewMerkleNode(nil, nil, txHash)
│ │ │
│ │ ├─ 逐层构建树: while len(nodes) > 1
│ │ │ │
│ │ │ ├─ 每两个节点合并: NewMerkleNode(left, right, nil)
│ │ │ │
│ │ │ └─ 计算父节点哈希: SHA256(left.Data + right.Data)
│ │ │
│ │ └─ 返回: MerkleTree{RootNode}
│ │
│ ├─ 输出根哈希: merkleTree.RootNode.Data
│ │
│ └─ 完成默克尔树演示
│
└─ 8. 程序结束
│
└─ 输出: "=== 演示完成 ==="
数据流向
Main → NewBlockchain → NewGenesisBlock → NewBlock → NewProofOfWork → Run()
│ │
│ ▼
│ 挖矿成功
│ │
│ ▼
│ 设置Hash和Nonce
│ │
│ ▼
│ 返回完整区块
│ │
▼ │
AddBlock ←─────────────────────────────────────────────────────────────┘
│
▼
添加到区块链
│
▼
继续下一个区块...