1#![no_std]
2#![doc = "Causal operation model types for BCX."]
3
4mod event;
5mod statement;
6mod truth;
7
8pub use event::{
9 AdmissionResult, CauseCapsule, CauseCapsuleParts, CauseKind, EffectResult, OperationAction,
10 RelationshipKind,
11};
12pub use statement::{
13 Admission, Checkpoint, Contradiction, Delegation, Effect, Intent, Revocation, StatementKind,
14};
15pub use truth::{AssuranceLevel, TruthStatus};
16
17#[cfg(test)]
18mod tests {
19 use super::*;
20 use bcx_core::{
21 CapabilityRef, CheckpointId, Digest, EventId, PolicyId, StatementId, SubjectId,
22 ValidationError,
23 };
24 use bcx_wire::WireLimits;
25
26 fn event(byte: u8) -> Result<EventId, ValidationError> {
27 EventId::new(Digest::new([byte; Digest::LEN]))
28 }
29
30 fn statement(byte: u8) -> Result<StatementId, ValidationError> {
31 StatementId::new(&[byte; Digest::LEN])
32 }
33
34 fn subject(bytes: &[u8]) -> Result<SubjectId, ValidationError> {
35 SubjectId::new(bytes)
36 }
37
38 fn policy(bytes: &[u8]) -> Result<PolicyId, ValidationError> {
39 PolicyId::new(bytes)
40 }
41
42 fn capability(byte: u8) -> Result<CapabilityRef, ValidationError> {
43 CapabilityRef::new(Digest::new([byte; Digest::LEN]))
44 }
45
46 fn checkpoint(byte: u8) -> Result<CheckpointId, ValidationError> {
47 CheckpointId::new(&[byte; Digest::LEN])
48 }
49
50 #[test]
51 fn cause_capsule_rejects_empty_parents() -> Result<(), ValidationError> {
52 assert_eq!(
53 CauseCapsule::new(
54 CauseCapsuleParts {
55 event_id: event(1)?,
56 parents: &[],
57 relationship: RelationshipKind::CausedBy,
58 cause_kind: CauseKind::ApplicationAction,
59 action: OperationAction::Execute,
60 authority: None,
61 policy_epoch: None,
62 },
63 WireLimits::UNSAFE_DEVELOPMENT_DO_NOT_USE_IN_PRODUCTION,
64 ),
65 Err(ValidationError::Empty)
66 );
67 Ok(())
68 }
69
70 #[test]
71 fn cause_capsule_rejects_too_many_parents() -> Result<(), ValidationError> {
72 let parents = [event(2)?, event(3)?];
73 let limits = WireLimits::new(1, 1, 1, 1)?;
74
75 assert_eq!(
76 CauseCapsule::new(
77 CauseCapsuleParts {
78 event_id: event(1)?,
79 parents: &parents,
80 relationship: RelationshipKind::JoinedFrom,
81 cause_kind: CauseKind::ApplicationAction,
82 action: OperationAction::Execute,
83 authority: None,
84 policy_epoch: None,
85 },
86 limits,
87 ),
88 Err(ValidationError::TooLarge)
89 );
90 Ok(())
91 }
92
93 #[test]
94 fn cause_capsule_rejects_self_referential_parent() -> Result<(), ValidationError> {
95 let event_id = event(1)?;
96 let parents = [event_id];
97
98 assert_eq!(
99 CauseCapsule::new(
100 CauseCapsuleParts {
101 event_id,
102 parents: &parents,
103 relationship: RelationshipKind::CausedBy,
104 cause_kind: CauseKind::ApplicationAction,
105 action: OperationAction::Execute,
106 authority: None,
107 policy_epoch: None,
108 },
109 WireLimits::UNSAFE_DEVELOPMENT_DO_NOT_USE_IN_PRODUCTION,
110 ),
111 Err(ValidationError::Malformed)
112 );
113 Ok(())
114 }
115
116 #[test]
117 fn statement_body_kinds_match_constructors() -> Result<(), ValidationError> {
118 let intent = Intent::new(
119 statement(1)?,
120 subject(b"invoice:123")?,
121 OperationAction::Create,
122 );
123 let admission = Admission::new(
124 statement(2)?,
125 statement(1)?,
126 policy(b"strict")?,
127 AdmissionResult::Allow,
128 )?;
129 let effect = Effect::new(statement(3)?, statement(1)?, EffectResult::Completed)?;
130 let delegation = Delegation::new(
131 statement(4)?,
132 subject(b"issuer")?,
133 subject(b"executor")?,
134 capability(5)?,
135 )?;
136 let revocation = Revocation::new(statement(6)?, statement(4)?, capability(7)?)?;
137 let checkpoint = Checkpoint::new(
138 statement(8)?,
139 checkpoint(9)?,
140 subject(b"ledger:checkpoint")?,
141 );
142 let contradiction = Contradiction::new(statement(10)?, statement(2)?, statement(3)?)?;
143
144 assert_eq!(intent.kind(), StatementKind::Intent);
145 assert_eq!(admission.kind(), StatementKind::Admission);
146 assert_eq!(effect.kind(), StatementKind::Effect);
147 assert_eq!(delegation.kind(), StatementKind::Delegation);
148 assert_eq!(revocation.kind(), StatementKind::Revocation);
149 assert_eq!(checkpoint.kind(), StatementKind::Checkpoint);
150 assert_eq!(contradiction.kind(), StatementKind::Contradiction);
151 Ok(())
152 }
153
154 #[test]
155 fn statement_body_validation_rejects_self_references() -> Result<(), ValidationError> {
156 assert_eq!(
157 Admission::new(
158 statement(1)?,
159 statement(1)?,
160 policy(b"strict")?,
161 AdmissionResult::Allow,
162 ),
163 Err(ValidationError::Malformed)
164 );
165 assert_eq!(
166 Effect::new(statement(2)?, statement(2)?, EffectResult::Completed),
167 Err(ValidationError::Malformed)
168 );
169 assert_eq!(
170 Revocation::new(statement(3)?, statement(3)?, capability(4)?),
171 Err(ValidationError::Malformed)
172 );
173 assert_eq!(
174 Contradiction::new(statement(5)?, statement(6)?, statement(6)?),
175 Err(ValidationError::Malformed)
176 );
177 Ok(())
178 }
179
180 #[test]
181 fn delegation_rejects_same_subject() -> Result<(), ValidationError> {
182 assert_eq!(
183 Delegation::new(
184 statement(1)?,
185 subject(b"same")?,
186 subject(b"same")?,
187 capability(2)?,
188 ),
189 Err(ValidationError::Malformed)
190 );
191 Ok(())
192 }
193}