v0.2.9-pre.008
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
// file: crates/ksp-onchain-transport-lib/src/grpc_subscribe.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
#[cfg(test)]
|
||||
const MAX_GRPC_BLOCKHASH_TEXT_LENGTH_BYTES: usize = 128;
|
||||
#[cfg(test)]
|
||||
const MAX_GRPC_BLOCK_VECTOR_COUNT: usize = 65_536;
|
||||
#[cfg(test)]
|
||||
const MAX_GRPC_SUBSCRIBE_ACCOUNT_DATA_LENGTH_BYTES: usize = 512 * 1024 * 1024;
|
||||
const MAX_GRPC_SUBSCRIBE_ACCOUNT_PREDICATE_COUNT: usize = 256;
|
||||
@@ -1945,24 +1949,478 @@ impl std::fmt::Debug for YellowstoneTransactionStatusUpdate {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.008`.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
/// Complete current block-family filter for the standard Yellowstone subscribe surface.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct YellowstoneSubscribeBlockFilter {
|
||||
_private: (),
|
||||
account_include: std::vec::Vec<ksp_core_lib::Pubkey>,
|
||||
include_transactions: std::option::Option<bool>,
|
||||
include_accounts: std::option::Option<bool>,
|
||||
include_entries: std::option::Option<bool>,
|
||||
cuckoo_account_include: std::option::Option<crate::YellowstoneCuckooFilter>,
|
||||
}
|
||||
|
||||
impl YellowstoneSubscribeBlockFilter {
|
||||
/// Creates an empty block filter group.
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
return Self { _private: () };
|
||||
pub fn new() -> Self {
|
||||
return Self {
|
||||
account_include: std::vec::Vec::new(),
|
||||
include_transactions: std::option::Option::None,
|
||||
include_accounts: std::option::Option::None,
|
||||
include_entries: std::option::Option::None,
|
||||
cuckoo_account_include: std::option::Option::None,
|
||||
};
|
||||
}
|
||||
|
||||
/// Adds one account include selector while preserving insertion order.
|
||||
pub fn push_account_include(&mut self, value: ksp_core_lib::Pubkey) -> ksp_core_lib::Result<()> {
|
||||
return push_block_selector(&mut self.account_include, value);
|
||||
}
|
||||
|
||||
/// Sets or clears whether full transaction payloads are included in matching block updates.
|
||||
pub fn set_include_transactions(&mut self, value: std::option::Option<bool>) {
|
||||
self.include_transactions = value;
|
||||
}
|
||||
|
||||
/// Sets or clears whether account payloads are included in matching block updates.
|
||||
pub fn set_include_accounts(&mut self, value: std::option::Option<bool>) {
|
||||
self.include_accounts = value;
|
||||
}
|
||||
|
||||
/// Sets or clears whether entry payloads are included in matching block updates.
|
||||
pub fn set_include_entries(&mut self, value: std::option::Option<bool>) {
|
||||
self.include_entries = value;
|
||||
}
|
||||
|
||||
/// Sets or clears the optional compressed account-include filter.
|
||||
pub fn set_cuckoo_account_include(&mut self, value: std::option::Option<crate::YellowstoneCuckooFilter>) {
|
||||
self.cuckoo_account_include = value;
|
||||
}
|
||||
|
||||
/// Returns ordered account include selectors.
|
||||
#[must_use]
|
||||
pub fn account_include(&self) -> &[ksp_core_lib::Pubkey] {
|
||||
return self.account_include.as_slice();
|
||||
}
|
||||
|
||||
/// Returns the optional transaction-payload inclusion flag.
|
||||
#[must_use]
|
||||
pub const fn include_transactions(&self) -> std::option::Option<bool> {
|
||||
return self.include_transactions;
|
||||
}
|
||||
|
||||
/// Returns the optional account-payload inclusion flag.
|
||||
#[must_use]
|
||||
pub const fn include_accounts(&self) -> std::option::Option<bool> {
|
||||
return self.include_accounts;
|
||||
}
|
||||
|
||||
/// Returns the optional entry-payload inclusion flag.
|
||||
#[must_use]
|
||||
pub const fn include_entries(&self) -> std::option::Option<bool> {
|
||||
return self.include_entries;
|
||||
}
|
||||
|
||||
/// Returns the optional compressed account-include filter.
|
||||
#[must_use]
|
||||
pub const fn cuckoo_account_include(&self) -> std::option::Option<&crate::YellowstoneCuckooFilter> {
|
||||
return self.cuckoo_account_include.as_ref();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn to_wire(&self) -> yellowstone_grpc_proto::geyser::SubscribeRequestFilterBlocks {
|
||||
return yellowstone_grpc_proto::geyser::SubscribeRequestFilterBlocks::default();
|
||||
return yellowstone_grpc_proto::geyser::SubscribeRequestFilterBlocks {
|
||||
account_include: self.account_include.iter().map(std::string::ToString::to_string).collect(),
|
||||
include_transactions: self.include_transactions,
|
||||
include_accounts: self.include_accounts,
|
||||
include_entries: self.include_entries,
|
||||
cuckoo_account_include: self.cuckoo_account_include.as_ref().map(crate::YellowstoneCuckooFilter::to_wire),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for YellowstoneSubscribeBlockFilter {
|
||||
fn default() -> Self {
|
||||
return Self::new();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for YellowstoneSubscribeBlockFilter {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter
|
||||
.debug_struct("YellowstoneSubscribeBlockFilter")
|
||||
.field("account_include_count", &self.account_include.len())
|
||||
.field("include_transactions", &self.include_transactions)
|
||||
.field("include_accounts", &self.include_accounts)
|
||||
.field("include_entries", &self.include_entries)
|
||||
.field("has_cuckoo_account_include", &self.cuckoo_account_include.is_some())
|
||||
.finish();
|
||||
}
|
||||
}
|
||||
|
||||
/// Rewards container carried by Yellowstone block and block-meta updates.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct YellowstoneBlockRewards {
|
||||
rewards: std::vec::Vec<crate::YellowstoneReward>,
|
||||
num_partitions: std::option::Option<u64>,
|
||||
}
|
||||
|
||||
impl YellowstoneBlockRewards {
|
||||
/// Returns ordered reward entries.
|
||||
#[must_use]
|
||||
pub fn rewards(&self) -> &[crate::YellowstoneReward] {
|
||||
return self.rewards.as_slice();
|
||||
}
|
||||
|
||||
/// Returns the optional number of reward partitions represented by the block.
|
||||
#[must_use]
|
||||
pub const fn num_partitions(&self) -> std::option::Option<u64> {
|
||||
return self.num_partitions;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for YellowstoneBlockRewards {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter
|
||||
.debug_struct("YellowstoneBlockRewards")
|
||||
.field("reward_count", &self.rewards.len())
|
||||
.field("num_partitions", &self.num_partitions)
|
||||
.finish();
|
||||
}
|
||||
}
|
||||
|
||||
/// One Yellowstone block-entry payload. The same wire message is used standalone and inside full block updates.
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub struct YellowstoneEntryInfo {
|
||||
slot: u64,
|
||||
index: u64,
|
||||
num_hashes: u64,
|
||||
hash: crate::YellowstoneHashBytes,
|
||||
executed_transaction_count: u64,
|
||||
starting_transaction_index: u64,
|
||||
}
|
||||
|
||||
impl YellowstoneEntryInfo {
|
||||
/// Returns the containing slot.
|
||||
#[must_use]
|
||||
pub const fn slot(self) -> u64 {
|
||||
return self.slot;
|
||||
}
|
||||
|
||||
/// Returns the entry index within the slot.
|
||||
#[must_use]
|
||||
pub const fn index(self) -> u64 {
|
||||
return self.index;
|
||||
}
|
||||
|
||||
/// Returns the number of hashes represented by this entry.
|
||||
#[must_use]
|
||||
pub const fn num_hashes(self) -> u64 {
|
||||
return self.num_hashes;
|
||||
}
|
||||
|
||||
/// Returns the exact fixed-width entry hash.
|
||||
#[must_use]
|
||||
pub const fn hash(self) -> crate::YellowstoneHashBytes {
|
||||
return self.hash;
|
||||
}
|
||||
|
||||
/// Returns the number of transactions executed by this entry.
|
||||
#[must_use]
|
||||
pub const fn executed_transaction_count(self) -> u64 {
|
||||
return self.executed_transaction_count;
|
||||
}
|
||||
|
||||
/// Returns the starting transaction index, including the legacy zero value emitted by older validators.
|
||||
#[must_use]
|
||||
pub const fn starting_transaction_index(self) -> u64 {
|
||||
return self.starting_transaction_index;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for YellowstoneEntryInfo {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter
|
||||
.debug_struct("YellowstoneEntryInfo")
|
||||
.field("slot", &self.slot)
|
||||
.field("index", &self.index)
|
||||
.field("num_hashes", &self.num_hashes)
|
||||
.field("executed_transaction_count", &self.executed_transaction_count)
|
||||
.field("starting_transaction_index", &self.starting_transaction_index)
|
||||
.finish_non_exhaustive();
|
||||
}
|
||||
}
|
||||
|
||||
/// Full standard Yellowstone block update projected into KSP-owned types.
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct YellowstoneBlockUpdate {
|
||||
filters: std::vec::Vec<crate::YellowstoneSubscribeFilterName>,
|
||||
created_at: std::option::Option<crate::YellowstoneUpdateTimestamp>,
|
||||
slot: u64,
|
||||
blockhash: std::string::String,
|
||||
rewards: std::option::Option<crate::YellowstoneBlockRewards>,
|
||||
block_time: std::option::Option<i64>,
|
||||
block_height: std::option::Option<u64>,
|
||||
parent_slot: u64,
|
||||
parent_blockhash: std::string::String,
|
||||
executed_transaction_count: u64,
|
||||
transactions: std::vec::Vec<crate::YellowstoneTransactionInfo>,
|
||||
updated_account_count: u64,
|
||||
accounts: std::vec::Vec<crate::YellowstoneAccountInfo>,
|
||||
entries_count: u64,
|
||||
entries: std::vec::Vec<crate::YellowstoneEntryInfo>,
|
||||
}
|
||||
|
||||
impl YellowstoneBlockUpdate {
|
||||
/// Returns echoed matching filter names in server order.
|
||||
#[must_use]
|
||||
pub fn filters(&self) -> &[crate::YellowstoneSubscribeFilterName] {
|
||||
return self.filters.as_slice();
|
||||
}
|
||||
|
||||
/// Returns optional server creation timestamp.
|
||||
#[must_use]
|
||||
pub const fn created_at(&self) -> std::option::Option<crate::YellowstoneUpdateTimestamp> {
|
||||
return self.created_at;
|
||||
}
|
||||
|
||||
/// Returns the block slot.
|
||||
#[must_use]
|
||||
pub const fn slot(&self) -> u64 {
|
||||
return self.slot;
|
||||
}
|
||||
|
||||
/// Returns the exact blockhash text validated as a 32-byte Base58 Solana hash.
|
||||
#[must_use]
|
||||
pub fn blockhash(&self) -> &str {
|
||||
return self.blockhash.as_str();
|
||||
}
|
||||
|
||||
/// Returns optional block rewards.
|
||||
#[must_use]
|
||||
pub const fn rewards(&self) -> std::option::Option<&crate::YellowstoneBlockRewards> {
|
||||
return self.rewards.as_ref();
|
||||
}
|
||||
|
||||
/// Returns optional Unix block time.
|
||||
#[must_use]
|
||||
pub const fn block_time(&self) -> std::option::Option<i64> {
|
||||
return self.block_time;
|
||||
}
|
||||
|
||||
/// Returns optional block height.
|
||||
#[must_use]
|
||||
pub const fn block_height(&self) -> std::option::Option<u64> {
|
||||
return self.block_height;
|
||||
}
|
||||
|
||||
/// Returns the parent slot.
|
||||
#[must_use]
|
||||
pub const fn parent_slot(&self) -> u64 {
|
||||
return self.parent_slot;
|
||||
}
|
||||
|
||||
/// Returns the exact parent blockhash text.
|
||||
#[must_use]
|
||||
pub fn parent_blockhash(&self) -> &str {
|
||||
return self.parent_blockhash.as_str();
|
||||
}
|
||||
|
||||
/// Returns the server-reported executed transaction count independently of whether transaction payloads were requested.
|
||||
#[must_use]
|
||||
pub const fn executed_transaction_count(&self) -> u64 {
|
||||
return self.executed_transaction_count;
|
||||
}
|
||||
|
||||
/// Returns transaction payloads included by the server.
|
||||
#[must_use]
|
||||
pub fn transactions(&self) -> &[crate::YellowstoneTransactionInfo] {
|
||||
return self.transactions.as_slice();
|
||||
}
|
||||
|
||||
/// Returns the server-reported updated account count independently of whether account payloads were requested.
|
||||
#[must_use]
|
||||
pub const fn updated_account_count(&self) -> u64 {
|
||||
return self.updated_account_count;
|
||||
}
|
||||
|
||||
/// Returns account payloads included by the server.
|
||||
#[must_use]
|
||||
pub fn accounts(&self) -> &[crate::YellowstoneAccountInfo] {
|
||||
return self.accounts.as_slice();
|
||||
}
|
||||
|
||||
/// Returns the server-reported entry count independently of whether entry payloads were requested.
|
||||
#[must_use]
|
||||
pub const fn entries_count(&self) -> u64 {
|
||||
return self.entries_count;
|
||||
}
|
||||
|
||||
/// Returns entry payloads included by the server.
|
||||
#[must_use]
|
||||
pub fn entries(&self) -> &[crate::YellowstoneEntryInfo] {
|
||||
return self.entries.as_slice();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for YellowstoneBlockUpdate {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter
|
||||
.debug_struct("YellowstoneBlockUpdate")
|
||||
.field("filter_count", &self.filters.len())
|
||||
.field("created_at", &self.created_at)
|
||||
.field("slot", &self.slot)
|
||||
.field("has_rewards", &self.rewards.is_some())
|
||||
.field("block_time", &self.block_time)
|
||||
.field("block_height", &self.block_height)
|
||||
.field("parent_slot", &self.parent_slot)
|
||||
.field("executed_transaction_count", &self.executed_transaction_count)
|
||||
.field("transaction_payload_count", &self.transactions.len())
|
||||
.field("updated_account_count", &self.updated_account_count)
|
||||
.field("account_payload_count", &self.accounts.len())
|
||||
.field("entries_count", &self.entries_count)
|
||||
.field("entry_payload_count", &self.entries.len())
|
||||
.finish_non_exhaustive();
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata-only standard Yellowstone block update.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct YellowstoneBlockMetaUpdate {
|
||||
filters: std::vec::Vec<crate::YellowstoneSubscribeFilterName>,
|
||||
created_at: std::option::Option<crate::YellowstoneUpdateTimestamp>,
|
||||
slot: u64,
|
||||
blockhash: std::string::String,
|
||||
rewards: std::option::Option<crate::YellowstoneBlockRewards>,
|
||||
block_time: std::option::Option<i64>,
|
||||
block_height: std::option::Option<u64>,
|
||||
parent_slot: u64,
|
||||
parent_blockhash: std::string::String,
|
||||
executed_transaction_count: u64,
|
||||
entries_count: u64,
|
||||
}
|
||||
|
||||
impl YellowstoneBlockMetaUpdate {
|
||||
/// Returns echoed matching filter names in server order.
|
||||
#[must_use]
|
||||
pub fn filters(&self) -> &[crate::YellowstoneSubscribeFilterName] {
|
||||
return self.filters.as_slice();
|
||||
}
|
||||
|
||||
/// Returns optional server creation timestamp.
|
||||
#[must_use]
|
||||
pub const fn created_at(&self) -> std::option::Option<crate::YellowstoneUpdateTimestamp> {
|
||||
return self.created_at;
|
||||
}
|
||||
|
||||
/// Returns the block slot.
|
||||
#[must_use]
|
||||
pub const fn slot(&self) -> u64 {
|
||||
return self.slot;
|
||||
}
|
||||
|
||||
/// Returns the exact blockhash text.
|
||||
#[must_use]
|
||||
pub fn blockhash(&self) -> &str {
|
||||
return self.blockhash.as_str();
|
||||
}
|
||||
|
||||
/// Returns optional block rewards.
|
||||
#[must_use]
|
||||
pub const fn rewards(&self) -> std::option::Option<&crate::YellowstoneBlockRewards> {
|
||||
return self.rewards.as_ref();
|
||||
}
|
||||
|
||||
/// Returns optional Unix block time.
|
||||
#[must_use]
|
||||
pub const fn block_time(&self) -> std::option::Option<i64> {
|
||||
return self.block_time;
|
||||
}
|
||||
|
||||
/// Returns optional block height.
|
||||
#[must_use]
|
||||
pub const fn block_height(&self) -> std::option::Option<u64> {
|
||||
return self.block_height;
|
||||
}
|
||||
|
||||
/// Returns the parent slot.
|
||||
#[must_use]
|
||||
pub const fn parent_slot(&self) -> u64 {
|
||||
return self.parent_slot;
|
||||
}
|
||||
|
||||
/// Returns the exact parent blockhash text.
|
||||
#[must_use]
|
||||
pub fn parent_blockhash(&self) -> &str {
|
||||
return self.parent_blockhash.as_str();
|
||||
}
|
||||
|
||||
/// Returns the server-reported executed transaction count.
|
||||
#[must_use]
|
||||
pub const fn executed_transaction_count(&self) -> u64 {
|
||||
return self.executed_transaction_count;
|
||||
}
|
||||
|
||||
/// Returns the server-reported entry count.
|
||||
#[must_use]
|
||||
pub const fn entries_count(&self) -> u64 {
|
||||
return self.entries_count;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for YellowstoneBlockMetaUpdate {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter
|
||||
.debug_struct("YellowstoneBlockMetaUpdate")
|
||||
.field("filter_count", &self.filters.len())
|
||||
.field("created_at", &self.created_at)
|
||||
.field("slot", &self.slot)
|
||||
.field("has_rewards", &self.rewards.is_some())
|
||||
.field("block_time", &self.block_time)
|
||||
.field("block_height", &self.block_height)
|
||||
.field("parent_slot", &self.parent_slot)
|
||||
.field("executed_transaction_count", &self.executed_transaction_count)
|
||||
.field("entries_count", &self.entries_count)
|
||||
.finish_non_exhaustive();
|
||||
}
|
||||
}
|
||||
|
||||
/// Standalone standard Yellowstone entry update with its update envelope.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct YellowstoneEntryUpdate {
|
||||
filters: std::vec::Vec<crate::YellowstoneSubscribeFilterName>,
|
||||
created_at: std::option::Option<crate::YellowstoneUpdateTimestamp>,
|
||||
entry: crate::YellowstoneEntryInfo,
|
||||
}
|
||||
|
||||
impl YellowstoneEntryUpdate {
|
||||
/// Returns echoed matching filter names in server order.
|
||||
#[must_use]
|
||||
pub fn filters(&self) -> &[crate::YellowstoneSubscribeFilterName] {
|
||||
return self.filters.as_slice();
|
||||
}
|
||||
|
||||
/// Returns optional server creation timestamp.
|
||||
#[must_use]
|
||||
pub const fn created_at(&self) -> std::option::Option<crate::YellowstoneUpdateTimestamp> {
|
||||
return self.created_at;
|
||||
}
|
||||
|
||||
/// Returns the complete entry payload.
|
||||
#[must_use]
|
||||
pub const fn entry(&self) -> crate::YellowstoneEntryInfo {
|
||||
return self.entry;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for YellowstoneEntryUpdate {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter
|
||||
.debug_struct("YellowstoneEntryUpdate")
|
||||
.field("filter_count", &self.filters.len())
|
||||
.field("created_at", &self.created_at)
|
||||
.field("entry", &self.entry)
|
||||
.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2417,6 +2875,192 @@ fn decode_transaction_status_update(wire: yellowstone_grpc_proto::geyser::Subscr
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn decode_block_update(wire: yellowstone_grpc_proto::geyser::SubscribeUpdate) -> ksp_core_lib::Result<crate::YellowstoneBlockUpdate> {
|
||||
let (filters, created_at) = match decode_update_envelope(wire.filters, wire.created_at) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let update = match wire.update_oneof {
|
||||
std::option::Option::Some(yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof::Block(value)) => value,
|
||||
_ => return invalid_subscribe_response("block", "Yellowstone update does not contain a block payload"),
|
||||
};
|
||||
if update.transactions.len() > MAX_GRPC_BLOCK_VECTOR_COUNT
|
||||
|| update.accounts.len() > MAX_GRPC_BLOCK_VECTOR_COUNT
|
||||
|| update.entries.len() > MAX_GRPC_BLOCK_VECTOR_COUNT
|
||||
{
|
||||
return invalid_subscribe_response("block", "Yellowstone block payload collection exceeds the KSP bound");
|
||||
}
|
||||
let blockhash = match decode_blockhash_text("block.blockhash", update.blockhash) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let parent_blockhash = match decode_blockhash_text("block.parent_blockhash", update.parent_blockhash) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let rewards = match update.rewards {
|
||||
std::option::Option::Some(value) => match decode_block_rewards(value) {
|
||||
std::result::Result::Ok(value) => std::option::Option::Some(value),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
},
|
||||
std::option::Option::None => std::option::Option::None,
|
||||
};
|
||||
let block_time = update.block_time.map(|value| return value.timestamp);
|
||||
let block_height = update.block_height.map(|value| return value.block_height);
|
||||
let mut transactions = std::vec::Vec::with_capacity(update.transactions.len());
|
||||
for value in update.transactions {
|
||||
let value = match decode_transaction_info(value) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
transactions.push(value);
|
||||
}
|
||||
let mut accounts = std::vec::Vec::with_capacity(update.accounts.len());
|
||||
for value in update.accounts {
|
||||
let value = match decode_account_info(value) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
accounts.push(value);
|
||||
}
|
||||
let mut entries = std::vec::Vec::with_capacity(update.entries.len());
|
||||
for value in update.entries {
|
||||
let value = match decode_entry_info(value) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
entries.push(value);
|
||||
}
|
||||
return std::result::Result::Ok(crate::YellowstoneBlockUpdate {
|
||||
filters,
|
||||
created_at,
|
||||
slot: update.slot,
|
||||
blockhash,
|
||||
rewards,
|
||||
block_time,
|
||||
block_height,
|
||||
parent_slot: update.parent_slot,
|
||||
parent_blockhash,
|
||||
executed_transaction_count: update.executed_transaction_count,
|
||||
transactions,
|
||||
updated_account_count: update.updated_account_count,
|
||||
accounts,
|
||||
entries_count: update.entries_count,
|
||||
entries,
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn decode_block_meta_update(wire: yellowstone_grpc_proto::geyser::SubscribeUpdate) -> ksp_core_lib::Result<crate::YellowstoneBlockMetaUpdate> {
|
||||
let (filters, created_at) = match decode_update_envelope(wire.filters, wire.created_at) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let update = match wire.update_oneof {
|
||||
std::option::Option::Some(yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof::BlockMeta(value)) => value,
|
||||
_ => return invalid_subscribe_response("block_meta", "Yellowstone update does not contain a block-meta payload"),
|
||||
};
|
||||
let blockhash = match decode_blockhash_text("block_meta.blockhash", update.blockhash) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let parent_blockhash = match decode_blockhash_text("block_meta.parent_blockhash", update.parent_blockhash) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let rewards = match update.rewards {
|
||||
std::option::Option::Some(value) => match decode_block_rewards(value) {
|
||||
std::result::Result::Ok(value) => std::option::Option::Some(value),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
},
|
||||
std::option::Option::None => std::option::Option::None,
|
||||
};
|
||||
return std::result::Result::Ok(crate::YellowstoneBlockMetaUpdate {
|
||||
filters,
|
||||
created_at,
|
||||
slot: update.slot,
|
||||
blockhash,
|
||||
rewards,
|
||||
block_time: update.block_time.map(|value| return value.timestamp),
|
||||
block_height: update.block_height.map(|value| return value.block_height),
|
||||
parent_slot: update.parent_slot,
|
||||
parent_blockhash,
|
||||
executed_transaction_count: update.executed_transaction_count,
|
||||
entries_count: update.entries_count,
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn decode_entry_update(wire: yellowstone_grpc_proto::geyser::SubscribeUpdate) -> ksp_core_lib::Result<crate::YellowstoneEntryUpdate> {
|
||||
let (filters, created_at) = match decode_update_envelope(wire.filters, wire.created_at) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let update = match wire.update_oneof {
|
||||
std::option::Option::Some(yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof::Entry(value)) => value,
|
||||
_ => return invalid_subscribe_response("entry", "Yellowstone update does not contain an entry payload"),
|
||||
};
|
||||
let entry = match decode_entry_info(update) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(crate::YellowstoneEntryUpdate { filters, created_at, entry });
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn decode_block_rewards(wire: yellowstone_grpc_proto::solana::storage::confirmed_block::Rewards) -> ksp_core_lib::Result<crate::YellowstoneBlockRewards> {
|
||||
if wire.rewards.len() > MAX_GRPC_BLOCK_VECTOR_COUNT {
|
||||
return invalid_subscribe_response("block.rewards", "Yellowstone block reward count exceeds the KSP bound");
|
||||
}
|
||||
let mut rewards = std::vec::Vec::with_capacity(wire.rewards.len());
|
||||
for value in wire.rewards {
|
||||
let value = match decode_reward(value) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
rewards.push(value);
|
||||
}
|
||||
return std::result::Result::Ok(crate::YellowstoneBlockRewards { rewards, num_partitions: wire.num_partitions.map(|value| return value.num_partitions) });
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn decode_entry_info(wire: yellowstone_grpc_proto::geyser::SubscribeUpdateEntry) -> ksp_core_lib::Result<crate::YellowstoneEntryInfo> {
|
||||
let hash = match decode_entry_hash(wire.hash) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(crate::YellowstoneEntryInfo {
|
||||
slot: wire.slot,
|
||||
index: wire.index,
|
||||
num_hashes: wire.num_hashes,
|
||||
hash,
|
||||
executed_transaction_count: wire.executed_transaction_count,
|
||||
starting_transaction_index: wire.starting_transaction_index,
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn decode_entry_hash(bytes: std::vec::Vec<u8>) -> ksp_core_lib::Result<crate::YellowstoneHashBytes> {
|
||||
let bytes: [u8; YELLOWSTONE_HASH_LENGTH_BYTES] = match bytes.try_into() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return invalid_subscribe_response("entry.hash", "Yellowstone entry contains an invalid hash length"),
|
||||
};
|
||||
return std::result::Result::Ok(crate::YellowstoneHashBytes { bytes });
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn decode_blockhash_text(field: &'static str, value: std::string::String) -> ksp_core_lib::Result<std::string::String> {
|
||||
if value.is_empty()
|
||||
|| value.len() > MAX_GRPC_BLOCKHASH_TEXT_LENGTH_BYTES
|
||||
|| value.trim() != value
|
||||
|| base58_decoded_length(value.as_str()) != std::option::Option::Some(YELLOWSTONE_HASH_LENGTH_BYTES)
|
||||
{
|
||||
return invalid_subscribe_response(field, "Yellowstone block update contains an invalid Solana blockhash");
|
||||
}
|
||||
return std::result::Result::Ok(value);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn decode_transaction_info(wire: yellowstone_grpc_proto::geyser::SubscribeUpdateTransactionInfo) -> ksp_core_lib::Result<crate::YellowstoneTransactionInfo> {
|
||||
let signature = match decode_transaction_signature("transaction.signature", wire.signature) {
|
||||
@@ -2873,6 +3517,17 @@ fn decode_pubkey_bytes(field: &'static str, bytes: std::vec::Vec<u8>) -> ksp_cor
|
||||
return std::result::Result::Ok(ksp_core_lib::Pubkey::new_from_array(array));
|
||||
}
|
||||
|
||||
fn push_block_selector(target: &mut std::vec::Vec<ksp_core_lib::Pubkey>, value: ksp_core_lib::Pubkey) -> ksp_core_lib::Result<()> {
|
||||
if target.len() >= MAX_GRPC_SUBSCRIBE_ACCOUNT_SELECTOR_COUNT {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_RPC_PARAMETERS, "Yellowstone block account selector count exceeds KSP bounds")
|
||||
.with_context("field", "grpc_subscribe.block.account_include"),
|
||||
);
|
||||
}
|
||||
target.push(value);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
fn push_transaction_selector(target: &mut std::vec::Vec<ksp_core_lib::Pubkey>, value: ksp_core_lib::Pubkey, field: &'static str) -> ksp_core_lib::Result<()> {
|
||||
if target.len() >= MAX_GRPC_SUBSCRIBE_ACCOUNT_SELECTOR_COUNT {
|
||||
return std::result::Result::Err(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-onchain-transport-lib/src/lib.rs
|
||||
// version: 40
|
||||
// version: 41
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -143,12 +143,22 @@ pub use self::grpc_subscribe::YellowstoneAccountMemcmpEncoding;
|
||||
pub use self::grpc_subscribe::YellowstoneAccountUpdate;
|
||||
/// One standard Yellowstone account-data slice.
|
||||
pub use self::grpc_subscribe::YellowstoneAccountsDataSlice;
|
||||
/// Metadata-only standard Yellowstone block update.
|
||||
pub use self::grpc_subscribe::YellowstoneBlockMetaUpdate;
|
||||
/// Rewards container carried by Yellowstone block and block-meta updates.
|
||||
pub use self::grpc_subscribe::YellowstoneBlockRewards;
|
||||
/// Full standard Yellowstone block update.
|
||||
pub use self::grpc_subscribe::YellowstoneBlockUpdate;
|
||||
/// One compiled instruction from the Yellowstone Solana-storage transaction wire.
|
||||
pub use self::grpc_subscribe::YellowstoneCompiledInstruction;
|
||||
/// Wire-preserving KSP representation of a standard Yellowstone Cuckoo filter.
|
||||
pub use self::grpc_subscribe::YellowstoneCuckooFilter;
|
||||
/// Hash algorithm carried by a standard Yellowstone Cuckoo filter.
|
||||
pub use self::grpc_subscribe::YellowstoneCuckooHashAlgorithm;
|
||||
/// One Yellowstone block-entry payload reused by block and standalone entry updates.
|
||||
pub use self::grpc_subscribe::YellowstoneEntryInfo;
|
||||
/// Standalone standard Yellowstone entry update.
|
||||
pub use self::grpc_subscribe::YellowstoneEntryUpdate;
|
||||
/// Fixed-width 32-byte hash from the Yellowstone Solana-storage transaction wire.
|
||||
pub use self::grpc_subscribe::YellowstoneHashBytes;
|
||||
/// One inner instruction from Yellowstone transaction status metadata.
|
||||
@@ -171,7 +181,7 @@ pub use self::grpc_subscribe::YellowstoneSlotUpdate;
|
||||
pub use self::grpc_subscribe::YellowstoneStoredTransaction;
|
||||
/// Complete account-family filter group for standard Yellowstone Subscribe.
|
||||
pub use self::grpc_subscribe::YellowstoneSubscribeAccountFilter;
|
||||
/// Block-family filter-group shell for standard Yellowstone Subscribe.
|
||||
/// Complete block-family filter group for standard Yellowstone Subscribe.
|
||||
pub use self::grpc_subscribe::YellowstoneSubscribeBlockFilter;
|
||||
/// Empty filter marker activating the standard Yellowstone blocks-meta family.
|
||||
pub use self::grpc_subscribe::YellowstoneSubscribeBlocksMetaFilter;
|
||||
|
||||
Reference in New Issue
Block a user