Validity policies
- WASM module, scope
transaction(recommended for apps) oraction. - Exports:
memory,lattice_alloc(len: i32) -> i32, andlattice_validate_transaction(ptr: i32, len: i32) -> i32(scopeactionuseslattice_validate_action). - Return
1to accept. Any other value, or a trap, rejects. - Input: the binary
LWPCTXenvelope. It holds the magic bytes, an encoding version, the ABI version, a scope tag, the block height and timestamp, a length-prefixed DAG-CBOR spec, the chain path strings, and the optional body, action and action index. Uselattice_policy_rules::transaction_bodyto extract the DAG-CBOR body. Reject anything you can’t parse. - Integer math only. Modules that use float or SIMD/vector instructions
are rejected by the determinism scan, so
lattice child createrefuses them. - The policy can’t change state, read other keys, hold funds or call other policies. It can’t see the coinbase either.
- There is no fuel limit, but keep the policy fast: every node hosting your chain runs it on every transaction.
Reference it from the spec:
"wasmPolicies": [{ "moduleCID": "<CID printed by your build/check step>", "abiVersion": 1, "scope": "transaction", "entrypoint": "lattice_validate_transaction"}]Rust skeleton
Section titled “Rust skeleton”The rules/ crate in https://github.com/adalinxx/moniker provides the ABI
macro and a body parser. Depend on it by git tag, not by a path:
[lib]crate-type = ["cdylib"]
[dependencies]lattice-policy-rules = { git = "https://github.com/adalinxx/moniker", tag = "<tag>" }
[profile.release]opt-level = "z"lto = truepanic = "abort"#![cfg_attr(not(test), no_std)]use lattice_policy_rules::{transaction_body, Body};lattice_policy_rules::lattice_policy_abi!(crate::validate);
pub fn validate(context: &[u8]) -> bool { transaction_body(context).and_then(|b| { let body = Body::parse(b)?; for action in body.actions()? { let action = action?; // Action { key, old_value, new_value } as &[u8] // Only keys shaped user/<owner>/... are allowed, and <owner> must sign. let rest = action.key.strip_prefix(b"user/")?; let owner = rest.split(|&b| b == b'/').next()?; body.is_signer(owner)?; // Option<()>: None rejects } Some(()) }).is_some()}The crate also provides helpers for tokens (check_token_write,
credited). Read rules/src/lib.rs at the tag you pin.
Build:
cargo build --release --target wasm32-unknown-unknowncp target/wasm32-unknown-unknown/release/<app>_policy.wasm chain/Testing
Section titled “Testing”- Unit-test
validatenatively on hand-built contexts: accept cases, every reject case, and malformed input. - Replay the same contexts through Lattice’s own policy evaluator (for
example, a small Swift package that depends on
Latticeand prints the module CID). The public app repos call thishost-check. - Add a test that fails if the committed
.wasm’s CID differs fromchain/spec.json. - Run an end-to-end test on a throwaway chain path. Sign with a local key, submit, then read the state back.
Source: lattice-skills/skills/lattice-app-builder/references/policy.md at 21a7fca17116