From 9158479dc40a72d8080da12cbb9da8ac9d330890 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Sat, 30 May 2020 00:39:24 -0700 Subject: [PATCH] Add node health check to transaction preflight --- core/src/rpc.rs | 21 ++++++++++++++++++++- core/src/rpc_health.rs | 13 +++++++++++++ core/src/rpc_service.rs | 28 ++++++++++++---------------- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/core/src/rpc.rs b/core/src/rpc.rs index bb7efc5d716215..8bf7c08f02e3e8 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -6,6 +6,7 @@ use crate::{ contact_info::ContactInfo, non_circulating_supply::calculate_non_circulating_supply, rpc_error::RpcCustomError, + rpc_health::*, validator::ValidatorExit, }; use bincode::serialize; @@ -74,6 +75,7 @@ pub struct JsonRpcRequestProcessor { blockstore: Arc, config: JsonRpcConfig, validator_exit: Arc>>, + health: Arc, } impl JsonRpcRequestProcessor { @@ -128,6 +130,7 @@ impl JsonRpcRequestProcessor { block_commitment_cache: Arc>, blockstore: Arc, validator_exit: Arc>>, + health: Arc, ) -> Self { JsonRpcRequestProcessor { config, @@ -135,6 +138,7 @@ impl JsonRpcRequestProcessor { block_commitment_cache, blockstore, validator_exit, + health, } } @@ -1442,6 +1446,13 @@ impl RpcSol for RpcSolImpl { .into()); } + if meta.request_processor.read().unwrap().health.check() != RpcHealthStatus::Ok { + return Err(RpcCustomError::SendTransactionPreflightFailure { + message: "RPC node is unhealthy, unable to simulate transaction".into(), + } + .into()); + } + let bank = &*meta.request_processor.read().unwrap().bank(None)?; if let Err(err) = run_transaction_simulation(&bank, &[transaction]) { // Note: it's possible that the transaction simulation failed but the actual @@ -1832,6 +1843,7 @@ pub mod tests { block_commitment_cache.clone(), blockstore, validator_exit, + RpcHealth::stub(), ))); let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(ContactInfo::default())); @@ -1880,6 +1892,7 @@ pub mod tests { block_commitment_cache, blockstore, validator_exit, + RpcHealth::stub(), ); thread::spawn(move || { let blockhash = bank.confirmed_last_blockhash().0; @@ -2824,6 +2837,7 @@ pub mod tests { block_commitment_cache, blockstore, validator_exit, + RpcHealth::stub(), ); Arc::new(RwLock::new(request_processor)) }, @@ -2894,7 +2908,9 @@ pub mod tests { ) } - pub fn create_validator_exit(exit: &Arc) -> Arc>> { + pub(crate) fn create_validator_exit( + exit: &Arc, + ) -> Arc>> { let mut validator_exit = ValidatorExit::default(); let exit_ = exit.clone(); validator_exit.register_exit(Box::new(move || exit_.store(true, Ordering::Relaxed))); @@ -2916,6 +2932,7 @@ pub mod tests { block_commitment_cache, blockstore, validator_exit, + RpcHealth::stub(), ); assert_eq!(request_processor.validator_exit(), Ok(false)); assert_eq!(exit.load(Ordering::Relaxed), false); @@ -2938,6 +2955,7 @@ pub mod tests { block_commitment_cache, blockstore, validator_exit, + RpcHealth::stub(), ); assert_eq!(request_processor.validator_exit(), Ok(true)); assert_eq!(exit.load(Ordering::Relaxed), true); @@ -3020,6 +3038,7 @@ pub mod tests { block_commitment_cache, blockstore, validator_exit, + RpcHealth::stub(), ); assert_eq!( request_processor.get_block_commitment(0), diff --git a/core/src/rpc_health.rs b/core/src/rpc_health.rs index ae5eb5444d3cd3..90e0f2da5dd674 100644 --- a/core/src/rpc_health.rs +++ b/core/src/rpc_health.rs @@ -6,6 +6,7 @@ use std::{ sync::Arc, }; +#[derive(PartialEq)] pub enum RpcHealthStatus { Ok, Behind, // Validator is behind its trusted validators @@ -88,4 +89,16 @@ impl RpcHealth { RpcHealthStatus::Ok } } + + #[cfg(test)] + pub(crate) fn stub() -> Arc { + Arc::new(Self::new( + Arc::new(ClusterInfo::new_with_invalid_keypair( + crate::contact_info::ContactInfo::default(), + )), + None, + 42, + Arc::new(AtomicBool::new(false)), + )) + } } diff --git a/core/src/rpc_service.rs b/core/src/rpc_service.rs index 2da311915925fa..0da94ae2bdc28f 100644 --- a/core/src/rpc_service.rs +++ b/core/src/rpc_service.rs @@ -255,6 +255,7 @@ impl JsonRpcService { block_commitment_cache, blockstore, validator_exit.clone(), + health.clone(), ))); #[cfg(test)] @@ -431,15 +432,12 @@ mod tests { #[test] fn test_is_file_get_path() { let bank_forks = create_bank_forks(); - let health = Arc::new(RpcHealth::new( - Arc::new(ClusterInfo::new_with_invalid_keypair(ContactInfo::default())), + let rrm = RpcRequestMiddleware::new( + PathBuf::from("/"), None, - 42, - Arc::new(AtomicBool::new(false)), - )); - - let rrm = - RpcRequestMiddleware::new(PathBuf::from("/"), None, bank_forks.clone(), health.clone()); + bank_forks.clone(), + RpcHealth::stub(), + ); let rrm_with_snapshot_config = RpcRequestMiddleware::new( PathBuf::from("/"), Some(SnapshotConfig { @@ -449,7 +447,7 @@ mod tests { compression: CompressionType::Bzip2, }), bank_forks, - health, + RpcHealth::stub(), ); assert!(rrm.is_file_get_path("/genesis.tar.bz2")); @@ -475,14 +473,12 @@ mod tests { #[test] fn test_health_check_with_no_trusted_validators() { - let health = Arc::new(RpcHealth::new( - Arc::new(ClusterInfo::new_with_invalid_keypair(ContactInfo::default())), + let rm = RpcRequestMiddleware::new( + PathBuf::from("/"), None, - 42, - Arc::new(AtomicBool::new(false)), - )); - - let rm = RpcRequestMiddleware::new(PathBuf::from("/"), None, create_bank_forks(), health); + create_bank_forks(), + RpcHealth::stub(), + ); assert_eq!(rm.health_check(), "ok"); }