111 lines
5.0 KiB
Rust
111 lines
5.0 KiB
Rust
// file: crates/ksp-worker-raw-transaction-ingest-lib/src/admission.rs
|
|
// version: 3
|
|
|
|
use sha2::Digest; // rust-rules: trait-import
|
|
|
|
const RAW_TRANSACTION_INGEST_OBSERVATION_DOMAIN: &[u8] = b"ksp.raw_transaction_ingest.observation.v1\0";
|
|
|
|
/// Crate-private source-neutral ingress admitted by the central bounded Worker queue.
|
|
pub(crate) struct RawTransactionIngress {
|
|
/// Complete Common RAW material supplied by one private source task.
|
|
pub(crate) material: ksp_raw_transaction_lib::RawTransactionMaterial,
|
|
/// Logical network expected to match both Worker settings and canonical material.
|
|
pub(crate) network: ksp_store_lib::RawNetworkId,
|
|
/// Safe source-independent provenance attached to the acquisition observation.
|
|
pub(crate) provenance: ksp_store_lib::RawAcquisitionProvenance,
|
|
/// Opaque deterministic source-owned key material used only for Worker observation-key derivation.
|
|
pub(crate) source_key: [u8; 32],
|
|
}
|
|
|
|
/// Receiver side of the bounded central RAW transaction admission queue owned by the Worker supervisor.
|
|
pub(crate) struct RawTransactionAdmission {
|
|
backpressure_wait_observed: bool,
|
|
capacity: usize,
|
|
receiver: tokio::sync::mpsc::Receiver<crate::RawTransactionIngress>,
|
|
}
|
|
|
|
impl crate::RawTransactionAdmission {
|
|
/// Creates one bounded admission queue and returns its crate-private source sender.
|
|
#[must_use]
|
|
pub(crate) fn new(capacity: usize) -> (crate::RawTransactionAdmission, tokio::sync::mpsc::Sender<crate::RawTransactionIngress>) {
|
|
let (sender, receiver) = tokio::sync::mpsc::channel(capacity);
|
|
return (Self { backpressure_wait_observed: false, capacity, receiver }, sender);
|
|
}
|
|
|
|
/// Returns the latest receiver-side queue depth without consuming an ingress entry.
|
|
#[must_use]
|
|
pub(crate) fn queue_depth(&self) -> usize {
|
|
return self.receiver.len();
|
|
}
|
|
|
|
/// Closes new admissions while preserving already queued ingress for deterministic drain.
|
|
pub(crate) fn close(&mut self) {
|
|
self.receiver.close();
|
|
return;
|
|
}
|
|
|
|
/// Receives and converts one queued ingress into a common RAW transaction acquisition.
|
|
pub(crate) async fn receive(
|
|
&mut self,
|
|
expected_network: &ksp_store_lib::RawNetworkId,
|
|
) -> ksp_core_lib::Result<std::option::Option<ksp_raw_transaction_lib::RawTransactionAcquisition>> {
|
|
self.backpressure_wait_observed = self.receiver.len() == self.capacity;
|
|
let ingress = match self.receiver.recv().await {
|
|
std::option::Option::Some(value) => value,
|
|
std::option::Option::None => return std::result::Result::Ok(std::option::Option::None),
|
|
};
|
|
let acquisition = canonicalize_ingress(expected_network, ingress);
|
|
return match acquisition {
|
|
std::result::Result::Ok(value) => std::result::Result::Ok(std::option::Option::Some(value)),
|
|
std::result::Result::Err(error) => std::result::Result::Err(error),
|
|
};
|
|
}
|
|
|
|
/// Takes and clears the source-neutral signal that the previous dequeue observed the bounded queue at full capacity.
|
|
#[must_use]
|
|
pub(crate) fn take_backpressure_wait_observed(&mut self) -> bool {
|
|
let observed = self.backpressure_wait_observed;
|
|
self.backpressure_wait_observed = false;
|
|
return observed;
|
|
}
|
|
}
|
|
|
|
fn canonicalize_ingress(
|
|
expected_network: &ksp_store_lib::RawNetworkId,
|
|
ingress: crate::RawTransactionIngress,
|
|
) -> ksp_core_lib::Result<ksp_raw_transaction_lib::RawTransactionAcquisition> {
|
|
if &ingress.network != expected_network {
|
|
return std::result::Result::Err(crate::runtime_error("admission.network_mismatch"));
|
|
}
|
|
let transaction = ksp_raw_transaction_lib::canonicalize_raw_transaction(ingress.material);
|
|
let transaction = match transaction {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return std::result::Result::Err(crate::runtime_error("admission.canonicalization_invalid")),
|
|
};
|
|
if transaction.reference().network() != expected_network {
|
|
return std::result::Result::Err(crate::runtime_error("admission.material_network_mismatch"));
|
|
}
|
|
let observation_key = observation_key(transaction.reference(), &ingress.source_key);
|
|
return std::result::Result::Ok(ksp_raw_transaction_lib::assemble_raw_transaction_acquisition(transaction, observation_key, ingress.provenance));
|
|
}
|
|
|
|
fn hash_bytes(hasher: &mut sha2::Sha256, value: &[u8]) {
|
|
hasher.update((value.len() as u64).to_be_bytes());
|
|
hasher.update(value);
|
|
return;
|
|
}
|
|
|
|
fn observation_key(reference: &ksp_store_lib::RawTransactionReference, source_key: &[u8; 32]) -> ksp_store_lib::RawObservationKey {
|
|
let mut hasher = sha2::Sha256::new();
|
|
hasher.update(RAW_TRANSACTION_INGEST_OBSERVATION_DOMAIN);
|
|
hash_bytes(&mut hasher, reference.network().as_str().as_bytes());
|
|
hash_bytes(&mut hasher, reference.signature().as_bytes());
|
|
hash_bytes(&mut hasher, source_key);
|
|
let bytes: [u8; 32] = hasher.finalize().into();
|
|
return ksp_store_lib::RawObservationKey::new(bytes);
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "../unit_tests/admission.rs"]
|
|
mod tests;
|