use std::collections::BTreeMap;
#[cfg(not(target_arch = "wasm32"))]
use std::time::Instant;
use crate::{Contract, Error, Result};
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
pub enum Value {
Null,
Boolean(bool),
Integer(i64),
Float(f64),
String(String),
Array(Vec<Value>),
Object(BTreeMap<String, Value>),
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Null => write!(f, "null"),
Value::Boolean(b) => write!(f, "{}", b),
Value::Integer(i) => write!(f, "{}", i),
Value::Float(v) => write!(f, "{}", v),
Value::String(s) => write!(f, "\"{}\"", s),
Value::Array(arr) => {
write!(f, "[")?;
for (i, v) in arr.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", v)?;
}
write!(f, "]")
}
Value::Object(map) => {
write!(f, "{{")?;
for (i, (k, v)) in map.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "\"{}\": {}", k, v)?;
}
write!(f, "}}")
}
}
}
}
impl Value {
pub fn is_truthy(&self) -> bool {
match self {
Value::Null => false,
Value::Boolean(b) => *b,
Value::Integer(i) => *i != 0,
Value::Float(f) => *f != 0.0,
Value::String(s) => !s.is_empty(),
Value::Array(a) => !a.is_empty(),
Value::Object(o) => !o.is_empty(),
}
}
pub fn type_name(&self) -> &'static str {
match self {
Value::Null => "Null",
Value::Boolean(_) => "Boolean",
Value::Integer(_) => "Integer",
Value::Float(_) => "Float",
Value::String(_) => "String",
Value::Array(_) => "Array",
Value::Object(_) => "Object",
}
}
pub fn from_json(json: &serde_json::Value) -> Self {
match json {
serde_json::Value::Null => Value::Null,
serde_json::Value::Bool(b) => Value::Boolean(*b),
serde_json::Value::Number(n) => {
if let Some(i) = n.as_i64() {
Value::Integer(i)
} else if let Some(f) = n.as_f64() {
Value::Float(f)
} else {
Value::Null
}
}
serde_json::Value::String(s) => Value::String(s.clone()),
serde_json::Value::Array(arr) => {
Value::Array(arr.iter().map(Value::from_json).collect())
}
serde_json::Value::Object(map) => {
let btree: BTreeMap<String, Value> = map
.iter()
.map(|(k, v)| (k.clone(), Value::from_json(v)))
.collect();
Value::Object(btree)
}
}
}
pub fn to_json(&self) -> serde_json::Value {
match self {
Value::Null => serde_json::Value::Null,
Value::Boolean(b) => serde_json::Value::Bool(*b),
Value::Integer(i) => serde_json::json!(*i),
Value::Float(f) => serde_json::json!(*f),
Value::String(s) => serde_json::Value::String(s.clone()),
Value::Array(arr) => {
serde_json::Value::Array(arr.iter().map(|v| v.to_json()).collect())
}
Value::Object(map) => {
let obj: serde_json::Map<String, serde_json::Value> =
map.iter().map(|(k, v)| (k.clone(), v.to_json())).collect();
serde_json::Value::Object(obj)
}
}
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ExecutionState {
pub fields: BTreeMap<String, Value>,
}
impl ExecutionState {
pub fn from_contract(contract: &Contract) -> Self {
let fields = if let serde_json::Value::Object(map) = &contract.data_semantics.state {
let mut btree = BTreeMap::new();
for (key, type_info) in map.iter() {
let value = Self::default_for_type(type_info);
btree.insert(key.clone(), value);
}
btree
} else {
BTreeMap::new()
};
ExecutionState { fields }
}
fn default_for_type(type_info: &serde_json::Value) -> Value {
match type_info {
serde_json::Value::String(type_name) => match type_name.as_str() {
"Integer" => Value::Integer(0),
"Float" => Value::Float(0.0),
"String" | "ISO8601" | "UUID" => Value::String(String::new()),
"Boolean" => Value::Boolean(false),
_ => Value::Null,
},
serde_json::Value::Object(obj) => {
if let Some(serde_json::Value::String(t)) = obj.get("type") {
match t.as_str() {
"Integer" | "Float" | "String" | "Boolean" | "ISO8601" | "UUID" => {
if let Some(default) = obj.get("default") {
Value::from_json(default)
} else {
Self::default_for_type(&serde_json::Value::String(t.clone()))
}
}
_ => Value::Null,
}
} else {
let mut btree = BTreeMap::new();
for (k, v) in obj {
btree.insert(k.clone(), Self::default_for_type(v));
}
Value::Object(btree)
}
}
serde_json::Value::Array(_) => Value::Array(Vec::new()),
_ => Value::Null,
}
}
pub fn get(&self, field: &str) -> Option<&Value> {
self.fields.get(field)
}
pub fn set(&mut self, field: String, value: Value) -> Option<Value> {
self.fields.insert(field, value)
}
pub fn memory_bytes(&self) -> u64 {
self.estimate_size() as u64
}
fn estimate_size(&self) -> usize {
self.fields
.iter()
.map(|(k, v)| k.len() + Self::value_size(v))
.sum()
}
fn value_size(value: &Value) -> usize {
match value {
Value::Null => 1,
Value::Boolean(_) => 1,
Value::Integer(_) => 8,
Value::Float(_) => 8,
Value::String(s) => s.len() + 24, Value::Array(arr) => 24 + arr.iter().map(Self::value_size).sum::<usize>(),
Value::Object(map) => {
24 + map
.iter()
.map(|(k, v)| k.len() + Self::value_size(v))
.sum::<usize>()
}
}
}
}
pub struct ExpressionEvaluator;
impl ExpressionEvaluator {
pub fn evaluate(condition: &str, state: &ExecutionState) -> (bool, bool) {
let trimmed = condition.trim();
if let Some(field) = trimmed.strip_suffix(" is not empty") {
let field = field.trim();
if let Some(value) = state.get(field) {
return (value.is_truthy(), true);
}
return (false, true);
}
if let Some((field, num)) = Self::parse_comparison(trimmed, " >= ") {
return (Self::numeric_cmp(state, field, num, |a, b| a >= b), true);
}
if let Some((field, num)) = Self::parse_comparison(trimmed, " <= ") {
return (Self::numeric_cmp(state, field, num, |a, b| a <= b), true);
}
if let Some((field, num)) = Self::parse_comparison(trimmed, " > ") {
return (Self::numeric_cmp(state, field, num, |a, b| a > b), true);
}
if let Some((field, num)) = Self::parse_comparison(trimmed, " < ") {
return (Self::numeric_cmp(state, field, num, |a, b| a < b), true);
}
if let Some(field) = trimmed.strip_suffix(" is boolean") {
let field = field.trim();
if let Some(Value::Boolean(_)) = state.get(field) {
return (true, true);
}
return (false, true);
}
if trimmed.contains("is valid ") {
return (true, false);
}
(true, false)
}
fn parse_comparison<'a>(s: &'a str, operator: &str) -> Option<(&'a str, f64)> {
let parts: Vec<&str> = s.splitn(2, operator).collect();
if parts.len() == 2 {
let field = parts[0].trim();
let num_str = parts[1].trim();
if let Ok(num) = num_str.parse::<f64>() {
return Some((field, num));
}
}
None
}
fn numeric_cmp(
state: &ExecutionState,
field: &str,
rhs: f64,
cmp: fn(f64, f64) -> bool,
) -> bool {
match state.get(field) {
Some(Value::Integer(i)) => cmp(*i as f64, rhs),
Some(Value::Float(f)) => cmp(*f, rhs),
_ => false,
}
}
pub fn check_invariants(
invariants: &[String],
state: &ExecutionState,
) -> std::result::Result<(), Vec<String>> {
let mut violations = Vec::new();
for inv in invariants {
let (result, evaluable) = Self::evaluate(inv, state);
if evaluable && !result {
violations.push(inv.clone());
}
}
if violations.is_empty() {
Ok(())
} else {
Err(violations)
}
}
}
#[derive(Debug, Clone)]
pub struct Sandbox {
pub max_memory_bytes: u64,
pub computation_timeout_ms: u64,
pub max_state_size_bytes: u64,
pub mode: SandboxMode,
pub permissions: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum SandboxMode {
FullIsolation,
Restricted,
None,
}
impl Sandbox {
pub fn from_contract(contract: &Contract) -> Self {
let mode = match contract.execution_constraints.sandbox_mode.as_str() {
"full_isolation" => SandboxMode::FullIsolation,
"restricted" => SandboxMode::Restricted,
"none" => SandboxMode::None,
_ => SandboxMode::FullIsolation, };
Sandbox {
max_memory_bytes: contract
.execution_constraints
.resource_limits
.max_memory_bytes,
computation_timeout_ms: contract
.execution_constraints
.resource_limits
.computation_timeout_ms,
max_state_size_bytes: contract
.execution_constraints
.resource_limits
.max_state_size_bytes,
mode,
permissions: contract.execution_constraints.external_permissions.clone(),
}
}
pub fn check_memory(&self, state: &ExecutionState) -> Result<()> {
let used = state.memory_bytes();
if used > self.max_state_size_bytes {
return Err(Error::ExecutionError(format!(
"State size {} bytes exceeds limit of {} bytes",
used, self.max_state_size_bytes
)));
}
if used > self.max_memory_bytes {
return Err(Error::ExecutionError(format!(
"Memory usage {} bytes exceeds limit of {} bytes",
used, self.max_memory_bytes
)));
}
Ok(())
}
pub fn check_permissions(&self, required: &[String]) -> Result<()> {
if self.mode == SandboxMode::FullIsolation && !required.is_empty() {
return Err(Error::ExecutionError(
"Full isolation sandbox does not permit external access".into(),
));
}
for perm in required {
if !self.permissions.contains(perm) {
return Err(Error::ExecutionError(format!(
"Permission '{}' not granted in sandbox",
perm
)));
}
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ProvenanceEntry {
pub sequence: u64,
pub operation: String,
pub inputs: serde_json::Value,
pub state_before: BTreeMap<String, Value>,
pub state_after: BTreeMap<String, Value>,
pub changes: Vec<StateChange>,
pub postconditions_verified: bool,
pub invariants_verified: bool,
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct StateChange {
pub field: String,
pub old_value: Value,
pub new_value: Value,
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ProvenanceLog {
pub entries: Vec<ProvenanceEntry>,
}
impl ProvenanceLog {
pub fn new() -> Self {
ProvenanceLog {
entries: Vec::new(),
}
}
pub fn append(&mut self, entry: ProvenanceEntry) {
self.entries.push(entry);
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
impl Default for ProvenanceLog {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct OperationResult {
pub operation: String,
pub success: bool,
pub state: BTreeMap<String, Value>,
pub error: Option<String>,
pub provenance: Option<ProvenanceEntry>,
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ExecutionResult {
pub contract_id: String,
pub success: bool,
pub operations: Vec<OperationResult>,
pub final_state: BTreeMap<String, Value>,
pub provenance: ProvenanceLog,
pub error: Option<String>,
}
pub struct Executor {
contract: Contract,
state: ExecutionState,
sandbox: Sandbox,
provenance: ProvenanceLog,
sequence: u64,
}
impl Executor {
pub fn new(contract: Contract) -> Self {
let state = ExecutionState::from_contract(&contract);
let sandbox = Sandbox::from_contract(&contract);
Executor {
contract,
state,
sandbox,
provenance: ProvenanceLog::new(),
sequence: 0,
}
}
pub fn execute_operation(
&mut self,
operation_name: &str,
inputs_json: &str,
) -> Result<OperationResult> {
#[cfg(not(target_arch = "wasm32"))]
let start = Instant::now();
let op = self
.contract
.behavioral_semantics
.operations
.iter()
.find(|o| o.name == operation_name)
.ok_or_else(|| {
Error::ExecutionError(format!(
"Operation '{}' not found in contract",
operation_name
))
})?
.clone();
let inputs: serde_json::Value = serde_json::from_str(inputs_json)
.map_err(|e| Error::ExecutionError(format!("Invalid JSON input: {}", e)))?;
self.validate_inputs(&op, &inputs)?;
let (pre_result, pre_evaluable) =
ExpressionEvaluator::evaluate(&op.precondition, &self.state);
if pre_evaluable && !pre_result {
return Err(Error::ExecutionError(format!(
"Precondition failed for operation '{}': {}",
operation_name, op.precondition
)));
}
let state_before = self.state.fields.clone();
self.apply_inputs(&inputs)?;
#[cfg(not(target_arch = "wasm32"))]
{
let elapsed_ms = start.elapsed().as_millis() as u64;
if elapsed_ms > self.sandbox.computation_timeout_ms {
self.state.fields = state_before.clone();
return Err(Error::ExecutionError(format!(
"Operation '{}' exceeded timeout of {}ms (took {}ms)",
operation_name, self.sandbox.computation_timeout_ms, elapsed_ms
)));
}
}
let (post_result, post_evaluable) =
ExpressionEvaluator::evaluate(&op.postcondition, &self.state);
let postconditions_verified = !post_evaluable || post_result;
if post_evaluable && !post_result {
self.state.fields = state_before;
return Err(Error::ContractViolation {
commitment: format!("postcondition of '{}'", operation_name),
violation: op.postcondition.clone(),
});
}
let invariants_verified = match ExpressionEvaluator::check_invariants(
&self.contract.data_semantics.invariants,
&self.state,
) {
Ok(()) => true,
Err(violations) => {
self.state.fields = state_before;
return Err(Error::ContractViolation {
commitment: "invariant".into(),
violation: format!("Violated invariants: {}", violations.join(", ")),
});
}
};
self.sandbox.check_memory(&self.state).inspect_err(|_| {
self.state.fields = state_before.clone();
})?;
let changes = Self::compute_changes(&state_before, &self.state.fields);
let entry = ProvenanceEntry {
sequence: self.sequence,
operation: operation_name.to_string(),
inputs: inputs.clone(),
state_before,
state_after: self.state.fields.clone(),
changes,
postconditions_verified,
invariants_verified,
};
self.provenance.append(entry.clone());
self.sequence += 1;
Ok(OperationResult {
operation: operation_name.to_string(),
success: true,
state: self.state.fields.clone(),
error: None,
provenance: Some(entry),
})
}
fn validate_inputs(&self, op: &crate::Operation, inputs: &serde_json::Value) -> Result<()> {
if let serde_json::Value::Object(params_def) = &op.parameters {
if let serde_json::Value::Object(input_map) = inputs {
for (param_name, _param_type) in params_def {
if !input_map.contains_key(param_name) {
return Err(Error::ExecutionError(format!(
"Missing required parameter '{}' for operation '{}'",
param_name, op.name
)));
}
}
}
}
Ok(())
}
fn apply_inputs(&mut self, inputs: &serde_json::Value) -> Result<()> {
if let serde_json::Value::Object(input_map) = inputs {
for (key, value) in input_map {
let typed_value = Value::from_json(value);
self.state.set(key.clone(), typed_value);
}
}
Ok(())
}
fn compute_changes(
before: &BTreeMap<String, Value>,
after: &BTreeMap<String, Value>,
) -> Vec<StateChange> {
let mut changes = Vec::new();
for (key, old_val) in before {
match after.get(key) {
Some(new_val) if new_val != old_val => {
changes.push(StateChange {
field: key.clone(),
old_value: old_val.clone(),
new_value: new_val.clone(),
});
}
None => {
changes.push(StateChange {
field: key.clone(),
old_value: old_val.clone(),
new_value: Value::Null,
});
}
_ => {}
}
}
for (key, new_val) in after {
if !before.contains_key(key) {
changes.push(StateChange {
field: key.clone(),
old_value: Value::Null,
new_value: new_val.clone(),
});
}
}
changes
}
pub fn execute_all(&mut self, requests_json: &str) -> Result<ExecutionResult> {
let requests: Vec<serde_json::Value> = serde_json::from_str(requests_json)
.map_err(|e| Error::ExecutionError(format!("Invalid JSON requests: {}", e)))?;
let mut operation_results = Vec::new();
for req in &requests {
let op_name = req
.get("operation")
.and_then(|v| v.as_str())
.ok_or_else(|| {
Error::ExecutionError("Each request must have an 'operation' field".into())
})?;
let empty_obj = serde_json::Value::Object(serde_json::Map::new());
let inputs = req.get("inputs").unwrap_or(&empty_obj);
let inputs_str = serde_json::to_string(inputs)
.map_err(|e| Error::ExecutionError(format!("Failed to serialize inputs: {}", e)))?;
match self.execute_operation(op_name, &inputs_str) {
Ok(result) => operation_results.push(result),
Err(e) => {
operation_results.push(OperationResult {
operation: op_name.to_string(),
success: false,
state: self.state.fields.clone(),
error: Some(e.to_string()),
provenance: None,
});
return Ok(ExecutionResult {
contract_id: self.contract.identity.stable_id.clone(),
success: false,
operations: operation_results,
final_state: self.state.fields.clone(),
provenance: self.provenance.clone(),
error: Some(e.to_string()),
});
}
}
}
Ok(ExecutionResult {
contract_id: self.contract.identity.stable_id.clone(),
success: true,
operations: operation_results,
final_state: self.state.fields.clone(),
provenance: self.provenance.clone(),
error: None,
})
}
pub fn state(&self) -> &ExecutionState {
&self.state
}
pub fn provenance(&self) -> &ProvenanceLog {
&self.provenance
}
}
pub fn execute_contract(contract: &Contract, inputs: &str) -> Result<String> {
let mut executor = Executor::new(contract.clone());
let inputs_trimmed = inputs.trim();
let requests_json = if inputs_trimmed.starts_with('[') {
inputs_trimmed.to_string()
} else if inputs_trimmed.starts_with('{') {
format!("[{}]", inputs_trimmed)
} else {
return Err(Error::ExecutionError(
"Input must be a JSON object or array of objects".into(),
));
};
let result = executor.execute_all(&requests_json)?;
serde_json::to_string_pretty(&result)
.map_err(|e| Error::ExecutionError(format!("Failed to serialize result: {}", e)))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::*;
fn test_contract() -> Contract {
Contract {
identity: Identity {
stable_id: "ic-test-001".into(),
version: 1,
created_timestamp: "2026-02-01T10:00:00Z".into(),
owner: "test".into(),
semantic_hash: "abc123".into(),
},
purpose_statement: PurposeStatement {
narrative: "Test contract".into(),
intent_source: "test".into(),
confidence_level: 1.0,
},
data_semantics: DataSemantics {
state: serde_json::json!({
"message": "String",
"count": "Integer"
}),
invariants: vec!["message is not empty".into(), "count >= 0".into()],
},
behavioral_semantics: BehavioralSemantics {
operations: vec![Operation {
name: "echo".into(),
precondition: "input_provided".into(),
parameters: serde_json::json!({
"message": "String"
}),
postcondition: "state_updated".into(),
side_effects: vec!["log_operation".into()],
idempotence: "idempotent".into(),
}],
},
execution_constraints: ExecutionConstraints {
trigger_types: vec!["manual".into()],
resource_limits: ResourceLimits {
max_memory_bytes: 1_048_576,
computation_timeout_ms: 1000,
max_state_size_bytes: 1_048_576,
},
external_permissions: vec![],
sandbox_mode: "full_isolation".into(),
},
human_machine_contract: HumanMachineContract {
system_commitments: vec!["All messages echoed".into()],
system_refusals: vec!["Will not lose data".into()],
user_obligations: vec!["Provide messages".into()],
},
}
}
#[test]
#[allow(clippy::approx_constant)]
fn test_value_from_json_primitives() {
assert_eq!(Value::from_json(&serde_json::json!(null)), Value::Null);
assert_eq!(
Value::from_json(&serde_json::json!(true)),
Value::Boolean(true)
);
assert_eq!(Value::from_json(&serde_json::json!(42)), Value::Integer(42));
assert_eq!(
Value::from_json(&serde_json::json!(3.14)),
Value::Float(3.14)
);
assert_eq!(
Value::from_json(&serde_json::json!("hello")),
Value::String("hello".into())
);
}
#[test]
fn test_value_from_json_collections() {
let arr = Value::from_json(&serde_json::json!([1, 2, 3]));
assert_eq!(
arr,
Value::Array(vec![
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
])
);
let obj = Value::from_json(&serde_json::json!({"a": 1, "b": "two"}));
let mut expected = BTreeMap::new();
expected.insert("a".into(), Value::Integer(1));
expected.insert("b".into(), Value::String("two".into()));
assert_eq!(obj, Value::Object(expected));
}
#[test]
fn test_value_roundtrip_json() {
let original = serde_json::json!({
"name": "test",
"count": 42,
"active": true,
"items": [1, 2, 3]
});
let value = Value::from_json(&original);
let back = value.to_json();
assert_eq!(original, back);
}
#[test]
fn test_value_is_truthy() {
assert!(!Value::Null.is_truthy());
assert!(!Value::Boolean(false).is_truthy());
assert!(Value::Boolean(true).is_truthy());
assert!(!Value::Integer(0).is_truthy());
assert!(Value::Integer(1).is_truthy());
assert!(!Value::String(String::new()).is_truthy());
assert!(Value::String("hello".into()).is_truthy());
assert!(!Value::Array(vec![]).is_truthy());
assert!(Value::Array(vec![Value::Integer(1)]).is_truthy());
}
#[test]
fn test_value_display() {
assert_eq!(format!("{}", Value::Null), "null");
assert_eq!(format!("{}", Value::Boolean(true)), "true");
assert_eq!(format!("{}", Value::Integer(42)), "42");
assert_eq!(format!("{}", Value::String("hi".into())), "\"hi\"");
}
#[test]
fn test_execution_state_from_contract() {
let contract = test_contract();
let state = ExecutionState::from_contract(&contract);
assert_eq!(state.get("message"), Some(&Value::String(String::new())));
assert_eq!(state.get("count"), Some(&Value::Integer(0)));
}
#[test]
fn test_execution_state_set_get() {
let mut state = ExecutionState {
fields: BTreeMap::new(),
};
state.set("x".into(), Value::Integer(10));
assert_eq!(state.get("x"), Some(&Value::Integer(10)));
let old = state.set("x".into(), Value::Integer(20));
assert_eq!(old, Some(Value::Integer(10)));
assert_eq!(state.get("x"), Some(&Value::Integer(20)));
}
#[test]
fn test_execution_state_memory_bytes() {
let mut state = ExecutionState {
fields: BTreeMap::new(),
};
let empty_size = state.memory_bytes();
state.set("big_string".into(), Value::String("x".repeat(1000)));
assert!(state.memory_bytes() > empty_size + 1000);
}
#[test]
fn test_eval_is_not_empty_true() {
let mut state = ExecutionState {
fields: BTreeMap::new(),
};
state.set("message".into(), Value::String("hello".into()));
let (result, evaluable) = ExpressionEvaluator::evaluate("message is not empty", &state);
assert!(evaluable);
assert!(result);
}
#[test]
fn test_eval_is_not_empty_false() {
let mut state = ExecutionState {
fields: BTreeMap::new(),
};
state.set("message".into(), Value::String(String::new()));
let (result, evaluable) = ExpressionEvaluator::evaluate("message is not empty", &state);
assert!(evaluable);
assert!(!result);
}
#[test]
fn test_eval_numeric_comparisons() {
let mut state = ExecutionState {
fields: BTreeMap::new(),
};
state.set("count".into(), Value::Integer(5));
assert!(ExpressionEvaluator::evaluate("count >= 0", &state).0);
assert!(ExpressionEvaluator::evaluate("count >= 5", &state).0);
assert!(!ExpressionEvaluator::evaluate("count >= 6", &state).0);
assert!(ExpressionEvaluator::evaluate("count > 4", &state).0);
assert!(!ExpressionEvaluator::evaluate("count > 5", &state).0);
assert!(ExpressionEvaluator::evaluate("count <= 5", &state).0);
assert!(ExpressionEvaluator::evaluate("count < 6", &state).0);
assert!(!ExpressionEvaluator::evaluate("count < 5", &state).0);
}
#[test]
fn test_eval_is_boolean() {
let mut state = ExecutionState {
fields: BTreeMap::new(),
};
state.set("flag".into(), Value::Boolean(true));
state.set("count".into(), Value::Integer(5));
assert!(ExpressionEvaluator::evaluate("flag is boolean", &state).0);
assert!(!ExpressionEvaluator::evaluate("count is boolean", &state).0);
}
#[test]
fn test_eval_opaque_condition() {
let state = ExecutionState {
fields: BTreeMap::new(),
};
let (result, evaluable) = ExpressionEvaluator::evaluate("some_opaque_condition", &state);
assert!(!evaluable);
assert!(result); }
#[test]
fn test_check_invariants_all_pass() {
let mut state = ExecutionState {
fields: BTreeMap::new(),
};
state.set("message".into(), Value::String("hello".into()));
state.set("count".into(), Value::Integer(5));
let invariants = vec!["message is not empty".into(), "count >= 0".into()];
assert!(ExpressionEvaluator::check_invariants(&invariants, &state).is_ok());
}
#[test]
fn test_check_invariants_one_fails() {
let mut state = ExecutionState {
fields: BTreeMap::new(),
};
state.set("message".into(), Value::String(String::new()));
state.set("count".into(), Value::Integer(5));
let invariants = vec!["message is not empty".into(), "count >= 0".into()];
let result = ExpressionEvaluator::check_invariants(&invariants, &state);
assert!(result.is_err());
let violations = result.unwrap_err();
assert_eq!(violations, vec!["message is not empty"]);
}
#[test]
fn test_sandbox_from_contract() {
let contract = test_contract();
let sandbox = Sandbox::from_contract(&contract);
assert_eq!(sandbox.mode, SandboxMode::FullIsolation);
assert_eq!(sandbox.max_memory_bytes, 1_048_576);
assert_eq!(sandbox.computation_timeout_ms, 1000);
}
#[test]
fn test_sandbox_check_memory_within_limits() {
let contract = test_contract();
let sandbox = Sandbox::from_contract(&contract);
let state = ExecutionState::from_contract(&contract);
assert!(sandbox.check_memory(&state).is_ok());
}
#[test]
fn test_sandbox_check_memory_exceeds_limit() {
let contract = test_contract();
let sandbox = Sandbox {
max_memory_bytes: 10,
max_state_size_bytes: 10,
..Sandbox::from_contract(&contract)
};
let mut state = ExecutionState::from_contract(&contract);
state.set("big".into(), Value::String("x".repeat(100)));
assert!(sandbox.check_memory(&state).is_err());
}
#[test]
fn test_sandbox_permissions_full_isolation() {
let sandbox = Sandbox {
max_memory_bytes: 1_000_000,
computation_timeout_ms: 1000,
max_state_size_bytes: 1_000_000,
mode: SandboxMode::FullIsolation,
permissions: vec![],
};
assert!(sandbox.check_permissions(&[]).is_ok());
assert!(sandbox.check_permissions(&["network".to_string()]).is_err());
}
#[test]
fn test_sandbox_permissions_restricted() {
let sandbox = Sandbox {
max_memory_bytes: 1_000_000,
computation_timeout_ms: 1000,
max_state_size_bytes: 1_000_000,
mode: SandboxMode::Restricted,
permissions: vec!["database_query".into()],
};
assert!(sandbox
.check_permissions(&["database_query".to_string()])
.is_ok());
assert!(sandbox.check_permissions(&["network".to_string()]).is_err());
}
#[test]
fn test_provenance_log_new_empty() {
let log = ProvenanceLog::new();
assert!(log.is_empty());
assert_eq!(log.len(), 0);
}
#[test]
fn test_provenance_log_append() {
let mut log = ProvenanceLog::new();
let entry = ProvenanceEntry {
sequence: 0,
operation: "test".into(),
inputs: serde_json::json!({}),
state_before: BTreeMap::new(),
state_after: BTreeMap::new(),
changes: vec![],
postconditions_verified: true,
invariants_verified: true,
};
log.append(entry);
assert_eq!(log.len(), 1);
assert!(!log.is_empty());
}
#[test]
fn test_executor_new() {
let contract = test_contract();
let executor = Executor::new(contract);
assert_eq!(
executor.state().get("message"),
Some(&Value::String(String::new()))
);
assert_eq!(executor.state().get("count"), Some(&Value::Integer(0)));
assert!(executor.provenance().is_empty());
}
#[test]
fn test_execute_operation_success() {
let contract = test_contract();
let mut executor = Executor::new(contract);
let result = executor
.execute_operation("echo", r#"{"message": "hello"}"#)
.unwrap();
assert!(result.success);
assert_eq!(result.operation, "echo");
assert_eq!(
executor.state().get("message"),
Some(&Value::String("hello".into()))
);
assert!(result.provenance.is_some());
}
#[test]
fn test_execute_operation_not_found() {
let contract = test_contract();
let mut executor = Executor::new(contract);
let result = executor.execute_operation("nonexistent", "{}");
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("not found"));
}
#[test]
fn test_execute_operation_invalid_json() {
let contract = test_contract();
let mut executor = Executor::new(contract);
let result = executor.execute_operation("echo", "not json");
assert!(result.is_err());
}
#[test]
fn test_execute_operation_invariant_violation() {
let contract = test_contract();
let mut executor = Executor::new(contract);
let result = executor.execute_operation("echo", r#"{"count": -1, "message": "hi"}"#);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("invariant") || err.contains("Violated"));
}
#[test]
fn test_execute_operation_state_rollback_on_failure() {
let contract = test_contract();
let mut executor = Executor::new(contract);
executor
.execute_operation("echo", r#"{"message": "hello"}"#)
.unwrap();
let state_after_success = executor.state().clone();
let _ = executor.execute_operation("echo", r#"{"count": -1, "message": "hi"}"#);
assert_eq!(*executor.state(), state_after_success);
}
#[test]
fn test_execute_all_success() {
let contract = test_contract();
let mut executor = Executor::new(contract);
let requests = r#"[
{"operation": "echo", "inputs": {"message": "hello"}},
{"operation": "echo", "inputs": {"message": "world"}}
]"#;
let result = executor.execute_all(requests).unwrap();
assert!(result.success);
assert_eq!(result.operations.len(), 2);
assert_eq!(result.provenance.len(), 2);
}
#[test]
fn test_execute_all_stops_on_failure() {
let contract = test_contract();
let mut executor = Executor::new(contract);
let requests = r#"[
{"operation": "echo", "inputs": {"message": "hello"}},
{"operation": "nonexistent", "inputs": {}},
{"operation": "echo", "inputs": {"message": "world"}}
]"#;
let result = executor.execute_all(requests).unwrap();
assert!(!result.success);
assert_eq!(result.operations.len(), 2); }
#[test]
fn test_provenance_records_state_changes() {
let contract = test_contract();
let mut executor = Executor::new(contract);
executor
.execute_operation("echo", r#"{"message": "hello"}"#)
.unwrap();
let log = executor.provenance();
assert_eq!(log.len(), 1);
let entry = &log.entries[0];
assert_eq!(entry.operation, "echo");
assert_eq!(entry.sequence, 0);
assert!(entry.postconditions_verified);
assert!(entry.invariants_verified);
assert!(!entry.changes.is_empty());
let msg_change = entry.changes.iter().find(|c| c.field == "message").unwrap();
assert_eq!(msg_change.old_value, Value::String(String::new()));
assert_eq!(msg_change.new_value, Value::String("hello".into()));
}
#[test]
fn test_provenance_sequential_numbering() {
let contract = test_contract();
let mut executor = Executor::new(contract);
executor
.execute_operation("echo", r#"{"message": "first"}"#)
.unwrap();
executor
.execute_operation("echo", r#"{"message": "second"}"#)
.unwrap();
executor
.execute_operation("echo", r#"{"message": "third"}"#)
.unwrap();
let log = executor.provenance();
assert_eq!(log.entries[0].sequence, 0);
assert_eq!(log.entries[1].sequence, 1);
assert_eq!(log.entries[2].sequence, 2);
}
#[test]
fn test_execute_contract_single_request() {
let contract = test_contract();
let result = execute_contract(
&contract,
r#"{"operation": "echo", "inputs": {"message": "hello"}}"#,
)
.unwrap();
let json: serde_json::Value = serde_json::from_str(&result).unwrap();
assert_eq!(json["success"], true);
assert_eq!(json["contract_id"], "ic-test-001");
}
#[test]
fn test_execute_contract_array_requests() {
let contract = test_contract();
let result = execute_contract(
&contract,
r#"[{"operation": "echo", "inputs": {"message": "hello"}}]"#,
)
.unwrap();
let json: serde_json::Value = serde_json::from_str(&result).unwrap();
assert_eq!(json["success"], true);
}
#[test]
fn test_execute_contract_invalid_input() {
let contract = test_contract();
let result = execute_contract(&contract, "not json");
assert!(result.is_err());
}
#[test]
fn test_deterministic_execution() {
let contract = test_contract();
let input = r#"{"operation": "echo", "inputs": {"message": "determinism test"}}"#;
let first = execute_contract(&contract, input).unwrap();
for i in 0..100 {
let result = execute_contract(&contract, input).unwrap();
assert_eq!(first, result, "Non-determinism at iteration {}", i);
}
}
#[test]
fn test_deterministic_multi_operation() {
let contract = test_contract();
let input = r#"[
{"operation": "echo", "inputs": {"message": "first"}},
{"operation": "echo", "inputs": {"message": "second"}}
]"#;
let first = execute_contract(&contract, input).unwrap();
for i in 0..100 {
let result = execute_contract(&contract, input).unwrap();
assert_eq!(first, result, "Non-determinism at iteration {}", i);
}
}
#[test]
fn test_deterministic_provenance() {
let contract = test_contract();
let input = r#"{"operation": "echo", "inputs": {"message": "prov test"}}"#;
let first_json: serde_json::Value =
serde_json::from_str(&execute_contract(&contract, input).unwrap()).unwrap();
let first_provenance = &first_json["provenance"];
for i in 0..100 {
let result_json: serde_json::Value =
serde_json::from_str(&execute_contract(&contract, input).unwrap()).unwrap();
assert_eq!(
first_provenance, &result_json["provenance"],
"Provenance non-determinism at iteration {}",
i
);
}
}
#[test]
fn test_resource_limit_memory_exceeded() {
let mut contract = test_contract();
contract
.execution_constraints
.resource_limits
.max_state_size_bytes = 10;
contract
.execution_constraints
.resource_limits
.max_memory_bytes = 10;
contract.data_semantics.invariants.clear();
let mut executor = Executor::new(contract);
let result = executor.execute_operation(
"echo",
r#"{"message": "this string is way too long for the tiny memory limit we set"}"#,
);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("exceeds limit") || err.contains("bytes"));
}
#[test]
fn test_precondition_enforcement() {
let mut contract = test_contract();
contract.behavioral_semantics.operations[0].precondition = "count >= 10".into();
contract.data_semantics.invariants.clear();
let mut executor = Executor::new(contract);
let result = executor.execute_operation("echo", r#"{"message": "hello"}"#);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("Precondition failed"));
}
#[test]
fn test_postcondition_verification() {
let mut contract = test_contract();
contract.behavioral_semantics.operations[0].postcondition = "count >= 1".into();
contract.data_semantics.invariants.clear();
let mut executor = Executor::new(contract);
let result = executor.execute_operation("echo", r#"{"message": "hello"}"#);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("postcondition") || err.contains("Contract violation"));
}
}