RPC: Add `getFeeCalculatorForBlockhash` method call (#8687)
Returns the `FeeCalculator` associated with the given blockhash, or `null` if said blockhash has expired
This commit is contained in:
parent
3eb00ef60f
commit
4db074a5aa
|
@ -71,6 +71,17 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
|
||||||
serde_json::to_value(FeeCalculator::default()).unwrap(),
|
serde_json::to_value(FeeCalculator::default()).unwrap(),
|
||||||
),
|
),
|
||||||
})?,
|
})?,
|
||||||
|
RpcRequest::GetFeeCalculatorForBlockhash => {
|
||||||
|
let value = if self.url == "blockhash_expired" {
|
||||||
|
Value::Null
|
||||||
|
} else {
|
||||||
|
serde_json::to_value(Some(FeeCalculator::default())).unwrap()
|
||||||
|
};
|
||||||
|
serde_json::to_value(Response {
|
||||||
|
context: RpcResponseContext { slot: 1 },
|
||||||
|
value,
|
||||||
|
})?
|
||||||
|
}
|
||||||
RpcRequest::GetFeeRateGovernor => serde_json::to_value(Response {
|
RpcRequest::GetFeeRateGovernor => serde_json::to_value(Response {
|
||||||
context: RpcResponseContext { slot: 1 },
|
context: RpcResponseContext { slot: 1 },
|
||||||
value: serde_json::to_value(FeeRateGovernor::default()).unwrap(),
|
value: serde_json::to_value(FeeRateGovernor::default()).unwrap(),
|
||||||
|
|
|
@ -6,8 +6,8 @@ use crate::{
|
||||||
rpc_request::RpcRequest,
|
rpc_request::RpcRequest,
|
||||||
rpc_response::{
|
rpc_response::{
|
||||||
Response, RpcAccount, RpcBlockhashFeeCalculator, RpcConfirmedBlock, RpcContactInfo,
|
Response, RpcAccount, RpcBlockhashFeeCalculator, RpcConfirmedBlock, RpcContactInfo,
|
||||||
RpcEpochInfo, RpcFeeRateGovernor, RpcIdentity, RpcKeyedAccount, RpcLeaderSchedule,
|
RpcEpochInfo, RpcFeeCalculator, RpcFeeRateGovernor, RpcIdentity, RpcKeyedAccount,
|
||||||
RpcResponse, RpcVersionInfo, RpcVoteAccountStatus,
|
RpcLeaderSchedule, RpcResponse, RpcVersionInfo, RpcVoteAccountStatus,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use bincode::serialize;
|
use bincode::serialize;
|
||||||
|
@ -839,6 +839,35 @@ impl RpcClient {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_fee_calculator_for_blockhash(
|
||||||
|
&self,
|
||||||
|
blockhash: &Hash,
|
||||||
|
) -> io::Result<Option<FeeCalculator>> {
|
||||||
|
let response = self
|
||||||
|
.client
|
||||||
|
.send(
|
||||||
|
&RpcRequest::GetFeeCalculatorForBlockhash,
|
||||||
|
json!([blockhash.to_string()]),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.map_err(|e| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("GetFeeCalculatorForBlockhash request failure: {:?}", e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let Response { value, .. } = serde_json::from_value::<Response<Option<RpcFeeCalculator>>>(
|
||||||
|
response,
|
||||||
|
)
|
||||||
|
.map_err(|e| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("GetFeeCalculatorForBlockhash parse failure: {:?}", e),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
Ok(value.map(|rf| rf.fee_calculator))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_fee_rate_governor(&self) -> RpcResponse<FeeRateGovernor> {
|
pub fn get_fee_rate_governor(&self) -> RpcResponse<FeeRateGovernor> {
|
||||||
let response = self
|
let response = self
|
||||||
.client
|
.client
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub enum RpcRequest {
|
||||||
GetNumBlocksSinceSignatureConfirmation,
|
GetNumBlocksSinceSignatureConfirmation,
|
||||||
GetProgramAccounts,
|
GetProgramAccounts,
|
||||||
GetRecentBlockhash,
|
GetRecentBlockhash,
|
||||||
|
GetFeeCalculatorForBlockhash,
|
||||||
GetFeeRateGovernor,
|
GetFeeRateGovernor,
|
||||||
GetSignatureStatus,
|
GetSignatureStatus,
|
||||||
GetSlot,
|
GetSlot,
|
||||||
|
@ -64,6 +65,7 @@ impl RpcRequest {
|
||||||
}
|
}
|
||||||
RpcRequest::GetProgramAccounts => "getProgramAccounts",
|
RpcRequest::GetProgramAccounts => "getProgramAccounts",
|
||||||
RpcRequest::GetRecentBlockhash => "getRecentBlockhash",
|
RpcRequest::GetRecentBlockhash => "getRecentBlockhash",
|
||||||
|
RpcRequest::GetFeeCalculatorForBlockhash => "getFeeCalculatorForBlockhash",
|
||||||
RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor",
|
RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor",
|
||||||
RpcRequest::GetSignatureStatus => "getSignatureStatus",
|
RpcRequest::GetSignatureStatus => "getSignatureStatus",
|
||||||
RpcRequest::GetSlot => "getSlot",
|
RpcRequest::GetSlot => "getSlot",
|
||||||
|
@ -127,7 +129,7 @@ mod tests {
|
||||||
assert_eq!(request["params"], json!([addr]));
|
assert_eq!(request["params"], json!([addr]));
|
||||||
|
|
||||||
let test_request = RpcRequest::GetBalance;
|
let test_request = RpcRequest::GetBalance;
|
||||||
let request = test_request.build_request_json(1, json!([addr]));
|
let request = test_request.build_request_json(1, json!([addr.clone()]));
|
||||||
assert_eq!(request["method"], "getBalance");
|
assert_eq!(request["method"], "getBalance");
|
||||||
|
|
||||||
let test_request = RpcRequest::GetEpochInfo;
|
let test_request = RpcRequest::GetEpochInfo;
|
||||||
|
@ -142,6 +144,10 @@ mod tests {
|
||||||
let request = test_request.build_request_json(1, Value::Null);
|
let request = test_request.build_request_json(1, Value::Null);
|
||||||
assert_eq!(request["method"], "getRecentBlockhash");
|
assert_eq!(request["method"], "getRecentBlockhash");
|
||||||
|
|
||||||
|
let test_request = RpcRequest::GetFeeCalculatorForBlockhash;
|
||||||
|
let request = test_request.build_request_json(1, json!([addr.clone()]));
|
||||||
|
assert_eq!(request["method"], "getFeeCalculatorForBlockhash");
|
||||||
|
|
||||||
let test_request = RpcRequest::GetFeeRateGovernor;
|
let test_request = RpcRequest::GetFeeRateGovernor;
|
||||||
let request = test_request.build_request_json(1, Value::Null);
|
let request = test_request.build_request_json(1, Value::Null);
|
||||||
assert_eq!(request["method"], "getFeeRateGovernor");
|
assert_eq!(request["method"], "getFeeRateGovernor");
|
||||||
|
|
|
@ -152,6 +152,12 @@ pub struct RpcBlockhashFeeCalculator {
|
||||||
pub fee_calculator: FeeCalculator,
|
pub fee_calculator: FeeCalculator,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RpcFeeCalculator {
|
||||||
|
pub fee_calculator: FeeCalculator,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct RpcFeeRateGovernor {
|
pub struct RpcFeeRateGovernor {
|
||||||
|
|
|
@ -445,6 +445,16 @@ impl SyncClient for ThinClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_fee_calculator_for_blockhash(
|
||||||
|
&self,
|
||||||
|
blockhash: &Hash,
|
||||||
|
) -> TransportResult<Option<FeeCalculator>> {
|
||||||
|
let fee_calculator = self
|
||||||
|
.rpc_client()
|
||||||
|
.get_fee_calculator_for_blockhash(blockhash)?;
|
||||||
|
Ok(fee_calculator)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_fee_rate_governor(&self) -> TransportResult<FeeRateGovernor> {
|
fn get_fee_rate_governor(&self) -> TransportResult<FeeRateGovernor> {
|
||||||
let fee_rate_governor = self.rpc_client().get_fee_rate_governor()?;
|
let fee_rate_governor = self.rpc_client().get_fee_rate_governor()?;
|
||||||
Ok(fee_rate_governor.value)
|
Ok(fee_rate_governor.value)
|
||||||
|
|
|
@ -9,9 +9,10 @@ use jsonrpc_core::{Error, Metadata, Result};
|
||||||
use jsonrpc_derive::rpc;
|
use jsonrpc_derive::rpc;
|
||||||
use solana_client::rpc_response::{
|
use solana_client::rpc_response::{
|
||||||
Response, RpcAccount, RpcBlockCommitment, RpcBlockhashFeeCalculator, RpcConfirmedBlock,
|
Response, RpcAccount, RpcBlockCommitment, RpcBlockhashFeeCalculator, RpcConfirmedBlock,
|
||||||
RpcContactInfo, RpcEpochInfo, RpcFeeRateGovernor, RpcIdentity, RpcKeyedAccount,
|
RpcContactInfo, RpcEpochInfo, RpcFeeCalculator, RpcFeeRateGovernor, RpcIdentity,
|
||||||
RpcLeaderSchedule, RpcResponseContext, RpcSignatureConfirmation, RpcStorageTurn,
|
RpcKeyedAccount, RpcLeaderSchedule, RpcResponseContext, RpcSignatureConfirmation,
|
||||||
RpcTransactionEncoding, RpcVersionInfo, RpcVoteAccountInfo, RpcVoteAccountStatus,
|
RpcStorageTurn, RpcTransactionEncoding, RpcVersionInfo, RpcVoteAccountInfo,
|
||||||
|
RpcVoteAccountStatus,
|
||||||
};
|
};
|
||||||
use solana_faucet::faucet::request_airdrop_transaction;
|
use solana_faucet::faucet::request_airdrop_transaction;
|
||||||
use solana_ledger::{
|
use solana_ledger::{
|
||||||
|
@ -33,6 +34,7 @@ use solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
net::{SocketAddr, UdpSocket},
|
net::{SocketAddr, UdpSocket},
|
||||||
|
str::FromStr,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
thread::sleep,
|
thread::sleep,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
|
@ -165,6 +167,18 @@ impl JsonRpcRequestProcessor {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_fee_calculator_for_blockhash(
|
||||||
|
&self,
|
||||||
|
blockhash: &Hash,
|
||||||
|
) -> RpcResponse<Option<RpcFeeCalculator>> {
|
||||||
|
let bank = &*self.bank(None);
|
||||||
|
let fee_calculator = bank.get_fee_calculator(blockhash);
|
||||||
|
new_response(
|
||||||
|
bank,
|
||||||
|
fee_calculator.map(|fee_calculator| RpcFeeCalculator { fee_calculator }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_fee_rate_governor(&self) -> RpcResponse<RpcFeeRateGovernor> {
|
fn get_fee_rate_governor(&self) -> RpcResponse<RpcFeeRateGovernor> {
|
||||||
let bank = &*self.bank(None);
|
let bank = &*self.bank(None);
|
||||||
let fee_rate_governor = bank.get_fee_rate_governor();
|
let fee_rate_governor = bank.get_fee_rate_governor();
|
||||||
|
@ -503,6 +517,13 @@ pub trait RpcSol {
|
||||||
commitment: Option<CommitmentConfig>,
|
commitment: Option<CommitmentConfig>,
|
||||||
) -> RpcResponse<RpcBlockhashFeeCalculator>;
|
) -> RpcResponse<RpcBlockhashFeeCalculator>;
|
||||||
|
|
||||||
|
#[rpc(meta, name = "getFeeCalculatorForBlockhash")]
|
||||||
|
fn get_fee_calculator_for_blockhash(
|
||||||
|
&self,
|
||||||
|
meta: Self::Metadata,
|
||||||
|
blockhash: String,
|
||||||
|
) -> RpcResponse<Option<RpcFeeCalculator>>;
|
||||||
|
|
||||||
#[rpc(meta, name = "getFeeRateGovernor")]
|
#[rpc(meta, name = "getFeeRateGovernor")]
|
||||||
fn get_fee_rate_governor(&self, meta: Self::Metadata) -> RpcResponse<RpcFeeRateGovernor>;
|
fn get_fee_rate_governor(&self, meta: Self::Metadata) -> RpcResponse<RpcFeeRateGovernor>;
|
||||||
|
|
||||||
|
@ -831,6 +852,20 @@ impl RpcSol for RpcSolImpl {
|
||||||
.get_recent_blockhash(commitment)
|
.get_recent_blockhash(commitment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_fee_calculator_for_blockhash(
|
||||||
|
&self,
|
||||||
|
meta: Self::Metadata,
|
||||||
|
blockhash: String,
|
||||||
|
) -> RpcResponse<Option<RpcFeeCalculator>> {
|
||||||
|
debug!("get_fee_calculator_for_blockhash rpc request received");
|
||||||
|
let blockhash =
|
||||||
|
Hash::from_str(&blockhash).map_err(|e| Error::invalid_params(format!("{:?}", e)))?;
|
||||||
|
meta.request_processor
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get_fee_calculator_for_blockhash(&blockhash)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_fee_rate_governor(&self, meta: Self::Metadata) -> RpcResponse<RpcFeeRateGovernor> {
|
fn get_fee_rate_governor(&self, meta: Self::Metadata) -> RpcResponse<RpcFeeRateGovernor> {
|
||||||
debug!("get_fee_rate_governor rpc request received");
|
debug!("get_fee_rate_governor rpc request received");
|
||||||
meta.request_processor
|
meta.request_processor
|
||||||
|
@ -1821,6 +1856,54 @@ pub mod tests {
|
||||||
assert_eq!(expected, result);
|
assert_eq!(expected, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rpc_get_fee_calculator_for_blockhash() {
|
||||||
|
let bob_pubkey = Pubkey::new_rand();
|
||||||
|
let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey);
|
||||||
|
|
||||||
|
let (blockhash, fee_calculator) = bank.last_blockhash_with_fee_calculator();
|
||||||
|
let fee_calculator = RpcFeeCalculator { fee_calculator };
|
||||||
|
|
||||||
|
let req = format!(
|
||||||
|
r#"{{"jsonrpc":"2.0","id":1,"method":"getFeeCalculatorForBlockhash","params":["{:?}"]}}"#,
|
||||||
|
blockhash
|
||||||
|
);
|
||||||
|
let res = io.handle_request_sync(&req, meta.clone());
|
||||||
|
let expected = json!({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"result": {
|
||||||
|
"context":{"slot":0},
|
||||||
|
"value":fee_calculator,
|
||||||
|
},
|
||||||
|
"id": 1
|
||||||
|
});
|
||||||
|
let expected: Response =
|
||||||
|
serde_json::from_value(expected).expect("expected response deserialization");
|
||||||
|
let result: Response = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
.expect("actual response deserialization");
|
||||||
|
assert_eq!(expected, result);
|
||||||
|
|
||||||
|
// Expired (non-existent) blockhash
|
||||||
|
let req = format!(
|
||||||
|
r#"{{"jsonrpc":"2.0","id":1,"method":"getFeeCalculatorForBlockhash","params":["{:?}"]}}"#,
|
||||||
|
Hash::default()
|
||||||
|
);
|
||||||
|
let res = io.handle_request_sync(&req, meta.clone());
|
||||||
|
let expected = json!({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"result": {
|
||||||
|
"context":{"slot":0},
|
||||||
|
"value":Value::Null,
|
||||||
|
},
|
||||||
|
"id": 1
|
||||||
|
});
|
||||||
|
let expected: Response =
|
||||||
|
serde_json::from_value(expected).expect("expected response deserialization");
|
||||||
|
let result: Response = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
.expect("actual response deserialization");
|
||||||
|
assert_eq!(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rpc_get_fee_rate_governor() {
|
fn test_rpc_get_fee_rate_governor() {
|
||||||
let bob_pubkey = Pubkey::new_rand();
|
let bob_pubkey = Pubkey::new_rand();
|
||||||
|
|
|
@ -24,6 +24,7 @@ To interact with a Solana node inside a JavaScript application, use the [solana-
|
||||||
* [getConfirmedBlocks](jsonrpc-api.md#getconfirmedblocks)
|
* [getConfirmedBlocks](jsonrpc-api.md#getconfirmedblocks)
|
||||||
* [getEpochInfo](jsonrpc-api.md#getepochinfo)
|
* [getEpochInfo](jsonrpc-api.md#getepochinfo)
|
||||||
* [getEpochSchedule](jsonrpc-api.md#getepochschedule)
|
* [getEpochSchedule](jsonrpc-api.md#getepochschedule)
|
||||||
|
* [getFeeCalculatorForBlockhash](jsonrpc-api.md#getfeecalculatorforblockhash)
|
||||||
* [getFeeRateGovernor](jsonrpc-api.md#getfeerategovernor)
|
* [getFeeRateGovernor](jsonrpc-api.md#getfeerategovernor)
|
||||||
* [getGenesisHash](jsonrpc-api.md#getgenesishash)
|
* [getGenesisHash](jsonrpc-api.md#getgenesishash)
|
||||||
* [getIdentity](jsonrpc-api.md#getidentity)
|
* [getIdentity](jsonrpc-api.md#getidentity)
|
||||||
|
@ -405,6 +406,30 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
|
||||||
{"jsonrpc":"2.0","result":{"firstNormalEpoch":8,"firstNormalSlot":8160,"leaderScheduleSlotOffset":8192,"slotsPerEpoch":8192,"warmup":true},"id":1}
|
{"jsonrpc":"2.0","result":{"firstNormalEpoch":8,"firstNormalSlot":8160,"leaderScheduleSlotOffset":8192,"slotsPerEpoch":8192,"warmup":true},"id":1}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### getFeeCalculatorForBlockhash
|
||||||
|
|
||||||
|
Returns the fee calculator associated with the query blockhash, or `null` if the blockhash has expired
|
||||||
|
|
||||||
|
#### Parameters:
|
||||||
|
|
||||||
|
* `blockhash: <string>`, query blockhash as a Base58 encoded string
|
||||||
|
|
||||||
|
#### Results:
|
||||||
|
|
||||||
|
The `result` field will be `null` if the query blockhash has expired, otherwise an `object` with the following fields:
|
||||||
|
|
||||||
|
* `feeCalculator: <object>`, `FeeCalculator` object describing the cluster fee rate at the queried blockhash
|
||||||
|
|
||||||
|
#### Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
// Request
|
||||||
|
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getFeeCalculatorForBlockhash", "params":["GJxqhuxcgfn5Tcj6y3f8X4FeCDd2RQ6SnEMo1AAxrPRZ"]}' http://localhost:8899
|
||||||
|
|
||||||
|
// Result
|
||||||
|
{"jsonrpc":"2.0","result":{"context":{"slot":221},"value":{"feeCalculator":{"lamportsPerSignature":5000}}},"id":1}
|
||||||
|
```
|
||||||
|
|
||||||
### getFeeRateGovernor
|
### getFeeRateGovernor
|
||||||
|
|
||||||
Returns the fee rate governor information from the root bank
|
Returns the fee rate governor information from the root bank
|
||||||
|
|
|
@ -137,6 +137,10 @@ impl SyncClient for BankClient {
|
||||||
Ok(self.bank.last_blockhash_with_fee_calculator())
|
Ok(self.bank.last_blockhash_with_fee_calculator())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_fee_calculator_for_blockhash(&self, blockhash: &Hash) -> Result<Option<FeeCalculator>> {
|
||||||
|
Ok(self.bank.get_fee_calculator(blockhash))
|
||||||
|
}
|
||||||
|
|
||||||
fn get_fee_rate_governor(&self) -> Result<FeeRateGovernor> {
|
fn get_fee_rate_governor(&self) -> Result<FeeRateGovernor> {
|
||||||
Ok(self.bank.get_fee_rate_governor().clone())
|
Ok(self.bank.get_fee_rate_governor().clone())
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,10 @@ pub trait SyncClient {
|
||||||
commitment_config: CommitmentConfig,
|
commitment_config: CommitmentConfig,
|
||||||
) -> Result<(Hash, FeeCalculator)>;
|
) -> Result<(Hash, FeeCalculator)>;
|
||||||
|
|
||||||
|
/// Get `Some(FeeCalculator)` associated with `blockhash` if it is still in
|
||||||
|
/// the BlockhashQueue`, otherwise `None`
|
||||||
|
fn get_fee_calculator_for_blockhash(&self, blockhash: &Hash) -> Result<Option<FeeCalculator>>;
|
||||||
|
|
||||||
/// Get recent fee rate governor
|
/// Get recent fee rate governor
|
||||||
fn get_fee_rate_governor(&self) -> Result<FeeRateGovernor>;
|
fn get_fee_rate_governor(&self) -> Result<FeeRateGovernor>;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue