automation_structures/primitives/
audit_sink.rs1use vstd::prelude::*;
7
8verus! {
9
10pub trait ChainOperation: Copy {
12 spec fn enabled(&self, previous: u64, operation: u64) -> bool;
14
15 spec fn combine_spec(&self, previous: u64, operation: u64) -> u64;
17
18 fn enabled_exec(&self, previous: u64, operation: u64) -> (enabled: bool)
20 ensures enabled == self.enabled(previous, operation);
21
22 fn combine(&self, previous: u64, operation: u64) -> (result: u64)
24 requires self.enabled(previous, operation),
25 ensures result == self.combine_spec(previous, operation);
26}
27
28#[derive(Clone, Copy)]
30pub struct BoundedHash;
31
32impl ChainOperation for BoundedHash {
33 open spec fn enabled(&self, _previous: u64, _operation: u64) -> bool {
34 true
35 }
36
37 open spec fn combine_spec(&self, previous: u64, operation: u64) -> u64 {
38 AuditSink::<BoundedHash>::hash_spec(previous, operation) as u64
39 }
40
41 fn enabled_exec(&self, _previous: u64, _operation: u64) -> (enabled: bool) {
42 true
43 }
44
45 fn combine(&self, previous: u64, operation: u64) -> (result: u64) {
46 AuditSink::<BoundedHash>::hash_exec(previous, operation)
47 }
48}
49
50#[derive(Clone, Copy)]
52pub struct AdditiveChain;
53
54impl ChainOperation for AdditiveChain {
55 open spec fn enabled(&self, previous: u64, operation: u64) -> bool {
56 previous as int + operation as int <= u64::MAX as int
57 }
58
59 open spec fn combine_spec(&self, previous: u64, operation: u64) -> u64 {
60 (previous + operation) as u64
61 }
62
63 fn enabled_exec(&self, previous: u64, operation: u64) -> (enabled: bool) {
64 operation <= u64::MAX - previous
65 }
66
67 fn combine(&self, previous: u64, operation: u64) -> (result: u64) {
68 previous + operation
69 }
70}
71
72pub struct AuditEntry {
74 pub operation: u64,
76 pub prev_hash: u64,
78 pub hash: u64,
80}
81
82pub struct AuditSink<O: ChainOperation = BoundedHash> {
84 pub operator: O,
86 pub max_log_len: usize,
88 pub log: Vec<AuditEntry>,
90 pub last_hash: u64,
92}
93
94impl AuditSink<BoundedHash> {
95 pub open spec fn hash_spec(previous: u64, operation: u64) -> int {
97 ((previous as int) * 3 + ((operation as int) % 100) + 1) % 100
98 }
99
100 pub fn hash_exec(previous: u64, operation: u64) -> (result: u64)
102 ensures
103 result as int == Self::hash_spec(previous, operation),
104 result < 100,
105 {
106 ((previous % 100) * 3 + (operation % 100) + 1) % 100
107 }
108
109 pub fn new(max_log_len: usize) -> (sink: AuditSink<BoundedHash>)
111 ensures
112 sink.max_log_len == max_log_len,
113 sink.log@.len() == 0,
114 sink.last_hash == 0,
115 sink.inv(),
116 {
117 AuditSink::with_operator(max_log_len, BoundedHash)
118 }
119}
120
121impl<O: ChainOperation> AuditSink<O> {
122 pub fn with_operator(max_log_len: usize, operator: O) -> (sink: AuditSink<O>)
124 ensures
125 sink.operator == operator,
126 sink.max_log_len == max_log_len,
127 sink.log@.len() == 0,
128 sink.last_hash == 0,
129 sink.inv(),
130 {
131 AuditSink { operator, max_log_len, log: Vec::new(), last_hash: 0 }
132 }
133
134 pub open spec fn type_invariant(&self) -> bool {
136 self.log.len() <= self.max_log_len
137 }
138
139 pub open spec fn chain_integrity(&self) -> bool {
141 forall|index: int|
142 #![trigger self.log@[index]]
143 1 <= index < self.log.len() ==>
144 self.log@[index].prev_hash == self.log@[index - 1].hash
145 }
146
147 pub open spec fn hash_consistency(&self) -> bool {
149 if self.log.len() > 0 {
150 self.last_hash == self.log@[self.log.len() - 1].hash
151 } else {
152 self.last_hash == 0
153 }
154 }
155
156 pub open spec fn hash_binds_content(&self) -> bool {
158 forall|index: int|
159 #![trigger self.log@[index]]
160 0 <= index < self.log.len() ==>
161 self.log@[index].hash
162 == self.operator.combine_spec(
163 self.log@[index].prev_hash,
164 self.log@[index].operation,
165 )
166 }
167
168 pub open spec fn operations_enabled(&self) -> bool {
170 forall|index: int|
171 #![trigger self.log@[index]]
172 0 <= index < self.log.len() ==>
173 self.operator.enabled(
174 self.log@[index].prev_hash,
175 self.log@[index].operation,
176 )
177 }
178
179 pub open spec fn genesis_consistency(&self) -> bool {
181 self.log.len() > 0 ==> self.log@[0].prev_hash == 0
182 }
183
184 pub open spec fn inv(&self) -> bool {
186 &&& self.type_invariant()
187 &&& self.chain_integrity()
188 &&& self.hash_consistency()
189 &&& self.hash_binds_content()
190 &&& self.operations_enabled()
191 &&& self.genesis_consistency()
192 }
193
194 pub fn record(&mut self, operation: u64) -> (accepted: bool)
196 requires
197 old(self).inv(),
198 old(self).operator.enabled(old(self).last_hash, operation),
199 ensures
200 final(self).inv(),
201 final(self).operator == old(self).operator,
202 final(self).max_log_len == old(self).max_log_len,
203 accepted == (old(self).log.len() < old(self).max_log_len),
204 accepted ==> {
205 &&& final(self).log@.len() == old(self).log@.len() + 1
206 &&& final(self).last_hash
207 == old(self).operator.combine_spec(old(self).last_hash, operation)
208 &&& final(self).log@[old(self).log@.len() as int].operation == operation
209 &&& final(self).log@[old(self).log@.len() as int].prev_hash
210 == old(self).last_hash
211 &&& forall|index: int|
212 #![trigger final(self).log@[index]]
213 0 <= index < old(self).log@.len() ==>
214 final(self).log@[index] == old(self).log@[index]
215 },
216 !accepted ==>
217 final(self).log@ == old(self).log@
218 && final(self).last_hash == old(self).last_hash,
219 {
220 if self.log.len() < self.max_log_len {
221 let new_hash = self.operator.combine(self.last_hash, operation);
222 let entry = AuditEntry {
223 operation,
224 prev_hash: self.last_hash,
225 hash: new_hash,
226 };
227 assert(self.log@.len() > 0 ==>
228 self.last_hash == self.log@[self.log@.len() - 1].hash);
229 self.log.push(entry);
230 self.last_hash = new_hash;
231 assert(self.chain_integrity()) by {
232 assert forall|index: int| #![trigger self.log@[index]]
233 1 <= index < self.log.len() implies
234 self.log@[index].prev_hash == self.log@[index - 1].hash by {
235 if index < self.log.len() - 1 {
236 }
237 }
238 }
239 assert(self.hash_binds_content()) by {
240 assert forall|index: int| #![trigger self.log@[index]]
241 0 <= index < self.log.len() implies
242 self.log@[index].hash == self.operator.combine_spec(
243 self.log@[index].prev_hash,
244 self.log@[index].operation,
245 ) by {
246 if index < self.log.len() - 1 {
247 }
248 }
249 }
250 assert(self.operations_enabled()) by {
251 assert forall|index: int| #![trigger self.log@[index]]
252 0 <= index < self.log.len() implies
253 self.operator.enabled(
254 self.log@[index].prev_hash,
255 self.log@[index].operation,
256 ) by {
257 if index < self.log.len() - 1 {
258 }
259 }
260 }
261 assert(self.genesis_consistency());
262 true
263 } else {
264 false
265 }
266 }
267
268 pub fn validate(&self) -> (valid: bool)
270 ensures valid == self.inv(),
271 {
272 if self.log.len() > self.max_log_len {
273 return false;
274 }
275
276 let length = self.log.len();
277 let mut index: usize = 0;
278 let mut expected_previous: u64 = 0;
279 while index < length
280 invariant
281 index <= length,
282 length == self.log.len(),
283 self.log.len() <= self.max_log_len,
284 index == 0 ==> expected_previous == 0,
285 index > 0 ==> expected_previous == self.log@[index as int - 1].hash,
286 forall|entry: int| 0 <= entry < index ==>
287 #[trigger] self.log@[entry].hash == self.operator.combine_spec(
288 self.log@[entry].prev_hash,
289 self.log@[entry].operation,
290 ),
291 forall|entry: int| 0 <= entry < index ==>
292 #[trigger] self.operator.enabled(
293 self.log@[entry].prev_hash,
294 self.log@[entry].operation,
295 ),
296 forall|entry: int| 1 <= entry < index ==>
297 #[trigger] self.log@[entry].prev_hash == self.log@[entry - 1].hash,
298 index > 0 ==> self.log@[0].prev_hash == 0,
299 decreases length - index,
300 {
301 if self.log[index].prev_hash != expected_previous {
302 return false;
303 }
304 if !self.operator.enabled_exec(self.log[index].prev_hash, self.log[index].operation) {
305 return false;
306 }
307 let expected_hash = self.operator.combine(
308 self.log[index].prev_hash,
309 self.log[index].operation,
310 );
311 if self.log[index].hash != expected_hash {
312 return false;
313 }
314 expected_previous = self.log[index].hash;
315 index = index + 1;
316 }
317
318 if self.last_hash != expected_previous {
319 return false;
320 }
321 true
322 }
323}
324
325}