diff --git a/Cargo.lock b/Cargo.lock index 31f42a2b19..48af3fb34f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5148,9 +5148,7 @@ dependencies = [ "solana-metrics", "solana-notifier", "solana-sdk", - "solana-transaction-status", "solana-version", - "solana-vote-program", ] [[package]] diff --git a/watchtower/Cargo.toml b/watchtower/Cargo.toml index 3aac50fa12..40523e9c99 100644 --- a/watchtower/Cargo.toml +++ b/watchtower/Cargo.toml @@ -20,13 +20,7 @@ solana-logger = { path = "../logger", version = "1.5.0" } solana-metrics = { path = "../metrics", version = "1.5.0" } solana-notifier = { path = "../notifier", version = "1.5.0" } solana-sdk = { path = "../sdk", version = "1.5.0" } -solana-transaction-status = { path = "../transaction-status", version = "1.5.0" } solana-version = { path = "../version", version = "1.5.0" } -solana-vote-program = { path = "../programs/vote", version = "1.5.0" } - -[[bin]] -name = "solana-watchtower" -path = "src/main.rs" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/watchtower/src/main.rs b/watchtower/src/main.rs index 047076adb4..0007d0b729 100644 --- a/watchtower/src/main.rs +++ b/watchtower/src/main.rs @@ -1,29 +1,27 @@ //! A command-line executable for monitoring the health of a cluster -use clap::{crate_description, crate_name, value_t, value_t_or_exit, App, Arg}; -use log::*; -use solana_clap_utils::{ - input_parsers::pubkeys_of, - input_validators::{is_pubkey_or_keypair, is_url}, -}; -use solana_cli_output::display::{format_labeled_address, write_transaction}; -use solana_client::{ - client_error::Result as ClientResult, rpc_client::RpcClient, rpc_response::RpcVoteAccountStatus, -}; -use solana_metrics::{datapoint_error, datapoint_info}; -use solana_notifier::Notifier; -use solana_sdk::{ - clock::Slot, hash::Hash, native_token::lamports_to_sol, program_utils::limited_deserialize, - pubkey::Pubkey, -}; -use solana_transaction_status::{EncodedConfirmedBlock, UiTransactionEncoding}; -use solana_vote_program::vote_instruction::VoteInstruction; -use std::{ - collections::HashMap, - error, - str::FromStr, - thread::sleep, - time::{Duration, Instant}, +use { + clap::{crate_description, crate_name, value_t, value_t_or_exit, App, Arg}, + log::*, + solana_clap_utils::{ + input_parsers::pubkeys_of, + input_validators::{is_pubkey_or_keypair, is_url}, + }, + solana_cli_output::display::format_labeled_address, + solana_client::{ + client_error::Result as ClientResult, rpc_client::RpcClient, + rpc_response::RpcVoteAccountStatus, + }, + solana_metrics::{datapoint_error, datapoint_info}, + solana_notifier::Notifier, + solana_sdk::{hash::Hash, native_token::lamports_to_sol, pubkey::Pubkey}, + std::{ + collections::HashMap, + error, + str::FromStr, + thread::sleep, + time::{Duration, Instant}, + }, }; struct Config { @@ -32,7 +30,6 @@ struct Config { validator_identity_pubkeys: Vec, no_duplicate_notifications: bool, monitor_active_stake: bool, - notify_on_transactions: bool, address_labels: HashMap, } @@ -108,14 +105,6 @@ fn get_config() -> Config { .takes_value(false) .help("Alert when the current stake for the cluster drops below 80%"), ) - .arg( - Arg::with_name("notify_on_transactions") - .long("notify-on-transactions") - .takes_value(false) - .help("Send a notification on all non-vote transactions. This can be very verbose!\ - Note that the notification environment variables used by this feature all require a \ - TRANSACTION_NOTIFIER_ prefix. For example: TRANSACTION_NOTIFIER_SLACK_WEBHOOK"), - ) .get_matches(); let config = if let Some(config_file) = matches.value_of("config_file") { @@ -135,7 +124,6 @@ fn get_config() -> Config { let no_duplicate_notifications = matches.is_present("no_duplicate_notifications"); let monitor_active_stake = matches.is_present("monitor_active_stake"); - let notify_on_transactions = matches.is_present("notify_on_transactions"); let config = Config { interval, @@ -143,7 +131,6 @@ fn get_config() -> Config { validator_identity_pubkeys, no_duplicate_notifications, monitor_active_stake, - notify_on_transactions, address_labels: config.address_labels, }; @@ -157,136 +144,6 @@ fn get_config() -> Config { config } -fn process_confirmed_block( - notifier: &Notifier, - slot: Slot, - confirmed_block: EncodedConfirmedBlock, -) { - let break_program_id = "BrEAK7zGZ6dM71zUDACDqJnekihmwF15noTddWTsknjC" - .parse::() - .unwrap(); - let mut vote_transactions = 0; - - for rpc_transaction in &confirmed_block.transactions { - if let Some(transaction) = rpc_transaction.transaction.decode() { - if transaction.verify().is_ok() { - let mut notify = true; - - // Ignore simple Vote transactions since they are too prevalent - if transaction.message.instructions.len() == 1 { - let instruction = &transaction.message.instructions[0]; - let program_pubkey = - transaction.message.account_keys[instruction.program_id_index as usize]; - if program_pubkey == solana_vote_program::id() { - if let Ok(VoteInstruction::Vote(_)) = - limited_deserialize::(&instruction.data) - { - vote_transactions += 1; - notify = false; - } - } - if program_pubkey == break_program_id { - notify = false; - } - } - - if notify { - let mut w = Vec::new(); - if write_transaction(&mut w, &transaction, &rpc_transaction.meta, "").is_ok() { - if let Ok(s) = String::from_utf8(w) { - notifier.send(&format!("```Slot: {}\n{}```", slot, s)); - } - } - } - } else { - datapoint_error!( - "watchtower-sanity-failure", - ("slot", slot, i64), - ("err", "Transaction signature verification failed", String) - ); - } - } - } - info!( - "Process slot {} with {} regular transactions (and {} votes)", - slot, - confirmed_block.transactions.len() - vote_transactions, - vote_transactions - ); -} - -fn load_blocks( - rpc_client: &RpcClient, - start_slot: Slot, - end_slot: Slot, -) -> ClientResult> { - info!( - "Loading confirmed blocks between slots: {} - {}", - start_slot, end_slot - ); - - let slots = rpc_client.get_confirmed_blocks(start_slot, Some(end_slot))?; - - let mut blocks = vec![]; - for slot in slots.into_iter() { - let block = - rpc_client.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Base64)?; - blocks.push((slot, block)); - } - Ok(blocks) -} - -fn transaction_monitor(rpc_client: RpcClient) { - let notifier = Notifier::new("TRANSACTION_NOTIFIER_"); - let mut start_slot = loop { - match rpc_client.get_slot() { - Ok(slot) => break slot, - Err(err) => { - warn!("Failed to get current slot: {}", err); - } - } - sleep(Duration::from_secs(1)); - }; - - loop { - let end_slot = start_slot + 50; - info!("start_slot:{} - end_slot:{}", start_slot, end_slot); - - let latest_available_slot = rpc_client.get_slot().unwrap_or_else(|err| { - info!("get_slot() failed: {}", err); - 0 - }); - - if latest_available_slot <= start_slot { - info!("Waiting for a slot greater than {}...", start_slot); - sleep(Duration::from_secs(5)); - continue; - } - - match load_blocks(&rpc_client, start_slot + 1, end_slot) { - Ok(blocks) => { - info!("Loaded {} blocks", blocks.len()); - - if blocks.is_empty() && end_slot < latest_available_slot { - start_slot = end_slot; - } else { - for (slot, block) in blocks.into_iter() { - process_confirmed_block(¬ifier, slot, block); - start_slot = slot; - } - } - } - Err(err) => { - info!( - "failed to get blocks in range ({},{}): {}", - start_slot, end_slot, err - ); - sleep(Duration::from_secs(1)); - } - } - } -} - fn get_cluster_info(rpc_client: &RpcClient) -> ClientResult<(u64, Hash, RpcVoteAccountStatus)> { let transaction_count = rpc_client.get_transaction_count()?; let recent_blockhash = rpc_client.get_recent_blockhash()?.0; @@ -300,13 +157,6 @@ fn main() -> Result<(), Box> { solana_logger::setup_with_default("solana=info"); solana_metrics::set_panic_hook("watchtower"); - let _notify_thread = if config.notify_on_transactions { - let rpc_client = RpcClient::new(config.json_rpc_url.clone()); - Some(std::thread::spawn(move || transaction_monitor(rpc_client))) - } else { - None - }; - let rpc_client = RpcClient::new(config.json_rpc_url.clone()); let notifier = Notifier::default(); let mut last_transaction_count = 0;