BNB Smart Chain 采用 Parlia 共识,这是一种权益证明权威(proof-of-staked-authority)共识,每天在 epoch 边界根据质押的 BNB 和监禁状态选举出一组活跃验证者。任意高度上的当前出块者可通过 eth_getBlockByNumber 在区块的 miner/coinbase 字段中看到,而权威的验证者集合和投票权则存储在系统合约中,可通过 eth_call 读取。罚没和监禁记录在 Slash Contract 中,并在 epoch 边界生效,因此集成方应关注出块者的变化和 epoch 转换。本文通过可运行的 Node.js 示例展示如何采样出块者、读取验证者合约以及计算 epoch 边界,同时指出系统合约地址、getter 名称和 epoch 长度是协议常量,必须从运行中的链上读取。
通过 RPC 读取 BNB Smart Chain 验证者选举与罚没
BNB Smart Chain(BSC)运行一种名为 Parlia 的混合共识,这是一种权益证明权威模型,其中一组数量有限的活跃验证者按轮询顺序轮流出块。活跃集合并非静态:它每天在 epoch 边界根据质押的 BNB 以及任何监禁或罚没状态重新计算。对于集成方、索引器和监控工具而言,实际的问题是如何在不运行完整验证者堆栈的情况下,通过 JSON-RPC 观察这种选举和罚没行为。
本指南涵盖验证者选举和罚没的链上表示、暴露这些信息的 RPC 方法,以及可复现的方法,用于针对你自己的端点测量当前出块者集合和验证者数量。它将已记录的协议行为与提供商特定行为以及你可以自行运行的测量方法区分开来。有关网络级访问的详细信息,请参阅 BNB Chain RPC 端点(RPC Assistant) 和 OnFinality Learn 中心。
- Parlia:活跃验证者在一个 epoch 内按轮询顺序出块。
- Epoch 边界:活跃集合每天根据质押和监禁状态重新计算。
- 系统合约:Validator Contract / stake hub 和 Slash Contract 保存权威状态。
- RPC 读取:eth_getBlockByNumber 用于出块者,eth_call 用于合约状态。
Parlia 共识与每日 Epoch 边界
Parlia 是一种权益证明权威共识,它将质押与许可制验证者集合相结合。根据 BNB Chain 文档:BSC 验证者概述,验证者根据质押的 BNB 数量选举产生,活跃验证者集合在 epoch 边界更新。在一个 epoch 内,验证者按确定性的轮询顺序轮流出块,这意味着如果你知道当前的验证者顺序,任意给定区块的出块者是可以预测的。
Epoch 长度是一个协议常量,但它在所有链版本和升级中并非固定不变。必须从运行中的链上读取,而不是从文档中硬编码。系统合约地址和 getter 名称也是如此:它们按版本记录,并可能随升级而变化。应将它们视为运行时配置,而不是代码中的常量。
- Epoch 长度:已记录 / 因链版本和升级而异。
- 验证者顺序:在一个 epoch 内确定性的轮询。
- 选举输入:质押的 BNB 和监禁状态。
- 权威来源:系统合约,而不仅仅是区块 miner。
保存验证者和罚没状态的系统合约
BSC 将验证者选举和罚没状态存储在系统合约中。Validator Contract(通常称为 stake hub)保存验证者集合、每个验证者的投票权以及委托信息。Slash Contract 记录罚没和监禁事件。这些合约不是普通的用户合约;它们是协议的一部分,由共识层在 epoch 边界更新。
由于这些是合约,你可以使用 ABI 编码的函数选择器通过 eth_call 读取它们。确切的地址和 getter 名称是协议常量,因链版本和升级而异,因此你应该从运行中的链或当前 BNB Chain 文档中读取它们,而不是硬编码本文中的值。BNB Chain 文档:BSC 罚没和罚没规则 描述了罚没规则以及 Slash Contract 的作用。
- Validator Contract / stake hub:验证者集合、投票权、委托。
- Slash Contract:罚没和监禁记录。
- 读取方法:使用 ABI 编码的 getter 进行 eth_call。
- 地址和 getter 名称:已记录 / 因链版本和升级而异。
使用 eth_getBlockByNumber 读取当前出块者
在 BSC 上,区块的 miner 字段(也称为 coinbase)标识了产生该区块的验证者。这是 Parlia 共识的已记录行为:区块的受益人就是该高度的验证者。你可以使用 eth_getBlockByNumber 读取它并检查 miner 字段。这为你提供了谁在出块的时间点视图。
要重建 epoch 内的验证者顺序,请在一段连续区块上采样 miner 字段,并对连续出块者进行分组。由于验证者按轮询轮流,足够长的样本将揭示重复序列。然而,这只是合约权威状态的近似:验证者可能被跳过、监禁或在 epoch 边界被替换,而仅凭 miner 字段无法告诉你完整的当选集合或投票权。
const https = require('https');
const RPC_URL = process.env.BSC_RPC_URL || 'https://your-bsc-rpc-endpoint';
function rpcCall(method, params) {
return new Promise((resolve, reject) => {
const body = JSON.stringify({ jsonrpc: '2.0', id: 1, method, params });
const req = https.request(RPC_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' } }, (res) => {
let data = '';
res.on('data', (chunk) => (data += chunk));
res.on('end', () => {
try {
const json = JSON.parse(data);
if (json.error) reject(new Error(json.error.message));
else resolve(json.result);
} catch (e) { reject(e); }
});
});
req.on('error', reject);
req.write(body);
req.end();
});
}
async function sampleProducers(startBlock, count) {
const producers = [];
for (let i = 0; i < count; i++) {
const block = await rpcCall('eth_getBlockByNumber', ['0x' + (startBlock + i).toString(16), false]);
if (block && block.miner) producers.push(block.miner.toLowerCase());
}
const tally = {};
for (const p of producers) tally[p] = (tally[p] || 0) + 1;
return { producers, tally };
}
(async () => {
const latest = await rpcCall('eth_blockNumber', []);
const start = parseInt(latest, 16) - 100;
const { producers, tally } = await sampleProducers(start, 100);
console.log('Distinct producers:', Object.keys(tally).length);
console.log('Tally:', tally);
})();通过 eth_call 读取验证者集合和投票权
权威的验证者集合和每个验证者的投票权存储在 Validator Contract 中。你可以使用合约的 getter 函数通过 eth_call 读取它们。一个常见的 getter 是 getValidators,它返回当前的验证者集合,但确切的名称和签名是协议常量,因链版本和升级而异。你应该从运行中的链或官方文档验证当前的 ABI。
投票权源自验证者的自质押和委托。合约通过返回质押金额和委托总额的 getter 暴露此信息。由于 eth_call 针对特定区块的状态执行,读取历史验证者集合需要具有归档访问权限的节点。有关归档要求的更多信息,请参阅 BNB Smart Chain 历史 RPC 与归档。
const { ethers } = require('ethers');
const RPC_URL = process.env.BSC_RPC_URL || 'https://your-bsc-rpc-endpoint';
const provider = new ethers.JsonRpcProvider(RPC_URL);
// Replace with the current Validator Contract address and ABI getter.
// These are protocol constants: documented / varies by chain version and upgrade.
const VALIDATOR_CONTRACT = process.env.VALIDATOR_CONTRACT || '0x0000000000000000000000000000000000001000';
const ABI = [
'function getValidators() view returns (address[])'
];
async function readValidatorCount(blockTag) {
const contract = new ethers.Contract(VALIDATOR_CONTRACT, ABI, provider);
const validators = await contract.getValidators({ blockTag });
return validators.length;
}
(async () => {
const count = await readValidatorCount('latest');
console.log('Validator count from contract:', count);
})();计算 Epoch 边界并观察出块者变化
Epoch 长度决定了验证者集合重新计算的频率。你可以从运行中的链读取 epoch 长度,通常通过系统合约 getter 或配置调用。一旦知道 epoch 长度,你就可以将下一个 epoch 边界计算为下一个是 epoch 长度倍数的区块高度。在该边界处,活跃验证者集合可能发生变化,你可以通过比较变化前后的出块者集合来观察变化。
一种实用方法是在跨越 epoch 边界的范围内采样 miner 字段,并寻找不同出块者集合或轮询顺序的变化。历史上,epoch 的第一个区块出块时间偏短或缺失曾是验证者集合转换的信号,但这并非保证指标,应针对合约状态进行验证。BNB Smart Chain RPC 可靠性与超时 指南涵盖了在此类观察期间如何处理 RPC 错误。
async function getEpochLength() {
// Replace with the current epoch length getter or configuration call.
// This is a protocol constant: documented / varies by chain version and upgrade.
const epochLengthHex = await rpcCall('eth_call', [
{ to: process.env.EPOCH_CONTRACT || '0x0000000000000000000000000000000000001000', data: '0x' },
'latest'
]);
return parseInt(epochLengthHex, 16);
}
async function findNextEpochBoundary(currentBlock) {
const epochLength = await getEpochLength();
const nextBoundary = Math.ceil((currentBlock + 1) / epochLength) * epochLength;
return { epochLength, nextBoundary };
}
(async () => {
const latest = await rpcCall('eth_blockNumber', []);
const current = parseInt(latest, 16);
const { epochLength, nextBoundary } = await findNextEpochBoundary(current);
console.log('Epoch length:', epochLength);
console.log('Next epoch boundary:', nextBoundary);
})();RPC 读取中的罚没和监禁信号
罚没和监禁记录在 Slash Contract 中。当验证者被罚没或监禁时,其状态会改变,对出块的影响在下一个 epoch 边界发生。对于集成方而言,实际信号是出块者的变化:被监禁的验证者在后续 epoch 中不会作为 miner 出现,直到被解除监禁。Slash Contract 本身可以通过 eth_call 读取以检查罚没记录,但确切的 getter 名称和事件签名是协议常量,因链版本和升级而异。
重要的是区分已记录的协议行为和提供商特定行为。罚没记录在 Slash Contract 中并在 epoch 边界生效这一事实记录在 BNB Chain 文档:BSC 罚没和罚没规则 中。你的 RPC 提供商反映这些变化的速度取决于提供商的节点同步和索引。始终针对相关区块的合约状态进行验证。
- 罚没和监禁:记录在 Slash Contract 中。
- 生效时间:状态变化在 epoch 边界生效。
- 集成方信号:跨 epoch 的出块者变化。
- 提供商行为:已记录 / 因提供商而异。
可复现的测量方法与结果表
要针对你自己的端点测量验证者选举和罚没行为,请运行上述采样和合约读取脚本,并将结果记录在表格中。此方法是可复现的:使用相同的区块范围和相同的 RPC 端点,并填写你观察到的值。不要依赖本文中的基准数字;针对你自己的基础设施进行测量。
下表是一个模板。用你自己的观察结果填写它。如果你想观察出块者变化,区块范围应至少跨越一个 epoch 边界。来自合约的验证者数量是权威数字;从区块 miner 看到的不同出块者是一个近似值。
- 区块范围:你采样的起始区块和结束区块。
- 看到的不同出块者:样本中唯一 miner 地址的数量。
- 来自合约的验证者数量:getValidators 调用的结果。
- Epoch 长度:从运行中的链读取的值。
- 边界处的出块者变化:是/否,以及发生变化的区块高度。
限制、权衡与协议常量
确切的系统合约地址、getter 名称和 epoch 长度是协议常量,按链版本和升级记录。它们必须从运行中的链读取,而不是从本文硬编码。如果你硬编码地址或 getter 名称,你的集成可能在网络升级后中断。始终获取当前值或使其可配置。
使用 eth_call 读取系统合约依赖于其状态位于所查询区块的节点。对于历史读取,你需要归档访问权限。仅从区块 miner 重建完整验证者集合只是合约权威状态的近似:它不会给你投票权、委托细节,或者如果某些验证者在你的采样窗口内没有出块,也不会给你完整的当选集合。有关查询这些端点时的延迟和可靠性考虑,请参阅 BNB Smart Chain RPC 延迟 和 BSC parity trace 与 trace_block 范围。
- 系统合约地址和 getter 名称:已记录 / 因链版本和升级而异。
- Epoch 长度:从运行中的链读取,不要硬编码。
- 历史 eth_call:需要归档访问权限。
- Miner 采样:近似值,不是权威验证者集合。
排查常见的 RPC 读取失败
通过 RPC 读取验证者状态时,你可能会遇到诸如 missing trie node、execution reverted 或超时等错误。Missing trie node 错误通常表明你的节点没有所查询区块的状态,这在没有归档访问权限的情况下读取历史数据时很常见。Execution reverted 可能意味着 getter 名称或 ABI 对当前链版本不正确。超时可能表明提供商速率限制或网络问题。
要进行排查,首先验证你的 RPC 端点支持 eth_call 方法,并且你使用的区块标签可用。如果你需要历史状态,请使用归档端点。如果遇到 execution reverted,请从官方文档或运行中的链检查当前合约地址和 ABI。对于提供商特定的限制,请查阅提供商的文档。OnFinality 的 API 服务 和 RPC 定价 页面描述了端点选项,BNB Chain RPC 端点(RPC Assistant) 页面列出了可用端点。
- Missing trie node:节点缺少所查询区块的状态;使用归档。
- Execution reverted:验证当前版本的合约地址和 ABI。
- 超时:检查提供商速率限制和网络状况。
- Method not found:确保端点支持 eth_call 和 eth_getBlockByNumber。
集成方和监控工具的后续步骤
要构建稳健的验证者监控工具,请将区块 miner 采样与定期合约读取相结合。使用 miner 采样实时检测出块者变化,并使用针对 Validator Contract 的 eth_call 获取权威的验证者集合和投票权。根据 epoch 长度计算 epoch 边界,并在这些边界前后安排合约读取,以捕捉选举变化。
对于生产系统,考虑使用具有归档支持以进行历史读取的可靠 RPC 提供商。OnFinality 提供 BNB Chain RPC 端点 及相关服务。你还可以查看 OnFinality Learn 中心,获取更多关于 BSC RPC 可靠性、延迟和 trace 方法的指南。始终针对你自己的端点进行测量,并将结果记录在可复现的表格中。
- 将 miner 采样与合约读取相结合以获取权威状态。
- 在 epoch 边界前后安排读取以捕捉选举。
- 使用归档端点进行历史验证者集合查询。
- 测量并记录你自己的结果;不要依赖第三方基准。