1use vstd::prelude::*;
24
25use crate::modalities::sequential::Sequential;
26use crate::primitives::actuation_pass::ActuationPass;
27use crate::primitives::audit_sink::AuditSink;
28#[expect(
29 unused_imports,
30 reason = "ChainOperation is used by ghost specifications erased by rustc"
31)]
32use crate::primitives::audit_sink::ChainOperation;
33use crate::primitives::budget::Budget;
34use crate::primitives::propagation_pass::PropagationPass;
35#[expect(
36 unused_imports,
37 reason = "Round appears in ghost specifications erased by rustc"
38)]
39use crate::primitives::propagation_pass::Round;
40use crate::primitives::resource_registry::ResourceRegistry;
41
42verus! {
43
44#[derive(Clone, Copy, PartialEq, Eq, Debug)]
45pub enum CommitPhase {
47 Pending,
49 Admitted,
51 Ready,
53 Retryable,
55 RecoveryPending,
57 Committed,
59 Rejected,
61}
62
63#[derive(Clone, Copy, PartialEq, Eq, Debug)]
66pub enum AbstractPhase {
67 Pending,
69 Active,
71 Recovering,
73 Committed,
75 Failed,
77}
78
79pub struct GovernedCommit {
83 pub registry: ResourceRegistry<u64, u64>,
85 pub budget: Budget,
87 pub propagation: PropagationPass,
89 pub actuation: ActuationPass,
91 pub audit: AuditSink,
93 pub sequential: Sequential,
95 pub phase: CommitPhase,
97 pub attempt_budget: Budget,
99 pub effect_applied: bool,
101 pub evidence_persisted: bool,
103 pub recovery_intent: bool,
105 pub crashed: bool,
107}
108
109impl GovernedCommit {
110 pub open spec fn abstract_phase(&self) -> AbstractPhase {
112 if self.phase == CommitPhase::Pending {
113 AbstractPhase::Pending
114 } else if self.phase == CommitPhase::Admitted
115 || self.phase == CommitPhase::Ready
116 || self.phase == CommitPhase::Retryable
117 {
118 AbstractPhase::Active
119 } else if self.phase == CommitPhase::RecoveryPending {
120 AbstractPhase::Recovering
121 } else if self.phase == CommitPhase::Committed {
122 AbstractPhase::Committed
123 } else {
124 AbstractPhase::Failed
125 }
126 }
127
128 pub open spec fn abstract_used(&self) -> int {
130 self.budget.allocated as int + self.budget.reserved as int
131 }
132
133 pub open spec fn abstract_init(&self) -> bool {
136 &&& self.abstract_phase() == AbstractPhase::Pending
137 &&& self.abstract_used() == 0
138 &&& !self.effect_applied
139 &&& !self.evidence_persisted
140 &&& !self.recovery_intent
141 }
142
143 pub open spec fn abstract_system_step(
145 pre: &GovernedCommit,
146 post: &GovernedCommit,
147 ) -> bool {
148 &&& post.budget.capacity == pre.budget.capacity
149 &&& post.effect_applied == pre.effect_applied
150 &&& post.evidence_persisted == pre.evidence_persisted
151 &&& post.recovery_intent == pre.recovery_intent
152 &&& ((pre.abstract_phase() == AbstractPhase::Pending
153 && post.abstract_phase() == AbstractPhase::Active
154 && post.abstract_used() == 1)
155 || (pre.abstract_phase() == AbstractPhase::Active
156 && post.abstract_phase() == AbstractPhase::Active
157 && post.abstract_used() == pre.abstract_used()))
158 }
159
160 pub open spec fn abstract_failure_step(
163 pre: &GovernedCommit,
164 post: &GovernedCommit,
165 ) -> bool {
166 &&& post.budget.capacity == pre.budget.capacity
167 &&& ((post.abstract_phase() == AbstractPhase::Failed
168 && post.abstract_used() == pre.abstract_used()
169 && post.effect_applied == pre.effect_applied
170 && post.evidence_persisted == pre.evidence_persisted
171 && post.recovery_intent == pre.recovery_intent)
172 || (pre.abstract_phase() == AbstractPhase::Active
173 && post.abstract_phase() == AbstractPhase::Active
174 && post.abstract_used() == pre.abstract_used()
175 && post.effect_applied == pre.effect_applied
176 && post.evidence_persisted == pre.evidence_persisted
177 && post.recovery_intent == pre.recovery_intent)
178 || (pre.abstract_phase() == AbstractPhase::Active
179 && post.abstract_phase() == AbstractPhase::Recovering
180 && post.abstract_used() == pre.abstract_used()
181 && post.effect_applied
182 && !post.evidence_persisted
183 && post.recovery_intent)
184 || (post.abstract_phase() == pre.abstract_phase()
185 && post.abstract_used() == pre.abstract_used()
186 && post.effect_applied == pre.effect_applied
187 && post.evidence_persisted == pre.evidence_persisted
188 && post.recovery_intent == pre.recovery_intent))
189 }
190
191 pub open spec fn abstract_commit_step(
193 pre: &GovernedCommit,
194 post: &GovernedCommit,
195 ) -> bool {
196 &&& (pre.abstract_phase() == AbstractPhase::Active
197 || pre.abstract_phase() == AbstractPhase::Recovering)
198 &&& post.abstract_phase() == AbstractPhase::Committed
199 &&& post.budget.capacity == pre.budget.capacity
200 &&& post.abstract_used() == 1
201 &&& post.effect_applied
202 &&& post.evidence_persisted
203 &&& !post.recovery_intent
204 }
205
206 pub open spec fn abstract_stutter_step(
208 pre: &GovernedCommit,
209 post: &GovernedCommit,
210 ) -> bool {
211 &&& post.abstract_phase() == pre.abstract_phase()
212 &&& post.budget.capacity == pre.budget.capacity
213 &&& post.abstract_used() == pre.abstract_used()
214 &&& post.effect_applied == pre.effect_applied
215 &&& post.evidence_persisted == pre.evidence_persisted
216 &&& post.recovery_intent == pre.recovery_intent
217 }
218
219 pub open spec fn abstract_observation_agrees(&self) -> bool {
222 &&& ((self.phase == CommitPhase::Committed)
223 == (self.abstract_phase() == AbstractPhase::Committed))
224 &&& ((self.phase == CommitPhase::Rejected
225 || self.phase == CommitPhase::RecoveryPending)
226 == (self.abstract_phase() == AbstractPhase::Failed
227 || self.abstract_phase() == AbstractPhase::Recovering))
228 }
229
230 pub proof fn prove_observation_agreement(&self)
232 ensures self.abstract_observation_agrees(),
233 {
234 }
235
236 pub open spec fn component_invariants(&self) -> bool {
238 &&& self.registry.unique_mapping()
239 &&& self.budget.safety_invariant()
240 &&& self.attempt_budget.safety_invariant()
241 &&& self.propagation.inv()
242 &&& self.actuation.invariant()
243 &&& self.audit.inv()
244 &&& self.sequential.inv()
245 }
246
247 pub open spec fn integrated_coupling(&self) -> bool {
249 &&& self.attempt_budget.capacity > 0
250 &&& self.attempt_budget.reserved == 0
251 &&& self.attempt_budget.pending_eviction == 0
252 &&& self.propagation.num_nodes == 1
253 &&& self.propagation.max_iterations == 1
254 &&& self.propagation.max_value == 0
255 &&& self.propagation.edges@.len() == 0
256 &&& self.registry.contains_key(0)
257 &&& self.actuation.num_seats == 1
258 &&& self.actuation.allocation@.len() == 1
259 &&& self.actuation.allocation@[0] is Some
260 &&& self.actuation.effects@.len() == 1
261 &&& self.audit.max_log_len == 1
262 &&& self.sequential.steps == 3
263 &&& self.sequential.value_domain_size == 4
264 &&& !self.sequential.active
265 &&& (self.effect_applied == (self.actuation.effects@[0] is Some))
266 &&& (self.evidence_persisted == (self.audit.log@.len() == 1))
267 &&& (self.phase == CommitPhase::Pending ==> self.sequential.pc == 0)
268 &&& (self.phase == CommitPhase::Pending
269 ==> self.budget.allocated == 0
270 && self.budget.reserved == 0
271 && !self.effect_applied
272 && !self.evidence_persisted
273 && !self.recovery_intent)
274 &&& (self.phase == CommitPhase::Admitted ==> self.sequential.pc == 1)
275 &&& (self.phase == CommitPhase::Ready
276 || self.phase == CommitPhase::Retryable
277 || self.phase == CommitPhase::RecoveryPending
278 ==> self.sequential.pc == 2)
279 &&& (self.phase == CommitPhase::Committed ==> self.sequential.pc == 3)
280 &&& (self.phase == CommitPhase::Admitted
281 || self.phase == CommitPhase::Ready
282 || self.phase == CommitPhase::Retryable
283 || self.phase == CommitPhase::RecoveryPending
284 ==> self.budget.reserved == 1)
285 &&& (self.phase == CommitPhase::Admitted
286 || self.phase == CommitPhase::Ready
287 || self.phase == CommitPhase::Retryable
288 ==> self.budget.allocated == 0
289 && !self.effect_applied
290 && !self.evidence_persisted
291 && !self.recovery_intent)
292 &&& (self.phase == CommitPhase::Rejected
293 ==> !self.effect_applied && !self.evidence_persisted && !self.recovery_intent)
294 &&& (self.phase == CommitPhase::RecoveryPending
295 ==> self.budget.allocated == 0
296 && self.effect_applied
297 && self.recovery_intent
298 && !self.evidence_persisted)
299 &&& (self.phase == CommitPhase::Committed
300 ==> self.effect_applied
301 && self.evidence_persisted
302 && !self.recovery_intent
303 && self.budget.allocated == 1
304 && self.budget.reserved == 0)
305 }
306
307 pub open spec fn inv(&self) -> bool {
309 self.component_invariants() && self.integrated_coupling()
310 }
311
312 pub open spec fn transferred_guarantee(&self) -> bool {
314 &&& self.budget.used() <= self.budget.capacity as int
315 &&& (self.phase == CommitPhase::Committed ==> self.evidence_persisted)
316 }
317
318 pub fn new(resource: u64, capacity: u64, max_attempts: u64) -> (s: GovernedCommit)
320 requires capacity <= 1, 0 < max_attempts <= 2,
321 ensures
322 s.inv(),
323 s.transferred_guarantee(),
324 s.abstract_init(),
325 s.abstract_observation_agrees(),
326 s.phase == CommitPhase::Pending,
327 s.attempt_budget.allocated == 0,
328 !s.effect_applied,
329 !s.evidence_persisted,
330 !s.recovery_intent,
331 !s.crashed,
332 s.budget.capacity == capacity,
333 s.attempt_budget.capacity == max_attempts,
334 s.registry.entries@ == seq![(0u64, resource)],
335 s.registry.maps_to(0, resource),
336 s.budget.allocated == 0,
337 s.budget.reserved == 0,
338 s.budget.pending_eviction == 0,
339 s.attempt_budget.allocated == 0,
340 s.attempt_budget.reserved == 0,
341 s.attempt_budget.pending_eviction == 0,
342 s.propagation.iteration == 0,
343 s.propagation.round == Round::Idle,
344 s.propagation.changed,
345 s.actuation.allocation@ == seq![Some(resource)],
346 s.actuation.effects@ == seq![None],
347 !s.actuation.complete,
348 s.audit.log@.len() == 0,
349 s.audit.last_hash == 0,
350 s.sequential.pc == 0,
351 !s.sequential.active,
352 s.sequential.history@.len() == 0,
353 {
354 let mut registry = ResourceRegistry::new();
355 registry.register(0, resource);
356
357 let budget = Budget::new(capacity);
358 let attempt_budget = Budget::new(max_attempts);
359
360 let edges: Vec<(usize, usize)> = Vec::new();
361 let mut values: Vec<u64> = Vec::new();
362 values.push(0);
363 let propagation = PropagationPass::new(1, 1, 0, edges, values);
364
365 let mut allocation: Vec<Option<u64>> = Vec::new();
366 allocation.push(Some(resource));
367 let actuation = ActuationPass::new(allocation, 1);
368
369 let audit = AuditSink::new(1);
370 let sequential = Sequential::new(3, 4, 0);
371
372 GovernedCommit {
373 registry,
374 budget,
375 propagation,
376 actuation,
377 audit,
378 sequential,
379 phase: CommitPhase::Pending,
380 attempt_budget,
381 effect_applied: false,
382 evidence_persisted: false,
383 recovery_intent: false,
384 crashed: false,
385 }
386 }
387
388 fn advance(sequential: &mut Sequential, next_value: u64)
389 requires
390 old(sequential).inv(),
391 old(sequential).pc < old(sequential).steps,
392 !old(sequential).active,
393 next_value < old(sequential).value_domain_size,
394 ensures
395 final(sequential).inv(),
396 final(sequential).steps == old(sequential).steps,
397 final(sequential).value_domain_size == old(sequential).value_domain_size,
398 final(sequential).pc == old(sequential).pc + 1,
399 !final(sequential).active,
400 final(sequential).value == next_value,
401 final(sequential).history@ == old(sequential).history@.push(next_value),
402 {
403 let began = sequential.begin_step();
404 let _ = began;
405 assert(began);
406 let completed = sequential.complete_step(next_value);
407 let _ = completed;
408 assert(completed);
409 }
410
411 pub fn admit(&mut self) -> (accepted: bool)
414 requires
415 old(self).inv(),
416 !old(self).crashed,
417 old(self).phase == CommitPhase::Pending,
418 old(self).sequential.pc == 0,
419 ensures
420 final(self).component_invariants(),
421 final(self).integrated_coupling(),
422 final(self).transferred_guarantee(),
423 accepted ==> Self::abstract_system_step(old(self), final(self)),
424 !accepted ==> Self::abstract_failure_step(old(self), final(self)),
425 accepted == (old(self).budget.used() + 1 <= old(self).budget.capacity as int),
426 accepted ==> final(self).phase == CommitPhase::Admitted,
427 !accepted ==> final(self).phase == CommitPhase::Rejected,
428 final(self).registry == old(self).registry,
429 final(self).budget.capacity == old(self).budget.capacity,
430 final(self).budget.allocated == old(self).budget.allocated,
431 final(self).budget.pending_eviction == old(self).budget.pending_eviction,
432 final(self).budget.reserved == if accepted {
433 (old(self).budget.reserved + 1) as u64
434 } else {
435 old(self).budget.reserved
436 },
437 final(self).propagation == old(self).propagation,
438 final(self).actuation == old(self).actuation,
439 final(self).audit == old(self).audit,
440 final(self).attempt_budget == old(self).attempt_budget,
441 accepted ==> {
442 &&& final(self).sequential.steps == old(self).sequential.steps
443 &&& final(self).sequential.value_domain_size
444 == old(self).sequential.value_domain_size
445 &&& final(self).sequential.pc == old(self).sequential.pc + 1
446 &&& !final(self).sequential.active
447 &&& final(self).sequential.value == 1
448 &&& final(self).sequential.history@
449 == old(self).sequential.history@.push(1)
450 },
451 !accepted ==> final(self).sequential == old(self).sequential,
452 final(self).effect_applied == old(self).effect_applied,
453 final(self).evidence_persisted == old(self).evidence_persisted,
454 final(self).recovery_intent == old(self).recovery_intent,
455 final(self).crashed == old(self).crashed,
456 {
457 let accepted = self.budget.reserve(1);
458 if accepted {
459 Self::advance(&mut self.sequential, 1);
460 self.phase = CommitPhase::Admitted;
461 } else {
462 self.phase = CommitPhase::Rejected;
463 }
464 accepted
465 }
466
467 pub fn propagate(&mut self)
470 requires
471 old(self).inv(),
472 !old(self).crashed,
473 old(self).phase == CommitPhase::Admitted,
474 old(self).sequential.pc == 1,
475 old(self).propagation.round == Round::Idle,
476 old(self).propagation.changed,
477 old(self).propagation.iteration == 0,
478 ensures
479 final(self).inv(),
480 final(self).transferred_guarantee(),
481 Self::abstract_system_step(old(self), final(self)),
482 final(self).phase == CommitPhase::Ready,
483 final(self).sequential.pc == 2,
484 final(self).propagation.iteration == 1,
485 final(self).propagation.round == Round::Idle,
486 !final(self).propagation.changed,
487 final(self).registry == old(self).registry,
488 final(self).budget == old(self).budget,
489 final(self).attempt_budget == old(self).attempt_budget,
490 final(self).actuation == old(self).actuation,
491 final(self).audit == old(self).audit,
492 final(self).propagation.num_nodes == old(self).propagation.num_nodes,
493 final(self).propagation.max_iterations
494 == old(self).propagation.max_iterations,
495 final(self).propagation.max_value == old(self).propagation.max_value,
496 final(self).propagation.edges@ == old(self).propagation.edges@,
497 final(self).propagation.values@ == old(self).propagation.values@,
498 final(self).propagation.snapshot@ == old(self).propagation.values@,
499 forall|index: int| 0 <= index < final(self).propagation.updated@.len() ==>
500 #[trigger] final(self).propagation.updated@[index],
501 final(self).sequential.steps == old(self).sequential.steps,
502 final(self).sequential.value_domain_size
503 == old(self).sequential.value_domain_size,
504 final(self).sequential.value == 2,
505 !final(self).sequential.active,
506 final(self).sequential.history@ == old(self).sequential.history@.push(2),
507 final(self).effect_applied == old(self).effect_applied,
508 final(self).evidence_persisted == old(self).evidence_persisted,
509 final(self).recovery_intent == old(self).recovery_intent,
510 final(self).crashed == old(self).crashed,
511 {
512 self.propagation.start_round();
513 self.propagation.update_node(0);
514 assert(self.propagation.all_updated());
515 assert(self.propagation.values@ == self.propagation.snapshot@);
516 self.propagation.end_round();
517 Self::advance(&mut self.sequential, 2);
518 self.phase = CommitPhase::Ready;
519 }
520
521 pub fn fail_before_effect(&mut self) -> (terminal: bool)
525 requires
526 old(self).inv(),
527 !old(self).crashed,
528 old(self).phase == CommitPhase::Ready
529 || old(self).phase == CommitPhase::Retryable,
530 old(self).attempt_budget.allocated < old(self).attempt_budget.capacity,
531 old(self).sequential.pc == 2,
532 !old(self).effect_applied,
533 !old(self).evidence_persisted,
534 ensures
535 final(self).inv(),
536 final(self).transferred_guarantee(),
537 Self::abstract_failure_step(old(self), final(self)),
538 final(self).attempt_budget.allocated == old(self).attempt_budget.allocated + 1,
539 final(self).registry == old(self).registry,
540 final(self).budget == old(self).budget,
541 final(self).propagation == old(self).propagation,
542 final(self).actuation == old(self).actuation,
543 final(self).audit == old(self).audit,
544 final(self).sequential == old(self).sequential,
545 final(self).attempt_budget.capacity == old(self).attempt_budget.capacity,
546 final(self).attempt_budget.reserved == old(self).attempt_budget.reserved,
547 final(self).attempt_budget.pending_eviction
548 == old(self).attempt_budget.pending_eviction,
549 final(self).effect_applied == old(self).effect_applied,
550 final(self).evidence_persisted == old(self).evidence_persisted,
551 final(self).recovery_intent == old(self).recovery_intent,
552 final(self).crashed == old(self).crashed,
553 final(self).attempt_budget.allocated < final(self).attempt_budget.capacity ==>
554 !terminal && final(self).phase == CommitPhase::Retryable,
555 final(self).attempt_budget.allocated == final(self).attempt_budget.capacity ==>
556 terminal && final(self).phase == CommitPhase::Rejected,
557 {
558 let recorded = self.attempt_budget.try_allocate(1);
559 let _ = recorded;
560 assert(recorded);
561 if self.attempt_budget.allocated == self.attempt_budget.capacity {
562 self.phase = CommitPhase::Rejected;
563 true
564 } else {
565 self.phase = CommitPhase::Retryable;
566 false
567 }
568 }
569
570 pub fn fail_after_effect(&mut self)
574 requires
575 old(self).inv(),
576 !old(self).crashed,
577 old(self).phase == CommitPhase::Ready
578 || old(self).phase == CommitPhase::Retryable,
579 old(self).attempt_budget.allocated < old(self).attempt_budget.capacity,
580 old(self).sequential.pc == 2,
581 !old(self).effect_applied,
582 !old(self).evidence_persisted,
583 old(self).audit.log@.len() == 0,
584 old(self).actuation.effects@[0] is None,
585 ensures
586 final(self).inv(),
587 final(self).transferred_guarantee(),
588 Self::abstract_failure_step(old(self), final(self)),
589 final(self).attempt_budget.allocated == old(self).attempt_budget.allocated + 1,
590 final(self).phase == CommitPhase::RecoveryPending,
591 final(self).effect_applied,
592 !final(self).evidence_persisted,
593 final(self).recovery_intent,
594 final(self).registry == old(self).registry,
595 final(self).budget == old(self).budget,
596 final(self).propagation == old(self).propagation,
597 final(self).audit == old(self).audit,
598 final(self).sequential == old(self).sequential,
599 final(self).attempt_budget.capacity == old(self).attempt_budget.capacity,
600 final(self).attempt_budget.reserved == old(self).attempt_budget.reserved,
601 final(self).attempt_budget.pending_eviction
602 == old(self).attempt_budget.pending_eviction,
603 final(self).actuation.num_seats == old(self).actuation.num_seats,
604 final(self).actuation.allocation@ == old(self).actuation.allocation@,
605 final(self).actuation.effects@
606 == old(self).actuation.effects@.update(
607 0,
608 old(self).actuation.allocation@[0],
609 ),
610 final(self).actuation.complete == old(self).actuation.complete,
611 final(self).crashed == old(self).crashed,
612 {
613 let recorded = self.attempt_budget.try_allocate(1);
614 let _ = recorded;
615 assert(recorded);
616 self.recovery_intent = true;
618 self.actuation.actuate(0);
619 self.effect_applied = true;
620 self.phase = CommitPhase::RecoveryPending;
621 }
622
623 pub fn commit_success(&mut self)
627 requires
628 old(self).inv(),
629 !old(self).crashed,
630 old(self).phase == CommitPhase::Ready
631 || old(self).phase == CommitPhase::Retryable,
632 old(self).attempt_budget.allocated < old(self).attempt_budget.capacity,
633 old(self).sequential.pc == 2,
634 !old(self).effect_applied,
635 !old(self).evidence_persisted,
636 old(self).audit.log@.len() == 0,
637 old(self).actuation.effects@[0] is None,
638 ensures
639 final(self).inv(),
640 final(self).transferred_guarantee(),
641 Self::abstract_commit_step(old(self), final(self)),
642 final(self).attempt_budget.allocated == old(self).attempt_budget.allocated + 1,
643 final(self).phase == CommitPhase::Committed,
644 final(self).effect_applied,
645 final(self).evidence_persisted,
646 !final(self).recovery_intent,
647 final(self).sequential.pc == 3,
648 final(self).registry == old(self).registry,
649 final(self).propagation == old(self).propagation,
650 final(self).attempt_budget.capacity == old(self).attempt_budget.capacity,
651 final(self).attempt_budget.allocated == old(self).attempt_budget.allocated + 1,
652 final(self).attempt_budget.reserved == old(self).attempt_budget.reserved,
653 final(self).attempt_budget.pending_eviction
654 == old(self).attempt_budget.pending_eviction,
655 final(self).budget.capacity == old(self).budget.capacity,
656 final(self).budget.allocated == old(self).budget.allocated + 1,
657 final(self).budget.reserved == old(self).budget.reserved - 1,
658 final(self).budget.pending_eviction == old(self).budget.pending_eviction,
659 final(self).actuation.num_seats == old(self).actuation.num_seats,
660 final(self).actuation.allocation@ == old(self).actuation.allocation@,
661 final(self).actuation.effects@
662 == old(self).actuation.effects@.update(
663 0,
664 old(self).actuation.allocation@[0],
665 ),
666 final(self).actuation.complete == old(self).actuation.complete,
667 final(self).audit.operator == old(self).audit.operator,
668 final(self).audit.max_log_len == old(self).audit.max_log_len,
669 final(self).audit.log@.len() == old(self).audit.log@.len() + 1,
670 final(self).audit.last_hash
671 == old(self).audit.operator.combine_spec(old(self).audit.last_hash, 0),
672 final(self).audit.log@[old(self).audit.log@.len() as int].operation == 0,
673 final(self).audit.log@[old(self).audit.log@.len() as int].prev_hash
674 == old(self).audit.last_hash,
675 forall|index: int| 0 <= index < old(self).audit.log@.len() ==>
676 #[trigger] final(self).audit.log@[index] == old(self).audit.log@[index],
677 final(self).sequential.steps == old(self).sequential.steps,
678 final(self).sequential.value_domain_size
679 == old(self).sequential.value_domain_size,
680 final(self).sequential.value == 3,
681 !final(self).sequential.active,
682 final(self).sequential.history@ == old(self).sequential.history@.push(3),
683 final(self).crashed == old(self).crashed,
684 {
685 let recorded = self.attempt_budget.try_allocate(1);
686 let _ = recorded;
687 assert(recorded);
688 self.actuation.actuate(0);
689 self.effect_applied = true;
690 self.budget.commit_reservation(1);
691 let recorded = self.audit.record(0);
692 let _ = recorded;
693 assert(recorded);
694 self.evidence_persisted = true;
695 self.recovery_intent = false;
696 Self::advance(&mut self.sequential, 3);
697 self.phase = CommitPhase::Committed;
698 }
699
700 pub fn recover(&mut self)
702 requires
703 old(self).inv(),
704 !old(self).crashed,
705 old(self).phase == CommitPhase::RecoveryPending,
706 old(self).sequential.pc == 2,
707 old(self).audit.log@.len() == 0,
708 ensures
709 final(self).inv(),
710 final(self).transferred_guarantee(),
711 Self::abstract_commit_step(old(self), final(self)),
712 final(self).phase == CommitPhase::Committed,
713 final(self).effect_applied,
714 final(self).evidence_persisted,
715 !final(self).recovery_intent,
716 final(self).sequential.pc == 3,
717 final(self).registry == old(self).registry,
718 final(self).propagation == old(self).propagation,
719 final(self).attempt_budget == old(self).attempt_budget,
720 final(self).budget.capacity == old(self).budget.capacity,
721 final(self).budget.allocated == old(self).budget.allocated + 1,
722 final(self).budget.reserved == old(self).budget.reserved - 1,
723 final(self).budget.pending_eviction == old(self).budget.pending_eviction,
724 final(self).actuation == old(self).actuation,
725 final(self).audit.operator == old(self).audit.operator,
726 final(self).audit.max_log_len == old(self).audit.max_log_len,
727 final(self).audit.log@.len() == old(self).audit.log@.len() + 1,
728 final(self).audit.last_hash
729 == old(self).audit.operator.combine_spec(old(self).audit.last_hash, 0),
730 final(self).audit.log@[old(self).audit.log@.len() as int].operation == 0,
731 final(self).audit.log@[old(self).audit.log@.len() as int].prev_hash
732 == old(self).audit.last_hash,
733 forall|index: int| 0 <= index < old(self).audit.log@.len() ==>
734 #[trigger] final(self).audit.log@[index] == old(self).audit.log@[index],
735 final(self).sequential.steps == old(self).sequential.steps,
736 final(self).sequential.value_domain_size
737 == old(self).sequential.value_domain_size,
738 final(self).sequential.value == 3,
739 !final(self).sequential.active,
740 final(self).sequential.history@ == old(self).sequential.history@.push(3),
741 final(self).crashed == old(self).crashed,
742 {
743 self.budget.commit_reservation(1);
744 let recorded = self.audit.record(0);
745 let _ = recorded;
746 assert(recorded);
747 self.evidence_persisted = true;
748 self.recovery_intent = false;
749 Self::advance(&mut self.sequential, 3);
750 self.phase = CommitPhase::Committed;
751 }
752
753 pub fn crash(&mut self)
756 requires old(self).inv(),
757 ensures
758 final(self).inv(),
759 final(self).transferred_guarantee(),
760 Self::abstract_failure_step(old(self), final(self)),
761 final(self).crashed,
762 final(self).phase == old(self).phase,
763 final(self).effect_applied == old(self).effect_applied,
764 final(self).evidence_persisted == old(self).evidence_persisted,
765 final(self).recovery_intent == old(self).recovery_intent,
766 final(self).registry == old(self).registry,
767 final(self).budget == old(self).budget,
768 final(self).propagation == old(self).propagation,
769 final(self).actuation == old(self).actuation,
770 final(self).audit == old(self).audit,
771 final(self).sequential == old(self).sequential,
772 final(self).attempt_budget == old(self).attempt_budget,
773 {
774 self.crashed = true;
775 }
776
777 pub fn restart(&mut self)
779 requires old(self).inv(), old(self).crashed,
780 ensures
781 final(self).inv(),
782 final(self).transferred_guarantee(),
783 Self::abstract_stutter_step(old(self), final(self)),
784 !final(self).crashed,
785 final(self).phase == old(self).phase,
786 final(self).effect_applied == old(self).effect_applied,
787 final(self).evidence_persisted == old(self).evidence_persisted,
788 final(self).recovery_intent == old(self).recovery_intent,
789 final(self).registry == old(self).registry,
790 final(self).budget == old(self).budget,
791 final(self).propagation == old(self).propagation,
792 final(self).actuation == old(self).actuation,
793 final(self).audit == old(self).audit,
794 final(self).sequential == old(self).sequential,
795 final(self).attempt_budget == old(self).attempt_budget,
796 {
797 self.crashed = false;
798 }
799}
800
801}