use ruvix_types::{ProofTier, RegionPolicy, WitTypeId};
#[cfg(feature = "alloc")]
use alloc::{string::String, vec::Vec};
#[cfg(feature = "std")]
use std::{string::String, vec::Vec};
#[allow(dead_code)]
pub const MAX_COMPONENT_NAME_LEN: usize = 256;
#[allow(dead_code)]
pub const MAX_ROLLBACK_HOOK_SIZE: usize = 64 * 1024;
#[derive(Debug, Clone)]
pub struct RvfManifest {
pub version: ManifestVersion,
pub content_hash: [u8; 32],
pub component_graph: ComponentGraph,
pub memory_schema: MemorySchema,
pub proof_policy: ProofPolicy,
pub rollback_hooks: RollbackHooks,
pub witness_log_policy: WitnessLogPolicy,
pub required_capabilities: RequiredCapabilities,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct ManifestVersion {
pub major: u16,
pub minor: u16,
pub patch: u16,
}
impl ManifestVersion {
pub const CURRENT: Self = Self {
major: 1,
minor: 0,
patch: 0,
};
#[inline]
#[must_use]
pub const fn new(major: u16, minor: u16, patch: u16) -> Self {
Self { major, minor, patch }
}
#[inline]
#[must_use]
pub const fn is_compatible(&self) -> bool {
self.major == Self::CURRENT.major
}
}
impl Default for ManifestVersion {
fn default() -> Self {
Self::CURRENT
}
}
#[derive(Debug, Clone, Default)]
pub struct ComponentGraph {
pub components: Vec<ComponentDecl>,
pub wirings: Vec<QueueWiring>,
}
impl ComponentGraph {
#[inline]
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[inline]
#[must_use]
pub fn component_count(&self) -> usize {
self.components.len()
}
#[inline]
#[must_use]
pub fn wiring_count(&self) -> usize {
self.wirings.len()
}
#[must_use]
pub fn validate(&self) -> bool {
if self.components.is_empty() {
return true;
}
let count = self.component_count();
for wiring in &self.wirings {
if wiring.source_component as usize >= count
|| wiring.target_component as usize >= count
{
return false;
}
}
true
}
}
#[derive(Debug, Clone)]
pub struct ComponentDecl {
pub index: u32,
pub name: String,
pub code_hash: [u8; 32],
pub code_offset: u64,
pub code_size: u64,
pub wit_type: WitTypeId,
pub entry_point: String,
pub dependencies: Vec<u32>,
}
impl ComponentDecl {
#[must_use]
pub fn name_str(&self) -> &str {
&self.name
}
#[must_use]
pub fn entry_point_str(&self) -> &str {
&self.entry_point
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct QueueWiring {
pub source_component: u32,
pub source_port_hash: u32,
pub target_component: u32,
pub target_port_hash: u32,
pub queue_capacity: u32,
pub max_message_size: u32,
pub message_type: WitTypeId,
}
#[derive(Debug, Clone, Default)]
pub struct MemorySchema {
pub regions: Vec<RegionDecl>,
pub total_memory_required: u64,
}
impl MemorySchema {
#[inline]
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[inline]
#[must_use]
pub fn region_count(&self) -> usize {
self.regions.len()
}
#[must_use]
pub fn validate(&self) -> bool {
let mut total: u64 = 0;
for region in &self.regions {
if let Some(size) = region.size_bytes() {
total = match total.checked_add(size) {
Some(t) => t,
None => return false,
};
}
}
total <= self.total_memory_required
}
}
#[derive(Debug, Clone)]
pub struct RegionDecl {
pub index: u32,
pub name: String,
pub policy: RegionPolicy,
pub owner_component: u32,
pub read_access: Vec<u32>,
pub write_access: Vec<u32>,
}
impl RegionDecl {
#[must_use]
pub fn size_bytes(&self) -> Option<u64> {
match &self.policy {
RegionPolicy::Immutable => None, RegionPolicy::AppendOnly { max_size } => Some(*max_size as u64),
RegionPolicy::Slab { slot_size, slot_count } => {
Some((*slot_size as u64) * (*slot_count as u64))
}
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ProofPolicy {
pub component_tiers: Vec<ComponentProofTier>,
pub default_tier: ProofTier,
pub allow_tier_escalation: bool,
}
impl ProofPolicy {
#[inline]
#[must_use]
pub fn new(default_tier: ProofTier) -> Self {
Self {
component_tiers: Vec::new(),
default_tier,
allow_tier_escalation: false,
}
}
#[must_use]
pub fn tier_for_component(&self, component_index: u32) -> ProofTier {
for ct in &self.component_tiers {
if ct.component_index == component_index {
return ct.required_tier;
}
}
self.default_tier
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct ComponentProofTier {
pub component_index: u32,
pub required_tier: ProofTier,
pub proof_required_ops: ProofRequiredOps,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(transparent)]
pub struct ProofRequiredOps(pub u32);
impl ProofRequiredOps {
pub const NONE: Self = Self(0);
pub const VECTOR_PUT: Self = Self(1 << 0);
pub const GRAPH_APPLY: Self = Self(1 << 1);
pub const REGION_WRITE: Self = Self(1 << 2);
pub const QUEUE_SEND: Self = Self(1 << 3);
pub const ALL: Self = Self(0b1111);
#[inline]
#[must_use]
pub const fn requires(&self, op: Self) -> bool {
(self.0 & op.0) != 0
}
}
#[derive(Debug, Clone, Default)]
pub struct RollbackHooks {
pub hooks: Vec<RollbackHook>,
}
impl RollbackHooks {
#[inline]
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[inline]
#[must_use]
pub fn count(&self) -> usize {
self.hooks.len()
}
}
#[derive(Debug, Clone)]
pub struct RollbackHook {
pub index: u32,
pub component_index: u32,
pub function_name: String,
pub accessible_regions: Vec<u32>,
pub timeout_us: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct WitnessLogPolicy {
pub max_entries: u64,
pub max_size_bytes: u64,
pub retention_seconds: u64,
pub compression: WitnessCompression,
pub export_policy: WitnessExportPolicy,
pub hash_chain: bool,
}
impl Default for WitnessLogPolicy {
fn default() -> Self {
Self {
max_entries: 1_000_000,
max_size_bytes: 100 * 1024 * 1024, retention_seconds: 0, compression: WitnessCompression::None,
export_policy: WitnessExportPolicy::OnRotation,
hash_chain: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum WitnessCompression {
#[default]
None = 0,
Lz4 = 1,
Zstd = 2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum WitnessExportPolicy {
#[default]
OnRotation = 0,
OnShutdown = 1,
Never = 2,
External = 3,
}
#[derive(Debug, Clone, Default)]
pub struct RequiredCapabilities {
pub physical_memory: bool,
pub interrupt_queue: bool,
pub timer: bool,
pub vector_store: bool,
pub graph_store: bool,
pub min_memory_bytes: u64,
}
impl RvfManifest {
pub fn parse(data: &[u8]) -> Result<Self, ruvix_types::KernelError> {
if data.len() < 96 {
return Err(ruvix_types::KernelError::InvalidManifest);
}
if &data[0..4] != b"RVF1" {
return Err(ruvix_types::KernelError::InvalidManifest);
}
let major = u16::from_le_bytes([data[4], data[5]]);
let minor = u16::from_le_bytes([data[6], data[7]]);
let patch = u16::from_le_bytes([data[8], data[9]]);
let version = ManifestVersion::new(major, minor, patch);
if !version.is_compatible() {
return Err(ruvix_types::KernelError::InvalidManifest);
}
let mut content_hash = [0u8; 32];
content_hash.copy_from_slice(&data[10..42]);
let component_graph_offset = u32::from_le_bytes([data[42], data[43], data[44], data[45]]) as usize;
let memory_schema_offset = u32::from_le_bytes([data[46], data[47], data[48], data[49]]) as usize;
let proof_policy_offset = u32::from_le_bytes([data[50], data[51], data[52], data[53]]) as usize;
let rollback_hooks_offset = u32::from_le_bytes([data[54], data[55], data[56], data[57]]) as usize;
let witness_log_offset = u32::from_le_bytes([data[58], data[59], data[60], data[61]]) as usize;
let required_caps_offset = u32::from_le_bytes([data[62], data[63], data[64], data[65]]) as usize;
let component_graph = Self::parse_component_graph(data, component_graph_offset)?;
let memory_schema = Self::parse_memory_schema(data, memory_schema_offset)?;
let proof_policy = Self::parse_proof_policy(data, proof_policy_offset)?;
let rollback_hooks = Self::parse_rollback_hooks(data, rollback_hooks_offset)?;
let witness_log_policy = Self::parse_witness_log_policy(data, witness_log_offset)?;
let required_capabilities = Self::parse_required_capabilities(data, required_caps_offset)?;
Ok(Self {
version,
content_hash,
component_graph,
memory_schema,
proof_policy,
rollback_hooks,
witness_log_policy,
required_capabilities,
})
}
fn parse_component_graph(_data: &[u8], offset: usize) -> Result<ComponentGraph, ruvix_types::KernelError> {
if offset == 0 {
return Ok(ComponentGraph::new());
}
Ok(ComponentGraph::new())
}
fn parse_memory_schema(_data: &[u8], offset: usize) -> Result<MemorySchema, ruvix_types::KernelError> {
if offset == 0 {
return Ok(MemorySchema::new());
}
Ok(MemorySchema::new())
}
fn parse_proof_policy(_data: &[u8], offset: usize) -> Result<ProofPolicy, ruvix_types::KernelError> {
if offset == 0 {
return Ok(ProofPolicy::new(ProofTier::Standard));
}
Ok(ProofPolicy::new(ProofTier::Standard))
}
fn parse_rollback_hooks(_data: &[u8], offset: usize) -> Result<RollbackHooks, ruvix_types::KernelError> {
if offset == 0 {
return Ok(RollbackHooks::new());
}
Ok(RollbackHooks::new())
}
fn parse_witness_log_policy(data: &[u8], offset: usize) -> Result<WitnessLogPolicy, ruvix_types::KernelError> {
if offset == 0 || offset >= data.len() {
return Ok(WitnessLogPolicy::default());
}
if offset + 32 > data.len() {
return Ok(WitnessLogPolicy::default());
}
let max_entries = u64::from_le_bytes([
data[offset], data[offset + 1], data[offset + 2], data[offset + 3],
data[offset + 4], data[offset + 5], data[offset + 6], data[offset + 7],
]);
let max_size_bytes = u64::from_le_bytes([
data[offset + 8], data[offset + 9], data[offset + 10], data[offset + 11],
data[offset + 12], data[offset + 13], data[offset + 14], data[offset + 15],
]);
let retention_seconds = u64::from_le_bytes([
data[offset + 16], data[offset + 17], data[offset + 18], data[offset + 19],
data[offset + 20], data[offset + 21], data[offset + 22], data[offset + 23],
]);
let compression = match data[offset + 24] {
1 => WitnessCompression::Lz4,
2 => WitnessCompression::Zstd,
_ => WitnessCompression::None,
};
let export_policy = match data[offset + 25] {
1 => WitnessExportPolicy::OnShutdown,
2 => WitnessExportPolicy::Never,
3 => WitnessExportPolicy::External,
_ => WitnessExportPolicy::OnRotation,
};
let hash_chain = data[offset + 26] != 0;
Ok(WitnessLogPolicy {
max_entries,
max_size_bytes,
retention_seconds,
compression,
export_policy,
hash_chain,
})
}
fn parse_required_capabilities(_data: &[u8], offset: usize) -> Result<RequiredCapabilities, ruvix_types::KernelError> {
if offset == 0 {
return Ok(RequiredCapabilities::default());
}
Ok(RequiredCapabilities::default())
}
#[must_use]
pub fn validate(&self) -> bool {
self.component_graph.validate() && self.memory_schema.validate()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_manifest_version_compatibility() {
let v1_0_0 = ManifestVersion::new(1, 0, 0);
let v1_1_0 = ManifestVersion::new(1, 1, 0);
let v2_0_0 = ManifestVersion::new(2, 0, 0);
assert!(v1_0_0.is_compatible());
assert!(v1_1_0.is_compatible());
assert!(!v2_0_0.is_compatible());
}
#[test]
fn test_component_graph_empty() {
let graph = ComponentGraph::new();
assert_eq!(graph.component_count(), 0);
assert!(graph.validate());
}
#[test]
fn test_memory_schema_empty() {
let schema = MemorySchema::new();
assert_eq!(schema.region_count(), 0);
assert!(schema.validate()); }
#[test]
fn test_proof_policy_default_tier() {
let policy = ProofPolicy::new(ProofTier::Reflex);
assert_eq!(policy.tier_for_component(0), ProofTier::Reflex);
assert_eq!(policy.tier_for_component(999), ProofTier::Reflex);
}
#[test]
fn test_proof_required_ops() {
let ops = ProofRequiredOps::VECTOR_PUT;
assert!(ops.requires(ProofRequiredOps::VECTOR_PUT));
assert!(!ops.requires(ProofRequiredOps::GRAPH_APPLY));
let all = ProofRequiredOps::ALL;
assert!(all.requires(ProofRequiredOps::VECTOR_PUT));
assert!(all.requires(ProofRequiredOps::REGION_WRITE));
}
#[test]
fn test_witness_log_policy_default() {
let policy = WitnessLogPolicy::default();
assert_eq!(policy.max_entries, 1_000_000);
assert!(policy.hash_chain);
assert_eq!(policy.compression, WitnessCompression::None);
}
#[test]
fn test_manifest_parse_invalid_magic() {
let data = b"XXXX";
assert!(RvfManifest::parse(data).is_err());
}
#[test]
fn test_manifest_parse_too_short() {
let data = b"RVF1";
assert!(RvfManifest::parse(data).is_err());
}
}