Skip to main content

lsp_max/runtime/control_plane/kernel/
raw_candidate.rs

1use crate::runtime::control_plane::admission::{
2    CandidateData, GraphAdmissionError, GraphAdmissionLaw, RawData, CANDIDATE, RAW,
3};
4use crate::runtime::control_plane::receipts::{Blake3Hash, CryptographicReceipt};
5use crate::runtime::{ChainError, Machine, TypestateKernel};
6use ed25519_dalek::Signer;
7
8use super::bytes_to_hex;
9use super::CandidateAdmitInput;
10
11// ==========================================
12// TypestateKernel for RAW
13// ==========================================
14impl TypestateKernel<GraphAdmissionLaw, RAW, RawData> for Machine<GraphAdmissionLaw, RAW, RawData> {
15    type Input = String; // representing snapshot_id
16    type OutputPhase = CANDIDATE;
17    type OutputData = CandidateData;
18    type Receipt = CryptographicReceipt;
19
20    fn validate(&self, _input: &Self::Input) -> Result<(), GraphAdmissionError> {
21        Ok(())
22    }
23
24    fn select(&self, _input: &Self::Input) -> Self::OutputPhase {
25        CANDIDATE
26    }
27
28    fn admit(
29        self,
30        input: Self::Input,
31    ) -> Result<Machine<GraphAdmissionLaw, Self::OutputPhase, Self::OutputData>, GraphAdmissionError>
32    {
33        self.validate(&input)?;
34        self.admit_candidate(&input)
35    }
36
37    fn receipt(&self) -> Self::Receipt {
38        let consequence_hash = Blake3Hash([0u8; 32]);
39        let mut receipt = CryptographicReceipt {
40            prev_hash: Blake3Hash([0u8; 32]),
41            discipline_id: uuid::Uuid::nil(),
42            law_id: uuid::Uuid::nil(),
43            consequence_hash,
44            sequence: 0,
45            signature: [0u8; 64],
46        };
47        let payload_hash = receipt.compute_payload_hash();
48        let signing_key = ed25519_dalek::SigningKey::from_bytes(&[0u8; 32]);
49        receipt.signature = signing_key.sign(&payload_hash.0).to_bytes();
50        receipt
51    }
52
53    fn exit(self) -> RawData {
54        self.data
55    }
56
57    fn replay(history: Vec<Self::Receipt>) -> Result<Self, ChainError> {
58        if history.is_empty() {
59            return Err(ChainError::EmptyHistory);
60        }
61        if history[0].sequence != 0 {
62            return Err(ChainError::ReceiptIdMismatch {
63                index: 0,
64                detail: "Expected genesis receipt with sequence 0".to_string(),
65            });
66        }
67        Ok(Machine::new(
68            RAW,
69            RawData {
70                elements: Vec::new(),
71            },
72        ))
73    }
74}
75
76// ==========================================
77// TypestateKernel for CANDIDATE
78// ==========================================
79impl TypestateKernel<GraphAdmissionLaw, CANDIDATE, CandidateData>
80    for Machine<GraphAdmissionLaw, CANDIDATE, CandidateData>
81{
82    type Input = CandidateAdmitInput;
83    type OutputPhase = crate::runtime::control_plane::admission::ADMITTED;
84    type OutputData = crate::runtime::control_plane::admission::AdmittedData;
85    type Receipt = CryptographicReceipt;
86
87    fn validate(&self, input: &Self::Input) -> Result<(), GraphAdmissionError> {
88        if input.receipt.sequence == 0 {
89            return Err(GraphAdmissionError::ParsingFailed(
90                "Receipt sequence cannot be 0".to_string(),
91            ));
92        }
93        Ok(())
94    }
95
96    fn select(&self, _input: &Self::Input) -> Self::OutputPhase {
97        crate::runtime::control_plane::admission::ADMITTED
98    }
99
100    fn admit(
101        self,
102        input: Self::Input,
103    ) -> Result<Machine<GraphAdmissionLaw, Self::OutputPhase, Self::OutputData>, GraphAdmissionError>
104    {
105        self.validate(&input)?;
106        self.admit_admitted(&input.store, input.graph_name, input.receipt)
107    }
108
109    fn receipt(&self) -> Self::Receipt {
110        let mut hasher = blake3::Hasher::new();
111        hasher.update(self.data.graph_hash.as_bytes());
112        let consequence_hash = Blake3Hash(*hasher.finalize().as_bytes());
113
114        let mut receipt = CryptographicReceipt {
115            prev_hash: Blake3Hash([0u8; 32]),
116            discipline_id: uuid::Uuid::nil(),
117            law_id: uuid::Uuid::nil(),
118            consequence_hash,
119            sequence: 1,
120            signature: [0u8; 64],
121        };
122        let payload_hash = receipt.compute_payload_hash();
123        let signing_key = ed25519_dalek::SigningKey::from_bytes(&[0u8; 32]);
124        receipt.signature = signing_key.sign(&payload_hash.0).to_bytes();
125        receipt
126    }
127
128    fn exit(self) -> CandidateData {
129        self.data
130    }
131
132    fn replay(history: Vec<Self::Receipt>) -> Result<Self, ChainError> {
133        if history.len() < 2 {
134            return Err(ChainError::InsufficientHistory {
135                required: 2,
136                got: history.len(),
137            });
138        }
139        let signing_key = ed25519_dalek::SigningKey::from_bytes(&[0u8; 32]);
140        let verifying_key = signing_key.verifying_key();
141        crate::runtime::control_plane::receipts::verify_receipt_chain(
142            &history,
143            &verifying_key,
144            &history[0].prev_hash,
145        )
146        .map_err(|e| ChainError::HashMismatch {
147            index: 1,
148            expected: "Valid signature".to_string(),
149            got: e.to_string(),
150        })?;
151
152        let graph_hash = bytes_to_hex(&history[1].consequence_hash.0);
153        Ok(Machine::new(
154            CANDIDATE,
155            CandidateData {
156                elements: Vec::new(),
157                quads: Vec::new(),
158                graph_hash,
159            },
160        ))
161    }
162}