feat(wire): split the frame discriminant into trait and method bytes - #357
feat(wire): split the frame discriminant into trait and method bytes#357decrypto21 wants to merge 20 commits into
Conversation
666a4ad to
78d84eb
Compare
1b83f59 to
9bed8ac
Compare
|
Great work, thank you! enum TruApi {
System(SystemPallet),
}
enum SystemPallet {
Locale(LocaleMethod),
}
enum LocaleMethod {
V1(Request<LocaleRequestPayloadV1, LocaleResponsePayloadV1>),
}
enum Request<Req, Res> {
Request(Req),
Response(Res),
} Same for subscriptions. As I said, I can create proper explanation in RFC. What it gives us:
Why now, not as incremental update later:
|
pgherveou
left a comment
There was a problem hiding this comment.
Codec-2 review: four correctness issues remain around interrupt semantics and legacy codec-1 compatibility behavior.
94d95ff to
6ebb401
Compare
…onIndex and guard it with a typed decode
…o and get_product_context
main independently extended codec 1's flat numbering to 192 via System::host_info, matching this branch's MIN_TRAIT_ID exactly and breaking the invariant that a codec 1 frame can never look like a valid codec 2 trait id. Every trait id shifts +1 (193..207), MAX_CODEC_1_METHOD_ID moves to 192 and MIN_TRAIT_ID to 193 so the floor is strictly above the known ceiling again, and every fixture, golden file, and generated artifact that pinned the old numbers is regenerated or hand-updated to match.
…(trait, method)
… the trait-id floor
6ebb401 to
6d07904
Compare
We are still not aligned with what @johnthecat described here, what he is suggesting is a 3rd u8 to encode the message type (request / response / interrupt) discussed offline Nidish will update the RFC and PR |
# Conflicts: # rust/crates/truapi-codegen/src/rust.rs # rust/crates/truapi-codegen/src/rustdoc.rs # rust/crates/truapi-codegen/src/ts.rs # rust/crates/truapi-macros/src/lib.rs # rust/crates/truapi/src/api/account.rs # rust/crates/truapi/src/api/coin_payment.rs # rust/crates/truapi/src/api/entropy.rs # rust/crates/truapi/src/api/local_storage.rs # rust/crates/truapi/src/api/payment.rs # rust/crates/truapi/src/api/signing.rs # rust/crates/truapi/src/api/statement_store.rs
What
u8s instead of one: the API trait (namespace) and the method within that trait.[SCALE str requestId][u8 id][payload][SCALE str requestId][u8 trait][u8 method][payload]#[wire_trait(id = N)]attribute, carried through the macro, rustdoc extraction, and both emitters. Missing or duplicate trait ids are hard codegen errors; there is no fallback that derives an id from declaration or module order.n/n+1, a subscription takesn..n+3. Append-only becomes a per-trait rule.--codec-versiondefault,scripts/codegen.sh, the generated client constant, and the handshake's acceptance check).system= 1 throughlocale= 16. No floor is reserved against codec 1's old flat numbering: codec 1 already assigned overlapping low ids of its own (e.g. Locale's flat ids 194-197), so a floor could not deliver the isolation it seemed to promise. A codec-1 peer is identified by its handshake codec version, not by trait-id range.Why
u8is the whole address today, so new methods can only append at the global tail. Five traits already have non-contiguous id blocks as a result (accountspans 18-28, 110-112, and 164), and ids 70-75 are permanently burned by the retiredJsonRpctrait. 163 of 255 values are already assigned, and a single subscription costs four slots.Compatibility
UnsupportedProtocolVersionrather than a hang.message_idwith its own codec indices and has no reference to this envelope's discriminant.Nested envelope (RFC 0028)
Folds wire direction (request/response, or a subscription's start/stop/interrupt/receive) and version into the payload itself, so a method costs one wire id instead of two or four:
truapi::versioned::Request<Req, Res>andSubscription<Start, Item, Err>, replacingRequestFrameIds/SubscriptionFrameIdsabove. Same unreleased codec 2 cutover, no third wire-breaking bump before release.Every subscription's
Interruptcarries a real, decodableOption<CallError<...>>, including plain (non-ResultSubscription) methods: the error type is derived structurally from the method's own envelope rather than defaulting to(). A framework-level decline of a host-initiated stream (e.g. an app declining a render) is encoded asInterrupt(Some(CallError::unavailable())), so a conforming peer sees it as a failure rather than a clean completion. A method whose request/item wrapper doesn't resolve to a{Base}Request/{Base}Item-shaped wire envelope is a hard codegen error in both emitters; neither has a directionless fallback payload to emit instead.docs/rfcs/0028-nested-wire-envelope.mdVerification
cargo fmt --all -- --check,cargo clippy --workspace --all-targets --all-features -- -D warnings, andcargo test --workspace --all-featuresare all clean: 1211 passed, 0 failed.js/packages/truapi:tscbuild clean,bun test171 passed, 0 failed.truapi-codegengolden snapshot test (wire_table.rs,dispatcher.rs) passes against freshly regenerated output, nothing hand-edited, everything reproduced via./scripts/codegen.shand blessed from its own.actualdump.dispatcher.rsandclient.tsgeneration, not a silent directionless payload.golden-account-get.binfixture, the handshake andaccount_get_accountwire-equality pins (Rust and TypeScript), and the Rust/TypeScript wire-table parity test all reflect the current (trait, method, version, direction) shape.wasm32-unknown-unknownbuild oftruapi-server, iOS bindings + Swift compile, Playground build/lint/unit, Explorer build/lint; not re-run locally in this pass, left to CI as this repo's own division of verification labor.