Ethereum POS共识
1、什么是权益证明
权益证明是一种机制或者说是一种规则,其用于规范及协调参与网络的验证节点,这些验证节点依照指定规则有条不紊的收集交易、打包区块、验证区块,并维护底层区块链的安全、高效的运行。为了制约验证节点的欺诈行为,该规则要求申请成为验证节点需要抵押32个ETH到指定的以太坊智能合约,当发现有节点试图欺骗网络或存在一些不诚实行为时(例如,在应该发送一个区块时提议多个区块,或者发送冲突的认证),视情节轻重而不同程度的销毁这些抵押的ETH。
源码: https://github.com/prysmaticlabs/prysm
2.、节点客户端
2.1、节点客户端
以太坊中的交易是通过验证者进行处理的,验证者需要运行三种不同功能的客户端来完成相应工作,它们分别是:执行客户端、共识客户端和验证客户端。
2.2、通讯协议
gRPC
2.2、通讯流程图
3、如何选择出块节点
在每个时隙以伪随机的方式选择一个验证者来提议区块。
在区块链中没有真正的随机性,因为如果每个节点生成真正的随机数,它们就无法达成共识。 相反,目的是使验证者的选择过程不可预测。 以太坊使用一种叫做 RANDAO 的算法来实现随机性,它将来自区块提议者的一个哈希与一个随每个区块更新的种子混合起来。 这个值用于所有验证者中选择一个特定的验证者。 验证者的选择提前两个时段固定,这是为了防范某些类型的种子操纵。
虽然验证者在每个时隙中都会向 RANDAO 添加内容,但全局 RANDAO 值仅在每个周期更新一次。 为了计算下一个区块提议者的索引,RANDAO 值在每个时隙与时隙号混合,以给出唯一的值。 单独验证者被选中的概率并不是简单的 1/N(其中 N = 活跃验证者的总数)。 相反,它是按照每个验证者的有效以太币余额进行加权的。 最大有效余额为 32 个以太币(这意味着 balance < 32 ETH 会产生低于 balance == 32 ETH 的权重,而 balance > 32 ETH 不会产生高于 balance == 32 ETH 的权重)。
每个时隙中只选择一个区块提议者。 在正常情况下,单个区块生产者会在他们专属的时隙中创建并发布单个区块。 为同一个时隙创建两个区块是一种可罚没的行为。
4、交易打包过程
在每个“时隙”(见下文)会从验证节点中随机选一个作为提议者来来打包交易。交易详细处理流程如下:
4.1、签名交易
用户通过钱包签名一笔交易,并在交易中设置gas价格(一般钱包会根据网络情况给出默认价格,高价格会鼓励矿工优先打包交易,但是EIP-1559对gas做了调整),然后将该交易发送到以太坊网。
// SignTx signs the transaction using the given signer and private key. func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, error) { h := s.Hash(tx) sig, err := crypto.Sign(h[:], prv) if err != nil { return nil, err } return tx.WithSignature(s, sig) }
4.2、验证交易
以太坊执行客户端收到该笔交易后,首先验证这笔交易的发起者是否有足够的余额用于完成这笔交易,其次验证签名是否正确。
// ValidateTransaction is a helper method to check whether a transaction is valid // according to the consensus rules, but does not check state-dependent validation // (balance, nonce, etc). func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types.Signer, opts *ValidationOptions) error { // Ensure transactions not implemented by the calling pool are rejected if opts.Accept&(1<<tx.Type()) == 0 { return fmt.Errorf("%w: tx type %v not supported by this pool", core.ErrTxTypeNotSupported, tx.Type()) } // Check various transaction validity rules if tx.Size() > opts.MaxSize { return fmt.Errorf("%w: transaction size %v, limit %v", ErrOversizedData, tx.Size(), opts.MaxSize) } // Verify signature if _, err := types.Sender(signer, tx); err != nil { return fmt.Errorf("%w: %v", ErrInvalidSender, err) } // Check gas limits intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, true, rules.IsIstanbul, rules.IsShanghai) if err != nil { return err } if tx.Gas() < intrGas { return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrIntrinsicGas, tx.Gas(), intrGas) } return nil }
4.3、加入内存池
执行客户端将交易加入本地内存池(用于缓存待处理交易),同时将交易广播到网络,其它节点收到广播消息后同样将交易信息加入自己本地内存池。
func (pool *LegacyPool) add(tx *types.Transaction) (replaced bool, err error) { // If the transaction is already known, discard it hash := tx.Hash() if pool.all.Get(hash) != nil { log.Trace("Discarding already known transaction", "hash", hash) knownTxMeter.Mark(1) return false, txpool.ErrAlreadyKnown } // If the transaction fails basic validation, discard it if err := pool.validateTx(tx); err != nil { log.Trace("Discarding invalid transaction", "hash", hash, "err", err) invalidTxMeter.Mark(1) return false, err } // Get sender from, _ := types.Sender(pool.signer, tx) // If the address is not yet known, request exclusivity to track the account if !hasPending && !hasQueued { if err := pool.reserver.Hold(from); err != nil { return false, err } } }
4.4、广播信标块
当前“时隙”的提议者将本地内存池中的交易打包并执行,执行的过程中会更新全局状态树,执行完成后会将刚才打包的交易信息同步到本地共识客户端,共识客户端会将收到的交易信息包装成”信标块“,其中包含有关奖励、惩罚、削减、证明等的信息,这些信息使网络能够就链头部的区块序列达成一致。共同识客户端广播”信标块“。
// 提议者打包和执行交易 if miner.chainConfig.IsCancun(env.header.Number, env.header.Time) { left := eip4844.MaxBlobsPerBlock(miner.chainConfig, env.header.Time) - env.blobs if left < int(ltx.BlobGas/params.BlobTxBlobGasPerBlob) { log.Trace("Not enough blob space left for transaction", "hash", ltx.Hash, "left", left, "needed", ltx.BlobGas/params.BlobTxBlobGasPerBlob) txs.Pop() continue } } // Transaction seems to fit, pull it up from the pool tx := ltx.Resolve() if tx == nil { log.Trace("Ignoring evicted transaction", "hash", ltx.Hash) txs.Pop() continue } // Get sender from, _ := types.Sender(env.signer, tx) // Start executing the transaction env.state.SetTxContext(tx.Hash(), env.tcount) err := miner.commitTransaction(env, tx)
4.5、节点同步验证
其它节点的共识客户端收到”信标块“后,从中解析出打包的交易,然后将交易信息传递给本地执行客户端,执行客户端执行交易并更新本地状态树以保证其与其它节点一致,待验证客端验证区块的有效性后将本块加入本地数据库。
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers // concurrently. The method returns a quit channel to abort the operations and // a results channel to retrieve the async verifications. func (beacon *Beacon) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header) (chan<- struct{}, <-chan error) { preHeaders, postHeaders := beacon.splitHeaders(headers) if len(postHeaders) == 0 { return beacon.ethone.VerifyHeaders(chain, headers) } if len(preHeaders) == 0 { return beacon.verifyHeaders(chain, headers, nil) } // Verify headers concurrently var ( abort = make(chan struct{}) results = make(chan error, len(headers)) ) go func() { var ( old, new, out = 0, len(preHeaders), 0 errors = make([]error, len(headers)) done = make([]bool, len(headers)) oldDone, oldResult = beacon.ethone.VerifyHeaders(chain, preHeaders) newDone, newResult = beacon.verifyHeaders(chain, postHeaders, preHeaders[len(preHeaders)-1]) ) // Collect verification results for { select { case err := <-oldResult: errors[old], done[old] = err, true old++ case err := <-newResult: errors[new], done[new] = err, true new++ case <-abort: return } } }() return abort, results }
-
理想环境下执行过程如上所述,但在分布式网络中由于网络波动和不诚实节点的存在,依然有如下两个问题待解:区块的最终确认、分叉处理。下面介绍以太坊POS是解决这两个问题的方法
5、区块如何被最终确认
以太坊每12秒产生一个块被称为”时隙“(slot),
每32个”时隙“被称为一个”周期“(epochs),
周期内的第一个”时隙“被称为”检查点“(checkpoint)。
一个 slot 最多只能有一个区块
区块的状态升级发生在“检查点”,也就是说只有checkpoint才有状态升级,其依次会经历“合理”到“最终确认”两种状态的变迁。因此区块被”最终确认“需要验证节点对检查点(检查点成对被投票)进行两轮投票。
区块必须获得总质押以太币 2/3 的投票,才能纳入权威链。 此条件可将区块升级至“合理”(justified)状态,此状态的区块回滚成本很高。
// beacon-chain/core/epoch/precompute/justification_finalization.go func processJustificationBits(state state.BeaconState, totalActiveBalance, prevEpochTargetBalance, currEpochTargetBalance uint64) { // 检查是否达到2/3阈值 if 3*prevEpochTargetBalance >= 2*totalActiveBalance { newBits.SetBitAt(1, true) } if 3*currEpochTargetBalance >= 2*totalActiveBalance { newBits.SetBitAt(0, true) } }
当一个”合理“区块上有另一个区块被合理化,那前者就被升级为“最终确定”状态。
6、分叉选择
Gasper 是一种定义验证者如何受到奖惩的机制,决定要接受和拒绝哪个区块,以及将区块建在哪个区块链分叉上。Gasper采用一种复杂的算法,即 LMD-GHOST(Latest Message-Driven Greedy Heaviest Observed Sub-Tree 最新消息驱动的最贪婪、最重的观测子树),该算法选择具有最大证明累积权重的分叉作为权威分叉(最贪婪、最重的子树);如果从验证者那里收到多条消息,则只考虑最新的那个(最新消息驱动)。 在将最重的区块添加到权威链之前,每位验证者都会使用这个规则来评估每个区块。
简而言之 LMD-GHOST 的规则是从创世区块开始 每次有分叉,就选择票数多的分支,重复,直至找到最新的块。
7、奖励
验证者的奖励都是由一个基本的奖励基数(base_reward )计算而来:
base_reward = effective_balance * (base_reward_factor / (base_rewards_per_epoch * sqrt(sum(active_balance))))
effective_balance 是验证者的有效余额 base_reward_factor 目前固定为 64 base_rewards_per_epoch 固定为 4 sum(active balance) 是所有活跃验证者的质押以太币总数。
由公式可以看出基础奖励与验证者的有效余额成正比,与网络中的验证者数量成反比。 验证者越多,活跃验证者的质押以太币总数越大(如 sqrt(N)),因而每个验证者的 base_reward 越小(如1/sqrt(N))。
当验证节点按照规则执行时会受到相应奖励,具体奖励以分为如下几部分:
来源投票:验证者给checkpoint pairs中时间较迟的块投票 目标投票:验证者给状态为“合理”的目标检查点及时投票 头部投票:验证者给正确的头部区块进行了及时投票 同步委员会奖励:验证者参与了同步委员会 提议者奖励:验证者在正确的时隙提议了区块 inclusion_delay_reward:快速认证激励奖励
以上奖励中分两类,其中inclusion_delay_reward单独计算,其它5部分按一定权重计算:
7.1、按权重计算的奖励
TIMELY_SOURCE_WEIGHT uint64(14) TIMELY_TARGET_WEIGHT uint64(26) TIMELY_HEAD_WEIGHT uint64(14) SYNC_REWARD_WEIGHT uint64(2) PROPOSER_WEIGHT uint64(8)
验证者根据自己完成的验证来获取相应权重的奖励,比如验证者及时给来源、目标和头部投票,提议一个区块以及参与同步委员会,他们就能获取 64/64 * base_reward == base_reward。然而,验证者通常不是区块提议者,所以它们的最大奖励是 64-8 /64 * base_reward == 7/8 * base_reward。 既不是区块提议者,也不参与同步委员会的验证者能收到 64-8-2 / 64 * base_reward == 6.75/8 * base_reward。
7.2、inclusion_delay_reward奖励
为了激励验证者快速认证, 这个值等于 base_reward 乘以 1/delay,其中 delay 是分隔区块提议和认证的时隙数。 例如,如果在区块提议的一个时隙内提交认证,那么认证者将获得 base_reward * 1/1 == base_reward。 如果认证在下一个时隙到达,认证者将收到 base_reward * 1/2,以此类推。
7.3、提议者额外奖励
包含在区块中的每一个有效认证都能让区块提议者获得 8 / 64 * base_reward,所以奖励的实际值与证明验证者的数量成比例。
通过在所提议区块中包含其他验证者的不良行为证据,区块提议者也能增加奖励。数额等于每个被罚没的验证者的 1/512 * effective balance。
8、罚没
视情节不同,对于不诚实或者因客观因素影响区块高效、安全运行时,该机制对节点实施了不同程度的惩罚。
8.1、轻微惩罚
一般是客观原因,如网络波动或者其它因素导致的宕机。验证者错失目标和来源投票的惩罚,等于认证者提交这些投票时获取到的奖励。 这意味着没有奖励会添加到他们的余额中,反而会从余额中移除同等价值。 错失头部投票没有惩罚(即,头部投票只有奖励,没有惩罚)。 也没有与 inclusion_delay 值相关的惩罚 - 只是不会把奖励添加到验证者余额。 区块提议失败也没有惩罚。
8.2、罚没
这种形式的惩罚更为严重,一般发生在验证者不诚实地提议或认证区块,其具体情形包含:
在同一时隙提议或签名两个不同的区块
认证一个“包围”另一个区块的区块(有效地更改历史)
通过证明同一个区块的两名候选人进行“双重投票”
如果这些行为被检测到,验证者就会被罚没。 这意味着 1/32 的质押以太币(最多是 1 个以太币)将被立即销毁,然后一个为期 36 天的移除期开始。 在移除期内,验证者的质押将逐渐流失。 在中间点(第 18 天),会有一个额外的惩罚,其幅度与罚没事件前 36 天内所有罚没验证者的质押以太币总数成比例。 这意味着被罚没的验证者越多,罚没的幅度就会增加。 最大的罚没幅度是所有罚没验证者的全部有效余额(即,如果有很多的验证者被罚没,那么他们将失去全部的质押)。 另一方面,一次单独的罚没事件只会销毁一小部分的验证者质押。 这个与罚没验证者的数量成比例的中间点惩罚被称为“相关性惩罚”。
8.3、怠惰惩罚
一般发生在群体性,有组织的破坏行为。这种情况下如果共识层超过四个时段都还没有最终确定,则该惩罚会被激活。 怠惰惩罚的最终目的是给该链恢复最终确定性创建必要条件。 正如上文所解释的那样,最终确定性需要 2/3 多数的总质押以太币来同意来源和目标检查点。 如果超过 1/3 验证者总数的验证者离线或者提交正确认证失败,那么就不可能有超过 2/3 的绝对多数来最终确定检查点。 怠惰惩罚会让非活跃验证者的质押逐渐流失,直到他们控制的总质押少于 1/3,这就允许了其余的活跃验证者可以最终确定该链。 不管非活跃验证者的池子有多大,其余的活跃验证者最终都会控制超过 2/3 的质押。 质押损失对于非活跃验证者尽快重新激活有着强大的激励! 在 Medalla 测试网曾遇到过一个怠惰惩罚的场景,少于 66% 的活跃验证者能够在当前区块链头部达成共识。 怠惰惩罚被激活,最终重新获得最终确定性!
9、VS PoSA
| 特性 | PoS | PoSA |
| 验证者选择 | 完全去中心化,基于质押量和随机性 | 预设的授权验证者集合 |
| 共识机制 | 需要大规模验证者参与:需要超过2/3的质押量 | 仅需授权验证者达成共识:超过2/3验证者 |
| 安全性保证 | 经济安全性(质押金) | 信任 + 经济安全性(质押金) |
| 去中心化程度 | 高 | 相对较低 |
| 区块时间 | 12秒 | 3秒 |
| 区块确认数 | 每个 slot 12秒 每个 epoch 32个 slot 最终确需要2个epoch(约12.8分钟) |
平均月2~3个区块,约6~9秒 |
| 性能 | 需要更多确认时间 | 更快的确认速度 |
| 扩展性 | 受验证者数量影响 | 更容易扩展 |

浙公网安备 33010602011771号