v0.2.9-pre.004
This commit is contained in:
562
crates/ksp-onchain-transport-lib/src/grpc_subscribe.rs
Normal file
562
crates/ksp-onchain-transport-lib/src/grpc_subscribe.rs
Normal file
@@ -0,0 +1,562 @@
|
||||
// file: crates/ksp-onchain-transport-lib/src/grpc_subscribe.rs
|
||||
// version: 1
|
||||
|
||||
const MAX_GRPC_SUBSCRIBE_DATA_SLICE_COUNT: usize = 128;
|
||||
const MAX_GRPC_SUBSCRIBE_DATA_SLICE_LENGTH_BYTES: u64 = 64 * 1024 * 1024;
|
||||
const MAX_GRPC_SUBSCRIBE_FILTER_GROUP_COUNT: usize = 1_024;
|
||||
const MAX_GRPC_SUBSCRIBE_FILTER_NAME_LENGTH_BYTES: usize = 128;
|
||||
|
||||
/// Validated logical filter name used by the standard Yellowstone `SubscribeRequest` maps.
|
||||
///
|
||||
/// Filter names are returned by Yellowstone in update envelopes. KSP therefore keeps them as typed public data, but omits their text from `Debug` so routine
|
||||
/// diagnostics cannot accidentally disclose caller-defined labels.
|
||||
#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct YellowstoneSubscribeFilterName {
|
||||
value: std::string::String,
|
||||
}
|
||||
|
||||
impl YellowstoneSubscribeFilterName {
|
||||
/// Validates one provider-neutral Yellowstone filter-group name.
|
||||
pub fn new(value: impl std::convert::Into<std::string::String>) -> ksp_core_lib::Result<Self> {
|
||||
let value = value.into();
|
||||
if value.is_empty() || value.len() > MAX_GRPC_SUBSCRIBE_FILTER_NAME_LENGTH_BYTES || value.trim() != value || value.chars().any(char::is_control) {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_RPC_PARAMETERS, "Yellowstone subscribe filter name is invalid")
|
||||
.with_context("field", "grpc_subscribe.filter_name"),
|
||||
);
|
||||
}
|
||||
return std::result::Result::Ok(Self { value });
|
||||
}
|
||||
|
||||
/// Returns the validated filter name text.
|
||||
#[must_use]
|
||||
pub fn as_str(&self) -> &str {
|
||||
return self.value.as_str();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for YellowstoneSubscribeFilterName {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter.write_str("YellowstoneSubscribeFilterName(<redacted>)");
|
||||
}
|
||||
}
|
||||
|
||||
/// One standard Yellowstone account-data slice applied to subscribed account payloads.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct YellowstoneAccountsDataSlice {
|
||||
offset: u64,
|
||||
length: u64,
|
||||
}
|
||||
|
||||
impl YellowstoneAccountsDataSlice {
|
||||
/// Creates and validates one account-data slice.
|
||||
pub fn new(offset: u64, length: u64) -> ksp_core_lib::Result<Self> {
|
||||
if length > MAX_GRPC_SUBSCRIBE_DATA_SLICE_LENGTH_BYTES || offset.checked_add(length).is_none() {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_RPC_PARAMETERS, "Yellowstone account-data slice exceeds KSP bounds")
|
||||
.with_context("field", "grpc_subscribe.accounts_data_slice"),
|
||||
);
|
||||
}
|
||||
return std::result::Result::Ok(Self { offset, length });
|
||||
}
|
||||
|
||||
/// Returns the byte offset.
|
||||
#[must_use]
|
||||
pub const fn offset(self) -> u64 {
|
||||
return self.offset;
|
||||
}
|
||||
|
||||
/// Returns the requested byte length.
|
||||
#[must_use]
|
||||
pub const fn length(self) -> u64 {
|
||||
return self.length;
|
||||
}
|
||||
|
||||
fn to_wire(self) -> yellowstone_grpc_proto::geyser::SubscribeRequestAccountsDataSlice {
|
||||
return yellowstone_grpc_proto::geyser::SubscribeRequestAccountsDataSlice { offset: self.offset, length: self.length };
|
||||
}
|
||||
}
|
||||
|
||||
/// Optional ping mutation carried inside the standard Yellowstone subscribe stream.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct YellowstoneSubscribePing {
|
||||
id: i32,
|
||||
}
|
||||
|
||||
impl YellowstoneSubscribePing {
|
||||
/// Creates one subscribe-stream ping request.
|
||||
#[must_use]
|
||||
pub const fn new(id: i32) -> Self {
|
||||
return Self { id };
|
||||
}
|
||||
|
||||
/// Returns the exact ping identifier carried on the wire.
|
||||
#[must_use]
|
||||
pub const fn id(self) -> i32 {
|
||||
return self.id;
|
||||
}
|
||||
|
||||
fn to_wire(self) -> yellowstone_grpc_proto::geyser::SubscribeRequestPing {
|
||||
return yellowstone_grpc_proto::geyser::SubscribeRequestPing { id: self.id };
|
||||
}
|
||||
}
|
||||
|
||||
/// Account-family filter-group shell for the standard Yellowstone subscribe surface.
|
||||
///
|
||||
/// `0.2.9-pre.004` intentionally materializes only the top-level map contract. Account selectors and account-specific filter oneofs are added by `pre.005`.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct YellowstoneSubscribeAccountFilter {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl YellowstoneSubscribeAccountFilter {
|
||||
/// Creates an empty account filter group.
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
return Self { _private: () };
|
||||
}
|
||||
|
||||
fn to_wire(&self) -> yellowstone_grpc_proto::geyser::SubscribeRequestFilterAccounts {
|
||||
return yellowstone_grpc_proto::geyser::SubscribeRequestFilterAccounts::default();
|
||||
}
|
||||
}
|
||||
|
||||
/// Slot-family filter-group shell for the standard Yellowstone subscribe surface.
|
||||
///
|
||||
/// Slot optional flags and update decoding are added by `0.2.9-pre.005`.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct YellowstoneSubscribeSlotFilter {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl YellowstoneSubscribeSlotFilter {
|
||||
/// Creates an empty slot filter group.
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
return Self { _private: () };
|
||||
}
|
||||
|
||||
fn to_wire(&self) -> yellowstone_grpc_proto::geyser::SubscribeRequestFilterSlots {
|
||||
return yellowstone_grpc_proto::geyser::SubscribeRequestFilterSlots::default();
|
||||
}
|
||||
}
|
||||
|
||||
/// Transaction-family filter-group shell shared by `transactions` and `transactions_status`.
|
||||
///
|
||||
/// Transaction selectors, Cuckoo filters and token-account expansion are added by `0.2.9-pre.006`.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct YellowstoneSubscribeTransactionFilter {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl YellowstoneSubscribeTransactionFilter {
|
||||
/// Creates an empty transaction filter group.
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
return Self { _private: () };
|
||||
}
|
||||
|
||||
fn to_wire(&self) -> yellowstone_grpc_proto::geyser::SubscribeRequestFilterTransactions {
|
||||
return yellowstone_grpc_proto::geyser::SubscribeRequestFilterTransactions::default();
|
||||
}
|
||||
}
|
||||
|
||||
/// Block-family filter-group shell for the standard Yellowstone subscribe surface.
|
||||
///
|
||||
/// Block selectors, include flags and Cuckoo filters are added by `0.2.9-pre.007`.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct YellowstoneSubscribeBlockFilter {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl YellowstoneSubscribeBlockFilter {
|
||||
/// Creates an empty block filter group.
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
return Self { _private: () };
|
||||
}
|
||||
|
||||
fn to_wire(&self) -> yellowstone_grpc_proto::geyser::SubscribeRequestFilterBlocks {
|
||||
return yellowstone_grpc_proto::geyser::SubscribeRequestFilterBlocks::default();
|
||||
}
|
||||
}
|
||||
|
||||
/// Named empty filter activating the standard Yellowstone `blocks_meta` family.
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub struct YellowstoneSubscribeBlocksMetaFilter {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl YellowstoneSubscribeBlocksMetaFilter {
|
||||
/// Creates the empty marker filter.
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
return Self { _private: () };
|
||||
}
|
||||
|
||||
fn to_wire(self) -> yellowstone_grpc_proto::geyser::SubscribeRequestFilterBlocksMeta {
|
||||
return yellowstone_grpc_proto::geyser::SubscribeRequestFilterBlocksMeta::default();
|
||||
}
|
||||
}
|
||||
|
||||
/// Named empty filter activating the standard Yellowstone `entry` family.
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub struct YellowstoneSubscribeEntryFilter {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl YellowstoneSubscribeEntryFilter {
|
||||
/// Creates the empty marker filter.
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
return Self { _private: () };
|
||||
}
|
||||
|
||||
fn to_wire(self) -> yellowstone_grpc_proto::geyser::SubscribeRequestFilterEntry {
|
||||
return yellowstone_grpc_proto::geyser::SubscribeRequestFilterEntry::default();
|
||||
}
|
||||
}
|
||||
|
||||
/// Provider-neutral standard Yellowstone subscribe request owned by KSP.
|
||||
///
|
||||
/// The seven upstream maps are represented independently and retain named empty entries. An entirely empty map is the logical KSP representation of no active
|
||||
/// filter in that family; protobuf map encoding does not distinguish an omitted map from an empty map. Filter-group names are globally unique across all seven
|
||||
/// maps so the names echoed by `SubscribeUpdate.filters` remain unambiguous. `Debug` exposes only counts and common scalar options, never filter names or future
|
||||
/// filter payloads.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct YellowstoneSubscribeRequest {
|
||||
accounts: std::collections::BTreeMap<crate::YellowstoneSubscribeFilterName, crate::YellowstoneSubscribeAccountFilter>,
|
||||
slots: std::collections::BTreeMap<crate::YellowstoneSubscribeFilterName, crate::YellowstoneSubscribeSlotFilter>,
|
||||
transactions: std::collections::BTreeMap<crate::YellowstoneSubscribeFilterName, crate::YellowstoneSubscribeTransactionFilter>,
|
||||
transactions_status: std::collections::BTreeMap<crate::YellowstoneSubscribeFilterName, crate::YellowstoneSubscribeTransactionFilter>,
|
||||
blocks: std::collections::BTreeMap<crate::YellowstoneSubscribeFilterName, crate::YellowstoneSubscribeBlockFilter>,
|
||||
blocks_meta: std::collections::BTreeMap<crate::YellowstoneSubscribeFilterName, crate::YellowstoneSubscribeBlocksMetaFilter>,
|
||||
entry: std::collections::BTreeMap<crate::YellowstoneSubscribeFilterName, crate::YellowstoneSubscribeEntryFilter>,
|
||||
commitment: std::option::Option<crate::SolanaCommitment>,
|
||||
accounts_data_slice: std::vec::Vec<crate::YellowstoneAccountsDataSlice>,
|
||||
ping: std::option::Option<crate::YellowstoneSubscribePing>,
|
||||
from_slot: std::option::Option<u64>,
|
||||
}
|
||||
|
||||
impl YellowstoneSubscribeRequest {
|
||||
/// Creates an empty subscribe request. Empty requests are valid because later bidi lifecycle code uses request mutations to clear filters or carry ping state.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
return Self {
|
||||
accounts: std::collections::BTreeMap::new(),
|
||||
slots: std::collections::BTreeMap::new(),
|
||||
transactions: std::collections::BTreeMap::new(),
|
||||
transactions_status: std::collections::BTreeMap::new(),
|
||||
blocks: std::collections::BTreeMap::new(),
|
||||
blocks_meta: std::collections::BTreeMap::new(),
|
||||
entry: std::collections::BTreeMap::new(),
|
||||
commitment: std::option::Option::None,
|
||||
accounts_data_slice: std::vec::Vec::new(),
|
||||
ping: std::option::Option::None,
|
||||
from_slot: std::option::Option::None,
|
||||
};
|
||||
}
|
||||
|
||||
/// Inserts one named account filter group.
|
||||
pub fn insert_account_filter(
|
||||
&mut self,
|
||||
name: crate::YellowstoneSubscribeFilterName,
|
||||
filter: crate::YellowstoneSubscribeAccountFilter,
|
||||
) -> ksp_core_lib::Result<()> {
|
||||
let validation = self.validate_new_filter_name(&name);
|
||||
if let std::result::Result::Err(error) = validation {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
self.accounts.insert(name, filter);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
/// Inserts one named slot filter group.
|
||||
pub fn insert_slot_filter(
|
||||
&mut self,
|
||||
name: crate::YellowstoneSubscribeFilterName,
|
||||
filter: crate::YellowstoneSubscribeSlotFilter,
|
||||
) -> ksp_core_lib::Result<()> {
|
||||
let validation = self.validate_new_filter_name(&name);
|
||||
if let std::result::Result::Err(error) = validation {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
self.slots.insert(name, filter);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
/// Inserts one named transaction filter group.
|
||||
pub fn insert_transaction_filter(
|
||||
&mut self,
|
||||
name: crate::YellowstoneSubscribeFilterName,
|
||||
filter: crate::YellowstoneSubscribeTransactionFilter,
|
||||
) -> ksp_core_lib::Result<()> {
|
||||
let validation = self.validate_new_filter_name(&name);
|
||||
if let std::result::Result::Err(error) = validation {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
self.transactions.insert(name, filter);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
/// Inserts one named transaction-status filter group.
|
||||
pub fn insert_transaction_status_filter(
|
||||
&mut self,
|
||||
name: crate::YellowstoneSubscribeFilterName,
|
||||
filter: crate::YellowstoneSubscribeTransactionFilter,
|
||||
) -> ksp_core_lib::Result<()> {
|
||||
let validation = self.validate_new_filter_name(&name);
|
||||
if let std::result::Result::Err(error) = validation {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
self.transactions_status.insert(name, filter);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
/// Inserts one named block filter group.
|
||||
pub fn insert_block_filter(
|
||||
&mut self,
|
||||
name: crate::YellowstoneSubscribeFilterName,
|
||||
filter: crate::YellowstoneSubscribeBlockFilter,
|
||||
) -> ksp_core_lib::Result<()> {
|
||||
let validation = self.validate_new_filter_name(&name);
|
||||
if let std::result::Result::Err(error) = validation {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
self.blocks.insert(name, filter);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
/// Inserts one named empty `blocks_meta` filter group.
|
||||
pub fn insert_blocks_meta_filter(
|
||||
&mut self,
|
||||
name: crate::YellowstoneSubscribeFilterName,
|
||||
filter: crate::YellowstoneSubscribeBlocksMetaFilter,
|
||||
) -> ksp_core_lib::Result<()> {
|
||||
let validation = self.validate_new_filter_name(&name);
|
||||
if let std::result::Result::Err(error) = validation {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
self.blocks_meta.insert(name, filter);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
/// Inserts one named empty `entry` filter group.
|
||||
pub fn insert_entry_filter(
|
||||
&mut self,
|
||||
name: crate::YellowstoneSubscribeFilterName,
|
||||
filter: crate::YellowstoneSubscribeEntryFilter,
|
||||
) -> ksp_core_lib::Result<()> {
|
||||
let validation = self.validate_new_filter_name(&name);
|
||||
if let std::result::Result::Err(error) = validation {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
self.entry.insert(name, filter);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
/// Sets or clears the optional standard Yellowstone commitment.
|
||||
pub fn set_commitment(&mut self, commitment: std::option::Option<crate::SolanaCommitment>) {
|
||||
self.commitment = commitment;
|
||||
}
|
||||
|
||||
/// Adds one validated account-data slice while preserving insertion order.
|
||||
pub fn push_accounts_data_slice(&mut self, slice: crate::YellowstoneAccountsDataSlice) -> ksp_core_lib::Result<()> {
|
||||
if self.accounts_data_slice.len() >= MAX_GRPC_SUBSCRIBE_DATA_SLICE_COUNT {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_RPC_PARAMETERS, "Yellowstone account-data slice count exceeds KSP bounds")
|
||||
.with_context("field", "grpc_subscribe.accounts_data_slice"),
|
||||
);
|
||||
}
|
||||
self.accounts_data_slice.push(slice);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
/// Sets or clears the optional subscribe-stream ping mutation.
|
||||
pub fn set_ping(&mut self, ping: std::option::Option<crate::YellowstoneSubscribePing>) {
|
||||
self.ping = ping;
|
||||
}
|
||||
|
||||
/// Sets or clears the optional replay starting slot.
|
||||
pub fn set_from_slot(&mut self, from_slot: std::option::Option<u64>) {
|
||||
self.from_slot = from_slot;
|
||||
}
|
||||
|
||||
/// Returns the number of active account filter groups.
|
||||
#[must_use]
|
||||
pub fn account_filter_count(&self) -> usize {
|
||||
return self.accounts.len();
|
||||
}
|
||||
|
||||
/// Returns the number of active slot filter groups.
|
||||
#[must_use]
|
||||
pub fn slot_filter_count(&self) -> usize {
|
||||
return self.slots.len();
|
||||
}
|
||||
|
||||
/// Returns the number of active transaction filter groups.
|
||||
#[must_use]
|
||||
pub fn transaction_filter_count(&self) -> usize {
|
||||
return self.transactions.len();
|
||||
}
|
||||
|
||||
/// Returns the number of active transaction-status filter groups.
|
||||
#[must_use]
|
||||
pub fn transaction_status_filter_count(&self) -> usize {
|
||||
return self.transactions_status.len();
|
||||
}
|
||||
|
||||
/// Returns the number of active block filter groups.
|
||||
#[must_use]
|
||||
pub fn block_filter_count(&self) -> usize {
|
||||
return self.blocks.len();
|
||||
}
|
||||
|
||||
/// Returns the number of active block-meta filter groups.
|
||||
#[must_use]
|
||||
pub fn blocks_meta_filter_count(&self) -> usize {
|
||||
return self.blocks_meta.len();
|
||||
}
|
||||
|
||||
/// Returns the number of active entry filter groups.
|
||||
#[must_use]
|
||||
pub fn entry_filter_count(&self) -> usize {
|
||||
return self.entry.len();
|
||||
}
|
||||
|
||||
/// Returns the optional request commitment.
|
||||
#[must_use]
|
||||
pub const fn commitment(&self) -> std::option::Option<crate::SolanaCommitment> {
|
||||
return self.commitment;
|
||||
}
|
||||
|
||||
/// Returns account-data slices in exact wire order.
|
||||
#[must_use]
|
||||
pub fn accounts_data_slices(&self) -> &[crate::YellowstoneAccountsDataSlice] {
|
||||
return self.accounts_data_slice.as_slice();
|
||||
}
|
||||
|
||||
/// Returns the optional subscribe-stream ping mutation.
|
||||
#[must_use]
|
||||
pub const fn ping(&self) -> std::option::Option<crate::YellowstoneSubscribePing> {
|
||||
return self.ping;
|
||||
}
|
||||
|
||||
/// Returns the optional replay starting slot.
|
||||
#[must_use]
|
||||
pub const fn from_slot(&self) -> std::option::Option<u64> {
|
||||
return self.from_slot;
|
||||
}
|
||||
|
||||
/// Validates all deterministic common subscribe-request bounds before any network I/O.
|
||||
pub fn validate(&self) -> ksp_core_lib::Result<()> {
|
||||
if self.total_filter_count() > MAX_GRPC_SUBSCRIBE_FILTER_GROUP_COUNT {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_RPC_PARAMETERS, "Yellowstone subscribe filter-group count exceeds KSP bounds")
|
||||
.with_context("field", "grpc_subscribe.filter_groups"),
|
||||
);
|
||||
}
|
||||
if self.accounts_data_slice.len() > MAX_GRPC_SUBSCRIBE_DATA_SLICE_COUNT {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_RPC_PARAMETERS, "Yellowstone account-data slice count exceeds KSP bounds")
|
||||
.with_context("field", "grpc_subscribe.accounts_data_slice"),
|
||||
);
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
fn to_wire(&self) -> ksp_core_lib::Result<yellowstone_grpc_proto::geyser::SubscribeRequest> {
|
||||
let validation = self.validate();
|
||||
if let std::result::Result::Err(error) = validation {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
return std::result::Result::Ok(yellowstone_grpc_proto::geyser::SubscribeRequest {
|
||||
accounts: self.accounts.iter().map(|(name, filter)| return (name.as_str().to_owned(), filter.to_wire())).collect(),
|
||||
slots: self.slots.iter().map(|(name, filter)| return (name.as_str().to_owned(), filter.to_wire())).collect(),
|
||||
transactions: self.transactions.iter().map(|(name, filter)| return (name.as_str().to_owned(), filter.to_wire())).collect(),
|
||||
transactions_status: self.transactions_status.iter().map(|(name, filter)| return (name.as_str().to_owned(), filter.to_wire())).collect(),
|
||||
blocks: self.blocks.iter().map(|(name, filter)| return (name.as_str().to_owned(), filter.to_wire())).collect(),
|
||||
blocks_meta: self.blocks_meta.iter().map(|(name, filter)| return (name.as_str().to_owned(), (*filter).to_wire())).collect(),
|
||||
entry: self.entry.iter().map(|(name, filter)| return (name.as_str().to_owned(), (*filter).to_wire())).collect(),
|
||||
commitment: commitment_to_wire(self.commitment),
|
||||
accounts_data_slice: self.accounts_data_slice.iter().copied().map(crate::YellowstoneAccountsDataSlice::to_wire).collect(),
|
||||
ping: self.ping.map(crate::YellowstoneSubscribePing::to_wire),
|
||||
from_slot: self.from_slot,
|
||||
});
|
||||
}
|
||||
|
||||
fn total_filter_count(&self) -> usize {
|
||||
return self.accounts.len()
|
||||
+ self.slots.len()
|
||||
+ self.transactions.len()
|
||||
+ self.transactions_status.len()
|
||||
+ self.blocks.len()
|
||||
+ self.blocks_meta.len()
|
||||
+ self.entry.len();
|
||||
}
|
||||
|
||||
fn contains_filter_name(&self, name: &crate::YellowstoneSubscribeFilterName) -> bool {
|
||||
return self.accounts.contains_key(name)
|
||||
|| self.slots.contains_key(name)
|
||||
|| self.transactions.contains_key(name)
|
||||
|| self.transactions_status.contains_key(name)
|
||||
|| self.blocks.contains_key(name)
|
||||
|| self.blocks_meta.contains_key(name)
|
||||
|| self.entry.contains_key(name);
|
||||
}
|
||||
|
||||
fn validate_new_filter_name(&self, name: &crate::YellowstoneSubscribeFilterName) -> ksp_core_lib::Result<()> {
|
||||
if self.contains_filter_name(name) {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_RPC_PARAMETERS, "Yellowstone subscribe filter name must be globally unique")
|
||||
.with_context("field", "grpc_subscribe.filter_name"),
|
||||
);
|
||||
}
|
||||
if self.total_filter_count() >= MAX_GRPC_SUBSCRIBE_FILTER_GROUP_COUNT {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_RPC_PARAMETERS, "Yellowstone subscribe filter-group count exceeds KSP bounds")
|
||||
.with_context("field", "grpc_subscribe.filter_groups"),
|
||||
);
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
fn commitment_to_wire(commitment: std::option::Option<crate::SolanaCommitment>) -> std::option::Option<i32> {
|
||||
return commitment.map(|value| {
|
||||
return match value {
|
||||
crate::SolanaCommitment::Processed => yellowstone_grpc_proto::geyser::CommitmentLevel::Processed as i32,
|
||||
crate::SolanaCommitment::Confirmed => yellowstone_grpc_proto::geyser::CommitmentLevel::Confirmed as i32,
|
||||
crate::SolanaCommitment::Finalized => yellowstone_grpc_proto::geyser::CommitmentLevel::Finalized as i32,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
impl std::default::Default for YellowstoneSubscribeRequest {
|
||||
fn default() -> Self {
|
||||
return Self::new();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for YellowstoneSubscribeRequest {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter
|
||||
.debug_struct("YellowstoneSubscribeRequest")
|
||||
.field("account_filter_count", &self.accounts.len())
|
||||
.field("slot_filter_count", &self.slots.len())
|
||||
.field("transaction_filter_count", &self.transactions.len())
|
||||
.field("transaction_status_filter_count", &self.transactions_status.len())
|
||||
.field("block_filter_count", &self.blocks.len())
|
||||
.field("blocks_meta_filter_count", &self.blocks_meta.len())
|
||||
.field("entry_filter_count", &self.entry.len())
|
||||
.field("commitment", &self.commitment)
|
||||
.field("accounts_data_slice_count", &self.accounts_data_slice.len())
|
||||
.field("ping", &self.ping)
|
||||
.field("from_slot", &self.from_slot)
|
||||
.finish();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/grpc_subscribe.rs"]
|
||||
mod tests;
|
||||
Reference in New Issue
Block a user