v0.3.12-pre.004
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
fn grpc_endpoint(cluster: &str) -> std::option::Option<ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings> {
|
||||
return grpc_endpoint_with_identity(cluster, "yellowstone-fixture", "fixture-provider");
|
||||
@@ -147,6 +147,199 @@ fn signal_source() -> std::option::Option<crate::RawTransactionIngestYellowstone
|
||||
};
|
||||
}
|
||||
|
||||
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") {
|
||||
@@ -474,3 +667,368 @@ async fn pre_003_source_rejects_route_identity_that_cannot_become_safe_provenanc
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user