forked from AU-COBRA/ConCert
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathEIP20TokenGens.v
108 lines (99 loc) · 4.17 KB
/
EIP20TokenGens.v
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
From ConCert.Execution Require Import Blockchain.
From ConCert.Execution Require Import Containers.
From ConCert.Execution Require Import Serializable.
#[warnings="-notation-incompatible-prefix"]
From ConCert.Execution.Test Require Import QCTest.
From ConCert.Examples.EIP20 Require Import EIP20Token.
Import MonadNotation.
From Coq Require Import List. Import ListNotations.
From Coq Require Import ZArith.
Module Type EIP20GensInfo.
Parameter contract_addr : Address.
Parameter gAccount : G Address.
End EIP20GensInfo.
Module EIP20Gens (Info : EIP20GensInfo).
Import Info.
Arguments SerializedValue : clear implicits.
Arguments deserialize : clear implicits.
Arguments serialize : clear implicits.
Definition serializeMsg := @serialize EIP20Token.Msg _.
(* This function tries to generate a transfer between existing accounts in the token contract's state.
Otherwise, tries to use accounts in the Blockchain state.
Has a small chance to transfer between "fresh" accounts. *)
Definition gTransfer (env : Environment)
(state : EIP20Token.State)
: G (Address * Msg) :=
let nr_accounts_in_state := FMap.size state.(balances) in
let weight_1 := 2 * nr_accounts_in_state + 1 in
let randomize_mk_gen g := (* the probability of sampling fresh accounts grows smaller over time *)
freq [
(weight_1, returnGen g) ;
(0, from_addr <- arbitrary ;;
to_addr <- arbitrary ;;
returnGen (from_addr, transfer to_addr 0%N))
] in
sample <- sampleFMapOpt state.(balances) ;;
match sample with
| Some (addr, balance) =>
transfer_amount <- choose (0%N, balance) ;;
account <- gAccount ;;
randomize_mk_gen (addr, transfer account transfer_amount)
(* if the contract state contains no accounts, just transfer 0 tokens between two arbitrary accounts *)
| None => from_addr <- arbitrary ;;
to_addr <- arbitrary ;;
returnGen (from_addr, transfer to_addr 0%N)
end.
Local Open Scope N_scope.
(* Note: not optimal implementation. Should filter on balances map instead of first sampling and then filtering *)
Definition gApprove (state : EIP20Token.State) : GOpt (Address * Msg) :=
bindOpt (sample2UniqueFMapOpt state.(balances)) (fun p =>
let addr1 := fst (fst p) in
let balance1 := snd (fst p) in
let addr2 := fst (snd p) in
let balance2 := snd (snd p) in
if 0 <? balance1
then amount <- choose (0, balance1) ;; returnGenSome (addr1, approve addr2 amount)
else if 0 <? balance2
then amount <- choose (0, balance2) ;; returnGenSome (addr2, approve addr1 amount)
else returnGen None
).
Definition gTransfer_from (state : EIP20Token.State) : GOpt (Address * Msg) :=
bindOpt (sampleFMapOpt state.(allowances)) (fun '(allower, allowance_map) =>
bindOpt (sampleFMapOpt allowance_map) (fun '(delegate, allowance) =>
bindOpt (sampleFMapOpt state.(balances)) (fun '(receiver, _) =>
let allower_balance := (FMap_find_ allower state.(balances) 0) in
amount <- (if allower_balance =? 0
then returnGen 0
else choose (0, N.min allowance allower_balance)) ;;
returnGenSome (delegate, transfer_from allower receiver amount)
)
)).
Local Close Scope N_scope.
(* Main generator. *)
Definition gEIP20TokenAction (env : Environment) : GOpt Action :=
let call contract_addr caller_addr msg :=
returnGenSome {|
act_origin := caller_addr;
act_from := caller_addr;
act_body := act_call contract_addr 0%Z (serializeMsg msg)
|} in
state <-- returnGen (get_contract_state EIP20Token.State env contract_addr) ;;
backtrack [
(* transfer *)
(2, '(caller, msg) <- gTransfer env state ;;
call contract_addr caller msg
) ;
(* transfer_from *)
(3, bindOpt (gTransfer_from state)
(fun '(caller, msg) =>
call contract_addr caller msg
)
);
(* approve *)
(2, bindOpt (gApprove state)
(fun '(caller, msg) =>
call contract_addr caller msg
)
)
].
End EIP20Gens.