diff --git a/mev-programs/Anchor.toml b/mev-programs/Anchor.toml index 3fbab07..e878ead 100644 --- a/mev-programs/Anchor.toml +++ b/mev-programs/Anchor.toml @@ -31,6 +31,7 @@ test = "yarn ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.{ts,js}" [test.validator] slots_per_epoch = "32" +bind_address = "127.0.0.1" [features] resolution = false diff --git a/mev-programs/Cargo.lock b/mev-programs/Cargo.lock index 4d88fc5..0eaffde 100644 --- a/mev-programs/Cargo.lock +++ b/mev-programs/Cargo.lock @@ -2364,6 +2364,7 @@ dependencies = [ "bincode", "serde", "serde_derive", + "solana-sdk-ids", ] [[package]] @@ -7557,9 +7558,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "sharded-slab", "thread_local", diff --git a/mev-programs/programs/priority-fee-distribution/src/lib.rs b/mev-programs/programs/priority-fee-distribution/src/lib.rs index 6f76d96..f95f91c 100644 --- a/mev-programs/programs/priority-fee-distribution/src/lib.rs +++ b/mev-programs/programs/priority-fee-distribution/src/lib.rs @@ -69,8 +69,9 @@ pub mod jito_priority_fee_distribution { return Err(MaxValidatorCommissionFeeBpsExceeded.into()); } - let validator_vote_state = VoteState::deserialize(&ctx.accounts.validator_vote_account)?; - if &validator_vote_state.node_pubkey != ctx.accounts.signer.key { + let validator_vote_account_node_pubkey = + VoteState::deserialize_node_pubkey(&ctx.accounts.validator_vote_account)?; + if validator_vote_account_node_pubkey != *ctx.accounts.signer.key { return Err(Unauthorized.into()); } diff --git a/mev-programs/programs/tip-distribution/src/lib.rs b/mev-programs/programs/tip-distribution/src/lib.rs index 4d30d91..16d589a 100644 --- a/mev-programs/programs/tip-distribution/src/lib.rs +++ b/mev-programs/programs/tip-distribution/src/lib.rs @@ -66,8 +66,9 @@ pub mod jito_tip_distribution { return Err(MaxValidatorCommissionFeeBpsExceeded.into()); } - let validator_vote_state = VoteState::deserialize(&ctx.accounts.validator_vote_account)?; - if &validator_vote_state.node_pubkey != ctx.accounts.signer.key { + let validator_vote_account_node_pubkey = + VoteState::deserialize_node_pubkey(&ctx.accounts.validator_vote_account)?; + if validator_vote_account_node_pubkey != *ctx.accounts.signer.key { return Err(Unauthorized.into()); } diff --git a/mev-programs/programs/vote-state/Cargo.toml b/mev-programs/programs/vote-state/Cargo.toml index 251c1e7..61b25dd 100644 --- a/mev-programs/programs/vote-state/Cargo.toml +++ b/mev-programs/programs/vote-state/Cargo.toml @@ -13,3 +13,4 @@ anchor-lang = { workspace = true } bincode = { workspace = true } serde = { workspace = true } serde_derive = { workspace = true } +solana-sdk-ids = { workspace = true } diff --git a/mev-programs/programs/vote-state/src/lib.rs b/mev-programs/programs/vote-state/src/lib.rs index 4974974..9403b72 100644 --- a/mev-programs/programs/vote-state/src/lib.rs +++ b/mev-programs/programs/vote-state/src/lib.rs @@ -1,281 +1,21 @@ -#![allow(dead_code)] -//! This code was mostly copy-pasta'd from [here](https://github.com/solana-labs/solana/blob/df128573127c324cb5b53634a7e2d77427c6f2d8/programs/vote/src/vote_state/mod.rs#L1). -//! In all current releases [VoteState] is defined in the `solana-vote-program` crate which is not compatible -//! with programs targeting BPF bytecode due to some BPF-incompatible libraries being pulled in. - -use std::collections::{BTreeMap, VecDeque}; - use anchor_lang::{ error::ErrorCode::{AccountDidNotDeserialize, ConstraintOwner}, - prelude::*, - solana_program, + prelude::{AccountInfo, Pubkey, Result}, }; use bincode::deserialize; -use serde_derive::Deserialize; - -type Epoch = u64; -type Slot = u64; -type UnixTimestamp = i64; - -#[derive(Clone, Deserialize)] -pub struct Lockout { - pub slot: Slot, - pub confirmation_count: u32, -} - -#[derive(Clone, Default, Deserialize)] -struct AuthorizedVoters { - authorized_voters: BTreeMap, -} - -impl AuthorizedVoters { - pub fn new(epoch: Epoch, pubkey: Pubkey) -> Self { - let mut authorized_voters = BTreeMap::new(); - authorized_voters.insert(epoch, pubkey); - Self { authorized_voters } - } -} - -const MAX_ITEMS: usize = 32; - -#[derive(Default, Deserialize)] -pub struct CircBuf { - buf: [I; MAX_ITEMS], - /// next pointer - idx: usize, - is_empty: bool, -} - -#[derive(Clone, Deserialize, Default)] -pub struct BlockTimestamp { - pub slot: Slot, - pub timestamp: UnixTimestamp, -} - -#[derive(Deserialize)] -pub enum VoteStateVersions { - V0_23_5(Box), - V1_14_11(Box), - Current(Box), -} - -impl VoteStateVersions { - pub fn convert_to_current(self) -> Box { - match self { - Self::V0_23_5(state) => { - let authorized_voters = - AuthorizedVoters::new(state.authorized_voter_epoch, state.authorized_voter); - - Box::new(VoteState { - node_pubkey: state.node_pubkey, - - authorized_withdrawer: state.authorized_withdrawer, - - commission: state.commission, - - votes: Self::landed_votes_from_lockouts(state.votes), - - root_slot: state.root_slot, - - authorized_voters, - - prior_voters: CircBuf::default(), - - epoch_credits: state.epoch_credits.clone(), - - last_timestamp: state.last_timestamp.clone(), - }) - } - Self::V1_14_11(state) => Box::new(VoteState { - node_pubkey: state.node_pubkey, - authorized_withdrawer: state.authorized_withdrawer, - commission: state.commission, - - votes: Self::landed_votes_from_lockouts(state.votes), - - root_slot: state.root_slot, - - authorized_voters: state.authorized_voters.clone(), - - prior_voters: state.prior_voters, - - epoch_credits: state.epoch_credits, - - last_timestamp: state.last_timestamp, - }), - Self::Current(state) => state, - } - } - - fn landed_votes_from_lockouts(lockouts: VecDeque) -> VecDeque { - lockouts.into_iter().map(|lockout| lockout.into()).collect() - } -} -#[derive(Deserialize)] -pub struct VoteState1_14_11 { - /// the node that votes in this account - pub node_pubkey: Pubkey, - - /// the signer for withdrawals - #[serde(skip_deserializing)] - pub authorized_withdrawer: Pubkey, - /// percentage (0-100) that represents what part of a rewards - /// payout should be given to this VoteAccount - #[serde(skip_deserializing)] - pub commission: u8, - #[serde(skip_deserializing)] - pub votes: VecDeque, - - /// This usually the last Lockout which was popped from self.votes. - /// However, it can be arbitrary slot, when being used inside Tower - #[serde(skip_deserializing)] - pub root_slot: Option, - - /// the signer for vote transactions - #[serde(skip_deserializing)] - authorized_voters: AuthorizedVoters, - - /// history of prior authorized voters and the epochs for which - /// they were set, the bottom end of the range is inclusive, - /// the top of the range is exclusive - #[serde(skip_deserializing)] - prior_voters: CircBuf<(Pubkey, Epoch, Epoch)>, - - /// history of how many credits earned by the end of each epoch - /// each tuple is (Epoch, credits, prev_credits) - #[serde(skip_deserializing)] - pub(crate) epoch_credits: Vec<(Epoch, u64, u64)>, - - /// most recent timestamp submitted with a vote - #[serde(skip_deserializing)] - pub last_timestamp: BlockTimestamp, -} - -#[derive(Deserialize)] -pub struct VoteState0_23_5 { - /// the node that votes in this account - pub node_pubkey: Pubkey, - - /// the signer for vote transactions - #[serde(skip_deserializing)] - pub authorized_voter: Pubkey, - /// when the authorized voter was set/initialized - #[serde(skip_deserializing)] - pub authorized_voter_epoch: Epoch, - - /// history of prior authorized voters and the epoch ranges for which - /// they were set - #[serde(skip_deserializing)] - pub prior_voters: CircBuf<(Pubkey, Epoch, Epoch, Slot)>, - - /// the signer for withdrawals - #[serde(skip_deserializing)] - pub authorized_withdrawer: Pubkey, - /// percentage (0-100) that represents what part of a rewards - /// payout should be given to this VoteAccount - #[serde(skip_deserializing)] - pub commission: u8, - - #[serde(skip_deserializing)] - pub votes: VecDeque, - #[serde(skip_deserializing)] - pub root_slot: Option, - - /// history of how many credits earned by the end of each epoch - /// each tuple is (Epoch, credits, prev_credits) - #[serde(skip_deserializing)] - pub epoch_credits: Vec<(Epoch, u64, u64)>, - - /// most recent timestamp submitted with a vote - #[serde(skip_deserializing)] - pub last_timestamp: BlockTimestamp, -} - -#[derive(Deserialize)] -pub struct VoteState { - /// the node that votes in this account - pub node_pubkey: Pubkey, - - /// the signer for withdrawals - #[serde(skip_deserializing)] - pub authorized_withdrawer: Pubkey, - /// percentage (0-100) that represents what part of a rewards - /// payout should be given to this VoteAccount - #[serde(skip_deserializing)] - pub commission: u8, - - #[serde(skip_deserializing)] - pub votes: VecDeque, - - // This usually the last Lockout which was popped from self.votes. - // However, it can be arbitrary slot, when being used inside Tower - #[serde(skip_deserializing)] - pub root_slot: Option, - - /// the signer for vote transactions - #[serde(skip_deserializing)] - authorized_voters: AuthorizedVoters, - - /// history of prior authorized voters and the epochs for which - /// they were set, the bottom end of the range is inclusive, - /// the top of the range is exclusive - #[serde(skip_deserializing)] - prior_voters: CircBuf<(Pubkey, Epoch, Epoch)>, - - /// history of how many credits earned by the end of each epoch - /// each tuple is (Epoch, credits, prev_credits) - #[serde(skip_deserializing)] - pub epoch_credits: Vec<(Epoch, u64, u64)>, - - /// most recent timestamp submitted with a vote - #[serde(skip_deserializing)] - pub last_timestamp: BlockTimestamp, -} - -#[derive(Deserialize)] -pub struct LandedVote { - // Latency is the difference in slot number between the slot that was voted on (lockout.slot) and the slot in - // which the vote that added this Lockout landed. For votes which were cast before versions of the validator - // software which recorded vote latencies, latency is recorded as 0. - pub latency: u8, - pub lockout: Lockout, -} - -impl LandedVote { - pub const fn slot(&self) -> Slot { - self.lockout.slot - } - - pub const fn confirmation_count(&self) -> u32 { - self.lockout.confirmation_count - } -} - -impl From for Lockout { - fn from(landed_vote: LandedVote) -> Self { - landed_vote.lockout - } -} - -impl From for LandedVote { - fn from(lockout: Lockout) -> Self { - Self { - latency: 0, - lockout, - } - } -} +pub struct VoteState; impl VoteState { - pub fn deserialize(account_info: &AccountInfo) -> Result> { - if account_info.owner != &solana_program::vote::program::id() { + pub fn deserialize_node_pubkey(account_info: &AccountInfo) -> Result { + if Pubkey::from(account_info.owner.to_bytes()) + != Pubkey::from(solana_sdk_ids::vote::id().to_bytes()) + { return Err(ConstraintOwner.into()); } + // The first 4 bytes are the enumeration type and the next 32 bytes of the vote state are the node pubkey. let data = account_info.data.borrow(); - deserialize::>(&data) - .map(|v| v.convert_to_current()) - .map_err(|_| AccountDidNotDeserialize.into()) + deserialize::(&data[4..36]).map_err(|_| AccountDidNotDeserialize.into()) } }