完整管理指南:创建、权限轮换、手续费与关闭)
Solana 验证者投票账户Vote Account完整管理指南创建、权限轮换、手续费与关闭【免费下载链接】solanaWeb-Scale Blockchain for fast, secure, scalable, decentralized apps and marketplaces.项目地址: https://gitcode.com/GitHub_Trending/so/solana本文以 Solana 仓库中 投票账户管理指南 为主体系统讲解如何创建链上投票账户vote account、理解其四大核心属性账户地址、验证者身份、投票权限、提款权限与手续费commission机制并给出面向线上运行中验证者的密钥轮换key rotation完整操作步骤与源码级实现佐证。读完后你将能够独立完成投票账户的创建与配置、安全地轮换身份/投票/提款密钥、在 epoch 边界平滑切换投票权限并理解 vote 程序对这些操作的底层校验逻辑。创建投票账户在 Solana 上运行验证者节点的前提是拥有一个链上投票账户。文档明确说明投票账户可以在创建时配置也可以在验证者上线运行后再调整除投票账户地址vote account address在账户生命周期内固定不变外其余所有方面身份、投票权限、提款权限、commission都可以更改。创建账户使用 CLI 命令solana create-vote-account。该命令的参数在 CLI 投票子命令实现 中定义其中--commission参数帮助文本为 “The commission taken on reward redemption (0-100)”见 cli/src/vote.rs#L83-L88即以u8类型解析、取值范围为 0–100 的整数。创建完成后账户结构对应的链上状态由 vote 程序维护其初始字段由VoteInit结构承载// sdk/program/src/vote/state/mod.rs pub struct VoteInit { pub node_pubkey: Pubkey, // validator identity pub authorized_voter: Pubkey, // vote authority pub authorized_withdrawer: Pubkey, pub commission: u8, }见 sdk/program/src/vote/state/mod.rs#L211-L216配置已有的投票账户对已存在的投票账户文档列出了四类变更操作及其对应命令变更目标CLI 命令源码处理入口validator identitysolana vote-update-validatorupdate_validator_identityvote authoritysolana vote-authorize-voter-checkedauthorizeauthorized withdrawersolana vote-authorize-withdrawer-checked同上VoteAuthorize::Withdrawer分支commissionsolana vote-update-commissionupdate_commission对应的 CLI 子命令注册vote-authorize-voter-checked、vote-update-validator、vote-update-commission、close-vote-account均可在 cli/src/vote.rs 中找到定义与解析逻辑parse_vote_update_commission见 cli/src/vote.rs#L617process_vote_update_commission最终调用vote_instruction::update_commission构造指令见 cli/src/vote.rs#L1134-L1154。投票账户结构四大属性与 commission投票账户的完整链上状态定义在 VoteStatepub struct VoteState { /// the node that votes in this account pub node_pubkey: Pubkey, /// the signer for withdrawals pub authorized_withdrawer: Pubkey, /// percentage (0-100) that represents what part of a rewards /// payout should be given to this VoteAccount pub commission: u8, pub votes: VecDequeLandedVote, pub root_slot: OptionSlot, /// the signer for vote transactions authorized_voters: AuthorizedVoters, /// history of prior authorized voters and the epochs for which /// they were set (包含范围) prior_voters: CircBuf(Pubkey, Epoch, Epoch), /// history of how many credits earned by the end of each epoch pub epoch_credits: Vec(Epoch, u64, u64), pub last_timestamp: BlockTimestamp, }从该结构可以印证文档中各属性的实现细节node_pubkey即 validator identityauthorized_voters注意是AuthorizedVoters容器而非单一Pubkey配合prior_voters环形缓冲区实现了“投票权限每 epoch 至多变更一次、下一个 epoch 生效”的平滑切换语义commission为 0–100 的u8。投票账户地址Vote Account Address投票账户创建在两类地址之一某个密钥对文件的公钥或基于密钥对公钥加种子字符串派生的地址。文档强调该地址从不用于签署交易仅用于查询账户信息当他人向该验证者委派质押时delegation 指令指向的正是这个投票账户地址。Validator Identity验证者身份validator identity 是一个系统账户负责支付提交到投票账户的所有投票交易费用。由于验证者预期要对收到的大部分有效区块投票identity 账户会频繁可能每秒多次签署交易并支付费用因此 identity 密钥对必须以“热钱包”形式存放在验证者进程所在系统的密钥对文件中。文档同时给出了安全实践建议热钱包的安全性低于离线冷钱包因此运营者可以让 identity 账户只保留覆盖有限时间数周或数月投票费用的 SOL并定期从更安全的钱包充值。这可以在验证者磁盘或文件系统被破坏或攻陷时降低资金损失风险。实现层面更新身份的双签要求体现在 update_validator_identity 中// current authorized withdrawer must say yay verify_authorized_signer(vote_state.authorized_withdrawer, signers)?; // new node must say yay verify_authorized_signer(node_pubkey, signers)?; vote_state.node_pubkey *node_pubkey;即必须由当前 authorized withdrawer 与新身份节点同时签名这与文档“修改 validator identity 需要 withdrawer 参与签名”的说法完全一致。Vote Authority投票权限vote authority 密钥对用于签署验证者节点要提交到集群的每一笔投票交易。由于同样高频签名它也必须是与验证者进程同文件系统的热密钥。文档给出两个关键要点可与 identity 合并以减半交易费用Solana 的交易费按签名数量计费若 vote authority 与 validator identity 设为同一地址每笔投票交易只需一个签名即可同时完成“签票”和“付费”相比两个不同账户费用减半。每 epoch 至多变更一次下个 epoch 生效若未在建账时显式指定 vote authority默认与 validator identity 相同之后可用solana vote-authorize-voter-checked修改变更将在下一个 epoch 开始时生效。源码中该语义由 authorize 的VoteAuthorize::Voter分支实现VoteAuthorize::Voter { let authorized_withdrawer_signer verify_authorized_signer(vote_state.authorized_withdrawer, signers).is_ok(); vote_state.set_new_authorized_voter( authorized, clock.epoch, clock.leader_schedule_epoch.checked_add(1) .expect(epoch should be much less than u64::MAX), |epoch_authorized_voter| { // current authorized withdrawer or authorized voter must say yay if authorized_withdrawer_signer { Ok(()) } else { verify_authorized_signer(epoch_authorized_voter, signers) } }, )?; }可见新投票权限的生效 epoch 取clock.leader_schedule_epoch 1而签名校验允许“当前 withdrawer 或当前 epoch 有效的 authorized voter”任一签署这正是vote-authorize-voter-checked当前权限方签名与 checked 语义的来源。为支撑跨 epoch 的平滑过渡solana-validator允许--authorized-voter参数多次指定。这一点在验证者 CLI 定义中得到确认见 validator/src/cli.rs#L85-L97.arg( Arg::with_name(authorized_voter_keypairs) .long(authorized-voter) .value_name(KEYPAIR) .takes_value(true) .validator(is_keypair_or_ask_keyword) .requires(vote_account) .multiple(true) // 可多次指定 .help(Include an additional authorized voter keypair. May be specified multiple times. \ [default: the --identity keypair]), )验证器进程同时持有新旧两个 authorized voter 密钥对时网络到达 epoch 边界、投票权限切换发生投票不会中断。Authorized Withdrawer授权提款人authorized withdrawer 用于通过solana withdraw-from-vote-account命令从投票账户提款。验证者获得的所有网络奖励都存入投票账户且只能由 authorized withdrawer 密钥签名才能取回。文档还指出该密钥还须参与签署两类交易修改 commission、变更 validator identity——这与上文 update_validator_identity 和 update_commission 中verify_authorized_signer(vote_state.authorized_withdrawer, signers)?的强制校验一致。安全建议被盗取的 withdrawer 密钥会让攻击者完全掌控验证者的运营因此应将其保存在离线冷钱包中验证者运行期间用不到它不应存放在验证者机器上。文档还规定withdrawer 在创建投票账户时必须指定且不得与 validator identity 密钥或 vote authority 密钥相同。修改则使用solana vote-authorize-withdrawer-checked命令源码路径在 authorize 的Withdrawer分支VoteAuthorize::Withdrawer { // current authorized withdrawer must say yay verify_authorized_signer(vote_state.authorized_withdrawer, signers)?; vote_state.authorized_withdrawer *authorized; }Commission手续费比例commission 是验证者从所获网络奖励中截留的百分比存入口袋即其投票账户剩余奖励按各委派质押账户的 active stake 权重比例分配给它们。文档示例commission 为 10% 时该 epoch 内赚取的奖励中 10% 会在下一 epoch 的第一个区块存入投票账户其余 90% 以即时激活immediately active的质押形式存入被委派的质押账户。运营策略上低 commission 有助于吸引质押委派但考虑到搭建与运行验证者的成本佣金应至少能覆盖开支。关键规则及其源码依据默认 100%创建时若不提供--commission默认取 100%即全部奖励进入投票账户、不向任何质押账户分发。CLI 解析逻辑印证了这一点cli/src/vote.rs#L449 中let commission value_t_or_exit!(matches, commission, u8);未提供时按默认值构造VoteInit该文件测试用例中可见commission: 100的默认断言见 cli/src/vote.rs#L1787 一带。仅接受 [0-100] 的整数u8类型天然保证 0–255文档约束为 0–100 个整数百分点--commission 10即 10%。只能在每个 epoch 的前半段修改目的是防止验证者“先把佣金调低吸引质押、epoch 末奖励发放前一刻调高、发放后再调回”来窃取委派者奖励。该规则由 update_commission 强制当commission_updates_only_allowed_in_first_half_of_epochfeature 激活且判定为“佣金上调”时若不在允许窗口内则返回VoteError::CommissionUpdateTooLate。判定函数 is_commission_update_allowed 以relative_slot * 2 slots_per_epoch精确实现“epoch 前半段”pub fn is_commission_update_allowed(slot: Slot, epoch_schedule: EpochSchedule) - bool { if let Some(relative_slot) slot .saturating_sub(epoch_schedule.first_normal_slot) .checked_rem(epoch_schedule.slots_per_epoch) { // allowed up to the midpoint of the epoch relative_slot.saturating_mul(2) epoch_schedule.slots_per_epoch } else { true } }从源码结构看还有一个后续演进allow_commission_decrease_at_any_timefeature 激活后下调佣金不再受前半段窗口限制只有上调才受限见 cli/src/vote.rs 对应的update_commission调用链中is_commission_increase分支定义于 programs/vote/src/vote_state/mod.rs#L940-L942。因此文档所述“前半段限制”针对的是防止“先低后高”的恶意上调路径。密钥轮换Key Rotation对线上运行的验证者做权限密钥轮换需要专门处理。文档首先强调一条重要事实投票账户密钥轮换不影响已委派到该投票账户的质押账户——例如可以通过密钥轮换把投票账户的全部权限移交给另一实体而对质押奖励零影响。轮换 Validator Identity前提需要访问该投票账户的authorized withdrawer密钥对以下示例假设位于~/authorized_withdrawer.json。创建新的 validator identity 密钥对solana-keygen new -o ~/new-validator-keypair.json确保新身份账户已充值solana transfer ~/new-validator-keypair.json 500执行solana vote-update-validator ~/vote-account-keypair.json ~/new-validator-keypair.json ~/authorized_withdrawer.json以修改投票账户中的 validator identity。使用新 identity 密钥对作为--identity参数重启验证者。若验证者已持有质押还需要额外步骤。文档解释leader schedule 是提前两个 epoch计算的因此旧 validator identity 在变更后的最长两个 epoch 内仍会留在 leader schedule 中如果不做额外处理在新身份被纳入 leader schedule 之前验证者将不产出任何区块。正确做法按第 4 步用新身份重启后在另一台机器上用旧 identity 密钥对启动一个第二的非投票验证者——不提供--vote-account参数同时提供--no-wait-for-vote-to-start-leader参数。该临时验证者应完整运行两个 epoch期间它会为分配给旧 validator identity 的剩余 slot 产出区块收取旧 validator identity 对应的交易费与 rent 奖励。当solana leader-schedule输出中不再列出旧 identity 时即可安全停止这个临时验证者。--no-wait-for-vote-to-start-leader参数确实存在于验证者 CLI 定义中见 validator/src/cli.rs#L760非投票模式不带--vote-account即禁用投票见 validator/src/cli.rs#L105-L108 的帮助文本 “If unspecified, voting will be disabled”与该步骤描述一致。轮换 Vote AuthorityAuthorized Votervote authority只能在 epoch 边界变更且需要为solana-validator提供额外参数以实现无缝迁移运行solana epoch-info。若当前 epoch 剩余时间不多考虑等到下一个 epoch 再动手给验证者留出充足的追赶时间。创建新的 vote authority 密钥对solana-keygen new -o ~/new-vote-authority.json通过solana vote-account ~/vote-account-keypair.json查询当前vote authority它可能就是验证者 identity 账户即默认值也可能是其他密钥对。以下步骤假设当前 vote authority 是~/validator-keypair.json。执行solana vote-authorize-voter-checked ~/vote-account-keypair.json ~/validator-keypair.json ~/new-vote-authority.json。 新 vote authority 被调度为从下一个 epoch 开始生效。重启solana-validator时同时携带新旧两个vote authority 密钥对使进程能在下个 epoch 平滑过渡--authorized-voter ~/validator-keypair.json --authorized-voter ~/new-vote-authority.json集群到达下一个 epoch 后移除--authorized-voter ~/validator-keypair.json参数并再次重启solana-validator——旧 vote authority 密钥对不再需要。轮换 Authorized Withdrawer无需特殊处理或时序考虑按需使用solana vote-authorize-withdrawer-checked命令即可对应 authorize 的即时赋值不受 epoch 边界约束。使用 Durable Nonce 实现授权转移的可信Trustless迁移文档指出若要将 Authorized Voter 或 Withdrawer移交给另一实体推荐采用基于 Durable Nonce 的两阶段签署流程使双方都无需向对方暴露密钥对实体 B 使用solana create-nonce-account创建一个 durable nonce实体 B 运行solana vote-authorize-voter-checked或solana vote-authorize-withdrawer-checked命令包含--sign-only参数只签名、不发送--nonce、--nonce-authority与--blockhash参数用于指定 nonce 细节实体 A 现有权限方地址以及实体 B 新权限方的密钥对命令成功执行后会输出交易签名实体 B 需将这些签名分享给实体 A实体 A 随后运行类似的vote-authorize-voter-checked或vote-authorize-withdrawer-checked命令做如下调整去掉--sign-only改为对实体 B 提供的每个签名各加一个--signer参数把实体 A 现有权限方地址替换为对应密钥对A 亲自签名把实体 B 新权限方密钥对替换为对应地址。成功后权限即完成变更而 A、B 双方都没有向对方暴露任何密钥对——尽管两方都对同一笔交易签了名。--sign-only/--signer属于 Solana CLI 的离线签名能力其解析与构造逻辑可参考 clap-utils/src/offline.rs 与 cli/src/offline.rsnonce 相关离线参数解析在 clap-utils/src/nonce.rs。关闭投票账户Close a Vote Account投票账户可以用solana close-vote-account命令关闭。关闭操作会把账户中剩余的 SOL 全部提现到指定的收款地址并使该账户作为投票账户失效。文档明确存在 active stake 的投票账户无法被关闭。源码印证关闭即调用withdraw抽干余额withdraw 首先强制要求 withdrawer 签名verify_authorized_signer(vote_state.authorized_withdrawer, signers)?当余额归零时依据epoch_credits记录判断是否为“活跃验证者”——若最近一个完整 epoch 内仍获得过 creditscurrent_epoch - last_epoch_with_credits 2则拒绝关闭并返回VoteError::ActiveVoteAccountClose否则将账户状态重置为VoteState::default()即完成去初始化。这与文档“不能关闭持有 active stake 的投票账户”的规则对应close-vote-account的 CLI 定义与参数见 cli/src/vote.rs#L409。小结围绕 docs/src/operations/guides/vote-accounts.md 的完整脉络可以归纳为创建solana create-vote-account地址终身固定其余字段均可后续调整身份identity热钱包、付费方变更需 withdrawer 新身份双签持质押时轮换需临时非投票节点过渡两个 epochleader schedule 提前两 epoch 计算的后果投票权限vote authority可合并 identity 以减半费用每 epoch 至多变更一次、下 epoch 生效靠--authorized-voter多次指定实现无缝过渡CLI 定义见 validator/src/cli.rs#L85-L97链上逻辑见 authorize提款权限withdrawer冷钱包保管是 commission 与 identity 变更的强制签名方变更无时序约束commissionu8、0–100、默认 100%上调仅在 epoch 前半段允许is_commission_update_allowed防“低佣金钓鱼、发放前抬价”权限移交Durable Nonce 两阶段离线签名双方互不暴露密钥关闭close-vote-account提现余额并去初始化active stake 或近期活跃账户会被链上拒绝。所有命令与行为均以本仓库当前源码为准solana-validator与solanaCLI 的实际参数以 validator/src/cli.rs 和 cli/src/vote.rs 中的定义为准vote 程序的链上校验以 programs/vote/src/vote_state/mod.rs 为准账户状态结构以 sdk/program/src/vote/state/mod.rs 中的冻结 ABI 定义为准。【免费下载链接】solanaWeb-Scale Blockchain for fast, secure, scalable, decentralized apps and marketplaces.项目地址: https://gitcode.com/GitHub_Trending/so/solana创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考