kcode-k1-transaction 0.1.0

Canonical K1 transaction field parsing
Documentation
# Public API

```rust
use kcode_k1_transaction_store::TxId;

pub const SUBSYSTEM_BYTES: usize = 20;
pub const PUBLIC_KEY_BYTES: usize = 32;
pub const SIGNATURE_BYTES: usize = 64;
pub const MIN_TRANSACTION_BYTES: usize = 136;
pub const GENESIS_PARENT: TxId = TxId::from_bytes([0xff; 12]);

#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SubsystemId;

impl SubsystemId {
    pub fn from_bytes(bytes: [u8; SUBSYSTEM_BYTES]) -> Result<Self, String>;
    pub fn from_str(value: &str) -> Result<Self, String>;
    pub const fn as_bytes(&self) -> &[u8; SUBSYSTEM_BYTES];
    pub fn as_str(&self) -> &str;
}

pub struct Transaction<'a>;

impl<'a> Transaction<'a> {
    pub fn parse(bytes: &'a [u8]) -> Result<Self, String>;
    pub fn parent(&self) -> TxId;
    pub fn timestamp(&self) -> u64;
    pub fn creator(&self) -> &[u8; PUBLIC_KEY_BYTES];
    pub fn subsystem(&self) -> SubsystemId;
    pub fn payload(&self) -> &'a [u8];
    pub fn signature(&self) -> &'a [u8; SIGNATURE_BYTES];
    pub fn signing_bytes(&self) -> &'a [u8];
}
```

`SubsystemId` is valid UTF-8 occupying exactly 20 bytes. It has no padding or terminator. Both constructors reject values whose encoded byte length is not exactly 20, and `from_bytes` also rejects invalid UTF-8.

A transaction has this wire layout:

| Byte range | Value |
| --- | --- |
| `0..12` | Parent transaction ID |
| `12..20` | Little-endian `u64` Unix timestamp in seconds |
| `20..52` | Ed25519 public key |
| `52..72` | Subsystem ID |
| `72..len - 64` | Payload |
| `len - 64..len` | Ed25519 signature |

Transactions are at least 136 bytes. An empty payload is valid. The signature covers every preceding byte, and `signing_bytes` returns that exact prefix. `GENESIS_PARENT` is a sentinel and does not identify a real transaction.

`Transaction::parse` borrows the supplied bytes. It validates only the minimum length and subsystem encoding. It does not validate the signature, creator authority, timestamp, leadership, payload, parent existence, or policy.

The library has no shared mutable state, locks, queues, background work, persistence, or internal concurrency owner. Independent operations never coordinate or wait for one another. All successful operations use bounded constant work independent of payload length and allocate no memory. Accessors are constant-time borrowed views or fixed-size value copies. Errors are returned immediately, with allocation limited to the returned error string. A slow or blocked caller cannot stall an unrelated caller.