jar_kernel/state.rs
1//! σ — the v3 chain state.
2//!
3//! σ is a `CacheDirectory` plus the validator set. Data blobs, image
4//! blobs, cnode blobs, and chain Instance blobs all live in the cache
5//! as `Cap` values, addressed by content hash.
6//!
7//! The state root is the SSZ `hash_tree_root` of the cache's blobs,
8//! each represented as a `(blob_hash, cap_hash)` leaf container.
9
10use javm_cap::{CacheDirectory, CapHash, cap_hash};
11use ssz::{Encode, HashTreeRoot};
12
13/// PoA validator key (placeholder — 32-byte public key).
14pub type ValidatorKey = [u8; 32];
15
16/// One state-root leaf: a `(blob_hash, cap_hash)` pair.
17///
18/// Encoded as the SSZ container `{ blob_hash: [u8;32], cap_hash: [u8;32] }`.
19/// The leaf's `hash_tree_root` is `merkleize([blob_hash, cap_hash], 2) =
20/// hash(blob_hash || cap_hash)` — equivalent to the legacy BMT leaf
21/// protocol but with SHA-256 instead of Blake2b.
22#[derive(Clone, Copy, Debug, PartialEq, Eq, Encode, HashTreeRoot)]
23pub struct StateLeaf {
24 pub blob_hash: CapHash,
25 pub cap_hash: CapHash,
26}
27
28/// The chain's σ-resident state.
29///
30/// All cap content lives in `caps` (a `CacheDirectory`). The validator
31/// set is kept alongside as a Vec for now; future revisions may move
32/// it into a dedicated registry cap.
33pub struct State {
34 pub caps: CacheDirectory,
35 pub validators: Vec<ValidatorKey>,
36}
37
38impl State {
39 pub fn new() -> Self {
40 Self {
41 caps: CacheDirectory::new(),
42 validators: Vec::new(),
43 }
44 }
45}
46
47impl Default for State {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53/// State-root: SSZ `hash_tree_root` of the cache's blobs.
54///
55/// Each leaf is a `StateLeaf { blob_hash, cap_hash }` container. The
56/// full state root is `hash_tree_root` of the `Vec<StateLeaf>`, which
57/// merkleizes the per-leaf roots, pads to the next power of two, and
58/// mixes in the length per SSZ `List` semantics.
59///
60/// Leaves are sorted by `blob_hash` explicitly so the result is
61/// independent of insertion order (the cap store is a HashMap, whose
62/// iteration order is unspecified). Empty caches reduce to the SSZ
63/// canonical empty-list root.
64pub fn state_root(state: &State) -> CapHash {
65 let mut leaves: Vec<StateLeaf> = state
66 .caps
67 .iter_blobs()
68 .into_iter()
69 .map(|(hash, cap)| StateLeaf {
70 blob_hash: hash,
71 cap_hash: cap_hash(&cap),
72 })
73 .collect();
74 leaves.sort_by_key(|l| l.blob_hash);
75 ssz::hash_tree_root(&leaves)
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 #[test]
83 fn empty_state_root_is_deterministic() {
84 let s1 = State::new();
85 let s2 = State::new();
86 assert_eq!(state_root(&s1), state_root(&s2));
87 }
88
89 #[test]
90 fn state_root_changes_with_published_data() {
91 let s = State::new();
92 let r0 = state_root(&s);
93 s.caps
94 .put_cap(&javm_cap::Cap::data_inline(b"hello"))
95 .unwrap();
96 let r1 = state_root(&s);
97 assert_ne!(r0, r1);
98 }
99}