From ecdbfaf958e4756fc5f5ab49ee216ec9374ec238 Mon Sep 17 00:00:00 2001 From: Evan Batsell Date: Mon, 3 Feb 2025 12:30:42 -0500 Subject: [PATCH 1/2] (WIP) set merkle root fix --- tip-router-operator-cli/src/main.rs | 12 +++--- tip-router-operator-cli/src/submit.rs | 48 ++++++++++++++++------- tip-router-operator-cli/src/tip_router.rs | 5 +++ 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/tip-router-operator-cli/src/main.rs b/tip-router-operator-cli/src/main.rs index 5b453b7f..5cd9c529 100644 --- a/tip-router-operator-cli/src/main.rs +++ b/tip-router-operator-cli/src/main.rs @@ -29,17 +29,17 @@ async fn main() -> Result<()> { let rpc_client = EllipsisClient::from_rpc_with_timeout( RpcClient::new(cli.rpc_url.clone()), &read_keypair_file(&cli.keypair_path).expect("Failed to read keypair file"), - 60_000, + 1_800_000, // 30 minutes )?; set_host_id(cli.operator_address.to_string()); // Ensure tx submission works - let test_meta_merkle_root = [1; 32]; - let ix = spl_memo::build_memo(&test_meta_merkle_root.to_vec(), &[&keypair.pubkey()]); - info!("Submitting test tx {:?}", test_meta_merkle_root); - let tx = Transaction::new_with_payer(&[ix], Some(&keypair.pubkey())); - rpc_client.process_transaction(tx, &[&keypair]).await?; + // let test_meta_merkle_root = [1; 32]; + // let ix = spl_memo::build_memo(&test_meta_merkle_root.to_vec(), &[&keypair.pubkey()]); + // info!("Submitting test tx {:?}", test_meta_merkle_root); + // let tx = Transaction::new_with_payer(&[ix], Some(&keypair.pubkey())); + // rpc_client.process_transaction(tx, &[&keypair]).await?; info!( "CLI Arguments: diff --git a/tip-router-operator-cli/src/submit.rs b/tip-router-operator-cli/src/submit.rs index 21938a22..d52dd062 100644 --- a/tip-router-operator-cli/src/submit.rs +++ b/tip-router-operator-cli/src/submit.rs @@ -1,13 +1,15 @@ +use std::time::Duration; use std::{path::PathBuf, str::FromStr}; use anchor_lang::AccountDeserialize; use ellipsis_client::EllipsisClient; use jito_bytemuck::AccountDeserialize as JitoAccountDeserialize; use jito_tip_distribution_sdk::{derive_config_account_address, TipDistributionAccount}; -use jito_tip_router_core::ballot_box::BallotBox; +use jito_tip_router_core::{ballot_box::BallotBox, config::Config}; use log::{debug, error, info}; use meta_merkle_tree::meta_merkle_tree::MetaMerkleTree; use solana_account_decoder::UiAccountEncoding; +use solana_client::nonblocking::rpc_client::RpcClient as AsyncRpcClient; use solana_client::{ rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig}, rpc_filter::{Memcmp, RpcFilterType}, @@ -76,7 +78,9 @@ pub async fn submit_to_ncn( ) -> Result<(), anyhow::Error> { let epoch_info = client.get_epoch_info()?; let meta_merkle_tree = MetaMerkleTree::new_from_file(meta_merkle_tree_path)?; + let config_pda = Config::find_program_address(tip_router_program_id, ncn_address).0; let config = get_ncn_config(client, tip_router_program_id, ncn_address).await?; + info!("Fetched NCN config"); // The meta merkle root files are tagged with the epoch they have created the snapshot for // Tip router accounts for that merkle root are created in the next epoch @@ -101,12 +105,15 @@ pub async fn submit_to_ncn( } }; + info!("Fetched ballot box account"); + let ballot_box = BallotBox::try_from_slice_unchecked(&ballot_box_account.data)?; let is_voting_valid = ballot_box.is_voting_valid( epoch_info.absolute_slot, config.valid_slots_after_consensus(), )?; + info!("Is voting valid: {}", is_voting_valid); // If exists, look for vote from current operator let vote = ballot_box @@ -186,10 +193,16 @@ pub async fn submit_to_ncn( let tip_distribution_accounts = get_tip_distribution_accounts_to_upload( client, merkle_root_epoch, + &config_pda, tip_distribution_program_id, ) .await?; + info!( + "Setting merkle roots for {} tip distribution accounts", + tip_distribution_accounts.len() + ); + // For each TipDistributionAccount returned, if it has no root uploaded, upload root with set_merkle_root match set_merkle_roots_batched( client, @@ -238,17 +251,19 @@ pub async fn submit_to_ncn( async fn get_tip_distribution_accounts_to_upload( client: &EllipsisClient, epoch: u64, - + tip_router_config_address: &Pubkey, tip_distribution_program_id: &Pubkey, ) -> Result, anyhow::Error> { - let config_address = derive_config_account_address(tip_distribution_program_id).0; + let rpc_client = AsyncRpcClient::new_with_timeout(client.url(), Duration::from_secs(1800)); + let tip_distribution_config_address = + derive_config_account_address(tip_distribution_program_id).0; // Filters assume merkle root is None let filters = vec![ RpcFilterType::Memcmp(Memcmp::new_raw_bytes( 8 // Discriminator + 32, // Pubkey - validator_vote_account - config_address.to_bytes().to_vec(), + tip_router_config_address.to_bytes().to_vec(), )), RpcFilterType::Memcmp(Memcmp::new_raw_bytes( 8 // Discriminator @@ -259,17 +274,19 @@ async fn get_tip_distribution_accounts_to_upload( )), ]; - let tip_distribution_accounts = client.get_program_accounts_with_config( - tip_distribution_program_id, - RpcProgramAccountsConfig { - filters: Some(filters), - account_config: RpcAccountInfoConfig { - encoding: Some(UiAccountEncoding::Base64), - ..RpcAccountInfoConfig::default() + let tip_distribution_accounts = rpc_client + .get_program_accounts_with_config( + tip_distribution_program_id, + RpcProgramAccountsConfig { + filters: Some(filters), + account_config: RpcAccountInfoConfig { + encoding: Some(UiAccountEncoding::Base64), + ..RpcAccountInfoConfig::default() + }, + ..RpcProgramAccountsConfig::default() }, - ..RpcProgramAccountsConfig::default() - }, - )?; + ) + .await?; let tip_distribution_accounts = tip_distribution_accounts .into_iter() @@ -280,7 +297,8 @@ async fn get_tip_distribution_accounts_to_upload( Ok(tip_distribution_account) => { // Double check that GPA filter worked if tip_distribution_account.epoch_created_at == epoch - && tip_distribution_account.merkle_root_upload_authority == config_address + && tip_distribution_account.merkle_root_upload_authority + == *tip_router_config_address { Some((pubkey, tip_distribution_account)) } else { diff --git a/tip-router-operator-cli/src/tip_router.rs b/tip-router-operator-cli/src/tip_router.rs index 624abff8..40742112 100644 --- a/tip-router-operator-cli/src/tip_router.rs +++ b/tip-router-operator-cli/src/tip_router.rs @@ -140,6 +140,11 @@ pub async fn set_merkle_roots_batched( }) .collect::>(); + info!( + "Build instructions for {} tip distribution accounts", + instructions.len() + ); + let mut results = vec![]; for _ in 0..instructions.len() { results.push(Err(EllipsisClientError::Other(anyhow::anyhow!( From 41d6234733676ebe9421b8aeec38ed9a749c36f7 Mon Sep 17 00:00:00 2001 From: Evan Batsell Date: Tue, 4 Feb 2025 16:25:35 -0500 Subject: [PATCH 2/2] Clean up set merkle fix --- tip-router-operator-cli/src/cli.rs | 3 +++ tip-router-operator-cli/src/main.rs | 3 +++ tip-router-operator-cli/src/submit.rs | 11 ++++------- tip-router-operator-cli/src/tip_router.rs | 9 ++------- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/tip-router-operator-cli/src/cli.rs b/tip-router-operator-cli/src/cli.rs index 03b171b1..09169c5b 100644 --- a/tip-router-operator-cli/src/cli.rs +++ b/tip-router-operator-cli/src/cli.rs @@ -63,6 +63,9 @@ pub enum Commands { #[arg(long, env)] override_target_slot: Option, + + #[arg(long, env, default_value = "false")] + set_merkle_roots: bool, }, SnapshotSlot { #[arg(short, long, env)] diff --git a/tip-router-operator-cli/src/main.rs b/tip-router-operator-cli/src/main.rs index 5cd9c529..972e688c 100644 --- a/tip-router-operator-cli/src/main.rs +++ b/tip-router-operator-cli/src/main.rs @@ -69,6 +69,7 @@ async fn main() -> Result<()> { num_monitored_epochs, start_next_epoch, override_target_slot, + set_merkle_roots, } => { info!("Running Tip Router..."); info!("NCN Address: {}", ncn_address); @@ -110,6 +111,7 @@ async fn main() -> Result<()> { &tip_distribution_program_id, num_monitored_epochs, &cli_clone, + set_merkle_roots, ) .await { @@ -240,6 +242,7 @@ async fn main() -> Result<()> { &tip_router_program_id, &tip_distribution_program_id, cli.submit_as_memo, + true, ) .await?; } diff --git a/tip-router-operator-cli/src/submit.rs b/tip-router-operator-cli/src/submit.rs index d52dd062..5ffe0dd7 100644 --- a/tip-router-operator-cli/src/submit.rs +++ b/tip-router-operator-cli/src/submit.rs @@ -30,6 +30,7 @@ pub async fn submit_recent_epochs_to_ncn( tip_distribution_program_id: &Pubkey, num_monitored_epochs: u64, cli_args: &Cli, + set_merkle_roots: bool, ) -> Result<(), anyhow::Error> { let epoch = client.get_epoch_info()?; let operator_address = Pubkey::from_str(&cli_args.operator_address)?; @@ -54,6 +55,7 @@ pub async fn submit_recent_epochs_to_ncn( tip_router_program_id, tip_distribution_program_id, cli_args.submit_as_memo, + set_merkle_roots, ) .await { @@ -75,12 +77,12 @@ pub async fn submit_to_ncn( tip_router_program_id: &Pubkey, tip_distribution_program_id: &Pubkey, submit_as_memo: bool, + set_merkle_roots: bool, ) -> Result<(), anyhow::Error> { let epoch_info = client.get_epoch_info()?; let meta_merkle_tree = MetaMerkleTree::new_from_file(meta_merkle_tree_path)?; let config_pda = Config::find_program_address(tip_router_program_id, ncn_address).0; let config = get_ncn_config(client, tip_router_program_id, ncn_address).await?; - info!("Fetched NCN config"); // The meta merkle root files are tagged with the epoch they have created the snapshot for // Tip router accounts for that merkle root are created in the next epoch @@ -105,15 +107,12 @@ pub async fn submit_to_ncn( } }; - info!("Fetched ballot box account"); - let ballot_box = BallotBox::try_from_slice_unchecked(&ballot_box_account.data)?; let is_voting_valid = ballot_box.is_voting_valid( epoch_info.absolute_slot, config.valid_slots_after_consensus(), )?; - info!("Is voting valid: {}", is_voting_valid); // If exists, look for vote from current operator let vote = ballot_box @@ -187,7 +186,7 @@ pub async fn submit_to_ncn( } } - if ballot_box.is_consensus_reached() { + if ballot_box.is_consensus_reached() && set_merkle_roots { // Fetch TipDistributionAccounts filtered by epoch and upload authority // Tip distribution accounts are derived from the epoch they are for let tip_distribution_accounts = get_tip_distribution_accounts_to_upload( @@ -255,8 +254,6 @@ async fn get_tip_distribution_accounts_to_upload( tip_distribution_program_id: &Pubkey, ) -> Result, anyhow::Error> { let rpc_client = AsyncRpcClient::new_with_timeout(client.url(), Duration::from_secs(1800)); - let tip_distribution_config_address = - derive_config_account_address(tip_distribution_program_id).0; // Filters assume merkle root is None let filters = vec![ diff --git a/tip-router-operator-cli/src/tip_router.rs b/tip-router-operator-cli/src/tip_router.rs index 40742112..9d32e7d4 100644 --- a/tip-router-operator-cli/src/tip_router.rs +++ b/tip-router-operator-cli/src/tip_router.rs @@ -11,7 +11,7 @@ use jito_tip_router_core::{ epoch_snapshot::{EpochSnapshot, OperatorSnapshot}, epoch_state::EpochState, }; -use log::info; +use log::{error, info}; use meta_merkle_tree::meta_merkle_tree::MetaMerkleTree; use solana_sdk::{ pubkey::Pubkey, @@ -114,7 +114,7 @@ pub async fn set_merkle_roots_batched( let proof = if let Some(proof) = meta_merkle_node.proof { proof } else { - // TODO emit big warning NO PROOF + error!("No proof found for tip distribution account {:?}", key); return None; }; @@ -140,11 +140,6 @@ pub async fn set_merkle_roots_batched( }) .collect::>(); - info!( - "Build instructions for {} tip distribution accounts", - instructions.len() - ); - let mut results = vec![]; for _ in 0..instructions.len() { results.push(Err(EllipsisClientError::Other(anyhow::anyhow!(