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-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]>
150 lines
4.8 KiB
Rust
150 lines
4.8 KiB
Rust
//! The ownership chain: signature-bound transitions (treatise §8.4, §10.1).
|
|
//!
|
|
//! Each transition moves the byte from its current owner to a new one at a
|
|
//! lockstep frame. The owner signs (byte identity, previous history hash,
|
|
//! from, to, frame), which scopes the signature to exactly one transition of
|
|
//! exactly one byte at one point in its history. The history hash starts as
|
|
//! the byte's identity and advances one step per transition:
|
|
//! `next = BLAKE3(previous ‖ payload)`, the payload being the transition's
|
|
//! from, to, frame and signature.
|
|
//!
|
|
//! A transition that moves the byte to another cluster (treatise §11.3.3)
|
|
//! also names its `destination`, and the signature and the history hash
|
|
//! cover it. A transition within a cluster has no `destination` member.
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::Blake3Hash;
|
|
use crate::keys::{ControllerSignature, Signer};
|
|
use crate::said::{Aid, Said};
|
|
|
|
/// One link in a byte's ownership chain.
|
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(deny_unknown_fields)]
|
|
pub struct Transition {
|
|
/// The byte's history hash before this transition.
|
|
#[serde(with = "crate::wire::fixed_bytes")]
|
|
pub previous_history_hash: Blake3Hash,
|
|
/// The owner giving the byte up.
|
|
pub from: Aid,
|
|
/// The owner receiving it.
|
|
pub to: Aid,
|
|
/// The lockstep frame the transition is applied in.
|
|
pub frame: u64,
|
|
/// The cluster the byte moves to, for a transition that leaves the
|
|
/// cluster it is applied in (treatise §11.3.3). Absent otherwise.
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
pub destination: Option<Said>,
|
|
/// `from`'s signature over [`Transition::signed_bytes`].
|
|
pub signature: ControllerSignature,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct Signed<'a> {
|
|
byte: &'a Said,
|
|
#[serde(with = "serde_bytes")]
|
|
previous_history_hash: &'a [u8],
|
|
from: &'a Aid,
|
|
to: &'a Aid,
|
|
frame: u64,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
destination: Option<&'a Said>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct Payload<'a> {
|
|
from: &'a Aid,
|
|
to: &'a Aid,
|
|
frame: u64,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
destination: Option<&'a Said>,
|
|
signature: &'a ControllerSignature,
|
|
}
|
|
|
|
impl Transition {
|
|
/// A transition of byte `byte` from `owner` to `to` at `frame`, from the
|
|
/// history point `previous_history_hash`, signed by `owner`.
|
|
pub fn sign(
|
|
owner: &Signer,
|
|
byte: &Said,
|
|
previous_history_hash: Blake3Hash,
|
|
to: Aid,
|
|
frame: u64,
|
|
) -> Self {
|
|
Self::signed(owner, byte, previous_history_hash, to, frame, None)
|
|
}
|
|
|
|
/// A transition that moves the byte to `destination`, another cluster,
|
|
/// where `to` becomes its owner (treatise §11.3.3).
|
|
pub fn sign_export(
|
|
owner: &Signer,
|
|
byte: &Said,
|
|
previous_history_hash: Blake3Hash,
|
|
to: Aid,
|
|
frame: u64,
|
|
destination: Said,
|
|
) -> Self {
|
|
Self::signed(
|
|
owner,
|
|
byte,
|
|
previous_history_hash,
|
|
to,
|
|
frame,
|
|
Some(destination),
|
|
)
|
|
}
|
|
|
|
fn signed(
|
|
owner: &Signer,
|
|
byte: &Said,
|
|
previous_history_hash: Blake3Hash,
|
|
to: Aid,
|
|
frame: u64,
|
|
destination: Option<Said>,
|
|
) -> Self {
|
|
let mut t = Transition {
|
|
previous_history_hash,
|
|
from: owner.aid,
|
|
to,
|
|
frame,
|
|
destination,
|
|
signature: ControllerSignature {
|
|
establishment: 0,
|
|
signatures: vec![],
|
|
},
|
|
};
|
|
t.signature = owner.sign(&t.signed_bytes(byte));
|
|
t
|
|
}
|
|
|
|
/// The message `from` signs: the canonical encoding of (byte identity,
|
|
/// previous history hash, from, to, frame) — and the destination, for a
|
|
/// transition that leaves the cluster (treatise §8.4, §11.3.3).
|
|
pub fn signed_bytes(&self, byte: &Said) -> Vec<u8> {
|
|
crate::wire::to_vec(&Signed {
|
|
byte,
|
|
previous_history_hash: &self.previous_history_hash,
|
|
from: &self.from,
|
|
to: &self.to,
|
|
frame: self.frame,
|
|
destination: self.destination.as_ref(),
|
|
})
|
|
.expect("a transition's signed fields always encode")
|
|
}
|
|
|
|
/// The history hash after this transition (treatise §10.1).
|
|
pub fn next_history_hash(&self) -> Blake3Hash {
|
|
let payload = crate::wire::to_vec(&Payload {
|
|
from: &self.from,
|
|
to: &self.to,
|
|
frame: self.frame,
|
|
destination: self.destination.as_ref(),
|
|
signature: &self.signature,
|
|
})
|
|
.expect("a transition's payload always encodes");
|
|
let mut h = blake3::Hasher::new();
|
|
h.update(&self.previous_history_hash);
|
|
h.update(&payload);
|
|
*h.finalize().as_bytes()
|
|
}
|
|
}
|