made_core/value_objects/execution/
execution_operation.rs1use serde::{Deserialize, Serialize};
2
3use super::{ExecutionOperationId, ExecutionRequestBytes, ExecutionRequestDigest};
4use crate::error::DomainError;
5use crate::value_objects::ceremony::{
6 CeremonyId, StateIteration, StateVisit, StepId, StepIteration,
7};
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
11pub struct ExecutionOperation {
12 operation_id: ExecutionOperationId,
13 ceremony_id: CeremonyId,
14 step_id: StepId,
15 state_visit: StateVisit,
16 state_iteration: StateIteration,
17 step_iteration: StepIteration,
18 request: ExecutionRequestBytes,
19 request_digest: ExecutionRequestDigest,
20}
21
22impl ExecutionOperation {
23 #[must_use]
24 pub fn new(
25 ceremony_id: CeremonyId,
26 step_id: StepId,
27 state_visit: StateVisit,
28 state_iteration: StateIteration,
29 step_iteration: StepIteration,
30 request: ExecutionRequestBytes,
31 ) -> Self {
32 let operation_id = ExecutionOperationId::for_step(
33 &ceremony_id,
34 &step_id,
35 state_visit,
36 state_iteration,
37 step_iteration,
38 );
39 let request_digest = request.digest();
40 Self {
41 operation_id,
42 ceremony_id,
43 step_id,
44 state_visit,
45 state_iteration,
46 step_iteration,
47 request,
48 request_digest,
49 }
50 }
51
52 #[must_use]
53 pub const fn operation_id(&self) -> &ExecutionOperationId {
54 &self.operation_id
55 }
56
57 #[must_use]
58 pub const fn ceremony_id(&self) -> &CeremonyId {
59 &self.ceremony_id
60 }
61
62 #[must_use]
63 pub const fn step_id(&self) -> &StepId {
64 &self.step_id
65 }
66
67 #[must_use]
68 pub const fn state_visit(&self) -> StateVisit {
69 self.state_visit
70 }
71
72 #[must_use]
73 pub const fn state_iteration(&self) -> StateIteration {
74 self.state_iteration
75 }
76
77 #[must_use]
78 pub const fn step_iteration(&self) -> StepIteration {
79 self.step_iteration
80 }
81
82 #[must_use]
83 pub const fn request(&self) -> &ExecutionRequestBytes {
84 &self.request
85 }
86
87 #[must_use]
88 pub const fn request_digest(&self) -> &ExecutionRequestDigest {
89 &self.request_digest
90 }
91
92 pub fn validate(&self) -> Result<(), DomainError> {
94 let expected_id = ExecutionOperationId::for_step(
95 &self.ceremony_id,
96 &self.step_id,
97 self.state_visit,
98 self.state_iteration,
99 self.step_iteration,
100 );
101 if self.operation_id != expected_id || self.request_digest != self.request.digest() {
102 return Err(DomainError::InvariantViolated {
103 reason: "execution operation derived fields do not match its semantic input",
104 });
105 }
106 Ok(())
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use serde_json::json;
113
114 use super::*;
115
116 fn operation() -> ExecutionOperation {
117 ExecutionOperation::new(
118 CeremonyId::new("ceremony").unwrap(),
119 StepId::new("work").unwrap(),
120 StateVisit::FIRST,
121 StateIteration::FIRST,
122 StepIteration::FIRST,
123 ExecutionRequestBytes::new(b"semantic request".to_vec()).unwrap(),
124 )
125 }
126
127 #[test]
128 fn deserialized_derived_fields_are_revalidated_before_use() {
129 let original = operation();
130 original.validate().unwrap();
131 let mut raw = serde_json::to_value(original).unwrap();
132 raw["request_digest"] = json!("0".repeat(64));
133 let corrupted: ExecutionOperation = serde_json::from_value(raw).unwrap();
134 assert!(corrupted.validate().is_err());
135 }
136}