kcode-k1-txn-ordering 0.2.0

Canonical transaction ordering and subsystem delivery for Kennedy K1
Documentation
# Public API
```rust
use std::{path::Path, sync::Arc};
pub use kcode_k1_canonical_chain::{SubmitError, TxId};
pub use kcode_k1_transaction::{GENESIS_PARENT, SubsystemId};
pub trait Subsystem: Send + Sync + 'static { fn submit_txn(&self, id: TxId, payload: &[u8]) -> Result<(), String>; fn reorg(&self) -> Result<(), String>; }
pub struct K1TxnOrdering;
impl K1TxnOrdering {
    pub fn open(root: &Path) -> Result<Self, String>;
    pub fn register_subsystem(&self, subsystem: SubsystemId, after: Option<TxId>, handler: Arc<dyn Subsystem>) -> Result<(), String>;
    pub fn submit_txn(&self, transaction: &[u8]) -> Result<(), SubmitError>;
    pub fn submit_local_txn<F>(&self, timestamp: u64, creator: [u8; 32], subsystem: SubsystemId, payload: &[u8], signer: F) -> Result<Vec<u8>, String> where F: FnOnce(&[u8]) -> Result<[u8; 64], String>;
    pub fn contains(&self, id: TxId) -> bool; pub fn tip(&self) -> Option<TxId>;
    pub fn between_txids(&self, older: TxId, newer: TxId) -> Result<Vec<TxId>, String>;
    pub fn get_txn(&self, id: TxId) -> Result<Option<Vec<u8>>, String>;
}
```
# Chain and storage
`open` owns the canonical-chain root containing `ordering.dat` and `k1-transaction-store/`. An absent root or existing empty real directory is initialized; a complete KTO 0.1.0 root opens without migration. Mixed, wrongly typed, symlinked, or malformed required components are rejected. The caller must provide one live instance and process, no separately opened component handles or external mutation, and a supported local storage device.
Complete transaction bytes become durable before their 32-byte order record. Appends synchronize before publication; a winning fork synchronizes suffix truncation and then its replacement before publication. Removed bytes remain stored. Mutation I/O failure, full or ambiguous storage outcome, external mutation, or missing/corrupt canonical bytes emits one concise secret-free fatal diagnostic and aborts without retry, repair, reopening, callback, or ambiguous return. Definite malformed input, missing parent, losing fork, signer failure, and transaction-ID collision are ordinary errors.
# Submission
`submit_txn` accepts complete peer bytes only after caller validation of signature, leader authority, timestamp, ingress policy, and parent-first catch-up order. Unknown or noncanonical parent returns `SubmitError::MissingParent` without mutation. An exact canonical duplicate succeeds without storage or delivery. A tip extension is stored and ordered. Siblings rank by lower complete creator key, then timestamp, then complete SHA-256 digest; a loser is not stored and a winner replaces the canonical suffix. `GENESIS_PARENT` cannot identify a transaction.
`submit_local_txn` is the leader-local path. Peering supplies timestamp, creator public key, payload, and a one-shot synchronous signer, and owns leader authorization, key correspondence, signature verification, and broadcast. Under the single writer lane, KTO selects the durable tip or `GENESIS_PARENT`, builds and signs once, stores the bytes, appends and publishes the order record, invokes the matching active subsystem, and returns the exact bytes. Signer error precedes storage mutation. `Ok(bytes)` means those bytes were durably committed and published when the commit completed; a later winning peer fork may reorganize them.
Subsystem callback `Err` never changes local or peer submission success; it only puts that registration out of commission. The callback runs synchronously after canonical commitment and before submission returns.
# Subsystems
`register_subsystem` rejects an already active registration. `None` replays from genesis; `Some(id)` requires a canonical transaction assigned to that subsystem and starts after it. Matching transactions load one at a time and deliver oldest-first. State tracks the latest successfully delivered `TxId`, not a global index. Replay callback `Err` stops replay, leaves the registration out of commission at its latest success, and returns a registration error; re-registration may use any valid caller-selected checkpoint.
After a winning reorganization, each active registration whose latest delivered transaction is no longer canonical receives one `reorg` call and becomes out of commission regardless of its result. Unaffected or already out-of-commission registrations receive no notice. The replacement is delivered only if its subsystem remains active. Subscriber state is not persisted.
Callbacks are synchronous, unbounded, unretried, and not panic-isolated; they must not call registration or submission on the same instance. A callback panic propagates and poisons the writer lane, so later registration or submission panics rather than resuming stale state. Already committed canonical reads remain available unless the chain mutex was itself poisoned; chain poison makes every later chain access panic.
# Concurrency, queries, and work
One blocking writer lane serializes local tip selection/signing, local and peer commits, registration/replay, reorganization notifications, and live callbacks. A slow signer or subsystem blocks later writers and registrations. There is no queue, worker thread, timeout, retry, backpressure result, or optimistic re-signing.
Canonical-chain access has a separate mutex released before subsystem callbacks, so `contains`, `tip`, `between_txids`, and `get_txn` may run during an ordinary post-commit callback, while waiting for active chain work or another query. `contains` excludes genesis; `tip` returns the newest canonical ID; `get_txn` returns `None` for noncanonical IDs and aborts on missing or corrupt canonical bytes.
`between_txids` uses exclusive canonical boundaries and returns IDs oldest-first. `older` may be `GENESIS_PARENT`; other boundaries must be canonical. Equal valid boundaries return empty; unknown or reversed boundaries are errors. At most 128 IDs are returned by the canonical chain's documented even-sampling formula.
For canonical length `N`, transaction length `B`, and removed suffix `R`: open is `O(N)` time and memory without payload reads; membership and tip are `O(1)`; bounded-between returns at most 128 IDs; local construction, retrieval, and normal submission are `O(B)` plus persistence; replacement removes `O(R)` memory; registration scans `O(N)` and reads matching transactions. The managed one-million-record root must open in under five seconds. A complete `open` over 100 milliseconds emits exactly one JSON warning containing only module, operation, elapsed microseconds, and `ready` or `error`. No wall-clock guarantee applies to storage, signer, or callback latency.