v0.2.9-pre.005
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-onchain-transport-lib/unit_tests/grpc_subscribe.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
fn filter_name(value: &str) -> crate::YellowstoneSubscribeFilterName {
|
||||
return crate::YellowstoneSubscribeFilterName::new(value).expect("fixture filter name must validate");
|
||||
@@ -123,3 +123,245 @@ fn yellowstone_subscribe_debug_omits_filter_names_and_future_payloads() {
|
||||
assert!(debug.contains("account_filter_count"));
|
||||
assert!(debug.contains("from_slot"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn yellowstone_account_and_slot_filters_encode_complete_current_wire() {
|
||||
let account = ksp_core_lib::Pubkey::new_from_array([1_u8; 32]);
|
||||
let owner = ksp_core_lib::Pubkey::new_from_array([2_u8; 32]);
|
||||
let mut filter = crate::YellowstoneSubscribeAccountFilter::new();
|
||||
assert!(filter.push_account(account).is_ok());
|
||||
assert!(filter.push_owner(owner).is_ok());
|
||||
let raw = crate::YellowstoneAccountMemcmp::bytes(4, vec![1_u8, 2, 3]).expect("raw memcmp must validate");
|
||||
let base58 = crate::YellowstoneAccountMemcmp::base58(8, "1234").expect("base58 memcmp must validate");
|
||||
let base64 = crate::YellowstoneAccountMemcmp::base64(12, "AQID==").expect("base64 memcmp must validate");
|
||||
assert!(filter.push_filter(crate::YellowstoneAccountFilterPredicate::Memcmp(raw)).is_ok());
|
||||
assert!(filter.push_filter(crate::YellowstoneAccountFilterPredicate::Memcmp(base58)).is_ok());
|
||||
assert!(filter.push_filter(crate::YellowstoneAccountFilterPredicate::Memcmp(base64)).is_ok());
|
||||
assert!(filter.push_filter(crate::YellowstoneAccountFilterPredicate::DataSize(165)).is_ok());
|
||||
assert!(filter.push_filter(crate::YellowstoneAccountFilterPredicate::TokenAccountState(true)).is_ok());
|
||||
assert!(filter.push_filter(crate::YellowstoneAccountFilterPredicate::Lamports(crate::YellowstoneAccountLamportsFilter::Eq(1))).is_ok());
|
||||
assert!(filter.push_filter(crate::YellowstoneAccountFilterPredicate::Lamports(crate::YellowstoneAccountLamportsFilter::Ne(2))).is_ok());
|
||||
assert!(filter.push_filter(crate::YellowstoneAccountFilterPredicate::Lamports(crate::YellowstoneAccountLamportsFilter::Lt(3))).is_ok());
|
||||
assert!(filter.push_filter(crate::YellowstoneAccountFilterPredicate::Lamports(crate::YellowstoneAccountLamportsFilter::Gt(4))).is_ok());
|
||||
filter.set_nonempty_txn_signature(std::option::Option::Some(true));
|
||||
let cuckoo =
|
||||
crate::YellowstoneCuckooFilter::new(vec![9_u8; 16], 4, 4, 8, 77, crate::YellowstoneCuckooHashAlgorithm::SipHash).expect("cuckoo fixture must validate");
|
||||
filter.set_cuckoo_accounts_filter(std::option::Option::Some(cuckoo));
|
||||
let wire = filter.to_wire();
|
||||
assert_eq!(wire.account, vec![account.to_string()]);
|
||||
assert_eq!(wire.owner, vec![owner.to_string()]);
|
||||
assert_eq!(wire.filters.len(), 9);
|
||||
match wire.filters[0].filter.as_ref().expect("raw memcmp oneof must be present") {
|
||||
yellowstone_grpc_proto::geyser::subscribe_request_filter_accounts_filter::Filter::Memcmp(value) => assert_eq!(
|
||||
value.data,
|
||||
std::option::Option::Some(yellowstone_grpc_proto::geyser::subscribe_request_filter_accounts_filter_memcmp::Data::Bytes(vec![1_u8, 2, 3]))
|
||||
),
|
||||
_ => panic!("first predicate must stay memcmp"),
|
||||
}
|
||||
match wire.filters[5].filter.as_ref().expect("lamports oneof must be present") {
|
||||
yellowstone_grpc_proto::geyser::subscribe_request_filter_accounts_filter::Filter::Lamports(value) => {
|
||||
assert_eq!(value.cmp, std::option::Option::Some(yellowstone_grpc_proto::geyser::subscribe_request_filter_accounts_filter_lamports::Cmp::Eq(1)))
|
||||
},
|
||||
_ => panic!("sixth predicate must stay lamports"),
|
||||
}
|
||||
assert_eq!(wire.nonempty_txn_signature, std::option::Option::Some(true));
|
||||
let cuckoo = wire.cuckoo_accounts_filter.expect("cuckoo filter must be present");
|
||||
assert_eq!(cuckoo.data, vec![9_u8; 16]);
|
||||
assert_eq!(cuckoo.bucket_count, 4);
|
||||
assert_eq!(cuckoo.entries_per_bucket, 4);
|
||||
assert_eq!(cuckoo.fingerprint_bits, 8);
|
||||
assert_eq!(cuckoo.hash_seed, 77);
|
||||
assert_eq!(cuckoo.hash_algorithm, yellowstone_grpc_proto::geyser::CuckooHashAlgorithm::SipHash as i32);
|
||||
let mut slots = crate::YellowstoneSubscribeSlotFilter::new();
|
||||
slots.set_filter_by_commitment(std::option::Option::Some(false));
|
||||
slots.set_interslot_updates(std::option::Option::Some(true));
|
||||
assert_eq!(slots.filter_by_commitment(), std::option::Option::Some(false));
|
||||
assert_eq!(slots.interslot_updates(), std::option::Option::Some(true));
|
||||
let slots_wire = slots.to_wire();
|
||||
assert_eq!(slots_wire.filter_by_commitment, std::option::Option::Some(false));
|
||||
assert_eq!(slots_wire.interslot_updates, std::option::Option::Some(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn yellowstone_account_filter_bounds_and_debug_are_provider_neutral() {
|
||||
assert!(crate::YellowstoneCuckooFilter::new(vec![], 0, 4, 8, 0, crate::YellowstoneCuckooHashAlgorithm::SipHash).is_err());
|
||||
assert!(crate::YellowstoneCuckooFilter::new(vec![], 1, 0, 8, 0, crate::YellowstoneCuckooHashAlgorithm::SipHash).is_err());
|
||||
assert!(crate::YellowstoneCuckooFilter::new(vec![], 1, 4, 7, 0, crate::YellowstoneCuckooHashAlgorithm::SipHash).is_err());
|
||||
assert!(crate::YellowstoneCuckooFilter::new(vec![], 1, 4, 12, 0, crate::YellowstoneCuckooHashAlgorithm::SipHash).is_ok());
|
||||
assert!(crate::YellowstoneAccountMemcmp::base58(0, "contains whitespace").is_err());
|
||||
assert!(crate::YellowstoneAccountMemcmp::base64(0, "line\nbreak").is_err());
|
||||
let memcmp = crate::YellowstoneAccountMemcmp::bytes(5, vec![7_u8, 8, 9]).expect("memcmp must validate");
|
||||
let debug = format!("{memcmp:?}");
|
||||
assert!(debug.contains("payload_length"));
|
||||
assert!(!debug.contains("7, 8, 9"));
|
||||
let mut filter = crate::YellowstoneSubscribeAccountFilter::new();
|
||||
assert!(filter.push_account(ksp_core_lib::Pubkey::new_from_array([3_u8; 32])).is_ok());
|
||||
assert!(filter.push_filter(crate::YellowstoneAccountFilterPredicate::Memcmp(memcmp)).is_ok());
|
||||
let filter_debug = format!("{filter:?}");
|
||||
assert!(filter_debug.contains("account_count"));
|
||||
assert!(!filter_debug.contains(&ksp_core_lib::Pubkey::new_from_array([3_u8; 32]).to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn yellowstone_account_update_decodes_complete_wire_and_redacts_payload_debug() {
|
||||
let signature = vec![6_u8; 64];
|
||||
let wire = yellowstone_grpc_proto::geyser::SubscribeUpdate {
|
||||
filters: vec!["accounts-main".to_owned(), "accounts-owner".to_owned()],
|
||||
update_oneof: std::option::Option::Some(yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof::Account(
|
||||
yellowstone_grpc_proto::geyser::SubscribeUpdateAccount {
|
||||
account: std::option::Option::Some(yellowstone_grpc_proto::geyser::SubscribeUpdateAccountInfo {
|
||||
pubkey: vec![1_u8; 32],
|
||||
lamports: 42,
|
||||
owner: vec![2_u8; 32],
|
||||
executable: true,
|
||||
rent_epoch: 9,
|
||||
data: vec![0xAA_u8, 0xBB, 0xCC],
|
||||
write_version: 7,
|
||||
txn_signature: std::option::Option::Some(signature),
|
||||
}),
|
||||
slot: 123,
|
||||
is_startup: true,
|
||||
},
|
||||
)),
|
||||
created_at: std::option::Option::Some(yellowstone_grpc_proto::prost_types::Timestamp { seconds: 1_700_000_000, nanos: 123_456_789 }),
|
||||
};
|
||||
let update = super::decode_account_update(wire).expect("account update fixture must decode");
|
||||
assert_eq!(update.filters()[0].as_str(), "accounts-main");
|
||||
assert_eq!(update.filters()[1].as_str(), "accounts-owner");
|
||||
assert_eq!(update.created_at().expect("timestamp must be present").seconds(), 1_700_000_000);
|
||||
assert_eq!(update.created_at().expect("timestamp must be present").nanos(), 123_456_789);
|
||||
assert_eq!(update.slot(), 123);
|
||||
assert!(update.is_startup());
|
||||
assert_eq!(update.account().pubkey(), &ksp_core_lib::Pubkey::new_from_array([1_u8; 32]));
|
||||
assert_eq!(update.account().owner(), &ksp_core_lib::Pubkey::new_from_array([2_u8; 32]));
|
||||
assert_eq!(update.account().lamports(), 42);
|
||||
assert!(update.account().executable());
|
||||
assert_eq!(update.account().rent_epoch(), 9);
|
||||
assert_eq!(update.account().data(), &[0xAA_u8, 0xBB, 0xCC]);
|
||||
assert_eq!(update.account().write_version(), 7);
|
||||
assert_eq!(update.account().transaction_signature().expect("signature must be present").as_bytes(), &[6_u8; 64]);
|
||||
let debug = format!("{update:?}");
|
||||
assert!(!debug.contains("accounts-main"));
|
||||
assert!(!debug.contains("170, 187, 204"));
|
||||
assert!(!debug.contains(&ksp_core_lib::Pubkey::new_from_array([1_u8; 32]).to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn yellowstone_account_update_rejects_malformed_fixed_width_and_envelope_fields() {
|
||||
let malformed_pubkey = yellowstone_grpc_proto::geyser::SubscribeUpdate {
|
||||
filters: vec!["accounts".to_owned()],
|
||||
update_oneof: std::option::Option::Some(yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof::Account(
|
||||
yellowstone_grpc_proto::geyser::SubscribeUpdateAccount {
|
||||
account: std::option::Option::Some(yellowstone_grpc_proto::geyser::SubscribeUpdateAccountInfo {
|
||||
pubkey: vec![1_u8; 31],
|
||||
lamports: 0,
|
||||
owner: vec![2_u8; 32],
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
data: vec![],
|
||||
write_version: 0,
|
||||
txn_signature: std::option::Option::None,
|
||||
}),
|
||||
slot: 0,
|
||||
is_startup: false,
|
||||
},
|
||||
)),
|
||||
created_at: std::option::Option::None,
|
||||
};
|
||||
assert!(super::decode_account_update(malformed_pubkey).is_err());
|
||||
let malformed_signature = yellowstone_grpc_proto::geyser::SubscribeUpdate {
|
||||
filters: vec!["accounts".to_owned()],
|
||||
update_oneof: std::option::Option::Some(yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof::Account(
|
||||
yellowstone_grpc_proto::geyser::SubscribeUpdateAccount {
|
||||
account: std::option::Option::Some(yellowstone_grpc_proto::geyser::SubscribeUpdateAccountInfo {
|
||||
pubkey: vec![1_u8; 32],
|
||||
lamports: 0,
|
||||
owner: vec![2_u8; 32],
|
||||
executable: false,
|
||||
rent_epoch: 0,
|
||||
data: vec![],
|
||||
write_version: 0,
|
||||
txn_signature: std::option::Option::Some(vec![9_u8; 63]),
|
||||
}),
|
||||
slot: 0,
|
||||
is_startup: false,
|
||||
},
|
||||
)),
|
||||
created_at: std::option::Option::None,
|
||||
};
|
||||
assert!(super::decode_account_update(malformed_signature).is_err());
|
||||
let missing_info = yellowstone_grpc_proto::geyser::SubscribeUpdate {
|
||||
filters: vec!["accounts".to_owned()],
|
||||
update_oneof: std::option::Option::Some(yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof::Account(
|
||||
yellowstone_grpc_proto::geyser::SubscribeUpdateAccount { account: std::option::Option::None, slot: 0, is_startup: false },
|
||||
)),
|
||||
created_at: std::option::Option::None,
|
||||
};
|
||||
assert!(super::decode_account_update(missing_info).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn yellowstone_slot_update_preserves_all_current_statuses_and_bounds_dead_error() {
|
||||
let statuses = [
|
||||
(yellowstone_grpc_proto::geyser::SlotStatus::SlotProcessed, crate::YellowstoneSlotStatus::Processed),
|
||||
(yellowstone_grpc_proto::geyser::SlotStatus::SlotConfirmed, crate::YellowstoneSlotStatus::Confirmed),
|
||||
(yellowstone_grpc_proto::geyser::SlotStatus::SlotFinalized, crate::YellowstoneSlotStatus::Finalized),
|
||||
(yellowstone_grpc_proto::geyser::SlotStatus::SlotFirstShredReceived, crate::YellowstoneSlotStatus::FirstShredReceived),
|
||||
(yellowstone_grpc_proto::geyser::SlotStatus::SlotCompleted, crate::YellowstoneSlotStatus::Completed),
|
||||
(yellowstone_grpc_proto::geyser::SlotStatus::SlotCreatedBank, crate::YellowstoneSlotStatus::CreatedBank),
|
||||
(yellowstone_grpc_proto::geyser::SlotStatus::SlotDead, crate::YellowstoneSlotStatus::Dead),
|
||||
];
|
||||
for (wire_status, expected) in statuses {
|
||||
let wire = yellowstone_grpc_proto::geyser::SubscribeUpdate {
|
||||
filters: vec!["slots".to_owned()],
|
||||
update_oneof: std::option::Option::Some(yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof::Slot(
|
||||
yellowstone_grpc_proto::geyser::SubscribeUpdateSlot {
|
||||
slot: 88,
|
||||
parent: std::option::Option::Some(87),
|
||||
status: wire_status as i32,
|
||||
dead_error: if expected == crate::YellowstoneSlotStatus::Dead {
|
||||
std::option::Option::Some("fork rejected".to_owned())
|
||||
} else {
|
||||
std::option::Option::None
|
||||
},
|
||||
},
|
||||
)),
|
||||
created_at: std::option::Option::Some(yellowstone_grpc_proto::prost_types::Timestamp { seconds: 5, nanos: 6 }),
|
||||
};
|
||||
let update = super::decode_slot_update(wire).expect("slot update fixture must decode");
|
||||
assert_eq!(update.status(), expected);
|
||||
assert_eq!(update.slot(), 88);
|
||||
assert_eq!(update.parent(), std::option::Option::Some(87));
|
||||
assert_eq!(update.filters()[0].as_str(), "slots");
|
||||
if expected == crate::YellowstoneSlotStatus::Dead {
|
||||
assert_eq!(update.dead_error(), std::option::Option::Some("fork rejected"));
|
||||
assert!(!format!("{update:?}").contains("fork rejected"));
|
||||
}
|
||||
}
|
||||
let unknown = yellowstone_grpc_proto::geyser::SubscribeUpdate {
|
||||
filters: vec!["slots".to_owned()],
|
||||
update_oneof: std::option::Option::Some(yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof::Slot(
|
||||
yellowstone_grpc_proto::geyser::SubscribeUpdateSlot {
|
||||
slot: 1,
|
||||
parent: std::option::Option::None,
|
||||
status: 99,
|
||||
dead_error: std::option::Option::None,
|
||||
},
|
||||
)),
|
||||
created_at: std::option::Option::None,
|
||||
};
|
||||
assert!(super::decode_slot_update(unknown).is_err());
|
||||
let oversized = yellowstone_grpc_proto::geyser::SubscribeUpdate {
|
||||
filters: vec!["slots".to_owned()],
|
||||
update_oneof: std::option::Option::Some(yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof::Slot(
|
||||
yellowstone_grpc_proto::geyser::SubscribeUpdateSlot {
|
||||
slot: 1,
|
||||
parent: std::option::Option::None,
|
||||
status: yellowstone_grpc_proto::geyser::SlotStatus::SlotDead as i32,
|
||||
dead_error: std::option::Option::Some("x".repeat(16 * 1024 + 1)),
|
||||
},
|
||||
)),
|
||||
created_at: std::option::Option::None,
|
||||
};
|
||||
assert!(super::decode_slot_update(oversized).is_err());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user