1use crate::machine::{step_allowed, tag};
18use planarian_schema::{
19 Actor, GenerationId, Hash, Incident, IncidentId, Lease, OpId, Outcome, Override, OwnerId, Resolution, Timestamp,
20 Transaction, Transition, TxnId, TxnState, UnitId,
21};
22use serde::{Deserialize, Deserializer, Serialize, Serializer};
23use std::collections::BTreeMap;
24use vstd::prelude::*;
25
26verus! {
27
28#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
29#[verifier::external_derive]
30pub struct Entry {
31 pub seq: u64,
32 pub at: Timestamp,
33 pub record: Record,
34}
35
36#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
37#[verifier::external_derive]
38pub enum Record {
39 Prepared(Box<Transaction>),
41 Advance {
42 txn: TxnId,
43 epoch: u64,
44 to: TxnState,
45 note: String,
46 #[serde(default)]
50 withdrawn_for: Option<TxnId>,
51 },
52 Takeover {
54 txn: TxnId,
55 from_epoch: u64,
56 lease: Lease,
57 },
58 Renew {
59 txn: TxnId,
60 epoch: u64,
61 worker_pid: Option<u32>,
62 #[serde(default)]
65 boot_at: Timestamp,
66 },
67 OpBegin {
69 op: OpId,
70 call: String,
71 },
72 OpAck {
73 op: OpId,
74 outcome: Outcome,
75 },
76 OverrideSet(Override),
77 OverrideLifted(UnitId),
78 Incident(Incident),
79 KnownGood(GenerationId),
80 LiveTested(Option<GenerationId>),
81 AcceptedBaseline(Option<GenerationId>),
82 SecurityFloor(Option<GenerationId>),
85 ExternalAccepted {
89 seq: u64,
90 digest: Hash,
91 },
92 TokenUsed {
94 nonce: [u8; 16],
95 expires: Timestamp,
96 #[serde(default)]
99 approver: u64,
100 #[serde(default)]
101 txn: u64,
102 },
103 AgentStarted {
108 name: String,
109 #[serde(default)]
111 instance: [u8; 16],
112 by: u32,
113 },
114 Resolved {
115 txn: TxnId,
116 resolution: Resolution,
117 by: Actor,
118 },
119 Reconciliation(Reconciliation),
120 HoldApplied {
124 unit: UnitId,
125 by: HoldBy,
126 },
127 HoldLifted {
130 unit: UnitId,
131 reason: String,
132 },
133}
134
135#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
138#[verifier::external_derive]
139pub enum HoldBy {
140 OnFailure,
141 Daemon,
142}
143
144#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
146#[verifier::external_derive]
147pub struct Reconciliation {
148 pub by: OwnerId,
149 pub found: Vec<(TxnId, TxnState)>,
150 pub actions: Vec<String>,
151}
152
153#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
154#[verifier::external_derive]
155pub struct OpRecord {
156 pub op: OpId,
157 pub call: String,
158 pub ack: Option<Outcome>,
159}
160
161#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
162#[verifier::external_derive]
163pub struct LiveTxn {
164 pub txn: Transaction,
165 pub ops: Vec<OpRecord>,
166 pub path: Vec<String>,
168 #[serde(default)]
170 pub why: String,
171 #[serde(default)]
176 pub boot_only: bool,
177}
178
179#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
182#[verifier::external_derive]
183pub struct TerminalTxn {
184 pub state: TxnState,
185 pub epoch: u64,
186 pub owner: OwnerId,
187 pub path: Vec<String>,
188 pub transition: Transition,
189 pub why: String,
191 #[serde(default)]
193 pub withdrawn_for: Option<TxnId>,
194 #[serde(default)]
196 pub prepared_again_from: Option<TxnId>,
197}
198
199#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
201#[verifier::external_derive]
202pub struct InterlockEntry {
203 pub txn: TxnId,
204 pub transition: Transition,
205 pub failed_from: String,
206 pub base_override: Option<Hash>,
207 pub candidate_override: Option<Hash>,
208 pub revert_to: GenerationId,
209 pub note: String,
210}
211
212#[derive(Clone, Debug, PartialEq, Eq)]
216pub struct IdMap<V> {
217 pub entries: Vec<(TxnId, V)>,
219}
220
221#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
222#[verifier::external_derive]
223pub struct Ledger {
224 pub last_seq: u64,
225 pub next_txn: u64,
226 pub live: IdMap<LiveTxn>,
227 pub terminal: BTreeMap<TxnId, TerminalTxn>,
228 pub overrides: Vec<Override>,
229 pub incidents: BTreeMap<IncidentId, Incident>,
230 pub tokens_used: Vec<([u8; 16], Timestamp)>,
231 pub known_good: Option<GenerationId>,
232 pub live_tested: Option<GenerationId>,
233 pub accepted_baseline: Option<GenerationId>,
234 #[serde(default)]
235 pub security_floor: Option<GenerationId>,
236 #[serde(default)]
238 pub external: Option<(u64, Hash)>,
239 pub interlock: IdMap<InterlockEntry>,
240 pub last_reconciliation: Option<Reconciliation>,
241 #[serde(default)]
244 pub agents: Vec<(String, [u8; 16], u32)>,
245 #[serde(default)]
250 pub hold_events: Vec<(UnitId, bool)>,
251}
252
253#[derive(Clone, Debug, PartialEq, Eq)]
254pub enum LedgerError {
255 Sequence {
256 expected: u64,
257 got: u64,
258 },
259 DuplicateTxn(TxnId),
260 NotLive(TxnId),
261 StaleEpoch {
263 txn: TxnId,
264 current: u64,
265 got: u64,
266 },
267 IllegalStep {
268 txn: TxnId,
269 from: &'static str,
270 to: &'static str,
271 },
272 Exclusive {
274 txn: TxnId,
275 blocker: TxnId,
276 },
277 Interlocked {
279 txn: TxnId,
280 },
281 UnknownOp(OpId),
282 DuplicateOp(OpId),
283 NotInterlocked(TxnId),
284 BadPrepared(&'static str),
285 ExternalReplay {
287 have: u64,
288 got: u64,
289 },
290}
291
292pub open spec fn autonomous(a: Actor) -> bool {
296 !(a is User)
297}
298
299pub open spec fn in_flight(x: LiveTxn) -> bool {
300 !x.txn.state.pre_mutation()
301}
302
303pub open spec fn generation(x: LiveTxn) -> bool {
304 x.txn.transition.changes_generation_spec()
305}
306
307pub open spec fn flying(s: TxnState) -> bool {
309 !s.pre_mutation() && !s.terminal()
310}
311
312pub open spec fn compatible(s1: TxnState, g1: bool, b1: bool, s2: TxnState, g2: bool, b2: bool) -> bool {
319 let excl1 = g1 && s1.exclusive() && !(s1 is Promoted && b1);
320 let excl2 = g2 && s2.exclusive() && !(s2 is Promoted && b2);
321 let act1 = g1 && (flying(s1) || s1 is Built);
322 let act2 = g2 && (flying(s2) || s2 is Built);
323 &&& excl1 ==> !flying(s2)
324 &&& excl2 ==> !flying(s1)
325 &&& !(act1 && act2)
326}
327
328pub open spec fn has_id<V>(s: Seq<(TxnId, V)>, id: TxnId) -> bool {
329 exists|i: int| 0 <= i < s.len() && (#[trigger] s[i]).0 == id
330}
331
332pub open spec fn interlocked(l: Ledger) -> bool {
333 l.interlock.entries@.len() > 0
334}
335
336pub open spec fn live_ok(x: (TxnId, LiveTxn), next_txn: u64) -> bool {
340 &&& x.0 == x.1.txn.id
341 &&& x.1.txn.lease.txn == x.0
342 &&& x.0.0 < next_txn
343 &&& !x.1.txn.state.terminal()
344 &&& x.1.txn.recovery == x.1.txn.transition.recovery_spec()
345}
346
347pub open spec fn pair_ok(a: (TxnId, LiveTxn), b: (TxnId, LiveTxn)) -> bool {
349 &&& a.0 != b.0
350 &&& compatible(a.1.txn.state, generation(a.1), a.1.boot_only, b.1.txn.state, generation(b.1), b.1.boot_only)
351}
352
353pub open spec fn inv(l: Ledger) -> bool {
357 let e = l.live.entries@;
358 &&& forall|i: int| 0 <= i < e.len() ==> live_ok(#[trigger] e[i], l.next_txn)
359 &&& forall|i: int, j: int| 0 <= i < e.len() && 0 <= j < e.len() && i != j ==> pair_ok(#[trigger] e[i], #[trigger] e[j])
360}
361
362pub open spec fn prepared_rule(l: Ledger, t: Transaction) -> bool {
363 &&& !has_id(l.live.entries@, t.id)
364 &&& l.next_txn <= t.id.0 < u64::MAX
365 &&& t.state == TxnState::Prepared
366 &&& t.lease.epoch == 1
367 &&& t.lease.txn == t.id
368 &&& t.recovery == t.transition.recovery_spec()
369 &&& !(interlocked(l) && autonomous(t.facts.caller))
370}
371
372pub open spec fn advance_rule(l: Ledger, txn: TxnId, epoch: u64, to: TxnState) -> bool {
373 exists|i: int| 0 <= i < l.live.entries@.len() && (#[trigger] l.live.entries@[i]).0 == txn && advance_ok(l, i, epoch, to)
374}
375
376pub open spec fn takeover_rule(l: Ledger, txn: TxnId, from_epoch: u64, lease: Lease) -> bool {
377 exists|i: int|
378 0 <= i < l.live.entries@.len() && (#[trigger] l.live.entries@[i]).0 == txn && l.live.entries@[i].1.txn.lease.epoch
379 == from_epoch && lease.epoch > from_epoch && lease.txn == txn
380}
381
382pub open spec fn renew_rule(l: Ledger, txn: TxnId, epoch: u64) -> bool {
383 exists|i: int|
384 0 <= i < l.live.entries@.len() && (#[trigger] l.live.entries@[i]).0 == txn && l.live.entries@[i].1.txn.lease.epoch
385 == epoch
386}
387
388pub open spec fn op_begin_rule(l: Ledger, op: OpId) -> bool {
389 exists|i: int|
390 0 <= i < l.live.entries@.len() && (#[trigger] l.live.entries@[i]).0 == op.txn && l.live.entries@[i].1.txn.lease.epoch
391 == op.epoch && forall|k: int| 0 <= k < l.live.entries@[i].1.ops@.len() ==> (
392 #[trigger] l.live.entries@[i].1.ops@[k]).op != op
393}
394
395pub open spec fn op_ack_rule(l: Ledger, op: OpId) -> bool {
396 exists|i: int, k: int|
397 0 <= i < l.live.entries@.len() && (#[trigger] l.live.entries@[i]).0 == op.txn && 0 <= k
398 < l.live.entries@[i].1.ops@.len() && (#[trigger] l.live.entries@[i].1.ops@[k]).op == op
399 && l.live.entries@[i].1.ops@[k].ack is None
400}
401
402pub open spec fn rules(l: Ledger, e: Entry) -> bool {
405 &&& e.seq == l.last_seq + 1
406 &&& match e.record {
407 Record::Prepared(t) => prepared_rule(l, *t),
408 Record::Advance { txn, epoch, to, .. } => advance_rule(l, txn, epoch, to),
409 Record::Takeover { txn, from_epoch, lease } => takeover_rule(l, txn, from_epoch, lease),
410 Record::Renew { txn, epoch, .. } => renew_rule(l, txn, epoch),
411 Record::OpBegin { op, .. } => op_begin_rule(l, op),
412 Record::OpAck { op, .. } => op_ack_rule(l, op),
413 Record::Resolved { txn, .. } => has_id(l.interlock.entries@, txn),
414 Record::ExternalAccepted { seq, .. } => external_rule(l, seq),
415 _ => true,
416 }
417}
418
419pub open spec fn external_rule(l: Ledger, seq: u64) -> bool {
423 match l.external {
424 None => true,
425 Some(p) => seq > p.0,
426 }
427}
428
429pub open spec fn advance_ok(l: Ledger, i: int, epoch: u64, to: TxnState) -> bool {
434 let live = l.live.entries@;
435 let x = live[i].1;
436 &&& x.txn.lease.epoch == epoch
437 &&& crate::machine::step_spec(x.txn.state, to, x.txn.recovery)
438 &&& (x.txn.state.pre_mutation() && !to.pre_mutation() && !to.terminal()) ==> !(interlocked(l) && autonomous(
439 x.txn.facts.caller,
440 ))
441 &&& forall|j: int| 0 <= j < live.len() && j != i ==> compatible(
442 to,
443 generation(x),
444 x.boot_only,
445 (#[trigger] live[j]).1.txn.state,
446 generation(live[j].1),
447 live[j].1.boot_only,
448 )
449}
450
451fn flying_exec(s: &TxnState) -> (r: bool)
454 ensures
455 r == flying(*s),
456{
457 !s.is_pre_mutation() && !s.is_terminal()
458}
459
460fn compatible_exec(s1: &TxnState, g1: bool, b1: bool, s2: &TxnState, g2: bool, b2: bool) -> (r: bool)
461 ensures
462 r == compatible(*s1, g1, b1, *s2, g2, b2),
463{
464 let built1 = match s1 {
465 TxnState::Built => true,
466 _ => false,
467 };
468 let built2 = match s2 {
469 TxnState::Built => true,
470 _ => false,
471 };
472 let f1 = flying_exec(s1);
473 let f2 = flying_exec(s2);
474 let promoted1 = match s1 {
475 TxnState::Promoted => true,
476 _ => false,
477 };
478 let promoted2 = match s2 {
479 TxnState::Promoted => true,
480 _ => false,
481 };
482 let excl1 = g1 && s1.is_exclusive() && !(promoted1 && b1);
483 let excl2 = g2 && s2.is_exclusive() && !(promoted2 && b2);
484 let act1 = g1 && (f1 || built1);
485 let act2 = g2 && (f2 || built2);
486 !(excl1 && f2 || excl2 && f1 || act1 && act2)
487}
488
489fn find<V>(m: &IdMap<V>, id: TxnId) -> (r: Option<usize>)
490 ensures
491 match r {
492 Some(i) => i < m.entries@.len() && m.entries@[i as int].0 == id,
493 None => !has_id(m.entries@, id),
494 },
495{
496 let mut i: usize = 0;
497 while i < m.entries.len()
498 invariant
499 i <= m.entries@.len(),
500 forall|k: int| 0 <= k < i ==> (#[trigger] m.entries@[k]).0 != id,
501 decreases m.entries.len() - i,
502 {
503 if m.entries[i].0.0 == id.0 {
504 return Some(i);
505 }
506 i += 1;
507 }
508 None
509}
510
511fn is_autonomous(a: &Actor) -> (r: bool)
512 ensures
513 r == autonomous(*a),
514{
515 match a {
516 Actor::User { .. } => false,
517 _ => true,
518 }
519}
520
521fn op_eq(a: OpId, b: OpId) -> (r: bool)
522 ensures
523 r == (a == b),
524{
525 a.txn.0 == b.txn.0 && a.epoch == b.epoch && a.seq == b.seq
526}
527
528fn stale(txn: TxnId, current: u64, got: u64) -> LedgerError {
529 LedgerError::StaleEpoch { txn, current, got }
530}
531
532impl Ledger {
533 pub fn well_formed(&self) -> (r: bool)
535 ensures
536 r == inv(*self),
537 {
538 let n = self.live.entries.len();
539 let mut i: usize = 0;
540 while i < n
541 invariant
542 n == self.live.entries@.len(),
543 i <= n,
544 forall|k: int| 0 <= k < i ==> live_ok(#[trigger] self.live.entries@[k], self.next_txn),
545 decreases n - i,
546 {
547 let x = &self.live.entries[i];
548 let ok = x.0.0 == x.1.txn.id.0 && x.1.txn.lease.txn.0 == x.0.0 && x.0.0 < self.next_txn
549 && !x.1.txn.state.is_terminal() && recovery_eq(x.1.txn.recovery, x.1.txn.transition.recovery());
550 if !ok {
551 assert(!live_ok(self.live.entries@[i as int], self.next_txn));
552 return false;
553 }
554 i += 1;
555 }
556 let mut i: usize = 0;
557 while i < n
558 invariant
559 n == self.live.entries@.len(),
560 i <= n,
561 forall|a: int, b: int|
562 0 <= a < i && 0 <= b < n && a != b ==> pair_ok(
563 #[trigger] self.live.entries@[a],
564 #[trigger] self.live.entries@[b],
565 ),
566 decreases n - i,
567 {
568 let mut j: usize = 0;
569 while j < n
570 invariant
571 n == self.live.entries@.len(),
572 i < n,
573 j <= n,
574 forall|a: int, b: int|
575 0 <= a < i && 0 <= b < n && a != b ==> pair_ok(
576 #[trigger] self.live.entries@[a],
577 #[trigger] self.live.entries@[b],
578 ),
579 forall|b: int|
580 0 <= b < j && b != i ==> pair_ok(self.live.entries@[i as int], #[trigger] self.live.entries@[b]),
581 decreases n - j,
582 {
583 if j != i {
584 let a = &self.live.entries[i];
585 let b = &self.live.entries[j];
586 let ok = a.0.0 != b.0.0 && compatible_exec(
587 &a.1.txn.state,
588 a.1.txn.transition.changes_generation(),
589 a.1.boot_only,
590 &b.1.txn.state,
591 b.1.txn.transition.changes_generation(),
592 b.1.boot_only,
593 );
594 if !ok {
595 assert(!pair_ok(self.live.entries@[i as int], self.live.entries@[j as int]));
596 return false;
597 }
598 }
599 j += 1;
600 }
601 i += 1;
602 }
603 true
604 }
605
606 #[verifier::loop_isolation(false)]
607 pub fn validate(&self, e: &Entry) -> (r: Result<(), LedgerError>)
608 ensures
609 r is Ok ==> rules(*self, *e),
610 {
611 if self.last_seq == u64::MAX || e.seq != self.last_seq + 1 {
612 return Err(LedgerError::Sequence { expected: self.last_seq.wrapping_add(1), got: e.seq });
613 }
614 match &e.record {
615 Record::Prepared(t) => {
616 if find(&self.live, t.id).is_some() || t.id.0 < self.next_txn || t.id.0 == u64::MAX {
617 return Err(LedgerError::DuplicateTxn(t.id));
618 }
619 let prepared = match t.state {
620 TxnState::Prepared => true,
621 _ => false,
622 };
623 if !prepared || t.lease.epoch != 1 || t.lease.txn.0 != t.id.0 {
624 return Err(LedgerError::BadPrepared("state Prepared, lease epoch 1 for this txn"));
625 }
626 if !recovery_eq(t.recovery, t.transition.recovery()) {
627 return Err(LedgerError::BadPrepared("recovery strategy is fixed by the transition"));
628 }
629 if !self.interlock.entries.is_empty() && is_autonomous(&t.facts.caller) {
630 return Err(LedgerError::Interlocked { txn: t.id });
631 }
632 Ok(())
633 },
634 Record::Advance { txn, epoch, to, .. } => {
635 let i = match find(&self.live, *txn) {
636 Some(i) => i,
637 None => return Err(LedgerError::NotLive(*txn)),
638 };
639 let l = &self.live.entries[i].1;
640 if l.txn.lease.epoch != *epoch {
641 return Err(stale(*txn, l.txn.lease.epoch, *epoch));
642 }
643 if !step_allowed(&l.txn.state, to, l.txn.recovery) {
644 return Err(LedgerError::IllegalStep { txn: *txn, from: tag(&l.txn.state), to: tag(to) });
645 }
646 if l.txn.state.is_pre_mutation() && !to.is_pre_mutation() && !to.is_terminal() {
647 if !self.interlock.entries.is_empty() && is_autonomous(&l.txn.facts.caller) {
649 return Err(LedgerError::Interlocked { txn: *txn });
650 }
651 }
652 let mine = l.txn.transition.changes_generation();
653 let boot_only = l.boot_only;
654 let mut j: usize = 0;
655 while j < self.live.entries.len()
656 invariant
657 j <= self.live.entries@.len(),
658 mine == generation(self.live.entries@[i as int].1),
659 boot_only == self.live.entries@[i as int].1.boot_only,
660 forall|k: int|
661 0 <= k < j && k != i ==> compatible(
662 *to,
663 mine,
664 boot_only,
665 (#[trigger] self.live.entries@[k]).1.txn.state,
666 generation(self.live.entries@[k].1),
667 self.live.entries@[k].1.boot_only,
668 ),
669 decreases self.live.entries.len() - j,
670 {
671 let o = &self.live.entries[j].1;
672 if j != i && !compatible_exec(
673 to,
674 mine,
675 boot_only,
676 &o.txn.state,
677 o.txn.transition.changes_generation(),
678 o.boot_only,
679 ) {
680 return Err(LedgerError::Exclusive { txn: *txn, blocker: o.txn.id });
681 }
682 j += 1;
683 }
684 assert(advance_ok(*self, i as int, *epoch, *to));
685 Ok(())
686 },
687 Record::Takeover { txn, from_epoch, lease } => {
688 let i = match find(&self.live, *txn) {
689 Some(i) => i,
690 None => return Err(LedgerError::NotLive(*txn)),
691 };
692 let l = &self.live.entries[i].1;
693 if l.txn.lease.epoch != *from_epoch {
694 return Err(stale(*txn, l.txn.lease.epoch, *from_epoch));
695 }
696 if lease.epoch <= *from_epoch || lease.txn.0 != txn.0 {
697 return Err(stale(*txn, *from_epoch, lease.epoch));
698 }
699 Ok(())
700 },
701 Record::Renew { txn, epoch, .. } => {
702 let i = match find(&self.live, *txn) {
703 Some(i) => i,
704 None => return Err(LedgerError::NotLive(*txn)),
705 };
706 let l = &self.live.entries[i].1;
707 if l.txn.lease.epoch != *epoch {
708 return Err(stale(*txn, l.txn.lease.epoch, *epoch));
709 }
710 Ok(())
711 },
712 Record::OpBegin { op, .. } => {
713 let i = match find(&self.live, op.txn) {
714 Some(i) => i,
715 None => return Err(LedgerError::NotLive(op.txn)),
716 };
717 let l = &self.live.entries[i].1;
718 if l.txn.lease.epoch != op.epoch {
719 return Err(stale(op.txn, l.txn.lease.epoch, op.epoch));
720 }
721 let mut k: usize = 0;
722 while k < l.ops.len()
723 invariant
724 k <= l.ops@.len(),
725 forall|m: int| 0 <= m < k ==> (#[trigger] l.ops@[m]).op != *op,
726 decreases l.ops.len() - k,
727 {
728 if op_eq(l.ops[k].op, *op) {
729 return Err(LedgerError::DuplicateOp(*op));
730 }
731 k += 1;
732 }
733 Ok(())
734 },
735 Record::OpAck { op, .. } => {
736 let i = match find(&self.live, op.txn) {
737 Some(i) => i,
738 None => return Err(LedgerError::NotLive(op.txn)),
739 };
740 let l = &self.live.entries[i].1;
741 let mut k: usize = 0;
742 while k < l.ops.len()
743 invariant
744 k <= l.ops@.len(),
745 decreases l.ops.len() - k,
746 {
747 if op_eq(l.ops[k].op, *op) {
748 return match &l.ops[k].ack {
749 None => {
750 assert(self.live.entries@[i as int].1.ops@[k as int].op == *op);
751 Ok(())
752 },
753 Some(_) => Err(LedgerError::DuplicateOp(*op)),
754 };
755 }
756 k += 1;
757 }
758 Err(LedgerError::UnknownOp(*op))
759 },
760 Record::Resolved { txn, .. } => {
761 if find(&self.interlock, *txn).is_none() {
762 return Err(LedgerError::NotInterlocked(*txn));
763 }
764 Ok(())
765 },
766 Record::ExternalAccepted { seq, .. } => match self.external {
767 Some((have, _)) if *seq <= have => Err(LedgerError::ExternalReplay { have, got: *seq }),
768 _ => Ok(()),
769 },
770 _ => Ok(()),
771 }
772 }
773}
774
775fn recovery_eq(a: planarian_schema::RecoveryStrategy, b: planarian_schema::RecoveryStrategy) -> (r: bool)
776 ensures
777 r == (a == b),
778{
779 use planarian_schema::RecoveryStrategy as R;
780 match (a, b) {
781 (R::ReobserveOnly, R::ReobserveOnly) => true,
782 (R::RevertGeneration, R::RevertGeneration) => true,
783 (R::UnbindDevice, R::UnbindDevice) => true,
784 (R::RestoreSnapshotThenRevert, R::RestoreSnapshotThenRevert) => true,
785 _ => false,
786 }
787}
788
789pub open spec fn fails(r: Record) -> Option<TxnId> {
791 match r {
792 Record::Advance { txn, to: TxnState::RecoveryFailed, .. } => Some(txn),
793 _ => None,
794 }
795}
796
797pub open spec fn epochs_grow(a: Ledger, b: Ledger) -> bool {
799 forall|i: int, j: int|
800 0 <= i < a.live.entries@.len() && 0 <= j < b.live.entries@.len() && (#[trigger] a.live.entries@[i]).0
801 == (#[trigger] b.live.entries@[j]).0 ==> a.live.entries@[i].1.txn.lease.epoch
802 <= b.live.entries@[j].1.txn.lease.epoch
803}
804
805#[verifier::external_body]
809fn push_tag(path: &mut Vec<String>, s: &TxnState) {
810 path.push(tag(s).into());
811}
812
813#[verifier::external_body]
814fn tag_string(s: &TxnState) -> String {
815 tag(s).into()
816}
817
818#[verifier::external_body]
819fn note_why(why: &mut String, note: &String) {
820 if !note.is_empty() && !note.starts_with("observed: ") && !note.starts_with("Succeeded") {
821 *why = note.clone();
822 }
823}
824
825#[verifier::external_body]
826fn archive(terminal: &mut BTreeMap<TxnId, TerminalTxn>, id: TxnId, l: LiveTxn, withdrawn_for: Option<TxnId>) {
827 terminal.insert(
828 id,
829 TerminalTxn {
830 state: l.txn.state,
831 epoch: l.txn.lease.epoch,
832 owner: l.txn.lease.owner,
833 path: l.path,
834 transition: l.txn.transition,
835 why: l.why,
836 withdrawn_for,
837 prepared_again_from: l.txn.prepared_again_from,
838 },
839 );
840 while terminal.len() > TERMINAL_KEEP {
841 let oldest = *terminal.keys().next().expect("non-empty");
842 terminal.remove(&oldest);
843 }
844}
845
846#[verifier::external_body]
847fn set_override(list: &mut Vec<Override>, s: Override) {
848 list.retain(|x| x.unit != s.unit);
849 list.push(s);
850}
851
852#[verifier::external_body]
853fn lift_override(list: &mut Vec<Override>, u: &UnitId) {
854 list.retain(|x| &x.unit != u);
855}
856
857#[verifier::external_body]
858fn record_incident(incidents: &mut BTreeMap<IncidentId, Incident>, i: Incident) {
859 if i.state == planarian_schema::IncidentState::Closed {
860 incidents.remove(&i.id);
861 } else {
862 incidents.insert(i.id, i);
863 }
864}
865
866#[verifier::external_body]
867fn use_token(tokens: &mut Vec<([u8; 16], Timestamp)>, at: Timestamp, nonce: [u8; 16], expires: Timestamp) {
868 tokens.retain(|(_, exp)| *exp > at);
869 tokens.push((nonce, expires));
870}
871
872fn put<V>(m: &mut IdMap<V>, id: TxnId, v: V)
874 ensures
875 has_id(final(m).entries@, id),
876{
877 match find(m, id) {
878 Some(i) => {
879 m.entries.remove(i);
880 m.entries.insert(i, (id, v));
881 assert(final(m).entries@[i as int].0 == id);
882 },
883 None => {
884 let mut p: usize = 0;
885 while p < m.entries.len() && m.entries[p].0.0 < id.0
886 invariant
887 p <= m.entries@.len(),
888 decreases m.entries.len() - p,
889 {
890 p += 1;
891 }
892 m.entries.insert(p, (id, v));
893 assert(final(m).entries@[p as int].0 == id);
894 },
895 }
896}
897
898proof fn lemma_unique_index(l: Ledger, i: int, j: int)
900 requires
901 inv(l),
902 0 <= i < l.live.entries@.len(),
903 0 <= j < l.live.entries@.len(),
904 l.live.entries@[i].0 == l.live.entries@[j].0,
905 ensures
906 i == j,
907{
908 if i != j {
909 assert(pair_ok(l.live.entries@[i], l.live.entries@[j]));
910 }
911}
912
913impl Ledger {
914 pub fn apply(&mut self, e: Entry) -> (r: Result<(), LedgerError>)
916 ensures
917 r is Ok ==> rules(*old(self), e),
918 r is Err ==> *final(self) == *old(self),
919 r is Ok ==> final(self).last_seq == e.seq,
920 (r is Ok && inv(*old(self))) ==> inv(*final(self)),
921 (r is Ok && inv(*old(self))) ==> epochs_grow(*old(self), *final(self)),
922 (r is Ok && fails(e.record) is Some) ==> has_id(final(self).interlock.entries@, fails(e.record)->Some_0),
923 {
924 match self.validate(&e) {
925 Err(x) => return Err(x),
926 Ok(()) => {},
927 }
928 self.apply_valid(e);
929 Ok(())
930 }
931
932 pub fn add_interlock(&mut self, entry: InterlockEntry)
935 ensures
936 inv(*old(self)) ==> inv(*final(self)),
937 has_id(final(self).interlock.entries@, entry.txn),
938 {
939 let id = entry.txn;
940 put(&mut self.interlock, id, entry);
941 }
942
943 fn apply_valid(&mut self, e: Entry)
944 requires
945 rules(*old(self), e),
946 ensures
947 final(self).last_seq == e.seq,
948 inv(*old(self)) ==> inv(*final(self)),
949 inv(*old(self)) ==> epochs_grow(*old(self), *final(self)),
950 fails(e.record) is Some ==> has_id(final(self).interlock.entries@, fails(e.record)->Some_0),
951 {
952 self.last_seq = e.seq;
953 let at = e.at;
954 match e.record {
955 Record::Prepared(t) => self.apply_prepared(*t),
956 Record::Advance { txn, epoch, to, note, withdrawn_for } => {
957 self.apply_advance(txn, epoch, to, note, withdrawn_for)
958 },
959 Record::Takeover { txn, from_epoch, lease } => self.apply_takeover(txn, from_epoch, lease),
960 Record::Renew { txn, epoch, worker_pid, boot_at } => self.apply_renew(txn, epoch, boot_at, worker_pid),
961 Record::OpBegin { op, call } => self.apply_op_begin(op, call),
962 Record::OpAck { op, outcome } => self.apply_op_ack(op, outcome),
963 Record::OverrideSet(s) => set_override(&mut self.overrides, s),
964 Record::OverrideLifted(u) => lift_override(&mut self.overrides, &u),
965 Record::Incident(i) => record_incident(&mut self.incidents, i),
966 Record::KnownGood(g) => self.known_good = Some(g),
967 Record::LiveTested(g) => self.live_tested = g,
968 Record::AcceptedBaseline(g) => self.accepted_baseline = g,
969 Record::SecurityFloor(g) => self.security_floor = g,
970 Record::ExternalAccepted { seq, digest } => self.external = Some((seq, digest)),
971 Record::TokenUsed { nonce, expires, .. } => use_token(&mut self.tokens_used, at, nonce, expires),
972 Record::Resolved { txn, .. } => {
973 if let Some(k) = find(&self.interlock, txn) {
974 self.interlock.entries.remove(k);
975 }
976 },
977 Record::Reconciliation(r) => self.last_reconciliation = Some(r),
978 Record::AgentStarted { name, instance, by } => self.agents.push((name, instance, by)),
979 Record::HoldApplied { unit, .. } => self.hold_events.push((unit, true)),
980 Record::HoldLifted { unit, .. } => self.hold_events.push((unit, false)),
981 }
982 }
983
984 fn apply_prepared(&mut self, t: Transaction)
985 requires
986 prepared_rule(*old(self), t),
987 ensures
988 final(self).last_seq == old(self).last_seq,
989 inv(*old(self)) ==> inv(*final(self)),
990 inv(*old(self)) ==> epochs_grow(*old(self), *final(self)),
991 {
992 let ghost pre = *self;
993 let id = t.id;
994 self.next_txn = id.0 + 1;
995 let mut path = Vec::new();
996 push_tag(&mut path, &t.state);
997 let why = String::new();
998 self.live.entries.push((id, LiveTxn { txn: t, ops: Vec::new(), path, why, boot_only: false }));
999 proof {
1000 let n = pre.live.entries@.len();
1001 assert(self.live.entries@ =~= pre.live.entries@.push(self.live.entries@[n as int]));
1002 if inv(pre) {
1003 lemma_push(pre, *self);
1004 }
1005 }
1006 }
1007
1008 #[allow(unused_variables)] fn apply_advance(&mut self, txn: TxnId, epoch: u64, to: TxnState, note: String, withdrawn_for: Option<TxnId>)
1010 requires
1011 advance_rule(*old(self), txn, epoch, to),
1012 ensures
1013 final(self).last_seq == old(self).last_seq,
1014 inv(*old(self)) ==> inv(*final(self)),
1015 inv(*old(self)) ==> epochs_grow(*old(self), *final(self)),
1016 to == TxnState::RecoveryFailed ==> has_id(final(self).interlock.entries@, txn),
1017 {
1018 let ghost pre = *self;
1019 let i = match find(&self.live, txn) {
1020 Some(i) => i,
1021 None => {
1022 proof {
1023 assert(false);
1024 }
1025 return;
1026 },
1027 };
1028 proof {
1029 if inv(pre) {
1030 let j = choose|j: int|
1031 0 <= j < pre.live.entries@.len() && (#[trigger] pre.live.entries@[j]).0 == txn && advance_ok(
1032 pre,
1033 j,
1034 epoch,
1035 to,
1036 );
1037 lemma_unique_index(pre, i as int, j);
1038 }
1039 }
1040 let (id, mut l) = self.live.entries.remove(i);
1041 let failed = match to {
1042 TxnState::RecoveryFailed => true,
1043 _ => false,
1044 };
1045 if failed {
1046 let entry = InterlockEntry {
1047 txn,
1048 transition: l.txn.transition.clone(),
1049 failed_from: tag_string(&l.txn.state),
1050 base_override: l.txn.base_override,
1051 candidate_override: l.txn.candidate_override,
1052 revert_to: l.txn.revert_to,
1053 note: note.clone(),
1054 };
1055 put(&mut self.interlock, txn, entry);
1056 }
1057 push_tag(&mut l.path, &to);
1058 note_why(&mut l.why, ¬e);
1059 let publishing = match to {
1060 TxnState::Publishing => true,
1061 _ => false,
1062 };
1063 l.boot_only = l.boot_only || publishing;
1064 l.txn.state = to;
1065 if l.txn.state.is_terminal() {
1066 archive(&mut self.terminal, id, l, withdrawn_for);
1067 proof {
1068 assert(self.live.entries@ =~= pre.live.entries@.remove(i as int));
1069 if inv(pre) {
1070 lemma_remove(pre, *self, i as int);
1071 }
1072 }
1073 } else {
1074 self.live.entries.insert(i, (id, l));
1075 proof {
1076 assert(self.live.entries@ =~= pre.live.entries@.update(i as int, self.live.entries@[i as int]));
1077 if inv(pre) {
1078 let x = pre.live.entries@[i as int].1;
1079 let y = self.live.entries@[i as int].1;
1080 assert(generation(y) == generation(x));
1081 lemma_update(pre, *self, i as int);
1082 }
1083 }
1084 }
1085 }
1086
1087 #[allow(unused_variables)] fn apply_takeover(&mut self, txn: TxnId, from_epoch: u64, lease: Lease)
1089 requires
1090 takeover_rule(*old(self), txn, from_epoch, lease),
1091 ensures
1092 final(self).last_seq == old(self).last_seq,
1093 inv(*old(self)) ==> inv(*final(self)),
1094 inv(*old(self)) ==> epochs_grow(*old(self), *final(self)),
1095 {
1096 let ghost pre = *self;
1097 let i = match find(&self.live, txn) {
1098 Some(i) => i,
1099 None => {
1100 proof {
1101 assert(false);
1102 }
1103 return;
1104 },
1105 };
1106 proof {
1107 if inv(pre) {
1108 let j = choose|j: int|
1109 0 <= j < pre.live.entries@.len() && (#[trigger] pre.live.entries@[j]).0 == txn
1110 && pre.live.entries@[j].1.txn.lease.epoch == from_epoch && lease.epoch > from_epoch
1111 && lease.txn == txn;
1112 lemma_unique_index(pre, i as int, j);
1113 }
1114 }
1115 let (id, mut l) = self.live.entries.remove(i);
1116 l.txn.lease = lease;
1117 self.live.entries.insert(i, (id, l));
1118 proof {
1119 assert(self.live.entries@ =~= pre.live.entries@.update(i as int, self.live.entries@[i as int]));
1120 if inv(pre) {
1121 lemma_update(pre, *self, i as int);
1122 }
1123 }
1124 }
1125
1126 #[allow(unused_variables)] fn apply_renew(&mut self, txn: TxnId, epoch: u64, at: Timestamp, worker_pid: Option<u32>)
1128 requires
1129 renew_rule(*old(self), txn, epoch),
1130 ensures
1131 final(self).last_seq == old(self).last_seq,
1132 inv(*old(self)) ==> inv(*final(self)),
1133 inv(*old(self)) ==> epochs_grow(*old(self), *final(self)),
1134 {
1135 let ghost pre = *self;
1136 let i = match find(&self.live, txn) {
1137 Some(i) => i,
1138 None => {
1139 proof {
1140 assert(false);
1141 }
1142 return;
1143 },
1144 };
1145 let (id, mut l) = self.live.entries.remove(i);
1146 l.txn.lease.renewed = at;
1147 l.txn.lease.worker_pid = worker_pid;
1148 self.live.entries.insert(i, (id, l));
1149 proof {
1150 assert(self.live.entries@ =~= pre.live.entries@.update(i as int, self.live.entries@[i as int]));
1151 if inv(pre) {
1152 lemma_update(pre, *self, i as int);
1153 }
1154 }
1155 }
1156
1157 fn apply_op_begin(&mut self, op: OpId, call: String)
1158 requires
1159 op_begin_rule(*old(self), op),
1160 ensures
1161 final(self).last_seq == old(self).last_seq,
1162 inv(*old(self)) ==> inv(*final(self)),
1163 inv(*old(self)) ==> epochs_grow(*old(self), *final(self)),
1164 {
1165 let ghost pre = *self;
1166 let i = match find(&self.live, op.txn) {
1167 Some(i) => i,
1168 None => {
1169 proof {
1170 assert(false);
1171 }
1172 return;
1173 },
1174 };
1175 let (id, mut l) = self.live.entries.remove(i);
1176 l.ops.push(OpRecord { op, call, ack: None });
1177 self.live.entries.insert(i, (id, l));
1178 proof {
1179 assert(self.live.entries@ =~= pre.live.entries@.update(i as int, self.live.entries@[i as int]));
1180 if inv(pre) {
1181 lemma_update(pre, *self, i as int);
1182 }
1183 }
1184 }
1185
1186 fn apply_op_ack(&mut self, op: OpId, outcome: Outcome)
1187 requires
1188 op_ack_rule(*old(self), op),
1189 ensures
1190 final(self).last_seq == old(self).last_seq,
1191 inv(*old(self)) ==> inv(*final(self)),
1192 inv(*old(self)) ==> epochs_grow(*old(self), *final(self)),
1193 {
1194 let ghost pre = *self;
1195 let i = match find(&self.live, op.txn) {
1196 Some(i) => i,
1197 None => {
1198 proof {
1199 assert(false);
1200 }
1201 return;
1202 },
1203 };
1204 let (id, mut l) = self.live.entries.remove(i);
1205 let ghost before = l;
1206 let mut k: usize = 0;
1207 while k < l.ops.len()
1208 invariant
1209 k <= l.ops@.len(),
1210 l.txn == before.txn,
1211 decreases l.ops.len() - k,
1212 {
1213 if op_eq(l.ops[k].op, op) {
1214 let mut o = l.ops.remove(k);
1215 o.ack = Some(outcome);
1216 l.ops.insert(k, o);
1217 break;
1218 }
1219 k += 1;
1220 }
1221 self.live.entries.insert(i, (id, l));
1222 proof {
1223 assert(self.live.entries@ =~= pre.live.entries@.update(i as int, self.live.entries@[i as int]));
1224 if inv(pre) {
1225 lemma_update(pre, *self, i as int);
1226 }
1227 }
1228 }
1229}
1230
1231pub proof fn i6_one_generation_active(l: Ledger, a: int, b: int)
1236 requires
1237 inv(l),
1238 0 <= a < l.live.entries@.len(),
1239 0 <= b < l.live.entries@.len(),
1240 a != b,
1241 ensures
1242 ({
1243 let x = l.live.entries@[a].1;
1244 let y = l.live.entries@[b].1;
1245 &&& (generation(x) && generation(y)) ==> !((flying(x.txn.state) || x.txn.state is Built) && (flying(
1246 y.txn.state,
1247 ) || y.txn.state is Built))
1248 &&& (generation(x) && x.txn.state.exclusive() && !(x.txn.state is Promoted && x.boot_only)) ==> !flying(
1249 y.txn.state,
1250 )
1251 }),
1252{
1253 assert(pair_ok(l.live.entries@[a], l.live.entries@[b]));
1254}
1255
1256proof fn lemma_update(pre: Ledger, post: Ledger, i: int)
1261 requires
1262 inv(pre),
1263 0 <= i < pre.live.entries@.len(),
1264 post.next_txn == pre.next_txn,
1265 post.live.entries@ == pre.live.entries@.update(i, post.live.entries@[i]),
1266 live_ok(post.live.entries@[i], post.next_txn),
1267 post.live.entries@[i].0 == pre.live.entries@[i].0,
1268 generation(post.live.entries@[i].1) == generation(pre.live.entries@[i].1),
1269 post.live.entries@[i].1.txn.lease.epoch >= pre.live.entries@[i].1.txn.lease.epoch,
1270 post.live.entries@[i].1.boot_only == pre.live.entries@[i].1.boot_only
1271 || !(post.live.entries@[i].1.txn.state is Promoted),
1272 post.live.entries@[i].1.txn.state == pre.live.entries@[i].1.txn.state || forall|j: int|
1273 0 <= j < pre.live.entries@.len() && j != i ==> compatible(
1274 post.live.entries@[i].1.txn.state,
1275 generation(post.live.entries@[i].1),
1276 post.live.entries@[i].1.boot_only,
1277 (#[trigger] pre.live.entries@[j]).1.txn.state,
1278 generation(pre.live.entries@[j].1),
1279 pre.live.entries@[j].1.boot_only,
1280 ),
1281 ensures
1282 inv(post),
1283 epochs_grow(pre, post),
1284{
1285 let a = pre.live.entries@;
1286 let b = post.live.entries@;
1287 assert forall|x: int, y: int| 0 <= x < b.len() && 0 <= y < b.len() && x != y implies pair_ok(
1288 #[trigger] b[x],
1289 #[trigger] b[y],
1290 ) by {
1291 assert(pair_ok(a[x], a[y]));
1292 if x == i {
1293 assert(pair_ok(a[i], a[y]));
1294 } else if y == i {
1295 assert(pair_ok(a[x], a[i]));
1296 }
1297 }
1298 assert forall|x: int| 0 <= x < b.len() implies live_ok(#[trigger] b[x], post.next_txn) by {
1299 if x != i {
1300 assert(live_ok(a[x], pre.next_txn));
1301 }
1302 }
1303 assert forall|x: int, y: int| 0 <= x < a.len() && 0 <= y < b.len() && (#[trigger] a[x]).0 == (#[trigger] b[y]).0 implies a[x].1.txn.lease.epoch <= b[y].1.txn.lease.epoch by {
1304 lemma_unique_index(pre, x, y);
1305 }
1306}
1307
1308proof fn lemma_remove(pre: Ledger, post: Ledger, i: int)
1310 requires
1311 inv(pre),
1312 0 <= i < pre.live.entries@.len(),
1313 post.next_txn == pre.next_txn,
1314 post.live.entries@ == pre.live.entries@.remove(i),
1315 ensures
1316 inv(post),
1317 epochs_grow(pre, post),
1318{
1319 let a = pre.live.entries@;
1320 let b = post.live.entries@;
1321 assert forall|x: int| 0 <= x < b.len() implies #[trigger] b[x] == a[if x < i { x } else { x + 1 }] by {}
1322 assert forall|x: int, y: int| 0 <= x < b.len() && 0 <= y < b.len() && x != y implies pair_ok(
1323 #[trigger] b[x],
1324 #[trigger] b[y],
1325 ) by {
1326 let x2 = if x < i { x } else { x + 1 };
1327 let y2 = if y < i { y } else { y + 1 };
1328 assert(pair_ok(a[x2], a[y2]));
1329 }
1330 assert forall|x: int| 0 <= x < b.len() implies live_ok(#[trigger] b[x], post.next_txn) by {
1331 let x2 = if x < i { x } else { x + 1 };
1332 assert(live_ok(a[x2], pre.next_txn));
1333 }
1334 assert forall|x: int, y: int| 0 <= x < a.len() && 0 <= y < b.len() && (#[trigger] a[x]).0 == (#[trigger] b[y]).0 implies a[x].1.txn.lease.epoch <= b[y].1.txn.lease.epoch by {
1335 let y2 = if y < i { y } else { y + 1 };
1336 lemma_unique_index(pre, x, y2);
1337 }
1338}
1339
1340proof fn lemma_push(pre: Ledger, post: Ledger)
1342 requires
1343 inv(pre),
1344 post.live.entries@.len() == pre.live.entries@.len() + 1,
1345 post.live.entries@ == pre.live.entries@.push(post.live.entries@[pre.live.entries@.len() as int]),
1346 post.next_txn > pre.next_txn,
1347 ({
1348 let x = post.live.entries@[pre.live.entries@.len() as int];
1349 &&& live_ok(x, post.next_txn)
1350 &&& x.1.txn.state is Prepared
1351 &&& x.0.0 >= pre.next_txn
1352 }),
1353 ensures
1354 inv(post),
1355 epochs_grow(pre, post),
1356{
1357 let a = pre.live.entries@;
1358 let b = post.live.entries@;
1359 let n = a.len() as int;
1360 assert forall|x: int| 0 <= x < b.len() implies live_ok(#[trigger] b[x], post.next_txn) by {
1361 if x < n {
1362 assert(live_ok(a[x], pre.next_txn));
1363 }
1364 }
1365 assert forall|x: int, y: int| 0 <= x < b.len() && 0 <= y < b.len() && x != y implies pair_ok(
1366 #[trigger] b[x],
1367 #[trigger] b[y],
1368 ) by {
1369 if x < n && y < n {
1370 assert(pair_ok(a[x], a[y]));
1371 } else if x < n {
1372 assert(live_ok(a[x], pre.next_txn));
1373 } else {
1374 assert(live_ok(a[y], pre.next_txn));
1375 }
1376 }
1377 assert forall|x: int, y: int| 0 <= x < a.len() && 0 <= y < b.len() && (#[trigger] a[x]).0 == (#[trigger] b[y]).0 implies a[x].1.txn.lease.epoch <= b[y].1.txn.lease.epoch by {
1378 if y < n {
1379 lemma_unique_index(pre, x, y);
1380 } else {
1381 assert(live_ok(a[x], pre.next_txn));
1382 }
1383 }
1384}
1385
1386} pub const TERMINAL_KEEP: usize = 256;
1389
1390impl Ledger {
1391 pub fn epoch(&self, txn: TxnId) -> Option<u64> {
1392 self.live.get(&txn).map(|l| l.txn.lease.epoch).or_else(|| self.terminal.get(&txn).map(|t| t.epoch))
1393 }
1394
1395 pub fn state(&self, txn: TxnId) -> Option<TxnState> {
1396 self.live.get(&txn).map(|l| l.txn.state.clone()).or_else(|| self.terminal.get(&txn).map(|t| t.state.clone()))
1397 }
1398
1399 pub fn path(&self, txn: TxnId) -> Option<&[String]> {
1400 self.live.get(&txn).map(|l| &l.path[..]).or_else(|| self.terminal.get(&txn).map(|t| &t.path[..]))
1401 }
1402
1403 pub fn interlocked(&self) -> bool {
1404 !self.interlock.is_empty()
1405 }
1406
1407 pub fn agent_for(&self, name: &str, instance: [u8; 16]) -> Option<u32> {
1411 if instance == [0; 16] {
1412 return None;
1413 }
1414 self.agents.iter().rev().find(|(n, i, _)| *i == instance && n == name).map(|(_, _, by)| *by)
1415 }
1416
1417 pub fn agent_owner(&self, name: &str) -> Option<u32> {
1419 self.agents.iter().rev().find(|(n, _, _)| n == name).map(|(_, _, by)| *by)
1420 }
1421
1422 pub fn holds(&self) -> std::collections::BTreeSet<UnitId> {
1426 let mut held = std::collections::BTreeSet::new();
1427 for (u, on) in &self.hold_events {
1428 if *on {
1429 held.insert(u.clone());
1430 } else {
1431 held.remove(u);
1432 }
1433 }
1434 held
1435 }
1436}
1437
1438impl LiveTxn {
1439 pub fn unacked_ops(&self) -> impl Iterator<Item = &OpRecord> {
1440 self.ops.iter().filter(|o| o.ack.is_none())
1441 }
1442}
1443
1444impl<V> Default for IdMap<V> {
1445 fn default() -> Self {
1446 IdMap { entries: Vec::new() }
1447 }
1448}
1449
1450impl<V> IdMap<V> {
1451 fn find_index(&self, id: &TxnId) -> Result<usize, usize> {
1452 self.entries.binary_search_by(|(k, _)| k.cmp(id))
1453 }
1454
1455 pub fn get(&self, id: &TxnId) -> Option<&V> {
1456 self.find_index(id).ok().map(|i| &self.entries[i].1)
1457 }
1458
1459 pub fn contains_key(&self, id: &TxnId) -> bool {
1460 self.find_index(id).is_ok()
1461 }
1462
1463 pub fn len(&self) -> usize {
1464 self.entries.len()
1465 }
1466
1467 pub fn is_empty(&self) -> bool {
1468 self.entries.is_empty()
1469 }
1470
1471 pub fn keys(&self) -> impl DoubleEndedIterator<Item = &TxnId> {
1472 self.entries.iter().map(|(k, _)| k)
1473 }
1474
1475 pub fn values(&self) -> impl DoubleEndedIterator<Item = &V> {
1476 self.entries.iter().map(|(_, v)| v)
1477 }
1478
1479 pub fn iter(&self) -> impl DoubleEndedIterator<Item = (&TxnId, &V)> {
1480 self.entries.iter().map(|(k, v)| (k, v))
1481 }
1482}
1483
1484impl<V> std::ops::Index<&TxnId> for IdMap<V> {
1485 type Output = V;
1486
1487 fn index(&self, id: &TxnId) -> &V {
1488 self.get(id).expect("no entry for this transaction")
1489 }
1490}
1491
1492impl<V: Serialize> Serialize for IdMap<V> {
1493 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1494 s.collect_map(self.iter())
1495 }
1496}
1497
1498impl<'de, V: Deserialize<'de>> Deserialize<'de> for IdMap<V> {
1499 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
1500 Ok(IdMap { entries: BTreeMap::<TxnId, V>::deserialize(d)?.into_iter().collect() })
1502 }
1503}