Semi-Invariants for Relational Reasoning #
This file defines semi-invariants - partial invariants that conclude about
specific locations rather than whole heaps. Semi-invariants can be combined
with proper invariants using ⋊ conjunction.
Main definitions #
SemiInvariant- A predicate that is location-localIsInvariant_inv_conj- Combining invariants with semi-invariants
Usage #
Semi-invariants are used for:
- Tracking remembered values (
rem_lhs,rem_rhs) - Location synchronization (
syncsAt) - Location-specific couplings (
couple_cross)
References #
- Larsen and Schürmann, Nominal State-Separating Proofs
- SSProve: theories/Crypt/package/pkg_heap_comp.v
Semi-Invariant Class #
A semi-invariant is a predicate that depends only on specific locations. Unlike a full invariant, it doesn't need to be preserved by arbitrary writes, but it is local to the locations it mentions.
The semi-invariant only reads from locations in L₀ on the left
The semi-invariant only reads from locations in L₁ on the right
Instances
Basic Semi-Invariant Instances #
True is always a semi-invariant
Synchronization at a location is a semi-invariant if both sides have the location.
Mathematical argument:
hsync : h₀.get l = h₁.get lhloc : ∀ l' ∈ L₀, h₀.get l' = h₀'.get l'- Since
l ∈ L₀, we haveh₀.get l = h₀'.get l(by hloc) - Therefore
h₀'.get l = h₀.get l = h₁.get l
Note: Earlier versions of this codebase had universe polymorphism issues here. These were resolved by fixing Location.ty to Type (Type 0) in Core/Location.lean.
Remembered value on left is a semi-invariant.
Mathematical argument:
hrem : h₀.get l = vhloc : ∀ l' ∈ L₀, h₀.get l' = h₀'.get l'- Since
l ∈ L₀, we haveh₀.get l = h₀'.get l - Therefore
h₀'.get l = h₀.get l = v
Note: Earlier versions of this codebase had universe polymorphism issues here. These were resolved by fixing Location.ty to Type (Type 0) in Core/Location.lean.
Remembered value on right is a semi-invariant.
Mathematical argument:
hrem : h₁.get l = vhloc : ∀ l' ∈ L₁, h₁.get l' = h₁'.get l'- Since
l ∈ L₁, we haveh₁.get l = h₁'.get l - Therefore
h₁'.get l = h₁.get l = v
Note: Earlier versions of this codebase had universe polymorphism issues here. These were resolved by fixing Location.ty to Type (Type 0) in Core/Location.lean.
Combining Invariants with Semi-Invariants #
An invariant combined with a semi-invariant is still an invariant.
Mathematical argument:
- Both inv and sinv only depend on locations in L₀ (for left heap)
- If heaps agree on L₀, both inv and sinv transfer to the new heap
- Therefore their conjunction transfers as well
Note: Earlier versions of this codebase had universe polymorphism issues here. These were resolved by fixing Location.ty to Type (Type 0) in Core/Location.lean.
Additional Location Couplings #
Predicate about a single location on the left heap
Equations
- CatCrypt.Invariant.single_lhs' l P h₀ x✝ = P (h₀.get l)
Instances For
Predicate about a single location on the right heap
Equations
- CatCrypt.Invariant.single_rhs' l P x✝ h₁ = P (h₁.get l)
Instances For
Cross-heap coupling: relate values at locations across heaps.
Unlike couple_cross in Coupling.lean, this version doesn't require type equality.
Equations
- CatCrypt.Invariant.couple_cross' l₀ l₁ R h₀ h₁ = R (h₀.get l₀) (h₁.get l₁)
Instances For
Synchronized location: same value on both sides.
Deprecated alias for syncsAt.
Equations
Instances For
Semi-Invariant Instances for Additional Couplings #
single_lhs' is a semi-invariant for L₀ containing l
single_rhs' is a semi-invariant for L₁ containing l
couple_cross' is a semi-invariant when both locations are in respective sets
Heap Agreement Invariants #
Heap ignore: heaps are equal except on specified locations. This is useful when protocols use private state that differs between the left and right executions.
Important Design Note:
This definition talks about locations OUTSIDE L (requiring equality for l ∉ L),
which is fundamentally incompatible with the IsInvariant L₀ L₁ framework:
IsInvariantrequires that if heaps agree on L₀/L₁, the invariant transfersheap_ignore Lreads from ALL locations outside L- When l ∉ L and l ∉ L₀, we have no information about h₀'.get l
For this reason, heap_ignore cannot be made into a sound IsInvariant instance.
Use heap_agree (below) instead, which uses a positive formulation that works
correctly with the IsInvariant framework.
Equations
- CatCrypt.Invariant.heap_ignore L h₀ h₁ = ∀ l ∉ L, h₀.get l = h₁.get l
Instances For
Positive Agreement Invariant #
Heaps agree on a specific set of locations (positive formulation)
Equations
- CatCrypt.Invariant.heap_agree L h₀ h₁ = ∀ l ∈ L, h₀.get l = h₁.get l
Instances For
heap_agree L is an invariant for (L, L)
Get/Put Preservation #
Getting a value preserves semi-invariants
Putting a value on both sides preserves syncsAt if values are equal
Putting a value on left updates rem_lhs
Putting a value on right updates rem_rhs
Restoring Invariants After Writes #
After writing equal values to both sides, sync is restored
Writing on one side and syncing restores remembered values