CRUMB a card from devarno-cloud

TimelineProof & StateProof Chain

vest intermediate 6 min read

ELI5

A ship’s logbook: each voyage (StateProof) records how many miles sailed and a map of checkpoints (MerkleProof chain). The captain’s logbook (TimelineProof) collects all voyages and is signed on the cover (root_signature). To verify the whole voyage history, check each entry is internally consistent, then verify the cover signature.

Technical Deep Dive

StateProof

StateProof (src/timeline_proof.rs) proves the state of a timeline at a specific point:

FieldTypeValidity constraint
timeline_idStringnone
state_hashVec<u8>non-empty, exactly 32 bytes
operation_countu64must be > 0
timestamp_msu64none
proof_chainVec<MerkleProof>all proofs must pass MerkleProof::verify()

verify() returns true iff !state_hash.is_empty() && state_hash.len() == 32 && operation_count > 0 && proof_chain.iter().all(|p| p.verify()).

size_bytes() = 32 (state_hash) + sum of all MerkleProof sizes.

TimelineProof

TimelineProof collects ordered StateProof objects and anchors them with a root AuditSignature:

classDiagram
class TimelineProof {
+timeline_id: String
+state_proofs: Vec~StateProof~
+root_signature: AuditSignature
+proof_depth: u32
+add_state_proof(StateProof) Result
+verify_chain() bool
+hash() Vec~u8~
+size_bytes() usize
}
class StateProof {
+timeline_id: String
+state_hash: Vec~u8~
+operation_count: u64
+timestamp_ms: u64
+proof_chain: Vec~MerkleProof~
+verify() bool
+add_proof(MerkleProof)
}
TimelineProof "1" *-- "0..*" StateProof

add_state_proof rejects an invalid StateProof (returns Err("Invalid state proof")); otherwise pushes it and updates proof_depth = state_proofs.len().max(proof_depth).

verify_chain = root_signature.verify_chain() && all state_proofs pass verify().

Sealing Sequence

sequenceDiagram
participant C as Caller
participant TP as TimelineProof
participant SP as StateProof
C->>SP: new(timeline_id, state_hash[32], op_count>0, ts)
SP->>SP: verify() → true
C->>TP: add_state_proof(sp)
TP->>SP: verify()
SP-->>TP: true
TP->>TP: push, proof_depth = max(len, depth)
TP-->>C: Ok(())
C->>TP: verify_chain()
TP->>TP: root_signature.verify_chain()
TP->>SP: verify() for each
TP-->>C: true

Size Accounting

TimelineProof::size_bytes() = sum of all StateProof sizes + 96 (fixed overhead for root_signature and timeline_id). This 96-byte constant is hard-coded; the proto wire size will differ.

Timeline Hash

TimelineProof::hash() XOR-folds state_hash bytes from each StateProof, then XOR-folds root_signature.proof.signature_bytes — a 32-byte deterministic fingerprint of the complete timeline.

Key Terms

  • StateProof → snapshot proof: (state_hash, operation_count, MerkleProof chain) at a point in time
  • TimelineProof → ordered collection of StateProofs anchored by root_signature
  • proof_depth → high-water mark of state_proofs.len(); updated via .max() on each add
  • verify_chain → validates root_signature chain AND every StateProof.verify()
  • SealedTimeline → production proto type; adds final_root, witnesses, and seal_reason

Q&A

Q: Can proof_depth ever decrease after add_state_proof calls? A: No. It is updated with .max(self.proof_depth) so it only grows.

Q: What happens if add_state_proof is called with operation_count = 0? A: StateProof::verify() returns false, causing add_state_proof to return Err("Invalid state proof"). The proof is not added and proof_depth does not change.

Q: Does TimelineProof::hash() include timestamps or operation counts from the StateProofs? A: No — only state_hash bytes and root_signature.proof.signature_bytes are folded in.

Examples

Building and verifying a two-state timeline:

let sig_proof = SignatureProof::new("alice".into(), vec![1u8;64], vec![2u8;32], 0);
let root_sig = AuditSignature::new(sig_proof, vec![3u8;32], 1);
let mut tp = TimelineProof::new("tl-001".into(), root_sig);
let sp1 = StateProof::new("tl-001".into(), vec![1u8;32], 10, 1000);
let sp2 = StateProof::new("tl-001".into(), vec![2u8;32], 20, 2000);
tp.add_state_proof(sp1).unwrap();
tp.add_state_proof(sp2).unwrap();
assert_eq!(tp.proof_depth, 2);
assert!(tp.verify_chain());
assert!(tp.size_bytes() > 96);

neighbors on the map