diff --git a/capybaraKV/capybarakv/Cargo.toml b/capybaraKV/capybarakv/Cargo.toml index cfe7e6c6..70bba5a6 100644 --- a/capybaraKV/capybarakv/Cargo.toml +++ b/capybaraKV/capybarakv/Cargo.toml @@ -20,7 +20,7 @@ default = [ "pmem" ] crc64fast = "1.0.0" # Avoid default features since @lopopolo reports that rand is unsound with both the log and thread_rng features rand = { version = "0.10.1", default-features = false, features = [ "thread_rng" ] } -vstd = "0.0.0-2026-04-20-1748" +vstd = "0.0.0-2026-05-06-1803" pmcopy = { path = "../pmcopy" } [target.'cfg(target_family = "unix")'.dependencies] diff --git a/capybaraKV/capybarakv/src/common/util_v.rs b/capybaraKV/capybarakv/src/common/util_v.rs index 9edc72ab..edfd668c 100644 --- a/capybaraKV/capybarakv/src/common/util_v.rs +++ b/capybaraKV/capybarakv/src/common/util_v.rs @@ -152,7 +152,7 @@ pub exec fn clone_pmcopy_vec(v: &Vec) -> (result: Vec) #[inline] pub exec fn extend_vec_u8_from_slice(v: &mut Vec, s: &[u8]) ensures - v@ == old(v)@ + s@, + final(v)@ == old(v)@ + s@, { v.extend_from_slice(s); assert(v@ =~= old(v)@ + s@); diff --git a/capybaraKV/capybarakv/src/journal/commit_v.rs b/capybaraKV/capybarakv/src/journal/commit_v.rs index b1350927..6147869b 100644 --- a/capybaraKV/capybarakv/src/journal/commit_v.rs +++ b/capybaraKV/capybarakv/src/journal/commit_v.rs @@ -90,28 +90,28 @@ where perm_factory.id() == old(self)@.powerpm_id, forall|s1: Seq, s2: Seq| Self::recovery_equivalent_for_app(s1, s2) ==> #[trigger] perm_factory.permits(s1, s2), ensures - self.inv(), - *self == (Self{ - powerpm: self.powerpm, + final(self).inv(), + *final(self) == (Self{ + powerpm: final(self).powerpm, ..*old(self) }), - self.powerpm.constants() == old(self).powerpm.constants(), - self.powerpm.id() == old(self).powerpm.id(), - next_pos == current_pos + self.entries@[current_entry_index as int].space_needed(), - seqs_match_in_range(original_durable_state, self.powerpm@.durable_state, - self.sm.app_area_start as int, self.sm.app_area_end as int), - seqs_match_in_range(original_read_state, self.powerpm@.read_state, - self.sm.app_area_start as int, self.sm.app_area_end as int), - parse_journal_entries(self.powerpm@.read_state.subrange(self.sm.journal_entries_start as int, + final(self).powerpm.constants() == old(self).powerpm.constants(), + final(self).powerpm.id() == old(self).powerpm.id(), + next_pos == current_pos + final(self).entries@[current_entry_index as int].space_needed(), + seqs_match_in_range(original_durable_state, final(self).powerpm@.durable_state, + final(self).sm.app_area_start as int, final(self).sm.app_area_end as int), + seqs_match_in_range(original_read_state, final(self).powerpm@.read_state, + final(self).sm.app_area_start as int, final(self).sm.app_area_end as int), + parse_journal_entries(final(self).powerpm@.read_state.subrange(final(self).sm.journal_entries_start as int, next_pos as int)) - == Some(self.entries@.take(current_entry_index + 1)), - current_pos < next_pos <= self.sm.journal_entries_start + self.journal_length, - next_pos == self.sm.journal_entries_start + - space_needed_for_journal_entries_list(self.entries@.take(current_entry_index + 1)), - next_pos == self.sm.journal_entries_start + self.journal_length - <==> current_entry_index == self.entries@.len() - 1, - crc_digest.bytes_in_digest() == - self.powerpm@.read_state.subrange(self.sm.journal_entries_start as int, next_pos as int), + == Some(final(self).entries@.take(current_entry_index + 1)), + current_pos < next_pos <= final(self).sm.journal_entries_start + final(self).journal_length, + next_pos == final(self).sm.journal_entries_start + + space_needed_for_journal_entries_list(final(self).entries@.take(current_entry_index + 1)), + next_pos == final(self).sm.journal_entries_start + final(self).journal_length + <==> current_entry_index == final(self).entries@.len() - 1, + final(crc_digest).bytes_in_digest() == + final(self).powerpm@.read_state.subrange(final(self).sm.journal_entries_start as int, next_pos as int), { broadcast use axiom_bytes_len; broadcast use group_can_result_from_write_effect; @@ -206,23 +206,23 @@ where perm_factory.id() == old(self)@.powerpm_id, forall|s1: Seq, s2: Seq| Self::recovery_equivalent_for_app(s1, s2) ==> #[trigger] perm_factory.permits(s1, s2), ensures - self.inv(), - *self == (Self{ - powerpm: self.powerpm, + final(self).inv(), + *final(self) == (Self{ + powerpm: final(self).powerpm, ..*old(self) }), - self.powerpm.constants() == old(self).powerpm.constants(), - self.powerpm.id() == old(self).powerpm.id(), - seqs_match_in_range(old(self).powerpm@.durable_state, self.powerpm@.durable_state, - self.sm.app_area_start as int, self.sm.app_area_end as int), - seqs_match_in_range(old(self).powerpm@.read_state, self.powerpm@.read_state, - self.sm.app_area_start as int, self.sm.app_area_end as int), - parse_journal_entries(extract_section(self.powerpm@.read_state, self.sm.journal_entries_start as int, - self.journal_length as nat)) - == Some(self.entries@), + final(self).powerpm.constants() == old(self).powerpm.constants(), + final(self).powerpm.id() == old(self).powerpm.id(), + seqs_match_in_range(old(self).powerpm@.durable_state, final(self).powerpm@.durable_state, + final(self).sm.app_area_start as int, final(self).sm.app_area_end as int), + seqs_match_in_range(old(self).powerpm@.read_state, final(self).powerpm@.read_state, + final(self).sm.app_area_start as int, final(self).sm.app_area_end as int), + parse_journal_entries(extract_section(final(self).powerpm@.read_state, final(self).sm.journal_entries_start as int, + final(self).journal_length as nat)) + == Some(final(self).entries@), journal_entries_crc == - spec_crc_u64(extract_section(self.powerpm@.read_state, self.sm.journal_entries_start as int, - self.journal_length as nat)), + spec_crc_u64(extract_section(final(self).powerpm@.read_state, final(self).sm.journal_entries_start as int, + final(self).journal_length as nat)), { let mut current_entry_index: usize = 0; let mut current_pos = self.sm.journal_entries_start; @@ -287,20 +287,20 @@ where perm_factory.id() == old(self)@.powerpm_id, forall|s1: Seq, s2: Seq| Self::recovery_equivalent_for_app(s1, s2) ==> #[trigger] perm_factory.permits(s1, s2), ensures - self.inv(), - self.powerpm.constants() == old(self).powerpm.constants(), - self.powerpm.id() == old(self).powerpm.id(), - *self == (Self{ - powerpm: self.powerpm, + final(self).inv(), + final(self).powerpm.constants() == old(self).powerpm.constants(), + final(self).powerpm.id() == old(self).powerpm.id(), + *final(self) == (Self{ + powerpm: final(self).powerpm, ..*old(self) }), - self.powerpm@.flush_predicted(), - seqs_match_in_range(old(self).powerpm@.durable_state, self.powerpm@.durable_state, - self.sm.app_area_start as int, self.sm.app_area_end as int), - seqs_match_in_range(old(self).powerpm@.read_state, self.powerpm@.read_state, - self.sm.app_area_start as int, self.sm.app_area_end as int), - recover_journal_length(self.powerpm@.read_state, self.sm) == Some(self.journal_length), - recover_journal_entries(self.powerpm@.read_state, self.sm, self.journal_length) == Some(self.entries@), + final(self).powerpm@.flush_predicted(), + seqs_match_in_range(old(self).powerpm@.durable_state, final(self).powerpm@.durable_state, + final(self).sm.app_area_start as int, final(self).sm.app_area_end as int), + seqs_match_in_range(old(self).powerpm@.read_state, final(self).powerpm@.read_state, + final(self).sm.app_area_start as int, final(self).sm.app_area_end as int), + recover_journal_length(final(self).powerpm@.read_state, final(self).sm) == Some(final(self).journal_length), + recover_journal_entries(final(self).powerpm@.read_state, final(self).sm, final(self).journal_length) == Some(final(self).entries@), { broadcast use group_can_result_from_write_effect; broadcast use pmcopy_axioms; @@ -363,27 +363,27 @@ where ==> #[trigger] perm.permits(s1, s2), recovers_to(original_commit_state, old(self).vm@, old(self).sm, old(self).constants), ensures - self.inv(), - self.powerpm.constants() == old(self).powerpm.constants(), - self.powerpm.id() == old(self).powerpm.id(), - *self == (Self{ + final(self).inv(), + final(self).powerpm.constants() == old(self).powerpm.constants(), + final(self).powerpm.id() == old(self).powerpm.id(), + *final(self) == (Self{ status: Ghost(JournalStatus::Committed), - powerpm: self.powerpm, + powerpm: final(self).powerpm, ..*old(self) }), - seqs_match_in_range(original_durable_state, self.powerpm@.durable_state, - self.sm.app_area_start as int, self.sm.app_area_end as int), - seqs_match_in_range(original_read_state, self.powerpm@.read_state, - self.sm.app_area_start as int, self.sm.app_area_end as int), - self.powerpm@.flush_predicted(), - recover_committed_cdb(self.powerpm@.read_state, self.sm) == Some(true), - recover_journal_length(self.powerpm@.read_state, self.sm) == Some(self.journal_length), - recover_journal_entries(self.powerpm@.read_state, self.sm, self.journal_length) == Some(self.entries@), + seqs_match_in_range(original_durable_state, final(self).powerpm@.durable_state, + final(self).sm.app_area_start as int, final(self).sm.app_area_end as int), + seqs_match_in_range(original_read_state, final(self).powerpm@.read_state, + final(self).sm.app_area_start as int, final(self).sm.app_area_end as int), + final(self).powerpm@.flush_predicted(), + recover_committed_cdb(final(self).powerpm@.read_state, final(self).sm) == Some(true), + recover_journal_length(final(self).powerpm@.read_state, final(self).sm) == Some(final(self).journal_length), + recover_journal_entries(final(self).powerpm@.read_state, final(self).sm, final(self).journal_length) == Some(final(self).entries@), ({ - &&& recover_journal(self.powerpm@.read_state) matches Some(j) - &&& j.constants == self.constants - &&& seqs_match_in_range(j.state, original_commit_state, self.sm.app_area_start as int, - self.sm.app_area_end as int) + &&& recover_journal(final(self).powerpm@.read_state) matches Some(j) + &&& j.constants == final(self).constants + &&& seqs_match_in_range(j.state, original_commit_state, final(self).sm.app_area_start as int, + final(self).sm.app_area_end as int) }), perm.completed(result@), { @@ -470,26 +470,26 @@ where perm_factory.id() == old(self)@.powerpm_id, forall|s1: Seq, s2: Seq| Self::recovery_equivalent_for_app(s1, s2) ==> #[trigger] perm_factory.permits(s1, s2), ensures - self.inv(), - *self == (Self{ - powerpm: self.powerpm, + final(self).inv(), + *final(self) == (Self{ + powerpm: final(self).powerpm, ..*old(self) }), - self.powerpm.constants() == old(self).powerpm.constants(), - self.powerpm.id() == old(self).powerpm.id(), - journal_entries_valid(self.entries@, self.sm), - apply_journal_entries(original_read_state, self.entries@, self.sm) is Some, - recover_version_metadata(self.powerpm@.durable_state) == Some(self.vm@), - recover_static_metadata(self.powerpm@.durable_state, self.vm@) == Some(self.sm), - recover_committed_cdb(self.powerpm@.durable_state, self.sm) == Some(true), - recover_journal_length(self.powerpm@.durable_state, self.sm) == Some(self.journal_length), - recover_journal_entries(self.powerpm@.durable_state, self.sm, self.journal_length) == Some(self.entries@), - recover_journal(self.powerpm@.durable_state) == recover_journal(original_read_state), - seqs_match_except_in_range(original_read_state, self.powerpm@.durable_state, - self.sm.app_area_start as int, self.sm.app_area_end as int), - seqs_match_except_in_range(original_read_state, self.powerpm@.read_state, - self.sm.app_area_start as int, self.sm.app_area_end as int), - apply_journal_entries(self.powerpm@.read_state, self.entries@.skip(num_entries_installed + 1), self.sm) + final(self).powerpm.constants() == old(self).powerpm.constants(), + final(self).powerpm.id() == old(self).powerpm.id(), + journal_entries_valid(final(self).entries@, final(self).sm), + apply_journal_entries(original_read_state, final(self).entries@, final(self).sm) is Some, + recover_version_metadata(final(self).powerpm@.durable_state) == Some(final(self).vm@), + recover_static_metadata(final(self).powerpm@.durable_state, final(self).vm@) == Some(final(self).sm), + recover_committed_cdb(final(self).powerpm@.durable_state, final(self).sm) == Some(true), + recover_journal_length(final(self).powerpm@.durable_state, final(self).sm) == Some(final(self).journal_length), + recover_journal_entries(final(self).powerpm@.durable_state, final(self).sm, final(self).journal_length) == Some(final(self).entries@), + recover_journal(final(self).powerpm@.durable_state) == recover_journal(original_read_state), + seqs_match_except_in_range(original_read_state, final(self).powerpm@.durable_state, + final(self).sm.app_area_start as int, final(self).sm.app_area_end as int), + seqs_match_except_in_range(original_read_state, final(self).powerpm@.read_state, + final(self).sm.app_area_start as int, final(self).sm.app_area_end as int), + apply_journal_entries(final(self).powerpm@.read_state, final(self).entries@.skip(num_entries_installed + 1), final(self).sm) == Some(desired_commit_state), { broadcast use group_can_result_from_write_effect; @@ -562,20 +562,20 @@ where perm_factory.id() == old(self)@.powerpm_id, forall|s1: Seq, s2: Seq| Self::recovery_equivalent_for_app(s1, s2) ==> #[trigger] perm_factory.permits(s1, s2), ensures - self.inv(), - *self == (Self{ - powerpm: self.powerpm, + final(self).inv(), + *final(self) == (Self{ + powerpm: final(self).powerpm, ..*old(self) }), - self.powerpm.constants() == old(self).powerpm.constants(), - self.powerpm.id() == old(self).powerpm.id(), - self.powerpm@.flush_predicted(), - seqs_match_in_range(self.powerpm@.read_state, original_commit_state, self.sm.app_area_start as int, - self.sm.app_area_end as int), + final(self).powerpm.constants() == old(self).powerpm.constants(), + final(self).powerpm.id() == old(self).powerpm.id(), + final(self).powerpm@.flush_predicted(), + seqs_match_in_range(final(self).powerpm@.read_state, original_commit_state, final(self).sm.app_area_start as int, + final(self).sm.app_area_end as int), ({ - &&& recover_journal(self.powerpm@.read_state) matches Some(j) - &&& j.constants == self.constants - &&& j.state == self.powerpm@.read_state + &&& recover_journal(final(self).powerpm@.read_state) matches Some(j) + &&& j.constants == final(self).constants + &&& j.state == final(self).powerpm@.read_state }), { let mut num_entries_installed: usize = 0; @@ -688,10 +688,10 @@ where perm_factory.id() == old(self)@.powerpm_id, forall|s1: Seq, s2: Seq| Self::recovery_equivalent_for_app(s1, s2) ==> #[trigger] perm_factory.permits(s1, s2), ensures - self.valid(), - self@.valid(), - self.recover_idempotent(), - self@.committed_from(old(self)@), + final(self).valid(), + final(self)@.valid(), + final(self).recover_idempotent(), + final(self)@.committed_from(old(self)@), perm.completed(result@), { proof { diff --git a/capybaraKV/capybarakv/src/journal/entry_v.rs b/capybaraKV/capybarakv/src/journal/entry_v.rs index d1fb54db..0fbca152 100644 --- a/capybaraKV/capybarakv/src/journal/entry_v.rs +++ b/capybaraKV/capybarakv/src/journal/entry_v.rs @@ -200,7 +200,7 @@ impl ConcreteJournalEntries #[inline] pub exec fn push(&mut self, e: ConcreteJournalEntry) ensures - self@ == old(self)@.push(e@) + final(self)@ == old(self)@.push(e@) { self.entries.push(e); assert(self@ =~= old(self)@.push(e@)); @@ -217,7 +217,7 @@ impl ConcreteJournalEntries #[inline] pub exec fn clear(&mut self) ensures - self@ == Seq::::empty(), + final(self)@ == Seq::::empty(), { self.entries.clear(); assert(self@ =~= Seq::::empty()); diff --git a/capybaraKV/capybarakv/src/journal/impl_v.rs b/capybaraKV/capybarakv/src/journal/impl_v.rs index 6dc07492..514bcac2 100644 --- a/capybaraKV/capybarakv/src/journal/impl_v.rs +++ b/capybaraKV/capybarakv/src/journal/impl_v.rs @@ -1,14 +1,15 @@ #![allow(unused_imports)] -use vstd::prelude::*; -use vstd::tokens::frac::*; -use crate::common::subrange_v::*; -use crate::pmem::pmcopy_t::*; -use crate::pmem::pmemspec_t::*; -use crate::pmem::power_t::*; use super::entry_v::*; use super::inv_v::*; use super::recover_v::*; use super::spec_v::*; +use crate::common::subrange_v::*; +use crate::pmem::pmcopy_t::*; +use crate::pmem::pmemspec_t::*; +use crate::pmem::power_t::*; +use vstd::prelude::*; +use vstd::resource::frac::*; +use vstd::resource::ghost_var::*; verus! { @@ -142,10 +143,10 @@ where requires old(self).valid(), ensures - self.valid(), - self@.valid(), - self.recover_idempotent(), - self@ == old(self)@.abort(), + final(self).valid(), + final(self)@.valid(), + final(self).recover_idempotent(), + final(self)@ == old(self)@.abort(), { self.journal_length = 0; self.journaled_addrs = Ghost(Set::::empty()); @@ -156,9 +157,9 @@ where requires old(self).valid(), ensures - self.valid(), - self@ == old(self)@, - self@.durable_state == self@.read_state, + final(self).valid(), + final(self)@ == old(self)@, + final(self)@.durable_state == final(self)@.read_state, { self.powerpm.flush(); } @@ -170,4 +171,3 @@ pub open(super) spec fn spec_journal_entry_overhead() -> nat } } - diff --git a/capybaraKV/capybarakv/src/journal/setup_v.rs b/capybaraKV/capybarakv/src/journal/setup_v.rs index 19a7a7c9..2bb771aa 100644 --- a/capybaraKV/capybarakv/src/journal/setup_v.rs +++ b/capybaraKV/capybarakv/src/journal/setup_v.rs @@ -203,27 +203,27 @@ where requires old(pm).inv(), ensures - pm.inv(), - pm.constants() == old(pm).constants(), + final(pm).inv(), + final(pm).constants() == old(pm).constants(), match result { Ok(constants) => { - &&& pm@.flush_predicted() - &&& Self::recover(pm@.durable_state) - == Some(RecoveredJournal{ constants: *jc, state: pm@.durable_state }) + &&& final(pm)@.flush_predicted() + &&& Self::recover(final(pm)@.durable_state) + == Some(RecoveredJournal{ constants: *jc, state: final(pm)@.durable_state }) &&& jc.app_area_start <= jc.app_area_end - &&& jc.app_area_end == pm@.len() - &&& seqs_match_in_range(old(pm)@.read_state, pm@.read_state, jc.app_area_start as int, + &&& jc.app_area_end == final(pm)@.len() + &&& seqs_match_in_range(old(pm)@.read_state, final(pm)@.read_state, jc.app_area_start as int, jc.app_area_end as int) }, Err(JournalError::InvalidSetupParameters) => { - &&& pm@ == old(pm)@ + &&& final(pm)@ == old(pm)@ &&& { ||| jc.app_area_start > jc.app_area_end - ||| jc.app_area_end != pm@.len() + ||| jc.app_area_end != final(pm)@.len() } }, Err(JournalError::NotEnoughSpace) => { - &&& pm@ == old(pm)@ + &&& final(pm)@ == old(pm)@ &&& jc.app_area_start < Self::spec_space_needed_for_setup(jc.journal_capacity as nat) }, Err(_) => false, diff --git a/capybaraKV/capybarakv/src/journal/spec_v.rs b/capybaraKV/capybarakv/src/journal/spec_v.rs index a07bf9cc..da33965f 100644 --- a/capybaraKV/capybarakv/src/journal/spec_v.rs +++ b/capybaraKV/capybarakv/src/journal/spec_v.rs @@ -1,6 +1,7 @@ -use vstd::prelude::*; -use crate::pmem::pmemspec_t::*; use crate::common::subrange_v::*; +use crate::pmem::pmemspec_t::*; +use vstd::prelude::*; +use vstd::resource::Loc; verus! { @@ -42,7 +43,7 @@ pub struct JournalView { pub commit_state: Seq, pub remaining_capacity: int, pub journaled_addrs: Set, - pub powerpm_id: int, + pub powerpm_id: Loc, } impl JournalView { diff --git a/capybaraKV/capybarakv/src/journal/start_v.rs b/capybaraKV/capybarakv/src/journal/start_v.rs index f4c8f31f..fa1bde5a 100644 --- a/capybaraKV/capybarakv/src/journal/start_v.rs +++ b/capybaraKV/capybarakv/src/journal/start_v.rs @@ -174,15 +174,15 @@ where forall|s1: Seq, s2: Seq| Self::recovery_equivalent_for_app(s1, s2) ==> #[trigger] perm_factory.permits(s1, s2), ensures - powerpm.inv(), - powerpm.constants() == old(powerpm).constants(), - powerpm.id() == old(powerpm).id(), - powerpm@.len() == old(powerpm)@.len(), - powerpm@.valid(), - recover_journal(powerpm@.durable_state) == recover_journal(old(powerpm)@.durable_state), - apply_journal_entries(powerpm@.read_state, entries.skip(num_entries_installed + 1), *sm) == Some(commit_state), - seqs_match_in_range(old(powerpm)@.durable_state, powerpm@.durable_state, 0, sm.app_area_start as int), - seqs_match_in_range(old(powerpm)@.read_state, powerpm@.read_state, 0, sm.app_area_start as int), + final(powerpm).inv(), + final(powerpm).constants() == old(powerpm).constants(), + final(powerpm).id() == old(powerpm).id(), + final(powerpm)@.len() == old(powerpm)@.len(), + final(powerpm)@.valid(), + recover_journal(final(powerpm)@.durable_state) == recover_journal(old(powerpm)@.durable_state), + apply_journal_entries(final(powerpm)@.read_state, entries.skip(num_entries_installed + 1), *sm) == Some(commit_state), + seqs_match_in_range(old(powerpm)@.durable_state, final(powerpm)@.durable_state, 0, sm.app_area_start as int), + seqs_match_in_range(old(powerpm)@.read_state, final(powerpm)@.read_state, 0, sm.app_area_start as int), { proof { lemma_addresses_in_entry_dont_affect_recovery(powerpm@.durable_state, vm, *sm, @@ -249,19 +249,19 @@ where forall|s1: Seq, s2: Seq| Self::recovery_equivalent_for_app(s1, s2) ==> #[trigger] perm_factory.permits(s1, s2), ensures - powerpm.inv(), - powerpm.constants() == old(powerpm).constants(), - powerpm.id() == old(powerpm).id(), - powerpm@.len() == old(powerpm)@.len(), - powerpm@.flush_predicted(), - recover_version_metadata(powerpm@.read_state) == Some(vm), - recover_static_metadata(powerpm@.read_state, vm) == Some(*sm), - recover_committed_cdb(powerpm@.read_state, *sm) == Some(true), - recover_journal_length(powerpm@.read_state, *sm) == Some(entries_bytes.len() as u64), - recover_journal_entries_bytes(powerpm@.read_state, *sm, entries_bytes.len() as u64) == Some(entries_bytes@), - apply_journal_entries(powerpm@.durable_state, entries, *sm) == Some(powerpm@.read_state), - apply_journal_entries(old(powerpm)@.read_state, entries, *sm) == Some(powerpm@.read_state), - recover_journal(powerpm@.durable_state) == recover_journal(old(powerpm)@.durable_state), + final(powerpm).inv(), + final(powerpm).constants() == old(powerpm).constants(), + final(powerpm).id() == old(powerpm).id(), + final(powerpm)@.len() == old(powerpm)@.len(), + final(powerpm)@.flush_predicted(), + recover_version_metadata(final(powerpm)@.read_state) == Some(vm), + recover_static_metadata(final(powerpm)@.read_state, vm) == Some(*sm), + recover_committed_cdb(final(powerpm)@.read_state, *sm) == Some(true), + recover_journal_length(final(powerpm)@.read_state, *sm) == Some(entries_bytes.len() as u64), + recover_journal_entries_bytes(final(powerpm)@.read_state, *sm, entries_bytes.len() as u64) == Some(entries_bytes@), + apply_journal_entries(final(powerpm)@.durable_state, entries, *sm) == Some(final(powerpm)@.read_state), + apply_journal_entries(old(powerpm)@.read_state, entries, *sm) == Some(final(powerpm)@.read_state), + recover_journal(final(powerpm)@.durable_state) == recover_journal(old(powerpm)@.durable_state), { let mut start: usize = 0; let end: usize = entries_bytes.len(); @@ -386,15 +386,15 @@ where forall|s1: Seq, s2: Seq| spec_recovery_equivalent_for_app(s1, s2) ==> #[trigger] perm_factory.permits(s1, s2), ensures - powerpm.inv(), - powerpm.constants() == old(powerpm).constants(), - powerpm.id() == old(powerpm).id(), - powerpm@.len() == old(powerpm)@.len(), - powerpm@.flush_predicted(), - recover_version_metadata(powerpm@.read_state) == Some(vm), - recover_static_metadata(powerpm@.read_state, vm) == Some(*sm), - recover_committed_cdb(powerpm@.read_state, *sm) == Some(false), - spec_recovery_equivalent_for_app(powerpm@.durable_state, old(powerpm)@.durable_state), + final(powerpm).inv(), + final(powerpm).constants() == old(powerpm).constants(), + final(powerpm).id() == old(powerpm).id(), + final(powerpm)@.len() == old(powerpm)@.len(), + final(powerpm)@.flush_predicted(), + recover_version_metadata(final(powerpm)@.read_state) == Some(vm), + recover_static_metadata(final(powerpm)@.read_state, vm) == Some(*sm), + recover_committed_cdb(final(powerpm)@.read_state, *sm) == Some(false), + spec_recovery_equivalent_for_app(final(powerpm)@.durable_state, old(powerpm)@.durable_state), { let new_cdb: u64 = CDB_FALSE; let ghost new_state = update_bytes(powerpm@.durable_state, sm.committed_cdb_start as int, diff --git a/capybaraKV/capybarakv/src/journal/write_v.rs b/capybaraKV/capybarakv/src/journal/write_v.rs index 5a4d6b8b..1dec5554 100644 --- a/capybaraKV/capybarakv/src/journal/write_v.rs +++ b/capybaraKV/capybarakv/src/journal/write_v.rs @@ -59,7 +59,7 @@ where requires old(self).write_preconditions(addr, bytes_to_write@, perm), ensures - self.write_postconditions(*old(self), addr, bytes_to_write@), + final(self).write_postconditions(*old(self), addr, bytes_to_write@), { broadcast use broadcast_seqs_match_in_range_can_narrow_range; broadcast use broadcast_update_bytes_effect; @@ -102,7 +102,7 @@ where requires old(self).write_preconditions(addr, bytes_to_write@, perm), ensures - self.write_postconditions(*old(self), addr, bytes_to_write@), + final(self).write_postconditions(*old(self), addr, bytes_to_write@), { self.write_slice::(addr, bytes_to_write.as_slice(), Tracked(perm)) } @@ -120,7 +120,7 @@ where requires old(self).write_preconditions(addr, object.spec_to_bytes(), perm), ensures - self.write_postconditions(*old(self), addr, object.spec_to_bytes()), + final(self).write_postconditions(*old(self), addr, object.spec_to_bytes()), { broadcast use pmcopy_axioms; self.write_slice::(addr, object.as_byte_slice(), Tracked(perm)) @@ -136,26 +136,26 @@ where old(self)@.constants.app_area_start <= addr, addr + bytes_to_write.len() <= old(self)@.constants.app_area_end, ensures - self.valid(), - self@.valid(), - self.recover_idempotent(), + final(self).valid(), + final(self)@.valid(), + final(self).recover_idempotent(), ({ let space_needed = spec_journal_entry_overhead() + bytes_to_write@.len(); match result { Ok(_) => { &&& space_needed <= old(self)@.remaining_capacity - &&& self@ == (JournalView{ + &&& final(self)@ == (JournalView{ commit_state: update_bytes(old(self)@.commit_state, addr as int, bytes_to_write@), journaled_addrs: old(self)@.journaled_addrs + Set::::new(|i: int| addr <= i < addr + bytes_to_write.len()), remaining_capacity: old(self)@.remaining_capacity - space_needed, ..old(self)@ }) - &&& self@.matches_except_in_range(old(self)@, addr as int, addr + bytes_to_write.len()) + &&& final(self)@.matches_except_in_range(old(self)@, addr as int, addr + bytes_to_write.len()) }, Err(JournalError::NotEnoughSpace) => { &&& space_needed > old(self)@.remaining_capacity - &&& *self == *old(self) + &&& *final(self) == *old(self) }, Err(_) => false, } diff --git a/capybaraKV/capybarakv/src/kv2/abort_v.rs b/capybaraKV/capybarakv/src/kv2/abort_v.rs index 06fb8fd5..e2071014 100644 --- a/capybaraKV/capybarakv/src/kv2/abort_v.rs +++ b/capybaraKV/capybarakv/src/kv2/abort_v.rs @@ -22,9 +22,9 @@ where requires old(self).valid(), ensures - self.valid(), + final(self).valid(), match result { - Ok(()) => self@ == old(self)@.abort(), + Ok(()) => final(self)@ == old(self)@.abort(), Err(_) => false, } { @@ -39,9 +39,9 @@ where old(self).inv(), old(self).status@ is MustAbort, ensures - self.valid(), - self@ == old(self)@.abort(), - self.journal@.durable_state == self.journal@.read_state, + final(self).valid(), + final(self)@ == old(self)@.abort(), + final(self).journal@.durable_state == final(self).journal@.read_state, { let ghost jv_before_abort = self.journal@; self.journal.abort(); diff --git a/capybaraKV/capybarakv/src/kv2/commit_v.rs b/capybaraKV/capybarakv/src/kv2/commit_v.rs index 92f85f9f..d548f894 100644 --- a/capybaraKV/capybarakv/src/kv2/commit_v.rs +++ b/capybaraKV/capybarakv/src/kv2/commit_v.rs @@ -1,15 +1,16 @@ #![allow(unused_imports)] use vstd::prelude::*; -use vstd::tokens::frac::*; +use vstd::resource::frac::*; +use vstd::resource::ghost_var::*; +use super::impl_v::*; +use super::recover_v::*; +use super::spec_t::*; use crate::common::subrange_v::*; -use crate::pmem::pmemspec_t::*; use crate::pmem::pmcopy_t::*; +use crate::pmem::pmemspec_t::*; use crate::pmem::power_t::*; use std::hash::Hash; -use super::impl_v::*; -use super::recover_v::*; -use super::spec_t::*; verus! { @@ -22,12 +23,12 @@ where L: PmCopy + LogicalRange + std::fmt::Debug + Copy, { pub exec fn commit( - &mut self, + &mut self, Tracked(perm): Tracked ) -> (result: Result, KvError>) where Perm: CheckPermission>, - requires + requires old(self).valid(), perm.id() == old(self)@.powerpm_id, forall|s1: Seq, s2: Seq| ({ @@ -37,11 +38,11 @@ where &&& Self::recover(s1) == Some(RecoveredKvStore::{ ps: old(self)@.ps, kv: old(self)@.durable }) &&& Self::recover(s2) == Some(RecoveredKvStore::{ ps: old(self)@.ps, kv: old(self)@.durable }) }) ==> #[trigger] perm.permits(s1, s2), - ensures - self.valid(), + ensures + final(self).valid(), match result { Ok(complete) => { - &&& self@ == old(self)@.commit() + &&& final(self)@ == old(self)@.commit() &&& perm.completed(complete@) }, Err(_) => false, diff --git a/capybaraKV/capybarakv/src/kv2/concurrentspec_t.rs b/capybaraKV/capybarakv/src/kv2/concurrentspec_t.rs index bd6f4b0c..caabafcc 100644 --- a/capybaraKV/capybarakv/src/kv2/concurrentspec_t.rs +++ b/capybaraKV/capybarakv/src/kv2/concurrentspec_t.rs @@ -2,12 +2,14 @@ #![cfg_attr(verus_keep_ghost, verus::trusted)] use vstd::prelude::*; +use super::spec_t::*; use crate::pmem::pmcopy_t::*; use crate::pmem::pmemspec_t::*; use crate::pmem::traits_t::*; use std::hash::Hash; -use super::spec_t::*; -use vstd::tokens::frac::*; +use vstd::resource::frac::*; +use vstd::resource::ghost_var::*; +use vstd::resource::Loc; verus! { @@ -70,9 +72,9 @@ pub trait ReadLinearizer> : Sized spec fn namespaces(self) -> Set; - spec fn pre(self, id: int, op: Op) -> bool; + spec fn pre(self, id: Loc, op: Op) -> bool; - spec fn post(self, apply: Self::Completion, id: int, op: Op, result: Result) -> bool; + spec fn post(self, apply: Self::Completion, id: Loc, op: Op, result: Result) -> bool; proof fn apply( tracked self, @@ -140,9 +142,9 @@ pub trait MutatingLinearizer> : Sized spec fn namespaces(self) -> Set; - spec fn pre(self, id: int, op: Op) -> bool; + spec fn pre(self, id: Loc, op: Op) -> bool; - spec fn post(self, complete: Self::Completion, id: int, op: Op, exec_result: Result) -> bool; + spec fn post(self, complete: Self::Completion, id: Loc, op: Op, exec_result: Result) -> bool; proof fn apply( tracked self, @@ -155,9 +157,9 @@ pub trait MutatingLinearizer> : Sized self.pre(old(r).id(), op), op.result_valid(old(r)@, new_ckv, exec_result), ensures - r.id() == old(r).id(), - r@ == new_ckv, - self.post(complete, r.id(), op, exec_result), + final(r).id() == old(r).id(), + final(r)@ == new_ckv, + self.post(complete, final(r).id(), op, exec_result), opens_invariants self.namespaces() ; } @@ -236,7 +238,7 @@ where Err(KvError::CRCMismatch) => { &&& new_ckv == old_ckv &&& !old_ckv.pm_constants.impervious_to_corruption() - }, + }, Err(KvError::OutOfSpace) => { &&& new_ckv == old_ckv &&& STRICT_SPACE ==> old_ckv.kv.num_keys() >= old_ckv.ps.max_keys @@ -304,7 +306,7 @@ where Err(KvError::CRCMismatch) => { &&& new_ckv == old_ckv &&& !old_ckv.pm_constants.impervious_to_corruption() - }, + }, Err(KvError::OutOfSpace) => { &&& new_ckv == old_ckv &&& STRICT_SPACE ==> old_ckv.kv.num_keys() >= old_ckv.ps.max_keys @@ -370,7 +372,7 @@ where Err(KvError::CRCMismatch) => { &&& new_ckv == old_ckv &&& !old_ckv.pm_constants.impervious_to_corruption() - }, + }, Err(e) => { &&& new_ckv == old_ckv &&& old_ckv.kv.delete(self.key) matches Err(e_spec) @@ -583,7 +585,7 @@ where Err(KvError::CRCMismatch) => { &&& new_ckv == old_ckv &&& !old_ckv.pm_constants.impervious_to_corruption() - }, + }, Err(KvError::OutOfSpace) => { &&& new_ckv == old_ckv &&& STRICT_SPACE ==> { @@ -657,7 +659,7 @@ where Err(KvError::CRCMismatch) => { &&& new_ckv == old_ckv &&& !old_ckv.pm_constants.impervious_to_corruption() - }, + }, Err(KvError::OutOfSpace) => { &&& new_ckv == old_ckv &&& STRICT_SPACE ==> { @@ -731,7 +733,7 @@ where Err(KvError::CRCMismatch) => { &&& new_ckv == old_ckv &&& !old_ckv.pm_constants.impervious_to_corruption() - }, + }, Err(KvError::OutOfSpace) => { &&& new_ckv == old_ckv &&& STRICT_SPACE ==> { @@ -806,7 +808,7 @@ where Err(KvError::CRCMismatch) => { &&& new_ckv == old_ckv &&& !old_ckv.pm_constants.impervious_to_corruption() - }, + }, Err(KvError::OutOfSpace) => { &&& new_ckv == old_ckv &&& STRICT_SPACE ==> { @@ -877,7 +879,7 @@ where Err(KvError::CRCMismatch) => { &&& new_ckv == old_ckv &&& !old_ckv.pm_constants.impervious_to_corruption() - }, + }, Err(KvError::OutOfSpace) => { &&& new_ckv == old_ckv &&& STRICT_SPACE ==> old_ckv.kv.num_keys() >= old_ckv.ps.max_keys @@ -946,7 +948,7 @@ where Err(KvError::CRCMismatch) => { &&& new_ckv == old_ckv &&& !old_ckv.pm_constants.impervious_to_corruption() - }, + }, Err(KvError::OutOfSpace) => { &&& new_ckv == old_ckv &&& STRICT_SPACE ==> old_ckv.kv.num_keys() >= old_ckv.ps.max_keys diff --git a/capybaraKV/capybarakv/src/kv2/crud_v.rs b/capybaraKV/capybarakv/src/kv2/crud_v.rs index 8edb86bc..dd9b735b 100644 --- a/capybaraKV/capybarakv/src/kv2/crud_v.rs +++ b/capybaraKV/capybarakv/src/kv2/crud_v.rs @@ -63,31 +63,31 @@ where requires old(self).valid(), ensures - self.valid(), + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_key_slots: old(self)@.used_key_slots + 1, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, ..old(self)@ } &&& old(self)@.tentative.create(*key, *item) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self }, Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& { ||| old(self)@.used_key_slots >= old(self)@.ps.max_keys ||| old(self)@.used_transaction_operation_slots >= old(self)@.ps.max_operations_per_transaction } } Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.create(*key, *item) matches Err(e_spec) &&& e == e_spec }, @@ -156,27 +156,27 @@ where requires old(self).valid(), ensures - self.valid(), + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, ..old(self)@ } &&& old(self)@.tentative.delete(*key) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self }, Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& old(self)@.used_transaction_operation_slots >= old(self)@.ps.max_operations_per_transaction } Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.delete(*key) matches Err(e_spec) &&& e == e_spec }, @@ -267,31 +267,31 @@ where requires old(self).valid(), ensures - self.valid(), + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_key_slots: old(self)@.used_key_slots + 1, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, ..old(self)@ } &&& old(self)@.tentative.update_item(*key, *new_item) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self } Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& { ||| old(self)@.used_key_slots >= old(self)@.ps.max_keys ||| old(self)@.used_transaction_operation_slots >= old(self)@.ps.max_operations_per_transaction } }, Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.update_item(*key, *new_item) matches Err(e_spec) &&& e_spec == e }, diff --git a/capybaraKV/capybarakv/src/kv2/elements_v.rs b/capybaraKV/capybarakv/src/kv2/elements_v.rs index dcd45a53..c62fd493 100644 --- a/capybaraKV/capybarakv/src/kv2/elements_v.rs +++ b/capybaraKV/capybarakv/src/kv2/elements_v.rs @@ -153,46 +153,46 @@ where Ok(list_addr) => { let old_list = if former_rm.list_addr == 0 { Seq::::empty() } else { old(self).lists@.tentative.unwrap().m[former_rm.list_addr] }; - &&& *self == Self{ status: Ghost(KvStoreStatus::ComponentsDontCorrespond), - journal: self.journal, lists: self.lists, ..*old(self) } - &&& self.inv() + &&& *final(self) == Self{ status: Ghost(KvStoreStatus::ComponentsDontCorrespond), + journal: final(self).journal, lists: final(self).lists, ..*old(self) } + &&& final(self).inv() &&& list_addr != 0 &&& list_addr == former_rm.list_addr || !old(self).lists@.tentative.unwrap().m.contains_key(list_addr) - &&& self.lists.validate_list_addr(list_addr) - &&& self.lists@ == ListTableView { - tentative: self.lists@.tentative, - used_slots: self.lists@.used_slots, + &&& final(self).lists.validate_list_addr(list_addr) + &&& final(self).lists@ == ListTableView { + tentative: final(self).lists@.tentative, + used_slots: final(self).lists@.used_slots, ..old(self).lists@ } - &&& self.lists@.used_slots <= old(self).lists@.used_slots + 1 - &&& self.lists@.tentative is Some - &&& self.lists@.tentative.unwrap() == if former_rm.list_addr == 0 { + &&& final(self).lists@.used_slots <= old(self).lists@.used_slots + 1 + &&& final(self).lists@.tentative is Some + &&& final(self).lists@.tentative.unwrap() == if former_rm.list_addr == 0 { old(self).lists@.tentative.unwrap().create_singleton(list_addr, new_list_element) } else { old(self).lists@.tentative.unwrap().append(former_rm.list_addr, list_addr, new_list_element) } &&& old_list.len() < usize::MAX - &&& match self.lists@.logical_range_gaps_policy { + &&& match final(self).lists@.logical_range_gaps_policy { LogicalRangeGapsPolicy::LogicalRangeGapsForbidden => new_list_element.start() == end_of_range(old_list), LogicalRangeGapsPolicy::LogicalRangeGapsPermitted => new_list_element.start() >= end_of_range(old_list), } - &&& self.journal@.matches_except_in_range(old(self).journal@, self.lists@.sm.start() as int, - self.lists@.sm.end() as int) - &&& self.journal@.remaining_capacity >= old(self).journal@.remaining_capacity - + &&& final(self).journal@.matches_except_in_range(old(self).journal@, final(self).lists@.sm.start() as int, + final(self).lists@.sm.end() as int) + &&& final(self).journal@.remaining_capacity >= old(self).journal@.remaining_capacity - spec_journal_entry_overhead() - u64::spec_size_of() - u64::spec_size_of() - &&& self.journal@.powerpm_id == old(self).journal@.powerpm_id + &&& final(self).journal@.powerpm_id == old(self).journal@.powerpm_id }, Err(KvError::CRCMismatch) => { - &&& self.valid() - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() + &&& final(self).valid() + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() }, Err(KvError::OutOfSpace) => { - &&& self.valid() - &&& self@ == old(self)@.abort() + &&& final(self).valid() + &&& final(self)@ == old(self)@.abort() &&& { ||| old(self)@.used_key_slots >= old(self)@.ps.max_keys ||| old(self)@.used_list_element_slots >= old(self)@.ps.max_list_elements @@ -200,8 +200,8 @@ where } }, Err(e) => { - &&& self.valid() - &&& self@ == old(self)@ + &&& final(self).valid() + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.append_to_list(*key, new_list_element) matches Err(e_spec) &&& e == e_spec }, @@ -266,25 +266,25 @@ where requires old(self).valid(), ensures - self.valid(), + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_key_slots: old(self)@.used_key_slots + 1, used_list_element_slots: old(self)@.used_list_element_slots + 1, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, ..old(self)@ } &&& old(self)@.tentative.append_to_list(*key, new_list_element) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self }, Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& { ||| old(self)@.used_key_slots >= old(self)@.ps.max_keys ||| old(self)@.used_list_element_slots >= old(self)@.ps.max_list_elements @@ -292,7 +292,7 @@ where } }, Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.append_to_list(*key, new_list_element) matches Err(e_spec) &&& e == e_spec }, @@ -356,11 +356,11 @@ where requires old(self).valid(), ensures - self.valid(), + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_key_slots: old(self)@.used_key_slots + 1, used_list_element_slots: old(self)@.used_list_element_slots + 1, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, @@ -368,14 +368,14 @@ where } &&& old(self)@.tentative.append_to_list_and_update_item(*key, new_list_element, *new_item) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self }, Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& { ||| old(self)@.used_key_slots >= old(self)@.ps.max_keys ||| old(self)@.used_list_element_slots >= old(self)@.ps.max_list_elements @@ -383,7 +383,7 @@ where } }, Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.append_to_list_and_update_item(*key, new_list_element, *new_item) matches Err(e_spec) &&& e == e_spec @@ -469,11 +469,11 @@ where requires old(self).valid(), ensures - self.valid(), + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_key_slots: old(self)@.used_key_slots + 1, used_list_element_slots: old(self)@.used_list_element_slots + 1, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, @@ -481,14 +481,14 @@ where } &&& old(self)@.tentative.update_list_element_at_index(*key, idx as nat, new_list_element) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self }, Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& { ||| old(self)@.used_key_slots >= old(self)@.ps.max_keys ||| old(self)@.used_list_element_slots >= old(self)@.ps.max_list_elements @@ -496,7 +496,7 @@ where } }, Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.update_list_element_at_index(*key, idx as nat, new_list_element) matches Err(e_spec) &&& e == e_spec @@ -595,11 +595,11 @@ where requires old(self).valid(), ensures - self.valid(), + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_key_slots: old(self)@.used_key_slots + 1, used_list_element_slots: old(self)@.used_list_element_slots + 1, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, @@ -607,14 +607,14 @@ where } &&& old(self)@.tentative.update_list_element_at_index_and_item(*key, idx as nat, new_list_element, *new_item) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self }, Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& { ||| old(self)@.used_key_slots >= old(self)@.ps.max_keys ||| old(self)@.used_list_element_slots >= old(self)@.ps.max_list_elements @@ -622,7 +622,7 @@ where } }, Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.update_list_element_at_index_and_item(*key, idx as nat, new_list_element, *new_item) matches Err(e_spec) &&& e == e_spec @@ -740,31 +740,31 @@ where requires old(self).valid(), ensures - self.valid(), + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_key_slots: old(self)@.used_key_slots + 1, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, ..old(self)@ } &&& old(self)@.tentative.trim_list(*key, trim_length as nat) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self }, Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& { ||| old(self)@.used_key_slots >= old(self)@.ps.max_keys ||| old(self)@.used_transaction_operation_slots >= old(self)@.ps.max_operations_per_transaction } }, Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.trim_list(*key, trim_length as nat) matches Err(e_spec) &&& e == e_spec }, @@ -869,32 +869,32 @@ where requires old(self).valid(), ensures - self.valid(), + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_key_slots: old(self)@.used_key_slots + 1, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, ..old(self)@ } &&& old(self)@.tentative.trim_list_and_update_item(*key, trim_length as nat, *new_item) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self }, Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& { ||| old(self)@.used_key_slots >= old(self)@.ps.max_keys ||| old(self)@.used_transaction_operation_slots >= old(self)@.ps.max_operations_per_transaction } }, Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.trim_list_and_update_item(*key, trim_length as nat, *new_item) matches Err(e_spec) &&& e == e_spec diff --git a/capybaraKV/capybarakv/src/kv2/impl_t.rs b/capybaraKV/capybarakv/src/kv2/impl_t.rs index f2547127..88326787 100644 --- a/capybaraKV/capybarakv/src/kv2/impl_t.rs +++ b/capybaraKV/capybarakv/src/kv2/impl_t.rs @@ -62,15 +62,17 @@ #![allow(unused_imports)] #![cfg_attr(verus_keep_ghost, verus::trusted)] use vstd::prelude::*; -use vstd::tokens::frac::*; +use vstd::resource::frac::*; +use vstd::resource::ghost_var::*; +use vstd::resource::Loc; -use crate::pmem::pmemspec_t::*; -use crate::pmem::pmcopy_t::*; -use crate::pmem::power_t::*; -use std::hash::Hash; use super::impl_v::*; use super::inv_v::*; use super::spec_t::*; +use crate::pmem::pmcopy_t::*; +use crate::pmem::pmemspec_t::*; +use crate::pmem::power_t::*; +use std::hash::Hash; verus! { @@ -82,7 +84,7 @@ verus! { pub struct TrustedKvPermission { ghost is_transition_allowable: spec_fn(Seq, Seq) -> bool, - ghost powerpm_id: int, + ghost powerpm_id: Loc, } impl CheckPermission> for TrustedKvPermission @@ -94,7 +96,7 @@ impl CheckPermission> for TrustedKvPermission (self.is_transition_allowable)(s1, s2) } - closed spec fn id(&self) -> int + closed spec fn id(&self) -> Loc { self.powerpm_id } @@ -129,7 +131,7 @@ impl CheckPermission> for TrustedKvPermission pub struct TrustedKvPermissionFactory { ghost is_transition_allowable: spec_fn(Seq, Seq) -> bool, - ghost powerpm_id: int + ghost powerpm_id: Loc } impl PermissionFactory> for TrustedKvPermissionFactory @@ -161,7 +163,7 @@ impl PermissionFactory> for TrustedKvPermissionFactory } } - closed spec fn id(&self) -> int + closed spec fn id(&self) -> Loc { self.powerpm_id } @@ -183,7 +185,7 @@ where L: PmCopy + LogicalRange + std::fmt::Debug + Copy, { untrusted_kv_impl: UntrustedKvStoreImpl, - powerpm_id: Ghost, + powerpm_id: Ghost, } impl View for KvStore @@ -244,20 +246,20 @@ where // representing an empty KV store. pub exec fn setup(pm: &mut PM, ps: &SetupParameters) -> (result: Result<(), KvError>) - requires + requires old(pm).inv(), ensures - pm.inv(), - pm.constants() == old(pm).constants(), + final(pm).inv(), + final(pm).constants() == old(pm).constants(), match result { Ok(()) => { - &&& pm@.flush_predicted() + &&& final(pm)@.flush_predicted() &&& ps.valid() - &&& Self::recover(pm@.durable_state) == Some(RecoveredKvStore::::init(*ps)) + &&& Self::recover(final(pm)@.durable_state) == Some(RecoveredKvStore::::init(*ps)) } Err(KvError::InvalidParameter) => !ps.valid(), Err(KvError::KeySizeTooSmall) => K::spec_size_of() == 0, - Err(KvError::OutOfSpace) => pm@.len() < Self::spec_space_needed_for_setup(*ps), + Err(KvError::OutOfSpace) => final(pm)@.len() < Self::spec_space_needed_for_setup(*ps), Err(_) => false, } { @@ -270,7 +272,7 @@ where // that's the case even during crashes. pub exec fn start(pm: PM, kvstore_id: u128) -> (result: Result) - requires + requires pm.inv(), Self::recover(pm@.read_state) is Some, vstd::std_specs::hash::obeys_key_model::(), @@ -338,9 +340,9 @@ where &self, key: &K, ) -> (result: Result) - requires + requires self.valid(), - ensures + ensures match result { Ok(item) => { &&& self@.tentative.read_item(*key) matches Ok(i) @@ -363,32 +365,32 @@ where ) -> (result: Result<(), KvError>) requires old(self).valid(), - ensures - self.valid(), + ensures + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_key_slots: old(self)@.used_key_slots + 1, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, ..old(self)@ } &&& old(self)@.tentative.create(*key, *item) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self } Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() - }, + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() + }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& { ||| old(self)@.used_key_slots >= old(self)@.ps.max_keys ||| old(self)@.used_transaction_operation_slots >= old(self)@.ps.max_operations_per_transaction } }, Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.create(*key, *item) matches Err(e_spec) &&& e == e_spec }, @@ -402,34 +404,34 @@ where key: &K, item: &I, ) -> (result: Result<(), KvError>) - requires + requires old(self).valid(), - ensures - self.valid(), + ensures + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_key_slots: old(self)@.used_key_slots + 1, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, ..old(self)@ } &&& old(self)@.tentative.update_item(*key, *item) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self } Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() - }, + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() + }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& { ||| old(self)@.used_key_slots >= old(self)@.ps.max_keys ||| old(self)@.used_transaction_operation_slots >= old(self)@.ps.max_operations_per_transaction } }, Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.update_item(*key, *item) matches Err(e_spec) &&& e_spec == e }, @@ -442,30 +444,30 @@ where &mut self, key: &K, ) -> (result: Result<(), KvError>) - requires + requires old(self).valid(), - ensures - self.valid(), + ensures + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, ..old(self)@ } &&& old(self)@.tentative.delete(*key) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self }, Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() - }, + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() + }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& old(self)@.used_transaction_operation_slots >= old(self)@.ps.max_operations_per_transaction }, Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.delete(*key) matches Err(e_spec) &&& e == e_spec }, @@ -475,12 +477,12 @@ where } pub exec fn abort(&mut self) -> (result: Result<(), KvError>) - requires + requires old(self).valid(), - ensures - self.valid(), + ensures + final(self).valid(), match result { - Ok(()) => self@ == old(self)@.abort(), + Ok(()) => final(self)@ == old(self)@.abort(), Err(_) => false, }, { @@ -488,12 +490,12 @@ where } pub exec fn commit(&mut self) -> (result: Result<(), KvError>) - requires + requires old(self).valid(), - ensures - self.valid(), + ensures + final(self).valid(), match result { - Ok(()) => self@ == old(self)@.commit(), + Ok(()) => final(self)@ == old(self)@.commit(), Err(_) => false, }, { @@ -529,7 +531,7 @@ where pub exec fn get_keys(&self) -> (result: Result, KvError>) requires self.valid(), - ensures + ensures match result { Ok(keys) => { &&& keys@.to_set() == self@.tentative.get_keys() @@ -546,7 +548,7 @@ where &self, key: &K, ) -> (result: Result<(I, Vec), KvError>) - requires + requires self.valid(), ensures match result { @@ -615,25 +617,25 @@ where requires old(self).valid(), ensures - self.valid(), + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_key_slots: old(self)@.used_key_slots + 1, used_list_element_slots: old(self)@.used_list_element_slots + 1, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, ..old(self)@ } &&& old(self)@.tentative.append_to_list(*key, new_list_element) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self }, Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() - }, + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() + }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& { ||| old(self)@.used_key_slots >= old(self)@.ps.max_keys ||| old(self)@.used_list_element_slots >= old(self)@.ps.max_list_elements @@ -641,7 +643,7 @@ where } }, Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.append_to_list(*key, new_list_element) matches Err(e_spec) &&& e == e_spec }, @@ -659,11 +661,11 @@ where requires old(self).valid(), ensures - self.valid(), + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_key_slots: old(self)@.used_key_slots + 1, used_list_element_slots: old(self)@.used_list_element_slots + 1, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, @@ -671,14 +673,14 @@ where } &&& old(self)@.tentative.append_to_list_and_update_item(*key, new_list_element, *new_item) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self }, Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() - }, + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() + }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& { ||| old(self)@.used_key_slots >= old(self)@.ps.max_keys ||| old(self)@.used_list_element_slots >= old(self)@.ps.max_list_elements @@ -686,7 +688,7 @@ where } }, Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.append_to_list_and_update_item(*key, new_list_element, *new_item) matches Err(e_spec) &&& e == e_spec @@ -705,11 +707,11 @@ where requires old(self).valid(), ensures - self.valid(), + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_key_slots: old(self)@.used_key_slots + 1, used_list_element_slots: old(self)@.used_list_element_slots + 1, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, @@ -717,14 +719,14 @@ where } &&& old(self)@.tentative.update_list_element_at_index(*key, idx as nat, new_list_element) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self }, Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() - }, + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() + }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& { ||| old(self)@.used_key_slots >= old(self)@.ps.max_keys ||| old(self)@.used_list_element_slots >= old(self)@.ps.max_list_elements @@ -732,7 +734,7 @@ where } }, Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.update_list_element_at_index(*key, idx as nat, new_list_element) matches Err(e_spec) &&& e == e_spec @@ -752,11 +754,11 @@ where requires old(self).valid(), ensures - self.valid(), + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_key_slots: old(self)@.used_key_slots + 1, used_list_element_slots: old(self)@.used_list_element_slots + 1, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, @@ -764,14 +766,14 @@ where } &&& old(self)@.tentative.update_list_element_at_index_and_item(*key, idx as nat, new_list_element, *new_item) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self }, Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() - }, + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() + }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& { ||| old(self)@.used_key_slots >= old(self)@.ps.max_keys ||| old(self)@.used_list_element_slots >= old(self)@.ps.max_list_elements @@ -779,7 +781,7 @@ where } }, Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.update_list_element_at_index_and_item(*key, idx as nat, new_list_element, *new_item) matches Err(e_spec) &&& e == e_spec @@ -797,31 +799,31 @@ where requires old(self).valid(), ensures - self.valid(), + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_key_slots: old(self)@.used_key_slots + 1, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, ..old(self)@ } &&& old(self)@.tentative.trim_list(*key, trim_length as nat) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self }, Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() - }, + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() + }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& { ||| old(self)@.used_key_slots >= old(self)@.ps.max_keys ||| old(self)@.used_transaction_operation_slots >= old(self)@.ps.max_operations_per_transaction } }, Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.trim_list(*key, trim_length as nat) matches Err(e_spec) &&& e == e_spec }, @@ -839,32 +841,32 @@ where requires old(self).valid(), ensures - self.valid(), + final(self).valid(), match result { Ok(()) => { - &&& self@ == KvStoreView{ - tentative: self@.tentative, + &&& final(self)@ == KvStoreView{ + tentative: final(self)@.tentative, used_key_slots: old(self)@.used_key_slots + 1, used_transaction_operation_slots: old(self)@.used_transaction_operation_slots + 1, ..old(self)@ } &&& old(self)@.tentative.trim_list_and_update_item(*key, trim_length as nat, *new_item) matches Ok(new_self) - &&& self@.tentative == new_self + &&& final(self)@.tentative == new_self }, Err(KvError::CRCMismatch) => { - &&& self@ == old(self)@.abort() - &&& !self@.pm_constants.impervious_to_corruption() - }, + &&& final(self)@ == old(self)@.abort() + &&& !final(self)@.pm_constants.impervious_to_corruption() + }, Err(KvError::OutOfSpace) => { - &&& self@ == old(self)@.abort() + &&& final(self)@ == old(self)@.abort() &&& { ||| old(self)@.used_key_slots >= old(self)@.ps.max_keys ||| old(self)@.used_transaction_operation_slots >= old(self)@.ps.max_operations_per_transaction } }, Err(e) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.trim_list_and_update_item(*key, trim_length as nat, *new_item) matches Err(e_spec) &&& e == e_spec diff --git a/capybaraKV/capybarakv/src/kv2/impl_v.rs b/capybaraKV/capybarakv/src/kv2/impl_v.rs index ea0165c2..12c9f8a2 100644 --- a/capybaraKV/capybarakv/src/kv2/impl_v.rs +++ b/capybaraKV/capybarakv/src/kv2/impl_v.rs @@ -1,16 +1,16 @@ #![allow(unused_imports)] use vstd::prelude::*; -use crate::common::align_v::*; -use crate::journal::*; -use crate::pmem::pmemspec_t::*; -use crate::pmem::pmcopy_t::*; -use crate::pmem::power_t::*; use super::items::*; use super::keys::*; use super::lists::*; use super::recover_v::*; use super::spec_t::*; +use crate::common::align_v::*; +use crate::journal::*; +use crate::pmem::pmcopy_t::*; +use crate::pmem::pmemspec_t::*; +use crate::pmem::power_t::*; use std::hash::Hash; verus! { diff --git a/capybaraKV/capybarakv/src/kv2/items/abort_v.rs b/capybaraKV/capybarakv/src/kv2/items/abort_v.rs index ff83e506..a48e08e6 100644 --- a/capybaraKV/capybarakv/src/kv2/items/abort_v.rs +++ b/capybaraKV/capybarakv/src/kv2/items/abort_v.rs @@ -35,10 +35,10 @@ where jv_after_abort == jv_before_abort.abort(), jv_before_abort.durable_state == jv_before_abort.read_state, ensures - self.valid(jv_after_abort), - self@ == (ItemTableView{ tentative: Some(old(self)@.durable), used_slots: self@.used_slots, ..old(self)@ }), - self@.durable.m.dom().finite(), - self@.used_slots == self@.durable.m.dom().len(), + final(self).valid(jv_after_abort), + final(self)@ == (ItemTableView{ tentative: Some(old(self)@.durable), used_slots: final(self)@.used_slots, ..old(self)@ }), + final(self)@.durable.m.dom().finite(), + final(self)@.used_slots == final(self)@.durable.m.dom().len(), { let ghost new_row_info = Map::>::new( diff --git a/capybaraKV/capybarakv/src/kv2/items/commit_v.rs b/capybaraKV/capybarakv/src/kv2/items/commit_v.rs index 69edb7e5..7b596eb3 100644 --- a/capybaraKV/capybarakv/src/kv2/items/commit_v.rs +++ b/capybaraKV/capybarakv/src/kv2/items/commit_v.rs @@ -35,10 +35,10 @@ where jv_after_commit.valid(), jv_after_commit.committed_from(jv_before_commit), ensures - self.valid(jv_after_commit), - self@ == (ItemTableView{ durable: old(self)@.tentative.unwrap(), used_slots: self@.used_slots, ..old(self)@ }), - self@.durable.m.dom().finite(), - self@.used_slots == self@.durable.m.dom().len(), + final(self).valid(jv_after_commit), + final(self)@ == (ItemTableView{ durable: old(self)@.tentative.unwrap(), used_slots: final(self)@.used_slots, ..old(self)@ }), + final(self)@.durable.m.dom().finite(), + final(self)@.used_slots == final(self)@.durable.m.dom().len(), { let ghost new_row_info = Map::>::new( diff --git a/capybaraKV/capybarakv/src/kv2/items/crud_v.rs b/capybaraKV/capybarakv/src/kv2/items/crud_v.rs index 75687932..dcee7471 100644 --- a/capybaraKV/capybarakv/src/kv2/items/crud_v.rs +++ b/capybaraKV/capybarakv/src/kv2/items/crud_v.rs @@ -153,28 +153,28 @@ where perm_factory.id() == old(journal)@.powerpm_id, old(self).perm_factory_permits_states_equivalent_for_me(old(journal)@, *perm_factory), ensures - self.valid(journal@), - journal.valid(), - journal@.powerpm_id == old(journal)@.powerpm_id, - journal@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int), - journal@.remaining_capacity == old(journal)@.remaining_capacity, + final(self).valid(final(journal)@), + final(journal).valid(), + final(journal)@.powerpm_id == old(journal)@.powerpm_id, + final(journal)@.matches_except_in_range(old(journal)@, final(self)@.sm.start() as int, final(self)@.sm.end() as int), + final(journal)@.remaining_capacity == old(journal)@.remaining_capacity, match result { Ok(row_addr) => { - &&& self@ == (ItemTableView { + &&& final(self)@ == (ItemTableView { tentative: Some(old(self)@.tentative.unwrap().create(row_addr, *item)), - used_slots: self@.used_slots, + used_slots: final(self)@.used_slots, ..old(self)@ }) - &&& self@.used_slots <= old(self)@.used_slots + 1 + &&& final(self)@.used_slots <= old(self)@.used_slots + 1 &&& !old(self)@.tentative.unwrap().m.contains_key(row_addr) - &&& self.validate_item_addr(row_addr) + &&& final(self).validate_item_addr(row_addr) }, Err(KvError::OutOfSpace) => { - &&& self@ == (ItemTableView { + &&& final(self)@ == (ItemTableView { tentative: None, ..old(self)@ }) - &&& self@.used_slots == self@.sm.num_rows() + &&& final(self)@.used_slots == final(self)@.sm.num_rows() }, _ => false, }, @@ -236,8 +236,8 @@ where old(self)@.tentative.is_some(), old(self)@.tentative.unwrap().m.contains_key(row_addr), ensures - self.valid(journal@), - self@ == (ItemTableView { + final(self).valid(journal@), + final(self)@ == (ItemTableView { tentative: Some(old(self)@.tentative.unwrap().delete(row_addr)), ..old(self)@ }), diff --git a/capybaraKV/capybarakv/src/kv2/items/setup_v.rs b/capybaraKV/capybarakv/src/kv2/items/setup_v.rs index 073aa744..0a70b646 100644 --- a/capybaraKV/capybarakv/src/kv2/items/setup_v.rs +++ b/capybaraKV/capybarakv/src/kv2/items/setup_v.rs @@ -49,12 +49,12 @@ where sm.valid::(), sm.table.end <= old(pm)@.len(), ensures - pm.inv(), - pm.constants() == old(pm).constants(), - pm@.valid(), - pm@.len() == old(pm)@.len(), - Self::recover(pm@.read_state, Set::::empty(), *sm) == Some(ItemTableSnapshot::::init()), - seqs_match_except_in_range(old(pm)@.read_state, pm@.read_state, sm.table.start as int, sm.table.end as int), + final(pm).inv(), + final(pm).constants() == old(pm).constants(), + final(pm)@.valid(), + final(pm)@.len() == old(pm)@.len(), + Self::recover(final(pm)@.read_state, Set::::empty(), *sm) == Some(ItemTableSnapshot::::init()), + seqs_match_except_in_range(old(pm)@.read_state, final(pm)@.read_state, sm.table.start as int, sm.table.end as int), { assert(Self::recover(pm@.read_state, Set::::empty(), *sm) =~= Some(ItemTableSnapshot::::init())); } @@ -71,14 +71,14 @@ where ps.valid(), min_start <= max_end <= old(pm)@.len(), ensures - pm.inv(), - pm.constants() == old(pm).constants(), - pm@.valid(), - pm@.len() == old(pm)@.len(), + final(pm).inv(), + final(pm).constants() == old(pm).constants(), + final(pm)@.valid(), + final(pm)@.len() == old(pm)@.len(), match result { Ok(sm) => { - &&& Self::recover(pm@.read_state, Set::::empty(), sm) == Some(ItemTableSnapshot::::init()) - &&& seqs_match_except_in_range(old(pm)@.read_state, pm@.read_state, sm.start() as int, sm.end() as int) + &&& Self::recover(final(pm)@.read_state, Set::::empty(), sm) == Some(ItemTableSnapshot::::init()) + &&& seqs_match_except_in_range(old(pm)@.read_state, final(pm)@.read_state, sm.start() as int, sm.end() as int) &&& sm.valid::() &&& min_start <= sm.start() <= sm.end() <= max_end &&& sm.end() - min_start == Self::spec_space_needed_for_setup(*ps, min_start as nat) diff --git a/capybaraKV/capybarakv/src/kv2/keys/abort_v.rs b/capybaraKV/capybarakv/src/kv2/keys/abort_v.rs index 874d02d5..08f22bff 100644 --- a/capybaraKV/capybarakv/src/kv2/keys/abort_v.rs +++ b/capybaraKV/capybarakv/src/kv2/keys/abort_v.rs @@ -29,11 +29,11 @@ where old(self).undo_records@.len() > 0, old(self).internal_view().apply_undo_record(old(self).undo_records@.last()).unwrap().valid(old(self).sm), ensures - self.inv(jv), - self.status == old(self).status, - self.must_abort == old(self).must_abort, - self.sm == old(self).sm, - self.undo_records@ == old(self).undo_records@.drop_last(), + final(self).inv(jv), + final(self).status == old(self).status, + final(self).must_abort == old(self).must_abort, + final(self).sm == old(self).sm, + final(self).undo_records@ == old(self).undo_records@.drop_last(), { broadcast use group_hash_axioms; @@ -72,11 +72,11 @@ where old(self).inv(jv), old(self).status@ is Inconsistent, ensures - self.inv(jv), - self.status == old(self).status, - self.must_abort == old(self).must_abort, - self.sm == old(self).sm, - self.undo_records@.len() == 0, + final(self).inv(jv), + final(self).status == old(self).status, + final(self).must_abort == old(self).must_abort, + final(self).sm == old(self).sm, + final(self).undo_records@.len() == 0, { while self.undo_records.len() > 0 invariant @@ -103,10 +103,10 @@ where jv_after_abort == jv_before_abort.abort(), jv_before_abort.durable_state == jv_before_abort.read_state, ensures - self.valid(jv_after_abort), - self@ == (KeyTableView{ tentative: Some(old(self)@.durable), used_slots: self@.used_slots, ..old(self)@ }), - self@.durable.key_info.dom().finite(), - self@.used_slots == self@.durable.key_info.dom().len(), + final(self).valid(jv_after_abort), + final(self)@ == (KeyTableView{ tentative: Some(old(self)@.durable), used_slots: final(self)@.used_slots, ..old(self)@ }), + final(self)@.durable.key_info.dom().finite(), + final(self)@.used_slots == final(self)@.durable.key_info.dom().len(), { self.status = Ghost(KeyTableStatus::Inconsistent); self.apply_all_undo_records(Ghost(jv_before_abort)); diff --git a/capybaraKV/capybarakv/src/kv2/keys/commit_v.rs b/capybaraKV/capybarakv/src/kv2/keys/commit_v.rs index 280fc3f4..bd5feccd 100644 --- a/capybaraKV/capybarakv/src/kv2/keys/commit_v.rs +++ b/capybaraKV/capybarakv/src/kv2/keys/commit_v.rs @@ -30,10 +30,10 @@ where jv_after_commit.valid(), jv_after_commit.committed_from(jv_before_commit), ensures - self.valid(jv_after_commit), - self@ == (KeyTableView{ durable: old(self)@.tentative.unwrap(), used_slots: self@.used_slots, ..old(self)@ }), - self@.durable.key_info.dom().finite(), - self@.used_slots == self@.durable.key_info.dom().len(), + final(self).valid(jv_after_commit), + final(self)@ == (KeyTableView{ durable: old(self)@.tentative.unwrap(), used_slots: final(self)@.used_slots, ..old(self)@ }), + final(self)@.durable.key_info.dom().finite(), + final(self)@.used_slots == final(self)@.durable.key_info.dom().len(), { // Delete all the undo records, and move everything in the pending deallocations // list to the free list. diff --git a/capybaraKV/capybarakv/src/kv2/keys/crud_v.rs b/capybaraKV/capybarakv/src/kv2/keys/crud_v.rs index 6a223f92..6b307648 100644 --- a/capybaraKV/capybarakv/src/kv2/keys/crud_v.rs +++ b/capybaraKV/capybarakv/src/kv2/keys/crud_v.rs @@ -144,34 +144,34 @@ where !old(self)@.tentative.unwrap().key_info.contains_key(*k), !old(self)@.tentative.unwrap().item_addrs().contains(item_addr), ensures - self.inv(journal@), - journal.valid(), - journal@.powerpm_id == old(journal)@.powerpm_id, - journal@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int), - journal@.durable_state == old(journal)@.durable_state, + final(self).inv(final(journal)@), + final(journal).valid(), + final(journal)@.powerpm_id == old(journal)@.powerpm_id, + final(journal)@.matches_except_in_range(old(journal)@, final(self)@.sm.start() as int, final(self)@.sm.end() as int), + final(journal)@.durable_state == old(journal)@.durable_state, match result { Ok(row_addr) => { - &&& 0 < self.free_list@.len() - &&& row_addr == self.free_list@.last() - &&& *self == (Self{ status: Ghost(KeyTableStatus::Inconsistent), ..*old(self) }) - &&& recover_cdb(journal@.commit_state, row_addr + self.sm.row_cdb_start) == Some(true) - &&& seqs_match_except_in_range(old(journal)@.commit_state, journal@.commit_state, - row_addr as int, row_addr + self.sm.table.row_size) - &&& journal@.journaled_addrs == old(journal)@.journaled_addrs + - Set::::new(|i: int| row_addr + self.sm.row_cdb_start <= i - < row_addr + self.sm.row_cdb_start + u64::spec_size_of()) - &&& journal@.remaining_capacity >= old(journal)@.remaining_capacity - + &&& 0 < final(self).free_list@.len() + &&& row_addr == final(self).free_list@.last() + &&& *final(self) == (Self{ status: Ghost(KeyTableStatus::Inconsistent), ..*old(self) }) + &&& recover_cdb(final(journal)@.commit_state, row_addr + final(self).sm.row_cdb_start) == Some(true) + &&& seqs_match_except_in_range(old(journal)@.commit_state, final(journal)@.commit_state, + row_addr as int, row_addr + final(self).sm.table.row_size) + &&& final(journal)@.journaled_addrs == old(journal)@.journaled_addrs + + Set::::new(|i: int| row_addr + final(self).sm.row_cdb_start <= i + < row_addr + final(self).sm.row_cdb_start + u64::spec_size_of()) + &&& final(journal)@.remaining_capacity >= old(journal)@.remaining_capacity - spec_journal_entry_overhead() - u64::spec_size_of() }, Err(KvError::OutOfSpace) => { - &&& self.valid(journal@) - &&& self@ == (KeyTableView { tentative: None, ..old(self)@ }) - &&& journal@.remaining_capacity == old(journal)@.remaining_capacity + &&& final(self).valid(final(journal)@) + &&& final(self)@ == (KeyTableView { tentative: None, ..old(self)@ }) + &&& final(journal)@.remaining_capacity == old(journal)@.remaining_capacity &&& { ||| old(journal)@.remaining_capacity < spec_journal_entry_overhead() + u64::spec_size_of() - ||| self@.used_slots == self@.sm.num_rows() + ||| final(self)@.used_slots == final(self)@.sm.num_rows() } }, _ => false, @@ -245,19 +245,19 @@ where perm_factory.id() == old(journal)@.powerpm_id, self.perm_factory_permits_states_equivalent_for_me(old(journal)@, *perm_factory), ensures - self.inv(journal@), - journal.valid(), - journal@.powerpm_id == old(journal)@.powerpm_id, - journal@.journaled_addrs == old(journal)@.journaled_addrs, - journal@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int), - journal@.remaining_capacity == old(journal)@.remaining_capacity, - recover_object::(journal@.commit_state, row_addr + self.sm.row_key_start, + self.inv(final(journal)@), + final(journal).valid(), + final(journal)@.powerpm_id == old(journal)@.powerpm_id, + final(journal)@.journaled_addrs == old(journal)@.journaled_addrs, + final(journal)@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int), + final(journal)@.remaining_capacity == old(journal)@.remaining_capacity, + recover_object::(final(journal)@.commit_state, row_addr + self.sm.row_key_start, row_addr + self.sm.row_key_crc_start as u64) == Some(*k), recover_object::( - journal@.commit_state, row_addr + self.sm.row_metadata_start, + final(journal)@.commit_state, row_addr + self.sm.row_metadata_start, row_addr + self.sm.row_metadata_crc_start ) == Some(KeyTableRowMetadata{ item_addr, list_addr: 0 }), - seqs_match_except_in_range(old(journal)@.commit_state, journal@.commit_state, + seqs_match_except_in_range(old(journal)@.commit_state, final(journal)@.commit_state, row_addr + self.sm.row_metadata_start, row_addr + self.sm.table.row_size), { @@ -325,23 +325,23 @@ where perm_factory.id() == old(journal)@.powerpm_id, old(self).perm_factory_permits_states_equivalent_for_me(old(journal)@, *perm_factory), ensures - self.valid(journal@), - journal.valid(), - journal@.powerpm_id == old(journal)@.powerpm_id, - journal@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int), + final(self).valid(final(journal)@), + final(journal).valid(), + final(journal)@.powerpm_id == old(journal)@.powerpm_id, + final(journal)@.matches_except_in_range(old(journal)@, final(self)@.sm.start() as int, final(self)@.sm.end() as int), match result { Ok(()) => { - &&& self@ == (KeyTableView { + &&& final(self)@ == (KeyTableView { tentative: Some(old(self)@.tentative.unwrap().create(*k, item_addr)), - used_slots: self@.used_slots, + used_slots: final(self)@.used_slots, ..old(self)@ }) - &&& self@.used_slots <= old(self)@.used_slots + 1 - &&& journal@.remaining_capacity >= old(journal)@.remaining_capacity - + &&& final(self)@.used_slots <= old(self)@.used_slots + 1 + &&& final(journal)@.remaining_capacity >= old(journal)@.remaining_capacity - spec_journal_entry_overhead() - u64::spec_size_of() }, Err(KvError::OutOfSpace) => { - &&& self@ == (KeyTableView { + &&& final(self)@ == (KeyTableView { tentative: None, ..old(self)@ }) @@ -349,7 +349,7 @@ where ||| old(journal)@.remaining_capacity < spec_journal_entry_overhead() + u64::spec_size_of() - ||| self@.used_slots == self@.sm.num_rows() + ||| final(self)@.used_slots == final(self)@.sm.num_rows() } }, _ => false, @@ -415,21 +415,21 @@ where old(self).key_corresponds_to_key_addr(*k, row_addr), old(self).perm_factory_permits_states_equivalent_for_me(old(journal)@, *perm_factory), ensures - self.valid(journal@), - journal.valid(), - journal@.powerpm_id == old(journal)@.powerpm_id, - journal@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int), + final(self).valid(final(journal)@), + final(journal).valid(), + final(journal)@.powerpm_id == old(journal)@.powerpm_id, + final(journal)@.matches_except_in_range(old(journal)@, final(self)@.sm.start() as int, final(self)@.sm.end() as int), match result { Ok(()) => { - &&& self@ == (KeyTableView { + &&& final(self)@ == (KeyTableView { tentative: Some(old(self)@.tentative.unwrap().delete(*k)), ..old(self)@ }) - &&& journal@.remaining_capacity >= old(journal)@.remaining_capacity - + &&& final(journal)@.remaining_capacity >= old(journal)@.remaining_capacity - spec_journal_entry_overhead() - u64::spec_size_of() }, Err(KvError::OutOfSpace) => { - &&& self@ == (KeyTableView { + &&& final(self)@ == (KeyTableView { tentative: None, ..old(self)@ }) @@ -520,29 +520,29 @@ where ||| !old(self)@.tentative.unwrap().list_addrs().contains(new_rm.list_addr) }), ensures - journal.valid(), - journal@.powerpm_id == old(journal)@.powerpm_id, + final(journal).valid(), + final(journal)@.powerpm_id == old(journal)@.powerpm_id, match result { Ok(()) => { - &&& *self == Self{ status: Ghost(KeyTableStatus::Inconsistent), ..*old(self) } - &&& self.inv(journal@) - &&& self.internal_view().consistent_with_journaled_addrs(journal@.journaled_addrs, self.sm) - &&& journal@.matches_except_in_range(old(journal)@, row_addr + self.sm.row_metadata_start, - row_addr + self.sm.row_metadata_crc_start + u64::spec_size_of()) + &&& *final(self) == Self{ status: Ghost(KeyTableStatus::Inconsistent), ..*old(self) } + &&& final(self).inv(final(journal)@) + &&& final(self).internal_view().consistent_with_journaled_addrs(final(journal)@.journaled_addrs, final(self).sm) + &&& final(journal)@.matches_except_in_range(old(journal)@, row_addr + final(self).sm.row_metadata_start, + row_addr + final(self).sm.row_metadata_crc_start + u64::spec_size_of()) &&& recover_object::( - journal@.commit_state, row_addr + self.sm.row_metadata_start, - row_addr + self.sm.row_metadata_crc_start + final(journal)@.commit_state, row_addr + final(self).sm.row_metadata_start, + row_addr + final(self).sm.row_metadata_crc_start ) == Some(new_rm) - &&& journal@.remaining_capacity >= old(journal)@.remaining_capacity + &&& final(journal)@.remaining_capacity >= old(journal)@.remaining_capacity - spec_journal_entry_overhead() - KeyTableRowMetadata::spec_size_of() - spec_journal_entry_overhead() - u64::spec_size_of() }, Err(KvError::OutOfSpace) => { - &&& journal@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int) - &&& self.valid(journal@) - &&& self@ == KeyTableView { tentative: None, ..old(self)@ } + &&& final(journal)@.matches_except_in_range(old(journal)@, final(self)@.sm.start() as int, final(self)@.sm.end() as int) + &&& final(self).valid(final(journal)@) + &&& final(self)@ == KeyTableView { tentative: None, ..old(self)@ } &&& old(journal)@.remaining_capacity < spec_journal_entry_overhead() + KeyTableRowMetadata::spec_size_of() @@ -629,26 +629,26 @@ where }), old(self).perm_factory_permits_states_equivalent_for_me(old(journal)@, *perm_factory), ensures - self.valid(journal@), - journal.valid(), - journal@.powerpm_id == old(journal)@.powerpm_id, - journal@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int), + final(self).valid(final(journal)@), + final(journal).valid(), + final(journal)@.powerpm_id == old(journal)@.powerpm_id, + final(journal)@.matches_except_in_range(old(journal)@, final(self)@.sm.start() as int, final(self)@.sm.end() as int), match result { Ok(()) => { - &&& self@ == (KeyTableView { + &&& final(self)@ == (KeyTableView { tentative: Some(old(self)@.tentative.unwrap().update(*k, new_rm, former_rm)), - used_slots: self@.used_slots, + used_slots: final(self)@.used_slots, ..old(self)@ }) - &&& self@.used_slots <= old(self)@.used_slots + 1 - &&& journal@.remaining_capacity >= old(journal)@.remaining_capacity + &&& final(self)@.used_slots <= old(self)@.used_slots + 1 + &&& final(journal)@.remaining_capacity >= old(journal)@.remaining_capacity - spec_journal_entry_overhead() - KeyTableRowMetadata::spec_size_of() - spec_journal_entry_overhead() - u64::spec_size_of() }, Err(KvError::OutOfSpace) => { - &&& self@ == (KeyTableView { + &&& final(self)@ == (KeyTableView { tentative: None, ..old(self)@ }) diff --git a/capybaraKV/capybarakv/src/kv2/keys/setup_v.rs b/capybaraKV/capybarakv/src/kv2/keys/setup_v.rs index 1d82117c..2d939065 100644 --- a/capybaraKV/capybarakv/src/kv2/keys/setup_v.rs +++ b/capybaraKV/capybarakv/src/kv2/keys/setup_v.rs @@ -55,12 +55,12 @@ where sm.valid::(), sm.table.end <= old(pm)@.len(), ensures - pm.inv(), - pm.constants() == old(pm).constants(), - pm@.valid(), - pm@.len() == old(pm)@.len(), - Self::recover(pm@.read_state, *sm) == Some(KeyTableSnapshot::::init()), - seqs_match_except_in_range(old(pm)@.read_state, pm@.read_state, sm.table.start as int, sm.table.end as int), + final(pm).inv(), + final(pm).constants() == old(pm).constants(), + final(pm)@.valid(), + final(pm)@.len() == old(pm)@.len(), + Self::recover(final(pm)@.read_state, *sm) == Some(KeyTableSnapshot::::init()), + seqs_match_except_in_range(old(pm)@.read_state, final(pm)@.read_state, sm.table.start as int, sm.table.end as int), { let mut row_index: u64 = 0; let mut row_addr: u64 = sm.table.start; @@ -132,14 +132,14 @@ where ps.valid(), min_start <= max_end <= old(pm)@.len(), ensures - pm.inv(), - pm.constants() == old(pm).constants(), - pm@.valid(), - pm@.len() == old(pm)@.len(), + final(pm).inv(), + final(pm).constants() == old(pm).constants(), + final(pm)@.valid(), + final(pm)@.len() == old(pm)@.len(), match result { Ok(sm) => { - &&& Self::recover(pm@.read_state, sm) == Some(KeyTableSnapshot::::init()) - &&& seqs_match_except_in_range(old(pm)@.read_state, pm@.read_state, sm.start() as int, + &&& Self::recover(final(pm)@.read_state, sm) == Some(KeyTableSnapshot::::init()) + &&& seqs_match_except_in_range(old(pm)@.read_state, final(pm)@.read_state, sm.start() as int, sm.end() as int) &&& sm.valid::() &&& min_start <= sm.start() <= sm.end() <= max_end diff --git a/capybaraKV/capybarakv/src/kv2/lists/abort_v.rs b/capybaraKV/capybarakv/src/kv2/lists/abort_v.rs index a713c1f5..1a7ca495 100644 --- a/capybaraKV/capybarakv/src/kv2/lists/abort_v.rs +++ b/capybaraKV/capybarakv/src/kv2/lists/abort_v.rs @@ -96,18 +96,18 @@ where &&& !(old(self).m@[list_addr] is Durable) }), ensures - *self == (Self{ m: self.m, ..*old(self) }), - self.internal_view() == (ListTableInternalView{ m: self.internal_view().m, ..old(self).internal_view() }), - forall|i: int| 0 <= i < self.modifications.len() ==> - (#[trigger] old(self).modifications[i] matches Some(list_addr) ==> !self.m@.contains_key(list_addr)), - forall|list_addr: u64| #[trigger] self.m@.contains_key(list_addr) ==> { + *final(self) == (Self{ m: final(self).m, ..*old(self) }), + final(self).internal_view() == (ListTableInternalView{ m: final(self).internal_view().m, ..old(self).internal_view() }), + forall|i: int| 0 <= i < final(self).modifications.len() ==> + (#[trigger] old(self).modifications[i] matches Some(list_addr) ==> !final(self).m@.contains_key(list_addr)), + forall|list_addr: u64| #[trigger] final(self).m@.contains_key(list_addr) ==> { &&& old(self).m@.contains_key(list_addr) - &&& self.m@[list_addr]@ == old(self).m@[list_addr]@ + &&& final(self).m@[list_addr]@ == old(self).m@[list_addr]@ }, forall|list_addr: u64| { &&& #[trigger] old(self).m@.contains_key(list_addr) &&& old(self).m[list_addr] is Durable - } ==> self.m@.contains_key(list_addr), + } ==> final(self).m@.contains_key(list_addr), { let num_modifications = self.modifications.len(); @@ -154,33 +154,33 @@ where }, forall|list_addr: u64| #[trigger] old(self).m@.contains_key(list_addr) ==> old(self).m@[list_addr] is Durable, ensures - *self == (Self{ m: self.m, ..*old(self) }), - forall|i: int| #![trigger self.deletes[i]] 0 <= i < self.deletes.len() ==> { - let summary = self.deletes[i]; - &&& self.m@.contains_key(summary.head) - &&& self.m@[summary.head] == ListTableEntry::::Durable{ summary } + *final(self) == (Self{ m: final(self).m, ..*old(self) }), + forall|i: int| #![trigger final(self).deletes[i]] 0 <= i < final(self).deletes.len() ==> { + let summary = final(self).deletes[i]; + &&& final(self).m@.contains_key(summary.head) + &&& final(self).m@[summary.head] == ListTableEntry::::Durable{ summary } }, - forall|list_addr: u64| #[trigger] self.m@.contains_key(list_addr) ==> { - if self.deletes_inverse@.contains_key(list_addr) { - let summary = self.deletes@[self.deletes_inverse@[list_addr] as int]; - &&& self.m@[list_addr] == ListTableEntry::::Durable{ summary } + forall|list_addr: u64| #[trigger] final(self).m@.contains_key(list_addr) ==> { + if final(self).deletes_inverse@.contains_key(list_addr) { + let summary = final(self).deletes@[final(self).deletes_inverse@[list_addr] as int]; + &&& final(self).m@[list_addr] == ListTableEntry::::Durable{ summary } } else { &&& old(self).m@.contains_key(list_addr) - &&& self.m@[list_addr]@ == old(self).m@[list_addr]@ + &&& final(self).m@[list_addr]@ == old(self).m@[list_addr]@ } }, forall|list_addr: u64| #[trigger] old(self).m@.contains_key(list_addr) ==> { ||| { &&& old(self).deletes_inverse@.contains_key(list_addr) - &&& self.m@.contains_key(list_addr) - &&& self.m@[list_addr] == ListTableEntry::::Durable{ - summary: self.deletes@[self.deletes_inverse@[list_addr] as int] + &&& final(self).m@.contains_key(list_addr) + &&& final(self).m@[list_addr] == ListTableEntry::::Durable{ + summary: final(self).deletes@[final(self).deletes_inverse@[list_addr] as int] } } ||| { - &&& self.m@.contains_key(list_addr) - &&& self.m@[list_addr] == old(self).m@[list_addr] + &&& final(self).m@.contains_key(list_addr) + &&& final(self).m@[list_addr] == old(self).m@[list_addr] } }, { @@ -244,8 +244,8 @@ where requires old(self).valid(jv), ensures - *self == (Self{ m: self.m, ..*old(self) }), - self.internal_view().m == old(self).internal_view().abort().m, + *final(self) == (Self{ m: final(self).m, ..*old(self) }), + final(self).internal_view().m == old(self).internal_view().abort().m, { self.update_m_to_reflect_abort_of_modifications(); @@ -277,12 +277,12 @@ where jv_after_abort.valid(), jv_after_abort == jv_before_abort.abort(), ensures - self.valid(jv_after_abort), - self@ == (ListTableView{ tentative: Some(old(self)@.durable), used_slots: self@.used_slots, ..old(self)@ }), + final(self).valid(jv_after_abort), + final(self)@ == (ListTableView{ tentative: Some(old(self)@.durable), used_slots: final(self)@.used_slots, ..old(self)@ }), ({ - let m = self@.durable.m; + let m = final(self)@.durable.m; &&& m.dom().finite() - &&& self@.used_slots == + &&& final(self)@.used_slots == m.dom().to_seq().fold_left(0, |total: int, row_addr: u64| total + m[row_addr].len()) }), { diff --git a/capybaraKV/capybarakv/src/kv2/lists/append_v.rs b/capybaraKV/capybarakv/src/kv2/lists/append_v.rs index a08b018f..282413f0 100644 --- a/capybaraKV/capybarakv/src/kv2/lists/append_v.rs +++ b/capybaraKV/capybarakv/src/kv2/lists/append_v.rs @@ -476,31 +476,31 @@ where _ => false, }, ensures - self.valid(journal@), - journal.valid(), - journal@.powerpm_id == old(journal)@.powerpm_id, - journal@.matches_except_in_range(prev_jv, self@.sm.start() as int, self@.sm.end() as int), + final(self).valid(final(journal)@), + final(journal).valid(), + final(journal)@.powerpm_id == old(journal)@.powerpm_id, + final(journal)@.matches_except_in_range(prev_jv, final(self)@.sm.start() as int, final(self)@.sm.end() as int), match result { Ok(new_list_addr) => { &&& new_list_addr != 0 &&& new_list_addr == list_addr || !prev_self@.tentative.unwrap().m.contains_key(new_list_addr) - &&& match self@.logical_range_gaps_policy { + &&& match final(self)@.logical_range_gaps_policy { LogicalRangeGapsPolicy::LogicalRangeGapsPermitted => new_element.start() >= end_of_range(prev_self@.tentative.unwrap().m[list_addr]), LogicalRangeGapsPolicy::LogicalRangeGapsForbidden => new_element.start() == end_of_range(prev_self@.tentative.unwrap().m[list_addr]), } - &&& self@ == (ListTableView { + &&& final(self)@ == (ListTableView { tentative: Some(prev_self@.tentative.unwrap().append(list_addr, new_list_addr, new_element)), used_slots: old(self)@.used_slots, ..prev_self@ }) - &&& self.validate_list_addr(new_list_addr) - &&& journal@.remaining_capacity >= old(journal)@.remaining_capacity - self.space_needed_to_journal_next + &&& final(self).validate_list_addr(new_list_addr) + &&& final(journal)@.remaining_capacity >= old(journal)@.remaining_capacity - final(self).space_needed_to_journal_next }, Err(KvError::CRCMismatch) => { - &&& !journal@.pm_constants.impervious_to_corruption() - &&& self@ == (ListTableView { + &&& !final(journal)@.pm_constants.impervious_to_corruption() + &&& final(self)@ == (ListTableView { tentative: None, ..prev_self@ }) @@ -633,32 +633,32 @@ where _ => false, }, ensures - self.valid(journal@), - journal.valid(), - journal@.powerpm_id == old(journal)@.powerpm_id, - journal@.matches_except_in_range(prev_jv, self@.sm.start() as int, self@.sm.end() as int), + final(self).valid(final(journal)@), + final(journal).valid(), + final(journal)@.powerpm_id == old(journal)@.powerpm_id, + final(journal)@.matches_except_in_range(prev_jv, final(self)@.sm.start() as int, final(self)@.sm.end() as int), match result { Ok(new_list_addr) => { &&& new_list_addr != 0 &&& new_list_addr == list_addr || !prev_self@.tentative.unwrap().m.contains_key(new_list_addr) - &&& match self@.logical_range_gaps_policy { + &&& match final(self)@.logical_range_gaps_policy { LogicalRangeGapsPolicy::LogicalRangeGapsPermitted => new_element.start() >= end_of_range(prev_self@.tentative.unwrap().m[list_addr]), LogicalRangeGapsPolicy::LogicalRangeGapsForbidden => new_element.start() == end_of_range(prev_self@.tentative.unwrap().m[list_addr]), } - &&& self@ == (ListTableView { + &&& final(self)@ == (ListTableView { tentative: Some(prev_self@.tentative.unwrap().append(list_addr, new_list_addr, new_element)), used_slots: old(self)@.used_slots, ..prev_self@ }) - &&& self@.used_slots <= old(self)@.used_slots + 1 - &&& self.validate_list_addr(new_list_addr) - &&& journal@.remaining_capacity >= old(journal)@.remaining_capacity - self.space_needed_to_journal_next + &&& final(self)@.used_slots <= old(self)@.used_slots + 1 + &&& final(self).validate_list_addr(new_list_addr) + &&& final(journal)@.remaining_capacity >= old(journal)@.remaining_capacity - final(self).space_needed_to_journal_next }, Err(KvError::CRCMismatch) => { - &&& !journal@.pm_constants.impervious_to_corruption() - &&& self@ == (ListTableView { + &&& !final(journal)@.pm_constants.impervious_to_corruption() + &&& final(self)@ == (ListTableView { tentative: None, ..prev_self@ }) @@ -741,29 +741,29 @@ where perm_factory.id() == old(journal)@.powerpm_id, self.perm_factory_permits_states_equivalent_for_me(old(journal)@, *perm_factory), ensures - journal.valid(), - journal@.powerpm_id == old(journal)@.powerpm_id, - journal@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int), - journal@.journaled_addrs == old(journal)@.journaled_addrs, - journal@.remaining_capacity == old(journal)@.remaining_capacity, - self.internal_view().corresponds_to_durable_state(journal@.durable_state, self.sm), - self.internal_view().corresponds_to_durable_state(journal@.read_state, self.sm), + final(journal).valid(), + final(journal)@.powerpm_id == old(journal)@.powerpm_id, + final(journal)@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int), + final(journal)@.journaled_addrs == old(journal)@.journaled_addrs, + final(journal)@.remaining_capacity == old(journal)@.remaining_capacity, + self.internal_view().corresponds_to_durable_state(final(journal)@.durable_state, self.sm), + self.internal_view().corresponds_to_durable_state(final(journal)@.read_state, self.sm), forall|other_row_addr: u64| { &&& self.sm.table.validate_row_addr(other_row_addr) &&& other_row_addr != row_addr } ==> { - &&& recover_object::(journal@.commit_state, other_row_addr + self.sm.row_element_start, + &&& recover_object::(final(journal)@.commit_state, other_row_addr + self.sm.row_element_start, other_row_addr + self.sm.row_element_crc_start) == recover_object::(old(journal)@.commit_state, other_row_addr + self.sm.row_element_start, other_row_addr + self.sm.row_element_crc_start) - &&& recover_object::(journal@.commit_state, other_row_addr + self.sm.row_next_start, + &&& recover_object::(final(journal)@.commit_state, other_row_addr + self.sm.row_next_start, other_row_addr + self.sm.row_next_start + u64::spec_size_of()) == recover_object::(old(journal)@.commit_state, other_row_addr + self.sm.row_next_start, other_row_addr + self.sm.row_next_start + u64::spec_size_of()) }, - recover_object::(journal@.commit_state, row_addr + self.sm.row_element_start, + recover_object::(final(journal)@.commit_state, row_addr + self.sm.row_element_start, row_addr + self.sm.row_element_crc_start) == Some(new_element), - recover_object::(journal@.commit_state, row_addr + self.sm.row_next_start, + recover_object::(final(journal)@.commit_state, row_addr + self.sm.row_next_start, row_addr + self.sm.row_next_start + u64::spec_size_of()) == Some(0u64), { proof { @@ -829,55 +829,55 @@ where perm_factory.id() == old(journal)@.powerpm_id, old(self).perm_factory_permits_states_equivalent_for_me(old(journal)@, *perm_factory), ensures - self.valid(journal@), - journal.valid(), - journal@.powerpm_id == old(journal)@.powerpm_id, - journal@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int), + final(self).valid(final(journal)@), + final(journal).valid(), + final(journal)@.powerpm_id == old(journal)@.powerpm_id, + final(journal)@.matches_except_in_range(old(journal)@, final(self)@.sm.start() as int, final(self)@.sm.end() as int), match result { Ok(new_list_addr) => { let old_list = old(self)@.tentative.unwrap().m[list_addr]; &&& new_list_addr != 0 &&& new_list_addr == list_addr || !old(self)@.tentative.unwrap().m.contains_key(new_list_addr) &&& old_list.len() < usize::MAX - &&& match self@.logical_range_gaps_policy { + &&& match final(self)@.logical_range_gaps_policy { LogicalRangeGapsPolicy::LogicalRangeGapsPermitted => new_element.start() >= end_of_range(old_list), LogicalRangeGapsPolicy::LogicalRangeGapsForbidden => new_element.start() == end_of_range(old_list), } - &&& self@ == (ListTableView { + &&& final(self)@ == (ListTableView { tentative: Some(old(self)@.tentative.unwrap().append(list_addr, new_list_addr, new_element)), - used_slots: self@.used_slots, + used_slots: final(self)@.used_slots, ..old(self)@ }) - &&& self@.used_slots <= old(self)@.used_slots + 1 - &&& self.validate_list_addr(new_list_addr) - &&& journal@.remaining_capacity >= old(journal)@.remaining_capacity - + &&& final(self)@.used_slots <= old(self)@.used_slots + 1 + &&& final(self).validate_list_addr(new_list_addr) + &&& final(journal)@.remaining_capacity >= old(journal)@.remaining_capacity - spec_journal_entry_overhead() - u64::spec_size_of() - u64::spec_size_of() }, Err(KvError::ListLengthWouldExceedUsizeMax) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.unwrap().m[list_addr].len() >= usize::MAX - &&& journal@.remaining_capacity == old(journal)@.remaining_capacity + &&& final(journal)@.remaining_capacity == old(journal)@.remaining_capacity }, Err(KvError::PageLeavesLogicalRangeGap{ end_of_valid_range }) => { - &&& self@ == old(self)@ - &&& self@.logical_range_gaps_policy is LogicalRangeGapsForbidden + &&& final(self)@ == old(self)@ + &&& final(self)@.logical_range_gaps_policy is LogicalRangeGapsForbidden &&& old(self)@.tentative.unwrap().m[list_addr].len() < usize::MAX &&& new_element.start() > end_of_range(old(self)@.tentative.unwrap().m[list_addr]) &&& end_of_valid_range == end_of_range(old(self)@.tentative.unwrap().m[list_addr]) - &&& journal@.remaining_capacity == old(journal)@.remaining_capacity + &&& final(journal)@.remaining_capacity == old(journal)@.remaining_capacity }, Err(KvError::PageOutOfLogicalRangeOrder{ end_of_valid_range }) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& old(self)@.tentative.unwrap().m[list_addr].len() < usize::MAX &&& new_element.start() < end_of_range(old(self)@.tentative.unwrap().m[list_addr]) &&& end_of_valid_range == end_of_range(old(self)@.tentative.unwrap().m[list_addr]) - &&& journal@.remaining_capacity == old(journal)@.remaining_capacity + &&& final(journal)@.remaining_capacity == old(journal)@.remaining_capacity } Err(KvError::OutOfSpace) => { - &&& self@ == (ListTableView { + &&& final(self)@ == (ListTableView { tentative: None, ..old(self)@ }) @@ -885,12 +885,12 @@ where ||| old(journal)@.remaining_capacity < spec_journal_entry_overhead() + u64::spec_size_of() + u64::spec_size_of() - ||| self@.used_slots == self@.sm.num_rows() + ||| final(self)@.used_slots == final(self)@.sm.num_rows() } }, Err(KvError::CRCMismatch) => { - &&& !journal@.pm_constants.impervious_to_corruption() - &&& self@ == (ListTableView { + &&& !final(journal)@.pm_constants.impervious_to_corruption() + &&& final(self)@ == (ListTableView { tentative: None, ..old(self)@ }) @@ -999,42 +999,42 @@ where perm_factory.id() == old(journal)@.powerpm_id, old(self).perm_factory_permits_states_equivalent_for_me(old(journal)@, *perm_factory), ensures - self.valid(journal@), - journal.valid(), - journal@.powerpm_id == old(journal)@.powerpm_id, - journal@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int), + final(self).valid(final(journal)@), + final(journal).valid(), + final(journal)@.powerpm_id == old(journal)@.powerpm_id, + final(journal)@.matches_except_in_range(old(journal)@, final(self)@.sm.start() as int, final(self)@.sm.end() as int), match result { Ok(new_row_addr) => { &&& new_row_addr != 0 - &&& self@.logical_range_gaps_policy is LogicalRangeGapsForbidden ==> + &&& final(self)@.logical_range_gaps_policy is LogicalRangeGapsForbidden ==> new_element.start() == 0 &&& !old(self)@.tentative.unwrap().m.contains_key(new_row_addr) - &&& self@ == (ListTableView { + &&& final(self)@ == (ListTableView { tentative: Some(old(self)@.tentative.unwrap().create_singleton(new_row_addr, new_element)), - used_slots: self@.used_slots, + used_slots: final(self)@.used_slots, ..old(self)@ }) - &&& self@.used_slots <= old(self)@.used_slots + 1 - &&& self.validate_list_addr(new_row_addr) - &&& journal@.remaining_capacity == old(journal)@.remaining_capacity + &&& final(self)@.used_slots <= old(self)@.used_slots + 1 + &&& final(self).validate_list_addr(new_row_addr) + &&& final(journal)@.remaining_capacity == old(journal)@.remaining_capacity }, Err(KvError::PageLeavesLogicalRangeGap{ end_of_valid_range }) => { - &&& self@ == old(self)@ - &&& self@.logical_range_gaps_policy is LogicalRangeGapsForbidden + &&& final(self)@ == old(self)@ + &&& final(self)@.logical_range_gaps_policy is LogicalRangeGapsForbidden &&& new_element.start() != 0 &&& end_of_valid_range == 0 - &&& journal@.remaining_capacity == old(journal)@.remaining_capacity + &&& final(journal)@.remaining_capacity == old(journal)@.remaining_capacity } Err(KvError::OutOfSpace) => { - &&& self@ == (ListTableView { + &&& final(self)@ == (ListTableView { tentative: None, ..old(self)@ }) - &&& self@.used_slots == self@.sm.num_rows() + &&& final(self)@.used_slots == final(self)@.sm.num_rows() }, Err(KvError::CRCMismatch) => { - &&& !journal@.pm_constants.impervious_to_corruption() - &&& self@ == (ListTableView { + &&& !final(journal)@.pm_constants.impervious_to_corruption() + &&& final(self)@ == (ListTableView { tentative: None, ..old(self)@ }) diff --git a/capybaraKV/capybarakv/src/kv2/lists/commit_v.rs b/capybaraKV/capybarakv/src/kv2/lists/commit_v.rs index 9a984012..832dedce 100644 --- a/capybaraKV/capybarakv/src/kv2/lists/commit_v.rs +++ b/capybaraKV/capybarakv/src/kv2/lists/commit_v.rs @@ -97,20 +97,20 @@ where (#[trigger] old(self).modifications[which_modification] matches Some(list_addr) ==> old(self).m@.contains_key(list_addr)), ensures - *self == (Self{ m: self.m, ..*old(self) }), - forall|i: int| 0 <= i < self.modifications.len() ==> + *final(self) == (Self{ m: final(self).m, ..*old(self) }), + forall|i: int| 0 <= i < final(self).modifications.len() ==> (#[trigger] old(self).modifications[i] matches Some(list_addr) ==> { - &&& self.m@.contains_key(list_addr) - &&& self.m@[list_addr]@ == old(self).m@[list_addr]@.commit() + &&& final(self).m@.contains_key(list_addr) + &&& final(self).m@[list_addr]@ == old(self).m@[list_addr]@.commit() }), - forall|list_addr: u64| #[trigger] self.m@.contains_key(list_addr) ==> { + forall|list_addr: u64| #[trigger] final(self).m@.contains_key(list_addr) ==> { &&& old(self).m@.contains_key(list_addr) &&& { - ||| self.m@[list_addr]@ == old(self).m@[list_addr]@ - ||| self.m@[list_addr]@ == old(self).m@[list_addr]@.commit() + ||| final(self).m@[list_addr]@ == old(self).m@[list_addr]@ + ||| final(self).m@[list_addr]@ == old(self).m@[list_addr]@.commit() } }, - forall|list_addr: u64| #[trigger] old(self).m@.contains_key(list_addr) ==> self.m@.contains_key(list_addr), + forall|list_addr: u64| #[trigger] old(self).m@.contains_key(list_addr) ==> final(self).m@.contains_key(list_addr), { let num_modifications = self.modifications.len(); for which_modification in 0..num_modifications @@ -153,8 +153,8 @@ where requires old(self).valid(jv), ensures - *self == (Self{ m: self.m, ..*old(self) }), - self.internal_view().m == old(self).internal_view().commit().m, + *final(self) == (Self{ m: final(self).m, ..*old(self) }), + final(self).internal_view().m == old(self).internal_view().commit().m, { self.update_m_to_reflect_commit_of_modifications(); assert(self.internal_view().m =~= old(self).internal_view().commit().m); @@ -172,12 +172,12 @@ where jv_after_commit.valid(), jv_after_commit.committed_from(jv_before_commit), ensures - self.valid(jv_after_commit), - self@ == (ListTableView{ durable: old(self)@.tentative.unwrap(), used_slots: self@.used_slots, ..old(self)@ }), + final(self).valid(jv_after_commit), + final(self)@ == (ListTableView{ durable: old(self)@.tentative.unwrap(), used_slots: final(self)@.used_slots, ..old(self)@ }), ({ - let m = self@.durable.m; + let m = final(self)@.durable.m; &&& m.dom().finite() - &&& self@.used_slots == + &&& final(self)@.used_slots == m.dom().to_seq().fold_left(0, |total: int, row_addr: u64| total + m[row_addr].len()) }), { diff --git a/capybaraKV/capybarakv/src/kv2/lists/delete_v.rs b/capybaraKV/capybarakv/src/kv2/lists/delete_v.rs index 2a29a72a..1fb9078f 100644 --- a/capybaraKV/capybarakv/src/kv2/lists/delete_v.rs +++ b/capybaraKV/capybarakv/src/kv2/lists/delete_v.rs @@ -351,21 +351,21 @@ where old(self)@.tentative.unwrap().m.contains_key(list_addr), old(self).perm_factory_permits_states_equivalent_for_me(old(journal)@, *perm_factory), ensures - self.valid(journal@), - journal.valid(), - journal@.powerpm_id == old(journal)@.powerpm_id, - journal@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int), - journal@.remaining_capacity == old(journal)@.remaining_capacity, + final(self).valid(final(journal)@), + final(journal).valid(), + final(journal)@.powerpm_id == old(journal)@.powerpm_id, + final(journal)@.matches_except_in_range(old(journal)@, final(self)@.sm.start() as int, final(self)@.sm.end() as int), + final(journal)@.remaining_capacity == old(journal)@.remaining_capacity, match result { Ok(_) => { - &&& self@ == (ListTableView { + &&& final(self)@ == (ListTableView { tentative: Some(old(self)@.tentative.unwrap().delete(list_addr)), ..old(self)@ }) }, Err(KvError::CRCMismatch) => { - &&& !journal@.pm_constants.impervious_to_corruption() - &&& self@ == (ListTableView { + &&& !final(journal)@.pm_constants.impervious_to_corruption() + &&& final(self)@ == (ListTableView { tentative: None, ..old(self)@ }) diff --git a/capybaraKV/capybarakv/src/kv2/lists/setup_v.rs b/capybaraKV/capybarakv/src/kv2/lists/setup_v.rs index 5ed38291..98a44afd 100644 --- a/capybaraKV/capybarakv/src/kv2/lists/setup_v.rs +++ b/capybaraKV/capybarakv/src/kv2/lists/setup_v.rs @@ -51,12 +51,12 @@ where sm.valid::(), sm.table.end <= old(pm)@.len(), ensures - pm.inv(), - pm.constants() == old(pm).constants(), - pm@.valid(), - pm@.len() == old(pm)@.len(), - Self::recover(pm@.read_state, Set::::empty(), *sm) == Some(ListTableSnapshot::::init()), - seqs_match_except_in_range(old(pm)@.read_state, pm@.read_state, sm.table.start as int, sm.table.end as int), + final(pm).inv(), + final(pm).constants() == old(pm).constants(), + final(pm)@.valid(), + final(pm)@.len() == old(pm)@.len(), + Self::recover(final(pm)@.read_state, Set::::empty(), *sm) == Some(ListTableSnapshot::::init()), + seqs_match_except_in_range(old(pm)@.read_state, final(pm)@.read_state, sm.table.start as int, sm.table.end as int), { let ghost mapping = ListRecoveryMapping::::new_empty(sm.table); let ghost list_addrs = Set::::empty(); @@ -82,14 +82,14 @@ where ps.valid(), 0 < min_start <= max_end <= old(pm)@.len(), ensures - pm.inv(), - pm.constants() == old(pm).constants(), - pm@.valid(), - pm@.len() == old(pm)@.len(), + final(pm).inv(), + final(pm).constants() == old(pm).constants(), + final(pm)@.valid(), + final(pm)@.len() == old(pm)@.len(), match result { Ok(sm) => { - &&& Self::recover(pm@.read_state, Set::::empty(), sm) == Some(ListTableSnapshot::::init()) - &&& seqs_match_except_in_range(old(pm)@.read_state, pm@.read_state, sm.start() as int, sm.end() as int) + &&& Self::recover(final(pm)@.read_state, Set::::empty(), sm) == Some(ListTableSnapshot::::init()) + &&& seqs_match_except_in_range(old(pm)@.read_state, final(pm)@.read_state, sm.start() as int, sm.end() as int) &&& sm.valid::() &&& min_start <= sm.start() <= sm.end() <= max_end &&& sm.end() - min_start == Self::spec_space_needed_for_setup(*ps, min_start as nat) diff --git a/capybaraKV/capybarakv/src/kv2/lists/start_v.rs b/capybaraKV/capybarakv/src/kv2/lists/start_v.rs index 323344c2..9f1c9726 100644 --- a/capybaraKV/capybarakv/src/kv2/lists/start_v.rs +++ b/capybaraKV/capybarakv/src/kv2/lists/start_v.rs @@ -44,10 +44,10 @@ where Ok(summary) => { let row_addrs = mapping.list_info[list_addr]; &&& forall|row_addr: u64| #[trigger] old(row_addrs_used)@.contains(row_addr) - ==> row_addrs_used@.contains(row_addr) + ==> final(row_addrs_used)@.contains(row_addr) &&& forall|i: int| 0 <= i < row_addrs.len() - ==> row_addrs_used@.contains(#[trigger] row_addrs[i]) - &&& forall|row_addr: u64| #[trigger] row_addrs_used@.contains(row_addr) + ==> final(row_addrs_used)@.contains(#[trigger] row_addrs[i]) + &&& forall|row_addr: u64| #[trigger] final(row_addrs_used)@.contains(row_addr) ==> { ||| old(row_addrs_used)@.contains(row_addr) ||| row_addrs.contains(row_addr) diff --git a/capybaraKV/capybarakv/src/kv2/lists/trim_v.rs b/capybaraKV/capybarakv/src/kv2/lists/trim_v.rs index 26afcc01..b968dd2f 100644 --- a/capybaraKV/capybarakv/src/kv2/lists/trim_v.rs +++ b/capybaraKV/capybarakv/src/kv2/lists/trim_v.rs @@ -757,9 +757,9 @@ where (TrimAction::Modify{ pending_deallocations, new_head }) .applicable(old(self).internal_view(), list_addr, trim_length as int), ensures - self.valid(journal@), + final(self).valid(journal@), new_list_addr != 0, - self.validate_list_addr(new_list_addr), + final(self).validate_list_addr(new_list_addr), ({ let old_list = old(self)@.tentative.unwrap().m[list_addr]; &&& { @@ -769,7 +769,7 @@ where } &&& trim_length <= old_list.len() &&& new_list_addr == 0 ==> old_list.skip(trim_length as int) == Seq::::empty() - &&& self@ == (ListTableView { + &&& final(self)@ == (ListTableView { tentative: Some(old(self)@.tentative.unwrap().trim(list_addr, new_list_addr, trim_length as int)), ..old(self)@ }) @@ -823,9 +823,9 @@ where (TrimAction::Advance{ pending_deallocations, new_head }) .applicable(old(self).internal_view(), list_addr, trim_length as int), ensures - self.valid(journal@), + final(self).valid(journal@), new_list_addr != 0, - self.validate_list_addr(new_list_addr), + final(self).validate_list_addr(new_list_addr), ({ let old_list = old(self)@.tentative.unwrap().m[list_addr]; &&& { @@ -835,7 +835,7 @@ where } &&& trim_length <= old_list.len() &&& new_list_addr == 0 ==> old_list.skip(trim_length as int) == Seq::::empty() - &&& self@ == (ListTableView { + &&& final(self)@ == (ListTableView { tentative: Some(old(self)@.tentative.unwrap().trim(list_addr, new_list_addr, trim_length as int)), ..old(self)@ }) @@ -889,13 +889,13 @@ where (TrimAction::Drain{ pending_deallocations }) .applicable(old(self).internal_view(), list_addr, trim_length as int), ensures - self.valid(journal@), + final(self).valid(journal@), new_list_addr != 0, - self.validate_list_addr(new_list_addr), + final(self).validate_list_addr(new_list_addr), new_list_addr == list_addr || !old(self)@.tentative.unwrap().m.contains_key(new_list_addr), ({ let old_list = old(self)@.tentative.unwrap().m[list_addr]; - &&& self@ == (ListTableView { + &&& final(self)@ == (ListTableView { tentative: Some(old(self)@.tentative.unwrap().trim(list_addr, new_list_addr, trim_length as int)), ..old(self)@ }) @@ -966,11 +966,11 @@ where 0 < trim_length, old(self).perm_factory_permits_states_equivalent_for_me(old(journal)@, *perm_factory), ensures - self.valid(journal@), - journal.valid(), - journal@.powerpm_id == old(journal)@.powerpm_id, - journal@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int), - journal@.remaining_capacity == old(journal)@.remaining_capacity, + final(self).valid(final(journal)@), + final(journal).valid(), + final(journal)@.powerpm_id == old(journal)@.powerpm_id, + final(journal)@.matches_except_in_range(old(journal)@, final(self)@.sm.start() as int, final(self)@.sm.end() as int), + final(journal)@.remaining_capacity == old(journal)@.remaining_capacity, match result { Ok(new_list_addr) => { let old_list = old(self)@.tentative.unwrap().m[list_addr]; @@ -981,21 +981,21 @@ where } &&& trim_length <= old_list.len() &&& new_list_addr == 0 ==> old_list.skip(trim_length as int) == Seq::::empty() - &&& self@ == (ListTableView { + &&& final(self)@ == (ListTableView { tentative: Some(old(self)@.tentative.unwrap().trim(list_addr, new_list_addr, trim_length as int)), ..old(self)@ }) - &&& new_list_addr != 0 ==> self.validate_list_addr(new_list_addr) + &&& new_list_addr != 0 ==> final(self).validate_list_addr(new_list_addr) }, Err(KvError::IndexOutOfRange{ upper_bound }) => { - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& trim_length > upper_bound &&& upper_bound == old(self)@.tentative.unwrap().m[list_addr].len() - &&& journal@.remaining_capacity == old(journal)@.remaining_capacity + &&& final(journal)@.remaining_capacity == old(journal)@.remaining_capacity }, Err(KvError::CRCMismatch) => { - &&& !journal@.pm_constants.impervious_to_corruption() - &&& self@ == (ListTableView { + &&& !final(journal)@.pm_constants.impervious_to_corruption() + &&& final(self)@ == (ListTableView { tentative: None, ..old(self)@ }) diff --git a/capybaraKV/capybarakv/src/kv2/lists/update_v.rs b/capybaraKV/capybarakv/src/kv2/lists/update_v.rs index 6eb356f5..0d033852 100644 --- a/capybaraKV/capybarakv/src/kv2/lists/update_v.rs +++ b/capybaraKV/capybarakv/src/kv2/lists/update_v.rs @@ -530,28 +530,28 @@ where old(self).m@ == prev_self.m@.remove(list_addr), ensures journal.valid(), - *self == (Self{ m: self.m, deletes: self.deletes, deletes_inverse: self.deletes_inverse, - modifications: self.modifications, ..*old(self) }), + *final(self) == (Self{ m: final(self).m, deletes: final(self).deletes, deletes_inverse: final(self).deletes_inverse, + modifications: final(self).modifications, ..*old(self) }), ({ let (success, new_entry) = result; if success { - let next_iv = self.internal_view().add_entry(list_addr, new_entry@); - &&& self@ == old(self)@ - &&& !self.m@.contains_key(list_addr) - &&& next_iv.corresponds_to_journal(journal@, self.sm) + let next_iv = final(self).internal_view().add_entry(list_addr, new_entry@); + &&& final(self)@ == old(self)@ + &&& !final(self).m@.contains_key(list_addr) + &&& next_iv.corresponds_to_journal(journal@, final(self).sm) &&& match new_entry { ListTableEntry::Modified{ summary, addrs, elements, .. } => { &&& summary.length == addrs.len() &&& addrs.len() == elements.len() - &&& addrs@ == self.tentative_mapping@.list_info[list_addr] - &&& elements@ == self.tentative_mapping@.list_elements[list_addr] + &&& addrs@ == final(self).tentative_mapping@.list_info[list_addr] + &&& elements@ == final(self).tentative_mapping@.list_elements[list_addr] }, _ => false, } } else { &&& !journal@.pm_constants.impervious_to_corruption() - &&& *self == *old(self) + &&& *final(self) == *old(self) &&& new_entry == entry } }), @@ -689,27 +689,27 @@ where _ => false, }, ensures - journal.valid(), - journal@.powerpm_id == old(journal)@.powerpm_id, - journal@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int), - journal@.remaining_capacity >= old(journal)@.remaining_capacity - self.space_needed_to_journal_next, + final(journal).valid(), + final(journal)@.powerpm_id == old(journal)@.powerpm_id, + final(journal)@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int), + final(journal)@.remaining_capacity >= old(journal)@.remaining_capacity - self.space_needed_to_journal_next, ({ let old_iv = self.internal_view().add_entry(list_addr, entry@).push_to_free_list(new_row_addr); - &&& old_iv.corresponds_to_durable_state(journal@.durable_state, self.sm) - &&& old_iv.corresponds_to_durable_state(journal@.read_state, self.sm) + &&& old_iv.corresponds_to_durable_state(final(journal)@.durable_state, self.sm) + &&& old_iv.corresponds_to_durable_state(final(journal)@.read_state, self.sm) }), ({ let addrs = entry->Modified_addrs@; let next: u64 = if idx == addrs.len() - 1 { 0 } else { addrs[idx + 1] }; - &&& recover_object::(journal@.commit_state, new_row_addr + self.sm.row_element_start, + &&& recover_object::(final(journal)@.commit_state, new_row_addr + self.sm.row_element_start, new_row_addr + self.sm.row_element_crc_start) == Some(new_element) - &&& recover_object::(journal@.commit_state, new_row_addr + self.sm.row_next_start, + &&& recover_object::(final(journal)@.commit_state, new_row_addr + self.sm.row_next_start, new_row_addr + self.sm.row_next_start + u64::spec_size_of()) == Some(next) &&& forall|other_row_addr: u64| { &&& self.sm.table.validate_row_addr(other_row_addr) &&& other_row_addr != new_row_addr } ==> { - recover_object::(journal@.commit_state, other_row_addr + self.sm.row_element_start, + recover_object::(final(journal)@.commit_state, other_row_addr + self.sm.row_element_start, other_row_addr + self.sm.row_element_crc_start) == recover_object::(old(journal)@.commit_state, other_row_addr + self.sm.row_element_start, other_row_addr + self.sm.row_element_crc_start) @@ -718,7 +718,7 @@ where if idx > 0 { let addrs = entry->Modified_addrs@; let prev_row_addr: u64 = addrs[idx - 1]; - &&& recover_object::(journal@.commit_state, prev_row_addr + self.sm.row_next_start, + &&& recover_object::(final(journal)@.commit_state, prev_row_addr + self.sm.row_next_start, prev_row_addr + self.sm.row_next_start + u64::spec_size_of()) == Some(new_row_addr) &&& forall|other_row_addr: u64| { @@ -726,12 +726,12 @@ where &&& other_row_addr != new_row_addr &&& other_row_addr != prev_row_addr } ==> { - recover_object::(journal@.commit_state, other_row_addr + self.sm.row_next_start, + recover_object::(final(journal)@.commit_state, other_row_addr + self.sm.row_next_start, other_row_addr + self.sm.row_next_start + u64::spec_size_of()) == recover_object::(old(journal)@.commit_state, other_row_addr + self.sm.row_next_start, other_row_addr + self.sm.row_next_start + u64::spec_size_of()) } - &&& journal@.journaled_addrs == + &&& final(journal)@.journaled_addrs == old(journal)@.journaled_addrs + Set::::new(|i: int| prev_row_addr + self.sm.row_next_start <= i < prev_row_addr + self.sm.row_next_start + u64::spec_size_of() + u64::spec_size_of()) @@ -740,12 +740,12 @@ where &&& self.sm.table.validate_row_addr(other_row_addr) &&& other_row_addr != new_row_addr } ==> { - recover_object::(journal@.commit_state, other_row_addr + self.sm.row_next_start, + recover_object::(final(journal)@.commit_state, other_row_addr + self.sm.row_next_start, other_row_addr + self.sm.row_next_start + u64::spec_size_of()) == recover_object::(old(journal)@.commit_state, other_row_addr + self.sm.row_next_start, other_row_addr + self.sm.row_next_start + u64::spec_size_of()) } - &&& journal@.journaled_addrs == old(journal)@.journaled_addrs + &&& final(journal)@.journaled_addrs == old(journal)@.journaled_addrs }, { let ghost old_iv = self.internal_view().add_entry(list_addr, entry@).push_to_free_list(new_row_addr); @@ -992,21 +992,21 @@ where _ => false, }, ensures - self.valid(journal@), - journal.valid(), - journal@.powerpm_id == old(journal)@.powerpm_id, - journal@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int), - journal@.remaining_capacity >= old(journal)@.remaining_capacity - self.space_needed_to_journal_next, + final(self).valid(final(journal)@), + final(journal).valid(), + final(journal)@.powerpm_id == old(journal)@.powerpm_id, + final(journal)@.matches_except_in_range(old(journal)@, final(self)@.sm.start() as int, final(self)@.sm.end() as int), + final(journal)@.remaining_capacity >= old(journal)@.remaining_capacity - final(self).space_needed_to_journal_next, new_list_addr != 0, new_list_addr == list_addr || new_list_addr == old(self).free_list@.last(), - self@ == (ListTableView { + final(self)@ == (ListTableView { tentative: Some(old(self)@.tentative.unwrap().update_element_at_index(list_addr, new_list_addr, idx, new_element)), - used_slots: self@.used_slots, + used_slots: final(self)@.used_slots, ..old(self)@ }), - self@.used_slots <= old(self)@.used_slots + 1, - self.validate_list_addr(new_list_addr), + final(self)@.used_slots <= old(self)@.used_slots + 1, + final(self).validate_list_addr(new_list_addr), ({ let old_list = old(self)@.tentative.unwrap().m[list_addr]; &&& idx < old_list.len() @@ -1095,10 +1095,10 @@ where perm_factory.id() == old(journal)@.powerpm_id, old(self).perm_factory_permits_states_equivalent_for_me(old(journal)@, *perm_factory), ensures - self.valid(journal@), - journal.valid(), - journal@.powerpm_id == old(journal)@.powerpm_id, - journal@.matches_except_in_range(old(journal)@, self@.sm.start() as int, self@.sm.end() as int), + final(self).valid(final(journal)@), + final(journal).valid(), + final(journal)@.powerpm_id == old(journal)@.powerpm_id, + final(journal)@.matches_except_in_range(old(journal)@, final(self)@.sm.start() as int, final(self)@.sm.end() as int), match result { Ok(new_list_addr) => { let old_list = old(self)@.tentative.unwrap().m[list_addr]; @@ -1107,38 +1107,38 @@ where &&& idx < old_list.len() &&& old_list[idx as int].start() == new_element.start() &&& old_list[idx as int].end() == new_element.end() - &&& self@ == (ListTableView { + &&& final(self)@ == (ListTableView { tentative: Some(old(self)@.tentative.unwrap().update_element_at_index(list_addr, new_list_addr, idx, new_element)), - used_slots: self@.used_slots, + used_slots: final(self)@.used_slots, ..old(self)@ }) - &&& self@.used_slots <= old(self)@.used_slots + 1 - &&& self.validate_list_addr(new_list_addr) - &&& journal@.remaining_capacity >= old(journal)@.remaining_capacity - + &&& final(self)@.used_slots <= old(self)@.used_slots + 1 + &&& final(self).validate_list_addr(new_list_addr) + &&& final(journal)@.remaining_capacity >= old(journal)@.remaining_capacity - spec_journal_entry_overhead() - u64::spec_size_of() - u64::spec_size_of() }, Err(KvError::IndexOutOfRange{ upper_bound }) => { let old_list = old(self)@.tentative.unwrap().m[list_addr]; - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& idx >= old_list.len() &&& upper_bound == old_list.len() - &&& journal@.remaining_capacity == old(journal)@.remaining_capacity + &&& final(journal)@.remaining_capacity == old(journal)@.remaining_capacity }, Err(KvError::LogicalRangeUpdateNotAllowed{ old_start, old_end, new_start, new_end }) => { let old_list = old(self)@.tentative.unwrap().m[list_addr]; - &&& self@ == old(self)@ + &&& final(self)@ == old(self)@ &&& idx < old_list.len() &&& old_start == old_list[idx as int].start() &&& old_end == old_list[idx as int].end() &&& new_start == new_element.start() &&& new_end == new_element.end() &&& old_start != new_start || old_end != new_end - &&& journal@.remaining_capacity == old(journal)@.remaining_capacity + &&& final(journal)@.remaining_capacity == old(journal)@.remaining_capacity } Err(KvError::OutOfSpace) => { - &&& self@ == (ListTableView { + &&& final(self)@ == (ListTableView { tentative: None, ..old(self)@ }) @@ -1146,12 +1146,12 @@ where ||| old(journal)@.remaining_capacity < spec_journal_entry_overhead() + u64::spec_size_of() + u64::spec_size_of() - ||| self@.used_slots == self@.sm.num_rows() + ||| final(self)@.used_slots == final(self)@.sm.num_rows() } }, Err(KvError::CRCMismatch) => { - &&& !journal@.pm_constants.impervious_to_corruption() - &&& self@ == (ListTableView { + &&& !final(journal)@.pm_constants.impervious_to_corruption() + &&& final(self)@ == (ListTableView { tentative: None, ..old(self)@ }) diff --git a/capybaraKV/capybarakv/src/kv2/rwkv_t.rs b/capybaraKV/capybarakv/src/kv2/rwkv_t.rs index b9dd1b60..6d899655 100644 --- a/capybaraKV/capybarakv/src/kv2/rwkv_t.rs +++ b/capybaraKV/capybarakv/src/kv2/rwkv_t.rs @@ -1,23 +1,25 @@ #![allow(unused_imports)] #![cfg_attr(verus_keep_ghost, verus::trusted)] -use vstd::prelude::*; -use vstd::tokens::frac::*; use vstd::invariant::*; +use vstd::prelude::*; +use vstd::resource::frac::*; +use vstd::resource::ghost_var::*; +use vstd::resource::Loc; -use std::sync::Arc; use std::hash::Hash; +use std::sync::Arc; use crate::pmem::crashinv_t::*; -use crate::pmem::pmemspec_t::*; use crate::pmem::pmcopy_t::*; +use crate::pmem::pmemspec_t::*; use crate::pmem::power_t::*; use super::concurrentspec_t::*; -use super::spec_t::*; -use super::rwkv_v::*; use super::impl_v::*; use super::recover_v::*; +use super::rwkv_v::*; +use super::spec_t::*; verus! { @@ -38,9 +40,9 @@ where } pub struct ConcurrentKvStoreInvPred { - pub rwlock_id: int, - pub caller_id: int, - pub durable_id: int, + pub rwlock_id: Loc, + pub caller_id: Loc, + pub durable_id: Loc, pub pm_constants: PersistentMemoryConstants, pub ps: SetupParameters, } @@ -80,7 +82,7 @@ where I: PmCopy + Sized + std::fmt::Debug, L: PmCopy + LogicalRange + std::fmt::Debug + Copy, { - spec fn id(self) -> int; + spec fn id(self) -> Loc; spec fn namespace(self) -> int; spec fn spec_space_needed_for_setup(ps: SetupParameters) -> nat; @@ -128,7 +130,7 @@ where ) -> (result: (Result, Tracked)) where CB: ReadLinearizer>, - requires + requires cb.pre(self.id(), ReadItemOp{ key: *key }), !cb.namespaces().contains(self.namespace()), ensures @@ -138,7 +140,7 @@ where &self, Tracked(cb): Tracked, ) -> (result: (Result, KvError>, Tracked)) - requires + requires cb.pre(self.id(), GetKeysOp{ }), !cb.namespaces().contains(self.namespace()), ensures @@ -149,7 +151,7 @@ where key: &K, Tracked(cb): Tracked, ) -> (result: (Result<(I, Vec), KvError>, Tracked)) - requires + requires cb.pre(self.id(), ReadItemAndListOp{ key: *key }), !cb.namespaces().contains(self.namespace()), ensures @@ -160,7 +162,7 @@ where key: &K, Tracked(cb): Tracked, ) -> (result: (Result, KvError>, Tracked)) - requires + requires cb.pre(self.id(), ReadListOp{ key: *key }), !cb.namespaces().contains(self.namespace()), ensures @@ -171,7 +173,7 @@ where key: &K, Tracked(cb): Tracked, ) -> (result: (Result, Tracked)) - requires + requires cb.pre(self.id(), GetListLengthOp{ key: *key }), !cb.namespaces().contains(self.namespace()), ensures @@ -188,7 +190,7 @@ where requires cb.pre(self.id(), CreateOp{ key: *key, item: *item }), !cb.namespaces().contains(self.namespace()), - ensures + ensures cb.post(result.1@, self.id(), CreateOp{ key: *key, item: *item }, result.0); exec fn update_item( @@ -202,7 +204,7 @@ where requires cb.pre(self.id(), UpdateItemOp{ key: *key, item: *item }), !cb.namespaces().contains(self.namespace()), - ensures + ensures cb.post(result.1@, self.id(), UpdateItemOp{ key: *key, item: *item }, result.0); exec fn delete( @@ -215,7 +217,7 @@ where requires cb.pre(self.id(), DeleteOp{ key: *key }), !cb.namespaces().contains(self.namespace()), - ensures + ensures cb.post(result.1@, self.id(), DeleteOp{ key: *key }, result.0); exec fn append_to_list( @@ -229,7 +231,7 @@ where requires cb.pre(self.id(), AppendToListOp{ key: *key, new_list_element }), !cb.namespaces().contains(self.namespace()), - ensures + ensures cb.post(result.1@, self.id(), AppendToListOp{ key: *key, new_list_element }, result.0); exec fn append_to_list_and_update_item( @@ -244,7 +246,7 @@ where requires cb.pre(self.id(), AppendToListAndUpdateItemOp{ key: *key, new_list_element, new_item: *new_item }), !cb.namespaces().contains(self.namespace()), - ensures + ensures cb.post(result.1@, self.id(), AppendToListAndUpdateItemOp{ key: *key, new_list_element, new_item: *new_item }, result.0); exec fn update_list_element_at_index( @@ -259,7 +261,7 @@ where requires cb.pre(self.id(), UpdateListElementAtIndexOp{ key: *key, idx, new_list_element }), !cb.namespaces().contains(self.namespace()), - ensures + ensures cb.post(result.1@, self.id(), UpdateListElementAtIndexOp{ key: *key, idx, new_list_element }, result.0); exec fn update_list_element_at_index_and_item( @@ -275,7 +277,7 @@ where requires cb.pre(self.id(), UpdateListElementAtIndexAndItemOp{ key: *key, idx, new_list_element, new_item: *new_item }), !cb.namespaces().contains(self.namespace()), - ensures + ensures cb.post(result.1@, self.id(), UpdateListElementAtIndexAndItemOp{ key: *key, idx, new_list_element, new_item: *new_item }, result.0); exec fn trim_list( @@ -289,7 +291,7 @@ where requires cb.pre(self.id(), TrimListOp{ key : *key, trim_length }), !cb.namespaces().contains(self.namespace()), - ensures + ensures cb.post(result.1@, self.id(), TrimListOp{ key: *key, trim_length }, result.0); exec fn trim_list_and_update_item( @@ -304,7 +306,7 @@ where requires cb.pre(self.id(), TrimListAndUpdateItemOp{ key : *key, trim_length, new_item: *new_item }), !cb.namespaces().contains(self.namespace()), - ensures + ensures cb.post(result.1@, self.id(), TrimListAndUpdateItemOp{ key: *key, trim_length, new_item: *new_item }, result.0); } @@ -319,13 +321,13 @@ where // - The PM region was given to setup(), which returned. // // - The PM region was given to recover(). -// +// // The underlying property is that both of the above call hold_until_crash() // to ensure that the invariant keeps holding until the system crashes. pub exec fn recover( mut pm: PM, kvstore_id: u128, - Ghost(id): Ghost, + Ghost(id): Ghost, Ghost(namespace): Ghost, ) -> (result: Result, KvError>) where diff --git a/capybaraKV/capybarakv/src/kv2/rwkv_v.rs b/capybaraKV/capybarakv/src/kv2/rwkv_v.rs index e5625e22..2b2a89d2 100644 --- a/capybaraKV/capybarakv/src/kv2/rwkv_v.rs +++ b/capybaraKV/capybarakv/src/kv2/rwkv_v.rs @@ -1,21 +1,25 @@ #![allow(unused_imports)] use vstd::prelude::*; -use crate::pmem::crashinv_t::*; -use crate::pmem::pmemspec_t::*; -use crate::pmem::pmcopy_t::*; -use crate::pmem::power_t::*; -use std::hash::Hash; use super::concurrentspec_t::*; use super::impl_v::*; -use super::spec_t::*; use super::recover_v::*; use super::rwkv_t::*; -use vstd::tokens::frac::*; -use super::rwlock_t::{RwLockReadGuardWithPredicate, RwLockPredicate, RwLockWithPredicate, RwLockWriter}; +use super::rwlock_t::{ + RwLockPredicate, RwLockReadGuardWithPredicate, RwLockWithPredicate, RwLockWriter, +}; +use super::spec_t::*; +use crate::pmem::crashinv_t::*; +use crate::pmem::pmcopy_t::*; +use crate::pmem::pmemspec_t::*; +use crate::pmem::power_t::*; +use std::hash::Hash; +use std::sync::Arc; use vstd::invariant::*; use vstd::modes::*; -use std::sync::Arc; +use vstd::resource::frac::*; +use vstd::resource::ghost_var::*; +use vstd::resource::Loc; verus! { @@ -35,8 +39,8 @@ where pub(super) struct ConcurrentKvStorePredicate { - id: int, - powerpm_id: int, + id: Loc, + powerpm_id: Loc, } impl RwLockPredicate> for ConcurrentKvStorePredicate @@ -147,7 +151,7 @@ exec fn maybe_commit( inv@.constant().rwlock_id == pred@.id, inv@.constant().durable_id == pred@.powerpm_id, ensures - pred@.inv(*kv_internal), + pred@.inv(*final(kv_internal)), cb.post(result.1@, inv@.constant().caller_id, op, result.0), { match tentative_result { @@ -232,25 +236,25 @@ trait OpParameters: Sized old(kv)@.durable == old(kv)@.tentative, old(kv)@.ps.logical_range_gaps_policy == old(kv)@.durable.logical_range_gaps_policy, ensures - kv.valid(), - kv@.ps == old(kv)@.ps, - kv@.pm_constants == old(kv)@.pm_constants, - kv@.durable == old(kv)@.durable, - kv@.powerpm_id == old(kv)@.powerpm_id, + final(kv).valid(), + final(kv)@.ps == old(kv)@.ps, + final(kv)@.pm_constants == old(kv)@.pm_constants, + final(kv)@.durable == old(kv)@.durable, + final(kv)@.powerpm_id == old(kv)@.powerpm_id, ({ let old_ckv = ConcurrentKvStoreView::{ ps: old(kv)@.ps, pm_constants: old(kv)@.pm_constants, kv: old(kv)@.tentative }; - let new_ckv = ConcurrentKvStoreView::{ ps: kv@.ps, pm_constants: kv@.pm_constants, - kv: kv@.tentative }; + let new_ckv = ConcurrentKvStoreView::{ ps: final(kv)@.ps, pm_constants: final(kv)@.pm_constants, + kv: final(kv)@.tentative }; self.op().result_valid(old_ckv, new_ckv, result) }), match result { Ok(_) => true, Err(_) => { - &&& kv@.used_key_slots == kv@.durable.num_keys() - &&& kv@.used_list_element_slots == kv@.durable.num_list_elements() - &&& kv@.used_transaction_operation_slots == 0 - &&& kv@.tentative == kv@.durable + &&& final(kv)@.used_key_slots == final(kv)@.durable.num_keys() + &&& final(kv)@.used_list_element_slots == final(kv)@.durable.num_list_elements() + &&& final(kv)@.used_transaction_operation_slots == 0 + &&& final(kv)@.tentative == final(kv)@.durable }, }, ; @@ -712,7 +716,7 @@ where I: PmCopy + Sized + std::fmt::Debug, L: PmCopy + LogicalRange + std::fmt::Debug + Copy, { - closed spec fn id(self) -> int + closed spec fn id(self) -> Loc { self.inv@.constant().caller_id } @@ -1167,7 +1171,7 @@ impl CheckPermission> for OpPerm int { + closed spec fn id(&self) -> Loc { self.inv.constant().durable_id } @@ -1252,7 +1256,7 @@ impl CheckPermission> for NoopPerm recover_journal_then_kv::(old_state) == recover_journal_then_kv::(new_state) } - closed spec fn id(&self) -> int { + closed spec fn id(&self) -> Loc { self.inv.constant().durable_id } @@ -1298,7 +1302,7 @@ impl PermissionFactory> for NoopPermFactory }.permits(old_state, new_state) } - closed spec fn id(&self) -> int { + closed spec fn id(&self) -> Loc { NoopPerm{ inv: self.inv.clone(), }.id() diff --git a/capybaraKV/capybarakv/src/kv2/rwlock_t.rs b/capybaraKV/capybarakv/src/kv2/rwlock_t.rs index fb1c3553..8949b12c 100644 --- a/capybaraKV/capybarakv/src/kv2/rwlock_t.rs +++ b/capybaraKV/capybarakv/src/kv2/rwlock_t.rs @@ -55,7 +55,7 @@ pub trait RwLockWriter>: Sized { self.pre(), self.pred().inv(*old(v)), ensures - self.pred().inv(*v), + self.pred().inv(*final(v)), self.post(completion), ; } diff --git a/capybaraKV/capybarakv/src/kv2/setup_v.rs b/capybaraKV/capybarakv/src/kv2/setup_v.rs index d715e780..c2e1de02 100644 --- a/capybaraKV/capybarakv/src/kv2/setup_v.rs +++ b/capybaraKV/capybarakv/src/kv2/setup_v.rs @@ -105,11 +105,11 @@ where jc.app_area_start + KvStaticMetadata::spec_size_of() + u64::spec_size_of() <= jc.app_area_end, jc.app_area_end <= old(pm)@.len(), ensures - pm.inv(), - pm.constants() == old(pm).constants(), - seqs_match_except_in_range(old(pm)@.read_state, pm@.read_state, jc.app_area_start as int, + final(pm).inv(), + final(pm).constants() == old(pm).constants(), + seqs_match_except_in_range(old(pm)@.read_state, final(pm)@.read_state, jc.app_area_start as int, jc.app_area_start + KvStaticMetadata::spec_size_of() + u64::spec_size_of()), - recover_static_metadata::(pm@.read_state, *jc) == Some(*sm), + recover_static_metadata::(final(pm)@.read_state, *jc) == Some(*sm), { broadcast use pmcopy_axioms; broadcast use broadcast_can_result_from_write_effect_on_read_state; @@ -133,25 +133,25 @@ where requires old(pm).inv(), ensures - pm.inv(), - pm.constants() == old(pm).constants(), + final(pm).inv(), + final(pm).constants() == old(pm).constants(), match result { Ok(()) => { - &&& pm@.flush_predicted() + &&& final(pm)@.flush_predicted() &&& ps.valid() - &&& Self::recover(pm@.durable_state) == Some(RecoveredKvStore::::init(*ps)) + &&& Self::recover(final(pm)@.durable_state) == Some(RecoveredKvStore::::init(*ps)) }, Err(KvError::InvalidParameter) => !ps.valid(), Err(KvError::KeySizeTooSmall) => K::spec_size_of() == 0, - Err(KvError::OutOfSpace) => pm@.len() < Self::spec_space_needed_for_setup(*ps), + Err(KvError::OutOfSpace) => final(pm)@.len() < Self::spec_space_needed_for_setup(*ps), Err(_) => false, }, match result { Ok(_) => true, Err(_) => { - &&& pm.inv() - &&& pm.constants() == old(pm).constants() - &&& pm@.len() == old(pm)@.len() + &&& final(pm).inv() + &&& final(pm).constants() == old(pm).constants() + &&& final(pm)@.len() == old(pm)@.len() }, }, { diff --git a/capybaraKV/capybarakv/src/kv2/shardkv_t.rs b/capybaraKV/capybarakv/src/kv2/shardkv_t.rs index 7f2fe862..d22fd49e 100644 --- a/capybaraKV/capybarakv/src/kv2/shardkv_t.rs +++ b/capybaraKV/capybarakv/src/kv2/shardkv_t.rs @@ -1,26 +1,28 @@ #![allow(unused_imports)] #![cfg_attr(verus_keep_ghost, verus::trusted)] -use vstd::prelude::*; -use vstd::tokens::frac::*; use vstd::invariant::*; +use vstd::prelude::*; +use vstd::resource::frac::*; +use vstd::resource::ghost_var::*; +use vstd::resource::Loc; -use std::sync::Arc; -use std::marker::PhantomData; -use std::hash::{Hash, Hasher, DefaultHasher}; use std::collections::VecDeque; +use std::hash::{DefaultHasher, Hash, Hasher}; +use std::marker::PhantomData; +use std::sync::Arc; use crate::pmem::crashinv_t::*; -use crate::pmem::pmemspec_t::*; use crate::pmem::pmcopy_t::*; +use crate::pmem::pmemspec_t::*; use crate::pmem::power_t::*; use super::concurrentspec_t::*; -use super::spec_t::*; use super::rwkv_t; use super::rwkv_t::*; use super::rwkv_v::*; use super::shardkv_v::*; +use super::spec_t::*; verus! { @@ -44,7 +46,7 @@ where L: PmCopy + LogicalRange + std::fmt::Debug + Copy, { spec fn namespaces(self) -> Set; - spec fn id(self) -> int; + spec fn id(self) -> Loc; exec fn setup( nshards: usize, @@ -376,7 +378,7 @@ pub exec fn recover( pms: VecDeque, kvstore_id: u128, Ghost(pm_constants): Ghost, - Ghost(id): Ghost, + Ghost(id): Ghost, Ghost(namespace): Ghost, Ghost(shard_namespace): Ghost, ) -> (result: Result, KvError>) @@ -437,7 +439,7 @@ pub exec fn recover( let pm = pms_mut.pop_front().unwrap(); // There was some shard ID before crash; it doesn't matter what it was. - let ghost shard_id: int = arbitrary(); + let ghost shard_id: Loc = arbitrary(); match rwkv_t::recover(pm, kvstore_id, Ghost(shard_id), Ghost(shard_namespace)) { Err(e) => return Err(e), diff --git a/capybaraKV/capybarakv/src/kv2/shardkv_v.rs b/capybaraKV/capybarakv/src/kv2/shardkv_v.rs index 317e18b0..3f932c7c 100644 --- a/capybaraKV/capybarakv/src/kv2/shardkv_v.rs +++ b/capybaraKV/capybarakv/src/kv2/shardkv_v.rs @@ -1,23 +1,25 @@ #![allow(unused_imports)] use vstd::prelude::*; -use crate::pmem::pmemspec_t::*; -use crate::pmem::pmcopy_t::*; -use crate::pmem::power_t::*; -use std::hash::{Hash, Hasher, DefaultHasher}; use super::concurrentspec_t::*; use super::impl_v::*; -use super::spec_t::*; +use super::recover_v::*; use super::rwkv_t::*; use super::rwkv_v::*; use super::shardkv_t::*; -use vstd::invariant::*; -use vstd::tokens::frac::*; -use std::sync::Arc; +use super::spec_t::*; +use crate::pmem::pmcopy_t::*; +use crate::pmem::pmemspec_t::*; +use crate::pmem::power_t::*; +use std::collections::VecDeque; +use std::hash::{DefaultHasher, Hash, Hasher}; use std::marker::PhantomData; +use std::sync::Arc; +use vstd::invariant::*; use vstd::modes::*; -use std::collections::VecDeque; -use super::recover_v::*; +use vstd::resource::frac::*; +use vstd::resource::ghost_var::*; +use vstd::resource::Loc; verus! { @@ -52,7 +54,7 @@ impl ShardState I: PmCopy + Sized + std::fmt::Debug, L: PmCopy + LogicalRange + std::fmt::Debug + Copy, { - spec fn valid(self, kv_id: int, shard: int, nshards: int, combined: ConcurrentKvStoreView) -> bool { + spec fn valid(self, kv_id: Loc, shard: int, nshards: int, combined: ConcurrentKvStoreView) -> bool { &&& self.kv_state.id() == kv_id &&& keys_match_shard(self.kv_state@.kv, shard, nshards) &&& self.kv_state@.ps == combined.ps @@ -77,8 +79,8 @@ pub struct ShardStates pub struct ShardingPredicate { - pub shard_ids: Seq, - pub combined_id: int, + pub shard_ids: Seq, + pub combined_id: Loc, pub pm_constants: PersistentMemoryConstants, } @@ -232,7 +234,7 @@ where set![self.inv@.namespace(), self.shard_namespace@] } - closed spec fn id(self) -> int { + closed spec fn id(self) -> Loc { self.inv@.constant().combined_id } @@ -557,7 +559,7 @@ impl ReadLinearizer for ShardedReadLinearizer bool { + closed spec fn pre(self, id: Loc, op: Op) -> bool { &&& 0 <= self.shard < self.inv.constant().nshard() &&& id == self.inv.constant().shard_ids[self.shard] &&& self.lin.pre(self.inv.constant().combined_id, op) @@ -565,7 +567,7 @@ impl ReadLinearizer for ShardedReadLinearizer) -> bool { + closed spec fn post(self, complete: Self::Completion, id: Loc, op: Op, result: Result) -> bool { &&& self.lin.post(complete, self.inv.constant().combined_id, op, result) } @@ -618,7 +620,7 @@ impl MutatingLinearizer for ShardedMutatingLinear self.lin.namespaces().insert(self.inv.namespace()) } - closed spec fn pre(self, id: int, op: Op) -> bool { + closed spec fn pre(self, id: Loc, op: Op) -> bool { &&& 0 <= self.shard < self.inv.constant().nshard() &&& id == self.inv.constant().shard_ids[self.shard] &&& self.lin.pre(self.inv.constant().combined_id, op) @@ -632,7 +634,7 @@ impl MutatingLinearizer for ShardedMutatingLinear } } - closed spec fn post(self, complete: Self::Completion, id: int, op: Op, result: Result) -> bool { + closed spec fn post(self, complete: Self::Completion, id: Loc, op: Op, result: Result) -> bool { &&& self.lin.post(complete, self.inv.constant().combined_id, op, result) } diff --git a/capybaraKV/capybarakv/src/kv2/spec_t.rs b/capybaraKV/capybarakv/src/kv2/spec_t.rs index 61d372fa..c8988b81 100644 --- a/capybaraKV/capybarakv/src/kv2/spec_t.rs +++ b/capybaraKV/capybarakv/src/kv2/spec_t.rs @@ -5,6 +5,7 @@ #![allow(unused_imports)] #![cfg_attr(verus_keep_ghost, verus::trusted)] use vstd::prelude::*; +use vstd::resource::Loc; use crate::pmem::pmemspec_t::*; use std::hash::Hash; @@ -452,7 +453,7 @@ pub struct KvStoreView pub pm_constants: PersistentMemoryConstants, pub durable: AtomicKvStore, pub tentative: AtomicKvStore, - pub powerpm_id: int, + pub powerpm_id: Loc, } impl KvStoreView diff --git a/capybaraKV/capybarakv/src/pmem/crc_t.rs b/capybaraKV/capybarakv/src/pmem/crc_t.rs index ce1b3985..dd17ed95 100644 --- a/capybaraKV/capybarakv/src/pmem/crc_t.rs +++ b/capybaraKV/capybarakv/src/pmem/crc_t.rs @@ -61,7 +61,7 @@ verus! { where S: PmCopy, ensures - self.bytes_in_digest() == old(self).bytes_in_digest() + val.spec_to_bytes(), + final(self).bytes_in_digest() == old(self).bytes_in_digest() + val.spec_to_bytes(), { // Cast `val` to bytes, then add them to the digest. // The crc64fast crate that we use computes the CRC iteratively and does @@ -85,7 +85,7 @@ verus! { #[verifier::external_body] pub fn write_bytes(&mut self, val: &[u8]) ensures - self.bytes_in_digest() == old(self).bytes_in_digest() + val@, + final(self).bytes_in_digest() == old(self).bytes_in_digest() + val@, { self.digest.digest.write(val); } diff --git a/capybaraKV/capybarakv/src/pmem/pmcopy_t.rs b/capybaraKV/capybarakv/src/pmem/pmcopy_t.rs index 72279d17..5e0b8c9e 100644 --- a/capybaraKV/capybarakv/src/pmem/pmcopy_t.rs +++ b/capybaraKV/capybarakv/src/pmem/pmcopy_t.rs @@ -256,7 +256,7 @@ verus! { pmc.maybe_corrupted(bytes@, true_val.spec_to_bytes(), addrs), bytes@.len() == S::spec_size_of(), ensures - self@ == bytes@ + final(self)@ == bytes@ { self.copy_from_slice_helper(bytes); } diff --git a/capybaraKV/capybarakv/src/pmem/pmem_async_spec_t.rs b/capybaraKV/capybarakv/src/pmem/pmem_async_spec_t.rs index 2a38e0b7..2c55d826 100644 --- a/capybaraKV/capybarakv/src/pmem/pmem_async_spec_t.rs +++ b/capybaraKV/capybarakv/src/pmem/pmem_async_spec_t.rs @@ -203,9 +203,9 @@ verus! { old(self).inv(), addr + bytes@.len() <= old(self)@.len(), ensures - self.inv(), - self.constants() == old(self).constants(), - self@ == old(self)@.write(addr as int, bytes@), + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@ == old(self)@.write(addr as int, bytes@), ; fn serialize_and_write(&mut self, addr: u64, to_write: &S) @@ -215,21 +215,21 @@ verus! { old(self).inv(), addr + S::spec_size_of() <= old(self)@.len(), ensures - self.inv(), - self.constants() == old(self).constants(), - self@ == old(self)@.write(addr as int, to_write.spec_to_bytes()), - self@.flush().committed().subrange(addr as int, addr + S::spec_size_of()) == to_write.spec_to_bytes(), + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@ == old(self)@.write(addr as int, to_write.spec_to_bytes()), + final(self)@.flush().committed().subrange(addr as int, addr + S::spec_size_of()) == to_write.spec_to_bytes(), // if we serialize and write an S to this address, we expect to be able to get it back - S::bytes_parseable(self@.flush().committed().subrange(addr as int, addr + S::spec_size_of())), + S::bytes_parseable(final(self)@.flush().committed().subrange(addr as int, addr + S::spec_size_of())), ; fn flush(&mut self) requires old(self).inv() ensures - self.inv(), - self.constants() == old(self).constants(), - self@ == old(self)@.flush(), + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@ == old(self)@.flush(), ; } diff --git a/capybaraKV/capybarakv/src/pmem/pmemspec_t.rs b/capybaraKV/capybarakv/src/pmem/pmemspec_t.rs index 998f9fbf..4d4ca9be 100644 --- a/capybaraKV/capybarakv/src/pmem/pmemspec_t.rs +++ b/capybaraKV/capybarakv/src/pmem/pmemspec_t.rs @@ -353,9 +353,9 @@ verus! { old(self).inv(), addr + bytes@.len() <= old(self)@.len(), ensures - self.inv(), - self.constants() == old(self).constants(), - self@.can_result_from_write(old(self)@, addr as int, bytes@), + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@.can_result_from_write(old(self)@, addr as int, bytes@), ; fn serialize_and_write(&mut self, addr: u64, to_write: &S) @@ -365,9 +365,9 @@ verus! { old(self).inv(), addr + S::spec_size_of() <= old(self)@.len(), ensures - self.inv(), - self.constants() == old(self).constants(), - self@.can_result_from_write(old(self)@, addr as int, to_write.spec_to_bytes()), + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@.can_result_from_write(old(self)@, addr as int, to_write.spec_to_bytes()), ; fn flush(&mut self) @@ -375,9 +375,9 @@ verus! { old(self).inv(), ensures old(self)@.flush_predicted(), // it must have been prophesized that this flush would happen - self.inv(), - self.constants() == old(self).constants(), - self@ == old(self)@, + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@ == old(self)@, ; } diff --git a/capybaraKV/capybarakv/src/pmem/power_sound_t.rs b/capybaraKV/capybarakv/src/pmem/power_sound_t.rs index b13aa676..e1a5e221 100644 --- a/capybaraKV/capybarakv/src/pmem/power_sound_t.rs +++ b/capybaraKV/capybarakv/src/pmem/power_sound_t.rs @@ -1,11 +1,13 @@ #![cfg_attr(verus_keep_ghost, verus::trusted)] +use super::crashinv_t::*; use super::pmemspec_t::*; use super::power_t::*; -use super::crashinv_t::*; -use vstd::prelude::*; -use vstd::invariant::*; -use vstd::tokens::frac::*; use std::sync::Arc; +use vstd::invariant::*; +use vstd::prelude::*; +use vstd::resource::frac::*; +use vstd::resource::ghost_var::*; +use vstd::resource::Loc; verus! { @@ -40,8 +42,8 @@ trait PoWERApplication : Sized requires old(pm).inv(), ensures - pm.inv(), - self.valid(pm@.durable_state); + final(pm).inv(), + self.valid(final(pm)@.durable_state); // run() is the executable function that implements the application's // logic to recover after a crash, taking a PersistentMemoryRegionAtomic @@ -178,7 +180,7 @@ struct DurablePredicate PM: PersistentMemoryRegion, A: PoWERApplication, { - id: int, + id: Loc, app: A, _pm: core::marker::PhantomData, } @@ -213,7 +215,7 @@ impl CheckPermission> for SoundPermission self.inv.constant().app.valid(s2) } - closed spec fn id(&self) -> int { + closed spec fn id(&self) -> Loc { self.inv.constant().id } @@ -240,7 +242,7 @@ impl PermissionFactory> for SoundPermission CheckPermission::permits(self, s1, s2) } - closed spec fn id(&self) -> int { + closed spec fn id(&self) -> Loc { CheckPermission::id(self) } diff --git a/capybaraKV/capybarakv/src/pmem/power_t.rs b/capybaraKV/capybarakv/src/pmem/power_t.rs index c7172b31..3a54334a 100644 --- a/capybaraKV/capybarakv/src/pmem/power_t.rs +++ b/capybaraKV/capybarakv/src/pmem/power_t.rs @@ -1,11 +1,13 @@ #![cfg_attr(verus_keep_ghost, verus::trusted)] -use crate::pmem::pmemspec_t::*; use crate::pmem::pmcopy_t::*; -use vstd::prelude::*; +use crate::pmem::pmemspec_t::*; use vstd::invariant::*; -use vstd::tokens::frac::*; +use vstd::prelude::*; +use vstd::resource::frac::*; +use vstd::resource::ghost_var::*; +use vstd::resource::Loc; -pub use crate::pmem::power_v::{PoWERPersistentMemoryRegion, PermissionFactory}; +pub use crate::pmem::power_v::{PermissionFactory, PoWERPersistentMemoryRegion}; verus! { @@ -14,7 +16,7 @@ pub trait CheckPermission : Sized type Completion; spec fn permits(&self, s1: State, s2: State) -> bool; - spec fn id(&self) -> int; + spec fn id(&self) -> Loc; spec fn completed(&self, c: Self::Completion) -> bool; proof fn apply(tracked self, tracked credit: OpenInvariantCredit, tracked r: &mut GhostVarAuth, new_state: State) -> (tracked complete: Self::Completion) @@ -22,8 +24,8 @@ pub trait CheckPermission : Sized self.id() == old(r).id(), self.permits(old(r)@, new_state), ensures - r.id() == old(r).id(), - r@ == new_state, + final(r).id() == old(r).id(), + final(r)@ == new_state, self.completed(complete), opens_invariants any; @@ -45,7 +47,7 @@ impl PersistentMemoryRegionAtomic { &&& self.res@.id() == self.id() } - pub closed spec fn id(self) -> int { + pub closed spec fn id(self) -> Loc { self.res@.id() } @@ -98,10 +100,10 @@ impl PersistentMemoryRegionAtomic { forall |s| can_result_from_partial_write(s, old(self)@.durable_state, addr as int, bytes@) ==> #[trigger] perm.permits(old(self)@.durable_state, s), ensures - self.inv(), - self.id() == old(self).id(), - self.constants() == old(self).constants(), - self@.can_result_from_write(old(self)@, addr as int, bytes@), + final(self).inv(), + final(self).id() == old(self).id(), + final(self).constants() == old(self).constants(), + final(self)@.can_result_from_write(old(self)@, addr as int, bytes@), perm.completed(result@), { self.pm.write(addr, bytes); @@ -124,10 +126,10 @@ impl PersistentMemoryRegionAtomic { forall |s| can_result_from_partial_write(s, old(self)@.durable_state, addr as int, to_write.spec_to_bytes()) ==> #[trigger] perm.permits(old(self)@.durable_state, s), ensures - self.inv(), - self.id() == old(self).id(), - self.constants() == old(self).constants(), - self@.can_result_from_write(old(self)@, addr as int, to_write.spec_to_bytes()), + final(self).inv(), + final(self).id() == old(self).id(), + final(self).constants() == old(self).constants(), + final(self)@.can_result_from_write(old(self)@, addr as int, to_write.spec_to_bytes()), perm.completed(result@), { broadcast use pmcopy_axioms; @@ -145,11 +147,11 @@ impl PersistentMemoryRegionAtomic { requires old(self).inv(), ensures - self.inv(), - self.id() == old(self).id(), - self.constants() == old(self).constants(), - self@ == old(self)@, - self@.flush_predicted(), + final(self).inv(), + final(self).id() == old(self).id(), + final(self).constants() == old(self).constants(), + final(self)@ == old(self)@, + final(self)@.flush_predicted(), { self.pm.flush() } diff --git a/capybaraKV/capybarakv/src/pmem/power_v.rs b/capybaraKV/capybarakv/src/pmem/power_v.rs index e8f9d630..86fdca3f 100644 --- a/capybaraKV/capybarakv/src/pmem/power_v.rs +++ b/capybaraKV/capybarakv/src/pmem/power_v.rs @@ -1,25 +1,27 @@ #![cfg_attr(verus_keep_ghost, verus::trusted)] -use crate::pmem::pmemspec_t::*; use crate::pmem::pmcopy_t::*; +use crate::pmem::pmemspec_t::*; use crate::pmem::power_t::*; -use vstd::prelude::*; use vstd::invariant::*; -use vstd::tokens::frac::*; +use vstd::prelude::*; +use vstd::resource::frac::*; +use vstd::resource::ghost_var::*; +use vstd::resource::Loc; verus! { pub trait SimpleCheckPermission : Sized { spec fn permits(&self, s: State) -> bool; - spec fn id(&self) -> int; + spec fn id(&self) -> Loc; proof fn apply(tracked self, tracked credit: OpenInvariantCredit, tracked r: &mut GhostVarAuth, new_state: State) requires self.id() == old(r).id(), self.permits(new_state), ensures - r.id() == old(r).id(), - r@ == new_state + final(r).id() == old(r).id(), + final(r)@ == new_state opens_invariants any; } @@ -58,7 +60,7 @@ impl CheckPermission for SimplePermissionAdapter int { + closed spec fn id(&self) -> Loc { self.perm.id() } @@ -77,7 +79,7 @@ pub trait PermissionFactory: Sized type Perm: CheckPermission; spec fn permits(&self, s1: State, s2: State) -> bool; - spec fn id(&self) -> int; + spec fn id(&self) -> Loc; proof fn grant_permission(tracked &self) -> (tracked perm: Self::Perm) ensures @@ -137,7 +139,7 @@ impl CheckPermission for CombinedPermission int { + closed spec fn id(&self) -> Loc { self.a.id() } @@ -183,7 +185,7 @@ impl PoWERPersistentMemoryRegion self.pm_region.constants() } - pub closed spec fn id(&self) -> int + pub closed spec fn id(&self) -> Loc { self.pm_region.id() } @@ -272,10 +274,10 @@ impl PoWERPersistentMemoryRegion forall |s| can_result_from_partial_write(s, old(self)@.durable_state, addr as int, bytes@) ==> #[trigger] perm@.permits(old(self)@.durable_state, s), ensures - self.inv(), - self.constants() == old(self).constants(), - self.id() == old(self).id(), - self@.can_result_from_write(old(self)@, addr as int, bytes@), + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self).id() == old(self).id(), + final(self)@.can_result_from_write(old(self)@, addr as int, bytes@), perm@.completed(result@), { self.pm_region.write(addr, bytes, perm) @@ -299,10 +301,10 @@ impl PoWERPersistentMemoryRegion forall |s| can_result_from_partial_write(s, old(self)@.durable_state, addr as int, to_write.spec_to_bytes()) ==> #[trigger] perm@.permits(old(self)@.durable_state, s), ensures - self.inv(), - self.constants() == old(self).constants(), - self.id() == old(self).id(), - self@.can_result_from_write(old(self)@, addr as int, to_write.spec_to_bytes()), + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self).id() == old(self).id(), + final(self)@.can_result_from_write(old(self)@, addr as int, to_write.spec_to_bytes()), perm@.completed(result@), { self.pm_region.serialize_and_write(addr, to_write, perm) @@ -318,10 +320,10 @@ impl PoWERPersistentMemoryRegion old(self).inv(), ensures old(self)@.flush_predicted(), // it must have been prophesized that this flush would happen - self.inv(), - self.constants() == old(self).constants(), - self.id() == old(self).id(), - self@ == old(self)@, + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self).id() == old(self).id(), + final(self)@ == old(self)@, { self.pm_region.flush() } diff --git a/capybaraKV/capybarakv/src/testkv_v.rs b/capybaraKV/capybarakv/src/testkv_v.rs index b4c74f7c..25cf3ba8 100644 --- a/capybaraKV/capybarakv/src/testkv_v.rs +++ b/capybaraKV/capybarakv/src/testkv_v.rs @@ -8,8 +8,9 @@ use crate::kv2::rwkv_v::*; use crate::kv2::shardkv_t; use crate::kv2::shardkv_t::*; use crate::kv2::shardkv_v::*; -use crate::kv2::spec_t::{AtomicKvStore, KvError, LogicalRange, LogicalRangeGapsPolicy, RecoveredKvStore, - SetupParameters}; +use crate::kv2::spec_t::{ + AtomicKvStore, KvError, LogicalRange, LogicalRangeGapsPolicy, RecoveredKvStore, SetupParameters, +}; // use crate::log::logimpl_t::*; // use crate::multilog::layout_v::*; // use crate::multilog::multilogimpl_t::*; @@ -19,8 +20,6 @@ use crate::kv2::spec_t::{AtomicKvStore, KvError, LogicalRange, LogicalRangeGapsP use crate::pmem::linux_pmemfile_t::*; #[cfg(all(target_os = "linux", not(feature = "pmem")))] use crate::pmem::mmap_pmemfile_t::*; -#[cfg(target_os = "windows")] -use crate::pmem::windows_pmemfile_t::*; #[cfg(target_os = "macos")] use crate::pmem::mmap_pmemfile_t::*; #[cfg(target_os = "macos")] @@ -29,15 +28,19 @@ use crate::pmem::pmcopy_t::*; use crate::pmem::pmemmock_t::*; use crate::pmem::pmemspec_t::*; use crate::pmem::pmemutil_v::*; -use crate::pmem::traits_t::*; use crate::pmem::power_t::*; +use crate::pmem::traits_t::*; +#[cfg(target_os = "windows")] +use crate::pmem::windows_pmemfile_t::*; use pmcopy::PmCopy; use rand::RngExt; -use std::hash::Hash; use std::collections::VecDeque; -use vstd::pcm::*; +use std::hash::Hash; use vstd::pervasive::runtime_assert; -use vstd::tokens::frac::*; +use vstd::resource::frac::*; +use vstd::resource::ghost_var::*; +use vstd::resource::pcm::*; +use vstd::resource::Loc; verus! { @@ -67,7 +70,7 @@ enum TestEnum1 { enum TestEnum2 { V1, V2, - V3, + V3, V4 } @@ -500,7 +503,7 @@ pub fn test_durable_on_memory_mapped_file() { let mut list_region = create_pm_region(list_file_name, region_size); let kvstore_id = generate_fresh_log_id(); - DurableKvStore::<_, TestKey, TestItem, TestListElement>::setup(&mut metadata_region, &mut item_table_region, &mut list_region, &mut log_region, + DurableKvStore::<_, TestKey, TestItem, TestListElement>::setup(&mut metadata_region, &mut item_table_region, &mut list_region, &mut log_region, kvstore_id, num_keys, node_size).unwrap(); let mut log_powerpm = PoWERPersistentMemoryRegion::::new(log_region); @@ -516,7 +519,7 @@ pub fn test_durable_on_memory_mapped_file() { let item1 = TestItem { val: 10 }; let item2 = TestItem { val: 20 }; - // Create a few kv pairs + // Create a few kv pairs let (key1_index, list1_index) = kv_store.tentative_create(&key1, &item1, kvstore_id, Tracked(&fake_kv_permission)).unwrap(); let (key2_index, list2_index) = kv_store.tentative_create(&key2, &item2, kvstore_id, Tracked(&fake_kv_permission)).unwrap(); @@ -574,9 +577,9 @@ pub fn test_durable_on_memory_mapped_file() { // append a list element let list_elem1 = TestListElement { val: 100 }; - kv_store.tentative_append(key1_index, &list_elem1, + kv_store.tentative_append(key1_index, &list_elem1, list1_index, 0, kvstore_id, Tracked(&fake_kv_permission)).unwrap(); - + // before commit, the list length should be 0 runtime_assert(kv_store.get_list_len(kvstore_id, key1_index).unwrap() == 0); // TODO: read fns will currently read whatever is at the specified location (and most likely return a CRC @@ -593,19 +596,19 @@ pub fn test_durable_on_memory_mapped_file() { kv_store.tentative_alloc_list_node(key1_index, kvstore_id, Tracked(&fake_kv_permission)).unwrap(); kv_store.commit(kvstore_id, Tracked(&fake_kv_permission)).unwrap(); - // we should be able to update an existing list entry + // we should be able to update an existing list entry let list_elem2 = TestListElement { val: 15 }; kv_store.tentative_update_list_entry_at_index(list1_index, 0, list_elem2, kvstore_id, Tracked(&fake_kv_permission)).unwrap(); // the new list element should not be visible yet runtime_assert(kv_store.get_list_len(kvstore_id, key1_index).unwrap() == 1); let read_list_elem1 = kv_store.read_list_entry_at_index(list1_index, 0, kvstore_id).unwrap(); - assert(read_list_elem1.val == list_elem1.val); + assert(read_list_elem1.val == list_elem1.val); // after commit, the list length should be the same but the contents of the list changed kv_store.commit(kvstore_id, Tracked(&fake_kv_permission)).unwrap(); runtime_assert(kv_store.get_list_len(kvstore_id, key1_index).unwrap() == 1); let read_list_elem2 = kv_store.read_list_entry_at_index(list1_index, 0, kvstore_id).unwrap(); - assert(read_list_elem2.val == list_elem2.val); + assert(read_list_elem2.val == list_elem2.val); // trimming the list should succeed kv_store.tentative_trim_list(key1_index, list1_index, list1_index, 1, 1, kvstore_id, Tracked(&fake_kv_permission)).unwrap(); @@ -703,7 +706,7 @@ impl ReadLinearizer> Set::empty() } - open spec fn pre(self, id: int, op: ReadItemOp) -> bool + open spec fn pre(self, id: Loc, op: ReadItemOp) -> bool { &&& self.id() == id } @@ -711,7 +714,7 @@ impl ReadLinearizer> open spec fn post( self, completion: Self, - id: int, + id: Loc, op: ReadItemOp, result: Result, ) -> bool @@ -740,7 +743,7 @@ where ghost op: Op, ghost old_ckv: Option>, ghost new_ckv: Option>, - ghost powerpm_id: int, + ghost powerpm_id: Loc, } impl MutatingLinearizer @@ -755,7 +758,7 @@ where Set::empty() } - closed spec fn pre(self, id: int, op: Op) -> bool + closed spec fn pre(self, id: Loc, op: Op) -> bool { &&& self.r.id() == id &&& self.op == op @@ -766,7 +769,7 @@ where closed spec fn post( self, complete: Self::Completion, - id: int, + id: Loc, op: Op, exec_result: Result, ) -> bool @@ -852,7 +855,7 @@ pub fn test_concurrent_kv_on_memory_mapped_file() -> Result<(), ()> op, old_ckv: None, new_ckv: None, - powerpm_id: 0, + powerpm_id: arbitrary(), }; let (create_result, Tracked(create_linearizer)) = ckv.create::>, true>( @@ -968,7 +971,7 @@ pub fn test_sharded_kv_on_memory_mapped_file() -> Result<(), ()> op, old_ckv: None, new_ckv: None, - powerpm_id: 0, + powerpm_id: arbitrary(), }; let (create_result, Tracked(create_linearizer)) = skv.create::>>( diff --git a/multilog/multilog/Cargo.toml b/multilog/multilog/Cargo.toml index e9ec6342..4318e1f6 100644 --- a/multilog/multilog/Cargo.toml +++ b/multilog/multilog/Cargo.toml @@ -20,7 +20,7 @@ default = [ "pmem" ] crc64fast = "1.0.0" # Avoid default features since @lopopolo reports that rand is unsound with both the log and thread_rng features rand = { version = "0.10.1", default-features = false, features = [ "thread_rng" ] } -vstd = "0.0.0-2026-04-20-1748" +vstd = "0.0.0-2026-05-06-1803" pmsafe = { path = "../pmsafe" } [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3.9", features = ["errhandlingapi", "fileapi", "handleapi", "memoryapi", "winbase", "winerror", "winnt"] } diff --git a/multilog/multilog/src/lib.rs b/multilog/multilog/src/lib.rs index 2bd3aad6..cd7c21c6 100644 --- a/multilog/multilog/src/lib.rs +++ b/multilog/multilog/src/lib.rs @@ -14,46 +14,45 @@ use crate::multilog::multilogimpl_v::*; use crate::multilog::multilogspec_t::*; #[cfg(all(target_os = "linux", feature = "pmem"))] use crate::pmem::linux_pmemfile_t::*; -#[cfg(target_os = "windows")] -use crate::pmem::windows_pmemfile_t::*; #[cfg(any(target_os = "macos", all(target_os = "linux", not(feature = "pmem"))))] use crate::pmem::mmap_pmemfile_t::*; use crate::pmem::pmemmock_t::*; use crate::pmem::pmemspec_t::*; use crate::pmem::pmemutil_v::*; +#[cfg(target_os = "windows")] +use crate::pmem::windows_pmemfile_t::*; #[cfg(all(target_os = "linux", feature = "pmem"))] include!("./bindings.rs"); mod tests { -use super::*; -/// This test ensures that the hardcoded constant size of each metadata structure -/// matches the actual size at runtime. This helps ensure that the serde specification -/// for each structure is correct. -// #[verifier::external_body] -#[test] -fn check_layout() { - let global_metadata_size = - core::mem::size_of::(); - let region_metadata_size = - core::mem::size_of::(); - let log_metadata_size = core::mem::size_of::(); - - println!("global metadata struct size: {:?}\n", global_metadata_size); - println!("region metadata struct size: {:?}\n", region_metadata_size); - println!("log metadata struct size: {:?}\n", log_metadata_size); - - assert!(global_metadata_size == LENGTH_OF_GLOBAL_METADATA.try_into().unwrap()); - assert!(region_metadata_size == LENGTH_OF_REGION_METADATA.try_into().unwrap()); - assert!(log_metadata_size == LENGTH_OF_LOG_METADATA.try_into().unwrap()); -} + use super::*; + /// This test ensures that the hardcoded constant size of each metadata structure + /// matches the actual size at runtime. This helps ensure that the serde specification + /// for each structure is correct. + // #[verifier::external_body] + #[test] + fn check_layout() { + let global_metadata_size = + core::mem::size_of::(); + let region_metadata_size = + core::mem::size_of::(); + let log_metadata_size = core::mem::size_of::(); + + println!("global metadata struct size: {:?}\n", global_metadata_size); + println!("region metadata struct size: {:?}\n", region_metadata_size); + println!("log metadata struct size: {:?}\n", log_metadata_size); + + assert!(global_metadata_size == LENGTH_OF_GLOBAL_METADATA.try_into().unwrap()); + assert!(region_metadata_size == LENGTH_OF_REGION_METADATA.try_into().unwrap()); + assert!(log_metadata_size == LENGTH_OF_LOG_METADATA.try_into().unwrap()); + } -#[test] -fn check_multilog_in_volatile_memory() { - assert!(test_multilog_in_volatile_memory()); -} - + #[test] + fn check_multilog_in_volatile_memory() { + assert!(test_multilog_in_volatile_memory()); + } } verus! { diff --git a/multilog/multilog/src/multilog/multilogimpl_t.rs b/multilog/multilog/src/multilog/multilogimpl_t.rs index 019b336f..ae0aa8db 100644 --- a/multilog/multilog/src/multilog/multilogimpl_t.rs +++ b/multilog/multilog/src/multilog/multilogimpl_t.rs @@ -265,40 +265,40 @@ verus! { requires old(pm_regions).inv(), ensures - pm_regions.inv(), - pm_regions@.no_outstanding_writes(), + final(pm_regions).inv(), + final(pm_regions)@.no_outstanding_writes(), match result { Ok((log_capacities, multilog_id)) => { let state = AbstractMultiLogState::initialize(log_capacities@); - &&& pm_regions@.len() == old(pm_regions)@.len() - &&& pm_regions@.len() >= 1 - &&& pm_regions@.len() <= u32::MAX - &&& log_capacities@.len() == pm_regions@.len() - &&& forall |i: int| 0 <= i < pm_regions@.len() ==> - #[trigger] log_capacities@[i] <= pm_regions@[i].len() - &&& forall |i: int| 0 <= i < pm_regions@.len() ==> - #[trigger] pm_regions@[i].len() == old(pm_regions)@[i].len() - &&& can_only_crash_as_state(pm_regions@, multilog_id, state) - &&& UntrustedMultiLogImpl::recover(pm_regions@.committed(), multilog_id) == Some(state) + &&& final(pm_regions)@.len() == old(pm_regions)@.len() + &&& final(pm_regions)@.len() >= 1 + &&& final(pm_regions)@.len() <= u32::MAX + &&& log_capacities@.len() == final(pm_regions)@.len() + &&& forall |i: int| 0 <= i < final(pm_regions)@.len() ==> + #[trigger] log_capacities@[i] <= final(pm_regions)@[i].len() + &&& forall |i: int| 0 <= i < final(pm_regions)@.len() ==> + #[trigger] final(pm_regions)@[i].len() == old(pm_regions)@[i].len() + &&& can_only_crash_as_state(final(pm_regions)@, multilog_id, state) + &&& UntrustedMultiLogImpl::recover(final(pm_regions)@.committed(), multilog_id) == Some(state) // Required by the `start` function's precondition. Putting this in the // postcond of `setup` ensures that the trusted caller doesn't have to prove it - &&& UntrustedMultiLogImpl::recover(pm_regions@.flush().committed(), multilog_id) == Some(state) + &&& UntrustedMultiLogImpl::recover(final(pm_regions)@.flush().committed(), multilog_id) == Some(state) &&& state == state.drop_pending_appends() }, Err(MultiLogErr::InsufficientSpaceForSetup { which_log, required_space }) => { let flushed_regions = old(pm_regions)@.flush(); - &&& pm_regions@ == flushed_regions - &&& pm_regions@[which_log as int].len() < required_space + &&& final(pm_regions)@ == flushed_regions + &&& final(pm_regions)@[which_log as int].len() < required_space }, Err(MultiLogErr::CantSetupWithFewerThanOneRegion { }) => { let flushed_regions = old(pm_regions)@.flush(); - &&& pm_regions@ == flushed_regions - &&& pm_regions@.len() < 1 + &&& final(pm_regions)@ == flushed_regions + &&& final(pm_regions)@.len() < 1 }, Err(MultiLogErr::CantSetupWithMoreThanU32MaxRegions { }) => { let flushed_regions = old(pm_regions)@.flush(); - &&& pm_regions@ == flushed_regions - &&& pm_regions@.len() > u32::MAX + &&& final(pm_regions)@ == flushed_regions + &&& final(pm_regions)@.len() > u32::MAX }, _ => false } @@ -367,25 +367,25 @@ verus! { requires old(self).valid(), ensures - self.valid(), - self.constants() == old(self).constants(), + final(self).valid(), + final(self).constants() == old(self).constants(), match result { Ok(offset) => { let state = old(self)@[which_log as int]; &&& which_log < old(self)@.num_logs() &&& offset == state.head + state.log.len() + state.pending.len() - &&& self@ == old(self)@.tentatively_append(which_log as int, bytes_to_append@) + &&& final(self)@ == old(self)@.tentatively_append(which_log as int, bytes_to_append@) }, Err(MultiLogErr::InvalidLogIndex { }) => { - &&& which_log >= self@.num_logs() - &&& self@ == old(self)@ + &&& which_log >= final(self)@.num_logs() + &&& final(self)@ == old(self)@ }, Err(MultiLogErr::InsufficientSpaceForAppend { available_space }) => { - &&& self@ == old(self)@ - &&& which_log < self@.num_logs() + &&& final(self)@ == old(self)@ + &&& which_log < final(self)@.num_logs() &&& available_space < bytes_to_append@.len() &&& { - let state = self@[which_log as int]; + let state = final(self)@[which_log as int]; ||| available_space == state.capacity - state.log.len() - state.pending.len() ||| available_space == u128::MAX - state.head - state.log.len() - state.pending.len() } @@ -413,10 +413,10 @@ verus! { requires old(self).valid(), ensures - self.valid(), - self.constants() == old(self).constants(), + final(self).valid(), + final(self).constants() == old(self).constants(), match result { - Ok(()) => self@ == old(self)@.commit(), + Ok(()) => final(self)@ == old(self)@.commit(), _ => false, } { @@ -442,29 +442,29 @@ verus! { requires old(self).valid(), ensures - self.valid(), - self.constants() == old(self).constants(), + final(self).valid(), + final(self).constants() == old(self).constants(), match result { Ok(()) => { let w = which_log as int; - &&& which_log < self@.num_logs() + &&& which_log < final(self)@.num_logs() &&& old(self)@[w].head <= new_head <= old(self)@[w].head + old(self)@[w].log.len() - &&& self@ == old(self)@.advance_head(w, new_head as int) + &&& final(self)@ == old(self)@.advance_head(w, new_head as int) }, Err(MultiLogErr::InvalidLogIndex{ }) => { - &&& which_log >= self@.num_logs() - &&& self@ == old(self)@ + &&& which_log >= final(self)@.num_logs() + &&& final(self)@ == old(self)@ }, Err(MultiLogErr::CantAdvanceHeadPositionBeforeHead { head }) => { - &&& self@ == old(self)@ - &&& which_log < self@.num_logs() - &&& head == self@[which_log as int].head + &&& final(self)@ == old(self)@ + &&& which_log < final(self)@.num_logs() + &&& head == final(self)@[which_log as int].head &&& new_head < head }, Err(MultiLogErr::CantAdvanceHeadPositionBeyondTail { tail }) => { - &&& self@ == old(self)@ - &&& which_log < self@.num_logs() - &&& tail == self@[which_log as int].head + self@[which_log as int].log.len() + &&& final(self)@ == old(self)@ + &&& which_log < final(self)@.num_logs() + &&& tail == final(self)@[which_log as int].head + final(self)@[which_log as int].log.len() &&& new_head > tail }, _ => false, diff --git a/multilog/multilog/src/multilog/multilogimpl_v.rs b/multilog/multilog/src/multilog/multilogimpl_v.rs index ba4ea610..d66e7525 100644 --- a/multilog/multilog/src/multilog/multilogimpl_v.rs +++ b/multilog/multilog/src/multilog/multilogimpl_v.rs @@ -160,38 +160,38 @@ verus! { requires old(pm_regions).inv(), ensures - pm_regions.inv(), - pm_regions.constants() == old(pm_regions).constants(), - pm_regions@.no_outstanding_writes(), + final(pm_regions).inv(), + final(pm_regions).constants() == old(pm_regions).constants(), + final(pm_regions)@.no_outstanding_writes(), match result { Ok(log_capacities) => { let state = AbstractMultiLogState::initialize(log_capacities@); - &&& pm_regions@.len() == old(pm_regions)@.len() - &&& pm_regions@.len() >= 1 - &&& pm_regions@.len() <= u32::MAX - &&& log_capacities@.len() == pm_regions@.len() - &&& forall |i: int| 0 <= i < pm_regions@.len() ==> #[trigger] log_capacities@[i] <= pm_regions@[i].len() - &&& forall |i: int| 0 <= i < pm_regions@.len() ==> - #[trigger] pm_regions@[i].len() == old(pm_regions)@[i].len() - &&& can_only_crash_as_state(pm_regions@, multilog_id, state) - &&& Self::recover(pm_regions@.committed(), multilog_id) == Some(state) - &&& Self::recover(pm_regions@.flush().committed(), multilog_id) == Some(state) + &&& final(pm_regions)@.len() == old(pm_regions)@.len() + &&& final(pm_regions)@.len() >= 1 + &&& final(pm_regions)@.len() <= u32::MAX + &&& log_capacities@.len() == final(pm_regions)@.len() + &&& forall |i: int| 0 <= i < final(pm_regions)@.len() ==> #[trigger] log_capacities@[i] <= final(pm_regions)@[i].len() + &&& forall |i: int| 0 <= i < final(pm_regions)@.len() ==> + #[trigger] final(pm_regions)@[i].len() == old(pm_regions)@[i].len() + &&& can_only_crash_as_state(final(pm_regions)@, multilog_id, state) + &&& Self::recover(final(pm_regions)@.committed(), multilog_id) == Some(state) + &&& Self::recover(final(pm_regions)@.flush().committed(), multilog_id) == Some(state) &&& state == state.drop_pending_appends() }, Err(MultiLogErr::InsufficientSpaceForSetup { which_log, required_space }) => { let flushed_regions = old(pm_regions)@.flush(); - &&& pm_regions@ == flushed_regions - &&& pm_regions@[which_log as int].len() < required_space + &&& final(pm_regions)@ == flushed_regions + &&& final(pm_regions)@[which_log as int].len() < required_space }, Err(MultiLogErr::CantSetupWithFewerThanOneRegion { }) => { let flushed_regions = old(pm_regions)@.flush(); - &&& pm_regions@ == flushed_regions - &&& pm_regions@.len() < 1 + &&& final(pm_regions)@ == flushed_regions + &&& final(pm_regions)@.len() < 1 }, Err(MultiLogErr::CantSetupWithMoreThanU32MaxRegions { }) => { let flushed_regions = old(pm_regions)@.flush(); - &&& pm_regions@ == flushed_regions - &&& pm_regions@.len() > u32::MAX + &&& final(pm_regions)@ == flushed_regions + &&& final(pm_regions)@.len() > u32::MAX }, _ => false } @@ -295,20 +295,20 @@ verus! { old(wrpm_regions).inv(), forall |s| #[trigger] perm.check_permission(s) <==> Self::recover(s, multilog_id) == Some(state), ensures - wrpm_regions.inv(), - wrpm_regions.constants() == old(wrpm_regions).constants(), + final(wrpm_regions).inv(), + final(wrpm_regions).constants() == old(wrpm_regions).constants(), match result { Ok(log_impl) => { - &&& log_impl.inv(wrpm_regions, multilog_id) + &&& log_impl.inv(final(wrpm_regions), multilog_id) &&& log_impl@ == state - &&& can_only_crash_as_state(wrpm_regions@, multilog_id, state.drop_pending_appends()) + &&& can_only_crash_as_state(final(wrpm_regions)@, multilog_id, state.drop_pending_appends()) }, - Err(MultiLogErr::CRCMismatch) => !wrpm_regions.constants().impervious_to_corruption, + Err(MultiLogErr::CRCMismatch) => !final(wrpm_regions).constants().impervious_to_corruption, Err(MultiLogErr::InsufficientSpaceForSetup { which_log, required_space }) => { let flushed_regions = old(wrpm_regions)@.flush(); &&& 0 <= which_log < flushed_regions.len() - &&& wrpm_regions@ == flushed_regions - &&& wrpm_regions@[which_log as int].len() < required_space + &&& final(wrpm_regions)@ == flushed_regions + &&& final(wrpm_regions)@[which_log as int].len() < required_space }, _ => false } @@ -399,26 +399,26 @@ verus! { forall |s| #[trigger] perm.check_permission(s) <==> Self::recover(s, multilog_id) == Some(old(self)@.drop_pending_appends()), ensures - self.inv(wrpm_regions, multilog_id), - wrpm_regions.constants() == old(wrpm_regions).constants(), - can_only_crash_as_state(wrpm_regions@, multilog_id, self@.drop_pending_appends()), + final(self).inv(final(wrpm_regions), multilog_id), + final(wrpm_regions).constants() == old(wrpm_regions).constants(), + can_only_crash_as_state(final(wrpm_regions)@, multilog_id, final(self)@.drop_pending_appends()), match result { Ok(offset) => { let state = old(self)@[which_log as int]; &&& which_log < old(self)@.num_logs() &&& offset == state.head + state.log.len() + state.pending.len() - &&& self@ == old(self)@.tentatively_append(which_log as int, bytes_to_append@) + &&& final(self)@ == old(self)@.tentatively_append(which_log as int, bytes_to_append@) }, Err(MultiLogErr::InvalidLogIndex { }) => { - &&& self@ == old(self)@ - &&& which_log >= self@.num_logs() + &&& final(self)@ == old(self)@ + &&& which_log >= final(self)@.num_logs() }, Err(MultiLogErr::InsufficientSpaceForAppend { available_space }) => { - &&& self@ == old(self)@ - &&& which_log < self@.num_logs() + &&& final(self)@ == old(self)@ + &&& which_log < final(self)@.num_logs() &&& available_space < bytes_to_append@.len() &&& { - let state = self@[which_log as int]; + let state = final(self)@[which_log as int]; ||| available_space == state.capacity - state.log.len() - state.pending.len() ||| available_space == u128::MAX - state.head - state.log.len() - state.pending.len() } @@ -653,9 +653,9 @@ verus! { ABSOLUTE_POS_OF_LOG_AREA < old(wrpm_regions)@[i].len(), old(wrpm_regions)@.len() > 0, ensures - self.inv(wrpm_regions, multilog_id), - wrpm_regions.constants() == old(wrpm_regions).constants(), - self.state == old(self).state, + final(self).inv(final(wrpm_regions), multilog_id), + final(wrpm_regions).constants() == old(wrpm_regions).constants(), + final(self).state == old(self).state, { broadcast use pmcopy_axioms; @@ -835,33 +835,33 @@ verus! { ABSOLUTE_POS_OF_LOG_AREA < old(wrpm_regions)@[i].len(), old(wrpm_regions)@.len() > 0, ensures - wrpm_regions.inv(), - self.state == old(self).state, - wrpm_regions.constants() == old(wrpm_regions).constants(), - memory_matches_deserialized_cdb(wrpm_regions@, self.cdb), - each_metadata_consistent_with_info(wrpm_regions@, multilog_id, self.num_logs, self.cdb, prev_infos), - each_info_consistent_with_log_area(wrpm_regions@, self.num_logs, prev_infos, prev_state), - each_info_consistent_with_log_area(wrpm_regions@.flush(), self.num_logs, self.infos@, self.state@), + final(wrpm_regions).inv(), + final(self).state == old(self).state, + final(wrpm_regions).constants() == old(wrpm_regions).constants(), + memory_matches_deserialized_cdb(final(wrpm_regions)@, final(self).cdb), + each_metadata_consistent_with_info(final(wrpm_regions)@, multilog_id, final(self).num_logs, final(self).cdb, prev_infos), + each_info_consistent_with_log_area(final(wrpm_regions)@, final(self).num_logs, prev_infos, prev_state), + each_info_consistent_with_log_area(final(wrpm_regions)@.flush(), final(self).num_logs, final(self).infos@, final(self).state@), forall |s| Self::recover(s, multilog_id) == Some(prev_state.drop_pending_appends()) ==> #[trigger] perm.check_permission(s), - forall |i: int| #[trigger] log_index_trigger(i) && 0 <= i < self.num_logs ==> - self.infos@[i].log_area_len == prev_infos[i].log_area_len, - forall |which_log: u32| #[trigger] log_index_trigger(which_log as int) && which_log < self.num_logs ==> { + forall |i: int| #[trigger] log_index_trigger(i) && 0 <= i < final(self).num_logs ==> + final(self).infos@[i].log_area_len == prev_infos[i].log_area_len, + forall |which_log: u32| #[trigger] log_index_trigger(which_log as int) && which_log < final(self).num_logs ==> { let w = which_log as int; - let flushed = wrpm_regions@.flush(); - metadata_consistent_with_info(flushed[w], multilog_id, self.num_logs, which_log, - !self.cdb, self.infos@[w]) + let flushed = final(wrpm_regions)@.flush(); + metadata_consistent_with_info(flushed[w], multilog_id, final(self).num_logs, which_log, + !final(self).cdb, final(self).infos@[w]) }, - no_outstanding_writes_to_active_metadata(wrpm_regions@, self.cdb), - metadata_types_set(wrpm_regions@.committed()), - active_metadata_is_equal(old(wrpm_regions)@, wrpm_regions@), - forall |i: int| #[trigger] log_index_trigger(i) && 0 <= i < wrpm_regions@.len() ==> - wrpm_regions@[i].len() == old(wrpm_regions)@[i].len(), - forall |i: int| #[trigger] log_index_trigger(i) && 0 <= i < wrpm_regions@.len() ==> - ABSOLUTE_POS_OF_LOG_AREA < wrpm_regions@[i].len(), - forall |i: int| #[trigger] log_index_trigger(i) && 0 <= i < wrpm_regions@.len() ==> - inactive_metadata_types_set_in_region(wrpm_regions@.flush().committed()[i], self.cdb), - wrpm_regions@.len() > 0, + no_outstanding_writes_to_active_metadata(final(wrpm_regions)@, final(self).cdb), + metadata_types_set(final(wrpm_regions)@.committed()), + active_metadata_is_equal(old(wrpm_regions)@, final(wrpm_regions)@), + forall |i: int| #[trigger] log_index_trigger(i) && 0 <= i < final(wrpm_regions)@.len() ==> + final(wrpm_regions)@[i].len() == old(wrpm_regions)@[i].len(), + forall |i: int| #[trigger] log_index_trigger(i) && 0 <= i < final(wrpm_regions)@.len() ==> + ABSOLUTE_POS_OF_LOG_AREA < final(wrpm_regions)@[i].len(), + forall |i: int| #[trigger] log_index_trigger(i) && 0 <= i < final(wrpm_regions)@.len() ==> + inactive_metadata_types_set_in_region(final(wrpm_regions)@.flush().committed()[i], final(self).cdb), + final(wrpm_regions)@.len() > 0, { broadcast use pmcopy_axioms; @@ -1102,11 +1102,11 @@ verus! { ||| Self::recover(s, multilog_id) == Some(old(self)@.commit().drop_pending_appends()) }, ensures - self.inv(wrpm_regions, multilog_id), - wrpm_regions.constants() == old(wrpm_regions).constants(), - can_only_crash_as_state(wrpm_regions@, multilog_id, self@.drop_pending_appends()), + final(self).inv(final(wrpm_regions), multilog_id), + final(wrpm_regions).constants() == old(wrpm_regions).constants(), + can_only_crash_as_state(final(wrpm_regions)@, multilog_id, final(self)@.drop_pending_appends()), result is Ok, - self@ == old(self)@.commit(), + final(self)@ == old(self)@.commit(), { let ghost prev_infos = self.infos@; let ghost prev_state = self.state@; @@ -1203,30 +1203,30 @@ verus! { Some(old(self)@.advance_head(which_log as int, new_head as int).drop_pending_appends()) }, ensures - self.inv(wrpm_regions, multilog_id), - wrpm_regions.constants() == old(wrpm_regions).constants(), - can_only_crash_as_state(wrpm_regions@, multilog_id, self@.drop_pending_appends()), + final(self).inv(final(wrpm_regions), multilog_id), + final(wrpm_regions).constants() == old(wrpm_regions).constants(), + can_only_crash_as_state(final(wrpm_regions)@, multilog_id, final(self)@.drop_pending_appends()), match result { Ok(()) => { let w = which_log as int; - &&& which_log < self@.num_logs() + &&& which_log < final(self)@.num_logs() &&& old(self)@[w].head <= new_head <= old(self)@[w].head + old(self)@[w].log.len() - &&& self@ == old(self)@.advance_head(w, new_head as int) + &&& final(self)@ == old(self)@.advance_head(w, new_head as int) }, Err(MultiLogErr::InvalidLogIndex{ }) => { - &&& self@ == old(self)@ - &&& which_log >= self@.num_logs() + &&& final(self)@ == old(self)@ + &&& which_log >= final(self)@.num_logs() }, Err(MultiLogErr::CantAdvanceHeadPositionBeforeHead { head }) => { - &&& self@ == old(self)@ - &&& which_log < self@.num_logs() - &&& head == self@[which_log as int].head + &&& final(self)@ == old(self)@ + &&& which_log < final(self)@.num_logs() + &&& head == final(self)@[which_log as int].head &&& new_head < head }, Err(MultiLogErr::CantAdvanceHeadPositionBeyondTail { tail }) => { - &&& self@ == old(self)@ - &&& which_log < self@.num_logs() - &&& tail == self@[which_log as int].head + self@[which_log as int].log.len() + &&& final(self)@ == old(self)@ + &&& which_log < final(self)@.num_logs() + &&& tail == final(self)@[which_log as int].head + final(self)@[which_log as int].log.len() &&& new_head > tail }, _ => false diff --git a/multilog/multilog/src/multilog/setup_v.rs b/multilog/multilog/src/multilog/setup_v.rs index b93ee559..da406e2c 100644 --- a/multilog/multilog/src/multilog/setup_v.rs +++ b/multilog/multilog/src/multilog/setup_v.rs @@ -173,14 +173,14 @@ verus! { region_size >= ABSOLUTE_POS_OF_LOG_AREA + MIN_LOG_AREA_SIZE, which_log != 0, ensures - pm_regions.inv(), - pm_regions.constants() == old(pm_regions).constants(), - pm_regions@.len() == old(pm_regions)@.len(), - forall |i: int| 0 <= i < pm_regions@.len() && i != which_log ==> pm_regions@[i] =~= old(pm_regions)@[i], + final(pm_regions).inv(), + final(pm_regions).constants() == old(pm_regions).constants(), + final(pm_regions)@.len() == old(pm_regions)@.len(), + forall |i: int| 0 <= i < final(pm_regions)@.len() && i != which_log ==> final(pm_regions)@[i] =~= old(pm_regions)@[i], memory_correctly_set_up_on_single_region( - pm_regions@[which_log as int].flush().committed(), // it'll be correct after the next flush + final(pm_regions)@[which_log as int].flush().committed(), // it'll be correct after the next flush region_size, multilog_id, num_logs, which_log), - metadata_types_set_in_region(pm_regions@[which_log as int].flush().committed(), false), + metadata_types_set_in_region(final(pm_regions)@[which_log as int].flush().committed(), false), { broadcast use pmcopy_axioms; reveal(spec_padding_needed); @@ -269,17 +269,17 @@ verus! { region_size >= ABSOLUTE_POS_OF_LOG_AREA + MIN_LOG_AREA_SIZE, ensures - pm_regions.inv(), - pm_regions.constants() == old(pm_regions).constants(), - pm_regions@.len() == old(pm_regions)@.len(), - forall |i: int| 1 <= i < pm_regions@.len() ==> pm_regions@[i] =~= old(pm_regions)@[i], + final(pm_regions).inv(), + final(pm_regions).constants() == old(pm_regions).constants(), + final(pm_regions)@.len() == old(pm_regions)@.len(), + forall |i: int| 1 <= i < final(pm_regions)@.len() ==> final(pm_regions)@[i] =~= old(pm_regions)@[i], memory_correctly_set_up_on_single_region( - pm_regions@[0].flush().committed(), // it'll be correct after the next flush + final(pm_regions)@[0].flush().committed(), // it'll be correct after the next flush region_size, multilog_id, num_logs, 0), - metadata_types_set_in_first_region(pm_regions@[0].flush().committed()), - metadata_types_set_in_region(pm_regions@[0].flush().committed(), false), - deserialize_and_check_log_cdb(pm_regions@[0].flush().committed()) is Some, - !deserialize_and_check_log_cdb(pm_regions@[0].flush().committed()).unwrap(), + metadata_types_set_in_first_region(final(pm_regions)@[0].flush().committed()), + metadata_types_set_in_region(final(pm_regions)@[0].flush().committed(), false), + deserialize_and_check_log_cdb(final(pm_regions)@[0].flush().committed()) is Some, + !deserialize_and_check_log_cdb(final(pm_regions)@[0].flush().committed()).unwrap(), { broadcast use pmcopy_axioms; reveal(spec_padding_needed); @@ -399,14 +399,14 @@ verus! { old(pm_regions)@[i].len() == log_capacities[i] + ABSOLUTE_POS_OF_LOG_AREA, old(pm_regions)@.no_outstanding_writes(), ensures - pm_regions.inv(), - pm_regions.constants() == old(pm_regions).constants(), - pm_regions@.len() == old(pm_regions)@.len(), - forall |i: int| #[trigger] log_index_trigger(i) && 0 <= i < pm_regions@.len() ==> - pm_regions@[i].len() == old(pm_regions)@[i].len(), - pm_regions@.no_outstanding_writes(), - recover_all(pm_regions@.committed(), multilog_id) == Some(AbstractMultiLogState::initialize(log_capacities)), - metadata_types_set(pm_regions@.committed()), + final(pm_regions).inv(), + final(pm_regions).constants() == old(pm_regions).constants(), + final(pm_regions)@.len() == old(pm_regions)@.len(), + forall |i: int| #[trigger] log_index_trigger(i) && 0 <= i < final(pm_regions)@.len() ==> + final(pm_regions)@[i].len() == old(pm_regions)@[i].len(), + final(pm_regions)@.no_outstanding_writes(), + recover_all(final(pm_regions)@.committed(), multilog_id) == Some(AbstractMultiLogState::initialize(log_capacities)), + metadata_types_set(final(pm_regions)@.committed()), { // Loop `which_log` from 0 to `region_sizes.len() - 1`, each time // setting up the metadata for region `which_log`. diff --git a/multilog/multilog/src/pmem/crc_t.rs b/multilog/multilog/src/pmem/crc_t.rs index 6a3b856c..a79b8685 100644 --- a/multilog/multilog/src/pmem/crc_t.rs +++ b/multilog/multilog/src/pmem/crc_t.rs @@ -48,7 +48,7 @@ verus! { where S: PmCopy, ensures - self.bytes_in_digest() == old(self).bytes_in_digest().push(val.spec_to_bytes()) + final(self).bytes_in_digest() == old(self).bytes_in_digest().push(val.spec_to_bytes()) { // Cast `val` to bytes, then add them to the digest. // The crc64fast crate that we use computes the CRC iteratively and does @@ -70,7 +70,7 @@ verus! { #[verifier::external_body] pub fn write_bytes(&mut self, val: &[u8]) ensures - self.bytes_in_digest() == old(self).bytes_in_digest().push(val@) + final(self).bytes_in_digest() == old(self).bytes_in_digest().push(val@) { self.digest.digest.write(val); } diff --git a/multilog/multilog/src/pmem/pmcopy_t.rs b/multilog/multilog/src/pmem/pmcopy_t.rs index 2955dcd8..993eaf79 100644 --- a/multilog/multilog/src/pmem/pmcopy_t.rs +++ b/multilog/multilog/src/pmem/pmcopy_t.rs @@ -199,7 +199,7 @@ verus! { }, bytes@.len() == S::spec_size_of(), ensures - self@ == bytes@ + final(self)@ == bytes@ { self.copy_from_slice_helper(bytes); } diff --git a/multilog/multilog/src/pmem/pmemspec_t.rs b/multilog/multilog/src/pmem/pmemspec_t.rs index d2112b35..44cbdff8 100644 --- a/multilog/multilog/src/pmem/pmemspec_t.rs +++ b/multilog/multilog/src/pmem/pmemspec_t.rs @@ -467,9 +467,9 @@ verus! { // Writes aren't allowed where there are already outstanding writes. old(self)@.no_outstanding_writes_in_range(addr as int, addr + bytes@.len()), ensures - self.inv(), - self.constants() == old(self).constants(), - self@ == old(self)@.write(addr as int, bytes@), + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@ == old(self)@.write(addr as int, bytes@), ; fn serialize_and_write(&mut self, addr: u64, to_write: &S) @@ -480,10 +480,10 @@ verus! { addr + S::spec_size_of() <= old(self)@.len(), old(self)@.no_outstanding_writes_in_range(addr as int, addr + S::spec_size_of()), ensures - self.inv(), - self.constants() == old(self).constants(), - self@ == old(self)@.write(addr as int, to_write.spec_to_bytes()), - self@.flush().committed().subrange(addr as int, addr + S::spec_size_of()) == to_write.spec_to_bytes(), + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@ == old(self)@.write(addr as int, to_write.spec_to_bytes()), + final(self)@.flush().committed().subrange(addr as int, addr + S::spec_size_of()) == to_write.spec_to_bytes(), ; @@ -491,9 +491,9 @@ verus! { requires old(self).inv() ensures - self.inv(), - self.constants() == old(self).constants(), - self@ == old(self)@.flush(), + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@ == old(self)@.flush(), ; } @@ -587,9 +587,9 @@ verus! { // Writes aren't allowed where there are already outstanding writes. old(self)@.no_outstanding_writes_in_range(index as int, addr as int, addr + bytes@.len()), ensures - self.inv(), - self.constants() == old(self).constants(), - self@ == old(self)@.write(index as int, addr as int, bytes@), + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@ == old(self)@.write(index as int, addr as int, bytes@), ; // Note that addr is a regular offset in terms of bytes, but to_write is type S @@ -602,18 +602,18 @@ verus! { addr + S::spec_size_of() <= old(self)@[index as int].len(), old(self)@.no_outstanding_writes_in_range(index as int, addr as int, addr + S::spec_size_of()), ensures - self.inv(), - self.constants() == old(self).constants(), - self@ == old(self)@.write(index as int, addr as int, to_write.spec_to_bytes()), + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@ == old(self)@.write(index as int, addr as int, to_write.spec_to_bytes()), ; fn flush(&mut self) requires old(self).inv(), ensures - self.inv(), - self.constants() == old(self).constants(), - self@ == old(self)@.flush(), + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@ == old(self)@.flush(), ; } diff --git a/multilog/multilog/src/pmem/subregion_v.rs b/multilog/multilog/src/pmem/subregion_v.rs index 592c54c0..c66c37ec 100644 --- a/multilog/multilog/src/pmem/subregion_v.rs +++ b/multilog/multilog/src/pmem/subregion_v.rs @@ -544,8 +544,8 @@ impl WriteRestrictedPersistentMemorySubregion relative_addr + bytes.len()), forall |i: int| relative_addr <= i < relative_addr + bytes@.len() ==> self.is_writable_relative_addr(i), ensures - self.inv(wrpm, perm), - self.view(wrpm) == self.view(old(wrpm)).write(relative_addr as int, bytes@), + self.inv(final(wrpm), perm), + self.view(final(wrpm)) == self.view(old(wrpm)).write(relative_addr as int, bytes@), { let ghost subregion_view = self.view(wrpm).write(relative_addr as int, bytes@); assert(forall |addr| #![trigger self.is_writable_absolute_addr_fn()(addr)] @@ -593,8 +593,8 @@ impl WriteRestrictedPersistentMemorySubregion forall |i: int| absolute_addr <= i < absolute_addr + bytes@.len() ==> #[trigger] self.is_writable_absolute_addr_fn()(i), ensures - self.inv(wrpm, perm), - self.view(wrpm) == self.view(old(wrpm)).write(absolute_addr - self.start(), bytes@), + self.inv(final(wrpm), perm), + self.view(final(wrpm)) == self.view(old(wrpm)).write(absolute_addr - self.start(), bytes@), { let ghost subregion_view = self.view(wrpm).write(absolute_addr - self.start(), bytes@); assert forall |i| #![trigger wrpm@.state[i]] @@ -625,8 +625,8 @@ impl WriteRestrictedPersistentMemorySubregion forall |i: int| relative_addr <= i < relative_addr + S::spec_size_of() ==> self.is_writable_relative_addr(i), ensures - self.inv(wrpm, perm), - self.view(wrpm) == self.view(old(wrpm)).write(relative_addr as int, to_write.spec_to_bytes()), + self.inv(final(wrpm), perm), + self.view(final(wrpm)) == self.view(old(wrpm)).write(relative_addr as int, to_write.spec_to_bytes()), { let ghost bytes = to_write.spec_to_bytes(); assert(bytes.len() == S::spec_size_of()); @@ -677,8 +677,8 @@ impl WriteRestrictedPersistentMemorySubregion forall |i: int| absolute_addr <= i < absolute_addr + S::spec_size_of() ==> #[trigger] self.is_writable_absolute_addr_fn()(i), ensures - self.inv(wrpm, perm), - self.view(wrpm) == self.view(old(wrpm)).write(absolute_addr - self.start(), + self.inv(final(wrpm), perm), + self.view(final(wrpm)) == self.view(old(wrpm)).write(absolute_addr - self.start(), to_write.spec_to_bytes()), { let ghost bytes = to_write.spec_to_bytes(); @@ -1285,8 +1285,8 @@ impl WritablePersistentMemorySubregion relative_addr + bytes.len()), forall |i: int| relative_addr <= i < relative_addr + bytes@.len() ==> self.is_writable_relative_addr(i), ensures - self.inv(pm), - self.view(pm) == self.view(old(pm)).write(relative_addr as int, bytes@), + self.inv(final(pm)), + self.view(final(pm)) == self.view(old(pm)).write(relative_addr as int, bytes@), { let ghost subregion_view = self.view(pm).write(relative_addr as int, bytes@); assert(forall |addr| #![trigger self.is_writable_absolute_addr_fn()(addr)] @@ -1317,8 +1317,8 @@ impl WritablePersistentMemorySubregion forall |i: int| absolute_addr <= i < absolute_addr + bytes@.len() ==> #[trigger] self.is_writable_absolute_addr_fn()(i), ensures - self.inv(pm), - self.view(pm) == self.view(old(pm)).write(absolute_addr - self.start(), bytes@), + self.inv(final(pm)), + self.view(final(pm)) == self.view(old(pm)).write(absolute_addr - self.start(), bytes@), { let ghost subregion_view = self.view(pm).write(absolute_addr - self.start(), bytes@); assert forall |i| #![trigger pm@.state[i]] @@ -1347,8 +1347,8 @@ impl WritablePersistentMemorySubregion forall |i: int| relative_addr <= i < relative_addr + S::spec_size_of() ==> self.is_writable_relative_addr(i), ensures - self.inv(pm), - self.view(pm) == self.view(old(pm)).write(relative_addr as int, to_write.spec_to_bytes()), + self.inv(final(pm)), + self.view(final(pm)) == self.view(old(pm)).write(relative_addr as int, to_write.spec_to_bytes()), { let ghost bytes = to_write.spec_to_bytes(); assert(bytes.len() == S::spec_size_of()); @@ -1384,8 +1384,8 @@ impl WritablePersistentMemorySubregion forall |i: int| absolute_addr <= i < absolute_addr + S::spec_size_of() ==> #[trigger] self.is_writable_absolute_addr_fn()(i), ensures - self.inv(pm), - self.view(pm) == self.view(old(pm)).write(absolute_addr - self.start(), + self.inv(final(pm)), + self.view(final(pm)) == self.view(old(pm)).write(absolute_addr - self.start(), to_write.spec_to_bytes()), { let ghost bytes = to_write.spec_to_bytes(); diff --git a/multilog/multilog/src/pmem/wrpm_t.rs b/multilog/multilog/src/pmem/wrpm_t.rs index b24b0872..96c8a271 100644 --- a/multilog/multilog/src/pmem/wrpm_t.rs +++ b/multilog/multilog/src/pmem/wrpm_t.rs @@ -91,9 +91,9 @@ impl WriteRestrictedPersistentMemoryRegions forall |s| old(self)@.write(index as int, addr as int, bytes@).can_crash_as(s) ==> #[trigger] perm@.check_permission(s), ensures - self.inv(), - self.constants() == old(self).constants(), - self@ == old(self)@.write(index as int, addr as int, bytes@), + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@ == old(self)@.write(index as int, addr as int, bytes@), { self.pm_regions.write(index, addr, bytes) } @@ -111,9 +111,9 @@ impl WriteRestrictedPersistentMemoryRegions forall |s| old(self)@.write(index as int, addr as int, to_write.spec_to_bytes()).can_crash_as(s) ==> #[trigger] perm@.check_permission(s), ensures - self.inv(), - self.constants() == old(self).constants(), - self@ == old(self)@.write(index as int, addr as int, to_write.spec_to_bytes()), + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@ == old(self)@.write(index as int, addr as int, to_write.spec_to_bytes()), { self.pm_regions.serialize_and_write(index, addr, to_write); } @@ -127,10 +127,10 @@ impl WriteRestrictedPersistentMemoryRegions requires old(self).inv(), ensures - self.inv(), - self@ == old(self)@.flush(), - self.constants() == old(self).constants(), - forall |i: int| #![auto] 0 <= i < self@.len() ==> old(self)@[i].len() == self@[i].len() + final(self).inv(), + final(self)@ == old(self)@.flush(), + final(self).constants() == old(self).constants(), + forall |i: int| #![auto] 0 <= i < final(self)@.len() ==> old(self)@[i].len() == final(self)@[i].len() { self.pm_regions.flush() } @@ -211,9 +211,9 @@ impl WriteRestrictedPersistentMemoryRegion forall |s| old(self)@.write(addr as int, bytes@).can_crash_as(s) ==> #[trigger] perm@.check_permission(s), ensures - self.inv(), - self.constants() == old(self).constants(), - self@ == old(self)@.write(addr as int, bytes@), + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@ == old(self)@.write(addr as int, bytes@), { let ghost pmr = self.pm_region; self.pm_region.write(addr, bytes); @@ -231,9 +231,9 @@ impl WriteRestrictedPersistentMemoryRegion forall |s| old(self)@.write(addr as int, to_write.spec_to_bytes()).can_crash_as(s) ==> #[trigger] perm@.check_permission(s), ensures - self.inv(), - self.constants() == old(self).constants(), - self@ == old(self)@.write(addr as int, to_write.spec_to_bytes()), + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@ == old(self)@.write(addr as int, to_write.spec_to_bytes()), { self.pm_region.serialize_and_write(addr, to_write); } @@ -247,9 +247,9 @@ impl WriteRestrictedPersistentMemoryRegion requires old(self).inv(), ensures - self.inv(), - self@ == old(self)@.flush(), - self.constants() == old(self).constants(), + final(self).inv(), + final(self)@ == old(self)@.flush(), + final(self).constants() == old(self).constants(), { self.pm_region.flush() } diff --git a/pmemlog/Cargo.toml b/pmemlog/Cargo.toml index 912a9ccb..63989de8 100644 --- a/pmemlog/Cargo.toml +++ b/pmemlog/Cargo.toml @@ -6,11 +6,11 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -vstd = "0.0.0-2026-04-20-1748" +vstd = "0.0.0-2026-05-06-1803" crc64fast = "1.0.0" [lints.rust] unexpected_cfgs = { level = "allow", check-cfg = ["cfg(verus_keep_ghost)"] } [package.metadata.verus] -verify = true \ No newline at end of file +verify = true diff --git a/pmemlog/src/logimpl_v.rs b/pmemlog/src/logimpl_v.rs index 3a4dde29..85d0bfb4 100644 --- a/pmemlog/src/logimpl_v.rs +++ b/pmemlog/src/logimpl_v.rs @@ -1,2091 +1,2091 @@ -#![allow(dead_code)] -#![allow(non_upper_case_globals)] -#![allow(unused_variables)] - -use crate::infinitelog_t::*; -use crate::main_t::*; -use crate::math_v::*; -use crate::pmemspec_t::*; -use crate::sccf::CheckPermission; -use core::convert::TryInto; -use std::f32::consts::E; -use std::fmt::Write; -use vstd::arithmetic::div_mod::*; -use vstd::bytes::*; -use vstd::prelude::*; -use vstd::seq::*; -use vstd::set::*; -use vstd::slice::*; - -verus! { - - // entire header structure: - // bytes 0-7: incorruptible boolean - // bytes 8-39: header 1 - // bytes 40-71: header 2 - - // header version structure: - // 0-7: header CRC - // 8-15: logical head - // 16-23: logical tail - // 24-31: log size - - pub const incorruptible_bool_pos: u64 = 0; - pub const header1_pos: u64 = 8; - pub const header2_pos: u64 = 40; - - // offsets of fields within the header structure - pub const header_crc_offset: u64 = 0; - pub const header_head_offset: u64 = 8; - pub const header_tail_offset: u64 = 16; - pub const header_log_size_offset: u64 = 24; - - pub const header_size: u64 = 32; - - /// Converts the view of a PM region into its incorruptible Boolean, a view of its header, - /// and a data region. - pub open spec fn pm_to_views(pm: Seq) -> (u64, HeaderView, Seq) - { - let incorruptible_bool = spec_u64_from_le_bytes(pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8)); - // read the CRC, then read the rest of the metadata, then combine them - let crc1 = spec_u64_from_le_bytes(pm.subrange(header1_pos + header_crc_offset, header1_pos + header_crc_offset + 8)); - let crc2 = spec_u64_from_le_bytes(pm.subrange(header2_pos + header_crc_offset, header2_pos + header_crc_offset + 8)); - - let header1_metadata = spec_bytes_to_metadata(pm.subrange(header1_pos + header_head_offset, header1_pos + header_size)); - let header2_metadata = spec_bytes_to_metadata(pm.subrange(header2_pos + header_head_offset, header2_pos + header_size)); - let header_view = HeaderView { - header1: PersistentHeader { - crc: crc1, - metadata: header1_metadata, - }, - header2: PersistentHeader { - crc: crc2, - metadata: header2_metadata, - } - }; - let data_view = pm.subrange(contents_offset as int, pm.len() as int); - ( - incorruptible_bool, - header_view, - data_view - ) - } - - pub open spec fn spec_get_live_header(pm: Seq) -> PersistentHeader - { - let (ib, headers, _) = pm_to_views(pm); - if ib == cdb0_val { - headers.header1 - } else { - headers.header2 - } - } - - pub open spec fn permissions_depend_only_on_recovery_view>>(perm: &Perm) -> bool - { - forall |s1, s2| recovery_view()(s1) == recovery_view()(s2) ==> perm.check_permission(s1) == perm.check_permission(s2) - } - - pub proof fn lemma_same_permissions>>(pm1: Seq, pm2: Seq, perm: &Perm) - requires - recovery_view()(pm1) =~= recovery_view()(pm2), - perm.check_permission(pm1), - permissions_depend_only_on_recovery_view(perm) - ensures - perm.check_permission(pm2) - {} - - /// Proves that a PM region has the given header at the given position. Useful for - /// associating a region with a header structure when the struct will be used later - /// in a proof. - pub proof fn lemma_header_match(pm: Seq, header_pos: int, header: PersistentHeader) - requires - pm.len() > contents_offset, - header_pos == header1_pos || header_pos == header2_pos, - spec_bytes_to_header(pm.subrange(header_pos as int, header_pos + header_size)) == header, - ensures - ({ - let (_, headers, _) = pm_to_views(pm); - &&& header_pos == header1_pos ==> - headers.header1 == header - &&& header_pos == header2_pos ==> - headers.header2 == header - }) - { - assert(pm.subrange(header_pos as int, header_pos + header_size) =~= - pm.subrange(header_pos + header_crc_offset, header_pos + header_crc_offset + 8) + - pm.subrange(header_pos + header_head_offset, header_pos + header_size) - ); - lemma_bytes_combine_into_header( - pm.subrange(header_pos + header_crc_offset, header_pos + header_crc_offset + 8), - pm.subrange(header_pos + header_head_offset, header_pos + header_size), - header - ); - } - - /// Proves that a given header structure consists of a CRC given in bytes as `crc_bytes` and a metadata structure - /// given in bytes as `metadata_bytes`. - pub proof fn lemma_bytes_combine_into_header(crc_bytes: Seq, metadata_bytes: Seq, header: PersistentHeader) - requires - crc_bytes.len() == 8, - metadata_bytes.len() == header_size - 8, - spec_bytes_to_header((crc_bytes + metadata_bytes)) == header, - ensures - ({ - let combined_header = PersistentHeader { crc: spec_u64_from_le_bytes(crc_bytes), metadata: spec_bytes_to_metadata(metadata_bytes) }; - header == combined_header - }) - { - let crc_val = spec_u64_from_le_bytes(crc_bytes); - let metadata = spec_bytes_to_metadata(metadata_bytes); - lemma_seq_addition(crc_bytes, metadata_bytes); - - let combined_header = spec_bytes_to_header((crc_bytes + metadata_bytes)); - assert(combined_header.crc == crc_val); - assert(metadata == spec_bytes_to_metadata((crc_bytes + metadata_bytes).subrange(header_head_offset as int, header_size as int))); - assert(combined_header.metadata == metadata); - } - - /// Converse of lemma_bytes_combine_into_header; proves that the byte representation of a header consists of - /// the byte representations of its CRC and metadata - pub proof fn lemma_header_split_into_bytes(crc_bytes: Seq, metadata_bytes: Seq, header_bytes: Seq) - requires - crc_bytes.len() == 8, - metadata_bytes.len() == header_size - 8, - header_bytes.len() == header_size, - ({ - let header = PersistentHeader { crc: spec_u64_from_le_bytes(crc_bytes), metadata: spec_bytes_to_metadata(metadata_bytes) }; - spec_bytes_to_header(header_bytes) == header - }), - ensures - crc_bytes + metadata_bytes =~= header_bytes - { - lemma_auto_spec_u64_to_from_le_bytes(); - let header = PersistentHeader { crc: spec_u64_from_le_bytes(crc_bytes), metadata: spec_bytes_to_metadata(metadata_bytes) }; - assert(header.crc == spec_u64_from_le_bytes(crc_bytes)); - assert(header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8) =~= spec_u64_to_le_bytes(header.crc)); - assert(crc_bytes =~= spec_u64_to_le_bytes(header.crc)); - - assert(header.metadata == spec_bytes_to_metadata(metadata_bytes)); - assert(header.metadata == spec_bytes_to_metadata(header_bytes.subrange(header_head_offset as int, header_size as int))); - lemma_metadata_bytes_eq(metadata_bytes, header_bytes.subrange(header_head_offset as int, header_size as int), header.metadata); - assert(header_bytes.subrange(header_head_offset as int, header_size as int) =~= metadata_bytes); - - } - - pub proof fn lemma_seq_addition(bytes1: Seq, bytes2: Seq) - ensures - ({ - let i = bytes1.len() as int; - let j = bytes2.len() as int; - &&& (bytes1 + bytes2).subrange(0, i) =~= bytes1 - &&& (bytes1 + bytes2).subrange(i, i + j) =~= bytes2 - }) - { - assert(forall |i: int| #![auto] 0 <= i < bytes1.len() ==> (bytes1 + bytes2)[i] == bytes1[i]); - assert(forall |i: int| #![auto] 0 <= i < bytes2.len() ==> (bytes1 + bytes2)[bytes1.len() + i] == bytes2[i]); - } - - #[verifier::ext_equal] - pub struct PersistentHeader { - pub crc: u64, - pub metadata: PersistentHeaderMetadata, - } - - #[verifier::ext_equal] - pub struct PersistentHeaderMetadata { - pub head: u64, - pub tail: u64, - pub log_size: u64, - } - - #[verifier::ext_equal] - pub struct HeaderView { - pub header1: PersistentHeader, - pub header2: PersistentHeader, - } - - /// Spec code only converts byte representations to structures and does not go the other way - /// to simplify reasoning about persistent structures (although the opposite direction is - /// implemented in exec code). - - exec fn bytes_to_header(bytes: &[u8]) -> (out: PersistentHeader) - requires - bytes@.len() == header_size - ensures - out == spec_bytes_to_header(bytes@) - { - let crc_bytes = slice_subrange(bytes, header_crc_offset as usize, (header_crc_offset + 8) as usize); - let metadata_bytes = slice_subrange(bytes, header_head_offset as usize, header_size as usize); - - PersistentHeader { - crc: u64_from_le_bytes(crc_bytes), - metadata: bytes_to_metadata(metadata_bytes), - } - } - - exec fn header_to_bytes(header: &PersistentHeader) -> (out: Vec) - ensures - header == spec_bytes_to_header(out@), - spec_u64_from_le_bytes(out@.subrange(header_crc_offset as int, header_crc_offset + 8)) == header.crc, - spec_bytes_to_metadata(out@.subrange(header_head_offset as int, header_size as int)) == header.metadata, - out@.len() == header_size - { - proof { lemma_auto_spec_u64_to_from_le_bytes(); } - - let mut metadata_bytes = metadata_to_bytes(&header.metadata); - let mut crc_bytes = u64_to_le_bytes(header.crc); - let ghost old_metadata_bytes = metadata_bytes@; - let ghost old_crc_bytes = crc_bytes@; - crc_bytes.append(&mut metadata_bytes); - proof { - lemma_auto_spec_u64_to_from_le_bytes(); - assert(old_crc_bytes =~= crc_bytes@.subrange(header_crc_offset as int, header_crc_offset + 8)); - assert(old_metadata_bytes =~= crc_bytes@.subrange(header_head_offset as int, header_size as int)); - } - crc_bytes - } - - exec fn bytes_to_metadata(bytes: &[u8]) -> (out: PersistentHeaderMetadata) - requires - bytes@.len() == header_size - 8 - ensures - out == spec_bytes_to_metadata(bytes@) - { - let head_bytes = slice_subrange(bytes, (header_head_offset - 8) as usize, (header_head_offset - 8 + 8) as usize); - let tail_bytes = slice_subrange(bytes, (header_tail_offset - 8) as usize, (header_tail_offset - 8+ 8) as usize); - let log_size_bytes = slice_subrange(bytes, (header_log_size_offset - 8) as usize, (header_log_size_offset - 8 + 8) as usize); - - PersistentHeaderMetadata { - head: u64_from_le_bytes(head_bytes), - tail: u64_from_le_bytes(tail_bytes), - log_size: u64_from_le_bytes(log_size_bytes), - } - } - - exec fn metadata_to_bytes(metadata: &PersistentHeaderMetadata) -> (out: Vec) - ensures - metadata == spec_bytes_to_metadata(out@), - out@.len() == header_size - 8, - { - let mut bytes: Vec = Vec::new(); - let ghost old_bytes = bytes@; - - let mut head_bytes = u64_to_le_bytes(metadata.head); - let ghost old_head_bytes = head_bytes@; - let mut tail_bytes = u64_to_le_bytes(metadata.tail); - let ghost old_tail_bytes = tail_bytes@; - let mut log_size_bytes = u64_to_le_bytes(metadata.log_size); - let ghost old_log_size_bytes = log_size_bytes@; - - bytes.append(&mut head_bytes); - bytes.append(&mut tail_bytes); - bytes.append(&mut log_size_bytes); - - proof { - lemma_auto_spec_u64_to_from_le_bytes(); - assert(old_bytes == Seq::::empty()); - assert(old_head_bytes =~= bytes@.subrange(header_head_offset - 8, header_head_offset - 8 + 8)); - assert(old_tail_bytes =~= bytes@.subrange(header_tail_offset - 8, header_tail_offset - 8 + 8)); - assert(old_log_size_bytes =~= bytes@.subrange(header_log_size_offset - 8, header_log_size_offset - 8 + 8)); - } - bytes - } - - exec fn crc_and_metadata_bytes_to_header(crc_bytes: &[u8], header_bytes: &[u8]) -> (out: PersistentHeader) - requires - crc_bytes@.len() == 8, - header_bytes@.len() == header_size - 8 - ensures - out.crc == spec_u64_from_le_bytes(crc_bytes@), - out.metadata == spec_bytes_to_metadata(header_bytes@) - { - let head_bytes = slice_subrange(header_bytes, (header_head_offset - 8) as usize, (header_head_offset + 8 - 8) as usize); - let tail_bytes = slice_subrange(header_bytes, (header_tail_offset - 8) as usize, (header_tail_offset + 8 - 8) as usize); - let log_size_bytes = slice_subrange(header_bytes, (header_log_size_offset - 8) as usize, (header_log_size_offset + 8 - 8) as usize); - - PersistentHeader { - crc: u64_from_le_bytes(crc_bytes), - metadata: PersistentHeaderMetadata { - head: u64_from_le_bytes(head_bytes), - tail: u64_from_le_bytes(tail_bytes), - log_size: u64_from_le_bytes(log_size_bytes) - } - } - } - - pub open spec(checked) fn spec_bytes_to_metadata(header_seq: Seq) -> PersistentHeaderMetadata - recommends - header_seq.len() == 3*8 - { - let head = spec_u64_from_le_bytes(header_seq.subrange(header_head_offset - 8, header_head_offset - 8 + 8)); - let tail = spec_u64_from_le_bytes(header_seq.subrange(header_tail_offset - 8, header_tail_offset - 8 + 8)); - let log_size = spec_u64_from_le_bytes(header_seq.subrange(header_log_size_offset - 8, header_log_size_offset - 8 + 8)); - PersistentHeaderMetadata { - head, - tail, - log_size - } - } - - /// Proves that two sequences of bytes (assumed to be the subrange of a persistent memory device containing - /// the PersistentHeaderMetadata) are equivalent if their PersistentHeaderMetadata representations are equivalent - pub proof fn lemma_metadata_bytes_eq(bytes1: Seq, bytes2: Seq, metadata: PersistentHeaderMetadata) - requires - bytes1.len() == header_size - 8, - bytes2.len() == header_size - 8, - metadata == spec_bytes_to_metadata(bytes1), - metadata == spec_bytes_to_metadata(bytes2), - ensures - bytes1 =~= bytes2 - { - let metadata1 = spec_bytes_to_metadata(bytes1); - let metadata2 = spec_bytes_to_metadata(bytes2); - - // TODO: could write a lemma that triggers on from instead of to - might help here - lemma_auto_spec_u64_to_from_le_bytes(); - assert(spec_u64_to_le_bytes(metadata1.head) == spec_u64_to_le_bytes(metadata2.head)); - assert(metadata1.head == spec_u64_from_le_bytes(bytes1.subrange(header_head_offset - 8, header_head_offset - 8 + 8))); - assert(metadata2.head == spec_u64_from_le_bytes(bytes2.subrange(header_head_offset - 8, header_head_offset - 8 + 8))); - assert(bytes1.subrange(header_head_offset - 8, header_head_offset - 8 + 8) =~= bytes2.subrange(header_head_offset - 8, header_head_offset - 8 + 8)); - - assert(spec_u64_to_le_bytes(metadata1.tail) == spec_u64_to_le_bytes(metadata2.tail)); - assert(metadata1.tail == spec_u64_from_le_bytes(bytes1.subrange(header_tail_offset - 8, header_tail_offset - 8 + 8))); - assert(metadata2.tail == spec_u64_from_le_bytes(bytes2.subrange(header_tail_offset - 8, header_tail_offset - 8 + 8))); - assert(bytes1.subrange(header_tail_offset - 8, header_tail_offset - 8 + 8) =~= bytes2.subrange(header_tail_offset - 8, header_tail_offset - 8 + 8)); - - assert(spec_u64_to_le_bytes(metadata1.log_size) == spec_u64_to_le_bytes(metadata2.log_size)); - assert(metadata1.log_size == spec_u64_from_le_bytes(bytes1.subrange(header_log_size_offset - 8, header_log_size_offset - 8 + 8))); - assert(metadata2.log_size == spec_u64_from_le_bytes(bytes2.subrange(header_log_size_offset - 8, header_log_size_offset - 8 + 8))); - assert(bytes1.subrange(header_log_size_offset - 8, header_log_size_offset - 8 + 8) =~= bytes2.subrange(header_log_size_offset - 8, header_log_size_offset - 8 + 8)); - - assert(bytes1 =~= bytes1.subrange(header_head_offset - 8, header_head_offset - 8 + 8) + - bytes1.subrange(header_tail_offset - 8, header_tail_offset - 8 + 8) + - bytes1.subrange(header_log_size_offset - 8, header_log_size_offset - 8 + 8)); - } - - pub open spec(checked) fn spec_bytes_to_header(header_seq: Seq) -> PersistentHeader - recommends - header_seq.len() == header_size - { - let crc_val = spec_u64_from_le_bytes(header_seq.subrange(header_crc_offset as int, header_crc_offset +8)); - let metadata = spec_bytes_to_metadata(header_seq.subrange(header_head_offset as int, header_size as int)); - PersistentHeader { - crc: crc_val, - metadata - } - } - - /// Proves that a write to data that does not touch any metadata is crash safe. - pub proof fn lemma_data_write_is_safe(pm: Seq, bytes: Seq, write_addr: int, perm: &Perm) - where - Perm: CheckPermission>, - requires - UntrustedLogImpl::recover(pm) is Some, - pm.len() > contents_offset, - contents_offset <= write_addr < pm.len(), - perm.check_permission(pm), - permissions_depend_only_on_recovery_view(perm), - ({ - // write must be a valid write and not overlap the live log - let live_header = spec_get_live_header(pm); - let physical_head = spec_addr_logical_to_physical(live_header.metadata.head as int, live_header.metadata.log_size as int); - let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); - &&& physical_head <= physical_tail ==> { - &&& write_addr + bytes.len() <= live_header.metadata.log_size + contents_offset - &&& write_addr < physical_head ==> write_addr + bytes.len() <= physical_head - &&& (physical_tail <= write_addr || write_addr < physical_head) - } - &&& physical_tail < physical_head ==> { - &&& physical_tail <= write_addr <= write_addr + bytes.len() < physical_head - } - }), - ensures - UntrustedLogImpl::recover(pm) is Some, - forall |chunks_flushed| { - let new_pm = #[trigger] update_contents_to_reflect_partially_flushed_write( - pm, write_addr, bytes, chunks_flushed); - perm.check_permission(new_pm) - }, - ({ - let new_pm = update_contents_to_reflect_write(pm, write_addr, bytes); - perm.check_permission(new_pm) - }), - update_data_view_postcond(pm, bytes, write_addr), - { - let new_pm = update_contents_to_reflect_write(pm, write_addr, bytes); - lemma_append_data_update_view(pm, bytes, write_addr); - lemma_same_log_state(pm, new_pm); - - assert forall |chunks_flushed| { - let new_pm = #[trigger] update_contents_to_reflect_partially_flushed_write( - pm, write_addr, bytes, chunks_flushed); - perm.check_permission(new_pm) - } by { - let new_pm = update_contents_to_reflect_partially_flushed_write( - pm, write_addr, bytes, chunks_flushed); - lemma_append_data_update_view_crash(pm, bytes, write_addr, chunks_flushed); - lemma_same_log_state(pm, new_pm); - lemma_same_permissions(pm, new_pm, perm); - } - } - - pub open spec fn update_data_view_postcond(pm: Seq, new_bytes: Seq, write_addr: int) -> bool - { - let new_pm = update_contents_to_reflect_write(pm, write_addr, new_bytes); - let (old_ib, old_headers, old_data) = pm_to_views(pm); - let (new_ib, new_headers, new_data) = pm_to_views(new_pm); - let live_header = spec_get_live_header(pm); - let physical_head = spec_addr_logical_to_physical(live_header.metadata.head as int, live_header.metadata.log_size as int); - let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); - &&& old_ib == new_ib - &&& old_headers == new_headers - &&& new_data.len() == old_data.len() - &&& new_data.subrange(write_addr - contents_offset, write_addr - contents_offset + new_bytes.len()) =~= new_bytes - &&& new_data.subrange(0, write_addr - contents_offset) =~= old_data.subrange(0, write_addr - contents_offset) - &&& new_data.subrange(write_addr - contents_offset + new_bytes.len(), new_data.len() as int) =~= - old_data.subrange(write_addr - contents_offset + new_bytes.len(), old_data.len() as int) - &&& UntrustedLogImpl::recover(new_pm) is Some - - &&& physical_head < physical_tail ==> - new_data.subrange(physical_head - contents_offset, physical_tail - contents_offset) =~= old_data.subrange(physical_head - contents_offset, physical_tail - contents_offset) - &&& physical_tail < physical_head ==> { - &&& old_data.subrange(physical_head - contents_offset, live_header.metadata.log_size as int) =~= new_data.subrange(physical_head - contents_offset, live_header.metadata.log_size as int) - &&& old_data.subrange(0, physical_tail - contents_offset) =~= new_data.subrange(0, physical_tail - contents_offset) - } - } - - /// Proves that a non-crashing data write updates data bytes but no log metadata. - pub proof fn lemma_append_data_update_view(pm: Seq, new_bytes: Seq, write_addr: int) - requires - UntrustedLogImpl::recover(pm) is Some, - pm.len() > contents_offset, - contents_offset <= write_addr < pm.len(), - ({ - // write must be a valid write and not overlap the live log - let live_header = spec_get_live_header(pm); - let physical_head = spec_addr_logical_to_physical(live_header.metadata.head as int, live_header.metadata.log_size as int); - let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); - &&& physical_head <= physical_tail ==> { - &&& write_addr + new_bytes.len() <= live_header.metadata.log_size + contents_offset - &&& write_addr < physical_head ==> write_addr + new_bytes.len() <= physical_head - &&& (physical_tail <= write_addr || write_addr < physical_head) - } - &&& physical_tail < physical_head ==> { - &&& physical_tail <= write_addr <= write_addr + new_bytes.len() < physical_head - } - }), - ensures - UntrustedLogImpl::recover(pm) is Some, - update_data_view_postcond(pm, new_bytes, write_addr), - { - let live_header = spec_get_live_header(pm); - let physical_head = spec_addr_logical_to_physical(live_header.metadata.head as int, live_header.metadata.log_size as int); - let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); - let new_pm = update_contents_to_reflect_write(pm, write_addr, new_bytes); - lemma_headers_unchanged(pm, new_pm); - lemma_incorruptible_bool_unchanged(pm, new_pm); - assert(live_header == spec_get_live_header(new_pm)); - assert(new_pm.subrange(0, write_addr) =~= pm.subrange(0, write_addr)); - assert(new_pm.subrange(write_addr + new_bytes.len(), new_pm.len() as int) =~= pm.subrange(write_addr + new_bytes.len(), pm.len() as int)); - lemma_subrange_equality_implies_subsubrange_equality(pm, new_pm, 0, write_addr); - lemma_subrange_equality_implies_subsubrange_equality(pm, new_pm, write_addr + new_bytes.len(), new_pm.len() as int); - if physical_head < physical_tail { - assert(new_pm.subrange(physical_head as int, physical_tail as int) =~= pm.subrange(physical_head as int, physical_tail as int)); - } - } - - /// Proves that a crashing data write updates data bytes but no log metadata. - pub proof fn lemma_append_data_update_view_crash(pm: Seq, new_bytes: Seq, write_addr: int, chunks_flushed: Set) - requires - UntrustedLogImpl::recover(pm) is Some, - pm.len() > contents_offset, - contents_offset <= write_addr < pm.len(), - ({ - // write must be a valid write and not overlap the live log - let live_header = spec_get_live_header(pm); - let physical_head = spec_addr_logical_to_physical(live_header.metadata.head as int, live_header.metadata.log_size as int); - let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); - &&& physical_head <= physical_tail ==> write_addr + new_bytes.len() <= live_header.metadata.log_size + contents_offset - &&& physical_tail < physical_head ==> write_addr + new_bytes.len() < physical_head - }) - ensures - UntrustedLogImpl::recover(pm) is Some, - ({ - let new_pm = update_contents_to_reflect_partially_flushed_write(pm, write_addr, new_bytes, chunks_flushed); - let (old_ib, old_headers, old_data) = pm_to_views(pm); - let (new_ib, new_headers, new_data) = pm_to_views(new_pm); - &&& old_ib == new_ib - &&& old_headers == new_headers - &&& new_data.len() == old_data.len() - &&& new_data.subrange(0, write_addr - contents_offset) =~= old_data.subrange(0, write_addr - contents_offset) - &&& new_data.subrange(write_addr - contents_offset + new_bytes.len(), new_data.len() as int) =~= - old_data.subrange(write_addr - contents_offset + new_bytes.len(), old_data.len() as int) - &&& UntrustedLogImpl::recover(new_pm) is Some - }) - { - let live_header = spec_get_live_header(pm); - let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); - let new_pm = update_contents_to_reflect_partially_flushed_write(pm, write_addr, new_bytes, chunks_flushed); - lemma_headers_unchanged(pm, new_pm); - lemma_incorruptible_bool_unchanged(pm, new_pm); - assert(new_pm.subrange(0, write_addr) =~= pm.subrange(0, write_addr)); - assert(new_pm.subrange(write_addr + new_bytes.len(), new_pm.len() as int) =~= pm.subrange(write_addr + new_bytes.len(), pm.len() as int)); - lemma_subrange_equality_implies_subsubrange_equality(pm, new_pm, 0, write_addr); - } - - /// Proves that a non-crashing update to the inactive header does not change any visible PM state. - pub proof fn lemma_inactive_header_update_view(pm: Seq, new_header_bytes: Seq, header_pos: int) - requires - UntrustedLogImpl::recover(pm) is Some, - header_pos == header1_pos || header_pos == header2_pos, - ({ - // the new bytes must be written to the inactive header - let (old_ib, old_headers, old_data) = pm_to_views(pm); - &&& old_ib == cdb0_val ==> header_pos == header2_pos - &&& old_ib == cdb1_val ==> header_pos == header1_pos - }), - new_header_bytes.len() == header_size, - pm.len() > contents_offset, - ensures - ({ - let new_pm = update_contents_to_reflect_write(pm, header_pos, new_header_bytes); - let (old_ib, old_headers, old_data) = pm_to_views(pm); - let (new_ib, new_headers, new_data) = pm_to_views(new_pm); - &&& old_ib == new_ib - &&& old_data =~= old_data - &&& header_pos == header1_pos ==> - old_headers.header2 == new_headers.header2 - &&& header_pos == header2_pos ==> - old_headers.header1 == new_headers.header1 - &&& UntrustedLogImpl::recover(new_pm) is Some - }) - { - let new_pm = update_contents_to_reflect_write(pm, header_pos, new_header_bytes); - let (new_ib, new_headers, new_data) = pm_to_views(new_pm); - assert(pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8) =~= new_pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8)); - if header_pos == header1_pos { - // we wrote to header1, so header2 should have stayed the same - assert(pm.subrange(header2_pos + header_crc_offset, header2_pos + header_crc_offset + 8) =~= - new_pm.subrange(header2_pos + header_crc_offset, header2_pos + header_crc_offset + 8)); - - assert(pm.subrange(header2_pos + header_head_offset, header2_pos + header_size) =~= - new_pm.subrange(header2_pos + header_head_offset, header2_pos + header_size)); - } else { - // we wrote to header2, so header1 should have stayed the same - assert(pm.subrange(header1_pos + header_crc_offset, header1_pos + header_crc_offset + 8) =~= - new_pm.subrange(header1_pos + header_crc_offset, header1_pos + header_crc_offset + 8)); - - assert(pm.subrange(header1_pos + header_head_offset, header1_pos + header_size) =~= - new_pm.subrange(header1_pos + header_head_offset, header1_pos + header_size)); - } - } - - /// Proves that a crashing update to the inactive header does not change any visible PM state. - pub proof fn lemma_inactive_header_update_view_crash(pm: Seq, new_header_bytes: Seq, header_pos: int, chunks_flushed: Set) - requires - UntrustedLogImpl::recover(pm) is Some, - header_pos == header1_pos || header_pos == header2_pos, - ({ - // the new bytes must be written to the inactive header - let (old_ib, old_headers, old_data) = pm_to_views(pm); - &&& old_ib == cdb0_val ==> header_pos == header2_pos - &&& old_ib == cdb1_val ==> header_pos == header1_pos - }), - new_header_bytes.len() == header_size, - pm.len() > contents_offset, - ensures - ({ - let new_pm = update_contents_to_reflect_partially_flushed_write( - pm, header_pos, new_header_bytes, chunks_flushed); - let (old_ib, old_headers, old_data) = pm_to_views(pm); - let (new_ib, new_headers, new_data) = pm_to_views(new_pm); - &&& old_ib == new_ib - &&& old_data =~= old_data - &&& header_pos == header1_pos ==> - old_headers.header2 == new_headers.header2 - &&& header_pos == header2_pos ==> - old_headers.header1 == new_headers.header1 - &&& UntrustedLogImpl::recover(new_pm) is Some - }) - { - let new_pm = update_contents_to_reflect_partially_flushed_write( - pm, header_pos, new_header_bytes, chunks_flushed); - assert(pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8) =~= new_pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8)); - if header_pos == header1_pos { - // we wrote to header1, so header2 should have stayed the same - assert(pm.subrange(header2_pos + header_crc_offset, header2_pos + header_crc_offset + 8) =~= - new_pm.subrange(header2_pos + header_crc_offset, header2_pos + header_crc_offset + 8)); - - assert(pm.subrange(header2_pos + header_head_offset, header2_pos + header_size) =~= - new_pm.subrange(header2_pos + header_head_offset, header2_pos + header_size)); - } else { - // we wrote to header2, so header1 should have stayed the same - assert(pm.subrange(header1_pos + header_crc_offset, header1_pos + header_crc_offset + 8) =~= - new_pm.subrange(header1_pos + header_crc_offset, header1_pos + header_crc_offset + 8)); - - assert(pm.subrange(header1_pos + header_head_offset, header1_pos + header_size) =~= - new_pm.subrange(header1_pos + header_head_offset, header1_pos + header_size)); - } - } - - pub proof fn lemma_append_ib_update_effect_on_committed>>( - pm: Seq, - new_ib: u64, - bytes_to_append: Seq, - new_header_bytes: Seq, - perm: &Perm - ) - requires - pm.len() > contents_offset, - UntrustedLogImpl::recover(pm) is Some, - new_ib == cdb0_val || new_ib == cdb1_val, - new_ib == cdb0_val ==> - pm.subrange(header1_pos as int, header1_pos + header_size) == new_header_bytes, - new_ib == cdb1_val ==> - pm.subrange(header2_pos as int, header2_pos + header_size) == new_header_bytes, - new_header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8) == - spec_crc_bytes(new_header_bytes.subrange(header_head_offset as int, header_size as int)), - ({ - let new_header = spec_bytes_to_header(new_header_bytes); - let live_header = spec_get_live_header(pm); - &&& new_header.metadata.tail == live_header.metadata.tail + bytes_to_append.len() - &&& new_header.metadata.head == live_header.metadata.head - &&& new_header.metadata.log_size == live_header.metadata.log_size - &&& new_header.metadata.tail - new_header.metadata.head < new_header.metadata.log_size - }), - perm.check_permission(pm), - permissions_depend_only_on_recovery_view(perm), - ({ - let live_header = spec_get_live_header(pm); - let physical_head = spec_addr_logical_to_physical(live_header.metadata.head as int, live_header.metadata.log_size as int); - let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); - let contents_end = (live_header.metadata.log_size + contents_offset) as int; - let append_size = bytes_to_append.len(); - let len1 = (contents_end - physical_tail); - let len2 = bytes_to_append.len() - len1; - - &&& physical_tail + append_size >= contents_end ==> { - &&& pm.subrange(physical_tail, contents_end) =~= bytes_to_append.subrange(0, len1) - &&& pm.subrange(contents_offset as int, contents_offset + len2) =~= bytes_to_append.subrange(len1 as int, append_size as int) - &&& bytes_to_append =~= pm.subrange(physical_tail, contents_end) + pm.subrange(contents_offset as int, contents_offset + len2) - } - &&& physical_head <= physical_tail && physical_tail + append_size < contents_end ==> { - pm.subrange(physical_tail, physical_tail + append_size) =~= bytes_to_append - } - &&& physical_tail < physical_head ==> { - &&& physical_tail + append_size < physical_head - &&& pm.subrange(physical_tail, physical_tail + append_size) =~= bytes_to_append - } - }), - ({ - let old_log_state = UntrustedLogImpl::recover(pm); - forall |pm_state| #[trigger] perm.check_permission(pm_state) <==> { - let log_state = UntrustedLogImpl::recover(pm_state); - log_state == old_log_state || log_state == Some(old_log_state.unwrap().append(bytes_to_append)) - } - }), - ensures - ({ - let ib_bytes = spec_u64_to_le_bytes(new_ib); - let new_pm = update_contents_to_reflect_write(pm, incorruptible_bool_pos as int, ib_bytes); - let old_log_state = UntrustedLogImpl::recover(pm); - let new_log_state = UntrustedLogImpl::recover(new_pm); - let new_live_header = spec_get_live_header(new_pm); - let (new_pm_ib, _, _) = pm_to_views(new_pm); - &&& match (old_log_state, new_log_state) { - (Some(old_log_state), Some(new_log_state)) => { - &&& new_log_state =~= old_log_state.append(bytes_to_append) - &&& perm.check_permission(new_pm) - } - _ => false, - } - &&& new_live_header == spec_bytes_to_header(new_header_bytes) - &&& new_ib == new_pm_ib - }), - { - let ib_bytes = spec_u64_to_le_bytes(new_ib); - let live_header = spec_get_live_header(pm); - let append_size = bytes_to_append.len(); - let contents_end = live_header.metadata.log_size + contents_offset; - let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); - - lemma_auto_spec_u64_to_from_le_bytes(); - - let new_pm = update_contents_to_reflect_write(pm, incorruptible_bool_pos as int, ib_bytes); - lemma_headers_unchanged(pm, new_pm); - assert(new_pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8) =~= ib_bytes); - - let new_header = spec_bytes_to_header(new_header_bytes); - let (ib, headers, data) = pm_to_views(new_pm); - let header_pos = if new_ib == cdb0_val { - header1_pos - } else { - header2_pos - }; - assert(new_pm.subrange(header_pos as int, header_pos + header_size) =~= new_header_bytes); - lemma_header_match(new_pm, header_pos as int, new_header); - lemma_header_correct(new_pm, new_header_bytes, header_pos as int); - - // prove that new pm has the append update - let new_log_state = UntrustedLogImpl::recover(new_pm); - let old_log_state = UntrustedLogImpl::recover(pm); - - match (new_log_state, old_log_state) { - (Some(new_log_state), Some(old_log_state)) => { - lemma_pm_state_header(new_pm); - lemma_pm_state_header(pm); - - let old_header = spec_get_live_header(pm); - let live_header = spec_get_live_header(new_pm); - assert(live_header == new_header); - - assert(live_header.metadata.head == old_header.metadata.head); - assert(live_header.metadata.tail == old_header.metadata.tail + bytes_to_append.len()); - - let physical_head = spec_addr_logical_to_physical(live_header.metadata.head as int, live_header.metadata.log_size as int); - let new_physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); - let old_physical_tail = spec_addr_logical_to_physical(old_header.metadata.tail as int, old_header.metadata.log_size as int); - assert(old_physical_tail == physical_tail); - - let (_, _, old_data) = pm_to_views(pm); - let (_, _, new_data) = pm_to_views(pm); - - if physical_head <= old_physical_tail { - if old_physical_tail + append_size >= contents_end { - assert(new_log_state.log =~= new_data.subrange(physical_head - contents_offset, old_physical_tail - contents_offset) + - new_data.subrange(old_physical_tail - contents_offset, contents_end - contents_offset) + - new_data.subrange(0, new_physical_tail - contents_offset)); - assert(new_log_state.log =~= old_data.subrange(physical_head - contents_offset, old_physical_tail - contents_offset) + - new_data.subrange(old_physical_tail - contents_offset, contents_end - contents_offset) + - new_data.subrange(0, new_physical_tail - contents_offset)); - let len1 = (contents_end - old_physical_tail); - let len2 = bytes_to_append.len() - len1; - assert(bytes_to_append =~= new_data.subrange(old_physical_tail - contents_offset, contents_end - contents_offset) + - new_data.subrange(0, new_physical_tail - contents_offset)); - assert(new_log_state.log =~= old_data.subrange(physical_head - contents_offset, old_physical_tail - contents_offset) + bytes_to_append); - } else { - assert(old_data.subrange(0, old_physical_tail - contents_offset) =~= new_data.subrange(0, old_physical_tail - contents_offset)); - assert(new_data.subrange(old_physical_tail - contents_offset, old_physical_tail - contents_offset + append_size) =~= bytes_to_append); - } - } else { // physical_tail < physical_head - assert(old_physical_tail + append_size < physical_head); - } - assert(new_log_state =~= old_log_state.append(bytes_to_append)); - assert(perm.check_permission(new_pm)); - } - _ => assert(false), - } - } - - /// Proves that an update to the incorruptible boolean is crash-safe and switches the log's - /// active header. This lemma does most of the work to prove that untrusted_append is - /// implemented correctly. - pub proof fn lemma_append_ib_update>>( - pm: Seq, - new_ib: u64, - bytes_to_append: Seq, - new_header_bytes: Seq, - perm: &Perm - ) - requires - pm.len() > contents_offset, - UntrustedLogImpl::recover(pm) is Some, - new_ib == cdb0_val || new_ib == cdb1_val, - new_ib == cdb0_val ==> - pm.subrange(header1_pos as int, header1_pos + header_size) == new_header_bytes, - new_ib == cdb1_val ==> - pm.subrange(header2_pos as int, header2_pos + header_size) == new_header_bytes, - new_header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8) == - spec_crc_bytes(new_header_bytes.subrange(header_head_offset as int, header_size as int)), - ({ - let new_header = spec_bytes_to_header(new_header_bytes); - let live_header = spec_get_live_header(pm); - &&& new_header.metadata.tail == live_header.metadata.tail + bytes_to_append.len() - &&& new_header.metadata.head == live_header.metadata.head - &&& new_header.metadata.log_size == live_header.metadata.log_size - &&& new_header.metadata.tail - new_header.metadata.head < new_header.metadata.log_size - }), - perm.check_permission(pm), - permissions_depend_only_on_recovery_view(perm), - ({ - let live_header = spec_get_live_header(pm); - let physical_head = spec_addr_logical_to_physical(live_header.metadata.head as int, live_header.metadata.log_size as int); - let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); - let contents_end = (live_header.metadata.log_size + contents_offset) as int; - let append_size = bytes_to_append.len(); - let len1 = (contents_end - physical_tail); - let len2 = bytes_to_append.len() - len1; - - &&& physical_tail + append_size >= contents_end ==> { - &&& pm.subrange(physical_tail, contents_end) =~= bytes_to_append.subrange(0, len1) - &&& pm.subrange(contents_offset as int, contents_offset + len2) =~= bytes_to_append.subrange(len1 as int, append_size as int) - &&& bytes_to_append =~= pm.subrange(physical_tail, contents_end) + pm.subrange(contents_offset as int, contents_offset + len2) - } - &&& physical_head <= physical_tail && physical_tail + append_size < contents_end ==> { - pm.subrange(physical_tail, physical_tail + append_size) =~= bytes_to_append - } - &&& physical_tail < physical_head ==> { - &&& physical_tail + append_size < physical_head - &&& pm.subrange(physical_tail, physical_tail + append_size) =~= bytes_to_append - } - }), - ({ - let old_log_state = UntrustedLogImpl::recover(pm); - forall |pm_state| #[trigger] perm.check_permission(pm_state) <==> { - let log_state = UntrustedLogImpl::recover(pm_state); - log_state == old_log_state || log_state == Some(old_log_state.unwrap().append(bytes_to_append)) - } - }), - ensures - ({ - let ib_bytes = spec_u64_to_le_bytes(new_ib); - let new_pm = update_contents_to_reflect_write(pm, incorruptible_bool_pos as int, ib_bytes); - let old_log_state = UntrustedLogImpl::recover(pm); - let new_log_state = UntrustedLogImpl::recover(new_pm); - let new_live_header = spec_get_live_header(new_pm); - let (new_pm_ib, _, _) = pm_to_views(new_pm); - &&& match (old_log_state, new_log_state) { - (Some(old_log_state), Some(new_log_state)) => { - &&& new_log_state =~= old_log_state.append(bytes_to_append) - &&& perm.check_permission(new_pm) - } - _ => false, - } - &&& new_live_header == spec_bytes_to_header(new_header_bytes) - &&& new_ib == new_pm_ib - }), - forall |chunks_flushed| { - let new_pm = #[trigger] update_contents_to_reflect_partially_flushed_write( - pm, incorruptible_bool_pos as int, spec_u64_to_le_bytes(new_ib), chunks_flushed); - &&& perm.check_permission(new_pm) - }, - { - lemma_append_ib_update_effect_on_committed::(pm, new_ib, bytes_to_append, new_header_bytes, perm); - - let ib_bytes = spec_u64_to_le_bytes(new_ib); - lemma_auto_spec_u64_to_from_le_bytes(); - lemma_single_write_crash(pm, incorruptible_bool_pos as int, ib_bytes); - } - - pub open spec fn live_data_view_eq(old_pm: Seq, new_pm: Seq) -> bool - { - let (old_ib, old_headers, old_data) = pm_to_views(old_pm); - let (new_ib, new_headers, new_data) = pm_to_views(new_pm); - let old_live_header = spec_get_live_header(old_pm); - let new_live_header = spec_get_live_header(new_pm); - let physical_head = spec_addr_logical_to_physical(old_live_header.metadata.head as int, old_live_header.metadata.log_size as int); - let physical_tail = spec_addr_logical_to_physical(old_live_header.metadata.tail as int, old_live_header.metadata.log_size as int); - let log_size = old_live_header.metadata.log_size; - let physical_data_head = physical_head - contents_offset; - let physical_data_tail = physical_tail - contents_offset; - - &&& new_live_header == old_live_header - &&& physical_head < physical_tail ==> - old_data.subrange(physical_data_head, physical_data_tail) =~= new_data.subrange(physical_data_head, physical_data_tail) - &&& physical_tail < physical_head ==> { - &&& old_data.subrange(physical_data_head as int, log_size as int) =~= new_data.subrange(physical_data_head as int, log_size as int) - &&& old_data.subrange(0, physical_data_tail as int) =~= new_data.subrange(0, physical_data_tail as int) - } - &&& physical_tail == physical_head ==> - physical_data_head == physical_data_tail - } - - pub proof fn lemma_same_log_state(old_pm: Seq, new_pm: Seq) - requires - UntrustedLogImpl::recover(old_pm) is Some, - UntrustedLogImpl::recover(new_pm) is Some, - live_data_view_eq(old_pm, new_pm), - ({ - let (old_ib, old_headers, old_data) = pm_to_views(old_pm); - let (new_ib, new_headers, new_data) = pm_to_views(new_pm); - &&& old_ib == cdb0_val || old_ib == cdb1_val - &&& old_ib == new_ib - &&& old_ib == cdb0_val ==> { - &&& old_headers.header1 == new_headers.header1 - } - &&& old_ib == cdb1_val ==> { - &&& old_headers.header2 == new_headers.header2 - } - }) - ensures - UntrustedLogImpl::recover(old_pm) =~= - UntrustedLogImpl::recover(new_pm) - { - let old_state = UntrustedLogImpl::recover(old_pm); - let new_state = UntrustedLogImpl::recover(new_pm); - let (old_ib, old_headers, old_data) = pm_to_views(old_pm); - let (new_ib, new_headers, new_data) = pm_to_views(new_pm); - - assert(old_state is Some); - assert(new_state is Some); - match (old_state, new_state) { - (Some(old_state), Some(new_state)) => { - let (old_live_header, new_live_header) = if old_ib == cdb0_val { - (old_headers.header1, new_headers.header1) - } else { - (old_headers.header2, new_headers.header2) - }; - - assert(old_state.head == old_live_header.metadata.head); - assert(new_state.head == new_live_header.metadata.head); - assert(old_live_header.metadata.tail == new_live_header.metadata.tail); - let physical_head = spec_addr_logical_to_physical(old_live_header.metadata.head as int, old_live_header.metadata.log_size as int); - let physical_tail = spec_addr_logical_to_physical(old_live_header.metadata.tail as int, old_live_header.metadata.log_size as int); - let contents_end = old_live_header.metadata.log_size + contents_offset; - - if physical_head < physical_tail { - assert(old_pm.subrange(physical_head, physical_tail) =~= old_data.subrange(physical_head - contents_offset, physical_tail - contents_offset)); - assert(old_pm.subrange(physical_head, physical_tail) =~= new_pm.subrange(physical_head, physical_tail)); - } else if physical_tail < physical_head { - assert(old_pm.subrange(physical_head, contents_end) =~= old_data.subrange(physical_head - contents_offset, contents_end - contents_offset)); - assert(old_pm.subrange(contents_offset as int, physical_tail) =~= old_data.subrange(contents_offset - contents_offset, physical_tail - contents_offset)); - assert(old_pm.subrange(physical_head, contents_end) + old_pm.subrange(contents_offset as int, physical_tail) =~= - new_pm.subrange(physical_head, contents_end) + new_pm.subrange(contents_offset as int, physical_tail)); - } else { - assert(physical_head == physical_tail); - assert(old_state.log.len() == 0); - assert(new_state.log.len() == 0); - } - } - _ => assert(false), - } - } - - pub proof fn lemma_subrange_equality_implies_index_equality(s1: Seq, s2: Seq, i: int, j: int) - requires - 0 <= i <= j <= s1.len(), - j <= s2.len(), - s1.subrange(i, j) == s2.subrange(i, j) - ensures - forall |k| i <= k < j ==> s1[k] == s2[k] - { - assert forall |k| i <= k < j implies s1[k] == s2[k] by { - // Trigger axiom_seq_subrange_index - assert (s1[k] == s1.subrange(i, j)[k - i]); - assert (s2[k] == s2.subrange(i, j)[k - i]); - } - } - - pub proof fn lemma_subrange_equality_implies_subsubrange_equality(s1: Seq, s2: Seq, i: int, j: int) - requires - 0 <= i <= j <= s1.len(), - j <= s2.len(), - s1.subrange(i, j) == s2.subrange(i, j) - ensures - forall |k, m| i <= k <= m <= j ==> s1.subrange(k, m) == s2.subrange(k, m) - { - lemma_subrange_equality_implies_index_equality(s1, s2, i, j); - assert forall |k, m| i <= k <= m <= j implies s1.subrange(k, m) == s2.subrange(k, m) by { - assert (s1.subrange(k, m) =~= s2.subrange(k, m)); - } - } - - pub proof fn lemma_subrange_equality_implies_subsubrange_equality_forall() - ensures - forall |s1: Seq, s2: Seq, i: int, j: int, k: int, m: int| - { - &&& 0 <= i <= j <= s1.len() - &&& j <= s2.len() - &&& s1.subrange(i, j) == s2.subrange(i, j) - &&& i <= k <= m <= j - } - ==> s1.subrange(k, m) == s2.subrange(k, m) - { - assert forall |s1: Seq, s2: Seq, i: int, j: int, k: int, m: int| - { - &&& 0 <= i <= j <= s1.len() - &&& j <= s2.len() - &&& s1.subrange(i, j) == s2.subrange(i, j) - &&& i <= k <= m <= j - } - implies s1.subrange(k, m) == s2.subrange(k, m) by { - lemma_subrange_equality_implies_subsubrange_equality(s1, s2, i, j); - } - } - - pub proof fn lemma_headers_unchanged(old_pm: Seq, new_pm: Seq) - requires - old_pm.len() == new_pm.len(), - old_pm.len() >= contents_offset, - old_pm.subrange(header1_pos as int, header1_pos + header_size) =~= new_pm.subrange(header1_pos as int, header1_pos + header_size), - old_pm.subrange(header2_pos as int, header2_pos + header_size) =~= new_pm.subrange(header2_pos as int, header2_pos + header_size), - ensures - ({ - let (_, old_headers, _) = pm_to_views(old_pm); - let (_, new_headers, _) = pm_to_views(new_pm); - old_headers == new_headers - }) - { - lemma_subrange_equality_implies_subsubrange_equality_forall::(); - } - - pub proof fn lemma_incorruptible_bool_unchanged(old_pm: Seq, new_pm: Seq) - requires - old_pm.len() == new_pm.len(), - old_pm.len() >= contents_offset, - old_pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8) =~= new_pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8) - ensures - ({ - let (old_ib, _, _) = pm_to_views(old_pm); - let (new_ib, _, _) = pm_to_views(new_pm); - old_ib == new_ib - }) - {} - - pub proof fn lemma_header_crc_correct(header_bytes: Seq, crc_bytes: Seq, metadata_bytes: Seq) - requires - header_bytes.len() == header_size, - crc_bytes.len() == 8, - metadata_bytes.len() == header_size - 8, - crc_bytes =~= spec_crc_bytes(metadata_bytes), - header_bytes =~= crc_bytes + metadata_bytes - ensures - header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8) =~= crc_bytes, - header_bytes.subrange(header_head_offset as int, header_size as int) =~= metadata_bytes, - header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8) =~= - spec_crc_bytes(header_bytes.subrange(header_head_offset as int, header_size as int)) - { - assert(header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8) =~= crc_bytes); - assert(header_bytes.subrange(header_head_offset as int, header_size as int) =~= metadata_bytes); - } - - pub proof fn lemma_header_correct(pm: Seq, header_bytes: Seq, header_pos: int) - requires - pm.len() > contents_offset, - header_bytes.len() == header_size, - header_pos == header1_pos || header_pos == header2_pos, - header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8) =~= - spec_crc_bytes(header_bytes.subrange(header_head_offset as int, header_size as int)), - pm.subrange(header_pos, header_pos + header_size) =~= header_bytes - ensures - pm.subrange(header_pos + header_crc_offset, header_pos + header_crc_offset + 8) =~= - header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8), - pm.subrange(header_pos + header_head_offset, header_pos + header_size) =~= - header_bytes.subrange(header_head_offset as int, header_size as int), - pm.subrange(header_pos + header_crc_offset, header_pos + header_crc_offset + 8) =~= - spec_crc_bytes(pm.subrange(header_pos + header_head_offset, header_pos + header_size)) - { - assert(pm.subrange(header_pos + header_crc_offset, header_pos + header_crc_offset + 8) =~= - header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8)); - assert(pm.subrange(header_pos + header_head_offset, header_pos + header_size) =~= - header_bytes.subrange(header_head_offset as int, header_size as int)); - } - - pub proof fn lemma_u64_bytes_eq(val1: u64, val2: u64) - requires - val1 == val2 - ensures - spec_u64_to_le_bytes(val1) =~= spec_u64_to_le_bytes(val2) - {} - - pub proof fn lemma_subrange_eq(bytes1: Seq, bytes2: Seq) - requires - bytes1 =~= bytes2 - ensures - forall |i: int, j: int| 0 <= i < j < bytes1.len() ==> bytes1.subrange(i, j) =~= bytes2.subrange(i, j) - {} - - /// If our write is persistence_chunk_size-sized and -aligned, then there are only 2 possible - /// resulting crash states, one with the write and one without. - pub proof fn lemma_single_write_crash(pm: Seq, write_addr: int, bytes_to_write: Seq) - requires - bytes_to_write.len() == persistence_chunk_size, - write_addr % persistence_chunk_size == 0, // currently seems to succeed without nonlinear arith - 0 <= write_addr < pm.len(), - write_addr + bytes_to_write.len() <= pm.len() - ensures - ({ - forall |chunks_flushed: Set| { - let new_crash_contents = #[trigger] update_contents_to_reflect_partially_flushed_write( - pm, write_addr, bytes_to_write, chunks_flushed); - let new_contents = update_contents_to_reflect_write(pm, write_addr, bytes_to_write); - new_crash_contents =~= pm || new_crash_contents =~= new_contents - } - }) - {} - - pub proof fn lemma_pm_state_header(pm: Seq) - requires - UntrustedLogImpl::recover(pm) is Some, - ({ - let header = spec_get_live_header(pm); - header.metadata.tail - header.metadata.head < header.metadata.log_size - }) - ensures - ({ - let pm_state = UntrustedLogImpl::recover(pm); - let header = spec_get_live_header(pm); - match pm_state { - Some(pm_state) => { - &&& header.metadata.head == pm_state.head - &&& pm_state.log.len() == header.metadata.tail - header.metadata.head - } - None => false - } - }) - { - let pm_state = UntrustedLogImpl::recover(pm); - let header = spec_get_live_header(pm); - lemma_mod_division_less_than_divisor(header.metadata.head as int, header.metadata.log_size as int); - lemma_mod_division_less_than_divisor(header.metadata.tail as int, header.metadata.log_size as int); - let head = header.metadata.head as int; - let tail = header.metadata.tail as int; - let log_size = header.metadata.log_size as int; - let physical_head = spec_addr_logical_to_physical(head, log_size); - let physical_tail = spec_addr_logical_to_physical(tail, log_size); - match pm_state { - Some(pm_state) => { - if physical_head < physical_tail { - // log does not wrap - lemma_mod_difference_equal(head, tail, log_size); - } else if physical_tail < physical_head { - // log wraps - lemma_mod_wrapped_len(head, tail, log_size); - } else { - // size is 0 - lemma_mod_equal(head, tail, log_size); - } - } - None => assert(false), - } - } - - pub open spec fn spec_addr_logical_to_physical(addr: int, log_size: int) -> int { - (addr % log_size) + contents_offset - } - - pub struct UntrustedLogImpl { - pub incorruptible_bool: u64, - // header fields are stored separately because of limitations - // on deriving Copy/Clone for the header structures - pub header_crc: u64, - pub head: u64, - pub tail: u64, - pub log_size: u64, - } - - // offset of actual log contents from the beginning of the device - pub const contents_offset: u64 = header2_pos + header_log_size_offset + 8; - - impl UntrustedLogImpl { - - pub exec fn addr_logical_to_physical(addr: u64, log_size: u64) -> (out: u64) - requires - addr <= u64::MAX, - log_size > 0, - log_size + contents_offset <= u64::MAX, - ensures - out == spec_addr_logical_to_physical(addr as int, log_size as int) - { - (addr % log_size) + contents_offset - } - - pub open spec fn log_state_is_valid(pm: Seq) -> bool { - let (ib, headers, data) = pm_to_views(pm); - let live_header = if ib == cdb0_val { - headers.header1 - } else { - headers.header2 - }; - - let head = live_header.metadata.head as int; - let tail = live_header.metadata.tail as int; - let log_size = live_header.metadata.log_size as int; - - &&& ib == cdb0_val || ib == cdb1_val - &&& log_size + contents_offset <= u64::MAX - &&& log_size > 0 - &&& log_size + contents_offset == pm.len() - &&& tail - head < log_size - &&& ib == cdb0_val ==> { - &&& live_header.crc == spec_u64_from_le_bytes(spec_crc_bytes(pm.subrange(header1_pos + header_head_offset, header1_pos + header_size))) - &&& pm.subrange(header1_pos + header_crc_offset, header1_pos + header_crc_offset + 8) =~= spec_crc_bytes(pm.subrange(header1_pos + header_head_offset, header1_pos + header_size)) - } - &&& ib == cdb1_val ==> { - &&& live_header.crc == spec_u64_from_le_bytes(spec_crc_bytes(pm.subrange(header2_pos + header_head_offset, header2_pos + header_size))) - &&& pm.subrange(header2_pos + header_crc_offset, header2_pos + header_crc_offset + 8) =~= spec_crc_bytes(pm.subrange(header2_pos + header_head_offset, header2_pos + header_size)) - } - &&& head <= tail - } - - pub open spec fn recover(pm: Seq) -> Option - { - let (ib, headers, data) = pm_to_views(pm); - if !Self::log_state_is_valid(pm) { - None - } else { - let live_header = if ib == cdb0_val { - headers.header1 - } else { - headers.header2 - }; - - let head = live_header.metadata.head as int; - let tail = live_header.metadata.tail as int; - let log_size = live_header.metadata.log_size as int; - let contents_end = log_size + contents_offset; - let physical_head = spec_addr_logical_to_physical(head, log_size); - let physical_tail = spec_addr_logical_to_physical(tail, log_size); - - let abstract_log = if physical_head < physical_tail { - pm.subrange(physical_head, physical_tail) - } else if physical_tail < physical_head { - let range1 = pm.subrange(physical_head, contents_end); - let range2 = pm.subrange(contents_offset as int, physical_tail); - range1 + range2 - } else { - Seq::empty() - }; - - Some(AbstractInfiniteLogState { head: head, log: abstract_log, capacity: log_size - 1 }) - } - } - - // This is the invariant that the untrusted log implementation - // maintains between its local state and the contents of - // persistent memory. - pub open spec fn inv_pm_contents(self, contents: Seq) -> bool - { - let (ib, headers, data) = pm_to_views(contents); - let header_pos = if ib == cdb0_val { header1_pos } else { header2_pos }; - let header = spec_get_live_header(contents); - let head = header.metadata.head; - let tail = header.metadata.tail; - let log_size = header.metadata.log_size; - &&& ib == cdb0_val || ib == cdb1_val - &&& spec_crc_bytes(contents.subrange(header_pos + header_head_offset, header_pos + header_size)) == - contents.subrange(header_pos + header_crc_offset, header_pos + header_crc_offset + 8) - &&& log_size + contents_offset <= u64::MAX - &&& tail - head < log_size - &&& log_size + contents_offset == contents.len() - &&& self.header_crc == header.crc - &&& self.head == head - &&& self.tail == tail - &&& self.log_size == log_size - &&& self.incorruptible_bool == ib - &&& match Self::recover(contents) { - Some(inf_log) => tail == head + inf_log.log.len(), - None => false, - } - } - - // This is the invariant that the untrusted log implementation - // maintains between its local state and the write-restricted - // persistent memory. - pub open spec fn inv(self, wrpm: &WriteRestrictedPersistentMemory) -> bool - where - Perm: CheckPermission>, - PM: PersistentMemory - { - &&& wrpm.inv() - &&& self.inv_pm_contents(wrpm@) - } - - pub exec fn read_incorruptible_boolean(pm: &PM) -> (result: Result) - requires - Self::recover(pm@) is Some, - pm.inv(), - pm@.len() > contents_offset - ensures - match result { - Ok(ib) => { - let (spec_ib, _, _) = pm_to_views(pm@); - ib == spec_ib - } - Err(InfiniteLogErr::CRCMismatch) => !pm.constants().impervious_to_corruption, - _ => false, - } - { - let bytes = pm.read(incorruptible_bool_pos, 8); - let ib = u64_from_le_bytes(bytes.as_slice()); - let ghost addrs = Seq::::new(8, |i: int| i + incorruptible_bool_pos); - if ib == cdb0_val || ib == cdb1_val { - proof { - let (spec_ib, _, _) = pm_to_views(pm@); - lemma_auto_spec_u64_to_from_le_bytes(); - if !pm.constants().impervious_to_corruption { - axiom_corruption_detecting_boolean(ib, spec_ib, addrs); - } - } - Ok(ib) - } else { - Err(InfiniteLogErr::CRCMismatch) - } - } - - exec fn update_header - ( - &mut self, - wrpm: &mut WriteRestrictedPersistentMemory, - Tracked(perm): Tracked<&Perm>, - new_header_bytes: &Vec - ) - where - Perm: CheckPermission>, - PM: PersistentMemory - requires - permissions_depend_only_on_recovery_view(perm), - contents_offset < old(wrpm)@.len(), - old(self).inv(&*old(wrpm)), - Self::recover(old(wrpm)@) is Some, - new_header_bytes@.subrange(header_crc_offset as int, header_crc_offset + 8) =~= - spec_crc_bytes(new_header_bytes@.subrange(header_head_offset as int, header_size as int)), - new_header_bytes.len() == header_size, - match Self::recover(old(wrpm)@) { - Some(log_state) => perm.check_permission(old(wrpm)@), - None => false - } - ensures - self.inv(wrpm), - Self::recover(wrpm@) is Some, - wrpm.constants() == old(wrpm).constants(), - match (Self::recover(old(wrpm)@), Self::recover(wrpm@)) { - (Some(old_log_state), Some(new_log_state)) => old_log_state =~= new_log_state, - _ => false - }, - ({ - let (old_pm_ib, old_metadata, old_data) = pm_to_views(old(wrpm)@); - let (new_pm_ib, new_metadata, new_data) = pm_to_views(wrpm@); - let new_header = spec_bytes_to_header(new_header_bytes@); - &&& old_pm_ib == new_pm_ib - &&& old_pm_ib == cdb0_val ==> { - &&& new_metadata.header1 == old_metadata.header1 - &&& new_metadata.header2 == new_header - &&& wrpm@.subrange(header2_pos + header_crc_offset, header2_pos + header_crc_offset + 8) =~= - spec_crc_bytes(wrpm@.subrange(header2_pos + header_head_offset, header2_pos + header_size)) - &&& wrpm@.subrange(header2_pos as int, header2_pos + header_size) =~= new_header_bytes@ - } - &&& old_pm_ib == cdb1_val ==> { - &&& new_metadata.header1 == new_header - &&& new_metadata.header2 == old_metadata.header2 - &&& wrpm@.subrange(header1_pos + header_crc_offset, header1_pos + header_crc_offset + 8) =~= - spec_crc_bytes(wrpm@.subrange(header1_pos + header_head_offset, header1_pos + header_size)) - &&& wrpm@.subrange(header1_pos as int, header1_pos + header_size) =~= new_header_bytes@ - } - &&& old_data =~= new_data - }), - - { - let ghost original_wrpm = wrpm@; - - // write to the header that is NOT pointed to by the IB - let header_pos = if self.incorruptible_bool == cdb0_val { - header2_pos - } else { - header1_pos - }; - - // TODO: we could probably roll all of this into a single lemma that contains all of the proofs - proof { - let new_pm = update_contents_to_reflect_write(wrpm@, header_pos as int, new_header_bytes@); - lemma_inactive_header_update_view(wrpm@, new_header_bytes@, header_pos as int); - lemma_same_log_state(wrpm@, new_pm); - assert(Self::recover(wrpm@) =~= Self::recover(new_pm)); - - // prove crash consistency - assert forall |chunks_flushed| { - let new_pm = #[trigger] update_contents_to_reflect_partially_flushed_write( - wrpm@, header_pos as int, new_header_bytes@, chunks_flushed); - perm.check_permission(new_pm) - } by { - let new_pm = update_contents_to_reflect_partially_flushed_write( - wrpm@, header_pos as int, new_header_bytes@, chunks_flushed); - lemma_inactive_header_update_view_crash(wrpm@, new_header_bytes@, header_pos as int, chunks_flushed); - lemma_same_log_state(wrpm@, new_pm); - assert(permissions_depend_only_on_recovery_view(perm)); - lemma_same_permissions(wrpm@, new_pm, perm); - } - } - wrpm.write(header_pos, new_header_bytes.as_slice(), Tracked(perm)); - proof { - // TODO: clean up once ib update is done. put this all in a lemma - assert(Self::recover(wrpm@) is Some); - let (_, headers, _) = pm_to_views(wrpm@); - assert(wrpm@.subrange(header_pos as int, header_pos + header_size) =~= new_header_bytes@); - lemma_header_correct(wrpm@, new_header_bytes@, header_pos as int); - - // live header is unchanged - let live_header_pos = if header_pos == header1_pos { - header2_pos - } else { - assert(header_pos == header2_pos); - header1_pos - }; - - // TODO: refactor into a lemma (ideally lemma_header_correct) - assert(old(wrpm)@.subrange(live_header_pos as int, live_header_pos + header_size) =~= - wrpm@.subrange(live_header_pos as int, live_header_pos + header_size)); - assert(old(wrpm)@.subrange(live_header_pos + header_crc_offset, live_header_pos + header_crc_offset + 8) =~= - spec_crc_bytes(old(wrpm)@.subrange(live_header_pos + header_head_offset, live_header_pos + header_size))); - assert(old(wrpm)@.subrange(live_header_pos + header_crc_offset, live_header_pos + header_crc_offset + 8) =~= - wrpm@.subrange(live_header_pos + header_crc_offset, live_header_pos + header_crc_offset + 8)); - assert(old(wrpm)@.subrange(live_header_pos + header_head_offset, live_header_pos + header_size) =~= - wrpm@.subrange(live_header_pos + header_head_offset, live_header_pos + header_size)); - - assert(wrpm@.subrange(live_header_pos + header_crc_offset, live_header_pos + header_crc_offset + 8) =~= - spec_crc_bytes(wrpm@.subrange(live_header_pos + header_head_offset, live_header_pos + header_size))); - } - } - - // Since untrusted_setup doesn't take a WriteRestrictedPersistentMemory, it is not guaranteed - // to perform crash-safe updates. - pub exec fn untrusted_setup(pm: &mut PM, device_size: u64) -> (result: Result) - where - PM: PersistentMemory - requires - old(pm).inv(), - old(pm)@.len() == device_size - ensures - pm.inv(), - pm.constants() == old(pm).constants(), - pm@.len() == device_size, - match result { - Ok(capacity) => Self::recover(pm@) == - Some(AbstractInfiniteLogState::initialize(capacity as int)), - Err(InfiniteLogErr::InsufficientSpaceForSetup{ required_space }) => device_size < required_space, - _ => false - } - { - if device_size <= contents_offset { - return Err(InfiniteLogErr::InsufficientSpaceForSetup { required_space: contents_offset + 1 }); - } - - let log_size = device_size - contents_offset; - - let log_header_metadata = PersistentHeaderMetadata { - head: 0, - tail: 0, - log_size - }; - let metadata_bytes = metadata_to_bytes(&log_header_metadata); - let crc_bytes = bytes_crc(&metadata_bytes); - let log_header = PersistentHeader { - crc: u64_from_le_bytes(crc_bytes.as_slice()), - metadata: log_header_metadata, - }; - let header_bytes = header_to_bytes(&log_header); - - let initial_ib_bytes = u64_to_le_bytes(cdb0_val); - pm.write(header1_pos, header_bytes.as_slice()); - pm.write(incorruptible_bool_pos, initial_ib_bytes.as_slice()); - - proof { - lemma_auto_spec_u64_to_from_le_bytes(); - assert(pm@.subrange(header1_pos as int, header1_pos + header_size) =~= header_bytes@); - assert(pm@.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8) =~= initial_ib_bytes@); - lemma_header_split_into_bytes(crc_bytes@, metadata_bytes@, header_bytes@); - assert(pm@.subrange(header1_pos + header_head_offset, header1_pos + header_size) =~= metadata_bytes@); - lemma_header_match(pm@, header1_pos as int, log_header); - let log_state = Self::recover(pm@); - match log_state { - Some(log_state) => { - assert(log_state.head == 0); - assert(log_state.log == Seq::::empty()); - assert(log_state.capacity == log_size - 1); - } - None => assert(false), - } - } - - Ok(log_size - 1) - } - - pub exec fn untrusted_start(wrpm: &mut WriteRestrictedPersistentMemory, - device_size: u64, - Tracked(perm): Tracked<&Perm>) - -> (result: Result) - where - Perm: CheckPermission>, - PM: PersistentMemory - requires - Self::recover(old(wrpm)@) is Some, - old(wrpm).inv(), - old(wrpm)@.len() == device_size, - header_crc_offset < header_crc_offset + crc_size <= header_head_offset < header_tail_offset < header_log_size_offset, - // The restriction on writing persistent memory during initialization is - // that it can't change the interpretation of that memory's contents. - ({ - forall |pm_state| #[trigger] perm.check_permission(pm_state) <==> - Self::recover(pm_state) == - Self::recover(old(wrpm)@) - }), - ensures - Self::recover(old(wrpm)@) == Self::recover(wrpm@), - wrpm.constants() == old(wrpm).constants(), - match result { - Ok(log_impl) => log_impl.inv(wrpm), - Err(InfiniteLogErr::CRCMismatch) => !wrpm.constants().impervious_to_corruption, - _ => false - } - { - let pm = wrpm.get_pm_ref(); - assert (device_size > contents_offset); - - let ib = match Self::read_incorruptible_boolean(pm) { - Ok(ib) => ib, - Err(e) => return Err(e) - }; - - let header_pos = if ib == cdb0_val { - header1_pos - } else { - assert(ib == cdb1_val); - header2_pos - }; - let crc_bytes = pm.read(header_pos + header_crc_offset, 8); - let ghost crc_addrs = Seq::::new(8, |i: int| i + header_pos + header_crc_offset); - let header_bytes = pm.read(header_pos + header_head_offset, header_size - header_head_offset); - let ghost header_addrs = Seq::::new((header_size - header_head_offset) as nat, |i: int| i + header_pos + header_head_offset); - - let header = if u64_from_le_bytes(bytes_crc(&header_bytes).as_slice()) == u64_from_le_bytes(crc_bytes.as_slice()) { - proof { - lemma_auto_spec_u64_to_from_le_bytes(); - lemma_u64_bytes_eq(spec_u64_from_le_bytes(spec_crc_bytes(header_bytes@)), spec_u64_from_le_bytes(crc_bytes@)); - if !wrpm.constants().impervious_to_corruption { - axiom_bytes_uncorrupted( - header_bytes@, - pm@.subrange(header_pos + header_head_offset, header_pos + header_size), - header_addrs, - crc_bytes@, - pm@.subrange(header_pos + header_crc_offset, header_pos + header_crc_offset + 8), - crc_addrs, - ); - } - } - crc_and_metadata_bytes_to_header(crc_bytes.as_slice(), header_bytes.as_slice()) - } else { - return Err(InfiniteLogErr::CRCMismatch); - }; - - let head = header.metadata.head; - let tail = header.metadata.tail; - let log_size = header.metadata.log_size; - // check log validity now that we have its uncorrupted metadata - assert(device_size == log_size + contents_offset); - assert(head <= tail); - assert(tail - head < log_size); - - let untrusted_log = UntrustedLogImpl { - incorruptible_bool: ib, - header_crc: u64_from_le_bytes(crc_bytes.as_slice()), - head, - tail, - log_size - }; - - proof { lemma_pm_state_header(pm@); } - Ok(untrusted_log) - } - - pub exec fn untrusted_append( - &mut self, - wrpm: &mut WriteRestrictedPersistentMemory, - bytes_to_append: &Vec, - Tracked(perm): Tracked<&Perm> - ) -> (result: Result) - where - Perm: CheckPermission>, - PM: PersistentMemory - requires - old(self).inv(&*old(wrpm)), - Self::recover(old(wrpm)@) is Some, - ({ - let old_log_state = Self::recover(old(wrpm)@); - forall |pm_state| #[trigger] perm.check_permission(pm_state) <==> { - let log_state = Self::recover(pm_state); - log_state == old_log_state || log_state == Some(old_log_state.unwrap().append(bytes_to_append@)) - } - }), - ensures - self.inv(wrpm), - wrpm.constants() == old(wrpm).constants(), - ({ - let old_log_state = Self::recover(old(wrpm)@); - let new_log_state = Self::recover(wrpm@); - match (result, old_log_state, new_log_state) { - (Ok(offset), Some(old_log_state), Some(new_log_state)) => { - &&& offset as nat == old_log_state.log.len() + old_log_state.head - &&& new_log_state == old_log_state.append(bytes_to_append@) - }, - (Err(InfiniteLogErr::InsufficientSpaceForAppend{ available_space }), _, _) => { - &&& new_log_state == old_log_state - &&& available_space < bytes_to_append@.len() - &&& { - let log = old_log_state.unwrap(); - ||| available_space == log.capacity - log.log.len() - ||| available_space == u64::MAX - log.head - log.log.len() - } - }, - (_, _, _) => false - } - }), - { - assert(permissions_depend_only_on_recovery_view(perm)); - - let pm = wrpm.get_pm_ref(); - let ghost original_pm = wrpm@; - - let physical_head = Self::addr_logical_to_physical(self.head, self.log_size); - let physical_tail = Self::addr_logical_to_physical(self.tail, self.log_size); - let contents_end = self.log_size + contents_offset; - let append_size: u64 = bytes_to_append.len() as u64; - let old_logical_tail = self.tail; - - if self.tail > u64::MAX - append_size { - Err(InfiniteLogErr::InsufficientSpaceForAppend{ available_space: u64::MAX - self.tail }) - } - else if append_size >= self.log_size - (self.tail - self.head) { - Err(InfiniteLogErr::InsufficientSpaceForAppend{ available_space: self.log_size - 1 - (self.tail - self.head) }) - } else { - let mut header_metadata = - PersistentHeaderMetadata { head: self.head, tail: self.tail, log_size: self.log_size }; - assert(header_metadata == spec_get_live_header(wrpm@).metadata); - - if physical_head <= physical_tail { - if physical_tail >= contents_end - append_size { - // wrap case - self.append_wrap(wrpm, bytes_to_append, &header_metadata, Tracked(perm)); - } else { - // no wrap - self.append_no_wrap(wrpm, bytes_to_append, &header_metadata, Tracked(perm)); - } - } else { // physical_tail < physical_head - if physical_tail + append_size >= physical_head { - return Err(InfiniteLogErr::InsufficientSpaceForAppend { available_space: physical_head - physical_tail }); - } - // no wrap - self.append_no_wrap(wrpm, bytes_to_append, &header_metadata, Tracked(perm)); - } - - let new_tail = self.tail + append_size; - header_metadata.tail = new_tail; - - let mut metadata_bytes = metadata_to_bytes(&header_metadata); - let new_crc_bytes = bytes_crc(&metadata_bytes); - let new_crc_val = u64_from_le_bytes(new_crc_bytes.as_slice()); - let ghost old_metadata_bytes = metadata_bytes@; - let mut new_header_bytes = new_crc_bytes; - new_header_bytes.append(&mut metadata_bytes); - - proof { lemma_header_crc_correct(new_header_bytes@, new_crc_bytes@, old_metadata_bytes); } - - self.update_header(wrpm, Tracked(perm), &new_header_bytes); - - // update incorruptible boolean - let old_ib = self.incorruptible_bool; - let new_ib = if old_ib == cdb0_val { - cdb1_val - } else { - assert(old_ib == cdb1_val); - cdb0_val - }; - let new_ib_bytes = u64_to_le_bytes(new_ib); - - proof { - lemma_append_ib_update(wrpm@, new_ib, bytes_to_append@, new_header_bytes@, perm); - } - - wrpm.write(incorruptible_bool_pos, new_ib_bytes.as_slice(), Tracked(perm)); - self.incorruptible_bool = new_ib; - self.tail = new_tail; - self.header_crc = new_crc_val; - - Ok(old_logical_tail) - } - } - - exec fn append_no_wrap( - &mut self, - wrpm: &mut WriteRestrictedPersistentMemory, - bytes_to_append: &Vec, - old_header: &PersistentHeaderMetadata, - Tracked(perm): Tracked<&Perm> - ) - where - Perm: CheckPermission>, - PM: PersistentMemory - requires - permissions_depend_only_on_recovery_view(perm), - perm.check_permission(old(wrpm)@), - old(self).inv(&*old(wrpm)), - Self::recover(old(wrpm)@) is Some, - old_header == spec_get_live_header(old(wrpm)@).metadata, - // TODO: clean up - ({ - let physical_tail = spec_addr_logical_to_physical(old_header.tail as int, old_header.log_size as int); - physical_tail + bytes_to_append@.len() < old_header.log_size + contents_offset - }), - ({ - let physical_head = spec_addr_logical_to_physical(old_header.head as int, old_header.log_size as int); - let physical_tail = spec_addr_logical_to_physical(old_header.tail as int, old_header.log_size as int); - let contents_end = old_header.log_size + contents_offset; - &&& physical_head <= physical_tail ==> physical_tail + bytes_to_append@.len() < contents_end - &&& physical_tail < physical_head ==> physical_tail <= physical_tail + bytes_to_append@.len() < physical_head - }) - ensures - self.inv(wrpm), - wrpm.constants() == old(wrpm).constants(), - Self::recover(wrpm@) is Some, - match (Self::recover(old(wrpm)@), Self::recover(wrpm@)) { - (Some(old_log_state), Some(new_log_state)) => old_log_state =~= new_log_state, - _ => false - }, - ({ - let (old_ib, old_headers, old_data) = pm_to_views(old(wrpm)@); - let (new_ib, new_headers, new_data) = pm_to_views(wrpm@); - let physical_tail = spec_addr_logical_to_physical(old_header.tail as int, old_header.log_size as int); - &&& old_ib == new_ib - &&& old_headers == new_headers - &&& new_data.subrange(physical_tail - contents_offset, physical_tail - contents_offset + bytes_to_append@.len() as int) =~= bytes_to_append@ - &&& new_data.subrange(0, physical_tail - contents_offset) =~= old_data.subrange(0, physical_tail - contents_offset) - &&& new_data.subrange(physical_tail - contents_offset + bytes_to_append@.len(), new_data.len() as int) =~= - old_data.subrange(physical_tail - contents_offset + bytes_to_append@.len(), old_data.len() as int) - }) - { - let physical_tail = Self::addr_logical_to_physical(old_header.tail, old_header.log_size); - proof { lemma_data_write_is_safe(wrpm@, bytes_to_append@, physical_tail as int, perm); } - wrpm.write(physical_tail, bytes_to_append.as_slice(), Tracked(perm)); - proof { - assert(wrpm@.subrange(0, physical_tail as int) =~= old(wrpm)@.subrange(0, physical_tail as int)); - lemma_subrange_equality_implies_subsubrange_equality(wrpm@, old(wrpm)@, 0, physical_tail as int); - } - } - - pub exec fn append_wrap( - &mut self, - wrpm: &mut WriteRestrictedPersistentMemory, - bytes_to_append: &Vec, - old_header: &PersistentHeaderMetadata, - Tracked(perm): Tracked<&Perm> - ) - where - Perm: CheckPermission>, - PM: PersistentMemory - requires - permissions_depend_only_on_recovery_view(perm), - perm.check_permission(old(wrpm)@), - old(self).inv(&*old(wrpm)), - Self::recover(old(wrpm)@) is Some, - old_header == spec_get_live_header(old(wrpm)@).metadata, - ({ - let physical_head = spec_addr_logical_to_physical(old_header.head as int, old_header.log_size as int); - let physical_tail = spec_addr_logical_to_physical(old_header.tail as int, old_header.log_size as int); - let contents_end = old_header.log_size + contents_offset; - &&& contents_offset < physical_head - &&& physical_tail + bytes_to_append@.len() >= contents_end - &&& physical_head <= physical_tail - &&& bytes_to_append@.len() <= old_header.log_size - (old_header.tail - old_header.head) - }), - ensures - self.inv(wrpm), - Self::recover(wrpm@) is Some, - wrpm.constants() == old(wrpm).constants(), - match (Self::recover(old(wrpm)@), Self::recover(wrpm@)) { - (Some(old_log_state), Some(new_log_state)) => old_log_state =~= new_log_state, - _ => false - }, - ({ - let (old_ib, old_headers, old_data) = pm_to_views(old(wrpm)@); - let (new_ib, new_headers, new_data) = pm_to_views(wrpm@); - let contents_end = old_header.log_size + contents_offset; - let physical_tail = spec_addr_logical_to_physical(old_header.tail as int, old_header.log_size as int); - let len1 = (contents_end - physical_tail); - let len2 = bytes_to_append@.len() - len1; - &&& old_ib == new_ib - &&& old_headers == new_headers - &&& new_data.subrange(physical_tail - contents_offset, contents_end - contents_offset) =~= bytes_to_append@.subrange(0, len1) - &&& new_data.subrange(0, len2 as int) =~= bytes_to_append@.subrange(len1 as int, bytes_to_append@.len() as int) - &&& new_data.subrange(len2 as int, physical_tail - contents_offset) =~= old_data.subrange(len2 as int, physical_tail - contents_offset) - &&& bytes_to_append@ =~= new_data.subrange(physical_tail - contents_offset, contents_end - contents_offset) + new_data.subrange(0, len2 as int) - }) - { - let physical_head = Self::addr_logical_to_physical(old_header.head, old_header.log_size); - let physical_tail = Self::addr_logical_to_physical(old_header.tail, old_header.log_size); - let contents_end = old_header.log_size + contents_offset; - let append_size = bytes_to_append.len(); - - let len1 = (contents_end - physical_tail) as usize; - let len2 = bytes_to_append.len() - len1; - let append_bytes_slice = bytes_to_append.as_slice(); - let bytes1 = slice_subrange(append_bytes_slice, 0, len1); - let bytes2 = slice_subrange(append_bytes_slice, len1, append_size); - - proof { lemma_data_write_is_safe(wrpm@, bytes1@, physical_tail as int, perm); } - wrpm.write(physical_tail, bytes1, Tracked(perm)); - - proof { lemma_data_write_is_safe(wrpm@, bytes2@, contents_offset as int, perm); } - wrpm.write(contents_offset, bytes2, Tracked(perm)); - - proof { - assert(wrpm@.subrange(0, contents_offset as int) =~= old(wrpm)@.subrange(0, contents_offset as int)); - lemma_subrange_equality_implies_subsubrange_equality(wrpm@, old(wrpm)@, 0, contents_offset as int); - } - } - - pub exec fn untrusted_advance_head( - &mut self, - wrpm: &mut WriteRestrictedPersistentMemory, - new_head: u64, - Tracked(perm): Tracked<&Perm> - ) -> (result: Result<(), InfiniteLogErr>) - where - Perm: CheckPermission>, - PM: PersistentMemory - requires - old(self).inv(&*old(wrpm)), - Self::recover(old(wrpm)@) is Some, - ({ - let old_log_state = Self::recover(old(wrpm)@); - forall |pm_state| #[trigger] perm.check_permission(pm_state) <==> { - let log_state = Self::recover(pm_state); - ||| log_state == old_log_state - ||| log_state == Some(old_log_state.unwrap().advance_head(new_head as int)) - } - }) - ensures - self.inv(wrpm), - wrpm.constants() == old(wrpm).constants(), - ({ - let old_log_state = Self::recover(old(wrpm)@); - let new_log_state = Self::recover(wrpm@); - match (result, old_log_state, new_log_state) { - (Ok(_), Some(old_log_state), Some(new_log_state)) => { - &&& old_log_state.head <= new_head <= old_log_state.head + old_log_state.log.len() - &&& new_log_state == old_log_state.advance_head(new_head as int) - }, - (Err(InfiniteLogErr::CantAdvanceHeadPositionBeforeHead{ head }), Some(old_log_state), Some(new_log_state)) => { - &&& new_log_state == old_log_state - &&& head == old_log_state.head - &&& new_head < head - }, - (Err(InfiniteLogErr::CantAdvanceHeadPositionBeyondTail{ tail }), Some(old_log_state), Some(new_log_state)) => { - &&& new_log_state == old_log_state - &&& tail == old_log_state.head + old_log_state.log.len() - &&& new_head > tail - }, - (_, _, _) => false - } - }) - { - let pm = wrpm.get_pm_ref(); - let ghost original_pm = wrpm@; - - let live_header = PersistentHeader { - crc: self.header_crc, - metadata: PersistentHeaderMetadata { head: self.head, tail: self.tail, log_size: self.log_size } - }; - - if new_head < live_header.metadata.head { - assert(self.header_crc == old(self).header_crc); - return Err(InfiniteLogErr::CantAdvanceHeadPositionBeforeHead{ head: live_header.metadata.head }); - } - - if new_head > live_header.metadata.tail { - assert(self.header_crc == old(self).header_crc); - return Err(InfiniteLogErr::CantAdvanceHeadPositionBeyondTail{ tail: live_header.metadata.tail }); - } - - // copy the header and update it - let mut new_header = live_header; - new_header.metadata.head = new_head; - let mut metadata_bytes = metadata_to_bytes(&new_header.metadata); - let new_crc_bytes = bytes_crc(&metadata_bytes); - let new_crc_val = u64_from_le_bytes(new_crc_bytes.as_slice()); - let ghost old_metadata_bytes = metadata_bytes@; - let mut new_header_bytes = new_crc_bytes; - new_header_bytes.append(&mut metadata_bytes); - - proof { lemma_header_crc_correct(new_header_bytes@, new_crc_bytes@, old_metadata_bytes); } - - self.update_header(wrpm, Tracked(perm), &new_header_bytes); - - // TODO: put ib update in a lemma - let old_ib = self.incorruptible_bool; - let new_ib = if old_ib == cdb0_val { - cdb1_val - } else { - assert(old_ib == cdb1_val); - cdb0_val - }; - let new_ib_bytes = u64_to_le_bytes(new_ib); - - proof { - lemma_auto_spec_u64_to_from_le_bytes(); - lemma_single_write_crash(wrpm@, incorruptible_bool_pos as int, new_ib_bytes@); - assert(perm.check_permission(old(wrpm)@)); - let new_pm = update_contents_to_reflect_write(wrpm@, incorruptible_bool_pos as int, new_ib_bytes@); - lemma_headers_unchanged(wrpm@, new_pm); - assert(new_pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8) =~= new_ib_bytes@); - - let new_header = spec_bytes_to_header(new_header_bytes@); - let (ib, headers, data) = pm_to_views(new_pm); - let header_pos = if new_ib == cdb0_val { - header1_pos - } else { - header2_pos - }; - assert(new_pm.subrange(header_pos as int, header_pos + header_size) =~= new_header_bytes@); - lemma_header_match(new_pm, header_pos as int, new_header); - lemma_header_correct(new_pm, new_header_bytes@, header_pos as int); - - // prove that new pm has the advance head update - let new_log_state = Self::recover(new_pm); - let old_log_state = Self::recover(old(wrpm)@); - match (new_log_state, old_log_state) { - (Some(new_log_state), Some(old_log_state)) => { - lemma_pm_state_header(new_pm); - lemma_pm_state_header(old(wrpm)@); - assert(new_log_state =~= old_log_state.advance_head(new_head as int)); - assert(perm.check_permission(new_pm)); - } - _ => assert(false), - } - } - - wrpm.write(incorruptible_bool_pos, new_ib_bytes.as_slice(), Tracked(perm)); - self.incorruptible_bool = new_ib; - self.head = new_head; - self.header_crc = new_crc_val; - - Ok(()) - } - - pub exec fn untrusted_read( - &self, - wrpm: &WriteRestrictedPersistentMemory, - pos: u64, - len: u64 - ) -> (result: Result, InfiniteLogErr>) - where - Perm: CheckPermission>, - PM: PersistentMemory - requires - self.inv(wrpm), - Self::recover(wrpm@) is Some, - ensures - ({ - let log = Self::recover(wrpm@).unwrap(); - match result { - Ok(bytes) => { - let true_bytes = log.log.subrange(pos - log.head, pos + len - log.head); - &&& pos >= log.head - &&& pos + len <= log.head + log.log.len() - &&& read_correct_modulo_corruption(bytes@, true_bytes, - wrpm.constants().impervious_to_corruption) - }, - Err(InfiniteLogErr::CantReadBeforeHead{ head: head_pos }) => { - &&& pos < log.head - &&& head_pos == log.head - }, - Err(InfiniteLogErr::CantReadPastTail{ tail }) => { - &&& pos + len > log.head + log.log.len() - &&& tail == log.head + log.log.len() - }, - _ => false - } - }) - { - let pm = wrpm.get_pm_ref(); - let physical_pos = Self::addr_logical_to_physical(pos, self.log_size); - let contents_end = self.log_size + contents_offset; - if pos < self.head { - Err(InfiniteLogErr::CantReadBeforeHead{ head: self.head }) - } else if pos > u64::MAX - len { - Err(InfiniteLogErr::CantReadPastTail{ tail: self.tail }) - } else if pos + len > self.tail { - Err(InfiniteLogErr::CantReadPastTail{ tail: self.tail }) - } else { - proof { - // we get a type error if we calculate physical head and tail in non-ghost code and use them here, - // so we need to calculate them here for the proof and again later for execution - let physical_head = spec_addr_logical_to_physical(self.head as int, self.log_size as int); - let physical_tail = spec_addr_logical_to_physical(self.tail as int, self.log_size as int); - if physical_head == physical_tail { - lemma_mod_equal(self.head as int, self.tail as int, self.log_size as int); - assert(len == 0); - } else if physical_head < physical_tail { - // read cannot wrap around - lemma_mod_between(self.log_size as int, self.head as int, self.tail as int, pos as int); - lemma_mod_difference_equal(self.head as int, pos as int, self.log_size as int); - } else { - // read may wrap around - lemma_mod_not_between(self.log_size as int, self.head as int, self.tail as int, pos as int); - if physical_pos <= physical_tail { - lemma_mod_wrapped_len(self.head as int, pos as int, self.log_size as int); - } else { - lemma_mod_difference_equal(self.head as int, pos as int, self.log_size as int); - } - } - } - - let physical_head = Self::addr_logical_to_physical(self.head, self.log_size); - let physical_tail = Self::addr_logical_to_physical(self.tail, self.log_size); - - let ghost log = Self::recover(pm@).unwrap(); - let ghost true_bytes = log.log.subrange(pos - log.head, pos + len - log.head); - if physical_head == physical_tail { - assert (Seq::::empty() =~= log.log.subrange(pos - log.head, pos + len - log.head)); - let buf = Vec::new(); - let ghost addrs = Seq::::empty(); - assert (if wrpm.constants().impervious_to_corruption { buf@ == true_bytes } - else { maybe_corrupted(buf@, true_bytes, addrs) }); - Ok(buf) - } else if physical_pos >= physical_head && physical_pos >= contents_end - len { - let r1_len: u64 = contents_end - physical_pos; - let r2_len: u64 = len - r1_len; - - let mut r1 = pm.read(physical_pos, r1_len); - let mut r2 = pm.read(contents_offset, r2_len); - let ghost r1_addrs = Seq::::new(r1_len as nat, |i: int| i + physical_pos as int); - let ghost r2_addrs = Seq::::new(r2_len as nat, |i: int| i + contents_offset as int); - let ghost addrs: Seq = r1_addrs.add(r2_addrs); - - r1.append(&mut r2); - assert (pm@.subrange(physical_pos as int, physical_pos + r1_len) - + pm@.subrange(contents_offset as int, contents_offset + r2_len) - =~= log.log.subrange(pos - log.head, pos + len - log.head)); - assert (if wrpm.constants().impervious_to_corruption { r1@ == true_bytes } - else { maybe_corrupted(r1@, true_bytes, addrs) }); - Ok(r1) - } else { - assert (pm@.subrange(physical_pos as int, physical_pos + len) =~= - log.log.subrange(pos - log.head, pos + len - log.head)); - let ghost addrs = Seq::::new(len as nat, |i: int| i + physical_pos); - let buf = pm.read(physical_pos, len); - assert (if wrpm.constants().impervious_to_corruption { buf@ == true_bytes } - else { maybe_corrupted(buf@, true_bytes, addrs) }); - Ok(buf) - } - } - } - - pub exec fn untrusted_get_head_and_tail( - &self, - wrpm: &WriteRestrictedPersistentMemory - ) -> (result: Result<(u64, u64, u64), InfiniteLogErr>) - where - Perm: CheckPermission>, - PM: PersistentMemory - requires - self.inv(wrpm), - Self::recover(wrpm@) is Some - ensures - match result { - Ok((result_head, result_tail, result_capacity)) => - match Self::recover(wrpm@).unwrap() { - AbstractInfiniteLogState{ head: head, log: log, capacity: capacity } => { - &&& result_head == head - &&& result_tail == head + log.len() - &&& result_capacity == capacity - } - }, - Err(_) => false, - } - { - let pm = wrpm.get_pm_ref(); - proof { lemma_pm_state_header(pm@); } - Ok((self.head, self.tail, self.log_size - 1)) - } - } -} +#![allow(dead_code)] +#![allow(non_upper_case_globals)] +#![allow(unused_variables)] + +use crate::infinitelog_t::*; +use crate::main_t::*; +use crate::math_v::*; +use crate::pmemspec_t::*; +use crate::sccf::CheckPermission; +use core::convert::TryInto; +use std::f32::consts::E; +use std::fmt::Write; +use vstd::arithmetic::div_mod::*; +use vstd::bytes::*; +use vstd::prelude::*; +use vstd::seq::*; +use vstd::set::*; +use vstd::slice::*; + +verus! { + + // entire header structure: + // bytes 0-7: incorruptible boolean + // bytes 8-39: header 1 + // bytes 40-71: header 2 + + // header version structure: + // 0-7: header CRC + // 8-15: logical head + // 16-23: logical tail + // 24-31: log size + + pub const incorruptible_bool_pos: u64 = 0; + pub const header1_pos: u64 = 8; + pub const header2_pos: u64 = 40; + + // offsets of fields within the header structure + pub const header_crc_offset: u64 = 0; + pub const header_head_offset: u64 = 8; + pub const header_tail_offset: u64 = 16; + pub const header_log_size_offset: u64 = 24; + + pub const header_size: u64 = 32; + + /// Converts the view of a PM region into its incorruptible Boolean, a view of its header, + /// and a data region. + pub open spec fn pm_to_views(pm: Seq) -> (u64, HeaderView, Seq) + { + let incorruptible_bool = spec_u64_from_le_bytes(pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8)); + // read the CRC, then read the rest of the metadata, then combine them + let crc1 = spec_u64_from_le_bytes(pm.subrange(header1_pos + header_crc_offset, header1_pos + header_crc_offset + 8)); + let crc2 = spec_u64_from_le_bytes(pm.subrange(header2_pos + header_crc_offset, header2_pos + header_crc_offset + 8)); + + let header1_metadata = spec_bytes_to_metadata(pm.subrange(header1_pos + header_head_offset, header1_pos + header_size)); + let header2_metadata = spec_bytes_to_metadata(pm.subrange(header2_pos + header_head_offset, header2_pos + header_size)); + let header_view = HeaderView { + header1: PersistentHeader { + crc: crc1, + metadata: header1_metadata, + }, + header2: PersistentHeader { + crc: crc2, + metadata: header2_metadata, + } + }; + let data_view = pm.subrange(contents_offset as int, pm.len() as int); + ( + incorruptible_bool, + header_view, + data_view + ) + } + + pub open spec fn spec_get_live_header(pm: Seq) -> PersistentHeader + { + let (ib, headers, _) = pm_to_views(pm); + if ib == cdb0_val { + headers.header1 + } else { + headers.header2 + } + } + + pub open spec fn permissions_depend_only_on_recovery_view>>(perm: &Perm) -> bool + { + forall |s1, s2| recovery_view()(s1) == recovery_view()(s2) ==> perm.check_permission(s1) == perm.check_permission(s2) + } + + pub proof fn lemma_same_permissions>>(pm1: Seq, pm2: Seq, perm: &Perm) + requires + recovery_view()(pm1) =~= recovery_view()(pm2), + perm.check_permission(pm1), + permissions_depend_only_on_recovery_view(perm) + ensures + perm.check_permission(pm2) + {} + + /// Proves that a PM region has the given header at the given position. Useful for + /// associating a region with a header structure when the struct will be used later + /// in a proof. + pub proof fn lemma_header_match(pm: Seq, header_pos: int, header: PersistentHeader) + requires + pm.len() > contents_offset, + header_pos == header1_pos || header_pos == header2_pos, + spec_bytes_to_header(pm.subrange(header_pos as int, header_pos + header_size)) == header, + ensures + ({ + let (_, headers, _) = pm_to_views(pm); + &&& header_pos == header1_pos ==> + headers.header1 == header + &&& header_pos == header2_pos ==> + headers.header2 == header + }) + { + assert(pm.subrange(header_pos as int, header_pos + header_size) =~= + pm.subrange(header_pos + header_crc_offset, header_pos + header_crc_offset + 8) + + pm.subrange(header_pos + header_head_offset, header_pos + header_size) + ); + lemma_bytes_combine_into_header( + pm.subrange(header_pos + header_crc_offset, header_pos + header_crc_offset + 8), + pm.subrange(header_pos + header_head_offset, header_pos + header_size), + header + ); + } + + /// Proves that a given header structure consists of a CRC given in bytes as `crc_bytes` and a metadata structure + /// given in bytes as `metadata_bytes`. + pub proof fn lemma_bytes_combine_into_header(crc_bytes: Seq, metadata_bytes: Seq, header: PersistentHeader) + requires + crc_bytes.len() == 8, + metadata_bytes.len() == header_size - 8, + spec_bytes_to_header((crc_bytes + metadata_bytes)) == header, + ensures + ({ + let combined_header = PersistentHeader { crc: spec_u64_from_le_bytes(crc_bytes), metadata: spec_bytes_to_metadata(metadata_bytes) }; + header == combined_header + }) + { + let crc_val = spec_u64_from_le_bytes(crc_bytes); + let metadata = spec_bytes_to_metadata(metadata_bytes); + lemma_seq_addition(crc_bytes, metadata_bytes); + + let combined_header = spec_bytes_to_header((crc_bytes + metadata_bytes)); + assert(combined_header.crc == crc_val); + assert(metadata == spec_bytes_to_metadata((crc_bytes + metadata_bytes).subrange(header_head_offset as int, header_size as int))); + assert(combined_header.metadata == metadata); + } + + /// Converse of lemma_bytes_combine_into_header; proves that the byte representation of a header consists of + /// the byte representations of its CRC and metadata + pub proof fn lemma_header_split_into_bytes(crc_bytes: Seq, metadata_bytes: Seq, header_bytes: Seq) + requires + crc_bytes.len() == 8, + metadata_bytes.len() == header_size - 8, + header_bytes.len() == header_size, + ({ + let header = PersistentHeader { crc: spec_u64_from_le_bytes(crc_bytes), metadata: spec_bytes_to_metadata(metadata_bytes) }; + spec_bytes_to_header(header_bytes) == header + }), + ensures + crc_bytes + metadata_bytes =~= header_bytes + { + lemma_auto_spec_u64_to_from_le_bytes(); + let header = PersistentHeader { crc: spec_u64_from_le_bytes(crc_bytes), metadata: spec_bytes_to_metadata(metadata_bytes) }; + assert(header.crc == spec_u64_from_le_bytes(crc_bytes)); + assert(header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8) =~= spec_u64_to_le_bytes(header.crc)); + assert(crc_bytes =~= spec_u64_to_le_bytes(header.crc)); + + assert(header.metadata == spec_bytes_to_metadata(metadata_bytes)); + assert(header.metadata == spec_bytes_to_metadata(header_bytes.subrange(header_head_offset as int, header_size as int))); + lemma_metadata_bytes_eq(metadata_bytes, header_bytes.subrange(header_head_offset as int, header_size as int), header.metadata); + assert(header_bytes.subrange(header_head_offset as int, header_size as int) =~= metadata_bytes); + + } + + pub proof fn lemma_seq_addition(bytes1: Seq, bytes2: Seq) + ensures + ({ + let i = bytes1.len() as int; + let j = bytes2.len() as int; + &&& (bytes1 + bytes2).subrange(0, i) =~= bytes1 + &&& (bytes1 + bytes2).subrange(i, i + j) =~= bytes2 + }) + { + assert(forall |i: int| #![auto] 0 <= i < bytes1.len() ==> (bytes1 + bytes2)[i] == bytes1[i]); + assert(forall |i: int| #![auto] 0 <= i < bytes2.len() ==> (bytes1 + bytes2)[bytes1.len() + i] == bytes2[i]); + } + + #[verifier::ext_equal] + pub struct PersistentHeader { + pub crc: u64, + pub metadata: PersistentHeaderMetadata, + } + + #[verifier::ext_equal] + pub struct PersistentHeaderMetadata { + pub head: u64, + pub tail: u64, + pub log_size: u64, + } + + #[verifier::ext_equal] + pub struct HeaderView { + pub header1: PersistentHeader, + pub header2: PersistentHeader, + } + + /// Spec code only converts byte representations to structures and does not go the other way + /// to simplify reasoning about persistent structures (although the opposite direction is + /// implemented in exec code). + + exec fn bytes_to_header(bytes: &[u8]) -> (out: PersistentHeader) + requires + bytes@.len() == header_size + ensures + out == spec_bytes_to_header(bytes@) + { + let crc_bytes = slice_subrange(bytes, header_crc_offset as usize, (header_crc_offset + 8) as usize); + let metadata_bytes = slice_subrange(bytes, header_head_offset as usize, header_size as usize); + + PersistentHeader { + crc: u64_from_le_bytes(crc_bytes), + metadata: bytes_to_metadata(metadata_bytes), + } + } + + exec fn header_to_bytes(header: &PersistentHeader) -> (out: Vec) + ensures + header == spec_bytes_to_header(out@), + spec_u64_from_le_bytes(out@.subrange(header_crc_offset as int, header_crc_offset + 8)) == header.crc, + spec_bytes_to_metadata(out@.subrange(header_head_offset as int, header_size as int)) == header.metadata, + out@.len() == header_size + { + proof { lemma_auto_spec_u64_to_from_le_bytes(); } + + let mut metadata_bytes = metadata_to_bytes(&header.metadata); + let mut crc_bytes = u64_to_le_bytes(header.crc); + let ghost old_metadata_bytes = metadata_bytes@; + let ghost old_crc_bytes = crc_bytes@; + crc_bytes.append(&mut metadata_bytes); + proof { + lemma_auto_spec_u64_to_from_le_bytes(); + assert(old_crc_bytes =~= crc_bytes@.subrange(header_crc_offset as int, header_crc_offset + 8)); + assert(old_metadata_bytes =~= crc_bytes@.subrange(header_head_offset as int, header_size as int)); + } + crc_bytes + } + + exec fn bytes_to_metadata(bytes: &[u8]) -> (out: PersistentHeaderMetadata) + requires + bytes@.len() == header_size - 8 + ensures + out == spec_bytes_to_metadata(bytes@) + { + let head_bytes = slice_subrange(bytes, (header_head_offset - 8) as usize, (header_head_offset - 8 + 8) as usize); + let tail_bytes = slice_subrange(bytes, (header_tail_offset - 8) as usize, (header_tail_offset - 8+ 8) as usize); + let log_size_bytes = slice_subrange(bytes, (header_log_size_offset - 8) as usize, (header_log_size_offset - 8 + 8) as usize); + + PersistentHeaderMetadata { + head: u64_from_le_bytes(head_bytes), + tail: u64_from_le_bytes(tail_bytes), + log_size: u64_from_le_bytes(log_size_bytes), + } + } + + exec fn metadata_to_bytes(metadata: &PersistentHeaderMetadata) -> (out: Vec) + ensures + metadata == spec_bytes_to_metadata(out@), + out@.len() == header_size - 8, + { + let mut bytes: Vec = Vec::new(); + let ghost old_bytes = bytes@; + + let mut head_bytes = u64_to_le_bytes(metadata.head); + let ghost old_head_bytes = head_bytes@; + let mut tail_bytes = u64_to_le_bytes(metadata.tail); + let ghost old_tail_bytes = tail_bytes@; + let mut log_size_bytes = u64_to_le_bytes(metadata.log_size); + let ghost old_log_size_bytes = log_size_bytes@; + + bytes.append(&mut head_bytes); + bytes.append(&mut tail_bytes); + bytes.append(&mut log_size_bytes); + + proof { + lemma_auto_spec_u64_to_from_le_bytes(); + assert(old_bytes == Seq::::empty()); + assert(old_head_bytes =~= bytes@.subrange(header_head_offset - 8, header_head_offset - 8 + 8)); + assert(old_tail_bytes =~= bytes@.subrange(header_tail_offset - 8, header_tail_offset - 8 + 8)); + assert(old_log_size_bytes =~= bytes@.subrange(header_log_size_offset - 8, header_log_size_offset - 8 + 8)); + } + bytes + } + + exec fn crc_and_metadata_bytes_to_header(crc_bytes: &[u8], header_bytes: &[u8]) -> (out: PersistentHeader) + requires + crc_bytes@.len() == 8, + header_bytes@.len() == header_size - 8 + ensures + out.crc == spec_u64_from_le_bytes(crc_bytes@), + out.metadata == spec_bytes_to_metadata(header_bytes@) + { + let head_bytes = slice_subrange(header_bytes, (header_head_offset - 8) as usize, (header_head_offset + 8 - 8) as usize); + let tail_bytes = slice_subrange(header_bytes, (header_tail_offset - 8) as usize, (header_tail_offset + 8 - 8) as usize); + let log_size_bytes = slice_subrange(header_bytes, (header_log_size_offset - 8) as usize, (header_log_size_offset + 8 - 8) as usize); + + PersistentHeader { + crc: u64_from_le_bytes(crc_bytes), + metadata: PersistentHeaderMetadata { + head: u64_from_le_bytes(head_bytes), + tail: u64_from_le_bytes(tail_bytes), + log_size: u64_from_le_bytes(log_size_bytes) + } + } + } + + pub open spec(checked) fn spec_bytes_to_metadata(header_seq: Seq) -> PersistentHeaderMetadata + recommends + header_seq.len() == 3*8 + { + let head = spec_u64_from_le_bytes(header_seq.subrange(header_head_offset - 8, header_head_offset - 8 + 8)); + let tail = spec_u64_from_le_bytes(header_seq.subrange(header_tail_offset - 8, header_tail_offset - 8 + 8)); + let log_size = spec_u64_from_le_bytes(header_seq.subrange(header_log_size_offset - 8, header_log_size_offset - 8 + 8)); + PersistentHeaderMetadata { + head, + tail, + log_size + } + } + + /// Proves that two sequences of bytes (assumed to be the subrange of a persistent memory device containing + /// the PersistentHeaderMetadata) are equivalent if their PersistentHeaderMetadata representations are equivalent + pub proof fn lemma_metadata_bytes_eq(bytes1: Seq, bytes2: Seq, metadata: PersistentHeaderMetadata) + requires + bytes1.len() == header_size - 8, + bytes2.len() == header_size - 8, + metadata == spec_bytes_to_metadata(bytes1), + metadata == spec_bytes_to_metadata(bytes2), + ensures + bytes1 =~= bytes2 + { + let metadata1 = spec_bytes_to_metadata(bytes1); + let metadata2 = spec_bytes_to_metadata(bytes2); + + // TODO: could write a lemma that triggers on from instead of to - might help here + lemma_auto_spec_u64_to_from_le_bytes(); + assert(spec_u64_to_le_bytes(metadata1.head) == spec_u64_to_le_bytes(metadata2.head)); + assert(metadata1.head == spec_u64_from_le_bytes(bytes1.subrange(header_head_offset - 8, header_head_offset - 8 + 8))); + assert(metadata2.head == spec_u64_from_le_bytes(bytes2.subrange(header_head_offset - 8, header_head_offset - 8 + 8))); + assert(bytes1.subrange(header_head_offset - 8, header_head_offset - 8 + 8) =~= bytes2.subrange(header_head_offset - 8, header_head_offset - 8 + 8)); + + assert(spec_u64_to_le_bytes(metadata1.tail) == spec_u64_to_le_bytes(metadata2.tail)); + assert(metadata1.tail == spec_u64_from_le_bytes(bytes1.subrange(header_tail_offset - 8, header_tail_offset - 8 + 8))); + assert(metadata2.tail == spec_u64_from_le_bytes(bytes2.subrange(header_tail_offset - 8, header_tail_offset - 8 + 8))); + assert(bytes1.subrange(header_tail_offset - 8, header_tail_offset - 8 + 8) =~= bytes2.subrange(header_tail_offset - 8, header_tail_offset - 8 + 8)); + + assert(spec_u64_to_le_bytes(metadata1.log_size) == spec_u64_to_le_bytes(metadata2.log_size)); + assert(metadata1.log_size == spec_u64_from_le_bytes(bytes1.subrange(header_log_size_offset - 8, header_log_size_offset - 8 + 8))); + assert(metadata2.log_size == spec_u64_from_le_bytes(bytes2.subrange(header_log_size_offset - 8, header_log_size_offset - 8 + 8))); + assert(bytes1.subrange(header_log_size_offset - 8, header_log_size_offset - 8 + 8) =~= bytes2.subrange(header_log_size_offset - 8, header_log_size_offset - 8 + 8)); + + assert(bytes1 =~= bytes1.subrange(header_head_offset - 8, header_head_offset - 8 + 8) + + bytes1.subrange(header_tail_offset - 8, header_tail_offset - 8 + 8) + + bytes1.subrange(header_log_size_offset - 8, header_log_size_offset - 8 + 8)); + } + + pub open spec(checked) fn spec_bytes_to_header(header_seq: Seq) -> PersistentHeader + recommends + header_seq.len() == header_size + { + let crc_val = spec_u64_from_le_bytes(header_seq.subrange(header_crc_offset as int, header_crc_offset +8)); + let metadata = spec_bytes_to_metadata(header_seq.subrange(header_head_offset as int, header_size as int)); + PersistentHeader { + crc: crc_val, + metadata + } + } + + /// Proves that a write to data that does not touch any metadata is crash safe. + pub proof fn lemma_data_write_is_safe(pm: Seq, bytes: Seq, write_addr: int, perm: &Perm) + where + Perm: CheckPermission>, + requires + UntrustedLogImpl::recover(pm) is Some, + pm.len() > contents_offset, + contents_offset <= write_addr < pm.len(), + perm.check_permission(pm), + permissions_depend_only_on_recovery_view(perm), + ({ + // write must be a valid write and not overlap the live log + let live_header = spec_get_live_header(pm); + let physical_head = spec_addr_logical_to_physical(live_header.metadata.head as int, live_header.metadata.log_size as int); + let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); + &&& physical_head <= physical_tail ==> { + &&& write_addr + bytes.len() <= live_header.metadata.log_size + contents_offset + &&& write_addr < physical_head ==> write_addr + bytes.len() <= physical_head + &&& (physical_tail <= write_addr || write_addr < physical_head) + } + &&& physical_tail < physical_head ==> { + &&& physical_tail <= write_addr <= write_addr + bytes.len() < physical_head + } + }), + ensures + UntrustedLogImpl::recover(pm) is Some, + forall |chunks_flushed| { + let new_pm = #[trigger] update_contents_to_reflect_partially_flushed_write( + pm, write_addr, bytes, chunks_flushed); + perm.check_permission(new_pm) + }, + ({ + let new_pm = update_contents_to_reflect_write(pm, write_addr, bytes); + perm.check_permission(new_pm) + }), + update_data_view_postcond(pm, bytes, write_addr), + { + let new_pm = update_contents_to_reflect_write(pm, write_addr, bytes); + lemma_append_data_update_view(pm, bytes, write_addr); + lemma_same_log_state(pm, new_pm); + + assert forall |chunks_flushed| { + let new_pm = #[trigger] update_contents_to_reflect_partially_flushed_write( + pm, write_addr, bytes, chunks_flushed); + perm.check_permission(new_pm) + } by { + let new_pm = update_contents_to_reflect_partially_flushed_write( + pm, write_addr, bytes, chunks_flushed); + lemma_append_data_update_view_crash(pm, bytes, write_addr, chunks_flushed); + lemma_same_log_state(pm, new_pm); + lemma_same_permissions(pm, new_pm, perm); + } + } + + pub open spec fn update_data_view_postcond(pm: Seq, new_bytes: Seq, write_addr: int) -> bool + { + let new_pm = update_contents_to_reflect_write(pm, write_addr, new_bytes); + let (old_ib, old_headers, old_data) = pm_to_views(pm); + let (new_ib, new_headers, new_data) = pm_to_views(new_pm); + let live_header = spec_get_live_header(pm); + let physical_head = spec_addr_logical_to_physical(live_header.metadata.head as int, live_header.metadata.log_size as int); + let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); + &&& old_ib == new_ib + &&& old_headers == new_headers + &&& new_data.len() == old_data.len() + &&& new_data.subrange(write_addr - contents_offset, write_addr - contents_offset + new_bytes.len()) =~= new_bytes + &&& new_data.subrange(0, write_addr - contents_offset) =~= old_data.subrange(0, write_addr - contents_offset) + &&& new_data.subrange(write_addr - contents_offset + new_bytes.len(), new_data.len() as int) =~= + old_data.subrange(write_addr - contents_offset + new_bytes.len(), old_data.len() as int) + &&& UntrustedLogImpl::recover(new_pm) is Some + + &&& physical_head < physical_tail ==> + new_data.subrange(physical_head - contents_offset, physical_tail - contents_offset) =~= old_data.subrange(physical_head - contents_offset, physical_tail - contents_offset) + &&& physical_tail < physical_head ==> { + &&& old_data.subrange(physical_head - contents_offset, live_header.metadata.log_size as int) =~= new_data.subrange(physical_head - contents_offset, live_header.metadata.log_size as int) + &&& old_data.subrange(0, physical_tail - contents_offset) =~= new_data.subrange(0, physical_tail - contents_offset) + } + } + + /// Proves that a non-crashing data write updates data bytes but no log metadata. + pub proof fn lemma_append_data_update_view(pm: Seq, new_bytes: Seq, write_addr: int) + requires + UntrustedLogImpl::recover(pm) is Some, + pm.len() > contents_offset, + contents_offset <= write_addr < pm.len(), + ({ + // write must be a valid write and not overlap the live log + let live_header = spec_get_live_header(pm); + let physical_head = spec_addr_logical_to_physical(live_header.metadata.head as int, live_header.metadata.log_size as int); + let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); + &&& physical_head <= physical_tail ==> { + &&& write_addr + new_bytes.len() <= live_header.metadata.log_size + contents_offset + &&& write_addr < physical_head ==> write_addr + new_bytes.len() <= physical_head + &&& (physical_tail <= write_addr || write_addr < physical_head) + } + &&& physical_tail < physical_head ==> { + &&& physical_tail <= write_addr <= write_addr + new_bytes.len() < physical_head + } + }), + ensures + UntrustedLogImpl::recover(pm) is Some, + update_data_view_postcond(pm, new_bytes, write_addr), + { + let live_header = spec_get_live_header(pm); + let physical_head = spec_addr_logical_to_physical(live_header.metadata.head as int, live_header.metadata.log_size as int); + let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); + let new_pm = update_contents_to_reflect_write(pm, write_addr, new_bytes); + lemma_headers_unchanged(pm, new_pm); + lemma_incorruptible_bool_unchanged(pm, new_pm); + assert(live_header == spec_get_live_header(new_pm)); + assert(new_pm.subrange(0, write_addr) =~= pm.subrange(0, write_addr)); + assert(new_pm.subrange(write_addr + new_bytes.len(), new_pm.len() as int) =~= pm.subrange(write_addr + new_bytes.len(), pm.len() as int)); + lemma_subrange_equality_implies_subsubrange_equality(pm, new_pm, 0, write_addr); + lemma_subrange_equality_implies_subsubrange_equality(pm, new_pm, write_addr + new_bytes.len(), new_pm.len() as int); + if physical_head < physical_tail { + assert(new_pm.subrange(physical_head as int, physical_tail as int) =~= pm.subrange(physical_head as int, physical_tail as int)); + } + } + + /// Proves that a crashing data write updates data bytes but no log metadata. + pub proof fn lemma_append_data_update_view_crash(pm: Seq, new_bytes: Seq, write_addr: int, chunks_flushed: Set) + requires + UntrustedLogImpl::recover(pm) is Some, + pm.len() > contents_offset, + contents_offset <= write_addr < pm.len(), + ({ + // write must be a valid write and not overlap the live log + let live_header = spec_get_live_header(pm); + let physical_head = spec_addr_logical_to_physical(live_header.metadata.head as int, live_header.metadata.log_size as int); + let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); + &&& physical_head <= physical_tail ==> write_addr + new_bytes.len() <= live_header.metadata.log_size + contents_offset + &&& physical_tail < physical_head ==> write_addr + new_bytes.len() < physical_head + }) + ensures + UntrustedLogImpl::recover(pm) is Some, + ({ + let new_pm = update_contents_to_reflect_partially_flushed_write(pm, write_addr, new_bytes, chunks_flushed); + let (old_ib, old_headers, old_data) = pm_to_views(pm); + let (new_ib, new_headers, new_data) = pm_to_views(new_pm); + &&& old_ib == new_ib + &&& old_headers == new_headers + &&& new_data.len() == old_data.len() + &&& new_data.subrange(0, write_addr - contents_offset) =~= old_data.subrange(0, write_addr - contents_offset) + &&& new_data.subrange(write_addr - contents_offset + new_bytes.len(), new_data.len() as int) =~= + old_data.subrange(write_addr - contents_offset + new_bytes.len(), old_data.len() as int) + &&& UntrustedLogImpl::recover(new_pm) is Some + }) + { + let live_header = spec_get_live_header(pm); + let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); + let new_pm = update_contents_to_reflect_partially_flushed_write(pm, write_addr, new_bytes, chunks_flushed); + lemma_headers_unchanged(pm, new_pm); + lemma_incorruptible_bool_unchanged(pm, new_pm); + assert(new_pm.subrange(0, write_addr) =~= pm.subrange(0, write_addr)); + assert(new_pm.subrange(write_addr + new_bytes.len(), new_pm.len() as int) =~= pm.subrange(write_addr + new_bytes.len(), pm.len() as int)); + lemma_subrange_equality_implies_subsubrange_equality(pm, new_pm, 0, write_addr); + } + + /// Proves that a non-crashing update to the inactive header does not change any visible PM state. + pub proof fn lemma_inactive_header_update_view(pm: Seq, new_header_bytes: Seq, header_pos: int) + requires + UntrustedLogImpl::recover(pm) is Some, + header_pos == header1_pos || header_pos == header2_pos, + ({ + // the new bytes must be written to the inactive header + let (old_ib, old_headers, old_data) = pm_to_views(pm); + &&& old_ib == cdb0_val ==> header_pos == header2_pos + &&& old_ib == cdb1_val ==> header_pos == header1_pos + }), + new_header_bytes.len() == header_size, + pm.len() > contents_offset, + ensures + ({ + let new_pm = update_contents_to_reflect_write(pm, header_pos, new_header_bytes); + let (old_ib, old_headers, old_data) = pm_to_views(pm); + let (new_ib, new_headers, new_data) = pm_to_views(new_pm); + &&& old_ib == new_ib + &&& old_data =~= old_data + &&& header_pos == header1_pos ==> + old_headers.header2 == new_headers.header2 + &&& header_pos == header2_pos ==> + old_headers.header1 == new_headers.header1 + &&& UntrustedLogImpl::recover(new_pm) is Some + }) + { + let new_pm = update_contents_to_reflect_write(pm, header_pos, new_header_bytes); + let (new_ib, new_headers, new_data) = pm_to_views(new_pm); + assert(pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8) =~= new_pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8)); + if header_pos == header1_pos { + // we wrote to header1, so header2 should have stayed the same + assert(pm.subrange(header2_pos + header_crc_offset, header2_pos + header_crc_offset + 8) =~= + new_pm.subrange(header2_pos + header_crc_offset, header2_pos + header_crc_offset + 8)); + + assert(pm.subrange(header2_pos + header_head_offset, header2_pos + header_size) =~= + new_pm.subrange(header2_pos + header_head_offset, header2_pos + header_size)); + } else { + // we wrote to header2, so header1 should have stayed the same + assert(pm.subrange(header1_pos + header_crc_offset, header1_pos + header_crc_offset + 8) =~= + new_pm.subrange(header1_pos + header_crc_offset, header1_pos + header_crc_offset + 8)); + + assert(pm.subrange(header1_pos + header_head_offset, header1_pos + header_size) =~= + new_pm.subrange(header1_pos + header_head_offset, header1_pos + header_size)); + } + } + + /// Proves that a crashing update to the inactive header does not change any visible PM state. + pub proof fn lemma_inactive_header_update_view_crash(pm: Seq, new_header_bytes: Seq, header_pos: int, chunks_flushed: Set) + requires + UntrustedLogImpl::recover(pm) is Some, + header_pos == header1_pos || header_pos == header2_pos, + ({ + // the new bytes must be written to the inactive header + let (old_ib, old_headers, old_data) = pm_to_views(pm); + &&& old_ib == cdb0_val ==> header_pos == header2_pos + &&& old_ib == cdb1_val ==> header_pos == header1_pos + }), + new_header_bytes.len() == header_size, + pm.len() > contents_offset, + ensures + ({ + let new_pm = update_contents_to_reflect_partially_flushed_write( + pm, header_pos, new_header_bytes, chunks_flushed); + let (old_ib, old_headers, old_data) = pm_to_views(pm); + let (new_ib, new_headers, new_data) = pm_to_views(new_pm); + &&& old_ib == new_ib + &&& old_data =~= old_data + &&& header_pos == header1_pos ==> + old_headers.header2 == new_headers.header2 + &&& header_pos == header2_pos ==> + old_headers.header1 == new_headers.header1 + &&& UntrustedLogImpl::recover(new_pm) is Some + }) + { + let new_pm = update_contents_to_reflect_partially_flushed_write( + pm, header_pos, new_header_bytes, chunks_flushed); + assert(pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8) =~= new_pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8)); + if header_pos == header1_pos { + // we wrote to header1, so header2 should have stayed the same + assert(pm.subrange(header2_pos + header_crc_offset, header2_pos + header_crc_offset + 8) =~= + new_pm.subrange(header2_pos + header_crc_offset, header2_pos + header_crc_offset + 8)); + + assert(pm.subrange(header2_pos + header_head_offset, header2_pos + header_size) =~= + new_pm.subrange(header2_pos + header_head_offset, header2_pos + header_size)); + } else { + // we wrote to header2, so header1 should have stayed the same + assert(pm.subrange(header1_pos + header_crc_offset, header1_pos + header_crc_offset + 8) =~= + new_pm.subrange(header1_pos + header_crc_offset, header1_pos + header_crc_offset + 8)); + + assert(pm.subrange(header1_pos + header_head_offset, header1_pos + header_size) =~= + new_pm.subrange(header1_pos + header_head_offset, header1_pos + header_size)); + } + } + + pub proof fn lemma_append_ib_update_effect_on_committed>>( + pm: Seq, + new_ib: u64, + bytes_to_append: Seq, + new_header_bytes: Seq, + perm: &Perm + ) + requires + pm.len() > contents_offset, + UntrustedLogImpl::recover(pm) is Some, + new_ib == cdb0_val || new_ib == cdb1_val, + new_ib == cdb0_val ==> + pm.subrange(header1_pos as int, header1_pos + header_size) == new_header_bytes, + new_ib == cdb1_val ==> + pm.subrange(header2_pos as int, header2_pos + header_size) == new_header_bytes, + new_header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8) == + spec_crc_bytes(new_header_bytes.subrange(header_head_offset as int, header_size as int)), + ({ + let new_header = spec_bytes_to_header(new_header_bytes); + let live_header = spec_get_live_header(pm); + &&& new_header.metadata.tail == live_header.metadata.tail + bytes_to_append.len() + &&& new_header.metadata.head == live_header.metadata.head + &&& new_header.metadata.log_size == live_header.metadata.log_size + &&& new_header.metadata.tail - new_header.metadata.head < new_header.metadata.log_size + }), + perm.check_permission(pm), + permissions_depend_only_on_recovery_view(perm), + ({ + let live_header = spec_get_live_header(pm); + let physical_head = spec_addr_logical_to_physical(live_header.metadata.head as int, live_header.metadata.log_size as int); + let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); + let contents_end = (live_header.metadata.log_size + contents_offset) as int; + let append_size = bytes_to_append.len(); + let len1 = (contents_end - physical_tail); + let len2 = bytes_to_append.len() - len1; + + &&& physical_tail + append_size >= contents_end ==> { + &&& pm.subrange(physical_tail, contents_end) =~= bytes_to_append.subrange(0, len1) + &&& pm.subrange(contents_offset as int, contents_offset + len2) =~= bytes_to_append.subrange(len1 as int, append_size as int) + &&& bytes_to_append =~= pm.subrange(physical_tail, contents_end) + pm.subrange(contents_offset as int, contents_offset + len2) + } + &&& physical_head <= physical_tail && physical_tail + append_size < contents_end ==> { + pm.subrange(physical_tail, physical_tail + append_size) =~= bytes_to_append + } + &&& physical_tail < physical_head ==> { + &&& physical_tail + append_size < physical_head + &&& pm.subrange(physical_tail, physical_tail + append_size) =~= bytes_to_append + } + }), + ({ + let old_log_state = UntrustedLogImpl::recover(pm); + forall |pm_state| #[trigger] perm.check_permission(pm_state) <==> { + let log_state = UntrustedLogImpl::recover(pm_state); + log_state == old_log_state || log_state == Some(old_log_state.unwrap().append(bytes_to_append)) + } + }), + ensures + ({ + let ib_bytes = spec_u64_to_le_bytes(new_ib); + let new_pm = update_contents_to_reflect_write(pm, incorruptible_bool_pos as int, ib_bytes); + let old_log_state = UntrustedLogImpl::recover(pm); + let new_log_state = UntrustedLogImpl::recover(new_pm); + let new_live_header = spec_get_live_header(new_pm); + let (new_pm_ib, _, _) = pm_to_views(new_pm); + &&& match (old_log_state, new_log_state) { + (Some(old_log_state), Some(new_log_state)) => { + &&& new_log_state =~= old_log_state.append(bytes_to_append) + &&& perm.check_permission(new_pm) + } + _ => false, + } + &&& new_live_header == spec_bytes_to_header(new_header_bytes) + &&& new_ib == new_pm_ib + }), + { + let ib_bytes = spec_u64_to_le_bytes(new_ib); + let live_header = spec_get_live_header(pm); + let append_size = bytes_to_append.len(); + let contents_end = live_header.metadata.log_size + contents_offset; + let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); + + lemma_auto_spec_u64_to_from_le_bytes(); + + let new_pm = update_contents_to_reflect_write(pm, incorruptible_bool_pos as int, ib_bytes); + lemma_headers_unchanged(pm, new_pm); + assert(new_pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8) =~= ib_bytes); + + let new_header = spec_bytes_to_header(new_header_bytes); + let (ib, headers, data) = pm_to_views(new_pm); + let header_pos = if new_ib == cdb0_val { + header1_pos + } else { + header2_pos + }; + assert(new_pm.subrange(header_pos as int, header_pos + header_size) =~= new_header_bytes); + lemma_header_match(new_pm, header_pos as int, new_header); + lemma_header_correct(new_pm, new_header_bytes, header_pos as int); + + // prove that new pm has the append update + let new_log_state = UntrustedLogImpl::recover(new_pm); + let old_log_state = UntrustedLogImpl::recover(pm); + + match (new_log_state, old_log_state) { + (Some(new_log_state), Some(old_log_state)) => { + lemma_pm_state_header(new_pm); + lemma_pm_state_header(pm); + + let old_header = spec_get_live_header(pm); + let live_header = spec_get_live_header(new_pm); + assert(live_header == new_header); + + assert(live_header.metadata.head == old_header.metadata.head); + assert(live_header.metadata.tail == old_header.metadata.tail + bytes_to_append.len()); + + let physical_head = spec_addr_logical_to_physical(live_header.metadata.head as int, live_header.metadata.log_size as int); + let new_physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); + let old_physical_tail = spec_addr_logical_to_physical(old_header.metadata.tail as int, old_header.metadata.log_size as int); + assert(old_physical_tail == physical_tail); + + let (_, _, old_data) = pm_to_views(pm); + let (_, _, new_data) = pm_to_views(pm); + + if physical_head <= old_physical_tail { + if old_physical_tail + append_size >= contents_end { + assert(new_log_state.log =~= new_data.subrange(physical_head - contents_offset, old_physical_tail - contents_offset) + + new_data.subrange(old_physical_tail - contents_offset, contents_end - contents_offset) + + new_data.subrange(0, new_physical_tail - contents_offset)); + assert(new_log_state.log =~= old_data.subrange(physical_head - contents_offset, old_physical_tail - contents_offset) + + new_data.subrange(old_physical_tail - contents_offset, contents_end - contents_offset) + + new_data.subrange(0, new_physical_tail - contents_offset)); + let len1 = (contents_end - old_physical_tail); + let len2 = bytes_to_append.len() - len1; + assert(bytes_to_append =~= new_data.subrange(old_physical_tail - contents_offset, contents_end - contents_offset) + + new_data.subrange(0, new_physical_tail - contents_offset)); + assert(new_log_state.log =~= old_data.subrange(physical_head - contents_offset, old_physical_tail - contents_offset) + bytes_to_append); + } else { + assert(old_data.subrange(0, old_physical_tail - contents_offset) =~= new_data.subrange(0, old_physical_tail - contents_offset)); + assert(new_data.subrange(old_physical_tail - contents_offset, old_physical_tail - contents_offset + append_size) =~= bytes_to_append); + } + } else { // physical_tail < physical_head + assert(old_physical_tail + append_size < physical_head); + } + assert(new_log_state =~= old_log_state.append(bytes_to_append)); + assert(perm.check_permission(new_pm)); + } + _ => assert(false), + } + } + + /// Proves that an update to the incorruptible boolean is crash-safe and switches the log's + /// active header. This lemma does most of the work to prove that untrusted_append is + /// implemented correctly. + pub proof fn lemma_append_ib_update>>( + pm: Seq, + new_ib: u64, + bytes_to_append: Seq, + new_header_bytes: Seq, + perm: &Perm + ) + requires + pm.len() > contents_offset, + UntrustedLogImpl::recover(pm) is Some, + new_ib == cdb0_val || new_ib == cdb1_val, + new_ib == cdb0_val ==> + pm.subrange(header1_pos as int, header1_pos + header_size) == new_header_bytes, + new_ib == cdb1_val ==> + pm.subrange(header2_pos as int, header2_pos + header_size) == new_header_bytes, + new_header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8) == + spec_crc_bytes(new_header_bytes.subrange(header_head_offset as int, header_size as int)), + ({ + let new_header = spec_bytes_to_header(new_header_bytes); + let live_header = spec_get_live_header(pm); + &&& new_header.metadata.tail == live_header.metadata.tail + bytes_to_append.len() + &&& new_header.metadata.head == live_header.metadata.head + &&& new_header.metadata.log_size == live_header.metadata.log_size + &&& new_header.metadata.tail - new_header.metadata.head < new_header.metadata.log_size + }), + perm.check_permission(pm), + permissions_depend_only_on_recovery_view(perm), + ({ + let live_header = spec_get_live_header(pm); + let physical_head = spec_addr_logical_to_physical(live_header.metadata.head as int, live_header.metadata.log_size as int); + let physical_tail = spec_addr_logical_to_physical(live_header.metadata.tail as int, live_header.metadata.log_size as int); + let contents_end = (live_header.metadata.log_size + contents_offset) as int; + let append_size = bytes_to_append.len(); + let len1 = (contents_end - physical_tail); + let len2 = bytes_to_append.len() - len1; + + &&& physical_tail + append_size >= contents_end ==> { + &&& pm.subrange(physical_tail, contents_end) =~= bytes_to_append.subrange(0, len1) + &&& pm.subrange(contents_offset as int, contents_offset + len2) =~= bytes_to_append.subrange(len1 as int, append_size as int) + &&& bytes_to_append =~= pm.subrange(physical_tail, contents_end) + pm.subrange(contents_offset as int, contents_offset + len2) + } + &&& physical_head <= physical_tail && physical_tail + append_size < contents_end ==> { + pm.subrange(physical_tail, physical_tail + append_size) =~= bytes_to_append + } + &&& physical_tail < physical_head ==> { + &&& physical_tail + append_size < physical_head + &&& pm.subrange(physical_tail, physical_tail + append_size) =~= bytes_to_append + } + }), + ({ + let old_log_state = UntrustedLogImpl::recover(pm); + forall |pm_state| #[trigger] perm.check_permission(pm_state) <==> { + let log_state = UntrustedLogImpl::recover(pm_state); + log_state == old_log_state || log_state == Some(old_log_state.unwrap().append(bytes_to_append)) + } + }), + ensures + ({ + let ib_bytes = spec_u64_to_le_bytes(new_ib); + let new_pm = update_contents_to_reflect_write(pm, incorruptible_bool_pos as int, ib_bytes); + let old_log_state = UntrustedLogImpl::recover(pm); + let new_log_state = UntrustedLogImpl::recover(new_pm); + let new_live_header = spec_get_live_header(new_pm); + let (new_pm_ib, _, _) = pm_to_views(new_pm); + &&& match (old_log_state, new_log_state) { + (Some(old_log_state), Some(new_log_state)) => { + &&& new_log_state =~= old_log_state.append(bytes_to_append) + &&& perm.check_permission(new_pm) + } + _ => false, + } + &&& new_live_header == spec_bytes_to_header(new_header_bytes) + &&& new_ib == new_pm_ib + }), + forall |chunks_flushed| { + let new_pm = #[trigger] update_contents_to_reflect_partially_flushed_write( + pm, incorruptible_bool_pos as int, spec_u64_to_le_bytes(new_ib), chunks_flushed); + &&& perm.check_permission(new_pm) + }, + { + lemma_append_ib_update_effect_on_committed::(pm, new_ib, bytes_to_append, new_header_bytes, perm); + + let ib_bytes = spec_u64_to_le_bytes(new_ib); + lemma_auto_spec_u64_to_from_le_bytes(); + lemma_single_write_crash(pm, incorruptible_bool_pos as int, ib_bytes); + } + + pub open spec fn live_data_view_eq(old_pm: Seq, new_pm: Seq) -> bool + { + let (old_ib, old_headers, old_data) = pm_to_views(old_pm); + let (new_ib, new_headers, new_data) = pm_to_views(new_pm); + let old_live_header = spec_get_live_header(old_pm); + let new_live_header = spec_get_live_header(new_pm); + let physical_head = spec_addr_logical_to_physical(old_live_header.metadata.head as int, old_live_header.metadata.log_size as int); + let physical_tail = spec_addr_logical_to_physical(old_live_header.metadata.tail as int, old_live_header.metadata.log_size as int); + let log_size = old_live_header.metadata.log_size; + let physical_data_head = physical_head - contents_offset; + let physical_data_tail = physical_tail - contents_offset; + + &&& new_live_header == old_live_header + &&& physical_head < physical_tail ==> + old_data.subrange(physical_data_head, physical_data_tail) =~= new_data.subrange(physical_data_head, physical_data_tail) + &&& physical_tail < physical_head ==> { + &&& old_data.subrange(physical_data_head as int, log_size as int) =~= new_data.subrange(physical_data_head as int, log_size as int) + &&& old_data.subrange(0, physical_data_tail as int) =~= new_data.subrange(0, physical_data_tail as int) + } + &&& physical_tail == physical_head ==> + physical_data_head == physical_data_tail + } + + pub proof fn lemma_same_log_state(old_pm: Seq, new_pm: Seq) + requires + UntrustedLogImpl::recover(old_pm) is Some, + UntrustedLogImpl::recover(new_pm) is Some, + live_data_view_eq(old_pm, new_pm), + ({ + let (old_ib, old_headers, old_data) = pm_to_views(old_pm); + let (new_ib, new_headers, new_data) = pm_to_views(new_pm); + &&& old_ib == cdb0_val || old_ib == cdb1_val + &&& old_ib == new_ib + &&& old_ib == cdb0_val ==> { + &&& old_headers.header1 == new_headers.header1 + } + &&& old_ib == cdb1_val ==> { + &&& old_headers.header2 == new_headers.header2 + } + }) + ensures + UntrustedLogImpl::recover(old_pm) =~= + UntrustedLogImpl::recover(new_pm) + { + let old_state = UntrustedLogImpl::recover(old_pm); + let new_state = UntrustedLogImpl::recover(new_pm); + let (old_ib, old_headers, old_data) = pm_to_views(old_pm); + let (new_ib, new_headers, new_data) = pm_to_views(new_pm); + + assert(old_state is Some); + assert(new_state is Some); + match (old_state, new_state) { + (Some(old_state), Some(new_state)) => { + let (old_live_header, new_live_header) = if old_ib == cdb0_val { + (old_headers.header1, new_headers.header1) + } else { + (old_headers.header2, new_headers.header2) + }; + + assert(old_state.head == old_live_header.metadata.head); + assert(new_state.head == new_live_header.metadata.head); + assert(old_live_header.metadata.tail == new_live_header.metadata.tail); + let physical_head = spec_addr_logical_to_physical(old_live_header.metadata.head as int, old_live_header.metadata.log_size as int); + let physical_tail = spec_addr_logical_to_physical(old_live_header.metadata.tail as int, old_live_header.metadata.log_size as int); + let contents_end = old_live_header.metadata.log_size + contents_offset; + + if physical_head < physical_tail { + assert(old_pm.subrange(physical_head, physical_tail) =~= old_data.subrange(physical_head - contents_offset, physical_tail - contents_offset)); + assert(old_pm.subrange(physical_head, physical_tail) =~= new_pm.subrange(physical_head, physical_tail)); + } else if physical_tail < physical_head { + assert(old_pm.subrange(physical_head, contents_end) =~= old_data.subrange(physical_head - contents_offset, contents_end - contents_offset)); + assert(old_pm.subrange(contents_offset as int, physical_tail) =~= old_data.subrange(contents_offset - contents_offset, physical_tail - contents_offset)); + assert(old_pm.subrange(physical_head, contents_end) + old_pm.subrange(contents_offset as int, physical_tail) =~= + new_pm.subrange(physical_head, contents_end) + new_pm.subrange(contents_offset as int, physical_tail)); + } else { + assert(physical_head == physical_tail); + assert(old_state.log.len() == 0); + assert(new_state.log.len() == 0); + } + } + _ => assert(false), + } + } + + pub proof fn lemma_subrange_equality_implies_index_equality(s1: Seq, s2: Seq, i: int, j: int) + requires + 0 <= i <= j <= s1.len(), + j <= s2.len(), + s1.subrange(i, j) == s2.subrange(i, j) + ensures + forall |k| i <= k < j ==> s1[k] == s2[k] + { + assert forall |k| i <= k < j implies s1[k] == s2[k] by { + // Trigger axiom_seq_subrange_index + assert (s1[k] == s1.subrange(i, j)[k - i]); + assert (s2[k] == s2.subrange(i, j)[k - i]); + } + } + + pub proof fn lemma_subrange_equality_implies_subsubrange_equality(s1: Seq, s2: Seq, i: int, j: int) + requires + 0 <= i <= j <= s1.len(), + j <= s2.len(), + s1.subrange(i, j) == s2.subrange(i, j) + ensures + forall |k, m| i <= k <= m <= j ==> s1.subrange(k, m) == s2.subrange(k, m) + { + lemma_subrange_equality_implies_index_equality(s1, s2, i, j); + assert forall |k, m| i <= k <= m <= j implies s1.subrange(k, m) == s2.subrange(k, m) by { + assert (s1.subrange(k, m) =~= s2.subrange(k, m)); + } + } + + pub proof fn lemma_subrange_equality_implies_subsubrange_equality_forall() + ensures + forall |s1: Seq, s2: Seq, i: int, j: int, k: int, m: int| + { + &&& 0 <= i <= j <= s1.len() + &&& j <= s2.len() + &&& s1.subrange(i, j) == s2.subrange(i, j) + &&& i <= k <= m <= j + } + ==> s1.subrange(k, m) == s2.subrange(k, m) + { + assert forall |s1: Seq, s2: Seq, i: int, j: int, k: int, m: int| + { + &&& 0 <= i <= j <= s1.len() + &&& j <= s2.len() + &&& s1.subrange(i, j) == s2.subrange(i, j) + &&& i <= k <= m <= j + } + implies s1.subrange(k, m) == s2.subrange(k, m) by { + lemma_subrange_equality_implies_subsubrange_equality(s1, s2, i, j); + } + } + + pub proof fn lemma_headers_unchanged(old_pm: Seq, new_pm: Seq) + requires + old_pm.len() == new_pm.len(), + old_pm.len() >= contents_offset, + old_pm.subrange(header1_pos as int, header1_pos + header_size) =~= new_pm.subrange(header1_pos as int, header1_pos + header_size), + old_pm.subrange(header2_pos as int, header2_pos + header_size) =~= new_pm.subrange(header2_pos as int, header2_pos + header_size), + ensures + ({ + let (_, old_headers, _) = pm_to_views(old_pm); + let (_, new_headers, _) = pm_to_views(new_pm); + old_headers == new_headers + }) + { + lemma_subrange_equality_implies_subsubrange_equality_forall::(); + } + + pub proof fn lemma_incorruptible_bool_unchanged(old_pm: Seq, new_pm: Seq) + requires + old_pm.len() == new_pm.len(), + old_pm.len() >= contents_offset, + old_pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8) =~= new_pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8) + ensures + ({ + let (old_ib, _, _) = pm_to_views(old_pm); + let (new_ib, _, _) = pm_to_views(new_pm); + old_ib == new_ib + }) + {} + + pub proof fn lemma_header_crc_correct(header_bytes: Seq, crc_bytes: Seq, metadata_bytes: Seq) + requires + header_bytes.len() == header_size, + crc_bytes.len() == 8, + metadata_bytes.len() == header_size - 8, + crc_bytes =~= spec_crc_bytes(metadata_bytes), + header_bytes =~= crc_bytes + metadata_bytes + ensures + header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8) =~= crc_bytes, + header_bytes.subrange(header_head_offset as int, header_size as int) =~= metadata_bytes, + header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8) =~= + spec_crc_bytes(header_bytes.subrange(header_head_offset as int, header_size as int)) + { + assert(header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8) =~= crc_bytes); + assert(header_bytes.subrange(header_head_offset as int, header_size as int) =~= metadata_bytes); + } + + pub proof fn lemma_header_correct(pm: Seq, header_bytes: Seq, header_pos: int) + requires + pm.len() > contents_offset, + header_bytes.len() == header_size, + header_pos == header1_pos || header_pos == header2_pos, + header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8) =~= + spec_crc_bytes(header_bytes.subrange(header_head_offset as int, header_size as int)), + pm.subrange(header_pos, header_pos + header_size) =~= header_bytes + ensures + pm.subrange(header_pos + header_crc_offset, header_pos + header_crc_offset + 8) =~= + header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8), + pm.subrange(header_pos + header_head_offset, header_pos + header_size) =~= + header_bytes.subrange(header_head_offset as int, header_size as int), + pm.subrange(header_pos + header_crc_offset, header_pos + header_crc_offset + 8) =~= + spec_crc_bytes(pm.subrange(header_pos + header_head_offset, header_pos + header_size)) + { + assert(pm.subrange(header_pos + header_crc_offset, header_pos + header_crc_offset + 8) =~= + header_bytes.subrange(header_crc_offset as int, header_crc_offset + 8)); + assert(pm.subrange(header_pos + header_head_offset, header_pos + header_size) =~= + header_bytes.subrange(header_head_offset as int, header_size as int)); + } + + pub proof fn lemma_u64_bytes_eq(val1: u64, val2: u64) + requires + val1 == val2 + ensures + spec_u64_to_le_bytes(val1) =~= spec_u64_to_le_bytes(val2) + {} + + pub proof fn lemma_subrange_eq(bytes1: Seq, bytes2: Seq) + requires + bytes1 =~= bytes2 + ensures + forall |i: int, j: int| 0 <= i < j < bytes1.len() ==> bytes1.subrange(i, j) =~= bytes2.subrange(i, j) + {} + + /// If our write is persistence_chunk_size-sized and -aligned, then there are only 2 possible + /// resulting crash states, one with the write and one without. + pub proof fn lemma_single_write_crash(pm: Seq, write_addr: int, bytes_to_write: Seq) + requires + bytes_to_write.len() == persistence_chunk_size, + write_addr % persistence_chunk_size == 0, // currently seems to succeed without nonlinear arith + 0 <= write_addr < pm.len(), + write_addr + bytes_to_write.len() <= pm.len() + ensures + ({ + forall |chunks_flushed: Set| { + let new_crash_contents = #[trigger] update_contents_to_reflect_partially_flushed_write( + pm, write_addr, bytes_to_write, chunks_flushed); + let new_contents = update_contents_to_reflect_write(pm, write_addr, bytes_to_write); + new_crash_contents =~= pm || new_crash_contents =~= new_contents + } + }) + {} + + pub proof fn lemma_pm_state_header(pm: Seq) + requires + UntrustedLogImpl::recover(pm) is Some, + ({ + let header = spec_get_live_header(pm); + header.metadata.tail - header.metadata.head < header.metadata.log_size + }) + ensures + ({ + let pm_state = UntrustedLogImpl::recover(pm); + let header = spec_get_live_header(pm); + match pm_state { + Some(pm_state) => { + &&& header.metadata.head == pm_state.head + &&& pm_state.log.len() == header.metadata.tail - header.metadata.head + } + None => false + } + }) + { + let pm_state = UntrustedLogImpl::recover(pm); + let header = spec_get_live_header(pm); + lemma_mod_division_less_than_divisor(header.metadata.head as int, header.metadata.log_size as int); + lemma_mod_division_less_than_divisor(header.metadata.tail as int, header.metadata.log_size as int); + let head = header.metadata.head as int; + let tail = header.metadata.tail as int; + let log_size = header.metadata.log_size as int; + let physical_head = spec_addr_logical_to_physical(head, log_size); + let physical_tail = spec_addr_logical_to_physical(tail, log_size); + match pm_state { + Some(pm_state) => { + if physical_head < physical_tail { + // log does not wrap + lemma_mod_difference_equal(head, tail, log_size); + } else if physical_tail < physical_head { + // log wraps + lemma_mod_wrapped_len(head, tail, log_size); + } else { + // size is 0 + lemma_mod_equal(head, tail, log_size); + } + } + None => assert(false), + } + } + + pub open spec fn spec_addr_logical_to_physical(addr: int, log_size: int) -> int { + (addr % log_size) + contents_offset + } + + pub struct UntrustedLogImpl { + pub incorruptible_bool: u64, + // header fields are stored separately because of limitations + // on deriving Copy/Clone for the header structures + pub header_crc: u64, + pub head: u64, + pub tail: u64, + pub log_size: u64, + } + + // offset of actual log contents from the beginning of the device + pub const contents_offset: u64 = header2_pos + header_log_size_offset + 8; + + impl UntrustedLogImpl { + + pub exec fn addr_logical_to_physical(addr: u64, log_size: u64) -> (out: u64) + requires + addr <= u64::MAX, + log_size > 0, + log_size + contents_offset <= u64::MAX, + ensures + out == spec_addr_logical_to_physical(addr as int, log_size as int) + { + (addr % log_size) + contents_offset + } + + pub open spec fn log_state_is_valid(pm: Seq) -> bool { + let (ib, headers, data) = pm_to_views(pm); + let live_header = if ib == cdb0_val { + headers.header1 + } else { + headers.header2 + }; + + let head = live_header.metadata.head as int; + let tail = live_header.metadata.tail as int; + let log_size = live_header.metadata.log_size as int; + + &&& ib == cdb0_val || ib == cdb1_val + &&& log_size + contents_offset <= u64::MAX + &&& log_size > 0 + &&& log_size + contents_offset == pm.len() + &&& tail - head < log_size + &&& ib == cdb0_val ==> { + &&& live_header.crc == spec_u64_from_le_bytes(spec_crc_bytes(pm.subrange(header1_pos + header_head_offset, header1_pos + header_size))) + &&& pm.subrange(header1_pos + header_crc_offset, header1_pos + header_crc_offset + 8) =~= spec_crc_bytes(pm.subrange(header1_pos + header_head_offset, header1_pos + header_size)) + } + &&& ib == cdb1_val ==> { + &&& live_header.crc == spec_u64_from_le_bytes(spec_crc_bytes(pm.subrange(header2_pos + header_head_offset, header2_pos + header_size))) + &&& pm.subrange(header2_pos + header_crc_offset, header2_pos + header_crc_offset + 8) =~= spec_crc_bytes(pm.subrange(header2_pos + header_head_offset, header2_pos + header_size)) + } + &&& head <= tail + } + + pub open spec fn recover(pm: Seq) -> Option + { + let (ib, headers, data) = pm_to_views(pm); + if !Self::log_state_is_valid(pm) { + None + } else { + let live_header = if ib == cdb0_val { + headers.header1 + } else { + headers.header2 + }; + + let head = live_header.metadata.head as int; + let tail = live_header.metadata.tail as int; + let log_size = live_header.metadata.log_size as int; + let contents_end = log_size + contents_offset; + let physical_head = spec_addr_logical_to_physical(head, log_size); + let physical_tail = spec_addr_logical_to_physical(tail, log_size); + + let abstract_log = if physical_head < physical_tail { + pm.subrange(physical_head, physical_tail) + } else if physical_tail < physical_head { + let range1 = pm.subrange(physical_head, contents_end); + let range2 = pm.subrange(contents_offset as int, physical_tail); + range1 + range2 + } else { + Seq::empty() + }; + + Some(AbstractInfiniteLogState { head: head, log: abstract_log, capacity: log_size - 1 }) + } + } + + // This is the invariant that the untrusted log implementation + // maintains between its local state and the contents of + // persistent memory. + pub open spec fn inv_pm_contents(self, contents: Seq) -> bool + { + let (ib, headers, data) = pm_to_views(contents); + let header_pos = if ib == cdb0_val { header1_pos } else { header2_pos }; + let header = spec_get_live_header(contents); + let head = header.metadata.head; + let tail = header.metadata.tail; + let log_size = header.metadata.log_size; + &&& ib == cdb0_val || ib == cdb1_val + &&& spec_crc_bytes(contents.subrange(header_pos + header_head_offset, header_pos + header_size)) == + contents.subrange(header_pos + header_crc_offset, header_pos + header_crc_offset + 8) + &&& log_size + contents_offset <= u64::MAX + &&& tail - head < log_size + &&& log_size + contents_offset == contents.len() + &&& self.header_crc == header.crc + &&& self.head == head + &&& self.tail == tail + &&& self.log_size == log_size + &&& self.incorruptible_bool == ib + &&& match Self::recover(contents) { + Some(inf_log) => tail == head + inf_log.log.len(), + None => false, + } + } + + // This is the invariant that the untrusted log implementation + // maintains between its local state and the write-restricted + // persistent memory. + pub open spec fn inv(self, wrpm: &WriteRestrictedPersistentMemory) -> bool + where + Perm: CheckPermission>, + PM: PersistentMemory + { + &&& wrpm.inv() + &&& self.inv_pm_contents(wrpm@) + } + + pub exec fn read_incorruptible_boolean(pm: &PM) -> (result: Result) + requires + Self::recover(pm@) is Some, + pm.inv(), + pm@.len() > contents_offset + ensures + match result { + Ok(ib) => { + let (spec_ib, _, _) = pm_to_views(pm@); + ib == spec_ib + } + Err(InfiniteLogErr::CRCMismatch) => !pm.constants().impervious_to_corruption, + _ => false, + } + { + let bytes = pm.read(incorruptible_bool_pos, 8); + let ib = u64_from_le_bytes(bytes.as_slice()); + let ghost addrs = Seq::::new(8, |i: int| i + incorruptible_bool_pos); + if ib == cdb0_val || ib == cdb1_val { + proof { + let (spec_ib, _, _) = pm_to_views(pm@); + lemma_auto_spec_u64_to_from_le_bytes(); + if !pm.constants().impervious_to_corruption { + axiom_corruption_detecting_boolean(ib, spec_ib, addrs); + } + } + Ok(ib) + } else { + Err(InfiniteLogErr::CRCMismatch) + } + } + + exec fn update_header + ( + &mut self, + wrpm: &mut WriteRestrictedPersistentMemory, + Tracked(perm): Tracked<&Perm>, + new_header_bytes: &Vec + ) + where + Perm: CheckPermission>, + PM: PersistentMemory + requires + permissions_depend_only_on_recovery_view(perm), + contents_offset < old(wrpm)@.len(), + old(self).inv(&*old(wrpm)), + Self::recover(old(wrpm)@) is Some, + new_header_bytes@.subrange(header_crc_offset as int, header_crc_offset + 8) =~= + spec_crc_bytes(new_header_bytes@.subrange(header_head_offset as int, header_size as int)), + new_header_bytes.len() == header_size, + match Self::recover(old(wrpm)@) { + Some(log_state) => perm.check_permission(old(wrpm)@), + None => false + } + ensures + final(self).inv(final(wrpm)), + Self::recover(final(wrpm)@) is Some, + final(wrpm).constants() == old(wrpm).constants(), + match (Self::recover(old(wrpm)@), Self::recover(final(wrpm)@)) { + (Some(old_log_state), Some(new_log_state)) => old_log_state =~= new_log_state, + _ => false + }, + ({ + let (old_pm_ib, old_metadata, old_data) = pm_to_views(old(wrpm)@); + let (new_pm_ib, new_metadata, new_data) = pm_to_views(final(wrpm)@); + let new_header = spec_bytes_to_header(new_header_bytes@); + &&& old_pm_ib == new_pm_ib + &&& old_pm_ib == cdb0_val ==> { + &&& new_metadata.header1 == old_metadata.header1 + &&& new_metadata.header2 == new_header + &&& final(wrpm)@.subrange(header2_pos + header_crc_offset, header2_pos + header_crc_offset + 8) =~= + spec_crc_bytes(final(wrpm)@.subrange(header2_pos + header_head_offset, header2_pos + header_size)) + &&& final(wrpm)@.subrange(header2_pos as int, header2_pos + header_size) =~= new_header_bytes@ + } + &&& old_pm_ib == cdb1_val ==> { + &&& new_metadata.header1 == new_header + &&& new_metadata.header2 == old_metadata.header2 + &&& final(wrpm)@.subrange(header1_pos + header_crc_offset, header1_pos + header_crc_offset + 8) =~= + spec_crc_bytes(final(wrpm)@.subrange(header1_pos + header_head_offset, header1_pos + header_size)) + &&& final(wrpm)@.subrange(header1_pos as int, header1_pos + header_size) =~= new_header_bytes@ + } + &&& old_data =~= new_data + }), + + { + let ghost original_wrpm = wrpm@; + + // write to the header that is NOT pointed to by the IB + let header_pos = if self.incorruptible_bool == cdb0_val { + header2_pos + } else { + header1_pos + }; + + // TODO: we could probably roll all of this into a single lemma that contains all of the proofs + proof { + let new_pm = update_contents_to_reflect_write(wrpm@, header_pos as int, new_header_bytes@); + lemma_inactive_header_update_view(wrpm@, new_header_bytes@, header_pos as int); + lemma_same_log_state(wrpm@, new_pm); + assert(Self::recover(wrpm@) =~= Self::recover(new_pm)); + + // prove crash consistency + assert forall |chunks_flushed| { + let new_pm = #[trigger] update_contents_to_reflect_partially_flushed_write( + wrpm@, header_pos as int, new_header_bytes@, chunks_flushed); + perm.check_permission(new_pm) + } by { + let new_pm = update_contents_to_reflect_partially_flushed_write( + wrpm@, header_pos as int, new_header_bytes@, chunks_flushed); + lemma_inactive_header_update_view_crash(wrpm@, new_header_bytes@, header_pos as int, chunks_flushed); + lemma_same_log_state(wrpm@, new_pm); + assert(permissions_depend_only_on_recovery_view(perm)); + lemma_same_permissions(wrpm@, new_pm, perm); + } + } + wrpm.write(header_pos, new_header_bytes.as_slice(), Tracked(perm)); + proof { + // TODO: clean up once ib update is done. put this all in a lemma + assert(Self::recover(wrpm@) is Some); + let (_, headers, _) = pm_to_views(wrpm@); + assert(wrpm@.subrange(header_pos as int, header_pos + header_size) =~= new_header_bytes@); + lemma_header_correct(wrpm@, new_header_bytes@, header_pos as int); + + // live header is unchanged + let live_header_pos = if header_pos == header1_pos { + header2_pos + } else { + assert(header_pos == header2_pos); + header1_pos + }; + + // TODO: refactor into a lemma (ideally lemma_header_correct) + assert(old(wrpm)@.subrange(live_header_pos as int, live_header_pos + header_size) =~= + wrpm@.subrange(live_header_pos as int, live_header_pos + header_size)); + assert(old(wrpm)@.subrange(live_header_pos + header_crc_offset, live_header_pos + header_crc_offset + 8) =~= + spec_crc_bytes(old(wrpm)@.subrange(live_header_pos + header_head_offset, live_header_pos + header_size))); + assert(old(wrpm)@.subrange(live_header_pos + header_crc_offset, live_header_pos + header_crc_offset + 8) =~= + wrpm@.subrange(live_header_pos + header_crc_offset, live_header_pos + header_crc_offset + 8)); + assert(old(wrpm)@.subrange(live_header_pos + header_head_offset, live_header_pos + header_size) =~= + wrpm@.subrange(live_header_pos + header_head_offset, live_header_pos + header_size)); + + assert(wrpm@.subrange(live_header_pos + header_crc_offset, live_header_pos + header_crc_offset + 8) =~= + spec_crc_bytes(wrpm@.subrange(live_header_pos + header_head_offset, live_header_pos + header_size))); + } + } + + // Since untrusted_setup doesn't take a WriteRestrictedPersistentMemory, it is not guaranteed + // to perform crash-safe updates. + pub exec fn untrusted_setup(pm: &mut PM, device_size: u64) -> (result: Result) + where + PM: PersistentMemory + requires + old(pm).inv(), + old(pm)@.len() == device_size + ensures + final(pm).inv(), + final(pm).constants() == old(pm).constants(), + final(pm)@.len() == device_size, + match result { + Ok(capacity) => Self::recover(final(pm)@) == + Some(AbstractInfiniteLogState::initialize(capacity as int)), + Err(InfiniteLogErr::InsufficientSpaceForSetup{ required_space }) => device_size < required_space, + _ => false + } + { + if device_size <= contents_offset { + return Err(InfiniteLogErr::InsufficientSpaceForSetup { required_space: contents_offset + 1 }); + } + + let log_size = device_size - contents_offset; + + let log_header_metadata = PersistentHeaderMetadata { + head: 0, + tail: 0, + log_size + }; + let metadata_bytes = metadata_to_bytes(&log_header_metadata); + let crc_bytes = bytes_crc(&metadata_bytes); + let log_header = PersistentHeader { + crc: u64_from_le_bytes(crc_bytes.as_slice()), + metadata: log_header_metadata, + }; + let header_bytes = header_to_bytes(&log_header); + + let initial_ib_bytes = u64_to_le_bytes(cdb0_val); + pm.write(header1_pos, header_bytes.as_slice()); + pm.write(incorruptible_bool_pos, initial_ib_bytes.as_slice()); + + proof { + lemma_auto_spec_u64_to_from_le_bytes(); + assert(pm@.subrange(header1_pos as int, header1_pos + header_size) =~= header_bytes@); + assert(pm@.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8) =~= initial_ib_bytes@); + lemma_header_split_into_bytes(crc_bytes@, metadata_bytes@, header_bytes@); + assert(pm@.subrange(header1_pos + header_head_offset, header1_pos + header_size) =~= metadata_bytes@); + lemma_header_match(pm@, header1_pos as int, log_header); + let log_state = Self::recover(pm@); + match log_state { + Some(log_state) => { + assert(log_state.head == 0); + assert(log_state.log == Seq::::empty()); + assert(log_state.capacity == log_size - 1); + } + None => assert(false), + } + } + + Ok(log_size - 1) + } + + pub exec fn untrusted_start(wrpm: &mut WriteRestrictedPersistentMemory, + device_size: u64, + Tracked(perm): Tracked<&Perm>) + -> (result: Result) + where + Perm: CheckPermission>, + PM: PersistentMemory + requires + Self::recover(old(wrpm)@) is Some, + old(wrpm).inv(), + old(wrpm)@.len() == device_size, + header_crc_offset < header_crc_offset + crc_size <= header_head_offset < header_tail_offset < header_log_size_offset, + // The restriction on writing persistent memory during initialization is + // that it can't change the interpretation of that memory's contents. + ({ + forall |pm_state| #[trigger] perm.check_permission(pm_state) <==> + Self::recover(pm_state) == + Self::recover(old(wrpm)@) + }), + ensures + Self::recover(old(wrpm)@) == Self::recover(final(wrpm)@), + final(wrpm).constants() == old(wrpm).constants(), + match result { + Ok(log_impl) => log_impl.inv(final(wrpm)), + Err(InfiniteLogErr::CRCMismatch) => !final(wrpm).constants().impervious_to_corruption, + _ => false + } + { + let pm = wrpm.get_pm_ref(); + assert (device_size > contents_offset); + + let ib = match Self::read_incorruptible_boolean(pm) { + Ok(ib) => ib, + Err(e) => return Err(e) + }; + + let header_pos = if ib == cdb0_val { + header1_pos + } else { + assert(ib == cdb1_val); + header2_pos + }; + let crc_bytes = pm.read(header_pos + header_crc_offset, 8); + let ghost crc_addrs = Seq::::new(8, |i: int| i + header_pos + header_crc_offset); + let header_bytes = pm.read(header_pos + header_head_offset, header_size - header_head_offset); + let ghost header_addrs = Seq::::new((header_size - header_head_offset) as nat, |i: int| i + header_pos + header_head_offset); + + let header = if u64_from_le_bytes(bytes_crc(&header_bytes).as_slice()) == u64_from_le_bytes(crc_bytes.as_slice()) { + proof { + lemma_auto_spec_u64_to_from_le_bytes(); + lemma_u64_bytes_eq(spec_u64_from_le_bytes(spec_crc_bytes(header_bytes@)), spec_u64_from_le_bytes(crc_bytes@)); + if !wrpm.constants().impervious_to_corruption { + axiom_bytes_uncorrupted( + header_bytes@, + pm@.subrange(header_pos + header_head_offset, header_pos + header_size), + header_addrs, + crc_bytes@, + pm@.subrange(header_pos + header_crc_offset, header_pos + header_crc_offset + 8), + crc_addrs, + ); + } + } + crc_and_metadata_bytes_to_header(crc_bytes.as_slice(), header_bytes.as_slice()) + } else { + return Err(InfiniteLogErr::CRCMismatch); + }; + + let head = header.metadata.head; + let tail = header.metadata.tail; + let log_size = header.metadata.log_size; + // check log validity now that we have its uncorrupted metadata + assert(device_size == log_size + contents_offset); + assert(head <= tail); + assert(tail - head < log_size); + + let untrusted_log = UntrustedLogImpl { + incorruptible_bool: ib, + header_crc: u64_from_le_bytes(crc_bytes.as_slice()), + head, + tail, + log_size + }; + + proof { lemma_pm_state_header(pm@); } + Ok(untrusted_log) + } + + pub exec fn untrusted_append( + &mut self, + wrpm: &mut WriteRestrictedPersistentMemory, + bytes_to_append: &Vec, + Tracked(perm): Tracked<&Perm> + ) -> (result: Result) + where + Perm: CheckPermission>, + PM: PersistentMemory + requires + old(self).inv(&*old(wrpm)), + Self::recover(old(wrpm)@) is Some, + ({ + let old_log_state = Self::recover(old(wrpm)@); + forall |pm_state| #[trigger] perm.check_permission(pm_state) <==> { + let log_state = Self::recover(pm_state); + log_state == old_log_state || log_state == Some(old_log_state.unwrap().append(bytes_to_append@)) + } + }), + ensures + final(self).inv(final(wrpm)), + final(wrpm).constants() == old(wrpm).constants(), + ({ + let old_log_state = Self::recover(old(wrpm)@); + let new_log_state = Self::recover(final(wrpm)@); + match (result, old_log_state, new_log_state) { + (Ok(offset), Some(old_log_state), Some(new_log_state)) => { + &&& offset as nat == old_log_state.log.len() + old_log_state.head + &&& new_log_state == old_log_state.append(bytes_to_append@) + }, + (Err(InfiniteLogErr::InsufficientSpaceForAppend{ available_space }), _, _) => { + &&& new_log_state == old_log_state + &&& available_space < bytes_to_append@.len() + &&& { + let log = old_log_state.unwrap(); + ||| available_space == log.capacity - log.log.len() + ||| available_space == u64::MAX - log.head - log.log.len() + } + }, + (_, _, _) => false + } + }), + { + assert(permissions_depend_only_on_recovery_view(perm)); + + let pm = wrpm.get_pm_ref(); + let ghost original_pm = wrpm@; + + let physical_head = Self::addr_logical_to_physical(self.head, self.log_size); + let physical_tail = Self::addr_logical_to_physical(self.tail, self.log_size); + let contents_end = self.log_size + contents_offset; + let append_size: u64 = bytes_to_append.len() as u64; + let old_logical_tail = self.tail; + + if self.tail > u64::MAX - append_size { + Err(InfiniteLogErr::InsufficientSpaceForAppend{ available_space: u64::MAX - self.tail }) + } + else if append_size >= self.log_size - (self.tail - self.head) { + Err(InfiniteLogErr::InsufficientSpaceForAppend{ available_space: self.log_size - 1 - (self.tail - self.head) }) + } else { + let mut header_metadata = + PersistentHeaderMetadata { head: self.head, tail: self.tail, log_size: self.log_size }; + assert(header_metadata == spec_get_live_header(wrpm@).metadata); + + if physical_head <= physical_tail { + if physical_tail >= contents_end - append_size { + // wrap case + self.append_wrap(wrpm, bytes_to_append, &header_metadata, Tracked(perm)); + } else { + // no wrap + self.append_no_wrap(wrpm, bytes_to_append, &header_metadata, Tracked(perm)); + } + } else { // physical_tail < physical_head + if physical_tail + append_size >= physical_head { + return Err(InfiniteLogErr::InsufficientSpaceForAppend { available_space: physical_head - physical_tail }); + } + // no wrap + self.append_no_wrap(wrpm, bytes_to_append, &header_metadata, Tracked(perm)); + } + + let new_tail = self.tail + append_size; + header_metadata.tail = new_tail; + + let mut metadata_bytes = metadata_to_bytes(&header_metadata); + let new_crc_bytes = bytes_crc(&metadata_bytes); + let new_crc_val = u64_from_le_bytes(new_crc_bytes.as_slice()); + let ghost old_metadata_bytes = metadata_bytes@; + let mut new_header_bytes = new_crc_bytes; + new_header_bytes.append(&mut metadata_bytes); + + proof { lemma_header_crc_correct(new_header_bytes@, new_crc_bytes@, old_metadata_bytes); } + + self.update_header(wrpm, Tracked(perm), &new_header_bytes); + + // update incorruptible boolean + let old_ib = self.incorruptible_bool; + let new_ib = if old_ib == cdb0_val { + cdb1_val + } else { + assert(old_ib == cdb1_val); + cdb0_val + }; + let new_ib_bytes = u64_to_le_bytes(new_ib); + + proof { + lemma_append_ib_update(wrpm@, new_ib, bytes_to_append@, new_header_bytes@, perm); + } + + wrpm.write(incorruptible_bool_pos, new_ib_bytes.as_slice(), Tracked(perm)); + self.incorruptible_bool = new_ib; + self.tail = new_tail; + self.header_crc = new_crc_val; + + Ok(old_logical_tail) + } + } + + exec fn append_no_wrap( + &mut self, + wrpm: &mut WriteRestrictedPersistentMemory, + bytes_to_append: &Vec, + old_header: &PersistentHeaderMetadata, + Tracked(perm): Tracked<&Perm> + ) + where + Perm: CheckPermission>, + PM: PersistentMemory + requires + permissions_depend_only_on_recovery_view(perm), + perm.check_permission(old(wrpm)@), + old(self).inv(&*old(wrpm)), + Self::recover(old(wrpm)@) is Some, + old_header == spec_get_live_header(old(wrpm)@).metadata, + // TODO: clean up + ({ + let physical_tail = spec_addr_logical_to_physical(old_header.tail as int, old_header.log_size as int); + physical_tail + bytes_to_append@.len() < old_header.log_size + contents_offset + }), + ({ + let physical_head = spec_addr_logical_to_physical(old_header.head as int, old_header.log_size as int); + let physical_tail = spec_addr_logical_to_physical(old_header.tail as int, old_header.log_size as int); + let contents_end = old_header.log_size + contents_offset; + &&& physical_head <= physical_tail ==> physical_tail + bytes_to_append@.len() < contents_end + &&& physical_tail < physical_head ==> physical_tail <= physical_tail + bytes_to_append@.len() < physical_head + }) + ensures + final(self).inv(final(wrpm)), + final(wrpm).constants() == old(wrpm).constants(), + Self::recover(final(wrpm)@) is Some, + match (Self::recover(old(wrpm)@), Self::recover(final(wrpm)@)) { + (Some(old_log_state), Some(new_log_state)) => old_log_state =~= new_log_state, + _ => false + }, + ({ + let (old_ib, old_headers, old_data) = pm_to_views(old(wrpm)@); + let (new_ib, new_headers, new_data) = pm_to_views(final(wrpm)@); + let physical_tail = spec_addr_logical_to_physical(old_header.tail as int, old_header.log_size as int); + &&& old_ib == new_ib + &&& old_headers == new_headers + &&& new_data.subrange(physical_tail - contents_offset, physical_tail - contents_offset + bytes_to_append@.len() as int) =~= bytes_to_append@ + &&& new_data.subrange(0, physical_tail - contents_offset) =~= old_data.subrange(0, physical_tail - contents_offset) + &&& new_data.subrange(physical_tail - contents_offset + bytes_to_append@.len(), new_data.len() as int) =~= + old_data.subrange(physical_tail - contents_offset + bytes_to_append@.len(), old_data.len() as int) + }) + { + let physical_tail = Self::addr_logical_to_physical(old_header.tail, old_header.log_size); + proof { lemma_data_write_is_safe(wrpm@, bytes_to_append@, physical_tail as int, perm); } + wrpm.write(physical_tail, bytes_to_append.as_slice(), Tracked(perm)); + proof { + assert(wrpm@.subrange(0, physical_tail as int) =~= old(wrpm)@.subrange(0, physical_tail as int)); + lemma_subrange_equality_implies_subsubrange_equality(wrpm@, old(wrpm)@, 0, physical_tail as int); + } + } + + pub exec fn append_wrap( + &mut self, + wrpm: &mut WriteRestrictedPersistentMemory, + bytes_to_append: &Vec, + old_header: &PersistentHeaderMetadata, + Tracked(perm): Tracked<&Perm> + ) + where + Perm: CheckPermission>, + PM: PersistentMemory + requires + permissions_depend_only_on_recovery_view(perm), + perm.check_permission(old(wrpm)@), + old(self).inv(&*old(wrpm)), + Self::recover(old(wrpm)@) is Some, + old_header == spec_get_live_header(old(wrpm)@).metadata, + ({ + let physical_head = spec_addr_logical_to_physical(old_header.head as int, old_header.log_size as int); + let physical_tail = spec_addr_logical_to_physical(old_header.tail as int, old_header.log_size as int); + let contents_end = old_header.log_size + contents_offset; + &&& contents_offset < physical_head + &&& physical_tail + bytes_to_append@.len() >= contents_end + &&& physical_head <= physical_tail + &&& bytes_to_append@.len() <= old_header.log_size - (old_header.tail - old_header.head) + }), + ensures + final(self).inv(final(wrpm)), + Self::recover(final(wrpm)@) is Some, + final(wrpm).constants() == old(wrpm).constants(), + match (Self::recover(old(wrpm)@), Self::recover(final(wrpm)@)) { + (Some(old_log_state), Some(new_log_state)) => old_log_state =~= new_log_state, + _ => false + }, + ({ + let (old_ib, old_headers, old_data) = pm_to_views(old(wrpm)@); + let (new_ib, new_headers, new_data) = pm_to_views(final(wrpm)@); + let contents_end = old_header.log_size + contents_offset; + let physical_tail = spec_addr_logical_to_physical(old_header.tail as int, old_header.log_size as int); + let len1 = (contents_end - physical_tail); + let len2 = bytes_to_append@.len() - len1; + &&& old_ib == new_ib + &&& old_headers == new_headers + &&& new_data.subrange(physical_tail - contents_offset, contents_end - contents_offset) =~= bytes_to_append@.subrange(0, len1) + &&& new_data.subrange(0, len2 as int) =~= bytes_to_append@.subrange(len1 as int, bytes_to_append@.len() as int) + &&& new_data.subrange(len2 as int, physical_tail - contents_offset) =~= old_data.subrange(len2 as int, physical_tail - contents_offset) + &&& bytes_to_append@ =~= new_data.subrange(physical_tail - contents_offset, contents_end - contents_offset) + new_data.subrange(0, len2 as int) + }) + { + let physical_head = Self::addr_logical_to_physical(old_header.head, old_header.log_size); + let physical_tail = Self::addr_logical_to_physical(old_header.tail, old_header.log_size); + let contents_end = old_header.log_size + contents_offset; + let append_size = bytes_to_append.len(); + + let len1 = (contents_end - physical_tail) as usize; + let len2 = bytes_to_append.len() - len1; + let append_bytes_slice = bytes_to_append.as_slice(); + let bytes1 = slice_subrange(append_bytes_slice, 0, len1); + let bytes2 = slice_subrange(append_bytes_slice, len1, append_size); + + proof { lemma_data_write_is_safe(wrpm@, bytes1@, physical_tail as int, perm); } + wrpm.write(physical_tail, bytes1, Tracked(perm)); + + proof { lemma_data_write_is_safe(wrpm@, bytes2@, contents_offset as int, perm); } + wrpm.write(contents_offset, bytes2, Tracked(perm)); + + proof { + assert(wrpm@.subrange(0, contents_offset as int) =~= old(wrpm)@.subrange(0, contents_offset as int)); + lemma_subrange_equality_implies_subsubrange_equality(wrpm@, old(wrpm)@, 0, contents_offset as int); + } + } + + pub exec fn untrusted_advance_head( + &mut self, + wrpm: &mut WriteRestrictedPersistentMemory, + new_head: u64, + Tracked(perm): Tracked<&Perm> + ) -> (result: Result<(), InfiniteLogErr>) + where + Perm: CheckPermission>, + PM: PersistentMemory + requires + old(self).inv(&*old(wrpm)), + Self::recover(old(wrpm)@) is Some, + ({ + let old_log_state = Self::recover(old(wrpm)@); + forall |pm_state| #[trigger] perm.check_permission(pm_state) <==> { + let log_state = Self::recover(pm_state); + ||| log_state == old_log_state + ||| log_state == Some(old_log_state.unwrap().advance_head(new_head as int)) + } + }) + ensures + final(self).inv(final(wrpm)), + final(wrpm).constants() == old(wrpm).constants(), + ({ + let old_log_state = Self::recover(old(wrpm)@); + let new_log_state = Self::recover(final(wrpm)@); + match (result, old_log_state, new_log_state) { + (Ok(_), Some(old_log_state), Some(new_log_state)) => { + &&& old_log_state.head <= new_head <= old_log_state.head + old_log_state.log.len() + &&& new_log_state == old_log_state.advance_head(new_head as int) + }, + (Err(InfiniteLogErr::CantAdvanceHeadPositionBeforeHead{ head }), Some(old_log_state), Some(new_log_state)) => { + &&& new_log_state == old_log_state + &&& head == old_log_state.head + &&& new_head < head + }, + (Err(InfiniteLogErr::CantAdvanceHeadPositionBeyondTail{ tail }), Some(old_log_state), Some(new_log_state)) => { + &&& new_log_state == old_log_state + &&& tail == old_log_state.head + old_log_state.log.len() + &&& new_head > tail + }, + (_, _, _) => false + } + }) + { + let pm = wrpm.get_pm_ref(); + let ghost original_pm = wrpm@; + + let live_header = PersistentHeader { + crc: self.header_crc, + metadata: PersistentHeaderMetadata { head: self.head, tail: self.tail, log_size: self.log_size } + }; + + if new_head < live_header.metadata.head { + assert(self.header_crc == old(self).header_crc); + return Err(InfiniteLogErr::CantAdvanceHeadPositionBeforeHead{ head: live_header.metadata.head }); + } + + if new_head > live_header.metadata.tail { + assert(self.header_crc == old(self).header_crc); + return Err(InfiniteLogErr::CantAdvanceHeadPositionBeyondTail{ tail: live_header.metadata.tail }); + } + + // copy the header and update it + let mut new_header = live_header; + new_header.metadata.head = new_head; + let mut metadata_bytes = metadata_to_bytes(&new_header.metadata); + let new_crc_bytes = bytes_crc(&metadata_bytes); + let new_crc_val = u64_from_le_bytes(new_crc_bytes.as_slice()); + let ghost old_metadata_bytes = metadata_bytes@; + let mut new_header_bytes = new_crc_bytes; + new_header_bytes.append(&mut metadata_bytes); + + proof { lemma_header_crc_correct(new_header_bytes@, new_crc_bytes@, old_metadata_bytes); } + + self.update_header(wrpm, Tracked(perm), &new_header_bytes); + + // TODO: put ib update in a lemma + let old_ib = self.incorruptible_bool; + let new_ib = if old_ib == cdb0_val { + cdb1_val + } else { + assert(old_ib == cdb1_val); + cdb0_val + }; + let new_ib_bytes = u64_to_le_bytes(new_ib); + + proof { + lemma_auto_spec_u64_to_from_le_bytes(); + lemma_single_write_crash(wrpm@, incorruptible_bool_pos as int, new_ib_bytes@); + assert(perm.check_permission(old(wrpm)@)); + let new_pm = update_contents_to_reflect_write(wrpm@, incorruptible_bool_pos as int, new_ib_bytes@); + lemma_headers_unchanged(wrpm@, new_pm); + assert(new_pm.subrange(incorruptible_bool_pos as int, incorruptible_bool_pos + 8) =~= new_ib_bytes@); + + let new_header = spec_bytes_to_header(new_header_bytes@); + let (ib, headers, data) = pm_to_views(new_pm); + let header_pos = if new_ib == cdb0_val { + header1_pos + } else { + header2_pos + }; + assert(new_pm.subrange(header_pos as int, header_pos + header_size) =~= new_header_bytes@); + lemma_header_match(new_pm, header_pos as int, new_header); + lemma_header_correct(new_pm, new_header_bytes@, header_pos as int); + + // prove that new pm has the advance head update + let new_log_state = Self::recover(new_pm); + let old_log_state = Self::recover(old(wrpm)@); + match (new_log_state, old_log_state) { + (Some(new_log_state), Some(old_log_state)) => { + lemma_pm_state_header(new_pm); + lemma_pm_state_header(old(wrpm)@); + assert(new_log_state =~= old_log_state.advance_head(new_head as int)); + assert(perm.check_permission(new_pm)); + } + _ => assert(false), + } + } + + wrpm.write(incorruptible_bool_pos, new_ib_bytes.as_slice(), Tracked(perm)); + self.incorruptible_bool = new_ib; + self.head = new_head; + self.header_crc = new_crc_val; + + Ok(()) + } + + pub exec fn untrusted_read( + &self, + wrpm: &WriteRestrictedPersistentMemory, + pos: u64, + len: u64 + ) -> (result: Result, InfiniteLogErr>) + where + Perm: CheckPermission>, + PM: PersistentMemory + requires + self.inv(wrpm), + Self::recover(wrpm@) is Some, + ensures + ({ + let log = Self::recover(wrpm@).unwrap(); + match result { + Ok(bytes) => { + let true_bytes = log.log.subrange(pos - log.head, pos + len - log.head); + &&& pos >= log.head + &&& pos + len <= log.head + log.log.len() + &&& read_correct_modulo_corruption(bytes@, true_bytes, + wrpm.constants().impervious_to_corruption) + }, + Err(InfiniteLogErr::CantReadBeforeHead{ head: head_pos }) => { + &&& pos < log.head + &&& head_pos == log.head + }, + Err(InfiniteLogErr::CantReadPastTail{ tail }) => { + &&& pos + len > log.head + log.log.len() + &&& tail == log.head + log.log.len() + }, + _ => false + } + }) + { + let pm = wrpm.get_pm_ref(); + let physical_pos = Self::addr_logical_to_physical(pos, self.log_size); + let contents_end = self.log_size + contents_offset; + if pos < self.head { + Err(InfiniteLogErr::CantReadBeforeHead{ head: self.head }) + } else if pos > u64::MAX - len { + Err(InfiniteLogErr::CantReadPastTail{ tail: self.tail }) + } else if pos + len > self.tail { + Err(InfiniteLogErr::CantReadPastTail{ tail: self.tail }) + } else { + proof { + // we get a type error if we calculate physical head and tail in non-ghost code and use them here, + // so we need to calculate them here for the proof and again later for execution + let physical_head = spec_addr_logical_to_physical(self.head as int, self.log_size as int); + let physical_tail = spec_addr_logical_to_physical(self.tail as int, self.log_size as int); + if physical_head == physical_tail { + lemma_mod_equal(self.head as int, self.tail as int, self.log_size as int); + assert(len == 0); + } else if physical_head < physical_tail { + // read cannot wrap around + lemma_mod_between(self.log_size as int, self.head as int, self.tail as int, pos as int); + lemma_mod_difference_equal(self.head as int, pos as int, self.log_size as int); + } else { + // read may wrap around + lemma_mod_not_between(self.log_size as int, self.head as int, self.tail as int, pos as int); + if physical_pos <= physical_tail { + lemma_mod_wrapped_len(self.head as int, pos as int, self.log_size as int); + } else { + lemma_mod_difference_equal(self.head as int, pos as int, self.log_size as int); + } + } + } + + let physical_head = Self::addr_logical_to_physical(self.head, self.log_size); + let physical_tail = Self::addr_logical_to_physical(self.tail, self.log_size); + + let ghost log = Self::recover(pm@).unwrap(); + let ghost true_bytes = log.log.subrange(pos - log.head, pos + len - log.head); + if physical_head == physical_tail { + assert (Seq::::empty() =~= log.log.subrange(pos - log.head, pos + len - log.head)); + let buf = Vec::new(); + let ghost addrs = Seq::::empty(); + assert (if wrpm.constants().impervious_to_corruption { buf@ == true_bytes } + else { maybe_corrupted(buf@, true_bytes, addrs) }); + Ok(buf) + } else if physical_pos >= physical_head && physical_pos >= contents_end - len { + let r1_len: u64 = contents_end - physical_pos; + let r2_len: u64 = len - r1_len; + + let mut r1 = pm.read(physical_pos, r1_len); + let mut r2 = pm.read(contents_offset, r2_len); + let ghost r1_addrs = Seq::::new(r1_len as nat, |i: int| i + physical_pos as int); + let ghost r2_addrs = Seq::::new(r2_len as nat, |i: int| i + contents_offset as int); + let ghost addrs: Seq = r1_addrs.add(r2_addrs); + + r1.append(&mut r2); + assert (pm@.subrange(physical_pos as int, physical_pos + r1_len) + + pm@.subrange(contents_offset as int, contents_offset + r2_len) + =~= log.log.subrange(pos - log.head, pos + len - log.head)); + assert (if wrpm.constants().impervious_to_corruption { r1@ == true_bytes } + else { maybe_corrupted(r1@, true_bytes, addrs) }); + Ok(r1) + } else { + assert (pm@.subrange(physical_pos as int, physical_pos + len) =~= + log.log.subrange(pos - log.head, pos + len - log.head)); + let ghost addrs = Seq::::new(len as nat, |i: int| i + physical_pos); + let buf = pm.read(physical_pos, len); + assert (if wrpm.constants().impervious_to_corruption { buf@ == true_bytes } + else { maybe_corrupted(buf@, true_bytes, addrs) }); + Ok(buf) + } + } + } + + pub exec fn untrusted_get_head_and_tail( + &self, + wrpm: &WriteRestrictedPersistentMemory + ) -> (result: Result<(u64, u64, u64), InfiniteLogErr>) + where + Perm: CheckPermission>, + PM: PersistentMemory + requires + self.inv(wrpm), + Self::recover(wrpm@) is Some + ensures + match result { + Ok((result_head, result_tail, result_capacity)) => + match Self::recover(wrpm@).unwrap() { + AbstractInfiniteLogState{ head: head, log: log, capacity: capacity } => { + &&& result_head == head + &&& result_tail == head + log.len() + &&& result_capacity == capacity + } + }, + Err(_) => false, + } + { + let pm = wrpm.get_pm_ref(); + proof { lemma_pm_state_header(pm@); } + Ok((self.head, self.tail, self.log_size - 1)) + } + } +} diff --git a/pmemlog/src/main.rs b/pmemlog/src/main.rs index 48b4d5ad..0001249d 100644 --- a/pmemlog/src/main.rs +++ b/pmemlog/src/main.rs @@ -3,9 +3,17 @@ use vstd::prelude::*; use vstd::slice::*; +pub mod infinitelog_t; +pub mod logimpl_v; +pub mod main_t; +pub mod math_v; +pub mod pmemmock_t; +pub mod pmemspec_t; +pub mod sccf; + use pmemlog::main_t::*; -use pmemlog::pmemspec_t::*; use pmemlog::pmemmock_t::*; +use pmemlog::pmemspec_t::*; verus! { diff --git a/pmemlog/src/main_t.rs b/pmemlog/src/main_t.rs index d58089a6..b43aed21 100644 --- a/pmemlog/src/main_t.rs +++ b/pmemlog/src/main_t.rs @@ -1,297 +1,297 @@ -#![allow(dead_code)] - -use std::fmt::Write; - -use crate::infinitelog_t::*; -use crate::logimpl_v::*; -use crate::pmemspec_t::*; -use crate::sccf::CheckPermission; -use vstd::prelude::*; -use vstd::set::*; -use vstd::slice::*; - -verus! { - - pub open spec fn recovery_view() -> (result: spec_fn(Seq) -> Option) - { - |c| UntrustedLogImpl::recover(c) - } - - pub open spec fn read_correct_modulo_corruption(bytes: Seq, true_bytes: Seq, - impervious_to_corruption: bool) -> bool - { - if impervious_to_corruption { - bytes == true_bytes - } - else { - exists |addrs: Seq| { - &&& all_elements_unique(addrs) - &&& #[trigger] maybe_corrupted(bytes, true_bytes, addrs) - } - } - } - - /// A `TrustedPermission` indicates what states of persistent - /// memory are permitted. The struct isn't public, so it can't be - /// created outside of this file. As a further defense against one - /// being created outside this file, its fields aren't public, and - /// the constructor `TrustedPermission::new` isn't public. - - struct TrustedPermission { - ghost is_state_allowable: spec_fn(Seq) -> bool - } - - impl CheckPermission> for TrustedPermission { - closed spec fn check_permission(&self, state: Seq) -> bool { - (self.is_state_allowable)(state) - } - } - - impl TrustedPermission { - proof fn new(cur: Seq, next: spec_fn(AbstractInfiniteLogState, AbstractInfiniteLogState) -> bool) - -> (tracked perm: Self) - ensures - forall |s| #[trigger] perm.check_permission(s) <==> - crate::sccf::is_state_allowable(cur, s, recovery_view(), next) - { - Self { is_state_allowable: |s| crate::sccf::is_state_allowable(cur, s, recovery_view(), next) } - } - } - - /// A `InfiniteLogImpl` wraps an `UntrustedLogImpl` to provide the - /// executable interface that turns a persistent memory region - /// into an effectively infinite log. It provides a simple - /// interface to higher-level code. - pub struct InfiniteLogImpl { - untrusted_log_impl: UntrustedLogImpl, - wrpm: WriteRestrictedPersistentMemory, - } - - #[derive(Debug)] - pub enum InfiniteLogErr { - InsufficientSpaceForSetup { required_space: u64 }, - CantReadBeforeHead { head: u64 }, - CantReadPastTail { tail: u64 }, - InsufficientSpaceForAppend { available_space: u64 }, - CRCMismatch, - CantAdvanceHeadPositionBeforeHead { head: u64 }, - CantAdvanceHeadPositionBeyondTail { tail: u64 }, - } - - impl InfiniteLogImpl { - pub closed spec fn view(self) -> Option - { - recovery_view()(self.wrpm@) - } - - pub closed spec fn constants(self) -> PersistentMemoryConstants - { - self.wrpm.constants() - } - - pub closed spec fn valid(self) -> bool { - &&& self.untrusted_log_impl.inv(&self.wrpm) - &&& recovery_view()(self.wrpm@) is Some - } - - /// This static function takes a `PersistentMemory` and writes - /// to it such that its state represents an empty log starting - /// at head position 0. This function is meant to be called - /// exactly once per log, to create and initialize it. - pub exec fn setup(pm: &mut PM, device_size: u64) -> (result: Result) - requires - old(pm).inv(), - old(pm)@.len() == device_size - ensures - pm.inv(), - pm.constants() == old(pm).constants(), - pm@.len() == device_size, - match result { - Ok(log_capacity) => - recovery_view()(pm@) == Some(AbstractInfiniteLogState::initialize(log_capacity as int)), - Err(InfiniteLogErr::InsufficientSpaceForSetup{ required_space }) => device_size < required_space, - _ => false - } - { - UntrustedLogImpl::untrusted_setup(pm, device_size) - } - - /// This static function takes a `PersistentMemory` and wraps - /// it into an `InfiniteLogImpl`. It's meant to be called after - /// setting up the persistent memory or after crashing and - /// restarting. - pub exec fn start(pm: PM, device_size: u64) -> (result: Result, InfiniteLogErr>) - requires - pm.inv(), - pm@.len() == device_size, - recovery_view()(pm@) is Some - ensures - match result { - Ok(trusted_log_impl) => { - &&& trusted_log_impl.valid() - &&& trusted_log_impl@ == recovery_view()(pm@) - &&& trusted_log_impl.constants() == pm.constants() - }, - Err(InfiniteLogErr::CRCMismatch) => !pm.constants().impervious_to_corruption, - _ => false - } - { - // The untrusted `start` routine may write to persistent memory, as long - // as it keeps its abstraction as a log unchanged. - let mut wrpm = WriteRestrictedPersistentMemory::new(pm); - let tracked perm = TrustedPermission::new(pm@, |s1, s2| false); - match UntrustedLogImpl::untrusted_start(&mut wrpm, device_size, Tracked(&perm)) { - Ok(untrusted_log_impl) => Ok(InfiniteLogImpl { untrusted_log_impl, wrpm }), - Err(e) => Err(e) - } - } - - /// This function appends to the log and returns the offset at - /// which the append happened. - pub exec fn append(&mut self, bytes_to_append: &Vec) -> (result: Result) - requires - old(self).valid() - ensures - self.valid(), - self.constants() == old(self).constants(), - match result { - Ok(offset) => - match (old(self)@, self@) { - (Some(old_log), Some(new_log)) => { - &&& offset as nat == old_log.log.len() + old_log.head - &&& new_log == old_log.append(bytes_to_append@) - }, - _ => false - }, - Err(InfiniteLogErr::InsufficientSpaceForAppend{ available_space }) => { - &&& self@ == old(self)@ - &&& available_space < bytes_to_append.len() - &&& { - let log = old(self)@.unwrap(); - ||| available_space == log.capacity - log.log.len() - ||| available_space == u64::MAX - log.head - log.log.len() - } - }, - _ => false - } - { - // For crash safety, we must restrict the untrusted code's - // writes to persistent memory. We must only let it write - // such that, if a crash happens in the middle of a write, - // the view of the persistent state is either the current - // state or the current state with `bytes_to_append` - // appended. - - let tracked perm = TrustedPermission::new(self.wrpm@, - |s1: AbstractInfiniteLogState, s2| s2 == s1.append(bytes_to_append@)); - self.untrusted_log_impl.untrusted_append(&mut self.wrpm, bytes_to_append, Tracked(&perm)) - } - - /// This function advances the head index of the log. - pub exec fn advance_head(&mut self, new_head: u64) -> (result: Result<(), InfiniteLogErr>) - requires - old(self).valid() - ensures - self.valid(), - self.constants() == old(self).constants(), - match result { - Ok(offset) => { - match (old(self)@, self@) { - (Some(old_log), Some(new_log)) => { - &&& old_log.head <= new_head <= old_log.head + old_log.log.len() - &&& new_log == old_log.advance_head(new_head as int) - }, - _ => false - } - } - Err(InfiniteLogErr::CantAdvanceHeadPositionBeforeHead{ head }) => { - &&& self@ == old(self)@ - &&& head == self@.unwrap().head - &&& new_head < head - }, - Err(InfiniteLogErr::CantAdvanceHeadPositionBeyondTail{ tail }) => { - &&& self@ == old(self)@ - &&& tail == self@.unwrap().head + self@.unwrap().log.len() - &&& new_head > tail - }, - _ => false - } - { - // For crash safety, we must restrict the untrusted code's - // writes to persistent memory. We must only let it write - // such that, if a crash happens in the middle of a write, - // the view of the persistent state is either the current - // state or the current state with the head advanced to - // `new_head`. - - let tracked perm = TrustedPermission::new(self.wrpm@, - |s1: AbstractInfiniteLogState, s2| s2 == s1.advance_head(new_head as int)); - self.untrusted_log_impl.untrusted_advance_head(&mut self.wrpm, new_head, Tracked(&perm)) - } - - /// This function reads `len` bytes from byte position `pos` - /// in the log. It returns a vector of those bytes. - pub exec fn read(&self, pos: u64, len: u64) -> (result: Result, InfiniteLogErr>) - requires - self.valid(), - pos + len <= u64::MAX - ensures - ({ - let state = self@.unwrap(); - let head = state.head; - let log = state.log; - match result { - Ok(bytes) => { - let true_bytes = log.subrange(pos - head, pos + len - head); - &&& pos >= head - &&& pos + len <= head + log.len() - &&& read_correct_modulo_corruption(bytes@, true_bytes, - self.constants().impervious_to_corruption) - }, - Err(InfiniteLogErr::CantReadBeforeHead{ head: head_pos }) => { - &&& pos < head - &&& head_pos == head - }, - Err(InfiniteLogErr::CantReadPastTail{ tail }) => { - &&& pos + len > head + log.len() - &&& tail == head + log.len() - }, - _ => false - } - }) - { - // We don't need to provide permission to write to the - // persistent memory because the untrusted code is only - // getting a non-mutable reference to it and thus can't - // write it. Note that the `UntrustedLogImpl` itself *is* - // mutable, so it can freely update its in-memory state - // (e.g., its cache) if it chooses. - self.untrusted_log_impl.untrusted_read(&self.wrpm, pos, len) - } - - /// This function returns a tuple consisting of the head and - /// tail positions of the log. - pub exec fn get_head_and_tail(&self) -> (result: Result<(u64, u64, u64), InfiniteLogErr>) - requires - self.valid() - ensures - match result { - Ok((result_head, result_tail, result_capacity)) => { - let inf_log = self@.unwrap(); - &&& result_head == inf_log.head - &&& result_tail == inf_log.head + inf_log.log.len() - &&& result_capacity == inf_log.capacity - }, - Err(_) => false - } - { - // We don't need to provide permission to write to the - // persistent memory because the untrusted code is only - // getting a non-mutable reference to it and thus can't - // write it. Note that the `UntrustedLogImpl` itself *is* - // mutable, so it can freely update its in-memory state - // (e.g., its local copy of head and tail) if it chooses. - self.untrusted_log_impl.untrusted_get_head_and_tail(&self.wrpm) - } - } -} +#![allow(dead_code)] + +use std::fmt::Write; + +use crate::infinitelog_t::*; +use crate::logimpl_v::*; +use crate::pmemspec_t::*; +use crate::sccf::CheckPermission; +use vstd::prelude::*; +use vstd::set::*; +use vstd::slice::*; + +verus! { + + pub open spec fn recovery_view() -> (result: spec_fn(Seq) -> Option) + { + |c| UntrustedLogImpl::recover(c) + } + + pub open spec fn read_correct_modulo_corruption(bytes: Seq, true_bytes: Seq, + impervious_to_corruption: bool) -> bool + { + if impervious_to_corruption { + bytes == true_bytes + } + else { + exists |addrs: Seq| { + &&& all_elements_unique(addrs) + &&& #[trigger] maybe_corrupted(bytes, true_bytes, addrs) + } + } + } + + /// A `TrustedPermission` indicates what states of persistent + /// memory are permitted. The struct isn't public, so it can't be + /// created outside of this file. As a further defense against one + /// being created outside this file, its fields aren't public, and + /// the constructor `TrustedPermission::new` isn't public. + + struct TrustedPermission { + ghost is_state_allowable: spec_fn(Seq) -> bool + } + + impl CheckPermission> for TrustedPermission { + closed spec fn check_permission(&self, state: Seq) -> bool { + (self.is_state_allowable)(state) + } + } + + impl TrustedPermission { + proof fn new(cur: Seq, next: spec_fn(AbstractInfiniteLogState, AbstractInfiniteLogState) -> bool) + -> (tracked perm: Self) + ensures + forall |s| #[trigger] perm.check_permission(s) <==> + crate::sccf::is_state_allowable(cur, s, recovery_view(), next) + { + Self { is_state_allowable: |s| crate::sccf::is_state_allowable(cur, s, recovery_view(), next) } + } + } + + /// A `InfiniteLogImpl` wraps an `UntrustedLogImpl` to provide the + /// executable interface that turns a persistent memory region + /// into an effectively infinite log. It provides a simple + /// interface to higher-level code. + pub struct InfiniteLogImpl { + untrusted_log_impl: UntrustedLogImpl, + wrpm: WriteRestrictedPersistentMemory, + } + + #[derive(Debug)] + pub enum InfiniteLogErr { + InsufficientSpaceForSetup { required_space: u64 }, + CantReadBeforeHead { head: u64 }, + CantReadPastTail { tail: u64 }, + InsufficientSpaceForAppend { available_space: u64 }, + CRCMismatch, + CantAdvanceHeadPositionBeforeHead { head: u64 }, + CantAdvanceHeadPositionBeyondTail { tail: u64 }, + } + + impl InfiniteLogImpl { + pub closed spec fn view(self) -> Option + { + recovery_view()(self.wrpm@) + } + + pub closed spec fn constants(self) -> PersistentMemoryConstants + { + self.wrpm.constants() + } + + pub closed spec fn valid(self) -> bool { + &&& self.untrusted_log_impl.inv(&self.wrpm) + &&& recovery_view()(self.wrpm@) is Some + } + + /// This static function takes a `PersistentMemory` and writes + /// to it such that its state represents an empty log starting + /// at head position 0. This function is meant to be called + /// exactly once per log, to create and initialize it. + pub exec fn setup(pm: &mut PM, device_size: u64) -> (result: Result) + requires + old(pm).inv(), + old(pm)@.len() == device_size + ensures + final(pm).inv(), + final(pm).constants() == old(pm).constants(), + final(pm)@.len() == device_size, + match result { + Ok(log_capacity) => + recovery_view()(final(pm)@) == Some(AbstractInfiniteLogState::initialize(log_capacity as int)), + Err(InfiniteLogErr::InsufficientSpaceForSetup{ required_space }) => device_size < required_space, + _ => false + } + { + UntrustedLogImpl::untrusted_setup(pm, device_size) + } + + /// This static function takes a `PersistentMemory` and wraps + /// it into an `InfiniteLogImpl`. It's meant to be called after + /// setting up the persistent memory or after crashing and + /// restarting. + pub exec fn start(pm: PM, device_size: u64) -> (result: Result, InfiniteLogErr>) + requires + pm.inv(), + pm@.len() == device_size, + recovery_view()(pm@) is Some + ensures + match result { + Ok(trusted_log_impl) => { + &&& trusted_log_impl.valid() + &&& trusted_log_impl@ == recovery_view()(pm@) + &&& trusted_log_impl.constants() == pm.constants() + }, + Err(InfiniteLogErr::CRCMismatch) => !pm.constants().impervious_to_corruption, + _ => false + } + { + // The untrusted `start` routine may write to persistent memory, as long + // as it keeps its abstraction as a log unchanged. + let mut wrpm = WriteRestrictedPersistentMemory::new(pm); + let tracked perm = TrustedPermission::new(pm@, |s1, s2| false); + match UntrustedLogImpl::untrusted_start(&mut wrpm, device_size, Tracked(&perm)) { + Ok(untrusted_log_impl) => Ok(InfiniteLogImpl { untrusted_log_impl, wrpm }), + Err(e) => Err(e) + } + } + + /// This function appends to the log and returns the offset at + /// which the append happened. + pub exec fn append(&mut self, bytes_to_append: &Vec) -> (result: Result) + requires + old(self).valid() + ensures + final(self).valid(), + final(self).constants() == old(self).constants(), + match result { + Ok(offset) => + match (old(self)@, final(self)@) { + (Some(old_log), Some(new_log)) => { + &&& offset as nat == old_log.log.len() + old_log.head + &&& new_log == old_log.append(bytes_to_append@) + }, + _ => false + }, + Err(InfiniteLogErr::InsufficientSpaceForAppend{ available_space }) => { + &&& final(self)@ == old(self)@ + &&& available_space < bytes_to_append.len() + &&& { + let log = old(self)@.unwrap(); + ||| available_space == log.capacity - log.log.len() + ||| available_space == u64::MAX - log.head - log.log.len() + } + }, + _ => false + } + { + // For crash safety, we must restrict the untrusted code's + // writes to persistent memory. We must only let it write + // such that, if a crash happens in the middle of a write, + // the view of the persistent state is either the current + // state or the current state with `bytes_to_append` + // appended. + + let tracked perm = TrustedPermission::new(self.wrpm@, + |s1: AbstractInfiniteLogState, s2| s2 == s1.append(bytes_to_append@)); + self.untrusted_log_impl.untrusted_append(&mut self.wrpm, bytes_to_append, Tracked(&perm)) + } + + /// This function advances the head index of the log. + pub exec fn advance_head(&mut self, new_head: u64) -> (result: Result<(), InfiniteLogErr>) + requires + old(self).valid() + ensures + final(self).valid(), + final(self).constants() == old(self).constants(), + match result { + Ok(offset) => { + match (old(self)@, final(self)@) { + (Some(old_log), Some(new_log)) => { + &&& old_log.head <= new_head <= old_log.head + old_log.log.len() + &&& new_log == old_log.advance_head(new_head as int) + }, + _ => false + } + } + Err(InfiniteLogErr::CantAdvanceHeadPositionBeforeHead{ head }) => { + &&& final(self)@ == old(self)@ + &&& head == final(self)@.unwrap().head + &&& new_head < head + }, + Err(InfiniteLogErr::CantAdvanceHeadPositionBeyondTail{ tail }) => { + &&& final(self)@ == old(self)@ + &&& tail == final(self)@.unwrap().head + final(self)@.unwrap().log.len() + &&& new_head > tail + }, + _ => false + } + { + // For crash safety, we must restrict the untrusted code's + // writes to persistent memory. We must only let it write + // such that, if a crash happens in the middle of a write, + // the view of the persistent state is either the current + // state or the current state with the head advanced to + // `new_head`. + + let tracked perm = TrustedPermission::new(self.wrpm@, + |s1: AbstractInfiniteLogState, s2| s2 == s1.advance_head(new_head as int)); + self.untrusted_log_impl.untrusted_advance_head(&mut self.wrpm, new_head, Tracked(&perm)) + } + + /// This function reads `len` bytes from byte position `pos` + /// in the log. It returns a vector of those bytes. + pub exec fn read(&self, pos: u64, len: u64) -> (result: Result, InfiniteLogErr>) + requires + self.valid(), + pos + len <= u64::MAX + ensures + ({ + let state = self@.unwrap(); + let head = state.head; + let log = state.log; + match result { + Ok(bytes) => { + let true_bytes = log.subrange(pos - head, pos + len - head); + &&& pos >= head + &&& pos + len <= head + log.len() + &&& read_correct_modulo_corruption(bytes@, true_bytes, + self.constants().impervious_to_corruption) + }, + Err(InfiniteLogErr::CantReadBeforeHead{ head: head_pos }) => { + &&& pos < head + &&& head_pos == head + }, + Err(InfiniteLogErr::CantReadPastTail{ tail }) => { + &&& pos + len > head + log.len() + &&& tail == head + log.len() + }, + _ => false + } + }) + { + // We don't need to provide permission to write to the + // persistent memory because the untrusted code is only + // getting a non-mutable reference to it and thus can't + // write it. Note that the `UntrustedLogImpl` itself *is* + // mutable, so it can freely update its in-memory state + // (e.g., its cache) if it chooses. + self.untrusted_log_impl.untrusted_read(&self.wrpm, pos, len) + } + + /// This function returns a tuple consisting of the head and + /// tail positions of the log. + pub exec fn get_head_and_tail(&self) -> (result: Result<(u64, u64, u64), InfiniteLogErr>) + requires + self.valid() + ensures + match result { + Ok((result_head, result_tail, result_capacity)) => { + let inf_log = self@.unwrap(); + &&& result_head == inf_log.head + &&& result_tail == inf_log.head + inf_log.log.len() + &&& result_capacity == inf_log.capacity + }, + Err(_) => false + } + { + // We don't need to provide permission to write to the + // persistent memory because the untrusted code is only + // getting a non-mutable reference to it and thus can't + // write it. Note that the `UntrustedLogImpl` itself *is* + // mutable, so it can freely update its in-memory state + // (e.g., its local copy of head and tail) if it chooses. + self.untrusted_log_impl.untrusted_get_head_and_tail(&self.wrpm) + } + } +} diff --git a/pmemlog/src/pmemspec_t.rs b/pmemlog/src/pmemspec_t.rs index 9b5ddd2c..0b3bd74f 100644 --- a/pmemlog/src/pmemspec_t.rs +++ b/pmemlog/src/pmemspec_t.rs @@ -1,304 +1,304 @@ -/* - - This file specifies, with the `PersistentMemory` type, the behavior - of a persistent memory region. One of the things it models is what - can happen to the persistent memory region if the system crashes in - the middle of a write. - -*/ - -#![allow(dead_code)] -#![allow(non_upper_case_globals)] -#![allow(unused_variables)] - -use crate::sccf::CheckPermission; -use vstd::bytes::*; -use vstd::prelude::*; -use vstd::set::*; -use vstd::slice::*; - -#[cfg(not(verus_keep_ghost))] -use crc64fast::Digest; - -verus! { - - pub open spec fn all_elements_unique(seq: Seq) -> bool { - forall |i: int, j: int| 0 <= i < j < seq.len() ==> seq[i] != seq[j] - } - - pub uninterp spec fn maybe_corrupted_byte(byte: u8, true_byte: u8, addr: int) -> bool; - - pub open spec fn maybe_corrupted(bytes: Seq, true_bytes: Seq, addrs: Seq) -> bool { - &&& bytes.len() == true_bytes.len() == addrs.len() - &&& forall |i: int| #![auto] 0 <= i < bytes.len() ==> maybe_corrupted_byte(bytes[i], true_bytes[i], addrs[i]) - } - - pub const crc_size: u64 = 8; - - pub uninterp spec fn spec_crc_bytes(header_bytes: Seq) -> Seq; - - #[verifier::external_body] - pub exec fn bytes_crc(header_bytes: &Vec) -> (out: Vec) - ensures - spec_crc_bytes(header_bytes@) == out@, - out@.len() == crc_size - { - #[cfg(not(verus_keep_ghost))] - { - let mut c = Digest::new(); - c.write(header_bytes.as_slice()); - u64_to_le_bytes(c.sum64()) - } - #[cfg(verus_keep_ghost)] - unimplemented!() - } - - // We make two assumptions about how CRCs can be used to detect - // corruption. - - // The first assumption, encapsulated in - // `axiom_bytes_uncorrupted`, is that if we store byte sequences - // `x` and `y` to persistent memory where `y` is the CRC of `x`, - // then we can detect an absence of corruption by reading both of - // them. Specifically, if we read from those locations and get - // `x_c` and `y_c` (corruptions of `x` and `y` respectively), and - // `y_c` is the CRC of `x_c`, then we can conclude that `x` wasn't - // corrupted, i.e., that `x_c == x`. - - #[verifier(external_body)] - pub proof fn axiom_bytes_uncorrupted(x_c: Seq, x: Seq, x_addrs: Seq, - y_c: Seq, y: Seq, y_addrs: Seq) - requires - maybe_corrupted(x_c, x, x_addrs), - maybe_corrupted(y_c, y, y_addrs), - y == spec_crc_bytes(x), - y_c == spec_crc_bytes(x_c), - all_elements_unique(x_addrs), - all_elements_unique(y_addrs) - ensures - x == x_c - {} - - // The second assumption, encapsulated in - // `axiom_corruption_detecting_boolean`, is that the values - // `cdb0_val` and `cdb1_val` are so randomly different from each - // other that corruption can't make one appear to be the other. - // That is, if we know we wrote either `cdb0_val` or `cdb1_val` to - // a certain part of persistent memory, and when we read that same - // part we get `cdb0_val` or `cdb1_val`, we can assume it matches - // what we last wrote to it. To justify the assumption that - // `cdb0_val` and `cdb1_val` are different from each other, we set - // them to CRC(b"0") and CRC(b"1"), respectively. - - pub const cdb0_val: u64 = 0xa32842d19001605e; // CRC(b"0") - pub const cdb1_val: u64 = 0xab21aa73069531b7; // CRC(b"1") - - #[verifier(external_body)] - pub proof fn axiom_corruption_detecting_boolean(cdb_c: u64, cdb: u64, addrs: Seq) - requires - maybe_corrupted(spec_u64_to_le_bytes(cdb_c), spec_u64_to_le_bytes(cdb), addrs), - all_elements_unique(addrs), - cdb == cdb0_val || cdb == cdb1_val, - cdb_c == cdb0_val || cdb_c == cdb1_val, - ensures - cdb_c == cdb - {} - - pub struct PersistentMemoryConstants { - pub impervious_to_corruption: bool - } - - // We mark this as `external_body` so that the verifier can't see - // that there's nothing important in it and thereby shortcut some - // checks. - - pub trait PersistentMemory : Sized { - spec fn view(self) -> Seq; - - spec fn inv(self) -> bool; - - spec fn constants(self) -> PersistentMemoryConstants; - - /// This is the model of some routine that reads the - /// `num_bytes` bytes at address `addr`. - fn read(&self, addr: u64, num_bytes: u64) -> (bytes: Vec) - requires - self.inv(), - addr + num_bytes <= self@.len() - ensures - ({ - let true_bytes = self@.subrange(addr as int, addr + num_bytes); - let addrs = Seq::::new(num_bytes as nat, |i: int| i + addr); - if self.constants().impervious_to_corruption { - bytes@ == true_bytes - } - else { - maybe_corrupted(bytes@, true_bytes, addrs) - } - }); - - /// This is the model of some routine that writes `bytes` - /// starting at address `addr`. - fn write(&mut self, addr: u64, bytes: &[u8]) - requires - old(self).inv(), - addr + bytes@.len() <= (old(self))@.len(), - addr + bytes@.len() <= u64::MAX - ensures - self.inv(), - self.constants() == old(self).constants(), - self@ == update_contents_to_reflect_write(old(self)@, addr as int, bytes@); - } - - /// We model the persistent memory as getting flushed in chunks, - /// where each chunk has `persistence_chunk_size` bytes. We refer - /// to chunk number `id` as the set of addresses `addr` such that - /// `addr / persistence_chunk_size == id`. - pub spec const persistence_chunk_size: int = 8; - - /// Return the byte at address `addr` after writing - /// `write_bytes` to address `write_addr`, if the byte at - /// `addr` before the write was `prewrite_byte`. - pub open spec fn update_byte_to_reflect_write(addr: int, prewrite_byte: u8, write_addr: int, - write_bytes: Seq) -> u8 - { - if write_addr <= addr && addr < write_addr + write_bytes.len() { - write_bytes[addr - write_addr] - } - else { - prewrite_byte - } - } - - /// Return the contents of persistent memory after writing - /// `write_bytes` to address `write_addr`, if the contents - /// before the write was `prewrite_contents`. - pub open spec(checked) fn update_contents_to_reflect_write(prewrite_contents: Seq, write_addr: int, - write_bytes: Seq) -> Seq - recommends - 0 <= write_addr, - write_addr + write_bytes.len() <= prewrite_contents.len(), - { - Seq::::new(prewrite_contents.len(), - |addr| update_byte_to_reflect_write(addr, prewrite_contents[addr], - write_addr, write_bytes)) - } - - /// Return the byte at address `addr` after initiating (but - /// not necessarily completing) a write of `write_bytes` to - /// address `write_addr`, given that the byte at `addr` before - /// the write was `prewrite_byte` and given that the set of - /// chunk IDs that have been flushed since the initiation of - /// the write is `chunks_flushed`. - pub open spec fn update_byte_to_reflect_partially_flushed_write(addr: int, prewrite_byte: u8, write_addr: int, - write_bytes: Seq, - chunks_flushed: Set) -> u8 - { - if chunks_flushed.contains(addr / persistence_chunk_size) { - update_byte_to_reflect_write(addr, prewrite_byte, write_addr, write_bytes) - } - else { - prewrite_byte - } - } - - /// Return the contents of persistent memory after initiating - /// (but not necessarily completing) a write of `write_bytes` - /// to address `write_addr`, given that the contents before - /// the write were `prewrite_contents` and given that the set of - /// chunk IDs that have been flushed since the initiation of - /// the write is `chunks_flushed`. - pub open spec(checked) fn update_contents_to_reflect_partially_flushed_write(contents: Seq, write_addr: int, - write_bytes: Seq, - chunks_flushed: Set) -> Seq - recommends - 0 <= write_addr, - write_addr + write_bytes.len() <= contents.len(), - { - Seq::::new(contents.len(), - |addr| update_byte_to_reflect_partially_flushed_write(addr, contents[addr], write_addr, - write_bytes, chunks_flushed)) - } - - /// A `WriteRestrictedPersistentMemory

` object wraps a - /// `PersistentMemory` object to restrict how it's written. - /// Untrusted code passed one of these can only write to the - /// encapsulated persistent memory by providing a permission of - /// type `P`. That permission must allow all possible states `s` - /// such that crashing in the middle of the write might leave the - /// persistent memory in state `s`. - pub struct WriteRestrictedPersistentMemory - where - Perm: CheckPermission>, - PM: PersistentMemory - { - pm: PM, - ghost perm: Option // unused, but Rust demands some reference to Perm - } - - impl WriteRestrictedPersistentMemory - where - Perm: CheckPermission>, - PM: PersistentMemory - { - pub closed spec fn view(self) -> Seq { - self.pm@ - } - - pub closed spec fn inv(self) -> bool { - self.pm.inv() - } - - pub closed spec fn constants(self) -> PersistentMemoryConstants { - self.pm.constants() - } - - pub exec fn new(pm: PM) -> (wrpm: Self) - requires - pm.inv() - ensures - wrpm@ == pm@, - wrpm.inv(), - wrpm.constants() == pm.constants() - { - Self { pm: pm, perm: None } - } - - pub exec fn get_pm_ref(&self) -> (pm: &PM) - requires - self.inv() - ensures - pm.inv(), - pm@ == self@, - pm.constants() == self.constants() - { - &self.pm - } - - /// This `write` function can only be called if a crash in the - /// middle of the requested write will leave the persistent - /// memory in a state allowed by `perm`. The state must be - /// allowed no matter what subset of the persistence chunks - /// have been flushed. - pub exec fn write(&mut self, addr: u64, bytes: &[u8], perm: Tracked<&Perm>) - requires - old(self).inv(), - addr + bytes@.len() <= old(self)@.len(), - addr + bytes@.len() <= u64::MAX, - forall |chunks_flushed| { - let new_contents: Seq = - #[trigger] update_contents_to_reflect_partially_flushed_write( - old(self)@, addr as int, bytes@, chunks_flushed - ); - perm@.check_permission(new_contents) - }, - ensures - self.inv(), - self.constants() == old(self).constants(), - self@ == update_contents_to_reflect_write(old(self)@, addr as int, bytes@), - { - self.pm.write(addr, bytes) - } - } - -} +/* + + This file specifies, with the `PersistentMemory` type, the behavior + of a persistent memory region. One of the things it models is what + can happen to the persistent memory region if the system crashes in + the middle of a write. + +*/ + +#![allow(dead_code)] +#![allow(non_upper_case_globals)] +#![allow(unused_variables)] + +use crate::sccf::CheckPermission; +use vstd::bytes::*; +use vstd::prelude::*; +use vstd::set::*; +use vstd::slice::*; + +#[cfg(not(verus_keep_ghost))] +use crc64fast::Digest; + +verus! { + + pub open spec fn all_elements_unique(seq: Seq) -> bool { + forall |i: int, j: int| 0 <= i < j < seq.len() ==> seq[i] != seq[j] + } + + pub uninterp spec fn maybe_corrupted_byte(byte: u8, true_byte: u8, addr: int) -> bool; + + pub open spec fn maybe_corrupted(bytes: Seq, true_bytes: Seq, addrs: Seq) -> bool { + &&& bytes.len() == true_bytes.len() == addrs.len() + &&& forall |i: int| #![auto] 0 <= i < bytes.len() ==> maybe_corrupted_byte(bytes[i], true_bytes[i], addrs[i]) + } + + pub const crc_size: u64 = 8; + + pub uninterp spec fn spec_crc_bytes(header_bytes: Seq) -> Seq; + + #[verifier::external_body] + pub exec fn bytes_crc(header_bytes: &Vec) -> (out: Vec) + ensures + spec_crc_bytes(header_bytes@) == out@, + out@.len() == crc_size + { + #[cfg(not(verus_keep_ghost))] + { + let mut c = Digest::new(); + c.write(header_bytes.as_slice()); + u64_to_le_bytes(c.sum64()) + } + #[cfg(verus_keep_ghost)] + unimplemented!() + } + + // We make two assumptions about how CRCs can be used to detect + // corruption. + + // The first assumption, encapsulated in + // `axiom_bytes_uncorrupted`, is that if we store byte sequences + // `x` and `y` to persistent memory where `y` is the CRC of `x`, + // then we can detect an absence of corruption by reading both of + // them. Specifically, if we read from those locations and get + // `x_c` and `y_c` (corruptions of `x` and `y` respectively), and + // `y_c` is the CRC of `x_c`, then we can conclude that `x` wasn't + // corrupted, i.e., that `x_c == x`. + + #[verifier(external_body)] + pub proof fn axiom_bytes_uncorrupted(x_c: Seq, x: Seq, x_addrs: Seq, + y_c: Seq, y: Seq, y_addrs: Seq) + requires + maybe_corrupted(x_c, x, x_addrs), + maybe_corrupted(y_c, y, y_addrs), + y == spec_crc_bytes(x), + y_c == spec_crc_bytes(x_c), + all_elements_unique(x_addrs), + all_elements_unique(y_addrs) + ensures + x == x_c + {} + + // The second assumption, encapsulated in + // `axiom_corruption_detecting_boolean`, is that the values + // `cdb0_val` and `cdb1_val` are so randomly different from each + // other that corruption can't make one appear to be the other. + // That is, if we know we wrote either `cdb0_val` or `cdb1_val` to + // a certain part of persistent memory, and when we read that same + // part we get `cdb0_val` or `cdb1_val`, we can assume it matches + // what we last wrote to it. To justify the assumption that + // `cdb0_val` and `cdb1_val` are different from each other, we set + // them to CRC(b"0") and CRC(b"1"), respectively. + + pub const cdb0_val: u64 = 0xa32842d19001605e; // CRC(b"0") + pub const cdb1_val: u64 = 0xab21aa73069531b7; // CRC(b"1") + + #[verifier(external_body)] + pub proof fn axiom_corruption_detecting_boolean(cdb_c: u64, cdb: u64, addrs: Seq) + requires + maybe_corrupted(spec_u64_to_le_bytes(cdb_c), spec_u64_to_le_bytes(cdb), addrs), + all_elements_unique(addrs), + cdb == cdb0_val || cdb == cdb1_val, + cdb_c == cdb0_val || cdb_c == cdb1_val, + ensures + cdb_c == cdb + {} + + pub struct PersistentMemoryConstants { + pub impervious_to_corruption: bool + } + + // We mark this as `external_body` so that the verifier can't see + // that there's nothing important in it and thereby shortcut some + // checks. + + pub trait PersistentMemory : Sized { + spec fn view(self) -> Seq; + + spec fn inv(self) -> bool; + + spec fn constants(self) -> PersistentMemoryConstants; + + /// This is the model of some routine that reads the + /// `num_bytes` bytes at address `addr`. + fn read(&self, addr: u64, num_bytes: u64) -> (bytes: Vec) + requires + self.inv(), + addr + num_bytes <= self@.len() + ensures + ({ + let true_bytes = self@.subrange(addr as int, addr + num_bytes); + let addrs = Seq::::new(num_bytes as nat, |i: int| i + addr); + if self.constants().impervious_to_corruption { + bytes@ == true_bytes + } + else { + maybe_corrupted(bytes@, true_bytes, addrs) + } + }); + + /// This is the model of some routine that writes `bytes` + /// starting at address `addr`. + fn write(&mut self, addr: u64, bytes: &[u8]) + requires + old(self).inv(), + addr + bytes@.len() <= (old(self))@.len(), + addr + bytes@.len() <= u64::MAX + ensures + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@ == update_contents_to_reflect_write(old(self)@, addr as int, bytes@); + } + + /// We model the persistent memory as getting flushed in chunks, + /// where each chunk has `persistence_chunk_size` bytes. We refer + /// to chunk number `id` as the set of addresses `addr` such that + /// `addr / persistence_chunk_size == id`. + pub spec const persistence_chunk_size: int = 8; + + /// Return the byte at address `addr` after writing + /// `write_bytes` to address `write_addr`, if the byte at + /// `addr` before the write was `prewrite_byte`. + pub open spec fn update_byte_to_reflect_write(addr: int, prewrite_byte: u8, write_addr: int, + write_bytes: Seq) -> u8 + { + if write_addr <= addr && addr < write_addr + write_bytes.len() { + write_bytes[addr - write_addr] + } + else { + prewrite_byte + } + } + + /// Return the contents of persistent memory after writing + /// `write_bytes` to address `write_addr`, if the contents + /// before the write was `prewrite_contents`. + pub open spec(checked) fn update_contents_to_reflect_write(prewrite_contents: Seq, write_addr: int, + write_bytes: Seq) -> Seq + recommends + 0 <= write_addr, + write_addr + write_bytes.len() <= prewrite_contents.len(), + { + Seq::::new(prewrite_contents.len(), + |addr| update_byte_to_reflect_write(addr, prewrite_contents[addr], + write_addr, write_bytes)) + } + + /// Return the byte at address `addr` after initiating (but + /// not necessarily completing) a write of `write_bytes` to + /// address `write_addr`, given that the byte at `addr` before + /// the write was `prewrite_byte` and given that the set of + /// chunk IDs that have been flushed since the initiation of + /// the write is `chunks_flushed`. + pub open spec fn update_byte_to_reflect_partially_flushed_write(addr: int, prewrite_byte: u8, write_addr: int, + write_bytes: Seq, + chunks_flushed: Set) -> u8 + { + if chunks_flushed.contains(addr / persistence_chunk_size) { + update_byte_to_reflect_write(addr, prewrite_byte, write_addr, write_bytes) + } + else { + prewrite_byte + } + } + + /// Return the contents of persistent memory after initiating + /// (but not necessarily completing) a write of `write_bytes` + /// to address `write_addr`, given that the contents before + /// the write were `prewrite_contents` and given that the set of + /// chunk IDs that have been flushed since the initiation of + /// the write is `chunks_flushed`. + pub open spec(checked) fn update_contents_to_reflect_partially_flushed_write(contents: Seq, write_addr: int, + write_bytes: Seq, + chunks_flushed: Set) -> Seq + recommends + 0 <= write_addr, + write_addr + write_bytes.len() <= contents.len(), + { + Seq::::new(contents.len(), + |addr| update_byte_to_reflect_partially_flushed_write(addr, contents[addr], write_addr, + write_bytes, chunks_flushed)) + } + + /// A `WriteRestrictedPersistentMemory

` object wraps a + /// `PersistentMemory` object to restrict how it's written. + /// Untrusted code passed one of these can only write to the + /// encapsulated persistent memory by providing a permission of + /// type `P`. That permission must allow all possible states `s` + /// such that crashing in the middle of the write might leave the + /// persistent memory in state `s`. + pub struct WriteRestrictedPersistentMemory + where + Perm: CheckPermission>, + PM: PersistentMemory + { + pm: PM, + ghost perm: Option // unused, but Rust demands some reference to Perm + } + + impl WriteRestrictedPersistentMemory + where + Perm: CheckPermission>, + PM: PersistentMemory + { + pub closed spec fn view(self) -> Seq { + self.pm@ + } + + pub closed spec fn inv(self) -> bool { + self.pm.inv() + } + + pub closed spec fn constants(self) -> PersistentMemoryConstants { + self.pm.constants() + } + + pub exec fn new(pm: PM) -> (wrpm: Self) + requires + pm.inv() + ensures + wrpm@ == pm@, + wrpm.inv(), + wrpm.constants() == pm.constants() + { + Self { pm: pm, perm: None } + } + + pub exec fn get_pm_ref(&self) -> (pm: &PM) + requires + self.inv() + ensures + pm.inv(), + pm@ == self@, + pm.constants() == self.constants() + { + &self.pm + } + + /// This `write` function can only be called if a crash in the + /// middle of the requested write will leave the persistent + /// memory in a state allowed by `perm`. The state must be + /// allowed no matter what subset of the persistence chunks + /// have been flushed. + pub exec fn write(&mut self, addr: u64, bytes: &[u8], perm: Tracked<&Perm>) + requires + old(self).inv(), + addr + bytes@.len() <= old(self)@.len(), + addr + bytes@.len() <= u64::MAX, + forall |chunks_flushed| { + let new_contents: Seq = + #[trigger] update_contents_to_reflect_partially_flushed_write( + old(self)@, addr as int, bytes@, chunks_flushed + ); + perm@.check_permission(new_contents) + }, + ensures + final(self).inv(), + final(self).constants() == old(self).constants(), + final(self)@ == update_contents_to_reflect_write(old(self)@, addr as int, bytes@), + { + self.pm.write(addr, bytes) + } + } + +} diff --git a/unverified/metadata_kv/Cargo.lock b/unverified/metadata_kv/Cargo.lock index 29fad221..6886351a 100644 --- a/unverified/metadata_kv/Cargo.lock +++ b/unverified/metadata_kv/Cargo.lock @@ -513,15 +513,15 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "verus_builtin" -version = "0.0.0-2026-04-12-0118" +version = "0.0.0-2026-05-06-1803" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a46cb431066009ad2035f6bca936b1c2b7e293bffec93a2090fead0f35ab4276" +checksum = "650bcc71ef90cbc79bc2a2494c54dd159edad63a72677be791954edc993883fe" [[package]] name = "verus_builtin_macros" -version = "0.0.0-2026-04-19-0121" +version = "0.0.0-2026-05-06-1803" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40353590ec73d419cbbae41ceac223b726d93c3db09d577311b3c000fa409017" +checksum = "eb76a6de59afd73bb34e6cfdf1dc17592b88ce460d7b3cb49eb24f0f86a173d2" dependencies = [ "proc-macro2", "quote", @@ -533,9 +533,9 @@ dependencies = [ [[package]] name = "verus_prettyplease" -version = "0.0.0-2026-04-12-0118" +version = "0.0.0-2026-05-06-1803" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b246e61b068e807cb05a030fc1d7efa83d2a0d227eaf67921661419edfe8e83" +checksum = "cdc83b14e7901e5b560a4d6a7107f6990996f8d67f47e3d31d61862df2c2b3d5" dependencies = [ "proc-macro2", "verus_syn", @@ -543,9 +543,9 @@ dependencies = [ [[package]] name = "verus_state_machines_macros" -version = "0.0.0-2026-04-19-0121" +version = "0.0.0-2026-05-06-1803" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08e3405834c76c48ebe6970b910364547c1fa167880bf6b0ac4025b835068423" +checksum = "8e8d918b213a69574fa69b389a72586cb2429addb938ff198e027499ae0a70d2" dependencies = [ "indexmap 1.9.3", "proc-macro2", @@ -555,9 +555,9 @@ dependencies = [ [[package]] name = "verus_syn" -version = "0.0.0-2026-04-05-0114" +version = "0.0.0-2026-05-06-1803" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "285b554a87b470ee705634ea1cdc92c14ba088a7b8bdacbb9116b32e832fe272" +checksum = "c69a13786a726af332f1fff4d79dd7400a714033ed3750266d2c2d9b44595201" dependencies = [ "proc-macro2", "quote", @@ -566,9 +566,9 @@ dependencies = [ [[package]] name = "vstd" -version = "0.0.0-2026-04-19-0121" +version = "0.0.0-2026-05-06-1803" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1fd7a88249f8f7fe3e1a7a4b7e40f84e5343e2b664a0e9dcec6da70a53df61" +checksum = "8565e53371c0e0ddf2ba80862c532e013d40a310f13beb858e3060cf62a382d0" dependencies = [ "verus_builtin", "verus_builtin_macros", diff --git a/unverified/metadata_kv/Cargo.toml b/unverified/metadata_kv/Cargo.toml index bc23943c..70e7dc8c 100644 --- a/unverified/metadata_kv/Cargo.toml +++ b/unverified/metadata_kv/Cargo.toml @@ -8,5 +8,5 @@ edition = "2021" [dependencies] # Avoid default features since @lopopolo reports that rand is unsound with both the log and thread_rng features rand = { version = "0.10.1", default-features = false, features = [ "thread_rng" ] } -vstd = "0.0.0-2026-04-20-1748" +vstd = "0.0.0-2026-05-06-1803" proptest = "1.4"