File size: 4,078 Bytes
1d3f990
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Instruction type and validation

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;

/// Canonical instruction for LOC dispatch
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Instruction {
    pub protocol_version: String,
    pub instruction_id: String,
    pub symbol: String,
    pub target_runtime: String,
    pub verb: String,
    pub arguments: HashMap<String, serde_json::Value>,
    pub timestamp: u64,
    pub instruction_hash: String,
    pub capability_id: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub source_cell_id: Option<String>,
}

impl Instruction {
    /// Create a new instruction
    pub fn new(
        symbol: String,
        target_runtime: String,
        verb: String,
        arguments: HashMap<String, serde_json::Value>,
        capability_id: String,
    ) -> Self {
        let timestamp = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_secs();

        let instruction_id = Uuid::new_v4().to_string();
        let instruction_hash = Self::compute_hash(&symbol, &target_runtime, &verb, &arguments);

        Instruction {
            protocol_version: "1.0.0".to_string(),
            instruction_id,
            symbol,
            target_runtime,
            verb,
            arguments,
            timestamp,
            instruction_hash,
            capability_id,
            source_cell_id: None,
        }
    }

    /// Compute canonical hash of instruction
    fn compute_hash(symbol: &str, runtime: &str, verb: &str, args: &HashMap<String, serde_json::Value>) -> String {
        use sha2::{Sha256, Digest};

        let canonical = serde_json::json!({
            "symbol": symbol,
            "runtime": runtime,
            "verb": verb,
            "arguments": args
        });

        let mut hasher = Sha256::new();
        hasher.update(canonical.to_string().as_bytes());
        format!("{:x}", hasher.finalize())
    }

    /// Verify instruction hash
    pub fn verify_hash(&self) -> bool {
        let computed = Self::compute_hash(&self.symbol, &self.target_runtime, &self.verb, &self.arguments);
        computed == self.instruction_hash
    }

    /// Add source cell ID
    pub fn with_source_cell(mut self, cell_id: String) -> Self {
        self.source_cell_id = Some(cell_id);
        self
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_instruction_creation() {
        let mut args = HashMap::new();
        args.insert("fn".to_string(), serde_json::json!("FreqAnchor1618"));

        let instr = Instruction::new(
            "⚡".to_string(),
            "holyc".to_string(),
            "Execute".to_string(),
            args,
            "capa_001".to_string(),
        );

        assert_eq!(instr.protocol_version, "1.0.0");
        assert_eq!(instr.symbol, "⚡");
        assert_eq!(instr.target_runtime, "holyc");
        assert!(instr.verify_hash());
    }

    #[test]
    fn test_instruction_hash_determinism() {
        let mut args1 = HashMap::new();
        args1.insert("fn".to_string(), serde_json::json!("FreqAnchor1618"));

        let mut args2 = HashMap::new();
        args2.insert("fn".to_string(), serde_json::json!("FreqAnchor1618"));

        let instr1 = Instruction::new(
            "⚡".to_string(),
            "holyc".to_string(),
            "Execute".to_string(),
            args1,
            "capa_001".to_string(),
        );

        let instr2 = Instruction::new(
            "⚡".to_string(),
            "holyc".to_string(),
            "Execute".to_string(),
            args2,
            "capa_001".to_string(),
        );

        // Same semantic instruction should have same hash
        // (Note: different instruction_ids due to UUID, but hash should match)
        assert_eq!(instr1.instruction_hash, instr2.instruction_hash);
    }
}