Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
# Execution Kernel - P0.1 Canonical zkVM Guest Program

[![Build Status](https://img.shields.io/badge/build-passing-brightgreen)](#testing)
[![Tests](https://img.shields.io/badge/tests-11%20passed-brightgreen)](#testing)
[![Tests](https://img.shields.io/badge/tests-%20passed-brightgreen)](#testing)
[![Deterministic](https://img.shields.io/badge/execution-deterministic-blue)](#consensus-critical-properties)

## Overview

This repository implements the **P0.1 Canonical zkVM Guest Program** as specified in `SPECIFICATION.md`. The kernel provides consensus-critical, deterministic agent execution within a RISC Zero zkVM environment.
This repository implements the **P0.1 Canonical zkVM Guest Program**. The kernel provides consensus-critical, deterministic agent execution within a RISC Zero zkVM environment.

**🎯 Purpose:** Define what constitutes a valid agent execution through cryptographically verifiable zero-knowledge proofs.

**⚡ Status:** ✅ **Implementation Complete** - All P0.1 requirements fulfilled with comprehensive test coverage.
**Purpose:** Define what constitutes a valid agent execution through cryptographically verifiable zero-knowledge proofs.

## Architecture

Expand Down
87 changes: 0 additions & 87 deletions SPECIFICATION.md

This file was deleted.

119 changes: 99 additions & 20 deletions crates/agent-traits/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,109 @@
use kernel_core::{AgentOutput, MAX_AGENT_OUTPUT_BYTES};
use kernel_core::{AgentOutput, ActionV1, AgentError, MAX_ACTIONS_PER_OUTPUT};

#[derive(Clone, Debug, PartialEq)]
pub enum AgentError {
InvalidInput,
ExecutionFailed,
OutputTooLarge,
/// Context provided to agents during execution.
///
/// Contains all identity and state information the agent needs
/// to make decisions and produce actions.
#[derive(Clone, Debug)]
pub struct AgentContext {
/// 32-byte agent identifier
pub agent_id: [u8; 32],
/// SHA-256 hash of the agent's own code
pub agent_code_hash: [u8; 32],
/// SHA-256 hash of the constraint set being enforced
pub constraint_set_hash: [u8; 32],
/// External state root (market/vault snapshot)
pub input_root: [u8; 32],
/// Execution nonce for replay protection
pub execution_nonce: u64,
}

/// Canonical agent interface.
///
/// All agents must implement this trait. The kernel calls `run()`
/// with the execution context and opaque inputs, and expects
/// a structured `AgentOutput` containing ordered actions.
///
/// # Determinism Requirements
///
/// Implementations MUST be fully deterministic:
/// - No randomness or time dependencies
/// - No floating-point operations
/// - No unordered iteration
/// - Bounded loops and memory usage
pub trait Agent {
fn run(input_root: [u8; 32], inputs: &[u8]) -> Result<AgentOutput, AgentError>;
/// Execute the agent with the given context and inputs.
///
/// # Arguments
/// * `ctx` - Execution context with identity and state information
/// * `inputs` - Opaque agent-specific input data
///
/// # Returns
/// * `Ok(AgentOutput)` - Structured output containing ordered actions
/// * `Err(AgentError)` - Execution failed, kernel will abort
fn run(ctx: &AgentContext, inputs: &[u8]) -> Result<AgentOutput, AgentError>;
}

/// Trivial reference agent implementation.
///
/// WARNING: This is NOT production-ready and exists only for testing.
/// It converts input bytes into a single "echo" action.
pub struct TrivialAgent;

/// Action type for echo action (used by TrivialAgent)
pub const ACTION_TYPE_ECHO: u32 = 0x00000001;

impl Agent for TrivialAgent {
/// WARNING: TrivialAgent is NOT production-ready and should only be used for testing.
/// It simply echoes input as output without any validation or processing.
fn run(_input_root: [u8; 32], inputs: &[u8]) -> Result<AgentOutput, AgentError> {
if inputs.len() > MAX_AGENT_OUTPUT_BYTES {
return Err(AgentError::OutputTooLarge);
}

// Pre-allocate with known size for efficiency
let mut data = Vec::with_capacity(inputs.len());
data.extend_from_slice(inputs);

Ok(AgentOutput { data })
/// Converts input into a single echo action.
///
/// The action targets the agent's own ID and carries
/// the input as payload (truncated to max payload size).
fn run(ctx: &AgentContext, inputs: &[u8]) -> Result<AgentOutput, AgentError> {
// Truncate to max payload size if needed
let payload_len = inputs.len().min(kernel_core::MAX_ACTION_PAYLOAD_BYTES);
let payload = inputs[..payload_len].to_vec();

let action = ActionV1 {
action_type: ACTION_TYPE_ECHO,
target: ctx.agent_id,
payload,
};

Ok(AgentOutput {
actions: vec![action],
})
}
}

/// No-op agent that produces no actions.
///
/// Useful for testing constraint enforcement with empty output.
pub struct NoOpAgent;

impl Agent for NoOpAgent {
fn run(_ctx: &AgentContext, _inputs: &[u8]) -> Result<AgentOutput, AgentError> {
Ok(AgentOutput { actions: vec![] })
}
}
}

/// Agent that produces multiple actions for testing.
pub struct MultiActionAgent;

impl Agent for MultiActionAgent {
/// Produces one action per byte of input (up to MAX_ACTIONS_PER_OUTPUT).
fn run(ctx: &AgentContext, inputs: &[u8]) -> Result<AgentOutput, AgentError> {
let action_count = inputs.len().min(MAX_ACTIONS_PER_OUTPUT);

let actions: Vec<ActionV1> = inputs[..action_count]
.iter()
.enumerate()
.map(|(i, &byte)| ActionV1 {
action_type: i as u32,
target: ctx.agent_id,
payload: vec![byte],
})
.collect();

Ok(AgentOutput { actions })
}
}
77 changes: 67 additions & 10 deletions crates/constraints/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,73 @@
use kernel_core::AgentOutput;

#[derive(Clone, Debug, PartialEq)]
pub enum ConstraintError {
ViolatedConstraint,
InvalidOutput,
}
use kernel_core::{AgentOutput, ConstraintError};

/// Metadata for constraint checking.
///
/// Contains context information needed to evaluate constraints
/// against the agent's output.
#[derive(Clone, Debug)]
pub struct Meta {
pub struct ConstraintMeta {
/// 32-byte agent identifier
pub agent_id: [u8; 32],
/// SHA-256 hash of the agent code
pub agent_code_hash: [u8; 32],
/// SHA-256 hash of the constraint set being enforced
pub constraint_set_hash: [u8; 32],
/// External state root that was observed
pub input_root: [u8; 32],
/// Execution nonce
pub execution_nonce: u64,
}

pub fn check(_output: &AgentOutput, _meta: &Meta) -> Result<(), ConstraintError> {
/// Check agent output against constraint set.
///
/// This function is MANDATORY - the kernel MUST call it and
/// MUST abort if it returns an error.
///
/// # P0.1 Implementation
///
/// For P0.1, this is a stub that always returns Ok(()).
/// Full constraint enforcement will be implemented in P0.2+.
///
/// # Future Implementation
///
/// Will validate:
/// - Action types are allowed for this agent
/// - Targets are within permitted scope
/// - Payload values are within bounds
/// - Total value/risk is within limits
/// - Rate limits are respected
///
/// # Arguments
/// * `output` - The agent's structured output
/// * `meta` - Constraint checking metadata
///
/// # Returns
/// * `Ok(())` - All constraints satisfied
/// * `Err(ConstraintError)` - A constraint was violated
pub fn check(_output: &AgentOutput, _meta: &ConstraintMeta) -> Result<(), ConstraintError> {
// P0.1: Stub implementation - always passes
// P0.2+: Will implement actual constraint checking
Ok(())
}
}

/// Validate that output is well-formed before constraint checking.
///
/// Ensures basic structural validity:
/// - Action count within limits
/// - Payload sizes within limits
/// - Required fields present
pub fn validate_output_structure(output: &AgentOutput) -> Result<(), ConstraintError> {
use kernel_core::{MAX_ACTIONS_PER_OUTPUT, MAX_ACTION_PAYLOAD_BYTES};

if output.actions.len() > MAX_ACTIONS_PER_OUTPUT {
return Err(ConstraintError::InvalidOutput);
}

for action in &output.actions {
if action.payload.len() > MAX_ACTION_PAYLOAD_BYTES {
return Err(ConstraintError::InvalidOutput);
}
}

Ok(())
}
Loading
Loading