// file: crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs // version: 8 fn grpc_endpoint(cluster: &str) -> std::option::Option { 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 { 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 { 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, ) -> std::option::Option { 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, family: super::RawTransactionIngestSourceFamily, filters: std::vec::Vec, index: u64, signature: ksp_onchain_transport_lib::YellowstoneTransactionSignature, slot: u64, } impl super::RawTransactionIngestYellowstoneSignalView for SignalViewFixture { fn created_at(&self) -> std::option::Option { 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, filters: std::vec::Vec, slot: u64, transactions: std::vec::Vec<(ksp_onchain_transport_lib::YellowstoneTransactionSignature, u64)>, } impl super::RawTransactionIngestYellowstoneBlockView for BlockViewFixture { fn created_at(&self) -> std::option::Option { 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, family: super::RawTransactionIngestContinuityFamily, filters: std::vec::Vec, parent_slot: std::option::Option, slot: u64, status: std::option::Option, } impl super::RawTransactionIngestYellowstoneContinuityView for ContinuityViewFixture { fn created_at(&self) -> std::option::Option { 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 { return self.parent_slot; } fn slot(&self) -> u64 { return self.slot; } fn status(&self) -> std::option::Option { return self.status; } } fn filter_names(values: &[&str]) -> std::option::Option> { 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 { 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 { 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 { 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 { 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 { 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 { 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>)> { 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 { 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::() { 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; }