当前位置: 首页 > news >正文

BSC链验证者添加完整流程详解:从StakeHub到Snapshot的完整链路 - 若

概述

BSC链的验证者添加是一个复杂而精密的流程,涉及多个合约、共识机制和时间节点的协调。本文将深入分析从StakeHub创建验证者到最终在Snapshot中生效的完整流程。

一、验证者添加流程概览

1.1 整体流程图

image

1.2 时间线图

image
 
 

二、详细流程分析

2.1 第一阶段:StakeHub创建验证者

2.1.1 StakeHub合约调用Go代码实现

func (vj *ValidatorJoiner) createValidator() error {fmt.Println("开始创建验证者...")// 验证配置if err := vj.validateConfig(); err != nil {return fmt.Errorf("配置验证失败: %v", err)}// 检查当前验证者数量currentCount, err := vj.getCurrentValidatorCount()if err != nil {return fmt.Errorf("获取当前验证者数量失败: %v", err)}maxValidators, err := vj.getMaxElectedValidators()if err != nil {return fmt.Errorf("获取最大验证者数量失败: %v", err)}fmt.Printf("当前验证者数量: %d, 最大验证者数量: %d\n", currentCount, maxValidators)if currentCount >= maxValidators {return fmt.Errorf("已达到最大验证者数量限制: %d", maxValidators)}// 检查最小自质押要求minDelegation, err := vj.getMinSelfDelegationBNB()if err != nil {return fmt.Errorf("获取最小自质押要求失败: %v", err)}// 解析自质押数量selfDelegationWei := new(big.Int)selfDelegationWei.SetString(vj.config.SelfDelegationBNB, 10)selfDelegationWei.Mul(selfDelegationWei, big.NewInt(1e18)) // 转换为weiif selfDelegationWei.Cmp(minDelegation) < 0 {return fmt.Errorf("自质押数量不足,需要至少 %s BNB", new(big.Float).Quo(new(big.Float).SetInt(minDelegation), new(big.Float).SetInt(big.NewInt(1e18))))}// 生成BLS签名证明fmt.Println("生成BLS签名证明...")blsProof, err := vj.generateBLSProof()if err != nil {return fmt.Errorf("生成BLS签名证明失败: %v", err)}// 准备佣金结构commission := struct {Rate          uint64MaxRate       uint64MaxChangeRate uint64}{Rate:          vj.config.CommissionRate,MaxRate:       vj.config.MaxCommissionRate,MaxChangeRate: vj.config.MaxChangeRate,}// 准备描述结构description := struct {Moniker  stringIdentity stringWebsite  stringDetails  string}{Moniker:  vj.config.Moniker,Identity: vj.config.Identity,Website:  vj.config.Website,Details:  vj.config.Details,}// 准备voteAddress (BLS公钥)voteAddress := common.FromHex(vj.config.BLSPublicKey)fmt.Println("验证者配置:")fmt.Printf("  验证者地址: %s\n", vj.config.ValidatorAddress)fmt.Printf("  名称: %s\n", vj.config.Moniker)fmt.Printf("  佣金率: %.1f%%\n", float64(vj.config.CommissionRate)/100)fmt.Printf("  自质押: %s BNB\n", vj.config.SelfDelegationBNB)// 检查钱包余额balance, err := vj.getBalance()if err != nil {return fmt.Errorf("获取余额失败: %v", err)}if balance.Cmp(selfDelegationWei) < 0 {return fmt.Errorf("钱包余额不足,需要 %s BNB,当前余额 %s BNB",new(big.Float).Quo(new(big.Float).SetInt(selfDelegationWei), new(big.Float).SetInt(big.NewInt(1e18))),new(big.Float).Quo(new(big.Float).SetInt(balance), new(big.Float).SetInt(big.NewInt(1e18))))}// 打包合约调用数据data, err := vj.contract.Pack("createValidator",common.HexToAddress(vj.config.ValidatorAddress),voteAddress,blsProof,commission,description,)if err != nil {return fmt.Errorf("打包createValidator调用失败: %v", err)}// 获取nonce和gas价格nonce, err := vj.getNonce()if err != nil {return fmt.Errorf("获取nonce失败: %v", err)}gasPrice, err := vj.getGasPrice()if err != nil {return fmt.Errorf("获取gas价格失败: %v", err)}// 创建交易tx := types.NewTransaction(nonce,common.HexToAddress(stakeHubAddress),selfDelegationWei,5000000, // gas limitgasPrice,data,)// 签名交易chainID, err := vj.client.ChainID(context.Background())if err != nil {return fmt.Errorf("获取链ID失败: %v", err)}signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), vj.privKey)if err != nil {return fmt.Errorf("签名交易失败: %v", err)}// 发送交易fmt.Println("发送交易...")err = vj.client.SendTransaction(context.Background(), signedTx)if err != nil {return fmt.Errorf("发送交易失败: %v", err)}fmt.Printf("交易已发送,交易哈希: %s\n", signedTx.Hash().Hex())fmt.Println("等待交易确认...")// 等待交易确认receipt, err := vj.waitForTransaction(signedTx.Hash())if err != nil {return fmt.Errorf("等待交易确认失败: %v", err)}if receipt.Status == 0 {// 获取交易执行失败的原因errorMsg, err := vj.getTransactionError(signedTx.Hash())if err != nil {return fmt.Errorf("获取交易执行失败原因失败: %v", err)}return fmt.Errorf("交易执行失败: %s", errorMsg)}fmt.Printf("交易已确认,区块号: %d\n", receipt.BlockNumber.Uint64())
}
 

2.1.2 创建验证者的关键要素

  • 质押金额:必须满足最小质押要求
  • 公钥信息:验证者的BLS公钥
  • 状态设置:初始状态为Active
  • 事件记录:记录创建事件

2.2 第二阶段:呼吸块与更新BSCValidatorSet合约

2.2.1 呼吸块24小时产生一个

params/protocol_params.go
 BreatheBlockInterval uint64 = 24 * 3600 // Controls the interval for updateValidatorSetV2

2.2.2updateValidatorSetV2调用流程

image

2.2.3 updateValidatorSetV2呼吸块调用更新BSCValidatorSet合约

 

// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
// nor block rewards given, and returns the final block.
func (p *Parlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB,.....// update validators every dayif p.chainConfig.IsFeynman(header.Number, header.Time) && isBreatheBlock(parent.Time, header.Time) {// we should avoid update validators in the Feynman upgrade blockif !p.chainConfig.IsOnFeynman(header.Number, parent.Time, header.Time) {if err := p.updateValidatorSetV2(state, header, cx, &body.Transactions, &receipts, nil, &header.GasUsed, true, tracer); err != nil {return nil, nil, err}}
}
 

2.3 第四阶段:Epoch边界与Snapshot更新

2.3.1 Snapshot更新流程

image

2.3.2 Snapshot更新验证者代码

判断呼吸块是否达到

Parlia.go
 func (p *Parlia) prepareValidators(chain consensus.ChainHeaderReader, header *types.Header) error {epochLength, err := p.epochLength(chain, header, nil)if err != nil {return err}if header.Number.Uint64()%epochLength != 0 {return nil}newValidators, voteAddressMap, err := p.getCurrentValidators(header.ParentHash, new(big.Int).Sub(header.Number, big.NewInt(1)))if err != nil {return err}// sort validator by addresssort.Sort(validatorsAscending(newValidators))if !p.chainConfig.IsLuban(header.Number) {for _, validator := range newValidators {header.Extra = append(header.Extra, validator.Bytes()...)}} else {header.Extra = append(header.Extra, byte(len(newValidators)))if p.chainConfig.IsOnLuban(header.Number) {voteAddressMap = make(map[common.Address]*types.BLSPublicKey, len(newValidators))var zeroBlsKey types.BLSPublicKeyfor _, validator := range newValidators {voteAddressMap[validator] = &zeroBlsKey}}for _, validator := range newValidators {header.Extra = append(header.Extra, validator.Bytes()...)header.Extra = append(header.Extra, voteAddressMap[validator].Bytes()...)}}return nil
}

 

Parlia.go获取当前验证者
 func (p *Parlia) getCurrentValidators(blockHash common.Hash, blockNum *big.Int) ([]common.Address, map[common.Address]*types.BLSPublicKey, error) {// blockblockNr := rpc.BlockNumberOrHashWithHash(blockHash, false)if !p.chainConfig.IsLuban(blockNum) {validators, err := p.getCurrentValidatorsBeforeLuban(blockHash, blockNum)return validators, nil, err}// methodmethod := "getMiningValidators"ctx, cancel := context.WithCancel(context.Background())defer cancel() // cancel when we are finished consuming integersdata, err := p.validatorSetABI.Pack(method)if err != nil {log.Error("Unable to pack tx for getMiningValidators", "error", err)return nil, nil, err}// callmsgData := (hexutil.Bytes)(data)toAddress := common.HexToAddress(systemcontracts.ValidatorContract)gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))result, err := p.ethAPI.Call(ctx, ethapi.TransactionArgs{Gas:  &gas,To:   &toAddress,Data: &msgData,}, &blockNr, nil, nil)if err != nil {return nil, nil, err}var valSet []common.Addressvar voteAddrSet []types.BLSPublicKeyif err := p.validatorSetABI.UnpackIntoInterface(&[]interface{}{&valSet, &voteAddrSet}, method, result); err != nil {return nil, nil, err}voteAddrMap := make(map[common.Address]*types.BLSPublicKey, len(valSet))for i := 0; i < len(valSet); i++ {voteAddrMap[valSet[i]] = &(voteAddrSet)[i]}return valSet, voteAddrMap, nil
}

三、时间延迟分析

3.1 各阶段时间延迟

StakeHub创建0分钟
呼吸块等待0-24小时
BSCValidatorSet更新0分钟
Epoch边界等待0-10分钟
Snapshot生效0分钟

3.2 时间计算

阶段 时间范围 说明
StakeHub创建 0分钟 立即执行
呼吸块等待 0-24小时 取决于创建时机
BSCValidatorSet更新 0分钟 立即生效
Epoch边界等待 0-10分钟 最多200个区块
Snapshot生效 0分钟 立即生效

3.3 最坏情况分析

  • 最长等待时间:24小时 + 10分钟 = 24小时10分钟
  • 最短等待时间:10分钟(如果刚错过呼吸块)
  • 平均等待时间:12小时 + 5分钟 = 12小时5分钟
 

总结

BSC链的验证者添加流程是一个精心设计的系统,通过多个阶段的协调工作,确保了验证者添加的安全性和稳定性。理解这个完整流程对于BSC链的运维和开发具有重要意义。
关键要点:
  • StakeHub创建:验证者添加的起点
  • 呼吸块机制:控制更新频率,确保网络稳定性
  • Epoch边界:Snapshot更新的时间节点
  • 实时检查:提供灵活性和即时响应能力
通过这个完整的流程,BSC链实现了安全、稳定、高效的验证者管理机制。
http://www.vanclimg.com/news/2674.html

相关文章:

  • Windows操作开机启动BAT文件
  • 千万
  • 仿射变换
  • 伙伴匹配系统(移动端 H5 网站(APP 风格)基于Spring Boot 后端 + Vue3 - 02 - Rainbow
  • 【IEEE冠名、香港中文大学(深圳)主办】第五届IEEE能源工程与电力系统国际学术会议(IEEE-EEPS 2025)
  • 【学习笔记】高等数学
  • ECS中实现Nginx四层和七层负载均衡以及ALB/NLB实现负载均衡
  • spring boot 日志增加 Trace Id (异步、任务都能支持)
  • 图像生成-Continuous Normalizing Flows(NFs)连续归一化流-07 - jack
  • 酵母文库:探索基因奥秘的有力工具
  • 【欧拉路】学习笔记
  • kafka 日志存储与查询
  • 基于MATLAB的不规则波下结构物波浪力计算
  • Slope Trick
  • ASP.NET WebForms调用ASMX的WebService接口
  • 透视畸变和单应性变换
  • JavaScript中的数据类型以及存储上的差别
  • webstorm关于git很慢的处理
  • 手工测试向左,测试开发向右
  • 设计汽车集群电源 - 详解
  • kafka rocketmq 零拷贝
  • 12N90-ASEMI电源逆变器专用12N90
  • 淀粉质(点分治)总结
  • MATLAB的图像融合方法:IHS、PCA、拉普拉斯、PCNN、小波
  • 基于YOLOv8的有无戴安全帽检测识别项目|完整源码数据集+PyQt5界面+完整训练流程+开箱即用!
  • 测试
  • IDEA Plugins:通义灵码
  • 平衡树
  • Windows 平台的路由表配置
  • 怎么使用德布鲁因序列编码三色激光条纹?