Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add epoch start timestamp #23

Merged
merged 4 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion programs/validator-history/idl/validator_history.json
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,10 @@
"name": "totalBlocks",
"type": "u32"
},
{
"name": "epochStartTimestamp",
"type": "u32"
},
{
"name": "epoch",
"type": "u16"
Expand All @@ -658,7 +662,7 @@
"type": {
"array": [
"u8",
250
246
]
}
}
Expand Down
17 changes: 12 additions & 5 deletions programs/validator-history/src/instructions/copy_cluster_info.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
use anchor_lang::{
prelude::*,
solana_program::{clock::Clock, slot_history::Check},
use {
crate::{
errors::ValidatorHistoryError,
utils::{cast_epoch, cast_epoch_start_timestamp},
ClusterHistory,
},
anchor_lang::{
prelude::*,
solana_program::{clock::Clock, slot_history::Check},
},
};

use crate::{errors::ValidatorHistoryError, utils::cast_epoch, ClusterHistory};

#[derive(Accounts)]
pub struct CopyClusterInfo<'info> {
#[account(
Expand All @@ -29,12 +34,14 @@ pub fn handle_copy_cluster_info(ctx: Context<CopyClusterInfo>) -> Result<()> {

let epoch = cast_epoch(clock.epoch);

let epoch_start_timestamp = cast_epoch_start_timestamp(clock.epoch_start_timestamp);
// Sets the slot history for the previous epoch, since the current epoch is not yet complete.
if epoch > 0 {
cluster_history_account
.set_blocks(epoch - 1, blocks_in_epoch(epoch - 1, &slot_history)?)?;
}
cluster_history_account.set_blocks(epoch, blocks_in_epoch(epoch, &slot_history)?)?;
cluster_history_account.set_epoch_start_timestamp(epoch, epoch_start_timestamp)?;

cluster_history_account.cluster_history_last_update_slot = clock.slot;

Expand Down
38 changes: 26 additions & 12 deletions programs/validator-history/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use std::{cmp::Ordering, collections::HashMap, mem::size_of, net::IpAddr};

use anchor_lang::prelude::*;
use borsh::{BorshDeserialize, BorshSerialize};
use type_layout::TypeLayout;

use crate::{
crds_value::{ContactInfo, LegacyContactInfo, LegacyVersion, Version2},
errors::ValidatorHistoryError,
utils::cast_epoch,
use {
crate::{
crds_value::{ContactInfo, LegacyContactInfo, LegacyVersion, Version2},
errors::ValidatorHistoryError,
utils::cast_epoch,
},
anchor_lang::prelude::*,
borsh::{BorshDeserialize, BorshSerialize},
std::{cmp::Ordering, collections::HashMap, mem::size_of, net::IpAddr},
type_layout::TypeLayout,
};

static_assertions::const_assert_eq!(size_of::<Config>(), 104);
Expand Down Expand Up @@ -654,16 +654,18 @@ pub struct ClusterHistory {
#[zero_copy]
pub struct ClusterHistoryEntry {
pub total_blocks: u32,
pub epoch_start_timestamp: u32,
pub epoch: u16,
pub padding: [u8; 250],
pub padding: [u8; 246],
}

impl Default for ClusterHistoryEntry {
fn default() -> Self {
Self {
total_blocks: u32::MAX,
epoch: u16::MAX,
padding: [u8::MAX; 250],
epoch_start_timestamp: u32::MAX,
padding: [u8::MAX; 246],
}
}
}
Expand Down Expand Up @@ -793,6 +795,18 @@ impl ClusterHistory {

Ok(())
}
pub fn set_epoch_start_timestamp(
&mut self,
epoch: u16,
epoch_start_timestamp: u32,
) -> Result<()> {
if let Some(entry) = self.history.last_mut() {
if entry.epoch == epoch {
entry.epoch_start_timestamp = epoch_start_timestamp;
}
}
Ok(())
}
}

#[cfg(test)]
Expand Down
10 changes: 8 additions & 2 deletions programs/validator-history/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
use anchor_lang::prelude::{AccountInfo, Pubkey};
use anchor_lang::solana_program::native_token::lamports_to_sol;
use anchor_lang::{
prelude::{AccountInfo, Pubkey},
solana_program::native_token::lamports_to_sol,
};

pub fn cast_epoch(epoch: u64) -> u16 {
(epoch % u16::MAX as u64).try_into().unwrap()
}

pub fn cast_epoch_start_timestamp(start_timestamp: i64) -> u32 {
start_timestamp.try_into().unwrap()
}

pub fn fixed_point_sol(lamports: u64) -> u32 {
// convert to sol
let mut sol = lamports_to_sol(lamports);
Expand Down
57 changes: 39 additions & 18 deletions tests/src/fixtures.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
#![allow(clippy::await_holding_refcell_ref)]
use anchor_lang::{
solana_program::{
clock::Clock,
pubkey::Pubkey,
vote::state::{VoteInit, VoteState, VoteStateVersions},
use {
anchor_lang::{
solana_program::{
clock::Clock,
pubkey::Pubkey,
vote::state::{VoteInit, VoteState, VoteStateVersions},
},
AccountSerialize, InstructionData, ToAccountMetas,
},
AccountSerialize, InstructionData, ToAccountMetas,
};
use solana_program_test::*;
use solana_sdk::{
account::Account, epoch_schedule::EpochSchedule, instruction::Instruction, signature::Keypair,
signer::Signer, transaction::Transaction,
};
use std::{cell::RefCell, rc::Rc};

use jito_tip_distribution::{
sdk::derive_tip_distribution_account_address,
state::{MerkleRoot, TipDistributionAccount},
jito_tip_distribution::{
sdk::derive_tip_distribution_account_address,
state::{MerkleRoot, TipDistributionAccount},
},
solana_program_test::*,
solana_sdk::{
account::Account, epoch_schedule::EpochSchedule, instruction::Instruction,
signature::Keypair, signer::Signer, transaction::Transaction,
},
std::{cell::RefCell, rc::Rc},
validator_history::{self, constants::MAX_ALLOC_BYTES, ClusterHistory, ValidatorHistory},
};
use validator_history::{self, constants::MAX_ALLOC_BYTES, ClusterHistory, ValidatorHistory};

pub struct TestFixture {
pub ctx: Rc<RefCell<ProgramTestContext>>,
Expand Down Expand Up @@ -248,6 +249,26 @@ impl TestFixture {
.expect("Failed warping to future epoch");
}

pub async fn advance_clock(&self, num_epochs: u64, ms_per_slot: u64) -> u64 {
let mut clock: Clock = self
.ctx
.borrow_mut()
.banks_client
.get_sysvar()
.await
.expect("Failed getting clock");

let epoch_schedule: EpochSchedule = self.ctx.borrow().genesis_config().epoch_schedule;
let target_epoch = clock.epoch + num_epochs;
let dif_slots = epoch_schedule.get_first_slot_in_epoch(target_epoch) - clock.slot;

clock.epoch_start_timestamp += (dif_slots * ms_per_slot) as i64;
clock.unix_timestamp += (dif_slots * ms_per_slot) as i64;
self.ctx.borrow_mut().set_sysvar(&clock);

dif_slots
}

pub async fn submit_transaction_assert_success(&self, transaction: Transaction) {
let mut ctx = self.ctx.borrow_mut();
if let Err(e) = ctx
Expand Down
104 changes: 75 additions & 29 deletions tests/tests/test_cluster_history.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,41 @@
#![allow(clippy::await_holding_refcell_ref)]
use anchor_lang::{
solana_program::{instruction::Instruction, slot_history::SlotHistory},
InstructionData, ToAccountMetas,
use {
anchor_lang::{
solana_program::{instruction::Instruction, slot_history::SlotHistory},
InstructionData, ToAccountMetas,
},
solana_program_test::*,
solana_sdk::{
clock::Clock, compute_budget::ComputeBudgetInstruction, signer::Signer,
transaction::Transaction,
},
tests::fixtures::TestFixture,
validator_history::ClusterHistory,
};
use solana_program_test::*;
use solana_sdk::{
clock::Clock, compute_budget::ComputeBudgetInstruction, signer::Signer,
transaction::Transaction,
};
use tests::fixtures::TestFixture;
use validator_history::ClusterHistory;

const MS_PER_SLOT: u64 = 400;

fn create_copy_cluster_history_transaction(fixture: &TestFixture) -> Transaction {
let instruction = Instruction {
program_id: validator_history::id(),
data: validator_history::instruction::CopyClusterInfo {}.data(),
accounts: validator_history::accounts::CopyClusterInfo {
cluster_history_account: fixture.cluster_history_account,
slot_history: anchor_lang::solana_program::sysvar::slot_history::id(),
signer: fixture.keypair.pubkey(),
}
.to_account_metas(None),
};
let heap_request_ix = ComputeBudgetInstruction::request_heap_frame(256 * 1024);
let compute_budget_ix = ComputeBudgetInstruction::set_compute_unit_limit(300_000);

Transaction::new_signed_with_payer(
&[heap_request_ix, compute_budget_ix, instruction],
Some(&fixture.keypair.pubkey()),
&[&fixture.keypair],
fixture.ctx.borrow().last_blockhash,
)
}

#[tokio::test]
async fn test_copy_cluster_info() {
Expand All @@ -32,25 +58,7 @@ async fn test_copy_cluster_info() {
slot_history.add(latest_slot + 1);

// Submit instruction
let instruction = Instruction {
program_id: validator_history::id(),
data: validator_history::instruction::CopyClusterInfo {}.data(),
accounts: validator_history::accounts::CopyClusterInfo {
cluster_history_account: fixture.cluster_history_account,
slot_history: anchor_lang::solana_program::sysvar::slot_history::id(),
signer: fixture.keypair.pubkey(),
}
.to_account_metas(None),
};
let heap_request_ix = ComputeBudgetInstruction::request_heap_frame(256 * 1024);
let compute_budget_ix = ComputeBudgetInstruction::set_compute_unit_limit(300_000);

let transaction = Transaction::new_signed_with_payer(
&[heap_request_ix, compute_budget_ix, instruction],
Some(&fixture.keypair.pubkey()),
&[&fixture.keypair],
ctx.borrow().last_blockhash,
);
let transaction = create_copy_cluster_history_transaction(&fixture);

ctx.borrow_mut().set_sysvar(&slot_history);
fixture.submit_transaction_assert_success(transaction).await;
Expand All @@ -74,3 +82,41 @@ async fn test_copy_cluster_info() {
assert!(account.history.arr[1].total_blocks == 2);
assert_eq!(account.cluster_history_last_update_slot, latest_slot)
}

#[tokio::test]
async fn test_start_epoch_timestamp() {
// Initialize
let fixture = TestFixture::new().await;
let ctx = &fixture.ctx;
fixture.initialize_config().await;
fixture.initialize_cluster_history_account().await;

// Set SlotHistory sysvar
let slot_history = SlotHistory::default();
ctx.borrow_mut().set_sysvar(&slot_history);

// Submit epoch 0 instruction
let transaction = create_copy_cluster_history_transaction(&fixture);
fixture.submit_transaction_assert_success(transaction).await;

// Change epoch and set clock timestamps in the future
fixture.advance_num_epochs(1).await;
let dif_slots = fixture.advance_clock(1, MS_PER_SLOT).await;

// Submit epoch 1 instruction
let transaction = create_copy_cluster_history_transaction(&fixture);
fixture.submit_transaction_assert_success(transaction).await;

let account: ClusterHistory = fixture
.load_and_deserialize(&fixture.cluster_history_account)
.await;

assert_eq!(account.history.arr[0].epoch, 0);
assert_eq!(account.history.arr[1].epoch, 1);
assert_ne!(account.history.arr[0].epoch_start_timestamp, u32::MAX);
assert_ne!(account.history.arr[1].epoch_start_timestamp, u32::MAX);
assert_eq!(
account.history.arr[0].epoch_start_timestamp,
account.history.arr[1].epoch_start_timestamp - (dif_slots * MS_PER_SLOT) as u32
);
}