部署 编译部署 git clone https://github.com/fystack/mpcium.git cd mpcium source /etc/profile go env -w GOPRIVATE=github.com/bcskill/* go mod tidy make 可用命令 mpcium：启动 MPCium 节点 mpcium-cli：用于对等方、身份和发起方配置的 CLI 实用程序 go build ./cmd/mpcium go build ./cmd/mpcium-cli 启动NATS 和 Consul cd mpcium docker compose up -d NATS：高性能、轻量级的 消息系统（消息队列 / 消息总线），主要用于服务之间的异步通信 Service A ───► NATS ───► Service B ​ ▲ ​ └─► Service C（也可同时收到消息） Consul：HashiCorp 开源的一个用于服务网络的工具，主要用于 服务发现、配置管理、健康检查，也支持 分布式 Key-Value 存储 和 服务网格功能。 [Service A] <---> [Consul Agent] <---> [Consul Server] <---> [Service B info] 部署节点 3.1 创建节点配置 cd ../ mkdir test-node cd test-node mpcium-cli generate-peers -n 3 cp ../mpcium/config.yaml.template ./config.yaml 编辑badger_password 为16，24，32 位密码 3.2 注册链节点 mpcium-cli register-peers 3.3 生成启动器 mpcium-cli generate-initiator 从生成的event_initiator.identity.json 中复制public_key, 更新到 config.yaml中的event_initiator_pubkey 3.4 创建目录 mkdir node{0..2} for dir in node{0..2}; do cp config.yaml peers.json "$dir/"; done 3.5 创建身份 cd node0 mpcium-cli generate-identity --node node0 cd ../node1 mpcium-cli generate-identity --node node1 cd ../node2 mpcium-cli generate-identity --node node2 cd ../ cp node0/identity/node0_identity.json node1/identity/node0_identity.json cp node0/identity/node0_identity.json node2/identity/node0_identity.json cp node1/identity/node1_identity.json node0/identity/node1_identity.json cp node1/identity/node1_identity.json node2/identity/node1_identity.json cp node2/identity/node2_identity.json node0/identity/node2_identity.json cp node2/identity/node2_identity.json node1/identity/node2_identity.json 3.6 启动节点 cd node0 mpcium start -n node0 cd node1 mpcium start -n node1 cd node2 mpcium start -n node2 实际测试 https://github.com/fystack/mpcium-client-ts event_initiator.key f81851f905abc51de2618ddb7f75dde2a422dd0f6f3abb0d51c850287ecac900 创建钱包 import { connect } from "nats"; import { KeygenSuccessEvent, MpciumClient } from "../src"; import { computeAddress, hexlify } from "ethers"; import base58 from "bs58"; import * as fs from "fs"; import * as path from "path"; import { v4 } from "uuid"; async function main() { const args = process.argv.slice(2); const nIndex = args.indexOf("-n"); const walletCount = nIndex !== -1 ? parseInt(args[nIndex + 1]) || 1 : 1; const nc = await connect({ servers: "nats://127.0.0.1:4222" }).catch( (err) => { console.error(`Failed to connect to NATS: ${err.message}`); process.exit(1); } ); console.log(`Connected to NATS at ${nc.getServer()}`); const mpcClient = await MpciumClient.create({ nc: nc, keyPath: "./event_initiator.key", // password: "your-password-here", }); const walletsPath = path.resolve("./wallets.json"); let wallets: Record<string, KeygenSuccessEvent> = {}; if (fs.existsSync(walletsPath)) { try { wallets = JSON.parse(fs.readFileSync(walletsPath, "utf8")); } catch (error) { console.warn(`Could not read wallets file: ${error.message}`); } } let remaining = walletCount; mpcClient.onWalletCreationResult((event: KeygenSuccessEvent) => { const timestamp = new Date().toISOString(); console.log(`${timestamp} Received wallet creation result:`, event); if (event.eddsa_pub_key) { const pubKeyBytes = Buffer.from(event.eddsa_pub_key, "base64"); const solanaAddress = base58.encode(pubKeyBytes); console.log(`Solana wallet address: ${solanaAddress}`); } if (event.ecdsa_pub_key) { const pubKeyBytes = Buffer.from(event.ecdsa_pub_key, "base64"); const uncompressedKey = pubKeyBytes.length === 65 ? pubKeyBytes : Buffer.concat([Buffer.from([0x04]), pubKeyBytes]); const ethAddress = computeAddress(hexlify(uncompressedKey)); console.log(`Ethereum wallet address: ${ethAddress}`); } wallets[event.wallet_id] = event; fs.writeFileSync(walletsPath, JSON.stringify(wallets, null, 2)); console.log(`Wallet saved to wallets.json with ID: ${event.wallet_id}`); remaining -= 1; if (remaining === 0) { console.log("All wallets generated."); } }); try { for (let i = 0; i < walletCount; i++) { const timestamp = new Date().toISOString(); const walletID = await mpcClient.createWallet(`${v4()}:${i}`); console.log( `${timestamp} CreateWallet sent #${ i + 1 }, awaiting result... walletID: ${walletID}` ); } const shutdown = async () => { console.log("Cleaning up..."); await mpcClient.cleanup(); await nc.drain(); process.exit(0); }; process.on("SIGINT", shutdown); } catch (error) { console.error("Error:", error); await mpcClient.cleanup(); await nc.drain(); process.exit(1); } } main().catch(console.error); /mpcium-client-ts# ts-node ./examples/generate.ts Connected to NATS at 127.0.0.1:4222 Subscribed to wallet creation results (consume mode) result_type: 'success', error_reason: '', error_code: '' } Solana wallet address: Cmroym1zhiv6Q3DaHSRQv3CJBFnBqm4dtLkYnsAqbQ2c Ethereum wallet address: 0xef049bd27aCD860D7A157e492378bf54eE9671B9 Wallet saved to wallets.json with ID: b7b3cb5e-fbea-4785-86c7-f67c3562ddfc:0 签名发送交易 测试地址，忽略手续费不足问题 import { connect } from "nats"; import { MpciumClient, KeyType, SigningResultEvent } from "../src"; import { ethers } from "ethers"; import { SigningResultType } from "../src/types"; import * as fs from "fs"; import * as path from "path"; // The wallet ID should be provided via command line argument const walletId = "b7b3cb5e-fbea-4785-86c7-f67c3562ddfc:0"; // Destination wallet to send ETH to const DESTINATION_WALLET = "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"; // Amount to send in ETH const AMOUNT_TO_SEND = "0.0001"; // 0.001 ETH // Function to load wallet from wallets.json function loadWallet(walletId: string) { const walletsPath = path.resolve("./wallets.json"); try { if (fs.existsSync(walletsPath)) { const wallets = JSON.parse(fs.readFileSync(walletsPath, "utf8")); if (wallets[walletId]) { return wallets[walletId]; } throw new Error(`Wallet with ID ${walletId} not found in wallets.json`); } else { throw new Error("wallets.json file not found"); } } catch (error) { console.error(`Failed to load wallet: ${error.message}`); process.exit(1); } } async function main() { console.log(`Using wallet ID: ${walletId}`); // First, establish NATS connection separately const nc = await connect({ servers: "nats://127.0.0.1:4222" }).catch( (err) => { console.error(`Failed to connect to NATS: ${err.message}`); process.exit(1); } ); console.log(`Connected to NATS at ${nc.getServer()}`); // Create client with key path const mpcClient = await MpciumClient.create({ nc: nc, keyPath: "./event_initiator.key", // password: "your-password-here", // Required for .age encrypted keys }); try { // Connect to Ethereum testnet (Sepolia) const provider = new ethers.JsonRpcProvider( "https://eth-sepolia.public.blastapi.io" ); console.log("Connected to Ethereum Sepolia testnet"); // Get the wallet's public key/address const ethAddress = await getEthAddressForWallet(walletId); const fromAddress = ethAddress; console.log(`Sender account: ${fromAddress}`); console.log(`Destination account: ${DESTINATION_WALLET}`); console.log(`Amount to send: ${AMOUNT_TO_SEND} ETH`); // Get the current nonce for the sender address const nonce = await provider.getTransactionCount(fromAddress); // Get the current gas price const feeData = await provider.getFeeData(); console.log("feeData:", feeData); // Create an Ethereum transaction const transaction = { to: DESTINATION_WALLET, value: ethers.parseEther(AMOUNT_TO_SEND), gasLimit: 21000, // Standard gas limit for ETH transfers maxFeePerGas: feeData.maxFeePerGas, maxPriorityFeePerGas: feeData.maxPriorityFeePerGas, nonce: nonce, type: 2, // EIP-1559 transaction chainId: 11155111, // Sepolia chain ID }; // Calculate the transaction hash (this is what needs to be signed) const unsignedTx = ethers.Transaction.from(transaction); const txHash = unsignedTx.unsignedHash; const txHashHex = txHash.substring(2); // Remove '0x' prefix console.log(`Transaction hash: ${txHash}`); // Subscribe to signing results let signatureReceived = false; mpcClient.onSignResult((event: SigningResultEvent) => { console.log("Received signing result:", event); signatureReceived = true; if (event.result_type === SigningResultType.Success) { processSuccessfulSignature(event); } else { console.error(`Signing failed: ${event.error_message}`); } }); // Process a successful signature function processSuccessfulSignature(event: SigningResultEvent) { try { // For ECDSA Ethereum signatures, we need the r, s, and v (recovery) values if (!event.r || !event.s || event.signature_recovery === null) { console.error("Missing signature components in result:", event); return; } // Convert from base64 to hex strings const r = "0x" + Buffer.from(event.r, "base64").toString("hex"); const s = "0x" + Buffer.from(event.s, "base64").toString("hex"); // Decode signature_recovery from base64 to a number const recoveryBuffer = Buffer.from(event.signature_recovery, "base64"); const v = recoveryBuffer[0]; // Get the first byte as the recovery value console.log(`Signature components - r: ${r}, s: ${s}, v: ${v}`); // Create a signed transaction const signedTx = ethers.Transaction.from({ ...transaction, signature: { r, s, v }, }); // Verify signature const recoveredAddress = signedTx.from; console.log(`Recovered signer: ${recoveredAddress}`); if (!recoveredAddress) { console.error("Signature verification failed!"); return; } if (recoveredAddress.toLowerCase() !== fromAddress.toLowerCase()) { console.error( "Signature verification failed! Addresses don't match." ); return; } console.log("Signature verification successful!"); broadcastTransaction(signedTx.serialized); } catch (error) { console.error("Error processing signature:", error); if (error instanceof Error) { console.error(error.stack); } } } // Broadcast the transaction to the network function broadcastTransaction(signedTxHex: string) { provider .broadcastTransaction(signedTxHex) .then((tx) => { console.log(`Transaction sent! Transaction hash: ${tx.hash}`); console.log( `View transaction: https://sepolia.etherscan.io/tx/${tx.hash}` ); }) .catch((err) => { console.error("Error broadcasting transaction:", err); }); } // Send the transaction hash for signing, not the serialized transaction const txId = await mpcClient.signTransaction({ walletId: walletId, keyType: KeyType.Secp256k1, networkInternalCode: "ethereum:sepolia", tx: Buffer.from(txHashHex, "hex").toString("base64"), // Convert hex to base64 }); console.log(`Signing request sent with txID: ${txId}`); // Wait for the result await new Promise((resolve) => { const checkInterval = setInterval(() => { if (signatureReceived) { clearInterval(checkInterval); resolve(null); } }, 1000); }); // Keep the process running to allow time for transaction confirmation await new Promise((resolve) => setTimeout(resolve, 5000)); console.log("Cleaning up..."); await mpcClient.cleanup(); await nc.drain(); } catch (error) { console.error("Error:", error); await mpcClient.cleanup(); await nc.drain(); process.exit(1); } } // Helper function to get a wallet's Ethereum address async function getEthAddressForWallet(walletId: string): Promise<string> { // Load wallet from wallets.json const wallet = loadWallet(walletId); if (wallet && wallet.ecdsa_pub_key) { // Convert base64 public key to Ethereum address const pubKeyBuffer = Buffer.from(wallet.ecdsa_pub_key, "base64"); // Convert the buffer to hex string with "0x" prefix const pubKeyHex = "0x" + pubKeyBuffer.toString("hex"); // Ethereum addresses are derived from the keccak256 hash of the uncompressed public key const address = ethers.computeAddress(pubKeyHex); return address; } throw new Error(`Wallet with ID ${walletId} has no ECDSA public key`); } // Run the example main().catch(console.error); /mpcium-client-ts# ts-node ./examples/sign-eth.ts Using wallet ID: b7b3cb5e-fbea-4785-86c7-f67c3562ddfc:0 Connected to NATS at 127.0.0.1:4222 Connected to Ethereum Sepolia testnet Sender account: 0xef049bd27aCD860D7A157e492378bf54eE9671B9 Destination account: 0x742d35Cc6634C0532925a3b844Bc454e4438f44e Amount to send: 0.0001 ETH feeData: FeeData { gasPrice: 14533302n, maxFeePerGas: 28066604n, maxPriorityFeePerGas: 1000000n } Transaction hash: 0x9c7835d42ef9643c110d5ddf038fba76a580c93a650242a1d66f5d74333bf3c7 Subscribed to signing results (consume mode) SignTransaction request sent via JetStream for txID: c8354d11-aba5-46e8-88c2-c846b1fdac51 Signing request sent with txID: c8354d11-aba5-46e8-88c2-c846b1fdac51 Received signing result: { result_type: 'success', error_code: '', error_reason: '', is_timeout: false, network_internal_code: 'ethereum:sepolia', wallet_id: 'b7b3cb5e-fbea-4785-86c7-f67c3562ddfc:0', tx_id: 'c8354d11-aba5-46e8-88c2-c846b1fdac51', r: 'pylQvdMr+Z7N+9ADAdruVN7MTqysS0LNvVERgEx4Ktw=', s: 'ZA4LMVBf3Di9E35C3vph3ZWUvH4GHNnRTBb7aKYHFuc=', signature_recovery: 'AA==', signature: null } Signature components - r: 0xa72950bdd32bf99ecdfbd00301daee54decc4eacac4b42cdbd5111804c782adc, s: 0x640e0b31505fdc38bd137e42defa61dd9594bc7e061cd9d14c16fb68a60716e7, v: 0 Recovered signer: 0xef049bd27aCD860D7A157e492378bf54eE9671B9 Signature verification successful! Error broadcasting transaction: Error: insufficient funds for intrinsic transaction cost (transaction="0x02f87283aa36a780830f42408401ac432c82520894742d35cc6634c0532925a3b844bc454e4438f44e865af3107a400080c080a0a72950bdd32bf99ecdfbd00 301daee54decc4eacac4b42cdbd5111804c782adca0640e0b31505fdc38bd137e42defa61dd9594bc7e061cd9d14c16fb68a60716e7", info={ "error": { "code": -32003, "message": "insufficient funds for gas * price + value: have 0 want 100589398684000" } }, code=INSUFFICIENT_FUNDS, version=6.13.7) at makeError (/mnt/d/github/bcskill/code/MPC/mpcium-client-ts/node_modules/ethers/src.ts/utils/errors.ts:694:21) at JsonRpcProvider.getRpcError (/mnt/d/github/bcskill/code/MPC/mpcium-client-ts/node_modules/ethers/src.ts/providers/provider-jsonrpc.ts:1025:33) at /mnt/d/github/bcskill/code/MPC/mpcium-client-ts/node_modules/ethers/src.ts/providers/provider-jsonrpc.ts:563:45 at processTicksAndRejections (node:internal/process/task_queues:95:5) { code: 'INSUFFICIENT_FUNDS', transaction: '0x02f87283aa36a780830f42408401ac432c82520894742d35cc6634c0532925a3b844bc454e4438f44e865af3107a400080c080a0a72950bdd32bf99ecdfbd00301daee54decc4eacac4b42cdbd5111804c782adca0640e0b31505fdc38bd137e42defa61dd9594bc7e061cd9d14c16fb68a60716e7', info: { error: { code: -32003, message: 'insufficient funds for gas * price + value: have 0 want 100589398684000' } }, shortMessage: 'insufficient funds for intrinsic transaction cost' } Cleaning up... Cleaned up all subscriptions 多钱包测试 Subscribed to wallet creation results (consume mode) CreateWallet request sent via JetStream for wallet: bbcd5019-2e7d-46aa-a1e8-5bc14371bc57:0 2025-07-14T12:57:13.424Z CreateWallet sent #1, awaiting result... walletID: bbcd5019-2e7d-46aa-a1e8-5bc14371bc57:0 2025-07-14T12:57:17.181Z Received wallet creation result: { wallet_id: 'bbcd5019-2e7d-46aa-a1e8-5bc14371bc57:0', ecdsa_pub_key: '2+ZZj1XCFR/OGWk9g1EMYZFyXfDNaNHCf+egn1oTo6DvR6kQ6tOBHK0oZkCL6QNsjmfi14tQN71W5n7OOXLSBg==', eddsa_pub_key: 's8rOVjh6dMwmpXqRDRYSyOBenY/uaHBOUvumU2QFuBE=', result_type: 'success', error_reason: '', error_code: '' } Solana wallet address: D6qLkdfZLXjHhSLAJ38UHjkYmBHU4Ac8S1oQKUDJZ2Gx Ethereum wallet address: 0xCfcec2Beed19a40A663494396fB73042f2049dB1 Wallet saved to wallets.json with ID: bbcd5019-2e7d-46aa-a1e8-5bc14371bc57:0 测试通过 异常测试 使用相同walletID WRN Keygen session error error="Key already exists: 81418adb-fb32-4e17-9b85-8ae207aa9cb8:0" context="Failed to create ECDSA key generation session" errorCode=ERROR_KEY_ALREADY_EXISTS walletID=81418adb-fb32-4e17-9b85-8ae207aa9cb8:0 https://github.com/fystack/mpcium/issues/56 常见问题 创建地址无返回 问题排查 nats consumer info > Select a Stream mpc > Select a Consumer mpc_keygen_result 查看 Waiting Pulls: 3 of maximum 512 当前有几个客户端连接 移除所有监听列表 nats consumer rm mpc mpc_keygen_result
产品简介 官网：https://dashboard.trustivon.com/ Trustivon Validator 是一个功能强大的区块链验证者管理平台，专为 Trustivon 网络设计，提供直观、安全、高效的验证者节点管理和委托服务。该平台采用现代化的技术栈构建，支持多语言、响应式设计，适配各种设备，为用户提供最佳的使用体验。 核心功能 1. 验证者列表管理 实时展示网络中的所有验证者节点 显示验证者的关键信息：地址、佣金率、总质押量、个人质押量、状态 支持按不同状态筛选验证者（活跃、挖矿） 响应式设计，适配桌面和移动设备 2. 委托管理 一键委托 TC 代币给验证者 支持取消委托操作 提供重新委托功能，方便用户在不同验证者之间转移质押 实时显示可委托余额和可用奖励 3. 奖励领取 支持领取委托奖励 显示待处理的解除绑定记录 提供解除绑定周期倒计时 一键发起奖励领取 4. 验证者详情 查看验证者的详细信息 显示验证者的投票地址 支持复制验证者地址 提供验证者的状态变化历史 5. MetaMask 集成 无缝连接 MetaMask 钱包 支持钱包地址显示和管理 安全的交易签名机制 实时监听钱包状态变化 6. 多语言支持 支持英文、繁体中文、日文三种语言 动态语言切换 本地化的用户界面 使用指南 1. 连接钱包 点击右上角的"连接钱包"按钮 在弹出的MetaMask窗口中确认连接 连接成功后，右上角会显示您的钱包地址 2. 委托操作 在验证者列表中选择一个验证者 点击"委托"按钮 输入委托金额 确认委托设置 在MetaMask中确认交易 3. 领取奖励 点击"领取奖励"按钮 查看可领取的奖励金额 确认领取操作 在MetaMask中确认交易 4. 管理委托 在验证者列表中找到您已委托的验证者 可以选择"取消委托"或"重新委托" 按照提示完成操作 优势和亮点 1. 安全性 基于MetaMask SDK的安全连接 所有交易都在用户钱包中签名 不存储用户的私钥或助记词 采用HTTPS协议传输数据 2. 用户体验 直观的界面设计，易于使用 响应式布局，适配各种设备 实时数据更新，提供最新信息 多语言支持，服务全球用户 3. 功能完整性 涵盖验证者管理的全生命周期 支持委托、取消委托、重新委托 提供奖励领取功能 详细的验证者信息展示 4. 技术先进性 采用现代化的技术栈 组件化架构，便于扩展和维护 类型安全的代码设计 优秀的性能表现 未来规划 支持更多网络 ：计划支持主网和更多测试网络 增强数据分析 ：提供更详细的验证者性能分析和统计 移动应用开发 ：推出iOS和Android移动应用 增强安全性 ：添加更多安全验证和提示 社区功能 ：支持验证者和委托者之间的交流 更多语言支持 ：添加更多语言选项 如何开始使用 访问 Trustivon Validator 平台 连接您的MetaMask钱包 浏览验证者列表，选择合适的验证者 开始您的委托之旅 系统要求 现代浏览器（Chrome、Firefox、Safari、Edge） 已安装MetaMask扩展 稳定的网络连接 少量TC代币用于支付交易手续费 结语 Trustivon Validator 平台为用户提供了一个安全、高效、易用的验证者管理工具，帮助用户更好地参与Trustivon网络的治理和质押。无论您是经验丰富的区块链用户还是初学者，都能轻松上手使用该平台。 加入 Trustivon Validator 生态，共同构建更安全、更高效的区块链网络！
BNB / BSC 节点中的 BLS 密钥：原理、作用与架构升级解析
随着 BNB Chain（包含 BNB Beacon Chain 与 BNB Smart Chain，简称 BSC）持续演进，其共识层经历了重要升级：从早期基于 ECDSA 的签名机制，过渡到采用 BLS（Boneh–Lynn–Shacham）聚合签名的高性能 PoSA 共识架构。 在新的节点体系中，每个验证者除了传统的 ECDSA 私钥之外，还必须生成并配置一套 BLS 密钥。本文将系统性介绍： BLS 是什么 为什么 BNB/BSC 要使用 BLS BLS 密钥在共识中的具体作用 与 ECDSA 的对比 架构升级带来的性能与安全变化 一、为什么 BNB/BSC 需要 BLS？ BSC 早期使用的是 PoSA（Proof of Staked Authority）+ ECDSA 签名。 这种方式在功能上可用，但存在两个核心问题： 签名无法聚合，每个区块需要存储和验证大量签名 验证者数量和区块大小难以进一步扩展 随着 BNB Chain 融合 Beacon Chain 架构、提升 TPS 的需求，BNB 引入了基于 BLS 的聚合签名技术，使共识开销更低、性能更高。 二、BLS 的核心能力 BLS（Boneh–Lynn–Shacham）最重要的特点包括以下三点。 1. 支持签名聚合（Aggregate Signature） 多个验证者的签名可以数学地合并为一个签名： 100 个签名 最终只需要 1 个聚合签名 区块头体积极大缩小，使出块更快、网络传输更轻。 2. 原生支持阈值签名（Threshold Signature） 可以实现多数验证者共同生成一个有效签名： 超过 2/3 的验证者签名 自动生成一个可验证的单一签名 无需额外的多重签名协议。 3. 验证速度快、可扩展性强 在验证区块时： 节点只需验证一个聚合签名 而不需要验证每个验证者的单独签名 这使得共识的可扩展性显著提升。 三、BLS 私钥在 BNB/BSC 中的作用 在新的 BNB Chain 共识架构中，BLS 密钥承担的是“共识身份”和“投票签名”的职责，而不是交易签名。 它主要有以下三个作用。 1. 作为验证者的共识层身份密钥 在共识过程（prevote、precommit、aggregate）中，验证者使用 BLS 私钥对投票进行签名。 链上保存的是验证者的 BLS 公钥，用于： 验证投票来源 验证者轮换 共识身份验证 可以理解为验证者的“共识身份证”。 2. 生成区块的 BLS 聚合签名 每个区块高度都需要验证者投票确认。 使用 BLS 后： 所有验证者的投票可以聚合为一个签名 区块头中只写入一个 BLS 聚合签名 节点验证速度更快，区块体积更小 这是 BNB Chain 性能提升的关键机制。 3. 支持安全的阈值共识机制 PoSA 要求超过三分之二的验证者同意区块才能最终确认。 BLS 天生支持阈值验证，并提供： 更简单的投票验证逻辑 更强的防伪造能力 更安全的验证者切换 让整个共识层更安全可靠。 四、为什么不是使用 ECDSA？（对比） 能力 ECDSA BLS 签名聚合 不支持 支持 阈值签名 不支持，需要外部协议 原生支持 多验证者共识性能 一般 高效 区块头大小 大 小 扩展性 显著受限 强 用途 交易签名 共识投票签名 结论： 交易层仍然使用 ECDSA（钱包、合约交互等）。 共识层使用 BLS 才能获得高性能和更佳的扩展性。 五、BNB/BSC 架构升级后的变化 随着 BLS 的引入，BNB Chain 获得了以下提升。 1. 共识性能显著提升 减少共识消息体积、减少签名验证次数，使： 区块时间更稳定 TPS 上限提升 网络带宽消耗更低 2. 验证者数量可扩展 BSC 早期固定 21 个验证者，使用 BLS 后可以支持更多验证者参与，而不用担心过高的签名成本。 3. 更安全的验证者管理 BLS 将验证者的共识身份抽象为统一的公钥，使身份管理更彻底、更可控。 六、BNB/BSC 节点中的两套密钥 BNB/BSC 节点实际上使用两套密钥体系，用途完全不同。 1. ECDSA Key（交易密钥） 用于链上交易签名 secp256k1 曲线 与以太坊、MetaMask 完全一致 2. BLS Key（共识密钥） 仅验证者节点使用 只用于共识投票 不用于交易 不出现在任何钱包中 这两套密钥互不影响，各司其职。 七、总结 BLS 密钥在 BNB/BSC 中主要用于验证者共识层的投票与聚合签名，通过支持多签名聚合、阈值验证和快速校验，显著增强了区块确认速度、网络扩展性与整体安全性。它不用于交易，只用于共识层，是 BNB Chain 迈向高性能架构的重要基础组件。
1. 合约架构概述 核心合约 System.sol / SystemV2.sol：基础合约，定义系统常量、地址和修饰符 BSCValidatorSet.sol：验证者集合管理 StakeHub.sol：验证者质押管理 GovHub.sol：治理参数更新 SlashIndicator.sol：验证者行为监控与惩罚 SystemReward.sol：系统奖励管理 合约关系图 2. 各合约主要功能与方法 2.1 System.sol / SystemV2.sol 功能：定义系统常量、地址和修饰符 核心常量：各系统合约地址（VALIDATOR_CONTRACT_ADDR、SLASH_CONTRACT_ADDR等） 修饰符：onlyCoinbase、onlyZeroGasPrice、onlyGov等 2.2 BSCValidatorSet.sol 功能：管理验证者集合，处理惩罚和维护 对外方法： initializeFromGenesis：从创世块初始化验证者集合 updateValidatorSetV2：更新验证者集合 deposit：收集交易费用并分配 distributeFinalityReward：分配最终性奖励 getLivingValidators：获取活跃验证者 getMiningValidators：获取挖矿验证者 enterMaintenance：进入维护模式 exitMaintenance：退出维护模式 被链代码调用的方法： updateValidatorSetV2：由共识引擎调用 deposit：由区块生产者调用 distributeFinalityReward：由区块生产者调用 2.3 StakeHub.sol 功能：验证者质押管理，包括创建验证者、委托等 对外方法： createValidator：创建验证者 editConsensusAddress：编辑共识地址 editCommissionRate：编辑佣金率 editDescription：编辑描述 editVoteAddress：编辑投票地址 unjail：解锁验证者 delegate：委托 undelegate：取消委托 redelegate：重新委托 claim：领取取消委托的BNB 被链代码调用的方法： distributeReward：由验证者合约调用 downtimeSlash：由惩罚合约调用 maliciousVoteSlash：由惩罚合约调用 doubleSignSlash：由惩罚合约调用 2.4 GovHub.sol 功能：治理参数更新 对外方法： updateParam：更新参数，只能由治理时间锁合约调用 2.5 SlashIndicator.sol 功能：监控验证者行为，处理惩罚 对外方法： slash：惩罚未出块的验证者 clean：清理惩罚记录 submitFinalityViolationEvidence：提交最终性违规证据 submitDoubleSignEvidence：提交双重签名证据 被链代码调用的方法： slash：由区块生产者调用 clean：由验证者合约调用 2.6 SystemReward.sol 功能：系统奖励管理 对外方法： claimRewards：领取奖励，只能由操作员调用 被链代码调用的方法： claimRewards：由轻客户端、激励合约等调用 3. 合约调用关系 3.1 链代码调用合约 共识引擎 → BSCValidatorSet.updateValidatorSetV2 区块生产者 → BSCValidatorSet.deposit 区块生产者 → BSCValidatorSet.distributeFinalityReward 区块生产者 → SlashIndicator.slash 3.2 合约间调用 BSCValidatorSet → StakeHub.distributeReward BSCValidatorSet → SlashIndicator.clean SlashIndicator → BSCValidatorSet.felony SlashIndicator → BSCValidatorSet.misdemeanor SlashIndicator → StakeHub.downtimeSlash SlashIndicator → StakeHub.maliciousVoteSlash SlashIndicator → StakeHub.doubleSignSlash GovHub → BSCValidatorSet.updateParam GovHub → StakeHub.updateParam GovHub → SlashIndicator.updateParam GovHub → SystemReward.updateParam 4. 接口定义 4.1 0.6.x 接口 IBSCValidatorSet：验证者集合接口 ISlashIndicator：惩罚接口 IStakeHub：质押管理接口 IParamSubscriber：参数订阅接口 ISystemReward：系统奖励接口 4.2 0.8.x 接口 IBSCValidatorSet：验证者集合接口 IGovToken：治理代币接口 IStakeCredit：质押信用接口 IStakeHub：质押管理接口 5. 系统流程 5.1 验证者创建流程 用户调用 StakeHub.createValidator 创建验证者 StakeHub 部署 StakeCredit 代理合约 StakeHub 记录验证者信息 用户可以调用 delegate 进行委托 5.2 验证者惩罚流程 区块生产者调用 SlashIndicator.slash 惩罚未出块的验证者 SlashIndicator 检查惩罚阈值 如果达到 misdemeanor 阈值，调用 BSCValidatorSet.misdemeanor 如果达到 felony 阈值，调用 BSCValidatorSet.felony 和 StakeHub.downtimeSlash 5.3 参数更新流程 治理提案通过后，GovernorTimelock 调用 GovHub.updateParam GovHub 调用目标合约的 updateParam 方法 目标合约更新参数并触发事件 6. 总结 该合约系统是一个完整的区块链验证者管理和治理系统，主要包括： 验证者管理：BSCValidatorSet 负责验证者集合的更新和维护 质押管理：StakeHub 负责验证者的质押、委托等操作 惩罚机制：SlashIndicator 负责监控验证者行为并处理惩罚 治理系统：GovHub 负责参数更新 奖励管理：SystemReward 负责系统奖励的分配 合约之间通过明确的接口进行交互，形成了一个完整的生态系统，确保区块链网络的安全和稳定运行。
dApp开发入门教程：从零开始构建链上留言板
1. 什么是dApp？ dApp（去中心化应用）是基于区块链技术构建的应用程序，与传统应用的主要区别在于： 去中心化：数据存储在区块链上，不依赖单一服务器 透明公开：智能合约代码和数据对所有人可见 用户控制：用户完全控制自己的数据和资产 无需信任：通过智能合约自动执行，无需第三方中介 本教程将指导你从零开始构建一个完整的dApp——链上留言板，让你掌握dApp开发的核心流程。 2. 开发环境搭建 2.1 安装Node.js dApp开发需要Node.js环境，推荐使用LTS版本： 访问 Node.js官网 下载并安装适合你操作系统的LTS版本 验证安装：node -v npm -v 2.2 安装MetaMask MetaMask是连接dApp和区块链的桥梁： 访问 MetaMask官网 下载并安装浏览器插件 创建钱包并保存助记词 添加Trustivon测试网络（参考MetaMask网络设置） 2.3 获取测试代币 在Trustivon测试网上开发需要测试代币： 访问 Trustivon水龙头 输入你的MetaMask地址 领取测试代币 3. 智能合约开发 3.1 初始化Hardhat项目 Hardhat是以太坊智能合约开发的流行框架： 创建项目目录： mkdir doomsday-dapp cd doomsday-dapp 初始化npm项目： npm init -y 安装Hardhat： npm install --save-dev hardhat 初始化Hardhat项目： npx hardhat init 选择"Create a TypeScript project" 按默认选项完成初始化 安装依赖： npm install --save-dev @nomicfoundation/hardhat-toolbox npm install dotenv 3.2 编写智能合约 创建一个简单的链上留言板合约： 创建合约文件： mkdir -p contracts touch contracts/MessageBoard.sol 编写合约代码： // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; contract MessageBoard { struct Message { string content; address sender; uint256 bidAmount; uint256 timestamp; bytes32 messageId; } Message[] public messages; uint256 public constant MAX_MESSAGES = 100; uint256 public constant DECAY_INTERVAL = 24 hours; // 衰减间隔：24小时 uint256 public constant DECAY_RATE = 50; // 衰减率：50% uint256 public lastMessageTimestamp; // 最后一条留言的时间戳 event MessageAdded( bytes32 messageId, string content, address sender, uint256 bidAmount, uint256 timestamp ); /** * @dev 获取当前能进入前100名的最低竞价 * @return 最低竞价金额，如果留言数量不足100，则返回0 */ function getMinimumBidForTop100() public view returns (uint256) { if (messages.length < MAX_MESSAGES) { // 如果留言数量不足100，任何大于0的竞价都能进入前100 return 0; } // 获取第100条留言的原始竞价 uint256 originalBid = messages[MAX_MESSAGES - 1].bidAmount; // 如果最后一条留言时间为0，说明还没有留言，返回0 if (lastMessageTimestamp == 0) { return originalBid; } // 计算自最后一条留言以来经过的时间 uint256 timeElapsed = block.timestamp - lastMessageTimestamp; // 如果经过的时间小于衰减间隔，返回原始竞价 if (timeElapsed < DECAY_INTERVAL) { return originalBid; } // 计算经过了多少个衰减周期 uint256 decayPeriods = timeElapsed / DECAY_INTERVAL; // 计算衰减后的竞价 uint256 decayedBid = originalBid; for (uint256 i = 0; i < decayPeriods; i++) { // 每次衰减50% decayedBid = decayedBid * (100 - DECAY_RATE) / 100; // 防止衰减到0以下 if (decayedBid == 0) { break; } } return decayedBid; } function addMessage(string calldata _content, bytes32 _messageId) external payable { require(msg.value > 0, "Bid amount must be greater than 0"); // 检查当前竞价是否大于等于进入前100名的最低竞价 uint256 minimumBid = getMinimumBidForTop100(); require(msg.value > minimumBid, "Bid amount must be greater than the current minimum bid for top 100"); Message memory newMessage = Message({ content: _content, sender: msg.sender, bidAmount: msg.value, timestamp: block.timestamp, messageId: _messageId }); // 插入排序，按竞价金额降序 uint256 i = messages.length; messages.push(newMessage); while (i > 0 && messages[i - 1].bidAmount < newMessage.bidAmount) { messages[i] = messages[i - 1]; i--; } if (i != messages.length - 1) { messages[i] = newMessage; } // 只保留前100条留言 if (messages.length > MAX_MESSAGES) { messages.pop(); } // 更新最后一条留言的时间戳 lastMessageTimestamp = block.timestamp; emit MessageAdded( _messageId, _content, msg.sender, msg.value, block.timestamp ); } function getMessages() external view returns (Message[] memory) { return messages; } /** * @dev 分页获取留言 * @param _page 页码，从1开始 * @param _pageSize 每页数量 * @return 分页后的留言数组 */ function getMessagesPaginated(uint256 _page, uint256 _pageSize) external view returns (Message[] memory) { require(_page > 0, "Page must be greater than 0"); require(_pageSize > 0, "Page size must be greater than 0"); uint256 totalMessages = messages.length; uint256 startIndex = (_page - 1) * _pageSize; // 如果起始索引大于等于总留言数，返回空数组 if (startIndex >= totalMessages) { return new Message[](0); } // 计算结束索引 uint256 endIndex = startIndex + _pageSize; if (endIndex > totalMessages) { endIndex = totalMessages; } // 创建结果数组 uint256 resultSize = endIndex - startIndex; Message[] memory result = new Message[](resultSize); // 填充结果数组 for (uint256 i = 0; i < resultSize; i++) { result[i] = messages[startIndex + i]; } return result; } function getMessageCount() external view returns (uint256) { return messages.length; } // 允许合约接收ETH receive() external payable {} // 允许合约接收ETH（当调用不存在的函数时） fallback() external payable {} } 3.3 编译和测试合约 编译合约： npx hardhat compile 编写测试文件： mkdir -p test touch test/MessageBoard.test.js 编写测试代码： const { expect } = require("chai"); const { ethers } = require("hardhat"); describe("MessageBoard", function () { let MessageBoard; let messageBoard; let owner; let addr1; beforeEach(async function () { [owner, addr1] = await ethers.getSigners(); MessageBoard = await ethers.getContractFactory("MessageBoard"); messageBoard = await MessageBoard.deploy(); await messageBoard.deployed(); }); it("Should add a message", async function () { const content = "Hello, Trustivon!"; const messageId = ethers.utils.formatBytes32String("test-1"); const bidAmount = ethers.utils.parseEther("0.1"); await expect( messageBoard.addMessage(content, messageId, { value: bidAmount }) ) .to.emit(messageBoard, "MessageAdded") .withArgs(messageId, content, owner.address, bidAmount, expect.any(BigInt)); const messages = await messageBoard.getMessages(); expect(messages.length).to.equal(1); expect(messages[0].content).to.equal(content); expect(messages[0].sender).to.equal(owner.address); }); }); 运行测试： npx hardhat test 3.4 部署合约 创建部署脚本： mkdir -p scripts touch scripts/deploy.js 编写部署代码： const { ethers } = require("hardhat"); async function main() { const [deployer] = await ethers.getSigners(); console.log("Deploying contracts with the account:", deployer.address); console.log("Account balance:", (await deployer.getBalance()).toString()); const MessageBoard = await ethers.getContractFactory("MessageBoard"); const messageBoard = await MessageBoard.deploy(); await messageBoard.deployed(); console.log("MessageBoard contract deployed to:", messageBoard.address); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); }); 配置Trustivon网络： 创建.env文件：touch .env 添加配置：PRIVATE_KEY=your-private-key TRUSTIVON_RPC_URL=https://rpc.trustivon.com 修改hardhat.config.js，添加Trustivon网络配置 部署合约到Trustivon测试网： npx hardhat run scripts/deploy.js --network trustivon 4. 前端开发 4.1 初始化React项目 创建前端目录： npx create-react-app frontend cd frontend npm install web3 @web3-react/core @web3-react/injected-connector 创建合约ABI目录： mkdir -p src/contracts 复制合约ABI： cp ../artifacts/contracts/MessageBoard.sol/MessageBoard.json src/contracts/ 4.2 编写前端代码 创建Web3连接组件： mkdir -p src/components touch src/components/Web3Provider.js 编写Web3连接代码： import React, { createContext, useContext, useEffect, useState } from 'react'; import { InjectedConnector } from '@web3-react/injected-connector'; import Web3 from 'web3'; const Web3Context = createContext(); export const useWeb3 = () => useContext(Web3Context); export const Web3Provider = ({ children }) => { const [web3, setWeb3] = useState(null); const [account, setAccount] = useState(null); const [networkId, setNetworkId] = useState(null); const [loading, setLoading] = useState(true); const connector = new InjectedConnector({ supportedChainIds: [19478], // Trustivon测试网链ID }); const connectWallet = async () => { try { const accounts = await connector.activate(); setAccount(accounts[0]); } catch (error) { console.error('Failed to connect wallet:', error); } }; useEffect(() => { const initWeb3 = async () => { try { if (window.ethereum) { const web3Instance = new Web3(window.ethereum); setWeb3(web3Instance); const network = await web3Instance.eth.net.getId(); setNetworkId(network); const accounts = await web3Instance.eth.getAccounts(); if (accounts.length > 0) { setAccount(accounts[0]); } } } catch (error) { console.error('Failed to initialize Web3:', error); } finally { setLoading(false); } }; initWeb3(); // 监听账户变化 window.ethereum?.on('accountsChanged', (accounts) => { setAccount(accounts[0]); }); // 监听网络变化 window.ethereum?.on('chainChanged', (chainId) => { setNetworkId(parseInt(chainId, 16)); }); }, []); const value = { web3, account, networkId, loading, connectWallet, }; return <Web3Context.Provider value={value}>{children}</Web3Context.Provider>; }; 创建留言板组件： touch src/components/MessageBoard.js 编写留言板代码： import React, { useState, useEffect } from 'react'; import { useWeb3 } from './Web3Provider'; import contractABI from '../contracts/MessageBoard.json'; const CONTRACT_ADDRESS = 'your-contract-address'; // 替换为你的合约地址 const MessageBoard = () => { const { web3, account, connectWallet } = useWeb3(); const [messages, setMessages] = useState([]); const [content, setContent] = useState(''); const [bidAmount, setBidAmount] = useState('0.1'); const [loading, setLoading] = useState(false); const contract = web3 && new web3.eth.Contract(contractABI.abi, CONTRACT_ADDRESS); const fetchMessages = async () => { if (!contract) return; try { const result = await contract.methods.getMessages().call(); setMessages(result); } catch (error) { console.error('Failed to fetch messages:', error); } }; useEffect(() => { fetchMessages(); }, [contract]); const addMessage = async (e) => { e.preventDefault(); if (!contract || !account) return; setLoading(true); try { const messageId = web3.utils.sha3(Date.now().toString()); const value = web3.utils.toWei(bidAmount, 'ether'); await contract.methods .addMessage(content, messageId) .send({ from: account, value }); setContent(''); setBidAmount('0.1'); fetchMessages(); } catch (error) { console.error('Failed to add message:', error); } finally { setLoading(false); } }; if (!account) { return ( Connect Wallet ); } return ( 链上留言板 留言内容 setContent(e.target.value)} className="w-full p-2 border border-gray-300 rounded" rows="3" required /> 竞价金额 (TC) setBidAmount(e.target.value)} className="w-full p-2 border border-gray-300 rounded" step="0.1" min="0.1" required /> {loading ? '提交中...' : '提交留言'} 留言列表 {messages.length === 0 ? ( 暂无留言 ) : ( messages.map((msg, index) => ( {msg.sender} {new Date(msg.timestamp * 1000).toLocaleString()} {msg.content} 竞价: {web3.utils.fromWei(msg.bidAmount, 'ether')} TC )) )} ); }; export default MessageBoard; 更新App.js： import React from 'react'; import './App.css'; import { Web3Provider } from './components/Web3Provider'; import MessageBoard from './components/MessageBoard'; function App() { return ( <Web3Provider> <MessageBoard /> </Web3Provider> ); } export default App; 4.3 运行前端应用 启动前端开发服务器： npm start 在浏览器中访问 http://localhost:3000 连接MetaMask钱包 测试留言功能： 输入留言内容 设置竞价金额 提交留言 查看留言列表 5. 部署和上线 5.1 构建前端应用 构建生产版本： npm run build 部署到静态网站托管服务（如Vercel、Netlify等） 5.2 验证和测试 在浏览器中访问部署后的网站 测试所有功能 确保与MetaMask正常交互 检查交易是否正确上链 6. 总结和扩展 6.1 开发总结 通过本教程，你已经学会了： 搭建dApp开发环境 编写和部署智能合约 开发React前端应用 连接Web3和MetaMask 与智能合约交互 6.2 扩展建议 你可以进一步扩展这个dApp： 添加用户认证和个人中心 实现留言编辑和删除功能 添加留言点赞和评论功能 实现留言搜索和筛选 添加链上身份验证 优化前端UI/UX设计 6.3 学习资源 Solidity官方文档 Hardhat文档 Web3.js文档 React官方文档 MetaMask文档 7. 常见问题 7.1 无法连接MetaMask 确保MetaMask已安装并解锁 确保已添加Trustivon测试网络 刷新页面后重试 7.2 交易失败 确保钱包中有足够的测试代币 检查Gas费用设置 查看MetaMask交易记录中的错误信息 7.3 留言不显示 检查合约地址是否正确 确保网络连接正常 刷新页面后重试 8. 社区支持 Trustivon GitHub Trustivon官方网站 Trustivon开发者文档 Telegram技术支持 恭喜你完成了第一个dApp的开发！继续学习和探索，你将能够构建更复杂和强大的去中心化应用。 线上预览：https://eternal.trustivon.com/ GitHub开源：https://github.com/Trustivon/eternal-message-dapp
对比 BNB Chain（BSC）旧版与新版 genesis-template.json：从 Parlia 到多硬分叉时代的全面升级
BNB Chain（BSC）近两年进行了大幅结构升级，特别是在 genesis-template.json 中，引入了多版本硬分叉、时间激活机制、Blob（EIP-4844）、EIP-1559、Deposit 接口等框架性变更，使其从传统 “Parlia 简易模式” 升级为与以太坊主网高度兼容的现代化架构。 本文将对比旧版与新版 BSC genesis-template.json 的结构变化，并解释每个字段设计背后的技术原因。 1. 旧版 BSC Genesis —— 只有 Parlia 配置的简易模式 早期版本的 BSC（包括 Mainnet 与 Testnet 很长时间）仅在 genesis 中包含 Parlia 共识参数： "parlia": { "period": 3, "epoch": 200 } 1.1 字段含义 字段 作用 period 出块间隔（秒），默认 3 秒 epoch validator 切换周期（多少区块重新选举），默认 200 1.2 特点 结构简单 没有 EIP-1559 没有 EIP-4844（Blob） 没有时间控制的 Hardfork 与现代以太坊协议差距越来越大 旧版 BSC genesis 的核心只有 Parlia 配置，因此功能扩展能力有限。 2. 新版 BSC Genesis —— 多硬分叉 + 时间激活 + Blob 时代 BNB Chain 在 2024–2025 年陆续引入了大量升级（Feynman、Haber、Pascal、Prague、Lorentz…），genesis 文件结构变得高度现代化： "feynmanFixTime": 0, "cancunTime": 0, "haberTime": 0, "haberFixTime": 0, "bohrTime": 0, "pascalTime": 0, "pragueTime": 0, "lorentzTime": 0, "depositContractAddress": "0x0000000000000000000000000000000000000000", "parlia": {}, "blobSchedule": { "cancun": { "target": 3, "max": 6, "baseFeeUpdateFraction": 3338477 }, "prague": { "target": 3, "max": 6, "baseFeeUpdateFraction": 3338477 } } 下面逐项解析这些字段。 3. 新增字段解析（硬分叉、升级机制、Blob） 3.1 时间激活（Timestamp-based）硬分叉 新版 BSC 使用 Timestamp-based Hardfork 与以太坊主网保持一致。 字段 说明 feynmanFixTime Feynman 修复分叉激活时间 cancunTime Cancun 升级激活时间（支持 EIP-4844 Blob） haberTime Haber 升级 haberFixTime Haber 修复版 bohrTime Bohr 升级 pascalTime Pascal 升级 pragueTime Prague 升级 lorentzTime Lorentz 升级 统一规则： 设置为 0 表示链启动后立即激活所有功能 → 适合私链 / DevNet / 测试链 → 公链通常使用具体 Unix 时间戳 从此 BSC 不再使用区块高度（blockNumber）触发升级，而全面迁移到 ETH 的时间触发机制，升级可控性更强。 3.2 parlia 变成空对象 旧版的： "parlia": { "period": 3, "epoch": 200 } 新版变成： "parlia": {} 这是因为： ✔ 新版 BSC 内置默认值 period = 3 epoch = 200 如果需要自定义，可以继续写入： "parlia": { "period": 1, "epoch": 200 } 3.3 EIP-4844 Blob 交易：blobSchedule BSC 新版开始兼容以太坊 Cancun 升级（EIP-4844），新增 Blob Gas 调整参数： "blobSchedule": { "cancun": { "target": 3, "max": 6, "baseFeeUpdateFraction": 3338477 }, "prague": { "target": 3, "max": 6, "baseFeeUpdateFraction": 3338477 } } 字段 说明 target 每个块的目标 Blob 数量 max 每块最大 blob 数量 baseFeeUpdateFraction Blob baseFee 调整参数 这是与 ETH 主网完全一致的 Blob Gas 定价模型，用于 Rollup / 数据可用性（DA）场景。 3.4 depositContractAddress "depositContractAddress": "0x0000000000000000000000000000000000000000" 目前 BSC 仍是 PoSA（并非 ETH PoS），但这一字段用于兼容未来可能的信标链接口，是预留字段。 4. 对比总结：从「简单模型」到「现代以太坊兼容链」 项目 旧版 BSC 新版 BSC 共识 Parlia Parlia（默认配置） 升级机制 blockNumber 激活 timestamp 激活（与 ETH 主网一致） EIP-1559 ❌ 不支持 ✔ 支持 EIP-4844 Blob ❌ 不支持 ✔ 支持 硬分叉版本 基本无 Feynman / Haber / Pascal / Prague / Lorentz genesis 结构 极简 高度模块化 可扩展性 较弱 强，接近 ETH 主网协议栈 新版 BSC 通过一套统一的时间激活 hardfork 机制，使其升级模型、blob gas 模型、EIP 兼容性全面向以太坊主网对齐。 5. 为什么 BSC 必须升级？ 需求 原因 兼容 ETH 主网升级（Cancun/Prague） 保持生态一致性 引入 Blob（EIP-4844） 提升 DA 能力、支持 L2 统一升级机制 简化多版本支持 为未来 PoS 预留功能 兼容 depositContractAddress 拓展链能力 支持更现代的 Gas 模型与交易类型 因此新版 genesis 是一个从 ETH 主网演化而来的高度可扩展结构。
doesn't look like an ERC 1967 proxy with a logic contract addres
背景 使用 upgrades.erc1967.getImplementationAddress 来获取通过 upgrades.deployProxy 部署的合约的逻辑合约地址（implementation address） 测试部署脚本 const [deployer] = await ethers.getSigners(); console.log("Deployer:", await deployer.getAddress()) const commonBridgeFactory = await ethers.getContractFactory("CommonBridge", deployer); let commonBridgeProxyContract; if (deployOutput.commonBridgeProxyContract === undefined || deployOutput.commonBridgeProxyContract === '') { commonBridgeProxyContract = await upgrades.deployProxy( commonBridgeFactory, [ process.env.DOMAIN_ID, process.env.SUPER_ADMIN_ADDRESS, process.env.BRIDGE_FEE_ADDRESS ], { constructorArgs: [], unsafeAllow: ['constructor', 'state-variable-immutable'], }); console.log('tx hash:', commonBridgeProxyContract.deploymentTransaction().hash); } else { commonBridgeProxyContract = commonBridgeFactory.attach(deployOutput.commonBridgeProxyContract); } console.log('commonBridgeProxyContract deployed to:', commonBridgeProxyContract.target); await sleep(6000); const commonBridgeImpl = await upgrades.erc1967.getImplementationAddress(commonBridgeProxyContract.target); console.log('commonBridgeProxyContract implementation deployed to:', commonBridgeImpl); 部署超时 Error: Timed out waiting for implementation contract deployment to address 0x5393... with transaction 0x9492... 解决：增加超时和轮询设置 ... commonBridgeProxyContract = await upgrades.deployProxy( commonBridgeFactory, [ process.env.DOMAIN_ID, process.env.SUPER_ADMIN_ADDRESS, process.env.BRIDGE_FEE_ADDRESS ], { constructorArgs: [], unsafeAllow: ['constructor', 'state-variable-immutable'], timeout: 300000, // 5分钟 pollingInterval: 5000, // 每5秒轮询一次 }); console.log('tx hash:', commonBridgeProxyContract.deploymentTransaction().hash); ... 不符合 ERC1967 标准 使用 upgrades.erc1967.getImplementationAddress 来获取通过 upgrades.deployProxy 部署的合约的逻辑合约地址（implementation address），结果报：部署地址不符合 ERC1967 标准的代理合约（Proxy） Error: Contract at 0xffa5f716D890CFd4f9D709A40d6EB7Fb19fC43B4 doesn't look like an ERC 1967 proxy with a logic contract address const implSlot = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc'; const proxyAddress = '0xffa5f716D890CFd4f9D709A40d6EB7Fb19fC43B4'; const storage = await ethers.provider.getStorage(proxyAddress, implSlot); console.log("Raw implementation slot:", storage); const implAddress = '0x' + storage.slice(-40); console.log("Implementation address:", implAddress); const implAddress1 = await upgrades.erc1967.getImplementationAddress(proxyAddress); console.log("Implementation address1:", implAddress1); 如果返回的 implStorage 是全零（0x000...），说明实现地址确实没有写进去，可能部署时失败或未初始化。 如果你用其他方式部署 Proxy（比如自己 new TransparentUpgradeableProxy，而不是用 upgrades.deployProxy()），OpenZeppelin 插件就不能保证你用了它支持的初始化逻辑。 解决： 是因为网络延迟原因，增加延迟时间 console.log('commonBridgeProxyContract deployed to:', commonBridgeProxyContract.target); await sleep(20000); // 提高延迟间隔 const commonBridgeImpl = await upgrades.erc1967.getImplementationAddress(commonBridgeProxyContract.target); console.log('commonBridgeProxyContract implementation deployed to:', commonBridgeImpl); 最终版本 const [deployer] = await ethers.getSigners(); console.log("Deployer:", await deployer.getAddress()) const commonBridgeFactory = await ethers.getContractFactory("CommonBridge", deployer); let commonBridgeProxyContract; if (deployOutput.commonBridgeProxyContract === undefined || deployOutput.commonBridgeProxyContract === '') { commonBridgeProxyContract = await upgrades.deployProxy( commonBridgeFactory, [ process.env.DOMAIN_ID, process.env.SUPER_ADMIN_ADDRESS, process.env.BRIDGE_FEE_ADDRESS ], { constructorArgs: [], unsafeAllow: ['constructor', 'state-variable-immutable'], timeout: 300000, pollingInterval: 5000, }); console.log('tx hash:', commonBridgeProxyContract.deploymentTransaction().hash); } else { commonBridgeProxyContract = commonBridgeFactory.attach(deployOutput.commonBridgeProxyContract); } console.log('commonBridgeProxyContract deployed to:', commonBridgeProxyContract.target); await sleep(20000); const commonBridgeImpl = await upgrades.erc1967.getImplementationAddress(commonBridgeProxyContract.target); console.log('commonBridgeProxyContract implementation deployed to:', commonBridgeImpl);
1. Intel 软件防护扩展技术 Intel® SGX Intel® SGX 的设计主旨是为用户应用程序提供可信的执行环境，使得应用程序有能力在用户地址空间中开辟一段特别的，受保护的内存空间，并对这段受保护的内存空间实行严格的访问控制和加密操作来保障数据机密性和代码完整性，确保即使是 Hypervisor、BIOS，操作系统等特权应用都不能随意访问这段地址空间，这一段地址空间被称之为 Enclave（称之为飞地）。 按照文章的前部分介绍，把应用程序部署在云上，需要验证程序确实运行在 TEE 里，具体更多的 SGX 技术细节，请参见 Intel 官方的技术文档。以及 Intel 架构开发者手册。验证包括本地验证和远程验证。本地验证指在同一个平台上，让不同的 Enclave 互相验证 Trusted Computing Base（TCB）。远程验证是指让一个平台中运行的 Enclave 向远端的信赖凭证者 Relying Party 证明自己的 TCB，证明自己运行在 SGX TEE 中，并且没有被篡改，同时证明当前的 CPU SGX 安全版本信息。 SGX 应用程序涉及两部分：安全区和非安全区。开发者或者用户可以把涉及敏感数据的处理、Key 的保护等都放在 Enclave 里面，从而将应用程序的代码、数据划分为受信任和不受信任的组件，因此开发者或者用户需要决定哪些组件应该置于 Enclave 内部，哪些置于 Enclave 外部。 2. Intel 信任域扩展技术 Intel® TDX Intel® TDX 旨在将虚拟机（VM）与平台上的虚拟机管理程序（VMM）和任何其他非信任域 Trust Domain (TD) 的软件隔离，以保护 TD VM 免受各种软件的安全威胁。 TDX 的设计思路是将整个虚拟机 VM 放在一个 TEE 可信执行环境里，这样不管应用在私有云还是公有云上，不需要再对应用程序和数据做受信任和不受信任的划分和修改，只要操作系统支持就行。 TDX 基本思路就是引入新的 CPU 工作模式，然后通过对内存加密技术，将两个虚拟机用不同的 key 加密，同时 key 由 CPU 来直接进行管理。本质上讲，TDX 是通过两种技术方式来配合实现，一种就是新的 CPU 模式，叫做安全仲裁模式 Secure Arbitration Mode（SEAM），第二种是多密钥内存加密技术方式 Multi-key Total-Memory-Encryption（MKTME）。SEAM 模式使用新的指令，例如 SEAMCALL, SEAMRET, SEAMOPS, TDCALL 等来实现和 TD OS，以及 Host/VMM 交互。同时，提供特定区域的物理内存来保护 Intel TDX Module 代码模块，通过 Intel Authenticated Code Module (SEAMLDR)来装载。多密钥内存加密 MKTME 技术中，MKTME engine 通过 PCONFIG 指令来分配和设置各个 TD VM 的内存加密 Keys。TDX 将内存分为两个部分，私有和共享内存。在 TDX 下还是需要在外部加固的，加固的时候需要共享内存，这部分内存外部是可以读取的，因为有些虚拟化或半虚拟化设备需要和外部通信时，是需要 host 操作系统有能力访问来提供网络等服务的。 对比 当前的 Intel® TDX 认证的本质原理和 SGX 认证类似，也需要提供远程证明来验证其运行在 TEE 环境里。更多详细的 TDX 技术说明，请参见 Intel 官方网站的 Intel® TDX 技术规范和白皮书。 Intel® TDX 和 Intel® SGX 是用来解决不同问题的。TDX 是把整个软件包不做修改直接放到虚拟机上就能实现安全，而 SGX 关注应用层面，通过对飞地 Enclave 来保障机密性和完整性，用户根据业务场景需求可以选择不同的技术。 参考文档 https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/overview.html https://www.intel.com/content/www/us/en/products/docs/accelerator-engines/software-guard-extensions.html https://cloud.tencent.com/developer/news/918918
基础信息 官网：https://www.ata.network/ GitHub： https://github.com/automata-network docs: https://docs.ata.network/ 重点关注项目 https://github.com/automata-network/automata-sgx-sdk https://github.com/automata-network/sgx-prover https://github.com/automata-network/sgx-scaffold/tree/main https://github.com/automata-network/automata-dcap-attestation https://github.com/automata-network/SGXDataCenterAttestationPrimitives 文档关键信息 TEE 验证器的设计 带有 Scroll 的 TEE Prover 的架构有两个主要组件： SGX 证明器。一种链下组件，用于检查安全区域内区块执行后状态根是否与现有状态根匹配，并将执行证明 (PoE) 提交给 SGX 验证器。 SGX 验证器。确认 SGX 证明器提出的状态转换正确性的 L1 合约。它还验证 Intel SGX 飞地提交的证明报告，以确保证明器的完整性。 Intel SGX 的链上验证 远程认证允许以编程方式验证英特尔 SGX 飞地的属性和完整性。这是建立并确保其执行的任何计算或数据处理都是值得信赖的关键过程。 https://docs.ata.network/tee-overview/tee-prover 多重证明器 AVS 的工作原理 任务提交：协议构建者参与 AVS 并提交多重证明任务。在汇总场景中，这包括对 L2 向 L1 提交的批量交易进行采样和证明。 验证器注册：独立操作员注册以承担这些任务。操作员利用可重现的构建来运行 TEE 验证器，并通过 Automata 的TEE Compile进行验证，以确保构建过程的完整性。 执行和证明：每个操作员（证明者）在其选择的 TEE 平台上执行任务。生成证明以验证执行的正确性和完整性。 链上验证：将证明提交至区块链，智能合约充当证明人的角色并在链上进行验证。 奖励发放：成功完成任务后，向操作员发放奖励。 https://docs.ata.network/tee-overview/multi-prover-avs-eigenlayer multi-prover-avs 代码分析 GitHub： https://github.com/automata-network/multi-prover-avs/tree/main 目录结构 ═── contract：Solidity合约，包括AVS合约和证明层合约。 │ │ │ ── dcap-v3-attestation：英特尔 SGX 的 Dcap 认证链上验证库。 │ │ ── src：AVS 合约的源文件。 │ └── test：智能合约的测试。 ═── operator：运算符实现。 ═── aggregator：聚合器实现。 ═── sgx-prover：TEE 证明器的 sgx 版本。 AVS任务描述 任务定义：寻求利用可信执行环境 (TEE) 内的独立执行来确定其正确性的状态转换或计算过程。 struct StateHeader { uint256 identifier; bytes metadata; bytes state; } 这是提交给证明者的状态头结构，下面是详细解释： identifier：处理任务的标识符，用于区分不同类型的任务，并用于计算各个算子的贡献 metadata：描述特定任务的元数据，例如keccak256(abi.encodePacked(chainID, blockNumber))用于证明特定区块高度的区块链状态的任务的元数据 state：TEE 证明器产生的最终状态，可以是区块链的根状态，也可以是 zk 电路证明的语句 AVS 架构 AVS 的架构包含： Eigenlayer 核心合约 AVS 合同 未来将添加 ServiceManager，允许操作员提交任务、奖励和削减逻辑 鉴证合约 管理各种 TEE 证明器的注册/注销和活跃性，它将验证不同 TEE 平台的认证，例如 Intel SGX、AMD SEV、ARM TrustZone 等 TEEProverRegister 是运营商和聚合器使用的认证层的接口 聚合器 汇总来自操作员的 BLS 签名并将汇总状态提交给 AVS 与自动机证明层交互，检查每个证明者（操作员）的有效性，未能通过证明验证或活性挑战的将被拒绝处理任务，直到其再次有效。 操作员 从 TEE 证明者处获取状态证明并将其提交给聚合器 TEE 证明器 TEE 证明器可以证明给定任务的最终状态，例如 zk-rollup L2 的证明器将在 TEE 内部执行区块并在特定区块处生成根状态 AVS 工作流程 以下是工作流程的详细图表 成分： 操作员 聚合器 多功能AVS TEE 活跃度合约 鉴证合同 工作流程分为两部分： 设置 按照Eigenlayer 的文档进行质押并注册成为 Multi-prover AVS 的运营商 生成证明并注册为 TEE 证明者，不同的 TEE 技术，证明及其生成过程不同。例如，dcap-v3-attestation是验证 Intel SGX Dcap 证明的合约 在职的 除了操作员处理任务之外，他们必须定期完成活性挑战，否则将被视为无效，其提交将被聚合器拒绝 算子获取新任务并在 TEE 内部完成计算 操作员签署最终状态并将其与签名一起发送给聚合器 聚合器将在接受运营商的提交之前获取其有效性 聚合器聚合所有 BLS 签名并提交给 AVS 服务管理器 TEE 委员会和法定人数 TEE 委员会是一组负责处理特定类型任务的法定人数。例如，在特定区块高度证明 Zk-Rollup 的根状态。操作员无需主动选择委员会，而是通过加入法定人数自动属于委员会。引入有利于TEE Committee操作员和任务更有组织的结构化。并为未来的增强奠定了基础，包括奖励机制和法定人数之间权益分配的约束。 TEE Quorum的概念与 Eigenlayer 使用的 Quorum 定义一致，但每个 Quorum 都与一个 TEE 平台相关联，例如 Intel SGX。每个 Quorum 都属于一个委员会，操作员可以选择加入任何 Quorum，但只有拥有必要证明的操作员的投票才会被聚合器接受。 如果您有兴趣加入以太坊主网上的 Multi-Prover AVS，请访问Operator 设置存储库。入职指南可在此处获取。 从源代码编译 操作员 go build -o out/operator ./cmd/operator 聚合器 go build -o out/aggregator ./cmd/aggregator 相关文档 https://scroll.io/blog/scaling-security https://blog.ata.network/verifiability-as-the-missing-piece-for-ai-agents-in-web3-504839dca893 https://blog.ata.network/towards-a-common-tee-stack-71a7812a4bf9 https://blog.ata.network/automata-joins-the-superchain-ecosystem-launching-on-optimisms-op-stack-da25840cc658 https://atanetwork.notion.site/Multi-Prover-AVS-with-TEE-545319c42885489196142d966f0ede86 https://gramine.readthedocs.io/en/stable/attestation.html https://sgx101.gitbook.io/sgx101