-
Notifications
You must be signed in to change notification settings - Fork 1
/
Merkel.dfy
127 lines (107 loc) · 3.59 KB
/
Merkel.dfy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// RUN: /compile:0 /rlimit:1000000
module Prelude {
type Data
type Time = real
type uint32 = x: int | 0 <= x < 0xFFFFFFFF
type Hash = uint32
function HashData(d: Data): Hash
function HashList(l: seq<Hash>): Hash
function MapRemove<A(==),B>(m: map<A, B>, a: A): map<A, B>
{
map x | x in m && x != a :: m[x]
}
}
module Dedup {
import opened Prelude
datatype Dedup =
| DChunk(data: Data, hash: Hash, KU: Time)
| DNode(children: seq<Hash>, hash: Hash, KU: Time)
datatype State = State(dedups: map<Hash, Dedup>, refs: set<(Hash, Hash)>, now: Time)
function DedupUpdateKU(d: Dedup, ku: Time): Dedup
{
match d
case DChunk(data, hash, _) => DChunk(data, hash, ku)
case DNode(children, hash, _) => DNode(children, hash, ku)
}
predicate DeleteDedup(s: State, s': State)
{
&& s'.refs == s.refs
&& s'.now == s.now
&& exists hash {:trigger MapRemove(s.dedups, hash)} | hash in s.dedups ::
var d := s.dedups[hash];
&& d.KU < s.now
&& (forall h | h in s.dedups && s.dedups[h].DNode? :: hash !in s.dedups[h].children)
&& s'.dedups == MapRemove(s.dedups, hash)
}
predicate ClockTick(s: State, s': State)
{
&& s'.dedups == s.dedups
&& s'.refs == s.refs
&& s'.now >= s.now
}
predicate ExtendKU(s: State, s': State, h: Hash, ku: Time)
{
&& s'.refs == s.refs
&& s'.now == s.now
&& h in s.dedups
&& var d := s.dedups[h];
&& ku >= d.KU
&& (d.DNode? ==> forall h' | h' in d.children && h' in s.dedups :: s.dedups[h'].KU >= ku)
&& s'.dedups == s.dedups[h := DedupUpdateKU(d, ku)]
}
predicate Inv(s: State)
{
&& (forall h, h' | h in s.dedups :: var d := s.dedups[h];
&& d.DNode?
&& h' in d.children
==>
&& h' in s.dedups
&& var d' := s.dedups[h'];
&& d'.KU >= d.KU)
}
predicate Next(s: State, s': State)
{
|| DeleteDedup(s, s')
|| ClockTick(s, s')
|| (exists h: Hash, ku :: ExtendKU(s, s', h, ku))
}
lemma InvInductive(s: State, s': State)
requires Inv(s) && Next(s, s')
ensures Inv(s')
{ }
}
module DedupTree {
import opened Prelude
import Dedup
datatype DedupTree =
| DTChunk(data: Data, hash: Hash, KU: Time)
| DTNode(children: seq<DedupTree>, hash: Hash, KU: Time)
predicate InTree(d: Dedup.Dedup, t: DedupTree)
{
match t
case DTChunk(data, hash, KU) => d == Dedup.DChunk(data, hash, KU)
case DTNode(children, hash, KU) =>
|| d == Dedup.DNode(Hashes(children), hash, KU)
|| exists c | c in children :: InTree(d, c)
}
function Hashes(l: seq<DedupTree>): seq<Hash>
{
if l == [] then
[]
else
[l[0].hash] + Hashes(l[1..])
}
predicate DedupTreeValid(m: DedupTree)
{
match m
case DTChunk(d,h,_) => h == HashData(d)
case DTNode(children, h, ku) =>
&& (forall child | child in children :: DedupTreeValid(child) && ku <= child.KU)
&& h == HashList(Hashes(children))
}
predicate DedupsMatchTrees(dedups: map<Hash, Dedup.Dedup>, trees: set<DedupTree>)
{
&& (forall h | h in dedups :: exists t | t in trees :: InTree(dedups[h], t))
&& (forall t, d | t in trees && InTree(d, t) :: d.hash in dedups && dedups[d.hash] == d)
}
}