v0.3.5-pre.003

This commit is contained in:
2026-08-31 11:01:45 +02:00
parent ca018095bf
commit 7d490eb5d8
9 changed files with 620 additions and 66 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-interface-lib/src/lib.rs
// version: 4
// version: 5
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -16,6 +16,7 @@ mod error;
mod program_account_meta;
mod program_instruction;
mod slot_lifecycle;
mod transaction_execution;
/// Error code used when an Interface-owned Program instruction admission limit is exceeded.
pub use self::error::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED;
@@ -31,5 +32,11 @@ pub use self::program_instruction::ProgramInstruction;
pub use self::slot_lifecycle::SlotLifecycleEvent;
/// Provider-neutral stage in the lifecycle of an observed Solana slot.
pub use self::slot_lifecycle::SlotLifecycleStage;
/// Passive provider-neutral observation of one transaction execution result.
pub use self::transaction_execution::TransactionExecutionEvent;
/// Provider-neutral outcome of one observed transaction execution.
pub use self::transaction_execution::TransactionExecutionOutcome;
/// Canonical 64-byte Solana transaction signature used by passive Interface events.
pub use self::transaction_execution::TransactionSignature;
/// Canonical Solana account address primitive owned by `ksp-core-lib`.
pub use ksp_core_lib::Pubkey;

View File

@@ -0,0 +1,85 @@
// file: crates/ksp-interface-lib/src/transaction_execution.rs
// version: 1
/// Canonical 64-byte Solana transaction signature used by passive Interface events.
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct TransactionSignature([u8; 64]);
impl TransactionSignature {
/// Creates one transaction signature from already-decoded canonical bytes.
#[must_use]
pub const fn new(bytes: [u8; 64]) -> Self {
return Self(bytes);
}
/// Returns the exact canonical signature bytes.
#[must_use]
pub const fn as_bytes(&self) -> &[u8; 64] {
return &self.0;
}
}
impl std::fmt::Debug for TransactionSignature {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter.write_str("TransactionSignature(..)");
}
}
/// Provider-neutral outcome of one observed transaction execution.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum TransactionExecutionOutcome {
/// The observed transaction execution completed without a transaction error.
Succeeded,
/// The observed transaction execution completed with a transaction error.
Failed,
}
/// Passive provider-neutral observation of one transaction execution result.
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct TransactionExecutionEvent {
slot: u64,
signature: TransactionSignature,
outcome: TransactionExecutionOutcome,
}
impl TransactionExecutionEvent {
/// Creates one transaction execution event from its common provider-neutral fields.
#[must_use]
pub const fn new(slot: u64, signature: TransactionSignature, outcome: TransactionExecutionOutcome) -> Self {
return Self { slot, signature, outcome };
}
/// Returns the containing slot exactly as observed by the producer.
#[must_use]
pub const fn slot(&self) -> u64 {
return self.slot;
}
/// Returns the canonical transaction signature.
#[must_use]
pub const fn signature(&self) -> TransactionSignature {
return self.signature;
}
/// Returns the normalized execution outcome.
#[must_use]
pub const fn outcome(&self) -> TransactionExecutionOutcome {
return self.outcome;
}
}
impl std::fmt::Debug for TransactionExecutionEvent {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter
.debug_struct("TransactionExecutionEvent")
.field("slot", &self.slot)
.field("signature", &"<omitted>")
.field("outcome", &self.outcome)
.finish();
}
}
#[cfg(test)]
#[path = "../unit_tests/transaction_execution.rs"]
mod tests;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-interface-lib/tests/release_completeness.rs
// version: 3
// version: 4
//! Release-completeness canaries for the public `ksp-interface-lib` surface.
@@ -9,7 +9,7 @@ fn crate_root_source() -> std::result::Result<std::string::String, std::io::Erro
}
#[test]
fn v0_3_5_pre_002_foundation_keeps_one_error_code_and_adds_one_passive_acquisition_family() {
fn v0_3_5_pre_003_foundation_keeps_one_error_code_and_two_passive_acquisition_families() {
assert_eq!(ksp_interface_lib::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED.domain(), "interface");
assert_eq!(ksp_interface_lib::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED.code(), "program_instruction_limit_exceeded");
assert_eq!(ksp_interface_lib::MAX_PROGRAM_INSTRUCTION_ACCOUNTS, 255);
@@ -21,16 +21,21 @@ fn v0_3_5_pre_002_foundation_keeps_one_error_code_and_adds_one_passive_acquisiti
let lifecycle = ksp_interface_lib::SlotLifecycleEvent::new(u64::MAX, ksp_interface_lib::SlotLifecycleStage::Rooted);
assert_eq!(lifecycle.slot(), u64::MAX);
assert_eq!(lifecycle.stage(), ksp_interface_lib::SlotLifecycleStage::Rooted);
let signature = ksp_interface_lib::TransactionSignature::new([0xB4_u8; 64]);
let execution = ksp_interface_lib::TransactionExecutionEvent::new(u64::MAX, signature, ksp_interface_lib::TransactionExecutionOutcome::Succeeded);
assert_eq!(execution.slot(), u64::MAX);
assert_eq!(execution.signature(), signature);
assert_eq!(execution.outcome(), ksp_interface_lib::TransactionExecutionOutcome::Succeeded);
}
#[test]
fn v0_3_5_pre_002_exact_crate_root_export_inventory_includes_slot_lifecycle() -> std::result::Result<(), std::io::Error> {
fn v0_3_5_pre_003_exact_crate_root_export_inventory_includes_both_acquisition_families() -> std::result::Result<(), std::io::Error> {
let source = match crate_root_source() {
Ok(source) => source,
Err(error) => return Err(error),
};
let public_use_count = source.lines().filter(|line| return line.starts_with("pub use ")).count();
assert_eq!(public_use_count, 8);
assert_eq!(public_use_count, 11);
assert!(source.contains("pub use self::error::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED;"));
assert!(source.contains("pub use self::program_account_meta::MAX_PROGRAM_INSTRUCTION_ACCOUNTS;"));
assert!(source.contains("pub use self::program_account_meta::ProgramAccountMeta;"));
@@ -38,12 +43,15 @@ fn v0_3_5_pre_002_exact_crate_root_export_inventory_includes_slot_lifecycle() ->
assert!(source.contains("pub use self::program_instruction::ProgramInstruction;"));
assert!(source.contains("pub use self::slot_lifecycle::SlotLifecycleEvent;"));
assert!(source.contains("pub use self::slot_lifecycle::SlotLifecycleStage;"));
assert!(source.contains("pub use self::transaction_execution::TransactionExecutionEvent;"));
assert!(source.contains("pub use self::transaction_execution::TransactionExecutionOutcome;"));
assert!(source.contains("pub use self::transaction_execution::TransactionSignature;"));
assert!(source.contains("pub use ksp_core_lib::Pubkey;"));
return Ok(());
}
#[test]
fn v0_3_5_pre_002_production_module_inventory_adds_only_slot_lifecycle() -> std::result::Result<(), std::io::Error> {
fn v0_3_5_pre_003_production_module_inventory_adds_only_transaction_execution() -> std::result::Result<(), std::io::Error> {
let source = match crate_root_source() {
Ok(source) => source,
Err(error) => return Err(error),
@@ -52,7 +60,7 @@ fn v0_3_5_pre_002_production_module_inventory_adds_only_slot_lifecycle() -> std:
.lines()
.filter_map(|line| return line.strip_prefix("mod ").and_then(|module| return module.strip_suffix(';')))
.collect::<std::vec::Vec<_>>();
assert_eq!(modules, std::vec!["error", "program_account_meta", "program_instruction", "slot_lifecycle"]);
assert_eq!(modules, std::vec!["error", "program_account_meta", "program_instruction", "slot_lifecycle", "transaction_execution"]);
assert!(!source.contains("serde"));
assert!(!source.contains("tracing"));
assert!(!source.contains("ksp_onchain_transport"));

View File

@@ -0,0 +1,28 @@
// file: crates/ksp-interface-lib/tests/transaction_execution_public_api.rs
// version: 1
//! Public API canaries for the provider-neutral transaction execution contract.
#[test]
fn public_v0_3_5_pre_003_transaction_execution_contract_is_available_from_crate_root() {
let signature = ksp_interface_lib::TransactionSignature::new([0xC7_u8; 64]);
let event = ksp_interface_lib::TransactionExecutionEvent::new(u64::MAX, signature, ksp_interface_lib::TransactionExecutionOutcome::Succeeded);
assert_eq!(signature.as_bytes(), &[0xC7_u8; 64]);
assert_eq!(event.slot(), u64::MAX);
assert_eq!(event.signature(), signature);
assert_eq!(event.outcome(), ksp_interface_lib::TransactionExecutionOutcome::Succeeded);
}
fn outcome_label(outcome: ksp_interface_lib::TransactionExecutionOutcome) -> &'static str {
return match outcome {
ksp_interface_lib::TransactionExecutionOutcome::Succeeded => "succeeded",
ksp_interface_lib::TransactionExecutionOutcome::Failed => "failed",
_ => "future",
};
}
#[test]
fn public_v0_3_5_pre_003_transaction_execution_outcome_remains_downstream_evolvable() {
assert_eq!(outcome_label(ksp_interface_lib::TransactionExecutionOutcome::Succeeded), "succeeded");
assert_eq!(outcome_label(ksp_interface_lib::TransactionExecutionOutcome::Failed), "failed");
}

View File

@@ -0,0 +1,28 @@
// file: crates/ksp-interface-lib/unit_tests/transaction_execution.rs
// version: 1
#[test]
fn transaction_signature_preserves_exact_bytes_and_redacts_debug() {
let signature = crate::TransactionSignature::new([0xA5_u8; 64]);
assert_eq!(signature.as_bytes(), &[0xA5_u8; 64]);
assert_eq!(std::format!("{signature:?}"), "TransactionSignature(..)");
}
#[test]
fn transaction_execution_outcomes_are_distinct_and_copy() {
let succeeded = crate::TransactionExecutionOutcome::Succeeded;
let failed = crate::TransactionExecutionOutcome::Failed;
assert_ne!(succeeded, failed);
let copied = succeeded;
assert_eq!(copied, crate::TransactionExecutionOutcome::Succeeded);
}
#[test]
fn transaction_execution_event_preserves_full_slot_signature_and_outcome() {
let signature = crate::TransactionSignature::new([0xB6_u8; 64]);
let event = crate::TransactionExecutionEvent::new(u64::MAX, signature, crate::TransactionExecutionOutcome::Failed);
assert_eq!(event.slot(), u64::MAX);
assert_eq!(event.signature(), signature);
assert_eq!(event.outcome(), crate::TransactionExecutionOutcome::Failed);
assert_eq!(std::format!("{event:?}"), "TransactionExecutionEvent { slot: 18446744073709551615, signature: \"<omitted>\", outcome: Failed }");
}