v0.3.13-pre.009
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/admission.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
fn material(
|
||||
network: &str,
|
||||
@@ -311,3 +311,104 @@ async fn v0_3_13_pre_008_distinct_source_keys_produce_distinct_idempotent_observ
|
||||
assert_ne!(first.observation().observation_key(), second.observation().observation_key());
|
||||
return;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn v0_3_13_pre_009_duplicate_storm_cannot_starve_second_ready_source_on_bounded_admission() {
|
||||
let (network, initial_material) = match material("mainnet", 60, "AQID") {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let (_, peer_material) = match material("mainnet", 99, "BwgJ") {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let initial_provenance = match provenance() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let storm_provenance = match provenance() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let peer_provenance = match provenance() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let (mut admission, sender) = crate::RawTransactionAdmission::new(1);
|
||||
assert!(
|
||||
sender
|
||||
.send(crate::RawTransactionIngress {
|
||||
material: initial_material,
|
||||
network: network.clone(),
|
||||
provenance: initial_provenance,
|
||||
source_key: [60_u8; 32],
|
||||
})
|
||||
.await
|
||||
.is_ok()
|
||||
);
|
||||
let storm_sender = sender.clone();
|
||||
let storm_network = network.clone();
|
||||
let storm_task = tokio::spawn(async move {
|
||||
for _ in 0..8 {
|
||||
let material = ksp_raw_transaction_lib::RawTransactionMaterial::binary_base64(
|
||||
storm_network.clone(),
|
||||
ksp_store_lib::RawTransactionSignature::new([61_u8; 64]),
|
||||
42,
|
||||
std::option::Option::Some(1_700_000_000),
|
||||
"BAUG",
|
||||
ksp_raw_transaction_lib::RawTransactionWireField::Omitted,
|
||||
ksp_raw_transaction_lib::RawTransactionWireField::Omitted,
|
||||
ksp_raw_transaction_lib::RawTransactionWireField::Omitted,
|
||||
);
|
||||
if storm_sender
|
||||
.send(crate::RawTransactionIngress {
|
||||
material,
|
||||
network: storm_network.clone(),
|
||||
provenance: storm_provenance.clone(),
|
||||
source_key: [61_u8; 32],
|
||||
})
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
tokio::task::yield_now().await;
|
||||
let peer_sender = sender.clone();
|
||||
let peer_network = network.clone();
|
||||
let peer_task = tokio::spawn(async move {
|
||||
return peer_sender
|
||||
.send(crate::RawTransactionIngress { material: peer_material, network: peer_network, provenance: peer_provenance, source_key: [99_u8; 32] })
|
||||
.await
|
||||
.is_ok();
|
||||
});
|
||||
tokio::task::yield_now().await;
|
||||
assert_eq!(admission.queue_depth(), 1);
|
||||
let first = match admission.receive(&network).await {
|
||||
std::result::Result::Ok(std::option::Option::Some(value)) => value,
|
||||
_ => return,
|
||||
};
|
||||
assert_eq!(first.transaction().reference().signature().as_bytes(), &[60_u8; 64]);
|
||||
let second = match admission.receive(&network).await {
|
||||
std::result::Result::Ok(std::option::Option::Some(value)) => value,
|
||||
_ => return,
|
||||
};
|
||||
assert_eq!(second.transaction().reference().signature().as_bytes(), &[61_u8; 64]);
|
||||
let third = match admission.receive(&network).await {
|
||||
std::result::Result::Ok(std::option::Option::Some(value)) => value,
|
||||
_ => return,
|
||||
};
|
||||
assert_eq!(third.transaction().reference().signature().as_bytes(), &[99_u8; 64]);
|
||||
for _ in 0..7 {
|
||||
let next = admission.receive(&network).await;
|
||||
assert!(matches!(next, std::result::Result::Ok(std::option::Option::Some(_))));
|
||||
assert!(admission.queue_depth() <= 1);
|
||||
}
|
||||
let peer_joined = peer_task.await;
|
||||
assert!(matches!(peer_joined, std::result::Result::Ok(true)));
|
||||
let storm_joined = storm_task.await;
|
||||
assert!(matches!(storm_joined, std::result::Result::Ok(true)));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/persistence.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum PortResponse {
|
||||
@@ -92,6 +92,17 @@ fn acquisition_with_observation_key(
|
||||
network_name: &str,
|
||||
signature_byte: u8,
|
||||
observation_byte: u8,
|
||||
) -> std::option::Option<ksp_raw_transaction_lib::RawTransactionAcquisition> {
|
||||
return acquisition_with_shape(network_name, signature_byte, observation_byte, 42, std::option::Option::Some(1_700_000_000), "AQID");
|
||||
}
|
||||
|
||||
fn acquisition_with_shape(
|
||||
network_name: &str,
|
||||
signature_byte: u8,
|
||||
observation_byte: u8,
|
||||
slot: u64,
|
||||
block_time: std::option::Option<i64>,
|
||||
transaction_data: &str,
|
||||
) -> std::option::Option<ksp_raw_transaction_lib::RawTransactionAcquisition> {
|
||||
let network = match ksp_store_lib::RawNetworkId::new(network_name) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
@@ -101,9 +112,9 @@ fn acquisition_with_observation_key(
|
||||
let material = ksp_raw_transaction_lib::RawTransactionMaterial::binary_base64(
|
||||
network,
|
||||
signature,
|
||||
42,
|
||||
std::option::Option::Some(1_700_000_000),
|
||||
"AQID",
|
||||
slot,
|
||||
block_time,
|
||||
transaction_data,
|
||||
ksp_raw_transaction_lib::RawTransactionWireField::Omitted,
|
||||
ksp_raw_transaction_lib::RawTransactionWireField::Omitted,
|
||||
ksp_raw_transaction_lib::RawTransactionWireField::Omitted,
|
||||
@@ -357,3 +368,41 @@ async fn v0_3_13_pre_008_replayed_same_observation_is_idempotent_without_raw_res
|
||||
assert_eq!(second_outcome.observation(), crate::RawTransactionIngestObservationPersistence::AlreadyPresent);
|
||||
return;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn v0_3_13_pre_009_source_skew_and_provider_disagreement_fail_before_additional_observation() {
|
||||
let cases = [
|
||||
(43_u64, std::option::Option::Some(1_700_000_000_i64), "AQID"),
|
||||
(42_u64, std::option::Option::Some(1_700_000_001_i64), "AQID"),
|
||||
(42_u64, std::option::Option::Some(1_700_000_000_i64), "BAUG"),
|
||||
];
|
||||
for (index, (slot, block_time, transaction_data)) in cases.into_iter().enumerate() {
|
||||
let network = match network("mainnet") {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let port = FakePersistencePort::new(
|
||||
network,
|
||||
PortResponse::Outcome(ksp_store_lib::RawEntityWriteOutcome::Inserted, ksp_store_lib::RawObservationWriteOutcome::Inserted),
|
||||
);
|
||||
let convergence = crate::RawTransactionIngestPersistenceConvergence::new(8);
|
||||
let first = match acquisition_with_shape("mainnet", 51, 10, 42, std::option::Option::Some(1_700_000_000), "AQID") {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let divergent = match acquisition_with_shape("mainnet", 51, index as u8 + 20, slot, block_time, transaction_data) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
assert!(crate::persist_raw_transaction_ingest_converged_acquisition(&port, first, &convergence).await.is_ok());
|
||||
let conflict = crate::persist_raw_transaction_ingest_converged_acquisition(&port, divergent, &convergence).await;
|
||||
let error = match conflict {
|
||||
std::result::Result::Ok(_) => return,
|
||||
std::result::Result::Err(value) => value,
|
||||
};
|
||||
assert_eq!(error.code(), crate::ERROR_CODE_RAW_TRANSACTION_INGEST_CONTENT_CONFLICT);
|
||||
assert_eq!(port.calls.load(std::sync::atomic::Ordering::Acquire), 1);
|
||||
assert_eq!(port.observation_calls.load(std::sync::atomic::Ordering::Acquire), 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs
|
||||
// version: 20
|
||||
// version: 21
|
||||
|
||||
fn grpc_endpoint(cluster: &str) -> std::option::Option<ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings> {
|
||||
return grpc_endpoint_with_identity(cluster, "yellowstone-fixture", "fixture-provider");
|
||||
@@ -3008,7 +3008,7 @@ fn v0_3_13_pre_008_global_hydration_registry_coalesces_cross_source_key_to_one_l
|
||||
network,
|
||||
signature: ksp_store_lib::RawTransactionSignature::new([61_u8; 64]),
|
||||
};
|
||||
let registry = super::RawTransactionIngestGlobalHydrationRegistry::new(&settings);
|
||||
let registry = super::RawTransactionIngestGlobalHydrationRegistry::new(settings.admission_queue_capacity(), settings.persistence_concurrency());
|
||||
let (first_leader, _first_receiver) = match registry.subscribe_or_lead(&key) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
@@ -3034,3 +3034,107 @@ fn v0_3_13_pre_008_global_hydration_registry_coalesces_cross_source_key_to_one_l
|
||||
assert!(third_leader);
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v0_3_13_pre_009_hydration_quotas_partition_exact_bounds_and_reject_underprovision() {
|
||||
assert!(super::validate_hydration_fairness_capacity(8, 5, 3).is_ok());
|
||||
let pending_error = match super::validate_hydration_fairness_capacity(2, 5, 3) {
|
||||
std::result::Result::Ok(()) => return,
|
||||
std::result::Result::Err(value) => value,
|
||||
};
|
||||
assert_eq!(pending_error.context()[0].value(), "runtime_resources.hydration_pending_capacity_insufficient");
|
||||
let concurrency_error = match super::validate_hydration_fairness_capacity(8, 2, 3) {
|
||||
std::result::Result::Ok(()) => return,
|
||||
std::result::Result::Err(value) => value,
|
||||
};
|
||||
assert_eq!(concurrency_error.context()[0].value(), "runtime_resources.hydration_concurrency_insufficient");
|
||||
let pending_limits = [super::hydration_pending_limit(8, 3, 0), super::hydration_pending_limit(8, 3, 1), super::hydration_pending_limit(8, 3, 2)];
|
||||
assert_eq!(pending_limits, [3, 3, 2]);
|
||||
assert_eq!(pending_limits.iter().copied().sum::<usize>(), 8);
|
||||
assert!(pending_limits.iter().copied().all(|limit| return limit >= 1));
|
||||
let in_flight_limits = [super::hydration_in_flight_limit(5, 3, 0), super::hydration_in_flight_limit(5, 3, 1), super::hydration_in_flight_limit(5, 3, 2)];
|
||||
assert_eq!(in_flight_limits, [2, 2, 1]);
|
||||
assert_eq!(in_flight_limits.iter().copied().sum::<usize>(), 5);
|
||||
assert!(in_flight_limits.iter().copied().all(|limit| return limit >= 1));
|
||||
assert!(super::validate_hydration_fairness_capacity(1, 1, 0).is_ok());
|
||||
assert_eq!(super::hydration_pending_limit(1, 0, 0), 0);
|
||||
assert_eq!(super::hydration_in_flight_limit(1, 0, 0), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v0_3_13_pre_009_duplicate_hydration_storm_keeps_one_global_key_and_distinct_keys_bounded() {
|
||||
let network = match ksp_store_lib::RawNetworkId::new("devnet") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let registry = super::RawTransactionIngestGlobalHydrationRegistry::new(2, 1);
|
||||
let first_key = super::RawTransactionIngestHydrationKey {
|
||||
commitment: ksp_onchain_transport_lib::SolanaCommitment::Confirmed.as_str(),
|
||||
network: network.clone(),
|
||||
signature: ksp_store_lib::RawTransactionSignature::new([71_u8; 64]),
|
||||
};
|
||||
for index in 0..32 {
|
||||
let subscribed = registry.subscribe_or_lead(&first_key);
|
||||
let (leader, _receiver) = match subscribed {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert_eq!(leader, index == 0);
|
||||
}
|
||||
let second_key = super::RawTransactionIngestHydrationKey {
|
||||
commitment: ksp_onchain_transport_lib::SolanaCommitment::Confirmed.as_str(),
|
||||
network: network.clone(),
|
||||
signature: ksp_store_lib::RawTransactionSignature::new([72_u8; 64]),
|
||||
};
|
||||
let third_key = super::RawTransactionIngestHydrationKey {
|
||||
commitment: ksp_onchain_transport_lib::SolanaCommitment::Confirmed.as_str(),
|
||||
network,
|
||||
signature: ksp_store_lib::RawTransactionSignature::new([73_u8; 64]),
|
||||
};
|
||||
assert!(matches!(registry.subscribe_or_lead(&second_key), std::result::Result::Ok((true, _))));
|
||||
let saturated = registry.subscribe_or_lead(&third_key);
|
||||
let error = match saturated {
|
||||
std::result::Result::Ok(_) => return,
|
||||
std::result::Result::Err(value) => value,
|
||||
};
|
||||
assert_eq!(error.code(), crate::ERROR_CODE_RAW_TRANSACTION_INGEST_RUNTIME_INVALID);
|
||||
assert_eq!(error.context()[0].value(), "source.global_hydration_pending_saturated");
|
||||
return;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn v0_3_13_pre_009_global_hydration_permit_and_leader_cleanup_are_bounded_and_fail_closed() {
|
||||
let network = match ksp_store_lib::RawNetworkId::new("devnet") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let registry = std::sync::Arc::new(super::RawTransactionIngestGlobalHydrationRegistry::new(4, 1));
|
||||
let first_permit = std::sync::Arc::clone(®istry.hydration_permits).try_acquire_owned();
|
||||
let first_permit = match first_permit {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert!(std::sync::Arc::clone(®istry.hydration_permits).try_acquire_owned().is_err());
|
||||
std::mem::drop(first_permit);
|
||||
assert!(std::sync::Arc::clone(®istry.hydration_permits).try_acquire_owned().is_ok());
|
||||
let key = super::RawTransactionIngestHydrationKey {
|
||||
commitment: ksp_onchain_transport_lib::SolanaCommitment::Confirmed.as_str(),
|
||||
network,
|
||||
signature: ksp_store_lib::RawTransactionSignature::new([74_u8; 64]),
|
||||
};
|
||||
let subscribed = registry.subscribe_or_lead(&key);
|
||||
let (leader, receiver) = match subscribed {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert!(leader);
|
||||
{
|
||||
let _guard = super::RawTransactionIngestHydrationLeaderGuard::new(std::sync::Arc::clone(®istry), key.clone());
|
||||
}
|
||||
let published = receiver.borrow().clone();
|
||||
assert!(matches!(published, std::option::Option::Some(super::RawTransactionIngestSharedHydrationResult::Failed(_))));
|
||||
let resubscribed = registry.subscribe_or_lead(&key);
|
||||
assert!(matches!(resubscribed, std::result::Result::Ok((true, _))));
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user