From bb2fb07b392fe23060af95a3e9de8bc8e048b834 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 16 Dec 2020 22:26:54 +0000 Subject: [PATCH] Add blockstore skipped api (#14145) (#14167) * Add blockstore api to determine if a slot was skipped * Return custom rpc error if slot is skipped (cherry picked from commit ac0d32bc7e81848ccb85032c09c6d6d5f63a4813) Co-authored-by: Tyera Eulberg --- client/src/rpc_custom_error.rs | 12 ++++++++++++ core/src/rpc.rs | 11 +++++++---- ledger/src/blockstore.rs | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/client/src/rpc_custom_error.rs b/client/src/rpc_custom_error.rs index b28fac9a68..ed4607cacc 100644 --- a/client/src/rpc_custom_error.rs +++ b/client/src/rpc_custom_error.rs @@ -10,6 +10,7 @@ pub const JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE: i64 pub const JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE: i64 = -32004; pub const JSON_RPC_SERVER_ERROR_NODE_UNHEALTHLY: i64 = -32005; pub const JSON_RPC_SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE: i64 = -32006; +pub const JSON_RPC_SERVER_ERROR_SLOT_SKIPPED: i64 = -32007; pub enum RpcCustomError { BlockCleanedUp { @@ -26,6 +27,9 @@ pub enum RpcCustomError { }, RpcNodeUnhealthy, TransactionPrecompileVerificationFailure(solana_sdk::transaction::TransactionError), + SlotSkipped { + slot: Slot, + }, } impl From for Error { @@ -73,6 +77,14 @@ impl From for Error { message: format!("Transaction precompile verification failure {:?}", e), data: None, }, + RpcCustomError::SlotSkipped { slot } => Self { + code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_SLOT_SKIPPED), + message: format!( + "Slot {} was skipped, or missing due to ledger jump to recent snapshot", + slot + ), + data: None, + }, } } } diff --git a/core/src/rpc.rs b/core/src/rpc.rs index a33abe7167..a577a7be72 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -601,7 +601,7 @@ impl JsonRpcRequestProcessor { } } - fn check_blockstore_max_root( + fn check_blockstore_root( &self, result: &std::result::Result, slot: Slot, @@ -612,7 +612,7 @@ impl JsonRpcRequestProcessor { if result.is_err() { let err = result.as_ref().unwrap_err(); debug!( - "check_blockstore_max_root, slot: {:?}, max root: {:?}, err: {:?}", + "check_blockstore_root, slot: {:?}, max root: {:?}, err: {:?}", slot, self.blockstore.max_root(), err @@ -620,6 +620,9 @@ impl JsonRpcRequestProcessor { if slot >= self.blockstore.max_root() { return Err(RpcCustomError::BlockNotAvailable { slot }.into()); } + if self.blockstore.is_skipped(slot) { + return Err(RpcCustomError::SlotSkipped { slot }.into()); + } } Ok(()) } @@ -662,7 +665,7 @@ impl JsonRpcRequestProcessor { .highest_confirmed_root() { let result = self.blockstore.get_confirmed_block(slot); - self.check_blockstore_max_root(&result, slot)?; + self.check_blockstore_root(&result, slot)?; if result.is_err() { if let Some(bigtable_ledger_storage) = &self.bigtable_ledger_storage { return Ok(self @@ -768,7 +771,7 @@ impl JsonRpcRequestProcessor { .highest_confirmed_root() { let result = self.blockstore.get_block_time(slot); - self.check_blockstore_max_root(&result, slot)?; + self.check_blockstore_root(&result, slot)?; if result.is_err() { if let Some(bigtable_ledger_storage) = &self.bigtable_ledger_storage { return Ok(self diff --git a/ledger/src/blockstore.rs b/ledger/src/blockstore.rs index f544d10426..fd4c0330cc 100644 --- a/ledger/src/blockstore.rs +++ b/ledger/src/blockstore.rs @@ -2649,6 +2649,21 @@ impl Blockstore { matches!(self.db.get::(slot), Ok(Some(true))) } + /// Returns true if a slot is between the rooted slot bounds of the ledger, but has not itself + /// been rooted. This is either because the slot was skipped, or due to a gap in ledger data, + /// as when booting from a newer snapshot. + pub fn is_skipped(&self, slot: Slot) -> bool { + let lowest_root = self + .rooted_slot_iterator(0) + .ok() + .and_then(|mut iter| iter.next()) + .unwrap_or_default(); + match self.db.get::(slot).ok().flatten() { + Some(_) => false, + None => slot < self.max_root() && slot > lowest_root, + } + } + pub fn set_roots(&self, rooted_slots: &[u64]) -> Result<()> { let mut write_batch = self.db.batch()?; for slot in rooted_slots { @@ -5523,6 +5538,25 @@ pub mod tests { Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction"); } + #[test] + fn test_is_skipped() { + let blockstore_path = get_tmp_ledger_path!(); + let blockstore = Blockstore::open(&blockstore_path).unwrap(); + let roots = vec![2, 4, 7, 12, 15]; + blockstore.set_roots(&roots).unwrap(); + + for i in 0..20 { + if i < 2 || roots.contains(&i) || i > 15 { + assert!(!blockstore.is_skipped(i)); + } else { + assert!(blockstore.is_skipped(i)); + } + } + + drop(blockstore); + Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction"); + } + #[test] fn test_iter_bounds() { let blockstore_path = get_tmp_ledger_path!();