Skip to content

Latest commit

 

History

History

trinity-stack

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 

README.md

trinity-stack

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.

Reading a stack

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).

What else is here

  • Document::parse and open read a document as served and apply a pointer's rules: a pointer with a key accepts only an envelope that verifies under strict Ed25519.
  • Pointer encodes and decodes the links a QR code carries, and same_stack and stack_id say which stack it is.
  • sign wraps a descriptor in a signed envelope, after checking it.
  • render turns a model, a descriptor without $v, sequence and generation, into a checked descriptor, and returns the PublishState to keep: sequence stays while the content is unchanged and rises when it changes. canonical and content_hash are the rendering and hash it uses.
  • The js feature builds the wasm-bindgen surface @parity/trinity-stack ships, with TypeScript types for the crate's values.
  • light_spec_genesis and Chain::check_light_spec check a light spec against its chain's genesis hash.
  • StackReader refreshes each stack's copy by a Refresh policy: it serves the copy it has, decides when to fetch, and takes the answer, so a descriptor is never a live dependency.
  • Descriptor::web_url gives the URL a dotNS name opens at on the stack's web-gateway.

Every error is one rule of the RFC, and Error::code is its stable name.

Releasing a change

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.

Conformance

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.