v0.3.14-pre.002
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-worker-raw-transaction-ingest-lib/src/runtime_resources.rs
|
||||
// version: 26
|
||||
// version: 27
|
||||
|
||||
use sha2::Digest; // rust-rules: trait-import
|
||||
|
||||
@@ -31,6 +31,7 @@ const RAW_TRANSACTION_INGEST_STANDARD_BLOCK_PROTOCOL: &str = "solana_ws";
|
||||
const RAW_TRANSACTION_INGEST_STANDARD_LOGS_FILTER_FINGERPRINT_DOMAIN: &[u8] = b"ksp.raw_transaction_ingest.standard_logs.filter.v1\0";
|
||||
const RAW_TRANSACTION_INGEST_STANDARD_LOGS_HTTP_PROTOCOL: &str = "solana_ws_http";
|
||||
const RAW_TRANSACTION_INGEST_STANDARD_LOGS_HTTP_SOURCE_KEY_DOMAIN: &[u8] = b"ksp.raw_transaction_ingest.standard_logs_http.source_key.v1\0";
|
||||
const RAW_TRANSACTION_INGEST_YELLOWSTONE_COVERAGE_SCOPE_FINGERPRINT_DOMAIN: &[u8] = b"ksp.raw_transaction_ingest.yellowstone.coverage_scope.v1\0";
|
||||
const RAW_TRANSACTION_INGEST_YELLOWSTONE_FILTER_FINGERPRINT_DOMAIN: &[u8] = b"ksp.raw_transaction_ingest.yellowstone.filters.v1\0";
|
||||
const RAW_TRANSACTION_INGEST_YELLOWSTONE_HTTP_PROTOCOL: &str = "yellowstone_http";
|
||||
const RAW_TRANSACTION_INGEST_YELLOWSTONE_HTTP_SOURCE_KEY_DOMAIN: &[u8] = b"ksp.raw_transaction_ingest.yellowstone_http.source_key.v1\0";
|
||||
@@ -75,6 +76,86 @@ impl RawTransactionIngestLiveSource {
|
||||
};
|
||||
}
|
||||
|
||||
fn repair_capability_descriptor(&self) -> ksp_core_lib::Result<crate::RawTransactionIngestRepairCapabilityDescriptor> {
|
||||
let commitment = match self {
|
||||
Self::HeliusTransaction(source) => source.commitment,
|
||||
Self::HttpBlockPolling(source) => source.commitment,
|
||||
Self::StandardBlock(source) => source.commitment,
|
||||
Self::StandardLogs(source) => source.commitment,
|
||||
Self::Yellowstone(source) => match source.subscribe_request.commitment() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return std::result::Result::Err(crate::runtime_error("continuity.yellowstone_commitment_missing")),
|
||||
},
|
||||
};
|
||||
let source_scope = match self {
|
||||
Self::HeliusTransaction(source) => crate::RawTransactionIngestCoverageScope::exact_source_scope("helius_transaction", source.filter_fingerprint),
|
||||
Self::HttpBlockPolling(_) => crate::RawTransactionIngestCoverageScope::full_ledger_transactions(),
|
||||
Self::StandardBlock(source) => match &source.filter {
|
||||
ksp_onchain_transport_lib::SolanaBlockSubscribeFilter::All => crate::RawTransactionIngestCoverageScope::full_ledger_transactions(),
|
||||
ksp_onchain_transport_lib::SolanaBlockSubscribeFilter::MentionsAccountOrProgram(_) => {
|
||||
crate::RawTransactionIngestCoverageScope::exact_source_scope("standard_block", source.filter_fingerprint)
|
||||
},
|
||||
},
|
||||
Self::StandardLogs(source) => crate::RawTransactionIngestCoverageScope::exact_source_scope("standard_logs", source.filter_fingerprint),
|
||||
Self::Yellowstone(source) => {
|
||||
let fingerprint = match yellowstone_coverage_scope_fingerprint(&source.subscribe_request) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
crate::RawTransactionIngestCoverageScope::exact_source_scope("yellowstone", fingerprint)
|
||||
},
|
||||
};
|
||||
let (reference_bearing, live_block_material, native_replay, http_block_scan, known_reference_hydration) = match self {
|
||||
Self::HeliusTransaction(source) => {
|
||||
let http_block_scan = match http_role_supports_repair_scan(&source.http_pool, &source.hydration_role, source.network.as_str()) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
(true, false, false, http_block_scan, true)
|
||||
},
|
||||
Self::HttpBlockPolling(source) => {
|
||||
let known_reference_hydration =
|
||||
match http_role_supports_rpc_method(&source.http_pool, &source.polling_role, "getTransaction", source.network.as_str()) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
(false, true, false, true, known_reference_hydration)
|
||||
},
|
||||
Self::StandardBlock(_) => (false, true, false, false, false),
|
||||
Self::StandardLogs(source) => {
|
||||
let http_block_scan = match http_role_supports_repair_scan(&source.http_pool, &source.hydration_role, source.network.as_str()) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
(true, false, false, http_block_scan, true)
|
||||
},
|
||||
Self::Yellowstone(source) => {
|
||||
let http_block_scan = match http_role_supports_repair_scan(&source.http_pool, &source.hydration_role, source.network.as_str()) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let reference_bearing =
|
||||
source.subscribe_request.transaction_filter_count() > 0 || source.subscribe_request.transaction_status_filter_count() > 0;
|
||||
let live_block_material = source.subscribe_request.block_filter_count() > 0;
|
||||
(reference_bearing, live_block_material, true, http_block_scan, true)
|
||||
},
|
||||
};
|
||||
let block_material = live_block_material || http_block_scan;
|
||||
let slot_enumerating = http_block_scan;
|
||||
return crate::RawTransactionIngestRepairCapabilityDescriptor::new(
|
||||
self.source_key(),
|
||||
self.network().clone(),
|
||||
commitment,
|
||||
source_scope,
|
||||
reference_bearing,
|
||||
block_material,
|
||||
slot_enumerating,
|
||||
known_reference_hydration,
|
||||
native_replay,
|
||||
http_block_scan,
|
||||
);
|
||||
}
|
||||
|
||||
fn source_key(&self) -> [u8; 32] {
|
||||
return match self {
|
||||
Self::HeliusTransaction(source) => source.source_key,
|
||||
@@ -2023,6 +2104,22 @@ impl crate::RawTransactionIngestRuntimeResources {
|
||||
if self.sources.is_empty() || self.sources.len() > crate::MAX_RAW_TRANSACTION_INGEST_LIVE_SOURCES {
|
||||
return std::result::Result::Err(crate::runtime_error("runtime_resources.source_collection_invalid"));
|
||||
}
|
||||
let mut repair_capabilities = std::vec::Vec::with_capacity(self.sources.len());
|
||||
for source in &self.sources {
|
||||
let capability = match source.repair_capability_descriptor() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
repair_capabilities.push(capability);
|
||||
}
|
||||
let continuity_contracts = match crate::RawTransactionIngestContinuityContracts::new(repair_capabilities) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let continuity_source_count = self.sources.len();
|
||||
if let std::result::Result::Err(error) = continuity_contracts.validate_for_source_count(continuity_source_count) {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let source_keys = self.sources.iter().map(RawTransactionIngestLiveSource::source_key).collect::<std::vec::Vec<_>>();
|
||||
let hydration_source_count = self.sources.iter().filter(|source| return source.uses_hydration()).count();
|
||||
if let std::result::Result::Err(error) =
|
||||
@@ -2072,7 +2169,11 @@ impl crate::RawTransactionIngestRuntimeResources {
|
||||
});
|
||||
}
|
||||
std::mem::drop(admission_sender);
|
||||
return supervise_live_source_tasks(stop_receiver, source_stop_sender, children).await;
|
||||
let result = supervise_live_source_tasks(stop_receiver, source_stop_sender, children).await;
|
||||
if let std::result::Result::Err(error) = continuity_contracts.validate_for_source_count(continuity_source_count) {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2205,6 +2306,57 @@ async fn drain_live_source_tasks(
|
||||
};
|
||||
}
|
||||
|
||||
fn http_role_supports_repair_scan(
|
||||
pool: &ksp_onchain_transport_lib::HttpTransportPool,
|
||||
role: &ksp_onchain_transport_lib::HttpRoleName,
|
||||
expected_cluster: &str,
|
||||
) -> ksp_core_lib::Result<bool> {
|
||||
let has_get_block = match http_role_supports_rpc_method(pool, role, "getBlock", expected_cluster) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let has_get_blocks = match http_role_supports_rpc_method(pool, role, "getBlocks", expected_cluster) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let has_get_blocks_with_limit = match http_role_supports_rpc_method(pool, role, "getBlocksWithLimit", expected_cluster) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let has_get_slot = match http_role_supports_rpc_method(pool, role, "getSlot", expected_cluster) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(has_get_block && has_get_slot && (has_get_blocks || has_get_blocks_with_limit));
|
||||
}
|
||||
|
||||
fn http_role_supports_rpc_method(
|
||||
pool: &ksp_onchain_transport_lib::HttpTransportPool,
|
||||
role: &ksp_onchain_transport_lib::HttpRoleName,
|
||||
method_name: &'static str,
|
||||
expected_cluster: &str,
|
||||
) -> ksp_core_lib::Result<bool> {
|
||||
let method = match ksp_onchain_transport_lib::find_http_rpc_method(method_name) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return std::result::Result::Err(crate::runtime_error("continuity.http_method_missing")),
|
||||
};
|
||||
let snapshot = pool.snapshot();
|
||||
for endpoint in snapshot.endpoints() {
|
||||
if !endpoint.enabled() || endpoint.cluster() != expected_cluster {
|
||||
continue;
|
||||
}
|
||||
for endpoint_role in endpoint.roles() {
|
||||
if !endpoint_role.enabled() || endpoint_role.role() != role.as_str() {
|
||||
continue;
|
||||
}
|
||||
if http_role_supports_request_kind(endpoint_role, method.request_kind()) {
|
||||
return std::result::Result::Ok(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::result::Result::Ok(false);
|
||||
}
|
||||
|
||||
fn http_block_polling_live_source_key(
|
||||
network: &ksp_store_lib::RawNetworkId,
|
||||
polling_role: &ksp_onchain_transport_lib::HttpRoleName,
|
||||
@@ -2639,6 +2791,22 @@ fn standard_logs_live_source_key(
|
||||
return hasher.finalize().into();
|
||||
}
|
||||
|
||||
fn yellowstone_coverage_scope_fingerprint(request: &ksp_onchain_transport_lib::YellowstoneSubscribeRequest) -> ksp_core_lib::Result<[u8; 32]> {
|
||||
let mut normalized = request.clone();
|
||||
normalized.set_commitment(std::option::Option::None);
|
||||
normalized.set_from_slot(std::option::Option::None);
|
||||
normalized.set_ping(std::option::Option::None);
|
||||
let identity = match normalized.identity() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(crate::runtime_error("continuity.yellowstone_scope_identity_invalid")),
|
||||
};
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(RAW_TRANSACTION_INGEST_YELLOWSTONE_COVERAGE_SCOPE_FINGERPRINT_DOMAIN);
|
||||
let mut writer = RawTransactionIngestSourceKeyHashWriter { hasher: &mut hasher };
|
||||
std::hash::Hash::hash(&identity, &mut writer);
|
||||
return std::result::Result::Ok(hasher.finalize().into());
|
||||
}
|
||||
|
||||
fn yellowstone_live_source_key(
|
||||
network: &ksp_store_lib::RawNetworkId,
|
||||
route: &RawTransactionIngestSourceRoute,
|
||||
|
||||
Reference in New Issue
Block a user