diff --git a/vote-interface/src/instruction.rs b/vote-interface/src/instruction.rs index 599755eea..3013ec486 100644 --- a/vote-interface/src/instruction.rs +++ b/vote-interface/src/instruction.rs @@ -4,7 +4,7 @@ use { super::state::TowerSync, crate::state::{ Vote, VoteAuthorize, VoteAuthorizeCheckedWithSeedArgs, VoteAuthorizeWithSeedArgs, VoteInit, - VoteStateUpdate, VoteStateV4, + VoteInitV2, VoteStateUpdate, VoteStateV4, }, solana_clock::{Slot, UnixTimestamp}, solana_hash::Hash, @@ -40,6 +40,9 @@ pub enum VoteInstruction { /// 0. `[WRITE]` Vote account to be updated with the Pubkey for authorization /// 1. `[]` Clock sysvar /// 2. `[SIGNER]` Vote or withdraw authority + /// + /// When SIMD-0387 is enabled, the `VoteAuthorize::Voter` variant is + /// disallowed for any vote accounts whose BLS pubkey is set to `Some`. Authorize(Pubkey, VoteAuthorize), /// A Vote instruction with recent votes @@ -93,6 +96,9 @@ pub enum VoteInstruction { /// 1. `[]` Clock sysvar /// 2. `[SIGNER]` Vote or withdraw authority /// 3. `[SIGNER]` New vote or withdraw authority + /// + /// When SIMD-0387 is enabled, the `VoteAuthorize::Voter` variant is + /// disallowed for any vote accounts whose BLS pubkey is set to `Some`. AuthorizeChecked(VoteAuthorize), /// Update the onchain vote state for the signer. @@ -117,6 +123,10 @@ pub enum VoteInstruction { /// 0. `[Write]` Vote account to be updated /// 1. `[]` Clock sysvar /// 2. `[SIGNER]` Base key of current Voter or Withdrawer authority's derived key + /// + /// When SIMD-0387 is enabled, the `VoteAuthorize::Voter` variant in + /// `authorization_type` is disallowed for any vote accounts whose BLS + /// pubkey is set to `Some`. AuthorizeWithSeed(VoteAuthorizeWithSeedArgs), /// Given that the current Voter or Withdrawer authority is a derived key, @@ -131,6 +141,10 @@ pub enum VoteInstruction { /// 1. `[]` Clock sysvar /// 2. `[SIGNER]` Base key of current Voter or Withdrawer authority's derived key /// 3. `[SIGNER]` New vote or withdraw authority + /// + /// When SIMD-0387 is enabled, the `VoteAuthorize::Voter` variant in + /// `authorization_type` is disallowed for any vote accounts whose BLS + /// pubkey is set to `Some`. AuthorizeCheckedWithSeed(VoteAuthorizeCheckedWithSeedArgs), /// Update the onchain vote state for the signer. @@ -169,6 +183,13 @@ pub enum VoteInstruction { #[cfg_attr(feature = "serde", serde(with = "serde_tower_sync"))] TowerSync, Hash, ), + + // Initialize a vote account using VoteInitV2 + /// + /// # Account references + /// 0. `[WRITE]` Uninitialized vote account + /// 1. `[SIGNER]` New validator identity (node_pubkey) + InitializeAccountV2(VoteInitV2), } impl VoteInstruction { @@ -265,6 +286,20 @@ fn initialize_account(vote_pubkey: &Pubkey, vote_init: &VoteInit) -> Instruction ) } +#[cfg(feature = "bincode")] +fn initialize_account_v2(vote_pubkey: &Pubkey, vote_init: &VoteInitV2) -> Instruction { + let account_metas = vec![ + AccountMeta::new(*vote_pubkey, false), + AccountMeta::new_readonly(vote_init.node_pubkey, true), + ]; + + Instruction::new_with_bincode( + id(), + &VoteInstruction::InitializeAccountV2(*vote_init), + account_metas, + ) +} + pub struct CreateVoteAccountConfig<'a> { pub space: u64, pub with_seed: Option<(&'a Pubkey, &'a str)>, @@ -311,6 +346,37 @@ pub fn create_account_with_config( vec![create_ix, init_ix] } +#[cfg(feature = "bincode")] +pub fn create_account_with_config_v2( + from_pubkey: &Pubkey, + vote_pubkey: &Pubkey, + vote_init: &VoteInitV2, + lamports: u64, + config: CreateVoteAccountConfig, +) -> Vec { + let create_ix = if let Some((base, seed)) = config.with_seed { + solana_system_interface::instruction::create_account_with_seed( + from_pubkey, + vote_pubkey, + base, + seed, + lamports, + config.space, + &id(), + ) + } else { + solana_system_interface::instruction::create_account( + from_pubkey, + vote_pubkey, + lamports, + config.space, + &id(), + ) + }; + let init_ix = initialize_account_v2(vote_pubkey, vote_init); + vec![create_ix, init_ix] +} + #[cfg(feature = "bincode")] pub fn authorize( vote_pubkey: &Pubkey, diff --git a/vote-interface/src/state/mod.rs b/vote-interface/src/state/mod.rs index c8e8fe512..35fec930b 100644 --- a/vote-interface/src/state/mod.rs +++ b/vote-interface/src/state/mod.rs @@ -33,6 +33,9 @@ pub(crate) mod vote_state_deserialize; /// Size of a BLS public key in a compressed point representation pub const BLS_PUBLIC_KEY_COMPRESSED_SIZE: usize = 48; +/// Size of a BLS signature in a compressed point representation +pub const BLS_SIGNATURE_COMPRESSED_SIZE: usize = 96; + // Maximum number of votes to keep around, tightly coupled with epoch_schedule::MINIMUM_SLOTS_PER_EPOCH pub const MAX_LOCKOUT_HISTORY: usize = 31; pub const INITIAL_LOCKOUT: usize = 2; diff --git a/vote-interface/src/state/vote_instruction_data.rs b/vote-interface/src/state/vote_instruction_data.rs index 136f2d71c..3d214e1dc 100644 --- a/vote-interface/src/state/vote_instruction_data.rs +++ b/vote-interface/src/state/vote_instruction_data.rs @@ -1,14 +1,19 @@ -#[cfg(feature = "serde")] -use serde_derive::{Deserialize, Serialize}; #[cfg(feature = "frozen-abi")] use solana_frozen_abi_macro::{frozen_abi, AbiExample}; use { - crate::state::{Lockout, MAX_LOCKOUT_HISTORY}, + crate::state::{ + Lockout, BLS_PUBLIC_KEY_COMPRESSED_SIZE, BLS_SIGNATURE_COMPRESSED_SIZE, MAX_LOCKOUT_HISTORY, + }, solana_clock::{Slot, UnixTimestamp}, solana_hash::Hash, solana_pubkey::Pubkey, std::{collections::VecDeque, fmt::Debug}, }; +#[cfg(feature = "serde")] +use { + serde_derive::{Deserialize, Serialize}, + serde_with::serde_as, +}; #[cfg_attr( feature = "frozen-abi", @@ -201,11 +206,70 @@ pub struct VoteInit { pub commission: u8, } +#[cfg_attr(feature = "serde", cfg_eval::cfg_eval, serde_as)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct VoteInitV2 { + pub node_pubkey: Pubkey, + pub authorized_voter: Pubkey, + #[cfg_attr( + feature = "serde", + serde_as(as = "[_; BLS_PUBLIC_KEY_COMPRESSED_SIZE]") + )] + pub authorized_voter_bls_pubkey: [u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE], + #[cfg_attr(feature = "serde", serde_as(as = "[_; BLS_SIGNATURE_COMPRESSED_SIZE]"))] + pub authorized_voter_bls_proof_of_possession: [u8; BLS_SIGNATURE_COMPRESSED_SIZE], + pub authorized_withdrawer: Pubkey, + pub inflation_rewards_commission_bps: u16, + pub inflation_rewards_collector: Pubkey, + pub block_revenue_commission_bps: u16, + pub block_revenue_collector: Pubkey, +} + +impl Default for VoteInitV2 { + fn default() -> Self { + Self { + node_pubkey: Pubkey::default(), + authorized_voter: Pubkey::default(), + authorized_voter_bls_pubkey: [0u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE], + authorized_voter_bls_proof_of_possession: [0u8; BLS_SIGNATURE_COMPRESSED_SIZE], + authorized_withdrawer: Pubkey::default(), + inflation_rewards_commission_bps: 0, + inflation_rewards_collector: Pubkey::default(), + block_revenue_commission_bps: 0, + block_revenue_collector: Pubkey::default(), + } + } +} + +#[cfg_attr(feature = "serde", cfg_eval::cfg_eval, serde_as)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct VoterWithBLSArgs { + #[cfg_attr( + feature = "serde", + serde_as(as = "[_; BLS_PUBLIC_KEY_COMPRESSED_SIZE]") + )] + pub bls_pub_key: [u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE], + #[cfg_attr(feature = "serde", serde_as(as = "[_; BLS_SIGNATURE_COMPRESSED_SIZE]"))] + pub bls_proof_of_possession: [u8; BLS_SIGNATURE_COMPRESSED_SIZE], +} + +impl Default for VoterWithBLSArgs { + fn default() -> Self { + Self { + bls_pub_key: [0u8; BLS_PUBLIC_KEY_COMPRESSED_SIZE], + bls_proof_of_possession: [0u8; BLS_SIGNATURE_COMPRESSED_SIZE], + } + } +} + #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum VoteAuthorize { Voter, Withdrawer, + VoterWithBLS(VoterWithBLSArgs), } #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] diff --git a/vote-interface/src/state/vote_state_v4.rs b/vote-interface/src/state/vote_state_v4.rs index 54a000992..101c41e3d 100644 --- a/vote-interface/src/state/vote_state_v4.rs +++ b/vote-interface/src/state/vote_state_v4.rs @@ -11,7 +11,7 @@ use solana_frozen_abi_macro::{frozen_abi, AbiExample}; #[cfg(any(target_os = "solana", feature = "bincode"))] use solana_instruction::error::InstructionError; use { - super::{BlockTimestamp, LandedVote, VoteInit, BLS_PUBLIC_KEY_COMPRESSED_SIZE}, + super::{BlockTimestamp, LandedVote, VoteInit, VoteInitV2, BLS_PUBLIC_KEY_COMPRESSED_SIZE}, crate::authorized_voters::AuthorizedVoters, solana_clock::{Clock, Epoch, Slot}, solana_pubkey::Pubkey, @@ -77,7 +77,7 @@ impl VoteStateV4 { 3762 // Same size as V3 to avoid account resizing } - pub fn new(vote_pubkey: &Pubkey, vote_init: &VoteInit, clock: &Clock) -> Self { + pub fn new_with_defaults(vote_pubkey: &Pubkey, vote_init: &VoteInit, clock: &Clock) -> Self { Self { node_pubkey: vote_init.node_pubkey, authorized_voters: AuthorizedVoters::new(clock.epoch, vote_init.authorized_voter), @@ -92,6 +92,20 @@ impl VoteStateV4 { } } + pub fn new(vote_init: &VoteInitV2, clock: &Clock) -> Self { + Self { + node_pubkey: vote_init.node_pubkey, + authorized_voters: AuthorizedVoters::new(clock.epoch, vote_init.authorized_voter), + bls_pubkey_compressed: Some(vote_init.authorized_voter_bls_pubkey), + authorized_withdrawer: vote_init.authorized_withdrawer, + inflation_rewards_commission_bps: vote_init.inflation_rewards_commission_bps, + inflation_rewards_collector: vote_init.inflation_rewards_collector, + block_revenue_commission_bps: vote_init.block_revenue_commission_bps, + block_revenue_collector: vote_init.block_revenue_collector, + ..Self::default() + } + } + #[cfg(any(target_os = "solana", feature = "bincode"))] pub fn deserialize(input: &[u8], vote_pubkey: &Pubkey) -> Result { let mut vote_state = Self::default();