区块链共识机制源码中,节点如何通过算法达成数据一致?
摘要:
由于区块链项目众多,我们选择最经典、最广为人知的 比特币 和 以太坊 作为分析对象,比特币是工作量证明的鼻祖,而以太坊则从 PoW 演进到了权益证明,代表了两种主流的共识思想,我们... 由于区块链项目众多,我们选择最经典、最广为人知的 比特币 和 以太坊 作为分析对象,比特币是工作量证明的鼻祖,而以太坊则从 PoW 演进到了权益证明,代表了两种主流的共识思想。
我们将从以下几个方面展开:
- 共识机制概览:理解不同共识的哲学和目标。
- 比特币 PoW 共识源码分析:深入
bitcoind的核心,看一个新区块是如何被“创造”出来的。 - 以太坊 PoS 共识源码分析:以最新的
Cancun升级为例,分析如何从 PoW 过渡到 PoS,以及 PoS 是如何运作的。 - 总结与对比:比较两种共识的优缺点和实现上的核心差异。
共识机制概览
共识机制的目标是确保网络的安全性和一致性,主要分为以下几类:
| 共识类型 | 核心思想 | 优点 | 缺点 | 代表项目 |
|---|---|---|---|---|
| 工作量证明 | 通过解决复杂的数学难题(哈希碰撞)来争夺记账权,算力越高,赢得记账权的概率越大。 | 去中心化程度高,安全性经过实战检验(比特币已运行十余年)。 | 能源消耗巨大,交易确认慢(可扩展性差),存在“51%攻击”理论风险。 | 比特币, 莱特币 |
| 权益证明 | 根据节点质押的代币数量和时间(“权益”)来分配记账权,质押越多,获得奖励的概率越高。 | 能耗极低,交易速度快,理论上可扩展性更好。 | “无利害关系”问题,初始分配不均可能导致中心化,安全模型相对较新。 | 以太坊 (2.0), 卡尔达诺, Solana |
| 委托权益证明 | DPoS 是 PoS 的变种,代币持有者投票选举少量(如 101 个)节点(见证人/超级节点)来负责出块和验证。 | 效率极高,交易秒级确认。 | 去中心化程度相对较低,容易出现“贿选”和节点作恶问题。 | EOS, TRON, BitShares |
| 实用拜占庭容错 | 通过多轮投票和消息传递,在节点间达成共识,要求至少 2/3 的节点是诚实的。 | 不依赖代币或算力,理论性能高。 | 在大规模节点网络中通信开销巨大,扩展性差,不适合公链。 | Hyperledger Fabric (联盟链), EOS 早期共识 |
比特币 PoW 共识源码分析
比特币的共识是“计算+经济”的结合,其核心是“最长有效链”原则,而“有效”的标志就是包含了满足特定难度条件的 PoW。
核心概念与源码模块
比特币的核心代码主要在 src/ 目录下,与共识最相关的模块是:
validation.cpp/h: 包含了验证区块和交易是否有效的核心逻辑。CheckBlock()和CheckBlockHeader()是入口函数。pow.cpp/h: 专门负责工作量证明的计算和验证。chain.cpp/h: 管理区块链的数据结构,如CChain和CBlockIndex,用于维护“最长链”。consensus/consensus.h: 定义了共识相关的常量和规则,如MAX_BLOCK_SIZE,nSubsidyHalvingInterval等。
共识流程源码剖析
我们以一个节点收到一个新区块后的处理流程为例,来分析 PoW 共识的实现。
Step 1: 接收新区块
当一个节点从网络接收到一个新区块(通过 inv 消息获取,然后通过 getdata 请求完整数据),它会首先调用 ProcessNewBlock() 函数(在 validation.cpp 中)。
// validation.cpp
bool ProcessNewBlock(const CChainParams& chainparams, const CNode* pfrom, CBlock* pblock, bool* fNewBlock) {
// ... 省略部分代码 ...
// 1. 验证区块头的基本结构
if (!CheckBlockHeader(block, chainparams.GetConsensus(), GetSpendHeight(pindex))) {
return false;
}
// 2. 验证整个区块(包括交易列表、默克尔树根等)
if (!CheckBlock(block, state, chainparams.GetConsensus(), fScriptChecks, GetSpendHeight(pindex))) {
return error("%s: CheckBlock failed", __func__);
return false;
}
// ...
}
Step 2: 验证 PoW (The Core of Consensus)
CheckBlockHeader() 是验证 PoW 的关键,它检查两件事:
- 区块头的
nBits字段是否在有效范围内。 - 区块头的哈希值是否小于当前网络目标值。
// pow.cpp
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& params) {
// 1. 检查 nBits 是否代表一个有效的难度目标
// nBits 是一个紧凑格式,需要转换为一个大整数
bool fNegative;
bool fOverflow;
arith_uint256 bnTarget;
bnTarget.SetCompact(nBits, &fNegative, &fOverflow);
// 检查是否超出最大/最小限制
if (fNegative || bnTarget == 0 || fOverflow || bnTarget > UintToArith256(params.powLimit)) {
return false;
}
// 2. 检查哈希值是否满足难度要求
// 这就是 PoW 的核心:区块头的哈希必须 <= 目标值
if (UintToArith256(hash) > bnTarget) {
return false;
}
return true;
}
代码解读:
nBits: 这是矿工在挖矿时不断调整的值,它编码了当前网络的目标难度,一个更小的nBits代表更高的难度。bnTarget.SetCompact(nBits): 将紧凑格式的nBits转换为一个 256 位的整数bnTarget,这个bnTarget目标值”。UintToArith256(hash) > bnTarget: 这是 PoW 的“题解”验证,矿工需要找到一个随机数(Nonce),使得SHA256(SHA256(区块头 + Nonce))的结果小于等于bnTarget,节点收到区块后,只需用区块头里的Nonce重新计算一次哈希,然后和bnTarget比较即可验证,这个过程非常快。
Step 3: 链的选择与重组
如果新区块有效,节点会尝试将其连接到自己的主链上,这涉及到 ActivateBestChain() 函数(在 validation.cpp 中)。
// validation.cpp
bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlockHeader* pblock, bool fJustCheck) {
// ...
// 1. 检查新接收的链是否比当前主链“更长”(总工作量更大)
if (!pindexMostWork || chainActive.Tip()->nChainWork < pindexMostWork->nChainWork) {
// ...
// 2. 如果是,则进行“重新组织”(Reorg)
// 将旧主链回滚,然后将新链连接上
if (!Reorg(state, chainparams, pindexMostWork, nConnectSize, fScriptChecks)) {
// ...
}
}
// ...
}
代码解读:
chainActive.Tip()->nChainWork < pindexMostWork->nChainWork: 这是“最长链”原则的体现,这里的“长”不是指区块数量,而是指总工作量,每个区块头里都存储了从创世块到该区块的累计工作量nChainWork,节点选择累计工作量最大的链作为主链。Reorg(): 当一条新的、工作量更大的链出现时,节点需要执行“重组”,它会断开当前主链的末端,然后沿着新链的分支重新连接区块,这确保了网络最终会收敛到唯一的一条“工作量最大链”上。
挖矿过程(PoW 的生成)
虽然分析的是“验证”端,但了解“生成”端有助于理解共识的全貌,挖矿的核心在 miner.cpp 中的 BitcoinsMiner() 函数,它会不断构建一个候选区块头,然后暴力尝试不同的 Nonce 值,直到找到一个满足 CheckProofOfWork() 的解。
// miner.cpp (简化逻辑)
void BitcoinsMiner(const CChainParams& chainparams) {
// ...
while (true) {
// 1. 构建候选区块
CBlock block;
// ... (填充交易、默克尔根等)
// 2. 循环尝试不同的 Nonce
block.nNonce = 0;
while (true) {
// 3. 计算哈希
uint256 hash = block.GetHash();
// 4. 检查是否满足难度要求
if (CheckProofOfWork(hash, block.nBits, chainparams.GetConsensus())) {
// 找到解!广播出去
ProcessNewBlock(chainparams, nullptr, &block, nullptr);
break;
}
// 5. Nonce 自增,继续尝试
++block.nNonce;
if ((block.nNonce & 0xffff) == 0) {
// ... (检查是否被停止或新交易加入)
}
}
}
}
以太坊 PoS 共识源码分析
以太坊的“合并”(The Merge)后,共识机制从 PoW 变为 PoS,其实现核心是 Casper FFG (Liveness + Finality) + LMD GHOST (Chain Re-org),它由一个名为 beacon chain 的独立链来协调所有分片链(包括主网)。
核心概念与源码模块
以太坊的 Go 客户端 Prysm 和 Rust 客户端 Lodestar 是分析 PoS 共识的绝佳选择,我们以 Prysm 为例,其代码结构清晰。
beacon-chain/:beacon chain的核心实现。core/: 包含核心数据结构,如BeaconState,BeaconBlock。consensus/: 共识算法的具体实现。fork_choice/: 实现了 LMD GHOST 算法,用于选择哪条链是“canonical chain”。state/: 实现了 Casper FFG 的状态转换函数,如ProcessAttestation,ProcessBlock。
operations/: 处理各种类型的“操作”,如Deposit(存入 ETH 成为验证者),Attestation( attestations, 即投票),VoluntaryExit(自愿退出)。
共识流程源码剖析
PoS 的共识流程与 PoW 完全不同,它不再依赖“挖矿”,而是依赖验证者的“投票”。
Step 1: 成为验证者
用户需要将至少 32 ETH 发送到一个指定的合约地址,这个过程称为“存入”(Deposit),一旦存入并被 beacon chain 确认,用户就成为了一个验证者。
Step 2: 分配职责
以太坊的共识时间被划分为时隙 和纪元。
- Slot (时隙): 12 秒一个。
- Epoch (纪元): 32 个时隙一个(约 6.4 分钟)。
在每个纪元开始时,beacon chain 会根据 RANDAO 算法随机地将验证者分配到不同的委员会 中。
// beacon-chain/core/assignments.go (简化)
func (s *BeaconState) CommitteeAtSlot(slot types.Slot) [][]ValidatorIndex {
// ...
// 根据验证者余额和 RANDAO 值,为每个 slot 生成一个委员会
// 每个委员会负责验证一个特定的 slot 产生的区块
// ...
}
Step 3: 提议与投票
在一个时隙内,只有一个验证者被选为提议者,负责创建并广播一个新区块,被分配到该时隙的委员会中的其他验证者,负责对该区块及其父区块进行“投票”(称为 Attestation)。
A. 提议者创建区块
BeaconBlock 结构体中包含了大量的 Attestations,这些就是其他验证者提前提交的投票。
// beacon-chain/types/block.go
type BeaconBlock struct {
Slot Slot
ProposerIndex ValidatorIndex
ParentRoot Root
StateRoot Root
Body BeaconBlockBody
// ...
}
type BeaconBlockBody struct {
RANDAOReveal []byte
Eth1Data *Eth1Data
Graffiti []byte
ProposerSlashings []*ProposerSlashing
AttesterSlashings []*AttesterSlashing
Attestations []*Attestation // 关键:包含来自委员会的投票
Deposits []*Deposit
VoluntaryExits []*VoluntaryExit
// ...
}
B. 验证者投票
一个验证者的投票 (Attestation) 包含两部分信息:
- 对哪个检查点 进行投票(Casper FFG 的 Liveness 部分)。
- 认为哪个区块 是当前链的头部(LMD GHOST 的链选择部分)。
// beacon-chain/types/attestation.go
type Attestation struct {
AggregationBits Bitlist // 委员员中哪些验证者参与了本次投票
Data *AttestationData
Signature []byte
}
type AttestationData struct {
Slot Slot
Index CommitteeIndex
BeaconBlockRoot Root // 投票的区块哈希
Source Checkpoint // 投票的源检查点
Target Checkpoint // 投票的目标检查点
}
Step 4: 共识达成 (Fork Choice + Finality)
当节点收到一个新区块时,它会运行两个核心算法:
LMD GHOST (链选择算法)
这个算法决定哪条链是“最重的”,它的核心思想是:一个区块的权重,等于所有投给它(及其祖先)的有效投票的总和。
// beacon-chain/consensus/fork_choice/lmd_ghost.go (简化逻辑)
func (f *ForkChoice) onBlock(block *pb.BeaconBlock, atts []*pb.Attestation) {
// ...
// 1. 将新区块添加到 DAG (Directed Acyclic Graph) 中
// 2. 遍历新区块收到的所有投票 (atts)
for _, att := range atts {
// 3. 投票指向的区块权重 +1
// 4. 该区块所有祖先的权重也 +1
f.updateVoteWeights(att.Data.BeaconBlockRoot, att)
}
// 5. 从当前头部开始,沿着权重最高的子区块向上追溯,找到新的头部
f.findHead()
}
Casper FFG (最终确定性算法)
FFG 保证了区块一旦被“最终确定”,就不可逆转,它通过两个超时阈值来实现:
- Justification (合理性): 当一个检查点收到了超过 2/3 的验证者投票时,它就被认为是“合理的”。
- Finalization (最终确定性): 当一个检查点
C被合理化,并且下一个检查点C+1也被合理化时,检查点C就被“最终确定”了。
// beacon-chain/consensus/state/process.go (简化逻辑)
func (p *StateProcessor) ProcessAttestation(state *pb.BeaconState, attestation *pb.Attestation) error {
// ...
// 1. 验证投票的有效性
// 2. 更新验证者对检查点的投票记录
// 3. 检查是否有新的检查点可以被合理化
if p.hasSupermajority(state, attestation.Data.Target) {
// 标记检查点为合理
state.CurrentJustifiedCheckpoint = attestation.Data.Target
}
// 4. 检查是否有新的检查点可以被最终确定
if p.isFinalizable(state) {
state.FinalizedCheckpoint = state.CurrentJustifiedCheckpoint
}
// ...
}
总结与对比
| 特性 | 比特币 PoW | 以太坊 PoS |
|---|---|---|
| 核心目标 | 防止女巫攻击,保证安全 | 能效,可扩展性,最终确定性 |
| 参与者 | 矿工 (拥有算力) | 验证者 (质押 ETH) |
| 记账权竞争 | 通过哈希算力竞争 | 通过随机分配的职责(提议/投票) |
| 安全性来源 | 算力成本高昂,攻击者需掌握 >51% 算力才能作恶。 | 质押金被罚没(Slashing),作弊成本高昂。 |
| 出块时间 | ~10 分钟 | ~12 秒 |
| 能源消耗 | 极高 (堪比一些国家) | 极低 (仅运行服务器的能耗) |
| 最终确定性 | 概率性,区块越深,越安全,但理论上仍可能被更长的链替代。 | 确定性,一旦被 Finalized,绝对不可逆。 |
| 源码核心 | validation.cpp, pow.cpp |
beacon-chain/consensus/ |
| 关键函数 | CheckBlockHeader(), ActivateBestChain() |
onBlock(), ProcessAttestation() |
源码层面的核心差异
-
验证逻辑:
- PoW: 验证是计算密集型的,但验证本身非常快(一次哈希计算),核心是验证一个数学难题的解。
- PoS: 验证是状态和逻辑密集型的,需要验证大量的签名、检查投票有效性、更新状态、运行复杂的 FFG 和 GHOST 算法。
-
链选择:
- PoW: 链选择是简单的“最长链”,比较
nChainWork即可。 - PoS: 链选择是复杂的“最重链”,需要聚合所有投票来计算每个区块的权重,实现更灵活的链重组。
- PoW: 链选择是简单的“最长链”,比较
-
状态管理:
- PoW: 比特币的共识状态相对简单,主要是 UTXO 集合和区块链本身。
- PoS: 以太坊的
beacon chain维护了一个极其复杂的状态,包括每个验证者的余额、退出状态、投票历史等,共识算法直接作用于这个巨大的状态机上。
通过以上分析,我们可以清晰地看到,从比特币到以太坊,共识机制的设计哲学发生了根本性的转变,源码的实现也因此呈现出截然不同的面貌,理解这些源码,是深入掌握区块链底层原理的关键一步。
作者:咔咔本文地址:https://www.jits.cn/content/23151.html发布于 01-04
文章转载或复制请以超链接形式并注明出处杰思科技・AI 股讯


还没有评论,来说两句吧...