1use planarian_schema::Hash;
11use vstd::prelude::*;
12
13verus! {
14
15#[derive(Clone, Copy)]
17pub struct Link {
18 pub seq: u64,
19 pub prev: Hash,
21 pub next: Hash,
23}
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub enum Follows {
28 Yes,
29 OutOfSequence,
31 Unchained,
33}
34
35pub open spec fn same(a: Hash, b: Hash) -> bool {
37 a.0@ == b.0@
38}
39
40pub open spec fn follows_spec(last: u64, head: Hash, seq: u64, prev: Hash) -> Follows {
41 if last == u64::MAX || seq != last + 1 {
42 Follows::OutOfSequence
43 } else if !same(prev, head) {
44 Follows::Unchained
45 } else {
46 Follows::Yes
47 }
48}
49
50pub open spec fn cursor_at(last: u64, head: Hash, links: Seq<Link>, i: int) -> (u64, Hash) {
52 if i == 0 {
53 (last, head)
54 } else {
55 (links[i - 1].seq, links[i - 1].next)
56 }
57}
58
59pub open spec fn link_ok(last: u64, head: Hash, links: Seq<Link>, i: int) -> bool {
60 follows_spec(cursor_at(last, head, links, i).0, cursor_at(last, head, links, i).1, links[i].seq, links[i].prev)
61 == Follows::Yes
62}
63
64pub open spec fn chained_upto(last: u64, head: Hash, links: Seq<Link>, n: int) -> bool {
66 forall|i: int| 0 <= i < n ==> #[trigger] link_ok(last, head, links, i)
67}
68
69pub open spec fn chained(last: u64, head: Hash, links: Seq<Link>) -> bool {
71 chained_upto(last, head, links, links.len() as int)
72}
73
74pub fn hash_eq(a: &Hash, b: &Hash) -> (r: bool)
75 ensures
76 r == same(*a, *b),
77{
78 let mut i: usize = 0;
79 while i < 32
80 invariant
81 0 <= i <= 32,
82 a.0@.len() == 32,
83 b.0@.len() == 32,
84 forall|j: int| 0 <= j < i ==> a.0@[j] == b.0@[j],
85 decreases 32 - i,
86 {
87 if a.0[i] != b.0[i] {
88 return false;
89 }
90 i += 1;
91 }
92 assert(a.0@ =~= b.0@);
93 true
94}
95
96pub fn follows(last: u64, head: &Hash, seq: u64, prev: &Hash) -> (r: Follows)
98 ensures
99 r == follows_spec(last, *head, seq, *prev),
100{
101 if last == u64::MAX || seq != last + 1 {
102 Follows::OutOfSequence
103 } else if !hash_eq(prev, head) {
104 Follows::Unchained
105 } else {
106 Follows::Yes
107 }
108}
109
110pub enum Verdict {
112 Chained { last: u64, head: Hash },
114 Break { at: usize, why: Follows },
116}
117
118pub fn verify(last: u64, head: Hash, links: &Vec<Link>) -> (r: Verdict)
121 ensures
122 (r is Chained) == chained(last, head, links@),
123 r matches Verdict::Chained { last: l, head: h } ==> (l, h) == cursor_at(last, head, links@, links@.len() as int),
124 r matches Verdict::Break { at, why } ==> {
125 &&& at < links@.len()
126 &&& chained_upto(last, head, links@, at as int)
127 &&& !link_ok(last, head, links@, at as int)
128 &&& why != Follows::Yes
129 },
130{
131 let mut i: usize = 0;
132 let mut cur_last = last;
133 let mut cur_head = head;
134 while i < links.len()
135 invariant
136 0 <= i <= links@.len(),
137 chained_upto(last, head, links@, i as int),
138 (cur_last, cur_head) == cursor_at(last, head, links@, i as int),
139 decreases links@.len() - i,
140 {
141 let l = links[i];
142 assert(l == links@[i as int]);
143 let f = follows(cur_last, &cur_head, l.seq, &l.prev);
144 match f {
145 Follows::Yes => {},
146 _ => {
147 assert(!link_ok(last, head, links@, i as int));
148 return Verdict::Break { at: i, why: f };
149 },
150 }
151 assert(link_ok(last, head, links@, i as int));
152 cur_last = l.seq;
153 cur_head = l.next;
154 i += 1;
155 assert(chained_upto(last, head, links@, i as int)) by {
156 assert forall|j: int| 0 <= j < i implies #[trigger] link_ok(last, head, links@, j) by {
157 if j < i - 1 {
158 assert(chained_upto(last, head, links@, (i - 1) as int));
159 }
160 }
161 }
162 }
163 Verdict::Chained { last: cur_last, head: cur_head }
164}
165
166pub fn next_link(last: u64, head: &Hash) -> (r: (u64, Hash))
169 requires
170 last < u64::MAX,
171 ensures
172 follows_spec(last, *head, r.0, r.1) == Follows::Yes,
173 r.0 == last + 1,
174 same(r.1, *head),
175{
176 (last + 1, *head)
177}
178
179pub proof fn appending_keeps_the_chain(last: u64, head: Hash, links: Seq<Link>, l: Link)
183 requires
184 chained(last, head, links),
185 follows_spec(
186 cursor_at(last, head, links, links.len() as int).0,
187 cursor_at(last, head, links, links.len() as int).1,
188 l.seq,
189 l.prev,
190 ) == Follows::Yes,
191 ensures
192 chained(last, head, links.push(l)),
193{
194 let s = links.push(l);
195 assert forall|i: int| 0 <= i < s.len() implies #[trigger] link_ok(last, head, s, i) by {
196 if i < links.len() {
197 assert(link_ok(last, head, links, i));
198 if i > 0 {
199 assert(s[i - 1] == links[i - 1]);
200 }
201 assert(s[i] == links[i]);
202 } else {
203 assert(s[i] == l);
204 if i > 0 {
205 assert(s[i - 1] == links[i - 1]);
206 }
207 }
208 }
209}
210
211pub proof fn an_edited_record_breaks_the_chain(last: u64, head: Hash, links: Seq<Link>, i: int, edited: Hash)
215 requires
216 chained(last, head, links),
217 0 <= i < links.len() - 1,
218 !same(edited, links[i].next),
219 ensures
220 !chained(last, head, links.update(i, Link { next: edited, ..links[i] })),
221{
222 let s = links.update(i, Link { next: edited, ..links[i] });
223 assert(link_ok(last, head, links, i + 1));
224 assert(s[i + 1] == links[i + 1]);
225 assert(!link_ok(last, head, s, i + 1));
226}
227
228}