Reads, verifies and signs Trinity stack descriptors, as the Trinity stack descriptor RFC defines them. A descriptor is one JSON file that says which chains and services make up a stack, such as previewnet, so hosts and tools read the same facts instead of keeping their own copies.
The crate holds every rule a reader applies, so the host core, truapi-host stack and @parity/trinity-stack (this
crate compiled to wasm) accept and refuse the same descriptors. It has no I/O: callers fetch, store and keep time.
use trinity_stack::{Fetched, Pointer, accept};
# fn fetch(_: &str) -> Option<Vec<u8>> { None }
# fn load() -> Option<Vec<u8>> { None }
# fn save(_: &[u8]) {}
# fn now_unix_seconds() -> i64 { 0 }
let pointer = Pointer::from_link("polkadotapp://stack?key=0x3b6a…&url=https%3A%2F%2F…")?;
let fetched = fetch(pointer.url().as_str());
let stored = load(); // the last `accepted.document`, or the copy a build bundles
let accepted = accept(
&pointer,
stored.as_deref(),
fetched.as_deref().map_or(Fetched::Unreachable, Fetched::Document),
now_unix_seconds(),
)?;
save(&accepted.document); // keyed by `pointer.stack_id()`
let chains = &accepted.stack.descriptor.chains;
# Ok::<(), trinity_stack::Error>(())accept refuses a signed descriptor with a lower sequence than the stored one, keeps the stored copy when the fetch
fails or breaks a rule, and reports a new generation (drop cached chain data), stale (past validUntil) and
expired (past expiresAt).
Document::parseandopenread a document as served and apply a pointer's rules: a pointer with a key accepts only an envelope that verifies under strict Ed25519.Pointerencodes and decodes the links a QR code carries, andsame_stackandstack_idsay which stack it is.signwraps a descriptor in a signed envelope, after checking it.renderturns a model, a descriptor without$v,sequenceandgeneration, into a checked descriptor, and returns thePublishStateto keep:sequencestays while the content is unchanged and rises when it changes.canonicalandcontent_hashare the rendering and hash it uses.- The
jsfeature builds the wasm-bindgen surface@parity/trinity-stackships, with TypeScript types for the crate's values. light_spec_genesisandChain::check_light_speccheck a light spec against its chain's genesis hash.StackReaderrefreshes each stack's copy by aRefreshpolicy: it serves the copy it has, decides when to fetch, and takes the answer, so a descriptor is never a live dependency.Descriptor::web_urlgives the URL a dotNS name opens at on the stack'sweb-gateway.
Every error is one rule of the RFC, and Error::code is its stable name.
truapi-host stack links this crate and ships with @parity/truapi, and @parity/trinity-stack ships it as wasm. A
change here reaches both only if its changeset names both packages.
vectors/cases.json holds a case for every rule, generated by tests/vectors.rs and run by every implementation.
vectors/refresh.json holds the reference StackReader's refresh cases, which fix choices the RFC leaves to each
reader and are informative. After a deliberate change, rerun with
TRINITY_STACK_WRITE_VECTORS=1 cargo test -p trinity-stack --test vectors.
Each case breaks one rule, so the expected code does not depend on the order a reader checks them in. The one exception
is a non-canonical key or R: no signature can verify under a point nobody knows the discrete log of, so those cases
also fail the equation, and a reader passes them by checking encodings before the equation.
schema/trinity-stack.v1.schema.json describes the descriptor's shape.