v0.2.2-pre.002
This commit is contained in:
577
crates/ksp-onchain-transport-lib/src/rpc_cluster.rs
Normal file
577
crates/ksp-onchain-transport-lib/src/rpc_cluster.rs
Normal file
@@ -0,0 +1,577 @@
|
||||
// file: crates/ksp-onchain-transport-lib/src/rpc_cluster.rs
|
||||
// version: 1
|
||||
|
||||
/// Contact information returned for one cluster node.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct SolanaClusterNode {
|
||||
pubkey: ksp_core_lib::Pubkey,
|
||||
feature_set: std::option::Option<u32>,
|
||||
gossip: std::option::Option<std::string::String>,
|
||||
pubsub: std::option::Option<std::string::String>,
|
||||
rpc: std::option::Option<std::string::String>,
|
||||
serve_repair: std::option::Option<std::string::String>,
|
||||
shred_version: std::option::Option<u16>,
|
||||
tpu: std::option::Option<std::string::String>,
|
||||
tpu_forwards: std::option::Option<std::string::String>,
|
||||
tpu_forwards_quic: std::option::Option<std::string::String>,
|
||||
tpu_quic: std::option::Option<std::string::String>,
|
||||
tpu_vote: std::option::Option<std::string::String>,
|
||||
tvu: std::option::Option<std::string::String>,
|
||||
version: std::option::Option<std::string::String>,
|
||||
client_id: std::option::Option<std::string::String>,
|
||||
}
|
||||
|
||||
impl SolanaClusterNode {
|
||||
/// Returns the node identity public key.
|
||||
#[must_use]
|
||||
pub const fn pubkey(&self) -> &ksp_core_lib::Pubkey { return &self.pubkey; }
|
||||
/// Returns the optional feature-set identifier.
|
||||
#[must_use]
|
||||
pub const fn feature_set(&self) -> std::option::Option<u32> { return self.feature_set; }
|
||||
/// Returns the optional gossip endpoint.
|
||||
#[must_use]
|
||||
pub fn gossip(&self) -> std::option::Option<&str> { return self.gossip.as_deref(); }
|
||||
/// Returns the optional PubSub endpoint.
|
||||
#[must_use]
|
||||
pub fn pubsub(&self) -> std::option::Option<&str> { return self.pubsub.as_deref(); }
|
||||
/// Returns the optional JSON-RPC endpoint.
|
||||
#[must_use]
|
||||
pub fn rpc(&self) -> std::option::Option<&str> { return self.rpc.as_deref(); }
|
||||
/// Returns the optional repair endpoint.
|
||||
#[must_use]
|
||||
pub fn serve_repair(&self) -> std::option::Option<&str> { return self.serve_repair.as_deref(); }
|
||||
/// Returns the optional shred version.
|
||||
#[must_use]
|
||||
pub const fn shred_version(&self) -> std::option::Option<u16> { return self.shred_version; }
|
||||
/// Returns the optional TPU endpoint.
|
||||
#[must_use]
|
||||
pub fn tpu(&self) -> std::option::Option<&str> { return self.tpu.as_deref(); }
|
||||
/// Returns the optional TPU forwards endpoint.
|
||||
#[must_use]
|
||||
pub fn tpu_forwards(&self) -> std::option::Option<&str> { return self.tpu_forwards.as_deref(); }
|
||||
/// Returns the optional TPU forwards QUIC endpoint.
|
||||
#[must_use]
|
||||
pub fn tpu_forwards_quic(&self) -> std::option::Option<&str> { return self.tpu_forwards_quic.as_deref(); }
|
||||
/// Returns the optional TPU QUIC endpoint.
|
||||
#[must_use]
|
||||
pub fn tpu_quic(&self) -> std::option::Option<&str> { return self.tpu_quic.as_deref(); }
|
||||
/// Returns the optional TPU vote endpoint.
|
||||
#[must_use]
|
||||
pub fn tpu_vote(&self) -> std::option::Option<&str> { return self.tpu_vote.as_deref(); }
|
||||
/// Returns the optional TVU endpoint.
|
||||
#[must_use]
|
||||
pub fn tvu(&self) -> std::option::Option<&str> { return self.tvu.as_deref(); }
|
||||
/// Returns the optional software-version string.
|
||||
#[must_use]
|
||||
pub fn version(&self) -> std::option::Option<&str> { return self.version.as_deref(); }
|
||||
/// Returns the optional Agave client identifier extension.
|
||||
#[must_use]
|
||||
pub fn client_id(&self) -> std::option::Option<&str> { return self.client_id.as_deref(); }
|
||||
|
||||
/// Decodes one cluster-node contact record from the Solana JSON wire shape.
|
||||
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
let decoded = crate::decode_wire_json::<WireClusterNode>(method, value);
|
||||
let wire = match decoded {
|
||||
std::result::Result::Ok(wire) => wire,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let pubkey = crate::parse_wire_pubkey(method, "pubkey", wire.pubkey.as_str());
|
||||
let pubkey = match pubkey {
|
||||
std::result::Result::Ok(pubkey) => pubkey,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(Self {
|
||||
pubkey,
|
||||
feature_set: wire.feature_set,
|
||||
gossip: wire.gossip,
|
||||
pubsub: wire.pubsub,
|
||||
rpc: wire.rpc,
|
||||
serve_repair: wire.serve_repair,
|
||||
shred_version: wire.shred_version,
|
||||
tpu: wire.tpu,
|
||||
tpu_forwards: wire.tpu_forwards,
|
||||
tpu_forwards_quic: wire.tpu_forwards_quic,
|
||||
tpu_quic: wire.tpu_quic,
|
||||
tpu_vote: wire.tpu_vote,
|
||||
tvu: wire.tvu,
|
||||
version: wire.version,
|
||||
client_id: wire.client_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Epoch information returned by `getEpochInfo`.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct SolanaEpochInfo {
|
||||
absolute_slot: u64,
|
||||
block_height: u64,
|
||||
epoch: u64,
|
||||
slot_index: u64,
|
||||
slots_in_epoch: u64,
|
||||
transaction_count: std::option::Option<u64>,
|
||||
}
|
||||
|
||||
impl SolanaEpochInfo {
|
||||
/// Returns the absolute slot.
|
||||
#[must_use]
|
||||
pub const fn absolute_slot(&self) -> u64 { return self.absolute_slot; }
|
||||
/// Returns the block height.
|
||||
#[must_use]
|
||||
pub const fn block_height(&self) -> u64 { return self.block_height; }
|
||||
/// Returns the epoch number.
|
||||
#[must_use]
|
||||
pub const fn epoch(&self) -> u64 { return self.epoch; }
|
||||
/// Returns the slot index within the epoch.
|
||||
#[must_use]
|
||||
pub const fn slot_index(&self) -> u64 { return self.slot_index; }
|
||||
/// Returns the number of slots in the epoch.
|
||||
#[must_use]
|
||||
pub const fn slots_in_epoch(&self) -> u64 { return self.slots_in_epoch; }
|
||||
/// Returns the nullable transaction count.
|
||||
#[must_use]
|
||||
pub const fn transaction_count(&self) -> std::option::Option<u64> { return self.transaction_count; }
|
||||
|
||||
/// Decodes epoch information from the Solana JSON wire shape.
|
||||
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
let decoded = crate::decode_wire_json::<WireEpochInfo>(method, value);
|
||||
return match decoded {
|
||||
std::result::Result::Ok(wire) => std::result::Result::Ok(Self {
|
||||
absolute_slot: wire.absolute_slot,
|
||||
block_height: wire.block_height,
|
||||
epoch: wire.epoch,
|
||||
slot_index: wire.slot_index,
|
||||
slots_in_epoch: wire.slots_in_epoch,
|
||||
transaction_count: wire.transaction_count,
|
||||
}),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Epoch schedule returned by `getEpochSchedule`.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct SolanaEpochSchedule {
|
||||
first_normal_epoch: u64,
|
||||
first_normal_slot: u64,
|
||||
leader_schedule_slot_offset: u64,
|
||||
slots_per_epoch: u64,
|
||||
warmup: bool,
|
||||
}
|
||||
|
||||
impl SolanaEpochSchedule {
|
||||
/// Returns the first normal epoch.
|
||||
#[must_use]
|
||||
pub const fn first_normal_epoch(&self) -> u64 { return self.first_normal_epoch; }
|
||||
/// Returns the first normal slot.
|
||||
#[must_use]
|
||||
pub const fn first_normal_slot(&self) -> u64 { return self.first_normal_slot; }
|
||||
/// Returns the leader-schedule slot offset.
|
||||
#[must_use]
|
||||
pub const fn leader_schedule_slot_offset(&self) -> u64 { return self.leader_schedule_slot_offset; }
|
||||
/// Returns the number of slots per epoch.
|
||||
#[must_use]
|
||||
pub const fn slots_per_epoch(&self) -> u64 { return self.slots_per_epoch; }
|
||||
/// Returns whether epoch warmup is enabled.
|
||||
#[must_use]
|
||||
pub const fn warmup(&self) -> bool { return self.warmup; }
|
||||
|
||||
/// Decodes an epoch schedule from the Solana JSON wire shape.
|
||||
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
let decoded = crate::decode_wire_json::<WireEpochSchedule>(method, value);
|
||||
return match decoded {
|
||||
std::result::Result::Ok(wire) => std::result::Result::Ok(Self {
|
||||
first_normal_epoch: wire.first_normal_epoch,
|
||||
first_normal_slot: wire.first_normal_slot,
|
||||
leader_schedule_slot_offset: wire.leader_schedule_slot_offset,
|
||||
slots_per_epoch: wire.slots_per_epoch,
|
||||
warmup: wire.warmup,
|
||||
}),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Highest full and optional incremental snapshot slots returned by `getHighestSnapshotSlot`.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct SolanaSnapshotSlotInfo {
|
||||
full: u64,
|
||||
incremental: std::option::Option<u64>,
|
||||
}
|
||||
|
||||
impl SolanaSnapshotSlotInfo {
|
||||
/// Returns the highest full snapshot slot.
|
||||
#[must_use]
|
||||
pub const fn full(&self) -> u64 { return self.full; }
|
||||
/// Returns the optional highest incremental snapshot slot.
|
||||
#[must_use]
|
||||
pub const fn incremental(&self) -> std::option::Option<u64> { return self.incremental; }
|
||||
|
||||
/// Decodes snapshot-slot information from the Solana JSON wire shape.
|
||||
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
let decoded = crate::decode_wire_json::<WireSnapshotSlotInfo>(method, value);
|
||||
return match decoded {
|
||||
std::result::Result::Ok(wire) => std::result::Result::Ok(Self { full: wire.full, incremental: wire.incremental }),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Optional configuration accepted by `getLeaderSchedule`.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct SolanaLeaderScheduleConfig {
|
||||
identity: std::option::Option<ksp_core_lib::Pubkey>,
|
||||
commitment: std::option::Option<crate::SolanaCommitment>,
|
||||
}
|
||||
|
||||
impl SolanaLeaderScheduleConfig {
|
||||
/// Creates a leader-schedule configuration.
|
||||
#[must_use]
|
||||
pub const fn new(identity: std::option::Option<ksp_core_lib::Pubkey>, commitment: std::option::Option<crate::SolanaCommitment>) -> Self {
|
||||
return Self { identity, commitment };
|
||||
}
|
||||
/// Returns the optional validator identity filter.
|
||||
#[must_use]
|
||||
pub const fn identity(&self) -> std::option::Option<&ksp_core_lib::Pubkey> { return self.identity.as_ref(); }
|
||||
/// Returns the optional commitment level.
|
||||
#[must_use]
|
||||
pub const fn commitment(&self) -> std::option::Option<crate::SolanaCommitment> { return self.commitment; }
|
||||
fn is_empty(&self) -> bool { return self.identity.is_none() && self.commitment.is_none(); }
|
||||
fn to_json_value(&self) -> serde_json::Value {
|
||||
let mut object = serde_json::Map::new();
|
||||
if let std::option::Option::Some(identity) = self.identity.as_ref() {
|
||||
object.insert("identity".to_owned(), serde_json::Value::String(identity.to_string()));
|
||||
}
|
||||
if let std::option::Option::Some(commitment) = self.commitment {
|
||||
object.insert("commitment".to_owned(), serde_json::Value::String(commitment.as_str().to_owned()));
|
||||
}
|
||||
return serde_json::Value::Object(object);
|
||||
}
|
||||
}
|
||||
|
||||
/// Typed parameter overload for `getLeaderSchedule`.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum SolanaLeaderScheduleRequest {
|
||||
/// Query the current epoch, optionally with a config object.
|
||||
CurrentEpoch(std::option::Option<crate::SolanaLeaderScheduleConfig>),
|
||||
/// Query the epoch containing one slot, optionally with a config object.
|
||||
Slot {
|
||||
/// Slot whose epoch should be queried.
|
||||
slot: u64,
|
||||
/// Optional leader-schedule config sent as the second positional parameter.
|
||||
config: std::option::Option<crate::SolanaLeaderScheduleConfig>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for SolanaLeaderScheduleRequest {
|
||||
fn default() -> Self { return Self::CurrentEpoch(std::option::Option::None); }
|
||||
}
|
||||
|
||||
impl SolanaLeaderScheduleRequest {
|
||||
/// Serializes the typed overload to the exact positional JSON-RPC params.
|
||||
#[must_use]
|
||||
pub(crate) fn to_json_params(&self) -> std::vec::Vec<serde_json::Value> {
|
||||
return match self {
|
||||
Self::CurrentEpoch(std::option::Option::None) => std::vec::Vec::new(),
|
||||
Self::CurrentEpoch(std::option::Option::Some(config)) if config.is_empty() => std::vec::Vec::new(),
|
||||
Self::CurrentEpoch(std::option::Option::Some(config)) => std::vec![config.to_json_value()],
|
||||
Self::Slot { slot, config: std::option::Option::None } => std::vec![serde_json::json!(slot)],
|
||||
Self::Slot { slot, config: std::option::Option::Some(config) } if config.is_empty() => std::vec![serde_json::json!(slot)],
|
||||
Self::Slot { slot, config: std::option::Option::Some(config) } => std::vec![serde_json::json!(slot), config.to_json_value()],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Leader schedule mapping validator identities to relative epoch slot indices.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct SolanaLeaderSchedule {
|
||||
entries: std::collections::BTreeMap<ksp_core_lib::Pubkey, std::vec::Vec<usize>>,
|
||||
}
|
||||
|
||||
impl SolanaLeaderSchedule {
|
||||
/// Returns the complete leader schedule map.
|
||||
#[must_use]
|
||||
pub const fn entries(&self) -> &std::collections::BTreeMap<ksp_core_lib::Pubkey, std::vec::Vec<usize>> { return &self.entries; }
|
||||
|
||||
/// Decodes a leader schedule map from the Solana JSON wire shape.
|
||||
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
let decoded = crate::decode_wire_json::<std::collections::BTreeMap<std::string::String, std::vec::Vec<usize>>>(method, value);
|
||||
let wire = match decoded {
|
||||
std::result::Result::Ok(wire) => wire,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut entries = std::collections::BTreeMap::new();
|
||||
for (identity, slots) in wire {
|
||||
let pubkey = crate::parse_wire_pubkey(method, "leader_identity", identity.as_str());
|
||||
let pubkey = match pubkey {
|
||||
std::result::Result::Ok(pubkey) => pubkey,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
entries.insert(pubkey, slots);
|
||||
}
|
||||
return std::result::Result::Ok(Self { entries });
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration accepted by `getVoteAccounts`.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct SolanaVoteAccountsConfig {
|
||||
commitment: std::option::Option<crate::SolanaCommitment>,
|
||||
vote_pubkey: std::option::Option<ksp_core_lib::Pubkey>,
|
||||
keep_unstaked_delinquents: std::option::Option<bool>,
|
||||
delinquent_slot_distance: std::option::Option<u64>,
|
||||
}
|
||||
|
||||
impl SolanaVoteAccountsConfig {
|
||||
/// Creates a vote-accounts configuration.
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
commitment: std::option::Option<crate::SolanaCommitment>,
|
||||
vote_pubkey: std::option::Option<ksp_core_lib::Pubkey>,
|
||||
keep_unstaked_delinquents: std::option::Option<bool>,
|
||||
delinquent_slot_distance: std::option::Option<u64>,
|
||||
) -> Self {
|
||||
return Self { commitment, vote_pubkey, keep_unstaked_delinquents, delinquent_slot_distance };
|
||||
}
|
||||
/// Returns the optional commitment.
|
||||
#[must_use]
|
||||
pub const fn commitment(&self) -> std::option::Option<crate::SolanaCommitment> { return self.commitment; }
|
||||
/// Returns the optional vote-account public key filter.
|
||||
#[must_use]
|
||||
pub const fn vote_pubkey(&self) -> std::option::Option<&ksp_core_lib::Pubkey> { return self.vote_pubkey.as_ref(); }
|
||||
/// Returns whether unstaked delinquent validators should be kept.
|
||||
#[must_use]
|
||||
pub const fn keep_unstaked_delinquents(&self) -> std::option::Option<bool> { return self.keep_unstaked_delinquents; }
|
||||
/// Returns the optional delinquent slot distance.
|
||||
#[must_use]
|
||||
pub const fn delinquent_slot_distance(&self) -> std::option::Option<u64> { return self.delinquent_slot_distance; }
|
||||
/// Serializes this config to the Solana JSON-RPC wire object.
|
||||
#[must_use]
|
||||
pub(crate) fn to_json_value(&self) -> serde_json::Value {
|
||||
let mut object = serde_json::Map::new();
|
||||
if let std::option::Option::Some(commitment) = self.commitment {
|
||||
object.insert("commitment".to_owned(), serde_json::Value::String(commitment.as_str().to_owned()));
|
||||
}
|
||||
if let std::option::Option::Some(vote_pubkey) = self.vote_pubkey.as_ref() {
|
||||
object.insert("votePubkey".to_owned(), serde_json::Value::String(vote_pubkey.to_string()));
|
||||
}
|
||||
if let std::option::Option::Some(value) = self.keep_unstaked_delinquents {
|
||||
object.insert("keepUnstakedDelinquents".to_owned(), serde_json::Value::Bool(value));
|
||||
}
|
||||
if let std::option::Option::Some(value) = self.delinquent_slot_distance {
|
||||
object.insert("delinquentSlotDistance".to_owned(), serde_json::Value::Number(value.into()));
|
||||
}
|
||||
return serde_json::Value::Object(object);
|
||||
}
|
||||
}
|
||||
|
||||
/// One epoch-credit history entry returned by `getVoteAccounts`.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct SolanaEpochCredits {
|
||||
epoch: u64,
|
||||
credits: u64,
|
||||
previous_credits: u64,
|
||||
}
|
||||
|
||||
impl SolanaEpochCredits {
|
||||
/// Returns the epoch number.
|
||||
#[must_use]
|
||||
pub const fn epoch(&self) -> u64 { return self.epoch; }
|
||||
/// Returns cumulative credits at the end of the epoch.
|
||||
#[must_use]
|
||||
pub const fn credits(&self) -> u64 { return self.credits; }
|
||||
/// Returns cumulative credits before the epoch.
|
||||
#[must_use]
|
||||
pub const fn previous_credits(&self) -> u64 { return self.previous_credits; }
|
||||
}
|
||||
|
||||
/// One validator vote-account record returned by `getVoteAccounts`.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct SolanaVoteAccountInfo {
|
||||
vote_pubkey: ksp_core_lib::Pubkey,
|
||||
node_pubkey: ksp_core_lib::Pubkey,
|
||||
activated_stake: u64,
|
||||
commission: u8,
|
||||
inflation_rewards_commission_bps: std::option::Option<u16>,
|
||||
epoch_vote_account: bool,
|
||||
epoch_credits: std::vec::Vec<crate::SolanaEpochCredits>,
|
||||
last_vote: u64,
|
||||
root_slot: u64,
|
||||
}
|
||||
|
||||
impl SolanaVoteAccountInfo {
|
||||
/// Returns the vote account public key.
|
||||
#[must_use]
|
||||
pub const fn vote_pubkey(&self) -> &ksp_core_lib::Pubkey { return &self.vote_pubkey; }
|
||||
/// Returns the validator identity public key.
|
||||
#[must_use]
|
||||
pub const fn node_pubkey(&self) -> &ksp_core_lib::Pubkey { return &self.node_pubkey; }
|
||||
/// Returns the activated stake in lamports.
|
||||
#[must_use]
|
||||
pub const fn activated_stake(&self) -> u64 { return self.activated_stake; }
|
||||
/// Returns the legacy/effective percentage commission field.
|
||||
#[must_use]
|
||||
pub const fn commission(&self) -> u8 { return self.commission; }
|
||||
/// Returns the optional raw inflation-rewards commission in basis points.
|
||||
#[must_use]
|
||||
pub const fn inflation_rewards_commission_bps(&self) -> std::option::Option<u16> { return self.inflation_rewards_commission_bps; }
|
||||
/// Returns whether the vote account is staked for the current epoch.
|
||||
#[must_use]
|
||||
pub const fn epoch_vote_account(&self) -> bool { return self.epoch_vote_account; }
|
||||
/// Returns the bounded RPC epoch-credit history.
|
||||
#[must_use]
|
||||
pub fn epoch_credits(&self) -> &[crate::SolanaEpochCredits] { return self.epoch_credits.as_slice(); }
|
||||
/// Returns the latest voted slot or zero when no vote exists.
|
||||
#[must_use]
|
||||
pub const fn last_vote(&self) -> u64 { return self.last_vote; }
|
||||
/// Returns the current root slot or zero when no root exists.
|
||||
#[must_use]
|
||||
pub const fn root_slot(&self) -> u64 { return self.root_slot; }
|
||||
|
||||
/// Decodes one vote-account record from the Solana JSON wire shape.
|
||||
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
let decoded = crate::decode_wire_json::<WireVoteAccountInfo>(method, value);
|
||||
let wire = match decoded {
|
||||
std::result::Result::Ok(wire) => wire,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let vote_pubkey = crate::parse_wire_pubkey(method, "votePubkey", wire.vote_pubkey.as_str());
|
||||
let vote_pubkey = match vote_pubkey {
|
||||
std::result::Result::Ok(pubkey) => pubkey,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let node_pubkey = crate::parse_wire_pubkey(method, "nodePubkey", wire.node_pubkey.as_str());
|
||||
let node_pubkey = match node_pubkey {
|
||||
std::result::Result::Ok(pubkey) => pubkey,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut epoch_credits = std::vec::Vec::with_capacity(wire.epoch_credits.len());
|
||||
for entry in wire.epoch_credits {
|
||||
epoch_credits.push(crate::SolanaEpochCredits { epoch: entry[0], credits: entry[1], previous_credits: entry[2] });
|
||||
}
|
||||
return std::result::Result::Ok(Self {
|
||||
vote_pubkey,
|
||||
node_pubkey,
|
||||
activated_stake: wire.activated_stake,
|
||||
commission: wire.commission,
|
||||
inflation_rewards_commission_bps: wire.inflation_rewards_commission_bps,
|
||||
epoch_vote_account: wire.epoch_vote_account,
|
||||
epoch_credits,
|
||||
last_vote: wire.last_vote,
|
||||
root_slot: wire.root_slot,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Current and delinquent validator vote-account groups returned by `getVoteAccounts`.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct SolanaVoteAccountStatus {
|
||||
current: std::vec::Vec<crate::SolanaVoteAccountInfo>,
|
||||
delinquent: std::vec::Vec<crate::SolanaVoteAccountInfo>,
|
||||
}
|
||||
|
||||
impl SolanaVoteAccountStatus {
|
||||
/// Returns current vote accounts.
|
||||
#[must_use]
|
||||
pub fn current(&self) -> &[crate::SolanaVoteAccountInfo] { return self.current.as_slice(); }
|
||||
/// Returns delinquent vote accounts.
|
||||
#[must_use]
|
||||
pub fn delinquent(&self) -> &[crate::SolanaVoteAccountInfo] { return self.delinquent.as_slice(); }
|
||||
|
||||
/// Decodes the complete vote-account status response from the Solana JSON wire shape.
|
||||
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
let decoded = crate::decode_wire_json::<WireVoteAccountStatus>(method, value);
|
||||
let wire = match decoded {
|
||||
std::result::Result::Ok(wire) => wire,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut current = std::vec::Vec::with_capacity(wire.current.len());
|
||||
for value in wire.current {
|
||||
let decoded = crate::SolanaVoteAccountInfo::decode_wire(method, value);
|
||||
match decoded {
|
||||
std::result::Result::Ok(info) => current.push(info),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
let mut delinquent = std::vec::Vec::with_capacity(wire.delinquent.len());
|
||||
for value in wire.delinquent {
|
||||
let decoded = crate::SolanaVoteAccountInfo::decode_wire(method, value);
|
||||
match decoded {
|
||||
std::result::Result::Ok(info) => delinquent.push(info),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
return std::result::Result::Ok(Self { current, delinquent });
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WireClusterNode {
|
||||
pubkey: std::string::String,
|
||||
#[serde(default)] feature_set: std::option::Option<u32>,
|
||||
#[serde(default)] gossip: std::option::Option<std::string::String>,
|
||||
#[serde(default)] pubsub: std::option::Option<std::string::String>,
|
||||
#[serde(default)] rpc: std::option::Option<std::string::String>,
|
||||
#[serde(default)] serve_repair: std::option::Option<std::string::String>,
|
||||
#[serde(default)] shred_version: std::option::Option<u16>,
|
||||
#[serde(default)] tpu: std::option::Option<std::string::String>,
|
||||
#[serde(default)] tpu_forwards: std::option::Option<std::string::String>,
|
||||
#[serde(default)] tpu_forwards_quic: std::option::Option<std::string::String>,
|
||||
#[serde(default)] tpu_quic: std::option::Option<std::string::String>,
|
||||
#[serde(default)] tpu_vote: std::option::Option<std::string::String>,
|
||||
#[serde(default)] tvu: std::option::Option<std::string::String>,
|
||||
#[serde(default)] version: std::option::Option<std::string::String>,
|
||||
#[serde(default)] client_id: std::option::Option<std::string::String>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WireEpochInfo {
|
||||
absolute_slot: u64,
|
||||
block_height: u64,
|
||||
epoch: u64,
|
||||
slot_index: u64,
|
||||
slots_in_epoch: u64,
|
||||
transaction_count: std::option::Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WireEpochSchedule {
|
||||
first_normal_epoch: u64,
|
||||
first_normal_slot: u64,
|
||||
leader_schedule_slot_offset: u64,
|
||||
slots_per_epoch: u64,
|
||||
warmup: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct WireSnapshotSlotInfo {
|
||||
full: u64,
|
||||
incremental: std::option::Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WireVoteAccountInfo {
|
||||
vote_pubkey: std::string::String,
|
||||
node_pubkey: std::string::String,
|
||||
activated_stake: u64,
|
||||
commission: u8,
|
||||
#[serde(default)]
|
||||
inflation_rewards_commission_bps: std::option::Option<u16>,
|
||||
epoch_vote_account: bool,
|
||||
epoch_credits: std::vec::Vec<[u64; 3]>,
|
||||
last_vote: u64,
|
||||
root_slot: u64,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct WireVoteAccountStatus {
|
||||
current: std::vec::Vec<serde_json::Value>,
|
||||
delinquent: std::vec::Vec<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/rpc_cluster.rs"]
|
||||
mod tests;
|
||||
Reference in New Issue
Block a user