HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • Web3 完整技术体系

    • Web3 完整技术体系
    • 区块链基础与密码学
    • 第一章:比特币原理与实现
    • 第二章:以太坊架构与核心概念
    • Solidity智能合约开发基础
    • 04-Solidity进阶与安全
    • 05-ERC标准详解
    • 06-DeFi核心协议-去中心化交易所
    • 07-DeFi核心协议-借贷协议
    • 08-DeFi核心协议-稳定币
    • NFT与元宇宙
    • Layer2扩容方案
    • 跨链技术
    • Web3前端开发
    • 链下数据与预言机
    • 智能合约测试与部署
    • MEV与交易优化
    • DAO治理
    • 项目实战:完整DeFi借贷协议

区块链基础与密码学

区块链的本质是密码学、分布式系统和博弈论的结合

📖 章节概述

本章介绍区块链的密码学基础,包括哈希函数、非对称加密、数字签名、Merkle树等核心概念,为后续学习打下坚实基础。

学习目标

  • 理解哈希函数的特性和应用
  • 掌握非对称加密和数字签名的原理
  • 理解 Merkle 树的结构和作用
  • 了解区块链如何使用密码学保证安全

一、哈希函数(Hash Function)

1.1 哈希函数的定义

哈希函数是一种单向函数,将任意长度的输入映射为固定长度的输出。

H(input) = output (固定长度)

示例(SHA-256):

H("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
H("hello world") = b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9

1.2 哈希函数的特性

特性说明区块链应用
确定性相同输入总是产生相同输出交易ID、区块ID
快速计算可以快速计算哈希值高效验证
单向性无法从哈希值反推原始数据隐私保护
雪崩效应输入微小变化导致输出剧变防篡改
抗碰撞性很难找到两个不同输入产生相同输出唯一性保证

雪崩效应示例

H("hello")      = 2cf24dba5fb0a30e...
H("hello!")     = e6b7c45e2e7c6c77...  (完全不同)
H("Hello")      = 185f8db32271fe25...  (完全不同)

1.3 常见哈希算法

算法输出长度安全性区块链应用
MD5128位已破解不推荐
SHA-1160位⚠️ 弱Git(非安全场景)
SHA-256256位强比特币、以太坊
SHA-3可变强新一代标准
Keccak-256256位强以太坊地址

1.4 Go 语言实现

package main

import (
    "crypto/sha256"
    "encoding/hex"
    "fmt"
)

func main() {
    data := "hello"

    // SHA-256
    hash := sha256.Sum256([]byte(data))
    hashHex := hex.EncodeToString(hash[:])

    fmt.Printf("SHA-256(%s) = %s\n", data, hashHex)

    // 验证雪崩效应
    data2 := "hello!"
    hash2 := sha256.Sum256([]byte(data2))
    hashHex2 := hex.EncodeToString(hash2[:])

    fmt.Printf("SHA-256(%s) = %s\n", data2, hashHex2)
}

1.5 区块链中的哈希应用

应用1:交易ID(TXID)

交易内容 → SHA-256 → 交易ID

以太坊交易ID示例:

0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060

应用2:区块哈希

区块头(包含前一个区块哈希) → SHA-256 → 当前区块哈希

比特币区块链示例:

区块 #100: 000000007bc154e0fa7ea32218a72fe2c1bb9f86cf8c9ebf9a715ed27fdb229a
区块 #101: 00000000c4cbd75af741f3a2b2ff72d9ed4d83a048462c1efe331be31ccf006b
            (包含#100的哈希)

🔑 二、非对称加密(Asymmetric Encryption)

2.1 非对称加密的原理

非对称加密使用一对密钥:公钥(Public Key)和私钥(Private Key)。

┌─────────────────────────────────────────┐
│          非对称加密                      │
├─────────────────────────────────────────┤
│ 公钥加密 → 私钥解密  (加密通信)          │
│ 私钥签名 → 公钥验证  (数字签名)          │
└─────────────────────────────────────────┘

核心特性:

  • 公钥可以公开,私钥必须保密
  • 从公钥无法反推出私钥
  • 公钥加密的数据只能用私钥解密
  • 私钥签名的数据可以用公钥验证

2.2 常见非对称加密算法

算法密钥长度安全性区块链应用
RSA2048/4096位强很少用(效率低)
ECC(椭圆曲线)256位强比特币、以太坊
secp256k1256位强比特币

为什么区块链用ECC而不是RSA?

  • 密钥更短:256位ECC ≈ 3072位RSA
  • 签名更快:ECC签名速度快10倍
  • 体积更小:区块链需要存储大量签名

2.3 椭圆曲线加密(ECC)原理

椭圆曲线方程:

y² = x³ + ax + b

secp256k1曲线(比特币、以太坊):

y² = x³ + 7

密钥生成:

私钥(k):随机生成的256位数字
公钥(K):K = k × G (G是曲线上的基点)

重要性质:

  • 已知 k 和 G,很容易计算 K
  • 已知 K 和 G,几乎不可能反推 k (离散对数难题)

2.4 Go 语言实现

package main

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "fmt"
)

func main() {
    // 生成私钥
    privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    if err != nil {
        panic(err)
    }

    // 导出公钥
    publicKey := privateKey.PublicKey

    fmt.Printf("私钥: %x\n", privateKey.D.Bytes())
    fmt.Printf("公钥X: %x\n", publicKey.X.Bytes())
    fmt.Printf("公钥Y: %x\n", publicKey.Y.Bytes())
}

✍️ 三、数字签名(Digital Signature)

3.1 数字签名的作用

数字签名用于证明身份和数据完整性:

作用说明
身份认证证明消息确实是私钥持有者发送的
不可抵赖签名者无法否认签过名
防篡改数据被修改后签名失效

3.2 签名和验证流程

┌──────────────────────────────────────────────┐
│               签名流程                        │
├──────────────────────────────────────────────┤
│ 1. 对消息计算哈希: h = SHA-256(message)       │
│ 2. 用私钥签名哈希: signature = sign(h, 私钥)  │
│ 3. 发送 (message, signature, 公钥)           │
└──────────────────────────────────────────────┘

┌──────────────────────────────────────────────┐
│               验证流程                        │
├──────────────────────────────────────────────┤
│ 1. 收到 (message, signature, 公钥)           │
│ 2. 计算哈希: h = SHA-256(message)            │
│ 3. 用公钥验证: verify(h, signature, 公钥)    │
│ 4. 如果验证通过,说明消息未被篡改且来自私钥持有者│
└──────────────────────────────────────────────┘

3.3 ECDSA 签名算法

ECDSA(Elliptic Curve Digital Signature Algorithm)是区块链最常用的签名算法。

签名过程:

1. 选择随机数 k
2. 计算点 R = k × G,取 R 的 x 坐标为 r
3. 计算 s = k⁻¹ (hash + r × 私钥) mod n
4. 签名结果:(r, s)

验证过程:

1. 计算 u1 = hash × s⁻¹ mod n
2. 计算 u2 = r × s⁻¹ mod n
3. 计算 R' = u1 × G + u2 × 公钥
4. 如果 R' 的 x 坐标 = r,则签名有效

3.4 Go 语言实现

package main

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/sha256"
    "fmt"
    "math/big"
)

func main() {
    // 1. 生成密钥对
    privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    publicKey := &privateKey.PublicKey

    // 2. 待签名的消息
    message := []byte("Transfer 10 BTC to Alice")
    hash := sha256.Sum256(message)

    // 3. 签名
    r, s, err := ecdsa.Sign(rand.Reader, privateKey, hash[:])
    if err != nil {
        panic(err)
    }
    fmt.Printf("签名 (r, s):\n")
    fmt.Printf("r = %x\n", r)
    fmt.Printf("s = %x\n", s)

    // 4. 验证签名
    valid := ecdsa.Verify(publicKey, hash[:], r, s)
    fmt.Printf("签名验证: %v\n", valid)

    // 5. 篡改消息后验证
    tamperedMessage := []byte("Transfer 100 BTC to Alice")
    tamperedHash := sha256.Sum256(tamperedMessage)
    validAfterTamper := ecdsa.Verify(publicKey, tamperedHash[:], r, s)
    fmt.Printf("篡改后验证: %v\n", validAfterTamper)
}

3.5 区块链中的签名应用

比特币交易签名

交易输入:
{
  "txid": "前一笔交易ID",
  "vout": 0,
  "scriptSig": "签名 + 公钥"  ← 证明你是UTXO的所有者
}

以太坊交易签名

// 以太坊交易
{
  nonce: 0,
  to: "0x1234...",
  value: 1000000000000000000, // 1 ETH
  gasLimit: 21000,
  gasPrice: 20000000000,
  data: "0x",
  v: 27,  ← 签名参数
  r: "0x...",  ← 签名参数
  s: "0x..."   ← 签名参数
}

签名流程:

1. 构造交易对象
2. 用私钥签名交易哈希
3. 广播交易到网络
4. 矿工用公钥(地址)验证签名

🌳 四、Merkle 树(Merkle Tree)

4.1 Merkle 树的结构

Merkle树是一种哈希二叉树,叶子节点存储数据哈希,非叶子节点存储子节点哈希的哈希。

                  Root Hash
                 /          \
            H(AB)              H(CD)
           /    \             /    \
        H(A)   H(B)       H(C)   H(D)
         |      |          |      |
        Data   Data       Data   Data
         A      B          C      D

计算过程:

H(A) = SHA-256(Data A)
H(B) = SHA-256(Data B)
H(AB) = SHA-256(H(A) + H(B))
Root Hash = SHA-256(H(AB) + H(CD))

4.2 Merkle 树的作用

作用说明区块链应用
高效验证只需 O(log n) 次哈希即可验证数据SPV轻节点
防篡改任何数据修改都会改变根哈希区块完整性
压缩存储只需存储根哈希即可验证整棵树区块头

4.3 Merkle Proof(Merkle 证明)

场景:轻节点验证某笔交易是否在区块中,但不下载整个区块。

示例:验证 Data B 是否在区块中

已知:
- Root Hash(存储在区块头中)
- Data B(待验证的交易)
- Merkle Proof:[H(A), H(CD)]

验证步骤:
1. 计算 H(B) = SHA-256(Data B)
2. 计算 H(AB) = SHA-256(H(A) + H(B))  ← 使用Proof中的H(A)
3. 计算 Root' = SHA-256(H(AB) + H(CD)) ← 使用Proof中的H(CD)
4. 如果 Root' == Root Hash,则 Data B 确实在区块中

优势:

  • 只需传输 log₂(n) 个哈希值
  • 例如:1000笔交易,只需传输 10 个哈希(320字节),而不是整个区块(几百KB)

4.4 Go 语言实现

package main

import (
    "crypto/sha256"
    "encoding/hex"
    "fmt"
)

type MerkleTree struct {
    RootNode *MerkleNode
}

type MerkleNode struct {
    Left  *MerkleNode
    Right *MerkleNode
    Data  []byte
}

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
}

func NewMerkleTree(data [][]byte) *MerkleTree {
    var nodes []*MerkleNode

    // 创建叶子节点
    for _, datum := range data {
        node := NewMerkleNode(nil, nil, datum)
        nodes = append(nodes, node)
    }

    // 如果节点数量为奇数,复制最后一个节点
    if len(nodes)%2 != 0 {
        nodes = append(nodes, nodes[len(nodes)-1])
    }

    // 构建树
    for len(nodes) > 1 {
        var level []*MerkleNode

        for i := 0; i < len(nodes); i += 2 {
            node := NewMerkleNode(nodes[i], nodes[i+1], nil)
            level = append(level, node)
        }

        nodes = level

        if len(nodes)%2 != 0 && len(nodes) > 1 {
            nodes = append(nodes, nodes[len(nodes)-1])
        }
    }

    return &MerkleTree{nodes[0]}
}

func main() {
    data := [][]byte{
        []byte("Transaction A"),
        []byte("Transaction B"),
        []byte("Transaction C"),
        []byte("Transaction D"),
    }

    tree := NewMerkleTree(data)

    fmt.Printf("Merkle Root: %s\n", hex.EncodeToString(tree.RootNode.Data))
}

4.5 比特币中的 Merkle 树

比特币区块头(80字节):
{
  "version": 1,
  "prev_block": "000000000019d668...",
  "merkle_root": "4a5e1e4baab89f3a...",  ← Merkle根哈希
  "timestamp": 1231006505,
  "bits": 486604799,
  "nonce": 2083236893
}

作用:

  • 区块头只存储 Merkle Root(32字节)
  • 可以验证任意交易是否在区块中
  • SPV 轻钱包只需下载区块头(80字节),不需要下载完整区块(可能几MB)

五、地址生成

5.1 比特币地址生成

1. 生成私钥(256位随机数)
   私钥: 18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725

2. 生成公钥(椭圆曲线)
   公钥: 04 + x坐标(32字节) + y坐标(32字节)

3. 哈希公钥
   SHA-256(公钥) → RIPEMD-160 → 公钥哈希(20字节)

4. 添加版本号和校验码
   版本号(1字节) + 公钥哈希(20字节) + 校验码(4字节)

5. Base58编码
   比特币地址: 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa

5.2 以太坊地址生成

1. 生成私钥(256位随机数)
   私钥: 0x4c0883a69102937d6231471b5dbb6204fe512961708279f8a37f7d9c9e5f1a5d

2. 生成公钥(椭圆曲线secp256k1)
   公钥: 04 + x + y (65字节)

3. Keccak-256哈希公钥
   Keccak-256(公钥) → 取后20字节

4. 添加0x前缀
   以太坊地址: 0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed

以太坊地址校验和(EIP-55):

对地址哈希,如果哈希值对应位>= 8,则该位字母大写
0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed
  (a变A,因为Keccak-256(地址)对应位>= 8)

📖 六、总结

密码学在区块链中的应用

密码学技术区块链应用作用
哈希函数交易ID、区块哈希唯一标识、防篡改
非对称加密公钥/私钥、地址生成身份认证
数字签名交易签名证明所有权、授权转账
Merkle树交易组织、SPV验证高效验证、压缩存储

核心要点

  1. 哈希函数是基石:所有数据的唯一标识都依赖哈希
  2. 私钥是一切:私钥丢失 = 资产丢失,私钥泄露 = 资产被盗
  3. 签名保证安全:没有私钥签名,无法转移资产
  4. Merkle树提升效率:轻节点可以快速验证交易

Prev
Web3 完整技术体系
Next
第一章:比特币原理与实现