forked from pirapira/eth-isabelle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathevm.lem
2299 lines (1960 loc) · 86.6 KB
/
evm.lem
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(* *)
(* Copyright 2016 Sami Mäkelä *)
(* Copyright 2016 Yoichi Hirai *)
(* *)
(* Licensed under the Apache License, Version 2.0 (the "License"); *)
(* you may not use this file except in compliance with the License. *)
(* You may obtain a copy of the License at *)
(* *)
(* http://www.apache.org/licenses/LICENSE-2.0 *)
(* *)
(* Unless required by applicable law or agreed to in writing, software *)
(* distributed under the License is distributed on an "AS IS" BASIS, *)
(* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *)
(* See the License for the specific language governing permissions and *)
(* limitations under the License. *)
open import Pervasives
open import List
open import Word
open import Word256
open import Word160
open import Word8
open import Word4
open import Keccak
type w8 = word8
(* The frequently used machine word types are named here. For example, address *)
(* is the type of 160-bit machine words. The type w256 is the type of EVM machine words. *)
type address = word160
val sintFromW256 : w256 -> integer
let sintFromW256 = word256ToInteger
val uint : w256 -> integer
let uint w = integerFromNatural (word256ToNatural w)
declare isabelle target_rep function uint = `Word.uint`
val uintFromW8 : w8 -> integer
let uintFromW8 w = integerFromNatural (word8ToNatural w)
declare isabelle target_rep function uintFromW8 = `Word.uint`
(* val word256ToNat : w256 -> nat *)
(* let word256ToNat = word256ToNat *)
val absnat : w256 -> nat
let absnat w = natFromInteger (word256ToInteger w)
val byteFromNat : nat -> byte
let byteFromNat = word8FromNat
(* val word_of_int : int -> w256 *)
(* let word_of_int = word256FromInt *)
val w256_of_bl : list bool -> w256
let w256_of_bl = word256FromBoollist
val w256_to_address : w256 -> address
let w256_to_address w = word160FromNatural (word256ToNatural w)
declare isabelle target_rep function w256_to_address = `Word.ucast`
val address_to_w256 : address -> w256
let address_to_w256 w = word256FromNatural (word160ToNatural w)
declare isabelle target_rep function address_to_w256 = `Word.ucast`
val w256_to_byte : w256 -> byte
let w256_to_byte w = word8FromNatural (word256ToNatural w)
declare isabelle target_rep function w256_to_byte = `Word.ucast`
val byte_to_w256 : byte -> w256
let byte_to_w256 w = word256FromNat (word8ToNat w)
declare isabelle target_rep function byte_to_w256 = `Word.ucast`
val word_rsplit256 : w256 -> list byte
let word_rsplit256 w = (word_rsplit_aux (boolListFromWord256 w) 32)
declare isabelle target_rep function word_rsplit256 = `word_rsplit`
val get_byte : w256 -> w256 -> w256
let get_byte position w =
if uint position >= 32 then 0 else
match List.index (word_rsplit256 w) (natFromInteger (uint position)) with
| Nothing -> 0
| Just (a:byte) -> byte_to_w256 a
end
(* log256floor takes a logarithm (base 256) of an integer and takes floor *)
val log2 : integer -> integer
let rec log2 x =
if x <= 1 then 0
else 1 + log2 (x / 2)
declare coq target_rep function log2 = `Z.log2`
declare termination_argument log2 = automatic
val log256floor : integer -> integer
let rec {isabelle; hol; ocaml} log256floor x =
if x <= 255 then 0
else 1 + log256floor (x / 256)
let {coq} log256floor x = log2 x / 8
declare termination_argument log256floor = automatic
val word_exp : integer -> natural -> integer
let rec {isabelle;hol;ocaml} word_exp i n =
match n with
| 0 -> 1
| _ ->
let half = n / 2 in
let modulo = natFromNatural (n mod 2) in
let recur = word_exp i half in
((i ** modulo) * recur * recur) mod (2 ** 256)
end
let rec {coq} word_exp i n =
match n with
| 0 -> 1
| n+1 -> (word_exp i n * i) mod (2 ** 256)
end
declare termination_argument word_exp = automatic
(* In EVM, the memory contains one byte for each machine word (offset). *)
(* The storage contains one machine word for each machine word (index). *)
(* As we will see, the memory is cleared for every invocation of smart contracts. *)
(* The storage is persistent for an account. *)
type memory = w256 -> byte
type storage = w256 -> w256
(* Looking up a number of bytes from the memory: *)
val cut_memory_aux : w256 -> natural -> memory -> list byte
let rec cut_memory_aux idx n memory = match n with
| 0 -> []
| n + 1 -> memory idx :: cut_memory_aux (idx + 1) n memory
end
declare termination_argument cut_memory_aux = automatic
val iota : w256 -> natural -> list w256 -> list w256
let rec iota start n rev_acc =
match n with
| 0 -> reverse rev_acc
| x + 1 -> iota (start + 1) x (start :: rev_acc)
end
declare termination_argument iota = automatic
val cut_memory_aux_alt : w256 -> natural -> memory -> list byte
let cut_memory_aux_alt idx n memory = List.map memory (iota idx n [])
val cut_memory : w256 -> w256 -> memory -> list byte
let cut_memory idx n memory = cut_memory_aux_alt idx (word256ToNatural n) memory
val put_return_values_aux : memory -> list byte -> integer -> natural -> memory
let rec put_return_values_aux orig lst b s = match s with
| 0 -> orig
| s + 1 ->
match lst with
| [] -> orig
| h :: t -> put_return_values_aux (fun addr -> if addr = word256FromInteger b then h else orig addr) t (b + 1) s
end
end
declare termination_argument put_return_values_aux = automatic
val put_return_values : memory -> list byte -> integer -> integer -> memory
let put_return_values orig lst b s =
if s <= 0 then orig else put_return_values_aux orig lst b (naturalFromInteger s)
(* The storage is modelled as a function. For example, the empty storage *)
(* is a function that returns zero for every index. Initially all accounts come with *)
(* the empty storage. *)
val empty_storage : storage
let empty_storage _ = 0
type network =
Frontier
| Homestead
| EIP150
| EIP158
| Metropolis
val network_of_block_number : integer -> network
let network_of_block_number bn =
if bn < 115 * 100 * 100 then Frontier
else if bn < 3 * 821 * 10 * 100 then Homestead
else if bn < 5 * 5 * 107 * 10 * 100 then EIP150
else EIP158
(* This needs to be updated when a new fork is planned. *)
val at_least_eip150 : network -> bool
let at_least_eip150 n =
match n with
Frontier -> false
| Homestead -> false
| EIP150 -> true
| EIP158 -> true
| Metropolis -> true
end
val before_homestead : network -> bool
let before_homestead n =
match n with
Frontier -> true
| Homestead -> false
| EIP150 -> false
| EIP158 -> false
| Metropolis -> false
end
(* This section lists the EVM instructions and their byte representations. *)
(* I also introduce an assertion instruction, whose byte representation is empty. *)
(* The assertion instruction is a statement about the state of the EVM at *)
(* that position of the program. *)
(****** What used to be Instructions.thy ******)
(* In Isabelle/HOL, it is expensive to define a single inductive type *)
(* that contains all instructions. When I do it, Isabelle/HOL automatically proves every *)
(* instruction is different from any other instruction, but this process has the computational *)
(* complexity of the square of the number of instructions. Instead, I define multiple *)
(* smaller inductive types and unify them at the end. *)
(* (delta, alpha) is the (consumption, production) on thae stack. *)
type stack_numbers = (int * int)
(* subsection "Bit Operations" *)
(* The following clause defines a type called \textit{bits\_inst}. *)
(* The type has five elements. It is automatically understood that nothing else *)
(* belongs to this type. It is also understood that every one of these five elements *)
(* is different from any of the other four. *)
(* Some instructions have \textit{inst\_} in front because names like AND, *)
(* OR and XOR are taken by the machine word library. *)
(* The instructions have different arities. They might consume some elements on the stack, *)
(* and produce some elements on the stack. However, the arity of the instructions are not specified *)
(* in this section. *)
type bits_inst =
| inst_AND (* bitwise AND *)
| inst_OR (* bitwise OR *)
| inst_XOR (* bitwise exclusive or *)
| inst_NOT (* bitwise negation *)
| BYTE (* taking one byte out of a word *)
(* These instructions are represented by the following bytes. *)
(* Most opcodes are a single byte. *)
val bits_inst_code : bits_inst -> byte
let bits_inst_code inst = match inst with
| inst_AND -> 0X16
| inst_OR -> 0X17
| inst_XOR -> 0X18
| inst_NOT -> 0X19
| BYTE -> 0X1a
end
val bits_stack_nums : bits_inst -> stack_numbers
let bits_stack_nums inst = match inst with
| inst_AND -> (2, 1)
| inst_OR -> (2, 1)
| inst_XOR -> (2, 1)
| inst_NOT -> (1, 1)
| BYTE -> (2, 1)
end
(* subsection "Signed Arithmetics" *)
(* More similar definitions follow. Below are instructions for signed arithmetics.
The operations common to signed and unsigned are listed further below in the
Unsigned Arithmetics section. *)
type sarith_inst =
| SDIV (* signed division *)
| SMOD (* signed modulo *)
| SGT (* signed greater-than *)
| SLT (* signed less-than *)
| SIGNEXTEND (* extend the size of a signed number *)
val sarith_inst_code : sarith_inst -> byte
let sarith_inst_code inst = match inst with
| SDIV -> 0X05
| SMOD -> 0X07
| SGT -> 0X13
| SLT -> 0X12
| SIGNEXTEND -> 0X0b
end
val sarith_inst_nums : sarith_inst -> stack_numbers
let sarith_inst_nums inst = match inst with
| SDIV -> (2, 1)
| SMOD -> (2, 1)
| SGT -> (2, 1)
| SLT -> (2, 1)
| SIGNEXTEND -> (2, 1)
end
(* subsection "Unsigned Arithmetics" *)
(* The names GT, EQ and LT are taken in the Cmp library *)
(* (which will be used for AVL trees). *)
type arith_inst =
| ADD (* addition *)
| MUL (* multiplication *)
| SUB (* subtraction *)
| DIV (* unsigned division *)
| MOD (* unsigned modulo *)
| ADDMOD (* addition under modulo *)
| MULMOD (* multiplication under modulo *)
| EXP (* exponentiation *)
| inst_GT (* unsigned greater-than *)
| inst_EQ (* equality *)
| inst_LT (* unsigned less-than *)
| ISZERO (* if zero, returns one *)
| SHA3 (* Keccak 256, dispite the name *)
val arith_inst_code : arith_inst -> byte
let arith_inst_code inst = match inst with
| ADD -> 0X01
| MUL -> 0X02
| SUB -> 0X03
| DIV -> 0X04
| MOD -> 0X06
| ADDMOD -> 0X08
| MULMOD -> 0X09
| EXP -> 0X0a
| inst_GT -> 0X11
| inst_LT -> 0X10
| inst_EQ -> 0X14
| ISZERO -> 0X15
| SHA3 -> 0X20
end
val arith_inst_numbers : arith_inst -> stack_numbers
let arith_inst_numbers inst = match inst with
| ADD -> (2, 1)
| MUL -> (2, 1)
| SUB -> (2, 1)
| DIV -> (2, 1)
| MOD -> (2, 1)
| ADDMOD -> (3, 1)
| MULMOD -> (3, 1)
| EXP -> (2, 1)
| inst_GT -> (2, 1)
| inst_LT -> (2, 1)
| inst_EQ -> (2, 1)
| ISZERO -> (1, 1)
| SHA3 -> (2, 1)
end
(* subsection "Informational Instructions" *)
type info_inst =
ADDRESS (* The address of the account currently running *)
| BALANCE (* The Eth balance of the specified account *)
| ORIGIN (* The address of the external account that started the transaction *)
| CALLER (* The immediate caller of this invocation *)
| CALLVALUE (* The Eth amount sent along this invocation *)
| CALLDATASIZE (* The number of bytes sent along this invocation *)
| CODESIZE (* The number of bytes in the code of the account currently running *)
| GASPRICE (* The current gas price *)
| EXTCODESIZE (* The size of a code of the specified account *)
| BLOCKHASH (* The block hash of a specified block among the recent blocks. *)
| COINBASE (* The address of the miner that validates the current block. *)
| TIMESTAMP (* The date and time of the block. *)
| NUMBER (* The block number *)
| DIFFICULTY (* The current difficulty *)
| GASLIMIT (* The current block gas limit *)
| GAS (* The remaining gas for the current execution. This changes after every instruction *)
(* is executed. *)
val info_inst_code : info_inst -> byte
let info_inst_code inst = match inst with
| ADDRESS -> 0X30
| BALANCE -> 0X31
| ORIGIN -> 0X32
| CALLVALUE -> 0X34
| CALLDATASIZE -> 0X36
| CALLER -> 0X33
| CODESIZE -> 0X38
| GASPRICE -> 0X3a
| EXTCODESIZE -> 0X3b
| BLOCKHASH -> 0X40
| COINBASE -> 0X41
| TIMESTAMP -> 0X42
| NUMBER -> 0X43
| DIFFICULTY -> 0X44
| GASLIMIT -> 0X45
| GAS -> 0X5a
end
val info_inst_numbers : info_inst -> stack_numbers
let info_inst_numbers inst = match inst with
| ADDRESS -> (0, 1)
| BALANCE -> (1, 1)
| ORIGIN -> (0, 1)
| CALLER -> (0, 1)
| CALLVALUE -> (0, 1)
| CALLDATASIZE -> (0, 1)
| CODESIZE -> (0, 1)
| GASPRICE -> (0, 1)
| EXTCODESIZE -> (1, 1)
| BLOCKHASH -> (1, 1)
| COINBASE -> (0, 1)
| TIMESTAMP -> (0, 1)
| NUMBER -> (0, 1)
| DIFFICULTY -> (0, 1)
| GASLIMIT -> (0, 1)
| GAS -> (0, 1)
end
(* subsection "Duplicating Stack Elements" *)
(* There are sixteen instructions for duplicating a stack element. These instructions take *)
(* a stack element and duplicate it on top of the stack. *)
type nibble = word4
type dup_inst = nibble
val dup_inst_code : dup_inst -> byte
let dup_inst_code m =
(word8FromInt (word4ToUInt m) + 0X80)
val dup_inst_numbers : dup_inst -> stack_numbers
let dup_inst_numbers m = (word4ToUInt m, word4ToUInt m + 1)
(* subsection {* Memory Operations *} *)
type memory_inst =
| MLOAD (* reading one machine word from the memory, beginning from the specified offset *)
| MSTORE (* writing one machine word to the memory *)
| MSTORE8 (* writing one byte to the memory *)
| CALLDATACOPY (* copying the caller's data to the memory *)
| CODECOPY (* copying a part of the currently running code to the memory *)
| EXTCODECOPY (* copying a part of the code of the specified account *)
| MSIZE (* the size of the currently used region of the memory. *)
val memory_inst_code : memory_inst -> byte
let memory_inst_code inst = match inst with
| MLOAD -> 0X51
| MSTORE -> 0X52
| MSTORE8 -> 0X53
| CALLDATACOPY -> 0X37
| CODECOPY -> 0X39
| EXTCODECOPY -> 0X3c
| MSIZE -> 0X59
end
val memory_inst_numbers : memory_inst -> stack_numbers
let memory_inst_numbers inst = match inst with
| MLOAD -> (1, 1)
| MSTORE -> (2, 0)
| MSTORE8 -> (2, 0)
| CALLDATACOPY -> (3, 0)
| CODECOPY -> (3, 0)
| EXTCODECOPY -> (4, 0)
| MSIZE -> (0, 1)
end
(* subsection {* Storage Operations *} *)
type storage_inst =
| SLOAD (* reading one word from the storage *)
| SSTORE (* writing one word to the storage *)
val storage_inst_code : storage_inst -> byte
let storage_inst_code inst = match inst with
| SLOAD -> 0X54
| SSTORE -> 0X55
end
val storage_inst_numbers : storage_inst -> stack_numbers
let storage_inst_numbers inst = match inst with
| SLOAD -> (1, 1)
| SSTORE -> (2, 0)
end
(* subsection {* Program-Counter Instructions *} *)
type pc_inst =
| JUMP (* jumping to the specified location in the code *)
| JUMPI (* jumping to the specified location in the code if a condition is met *)
| PC (* the current location in the code *)
| JUMPDEST (* a no-op instruction located to indicate jump destinations. *)
(* experimental subroutine instructions *)
type evm15_inst =
| JUMPIF of w8 * w8
| JUMPTO of w8 * w8
| JUMPSUB of w8 * w8
| BEGINSUB of w8 * w8 * w8 * w8
| RETURNSUB
| GETLOCAL of w8 * w8
| PUTLOCAL of w8 * w8
(* If a jump occurs to a location where @{term JUMPDEST} is not found, the execution fails. *)
(* 0xB0 JUMPTO
* 0xB1 JUMPIF
* 0XB2 JUMPSUB
* 0xB4 JUMPSUBV
* 0xB5 BEGINSUB
* 0xB6 BEGINDATA
* 0xB8 RETURNSUB
* 0xB9 PUTLOCAL
* 0xBA GETLOCAL*)
val pc_inst_code : pc_inst -> byte
let pc_inst_code inst = match inst with
| JUMP -> 0X56
| JUMPI -> 0X57
| PC -> 0X58
| JUMPDEST -> 0X5b
end
val evm15_inst_code : evm15_inst -> list byte
let evm15_inst_code inst = match inst with
| JUMPTO a1 a2 -> [0XB0; a1; a2]
| JUMPIF a1 a2 -> [0XB1; a1; a2]
| JUMPSUB a1 a2 -> [0XB2; a1; a2]
| BEGINSUB a1 a2 r1 r2 -> [0XB5; a1; a2; r1; r2]
| RETURNSUB -> [0XB8]
| GETLOCAL a1 a2 -> [0XB9; a1; a2]
| PUTLOCAL a1 a2 -> [0XB8; a1; a2]
end
val pc_inst_numbers : pc_inst -> stack_numbers
let pc_inst_numbers inst = match inst with
| JUMP -> (1, 0)
| JUMPI -> (2, 0)
| PC -> (0, 1)
| JUMPDEST -> (0, 0)
end
val evm15_inst_numbers : evm15_inst -> stack_numbers
let evm15_inst_numbers inst = match inst with
| JUMPTO _ _ -> (0, 0)
| JUMPIF _ _ -> (1, 0)
| JUMPSUB _ _ -> (0, 0)
| BEGINSUB _ _ _ _-> (0, 0)
| RETURNSUB -> (0, 0)
| GETLOCAL _ _ -> (0, 1)
| PUTLOCAL _ _ -> (1, 0)
end
(* subsection {* Stack Instructions *} *)
(* The PUSH instructions have longer byte representations than the other instructions *)
(* because they contain immediate values. *)
(* Here the immediate value is represented by a list of bytes. Depending on the *)
(* length of the list, the PUSH operation takes different opcodes. *)
type stack_inst =
| POP (* throwing away the topmost element of the stack *)
| PUSH_N of list byte (* pushing an element to the stack *)
| CALLDATALOAD (* pushing a word to the stack, taken from the caller's data *)
val stack_inst_code : stack_inst -> list byte
let stack_inst_code inst = match inst with
| POP -> [0X50]
| PUSH_N lst ->
if length lst < 1 then [0X60; 0X00] (* this case should not exist *)
else if length lst > 32 then [0X60; 0X00] (* this case should not exist *)
else [byteFromNat (length lst) + 0X5f] ++ lst
| CALLDATALOAD -> [0X35]
end
val stack_inst_numbers : stack_inst -> stack_numbers
let stack_inst_numbers inst = match inst with
| POP -> (1, 0)
| PUSH_N _ -> (0, 1)
| CALLDATALOAD -> (1, 1)
end
type swap_inst = nibble
val swap_inst_code : swap_inst -> byte
let swap_inst_code m =
(word8FromInt (word4ToUInt m) + 0X90)
val swap_inst_numbers : swap_inst -> stack_numbers
let swap_inst_numbers m =
(word4ToUInt m + 1, word4ToUInt m + 1)
(* subsection {* Logging Instructions *} *)
(* There are instructions for logging events with different number of arguments. *)
type log_inst =
| LOG0
| LOG1
| LOG2
| LOG3
| LOG4
val log_inst_code : log_inst -> byte
let log_inst_code inst = match inst with
| LOG0 -> 0Xa0
| LOG1 -> 0Xa1
| LOG2 -> 0Xa2
| LOG3 -> 0Xa3
| LOG4 -> 0Xa4
end
val log_inst_numbers : log_inst -> stack_numbers
let log_inst_numbers inst = match inst with
| LOG0 -> (2, 0)
| LOG1 -> (3, 0)
| LOG2 -> (4, 0)
| LOG3 -> (5, 0)
| LOG4 -> (6, 0)
end
(* subsection {* Miscellaneous Instructions *} *)
(* This section contains the instructions that alter the account-wise control flow. *)
(* In other words, they cause communication between accounts (or at least interaction with *)
(* other accounts' code). *)
type misc_inst =
| STOP (* finishing the execution normally, with the empty return data *)
| CREATE (* deploying some code in an account *)
| CALL (* calling (i.e. sending a message to) an account *)
| CALLCODE (* calling into this account, but the executed code can be some other account's *)
| DELEGATECALL (* calling into this account, the executed code can be some other account's
but the sent value and the sent data are unchanged. *)
| RETURN (* finishing the execution normally with data *)
| SUICIDE (* send all remaining Eth balance to the specified account, *)
(* finishing the execution normally, and flagging the current account for deletion *)
val misc_inst_code : misc_inst -> byte
let misc_inst_code inst = match inst with
| STOP -> 0X00
| CREATE -> 0Xf0
| CALL -> 0Xf1
| CALLCODE -> 0Xf2
| RETURN -> 0Xf3
| DELEGATECALL -> 0Xf4
| SUICIDE -> 0Xff
end
val misc_inst_numbers : misc_inst -> stack_numbers
let misc_inst_numbers inst = match inst with
| STOP -> (0, 0)
| CREATE -> (3, 1)
| CALL -> (7, 1)
| CALLCODE -> (7, 1)
| RETURN -> (2, 0)
| DELEGATECALL -> (6, 1)
| SUICIDE -> (1, 0)
end
type inst =
| Unknown of byte
| Bits of bits_inst
| Sarith of sarith_inst
| Arith of arith_inst
| Info of info_inst
| Dup of dup_inst
| Memory of memory_inst
| Storage of storage_inst
| Pc of pc_inst
| Stack of stack_inst
| Swap of swap_inst
| Log of log_inst
| Misc of misc_inst
| Evm15 of evm15_inst
(* subsection {* The Whole Instruction Set *} *)
(* The small inductive sets above are here combined into a single type. *)
let maybe_to_list m = match m with
| Nothing -> []
| Just s -> [s]
end
val inst_code : inst -> list byte
let inst_code inst = match inst with
| Unknown byte -> [byte]
| Bits b -> [bits_inst_code b]
| Sarith s -> [sarith_inst_code s]
| Arith a -> [arith_inst_code a]
| Info i -> [info_inst_code i]
| Dup d -> [dup_inst_code d]
| Memory m -> [memory_inst_code m]
| Storage s -> [storage_inst_code s]
| Pc p -> [pc_inst_code p]
| Stack s -> stack_inst_code s
| Swap s -> [swap_inst_code s]
| Log l -> [log_inst_code l]
| Misc m -> [misc_inst_code m]
| Evm15 e -> evm15_inst_code e
end
val inst_stack_numbers : inst -> stack_numbers
let inst_stack_numbers i = match i with
| Unknown _ -> (0, 0)
| Bits b -> bits_stack_nums b
| Sarith s -> sarith_inst_nums s
| Arith a -> arith_inst_numbers a
| Info i' -> info_inst_numbers i'
| Dup d -> dup_inst_numbers d
| Memory m -> memory_inst_numbers m
| Storage s -> storage_inst_numbers s
| Pc p -> pc_inst_numbers p
| Stack s -> stack_inst_numbers s
| Swap s -> swap_inst_numbers s
| Log l -> log_inst_numbers l
| Misc m -> misc_inst_numbers m
| Evm15 e -> evm15_inst_numbers e
end
(* The size of an opcode is useful for parsing a hex representation of an *)
(* EVM code. *)
val inst_size : inst -> int
let inst_size i = intFromNat (length (inst_code i))
(* section "Gas schedule" *)
val Gzero : integer
let Gzero = 0
val Gbase : integer
let Gbase = 2
val Gverylow : integer
let Gverylow = 3
val Glow : integer
let Glow = 5
val Gmid : integer
let Gmid = 8
val Ghigh : integer
let Ghigh = 10
val eip150_block : integer
let inline eip150_block = 2463*1000
val homestead_block : integer
let homestead_block = 1150*1000
val Gextcode : network -> integer
let Gextcode net =
if at_least_eip150 net then 700 else 20
val Gbalance : network -> integer
let Gbalance n =
if at_least_eip150 n then 400 else 20
val Gsload : network -> integer
let Gsload n =
if at_least_eip150 n then 200 else 50
val Gjumpdest : integer
let Gjumpdest = 1
val Gsset : integer
let Gsset = 20000
val Gsreset : integer
let Gsreset = 5000
val Rsclear : integer
let Rsclear = 15000
val Rsuicide : integer
let Rsuicide = 24000
val Gsuicide : network -> integer
let Gsuicide n =
if at_least_eip150 n then 5000 else 0
val Gcreate : integer
let Gcreate = 32000
val Gcodedeposit : integer
let Gcodedeposit = 200
val Gcall : network -> integer
let Gcall net =
if at_least_eip150 net then 700 else 40
val Gcallvalue : integer
let Gcallvalue = 9000
val Gcallstipend : integer
let Gcallstipend = 2300
val Gnewaccount : integer
let Gnewaccount = 25000
val Gexp : integer
let Gexp = 10
val Gexpbyte : network -> integer
let Gexpbyte net =
if at_least_eip150 net then 50 else 10
val Gmemory : integer
let Gmemory = 3
val Gtxcreate : integer
let Gtxcreate = 32000
val Gtxdatazero : integer
let Gtxdatazero = 4
val Gtxdatanonzero : integer
let Gtxdatanonzero = 68
val Gtransaction : integer
let Gtransaction = 21000
val Glog : integer
let Glog = 375
val Glogdata : integer
let Glogdata = 8
val Glogtopic : integer
let Glogtopic = 375
val Gsha3 : integer
let Gsha3 = 30
val Gsha3word : integer
let Gsha3word = 6
val Gcopy : integer
let Gcopy = 3
val Gblockhash : integer
let Gblockhash = 20
(* section "A Contract Centric View of the EVM" *)
(* subsection "The Interaction between the Contract and the Environment" *)
(* In this development, the EVM execution is seen as an interaction between a single contract *)
(* and the environment. The environment can call into the contract. The contract can reply by just *)
(* finishing or failing, but it can also call an account\footnote{This might be the same account as our *)
(* invocation, but still the deeper calls is part of the world.}. When our contract execution calls an account, *)
(* this is seen as an action towards the environment, because the environment then has to decide the *)
(* result of this call. The environment can say that the call finished successfully or exceptionally. *)
(* The environment can also say that the call resulted in a reentrancy. In other words, *)
(* the environment can call the contract again and change the storage and the balance of our contract. *)
(* The whole process is captured as a game between the environment and the contract. *)
(* subsubsection "The Environment's Moves" *)
(* The environment can call into our contract. *)
(* Then the environment provides our\footnote{ *)
(* The contract's behavior is controlled by a concrete code, but the environment's behavior is unrestricted.*)
(* So when I get emotional I call the contract ``our'' contract. *)
(* } contract *)
(* with the following information. *)
type call_env = <|
callenv_gaslimit : w256; (* the current invocation's gas limit *)
callenv_value : w256; (* the amount of Eth sent along*)
callenv_data : list byte; (* the data sent along *)
callenv_caller : address; (* the caller's address *)
callenv_timestamp : w256; (* the timestamp of the current block *)
callenv_blocknum : w256; (* the block number of the current block *)
callenv_balance : address -> w256; (* the balances of all accounts. *)
|>
(* After our contract calls accounts, the environment can make those accounts *)
(* return into our contracts. The return value is not under control of our current *)
(* contract, so it is the environment's move. In that case, the environment provides the *)
(* following information. *)
type return_result = <|
return_data : list byte; (* the returned data *)
return_balance : address -> w256 (* the balance of all accounts at the moment of the return*)
|>
(* Even our account's balance (and its storage) might have changed at this moment. *)
(* @{typ return_result} type is also used when our contract returns, as we will see. *)
(* With these definitions now we can define the environment's actions. In addition to call and return, *)
(* there is another clause for failing back to the account. This happens when our contract calls *)
(* an account but the called account fails. *)
(* When our contract deploys a smart contract, our contract should provide the following *)
(* information. *)
type environment_action =
| EnvironmentCall of call_env (* the environment calls into the account *)
| EnvironmentRet of return_result (* the environment returns back to the account *)
| EnvironmentFail (* the environment fails back to the account. *)
(* subsubsection "The Contract's Moves" *)
(* After being invoked, the contract can respond by calling an account, creating (or deploying)
a smart contract, destroying itself, returning, or failing. When the contract calls an account,
the contract provides the following information.*)
(* When our contract deploys a smart contract, our contract should provide the following
information. *)
(* The contract's moves are summarized as follows. *)
type call_arguments = <|
callarg_gas : w256; (* The portion of the remaining gas that the callee is allowed to use *)
callarg_code : address; (* The code that executes during the call *)
callarg_recipient : address; (* The recipient of the call, whose balance and the storage are modified. *)
callarg_value : w256; (* The amount of Eth sent along *)
callarg_data : list byte; (* The data sent along *)
callarg_output_begin : w256; (* The beginning of the memory region where the output data should be written. *)
callarg_output_size : w256; (* The size of the memory regions where the output data should be written. *)
|>
type create_arguments = <|
createarg_value : w256; (* The value sent to the account *)
createarg_code : list byte; (* The code that deploys the runtime code. *)
|>
type failure_reason =
| OutOfGas
| TooLongStack
| TooShortStack
| InvalidJumpDestination
| ShouldNotHappen
type contract_action =
| ContractCall of call_arguments (* calling an account *)
| ContractDelegateCall of call_arguments (* calling some code to run on behalf of the contract *)
| ContractCreate of create_arguments (* deploying a smart contract *)
| ContractFail of list failure_reason (* failing back to the caller *)
| ContractSuicide of address (* destroying itself and returning back to the caller *)
| ContractReturn of list byte (* normally returning back to the caller *)
(* subsection "Program Representation" *)
(* For performance reasons, the instructions can be stored in a binary tree that allows *)
(* looking up instructions from the program counters. *)
type program = <|
program_content : integer -> maybe inst; (* a way to look up instructions from positions *)
program_length : integer; (* the length of the program in bytes *)
|>
(* The empty program is easy to define. *)
val empty_program : program
let empty_program = <|
program_content = (fun _ -> Nothing);
program_length = 0
|>
(* subsection "Translating an Instruction List into a Program" *)
(* subsubsection {* Translating a list of instructions into a program *} *)
(* The results of the above translations are packed together in a record. *)
(* For efficiency reasons, the program content is going to be packed as *)
(* an AVL tree, but this particular encoding is not part of the Lem definition. *)
(* So such encoders are parametrised here. *)
val program_of_lst : list inst -> (list inst -> (integer -> maybe inst)) -> program
let program_of_lst lst program_content_formatter = <|
program_content = program_content_formatter lst;
program_length = integerFromNat (length lst);
|>
(* subsection {* Program as a Byte Sequence *} *)
(* For CODECOPY instruction, the program must be seen as a byte-indexed read-only memory. *)
(* Such a memory is here implemented by a lookup on an AVL tree.*)
val program_as_natural_map : program -> natural -> byte
let program_as_natural_map p idx =
match p.program_content (integerFromNatural idx) with
| Nothing -> 0
| Just inst ->
match List.index (inst_code inst) 0 with
| Nothing -> 0
| Just a -> a
end
end
(* Execution Environments *)
(* I model an instruction as a function that takes environments and modifies some parts of them. *)