Skip to content

Commit 52a84d6

Browse files
coinage-layer Phase 7 (part 15): check_has_live_coin_in pre-flight guard
Exec witness for the has_live_coin_in spec predicate: check_has_live_coin_in(p) -> bool Returns true iff at least one coin in purse p is non-Spent. Sharp postcondition: result exactly equals the spec predicate. Pair with has_in_flight_op_for_purse before delete_purse to surface "purse not empty" as an early bail returned to the user, instead of a hard precondition trap. 208 verified, 0 errors.
1 parent c0535c3 commit 52a84d6

1 file changed

Lines changed: 50 additions & 0 deletions

File tree

  • rust/crates/coinage-layer/src

rust/crates/coinage-layer/src/lib.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4689,6 +4689,56 @@ impl State {
46894689
c
46904690
}
46914691

4692+
/// Exec witness for the [`Self::has_live_coin_in`] spec predicate:
4693+
/// `true` iff at least one coin in purse `p` is in any non-`Spent`
4694+
/// state. Pair with [`Self::has_in_flight_op_for_purse`] before
4695+
/// `delete_purse` to surface "purse not empty" as an early bail
4696+
/// instead of a precondition trap.
4697+
pub fn check_has_live_coin_in(&self, p: PurseId) -> (res: bool)
4698+
requires
4699+
self.invariant(),
4700+
ensures
4701+
res == self.has_live_coin_in(p),
4702+
{
4703+
let mut j: usize = 0;
4704+
while j < self.coins.len()
4705+
invariant
4706+
0 <= j <= self.coins.len(),
4707+
self.invariant(),
4708+
forall|jj: int| 0 <= jj < j ==>
4709+
(#[trigger] self.coins@[jj]).purse != p
4710+
|| self.coins@[jj].state == CoinState::Spent,
4711+
decreases self.coins.len() - j,
4712+
{
4713+
let c = &self.coins[j];
4714+
let is_spent = matches!(c.state, CoinState::Spent);
4715+
if c.purse == p && !is_spent {
4716+
let key = (c.purse, c.idx);
4717+
proof {
4718+
assert(self.spec_coins@.dom().contains(key));
4719+
assert(self.coins()[key].state == self.coins@[j as int].state);
4720+
}
4721+
return true;
4722+
}
4723+
j = j + 1;
4724+
}
4725+
proof {
4726+
assert forall|k: (PurseId, u64)|
4727+
#[trigger] self.coins().dom().contains(k)
4728+
&& k.0 == p
4729+
implies self.coins()[k].state == CoinState::Spent
4730+
by {
4731+
let w = choose|jj: int|
4732+
0 <= jj < self.coins@.len()
4733+
&& #[trigger] self.coins@[jj].purse == k.0
4734+
&& self.coins@[jj].idx == k.1;
4735+
assert(self.coins@[w].purse == p);
4736+
assert(self.coins@[w].state == self.coins()[k].state);
4737+
}
4738+
}
4739+
false
4740+
}
4741+
46924742
/// Count of operations currently in-flight (non-terminal status).
46934743
pub fn op_count_in_flight(&self) -> (count: usize)
46944744
requires

0 commit comments

Comments
 (0)