Skip to content

Commit 17e79a0

Browse files
coinage-layer Phase 7 (part 16): delete_purse_safe — checks + delete composite
Composes the existing pre-flight guards with delete_purse for a single-call safe deletion: delete_purse_safe(p) -> Result<(), Error> Returns (in this order): - PurseHasInFlightOperations — if has_op_targeting_purse(p) - InsufficientFunds — if check_has_live_coin_in(p) - Then anything delete_purse itself can return. Sharp Ok postcondition: the purse exists, isn't MAIN_PURSE, has no live coins, and no op targets it. Verus discharges the implication from the pre-flight guards' sharp postconditions composed with delete_purse's existing contract. 209 verified, 0 errors.
1 parent 52a84d6 commit 17e79a0

1 file changed

Lines changed: 38 additions & 0 deletions

File tree

  • rust/crates/coinage-layer/src

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,44 @@ impl State {
13061306
/// - `Ok(())` if the purse is removed.
13071307
/// - `Err(CannotDeleteMainPurse)` if `p == MAIN_PURSE`; state unchanged.
13081308
/// - `Err(PurseNotFound(p))` if `p` is not a known purse; state unchanged.
1309+
/// Safe variant of [`Self::delete_purse`]: runs the safety checks
1310+
/// first and returns a typed error if the purse can't be removed,
1311+
/// rather than tripping a hard precondition. Composes with the
1312+
/// existing exec pre-flight guards (`check_has_live_coin_in`,
1313+
/// `has_op_targeting_purse`).
1314+
///
1315+
/// Errors surface (in the order checked):
1316+
/// - PurseHasInFlightOperations — at least one op targets `p`.
1317+
/// - InsufficientFunds — `p` still has at least one live coin.
1318+
/// - Then anything delete_purse itself can return.
1319+
pub fn delete_purse_safe(&mut self, p: PurseId) -> (res: Result<(), Error>)
1320+
requires
1321+
old(self).invariant(),
1322+
ensures
1323+
final(self).invariant(),
1324+
match res {
1325+
Ok(()) =>
1326+
!old(self).has_live_coin_in(p)
1327+
&& (forall|h: OpHandle|
1328+
#[trigger] old(self).operations().dom().contains(h)
1329+
==> old(self).operations()[h].purse != p)
1330+
&& old(self).purses().dom().contains(p)
1331+
&& p != MAIN_PURSE,
1332+
Err(_) => true,
1333+
},
1334+
{
1335+
if self.has_op_targeting_purse(p) {
1336+
return Err(Error::PurseHasInFlightOperations);
1337+
}
1338+
if self.check_has_live_coin_in(p) {
1339+
return Err(Error::InsufficientFunds {
1340+
requested: 0,
1341+
available: 0,
1342+
});
1343+
}
1344+
self.delete_purse(p)
1345+
}
1346+
13091347
pub fn delete_purse(&mut self, p: PurseId) -> (res: Result<(), Error>)
13101348
requires
13111349
old(self).invariant(),

0 commit comments

Comments
 (0)