从手写区块链demo来理解区块链

一直想手写一次区块链来理解,终于补充了。
首先上代码:

// 区块链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. 关键技术点

  1. 链式结构: 每个区块包含前一个区块的哈希
  2. 工作量证明: 通过调整nonce找到满足难度的哈希
  3. 默克尔树: 高效验证区块中的交易
  4. SHA256哈希: 确保数据完整性
  5. 序列化: 使用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. 关键观察点

  1. 哈希链式结构: 每个区块都包含前一个区块的哈希
  2. 时间递增: 时间戳依次递增,体现区块的时间顺序
  3. 挖矿难度: 所有哈希都以 0000 开头,满足16位难度
  4. 交易完整性: 每个区块都包含有效的coinbase交易
  5. 验证通过: 所有区块都通过了工作量证明验证

时序图

区块链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 ←─────────────────────────────────────────────────────────────┘
  │
  ▼
添加到区块链
  │
  ▼
继续下一个区块...

主要流程时序图

挖矿过程详细时序图

区块链结构图

默克尔树结构图

验证流程时序图

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注