-
Notifications
You must be signed in to change notification settings - Fork 201
/
MCNano.tla
122 lines (100 loc) · 4.45 KB
/
MCNano.tla
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
------------------------------- MODULE MCNano -------------------------------
(***************************************************************************)
(* This spec tries to make the Nano.tla spec model-checkable. The *)
(* CalculateHash constant is the greatest source of trouble. The way this *)
(* works is by playing fast and loose with TLC's level checker: it will *)
(* rightfully error out if we instantiate the Nano spec with a variable- *)
(* level function directly, but if we instead also make CalculateHash a *)
(* constant in this spec then override it with a variable-level function *)
(* *in the model* then all is well. The specific operator used is the *)
(* CalculateHashImpl operator defined down below. See discussion here: *)
(* *)
(* https://groups.google.com/g/tlaplus/c/r5sB2vgil_Q/m/lM546pjpAQAJ *)
(* *)
(* The action StutterWhenHashesDepleted also serves as a state restriction *)
(* to gracefully terminate the spec when we have run out of hashes. We *)
(* also make use of the VIEW functionality to remove specific hash order *)
(* from consideration when calculating whether we have visited a state. *)
(* It would make sense to declare the Hash set symmetric, but since this *)
(* set gets fairly large by modelchecking standards (5+ elements) the *)
(* computation becomes dominated by substituting all 5+! permutations of *)
(* the set while generating new states, so actually increases modelcheck *)
(* time. *)
(* *)
(* Unfortunately blockchains by their very nature have super-exponential *)
(* scaling in the number of possible states as a function of chain length, *)
(* because the order of actions is recorded in the chain itself. This is *)
(* the same issue faced when using any sort of history variable in finite *)
(* modelchecking: all action order permutations are explored, not just *)
(* combinations. Thus finite modelchecking is not very effective at *)
(* analyzing blockchains and formal proofs should be used instead. *)
(* *)
(***************************************************************************)
EXTENDS
Naturals,
FiniteSets
CONSTANTS
CalculateHash(_,_,_),
Hash,
NoHashVal,
PrivateKey,
PublicKey,
Node,
GenesisBalance,
NoBlockVal
ASSUME
/\ Cardinality(PrivateKey) = Cardinality(PublicKey)
/\ Cardinality(PrivateKey) <= Cardinality(Node)
/\ GenesisBalance \in Nat
VARIABLES
hashFunction,
lastHash,
distributedLedger,
received
Vars == <<hashFunction, lastHash, distributedLedger, received>>
\* Ignore hashFunction and lastHash when calculating state hash
View == <<distributedLedger, received>>
-----------------------------------------------------------------------------
KeyPair ==
CHOOSE mapping \in [PrivateKey -> PublicKey] :
/\ \A publicKey \in PublicKey :
/\ \E privateKey \in PrivateKey :
/\ mapping[privateKey] = publicKey
Ownership ==
CHOOSE mapping \in [Node -> PrivateKey] :
/\ \A privateKey \in PrivateKey :
/\ \E node \in Node :
/\ mapping[node] = privateKey
N == INSTANCE Nano
UndefinedHashesExist ==
\E hash \in Hash: hashFunction[hash] = N!NoBlock
HashOf(block) ==
IF \E hash \in Hash : hashFunction[hash] = block
THEN CHOOSE hash \in Hash : hashFunction[hash] = block
ELSE CHOOSE hash \in Hash : hashFunction[hash] = N!NoBlock
CalculateHashImpl(block, oldLastHash, newLastHash) ==
LET hash == HashOf(block) IN
/\ newLastHash = hash
/\ hashFunction' = [hashFunction EXCEPT ![hash] = block]
TypeInvariant ==
/\ hashFunction \in [Hash -> N!Block \cup {N!NoBlock}]
/\ N!TypeInvariant
SafetyInvariant ==
/\ N!SafetyInvariant
Init ==
/\ hashFunction = [hash \in Hash |-> N!NoBlock]
/\ N!Init
StutterWhenHashesDepleted ==
/\ UNCHANGED hashFunction
/\ UNCHANGED lastHash
/\ UNCHANGED distributedLedger
/\ UNCHANGED received
Next ==
IF UndefinedHashesExist
THEN N!Next
ELSE StutterWhenHashesDepleted
Spec ==
/\ Init
/\ [][Next]_Vars
THEOREM Safety == Spec => TypeInvariant /\ SafetyInvariant
=============================================================================