Skip to content
Open
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
27 changes: 25 additions & 2 deletions utils/steward-cli/src/commands/command_args.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
use clap::{arg, command, Parser, Subcommand};
use clap::{arg, command, Parser, Subcommand, ValueEnum};
use jito_steward::{UpdateParametersArgs, UpdatePriorityFeeParametersArgs};
use solana_sdk::pubkey::Pubkey;
use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey};
use std::path::PathBuf;

/// Commitment level for RPC queries
#[derive(Debug, Clone, Copy, Default, ValueEnum)]
pub enum CommitmentLevel {
Processed,
#[default]
Confirmed,
Finalized,
}

impl From<CommitmentLevel> for CommitmentConfig {
fn from(level: CommitmentLevel) -> Self {
match level {
CommitmentLevel::Processed => CommitmentConfig::processed(),
CommitmentLevel::Confirmed => CommitmentConfig::confirmed(),
CommitmentLevel::Finalized => CommitmentConfig::finalized(),
}
}
}

use crate::commands::{
actions::{
add_to_directed_stake_whitelist::AddToDirectedStakeWhitelist,
Expand Down Expand Up @@ -57,6 +76,10 @@ pub struct Args {
#[arg(long, global = true, env)]
pub signer: Option<String>,

/// Commitment level for RPC queries
#[arg(long, global = true, env, default_value = "confirmed")]
pub commitment: CommitmentLevel,

Comment on lines +79 to +82
Copy link
Collaborator

@aoikurokawa aoikurokawa Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can use CommitmentLevel from solana-sdk instead, what do you think?

use solana_sdk::commitment_config::CommitmentLevel;

impl From<CommitmentLevel> for CommitmentConfig {
    fn from(level: CommitmentLevel) -> Self {
        match level {
            CommitmentLevel::Processed => CommitmentConfig::processed(),
            CommitmentLevel::Confirmed => CommitmentConfig::confirmed(),
            CommitmentLevel::Finalized => CommitmentConfig::finalized(),
        }
    }
}

#[arg(long, global = true, env, default_value = "confirmed", value_parser = clap::value_parser!(CommitmentLevel))]
pub commitment: CommitmentLevel,

#[command(subcommand)]
pub commands: Commands,
}
Expand Down
4 changes: 3 additions & 1 deletion utils/steward-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,11 @@ pub mod utils;
async fn main() -> Result<()> {
dotenv().ok(); // Loads in .env file
let args = Args::parse();
let client = Arc::new(RpcClient::new_with_timeout(
let commitment_config = args.commitment.into();
let client = Arc::new(RpcClient::new_with_timeout_and_commitment(
args.json_rpc_url.clone(),
Duration::from_secs(60),
commitment_config,
));

let steward_program_id = args.steward_program_id;
Expand Down
104 changes: 46 additions & 58 deletions utils/validator-history-cli/src/commands/cranks/copy_vote_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@ use std::{
use anyhow::anyhow;
use clap::Parser;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::{account::Account, pubkey::Pubkey, signature::read_keypair_file, signer::Signer};
use solana_sdk::{pubkey::Pubkey, signature::read_keypair_file, signer::Signer};
use stakenet_keeper::entries::copy_vote_account_entry::CopyVoteAccountEntry;
use stakenet_sdk::{
models::entries::UpdateInstruction,
utils::{
accounts::get_all_validator_history_accounts,
accounts::{get_all_validator_history_accounts, get_validator_list_account},
helpers::vote_account_uploaded_recently,
transactions::{
get_multiple_accounts_batched, get_vote_accounts_with_retry, submit_instructions,
},
transactions::submit_instructions,
},
};

Expand All @@ -28,20 +26,22 @@ pub struct CrankCopyVoteAccount {
#[arg(short, long, env, default_value = "~/.config/solana/id.json")]
keypair_path: PathBuf,

/// Only process validators returned by get_vote_accounts RPC (active validators)
#[arg(long, env, default_value_t = false)]
active_only: bool,
/// Validator list account pubkey (for filtering to pool validators), e.g. 3R3nGZpQs2aZo5FDQvd2MUQ6R7KhAPainds6uT6uE2mn
/// If not provided, processes all validator history accounts.
#[arg(long, env)]
validator_list_pubkey: Option<String>,
}

pub async fn run(args: CrankCopyVoteAccount, rpc_url: String) -> anyhow::Result<()> {
let keypair = read_keypair_file(args.keypair_path)
.map_err(|e| anyhow!("Failed reading keypair file: {e}"))?;
let keypair = Arc::new(keypair);
let client = RpcClient::new(rpc_url);
let client = RpcClient::new(rpc_url.clone());
let client = Arc::new(client);

let epoch_info = client.get_epoch_info().await?;

// Fetch validator history accounts
let validator_histories =
get_all_validator_history_accounts(&client, validator_history::id()).await?;

Expand All @@ -51,61 +51,49 @@ pub async fn run(args: CrankCopyVoteAccount, rpc_url: String) -> anyhow::Result<
.map(|vote_history| (vote_history.vote_account, *vote_history)),
);

// If active_only is set, fetch active vote accounts from get_vote_accounts RPC
let active_vote_accounts: Option<HashSet<Pubkey>> = if args.active_only {
let vote_accounts = get_vote_accounts_with_retry(&client, 0, None).await?;
let active_set: HashSet<Pubkey> = vote_accounts
.iter()
.filter_map(|va| Pubkey::from_str(&va.vote_pubkey).ok())
.collect();
println!(
"Filtering to {} active vote accounts from get_vote_accounts RPC",
active_set.len()
);
Some(active_set)
} else {
None
};

let all_history_vote_account_pubkeys: Vec<Pubkey> =
validator_history_map.keys().cloned().collect();

let all_history_vote_accounts =
get_multiple_accounts_batched(all_history_vote_account_pubkeys.as_slice(), &client).await?;

let all_history_vote_account_map = all_history_vote_account_pubkeys
.into_iter()
.zip(all_history_vote_accounts)
.collect::<HashMap<Pubkey, Option<Account>>>();

let mut vote_accounts_to_update: HashSet<&Pubkey> = all_history_vote_account_map
// Optionally filter to pool validators if validator list is provided
let pool_vote_accounts: Option<HashSet<Pubkey>> =
if let Some(validator_list_pubkey_str) = &args.validator_list_pubkey {
let validator_list_pubkey = Pubkey::from_str(validator_list_pubkey_str)
.map_err(|e| anyhow!("Failed to parse validator list pubkey: {e}"))?;

let validator_list = get_validator_list_account(&client, &validator_list_pubkey)
.await
.map_err(|e| anyhow!("Failed to fetch validator list: {e}"))?;

let pool_vote_accounts: HashSet<Pubkey> = validator_list
.validators
.iter()
.map(|v| v.vote_account_address)
.collect();

Some(pool_vote_accounts)
} else {
None
};

// Filter to accounts that haven't been updated recently, optionally restricted to pool validators
let vote_accounts_to_update: Vec<&Pubkey> = validator_histories
.iter()
.filter_map(|(vote_address, vote_account)| match vote_account {
Some(account) => {
if account.owner == solana_sdk::vote::program::id() {
Some(vote_address)
} else {
None
.filter(|vote_history| {
// If pool filtering is enabled, must be in the pool
if let Some(ref pool) = pool_vote_accounts {
if !pool.contains(&vote_history.vote_account) {
return false;
}
}
_ => None,

// Must not have been uploaded recently
!vote_account_uploaded_recently(
&validator_history_map,
&vote_history.vote_account,
epoch_info.epoch,
epoch_info.absolute_slot,
)
})
.map(|vote_history| &vote_history.vote_account)
.collect();

// Filter to only active vote accounts if the flag is set
if let Some(ref active_set) = active_vote_accounts {
vote_accounts_to_update.retain(|vote_account| active_set.contains(vote_account));
}

vote_accounts_to_update.retain(|vote_account| {
!vote_account_uploaded_recently(
&validator_history_map,
vote_account,
epoch_info.epoch,
epoch_info.absolute_slot,
)
});

println!(
"Found {} vote accounts to update",
vote_accounts_to_update.len()
Expand Down
35 changes: 32 additions & 3 deletions utils/validator-history-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anchor_lang::{AccountDeserialize, Discriminator, InstructionData, ToAccountMetas};
use clap::{arg, command, Parser, Subcommand};
use clap::{arg, command, Parser, Subcommand, ValueEnum};
use dotenvy::dotenv;
use ipinfo::{BatchReqOpts, IpInfo, IpInfoConfig};
use rusqlite::Connection;
Expand All @@ -10,8 +10,28 @@ use solana_client::{
};
use solana_program::instruction::Instruction;
use solana_sdk::{
pubkey::Pubkey, signature::read_keypair_file, signer::Signer, transaction::Transaction,
commitment_config::CommitmentConfig, pubkey::Pubkey, signature::read_keypair_file,
signer::Signer, transaction::Transaction,
};

/// Commitment level for RPC queries
#[derive(Debug, Clone, Copy, Default, ValueEnum)]
pub enum CommitmentLevel {
Processed,
#[default]
Confirmed,
Finalized,
}

impl From<CommitmentLevel> for CommitmentConfig {
fn from(level: CommitmentLevel) -> Self {
match level {
CommitmentLevel::Processed => CommitmentConfig::processed(),
CommitmentLevel::Confirmed => CommitmentConfig::confirmed(),
CommitmentLevel::Finalized => CommitmentConfig::finalized(),
}
}
}
use spl_stake_pool::state::{StakePool, ValidatorList};
use stakenet_keeper::operations::block_metadata::db::DBSlotInfo;
use std::{collections::HashMap, path::PathBuf, thread::sleep, time::Duration};
Expand Down Expand Up @@ -41,6 +61,10 @@ struct Args {
)]
json_rpc_url: String,

/// Commitment level for RPC queries
#[arg(long, global = true, env, default_value = "confirmed")]
commitment: CommitmentLevel,

#[command(subcommand)]
commands: Commands,
}
Expand Down Expand Up @@ -1265,7 +1289,12 @@ async fn main() -> anyhow::Result<()> {
dotenv().ok();
env_logger::init();
let args = Args::parse();
let client = RpcClient::new_with_timeout(args.json_rpc_url.clone(), Duration::from_secs(60));
let commitment_config = args.commitment.into();
let client = RpcClient::new_with_timeout_and_commitment(
args.json_rpc_url.clone(),
Duration::from_secs(60),
commitment_config,
);
match args.commands {
Commands::InitConfig(args) => command_init_config(args, client),
Commands::ReallocConfig(args) => command_realloc_config(args, client),
Expand Down
Loading