kcode-k1-transaction 0.3.0

Canonical K1 transaction parsing and construction
Documentation
# Consumer contract

```rust
use kcode_k1_transaction_store::TxId;

pub const GENESIS_PARENT: TxId = TxId::from_bytes([0xff; 12]);
pub const REGISTER_AT_TIP: TxId = TxId::from_bytes([
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
]);

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

impl SubsystemId {
    pub fn from_bytes(bytes: [u8; 20]) -> Result<Self, String>;
    pub fn from_str(value: &str) -> Result<Self, String>;
    pub const fn as_bytes(&self) -> &[u8; 20];
    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; 32];
    pub fn subsystem(&self) -> SubsystemId;
    pub fn payload(&self) -> &'a [u8];
}

pub fn build_signed_transaction<F>(
    parent: TxId,
    timestamp: u64,
    creator: [u8; 32],
    subsystem: SubsystemId,
    payload: &[u8],
    signer: F,
) -> Result<Vec<u8>, String>
where
    F: FnOnce(&[u8]) -> Result<[u8; 64], String>;
```

`GENESIS_PARENT` is the all-`0xff` transaction-parent sentinel and does not identify a real transaction. `REGISTER_AT_TIP` is only a subsystem-registration cursor requesting activation at the current canonical tip; it is not a transaction parent or real transaction identity.

`SubsystemId` is a nonempty UTF-8 name of at most 20 encoded bytes with no NUL. Its canonical representation is the logical bytes followed by zero padding to 20 bytes. `from_str` creates that form. `from_bytes` accepts a valid 20-byte UTF-8 name with no NUL or a nonempty valid UTF-8 prefix followed only by zeroes; it rejects all-zero input, invalid UTF-8, and nonzero bytes after the first zero. `as_str` returns the logical name and `as_bytes` the canonical representation.

Transactions contain, in order, a 12-byte parent ID, little-endian `u64` Unix timestamp in seconds, 32-byte Ed25519 public key, canonical 20-byte subsystem ID, payload, and 64-byte Ed25519 signature. Their minimum length is 136 bytes and an empty payload is valid.

`parse` borrows its input and validates only minimum length and canonical subsystem encoding. It does not validate the signature, creator or leader authority, timestamp policy, payload, parent existence, storage, or ingress policy. The accessors return the decoded fields, and `payload` excludes the final 64-byte signature.

`build_signed_transaction` accepts every payload whose `136 + payload.len()` output is representable and allocatable. It encodes the fields, invokes `signer` exactly once with the exact parent-through-payload prefix, and appends the returned signature unchanged. Length overflow or the signer's error is returned as a `String`; signer failure discards the partial output. The function performs no signature verification, clock or key lookup, authority decision, storage, or other I/O.

All operations are synchronous and stateless. Independent calls do not coordinate except through caller-supplied signer code, and the library performs no retries or timeouts.

Performance: Not yet benchmarked; `SubsystemId::from_bytes` examines exactly 20 bytes with bounded constant work and no successful-call allocation or I/O.

Performance: Not yet benchmarked; `SubsystemId::from_str` examines at most 20 bytes with bounded constant work and no successful-call allocation or I/O.

Performance: Not yet benchmarked; `SubsystemId::as_bytes` uses bounded constant work with no allocation or I/O.

Performance: Not yet benchmarked; `SubsystemId::as_str` examines at most 20 bytes with bounded constant work and no allocation or I/O.

Performance: Not yet benchmarked; `Transaction::parse` examines fixed-width fields with bounded constant work independent of transaction length and no successful-call allocation or I/O.

Performance: Not yet benchmarked; `Transaction::parent` uses bounded constant work with no allocation or I/O.

Performance: Not yet benchmarked; `Transaction::timestamp` uses bounded constant work with no allocation or I/O.

Performance: Not yet benchmarked; `Transaction::creator` uses bounded constant work with no allocation or I/O.

Performance: Not yet benchmarked; `Transaction::subsystem` uses bounded constant work with no allocation or I/O.

Performance: Not yet benchmarked; `Transaction::payload` uses bounded constant work with no allocation or I/O.

Performance: Not yet benchmarked; `build_signed_transaction` performs O(n) work for n payload bytes, copies the payload once into one exact-capacity `136 + n` allocation, and makes one inline signer call whose work and completion are caller-owned.