Skip to content

Commit 5dfe4ba

Browse files
authored
Merge pull request #2 from formal-land/guillaume-claret@add-comments-for-erc20
Add comments to the ERC-20 smart contract
2 parents cb9275b + e655cf0 commit 5dfe4ba

File tree

1 file changed

+124
-8
lines changed

1 file changed

+124
-8
lines changed

erc20.v

+124-8
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,65 @@ Import ListNotations.
55

66
Local Open Scope type.
77

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. *)
810
Parameter World : Set.
11+
12+
(** The type of the user. We do not explicitly say how a user is described. *)
913
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. *)
1018
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. *)
1122
Parameter TokenQuantity : forall (token_kind : TokenKind), Set.
1223

24+
(** The primitives that we assume as given on the types provided above. *)
1325
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.
1531

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. *)
1733
Parameter get_balance :
1834
forall (token_kind : TokenKind),
1935
User ->
20-
World -> TokenQuantity token_kind.
36+
World ->
37+
TokenQuantity token_kind.
2138

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. *)
2341
Parameter transfer :
2442
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.
2748
End Primitives.
2849

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+
*)
2956
Module Action.
3057
Inductive t : Set -> Set :=
58+
(** Instantiate a new kind of token *)
3159
| CreateTokenKind : t TokenKind
60+
(** Ask for the number of tokens owned by a user *)
3261
| 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. *)
3364
| Transfer (token_kind : TokenKind) (from to : User) (value : TokenQuantity token_kind) : t bool.
3465

66+
(** This function maps the actions we defined to the primitives acting on the world above *)
3567
Definition run (world : World) {A : Set} (action : t A) : A * World :=
3668
match action with
3769
| CreateTokenKind =>
@@ -47,14 +79,25 @@ Module Action.
4779
End Action.
4880

4981
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+
*)
5088
Inductive t : Set :=
89+
(** Empty tree *)
5190
| Pure
91+
(** A tree composed of two sub-trees *)
5292
| Let (tree1 tree2 : t)
93+
(** A leaf with a single action *)
5394
| MakeAction {A : Set} (action : Action.t A).
5495

5596
Module Forall.
97+
(** This inductive predicate states that all the actions in the tree satisfy a given property. *)
5698
Inductive t (P : forall {A : Set}, Action.t A -> Prop) : ActionTree.t -> Prop :=
57-
| Pure : t _ Pure
99+
| Pure :
100+
t _ Pure
58101
| Let (tree1 tree2 : ActionTree.t) :
59102
t _ tree1 ->
60103
t _ tree2 ->
@@ -66,15 +109,24 @@ Module ActionTree.
66109
End ActionTree.
67110

68111
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. *)
70114
Inductive t (A : Set) : Set :=
115+
(** A program without any actions, returning [value] of type [A] *)
71116
| 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. *)
72120
| Let {B : Set} (e : t B) (k : B -> t A)
121+
(** A program that executes a single action [action] *)
73122
| MakeAction (action : Action.t A).
123+
(* Some commands to set the implicit parameters of the constructors above *)
74124
Arguments Pure {_}.
75125
Arguments Let {_ _}.
76126
Arguments MakeAction {_}.
77127

128+
(** Execute a program using the primitives above and returning the tree of actions that were
129+
made. *)
78130
Fixpoint run {A : Set} (e : t A) (world : World) : A * World * ActionTree.t :=
79131
match e with
80132
| Pure value => (value, world, ActionTree.Pure)
@@ -88,54 +140,84 @@ Module M.
88140
end.
89141
End M.
90142

143+
(** A convenient notation for the [M.Let] constructor that sequences two programs *)
91144
Notation "'let!' a ':=' b 'in' c" :=
92145
(M.Let b (fun a => c))
93146
(at level 200, a pattern, b at level 100, c at level 200).
94147

95148
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+
*)
96157
Record t {InitInput InitOutput : Set} {Command : InitOutput -> Set -> Set} {State : Set} : Set := {
97158
init
159+
(** The user instantiating the smart contract on chain *)
98160
(sender : User)
161+
(** Some parameters that the user can choose to instantiate the contract *)
99162
(init_input : InitInput) :
100163
M.t (option (InitOutput * State));
101164
call {A : Set}
165+
(** Some initialization value that was computed by the [init] function *)
102166
(init_output : InitOutput)
167+
(** All smart contract calls originate form a certain user [sender] that is running and paying
168+
for the call *)
103169
(sender : User)
170+
(** The [command] is the payload that we sent to the smart contract to execute it *)
104171
(command : Command init_output A)
172+
(** The initial internal storage [state] at the beginning of the call *)
105173
(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. *)
106177
M.t (option (A * State));
107178
}.
108179
Arguments t : clear implicits.
109180
End SmartContract.
110181

182+
(** Here we give an application of our DSL above with a representation of a simplified version of
183+
an [ERC-20] smart contract. *)
111184
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. *)
112187
Definition InitInput : Set :=
113188
string * string.
114189

190+
(** The init outputs a new token kind *)
115191
Definition InitOutput : Set :=
116192
TokenKind.
117193

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. *)
118196
Module State.
119197
Record t : Set := {
120198
name : string;
121199
symbol : string;
122200
}.
123201
End State.
124202

203+
(** The commands representing the entrypoints of our smart contract, with the type of their
204+
output. *)
125205
Module Command.
126206
Inductive t {token_kind : InitOutput} : Set -> Set :=
127207
| BalanceOf : User -> t (TokenQuantity token_kind)
128208
| Transfer : User -> TokenQuantity token_kind -> t unit.
129209
Arguments t : clear implicits.
130210
End Command.
131211

212+
(** The definition of our ERC-20 smart contract *)
132213
Definition smart_contract :
133214
SmartContract.t
134215
InitInput
135216
InitOutput
136217
Command.t
137218
State.t :=
138219
{|
220+
(* The constructor function *)
139221
SmartContract.init _sender '(name, symbol) :=
140222
let! token_kind := M.MakeAction Action.CreateTokenKind in
141223
let state := {|
@@ -145,10 +227,14 @@ Module Erc20.
145227
M.Pure (Some (token_kind, state));
146228
SmartContract.call A token_kind sender command state :=
147229
match command in Command.t _ A return M.t (option (A * _)) with
230+
(* The "balanceOf" entrypoint *)
148231
| Command.BalanceOf user =>
232+
(* We run the action to get the balance *)
149233
let! balance := M.MakeAction (Action.GetBalance token_kind user) in
150234
M.Pure (Some (balance, state))
235+
(* The "transfer" entrypoint *)
151236
| Command.Transfer to value =>
237+
(* We run the action to make the transfer and to know if it succeeded *)
152238
let! is_success := M.MakeAction (Action.Transfer token_kind sender to value) in
153239
if is_success then
154240
M.Pure (Some (tt, state))
@@ -158,29 +244,42 @@ Module Erc20.
158244
|}.
159245
End Erc20.
160246

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. *)
161249
Module NoStealing.
162250
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. *)
163254
Definition t (sender : User) {A : Set} (action : Action.t A) : Prop :=
164255
match action with
256+
(** Creating a new kind of token is safe *)
165257
| Action.CreateTokenKind => True
258+
(** Asking for the balance of a user is safe (all data are public in a blockchain) *)
166259
| 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 *)
167262
| Action.Transfer token_kind from to value =>
168263
from = sender
169264
end.
170265
End InAction.
171266

172267
Module InActionTree.
268+
(** We generalize the safety of a an action to a tree of actions *)
173269
Definition t (sender : User) (tree : ActionTree.t) : Prop :=
174270
ActionTree.Forall.t (@InAction.t sender) tree.
175271
End InActionTree.
176272

177273
Module InRun.
274+
(** We define the safety of the execution of an expression [e] from the safety of its tree of
275+
actions *)
178276
Definition t {A : Set} (world : World) (sender : User) (e : M.t A) : Prop :=
179277
let '(_, _, tree) := M.run e world in
180278
InActionTree.t sender tree.
181279
End InRun.
182280

183281
Module InSmartContract.
282+
(** We say that a smart contract is safe if all its possible executions are safe *)
184283
Record t {InitInput InitOutput : Set} {Command : InitOutput -> Set -> Set} {State : Set}
185284
(smart_contract : SmartContract.t InitInput InitOutput Command State) :
186285
Prop := {
@@ -193,15 +292,26 @@ Module NoStealing.
193292
End InSmartContract.
194293
End NoStealing.
195294

295+
(** Now we will verify that our ERC-20 smart contract is safe *)
196296
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. *)
197299
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+
*)
198307
Proof.
199308
constructor; intros; cbn.
200309
{ (* init *)
201310
destruct init_input as [name symbol].
202311
unfold NoStealing.InRun.t; cbn.
203312
destruct Primitives.create_token_kind.
204313
apply ActionTree.Forall.Let. {
314+
(* The init is safe because the action we make to create a new token kind is safe *)
205315
apply ActionTree.Forall.MakeAction.
206316
cbn.
207317
trivial.
@@ -213,6 +323,8 @@ Module Erc20IsSafe.
213323
{ (* BalanceOf *)
214324
unfold NoStealing.InRun.t; cbn.
215325
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 *)
216328
apply ActionTree.Forall.MakeAction.
217329
cbn.
218330
trivial.
@@ -221,6 +333,10 @@ Module Erc20IsSafe.
221333
}
222334
{ (* Transfer *)
223335
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+
*)
224340
destruct Primitives.transfer; cbn.
225341
{ apply ActionTree.Forall.Let. {
226342
apply ActionTree.Forall.MakeAction.

0 commit comments

Comments
 (0)