v0.2.9-pre.008

This commit is contained in:
2026-08-24 16:16:48 +02:00
parent 05aec88178
commit affa18a6e9
9 changed files with 1198 additions and 40 deletions

View File

@@ -1,10 +1,37 @@
// file: crates/ksp-onchain-transport-lib/unit_tests/grpc_subscribe.rs
// version: 3
// version: 4
fn filter_name(value: &str) -> crate::YellowstoneSubscribeFilterName {
return crate::YellowstoneSubscribeFilterName::new(value).expect("fixture filter name must validate");
}
fn minimal_transaction_info(signature_byte: u8, index: u64) -> yellowstone_grpc_proto::geyser::SubscribeUpdateTransactionInfo {
let mut meta = yellowstone_grpc_proto::solana::storage::confirmed_block::TransactionStatusMeta::default();
meta.fee = 5_000;
return yellowstone_grpc_proto::geyser::SubscribeUpdateTransactionInfo {
signature: vec![signature_byte; 64],
is_vote: false,
transaction: std::option::Option::Some(yellowstone_grpc_proto::solana::storage::confirmed_block::Transaction {
signatures: vec![vec![signature_byte; 64]],
message: std::option::Option::Some(yellowstone_grpc_proto::solana::storage::confirmed_block::Message {
header: std::option::Option::Some(yellowstone_grpc_proto::solana::storage::confirmed_block::MessageHeader {
num_required_signatures: 1,
num_readonly_signed_accounts: 0,
num_readonly_unsigned_accounts: 0,
}),
account_keys: vec![vec![1_u8; 32]],
recent_blockhash: vec![2_u8; 32],
instructions: vec![],
versioned: false,
address_table_lookups: vec![],
config: std::option::Option::None,
}),
}),
meta: std::option::Option::Some(meta),
index,
};
}
#[test]
fn yellowstone_subscribe_filter_name_is_bounded_globally_unique_and_debug_redacted() {
assert!(crate::YellowstoneSubscribeFilterName::new("").is_err());
@@ -553,3 +580,199 @@ fn yellowstone_transaction_status_update_preserves_error_and_rejects_malformed_s
};
assert!(super::decode_transaction_status_update(malformed).is_err());
}
#[test]
fn yellowstone_block_filter_encodes_complete_current_wire_and_redacts_selectors() {
let mut filter = crate::YellowstoneSubscribeBlockFilter::new();
let account = ksp_core_lib::Pubkey::new_from_array([31_u8; 32]);
assert!(filter.push_account_include(account).is_ok());
filter.set_include_transactions(std::option::Option::Some(true));
filter.set_include_accounts(std::option::Option::Some(false));
filter.set_include_entries(std::option::Option::Some(true));
let cuckoo = crate::YellowstoneCuckooFilter::new(vec![0_u8; 16], 4, 4, 8, 9, crate::YellowstoneCuckooHashAlgorithm::SipHash)
.expect("block Cuckoo filter must validate");
filter.set_cuckoo_account_include(std::option::Option::Some(cuckoo));
let wire = filter.to_wire();
assert_eq!(wire.account_include, vec![account.to_string()]);
assert_eq!(wire.include_transactions, std::option::Option::Some(true));
assert_eq!(wire.include_accounts, std::option::Option::Some(false));
assert_eq!(wire.include_entries, std::option::Option::Some(true));
assert!(wire.cuckoo_account_include.is_some());
let debug = format!("{filter:?}");
assert!(debug.contains("account_include_count"));
assert!(!debug.contains(&account.to_string()));
}
#[test]
fn yellowstone_block_update_reuses_transaction_account_entry_dtos_and_preserves_server_counts() {
let blockhash = ksp_core_lib::Pubkey::new_from_array([21_u8; 32]).to_string();
let parent_blockhash = ksp_core_lib::Pubkey::new_from_array([22_u8; 32]).to_string();
let reward_pubkey = ksp_core_lib::Pubkey::new_from_array([23_u8; 32]);
let account_pubkey = ksp_core_lib::Pubkey::new_from_array([24_u8; 32]);
let wire = yellowstone_grpc_proto::geyser::SubscribeUpdate {
filters: vec!["blocks-main".to_owned()],
update_oneof: std::option::Option::Some(yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof::Block(
yellowstone_grpc_proto::geyser::SubscribeUpdateBlock {
slot: 500,
blockhash: blockhash.clone(),
rewards: std::option::Option::Some(yellowstone_grpc_proto::solana::storage::confirmed_block::Rewards {
rewards: vec![yellowstone_grpc_proto::solana::storage::confirmed_block::Reward {
pubkey: reward_pubkey.to_string(),
lamports: 10,
post_balance: 11,
reward_type: yellowstone_grpc_proto::solana::storage::confirmed_block::RewardType::Fee as i32,
commission: "".to_owned(),
commission_bps: "".to_owned(),
}],
num_partitions: std::option::Option::Some(yellowstone_grpc_proto::solana::storage::confirmed_block::NumPartitions { num_partitions: 3 }),
}),
block_time: std::option::Option::Some(yellowstone_grpc_proto::solana::storage::confirmed_block::UnixTimestamp { timestamp: 1_700_000_000 }),
block_height: std::option::Option::Some(yellowstone_grpc_proto::solana::storage::confirmed_block::BlockHeight { block_height: 499 }),
transactions: vec![minimal_transaction_info(7, 2)],
parent_slot: 499,
parent_blockhash: parent_blockhash.clone(),
executed_transaction_count: 12,
updated_account_count: 34,
accounts: vec![yellowstone_grpc_proto::geyser::SubscribeUpdateAccountInfo {
pubkey: vec![24_u8; 32],
lamports: 77,
owner: vec![25_u8; 32],
executable: false,
rent_epoch: 4,
data: vec![1_u8, 2, 3],
write_version: 8,
txn_signature: std::option::Option::Some(vec![7_u8; 64]),
}],
entries_count: 56,
entries: vec![yellowstone_grpc_proto::geyser::SubscribeUpdateEntry {
slot: 500,
index: 4,
num_hashes: 5,
hash: vec![26_u8; 32],
executed_transaction_count: 6,
starting_transaction_index: 7,
}],
},
)),
created_at: std::option::Option::Some(yellowstone_grpc_proto::prost_types::Timestamp { seconds: 12, nanos: 34 }),
};
let update = super::decode_block_update(wire).expect("block update fixture must decode");
assert_eq!(update.filters()[0].as_str(), "blocks-main");
assert_eq!(update.slot(), 500);
assert_eq!(update.blockhash(), blockhash);
assert_eq!(update.parent_slot(), 499);
assert_eq!(update.parent_blockhash(), parent_blockhash);
assert_eq!(update.block_time(), std::option::Option::Some(1_700_000_000));
assert_eq!(update.block_height(), std::option::Option::Some(499));
assert_eq!(update.executed_transaction_count(), 12);
assert_eq!(update.transactions().len(), 1);
assert_eq!(update.transactions()[0].index(), 2);
assert_eq!(update.updated_account_count(), 34);
assert_eq!(update.accounts().len(), 1);
assert_eq!(update.accounts()[0].pubkey(), &account_pubkey);
assert_eq!(update.entries_count(), 56);
assert_eq!(update.entries().len(), 1);
assert_eq!(update.entries()[0].starting_transaction_index(), 7);
let rewards = update.rewards().expect("block rewards must be present");
assert_eq!(rewards.rewards().len(), 1);
assert_eq!(rewards.num_partitions(), std::option::Option::Some(3));
assert_eq!(rewards.rewards()[0].pubkey(), &reward_pubkey);
let debug = format!("{update:?}");
assert!(!debug.contains(&blockhash));
assert!(!debug.contains(&parent_blockhash));
assert!(!debug.contains(&reward_pubkey.to_string()));
assert!(!debug.contains(&account_pubkey.to_string()));
let malformed = yellowstone_grpc_proto::geyser::SubscribeUpdate {
filters: vec!["blocks-main".to_owned()],
update_oneof: std::option::Option::Some(yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof::Block(
yellowstone_grpc_proto::geyser::SubscribeUpdateBlock {
slot: 1,
blockhash: "not-a-solana-hash".to_owned(),
rewards: std::option::Option::None,
block_time: std::option::Option::None,
block_height: std::option::Option::None,
transactions: vec![],
parent_slot: 0,
parent_blockhash: ksp_core_lib::Pubkey::new_from_array([1_u8; 32]).to_string(),
executed_transaction_count: 0,
updated_account_count: 0,
accounts: vec![],
entries_count: 0,
entries: vec![],
},
)),
created_at: std::option::Option::None,
};
assert!(super::decode_block_update(malformed).is_err());
}
#[test]
fn yellowstone_block_meta_and_entry_updates_preserve_optional_and_legacy_entry_fields() {
let blockhash = ksp_core_lib::Pubkey::new_from_array([41_u8; 32]).to_string();
let parent_blockhash = ksp_core_lib::Pubkey::new_from_array([42_u8; 32]).to_string();
let meta_wire = yellowstone_grpc_proto::geyser::SubscribeUpdate {
filters: vec!["meta".to_owned()],
update_oneof: std::option::Option::Some(yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof::BlockMeta(
yellowstone_grpc_proto::geyser::SubscribeUpdateBlockMeta {
slot: 900,
blockhash: blockhash.clone(),
rewards: std::option::Option::None,
block_time: std::option::Option::None,
block_height: std::option::Option::None,
parent_slot: 899,
parent_blockhash: parent_blockhash.clone(),
executed_transaction_count: 17,
entries_count: 18,
},
)),
created_at: std::option::Option::None,
};
let meta = super::decode_block_meta_update(meta_wire).expect("block-meta update must decode");
assert_eq!(meta.slot(), 900);
assert_eq!(meta.blockhash(), blockhash);
assert_eq!(meta.parent_blockhash(), parent_blockhash);
assert_eq!(meta.rewards(), std::option::Option::None);
assert_eq!(meta.block_time(), std::option::Option::None);
assert_eq!(meta.block_height(), std::option::Option::None);
assert_eq!(meta.executed_transaction_count(), 17);
assert_eq!(meta.entries_count(), 18);
assert!(!format!("{meta:?}").contains(&blockhash));
let entry_wire = yellowstone_grpc_proto::geyser::SubscribeUpdate {
filters: vec!["entries".to_owned()],
update_oneof: std::option::Option::Some(yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof::Entry(
yellowstone_grpc_proto::geyser::SubscribeUpdateEntry {
slot: 900,
index: 3,
num_hashes: 4,
hash: vec![43_u8; 32],
executed_transaction_count: 5,
starting_transaction_index: 0,
},
)),
created_at: std::option::Option::Some(yellowstone_grpc_proto::prost_types::Timestamp { seconds: 6, nanos: 7 }),
};
let entry = super::decode_entry_update(entry_wire).expect("entry update must decode");
assert_eq!(entry.filters()[0].as_str(), "entries");
assert_eq!(entry.entry().slot(), 900);
assert_eq!(entry.entry().index(), 3);
assert_eq!(entry.entry().num_hashes(), 4);
assert_eq!(entry.entry().hash().as_bytes(), &[43_u8; 32]);
assert_eq!(entry.entry().executed_transaction_count(), 5);
assert_eq!(entry.entry().starting_transaction_index(), 0);
assert!(!format!("{entry:?}").contains("43, 43"));
let malformed_entry = yellowstone_grpc_proto::geyser::SubscribeUpdate {
filters: vec!["entries".to_owned()],
update_oneof: std::option::Option::Some(yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof::Entry(
yellowstone_grpc_proto::geyser::SubscribeUpdateEntry {
slot: 1,
index: 0,
num_hashes: 0,
hash: vec![0_u8; 31],
executed_transaction_count: 0,
starting_transaction_index: 0,
},
)),
created_at: std::option::Option::None,
};
assert!(super::decode_entry_update(malformed_entry).is_err());
}