@@ -5,33 +5,65 @@ Import ListNotations.
5
5
6
6
Local Open Scope type.
7
7
8
+ (** The type of the state of the world. The world contains all the user, tokens, and ownership
9
+ relations that have been stated until now. We never explicitly say what ia actually contains. *)
8
10
Parameter World : Set.
11
+
12
+ (** The type of the user. We do not explicitly say how a user is described. *)
9
13
Parameter User : Set.
14
+
15
+ (** The kind of a token. We will instantiate one kind of token per coin that we create, in order to
16
+ trace them and to be able to say that it is impossible to transfer one kind of tokens to the
17
+ other. *)
10
18
Parameter TokenKind : Set.
19
+
20
+ (** A quantity of token for a given [token_kind]. This is not explicitly defined for now, but it
21
+ will be positive integer or a positive real number, if we allow to own a part of a token. *)
11
22
Parameter TokenQuantity : forall (token_kind : TokenKind), Set .
12
23
24
+ (** The primitives that we assume as given on the types provided above. *)
13
25
Module Primitives.
14
- Parameter create_token_kind : World -> TokenKind * World.
26
+ (** Create a new kind of token, different from all the kinds that existed before, and return the
27
+ new state of the world with this added token. *)
28
+ Parameter create_token_kind :
29
+ World ->
30
+ TokenKind * World.
15
31
16
- (** Get the quantity of token for a certain kind and a user *)
32
+ (** Get the quantity of token owned by a user, considering a certain token kind. *)
17
33
Parameter get_balance :
18
34
forall (token_kind : TokenKind),
19
35
User ->
20
- World -> TokenQuantity token_kind.
36
+ World ->
37
+ TokenQuantity token_kind.
21
38
22
- (** Transfer a certain quantity of tokens from a user to another *)
39
+ (** Transfer a certain quantity of tokens from a user to another, and return the new state of the
40
+ world where the quantity of tokens they both own has been updated. *)
23
41
Parameter transfer :
24
42
forall (token_kind : TokenKind),
25
- User -> User -> TokenQuantity token_kind ->
26
- World -> option World.
43
+ User ->
44
+ User ->
45
+ TokenQuantity token_kind ->
46
+ World ->
47
+ option World.
27
48
End Primitives.
28
49
50
+ (** Actions are the primitives that we can run in our DSL to interact with tokens, make transfers,
51
+ and all interactions with the state of the world.
52
+
53
+ Note that in contrast to the primitives above, we do not have access to the [World]. We only
54
+ describe the actions that we can perform on it.
55
+ *)
29
56
Module Action.
30
57
Inductive t : Set -> Set :=
58
+ (** Instantiate a new kind of token *)
31
59
| CreateTokenKind : t TokenKind
60
+ (** Ask for the number of tokens owned by a user *)
32
61
| GetBalance (token_kind : TokenKind) (user : User) : t (TokenQuantity token_kind)
62
+ (** Ask to transfer token from a user to another one. The result is a boolean stating if the
63
+ transfer was successful, meaning if there were enough funds. *)
33
64
| Transfer (token_kind : TokenKind) (from to : User) (value : TokenQuantity token_kind) : t bool.
34
65
66
+ (** This function maps the actions we defined to the primitives acting on the world above *)
35
67
Definition run (world : World) {A : Set } (action : t A) : A * World :=
36
68
match action with
37
69
| CreateTokenKind =>
@@ -47,14 +79,25 @@ Module Action.
47
79
End Action.
48
80
49
81
Module ActionTree.
82
+ (** Here we define a tree of actions. This data structure will be useful later to specify that a
83
+ smart contract is behaving as expected.
84
+
85
+ It aims to represent the tree of all the actions that were executed by a smart contract call.
86
+ The tree should be ordered from left to right in the order the actions were executed.
87
+ *)
50
88
Inductive t : Set :=
89
+ (** Empty tree *)
51
90
| Pure
91
+ (** A tree composed of two sub-trees *)
52
92
| Let (tree1 tree2 : t)
93
+ (** A leaf with a single action *)
53
94
| MakeAction {A : Set} (action : Action.t A).
54
95
55
96
Module Forall.
97
+ (** This inductive predicate states that all the actions in the tree satisfy a given property. *)
56
98
Inductive t (P : forall {A : Set}, Action.t A -> Prop ) : ActionTree.t -> Prop :=
57
- | Pure : t _ Pure
99
+ | Pure :
100
+ t _ Pure
58
101
| Let (tree1 tree2 : ActionTree.t) :
59
102
t _ tree1 ->
60
103
t _ tree2 ->
@@ -66,15 +109,24 @@ Module ActionTree.
66
109
End ActionTree.
67
110
68
111
Module M.
69
- (** A free monad where we can run actions about the world but not manipulate it directly *)
112
+ (** A "monad" to define our DSL from the action above. We can never access to the world
113
+ explicitly, but we are still manipulating it through the actions. *)
70
114
Inductive t (A : Set ) : Set :=
115
+ (** A program without any actions, returning [value] of type [A] *)
71
116
| Pure (value : A)
117
+ (** A program doing the sequencing of two sub-programs, with [e] being executed first and [k]
118
+ being executed second. Additionally, [k] takes as a parameter the result of [e] that can be
119
+ useful sometimes. *)
72
120
| Let {B : Set } (e : t B) (k : B -> t A)
121
+ (** A program that executes a single action [action] *)
73
122
| MakeAction (action : Action.t A).
123
+ (* Some commands to set the implicit parameters of the constructors above *)
74
124
Arguments Pure {_}.
75
125
Arguments Let {_ _}.
76
126
Arguments MakeAction {_}.
77
127
128
+ (** Execute a program using the primitives above and returning the tree of actions that were
129
+ made. *)
78
130
Fixpoint run {A : Set} (e : t A) (world : World) : A * World * ActionTree.t :=
79
131
match e with
80
132
| Pure value => (value, world, ActionTree.Pure)
@@ -88,54 +140,84 @@ Module M.
88
140
end .
89
141
End M.
90
142
143
+ (** A convenient notation for the [M.Let] constructor that sequences two programs *)
91
144
Notation "'let !' a ':=' b 'in ' c" :=
92
145
(M.Let b (fun a => c))
93
146
(at level 200, a pattern , b at level 100, c at level 200).
94
147
95
148
Module SmartContract.
149
+ (** A general definition of what is a smart contract. A smart contract has many type parameters
150
+ like [State] to represent the type of its internal storage.
151
+
152
+ It has two methods [init] and [call] to represent the initial call (the constructor in
153
+ Solidity) and the sub-sequent calls. We will use an [Inductive] type to represent the payload
154
+ of the [call] function and encode the fact that there might be different entrypoints in the
155
+ smart contract.
156
+ *)
96
157
Record t {InitInput InitOutput : Set } {Command : InitOutput -> Set -> Set } {State : Set } : Set := {
97
158
init
159
+ (** The user instantiating the smart contract on chain *)
98
160
(sender : User)
161
+ (** Some parameters that the user can choose to instantiate the contract *)
99
162
(init_input : InitInput) :
100
163
M.t (option (InitOutput * State));
101
164
call {A : Set }
165
+ (** Some initialization value that was computed by the [init] function *)
102
166
(init_output : InitOutput)
167
+ (** All smart contract calls originate form a certain user [sender] that is running and paying
168
+ for the call *)
103
169
(sender : User)
170
+ (** The [command] is the payload that we sent to the smart contract to execute it *)
104
171
(command : Command init_output A)
172
+ (** The initial internal storage [state] at the beginning of the call *)
105
173
(state : State) :
174
+ (** We return an option type to represent a potential execution failure (a revert in
175
+ Solidity). In case of success, the output is a couple of a value of type [A], depending on
176
+ the kind of [command] we call, and a new internal storage state. *)
106
177
M.t (option (A * State));
107
178
}.
108
179
Arguments t : clear implicits.
109
180
End SmartContract.
110
181
182
+ (** Here we give an application of our DSL above with a representation of a simplified version of
183
+ an [ERC-20] smart contract. *)
111
184
Module Erc20.
185
+ (** The init parameters are a name and a symbol for the token we are creating. They do not play
186
+ any role in the computations, but are there in the ERC-20 of OpenZeppelin that we study. *)
112
187
Definition InitInput : Set :=
113
188
string * string.
114
189
190
+ (** The init outputs a new token kind *)
115
191
Definition InitOutput : Set :=
116
192
TokenKind.
117
193
194
+ (** The state of the contract is just the name and the symbol of its token. The token balance for
195
+ each user is stored in the global world and we do not have to handle it here. *)
118
196
Module State.
119
197
Record t : Set := {
120
198
name : string;
121
199
symbol : string;
122
200
}.
123
201
End State.
124
202
203
+ (** The commands representing the entrypoints of our smart contract, with the type of their
204
+ output. *)
125
205
Module Command.
126
206
Inductive t {token_kind : InitOutput} : Set -> Set :=
127
207
| BalanceOf : User -> t (TokenQuantity token_kind)
128
208
| Transfer : User -> TokenQuantity token_kind -> t unit.
129
209
Arguments t : clear implicits.
130
210
End Command.
131
211
212
+ (** The definition of our ERC-20 smart contract *)
132
213
Definition smart_contract :
133
214
SmartContract.t
134
215
InitInput
135
216
InitOutput
136
217
Command.t
137
218
State.t :=
138
219
{|
220
+ (* The constructor function *)
139
221
SmartContract.init _sender '(name, symbol) :=
140
222
let ! token_kind := M.MakeAction Action.CreateTokenKind in
141
223
let state := {|
@@ -145,10 +227,14 @@ Module Erc20.
145
227
M.Pure (Some (token_kind, state));
146
228
SmartContract.call A token_kind sender command state :=
147
229
match command in Command.t _ A return M.t (option (A * _)) with
230
+ (* The "balanceOf" entrypoint *)
148
231
| Command.BalanceOf user =>
232
+ (* We run the action to get the balance *)
149
233
let ! balance := M.MakeAction (Action.GetBalance token_kind user) in
150
234
M.Pure (Some (balance, state))
235
+ (* The "transfer" entrypoint *)
151
236
| Command.Transfer to value =>
237
+ (* We run the action to make the transfer and to know if it succeeded *)
152
238
let ! is_success := M.MakeAction (Action.Transfer token_kind sender to value) in
153
239
if is_success then
154
240
M.Pure (Some (tt, state))
@@ -158,29 +244,42 @@ Module Erc20.
158
244
|}.
159
245
End Erc20.
160
246
247
+ (** Here we will define what it means for a smart contract defined in our DSL to be safe, in the
248
+ sense that no one can steal money from others. *)
161
249
Module NoStealing.
162
250
Module InAction.
251
+ (** We first define that a smart contract is safe at the level of a single action. We consider
252
+ an action [action] and a user [sender] which is executing the current smart contract
253
+ execution. *)
163
254
Definition t (sender : User) {A : Set} (action : Action.t A) : Prop :=
164
255
match action with
256
+ (** Creating a new kind of token is safe *)
165
257
| Action.CreateTokenKind => True
258
+ (** Asking for the balance of a user is safe (all data are public in a blockchain) *)
166
259
| Action.GetBalance _ _ => True
260
+ (** Transferring tokens is only safe is the account from which we take the money is the same
261
+ as the user running the smart contract *)
167
262
| Action.Transfer token_kind from to value =>
168
263
from = sender
169
264
end .
170
265
End InAction.
171
266
172
267
Module InActionTree.
268
+ (** We generalize the safety of a an action to a tree of actions *)
173
269
Definition t (sender : User) (tree : ActionTree.t) : Prop :=
174
270
ActionTree.Forall.t (@InAction.t sender) tree.
175
271
End InActionTree.
176
272
177
273
Module InRun.
274
+ (** We define the safety of the execution of an expression [e] from the safety of its tree of
275
+ actions *)
178
276
Definition t {A : Set} (world : World) (sender : User) (e : M.t A) : Prop :=
179
277
let '(_, _, tree) := M.run e world in
180
278
InActionTree.t sender tree.
181
279
End InRun.
182
280
183
281
Module InSmartContract.
282
+ (** We say that a smart contract is safe if all its possible executions are safe *)
184
283
Record t {InitInput InitOutput : Set } {Command : InitOutput -> Set -> Set } {State : Set }
185
284
(smart_contract : SmartContract.t InitInput InitOutput Command State) :
186
285
Prop := {
@@ -193,15 +292,26 @@ Module NoStealing.
193
292
End InSmartContract.
194
293
End NoStealing.
195
294
295
+ (** Now we will verify that our ERC-20 smart contract is safe *)
196
296
Module Erc20IsSafe.
297
+ (** Here is the specification saying that our smart contract is safe. It applies the predicate
298
+ saying that a smart contract is safe to our definition of ERC-20. *)
197
299
Lemma is_safe : NoStealing.InSmartContract.t Erc20.smart_contract.
300
+ (** Here is the proof that the specification is correct. It should be executed in an IDE to see
301
+ the proof state as we progress line by line.
302
+
303
+ The general idea is to go over all the possible execution scenarios of the smart contract,
304
+ with the init, the "balanceOf", and the "transfer" entrypoints, and to prove that they are all
305
+ safe.
306
+ *)
198
307
Proof .
199
308
constructor; intros; cbn.
200
309
{ (* init *)
201
310
destruct init_input as [name symbol].
202
311
unfold NoStealing.InRun.t; cbn.
203
312
destruct Primitives.create_token_kind.
204
313
apply ActionTree.Forall.Let. {
314
+ (* The init is safe because the action we make to create a new token kind is safe *)
205
315
apply ActionTree.Forall.MakeAction.
206
316
cbn.
207
317
trivial.
@@ -213,6 +323,8 @@ Module Erc20IsSafe.
213
323
{ (* BalanceOf *)
214
324
unfold NoStealing.InRun.t; cbn.
215
325
apply ActionTree.Forall.Let. {
326
+ (* The "balanceOf" entrypoint is safe because the action we make to get the balance of a
327
+ user is safe *)
216
328
apply ActionTree.Forall.MakeAction.
217
329
cbn.
218
330
trivial.
@@ -221,6 +333,10 @@ Module Erc20IsSafe.
221
333
}
222
334
{ (* Transfer *)
223
335
unfold NoStealing.InRun.t; cbn.
336
+ (* We have two cases, depending on whether the transfer succeeded or not. In both cases we
337
+ make a single transfer (or transfer attempt) with the right user for the source account,
338
+ so this is safe.
339
+ *)
224
340
destruct Primitives.transfer; cbn.
225
341
{ apply ActionTree.Forall.Let. {
226
342
apply ActionTree.Forall.MakeAction.
0 commit comments