diff --git a/keepers/validator-keeper/src/lib.rs b/keepers/validator-keeper/src/lib.rs index f1aaf728..e6a88561 100644 --- a/keepers/validator-keeper/src/lib.rs +++ b/keepers/validator-keeper/src/lib.rs @@ -80,6 +80,16 @@ pub fn emit_mev_commission_datapoint(stats: CreateUpdateStats) { ); } +pub fn emit_mev_earned_datapoint(stats: CreateUpdateStats) { + datapoint_info!( + "mev-earned-stats", + ("num_creates_success", stats.creates.successes, i64), + ("num_creates_error", stats.creates.errors, i64), + ("num_updates_success", stats.updates.successes, i64), + ("num_updates_error", stats.updates.errors, i64), + ); +} + pub fn emit_validator_commission_datapoint(stats: CreateUpdateStats, runs_for_epoch: i64) { datapoint_info!( "vote-account-stats", diff --git a/keepers/validator-keeper/src/main.rs b/keepers/validator-keeper/src/main.rs index 22e6a848..f1e2eebb 100644 --- a/keepers/validator-keeper/src/main.rs +++ b/keepers/validator-keeper/src/main.rs @@ -18,10 +18,10 @@ use solana_sdk::{ use tokio::time::sleep; use validator_keeper::{ cluster_info::update_cluster_info, - emit_cluster_history_datapoint, emit_mev_commission_datapoint, + emit_cluster_history_datapoint, emit_mev_commission_datapoint, emit_mev_earned_datapoint, emit_validator_commission_datapoint, emit_validator_history_metrics, gossip::{emit_gossip_datapoint, upload_gossip_values}, - mev_commission::update_mev_commission, + mev_commission::{update_mev_commission, update_mev_earned}, stake::{emit_stake_history_datapoint, update_stake_history}, vote_account::update_vote_accounts, }; @@ -115,6 +115,43 @@ async fn mev_commission_loop( } } +async fn mev_earned_loop( + client: Arc, + keypair: Arc, + commission_history_program_id: Pubkey, + tip_distribution_program_id: Pubkey, + interval: u64, +) { + let mut curr_epoch = 0; + // {TipDistributionAccount : VoteAccount} + let mut validators_updated: HashMap = HashMap::new(); + + loop { + // Continuously runs throughout an epoch, polling for tip distribution accounts from the prev epoch with uploaded merkle roots + // and submitting update_mev_earned (technically update_mev_comission) txs when the uploaded merkle roots are detected + match update_mev_earned( + &client, + &keypair, + &commission_history_program_id, + &tip_distribution_program_id, + &mut validators_updated, + &mut curr_epoch, + ) + .await + { + Ok(stats) => { + emit_mev_earned_datapoint(stats); + sleep(Duration::from_secs(interval)).await; + } + Err((e, stats)) => { + emit_mev_earned_datapoint(stats); + datapoint_error!("mev-earned-error", ("error", e.to_string(), String),); + sleep(Duration::from_secs(5)).await; + } + }; + } +} + async fn vote_account_loop( rpc_client: Arc, keypair: Arc, @@ -362,12 +399,19 @@ async fn main() { } tokio::spawn(mev_commission_loop( - Arc::clone(&client), - Arc::clone(&keypair), + client.clone(), + keypair.clone(), args.program_id, args.tip_distribution_program_id, args.interval, )); + tokio::spawn(mev_earned_loop( + client.clone(), + keypair.clone(), + args.program_id, + args.tip_distribution_program_id, + args.interval, + )); gossip_upload_loop(client, keypair, args.program_id, entrypoint, args.interval).await; } diff --git a/keepers/validator-keeper/src/mev_commission.rs b/keepers/validator-keeper/src/mev_commission.rs index 490854d4..eea66619 100644 --- a/keepers/validator-keeper/src/mev_commission.rs +++ b/keepers/validator-keeper/src/mev_commission.rs @@ -1,7 +1,8 @@ use std::{collections::HashMap, str::FromStr, sync::Arc}; -use anchor_lang::{InstructionData, ToAccountMetas}; +use anchor_lang::{AccountDeserialize, InstructionData, ToAccountMetas}; use jito_tip_distribution::sdk::derive_tip_distribution_account_address; +use jito_tip_distribution::state::TipDistributionAccount; use keeper_core::{ build_create_and_update_instructions, get_multiple_accounts_batched, get_vote_accounts_with_retry, submit_create_and_update, Address, CreateTransaction, @@ -34,6 +35,7 @@ pub struct ValidatorMevCommissionEntry { pub config: Pubkey, pub program_id: Pubkey, pub signer: Pubkey, + pub epoch: u64, } impl ValidatorMevCommissionEntry { @@ -67,6 +69,7 @@ impl ValidatorMevCommissionEntry { config, program_id: *program_id, signer: *signer, + epoch, } } } @@ -114,7 +117,7 @@ impl UpdateInstruction for ValidatorMevCommissionEntry { fn update_instruction(&self) -> Instruction { Instruction { program_id: self.program_id, - accounts: validator_history::accounts::UpdateMevCommission { + accounts: validator_history::accounts::CopyTipDistributionAccount { validator_history_account: self.validator_history_account, vote_account: self.vote_account, tip_distribution_account: self.tip_distribution_account, @@ -122,7 +125,8 @@ impl UpdateInstruction for ValidatorMevCommissionEntry { signer: self.signer, } .to_account_metas(None), - data: validator_history::instruction::UpdateMevCommission {}.data(), + data: validator_history::instruction::CopyTipDistributionAccount { epoch: self.epoch } + .data(), } } } @@ -190,6 +194,72 @@ pub async fn update_mev_commission( submit_result.map_err(|(e, stats)| (e.into(), stats)) } +pub async fn update_mev_earned( + client: &Arc, + keypair: &Arc, + validator_history_program_id: &Pubkey, + tip_distribution_program_id: &Pubkey, + validators_updated: &mut HashMap, + curr_epoch: &mut u64, +) -> Result { + let epoch = client + .get_epoch_info() + .await + .map_err(|e| (e.into(), CreateUpdateStats::default()))? + .epoch; + + if epoch > *curr_epoch { + // new epoch started, we assume here that all the validators with TDAs from curr_epoch-1 have had their merkle roots uploaded/processed by this point + // clear our map of TDAs derived from curr_epoch -1 and start fresh for epoch-1 (or curr_epoch) + validators_updated.clear(); + } + *curr_epoch = epoch; + + let vote_accounts = get_vote_accounts_with_retry(client, MIN_VOTE_EPOCHS, None) + .await + .map_err(|e| (e.into(), CreateUpdateStats::default()))?; + + let entries = vote_accounts + .iter() + .map(|vote_account| { + ValidatorMevCommissionEntry::new( + vote_account, + epoch.saturating_sub(1), // TDA derived from the prev epoch since the merkle roots are uploaded shortly after rollover + validator_history_program_id, + tip_distribution_program_id, + &keypair.pubkey(), + ) + }) + .collect::>(); + + let uploaded_merkleroot_entries = get_entries_with_uploaded_merkleroot(client, &entries) + .await + .map_err(|e| (e.into(), CreateUpdateStats::default()))?; + + let entries_to_update = uploaded_merkleroot_entries + .into_iter() + .filter(|entry| !validators_updated.contains_key(&entry.tip_distribution_account)) + .collect::>(); + let (create_transactions, update_instructions) = + build_create_and_update_instructions(client, &entries_to_update) + .await + .map_err(|e| (e.into(), CreateUpdateStats::default()))?; + + let submit_result = + submit_create_and_update(client, create_transactions, update_instructions, keypair).await; + if submit_result.is_ok() { + for ValidatorMevCommissionEntry { + vote_account, + tip_distribution_account, + .. + } in entries_to_update + { + validators_updated.insert(tip_distribution_account, vote_account); + } + } + submit_result.map_err(|(e, stats)| (e.into(), stats)) +} + async fn get_existing_entries( client: Arc, entries: &[ValidatorMevCommissionEntry], @@ -215,3 +285,33 @@ async fn get_existing_entries( // Fetch existing tip distribution accounts for this epoch Ok(result) } + +async fn get_entries_with_uploaded_merkleroot( + client: &Arc, + entries: &[ValidatorMevCommissionEntry], +) -> Result, MultipleAccountsError> { + /* Filters tip distribution tuples to the addresses, then fetches accounts to see which ones have an uploaded merkle root */ + let tip_distribution_addresses = entries + .iter() + .map(|entry| entry.tip_distribution_account) + .collect::>(); + + let accounts = get_multiple_accounts_batched(&tip_distribution_addresses, client).await?; + let result = accounts + .iter() + .enumerate() + .filter_map(|(i, account_data)| { + if let Some(account_data) = account_data { + let mut data: &[u8] = &account_data.data; + if let Ok(tda) = TipDistributionAccount::try_deserialize(&mut data) { + if tda.merkle_root.is_some() { + return Some(entries[i].clone()); + } + } + } + None + }) + .collect::>(); + // Fetch tip distribution accounts with uploaded merkle roots for this epoch + Ok(result) +} diff --git a/programs/validator-history/idl/validator_history.json b/programs/validator-history/idl/validator_history.json index e9febf2a..3e272d7d 100644 --- a/programs/validator-history/idl/validator_history.json +++ b/programs/validator-history/idl/validator_history.json @@ -126,7 +126,7 @@ "args": [] }, { - "name": "updateMevCommission", + "name": "copyTipDistributionAccount", "accounts": [ { "name": "validatorHistoryAccount", @@ -160,7 +160,12 @@ "isSigner": true } ], - "args": [] + "args": [ + { + "name": "epoch", + "type": "u64" + } + ] }, { "name": "initializeConfig", @@ -563,12 +568,16 @@ "name": "voteAccountLastUpdateSlot", "type": "u64" }, + { + "name": "mevEarned", + "type": "u32" + }, { "name": "padding1", "type": { "array": [ "u8", - 88 + 84 ] } } diff --git a/programs/validator-history/src/instructions/update_mev_commission.rs b/programs/validator-history/src/instructions/copy_tip_distribution_account.rs similarity index 68% rename from programs/validator-history/src/instructions/update_mev_commission.rs rename to programs/validator-history/src/instructions/copy_tip_distribution_account.rs index 80d5864b..1fab553e 100644 --- a/programs/validator-history/src/instructions/update_mev_commission.rs +++ b/programs/validator-history/src/instructions/copy_tip_distribution_account.rs @@ -1,16 +1,17 @@ -use anchor_lang::{ - prelude::*, - solana_program::{clock::Clock, vote}, -}; +use anchor_lang::{prelude::*, solana_program::vote}; use crate::{ + errors::ValidatorHistoryError, state::{Config, ValidatorHistory}, utils::cast_epoch, + utils::fixed_point_sol, }; + use jito_tip_distribution::state::TipDistributionAccount; #[derive(Accounts)] -pub struct UpdateMevCommission<'info> { +#[instruction(epoch: u64)] +pub struct CopyTipDistributionAccount<'info> { #[account( mut, seeds = [ValidatorHistory::SEED, vote_account.key().as_ref()], @@ -36,7 +37,7 @@ pub struct UpdateMevCommission<'info> { seeds = [ TipDistributionAccount::SEED, vote_account.key().as_ref(), - Clock::get().unwrap().epoch.to_le_bytes().as_ref(), + epoch.to_le_bytes().as_ref(), ], bump, seeds::program = config.tip_distribution_program.key(), @@ -48,16 +49,25 @@ pub struct UpdateMevCommission<'info> { pub signer: Signer<'info>, } -pub fn handler(ctx: Context) -> Result<()> { +pub fn handler(ctx: Context, epoch: u64) -> Result<()> { + // cant set data in validator history for future epochs + if epoch > Clock::get()?.epoch { + return Err(ValidatorHistoryError::EpochOutOfRange.into()); + } + let epoch = cast_epoch(epoch); let mut validator_history_account = ctx.accounts.validator_history_account.load_mut()?; let mut tda_data: &[u8] = &ctx.accounts.tip_distribution_account.try_borrow_data()?; let tip_distribution_account = TipDistributionAccount::try_deserialize(&mut tda_data)?; let mev_commission_bps = tip_distribution_account.validator_commission_bps; - let epoch = cast_epoch(Clock::get()?.epoch); + let mut mev_earned: u32 = 0; + // if the merkle_root has been uploaded pull the mev_earned for the epoch + if let Some(merkle_root) = tip_distribution_account.merkle_root { + mev_earned = fixed_point_sol(merkle_root.max_total_claim); + } - validator_history_account.set_mev_commission(epoch, mev_commission_bps)?; + validator_history_account.set_mev_commission(epoch, mev_commission_bps, mev_earned)?; Ok(()) } diff --git a/programs/validator-history/src/instructions/mod.rs b/programs/validator-history/src/instructions/mod.rs index 6930a028..4766a3fa 100644 --- a/programs/validator-history/src/instructions/mod.rs +++ b/programs/validator-history/src/instructions/mod.rs @@ -2,6 +2,7 @@ pub mod backfill_total_blocks; pub mod copy_cluster_info; pub mod copy_gossip_contact_info; +pub mod copy_tip_distribution_account; pub mod copy_vote_account; pub mod initialize_cluster_history_account; pub mod initialize_config; @@ -11,12 +12,12 @@ pub mod realloc_validator_history_account; pub mod set_new_admin; pub mod set_new_oracle_authority; pub mod set_new_tip_distribution_program; -pub mod update_mev_commission; pub mod update_stake_history; pub use backfill_total_blocks::*; pub use copy_cluster_info::*; pub use copy_gossip_contact_info::*; +pub use copy_tip_distribution_account::*; pub use copy_vote_account::*; pub use initialize_cluster_history_account::*; pub use initialize_config::*; @@ -26,5 +27,4 @@ pub use realloc_validator_history_account::*; pub use set_new_admin::*; pub use set_new_oracle_authority::*; pub use set_new_tip_distribution_program::*; -pub use update_mev_commission::*; pub use update_stake_history::*; diff --git a/programs/validator-history/src/lib.rs b/programs/validator-history/src/lib.rs index e545323f..ec4fba36 100644 --- a/programs/validator-history/src/lib.rs +++ b/programs/validator-history/src/lib.rs @@ -69,8 +69,11 @@ pub mod validator_history { instructions::copy_vote_account::handler(ctx) } - pub fn update_mev_commission(ctx: Context) -> Result<()> { - instructions::update_mev_commission::handler(ctx) + pub fn copy_tip_distribution_account( + ctx: Context, + epoch: u64, + ) -> Result<()> { + instructions::copy_tip_distribution_account::handler(ctx, epoch) } pub fn initialize_config(ctx: Context, authority: Pubkey) -> Result<()> { diff --git a/programs/validator-history/src/state.rs b/programs/validator-history/src/state.rs index e08d2f36..5ad2fcfe 100644 --- a/programs/validator-history/src/state.rs +++ b/programs/validator-history/src/state.rs @@ -52,7 +52,8 @@ pub struct ValidatorHistoryEntry { pub client_type: u8, pub version: ClientVersion, pub ip: [u8; 4], - // Required to keep 8-byte alignment + // Required so that `rank` is aligned such that curr_offset % 4 == 0 (u32 field.alignment) as per https://doc.rust-lang.org/reference/type-layout.html#reprc-structs + // without it - `rank` would have offset 27, and the compiler would add an implicit padding byte after `is_superminority` and before `rank` pub padding0: u8, // 0 if not a superminority validator, 1 if superminority validator pub is_superminority: u8, @@ -60,7 +61,9 @@ pub struct ValidatorHistoryEntry { pub rank: u32, // Most recent updated slot for epoch credits and commission pub vote_account_last_update_slot: u64, - pub padding1: [u8; 88], + // MEV earned, stored as 1/100th SOL. mev_earned = 100 means 1 SOL earned + pub mev_earned: u32, + pub padding1: [u8; 84], } impl Default for ValidatorHistoryEntry { @@ -82,7 +85,8 @@ impl Default for ValidatorHistoryEntry { is_superminority: u8::MAX, rank: u32::MAX, vote_account_last_update_slot: u64::MAX, - padding1: [u8::MAX; 88], + mev_earned: u32::MAX, + padding1: [u8::MAX; 84], } } } @@ -278,17 +282,38 @@ impl ValidatorHistory { pub const MAX_ITEMS: usize = MAX_ITEMS; pub const SEED: &'static [u8] = b"validator-history"; - pub fn set_mev_commission(&mut self, epoch: u16, commission: u16) -> Result<()> { - // check if entry exists for the epoch + pub fn set_mev_commission( + &mut self, + epoch: u16, + commission: u16, + mev_earned: u32, + ) -> Result<()> { if let Some(entry) = self.history.last_mut() { - if entry.epoch == epoch { - entry.mev_commission = commission; - return Ok(()); + match entry.epoch.cmp(&epoch) { + Ordering::Equal => { + entry.mev_earned = mev_earned; + entry.mev_commission = commission; + return Ok(()); + } + Ordering::Greater => { + if let Some(entry) = self + .history + .arr_mut() + .iter_mut() + .find(|entry| entry.epoch == epoch) + { + entry.mev_earned = mev_earned; + entry.mev_commission = commission; + } + return Ok(()); + } + Ordering::Less => {} } } let entry = ValidatorHistoryEntry { epoch, mev_commission: commission, + mev_earned, ..ValidatorHistoryEntry::default() }; self.history.push(entry); @@ -383,10 +408,25 @@ impl ValidatorHistory { pub fn set_commission_and_slot(&mut self, epoch: u16, commission: u8, slot: u64) -> Result<()> { if let Some(entry) = self.history.last_mut() { - if entry.epoch == epoch { - entry.commission = commission; - entry.vote_account_last_update_slot = slot; - return Ok(()); + match entry.epoch.cmp(&epoch) { + Ordering::Equal => { + entry.commission = commission; + entry.vote_account_last_update_slot = slot; + return Ok(()); + } + Ordering::Greater => { + if let Some(entry) = self + .history + .arr_mut() + .iter_mut() + .find(|entry| entry.epoch == epoch) + { + entry.commission = commission; + entry.vote_account_last_update_slot = slot; + } + return Ok(()); + } + Ordering::Less => {} } } let entry = ValidatorHistoryEntry { @@ -420,16 +460,33 @@ impl ValidatorHistory { self.last_version_timestamp = contact_info_ts; if let Some(entry) = self.history.last_mut() { - if entry.epoch == epoch { - entry.ip = ip; - entry.client_type = contact_info.version.client as u8; - entry.version.major = contact_info.version.major as u8; - entry.version.minor = contact_info.version.minor as u8; - entry.version.patch = contact_info.version.patch; - return Ok(()); + match entry.epoch.cmp(&epoch) { + Ordering::Equal => { + entry.ip = ip; + entry.client_type = contact_info.version.client as u8; + entry.version.major = contact_info.version.major as u8; + entry.version.minor = contact_info.version.minor as u8; + entry.version.patch = contact_info.version.patch; + return Ok(()); + } + Ordering::Greater => { + if let Some(entry) = self + .history + .arr_mut() + .iter_mut() + .find(|entry| entry.epoch == epoch) + { + entry.ip = ip; + entry.client_type = contact_info.version.client as u8; + entry.version.major = contact_info.version.major as u8; + entry.version.minor = contact_info.version.minor as u8; + entry.version.patch = contact_info.version.patch; + } + return Ok(()); + } + Ordering::Less => {} } } - let entry = ValidatorHistoryEntry { epoch, ip, @@ -463,9 +520,23 @@ impl ValidatorHistory { self.last_ip_timestamp = contact_info_ts; if let Some(entry) = self.history.last_mut() { - if entry.epoch == epoch { - entry.ip = ip; - return Ok(()); + match entry.epoch.cmp(&epoch) { + Ordering::Equal => { + entry.ip = ip; + return Ok(()); + } + Ordering::Greater => { + if let Some(entry) = self + .history + .arr_mut() + .iter_mut() + .find(|entry| entry.epoch == epoch) + { + entry.ip = ip; + } + return Ok(()); + } + Ordering::Less => {} } } @@ -485,11 +556,27 @@ impl ValidatorHistory { self.last_version_timestamp = version_ts; if let Some(entry) = self.history.last_mut() { - if entry.epoch == epoch { - entry.version.major = version.version.major as u8; - entry.version.minor = version.version.minor as u8; - entry.version.patch = version.version.patch; - return Ok(()); + match entry.epoch.cmp(&epoch) { + Ordering::Equal => { + entry.version.major = version.version.major as u8; + entry.version.minor = version.version.minor as u8; + entry.version.patch = version.version.patch; + return Ok(()); + } + Ordering::Greater => { + if let Some(entry) = self + .history + .arr_mut() + .iter_mut() + .find(|entry| entry.epoch == epoch) + { + entry.version.major = version.version.major as u8; + entry.version.minor = version.version.minor as u8; + entry.version.patch = version.version.patch; + } + return Ok(()); + } + Ordering::Less => {} } } let entry = ValidatorHistoryEntry { @@ -517,11 +604,27 @@ impl ValidatorHistory { self.last_version_timestamp = version_ts; if let Some(entry) = self.history.last_mut() { - if entry.epoch == epoch { - entry.version.major = legacy_version.version.major as u8; - entry.version.minor = legacy_version.version.minor as u8; - entry.version.patch = legacy_version.version.patch; - return Ok(()); + match entry.epoch.cmp(&epoch) { + Ordering::Equal => { + entry.version.major = legacy_version.version.major as u8; + entry.version.minor = legacy_version.version.minor as u8; + entry.version.patch = legacy_version.version.patch; + return Ok(()); + } + Ordering::Greater => { + if let Some(entry) = self + .history + .arr_mut() + .iter_mut() + .find(|entry| entry.epoch == epoch) + { + entry.version.major = legacy_version.version.major as u8; + entry.version.minor = legacy_version.version.minor as u8; + entry.version.patch = legacy_version.version.patch; + } + return Ok(()); + } + Ordering::Less => {} } } let entry = ValidatorHistoryEntry { diff --git a/programs/validator-history/src/utils.rs b/programs/validator-history/src/utils.rs index 8fc9309c..5506e503 100644 --- a/programs/validator-history/src/utils.rs +++ b/programs/validator-history/src/utils.rs @@ -1,12 +1,36 @@ use anchor_lang::prelude::{AccountInfo, Pubkey}; +use anchor_lang::solana_program::native_token::lamports_to_sol; pub fn cast_epoch(epoch: u64) -> u16 { (epoch % u16::MAX as u64).try_into().unwrap() } +pub fn fixed_point_sol(lamports: u64) -> u32 { + // convert to sol + let mut sol = lamports_to_sol(lamports); + // truncate to 2 decimal points by rounding up, technically we can combine this line and the next + sol = f64::round(sol * 100.0) / 100.0; + // return a 4byte unsigned fixed point number with a 1/100 scaling factor + // this will internally represent a max value of 42949672.95 SOL + (sol * 100.0) as u32 +} + pub fn get_vote_account(validator_history_account_info: &AccountInfo) -> Pubkey { let pubkey_bytes = &validator_history_account_info.data.borrow()[8..32 + 8]; let mut data = [0; 32]; data.copy_from_slice(pubkey_bytes); Pubkey::from(data) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_fixed_point_sol() { + assert_eq!(fixed_point_sol(1000000000), 100); + assert_eq!(fixed_point_sol(4294967295000000000), 4294967295); + + assert_eq!(fixed_point_sol(429496729600000000), 4294967295) + } +} diff --git a/tests/src/fixtures.rs b/tests/src/fixtures.rs index 8c198550..fd6e5b87 100644 --- a/tests/src/fixtures.rs +++ b/tests/src/fixtures.rs @@ -15,7 +15,8 @@ use solana_sdk::{ use std::{cell::RefCell, rc::Rc}; use jito_tip_distribution::{ - sdk::derive_tip_distribution_account_address, state::TipDistributionAccount, + sdk::derive_tip_distribution_account_address, + state::{MerkleRoot, TipDistributionAccount}, }; use validator_history::{self, constants::MAX_ALLOC_BYTES, ClusterHistory, ValidatorHistory}; @@ -326,10 +327,19 @@ pub fn new_vote_account( } } -pub fn new_tip_distribution_account(vote_account: Pubkey, mev_commission_bps: u16) -> Account { +pub fn new_tip_distribution_account( + vote_account: Pubkey, + mev_commission_bps: u16, + mev_earned: u64, +) -> Account { + let merkle_root = MerkleRoot { + max_total_claim: mev_earned, + ..Default::default() + }; let tda = TipDistributionAccount { validator_vote_account: vote_account, validator_commission_bps: mev_commission_bps, + merkle_root: Some(merkle_root), ..TipDistributionAccount::default() }; let mut data = vec![]; diff --git a/tests/tests/test_mev_commission.rs b/tests/tests/test_mev_commission.rs index 7f41a098..7cf4f423 100644 --- a/tests/tests/test_mev_commission.rs +++ b/tests/tests/test_mev_commission.rs @@ -22,14 +22,14 @@ async fn test_mev_commission() { .0; ctx.borrow_mut().set_account( &tip_distribution_account, - &new_tip_distribution_account(fixture.vote_account, 42).into(), + &new_tip_distribution_account(fixture.vote_account, 42, 123_236_567_899).into(), ); // update mev commission let instruction = Instruction { program_id: validator_history::id(), - data: validator_history::instruction::UpdateMevCommission {}.data(), - accounts: validator_history::accounts::UpdateMevCommission { + data: validator_history::instruction::CopyTipDistributionAccount { epoch }.data(), + accounts: validator_history::accounts::CopyTipDistributionAccount { validator_history_account: fixture.validator_history_account, vote_account: fixture.vote_account, config: fixture.validator_history_config, @@ -62,7 +62,8 @@ async fn test_mev_commission() { assert!(account.history.idx == 0); assert!(account.history.arr[0].epoch == 0); assert!(account.history.arr[0].mev_commission == 42); - + assert!(account.history.arr[0].mev_earned == 12324); // fixed point representation + assert!((account.history.arr[0].mev_earned as f64 / 100.0) == 123.24_f64); // TODO this is causing a hash mismatch issue // fixture.advance_num_epochs(1).await; @@ -114,18 +115,19 @@ async fn test_mev_commission_fail() { let ctx = &fixture.ctx; fixture.initialize_config().await; fixture.initialize_validator_history_account().await; + let epoch = 0; // test update mev commission with uninitialized TDA let tip_distribution_account = derive_tip_distribution_account_address( &jito_tip_distribution::id(), &fixture.vote_account, - 0, + epoch, ) .0; let instruction = Instruction { program_id: validator_history::id(), - data: validator_history::instruction::UpdateMevCommission {}.data(), - accounts: validator_history::accounts::UpdateMevCommission { + data: validator_history::instruction::CopyTipDistributionAccount { epoch }.data(), + accounts: validator_history::accounts::CopyTipDistributionAccount { validator_history_account: fixture.validator_history_account, vote_account: fixture.vote_account, config: fixture.validator_history_config, @@ -146,6 +148,20 @@ async fn test_mev_commission_fail() { fixture.advance_num_epochs(1).await; // test update mev commission with wrong epoch + // note that just advancing the fixture's epoch cause a failure bc we relaxed the epoch constraints in the instruction/on tip_distribution_account + // explicitly pass the instruction a different epoch than the one used to generate the tip_distribution pda + let instruction = Instruction { + program_id: validator_history::id(), + data: validator_history::instruction::CopyTipDistributionAccount { epoch: 1 }.data(), + accounts: validator_history::accounts::CopyTipDistributionAccount { + validator_history_account: fixture.validator_history_account, + vote_account: fixture.vote_account, + config: fixture.validator_history_config, + tip_distribution_account, + signer: fixture.keypair.pubkey(), + } + .to_account_metas(None), + }; let transaction = Transaction::new_signed_with_payer( &[instruction], Some(&fixture.keypair.pubkey()), @@ -162,14 +178,14 @@ async fn test_mev_commission_fail() { .0; ctx.borrow_mut().set_account( &tip_distribution_account, - &new_tip_distribution_account(new_vote_account, 42).into(), + &new_tip_distribution_account(new_vote_account, 42, 123456).into(), ); // test update mev commission with wrong validator's TDA let instruction = Instruction { program_id: validator_history::id(), - data: validator_history::instruction::UpdateMevCommission {}.data(), - accounts: validator_history::accounts::UpdateMevCommission { + data: validator_history::instruction::CopyTipDistributionAccount { epoch }.data(), + accounts: validator_history::accounts::CopyTipDistributionAccount { validator_history_account: fixture.validator_history_account, vote_account: new_vote_account, config: fixture.validator_history_config,