Files
khadhroony-solana-project/crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs
2026-09-09 20:42:32 +02:00

1415 lines
66 KiB
Rust

// file: crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs
// version: 9
fn grpc_endpoint(cluster: &str) -> std::option::Option<ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings> {
return grpc_endpoint_with_identity(cluster, "yellowstone-fixture", "fixture-provider");
}
fn grpc_endpoint_with_identity(
cluster: &str,
endpoint_name: &str,
provider: &str,
) -> std::option::Option<ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings> {
let url = match ksp_onchain_transport_lib::YellowstoneGrpcEndpointUrl::parse("http://127.0.0.1:10000/GRPC-SECRET-CANARY") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
return std::option::Option::Some(ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings::new(
endpoint_name,
true,
ksp_onchain_transport_lib::YellowstoneGrpcProviderName::new(provider),
ksp_onchain_transport_lib::YellowstoneGrpcClusterName::new(cluster),
url,
ksp_onchain_transport_lib::YellowstoneGrpcSessionSettings::default(),
));
}
fn http_pool(cluster: &str, role_name: &str, request_kind: &str) -> std::option::Option<ksp_onchain_transport_lib::HttpTransportPool> {
let url = match ksp_onchain_transport_lib::HttpEndpointUrl::parse("https://fixture.invalid/rpc?token=HTTP-SECRET-CANARY") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
let role = ksp_onchain_transport_lib::HttpEndpointRoleSettings::new(
ksp_onchain_transport_lib::HttpRoleName::new(role_name),
true,
std::vec![ksp_onchain_transport_lib::HttpRequestKind::new(request_kind)],
10,
ksp_onchain_transport_lib::HttpRoleLimits::new(
std::option::Option::None,
std::option::Option::None,
std::option::Option::None,
std::option::Option::None,
),
);
let endpoint = ksp_onchain_transport_lib::HttpEndpointSettings::new(
"http-fixture",
true,
ksp_onchain_transport_lib::HttpProviderName::new("fixture-provider"),
ksp_onchain_transport_lib::HttpClusterName::new(cluster),
url,
std::time::Duration::from_secs(1),
std::time::Duration::from_secs(2),
std::option::Option::Some(4),
std::vec![role],
);
let settings = ksp_onchain_transport_lib::HttpTransportSettings::new(
std::vec![endpoint],
ksp_onchain_transport_lib::HttpRetrySettings::new(1, std::time::Duration::from_millis(10), std::time::Duration::from_millis(20)),
);
return match ksp_onchain_transport_lib::HttpTransportPool::new(settings) {
std::result::Result::Ok(value) => std::option::Option::Some(value),
std::result::Result::Err(_) => std::option::Option::None,
};
}
fn transaction_request(
commitment: std::option::Option<ksp_onchain_transport_lib::SolanaCommitment>,
) -> std::option::Option<ksp_onchain_transport_lib::YellowstoneSubscribeRequest> {
let name = match ksp_onchain_transport_lib::YellowstoneSubscribeFilterName::new("tx-fixture") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
let mut request = ksp_onchain_transport_lib::YellowstoneSubscribeRequest::new();
if request.insert_transaction_filter(name, ksp_onchain_transport_lib::YellowstoneSubscribeTransactionFilter::new()).is_err() {
return std::option::Option::None;
}
request.set_commitment(commitment);
return std::option::Option::Some(request);
}
struct SignalViewFixture {
created_at: std::option::Option<ksp_onchain_transport_lib::YellowstoneUpdateTimestamp>,
family: super::RawTransactionIngestSourceFamily,
filters: std::vec::Vec<ksp_onchain_transport_lib::YellowstoneSubscribeFilterName>,
index: u64,
signature: ksp_onchain_transport_lib::YellowstoneTransactionSignature,
slot: u64,
}
impl super::RawTransactionIngestYellowstoneSignalView for SignalViewFixture {
fn created_at(&self) -> std::option::Option<ksp_onchain_transport_lib::YellowstoneUpdateTimestamp> {
return self.created_at;
}
fn family(&self) -> super::RawTransactionIngestSourceFamily {
return self.family;
}
fn filters(&self) -> &[ksp_onchain_transport_lib::YellowstoneSubscribeFilterName] {
return self.filters.as_slice();
}
fn index(&self) -> u64 {
return self.index;
}
fn signature(&self) -> ksp_onchain_transport_lib::YellowstoneTransactionSignature {
return self.signature;
}
fn slot(&self) -> u64 {
return self.slot;
}
}
struct BlockViewFixture {
created_at: std::option::Option<ksp_onchain_transport_lib::YellowstoneUpdateTimestamp>,
filters: std::vec::Vec<ksp_onchain_transport_lib::YellowstoneSubscribeFilterName>,
slot: u64,
transactions: std::vec::Vec<(ksp_onchain_transport_lib::YellowstoneTransactionSignature, u64)>,
}
impl super::RawTransactionIngestYellowstoneBlockView for BlockViewFixture {
fn created_at(&self) -> std::option::Option<ksp_onchain_transport_lib::YellowstoneUpdateTimestamp> {
return self.created_at;
}
fn filters(&self) -> &[ksp_onchain_transport_lib::YellowstoneSubscribeFilterName] {
return self.filters.as_slice();
}
fn slot(&self) -> u64 {
return self.slot;
}
fn transaction_count(&self) -> usize {
return self.transactions.len();
}
fn transaction_identity(&self, position: usize) -> ksp_core_lib::Result<(ksp_onchain_transport_lib::YellowstoneTransactionSignature, u64)> {
return match self.transactions.get(position) {
std::option::Option::Some(value) => std::result::Result::Ok(*value),
std::option::Option::None => std::result::Result::Err(crate::runtime_error("fixture.block_transaction_missing")),
};
}
}
struct ContinuityViewFixture {
created_at: std::option::Option<ksp_onchain_transport_lib::YellowstoneUpdateTimestamp>,
family: super::RawTransactionIngestContinuityFamily,
filters: std::vec::Vec<ksp_onchain_transport_lib::YellowstoneSubscribeFilterName>,
parent_slot: std::option::Option<u64>,
slot: u64,
status: std::option::Option<super::RawTransactionIngestContinuityStatus>,
}
impl super::RawTransactionIngestYellowstoneContinuityView for ContinuityViewFixture {
fn created_at(&self) -> std::option::Option<ksp_onchain_transport_lib::YellowstoneUpdateTimestamp> {
return self.created_at;
}
fn family(&self) -> super::RawTransactionIngestContinuityFamily {
return self.family;
}
fn filters(&self) -> &[ksp_onchain_transport_lib::YellowstoneSubscribeFilterName] {
return self.filters.as_slice();
}
fn parent_slot(&self) -> std::option::Option<u64> {
return self.parent_slot;
}
fn slot(&self) -> u64 {
return self.slot;
}
fn status(&self) -> std::option::Option<super::RawTransactionIngestContinuityStatus> {
return self.status;
}
}
fn filter_names(values: &[&str]) -> std::option::Option<std::vec::Vec<ksp_onchain_transport_lib::YellowstoneSubscribeFilterName>> {
let mut filters = std::vec::Vec::with_capacity(values.len());
for value in values {
let filter = match ksp_onchain_transport_lib::YellowstoneSubscribeFilterName::new(*value) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
filters.push(filter);
}
return std::option::Option::Some(filters);
}
fn signal_source() -> std::option::Option<crate::RawTransactionIngestYellowstoneSource> {
let endpoint = match grpc_endpoint("devnet") {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::option::Option::None,
};
let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::prepare(&endpoint) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
let request = match transaction_request(std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed)) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::option::Option::None,
};
let pool = match http_pool("devnet", "hydration", "get_transaction") {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::option::Option::None,
};
return match crate::RawTransactionIngestYellowstoneSource::new(channel, request, pool, ksp_onchain_transport_lib::HttpRoleName::new("hydration")) {
std::result::Result::Ok(value) => std::option::Option::Some(value),
std::result::Result::Err(_) => std::option::Option::None,
};
}
const PRE_004_OTHER_TRANSACTION_BASE64: &str = "AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEA";
const PRE_004_ZERO_SIGNATURE_TEXT: &str = "1111111111111111111111111111111111111111111111111111111111111111";
const PRE_004_ZERO_TRANSACTION_BASE64: &str = "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
fn http_pool_for_url(url: &str, cluster: &str, endpoint_name: &str, provider: &str) -> std::option::Option<ksp_onchain_transport_lib::HttpTransportPool> {
let url = match ksp_onchain_transport_lib::HttpEndpointUrl::parse(url) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
let role = ksp_onchain_transport_lib::HttpEndpointRoleSettings::new(
ksp_onchain_transport_lib::HttpRoleName::new("hydration"),
true,
std::vec![ksp_onchain_transport_lib::HttpRequestKind::new("get_transaction")],
10,
ksp_onchain_transport_lib::HttpRoleLimits::new(
std::option::Option::None,
std::option::Option::None,
std::option::Option::None,
std::option::Option::None,
),
);
let endpoint = ksp_onchain_transport_lib::HttpEndpointSettings::new(
endpoint_name,
true,
ksp_onchain_transport_lib::HttpProviderName::new(provider),
ksp_onchain_transport_lib::HttpClusterName::new(cluster),
url,
std::time::Duration::from_secs(1),
std::time::Duration::from_secs(1),
std::option::Option::Some(1),
std::vec![role],
);
let settings = ksp_onchain_transport_lib::HttpTransportSettings::new(
std::vec![endpoint],
ksp_onchain_transport_lib::HttpRetrySettings::new(0, std::time::Duration::from_millis(1), std::time::Duration::from_millis(1)),
);
return match ksp_onchain_transport_lib::HttpTransportPool::new(settings) {
std::result::Result::Ok(value) => std::option::Option::Some(value),
std::result::Result::Err(_) => std::option::Option::None,
};
}
fn signal_source_for_http_url(url: &str) -> std::option::Option<crate::RawTransactionIngestYellowstoneSource> {
return signal_source_for_http_url_with_commitment(url, ksp_onchain_transport_lib::SolanaCommitment::Confirmed);
}
fn signal_source_for_http_url_with_commitment(
url: &str,
commitment: ksp_onchain_transport_lib::SolanaCommitment,
) -> std::option::Option<crate::RawTransactionIngestYellowstoneSource> {
let endpoint = match grpc_endpoint("devnet") {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::option::Option::None,
};
let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::prepare(&endpoint) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
let request = match transaction_request(std::option::Option::Some(commitment)) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::option::Option::None,
};
let pool = match http_pool_for_url(url, "devnet", "http-hydration-fixture", "fixture-http-provider") {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::option::Option::None,
};
return match crate::RawTransactionIngestYellowstoneSource::new(channel, request, pool, ksp_onchain_transport_lib::HttpRoleName::new("hydration")) {
std::result::Result::Ok(value) => std::option::Option::Some(value),
std::result::Result::Err(_) => std::option::Option::None,
};
}
fn pre_004_settings() -> std::option::Option<crate::RawTransactionIngestSettings> {
let network = match ksp_store_lib::RawNetworkId::new("devnet") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
let worker_id = match ksp_worker_api::WorkerId::new("worker-fixture") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
return std::option::Option::Some(crate::RawTransactionIngestSettings::with_defaults(network, worker_id));
}
fn pre_004_signal(
source: &crate::RawTransactionIngestYellowstoneSource,
family: super::RawTransactionIngestSourceFamily,
slot: u64,
index: u64,
filters: &[&str],
signature_byte: u8,
) -> std::option::Option<super::RawTransactionIngestSourceSignal> {
let filters = match filter_names(filters) {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::option::Option::None,
};
let created_at = match ksp_onchain_transport_lib::YellowstoneUpdateTimestamp::new(1_760_000_123, 456_000_000) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
let fixture = SignalViewFixture {
created_at: std::option::Option::Some(created_at),
family,
filters,
index,
signature: ksp_onchain_transport_lib::YellowstoneTransactionSignature::new([signature_byte; 64]),
slot,
};
return std::option::Option::Some(super::project_yellowstone_signal(source, &fixture));
}
fn serve_http_once(body: std::string::String) -> std::io::Result<(std::string::String, std::thread::JoinHandle<std::io::Result<std::string::String>>)> {
let listener = match std::net::TcpListener::bind("127.0.0.1:0") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let address = match listener.local_addr() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let handle = std::thread::spawn(move || {
let (mut stream, _) = match listener.accept() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let request = match read_http_request(&mut stream) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let response =
std::format!("HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}", body.len(), body,);
if let std::result::Result::Err(error) = std::io::Write::write_all(&mut stream, response.as_bytes()) {
return std::result::Result::Err(error);
}
return std::result::Result::Ok(request);
});
return std::result::Result::Ok((std::format!("http://{address}"), handle));
}
fn read_http_request(stream: &mut std::net::TcpStream) -> std::io::Result<std::string::String> {
let mut bytes = std::vec::Vec::new();
let mut buffer = [0_u8; 1024];
loop {
let count = match std::io::Read::read(stream, &mut buffer) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if count == 0 {
break;
}
bytes.extend_from_slice(&buffer[..count]);
if http_request_complete(bytes.as_slice()) {
break;
}
}
return match std::string::String::from_utf8(bytes) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(_) => std::result::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "fixture HTTP request is not UTF-8")),
};
}
fn http_request_complete(bytes: &[u8]) -> bool {
let text = match std::str::from_utf8(bytes) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return false,
};
let header_end = match text.find("\r\n\r\n") {
std::option::Option::Some(value) => value,
std::option::Option::None => return false,
};
let mut content_length = 0_usize;
for line in text[..header_end].lines() {
let (name, value) = match line.split_once(':') {
std::option::Option::Some(value) => value,
std::option::Option::None => continue,
};
if name.eq_ignore_ascii_case("content-length") {
content_length = match value.trim().parse::<usize>() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return false,
};
}
}
return bytes.len() >= header_end.saturating_add(4).saturating_add(content_length);
}
fn http_request_body(request: &str) -> &str {
return match request.split_once("\r\n\r\n") {
std::option::Option::Some((_, body)) => body,
std::option::Option::None => "",
};
}
#[tokio::test(flavor = "current_thread")]
async fn pre_002_source_accepts_matching_confirmed_transaction_and_get_transaction_route_without_io() {
let endpoint = match grpc_endpoint("devnet") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::prepare(&endpoint) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let request = match transaction_request(std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed)) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let pool = match http_pool("devnet", "hydration", "get_transaction") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let source = crate::RawTransactionIngestYellowstoneSource::new(channel, request, pool, ksp_onchain_transport_lib::HttpRoleName::new("hydration"));
assert!(source.is_ok());
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_002_source_rejects_processed_and_implicit_commitment() {
for commitment in [std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Processed), std::option::Option::None] {
let endpoint = match grpc_endpoint("devnet") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::prepare(&endpoint) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let request = match transaction_request(commitment) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let pool = match http_pool("devnet", "hydration", "get_transaction") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let source = crate::RawTransactionIngestYellowstoneSource::new(channel, request, pool, ksp_onchain_transport_lib::HttpRoleName::new("hydration"));
let error = match source {
std::result::Result::Ok(_) => return,
std::result::Result::Err(error) => error,
};
assert_eq!(error.code(), crate::ERROR_CODE_RAW_TRANSACTION_INGEST_RUNTIME_INVALID);
assert!(error.context().iter().any(|context| return context.value() == "runtime_resources.hydration_commitment_invalid"));
}
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_002_source_requires_ingestion_bearing_filter_family() {
let endpoint = match grpc_endpoint("devnet") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::prepare(&endpoint) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let mut request = ksp_onchain_transport_lib::YellowstoneSubscribeRequest::new();
request.set_commitment(std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Finalized));
let pool = match http_pool("devnet", "hydration", "get_transaction") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let source = crate::RawTransactionIngestYellowstoneSource::new(channel, request, pool, ksp_onchain_transport_lib::HttpRoleName::new("hydration"));
let error = match source {
std::result::Result::Ok(_) => return,
std::result::Result::Err(error) => error,
};
assert_eq!(error.code(), crate::ERROR_CODE_RAW_TRANSACTION_INGEST_RUNTIME_INVALID);
assert!(error.context().iter().any(|context| return context.value() == "runtime_resources.ingestion_filter_missing"));
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_002_source_rejects_missing_hydration_capability_and_cross_transport_network() {
for (http_cluster, request_kind, expected_condition) in [
("devnet", "get_balance", "runtime_resources.hydration_role_unsupported"),
("mainnet", "get_transaction", "runtime_resources.transport_network_mismatch"),
] {
let endpoint = match grpc_endpoint("devnet") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::prepare(&endpoint) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let request = match transaction_request(std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed)) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let pool = match http_pool(http_cluster, "hydration", request_kind) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let source = crate::RawTransactionIngestYellowstoneSource::new(channel, request, pool, ksp_onchain_transport_lib::HttpRoleName::new("hydration"));
let error = match source {
std::result::Result::Ok(_) => return,
std::result::Result::Err(error) => error,
};
assert_eq!(error.code(), crate::ERROR_CODE_RAW_TRANSACTION_INGEST_RUNTIME_INVALID);
assert!(error.context().iter().any(|context| return context.value() == expected_condition));
}
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_002_runtime_resource_debug_is_safe_and_exposes_no_client_inner() {
let endpoint = match grpc_endpoint("devnet") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::prepare(&endpoint) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let request = match transaction_request(std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed)) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let pool = match http_pool("devnet", "hydration", "get_transaction") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let source = match crate::RawTransactionIngestYellowstoneSource::new(channel, request, pool, ksp_onchain_transport_lib::HttpRoleName::new("hydration")) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let resources = crate::RawTransactionIngestRuntimeResources::new(source);
let debug = std::format!("{resources:?}");
assert!(debug.contains("RawTransactionIngestRuntimeResources"));
assert!(debug.contains("devnet"));
assert!(debug.contains("blocks_meta_filter_count"));
assert!(debug.contains("slot_filter_count"));
assert!(!debug.contains("GRPC-SECRET-CANARY"));
assert!(!debug.contains("HTTP-SECRET-CANARY"));
assert!(!debug.contains("127.0.0.1"));
assert!(!debug.contains("fixture.invalid"));
assert!(!debug.contains("tx-fixture"));
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_002_runtime_resources_reject_worker_network_mismatch_without_starting_source() {
let endpoint = match grpc_endpoint("devnet") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::prepare(&endpoint) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let request = match transaction_request(std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed)) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let pool = match http_pool("devnet", "hydration", "get_transaction") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let source = match crate::RawTransactionIngestYellowstoneSource::new(channel, request, pool, ksp_onchain_transport_lib::HttpRoleName::new("hydration")) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let resources = crate::RawTransactionIngestRuntimeResources::new(source);
let network = match ksp_store_lib::RawNetworkId::new("mainnet") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let error = match resources.validate_network(&network) {
std::result::Result::Ok(()) => return,
std::result::Result::Err(error) => error,
};
assert_eq!(error.code(), crate::ERROR_CODE_RAW_TRANSACTION_INGEST_RUNTIME_INVALID);
assert!(error.context().iter().any(|context| return context.value() == "runtime_resources.worker_network_mismatch"));
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_003_transaction_signal_fixture_preserves_exact_source_neutral_identity() {
let source = match signal_source() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let filters = match filter_names(&["filter-b", "filter-a", "filter-a"]) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let created_at = match ksp_onchain_transport_lib::YellowstoneUpdateTimestamp::new(1_760_000_123, 456_000_000) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let fixture = SignalViewFixture {
created_at: std::option::Option::Some(created_at),
family: super::RawTransactionIngestSourceFamily::Transaction,
filters,
index: 7,
signature: ksp_onchain_transport_lib::YellowstoneTransactionSignature::new([0xAB; 64]),
slot: 42,
};
let signal = super::project_yellowstone_signal(&source, &fixture);
assert_eq!(signal.family, super::RawTransactionIngestSourceFamily::Transaction);
assert_eq!(signal.network.as_str(), "devnet");
assert_eq!(signal.signature.as_bytes(), &[0xAB; 64]);
assert_eq!(signal.slot, 42);
assert_eq!(signal.transaction_index, std::option::Option::Some(7));
assert_eq!(signal.route.provider.as_str(), "fixture-provider");
assert_eq!(signal.route.endpoint_id.as_str(), "yellowstone-fixture");
assert_eq!(signal.matched_filter_count, 3);
assert_eq!(
signal.matched_filter_fingerprint,
[
0x20, 0x83, 0x1d, 0xc8, 0x37, 0x8b, 0x60, 0x70, 0xf9, 0x1d, 0x9d, 0xe6, 0xd3, 0xa1, 0xd2, 0xb3, 0xd7, 0x72, 0x98, 0xe3, 0xad, 0xa7, 0x6c, 0x01,
0xfa, 0xa3, 0x08, 0x26, 0xff, 0xab, 0xcd, 0x84,
]
);
assert_eq!(signal.created_at, std::option::Option::Some(super::RawTransactionIngestSourceTimestamp { nanos: 456_000_000, seconds: 1_760_000_123 }));
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_003_transaction_status_signal_has_no_payload_or_remote_error_material() {
let source = match signal_source() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let filters = match filter_names(&["status-secret-canary"]) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let fixture = SignalViewFixture {
created_at: std::option::Option::None,
family: super::RawTransactionIngestSourceFamily::TransactionStatus,
filters,
index: 9,
signature: ksp_onchain_transport_lib::YellowstoneTransactionSignature::new([0xCD; 64]),
slot: 84,
};
let signal = super::project_yellowstone_signal(&source, &fixture);
assert_eq!(signal.family, super::RawTransactionIngestSourceFamily::TransactionStatus);
assert_eq!(signal.signature.as_bytes(), &[0xCD; 64]);
assert_eq!(signal.slot, 84);
assert_eq!(signal.transaction_index, std::option::Option::Some(9));
assert_eq!(signal.created_at, std::option::Option::None);
let debug = std::format!("{signal:?}");
assert!(!debug.contains("status-secret-canary"));
assert!(debug.contains("signature_bytes"));
assert!(!debug.contains("YellowstoneTransactionError"));
assert!(!debug.contains("transaction_body"));
assert!(!debug.contains("transaction_meta"));
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_003_filter_fingerprint_is_order_and_duplicate_insensitive() {
let source = match signal_source() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let first_filters = match filter_names(&["filter-b", "filter-a", "filter-a"]) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let second_filters = match filter_names(&["filter-a", "filter-b"]) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let first = SignalViewFixture {
created_at: std::option::Option::None,
family: super::RawTransactionIngestSourceFamily::Transaction,
filters: first_filters,
index: 1,
signature: ksp_onchain_transport_lib::YellowstoneTransactionSignature::new([1; 64]),
slot: 1,
};
let second = SignalViewFixture {
created_at: std::option::Option::None,
family: super::RawTransactionIngestSourceFamily::Transaction,
filters: second_filters,
index: 1,
signature: ksp_onchain_transport_lib::YellowstoneTransactionSignature::new([1; 64]),
slot: 1,
};
let first_signal = super::project_yellowstone_signal(&source, &first);
let second_signal = super::project_yellowstone_signal(&source, &second);
assert_eq!(first_signal.matched_filter_fingerprint, second_signal.matched_filter_fingerprint);
assert_eq!(first_signal.matched_filter_count, 3);
assert_eq!(second_signal.matched_filter_count, 2);
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_003_source_rejects_route_identity_that_cannot_become_safe_provenance() {
for (endpoint_name, provider, expected_condition) in [
("yellowstone-fixture", "fixture/provider", "runtime_resources.yellowstone_provider_unrepresentable"),
("yellowstone/fixture", "fixture-provider", "runtime_resources.yellowstone_endpoint_unrepresentable"),
] {
let endpoint = match grpc_endpoint_with_identity("devnet", endpoint_name, provider) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::prepare(&endpoint) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let request = match transaction_request(std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed)) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let pool = match http_pool("devnet", "hydration", "get_transaction") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let result = crate::RawTransactionIngestYellowstoneSource::new(channel, request, pool, ksp_onchain_transport_lib::HttpRoleName::new("hydration"));
let error = match result {
std::result::Result::Ok(_) => return,
std::result::Result::Err(error) => error,
};
assert_eq!(error.code(), crate::ERROR_CODE_RAW_TRANSACTION_INGEST_RUNTIME_INVALID);
assert!(error.context().iter().any(|context| return context.value() == expected_condition));
}
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_005_block_projects_transaction_signals_in_exact_source_order() {
let source = match signal_source() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let filters = match filter_names(&["block-filter-b", "block-filter-a"]) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let created_at = match ksp_onchain_transport_lib::YellowstoneUpdateTimestamp::new(1_760_000_321, 123_000_000) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let fixture = BlockViewFixture {
created_at: std::option::Option::Some(created_at),
filters,
slot: 777,
transactions: std::vec![
(ksp_onchain_transport_lib::YellowstoneTransactionSignature::new([0x11; 64]), 9),
(ksp_onchain_transport_lib::YellowstoneTransactionSignature::new([0x22; 64]), 3),
(ksp_onchain_transport_lib::YellowstoneTransactionSignature::new([0x33; 64]), 17),
],
};
let signals = match super::project_yellowstone_block_signals(&source, &fixture) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(signals.len(), 3);
assert_eq!(signals[0].signature.as_bytes(), &[0x11; 64]);
assert_eq!(signals[1].signature.as_bytes(), &[0x22; 64]);
assert_eq!(signals[2].signature.as_bytes(), &[0x33; 64]);
assert_eq!(signals[0].transaction_index, std::option::Option::Some(9));
assert_eq!(signals[1].transaction_index, std::option::Option::Some(3));
assert_eq!(signals[2].transaction_index, std::option::Option::Some(17));
for signal in &signals {
assert_eq!(signal.family, super::RawTransactionIngestSourceFamily::Block);
assert_eq!(signal.slot, 777);
assert_eq!(signal.network.as_str(), "devnet");
assert_eq!(signal.matched_filter_count, 2);
assert_eq!(signal.created_at, std::option::Option::Some(super::RawTransactionIngestSourceTimestamp { nanos: 123_000_000, seconds: 1_760_000_321 }));
}
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_005_block_hydration_uses_distinct_composite_method() {
let source = match signal_source() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let settings = match pre_004_settings() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let signal = match pre_004_signal(&source, super::RawTransactionIngestSourceFamily::Block, 42, 7, &["block-filter"], 0) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let received_at = match ksp_store_lib::RawTimestamp::from_unix_millis(1_760_000_124_000) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let provenance = match super::build_hydration_provenance(
&settings,
&signal,
"fixture-http-provider",
"http-hydration-fixture",
ksp_onchain_transport_lib::SolanaCommitment::Confirmed,
received_at,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(provenance.acquisition_method().as_str(), "block_get_transaction");
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_005_block_meta_and_slot_project_continuity_only_without_remote_dead_error() {
let source = match signal_source() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let block_filters = match filter_names(&["block-meta-filter"]) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let slot_filters = match filter_names(&["slot-filter-secret", "slot-filter-secret"]) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let block_meta = ContinuityViewFixture {
created_at: std::option::Option::None,
family: super::RawTransactionIngestContinuityFamily::BlockMeta,
filters: block_filters,
parent_slot: std::option::Option::Some(899),
slot: 900,
status: std::option::Option::None,
};
let slot = ContinuityViewFixture {
created_at: std::option::Option::None,
family: super::RawTransactionIngestContinuityFamily::Slot,
filters: slot_filters,
parent_slot: std::option::Option::Some(900),
slot: 901,
status: std::option::Option::Some(super::RawTransactionIngestContinuityStatus::Dead),
};
let block_meta_signal = super::project_yellowstone_continuity_signal(&source, &block_meta);
let slot_signal = super::project_yellowstone_continuity_signal(&source, &slot);
assert_eq!(block_meta_signal.family, super::RawTransactionIngestContinuityFamily::BlockMeta);
assert_eq!(block_meta_signal.slot, 900);
assert_eq!(block_meta_signal.parent_slot, std::option::Option::Some(899));
assert_eq!(block_meta_signal.status, std::option::Option::None);
assert_eq!(slot_signal.family, super::RawTransactionIngestContinuityFamily::Slot);
assert_eq!(slot_signal.slot, 901);
assert_eq!(slot_signal.parent_slot, std::option::Option::Some(900));
assert_eq!(slot_signal.status, std::option::Option::Some(super::RawTransactionIngestContinuityStatus::Dead));
assert_eq!(slot_signal.matched_filter_count, 2);
let debug = std::format!("{slot_signal:?}");
assert!(!debug.contains("slot-filter-secret"));
assert!(!debug.contains("dead_error"));
assert!(!debug.contains("transaction"));
assert!(!debug.contains("meta"));
assert!(!debug.contains("payload"));
return;
}
#[test]
fn pre_005_slot_status_projection_is_exact_and_source_neutral() {
for (source, expected) in [
(ksp_onchain_transport_lib::YellowstoneSlotStatus::Processed, super::RawTransactionIngestContinuityStatus::Processed),
(ksp_onchain_transport_lib::YellowstoneSlotStatus::Confirmed, super::RawTransactionIngestContinuityStatus::Confirmed),
(ksp_onchain_transport_lib::YellowstoneSlotStatus::Finalized, super::RawTransactionIngestContinuityStatus::Finalized),
(ksp_onchain_transport_lib::YellowstoneSlotStatus::FirstShredReceived, super::RawTransactionIngestContinuityStatus::FirstShredReceived),
(ksp_onchain_transport_lib::YellowstoneSlotStatus::Completed, super::RawTransactionIngestContinuityStatus::Completed),
(ksp_onchain_transport_lib::YellowstoneSlotStatus::CreatedBank, super::RawTransactionIngestContinuityStatus::CreatedBank),
(ksp_onchain_transport_lib::YellowstoneSlotStatus::Dead, super::RawTransactionIngestContinuityStatus::Dead),
] {
assert_eq!(super::map_yellowstone_slot_status(source), expected);
}
return;
}
#[test]
fn pre_004_composite_provenance_rejects_overflow_without_truncation() {
let provider = match ksp_store_lib::RawProvenanceCode::new("yellowstone-provider") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let endpoint_id = match ksp_store_lib::RawProvenanceCode::new("yellowstone-endpoint") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let route = super::RawTransactionIngestSourceRoute { endpoint_id, provider };
let result = super::composite_provenance_codes(&route, "h".repeat(120).as_str(), "http-endpoint");
let error = match result {
std::result::Result::Ok(_) => return,
std::result::Result::Err(error) => error,
};
assert_eq!(error.code(), crate::ERROR_CODE_RAW_TRANSACTION_INGEST_RUNTIME_INVALID);
assert!(error.context().iter().any(|context| return context.value() == "runtime_resources.composite_provider_unrepresentable"));
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_004_observed_get_transaction_closes_signal_to_common_raw_ingress_with_composite_provenance() {
let body = std::format!(
"{{\"jsonrpc\":\"2.0\",\"result\":{{\"slot\":42,\"blockTime\":1760000120,\"transaction\":[\"{}\",\"base64\"],\"meta\":{{\"err\":null,\"fee\":5000}},\"version\":\"legacy\",\"transactionIndex\":7}},\"id\":1}}",
PRE_004_ZERO_TRANSACTION_BASE64,
);
let (url, server) = match serve_http_once(body) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let source = match signal_source_for_http_url(url.as_str()) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let settings = match pre_004_settings() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let signal = match pre_004_signal(&source, super::RawTransactionIngestSourceFamily::Transaction, 42, 7, &["tx-fixture"], 0) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let received_at = match ksp_store_lib::RawTimestamp::from_unix_millis(1_760_000_124_000) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let outcome = super::hydrate_yellowstone_signal(&source, &settings, signal, received_at).await;
assert!(outcome.is_ok());
let outcome = match outcome {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let request = match server.join() {
std::result::Result::Ok(std::result::Result::Ok(value)) => value,
_ => return,
};
let request_body = http_request_body(request.as_str());
assert!(request_body.contains("\"method\":\"getTransaction\""));
assert!(request_body.contains(PRE_004_ZERO_SIGNATURE_TEXT));
assert!(request_body.contains("\"commitment\":\"confirmed\""));
assert!(request_body.contains("\"encoding\":\"base64\""));
assert!(request_body.contains("\"maxSupportedTransactionVersion\":0"));
let ingress = match outcome {
super::RawTransactionIngestHydrationOutcome::Available(value) => value,
super::RawTransactionIngestHydrationOutcome::Missing(_) => return,
};
assert_eq!(ingress.provenance.provider().as_str(), "ys.fixture-provider:http.fixture-http-provider");
assert_eq!(ingress.provenance.protocol().as_str(), "yellowstone_http");
assert_eq!(ingress.provenance.acquisition_method().as_str(), "transaction_get_transaction");
assert_eq!(ingress.provenance.origin(), ksp_store_lib::RawAcquisitionOrigin::Live);
assert_eq!(ingress.provenance.received_at(), received_at);
assert_eq!(
ingress.provenance.endpoint_id().map(ksp_store_lib::RawProvenanceCode::as_str),
std::option::Option::Some("ys.yellowstone-fixture:http.http-hydration-fixture"),
);
assert_eq!(ingress.provenance.commitment().map(ksp_store_lib::RawProvenanceCode::as_str), std::option::Option::Some("confirmed"));
assert_eq!(ingress.provenance.capture_session_id().map(ksp_store_lib::RawProvenanceCode::as_str), std::option::Option::Some("worker-fixture"));
assert_eq!(ingress.provenance.filter_id().map(ksp_store_lib::RawProvenanceCode::as_str), std::option::Option::Some("tx-fixture"));
assert_eq!(ingress.provenance.observed_at().map(|value| return value.unix_millis()), std::option::Option::Some(1_760_000_123_456));
let (mut admission, sender) = crate::RawTransactionAdmission::new(1);
if sender.send(*ingress).await.is_err() {
return;
}
std::mem::drop(sender);
let acquisition = match admission.receive(settings.network()).await {
std::result::Result::Ok(std::option::Option::Some(value)) => value,
_ => return,
};
assert_eq!(acquisition.transaction().reference().network().as_str(), "devnet");
assert_eq!(acquisition.transaction().reference().signature().as_bytes(), &[0_u8; 64]);
assert_eq!(acquisition.transaction().slot(), 42);
assert_eq!(acquisition.transaction().block_time().map(|value| return value.unix_millis()), std::option::Option::Some(1_760_000_120_000),);
assert_eq!(acquisition.observation().provenance().provider().as_str(), "ys.fixture-provider:http.fixture-http-provider");
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_004_finalized_hydration_preserves_request_and_provenance_commitment() {
let body = std::format!(
"{{\"jsonrpc\":\"2.0\",\"result\":{{\"slot\":42,\"blockTime\":1760000120,\"transaction\":[\"{}\",\"base64\"],\"meta\":null,\"version\":\"legacy\",\"transactionIndex\":7}},\"id\":1}}",
PRE_004_ZERO_TRANSACTION_BASE64,
);
let (url, server) = match serve_http_once(body) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let source = match signal_source_for_http_url_with_commitment(url.as_str(), ksp_onchain_transport_lib::SolanaCommitment::Finalized) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let settings = match pre_004_settings() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let signal = match pre_004_signal(&source, super::RawTransactionIngestSourceFamily::Transaction, 42, 7, &["tx-fixture"], 0) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let received_at = match ksp_store_lib::RawTimestamp::from_unix_millis(1_760_000_124_000) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let outcome = match super::hydrate_yellowstone_signal(&source, &settings, signal, received_at).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let request = match server.join() {
std::result::Result::Ok(std::result::Result::Ok(value)) => value,
_ => return,
};
let request_body = http_request_body(request.as_str());
assert!(request_body.contains("\"commitment\":\"finalized\""));
assert!(request_body.contains("\"encoding\":\"base64\""));
assert!(request_body.contains("\"maxSupportedTransactionVersion\":0"));
let ingress = match outcome {
super::RawTransactionIngestHydrationOutcome::Available(value) => value,
super::RawTransactionIngestHydrationOutcome::Missing(_) => return,
};
assert_eq!(ingress.provenance.commitment().map(ksp_store_lib::RawProvenanceCode::as_str), std::option::Option::Some("finalized"));
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_004_get_transaction_null_is_missing_without_ingress_or_worker_retry() {
let (url, server) = match serve_http_once("{\"jsonrpc\":\"2.0\",\"result\":null,\"id\":1}".to_owned()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let source = match signal_source_for_http_url(url.as_str()) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let settings = match pre_004_settings() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let signal = match pre_004_signal(&source, super::RawTransactionIngestSourceFamily::TransactionStatus, 42, 7, &["status-fixture"], 0) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let received_at = match ksp_store_lib::RawTimestamp::from_unix_millis(1_760_000_124_000) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let outcome = match super::hydrate_yellowstone_signal(&source, &settings, signal, received_at).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let request = match server.join() {
std::result::Result::Ok(std::result::Result::Ok(value)) => value,
_ => return,
};
assert!(http_request_body(request.as_str()).contains(PRE_004_ZERO_SIGNATURE_TEXT));
let reference = match outcome {
super::RawTransactionIngestHydrationOutcome::Available(_) => return,
super::RawTransactionIngestHydrationOutcome::Missing(value) => value,
};
assert_eq!(reference.network().as_str(), "devnet");
assert_eq!(reference.signature().as_bytes(), &[0_u8; 64]);
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_004_v0_null_and_omitted_wire_fields_preserve_common_raw_semantics() {
let body = std::format!(
"{{\"jsonrpc\":\"2.0\",\"result\":{{\"slot\":42,\"blockTime\":null,\"transaction\":[\"{}\",\"base64\"],\"meta\":null,\"version\":0}},\"id\":1}}",
PRE_004_ZERO_TRANSACTION_BASE64,
);
let (url, server) = match serve_http_once(body) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let source = match signal_source_for_http_url(url.as_str()) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let settings = match pre_004_settings() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let signal = match pre_004_signal(&source, super::RawTransactionIngestSourceFamily::Transaction, 42, 7, &["filter-a", "filter-b"], 0) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let received_at = match ksp_store_lib::RawTimestamp::from_unix_millis(1_760_000_124_000) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let outcome = match super::hydrate_yellowstone_signal(&source, &settings, signal, received_at).await {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let _request = match server.join() {
std::result::Result::Ok(std::result::Result::Ok(value)) => value,
_ => return,
};
let ingress = match outcome {
super::RawTransactionIngestHydrationOutcome::Available(value) => value,
super::RawTransactionIngestHydrationOutcome::Missing(_) => return,
};
let filter_id = match ingress.provenance.filter_id() {
std::option::Option::Some(value) => value.as_str(),
std::option::Option::None => return,
};
assert!(filter_id.starts_with("sha256."));
assert_eq!(filter_id.len(), 71);
let (mut admission, sender) = crate::RawTransactionAdmission::new(1);
if sender.send(*ingress).await.is_err() {
return;
}
std::mem::drop(sender);
let acquisition = match admission.receive(settings.network()).await {
std::result::Result::Ok(std::option::Option::Some(value)) => value,
_ => return,
};
assert_eq!(acquisition.transaction().block_time(), std::option::Option::None);
let payload = match std::str::from_utf8(acquisition.transaction().payload().bytes()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert!(payload.contains("\"meta\":null"));
assert!(payload.contains("\"version\":0"));
assert!(!payload.contains("transactionIndex"));
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_004_source_hydration_rejects_slot_index_signature_and_network_mismatch_before_admission() {
for (slot, index, transaction_data, expected_condition) in [
(43_u64, 7_u32, PRE_004_ZERO_TRANSACTION_BASE64, "hydration.slot_mismatch"),
(42_u64, 8_u32, PRE_004_ZERO_TRANSACTION_BASE64, "hydration.transaction_index_mismatch"),
(42_u64, 7_u32, PRE_004_OTHER_TRANSACTION_BASE64, "hydration.signature_mismatch"),
] {
let body = std::format!(
"{{\"jsonrpc\":\"2.0\",\"result\":{{\"slot\":{},\"blockTime\":1760000120,\"transaction\":[\"{}\",\"base64\"],\"meta\":null,\"version\":\"legacy\",\"transactionIndex\":{}}},\"id\":1}}",
slot,
transaction_data,
index,
);
let (url, server) = match serve_http_once(body) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let source = match signal_source_for_http_url(url.as_str()) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let settings = match pre_004_settings() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let signal = match pre_004_signal(&source, super::RawTransactionIngestSourceFamily::Transaction, 42, 7, &["tx-fixture"], 0) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let received_at = match ksp_store_lib::RawTimestamp::from_unix_millis(1_760_000_124_000) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let error = match super::hydrate_yellowstone_signal(&source, &settings, signal, received_at).await {
std::result::Result::Ok(_) => return,
std::result::Result::Err(error) => error,
};
let _request = match server.join() {
std::result::Result::Ok(std::result::Result::Ok(value)) => value,
_ => return,
};
assert_eq!(error.code(), crate::ERROR_CODE_RAW_TRANSACTION_INGEST_RUNTIME_INVALID);
assert!(error.context().iter().any(|context| return context.value() == expected_condition));
}
let source = match signal_source() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let settings = match pre_004_settings() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let mut signal = match pre_004_signal(&source, super::RawTransactionIngestSourceFamily::Transaction, 42, 7, &["tx-fixture"], 0) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
signal.network = match ksp_store_lib::RawNetworkId::new("mainnet") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let received_at = match ksp_store_lib::RawTimestamp::from_unix_millis(1_760_000_124_000) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let error = match super::hydrate_yellowstone_signal(&source, &settings, signal, received_at).await {
std::result::Result::Ok(_) => return,
std::result::Result::Err(error) => error,
};
assert_eq!(error.code(), crate::ERROR_CODE_RAW_TRANSACTION_INGEST_RUNTIME_INVALID);
assert!(error.context().iter().any(|context| return context.value() == "hydration.network_mismatch"));
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_004_status_provenance_and_future_source_timestamp_are_bounded_and_redacted() {
let source = match signal_source() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let settings = match pre_004_settings() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let mut signal = match pre_004_signal(&source, super::RawTransactionIngestSourceFamily::TransactionStatus, 42, 7, &["filter-b", "filter-a", "filter-a"], 0)
{
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
signal.created_at = std::option::Option::Some(super::RawTransactionIngestSourceTimestamp { seconds: 1_760_000_200, nanos: 0 });
let received_at = match ksp_store_lib::RawTimestamp::from_unix_millis(1_760_000_124_000) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let provenance = match super::build_hydration_provenance(
&settings,
&signal,
"fixture-http-provider",
"http-hydration-fixture",
ksp_onchain_transport_lib::SolanaCommitment::Confirmed,
received_at,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(provenance.acquisition_method().as_str(), "status_get_transaction");
assert_eq!(provenance.observed_at(), std::option::Option::None);
let filter_id = match provenance.filter_id() {
std::option::Option::Some(value) => value.as_str(),
std::option::Option::None => return,
};
assert!(filter_id.starts_with("sha256."));
let debug = std::format!("{provenance:?}");
assert!(!debug.contains("filter-a"));
assert!(!debug.contains("filter-b"));
assert!(!debug.contains(PRE_004_ZERO_SIGNATURE_TEXT));
return;
}
#[tokio::test(flavor = "current_thread")]
async fn pre_006_coalescence_key_merges_transaction_and_status_before_http_fanout() {
let source = match signal_source() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let settings = match pre_004_settings() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let transaction = match pre_004_signal(&source, super::RawTransactionIngestSourceFamily::Transaction, 91, 3, &["transaction-filter"], 0x44) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let status = match pre_004_signal(&source, super::RawTransactionIngestSourceFamily::TransactionStatus, 91, 3, &["status-filter"], 0x44) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let transaction_key = match super::hydration_key(&source, &transaction) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let status_key = match super::hydration_key(&source, &status) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert!(transaction_key == status_key);
let received_at = match ksp_store_lib::RawTimestamp::from_unix_millis(1_760_000_200_000) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let mut coordinator = super::RawTransactionIngestHydrationCoordinator::new(&settings);
let (frontier_sender, _frontier_receiver) = tokio::sync::watch::channel(crate::RawTransactionIngestProcessingFrontierProjection::empty());
let mut processing_frontier = super::RawTransactionIngestProcessingFrontierReporter::new(frontier_sender);
if coordinator.queue_signal(&source, transaction, received_at, &mut processing_frontier).is_err() {
return;
}
if coordinator.queue_signal(&source, status, received_at, &mut processing_frontier).is_err() {
return;
}
assert_eq!(coordinator.pending.len(), 1);
assert_eq!(coordinator.pending_signal_count, 2);
assert_eq!(coordinator.tasks.len(), 1);
coordinator.abort_all().await;
assert_eq!(coordinator.pending_signal_count, 0);
assert!(coordinator.pending.is_empty());
assert!(coordinator.tasks.is_empty());
return;
}
#[test]
fn pre_006_coalescence_key_separates_network_signature_and_commitment() {
let source = match signal_source() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let first = match pre_004_signal(&source, super::RawTransactionIngestSourceFamily::Transaction, 12, 1, &["same"], 0x10) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let second = match pre_004_signal(&source, super::RawTransactionIngestSourceFamily::Block, 12, 1, &["same"], 0x11) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let first_key = match super::hydration_key(&source, &first) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let second_key = match super::hydration_key(&source, &second) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert!(first_key != second_key);
assert_eq!(first_key.commitment, "confirmed");
assert_eq!(first_key.network.as_str(), "devnet");
return;
}
#[test]
fn pre_007_processing_frontier_never_advances_through_pending_work() {
let mut frontier = super::RawTransactionIngestProcessingFrontier::new();
assert!(frontier.observe_settled(100).is_ok());
let projection = frontier.projection();
assert_eq!(projection.hydration_pending(), 0);
assert_eq!(projection.processing_frontier_slot(), std::option::Option::Some(100));
assert_eq!(projection.oldest_pending_slot(), std::option::Option::None);
assert!(frontier.observe_pending(102).is_ok());
assert!(frontier.observe_settled(105).is_ok());
assert!(frontier.observe_pending(104).is_ok());
let projection = frontier.projection();
assert_eq!(projection.hydration_pending(), 2);
assert_eq!(projection.processing_frontier_slot(), std::option::Option::Some(100));
assert_eq!(projection.oldest_pending_slot(), std::option::Option::Some(102));
assert!(frontier.settle_pending(102).is_ok());
let projection = frontier.projection();
assert_eq!(projection.hydration_pending(), 1);
assert_eq!(projection.processing_frontier_slot(), std::option::Option::Some(102));
assert_eq!(projection.oldest_pending_slot(), std::option::Option::Some(104));
assert!(frontier.settle_pending(104).is_ok());
let projection = frontier.projection();
assert_eq!(projection.hydration_pending(), 0);
assert_eq!(projection.processing_frontier_slot(), std::option::Option::Some(105));
assert_eq!(projection.oldest_pending_slot(), std::option::Option::None);
return;
}
#[test]
fn pre_007_processing_frontier_compacts_settled_slots_to_pending_intervals() {
let mut frontier = super::RawTransactionIngestProcessingFrontier::new();
assert!(frontier.observe_pending(10).is_ok());
assert!(frontier.observe_pending(20).is_ok());
for slot in 0_u64..=1000_u64 {
if slot == 10 || slot == 20 {
continue;
}
assert!(frontier.observe_settled(slot).is_ok());
}
assert!(frontier.slot_state_count() <= 5);
let projection = frontier.projection();
assert_eq!(projection.hydration_pending(), 2);
assert_eq!(projection.oldest_pending_slot(), std::option::Option::Some(10));
assert_eq!(projection.processing_frontier_slot(), std::option::Option::Some(9));
return;
}
#[test]
fn pre_008_reconnect_replay_and_proven_gap_are_distinct_monotone_and_frontier_preserving() {
let (sender, receiver) = tokio::sync::watch::channel(crate::RawTransactionIngestProcessingFrontierProjection::empty());
let mut reporter = super::RawTransactionIngestProcessingFrontierReporter::new(sender);
assert!(reporter.observe_pending(42).is_ok());
assert!(reporter.observe_source_continuity(crate::RawTransactionIngestSourceState::Reconnecting, 0, 1, 0).is_ok());
let replaying = *receiver.borrow();
assert_eq!(replaying.hydration_pending(), 1);
assert_eq!(replaying.oldest_pending_slot(), std::option::Option::Some(42));
assert_eq!(replaying.source_state(), std::option::Option::Some(crate::RawTransactionIngestSourceState::Reconnecting));
assert_eq!(replaying.source_reconnect_total(), 0);
assert_eq!(replaying.source_replay_attempt_total(), 1);
assert_eq!(replaying.source_continuity_gap_total(), 0);
assert!(reporter.observe_source_continuity(crate::RawTransactionIngestSourceState::Active, 1, 1, 0).is_ok());
let gap = reporter.observe_source_continuity(crate::RawTransactionIngestSourceState::Reconnecting, 1, 2, 1);
assert!(gap.is_err());
let gap = match gap {
std::result::Result::Ok(()) => return,
std::result::Result::Err(value) => value,
};
assert_eq!(gap.code(), crate::ERROR_CODE_RAW_TRANSACTION_INGEST_RUNTIME_INVALID);
assert!(gap.context().iter().any(|context| {
return context.value() == "source.continuity_gap_proven";
}));
let proven = *receiver.borrow();
assert_eq!(proven.hydration_pending(), 1);
assert_eq!(proven.oldest_pending_slot(), std::option::Option::Some(42));
assert_eq!(proven.source_reconnect_total(), 1);
assert_eq!(proven.source_replay_attempt_total(), 2);
assert_eq!(proven.source_continuity_gap_total(), 1);
let regression = reporter.observe_source_continuity(crate::RawTransactionIngestSourceState::Active, 0, 2, 1);
assert!(regression.is_err());
let regression = match regression {
std::result::Result::Ok(()) => return,
std::result::Result::Err(value) => value,
};
assert!(regression.context().iter().any(|context| {
return context.value() == "source.continuity_counter_regression";
}));
return;
}