use vstd::prelude::*;
verus! {
pub open spec fn sum_to(s: Seq<u64>, n: int) -> int
decreases n,
{
if n <= 0 {
0
} else if n > s.len() as int {
0
} else {
s[n - 1] as int + sum_to(s, n - 1)
}
}
pub proof fn lemma_sum_nonneg(s: Seq<u64>, n: int)
requires 0 <= n <= s.len(),
ensures sum_to(s, n) >= 0,
decreases n,
{
if n > 0 {
lemma_sum_nonneg(s, n - 1);
}
}
pub proof fn lemma_sum_zeros(s: Seq<u64>, n: int)
requires
0 <= n <= s.len(),
forall|j: int| 0 <= j < n ==> s[j] == 0,
ensures sum_to(s, n) == 0,
decreases n,
{
if n > 0 {
lemma_sum_zeros(s, n - 1);
}
}
pub proof fn lemma_elem_le_sum(s: Seq<u64>, k: int, n: int)
requires 0 <= k < n <= s.len(),
ensures s[k] as int <= sum_to(s, n),
decreases n,
{
if n == k + 1 {
lemma_sum_nonneg(s, k);
} else {
lemma_elem_le_sum(s, k, n - 1);
}
}
pub proof fn lemma_sum_unaffected(s: Seq<u64>, k: int, nv: u64, m: int)
requires 0 <= m <= k < s.len(),
ensures sum_to(s.update(k, nv), m) == sum_to(s, m),
decreases m,
{
if m > 0 {
lemma_sum_unaffected(s, k, nv, m - 1);
}
}
pub proof fn lemma_sum_update(s: Seq<u64>, k: int, nv: u64, n: int)
requires 0 <= k < n <= s.len(),
ensures sum_to(s.update(k, nv), n) == sum_to(s, n) - s[k] as int + nv as int,
decreases n,
{
if n == k + 1 {
lemma_sum_unaffected(s, k, nv, k);
} else {
lemma_sum_update(s, k, nv, n - 1);
}
}
pub struct FederatedBudget {
pub master_capacity: u64,
pub master_allocated: u64,
pub sub_capacities: Vec<u64>,
pub sub_allocated: Vec<u64>,
}
impl FederatedBudget {
pub open spec fn sum_caps(&self) -> int {
sum_to(self.sub_capacities@, self.sub_capacities@.len() as int)
}
pub open spec fn type_invariant(&self) -> bool {
self.sub_capacities.len() == self.sub_allocated.len()
}
pub open spec fn master_capacity_bound(&self) -> bool {
self.master_allocated <= self.master_capacity
}
pub open spec fn sub_pool_capacity_bound(&self) -> bool {
forall|i: int|
0 <= i < self.sub_capacities.len() ==>
#[trigger] self.sub_allocated@[i] <= self.sub_capacities@[i]
}
pub open spec fn capacity_conservation(&self) -> bool {
self.master_allocated == self.sum_caps()
}
pub open spec fn inv(&self) -> bool {
&&& self.type_invariant()
&&& self.master_capacity_bound()
&&& self.sub_pool_capacity_bound()
&&& self.capacity_conservation()
}
pub fn new(master_capacity: u64, num_pools: usize) -> (fb: FederatedBudget)
ensures
fb.master_capacity == master_capacity,
fb.sub_capacities@.len() == num_pools,
fb.master_allocated == 0,
fb.inv(),
{
let mut sub_capacities: Vec<u64> = Vec::new();
let mut sub_allocated: Vec<u64> = Vec::new();
let mut i: usize = 0;
while i < num_pools
invariant
i <= num_pools,
sub_capacities.len() == i,
sub_allocated.len() == i,
forall|j: int| 0 <= j < i ==> sub_capacities@[j] == 0,
forall|j: int| 0 <= j < i ==> sub_allocated@[j] == 0,
decreases num_pools - i,
{
sub_capacities.push(0);
sub_allocated.push(0);
i = i + 1;
}
let fb = FederatedBudget { master_capacity, master_allocated: 0, sub_capacities, sub_allocated };
proof {
lemma_sum_zeros(fb.sub_capacities@, fb.sub_capacities@.len() as int);
}
fb
}
pub fn allocate_sub_pool(&mut self, name: usize, amount: u64) -> (ok: bool)
requires old(self).inv(),
ensures
final(self).inv(),
final(self).master_capacity == old(self).master_capacity,
final(self).sub_capacities.len() == old(self).sub_capacities.len(),
ok == (name < old(self).sub_capacities.len()
&& 1 <= amount <= old(self).master_capacity
&& old(self).master_allocated + amount as int <= old(self).master_capacity as int),
ok ==> {
&&& final(self).master_allocated == old(self).master_allocated + amount
&&& final(self).sub_capacities@ ==
old(self).sub_capacities@.update(name as int, (old(self).sub_capacities@[name as int] + amount) as u64)
},
!ok ==> final(self).master_allocated == old(self).master_allocated
&& final(self).sub_capacities@ == old(self).sub_capacities@,
final(self).sub_allocated@ == old(self).sub_allocated@,
{
if name >= self.sub_capacities.len() || amount == 0 || amount > self.master_capacity {
false
} else if amount <= self.master_capacity - self.master_allocated {
proof {
lemma_elem_le_sum(self.sub_capacities@, name as int, self.sub_capacities@.len() as int);
}
let new_cap = self.sub_capacities[name] + amount;
let old_cap = self.sub_capacities[name];
let _ = old_cap;
self.sub_capacities.set(name, new_cap);
self.master_allocated = self.master_allocated + amount;
proof {
lemma_sum_update(old(self).sub_capacities@, name as int, new_cap,
old(self).sub_capacities@.len() as int);
}
true
} else {
false
}
}
pub fn allocate_from_sub_pool(&mut self, name: usize, amount: u64) -> (ok: bool)
requires old(self).inv(),
ensures
final(self).inv(),
final(self).master_capacity == old(self).master_capacity,
final(self).master_allocated == old(self).master_allocated,
final(self).sub_capacities@ == old(self).sub_capacities@,
ok == (name < old(self).sub_allocated.len()
&& 1 <= amount <= old(self).master_capacity
&& old(self).sub_allocated@[name as int] + amount as int
<= old(self).sub_capacities@[name as int] as int),
ok ==> final(self).sub_allocated@ == old(self).sub_allocated@.update(
name as int,
(old(self).sub_allocated@[name as int] + amount) as u64),
!ok ==> final(self).sub_allocated@ == old(self).sub_allocated@,
{
if name >= self.sub_allocated.len() || amount == 0 || amount > self.master_capacity {
false
} else if amount <= self.sub_capacities[name] - self.sub_allocated[name] {
let new_alloc = self.sub_allocated[name] + amount;
self.sub_allocated.set(name, new_alloc);
true
} else {
false
}
}
pub fn release_from_sub_pool(&mut self, name: usize, amount: u64) -> (ok: bool)
requires old(self).inv(),
ensures
final(self).inv(),
final(self).master_capacity == old(self).master_capacity,
final(self).master_allocated == old(self).master_allocated,
final(self).sub_capacities@ == old(self).sub_capacities@,
ok == (name < old(self).sub_allocated.len()
&& 1 <= amount <= old(self).master_capacity
&& amount <= old(self).sub_allocated@[name as int]),
ok ==> final(self).sub_allocated@ == old(self).sub_allocated@.update(
name as int, (old(self).sub_allocated@[name as int] - amount) as u64),
!ok ==> final(self).sub_allocated@ == old(self).sub_allocated@,
{
if name >= self.sub_allocated.len() || amount == 0 || amount > self.master_capacity {
false
} else if amount <= self.sub_allocated[name] {
let new_alloc = self.sub_allocated[name] - amount;
self.sub_allocated.set(name, new_alloc);
true
} else {
false
}
}
}
}