harn-kernel 0.10.52

Portable compiler, program artifact, and deterministic execution kernel for Harn
Documentation
use serde::{Deserialize, Serialize};

use super::{diagnostic, CapabilityResult, DataValue};
use crate::Diagnostic;

const SNAPSHOT_MAGIC: &[u8; 8] = b"HARNSP01";
const MAX_SNAPSHOT_BYTES: usize = 1024 * 1024;
pub(super) const SNAPSHOT_TAG_BYTES: usize = 32;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub(super) struct ReplaySnapshot {
    pub(super) artifact_digest: [u8; 32],
    pub(super) grant_fingerprint: [u8; 32],
    pub(super) fuel_consumed: u64,
    pub(super) input: DataValue,
    pub(super) responses: Vec<CapabilityResult>,
    pub(super) pending_request: String,
}

pub(super) fn encode_snapshot(
    snapshot: &ReplaySnapshot,
    snapshot_key: &[u8; 32],
) -> Result<Vec<u8>, Diagnostic> {
    let payload = serde_json::to_vec(snapshot)
        .map_err(|error| diagnostic("snapshot_encode", error.to_string()))?;
    let tag = blake3::keyed_hash(snapshot_key, &payload);
    let mut bytes = Vec::with_capacity(12 + payload.len() + SNAPSHOT_TAG_BYTES);
    bytes.extend_from_slice(SNAPSHOT_MAGIC);
    bytes.extend_from_slice(&(payload.len() as u32).to_be_bytes());
    bytes.extend_from_slice(&payload);
    bytes.extend_from_slice(tag.as_bytes());
    if bytes.len() > MAX_SNAPSHOT_BYTES {
        return Err(diagnostic(
            "snapshot_too_large",
            "snapshot exceeds its byte limit",
        ));
    }
    Ok(bytes)
}

pub(super) fn decode_snapshot(
    bytes: &[u8],
    snapshot_key: Option<&[u8; 32]>,
) -> Result<ReplaySnapshot, Diagnostic> {
    let Some(snapshot_key) = snapshot_key else {
        return Err(diagnostic(
            "snapshot_key_required",
            "resuming a suspended execution requires its host-owned snapshot key",
        ));
    };
    if bytes.len() > MAX_SNAPSHOT_BYTES {
        return Err(diagnostic(
            "snapshot_too_large",
            "snapshot exceeds its byte limit",
        ));
    }
    if bytes.len() < 12 + SNAPSHOT_TAG_BYTES || &bytes[..8] != SNAPSHOT_MAGIC {
        return Err(diagnostic(
            "snapshot_malformed",
            "snapshot header is invalid",
        ));
    }
    let length = u32::from_be_bytes(bytes[8..12].try_into().expect("length checked")) as usize;
    if length != bytes.len() - 12 - SNAPSHOT_TAG_BYTES {
        return Err(diagnostic(
            "snapshot_malformed",
            "snapshot length does not match its header",
        ));
    }
    let payload_end = 12 + length;
    let payload = &bytes[12..payload_end];
    let expected = blake3::keyed_hash(snapshot_key, payload);
    if bytes[payload_end..] != expected.as_bytes()[..] {
        return Err(diagnostic(
            "snapshot_authentication",
            "snapshot authentication failed",
        ));
    }
    serde_json::from_slice(payload)
        .map_err(|error| diagnostic("snapshot_malformed", error.to_string()))
}