RPC: Add API version to context response (#25134)

* RPC: Add API version to context response

* restore backwards compatibility
This commit is contained in:
Justin Starry 2022-05-12 12:17:21 +08:00 committed by GitHub
parent 9d18fe019b
commit a118af069e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 214 additions and 78 deletions

1
Cargo.lock generated
View File

@ -6103,6 +6103,7 @@ version = "1.11.0"
dependencies = [ dependencies = [
"log", "log",
"rustc_version", "rustc_version",
"semver",
"serde", "serde",
"serde_derive", "serde_derive",
"solana-frozen-abi 1.11.0", "solana-frozen-abi 1.11.0",

View File

@ -163,7 +163,10 @@ mod tests {
fn test_check_account_for_fees() { fn test_check_account_for_fees() {
let account_balance = 1; let account_balance = 1;
let account_balance_response = json!(Response { let account_balance_response = json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext {
slot: 1,
api_version: None
},
value: json!(account_balance), value: json!(account_balance),
}); });
let pubkey = solana_sdk::pubkey::new_rand(); let pubkey = solana_sdk::pubkey::new_rand();
@ -183,7 +186,10 @@ mod tests {
check_account_for_fee(&rpc_client, &pubkey, &message0).expect("unexpected result"); check_account_for_fee(&rpc_client, &pubkey, &message0).expect("unexpected result");
let check_fee_response = json!(Response { let check_fee_response = json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext {
slot: 1,
api_version: None
},
value: json!(2), value: json!(2),
}); });
let mut mocks = HashMap::new(); let mut mocks = HashMap::new();
@ -193,7 +199,10 @@ mod tests {
assert!(check_account_for_fee(&rpc_client, &pubkey, &message1).is_err()); assert!(check_account_for_fee(&rpc_client, &pubkey, &message1).is_err());
let check_fee_response = json!(Response { let check_fee_response = json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext {
slot: 1,
api_version: None
},
value: json!(2), value: json!(2),
}); });
let mut mocks = HashMap::new(); let mut mocks = HashMap::new();
@ -206,11 +215,17 @@ mod tests {
let account_balance = 2; let account_balance = 2;
let account_balance_response = json!(Response { let account_balance_response = json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext {
slot: 1,
api_version: None
},
value: json!(account_balance), value: json!(account_balance),
}); });
let check_fee_response = json!(Response { let check_fee_response = json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext {
slot: 1,
api_version: None
},
value: json!(1), value: json!(1),
}); });
@ -227,7 +242,10 @@ mod tests {
fn test_check_account_for_balance() { fn test_check_account_for_balance() {
let account_balance = 50; let account_balance = 50;
let account_balance_response = json!(Response { let account_balance_response = json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext {
slot: 1,
api_version: None
},
value: json!(account_balance), value: json!(account_balance),
}); });
let pubkey = solana_sdk::pubkey::new_rand(); let pubkey = solana_sdk::pubkey::new_rand();
@ -244,7 +262,10 @@ mod tests {
#[test] #[test]
fn test_get_fee_for_messages() { fn test_get_fee_for_messages() {
let check_fee_response = json!(Response { let check_fee_response = json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext {
slot: 1,
api_version: None
},
value: json!(1), value: json!(1),
}); });
let mut mocks = HashMap::new(); let mut mocks = HashMap::new();
@ -263,7 +284,10 @@ mod tests {
// No signatures, no fee. // No signatures, no fee.
let check_fee_response = json!(Response { let check_fee_response = json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext {
slot: 1,
api_version: None
},
value: json!(0), value: json!(0),
}); });
let mut mocks = HashMap::new(); let mut mocks = HashMap::new();

View File

@ -1986,7 +1986,10 @@ mod tests {
assert!(result.is_ok()); assert!(result.is_ok());
let vote_account_info_response = json!(Response { let vote_account_info_response = json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext {
slot: 1,
api_version: None
},
value: json!({ value: json!({
"data": ["KLUv/QBYNQIAtAIBAAAAbnoc3Smwt4/ROvTFWY/v9O8qlxZuPKby5Pv8zYBQW/EFAAEAAB8ACQD6gx92zAiAAecDP4B2XeEBSIx7MQeung==", "base64+zstd"], "data": ["KLUv/QBYNQIAtAIBAAAAbnoc3Smwt4/ROvTFWY/v9O8qlxZuPKby5Pv8zYBQW/EFAAEAAB8ACQD6gx92zAiAAecDP4B2XeEBSIx7MQeung==", "base64+zstd"],
"lamports": 42, "lamports": 42,
@ -2272,7 +2275,10 @@ mod tests {
// Success case // Success case
let mut config = CliConfig::default(); let mut config = CliConfig::default();
let account_info_response = json!(Response { let account_info_response = json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext {
slot: 1,
api_version: None
},
value: Value::Null, value: Value::Null,
}); });
let mut mocks = HashMap::new(); let mut mocks = HashMap::new();

View File

@ -355,7 +355,10 @@ mod tests {
let rpc_blockhash = hash(&[1u8]); let rpc_blockhash = hash(&[1u8]);
let rpc_fee_calc = FeeCalculator::new(42); let rpc_fee_calc = FeeCalculator::new(42);
let get_recent_blockhash_response = json!(Response { let get_recent_blockhash_response = json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext {
slot: 1,
api_version: None
},
value: json!(RpcFees { value: json!(RpcFees {
blockhash: rpc_blockhash.to_string(), blockhash: rpc_blockhash.to_string(),
fee_calculator: rpc_fee_calc.clone(), fee_calculator: rpc_fee_calc.clone(),
@ -364,7 +367,10 @@ mod tests {
}), }),
}); });
let get_fee_calculator_for_blockhash_response = json!(Response { let get_fee_calculator_for_blockhash_response = json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext {
slot: 1,
api_version: None
},
value: json!(RpcFeeCalculator { value: json!(RpcFeeCalculator {
fee_calculator: rpc_fee_calc.clone() fee_calculator: rpc_fee_calc.clone()
}), }),
@ -428,7 +434,10 @@ mod tests {
None, None,
); );
let get_account_response = json!(Response { let get_account_response = json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext {
slot: 1,
api_version: None
},
value: json!(Some(rpc_nonce_account)), value: json!(Some(rpc_nonce_account)),
}); });

View File

@ -108,15 +108,15 @@ impl RpcSender for MockSender {
let val = match method.as_str().unwrap() { let val = match method.as_str().unwrap() {
"getAccountInfo" => serde_json::to_value(Response { "getAccountInfo" => serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1, api_version: None },
value: Value::Null, value: Value::Null,
})?, })?,
"getBalance" => serde_json::to_value(Response { "getBalance" => serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1, api_version: None },
value: Value::Number(Number::from(50)), value: Value::Number(Number::from(50)),
})?, })?,
"getRecentBlockhash" => serde_json::to_value(Response { "getRecentBlockhash" => serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1, api_version: None },
value: ( value: (
Value::String(PUBKEY.to_string()), Value::String(PUBKEY.to_string()),
serde_json::to_value(FeeCalculator::default()).unwrap(), serde_json::to_value(FeeCalculator::default()).unwrap(),
@ -137,16 +137,16 @@ impl RpcSender for MockSender {
serde_json::to_value(Some(FeeCalculator::default())).unwrap() serde_json::to_value(Some(FeeCalculator::default())).unwrap()
}; };
serde_json::to_value(Response { serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1, api_version: None },
value, value,
})? })?
} }
"getFeeRateGovernor" => serde_json::to_value(Response { "getFeeRateGovernor" => serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1, api_version: None },
value: serde_json::to_value(FeeRateGovernor::default()).unwrap(), value: serde_json::to_value(FeeRateGovernor::default()).unwrap(),
})?, })?,
"getFees" => serde_json::to_value(Response { "getFees" => serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1, api_version: None },
value: serde_json::to_value(RpcFees { value: serde_json::to_value(RpcFees {
blockhash: PUBKEY.to_string(), blockhash: PUBKEY.to_string(),
fee_calculator: FeeCalculator::default(), fee_calculator: FeeCalculator::default(),
@ -185,7 +185,7 @@ impl RpcSender for MockSender {
.map(|_| status.clone()) .map(|_| status.clone())
.collect(); .collect();
serde_json::to_value(Response { serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1, api_version: None },
value: statuses, value: statuses,
})? })?
} }
@ -248,7 +248,7 @@ impl RpcSender for MockSender {
"getBlockProduction" => { "getBlockProduction" => {
if params.is_null() { if params.is_null() {
json!(Response { json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1, api_version: None },
value: RpcBlockProduction { value: RpcBlockProduction {
by_identity: HashMap::new(), by_identity: HashMap::new(),
range: RpcBlockProductionRange { range: RpcBlockProductionRange {
@ -266,7 +266,7 @@ impl RpcSender for MockSender {
let config_range = config.range.unwrap_or_default(); let config_range = config.range.unwrap_or_default();
json!(Response { json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1, api_version: None },
value: RpcBlockProduction { value: RpcBlockProduction {
by_identity, by_identity,
range: RpcBlockProductionRange { range: RpcBlockProductionRange {
@ -289,7 +289,7 @@ impl RpcSender for MockSender {
inactive: 12, inactive: 12,
}), }),
"getSupply" => json!(Response { "getSupply" => json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1, api_version: None },
value: RpcSupply { value: RpcSupply {
total: 100000000, total: 100000000,
circulating: 50000, circulating: 50000,
@ -304,7 +304,7 @@ impl RpcSender for MockSender {
}; };
json!(Response { json!(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1, api_version: None },
value: vec![rpc_account_balance], value: vec![rpc_account_balance],
}) })
} }
@ -335,7 +335,7 @@ impl RpcSender for MockSender {
Value::String(signature) Value::String(signature)
} }
"simulateTransaction" => serde_json::to_value(Response { "simulateTransaction" => serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1, api_version: None },
value: RpcSimulateTransactionResult { value: RpcSimulateTransactionResult {
err: None, err: None,
logs: None, logs: None,
@ -353,14 +353,14 @@ impl RpcSender for MockSender {
}) })
} }
"getLatestBlockhash" => serde_json::to_value(Response { "getLatestBlockhash" => serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1, api_version: None },
value: RpcBlockhash { value: RpcBlockhash {
blockhash: PUBKEY.to_string(), blockhash: PUBKEY.to_string(),
last_valid_block_height: 1234, last_valid_block_height: 1234,
}, },
})?, })?,
"getFeeForMessage" => serde_json::to_value(Response { "getFeeForMessage" => serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1, api_version: None },
value: json!(Some(0)), value: json!(Some(0)),
})?, })?,
"getClusterNodes" => serde_json::to_value(vec![RpcContactInfo { "getClusterNodes" => serde_json::to_value(vec![RpcContactInfo {
@ -441,7 +441,7 @@ impl RpcSender for MockSender {
"minimumLedgerSlot" => json![123], "minimumLedgerSlot" => json![123],
"getMaxRetransmitSlot" => json![123], "getMaxRetransmitSlot" => json![123],
"getMultipleAccounts" => serde_json::to_value(Response { "getMultipleAccounts" => serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext { slot: 1, api_version: None },
value: vec![Value::Null, Value::Null] value: vec![Value::Null, Value::Null]
})?, })?,
"getProgramAccounts" => { "getProgramAccounts" => {

View File

@ -419,7 +419,7 @@ impl RpcClient {
/// // Create a mock with a custom repsonse to the `GetBalance` request /// // Create a mock with a custom repsonse to the `GetBalance` request
/// let account_balance = 50; /// let account_balance = 50;
/// let account_balance_response = json!(Response { /// let account_balance_response = json!(Response {
/// context: RpcResponseContext { slot: 1 }, /// context: RpcResponseContext { slot: 1, api_version: None },
/// value: json!(account_balance), /// value: json!(account_balance),
/// }); /// });
/// ///
@ -5394,7 +5394,10 @@ pub fn create_rpc_client_mocks() -> crate::mock_sender::Mocks {
let get_account_request = RpcRequest::GetAccountInfo; let get_account_request = RpcRequest::GetAccountInfo;
let get_account_response = serde_json::to_value(Response { let get_account_response = serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext {
slot: 1,
api_version: None,
},
value: { value: {
let pubkey = Pubkey::from_str("BgvYtJEfmZYdVKiptmMjxGzv8iQoo4MWjsP3QsTkhhxa").unwrap(); let pubkey = Pubkey::from_str("BgvYtJEfmZYdVKiptmMjxGzv8iQoo4MWjsP3QsTkhhxa").unwrap();
let account = Account { let account = Account {

View File

@ -448,7 +448,7 @@ impl RpcClient {
/// // Create a mock with a custom repsonse to the `GetBalance` request /// // Create a mock with a custom repsonse to the `GetBalance` request
/// let account_balance = 50; /// let account_balance = 50;
/// let account_balance_response = json!(Response { /// let account_balance_response = json!(Response {
/// context: RpcResponseContext { slot: 1 }, /// context: RpcResponseContext { slot: 1, api_version: None },
/// value: json!(account_balance), /// value: json!(account_balance),
/// }); /// });
/// ///
@ -4061,7 +4061,10 @@ pub fn create_rpc_client_mocks() -> crate::mock_sender::Mocks {
let get_account_request = RpcRequest::GetAccountInfo; let get_account_request = RpcRequest::GetAccountInfo;
let get_account_response = serde_json::to_value(Response { let get_account_response = serde_json::to_value(Response {
context: RpcResponseContext { slot: 1 }, context: RpcResponseContext {
slot: 1,
api_version: None,
},
value: { value: {
let pubkey = Pubkey::from_str("BgvYtJEfmZYdVKiptmMjxGzv8iQoo4MWjsP3QsTkhhxa").unwrap(); let pubkey = Pubkey::from_str("BgvYtJEfmZYdVKiptmMjxGzv8iQoo4MWjsP3QsTkhhxa").unwrap();
let account = Account { let account = Account {

View File

@ -1,5 +1,6 @@
use { use {
crate::client_error, crate::client_error,
serde::{Deserialize, Deserializer, Serialize, Serializer},
solana_account_decoder::{parse_token::UiTokenAmount, UiAccount}, solana_account_decoder::{parse_token::UiTokenAmount, UiAccount},
solana_sdk::{ solana_sdk::{
clock::{Epoch, Slot, UnixTimestamp}, clock::{Epoch, Slot, UnixTimestamp},
@ -12,15 +13,64 @@ use {
solana_transaction_status::{ solana_transaction_status::{
ConfirmedTransactionStatusWithSignature, TransactionConfirmationStatus, UiConfirmedBlock, ConfirmedTransactionStatusWithSignature, TransactionConfirmationStatus, UiConfirmedBlock,
}, },
std::{collections::HashMap, fmt, net::SocketAddr}, std::{collections::HashMap, fmt, net::SocketAddr, str::FromStr},
thiserror::Error, thiserror::Error,
}; };
pub type RpcResult<T> = client_error::Result<Response<T>>; pub type RpcResult<T> = client_error::Result<Response<T>>;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcResponseContext { pub struct RpcResponseContext {
pub slot: u64, pub slot: Slot,
#[serde(skip_serializing_if = "Option::is_none")]
pub api_version: Option<RpcApiVersion>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct RpcApiVersion(semver::Version);
impl std::ops::Deref for RpcApiVersion {
type Target = semver::Version;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Default for RpcApiVersion {
fn default() -> Self {
Self(solana_version::Version::default().as_semver_version())
}
}
impl Serialize for RpcApiVersion {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for RpcApiVersion {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer)?;
Ok(RpcApiVersion(
semver::Version::from_str(&s).map_err(serde::de::Error::custom)?,
))
}
}
impl RpcResponseContext {
pub fn new(slot: Slot) -> Self {
Self {
slot,
api_version: Some(RpcApiVersion::default()),
}
}
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]

View File

@ -5379,6 +5379,7 @@ version = "1.11.0"
dependencies = [ dependencies = [
"log", "log",
"rustc_version", "rustc_version",
"semver",
"serde", "serde",
"serde_derive", "serde_derive",
"solana-frozen-abi 1.11.0", "solana-frozen-abi 1.11.0",

View File

@ -116,8 +116,10 @@ pub const PERFORMANCE_SAMPLES_LIMIT: usize = 720;
const MAX_RPC_EPOCH_CREDITS_HISTORY: usize = 5; const MAX_RPC_EPOCH_CREDITS_HISTORY: usize = 5;
fn new_response<T>(bank: &Bank, value: T) -> RpcResponse<T> { fn new_response<T>(bank: &Bank, value: T) -> RpcResponse<T> {
let context = RpcResponseContext { slot: bank.slot() }; RpcResponse {
RpcResponse { context, value } context: RpcResponseContext::new(bank.slot()),
value,
}
} }
/// Wrapper for rpc return types of methods that provide responses both with and without context. /// Wrapper for rpc return types of methods that provide responses both with and without context.
@ -774,7 +776,7 @@ impl JsonRpcRequestProcessor {
if let Some((slot, accounts)) = self.get_cached_largest_accounts(&config.filter) { if let Some((slot, accounts)) = self.get_cached_largest_accounts(&config.filter) {
Ok(RpcResponse { Ok(RpcResponse {
context: RpcResponseContext { slot }, context: RpcResponseContext::new(slot),
value: accounts, value: accounts,
}) })
} else { } else {
@ -4814,7 +4816,7 @@ pub mod tests {
let expected = json!({ let expected = json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": { "result": {
"context":{"slot":0}, "context": {"slot": 0, "apiVersion": RpcApiVersion::default()},
"value":20, "value":20,
}, },
"id": 1, "id": 1,
@ -5211,7 +5213,7 @@ pub mod tests {
); );
let result: Value = parse_success_result(rpc.handle_request_sync(request)); let result: Value = parse_success_result(rpc.handle_request_sync(request));
let expected = json!({ let expected = json!({
"context": {"slot": 0}, "context": {"slot": 0, "apiVersion": RpcApiVersion::default()},
"value":{ "value":{
"owner": "11111111111111111111111111111111", "owner": "11111111111111111111111111111111",
"lamports": 1_000_000, "lamports": 1_000_000,
@ -5396,7 +5398,7 @@ pub mod tests {
let result: RpcResponse<Vec<RpcKeyedAccount>> = let result: RpcResponse<Vec<RpcKeyedAccount>> =
parse_success_result(rpc.handle_request_sync(request)); parse_success_result(rpc.handle_request_sync(request));
let expected = RpcResponse { let expected = RpcResponse {
context: RpcResponseContext { slot: 0 }, context: RpcResponseContext::new(0),
value: expected_value, value: expected_value,
}; };
assert_eq!(result, expected); assert_eq!(result, expected);
@ -5562,7 +5564,7 @@ pub mod tests {
let expected = json!({ let expected = json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": { "result": {
"context":{"slot":0}, "context": {"slot": 0, "apiVersion": RpcApiVersion::default()},
"value":{ "value":{
"accounts": [ "accounts": [
null, null,
@ -5658,7 +5660,7 @@ pub mod tests {
let expected = json!({ let expected = json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": { "result": {
"context":{"slot":0}, "context": {"slot": 0, "apiVersion": RpcApiVersion::default()},
"value":{ "value":{
"accounts":null, "accounts":null,
"err":null, "err":null,
@ -5687,7 +5689,7 @@ pub mod tests {
let expected = json!({ let expected = json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": { "result": {
"context":{"slot":0}, "context": {"slot": 0, "apiVersion": RpcApiVersion::default()},
"value":{ "value":{
"accounts":null, "accounts":null,
"err":null, "err":null,
@ -5740,7 +5742,7 @@ pub mod tests {
let expected = json!({ let expected = json!({
"jsonrpc":"2.0", "jsonrpc":"2.0",
"result": { "result": {
"context":{"slot":0}, "context": {"slot": 0, "apiVersion": RpcApiVersion::default()},
"value":{ "value":{
"err":"BlockhashNotFound", "err":"BlockhashNotFound",
"accounts":null, "accounts":null,
@ -5767,7 +5769,7 @@ pub mod tests {
let expected = json!({ let expected = json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": { "result": {
"context":{"slot":0}, "context": {"slot": 0, "apiVersion": RpcApiVersion::default()},
"value":{ "value":{
"accounts":null, "accounts":null,
"err":null, "err":null,
@ -5906,13 +5908,14 @@ pub mod tests {
let expected = json!({ let expected = json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": { "result": {
"context":{"slot":0}, "context": {"slot": 0, "apiVersion": RpcApiVersion::default()},
"value":{ "value":{
"blockhash": recent_blockhash.to_string(), "blockhash": recent_blockhash.to_string(),
"feeCalculator": { "feeCalculator": {
"lamportsPerSignature": 0, "lamportsPerSignature": 0,
} }
}}, },
},
"id": 1 "id": 1
}); });
let expected: Response = let expected: Response =
@ -5934,7 +5937,7 @@ pub mod tests {
let expected = json!({ let expected = json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": { "result": {
"context": {"slot": 0}, "context": {"slot": 0, "apiVersion": RpcApiVersion::default()},
"value": { "value": {
"blockhash": recent_blockhash.to_string(), "blockhash": recent_blockhash.to_string(),
"feeCalculator": { "feeCalculator": {
@ -5973,7 +5976,7 @@ pub mod tests {
let expected = json!({ let expected = json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": { "result": {
"context":{"slot":0}, "context": {"slot": 0, "apiVersion": RpcApiVersion::default()},
"value":fee_calculator, "value":fee_calculator,
}, },
"id": 1 "id": 1
@ -5993,7 +5996,7 @@ pub mod tests {
let expected = json!({ let expected = json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": { "result": {
"context":{"slot":0}, "context": {"slot": 0, "apiVersion": RpcApiVersion::default()},
"value":Value::Null, "value":Value::Null,
}, },
"id": 1 "id": 1
@ -6014,16 +6017,17 @@ pub mod tests {
let expected = json!({ let expected = json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": { "result": {
"context":{"slot":0}, "context": {"slot": 0, "apiVersion": RpcApiVersion::default()},
"value":{ "value":{
"feeRateGovernor": { "feeRateGovernor": {
"burnPercent": DEFAULT_BURN_PERCENT, "burnPercent": DEFAULT_BURN_PERCENT,
"maxLamportsPerSignature": 0, "maxLamportsPerSignature": 0,
"minLamportsPerSignature": 0, "minLamportsPerSignature": 0,
"targetLamportsPerSignature": 0, "targetLamportsPerSignature": 0,
"targetSignaturesPerSlot": 0 "targetSignaturesPerSlot": 0
} }
}}, },
},
"id": 1 "id": 1
}); });
let expected: Response = let expected: Response =

View File

@ -1,4 +1,5 @@
//! The `pubsub` module implements a threaded subscription service on client RPC request //! The `pubsub` module implements a threaded subscription service on client RPC request
use { use {
crate::{ crate::{
optimistically_confirmed_bank_tracker::OptimisticallyConfirmedBank, optimistically_confirmed_bank_tracker::OptimisticallyConfirmedBank,
@ -16,7 +17,7 @@ use {
serde::Serialize, serde::Serialize,
solana_account_decoder::{parse_token::is_known_spl_token_id, UiAccount, UiAccountEncoding}, solana_account_decoder::{parse_token::is_known_spl_token_id, UiAccount, UiAccountEncoding},
solana_client::rpc_response::{ solana_client::rpc_response::{
ProcessedSignatureResult, ReceivedSignatureResult, Response, RpcBlockUpdate, ProcessedSignatureResult, ReceivedSignatureResult, Response as RpcResponse, RpcBlockUpdate,
RpcBlockUpdateError, RpcKeyedAccount, RpcLogsResponse, RpcResponseContext, RpcBlockUpdateError, RpcKeyedAccount, RpcLogsResponse, RpcResponseContext,
RpcSignatureResult, RpcVote, SlotInfo, SlotUpdate, RpcSignatureResult, RpcVote, SlotInfo, SlotUpdate,
}, },
@ -153,10 +154,10 @@ where
filter_results(results, params, *w_last_notified_slot, bank); filter_results(results, params, *w_last_notified_slot, bank);
for result in filter_results { for result in filter_results {
notifier.notify( notifier.notify(
Response { RpcResponse::from(RpcNotificationResponse {
context: RpcResponseContext { slot }, context: RpcNotificationContext { slot },
value: result, value: result,
}, }),
subscription, subscription,
is_final, is_final,
); );
@ -176,6 +177,33 @@ pub struct RpcNotification {
pub created_at: Instant, pub created_at: Instant,
} }
#[derive(Debug, Clone, PartialEq)]
struct RpcNotificationResponse<T> {
context: RpcNotificationContext,
value: T,
}
impl<T> From<RpcNotificationResponse<T>> for RpcResponse<T> {
fn from(notification: RpcNotificationResponse<T>) -> Self {
let RpcNotificationResponse {
context: RpcNotificationContext { slot },
value,
} = notification;
Self {
context: RpcResponseContext {
slot,
api_version: None,
},
value,
}
}
}
#[derive(Debug, Clone, PartialEq)]
struct RpcNotificationContext {
slot: Slot,
}
const RPC_NOTIFICATIONS_METRICS_SUBMISSION_INTERVAL_MS: Duration = Duration::from_millis(2_000); const RPC_NOTIFICATIONS_METRICS_SUBMISSION_INTERVAL_MS: Duration = Duration::from_millis(2_000);
struct RecentItems { struct RecentItems {
@ -839,12 +867,12 @@ impl RpcSubscriptions {
{ {
if params.enable_received_notification { if params.enable_received_notification {
notifier.notify( notifier.notify(
Response { RpcResponse::from(RpcNotificationResponse {
context: RpcResponseContext { slot }, context: RpcNotificationContext { slot },
value: RpcSignatureResult::ReceivedSignature( value: RpcSignatureResult::ReceivedSignature(
ReceivedSignatureResult::ReceivedSignature, ReceivedSignatureResult::ReceivedSignature,
), ),
}, }),
subscription, subscription,
false, false,
); );
@ -987,10 +1015,10 @@ impl RpcSubscriptions {
Ok(block_update) => { Ok(block_update) => {
if let Some(block_update) = block_update { if let Some(block_update) = block_update {
notifier.notify( notifier.notify(
Response { RpcResponse::from(RpcNotificationResponse {
context: RpcResponseContext { slot: s }, context: RpcNotificationContext { slot: s },
value: block_update, value: block_update,
}, }),
subscription, subscription,
false, false,
); );
@ -1004,14 +1032,14 @@ impl RpcSubscriptions {
// we don't advance `w_last_unnotified_slot` so that // we don't advance `w_last_unnotified_slot` so that
// it'll retry on the next notification trigger // it'll retry on the next notification trigger
notifier.notify( notifier.notify(
Response { RpcResponse::from(RpcNotificationResponse {
context: RpcResponseContext { slot: s }, context: RpcNotificationContext { slot: s },
value: RpcBlockUpdate { value: RpcBlockUpdate {
slot, slot,
block: None, block: None,
err: Some(err), err: Some(err),
}, },
}, }),
subscription, subscription,
false, false,
); );

View File

@ -11,6 +11,7 @@ edition = "2021"
[dependencies] [dependencies]
log = "0.4.17" log = "0.4.17"
semver = "1.0.9"
serde = "1.0.137" serde = "1.0.137"
serde_derive = "1.0.103" serde_derive = "1.0.103"
solana-frozen-abi = { path = "../frozen-abi", version = "=1.11.0" } solana-frozen-abi = { path = "../frozen-abi", version = "=1.11.0" }

View File

@ -29,6 +29,12 @@ pub struct Version {
pub feature_set: u32, // first 4 bytes of the FeatureSet identifier pub feature_set: u32, // first 4 bytes of the FeatureSet identifier
} }
impl Version {
pub fn as_semver_version(&self) -> semver::Version {
semver::Version::new(self.major as u64, self.minor as u64, self.patch as u64)
}
}
impl From<LegacyVersion> for Version { impl From<LegacyVersion> for Version {
fn from(legacy_version: LegacyVersion) -> Self { fn from(legacy_version: LegacyVersion) -> Self {
Self { Self {