1use std::collections::BTreeMap;
26#[cfg(not(target_arch = "wasm32"))]
27use std::time::Instant;
28
29use crate::{Contract, Error, Result};
30
31#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
35#[serde(untagged)]
36pub enum Value {
37 Null,
39 Boolean(bool),
41 Integer(i64),
43 Float(f64),
45 String(String),
47 Array(Vec<Value>),
49 Object(BTreeMap<String, Value>),
51}
52
53impl std::fmt::Display for Value {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match self {
56 Value::Null => write!(f, "null"),
57 Value::Boolean(b) => write!(f, "{}", b),
58 Value::Integer(i) => write!(f, "{}", i),
59 Value::Float(v) => write!(f, "{}", v),
60 Value::String(s) => write!(f, "\"{}\"", s),
61 Value::Array(arr) => {
62 write!(f, "[")?;
63 for (i, v) in arr.iter().enumerate() {
64 if i > 0 {
65 write!(f, ", ")?;
66 }
67 write!(f, "{}", v)?;
68 }
69 write!(f, "]")
70 }
71 Value::Object(map) => {
72 write!(f, "{{")?;
73 for (i, (k, v)) in map.iter().enumerate() {
74 if i > 0 {
75 write!(f, ", ")?;
76 }
77 write!(f, "\"{}\": {}", k, v)?;
78 }
79 write!(f, "}}")
80 }
81 }
82 }
83}
84
85impl Value {
86 pub fn is_truthy(&self) -> bool {
88 match self {
89 Value::Null => false,
90 Value::Boolean(b) => *b,
91 Value::Integer(i) => *i != 0,
92 Value::Float(f) => *f != 0.0,
93 Value::String(s) => !s.is_empty(),
94 Value::Array(a) => !a.is_empty(),
95 Value::Object(o) => !o.is_empty(),
96 }
97 }
98
99 pub fn type_name(&self) -> &'static str {
101 match self {
102 Value::Null => "Null",
103 Value::Boolean(_) => "Boolean",
104 Value::Integer(_) => "Integer",
105 Value::Float(_) => "Float",
106 Value::String(_) => "String",
107 Value::Array(_) => "Array",
108 Value::Object(_) => "Object",
109 }
110 }
111
112 pub fn from_json(json: &serde_json::Value) -> Self {
114 match json {
115 serde_json::Value::Null => Value::Null,
116 serde_json::Value::Bool(b) => Value::Boolean(*b),
117 serde_json::Value::Number(n) => {
118 if let Some(i) = n.as_i64() {
119 Value::Integer(i)
120 } else if let Some(f) = n.as_f64() {
121 Value::Float(f)
122 } else {
123 Value::Null
124 }
125 }
126 serde_json::Value::String(s) => Value::String(s.clone()),
127 serde_json::Value::Array(arr) => {
128 Value::Array(arr.iter().map(Value::from_json).collect())
129 }
130 serde_json::Value::Object(map) => {
131 let btree: BTreeMap<String, Value> = map
132 .iter()
133 .map(|(k, v)| (k.clone(), Value::from_json(v)))
134 .collect();
135 Value::Object(btree)
136 }
137 }
138 }
139
140 pub fn to_json(&self) -> serde_json::Value {
142 match self {
143 Value::Null => serde_json::Value::Null,
144 Value::Boolean(b) => serde_json::Value::Bool(*b),
145 Value::Integer(i) => serde_json::json!(*i),
146 Value::Float(f) => serde_json::json!(*f),
147 Value::String(s) => serde_json::Value::String(s.clone()),
148 Value::Array(arr) => {
149 serde_json::Value::Array(arr.iter().map(|v| v.to_json()).collect())
150 }
151 Value::Object(map) => {
152 let obj: serde_json::Map<String, serde_json::Value> =
153 map.iter().map(|(k, v)| (k.clone(), v.to_json())).collect();
154 serde_json::Value::Object(obj)
155 }
156 }
157 }
158}
159
160#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
165pub struct ExecutionState {
166 pub fields: BTreeMap<String, Value>,
168}
169
170impl ExecutionState {
171 pub fn from_contract(contract: &Contract) -> Self {
173 let fields = if let serde_json::Value::Object(map) = &contract.data_semantics.state {
174 let mut btree = BTreeMap::new();
175 for (key, type_info) in map.iter() {
176 let value = Self::default_for_type(type_info);
178 btree.insert(key.clone(), value);
179 }
180 btree
181 } else {
182 BTreeMap::new()
183 };
184 ExecutionState { fields }
185 }
186
187 fn default_for_type(type_info: &serde_json::Value) -> Value {
189 match type_info {
190 serde_json::Value::String(type_name) => match type_name.as_str() {
191 "Integer" => Value::Integer(0),
192 "Float" => Value::Float(0.0),
193 "String" | "ISO8601" | "UUID" => Value::String(String::new()),
194 "Boolean" => Value::Boolean(false),
195 _ => Value::Null,
196 },
197 serde_json::Value::Object(obj) => {
198 if let Some(serde_json::Value::String(t)) = obj.get("type") {
199 match t.as_str() {
200 "Integer" | "Float" | "String" | "Boolean" | "ISO8601" | "UUID" => {
201 if let Some(default) = obj.get("default") {
203 Value::from_json(default)
204 } else {
205 Self::default_for_type(&serde_json::Value::String(t.clone()))
206 }
207 }
208 _ => Value::Null,
209 }
210 } else {
211 let mut btree = BTreeMap::new();
213 for (k, v) in obj {
214 btree.insert(k.clone(), Self::default_for_type(v));
215 }
216 Value::Object(btree)
217 }
218 }
219 serde_json::Value::Array(_) => Value::Array(Vec::new()),
220 _ => Value::Null,
221 }
222 }
223
224 pub fn get(&self, field: &str) -> Option<&Value> {
226 self.fields.get(field)
227 }
228
229 pub fn set(&mut self, field: String, value: Value) -> Option<Value> {
231 self.fields.insert(field, value)
232 }
233
234 pub fn memory_bytes(&self) -> u64 {
236 self.estimate_size() as u64
237 }
238
239 fn estimate_size(&self) -> usize {
240 self.fields
241 .iter()
242 .map(|(k, v)| k.len() + Self::value_size(v))
243 .sum()
244 }
245
246 fn value_size(value: &Value) -> usize {
247 match value {
248 Value::Null => 1,
249 Value::Boolean(_) => 1,
250 Value::Integer(_) => 8,
251 Value::Float(_) => 8,
252 Value::String(s) => s.len() + 24, Value::Array(arr) => 24 + arr.iter().map(Self::value_size).sum::<usize>(),
254 Value::Object(map) => {
255 24 + map
256 .iter()
257 .map(|(k, v)| k.len() + Self::value_size(v))
258 .sum::<usize>()
259 }
260 }
261 }
262}
263
264pub struct ExpressionEvaluator;
278
279impl ExpressionEvaluator {
280 pub fn evaluate(condition: &str, state: &ExecutionState) -> (bool, bool) {
284 let trimmed = condition.trim();
285
286 if let Some(field) = trimmed.strip_suffix(" is not empty") {
288 let field = field.trim();
289 if let Some(value) = state.get(field) {
290 return (value.is_truthy(), true);
291 }
292 return (false, true);
294 }
295
296 if let Some((field, num)) = Self::parse_comparison(trimmed, " >= ") {
298 return (Self::numeric_cmp(state, field, num, |a, b| a >= b), true);
299 }
300
301 if let Some((field, num)) = Self::parse_comparison(trimmed, " <= ") {
303 return (Self::numeric_cmp(state, field, num, |a, b| a <= b), true);
304 }
305
306 if let Some((field, num)) = Self::parse_comparison(trimmed, " > ") {
308 return (Self::numeric_cmp(state, field, num, |a, b| a > b), true);
310 }
311
312 if let Some((field, num)) = Self::parse_comparison(trimmed, " < ") {
314 return (Self::numeric_cmp(state, field, num, |a, b| a < b), true);
315 }
316
317 if let Some(field) = trimmed.strip_suffix(" is boolean") {
319 let field = field.trim();
320 if let Some(Value::Boolean(_)) = state.get(field) {
321 return (true, true);
322 }
323 return (false, true);
324 }
325
326 if trimmed.contains("is valid ") {
328 return (true, false);
329 }
330
331 (true, false)
333 }
334
335 fn parse_comparison<'a>(s: &'a str, operator: &str) -> Option<(&'a str, f64)> {
337 let parts: Vec<&str> = s.splitn(2, operator).collect();
338 if parts.len() == 2 {
339 let field = parts[0].trim();
340 let num_str = parts[1].trim();
341 if let Ok(num) = num_str.parse::<f64>() {
342 return Some((field, num));
343 }
344 }
345 None
346 }
347
348 fn numeric_cmp(
350 state: &ExecutionState,
351 field: &str,
352 rhs: f64,
353 cmp: fn(f64, f64) -> bool,
354 ) -> bool {
355 match state.get(field) {
356 Some(Value::Integer(i)) => cmp(*i as f64, rhs),
357 Some(Value::Float(f)) => cmp(*f, rhs),
358 _ => false,
359 }
360 }
361
362 pub fn check_invariants(
364 invariants: &[String],
365 state: &ExecutionState,
366 ) -> std::result::Result<(), Vec<String>> {
367 let mut violations = Vec::new();
368 for inv in invariants {
369 let (result, evaluable) = Self::evaluate(inv, state);
370 if evaluable && !result {
371 violations.push(inv.clone());
372 }
373 }
374 if violations.is_empty() {
375 Ok(())
376 } else {
377 Err(violations)
378 }
379 }
380}
381
382#[derive(Debug, Clone)]
386pub struct Sandbox {
387 pub max_memory_bytes: u64,
389 pub computation_timeout_ms: u64,
391 pub max_state_size_bytes: u64,
393 pub mode: SandboxMode,
395 pub permissions: Vec<String>,
397}
398
399#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
401pub enum SandboxMode {
402 FullIsolation,
404 Restricted,
406 None,
408}
409
410impl Sandbox {
411 pub fn from_contract(contract: &Contract) -> Self {
413 let mode = match contract.execution_constraints.sandbox_mode.as_str() {
414 "full_isolation" => SandboxMode::FullIsolation,
415 "restricted" => SandboxMode::Restricted,
416 "none" => SandboxMode::None,
417 _ => SandboxMode::FullIsolation, };
419
420 Sandbox {
421 max_memory_bytes: contract
422 .execution_constraints
423 .resource_limits
424 .max_memory_bytes,
425 computation_timeout_ms: contract
426 .execution_constraints
427 .resource_limits
428 .computation_timeout_ms,
429 max_state_size_bytes: contract
430 .execution_constraints
431 .resource_limits
432 .max_state_size_bytes,
433 mode,
434 permissions: contract.execution_constraints.external_permissions.clone(),
435 }
436 }
437
438 pub fn check_memory(&self, state: &ExecutionState) -> Result<()> {
440 let used = state.memory_bytes();
441 if used > self.max_state_size_bytes {
442 return Err(Error::ExecutionError(format!(
443 "State size {} bytes exceeds limit of {} bytes",
444 used, self.max_state_size_bytes
445 )));
446 }
447 if used > self.max_memory_bytes {
448 return Err(Error::ExecutionError(format!(
449 "Memory usage {} bytes exceeds limit of {} bytes",
450 used, self.max_memory_bytes
451 )));
452 }
453 Ok(())
454 }
455
456 pub fn check_permissions(&self, required: &[String]) -> Result<()> {
458 if self.mode == SandboxMode::FullIsolation && !required.is_empty() {
459 return Err(Error::ExecutionError(
460 "Full isolation sandbox does not permit external access".into(),
461 ));
462 }
463 for perm in required {
464 if !self.permissions.contains(perm) {
465 return Err(Error::ExecutionError(format!(
466 "Permission '{}' not granted in sandbox",
467 perm
468 )));
469 }
470 }
471 Ok(())
472 }
473}
474
475#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
479pub struct ProvenanceEntry {
480 pub sequence: u64,
482 pub operation: String,
484 pub inputs: serde_json::Value,
486 pub state_before: BTreeMap<String, Value>,
488 pub state_after: BTreeMap<String, Value>,
490 pub changes: Vec<StateChange>,
492 pub postconditions_verified: bool,
494 pub invariants_verified: bool,
496}
497
498#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
500pub struct StateChange {
501 pub field: String,
502 pub old_value: Value,
503 pub new_value: Value,
504}
505
506#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
508pub struct ProvenanceLog {
509 pub entries: Vec<ProvenanceEntry>,
510}
511
512impl ProvenanceLog {
513 pub fn new() -> Self {
514 ProvenanceLog {
515 entries: Vec::new(),
516 }
517 }
518
519 pub fn append(&mut self, entry: ProvenanceEntry) {
520 self.entries.push(entry);
521 }
522
523 pub fn len(&self) -> usize {
524 self.entries.len()
525 }
526
527 pub fn is_empty(&self) -> bool {
528 self.entries.is_empty()
529 }
530}
531
532impl Default for ProvenanceLog {
533 fn default() -> Self {
534 Self::new()
535 }
536}
537
538#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
542pub struct OperationResult {
543 pub operation: String,
545 pub success: bool,
547 pub state: BTreeMap<String, Value>,
549 pub error: Option<String>,
551 pub provenance: Option<ProvenanceEntry>,
553}
554
555#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
557pub struct ExecutionResult {
558 pub contract_id: String,
560 pub success: bool,
562 pub operations: Vec<OperationResult>,
564 pub final_state: BTreeMap<String, Value>,
566 pub provenance: ProvenanceLog,
568 pub error: Option<String>,
570}
571
572pub struct Executor {
576 contract: Contract,
578 state: ExecutionState,
580 sandbox: Sandbox,
582 provenance: ProvenanceLog,
584 sequence: u64,
586}
587
588impl Executor {
589 pub fn new(contract: Contract) -> Self {
591 let state = ExecutionState::from_contract(&contract);
592 let sandbox = Sandbox::from_contract(&contract);
593 Executor {
594 contract,
595 state,
596 sandbox,
597 provenance: ProvenanceLog::new(),
598 sequence: 0,
599 }
600 }
601
602 pub fn execute_operation(
604 &mut self,
605 operation_name: &str,
606 inputs_json: &str,
607 ) -> Result<OperationResult> {
608 #[cfg(not(target_arch = "wasm32"))]
609 let start = Instant::now();
610
611 let op = self
613 .contract
614 .behavioral_semantics
615 .operations
616 .iter()
617 .find(|o| o.name == operation_name)
618 .ok_or_else(|| {
619 Error::ExecutionError(format!(
620 "Operation '{}' not found in contract",
621 operation_name
622 ))
623 })?
624 .clone();
625
626 let inputs: serde_json::Value = serde_json::from_str(inputs_json)
628 .map_err(|e| Error::ExecutionError(format!("Invalid JSON input: {}", e)))?;
629
630 self.validate_inputs(&op, &inputs)?;
632
633 let (pre_result, pre_evaluable) =
635 ExpressionEvaluator::evaluate(&op.precondition, &self.state);
636 if pre_evaluable && !pre_result {
637 return Err(Error::ExecutionError(format!(
638 "Precondition failed for operation '{}': {}",
639 operation_name, op.precondition
640 )));
641 }
642
643 let state_before = self.state.fields.clone();
645
646 self.apply_inputs(&inputs)?;
648
649 #[cfg(not(target_arch = "wasm32"))]
651 {
652 let elapsed_ms = start.elapsed().as_millis() as u64;
653 if elapsed_ms > self.sandbox.computation_timeout_ms {
654 self.state.fields = state_before.clone();
656 return Err(Error::ExecutionError(format!(
657 "Operation '{}' exceeded timeout of {}ms (took {}ms)",
658 operation_name, self.sandbox.computation_timeout_ms, elapsed_ms
659 )));
660 }
661 }
662
663 let (post_result, post_evaluable) =
665 ExpressionEvaluator::evaluate(&op.postcondition, &self.state);
666 let postconditions_verified = !post_evaluable || post_result;
667
668 if post_evaluable && !post_result {
669 self.state.fields = state_before;
671 return Err(Error::ContractViolation {
672 commitment: format!("postcondition of '{}'", operation_name),
673 violation: op.postcondition.clone(),
674 });
675 }
676
677 let invariants_verified = match ExpressionEvaluator::check_invariants(
679 &self.contract.data_semantics.invariants,
680 &self.state,
681 ) {
682 Ok(()) => true,
683 Err(violations) => {
684 self.state.fields = state_before;
686 return Err(Error::ContractViolation {
687 commitment: "invariant".into(),
688 violation: format!("Violated invariants: {}", violations.join(", ")),
689 });
690 }
691 };
692
693 self.sandbox.check_memory(&self.state).inspect_err(|_| {
695 self.state.fields = state_before.clone();
696 })?;
697
698 let changes = Self::compute_changes(&state_before, &self.state.fields);
700
701 let entry = ProvenanceEntry {
703 sequence: self.sequence,
704 operation: operation_name.to_string(),
705 inputs: inputs.clone(),
706 state_before,
707 state_after: self.state.fields.clone(),
708 changes,
709 postconditions_verified,
710 invariants_verified,
711 };
712 self.provenance.append(entry.clone());
713 self.sequence += 1;
714
715 Ok(OperationResult {
716 operation: operation_name.to_string(),
717 success: true,
718 state: self.state.fields.clone(),
719 error: None,
720 provenance: Some(entry),
721 })
722 }
723
724 fn validate_inputs(&self, op: &crate::Operation, inputs: &serde_json::Value) -> Result<()> {
726 if let serde_json::Value::Object(params_def) = &op.parameters {
727 if let serde_json::Value::Object(input_map) = inputs {
728 for (param_name, _param_type) in params_def {
730 if !input_map.contains_key(param_name) {
731 return Err(Error::ExecutionError(format!(
732 "Missing required parameter '{}' for operation '{}'",
733 param_name, op.name
734 )));
735 }
736 }
737 }
738 }
739 Ok(())
740 }
741
742 fn apply_inputs(&mut self, inputs: &serde_json::Value) -> Result<()> {
744 if let serde_json::Value::Object(input_map) = inputs {
745 for (key, value) in input_map {
746 let typed_value = Value::from_json(value);
747 self.state.set(key.clone(), typed_value);
748 }
749 }
750 Ok(())
751 }
752
753 fn compute_changes(
755 before: &BTreeMap<String, Value>,
756 after: &BTreeMap<String, Value>,
757 ) -> Vec<StateChange> {
758 let mut changes = Vec::new();
759
760 for (key, old_val) in before {
762 match after.get(key) {
763 Some(new_val) if new_val != old_val => {
764 changes.push(StateChange {
765 field: key.clone(),
766 old_value: old_val.clone(),
767 new_value: new_val.clone(),
768 });
769 }
770 None => {
771 changes.push(StateChange {
772 field: key.clone(),
773 old_value: old_val.clone(),
774 new_value: Value::Null,
775 });
776 }
777 _ => {}
778 }
779 }
780
781 for (key, new_val) in after {
783 if !before.contains_key(key) {
784 changes.push(StateChange {
785 field: key.clone(),
786 old_value: Value::Null,
787 new_value: new_val.clone(),
788 });
789 }
790 }
791
792 changes
793 }
794
795 pub fn execute_all(&mut self, requests_json: &str) -> Result<ExecutionResult> {
798 let requests: Vec<serde_json::Value> = serde_json::from_str(requests_json)
799 .map_err(|e| Error::ExecutionError(format!("Invalid JSON requests: {}", e)))?;
800
801 let mut operation_results = Vec::new();
802
803 for req in &requests {
804 let op_name = req
805 .get("operation")
806 .and_then(|v| v.as_str())
807 .ok_or_else(|| {
808 Error::ExecutionError("Each request must have an 'operation' field".into())
809 })?;
810
811 let empty_obj = serde_json::Value::Object(serde_json::Map::new());
812 let inputs = req.get("inputs").unwrap_or(&empty_obj);
813
814 let inputs_str = serde_json::to_string(inputs)
815 .map_err(|e| Error::ExecutionError(format!("Failed to serialize inputs: {}", e)))?;
816
817 match self.execute_operation(op_name, &inputs_str) {
818 Ok(result) => operation_results.push(result),
819 Err(e) => {
820 operation_results.push(OperationResult {
821 operation: op_name.to_string(),
822 success: false,
823 state: self.state.fields.clone(),
824 error: Some(e.to_string()),
825 provenance: None,
826 });
827 return Ok(ExecutionResult {
828 contract_id: self.contract.identity.stable_id.clone(),
829 success: false,
830 operations: operation_results,
831 final_state: self.state.fields.clone(),
832 provenance: self.provenance.clone(),
833 error: Some(e.to_string()),
834 });
835 }
836 }
837 }
838
839 Ok(ExecutionResult {
840 contract_id: self.contract.identity.stable_id.clone(),
841 success: true,
842 operations: operation_results,
843 final_state: self.state.fields.clone(),
844 provenance: self.provenance.clone(),
845 error: None,
846 })
847 }
848
849 pub fn state(&self) -> &ExecutionState {
851 &self.state
852 }
853
854 pub fn provenance(&self) -> &ProvenanceLog {
856 &self.provenance
857 }
858}
859
860pub fn execute_contract(contract: &Contract, inputs: &str) -> Result<String> {
876 let mut executor = Executor::new(contract.clone());
877
878 let inputs_trimmed = inputs.trim();
880 let requests_json = if inputs_trimmed.starts_with('[') {
881 inputs_trimmed.to_string()
882 } else if inputs_trimmed.starts_with('{') {
883 format!("[{}]", inputs_trimmed)
884 } else {
885 return Err(Error::ExecutionError(
886 "Input must be a JSON object or array of objects".into(),
887 ));
888 };
889
890 let result = executor.execute_all(&requests_json)?;
891
892 serde_json::to_string_pretty(&result)
893 .map_err(|e| Error::ExecutionError(format!("Failed to serialize result: {}", e)))
894}
895
896#[cfg(test)]
899mod tests {
900 use super::*;
901 use crate::*;
902
903 fn test_contract() -> Contract {
905 Contract {
906 identity: Identity {
907 stable_id: "ic-test-001".into(),
908 version: 1,
909 created_timestamp: "2026-02-01T10:00:00Z".into(),
910 owner: "test".into(),
911 semantic_hash: "abc123".into(),
912 },
913 purpose_statement: PurposeStatement {
914 narrative: "Test contract".into(),
915 intent_source: "test".into(),
916 confidence_level: 1.0,
917 },
918 data_semantics: DataSemantics {
919 state: serde_json::json!({
920 "message": "String",
921 "count": "Integer"
922 }),
923 invariants: vec!["message is not empty".into(), "count >= 0".into()],
924 },
925 behavioral_semantics: BehavioralSemantics {
926 operations: vec![Operation {
927 name: "echo".into(),
928 precondition: "input_provided".into(),
929 parameters: serde_json::json!({
930 "message": "String"
931 }),
932 postcondition: "state_updated".into(),
933 side_effects: vec!["log_operation".into()],
934 idempotence: "idempotent".into(),
935 }],
936 },
937 execution_constraints: ExecutionConstraints {
938 trigger_types: vec!["manual".into()],
939 resource_limits: ResourceLimits {
940 max_memory_bytes: 1_048_576,
941 computation_timeout_ms: 1000,
942 max_state_size_bytes: 1_048_576,
943 },
944 external_permissions: vec![],
945 sandbox_mode: "full_isolation".into(),
946 },
947 human_machine_contract: HumanMachineContract {
948 system_commitments: vec!["All messages echoed".into()],
949 system_refusals: vec!["Will not lose data".into()],
950 user_obligations: vec!["Provide messages".into()],
951 },
952 }
953 }
954
955 #[test]
958 #[allow(clippy::approx_constant)]
959 fn test_value_from_json_primitives() {
960 assert_eq!(Value::from_json(&serde_json::json!(null)), Value::Null);
961 assert_eq!(
962 Value::from_json(&serde_json::json!(true)),
963 Value::Boolean(true)
964 );
965 assert_eq!(Value::from_json(&serde_json::json!(42)), Value::Integer(42));
966 assert_eq!(
967 Value::from_json(&serde_json::json!(3.14)),
968 Value::Float(3.14)
969 );
970 assert_eq!(
971 Value::from_json(&serde_json::json!("hello")),
972 Value::String("hello".into())
973 );
974 }
975
976 #[test]
977 fn test_value_from_json_collections() {
978 let arr = Value::from_json(&serde_json::json!([1, 2, 3]));
979 assert_eq!(
980 arr,
981 Value::Array(vec![
982 Value::Integer(1),
983 Value::Integer(2),
984 Value::Integer(3),
985 ])
986 );
987
988 let obj = Value::from_json(&serde_json::json!({"a": 1, "b": "two"}));
989 let mut expected = BTreeMap::new();
990 expected.insert("a".into(), Value::Integer(1));
991 expected.insert("b".into(), Value::String("two".into()));
992 assert_eq!(obj, Value::Object(expected));
993 }
994
995 #[test]
996 fn test_value_roundtrip_json() {
997 let original = serde_json::json!({
998 "name": "test",
999 "count": 42,
1000 "active": true,
1001 "items": [1, 2, 3]
1002 });
1003 let value = Value::from_json(&original);
1004 let back = value.to_json();
1005 assert_eq!(original, back);
1006 }
1007
1008 #[test]
1009 fn test_value_is_truthy() {
1010 assert!(!Value::Null.is_truthy());
1011 assert!(!Value::Boolean(false).is_truthy());
1012 assert!(Value::Boolean(true).is_truthy());
1013 assert!(!Value::Integer(0).is_truthy());
1014 assert!(Value::Integer(1).is_truthy());
1015 assert!(!Value::String(String::new()).is_truthy());
1016 assert!(Value::String("hello".into()).is_truthy());
1017 assert!(!Value::Array(vec![]).is_truthy());
1018 assert!(Value::Array(vec![Value::Integer(1)]).is_truthy());
1019 }
1020
1021 #[test]
1022 fn test_value_display() {
1023 assert_eq!(format!("{}", Value::Null), "null");
1024 assert_eq!(format!("{}", Value::Boolean(true)), "true");
1025 assert_eq!(format!("{}", Value::Integer(42)), "42");
1026 assert_eq!(format!("{}", Value::String("hi".into())), "\"hi\"");
1027 }
1028
1029 #[test]
1032 fn test_execution_state_from_contract() {
1033 let contract = test_contract();
1034 let state = ExecutionState::from_contract(&contract);
1035
1036 assert_eq!(state.get("message"), Some(&Value::String(String::new())));
1037 assert_eq!(state.get("count"), Some(&Value::Integer(0)));
1038 }
1039
1040 #[test]
1041 fn test_execution_state_set_get() {
1042 let mut state = ExecutionState {
1043 fields: BTreeMap::new(),
1044 };
1045 state.set("x".into(), Value::Integer(10));
1046 assert_eq!(state.get("x"), Some(&Value::Integer(10)));
1047
1048 let old = state.set("x".into(), Value::Integer(20));
1049 assert_eq!(old, Some(Value::Integer(10)));
1050 assert_eq!(state.get("x"), Some(&Value::Integer(20)));
1051 }
1052
1053 #[test]
1054 fn test_execution_state_memory_bytes() {
1055 let mut state = ExecutionState {
1056 fields: BTreeMap::new(),
1057 };
1058 let empty_size = state.memory_bytes();
1059 state.set("big_string".into(), Value::String("x".repeat(1000)));
1060 assert!(state.memory_bytes() > empty_size + 1000);
1061 }
1062
1063 #[test]
1066 fn test_eval_is_not_empty_true() {
1067 let mut state = ExecutionState {
1068 fields: BTreeMap::new(),
1069 };
1070 state.set("message".into(), Value::String("hello".into()));
1071 let (result, evaluable) = ExpressionEvaluator::evaluate("message is not empty", &state);
1072 assert!(evaluable);
1073 assert!(result);
1074 }
1075
1076 #[test]
1077 fn test_eval_is_not_empty_false() {
1078 let mut state = ExecutionState {
1079 fields: BTreeMap::new(),
1080 };
1081 state.set("message".into(), Value::String(String::new()));
1082 let (result, evaluable) = ExpressionEvaluator::evaluate("message is not empty", &state);
1083 assert!(evaluable);
1084 assert!(!result);
1085 }
1086
1087 #[test]
1088 fn test_eval_numeric_comparisons() {
1089 let mut state = ExecutionState {
1090 fields: BTreeMap::new(),
1091 };
1092 state.set("count".into(), Value::Integer(5));
1093
1094 assert!(ExpressionEvaluator::evaluate("count >= 0", &state).0);
1095 assert!(ExpressionEvaluator::evaluate("count >= 5", &state).0);
1096 assert!(!ExpressionEvaluator::evaluate("count >= 6", &state).0);
1097 assert!(ExpressionEvaluator::evaluate("count > 4", &state).0);
1098 assert!(!ExpressionEvaluator::evaluate("count > 5", &state).0);
1099 assert!(ExpressionEvaluator::evaluate("count <= 5", &state).0);
1100 assert!(ExpressionEvaluator::evaluate("count < 6", &state).0);
1101 assert!(!ExpressionEvaluator::evaluate("count < 5", &state).0);
1102 }
1103
1104 #[test]
1105 fn test_eval_is_boolean() {
1106 let mut state = ExecutionState {
1107 fields: BTreeMap::new(),
1108 };
1109 state.set("flag".into(), Value::Boolean(true));
1110 state.set("count".into(), Value::Integer(5));
1111
1112 assert!(ExpressionEvaluator::evaluate("flag is boolean", &state).0);
1113 assert!(!ExpressionEvaluator::evaluate("count is boolean", &state).0);
1114 }
1115
1116 #[test]
1117 fn test_eval_opaque_condition() {
1118 let state = ExecutionState {
1119 fields: BTreeMap::new(),
1120 };
1121 let (result, evaluable) = ExpressionEvaluator::evaluate("some_opaque_condition", &state);
1122 assert!(!evaluable);
1123 assert!(result); }
1125
1126 #[test]
1127 fn test_check_invariants_all_pass() {
1128 let mut state = ExecutionState {
1129 fields: BTreeMap::new(),
1130 };
1131 state.set("message".into(), Value::String("hello".into()));
1132 state.set("count".into(), Value::Integer(5));
1133
1134 let invariants = vec!["message is not empty".into(), "count >= 0".into()];
1135 assert!(ExpressionEvaluator::check_invariants(&invariants, &state).is_ok());
1136 }
1137
1138 #[test]
1139 fn test_check_invariants_one_fails() {
1140 let mut state = ExecutionState {
1141 fields: BTreeMap::new(),
1142 };
1143 state.set("message".into(), Value::String(String::new()));
1144 state.set("count".into(), Value::Integer(5));
1145
1146 let invariants = vec!["message is not empty".into(), "count >= 0".into()];
1147 let result = ExpressionEvaluator::check_invariants(&invariants, &state);
1148 assert!(result.is_err());
1149 let violations = result.unwrap_err();
1150 assert_eq!(violations, vec!["message is not empty"]);
1151 }
1152
1153 #[test]
1156 fn test_sandbox_from_contract() {
1157 let contract = test_contract();
1158 let sandbox = Sandbox::from_contract(&contract);
1159 assert_eq!(sandbox.mode, SandboxMode::FullIsolation);
1160 assert_eq!(sandbox.max_memory_bytes, 1_048_576);
1161 assert_eq!(sandbox.computation_timeout_ms, 1000);
1162 }
1163
1164 #[test]
1165 fn test_sandbox_check_memory_within_limits() {
1166 let contract = test_contract();
1167 let sandbox = Sandbox::from_contract(&contract);
1168 let state = ExecutionState::from_contract(&contract);
1169 assert!(sandbox.check_memory(&state).is_ok());
1170 }
1171
1172 #[test]
1173 fn test_sandbox_check_memory_exceeds_limit() {
1174 let contract = test_contract();
1175 let sandbox = Sandbox {
1176 max_memory_bytes: 10,
1177 max_state_size_bytes: 10,
1178 ..Sandbox::from_contract(&contract)
1179 };
1180 let mut state = ExecutionState::from_contract(&contract);
1181 state.set("big".into(), Value::String("x".repeat(100)));
1182 assert!(sandbox.check_memory(&state).is_err());
1183 }
1184
1185 #[test]
1186 fn test_sandbox_permissions_full_isolation() {
1187 let sandbox = Sandbox {
1188 max_memory_bytes: 1_000_000,
1189 computation_timeout_ms: 1000,
1190 max_state_size_bytes: 1_000_000,
1191 mode: SandboxMode::FullIsolation,
1192 permissions: vec![],
1193 };
1194 assert!(sandbox.check_permissions(&[]).is_ok());
1195 assert!(sandbox.check_permissions(&["network".to_string()]).is_err());
1196 }
1197
1198 #[test]
1199 fn test_sandbox_permissions_restricted() {
1200 let sandbox = Sandbox {
1201 max_memory_bytes: 1_000_000,
1202 computation_timeout_ms: 1000,
1203 max_state_size_bytes: 1_000_000,
1204 mode: SandboxMode::Restricted,
1205 permissions: vec!["database_query".into()],
1206 };
1207 assert!(sandbox
1208 .check_permissions(&["database_query".to_string()])
1209 .is_ok());
1210 assert!(sandbox.check_permissions(&["network".to_string()]).is_err());
1211 }
1212
1213 #[test]
1216 fn test_provenance_log_new_empty() {
1217 let log = ProvenanceLog::new();
1218 assert!(log.is_empty());
1219 assert_eq!(log.len(), 0);
1220 }
1221
1222 #[test]
1223 fn test_provenance_log_append() {
1224 let mut log = ProvenanceLog::new();
1225 let entry = ProvenanceEntry {
1226 sequence: 0,
1227 operation: "test".into(),
1228 inputs: serde_json::json!({}),
1229 state_before: BTreeMap::new(),
1230 state_after: BTreeMap::new(),
1231 changes: vec![],
1232 postconditions_verified: true,
1233 invariants_verified: true,
1234 };
1235 log.append(entry);
1236 assert_eq!(log.len(), 1);
1237 assert!(!log.is_empty());
1238 }
1239
1240 #[test]
1243 fn test_executor_new() {
1244 let contract = test_contract();
1245 let executor = Executor::new(contract);
1246 assert_eq!(
1247 executor.state().get("message"),
1248 Some(&Value::String(String::new()))
1249 );
1250 assert_eq!(executor.state().get("count"), Some(&Value::Integer(0)));
1251 assert!(executor.provenance().is_empty());
1252 }
1253
1254 #[test]
1255 fn test_execute_operation_success() {
1256 let contract = test_contract();
1257 let mut executor = Executor::new(contract);
1258
1259 let result = executor
1260 .execute_operation("echo", r#"{"message": "hello"}"#)
1261 .unwrap();
1262
1263 assert!(result.success);
1264 assert_eq!(result.operation, "echo");
1265 assert_eq!(
1266 executor.state().get("message"),
1267 Some(&Value::String("hello".into()))
1268 );
1269 assert!(result.provenance.is_some());
1270 }
1271
1272 #[test]
1273 fn test_execute_operation_not_found() {
1274 let contract = test_contract();
1275 let mut executor = Executor::new(contract);
1276
1277 let result = executor.execute_operation("nonexistent", "{}");
1278 assert!(result.is_err());
1279 let err = result.unwrap_err().to_string();
1280 assert!(err.contains("not found"));
1281 }
1282
1283 #[test]
1284 fn test_execute_operation_invalid_json() {
1285 let contract = test_contract();
1286 let mut executor = Executor::new(contract);
1287
1288 let result = executor.execute_operation("echo", "not json");
1289 assert!(result.is_err());
1290 }
1291
1292 #[test]
1293 fn test_execute_operation_invariant_violation() {
1294 let contract = test_contract();
1295 let mut executor = Executor::new(contract);
1296
1297 let result = executor.execute_operation("echo", r#"{"count": -1, "message": "hi"}"#);
1299 assert!(result.is_err());
1300 let err = result.unwrap_err().to_string();
1301 assert!(err.contains("invariant") || err.contains("Violated"));
1302 }
1303
1304 #[test]
1305 fn test_execute_operation_state_rollback_on_failure() {
1306 let contract = test_contract();
1307 let mut executor = Executor::new(contract);
1308
1309 executor
1311 .execute_operation("echo", r#"{"message": "hello"}"#)
1312 .unwrap();
1313
1314 let state_after_success = executor.state().clone();
1315
1316 let _ = executor.execute_operation("echo", r#"{"count": -1, "message": "hi"}"#);
1318
1319 assert_eq!(*executor.state(), state_after_success);
1321 }
1322
1323 #[test]
1324 fn test_execute_all_success() {
1325 let contract = test_contract();
1326 let mut executor = Executor::new(contract);
1327
1328 let requests = r#"[
1329 {"operation": "echo", "inputs": {"message": "hello"}},
1330 {"operation": "echo", "inputs": {"message": "world"}}
1331 ]"#;
1332
1333 let result = executor.execute_all(requests).unwrap();
1334 assert!(result.success);
1335 assert_eq!(result.operations.len(), 2);
1336 assert_eq!(result.provenance.len(), 2);
1337 }
1338
1339 #[test]
1340 fn test_execute_all_stops_on_failure() {
1341 let contract = test_contract();
1342 let mut executor = Executor::new(contract);
1343
1344 let requests = r#"[
1345 {"operation": "echo", "inputs": {"message": "hello"}},
1346 {"operation": "nonexistent", "inputs": {}},
1347 {"operation": "echo", "inputs": {"message": "world"}}
1348 ]"#;
1349
1350 let result = executor.execute_all(requests).unwrap();
1351 assert!(!result.success);
1352 assert_eq!(result.operations.len(), 2); }
1354
1355 #[test]
1356 fn test_provenance_records_state_changes() {
1357 let contract = test_contract();
1358 let mut executor = Executor::new(contract);
1359
1360 executor
1361 .execute_operation("echo", r#"{"message": "hello"}"#)
1362 .unwrap();
1363
1364 let log = executor.provenance();
1365 assert_eq!(log.len(), 1);
1366
1367 let entry = &log.entries[0];
1368 assert_eq!(entry.operation, "echo");
1369 assert_eq!(entry.sequence, 0);
1370 assert!(entry.postconditions_verified);
1371 assert!(entry.invariants_verified);
1372 assert!(!entry.changes.is_empty());
1373
1374 let msg_change = entry.changes.iter().find(|c| c.field == "message").unwrap();
1376 assert_eq!(msg_change.old_value, Value::String(String::new()));
1377 assert_eq!(msg_change.new_value, Value::String("hello".into()));
1378 }
1379
1380 #[test]
1381 fn test_provenance_sequential_numbering() {
1382 let contract = test_contract();
1383 let mut executor = Executor::new(contract);
1384
1385 executor
1386 .execute_operation("echo", r#"{"message": "first"}"#)
1387 .unwrap();
1388 executor
1389 .execute_operation("echo", r#"{"message": "second"}"#)
1390 .unwrap();
1391 executor
1392 .execute_operation("echo", r#"{"message": "third"}"#)
1393 .unwrap();
1394
1395 let log = executor.provenance();
1396 assert_eq!(log.entries[0].sequence, 0);
1397 assert_eq!(log.entries[1].sequence, 1);
1398 assert_eq!(log.entries[2].sequence, 2);
1399 }
1400
1401 #[test]
1404 fn test_execute_contract_single_request() {
1405 let contract = test_contract();
1406 let result = execute_contract(
1407 &contract,
1408 r#"{"operation": "echo", "inputs": {"message": "hello"}}"#,
1409 )
1410 .unwrap();
1411
1412 let json: serde_json::Value = serde_json::from_str(&result).unwrap();
1413 assert_eq!(json["success"], true);
1414 assert_eq!(json["contract_id"], "ic-test-001");
1415 }
1416
1417 #[test]
1418 fn test_execute_contract_array_requests() {
1419 let contract = test_contract();
1420 let result = execute_contract(
1421 &contract,
1422 r#"[{"operation": "echo", "inputs": {"message": "hello"}}]"#,
1423 )
1424 .unwrap();
1425
1426 let json: serde_json::Value = serde_json::from_str(&result).unwrap();
1427 assert_eq!(json["success"], true);
1428 }
1429
1430 #[test]
1431 fn test_execute_contract_invalid_input() {
1432 let contract = test_contract();
1433 let result = execute_contract(&contract, "not json");
1434 assert!(result.is_err());
1435 }
1436
1437 #[test]
1440 fn test_deterministic_execution() {
1441 let contract = test_contract();
1442 let input = r#"{"operation": "echo", "inputs": {"message": "determinism test"}}"#;
1443
1444 let first = execute_contract(&contract, input).unwrap();
1445 for i in 0..100 {
1446 let result = execute_contract(&contract, input).unwrap();
1447 assert_eq!(first, result, "Non-determinism at iteration {}", i);
1448 }
1449 }
1450
1451 #[test]
1452 fn test_deterministic_multi_operation() {
1453 let contract = test_contract();
1454 let input = r#"[
1455 {"operation": "echo", "inputs": {"message": "first"}},
1456 {"operation": "echo", "inputs": {"message": "second"}}
1457 ]"#;
1458
1459 let first = execute_contract(&contract, input).unwrap();
1460 for i in 0..100 {
1461 let result = execute_contract(&contract, input).unwrap();
1462 assert_eq!(first, result, "Non-determinism at iteration {}", i);
1463 }
1464 }
1465
1466 #[test]
1467 fn test_deterministic_provenance() {
1468 let contract = test_contract();
1469 let input = r#"{"operation": "echo", "inputs": {"message": "prov test"}}"#;
1470
1471 let first_json: serde_json::Value =
1472 serde_json::from_str(&execute_contract(&contract, input).unwrap()).unwrap();
1473 let first_provenance = &first_json["provenance"];
1474
1475 for i in 0..100 {
1476 let result_json: serde_json::Value =
1477 serde_json::from_str(&execute_contract(&contract, input).unwrap()).unwrap();
1478 assert_eq!(
1479 first_provenance, &result_json["provenance"],
1480 "Provenance non-determinism at iteration {}",
1481 i
1482 );
1483 }
1484 }
1485
1486 #[test]
1489 fn test_resource_limit_memory_exceeded() {
1490 let mut contract = test_contract();
1491 contract
1492 .execution_constraints
1493 .resource_limits
1494 .max_state_size_bytes = 10;
1495 contract
1496 .execution_constraints
1497 .resource_limits
1498 .max_memory_bytes = 10;
1499 contract.data_semantics.invariants.clear();
1501
1502 let mut executor = Executor::new(contract);
1503 let result = executor.execute_operation(
1504 "echo",
1505 r#"{"message": "this string is way too long for the tiny memory limit we set"}"#,
1506 );
1507 assert!(result.is_err());
1508 let err = result.unwrap_err().to_string();
1509 assert!(err.contains("exceeds limit") || err.contains("bytes"));
1510 }
1511
1512 #[test]
1513 fn test_precondition_enforcement() {
1514 let mut contract = test_contract();
1516 contract.behavioral_semantics.operations[0].precondition = "count >= 10".into();
1517 contract.data_semantics.invariants.clear();
1519
1520 let mut executor = Executor::new(contract);
1521 let result = executor.execute_operation("echo", r#"{"message": "hello"}"#);
1523 assert!(result.is_err());
1524 let err = result.unwrap_err().to_string();
1525 assert!(err.contains("Precondition failed"));
1526 }
1527
1528 #[test]
1529 fn test_postcondition_verification() {
1530 let mut contract = test_contract();
1532 contract.behavioral_semantics.operations[0].postcondition = "count >= 1".into();
1533 contract.data_semantics.invariants.clear();
1535
1536 let mut executor = Executor::new(contract);
1537 let result = executor.execute_operation("echo", r#"{"message": "hello"}"#);
1539 assert!(result.is_err());
1540 let err = result.unwrap_err().to_string();
1541 assert!(err.contains("postcondition") || err.contains("Contract violation"));
1542 }
1543}