diff --git a/cli/src/cluster_query.rs b/cli/src/cluster_query.rs index 906cdd6fc1..0484ee01c2 100644 --- a/cli/src/cluster_query.rs +++ b/cli/src/cluster_query.rs @@ -1093,8 +1093,8 @@ pub fn process_get_slot(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessR } pub fn process_get_block_height(rpc_client: &RpcClient, _config: &CliConfig) -> ProcessResult { - let epoch_info = rpc_client.get_epoch_info()?; - Ok(epoch_info.block_height.to_string()) + let block_height = rpc_client.get_block_height()?; + Ok(block_height.to_string()) } pub fn parse_show_block_production(matches: &ArgMatches<'_>) -> Result { diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index a7fff8d3be..bab867edb8 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -404,6 +404,20 @@ impl RpcClient { ) } + pub fn get_block_height(&self) -> ClientResult { + self.get_block_height_with_commitment(self.commitment_config) + } + + pub fn get_block_height_with_commitment( + &self, + commitment_config: CommitmentConfig, + ) -> ClientResult { + self.send( + RpcRequest::GetBlockHeight, + json!([self.maybe_map_commitment(commitment_config)?]), + ) + } + pub fn get_slot_leaders(&self, start_slot: Slot, limit: u64) -> ClientResult> { self.send(RpcRequest::GetSlotLeaders, json!([start_slot, limit])) .and_then(|slot_leaders: Vec| { @@ -1081,6 +1095,7 @@ impl RpcClient { blockhash, fee_calculator, last_valid_slot, + .. }, }) = self .send::>( @@ -1088,6 +1103,19 @@ impl RpcClient { json!([self.maybe_map_commitment(commitment_config)?]), ) { (context, blockhash, fee_calculator, last_valid_slot) + } else if let Ok(Response { + context, + value: + DeprecatedRpcFees { + blockhash, + fee_calculator, + last_valid_slot, + }, + }) = self.send::>( + RpcRequest::GetFees, + json!([self.maybe_map_commitment(commitment_config)?]), + ) { + (context, blockhash, fee_calculator, last_valid_slot) } else if let Ok(Response { context, value: diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index 5bafad08cb..345ea1f0b4 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -11,6 +11,7 @@ pub enum RpcRequest { DeregisterNode, GetAccountInfo, GetBalance, + GetBlockHeight, GetBlockProduction, GetBlockTime, GetClusterNodes, @@ -84,6 +85,7 @@ impl fmt::Display for RpcRequest { RpcRequest::DeregisterNode => "deregisterNode", RpcRequest::GetAccountInfo => "getAccountInfo", RpcRequest::GetBalance => "getBalance", + RpcRequest::GetBlockHeight => "getBlockHeight", RpcRequest::GetBlockProduction => "getBlockProduction", RpcRequest::GetBlockTime => "getBlockTime", RpcRequest::GetClusterNodes => "getClusterNodes", diff --git a/client/src/rpc_response.rs b/client/src/rpc_response.rs index 680e018d77..9e60266468 100644 --- a/client/src/rpc_response.rs +++ b/client/src/rpc_response.rs @@ -46,6 +46,15 @@ pub struct RpcFees { pub blockhash: String, pub fee_calculator: FeeCalculator, pub last_valid_slot: Slot, + pub last_valid_block_height: u64, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct DeprecatedRpcFees { + pub blockhash: String, + pub fee_calculator: FeeCalculator, + pub last_valid_slot: Slot, } #[derive(Serialize, Deserialize, Clone, Debug)] diff --git a/core/src/rpc.rs b/core/src/rpc.rs index 7d90bbba16..2596ec254d 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -546,12 +546,16 @@ impl JsonRpcRequestProcessor { let last_valid_slot = bank .get_blockhash_last_valid_slot(&blockhash) .expect("bank blockhash queue should contain blockhash"); + let last_valid_block_height = bank + .get_blockhash_last_valid_block_height(&blockhash) + .expect("bank blockhash queue should contain blockhash"); new_response( &bank, RpcFees { blockhash: blockhash.to_string(), fee_calculator, last_valid_slot, + last_valid_block_height, }, ) } @@ -607,6 +611,10 @@ impl JsonRpcRequestProcessor { self.bank(commitment).slot() } + fn get_block_height(&self, commitment: Option) -> u64 { + self.bank(commitment).block_height() + } + fn get_max_retransmit_slot(&self) -> Slot { self.max_slots.retransmit.load(Ordering::Relaxed) } @@ -2173,6 +2181,13 @@ pub mod rpc_minimal { commitment: Option, ) -> Result; + #[rpc(meta, name = "getBlockHeight")] + fn get_block_height( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result; + #[rpc(meta, name = "getSnapshotSlot")] fn get_snapshot_slot(&self, meta: Self::Metadata) -> Result; @@ -2261,6 +2276,15 @@ pub mod rpc_minimal { Ok(meta.get_slot(commitment)) } + fn get_block_height( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result { + debug!("get_block_height rpc request received"); + Ok(meta.get_block_height(commitment)) + } + fn get_snapshot_slot(&self, meta: Self::Metadata) -> Result { debug!("get_snapshot_slot rpc request received"); @@ -5267,6 +5291,7 @@ pub mod tests { "lamportsPerSignature": 0, }, "lastValidSlot": MAX_RECENT_BLOCKHASHES, + "lastValidBlockHeight": MAX_RECENT_BLOCKHASHES, }}, "id": 1 }); diff --git a/docs/src/developing/clients/jsonrpc-api.md b/docs/src/developing/clients/jsonrpc-api.md index 40d1e971d3..ef41618f1e 100644 --- a/docs/src/developing/clients/jsonrpc-api.md +++ b/docs/src/developing/clients/jsonrpc-api.md @@ -20,6 +20,7 @@ gives a convenient interface for the RPC methods. - [getAccountInfo](jsonrpc-api.md#getaccountinfo) - [getBalance](jsonrpc-api.md#getbalance) +- [getBlockHeight](jsonrpc-api.md#getblockheight) - [getBlockProduction](jsonrpc-api.md#getblockproduction) - [getBlockCommitment](jsonrpc-api.md#getblockcommitment) - [getBlockTime](jsonrpc-api.md#getblocktime) @@ -377,6 +378,137 @@ Result: } ``` +### getBlockHeight + +Returns the current block height of the node + +#### Parameters: + +- `` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) + +#### Results: + +- `` - Current block height + +#### Example: + +Request: +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' +{"jsonrpc":"2.0","id":1, "method":"getBlockHeight"} +' +``` + +Result: +```json +{"jsonrpc":"2.0","result":1233,"id":1} +``` + +### getBlockProduction + +Returns recent block production information from the current or previous epoch. + +#### Parameters: + +- `` - (optional) Configuration object containing the following optional fields: +- (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) +- (optional) `range: ` - Slot range to return block production for. If parameter not provided, defaults to current epoch. + - `firstSlot: ` - first slot to return block production information for (inclusive) + - (optional) `lastSlot: ` - last slot to return block production information for (inclusive). If parameter not provided, defaults to the highest slot +- (optional) `identity: ` - Only return results for this validator identity (base-58 encoded) + +#### Results: + +The result will be an RpcResponse JSON object with `value` equal to: +- `` +- `byIdentity: ` - a dictionary of validator identities, + as base-58 encoded strings. Value is a two element array containing the + number of leader slots and the number of blocks produced. +- `range: ` - Block production slot range + - `firstSlot: ` - first slot of the block production information (inclusive) + - `lastSlot: ` - last slot of block production information (inclusive) + +#### Example: + +Request: +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' +{"jsonrpc":"2.0","id":1, "method":"getBlockProduction"} +' +``` + +Result: +```json +{ +"jsonrpc": "2.0", +"result": { + "context": { + "slot": 9887 + }, + "value": { + "byIdentity": { + "85iYT5RuzRTDgjyRa3cP8SYhM2j21fj7NhfJ3peu1DPr": [ + 9888, + 9886 + ] + }, + "range": { + "firstSlot": 0, + "lastSlot": 9887, + } + } +}, +"id": 1 +} +``` + +#### Example: + +Request: +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' +{ + "jsonrpc": "2.0", + "id": 1, + "method": "getBlockProduction", + "params": [ + { + "identity": "85iYT5RuzRTDgjyRa3cP8SYhM2j21fj7NhfJ3peu1DPr", + "range": { + "firstSlot": 40, + "lastSlot": 50 + } + } + ] +} +' +``` + +Result: +```json +{ +"jsonrpc": "2.0", +"result": { + "context": { + "slot": 10102 + }, + "value": { + "byIdentity": { + "85iYT5RuzRTDgjyRa3cP8SYhM2j21fj7NhfJ3peu1DPr": [ + 11, + 11 + ] + }, + "range": { + "firstSlot": 50, + "lastSlot": 40 + } + } +}, +"id": 1 +} +``` + ### getBlockTime Returns the estimated production time of a block. @@ -681,111 +813,6 @@ The JSON structure of token balances is defined as a list of objects in the foll - `uiAmount: ` - Token amount as a float, accounting for decimals. **DEPRECATED** - `uiAmountString: ` - Token amount as a string, accounting for decimals. -### getBlockProduction - -Returns recent block production information from the current or previous epoch. - -#### Parameters: - -- `` - (optional) Configuration object containing the following optional fields: - - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `range: ` - Slot range to return block production for. If parameter not provided, defaults to current epoch. - - `firstSlot: ` - first slot to return block production information for (inclusive) - - (optional) `lastSlot: ` - last slot to return block production information for (inclusive). If parameter not provided, defaults to the highest slot - - (optional) `identity: ` - Only return results for this validator identity (base-58 encoded) - -#### Results: - -The result will be an RpcResponse JSON object with `value` equal to: -- `` - - `byIdentity: ` - a dictionary of validator identities, - as base-58 encoded strings. Value is a two element array containing the - number of leader slots and the number of blocks produced. - - `range: ` - Block production slot range - - `firstSlot: ` - first slot of the block production information (inclusive) - - `lastSlot: ` - last slot of block production information (inclusive) - -#### Example: - -Request: -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getBlockProduction"} -' -``` - -Result: -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 9887 - }, - "value": { - "byIdentity": { - "85iYT5RuzRTDgjyRa3cP8SYhM2j21fj7NhfJ3peu1DPr": [ - 9888, - 9886 - ] - }, - "range": { - "firstSlot": 0, - "lastSlot": 9887, - } - } - }, - "id": 1 -} -``` - -#### Example: - -Request: -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getBlockProduction", - "params": [ - { - "identity": "85iYT5RuzRTDgjyRa3cP8SYhM2j21fj7NhfJ3peu1DPr", - "range": { - "firstSlot": 40, - "lastSlot": 50 - } - } - ] - } -' -``` - -Result: -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 10102 - }, - "value": { - "byIdentity": { - "85iYT5RuzRTDgjyRa3cP8SYhM2j21fj7NhfJ3peu1DPr": [ - 11, - 11 - ] - }, - "range": { - "firstSlot": 50, - "lastSlot": 40 - } - } - }, - "id": 1 -} -``` - ### getConfirmedBlocks Returns a list of confirmed blocks between two slots @@ -1329,6 +1356,7 @@ The result will be an RpcResponse JSON object with `value` set to a JSON object - `blockhash: ` - a Hash as base-58 encoded string - `feeCalculator: ` - FeeCalculator object, the fee schedule for this block hash - `lastValidSlot: ` - DEPRECATED - this value is inaccurate and should not be relied upon +- `lastValidBlockHeight: ` - last [block height](../../terminology.md#block-height) at which a blockhash will be valid #### Example: @@ -1352,7 +1380,8 @@ Result: "feeCalculator": { "lamportsPerSignature": 5000 }, - "lastValidSlot": 297 + "lastValidSlot": 297, + "lastValidBlockHeight": 296 } }, "id": 1 diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 750122955b..90b2f18484 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -2333,6 +2333,7 @@ impl Bank { &self.fee_rate_governor } + // DEPRECATED pub fn get_blockhash_last_valid_slot(&self, blockhash: &Hash) -> Option { let blockhash_queue = self.blockhash_queue.read().unwrap(); // This calculation will need to be updated to consider epoch boundaries if BlockhashQueue @@ -2342,6 +2343,15 @@ impl Bank { .map(|age| self.slot + blockhash_queue.len() as u64 - age) } + pub fn get_blockhash_last_valid_block_height(&self, blockhash: &Hash) -> Option { + let blockhash_queue = self.blockhash_queue.read().unwrap(); + // This calculation will need to be updated to consider epoch boundaries if BlockhashQueue + // length is made variable by epoch + blockhash_queue + .get_hash_age(blockhash) + .map(|age| self.block_height + blockhash_queue.len() as u64 - age) + } + pub fn confirmed_last_blockhash(&self) -> (Hash, FeeCalculator) { const NUM_BLOCKHASH_CONFIRMATIONS: usize = 3;