open-standards/smart-byte/smart-byte-rs/crates/smart-byte-state/tests/lifecycle.rs
Transaction Science f9fbd5a3af
Some checks failed
smart-byte conformance gate / Validate conformance JSON files (pull_request) Successful in 4s
smart-byte conformance gate / Second implementation reproduces every vector (pull_request) Successful in 5s
smart-byte conformance gate / Spec changes require vector-pack updates (pull_request) Successful in 4s
smart-byte conformance gate / VERSIONS.md and README.md must exist (pull_request) Successful in 3s
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 4s
standards workspaces build + test / eoc-rs (pull_request) Successful in 1m51s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m24s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m41s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m10s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 3m38s
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 7m45s
standards workspaces build + test / aac-rs (pull_request) Successful in 4m3s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 1m51s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 2m3s
standards workspaces build + test / map-rs (pull_request) Successful in 5m27s
standards workspaces build + test / mesh-rs (pull_request) Successful in 2m19s
standards workspaces build + test / openpay (pull_request) Successful in 10m49s
standards workspaces build + test / arl-rs (pull_request) Successful in 47m4s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 46m44s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 42m33s
standards workspaces build + test / joulehook-rs (pull_request) Failing after 18m22s
standards workspaces build + test / sense-rs (pull_request) Successful in 10m25s
standards workspaces build + test / macOS energy backend (jcp-energy) (pull_request) Successful in 4m56s
smart-byte: governance, burns and transfers between clusters, with vectors
smart-byte-state implements cluster_state.md:
- governance: Change and Proposal, electorates, the quorum, the time lock;
- burns: Burn and the retired record;
- exports: ExportStatement and Certificate;
- the import checks.

smart-byte-core: Transition::sign_export and the destination member. The
envelope compares frames within a cluster only.

smart-byte-lockstep: the committing nodes sign each committed frame's export
certificates. The certificates are built from a committing node's report,
never from a node whose state diverged.

state_vectors.json grows to 23 vectors (state-013 to state-023 new,
state-011 extended) and is regenerated in place; v1 is DRAFT. Every new check
has a vector that fails an implementation leaving it out. The README
documents the 14 new error classes, and the regeneration log records the
change.

crosscheck/verify_v1.py implements governance, burns, exports and imports
from the spec text. It reproduces every vector, 117 of them, both plain and
under -O.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-01 11:48:44 -04:00

491 lines
16 KiB
Rust

//! Governance, burns, and bytes moving between clusters.
use smart_byte_core::keys::IndexedSignature;
use smart_byte_core::{Aid, Cargo, Envelope, JouleCost, Said, SigningKey};
use smart_byte_keri_witness::{Controller, KeyEvent, KeyPair, Threshold};
use smart_byte_state::certificate::node_key;
use smart_byte_state::{Approval, Burn, Certificate, Change, Input, Proposal, State};
fn key(label: &str) -> KeyPair {
KeyPair::from_signing(SigningKey::from_bytes(&Said::hash(label.as_bytes()).0))
}
/// A one-key controller and the signed events of its log so far.
struct Party {
c: Controller,
log: Vec<(KeyEvent, Vec<IndexedSignature>)>,
}
impl Party {
fn new(label: &str) -> Self {
let (c, icp) = Controller::incept(
vec![key(&format!("{label}-0"))],
vec![key(&format!("{label}-1"))],
Threshold(1),
Threshold(1),
vec![],
Threshold(0),
)
.unwrap();
let event = KeyEvent::Inception(icp);
let sigs = sigs(&c, &event);
Party {
c,
log: vec![(event, sigs)],
}
}
fn aid(&self) -> Aid {
self.c.aid
}
fn incept(&self) -> Vec<u8> {
Input::key_event(&self.log[0].0, &self.log[0].1)
}
fn approve(&self, proposal: &Proposal) -> Approval {
Approval {
approver: self.aid(),
signature: self
.c
.signer()
.sign(&smart_byte_core::wire::to_vec(proposal).unwrap()),
}
}
}
fn sigs(c: &Controller, event: &KeyEvent) -> Vec<IndexedSignature> {
c.sign_event(event)
.unwrap()
.into_iter()
.map(|(index, signature)| IndexedSignature {
index: u64::from(index),
signature,
})
.collect()
}
fn classes(report: &smart_byte_state::FrameReport) -> Vec<Result<(), &'static str>> {
report
.results
.iter()
.map(|r| r.outcome.as_ref().map(|_| ()).map_err(|e| e.class()))
.collect()
}
fn sorted(mut v: Vec<Result<(), &'static str>>) -> Vec<Result<(), &'static str>> {
v.sort();
v
}
const USD: u64 = 1;
fn cluster(label: &str) -> Said {
Said::hash(label.as_bytes())
}
#[test]
fn an_issuer_is_added_by_the_others_and_only_after_the_delay() {
let (a, b, c, new) = (
Party::new("a"),
Party::new("b"),
Party::new("c"),
Party::new("new"),
);
let id = cluster("one");
let mut state = State::genesis(id, [(a.aid(), USD), (b.aid(), USD), (c.aid(), USD)], [], 2);
state.apply_frame(&[a.incept(), b.incept(), c.incept(), new.incept()]);
let proposal = Proposal {
cluster: id,
sequence: 0,
change: Change::AddIssuer {
cargo_type: USD,
issuer: new.aid(),
},
};
// Three issuers: the quorum is floor(2·3/3)+1 = 3.
let two = Input::governance(&proposal, &[a.approve(&proposal), b.approve(&proposal)]);
let three = Input::governance(
&proposal,
&[
a.approve(&proposal),
b.approve(&proposal),
c.approve(&proposal),
],
);
assert_eq!(
classes(&state.apply_frame(&[two])),
vec![Err("below_quorum")]
);
assert_eq!(classes(&state.apply_frame(&[three.clone()])), vec![Ok(())]);
assert_eq!(state.governance.pending.len(), 1);
assert_eq!(state.governance.pending[0].effective, 2 + 1 + 2);
// The same proposal again: its sequence number is spent.
assert_eq!(
classes(&state.apply_frame(&[three])),
vec![Err("stale_sequence")]
);
let mint = |label: &str, frame_note: &str| {
let e = Envelope::issue(
&new.c.signer(),
a.aid(),
[frame_note.len() as u8; 16],
Cargo::usd_micros(1),
JouleCost::default(),
);
let _ = label;
Input::mint(&e.provenance)
};
// Frame 4: not yet in force.
assert_eq!(state.frame, 4);
assert_eq!(
classes(&state.apply_frame(&[mint("early", "x")])),
vec![Err("issuer_not_registered")]
);
// Frame 5: in force from its start.
assert_eq!(
classes(&state.apply_frame(&[mint("in force", "xy")])),
vec![Ok(())]
);
assert!(state.authorizes(&new.aid(), USD) && state.governance.pending.is_empty());
}
#[test]
fn governance_is_checked_before_it_is_counted() {
let (a, b, outsider) = (Party::new("a"), Party::new("b"), Party::new("outsider"));
let id = cluster("one");
let mut state = State::genesis(id, [(a.aid(), USD), (b.aid(), USD)], [], 0);
state.apply_frame(&[a.incept(), b.incept(), outsider.incept()]);
let p = |change: Change, sequence: u64, cluster: Said| Proposal {
cluster,
sequence,
change,
};
let add = |issuer: Aid| Change::AddIssuer {
cargo_type: USD,
issuer,
};
let remove = |issuer: Aid| Change::RemoveIssuer {
cargo_type: USD,
issuer,
};
let elsewhere = p(add(outsider.aid()), 0, cluster("two"));
let stale = p(add(outsider.aid()), 7, id);
let registered = p(add(a.aid()), 0, id);
let unregistered = p(remove(outsider.aid()), 0, id);
let by_outsider = p(add(outsider.aid()), 0, id);
let twice = p(add(outsider.aid()), 0, id);
let report = state.apply_frame(&[
Input::governance(&elsewhere, &[a.approve(&elsewhere), b.approve(&elsewhere)]),
Input::governance(&stale, &[a.approve(&stale), b.approve(&stale)]),
Input::governance(
&registered,
&[a.approve(&registered), b.approve(&registered)],
),
Input::governance(
&unregistered,
&[a.approve(&unregistered), b.approve(&unregistered)],
),
Input::governance(
&by_outsider,
&[a.approve(&by_outsider), outsider.approve(&by_outsider)],
),
Input::governance(&twice, &[a.approve(&twice), a.approve(&twice)]),
]);
assert_eq!(
sorted(classes(&report)),
sorted(vec![
Err("wrong_cluster"),
Err("stale_sequence"),
Err("already_registered"),
Err("not_registered"),
Err("not_an_elector"),
Err("duplicate_approver"),
])
);
assert_eq!(state.governance.sequence, 0);
}
#[test]
fn an_issuer_cannot_block_its_own_removal() {
let (a, b, c) = (Party::new("a"), Party::new("b"), Party::new("c"));
let id = cluster("one");
let mut state = State::genesis(id, [(a.aid(), USD), (b.aid(), USD), (c.aid(), USD)], [], 0);
state.apply_frame(&[a.incept(), b.incept(), c.incept()]);
let proposal = Proposal {
cluster: id,
sequence: 0,
change: Change::RemoveIssuer {
cargo_type: USD,
issuer: c.aid(),
},
};
// The electorate is the other two; both approve. The target's vote is
// not counted, and not needed.
let with_target = Input::governance(&proposal, &[a.approve(&proposal), c.approve(&proposal)]);
assert_eq!(
classes(&state.apply_frame(&[with_target])),
vec![Err("not_an_elector")]
);
let others = Input::governance(&proposal, &[a.approve(&proposal), b.approve(&proposal)]);
assert_eq!(classes(&state.apply_frame(&[others])), vec![Ok(())]);
state.apply_frame(&[]);
assert!(!state.authorizes(&c.aid(), USD));
}
#[test]
fn redemption_is_a_return_to_the_issuer_and_a_burn() {
let (issuer, alice) = (Party::new("issuer"), Party::new("alice"));
let mut state = State::genesis(cluster("one"), [(issuer.aid(), USD)], [], 0);
let byte = Envelope::issue(
&issuer.c.signer(),
alice.aid(),
[1; 16],
Cargo::usd_micros(5),
JouleCost::default(),
);
let mint = Input::mint(&byte.provenance);
state.apply_frame(&[issuer.incept(), alice.incept(), mint.clone()]);
let id = byte.identity();
// Alice returns it; the issuer burns it in the same frame: burns apply
// after transfers.
let back = byte.transfer(&alice.c.signer(), issuer.aid(), 1);
let t = back.ownership_chain.last().unwrap();
let burn = Burn::sign(&issuer.c.signer(), id, back.history_hash(), 1);
let by_alice = Burn::sign(&alice.c.signer(), id, back.history_hash(), 1);
let report = state.apply_frame(&[
Input::burn(&burn),
Input::burn(&by_alice),
Input::transfer(&id, t),
]);
assert_eq!(
sorted(classes(&report)),
sorted(vec![Ok(()), Ok(()), Err("not_current_owner")])
);
assert!(state.bytes.is_empty());
assert_eq!(state.retired[&id].owner, issuer.aid());
// Burned for good: no transfer, no re-mint.
let after = Burn::sign(&issuer.c.signer(), id, back.history_hash(), 2);
assert_eq!(
sorted(classes(&state.apply_frame(&[Input::burn(&after), mint]))),
vec![Err("byte_retired"), Err("duplicate_byte")]
);
}
/// Two clusters, each with four nodes, peering with each other.
struct Federation {
a: State,
b: State,
a_nodes: Vec<SigningKey>,
b_nodes: Vec<SigningKey>,
}
fn federation(registry: &[(Aid, u64)]) -> Federation {
let nodes = |label: &str| {
(0..4)
.map(|i| SigningKey::from_bytes(&Said::hash(format!("{label}-node-{i}").as_bytes()).0))
.collect::<Vec<_>>()
};
let (a_nodes, b_nodes) = (nodes("a"), nodes("b"));
let keys = |n: &[SigningKey]| n.iter().map(node_key).collect::<Vec<_>>();
Federation {
a: State::genesis(
cluster("A"),
registry.iter().copied(),
[(cluster("B"), keys(&b_nodes))],
0,
),
b: State::genesis(
cluster("B"),
registry.iter().copied(),
[(cluster("A"), keys(&a_nodes))],
0,
),
a_nodes,
b_nodes,
}
}
fn certify(
statement: smart_byte_state::ExportStatement,
nodes: &[SigningKey],
signers: &[u64],
) -> Certificate {
let picked: Vec<(u64, &SigningKey)> =
signers.iter().map(|i| (*i, &nodes[*i as usize])).collect();
Certificate::sign(statement, &picked)
}
#[test]
fn a_byte_moves_between_clusters_and_back() {
let (issuer, alice, bob) = (Party::new("issuer"), Party::new("alice"), Party::new("bob"));
let mut f = federation(&[(issuer.aid(), USD)]);
let byte = Envelope::issue(
&issuer.c.signer(),
alice.aid(),
[1; 16],
Cargo::usd_micros(5),
JouleCost::default(),
);
let id = byte.identity();
f.a.apply_frame(&[
issuer.incept(),
alice.incept(),
Input::mint(&byte.provenance),
]);
f.b.apply_frame(&[bob.incept()]);
// Alice sends it to Bob, in cluster B.
let out = byte.export(&alice.c.signer(), bob.aid(), 1, cluster("B"));
let report =
f.a.apply_frame(&[Input::transfer(&id, out.ownership_chain.last().unwrap())]);
assert_eq!(classes(&report), vec![Ok(())]);
assert!(f.a.bytes.is_empty() && f.a.exported.contains_key(&id));
let statement = report.exports[0].clone();
// A transfer of a byte that has left is refused.
let stale = byte.transfer(&alice.c.signer(), issuer.aid(), 2);
assert_eq!(
classes(&f.a.apply_frame(&[Input::transfer(&id, stale.ownership_chain.last().unwrap())])),
vec![Err("byte_exported")]
);
let logs = vec![issuer.log.clone(), alice.log.clone()];
let import = |cert: &Certificate, logs: &[Vec<(KeyEvent, Vec<IndexedSignature>)>]| {
Input::import(cert, &out, logs)
};
let three = certify(statement.clone(), &f.a_nodes, &[0, 1, 2]);
let two = certify(statement.clone(), &f.a_nodes, &[0, 2]);
let missing_log = import(&three, &logs[..1]);
let report = f.b.apply_frame(&[import(&two, &logs), missing_log]);
assert_eq!(
sorted(classes(&report)),
vec![Err("below_threshold"), Err("unknown_key_state")]
);
// With three of A's four nodes and every log the history needs: applied.
assert_eq!(
classes(&f.b.apply_frame(&[import(&three, &logs)])),
vec![Ok(())]
);
assert_eq!(f.b.bytes[&id].owner, bob.aid());
assert_eq!(f.b.bytes[&id].history_hash, out.history_hash());
// Once.
assert_eq!(
classes(&f.b.apply_frame(&[import(&three, &logs)])),
vec![Err("duplicate_import")]
);
// Bob sends it back to Alice in A; A takes it back over its own export.
let home = out.export(&bob.c.signer(), alice.aid(), f.b.frame, cluster("A"));
let report =
f.b.apply_frame(&[Input::transfer(&id, home.ownership_chain.last().unwrap())]);
assert_eq!(classes(&report), vec![Ok(())]);
let cert = certify(report.exports[0].clone(), &f.b_nodes, &[1, 2, 3]);
let logs = vec![issuer.log.clone(), alice.log.clone(), bob.log.clone()];
assert_eq!(
classes(&f.a.apply_frame(&[Input::import(&cert, &home, &logs)])),
vec![Ok(())]
);
assert!(f.a.bytes.contains_key(&id) && !f.a.exported.contains_key(&id));
// A's first certificate, replayed in A, is not for A.
assert_eq!(
classes(&f.a.apply_frame(&[import(&three, &[issuer.log.clone(), alice.log.clone()])])),
vec![Err("wrong_cluster")]
);
}
#[test]
fn an_import_must_be_the_byte_certified() {
let (issuer, alice, bob) = (Party::new("issuer"), Party::new("alice"), Party::new("bob"));
let mut f = federation(&[(issuer.aid(), USD)]);
let byte = Envelope::issue(
&issuer.c.signer(),
alice.aid(),
[1; 16],
Cargo::usd_micros(5),
JouleCost::default(),
);
let id = byte.identity();
f.a.apply_frame(&[
issuer.incept(),
alice.incept(),
Input::mint(&byte.provenance),
]);
let out = byte.export(&alice.c.signer(), bob.aid(), 1, cluster("B"));
let report =
f.a.apply_frame(&[Input::transfer(&id, out.ownership_chain.last().unwrap())]);
let cert = certify(report.exports[0].clone(), &f.a_nodes, &[0, 1, 2]);
let logs = vec![issuer.log.clone(), alice.log.clone()];
// The envelope before the export: not the history A certified.
let report = f.b.apply_frame(&[Input::import(&cert, &byte, &logs)]);
assert_eq!(classes(&report), vec![Err("certificate_mismatch")]);
// A certificate from a cluster B does not peer with.
let mut stranger = f.b.clone();
stranger.peers.clear();
assert_eq!(
classes(&stranger.apply_frame(&[Input::import(&cert, &out, &logs)])),
vec![Err("unknown_cluster")]
);
// An export to a cluster A does not peer with is refused in A.
let lost = byte.export(&alice.c.signer(), bob.aid(), 2, cluster("nowhere"));
let mut a = f.a.clone();
a.exported.clear();
a.bytes.insert(
id,
smart_byte_state::ByteRecord {
issuer: issuer.aid(),
cargo: Cargo::usd_micros(5),
minted: 0,
owner: alice.aid(),
history_hash: id.0,
},
);
assert_eq!(
classes(&a.apply_frame(&[Input::transfer(&id, lost.ownership_chain.last().unwrap())])),
vec![Err("unknown_cluster")]
);
}
#[test]
fn peers_change_by_governance() {
let (a, b) = (Party::new("a"), Party::new("b"));
let id = cluster("one");
let mut state = State::genesis(id, [(a.aid(), USD), (b.aid(), 2)], [], 0);
state.apply_frame(&[a.incept(), b.incept()]);
let node = SigningKey::from_bytes(&[9; 32]);
let proposal = Proposal {
cluster: id,
sequence: 0,
change: Change::SetPeer {
peer: cluster("two"),
keys: vec![node_key(&node)],
},
};
// Peer changes are every issuer's business: both must approve.
let one = Input::governance(&proposal, &[a.approve(&proposal)]);
let both = Input::governance(&proposal, &[a.approve(&proposal), b.approve(&proposal)]);
assert_eq!(
classes(&state.apply_frame(&[one])),
vec![Err("below_quorum")]
);
assert_eq!(classes(&state.apply_frame(&[both])), vec![Ok(())]);
state.apply_frame(&[]);
assert!(state.peers.contains_key(&cluster("two")));
// A peer's keys must be keys.
let bad = Proposal {
cluster: id,
sequence: 1,
change: Change::SetPeer {
peer: cluster("three"),
keys: vec![smart_byte_core::PrefixedKey(vec![1, 2, 3])],
},
};
assert_eq!(
classes(
&state.apply_frame(&[Input::governance(&bad, &[a.approve(&bad), b.approve(&bad)])])
),
vec![Err("malformed_verifying_key")]
);
}