From 75b621160ea71d3b375cb5a49c30bdeefb11adce Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Wed, 30 Sep 2020 13:47:12 -0700 Subject: [PATCH] Add GetConfirmedBlocksWithLimit RPC method --- client/src/rpc_client.rs | 11 +++ client/src/rpc_request.rs | 2 + core/src/rpc.rs | 125 +++++++++++++++++++++++++++++++++-- docs/src/apps/jsonrpc-api.md | 27 +++++++- 4 files changed, 160 insertions(+), 5 deletions(-) diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index dd78ef5b29..7923302e79 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -271,6 +271,17 @@ impl RpcClient { ) } + pub fn get_confirmed_blocks_with_limit( + &self, + start_slot: Slot, + limit: usize, + ) -> ClientResult> { + self.send( + RpcRequest::GetConfirmedBlocksWithLimit, + json!([start_slot, limit]), + ) + } + pub fn get_confirmed_signatures_for_address( &self, address: &Pubkey, diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index 7bf60294e5..66e66a61ec 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -13,6 +13,7 @@ pub enum RpcRequest { GetClusterNodes, GetConfirmedBlock, GetConfirmedBlocks, + GetConfirmedBlocksWithLimit, GetConfirmedSignaturesForAddress, GetConfirmedSignaturesForAddress2, GetConfirmedTransaction, @@ -67,6 +68,7 @@ impl fmt::Display for RpcRequest { RpcRequest::GetClusterNodes => "getClusterNodes", RpcRequest::GetConfirmedBlock => "getConfirmedBlock", RpcRequest::GetConfirmedBlocks => "getConfirmedBlocks", + RpcRequest::GetConfirmedBlocksWithLimit => "getConfirmedBlocksWithLimit", RpcRequest::GetConfirmedSignaturesForAddress => "getConfirmedSignaturesForAddress", RpcRequest::GetConfirmedSignaturesForAddress2 => "getConfirmedSignaturesForAddress2", RpcRequest::GetConfirmedTransaction => "getConfirmedTransaction", diff --git a/core/src/rpc.rs b/core/src/rpc.rs index ba2b5d35bf..e98673b129 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -679,8 +679,8 @@ impl JsonRpcRequestProcessor { ))); } - let lowest_slot = self.blockstore.lowest_slot(); - if start_slot < lowest_slot { + let lowest_blockstore_slot = self.blockstore.lowest_slot(); + if start_slot < lowest_blockstore_slot { // If the starting slot is lower than what's available in blockstore assume the entire // [start_slot..end_slot] can be fetched from BigTable. if let Some(bigtable_ledger_storage) = &self.bigtable_ledger_storage { @@ -696,12 +696,45 @@ impl JsonRpcRequestProcessor { Ok(self .blockstore - .rooted_slot_iterator(max(start_slot, lowest_slot)) + .rooted_slot_iterator(max(start_slot, lowest_blockstore_slot)) .map_err(|_| Error::internal_error())? .filter(|&slot| slot <= end_slot) .collect()) } + pub fn get_confirmed_blocks_with_limit( + &self, + start_slot: Slot, + limit: usize, + ) -> Result> { + if limit > MAX_GET_CONFIRMED_BLOCKS_RANGE as usize { + return Err(Error::invalid_params(format!( + "Limit too large; max {}", + MAX_GET_CONFIRMED_BLOCKS_RANGE + ))); + } + + let lowest_blockstore_slot = self.blockstore.lowest_slot(); + + if start_slot < lowest_blockstore_slot { + // If the starting slot is lower than what's available in blockstore assume the entire + // range can be fetched from BigTable. + if let Some(bigtable_ledger_storage) = &self.bigtable_ledger_storage { + return Ok(self + .runtime_handle + .block_on(bigtable_ledger_storage.get_confirmed_blocks(start_slot, limit)) + .unwrap_or_else(|_| vec![])); + } + } + + Ok(self + .blockstore + .rooted_slot_iterator(max(start_slot, lowest_blockstore_slot)) + .map_err(|_| Error::internal_error())? + .take(limit) + .collect()) + } + pub fn get_block_time(&self, slot: Slot) -> Result> { if slot <= self @@ -1699,6 +1732,14 @@ pub trait RpcSol { end_slot: Option, ) -> Result>; + #[rpc(meta, name = "getConfirmedBlocksWithLimit")] + fn get_confirmed_blocks_with_limit( + &self, + meta: Self::Metadata, + start_slot: Slot, + limit: usize, + ) -> Result>; + #[rpc(meta, name = "getConfirmedTransaction")] fn get_confirmed_transaction( &self, @@ -2366,12 +2407,25 @@ impl RpcSol for RpcSolImpl { end_slot: Option, ) -> Result> { debug!( - "get_confirmed_blocks rpc request received: {:?}-{:?}", + "get_confirmed_blocks rpc request received: {}-{:?}", start_slot, end_slot ); meta.get_confirmed_blocks(start_slot, end_slot) } + fn get_confirmed_blocks_with_limit( + &self, + meta: Self::Metadata, + start_slot: Slot, + limit: usize, + ) -> Result> { + debug!( + "get_confirmed_blocks_with_limit rpc request received: {}-{}", + start_slot, limit, + ); + meta.get_confirmed_blocks_with_limit(start_slot, limit) + } + fn get_block_time(&self, meta: Self::Metadata, slot: Slot) -> Result> { meta.get_block_time(slot) } @@ -4733,6 +4787,69 @@ pub mod tests { ); } + #[test] + fn test_get_confirmed_blocks_with_limit() { + let bob_pubkey = Pubkey::new_rand(); + let roots = vec![0, 1, 3, 4, 8]; + let RpcHandler { + io, + meta, + block_commitment_cache, + .. + } = start_rpc_handler_with_tx_and_blockstore(&bob_pubkey, roots, 0); + block_commitment_cache + .write() + .unwrap() + .set_highest_confirmed_root(8); + + let req = r#"{"jsonrpc":"2.0","id":1,"method":"getConfirmedBlocksWithLimit","params":[0,500001]}"#; + let res = io.handle_request_sync(&req, meta.clone()); + assert_eq!( + res, + Some( + r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Limit too large; max 500000"},"id":1}"#.to_string(), + ) + ); + + let req = + r#"{"jsonrpc":"2.0","id":1,"method":"getConfirmedBlocksWithLimit","params":[0,0]}"#; + let res = io.handle_request_sync(&req, meta.clone()); + let result: Value = serde_json::from_str(&res.expect("actual response")) + .expect("actual response deserialization"); + let confirmed_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); + assert!(confirmed_blocks.is_empty()); + + let req = + r#"{"jsonrpc":"2.0","id":1,"method":"getConfirmedBlocksWithLimit","params":[2,2]}"#; + let res = io.handle_request_sync(&req, meta.clone()); + let result: Value = serde_json::from_str(&res.expect("actual response")) + .expect("actual response deserialization"); + let confirmed_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); + assert_eq!(confirmed_blocks, vec![3, 4]); + + let req = + r#"{"jsonrpc":"2.0","id":1,"method":"getConfirmedBlocksWithLimit","params":[2,3]}"#; + let res = io.handle_request_sync(&req, meta.clone()); + let result: Value = serde_json::from_str(&res.expect("actual response")) + .expect("actual response deserialization"); + let confirmed_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); + assert_eq!(confirmed_blocks, vec![3, 4, 8]); + + let req = r#"{"jsonrpc":"2.0","id":1,"method":"getConfirmedBlocksWithLimit","params":[2,500000]}"#; + let res = io.handle_request_sync(&req, meta.clone()); + let result: Value = serde_json::from_str(&res.expect("actual response")) + .expect("actual response deserialization"); + let confirmed_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); + assert_eq!(confirmed_blocks, vec![3, 4, 8]); + + let req = r#"{"jsonrpc":"2.0","id":1,"method":"getConfirmedBlocksWithLimit","params":[9,500000]}"#; + let res = io.handle_request_sync(&req, meta); + let result: Value = serde_json::from_str(&res.expect("actual response")) + .expect("actual response deserialization"); + let confirmed_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); + assert_eq!(confirmed_blocks, Vec::::new()); + } + #[test] fn test_get_block_time() { let bob_pubkey = Pubkey::new_rand(); diff --git a/docs/src/apps/jsonrpc-api.md b/docs/src/apps/jsonrpc-api.md index b2996b085e..7a2c7a4f8d 100644 --- a/docs/src/apps/jsonrpc-api.md +++ b/docs/src/apps/jsonrpc-api.md @@ -23,6 +23,7 @@ To interact with a Solana node inside a JavaScript application, use the [solana- - [getClusterNodes](jsonrpc-api.md#getclusternodes) - [getConfirmedBlock](jsonrpc-api.md#getconfirmedblock) - [getConfirmedBlocks](jsonrpc-api.md#getconfirmedblocks) +- [getConfirmedBlocksWithLimit](jsonrpc-api.md#getconfirmedblockswithlimit) - [getConfirmedSignaturesForAddress](jsonrpc-api.md#getconfirmedsignaturesforaddress) - [getConfirmedSignaturesForAddress2](jsonrpc-api.md#getconfirmedsignaturesforaddress2) - [getConfirmedTransaction](jsonrpc-api.md#getconfirmedtransaction) @@ -392,7 +393,7 @@ The JSON structure of inner instructions is defined as a list of objects in the ### getConfirmedBlocks -Returns a list of confirmed blocks +Returns a list of confirmed blocks between two slots #### Parameters: @@ -416,6 +417,30 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"m {"jsonrpc":"2.0","result":[5,6,7,8,9,10],"id":1} ``` +### getConfirmedBlocksWithLimit + +Returns a list of confirmed blocks starting at the given slot + +#### Parameters: + +- `` - start_slot, as u64 integer +- `` - limit, as u64 integer + +#### Results: + +The result field will be an array of u64 integers listing confirmed blocks +starting at `start_slot` for up to `limit` blocks, inclusive. + +#### Example: + +```bash +// Request +curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlockWithLimit2","params":[5, 3]}' localhost:8899 + +// Result +{"jsonrpc":"2.0","result":[5,6,7],"id":1} +``` + ### getConfirmedSignaturesForAddress **DEPRECATED: Please use getConfirmedSignaturesForAddress2 instead**