第一章:比特币原理与实现
1.1 比特币概述
比特币(Bitcoin)是第一个成功的去中心化数字货币系统,由中本聪(Satoshi Nakamoto)在2008年发布的白皮书《Bitcoin: A Peer-to-Peer Electronic Cash System》中提出。比特币解决了数字货币领域长期存在的"双重支付"问题,通过巧妙结合密码学、分布式系统和博弈论等技术,创建了一个无需信任中介的电子现金系统。
1.1.1 比特币的核心特性
比特币系统具有以下核心特性:
- 去中心化:没有中央权威机构控制网络,所有节点地位平等
- 防篡改:通过工作量证明和链式结构,历史交易几乎不可篡改
- 透明性:所有交易记录公开可查,任何人都可以验证
- 有限供应:总量恒定为2100万枚,通过减半机制控制发行速度
- 匿名性:用户通过公钥地址进行交易,不直接关联真实身份
1.1.2 比特币网络架构
比特币网络采用点对点(P2P)架构,主要包含以下组件:
- 全节点(Full Node):存储完整区块链数据,验证所有交易和区块
- 轻节点(Light Node):只存储区块头,通过SPV验证交易
- 矿工节点(Miner Node):参与挖矿,竞争记账权
- 钱包(Wallet):管理私钥,创建和签名交易
1.2 UTXO模型详解
UTXO(Unspent Transaction Output,未花费的交易输出)是比特币的核心数据模型,与传统的账户余额模型有本质区别。
1.2.1 UTXO基本概念
在UTXO模型中,比特币的余额不是通过账户记录的,而是由多个UTXO组成。每个UTXO代表一定数量的比特币,具有以下特征:
- 每个UTXO是原子性的,要么完全花费,要么完全不花费
- 一次交易可以消费多个UTXO作为输入
- 一次交易可以产生多个新的UTXO作为输出
- UTXO一旦被花费,就从UTXO集合中移除
UTXO的优势:
- 并行验证:不同的UTXO可以独立验证,提高并发性能
- 隐私保护:每次交易可以使用新地址,增强匿名性
- 简化验证:只需验证输入UTXO是否存在且未被花费
- 防止双花:通过UTXO集合快速检测双重支付
1.2.2 交易结构
比特币交易包含以下核心字段:
Transaction {
Version: 版本号
Inputs: [{
PrevTxHash: 引用的前一笔交易哈希
OutputIndex: 引用的输出索引
ScriptSig: 解锁脚本(签名)
Sequence: 序列号
}]
Outputs: [{
Value: 输出金额(聪为单位)
ScriptPubKey: 锁定脚本(收款地址)
}]
LockTime: 锁定时间
}
交易示例:
假设Alice有一个10 BTC的UTXO,想要给Bob转账6 BTC:
输入:
- UTXO_1: 10 BTC (来自交易Hash_X的输出0)
输出:
- UTXO_2: 6 BTC (Bob的地址)
- UTXO_3: 3.99 BTC (Alice的找零地址)
- 0.01 BTC (矿工费,隐含在差额中)
1.2.3 脚本系统
比特币使用基于栈的脚本语言来实现灵活的支付条件。脚本分为两部分:
锁定脚本(ScriptPubKey):定义花费条件
OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
解锁脚本(ScriptSig):提供满足条件的数据
<Signature> <PubKey>
验证过程:
- 将解锁脚本和锁定脚本拼接
- 从左到右依次执行操作码
- 如果最终栈顶为真,则验证通过
执行流程:
1. <Signature> <PubKey> OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
2. 压入签名和公钥
3. OP_DUP复制公钥
4. OP_HASH160计算公钥哈希
5. 压入期望的公钥哈希
6. OP_EQUALVERIFY验证哈希是否相等
7. OP_CHECKSIG验证签名
1.3 区块结构与链式存储
1.3.1 区块头结构
区块头(Block Header)包含80字节的关键信息:
BlockHeader {
Version: 4字节,版本号
PrevBlockHash: 32字节,前一个区块哈希
MerkleRoot: 32字节,交易的Merkle树根
Timestamp: 4字节,时间戳
Bits: 4字节,难度目标
Nonce: 4字节,工作量证明随机数
}
1.3.2 Merkle树
Merkle树是一种哈希二叉树,用于高效验证交易是否包含在区块中。
构建过程:
- 对每笔交易计算哈希(叶子节点)
- 两两配对计算父节点哈希:
Hash(Hash_A + Hash_B) - 重复步骤2,直到得到根哈希(Merkle Root)
Root Hash
/ \
Hash_AB Hash_CD
/ \ / \
Hash_A Hash_B Hash_C Hash_D
| | | |
Tx_A Tx_B Tx_C Tx_D
Merkle证明(SPV验证):
轻节点只需下载区块头,通过Merkle路径即可验证交易:
验证Tx_B在区块中:
1. 提供路径:Hash_A, Hash_CD
2. 计算:Hash_AB = Hash(Hash_A + Hash_B)
3. 计算:Root = Hash(Hash_AB + Hash_CD)
4. 对比Root与区块头中的MerkleRoot
时间复杂度:O(log n),空间复杂度:O(log n)
1.3.3 区块链式结构
每个区块通过PrevBlockHash字段指向前一个区块,形成链式结构:
Genesis Block → Block 1 → Block 2 → ... → Block N
Hash_0 → Hash_1 → Hash_2 → ... → Hash_N
链式结构的特性:
- 不可篡改性:修改历史区块会导致后续所有区块哈希失效
- 唯一性:最长链代表网络共识
- 追溯性:可以从任意区块追溯到创世区块
1.4 工作量证明(Proof of Work)
1.4.1 PoW原理
工作量证明是比特币共识机制的核心,要求矿工找到一个Nonce值,使得区块哈希满足难度目标。
目标条件:
SHA256(SHA256(BlockHeader)) < Target
Target是一个256位的数,由Bits字段编码。区块哈希必须小于这个目标值,即前导零的数量要达到要求。
难度计算:
Difficulty = 最大目标值 / 当前目标值
最大目标值(难度1):
0x00000000FFFF0000000000000000000000000000000000000000000000000000
当前比特币网络难度通常在数十万亿以上,意味着需要进行大量哈希计算才能找到有效区块。
1.4.2 难度调整机制
比特币网络每2016个区块(约两周)自动调整一次难度,目标是保持平均出块时间为10分钟。
调整公式:
新难度 = 旧难度 × (实际用时 / 20160分钟)
限制条件:
- 单次调整幅度不超过4倍
- 最小难度为1
这种动态调整确保了无论算力如何变化,出块速度都能保持稳定。
1.4.3 挖矿过程
挖矿是寻找有效Nonce的过程:
1. 收集待确认交易,构建交易列表
2. 创建Coinbase交易(区块奖励)
3. 构建Merkle树,计算MerkleRoot
4. 填充区块头其他字段
5. 循环尝试不同的Nonce值:
a. 计算区块哈希
b. 检查是否满足难度目标
c. 如果满足,广播区块;否则增加Nonce继续
6. 如果遍历完所有Nonce仍未找到,修改Coinbase交易或时间戳
挖矿难度示例:
假设当前难度要求前导20个零(十六进制5个零):
有效哈希:0x00000a1b2c3d4e5f...
无效哈希:0x00001a1b2c3d4e5f...
平均需要尝试2^20 ≈ 100万次才能找到一个有效哈希。
1.4.4 区块奖励与减半
矿工成功挖出区块后获得两部分收益:
- 区块奖励(Coinbase):新发行的比特币
- 交易手续费:区块内所有交易的手续费总和
减半机制:
区块高度 区块奖励
0 - 209,999 50 BTC
210,000 - 419,999 25 BTC
420,000 - 629,999 12.5 BTC
630,000 - 839,999 6.25 BTC
840,000 - ... 3.125 BTC
...
每21万个区块(约4年)奖励减半,预计在2140年左右,所有比特币将被挖完,之后矿工仅依靠交易手续费获得收益。
总供应量计算:
总量 = 50×210,000 + 25×210,000 + 12.5×210,000 + ...
= 210,000 × (50 + 25 + 12.5 + 6.25 + ...)
= 210,000 × 100
= 21,000,000 BTC
1.5 共识机制与分叉处理
1.5.1 最长链原则
当网络中出现多条竞争链时,节点遵循"最长链"(累计工作量最大的链)原则:
Block A1 → Block A2 → Block A3
/
Block 0
\
Block B1 → Block B2
如果链A的累计难度更高,节点会选择链A作为主链,链B的区块成为孤块(Orphan Block)。
1.5.2 临时分叉
当两个矿工几乎同时找到有效区块时,会产生临时分叉:
时刻T1: ... → Block N → Block X (矿工A广播)
→ Block Y (矿工B广播)
时刻T2: ... → Block N → Block X → Block X' (链X更长)
→ Block Y (成为孤块)
网络会自动收敛到最长链,孤块中的交易会重新回到交易池等待打包。
1.5.3 硬分叉与软分叉
软分叉(Soft Fork):
- 新规则是旧规则的子集
- 旧节点仍可验证新区块
- 向后兼容,不强制升级
- 例如:SegWit、P2SH
硬分叉(Hard Fork):
- 新规则与旧规则不兼容
- 旧节点无法验证新区块
- 需要所有节点升级
- 可能导致链分裂(如Bitcoin Cash)
1.6 比特币网络协议
1.6.1 P2P网络通信
比特币节点通过TCP协议进行通信,默认端口8333。主要消息类型:
握手过程:
节点A → 节点B: version (包含版本号、时间戳、服务类型)
节点B → 节点A: version
节点B → 节点A: verack (确认)
节点A → 节点B: verack
消息类型:
version/verack:版本协商addr:节点地址信息inv:库存声明(通知有新的区块或交易)getdata:请求数据block:区块数据tx:交易数据ping/pong:心跳保活
1.6.2 区块同步
新节点加入网络后,需要同步完整区块链:
1. 发送getblocks消息,包含本地最高区块哈希
2. 对方返回inv消息,列出后续区块哈希
3. 发送getdata消息,请求具体区块
4. 接收block消息,验证并存储
5. 重复步骤1-4,直到同步完成
快速同步优化:
- Headers-First:先同步所有区块头,再下载区块体
- 并行下载:从多个节点同时下载不同区块
- Checkpoint:硬编码已知安全的区块哈希,跳过早期验证
1.6.3 交易传播
新交易的传播过程:
1. 钱包创建并签名交易
2. 发送至连接的节点
3. 节点验证交易(签名、UTXO、手续费)
4. 验证通过后,加入本地内存池(Mempool)
5. 向其他节点广播inv消息
6. 其他节点请求并验证,继续传播
7. 矿工从Mempool选择交易打包进区块
交易手续费估算:
手续费率 = 总手续费 / 交易大小(字节)
矿工优先打包高手续费率的交易
用户可根据网络拥堵情况调整手续费
1.7 密码学基础
1.7.1 SHA-256哈希算法
比特币使用SHA-256作为主要哈希函数,具有以下特性:
- 确定性:相同输入总是产生相同输出
- 快速计算:计算哈希速度快
- 抗碰撞:几乎不可能找到两个输入产生相同哈希
- 单向性:无法从哈希反推原始数据
- 雪崩效应:输入微小变化导致哈希完全不同
应用场景:
- 区块哈希计算
- 交易哈希计算
- Merkle树构建
- 地址生成(RIPEMD-160(SHA-256(PubKey)))
1.7.2 椭圆曲线密码学(ECC)
比特币使用secp256k1椭圆曲线进行数字签名。
密钥生成:
1. 生成私钥:随机256位数字 (k)
2. 计算公钥:P = k × G (G为基点)
3. 私钥范围:1 到 n-1 (n为曲线阶数)
数字签名(ECDSA):
签名过程:
1. 计算消息哈希:z = Hash(message)
2. 生成随机数:k
3. 计算点:(x, y) = k × G
4. 计算 r = x mod n
5. 计算 s = k^(-1) × (z + r × private_key) mod n
6. 签名为 (r, s)
验证过程:
1. 计算消息哈希:z = Hash(message)
2. 计算 u1 = z × s^(-1) mod n
3. 计算 u2 = r × s^(-1) mod n
4. 计算点:(x, y) = u1 × G + u2 × Public_Key
5. 验证 r == x mod n
1.7.3 地址生成
比特币地址由公钥通过多步哈希和编码生成:
1. 计算公钥哈希:
PubKeyHash = RIPEMD-160(SHA-256(PublicKey))
2. 添加版本前缀:
VersionedHash = 0x00 + PubKeyHash (主网)
3. 计算校验和:
Checksum = SHA-256(SHA-256(VersionedHash))[0:4]
4. 拼接并Base58编码:
Address = Base58(VersionedHash + Checksum)
地址类型:
- P2PKH (Pay-to-PubKey-Hash):1开头,传统地址
- P2SH (Pay-to-Script-Hash):3开头,多签地址
- Bech32 (SegWit):bc1开头,原生隔离见证地址
1.8 Go语言实现简化版比特币
下面使用Go语言实现一个简化的比特币系统,包含区块链、交易、挖矿等核心功能。
1.8.1 基础数据结构
package main
import (
"bytes"
"crypto/sha256"
"encoding/gob"
"encoding/hex"
"fmt"
"log"
"math"
"math/big"
"time"
)
// 区块结构
type Block struct {
Timestamp int64 // 时间戳
Transactions []*Transaction // 交易列表
PrevBlockHash []byte // 前一个区块哈希
Hash []byte // 当前区块哈希
Nonce int // 工作量证明随机数
Height int // 区块高度
}
// 区块链结构
type Blockchain struct {
tip []byte // 最新区块的哈希
db *DB // 数据库接口
}
// 交易输入
type TXInput struct {
Txid []byte // 引用的交易ID
Vout int // 引用的输出索引
Signature []byte // 签名
PubKey []byte // 公钥
}
// 交易输出
type TXOutput struct {
Value int // 金额(聪)
PubKeyHash []byte // 公钥哈希
}
// 交易
type Transaction struct {
ID []byte // 交易ID
Vin []TXInput // 输入列表
Vout []TXOutput // 输出列表
}
// UTXO集合
type UTXOSet struct {
Blockchain *Blockchain
}
// 常量定义
const (
targetBits = 16 // 挖矿难度(前导零的位数)
maxNonce = math.MaxInt64 // 最大Nonce值
blockReward = 50 // 区块奖励(BTC)
version = byte(0x00) // 地址版本
addressChecksumLen = 4 // 地址校验和长度
)
1.8.2 工作量证明实现
// 工作量证明结构
type ProofOfWork struct {
block *Block
target *big.Int // 难度目标
}
// 创建工作量证明
func NewProofOfWork(b *Block) *ProofOfWork {
target := big.NewInt(1)
target.Lsh(target, uint(256-targetBits)) // 左移,设置难度
pow := &ProofOfWork{b, target}
return pow
}
// 准备数据进行哈希
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
}
// 执行工作量证明(挖矿)
func (pow *ProofOfWork) Run() (int, []byte) {
var hashInt big.Int
var hash [32]byte
nonce := 0
fmt.Printf("Mining a new block")
for nonce < maxNonce {
data := pow.prepareData(nonce)
hash = sha256.Sum256(data)
if math.Remainder(float64(nonce), 100000) == 0 {
fmt.Printf("\r%x", hash)
}
hashInt.SetBytes(hash[:])
// 检查哈希是否小于目标值
if hashInt.Cmp(pow.target) == -1 {
fmt.Printf("\r%x\n", hash)
break
} else {
nonce++
}
}
fmt.Print("\n\n")
return nonce, hash[:]
}
// 验证工作量证明
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
}
// 计算区块内所有交易的哈希(Merkle Root)
func (b *Block) HashTransactions() []byte {
var transactions [][]byte
for _, tx := range b.Transactions {
transactions = append(transactions, tx.Serialize())
}
mTree := NewMerkleTree(transactions)
return mTree.RootNode.Data
}
1.8.3 Merkle树实现
// Merkle树节点
type MerkleNode struct {
Left *MerkleNode
Right *MerkleNode
Data []byte
}
// Merkle树
type MerkleTree struct {
RootNode *MerkleNode
}
// 创建Merkle节点
func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode {
mNode := MerkleNode{}
if left == nil && right == nil {
// 叶子节点
hash := sha256.Sum256(data)
mNode.Data = hash[:]
} else {
// 中间节点
prevHashes := append(left.Data, right.Data...)
hash := sha256.Sum256(prevHashes)
mNode.Data = hash[:]
}
mNode.Left = left
mNode.Right = right
return &mNode
}
// 创建Merkle树
func NewMerkleTree(data [][]byte) *MerkleTree {
var nodes []MerkleNode
// 如果交易数量为奇数,复制最后一个
if len(data)%2 != 0 {
data = append(data, data[len(data)-1])
}
// 创建叶子节点
for _, datum := range data {
node := NewMerkleNode(nil, nil, datum)
nodes = append(nodes, *node)
}
// 逐层构建树
for len(nodes) > 1 {
var newLevel []MerkleNode
// 如果当前层节点数为奇数,复制最后一个
if len(nodes)%2 != 0 {
nodes = append(nodes, nodes[len(nodes)-1])
}
for j := 0; j < len(nodes); j += 2 {
node := NewMerkleNode(&nodes[j], &nodes[j+1], nil)
newLevel = append(newLevel, *node)
}
nodes = newLevel
}
mTree := MerkleTree{&nodes[0]}
return &mTree
}
1.8.4 交易实现
// 设置交易ID
func (tx *Transaction) Hash() []byte {
var hash [32]byte
txCopy := *tx
txCopy.ID = []byte{}
hash = sha256.Sum256(txCopy.Serialize())
return hash[:]
}
// 序列化交易
func (tx Transaction) Serialize() []byte {
var encoded bytes.Buffer
enc := gob.NewEncoder(&encoded)
err := enc.Encode(tx)
if err != nil {
log.Panic(err)
}
return encoded.Bytes()
}
// 创建Coinbase交易(挖矿奖励)
func NewCoinbaseTX(to, data string) *Transaction {
if data == "" {
data = fmt.Sprintf("Reward to '%s'", to)
}
txin := TXInput{[]byte{}, -1, nil, []byte(data)}
txout := NewTXOutput(blockReward*100000000, to) // 转换为聪
tx := Transaction{nil, []TXInput{txin}, []TXOutput{*txout}}
tx.ID = tx.Hash()
return &tx
}
// 创建UTXO交易
func NewUTXOTransaction(wallet *Wallet, to string, amount int, UTXOSet *UTXOSet) *Transaction {
var inputs []TXInput
var outputs []TXOutput
pubKeyHash := HashPubKey(wallet.PublicKey)
acc, validOutputs := UTXOSet.FindSpendableOutputs(pubKeyHash, 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, nil, wallet.PublicKey}
inputs = append(inputs, input)
}
}
// 构建输出列表
from := fmt.Sprintf("%s", wallet.GetAddress())
outputs = append(outputs, *NewTXOutput(amount, to))
if acc > amount {
// 找零
outputs = append(outputs, *NewTXOutput(acc-amount, from))
}
tx := Transaction{nil, inputs, outputs}
tx.ID = tx.Hash()
UTXOSet.Blockchain.SignTransaction(&tx, wallet.PrivateKey)
return &tx
}
// 检查是否为Coinbase交易
func (tx Transaction) IsCoinbase() bool {
return len(tx.Vin) == 1 && len(tx.Vin[0].Txid) == 0 && tx.Vin[0].Vout == -1
}
// 创建交易输出
func NewTXOutput(value int, address string) *TXOutput {
txo := &TXOutput{value, nil}
txo.Lock([]byte(address))
return txo
}
// 锁定输出到地址
func (out *TXOutput) Lock(address []byte) {
pubKeyHash := Base58Decode(address)
pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-addressChecksumLen]
out.PubKeyHash = pubKeyHash
}
// 检查输出是否被指定公钥哈希锁定
func (out *TXOutput) IsLockedWithKey(pubKeyHash []byte) bool {
return bytes.Compare(out.PubKeyHash, pubKeyHash) == 0
}
// 检查输入是否使用了指定公钥
func (in *TXInput) UsesKey(pubKeyHash []byte) bool {
lockingHash := HashPubKey(in.PubKey)
return bytes.Compare(lockingHash, pubKeyHash) == 0
}
1.8.5 区块链核心实现
// 创建区块
func NewBlock(transactions []*Transaction, prevBlockHash []byte, height int) *Block {
block := &Block{
Timestamp: time.Now().Unix(),
Transactions: transactions,
PrevBlockHash: prevBlockHash,
Hash: []byte{},
Nonce: 0,
Height: height,
}
// 执行工作量证明
pow := NewProofOfWork(block)
nonce, hash := pow.Run()
block.Hash = hash[:]
block.Nonce = nonce
return block
}
// 创建创世区块
func NewGenesisBlock(coinbase *Transaction) *Block {
return NewBlock([]*Transaction{coinbase}, []byte{}, 0)
}
// 创建区块链
func CreateBlockchain(address, nodeID string) *Blockchain {
dbFile := fmt.Sprintf("blockchain_%s.db", nodeID)
if dbExists(dbFile) {
fmt.Println("Blockchain already exists.")
os.Exit(1)
}
var tip []byte
cbtx := NewCoinbaseTX(address, "Genesis Block")
genesis := NewGenesisBlock(cbtx)
db := openDB(dbFile)
err := db.Update(func(txn *Txn) error {
// 存储区块
err := txn.Set(genesis.Hash, genesis.Serialize())
if err != nil {
return err
}
// 存储最新区块哈希
err = txn.Set([]byte("l"), genesis.Hash)
if err != nil {
return err
}
tip = genesis.Hash
return nil
})
if err != nil {
log.Panic(err)
}
bc := Blockchain{tip, db}
return &bc
}
// 添加区块到区块链
func (bc *Blockchain) MineBlock(transactions []*Transaction) *Block {
var lastHash []byte
var lastHeight int
// 验证所有交易
for _, tx := range transactions {
if bc.VerifyTransaction(tx) != true {
log.Panic("ERROR: Invalid transaction")
}
}
// 获取最新区块信息
err := bc.db.View(func(txn *Txn) error {
lastHash = txn.Get([]byte("l"))
blockData := txn.Get(lastHash)
block := DeserializeBlock(blockData)
lastHeight = block.Height
return nil
})
if err != nil {
log.Panic(err)
}
// 创建新区块
newBlock := NewBlock(transactions, lastHash, lastHeight+1)
// 存储新区块
err = bc.db.Update(func(txn *Txn) error {
err := txn.Set(newBlock.Hash, newBlock.Serialize())
if err != nil {
return err
}
err = txn.Set([]byte("l"), newBlock.Hash)
if err != nil {
return err
}
bc.tip = newBlock.Hash
return nil
})
if err != nil {
log.Panic(err)
}
return newBlock
}
// 序列化区块
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()
}
// 反序列化区块
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
}
1.8.6 UTXO集合管理
// UTXO集合中的交易输出
type TXOutputs struct {
Outputs []TXOutput
}
// 查找可花费的输出
func (u UTXOSet) FindSpendableOutputs(pubkeyHash []byte, amount int) (int, map[string][]int) {
unspentOutputs := make(map[string][]int)
accumulated := 0
db := u.Blockchain.db
err := db.View(func(txn *Txn) error {
cursor := txn.NewCursor()
for k, v := cursor.Seek([]byte("utxo-")); bytes.HasPrefix(k, []byte("utxo-")); k, v = cursor.Next() {
txID := hex.EncodeToString(k[5:])
outs := DeserializeOutputs(v)
for outIdx, out := range outs.Outputs {
if out.IsLockedWithKey(pubkeyHash) && accumulated < amount {
accumulated += out.Value
unspentOutputs[txID] = append(unspentOutputs[txID], outIdx)
}
}
}
return nil
})
if err != nil {
log.Panic(err)
}
return accumulated, unspentOutputs
}
// 查找指定地址的UTXO
func (u UTXOSet) FindUTXO(pubKeyHash []byte) []TXOutput {
var UTXOs []TXOutput
db := u.Blockchain.db
err := db.View(func(txn *Txn) error {
cursor := txn.NewCursor()
for k, v := cursor.Seek([]byte("utxo-")); bytes.HasPrefix(k, []byte("utxo-")); k, v = cursor.Next() {
outs := DeserializeOutputs(v)
for _, out := range outs.Outputs {
if out.IsLockedWithKey(pubKeyHash) {
UTXOs = append(UTXOs, out)
}
}
}
return nil
})
if err != nil {
log.Panic(err)
}
return UTXOs
}
// 重建UTXO集合
func (u UTXOSet) Reindex() {
db := u.Blockchain.db
bucketName := []byte("utxo-")
// 删除旧的UTXO集合
err := db.Update(func(txn *Txn) error {
cursor := txn.NewCursor()
for k, _ := cursor.Seek(bucketName); bytes.HasPrefix(k, bucketName); k, _ = cursor.Next() {
err := txn.Delete(k)
if err != nil {
return err
}
}
return nil
})
if err != nil {
log.Panic(err)
}
// 查找所有未花费输出
UTXO := u.Blockchain.FindUTXO()
// 存储新的UTXO集合
err = db.Update(func(txn *Txn) error {
for txID, outs := range UTXO {
key := append(bucketName, []byte(txID)...)
err := txn.Set(key, outs.Serialize())
if err != nil {
return err
}
}
return nil
})
if err != nil {
log.Panic(err)
}
}
// 更新UTXO集合
func (u UTXOSet) Update(block *Block) {
db := u.Blockchain.db
err := db.Update(func(txn *Txn) error {
for _, tx := range block.Transactions {
if tx.IsCoinbase() == false {
// 删除已花费的输出
for _, vin := range tx.Vin {
updatedOuts := TXOutputs{}
key := append([]byte("utxo-"), vin.Txid...)
outsBytes := txn.Get(key)
outs := DeserializeOutputs(outsBytes)
for outIdx, out := range outs.Outputs {
if outIdx != vin.Vout {
updatedOuts.Outputs = append(updatedOuts.Outputs, out)
}
}
if len(updatedOuts.Outputs) == 0 {
err := txn.Delete(key)
if err != nil {
return err
}
} else {
err := txn.Set(key, updatedOuts.Serialize())
if err != nil {
return err
}
}
}
}
// 添加新的输出
newOutputs := TXOutputs{}
for _, out := range tx.Vout {
newOutputs.Outputs = append(newOutputs.Outputs, out)
}
key := append([]byte("utxo-"), tx.ID...)
err := txn.Set(key, newOutputs.Serialize())
if err != nil {
return err
}
}
return nil
})
if err != nil {
log.Panic(err)
}
}
// 序列化输出
func (outs TXOutputs) Serialize() []byte {
var buff bytes.Buffer
enc := gob.NewEncoder(&buff)
err := enc.Encode(outs)
if err != nil {
log.Panic(err)
}
return buff.Bytes()
}
// 反序列化输出
func DeserializeOutputs(data []byte) TXOutputs {
var outputs TXOutputs
dec := gob.NewDecoder(bytes.NewReader(data))
err := dec.Decode(&outputs)
if err != nil {
log.Panic(err)
}
return outputs
}
1.8.7 数字签名实现
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/hex"
"math/big"
)
// 钱包结构
type Wallet struct {
PrivateKey ecdsa.PrivateKey
PublicKey []byte
}
// 创建钱包
func NewWallet() *Wallet {
private, public := newKeyPair()
wallet := Wallet{private, public}
return &wallet
}
// 生成密钥对
func newKeyPair() (ecdsa.PrivateKey, []byte) {
curve := elliptic.P256()
private, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
log.Panic(err)
}
pubKey := append(private.PublicKey.X.Bytes(), private.PublicKey.Y.Bytes()...)
return *private, pubKey
}
// 获取地址
func (w Wallet) GetAddress() []byte {
pubKeyHash := HashPubKey(w.PublicKey)
versionedPayload := append([]byte{version}, pubKeyHash...)
checksum := checksum(versionedPayload)
fullPayload := append(versionedPayload, checksum...)
address := Base58Encode(fullPayload)
return address
}
// 公钥哈希
func HashPubKey(pubKey []byte) []byte {
publicSHA256 := sha256.Sum256(pubKey)
RIPEMD160Hasher := ripemd160.New()
_, err := RIPEMD160Hasher.Write(publicSHA256[:])
if err != nil {
log.Panic(err)
}
publicRIPEMD160 := RIPEMD160Hasher.Sum(nil)
return publicRIPEMD160
}
// 计算校验和
func checksum(payload []byte) []byte {
firstSHA := sha256.Sum256(payload)
secondSHA := sha256.Sum256(firstSHA[:])
return secondSHA[:addressChecksumLen]
}
// 签名交易
func (bc *Blockchain) SignTransaction(tx *Transaction, privKey ecdsa.PrivateKey) {
prevTXs := make(map[string]Transaction)
for _, vin := range tx.Vin {
prevTX, err := bc.FindTransaction(vin.Txid)
if err != nil {
log.Panic(err)
}
prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX
}
tx.Sign(privKey, prevTXs)
}
// 交易签名
func (tx *Transaction) Sign(privKey ecdsa.PrivateKey, prevTXs map[string]Transaction) {
if tx.IsCoinbase() {
return
}
for _, vin := range tx.Vin {
if prevTXs[hex.EncodeToString(vin.Txid)].ID == nil {
log.Panic("ERROR: Previous transaction is not correct")
}
}
txCopy := tx.TrimmedCopy()
for inID, vin := range txCopy.Vin {
prevTx := prevTXs[hex.EncodeToString(vin.Txid)]
txCopy.Vin[inID].Signature = nil
txCopy.Vin[inID].PubKey = prevTx.Vout[vin.Vout].PubKeyHash
dataToSign := fmt.Sprintf("%x\n", txCopy)
r, s, err := ecdsa.Sign(rand.Reader, &privKey, []byte(dataToSign))
if err != nil {
log.Panic(err)
}
signature := append(r.Bytes(), s.Bytes()...)
tx.Vin[inID].Signature = signature
txCopy.Vin[inID].PubKey = nil
}
}
// 验证交易
func (bc *Blockchain) VerifyTransaction(tx *Transaction) bool {
if tx.IsCoinbase() {
return true
}
prevTXs := make(map[string]Transaction)
for _, vin := range tx.Vin {
prevTX, err := bc.FindTransaction(vin.Txid)
if err != nil {
log.Panic(err)
}
prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX
}
return tx.Verify(prevTXs)
}
// 交易验证
func (tx *Transaction) Verify(prevTXs map[string]Transaction) bool {
if tx.IsCoinbase() {
return true
}
for _, vin := range tx.Vin {
if prevTXs[hex.EncodeToString(vin.Txid)].ID == nil {
log.Panic("ERROR: Previous transaction is not correct")
}
}
txCopy := tx.TrimmedCopy()
curve := elliptic.P256()
for inID, vin := range tx.Vin {
prevTx := prevTXs[hex.EncodeToString(vin.Txid)]
txCopy.Vin[inID].Signature = nil
txCopy.Vin[inID].PubKey = prevTx.Vout[vin.Vout].PubKeyHash
r := big.Int{}
s := big.Int{}
sigLen := len(vin.Signature)
r.SetBytes(vin.Signature[:(sigLen / 2)])
s.SetBytes(vin.Signature[(sigLen / 2):])
x := big.Int{}
y := big.Int{}
keyLen := len(vin.PubKey)
x.SetBytes(vin.PubKey[:(keyLen / 2)])
y.SetBytes(vin.PubKey[(keyLen / 2):])
dataToVerify := fmt.Sprintf("%x\n", txCopy)
rawPubKey := ecdsa.PublicKey{Curve: curve, X: &x, Y: &y}
if ecdsa.Verify(&rawPubKey, []byte(dataToVerify), &r, &s) == false {
return false
}
txCopy.Vin[inID].PubKey = nil
}
return true
}
// 创建交易副本(用于签名)
func (tx *Transaction) TrimmedCopy() Transaction {
var inputs []TXInput
var outputs []TXOutput
for _, vin := range tx.Vin {
inputs = append(inputs, TXInput{vin.Txid, vin.Vout, nil, nil})
}
for _, vout := range tx.Vout {
outputs = append(outputs, TXOutput{vout.Value, vout.PubKeyHash})
}
txCopy := Transaction{tx.ID, inputs, outputs}
return txCopy
}
1.8.8 工具函数
import (
"bytes"
"encoding/binary"
)
// 整数转字节
func IntToHex(num int64) []byte {
buff := new(bytes.Buffer)
err := binary.Write(buff, binary.BigEndian, num)
if err != nil {
log.Panic(err)
}
return buff.Bytes()
}
// Base58编码字符集
var b58Alphabet = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
// Base58编码
func Base58Encode(input []byte) []byte {
var result []byte
x := big.NewInt(0).SetBytes(input)
base := big.NewInt(int64(len(b58Alphabet)))
zero := big.NewInt(0)
mod := &big.Int{}
for x.Cmp(zero) != 0 {
x.DivMod(x, base, mod)
result = append(result, b58Alphabet[mod.Int64()])
}
// 反转
ReverseBytes(result)
// 添加前导零
for b := range input {
if b == 0x00 {
result = append([]byte{b58Alphabet[0]}, result...)
} else {
break
}
}
return result
}
// Base58解码
func Base58Decode(input []byte) []byte {
result := big.NewInt(0)
for _, b := range input {
charIndex := bytes.IndexByte(b58Alphabet, b)
result.Mul(result, big.NewInt(58))
result.Add(result, big.NewInt(int64(charIndex)))
}
decoded := result.Bytes()
// 添加前导零
for b := range input {
if b == b58Alphabet[0] {
decoded = append([]byte{0x00}, decoded...)
} else {
break
}
}
return decoded
}
// 反转字节数组
func ReverseBytes(data []byte) {
for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 {
data[i], data[j] = data[j], data[i]
}
}
// 验证地址
func ValidateAddress(address string) bool {
pubKeyHash := Base58Decode([]byte(address))
actualChecksum := pubKeyHash[len(pubKeyHash)-addressChecksumLen:]
version := pubKeyHash[0]
pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-addressChecksumLen]
targetChecksum := checksum(append([]byte{version}, pubKeyHash...))
return bytes.Compare(actualChecksum, targetChecksum) == 0
}
1.8.9 CLI命令行接口
import (
"flag"
"fmt"
"os"
)
// CLI结构
type CLI struct{}
// 打印使用说明
func (cli *CLI) printUsage() {
fmt.Println("Usage:")
fmt.Println(" createblockchain -address ADDRESS - 创建区块链")
fmt.Println(" createwallet - 创建新钱包")
fmt.Println(" getbalance -address ADDRESS - 获取余额")
fmt.Println(" listaddresses - 列出所有地址")
fmt.Println(" printchain - 打印区块链")
fmt.Println(" send -from FROM -to TO -amount AMOUNT - 发送交易")
fmt.Println(" reindexutxo - 重建UTXO集合")
}
// 验证参数
func (cli *CLI) validateArgs() {
if len(os.Args) < 2 {
cli.printUsage()
os.Exit(1)
}
}
// 运行CLI
func (cli *CLI) Run() {
cli.validateArgs()
nodeID := os.Getenv("NODE_ID")
if nodeID == "" {
fmt.Printf("NODE_ID env. var is not set!")
os.Exit(1)
}
getBalanceCmd := flag.NewFlagSet("getbalance", flag.ExitOnError)
createBlockchainCmd := flag.NewFlagSet("createblockchain", flag.ExitOnError)
createWalletCmd := flag.NewFlagSet("createwallet", flag.ExitOnError)
listAddressesCmd := flag.NewFlagSet("listaddresses", flag.ExitOnError)
printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError)
reindexUTXOCmd := flag.NewFlagSet("reindexutxo", flag.ExitOnError)
sendCmd := flag.NewFlagSet("send", flag.ExitOnError)
getBalanceAddress := getBalanceCmd.String("address", "", "查询余额的地址")
createBlockchainAddress := createBlockchainCmd.String("address", "", "接收挖矿奖励的地址")
sendFrom := sendCmd.String("from", "", "源地址")
sendTo := sendCmd.String("to", "", "目标地址")
sendAmount := sendCmd.Int("amount", 0, "发送金额")
switch os.Args[1] {
case "getbalance":
err := getBalanceCmd.Parse(os.Args[2:])
if err != nil {
log.Panic(err)
}
case "createblockchain":
err := createBlockchainCmd.Parse(os.Args[2:])
if err != nil {
log.Panic(err)
}
case "createwallet":
err := createWalletCmd.Parse(os.Args[2:])
if err != nil {
log.Panic(err)
}
case "listaddresses":
err := listAddressesCmd.Parse(os.Args[2:])
if err != nil {
log.Panic(err)
}
case "printchain":
err := printChainCmd.Parse(os.Args[2:])
if err != nil {
log.Panic(err)
}
case "reindexutxo":
err := reindexUTXOCmd.Parse(os.Args[2:])
if err != nil {
log.Panic(err)
}
case "send":
err := sendCmd.Parse(os.Args[2:])
if err != nil {
log.Panic(err)
}
default:
cli.printUsage()
os.Exit(1)
}
if getBalanceCmd.Parsed() {
if *getBalanceAddress == "" {
getBalanceCmd.Usage()
os.Exit(1)
}
cli.getBalance(*getBalanceAddress, nodeID)
}
if createBlockchainCmd.Parsed() {
if *createBlockchainAddress == "" {
createBlockchainCmd.Usage()
os.Exit(1)
}
cli.createBlockchain(*createBlockchainAddress, nodeID)
}
if createWalletCmd.Parsed() {
cli.createWallet(nodeID)
}
if listAddressesCmd.Parsed() {
cli.listAddresses(nodeID)
}
if printChainCmd.Parsed() {
cli.printChain(nodeID)
}
if reindexUTXOCmd.Parsed() {
cli.reindexUTXO(nodeID)
}
if sendCmd.Parsed() {
if *sendFrom == "" || *sendTo == "" || *sendAmount <= 0 {
sendCmd.Usage()
os.Exit(1)
}
cli.send(*sendFrom, *sendTo, *sendAmount, nodeID)
}
}
// 主函数
func main() {
defer os.Exit(0)
cli := CLI{}
cli.Run()
}
1.9 比特币的安全性分析
1.9.1 51%攻击
当攻击者控制超过50%的全网算力时,可以进行以下攻击:
可能的攻击:
- 双重支付:在私有链上重写交易历史
- 阻止交易确认:拒绝打包特定交易
- 阻止其他矿工出块:始终挖出更长的链
无法做到:
- 凭空创造比特币
- 盗取他人的比特币(需要私钥)
- 修改已深度确认的区块(成本极高)
防御措施:
- 等待足够的确认数(通常6个确认)
- 分散化挖矿(避免矿池过于集中)
- 监控算力分布
1.9.2 双重支付攻击
攻击者试图将同一笔UTXO花费两次:
攻击场景:
1. 攻击者创建交易Tx1:支付给商家
2. 商家看到交易,发货(0确认接受)
3. 攻击者同时创建交易Tx2:将相同UTXO转回自己
4. 攻击者利用算力使Tx2所在链成为主链
5. Tx1被废弃,攻击者既获得商品又保留比特币
防御措施:
- 等待多个确认(推荐6个确认)
- 大额交易等待更多确认
- 0确认交易仅用于小额支付
1.9.3 私钥安全
私钥是控制比特币的唯一凭证,丢失私钥意味着永久失去比特币。
最佳实践:
- 冷钱包存储:离线生成和保存私钥
- 硬件钱包:专用硬件设备管理私钥
- 多重签名:要求多个私钥共同签名
- 助记词备份:使用BIP39标准助记词
- 定期备份:多地备份,防止单点故障
多重签名示例:
2-of-3多签地址:
- 需要3个密钥中的任意2个才能花费
- 即使丢失1个密钥,仍可使用剩余2个
- 适合企业账户和大额存储
1.10 比特币的扩展性问题
1.10.1 区块容量限制
比特币区块大小限制为1MB(SegWit后约4MB),导致:
- 每秒处理约7笔交易(TPS)
- 网络拥堵时手续费飙升
- 交易确认时间延长
对比传统支付系统:
- Visa: 约24,000 TPS
- 支付宝: 超过100,000 TPS
1.10.2 扩容方案
链上扩容(On-chain):
增大区块:提高区块大小限制
- 优点:直接提升TPS
- 缺点:增加节点存储和带宽压力
隔离见证(SegWit):将签名数据分离
- 优点:变相增加区块容量,修复交易延展性
- 缺点:需要软分叉升级
链下扩容(Off-chain):
闪电网络(Lightning Network):
- 建立双向支付通道
- 链下进行大量微交易
- 仅在开启和关闭通道时上链
- 理论TPS可达百万级
侧链(Sidechain):
- 独立的区块链,与主链双向锚定
- 可实验新特性,不影响主链
- 例如:Liquid Network
1.10.3 区块链膨胀问题
完整区块链大小已超过500GB(截至2024年),带来以下问题:
- 全节点同步时间长
- 存储成本增加
- 带宽要求高
解决方案:
- 轻节点:仅存储区块头,使用SPV验证
- 修剪模式:删除旧UTXO,仅保留最近区块
- UTXO承诺:将UTXO集合哈希写入区块,加速同步
1.11 比特币的经济模型
1.11.1 通缩属性
比特币总量恒定2100万枚,且不断有私钥丢失导致永久损失,呈现通缩特性。
通缩影响:
- 长期持有激励(HODL文化)
- 作为价值存储(数字黄金)
- 不鼓励日常消费支付
1.11.2 手续费市场
随着区块奖励减少,手续费将成为矿工的主要收入:
当前(2024年):
区块奖励: 6.25 BTC
平均手续费: 0.1-0.5 BTC
预计2140年:
区块奖励: 0 BTC
手续费: 必须足够激励矿工维持网络安全
手续费市场动态:
- 用户竞价:高手续费交易优先打包
- 动态调整:根据网络拥堵程度
- RBF(Replace-By-Fee):用更高手续费替换未确认交易
1.11.3 挖矿经济学
挖矿是否盈利取决于:
收益 = 区块奖励 + 交易手续费
成本 = 电费 + 硬件成本 + 维护费用
盈利条件:收益 > 成本
影响因素:
- 比特币价格
- 网络难度
- 电费成本
- 矿机效率(算力/功耗比)
矿业集中化问题:
- 规模经济导致大型矿场优势明显
- 地理位置(廉价电力地区)影响显著
- 矿池的兴起降低个人挖矿门槛
1.12 比特币生态系统
1.12.1 比特币改进提案(BIP)
BIP是比特币协议改进的标准化流程:
重要BIP:
- BIP-32:分层确定性钱包(HD Wallet)
- BIP-39:助记词标准
- BIP-44:多币种HD钱包路径
- BIP-141:隔离见证
- BIP-173:Bech32地址格式
- BIP-340/341/342:Taproot升级
1.12.2 比特币应用层
第二层协议:
- 染色币(Colored Coins):在比特币上发行资产
- Omni Layer:智能合约平台(USDT最初在此发行)
- RSK:兼容以太坊的侧链
- Stacks:智能合约层,使用STX代币
其他应用:
- 时间戳服务:利用区块链不可篡改性
- 公证服务:文档存证
- 去中心化身份:DID系统
1.13 本章总结
本章详细介绍了比特币的核心原理和技术实现:
- UTXO模型:比特币独特的账本模型,提供并发性能和隐私保护
- 工作量证明:通过哈希计算保证网络安全和共识
- 区块结构:链式存储和Merkle树实现高效验证
- 密码学:ECC签名、SHA-256哈希确保交易安全
- 共识机制:最长链原则解决分布式一致性问题
- 经济模型:固定供应和减半机制创造稀缺性
通过Go语言实现的简化版比特币,展示了核心组件的工作原理:
- 区块链数据结构和存储
- 工作量证明挖矿算法
- UTXO交易模型
- 数字签名和验证
- Merkle树构建
比特币的创新意义:
比特币不仅是第一个成功的加密货币,更重要的是证明了去中心化数字货币的可行性。它解决了困扰密码学家数十年的拜占庭将军问题,在无需信任的环境中实现了价值转移。
局限性:
尽管比特币具有开创性,但也存在明显的局限:
- 扩展性受限(TPS低)
- 能耗高(PoW挖矿)
- 脚本功能有限(不支持复杂智能合约)
- 交易确认慢(平均10分钟)
这些局限促使了新一代区块链的诞生,其中最具代表性的就是以太坊。下一章将详细讲解以太坊的架构和核心概念,了解它如何在比特币的基础上实现更强大的可编程性和灵活性。