File size: 4,734 Bytes
30f011f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | //! ledger.rs β WORM append-only audit ledger
//!
//! Every entailment attestation is sealed into an append-only chain.
//! Each record contains:
//! - BLAKE3 hash of the attestation payload
//! - BLAKE3 hash of the previous record (chain link)
//! - The raw bincode payload
//!
//! The chain is stored as a length-prefixed binary flat file.
//! A corrupt or tampered record breaks the chain hash and is immediately detectable.
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::path::Path;
use std::sync::Arc;
use tokio::sync::mpsc;
/// One sealed record in the WORM chain.
#[derive(Debug)]
pub struct LedgerRecord {
pub sequence: u64,
pub prev_hash: [u8; 32],
pub payload_hash: [u8; 32],
pub payload: Vec<u8>,
}
impl LedgerRecord {
/// Serialise to bytes: [seq 8B][prev 32B][hash 32B][len 8B][payload].
fn to_bytes(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(80 + self.payload.len());
out.extend_from_slice(&self.sequence.to_le_bytes());
out.extend_from_slice(&self.prev_hash);
out.extend_from_slice(&self.payload_hash);
out.extend_from_slice(&(self.payload.len() as u64).to_le_bytes());
out.extend_from_slice(&self.payload);
out
}
}
/// Append-only WORM ledger backed by a flat binary file.
pub struct WormLedger {
file: std::fs::File,
sequence: u64,
last_hash: [u8; 32],
}
impl WormLedger {
pub fn open(path: impl AsRef<Path>) -> io::Result<Self> {
let path = path.as_ref();
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
let mut file = std::fs::OpenOptions::new()
.create(true)
.append(true)
.read(true)
.open(path)?;
// Replay existing records to find last hash and sequence number.
let (sequence, last_hash) = Self::replay(&mut file)?;
log::info!(
"[ledger] opened {} β {} existing records",
path.display(),
sequence
);
Ok(Self { file, sequence, last_hash })
}
fn replay(file: &mut std::fs::File) -> io::Result<(u64, [u8; 32])> {
file.seek(SeekFrom::Start(0))?;
let mut seq: u64 = 0;
let mut last: [u8; 32] = [0u8; 32];
loop {
let mut seq_buf = [0u8; 8];
match file.read_exact(&mut seq_buf) {
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => break,
Err(e) => return Err(e),
Ok(_) => {}
}
let mut prev = [0u8; 32];
let mut hash = [0u8; 32];
file.read_exact(&mut prev)?;
file.read_exact(&mut hash)?;
let mut len_buf = [0u8; 8];
file.read_exact(&mut len_buf)?;
let len = u64::from_le_bytes(len_buf) as usize;
let mut payload = vec![0u8; len];
file.read_exact(&mut payload)?;
seq = u64::from_le_bytes(seq_buf) + 1;
last = hash;
}
Ok((seq, last))
}
/// Append a sealed record. Returns the receipt (sequence + hash).
pub fn append(&mut self, payload_hash: [u8; 32], payload: Vec<u8>) -> io::Result<(u64, [u8; 32])> {
let record = LedgerRecord {
sequence: self.sequence,
prev_hash: self.last_hash,
payload_hash,
payload,
};
let bytes = record.to_bytes();
self.file.write_all(&bytes)?;
self.file.flush()?;
self.last_hash = payload_hash;
let seq = self.sequence;
self.sequence += 1;
Ok((seq, payload_hash))
}
}
// ββ Background WORM worker βββββββββββββββββββββββββββββββββββββββββββββββββββ
/// Runs as a dedicated Tokio task.
/// Receives (hash, payload) from the inference loop and appends to the ledger.
/// Does not block the GPU batching thread.
pub async fn run_ledger_worker(
mut rx: mpsc::Receiver<([u8; 32], Vec<u8>)>,
ledger_path: String,
) {
let mut ledger = WormLedger::open(&ledger_path)
.unwrap_or_else(|e| panic!("cannot open ledger {}: {}", ledger_path, e));
while let Some((hash, payload)) = rx.recv().await {
match ledger.append(hash, payload) {
Ok((seq, _)) => log::debug!("[ledger] sealed record seq={}", seq),
Err(e) => log::error!("[ledger] write fault: {}", e),
}
}
log::info!("[ledger] worker shutting down");
}
|