-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathValidation.lean
More file actions
1915 lines (1835 loc) · 94.5 KB
/
Copy pathValidation.lean
File metadata and controls
1915 lines (1835 loc) · 94.5 KB
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
/-
Compiler.CompilationModel.Validation: Validation walkers and shape checks
-/
import Compiler.CompilationModel.Types
import Compiler.CompilationModel.AbiHelpers
import Compiler.CompilationModel.AbiTypeLayout
import Compiler.CompilationModel.DynamicData
import Compiler.CompilationModel.EcmAxiomCollection
import Compiler.CompilationModel.EventAbiHelpers
import Compiler.CompilationModel.InternalNaming
import Compiler.CompilationModel.IssueRefs
import Compiler.CompilationModel.LayoutValidation
import Compiler.CompilationModel.LogicalPurity
import Compiler.CompilationModel.MappingWrites
import Compiler.CompilationModel.ScopeValidation
import Compiler.CompilationModel.TrustSurface
import Compiler.CompilationModel.UsageAnalysis
import Compiler.CompilationModel.ValidationCalls
import Compiler.CompilationModel.ValidationEvents
import Compiler.CompilationModel.ValidationHelpers
import Compiler.CompilationModel.ValidationInterop
import Compiler.CompilationModel.SelectorInteropHelpers
import Compiler.CompilationModel.ExpressionCompile
namespace Compiler.CompilationModel
open Compiler
open Compiler.Yul
private def firstDuplicateString : List String → Option String
| [] => none
| name :: rest =>
if rest.contains name then some name else firstDuplicateString rest
private def adtPayloadParamNames (params : List Param) : List String :=
params.flatMap fun param =>
match param.ty with
| ParamType.adt _ maxFields =>
(List.range maxFields).map fun idx => s!"{param.name}_f{idx}"
| _ => []
private def validateAdtPayloadParamNameCollisions
(context : String) (params : List Param) (body : List Stmt) : Except String Unit := do
let generated := adtPayloadParamNames params
match firstDuplicateString generated with
| some name =>
throw s!"Compilation error: {context} has ADT parameters whose generated payload local '{name}' collides. Rename the ADT parameters so generated '<param>_f<i>' locals are unique."
| none => pure ()
let userNames := params.map (·.name) ++ collectStmtListBindNames body
match generated.find? (fun name => userNames.contains name) with
| some name =>
throw s!"Compilation error: {context} reserves generated ADT payload local '{name}'. Rename the parameter or local binding that conflicts with generated '<param>_f<i>' locals."
| none => pure ()
def isStorageWordArrayParam : ParamType → Bool
| ty => isWordArrayParam ty
mutual
def validateStmtParamReferences (fnName : String) (params : List Param) :
Stmt → Except String Unit
| Stmt.returnArray name =>
match findParamType params name with
| some ty =>
if isWordArrayParam ty then
pure ()
else
throw s!"Compilation error: function '{fnName}' returnArray '{name}' requires an array parameter with single-word static elements, got {repr ty}"
| none =>
pure ()
| Stmt.returnBytes name =>
match findParamType params name with
| some ParamType.bytes | some ParamType.string => pure ()
| some ty =>
throw s!"Compilation error: function '{fnName}' returnBytes '{name}' requires bytes/string parameter, got {repr ty}"
| none =>
throw s!"Compilation error: function '{fnName}' returnBytes references unknown parameter '{name}'"
| Stmt.returnStorageWords name =>
match findParamType params name with
| some ty =>
if isStorageWordArrayParam ty then
pure ()
else
throw s!"Compilation error: function '{fnName}' returnStorageWords '{name}' requires an array parameter with single-word static elements, got {repr ty}"
| none =>
throw s!"Compilation error: function '{fnName}' returnStorageWords references unknown parameter '{name}'"
| Stmt.ite _ thenBranch elseBranch => do
validateStmtParamReferencesInList fnName params thenBranch
validateStmtParamReferencesInList fnName params elseBranch
| Stmt.forEach _ _ body => do
validateStmtParamReferencesInList fnName params body
| Stmt.unsafeBlock _ body => do
validateStmtParamReferencesInList fnName params body
| Stmt.matchAdt _ _ branches =>
validateStmtParamReferencesInBranches fnName params branches
| _ => pure ()
termination_by s => sizeOf s
decreasing_by all_goals simp_wf; all_goals omega
def validateStmtParamReferencesInList (fnName : String) (params : List Param) :
List Stmt → Except String Unit
| [] => pure ()
| s :: ss => do
validateStmtParamReferences fnName params s
validateStmtParamReferencesInList fnName params ss
termination_by ss => sizeOf ss
decreasing_by all_goals simp_wf; all_goals omega
def validateStmtParamReferencesInBranches (fnName : String) (params : List Param) :
List (String × List String × List Stmt) → Except String Unit
| [] => pure ()
| (_, _, body) :: rest => do
validateStmtParamReferencesInList fnName params body
validateStmtParamReferencesInBranches fnName params rest
termination_by bs => sizeOf bs
decreasing_by all_goals simp_wf; all_goals omega
end
mutual
def validateReturnShapesInStmt (fnName : String) (params : List Param)
(expectedReturns : List ParamType) (isInternal : Bool) : Stmt → Except String Unit
| Stmt.return _ =>
if isInternal then
match expectedReturns with
| [_] => pure ()
| [] =>
throw s!"Compilation error: function '{fnName}' uses Stmt.return but declares no return values"
| _ =>
throw s!"Compilation error: function '{fnName}' uses Stmt.return but declares multiple return values; use Stmt.returnValues"
else if expectedReturns.length > 1 then
throw s!"Compilation error: function '{fnName}' uses Stmt.return but declares multiple return values; use Stmt.returnValues"
else
pure ()
| Stmt.returnValues values =>
if expectedReturns.isEmpty then
throw s!"Compilation error: function '{fnName}' uses Stmt.returnValues but declares no return values"
else if values.length != expectedReturns.length then
throw s!"Compilation error: function '{fnName}' returnValues count mismatch: expected {expectedReturns.length}, got {values.length}"
else
pure ()
| Stmt.returnArray name =>
match findParamType params name with
| some ty =>
if !isWordArrayParam ty then
throw s!"Compilation error: function '{fnName}' uses Stmt.returnArray with parameter '{name}' of type {repr ty}; only arrays with single-word static elements are currently supported"
else if expectedReturns == [ty] then
pure ()
else
throw s!"Compilation error: function '{fnName}' uses Stmt.returnArray to return parameter '{name}' of type {repr ty}, but declared returns are {repr expectedReturns}"
| none =>
match expectedReturns with
| [ty] =>
if isWordArrayParam ty then
pure ()
else
throw s!"Compilation error: function '{fnName}' uses Stmt.returnArray with memory array '{name}', but declared return type {repr ty} is not a supported word array"
| _ =>
throw s!"Compilation error: function '{fnName}' returnArray references unknown parameter '{name}'"
| Stmt.returnBytes name =>
if isInternal then
throw s!"Compilation error: internal function '{fnName}' cannot use Stmt.returnBytes; only static returns via Stmt.return/Stmt.returnValues are supported ({issue625Ref})."
else if expectedReturns == [ParamType.bytes] || expectedReturns == [ParamType.string] then
match findParamType params name with
| some ty =>
if expectedReturns == [ty] then
pure ()
else
throw s!"Compilation error: function '{fnName}' uses Stmt.returnBytes to return parameter '{name}' of type {repr ty}, but declared returns are {repr expectedReturns}"
| none =>
throw s!"Compilation error: function '{fnName}' returnBytes references unknown parameter '{name}'"
else
throw s!"Compilation error: function '{fnName}' uses Stmt.returnBytes but declared returns are {repr expectedReturns}"
| Stmt.returnStorageWords _ =>
if isInternal then
throw s!"Compilation error: internal function '{fnName}' cannot use Stmt.returnStorageWords; only static returns via Stmt.return/Stmt.returnValues are supported ({issue625Ref})."
else if expectedReturns == [ParamType.array ParamType.uint256] then
pure ()
else
throw s!"Compilation error: function '{fnName}' uses Stmt.returnStorageWords but declared returns are {repr expectedReturns}"
| Stmt.ite _ thenBranch elseBranch => do
validateReturnShapesInStmtList fnName params expectedReturns isInternal thenBranch
validateReturnShapesInStmtList fnName params expectedReturns isInternal elseBranch
| Stmt.forEach _ _ body =>
validateReturnShapesInStmtList fnName params expectedReturns isInternal body
| Stmt.unsafeBlock _ body =>
validateReturnShapesInStmtList fnName params expectedReturns isInternal body
| Stmt.matchAdt _ _ branches =>
validateReturnShapesInBranches fnName params expectedReturns isInternal branches
| _ => pure ()
termination_by s => sizeOf s
decreasing_by all_goals simp_wf; all_goals omega
def validateReturnShapesInStmtList (fnName : String)
(params : List Param) (expectedReturns : List ParamType) (isInternal : Bool) : List Stmt → Except String Unit
| [] => pure ()
| s :: ss => do
validateReturnShapesInStmt fnName params expectedReturns isInternal s
validateReturnShapesInStmtList fnName params expectedReturns isInternal ss
termination_by ss => sizeOf ss
decreasing_by all_goals simp_wf; all_goals omega
def validateReturnShapesInBranches (fnName : String)
(params : List Param) (expectedReturns : List ParamType) (isInternal : Bool) :
List (String × List String × List Stmt) → Except String Unit
| [] => pure ()
| (_, _, body) :: rest => do
validateReturnShapesInStmtList fnName params expectedReturns isInternal body
validateReturnShapesInBranches fnName params expectedReturns isInternal rest
termination_by bs => sizeOf bs
decreasing_by all_goals simp_wf; all_goals omega
end
mutual
private def stmtListAlwaysReturnsOrReverts : List Stmt → Bool
| [] => false
| stmt :: rest =>
if stmtAlwaysReturnsOrReverts stmt then
true
else
stmtListAlwaysReturnsOrReverts rest
termination_by ss => sizeOf ss
decreasing_by all_goals simp_wf; all_goals omega
private def stmtAlwaysReturnsOrReverts : Stmt → Bool
| Stmt.return _ | Stmt.returnValues _ | Stmt.returnArray _
| Stmt.returnBytes _ | Stmt.returnStorageWords _
| Stmt.revertError _ _ | Stmt.revertReturndata =>
true
| Stmt.ite _ thenBranch elseBranch =>
stmtListAlwaysReturnsOrReverts thenBranch && stmtListAlwaysReturnsOrReverts elseBranch
| Stmt.unsafeBlock _ body =>
stmtListAlwaysReturnsOrReverts body
| Stmt.matchAdt _ _ branches =>
matchBranchesAllReturnOrRevert branches
| _ =>
false
termination_by s => sizeOf s
decreasing_by all_goals simp_wf; all_goals omega
private def matchBranchesAllReturnOrRevert : List (String × List String × List Stmt) → Bool
| [] => true
| (_, _, body) :: rest =>
stmtListAlwaysReturnsOrReverts body && matchBranchesAllReturnOrRevert rest
termination_by bs => sizeOf bs
decreasing_by all_goals simp_wf; all_goals omega
end
def exprReadsStateOrEnv : Expr → Bool
| Expr.literal _ => false
| Expr.param _ => false
| Expr.constructorArg _ => false
| Expr.storage _ | Expr.storageAddr _ => true
| Expr.mapping _ _ | Expr.mappingWord _ _ _ | Expr.mappingPackedWord _ _ _ _
| Expr.mapping2 _ _ _ | Expr.mapping2Word _ _ _ _
| Expr.mappingUint _ _
| Expr.mappingChain _ _
| Expr.structMember _ _ _ | Expr.structMember2 _ _ _ _ => true
| Expr.caller => true
| Expr.contractAddress => true
| Expr.chainid => true
| Expr.extcodesize _ => true
| Expr.msgValue => true
| Expr.selfBalance => true
| Expr.blockTimestamp => true
| Expr.blockNumber => true
| Expr.blobbasefee => true
| Expr.calldatasize => true
| Expr.calldataload _ => true
| Expr.mload offset => exprReadsStateOrEnv offset
| Expr.tload _ => true
| Expr.keccak256 offset size => exprReadsStateOrEnv offset || exprReadsStateOrEnv size
| Expr.call _ _ _ _ _ _ _ | Expr.staticcall _ _ _ _ _ _
| Expr.delegatecall _ _ _ _ _ _ => true
| Expr.returndataSize => true
| Expr.returndataOptionalBoolAt _ => true
| Expr.dynamicBytesEq _ _ => false
| Expr.localVar _ => false
| Expr.externalCall name args =>
if name == builtinExpName then exprListReadsStateOrEnv args else true
| Expr.internalCall _ _ => true
| Expr.arrayLength _ | Expr.memoryArrayLength _ => false
| Expr.paramDynamicHeadWord _ _
| Expr.paramDynamicStaticComposite _ _
| Expr.paramDynamicMemberLength _ _
| Expr.paramDynamicMemberDataOffset _ _ => false
| Expr.paramDynamicMemberElement _ _ innerIndex => exprReadsStateOrEnv innerIndex
| Expr.storageArrayLength _ => true
| Expr.storageArrayElement _ index => true || exprReadsStateOrEnv index
| Expr.arrayElement _ index | Expr.memoryArrayElement _ index
| Expr.arrayElementWord _ index _ _ | Expr.arrayElementDynamicWord _ index _
| Expr.arrayElementDynamicDataOffset _ index
| Expr.arrayElementDynamicMemberDataOffset _ index _
| Expr.arrayElementDynamicMemberLength _ index _ => exprReadsStateOrEnv index
| Expr.arrayElementDynamicMemberElement _ index _ innerIndex =>
exprReadsStateOrEnv index || exprReadsStateOrEnv innerIndex
| Expr.add a b | Expr.sub a b | Expr.mul a b | Expr.div a b | Expr.sdiv a b
| Expr.mod a b | Expr.smod a b |
Expr.bitAnd a b | Expr.bitOr a b | Expr.bitXor a b | Expr.shl a b | Expr.shr a b
| Expr.sar a b | Expr.signextend a b | Expr.byte a b |
Expr.eq a b | Expr.ge a b | Expr.gt a b | Expr.sgt a b | Expr.lt a b | Expr.slt a b | Expr.le a b |
Expr.logicalAnd a b | Expr.logicalOr a b |
Expr.wMulDown a b | Expr.wDivUp a b | Expr.min a b | Expr.max a b |
Expr.ceilDiv a b =>
exprReadsStateOrEnv a || exprReadsStateOrEnv b
| Expr.intrinsic _ _ _ args => exprListReadsStateOrEnv args
| Expr.forkIfAtLeast _ thenExpr elseExpr =>
exprReadsStateOrEnv thenExpr || exprReadsStateOrEnv elseExpr
| Expr.mulDivDown a b c | Expr.mulDivUp a b c
| Expr.mulDiv512Down a b c | Expr.mulDiv512Up a b c =>
exprReadsStateOrEnv a || exprReadsStateOrEnv b || exprReadsStateOrEnv c
| Expr.bitNot a | Expr.logicalNot a =>
exprReadsStateOrEnv a
| Expr.ite cond thenVal elseVal =>
exprReadsStateOrEnv cond || exprReadsStateOrEnv thenVal || exprReadsStateOrEnv elseVal
| Expr.adtConstruct _ _ args => exprListReadsStateOrEnv args
| Expr.adtTag _ _ | Expr.adtField _ _ _ _ _ => true
where
exprListReadsStateOrEnv : List Expr → Bool
| [] => false
| e :: es => exprReadsStateOrEnv e || exprListReadsStateOrEnv es
mutual
def exprWritesState : Expr → Bool
| Expr.add a b | Expr.sub a b | Expr.mul a b | Expr.div a b | Expr.sdiv a b
| Expr.mod a b | Expr.smod a b |
Expr.bitAnd a b | Expr.bitOr a b | Expr.bitXor a b | Expr.shl a b | Expr.shr a b
| Expr.sar a b | Expr.signextend a b | Expr.byte a b |
Expr.eq a b | Expr.ge a b | Expr.gt a b | Expr.sgt a b | Expr.lt a b | Expr.slt a b | Expr.le a b |
Expr.logicalAnd a b | Expr.logicalOr a b |
Expr.wMulDown a b | Expr.wDivUp a b | Expr.min a b | Expr.max a b |
Expr.ceilDiv a b =>
exprWritesState a || exprWritesState b
| Expr.mulDivDown a b c | Expr.mulDivUp a b c
| Expr.mulDiv512Down a b c | Expr.mulDiv512Up a b c =>
exprWritesState a || exprWritesState b || exprWritesState c
| Expr.bitNot a | Expr.logicalNot a =>
exprWritesState a
| Expr.ite cond thenVal elseVal =>
exprWritesState cond || exprWritesState thenVal || exprWritesState elseVal
| Expr.mapping _ key | Expr.mappingWord _ key _ | Expr.mappingPackedWord _ key _ _ | Expr.mappingUint _ key
| Expr.structMember _ key _ =>
exprWritesState key
| Expr.mappingChain _ keys =>
exprListWritesState keys
| Expr.mapping2 _ key1 key2 | Expr.mapping2Word _ key1 key2 _
| Expr.structMember2 _ key1 key2 _ =>
exprWritesState key1 || exprWritesState key2
| Expr.call _ _ _ _ _ _ _ => true
| Expr.staticcall gas target inOffset inSize outOffset outSize =>
exprWritesState gas || exprWritesState target ||
exprWritesState inOffset || exprWritesState inSize ||
exprWritesState outOffset || exprWritesState outSize
| Expr.delegatecall _ _ _ _ _ _ => true
| Expr.mload offset | Expr.tload offset =>
exprWritesState offset
| Expr.calldataload offset =>
exprWritesState offset
| Expr.keccak256 offset size =>
exprWritesState offset || exprWritesState size
| Expr.returndataOptionalBoolAt outOffset =>
exprWritesState outOffset
| Expr.dynamicBytesEq _ _ =>
false
| Expr.externalCall name args =>
if name == builtinExpName then exprListWritesState args else true
| Expr.intrinsic _ _ _ args => exprListWritesState args
| Expr.forkIfAtLeast _ thenExpr elseExpr =>
exprWritesState thenExpr || exprWritesState elseExpr
| Expr.internalCall _ _ => true
| Expr.adtConstruct _ _ args => exprListWritesState args
| Expr.extcodesize addr =>
exprWritesState addr
| Expr.storageArrayLength _ =>
false
| Expr.storageArrayElement _ index =>
exprWritesState index
| Expr.arrayElement _ index | Expr.memoryArrayElement _ index
| Expr.arrayElementWord _ index _ _ | Expr.arrayElementDynamicWord _ index _
| Expr.arrayElementDynamicDataOffset _ index
| Expr.arrayElementDynamicMemberDataOffset _ index _
| Expr.arrayElementDynamicMemberLength _ index _ =>
exprWritesState index
| Expr.arrayElementDynamicMemberElement _ index _ innerIndex =>
exprWritesState index || exprWritesState innerIndex
-- Pure leaves: no state writes. Listed explicitly to avoid `_mutual.eq_def`
-- heartbeat-ceiling failures when new constructors land.
| Expr.literal _ | Expr.param _ | Expr.constructorArg _ | Expr.storage _ | Expr.storageAddr _
| Expr.caller | Expr.contractAddress | Expr.chainid | Expr.msgValue | Expr.selfBalance
| Expr.blockTimestamp | Expr.blockNumber | Expr.blobbasefee
| Expr.calldatasize | Expr.returndataSize | Expr.localVar _ | Expr.arrayLength _
| Expr.memoryArrayLength _
| Expr.paramDynamicHeadWord _ _
| Expr.paramDynamicStaticComposite _ _
| Expr.paramDynamicMemberLength _ _
| Expr.paramDynamicMemberDataOffset _ _
| Expr.adtTag _ _ | Expr.adtField _ _ _ _ _ =>
false
| Expr.paramDynamicMemberElement _ _ innerIndex =>
exprWritesState innerIndex
termination_by e => sizeOf e
decreasing_by all_goals simp_wf; all_goals omega
def exprListWritesState : List Expr → Bool
| [] => false
| e :: es => exprWritesState e || exprListWritesState es
termination_by es => sizeOf es
decreasing_by all_goals simp_wf; all_goals omega
def stmtWritesState : Stmt → Bool
| Stmt.letVar _ value | Stmt.assignVar _ value =>
exprWritesState value
| Stmt.setStorage _ _ | Stmt.setStorageAddr _ _ | Stmt.setStorageWord _ _ _
| Stmt.storageArrayPush _ _ | Stmt.storageArrayPop _ | Stmt.setStorageArrayElement _ _ _
| Stmt.setMapping _ _ _ | Stmt.setMappingWord _ _ _ _ | Stmt.setMappingPackedWord _ _ _ _ _ | Stmt.setMappingUint _ _ _
| Stmt.setMappingChain _ _ _
| Stmt.setMapping2 _ _ _ _ | Stmt.setMapping2Word _ _ _ _ _
| Stmt.setStructMember _ _ _ _ | Stmt.setStructMember2 _ _ _ _ _ => true
| Stmt.require cond _ =>
exprWritesState cond
| Stmt.requireError cond _ args =>
exprWritesState cond || exprListWritesState args
| Stmt.revertError _ args =>
exprListWritesState args
| Stmt.return value =>
exprWritesState value
| Stmt.returnValues values =>
exprListWritesState values
| Stmt.returnArray _ =>
false
| Stmt.returnBytes _ =>
false
| Stmt.returnStorageWords _ =>
false
| Stmt.mstore offset value =>
exprWritesState offset || exprWritesState value
| Stmt.tstore _ _ =>
true
| Stmt.calldatacopy destOffset sourceOffset size =>
exprWritesState destOffset || exprWritesState sourceOffset || exprWritesState size
| Stmt.returndataCopy destOffset sourceOffset size =>
exprWritesState destOffset || exprWritesState sourceOffset || exprWritesState size
| Stmt.revertReturndata =>
false
| Stmt.stop =>
false
| Stmt.ite cond thenBranch elseBranch =>
exprWritesState cond || stmtListWritesState thenBranch || stmtListWritesState elseBranch
| Stmt.forEach _ count body =>
exprWritesState count || stmtListWritesState body
| Stmt.unsafeBlock _ body =>
stmtListWritesState body
| Stmt.emit _ _ | Stmt.rawLog _ _ _
| Stmt.internalCall _ _ | Stmt.internalCallAssign _ _ _
| Stmt.externalCallBind _ _ _ | Stmt.tryExternalCallBind _ _ _ _ => true
| Stmt.ecm mod args =>
mod.writesState || exprListWritesState args
| Stmt.matchAdt _ scrutinee branches =>
exprWritesState scrutinee ||
matchBranchesWriteState branches
termination_by s => sizeOf s
decreasing_by all_goals simp_wf; all_goals omega
def stmtListWritesState : List Stmt → Bool
| [] => false
| s :: ss => stmtWritesState s || stmtListWritesState ss
termination_by ss => sizeOf ss
decreasing_by all_goals simp_wf; all_goals omega
def matchBranchesWriteState : List (String × List String × List Stmt) → Bool
| [] => false
| (_, _, body) :: rest =>
stmtListWritesState body || matchBranchesWriteState rest
termination_by bs => sizeOf bs
decreasing_by all_goals simp_wf; all_goals omega
end
mutual
/-- Collect the set of storage field names written by a statement.
Returns a list of field name strings found in `setStorage`, `setStorageAddr`,
`setMapping*`, `storageArray*`, and `setStructMember*` constructors.
Used by `modifies(...)` validation (#1729, Axis 3 Step 1b). -/
def stmtWrittenFields : Stmt → List String
| Stmt.setStorage field _ | Stmt.setStorageAddr field _ | Stmt.setStorageWord field _ _
| Stmt.storageArrayPush field _ | Stmt.storageArrayPop field | Stmt.setStorageArrayElement field _ _
| Stmt.setMapping field _ _ | Stmt.setMappingWord field _ _ _ | Stmt.setMappingPackedWord field _ _ _ _
| Stmt.setMappingUint field _ _
| Stmt.setMappingChain field _ _
| Stmt.setMapping2 field _ _ _ | Stmt.setMapping2Word field _ _ _ _
| Stmt.setStructMember field _ _ _ | Stmt.setStructMember2 field _ _ _ _ => [field]
| Stmt.ite _ thenBranch elseBranch =>
stmtListWrittenFields thenBranch ++ stmtListWrittenFields elseBranch
| Stmt.forEach _ _ body =>
stmtListWrittenFields body
| Stmt.unsafeBlock _ body =>
stmtListWrittenFields body
| Stmt.matchAdt _ _ branches =>
matchBranchesWrittenFields branches
| _ => []
termination_by s => sizeOf s
decreasing_by all_goals simp_wf; all_goals omega
def stmtListWrittenFields : List Stmt → List String
| [] => []
| s :: ss => stmtWrittenFields s ++ stmtListWrittenFields ss
termination_by ss => sizeOf ss
decreasing_by all_goals simp_wf; all_goals omega
def matchBranchesWrittenFields : List (String × List String × List Stmt) → List String
| [] => []
| (_, _, body) :: rest =>
stmtListWrittenFields body ++ matchBranchesWrittenFields rest
termination_by bs => sizeOf bs
decreasing_by all_goals simp_wf; all_goals omega
end
mutual
/-- Detect expression-position internal helper calls whose callee write set is
not visible to single-function `modifies(...)` validation. -/
def exprHasUntrackableWrites : Expr → Bool
| Expr.internalCall _ _ => true
| Expr.add a b | Expr.sub a b | Expr.mul a b | Expr.div a b | Expr.sdiv a b
| Expr.mod a b | Expr.smod a b
| Expr.bitAnd a b | Expr.bitOr a b | Expr.bitXor a b | Expr.shl a b | Expr.shr a b | Expr.sar a b
| Expr.byte a b =>
exprHasUntrackableWrites a || exprHasUntrackableWrites b
| Expr.intrinsic _ _ _ args => exprListHasUntrackableWrites args
| Expr.forkIfAtLeast _ thenExpr elseExpr =>
exprHasUntrackableWrites thenExpr || exprHasUntrackableWrites elseExpr
| Expr.lt a b | Expr.gt a b | Expr.slt a b | Expr.sgt a b | Expr.eq a b
| Expr.ge a b | Expr.le a b | Expr.signextend a b
| Expr.logicalAnd a b | Expr.logicalOr a b
| Expr.wMulDown a b | Expr.wDivUp a b | Expr.min a b | Expr.max a b
| Expr.ceilDiv a b =>
exprHasUntrackableWrites a || exprHasUntrackableWrites b
| Expr.mulDivDown a b c | Expr.mulDivUp a b c
| Expr.mulDiv512Down a b c | Expr.mulDiv512Up a b c =>
exprHasUntrackableWrites a || exprHasUntrackableWrites b || exprHasUntrackableWrites c
| Expr.bitNot a | Expr.logicalNot a | Expr.extcodesize a =>
exprHasUntrackableWrites a
| Expr.ite cond thenVal elseVal =>
exprHasUntrackableWrites cond || exprHasUntrackableWrites thenVal || exprHasUntrackableWrites elseVal
| Expr.mapping _ key | Expr.mappingWord _ key _ | Expr.mappingPackedWord _ key _ _ | Expr.mappingUint _ key
| Expr.structMember _ key _ | Expr.arrayElement _ key | Expr.memoryArrayElement _ key
| Expr.arrayElementWord _ key _ _
| Expr.arrayElementDynamicWord _ key _
| Expr.arrayElementDynamicDataOffset _ key
| Expr.arrayElementDynamicMemberDataOffset _ key _
| Expr.arrayElementDynamicMemberLength _ key _
| Expr.storageArrayElement _ key =>
exprHasUntrackableWrites key
| Expr.arrayElementDynamicMemberElement _ key _ innerKey =>
exprHasUntrackableWrites key || exprHasUntrackableWrites innerKey
| Expr.mappingChain _ keys =>
exprListHasUntrackableWrites keys
| Expr.mapping2 _ key1 key2 | Expr.mapping2Word _ key1 key2 _
| Expr.structMember2 _ key1 key2 _ =>
exprHasUntrackableWrites key1 || exprHasUntrackableWrites key2
| Expr.mload offset | Expr.tload offset | Expr.calldataload offset
| Expr.returndataOptionalBoolAt offset =>
exprHasUntrackableWrites offset
| Expr.keccak256 offset size =>
exprHasUntrackableWrites offset || exprHasUntrackableWrites size
| Expr.adtConstruct _ _ args =>
exprListHasUntrackableWrites args
-- Pure leaves: cannot write state. Listed explicitly to avoid
-- `_mutual.eq_def` heartbeat-ceiling failures when new constructors land.
| Expr.literal _ | Expr.param _ | Expr.constructorArg _ | Expr.storage _ | Expr.storageAddr _
| Expr.caller | Expr.contractAddress | Expr.chainid | Expr.msgValue | Expr.selfBalance
| Expr.blockTimestamp | Expr.blockNumber | Expr.blobbasefee
| Expr.calldatasize | Expr.returndataSize | Expr.localVar _ | Expr.arrayLength _
| Expr.memoryArrayLength _
| Expr.storageArrayLength _
| Expr.paramDynamicHeadWord _ _
| Expr.paramDynamicStaticComposite _ _
| Expr.paramDynamicMemberLength _ _
| Expr.paramDynamicMemberDataOffset _ _
| Expr.dynamicBytesEq _ _
| Expr.externalCall _ _ | Expr.call _ _ _ _ _ _ _
| Expr.staticcall _ _ _ _ _ _ | Expr.delegatecall _ _ _ _ _ _
| Expr.adtTag _ _ | Expr.adtField _ _ _ _ _ =>
false
| Expr.paramDynamicMemberElement _ _ innerIndex =>
exprHasUntrackableWrites innerIndex
termination_by e => sizeOf e
decreasing_by all_goals simp_wf; all_goals omega
def exprListHasUntrackableWrites : List Expr → Bool
| [] => false
| e :: es => exprHasUntrackableWrites e || exprListHasUntrackableWrites es
termination_by es => sizeOf es
decreasing_by all_goals simp_wf; all_goals omega
end
mutual
/-- Check whether a statement may write to storage fields that `stmtWrittenFields`
cannot track — specifically internal calls whose callee bodies are not visible
at single-function validation scope. External calls (`externalCallBind`,
`tryExternalCallBind`, `ecm`) target other contracts and cannot directly modify
the current contract's storage fields, so they are safe for `modifies()`.
Used by `modifies(...)` validation to conservatively reject annotations when
write-set tracking is incomplete. -/
def stmtHasUntrackableWrites : Stmt → Bool
| Stmt.internalCall _ _ | Stmt.internalCallAssign _ _ _ => true
| Stmt.letVar _ value | Stmt.assignVar _ value =>
exprHasUntrackableWrites value
| Stmt.setStorage _ value | Stmt.setStorageAddr _ value
| Stmt.require value _ =>
exprHasUntrackableWrites value
| Stmt.setStorageWord _ wordOffset value =>
-- Nonzero wordOffset writes to a sibling field that `stmtWrittenFields`
-- cannot track (it returns only the base field), so flag as untrackable
-- to keep `modifies(...)` validation sound.
wordOffset != 0 || exprHasUntrackableWrites value
| Stmt.requireError cond _ args =>
exprHasUntrackableWrites cond || args.any exprHasUntrackableWrites
| Stmt.revertError _ args | Stmt.returnValues args | Stmt.emit _ args =>
args.any exprHasUntrackableWrites
| Stmt.return value | Stmt.storageArrayPush _ value =>
exprHasUntrackableWrites value
| Stmt.setStorageArrayElement _ index value =>
exprHasUntrackableWrites index || exprHasUntrackableWrites value
| Stmt.setMapping _ key value | Stmt.setMappingUint _ key value =>
exprHasUntrackableWrites key || exprHasUntrackableWrites value
| Stmt.setMappingWord _ key _ value | Stmt.setMappingPackedWord _ key _ _ value =>
exprHasUntrackableWrites key || exprHasUntrackableWrites value
| Stmt.setMappingChain _ keys value =>
keys.any exprHasUntrackableWrites || exprHasUntrackableWrites value
| Stmt.setMapping2 _ key1 key2 value | Stmt.setMapping2Word _ key1 key2 _ value
| Stmt.setStructMember2 _ key1 key2 _ value =>
exprHasUntrackableWrites key1 || exprHasUntrackableWrites key2 || exprHasUntrackableWrites value
| Stmt.setStructMember _ key _ value =>
exprHasUntrackableWrites key || exprHasUntrackableWrites value
| Stmt.rawLog topics dataOffset dataSize =>
topics.any exprHasUntrackableWrites || exprHasUntrackableWrites dataOffset || exprHasUntrackableWrites dataSize
| Stmt.mstore offset value | Stmt.tstore offset value =>
exprHasUntrackableWrites offset || exprHasUntrackableWrites value
| Stmt.calldatacopy destOffset sourceOffset size | Stmt.returndataCopy destOffset sourceOffset size =>
exprHasUntrackableWrites destOffset || exprHasUntrackableWrites sourceOffset || exprHasUntrackableWrites size
| Stmt.ite cond thenBranch elseBranch =>
exprHasUntrackableWrites cond ||
stmtListHasUntrackableWrites thenBranch ||
stmtListHasUntrackableWrites elseBranch
| Stmt.forEach _ count body =>
exprHasUntrackableWrites count || stmtListHasUntrackableWrites body
| Stmt.unsafeBlock _ body =>
stmtListHasUntrackableWrites body
| Stmt.matchAdt _ scrutinee branches =>
exprHasUntrackableWrites scrutinee || matchBranchesHasUntrackableWrites branches
| _ => false
termination_by s => sizeOf s
decreasing_by all_goals simp_wf; all_goals omega
def stmtListHasUntrackableWrites : List Stmt → Bool
| [] => false
| s :: ss => stmtHasUntrackableWrites s || stmtListHasUntrackableWrites ss
termination_by ss => sizeOf ss
decreasing_by all_goals simp_wf; all_goals omega
def matchBranchesHasUntrackableWrites : List (String × List String × List Stmt) → Bool
| [] => false
| (_, _, body) :: rest =>
stmtListHasUntrackableWrites body || matchBranchesHasUntrackableWrites rest
termination_by bs => sizeOf bs
decreasing_by all_goals simp_wf; all_goals omega
end
mutual
/-- Check whether an expression contains an external call (call, staticcall, delegatecall,
or externalCall). Used by `no_external_calls` validation (#1729, Axis 3 Step 1c). -/
def exprContainsExternalCall : Expr → Bool
| Expr.call _ _ _ _ _ _ _ | Expr.staticcall _ _ _ _ _ _
| Expr.delegatecall _ _ _ _ _ _ => true
| Expr.externalCall name args =>
if name == builtinExpName then exprListContainsExternalCall args else true
| Expr.add a b | Expr.sub a b | Expr.mul a b | Expr.div a b | Expr.sdiv a b
| Expr.mod a b | Expr.smod a b
| Expr.bitAnd a b | Expr.bitOr a b | Expr.bitXor a b | Expr.shl a b | Expr.shr a b | Expr.sar a b
| Expr.byte a b =>
exprContainsExternalCall a || exprContainsExternalCall b
| Expr.intrinsic _ _ _ args => exprListContainsExternalCall args
| Expr.forkIfAtLeast _ thenExpr elseExpr =>
exprContainsExternalCall thenExpr || exprContainsExternalCall elseExpr
| Expr.lt a b | Expr.gt a b | Expr.slt a b | Expr.sgt a b | Expr.eq a b
| Expr.ge a b | Expr.le a b | Expr.signextend a b
| Expr.logicalAnd a b | Expr.logicalOr a b
| Expr.wMulDown a b | Expr.wDivUp a b | Expr.min a b | Expr.max a b
| Expr.ceilDiv a b =>
exprContainsExternalCall a || exprContainsExternalCall b
| Expr.mulDivDown a b c | Expr.mulDivUp a b c
| Expr.mulDiv512Down a b c | Expr.mulDiv512Up a b c =>
exprContainsExternalCall a || exprContainsExternalCall b || exprContainsExternalCall c
| Expr.bitNot a | Expr.logicalNot a | Expr.extcodesize a =>
exprContainsExternalCall a
| Expr.ite cond thenVal elseVal =>
exprContainsExternalCall cond || exprContainsExternalCall thenVal || exprContainsExternalCall elseVal
| Expr.mapping _ key | Expr.mappingWord _ key _ | Expr.mappingPackedWord _ key _ _ | Expr.mappingUint _ key
| Expr.structMember _ key _ | Expr.arrayElement _ key | Expr.memoryArrayElement _ key
| Expr.arrayElementWord _ key _ _
| Expr.arrayElementDynamicWord _ key _
| Expr.arrayElementDynamicDataOffset _ key
| Expr.arrayElementDynamicMemberDataOffset _ key _
| Expr.arrayElementDynamicMemberLength _ key _
| Expr.storageArrayElement _ key =>
exprContainsExternalCall key
| Expr.arrayElementDynamicMemberElement _ key _ innerKey =>
exprContainsExternalCall key || exprContainsExternalCall innerKey
| Expr.paramDynamicMemberElement _ _ innerIndex =>
exprContainsExternalCall innerIndex
| Expr.mappingChain _ keys =>
exprListContainsExternalCall keys
| Expr.mapping2 _ key1 key2 | Expr.mapping2Word _ key1 key2 _
| Expr.structMember2 _ key1 key2 _ =>
exprContainsExternalCall key1 || exprContainsExternalCall key2
| Expr.mload offset | Expr.tload offset | Expr.calldataload offset
| Expr.returndataOptionalBoolAt offset =>
exprContainsExternalCall offset
| Expr.keccak256 offset size =>
exprContainsExternalCall offset || exprContainsExternalCall size
| Expr.internalCall _ args =>
exprListContainsExternalCall args
| Expr.adtConstruct _ _ args =>
exprListContainsExternalCall args
| Expr.dynamicBytesEq _ _ => false
-- Pure leaves: no external call inside. Listed explicitly to avoid
-- `_mutual.eq_def` heartbeat-ceiling failures when new constructors land.
| Expr.literal _ | Expr.param _ | Expr.constructorArg _ | Expr.storage _ | Expr.storageAddr _
| Expr.caller | Expr.contractAddress | Expr.chainid | Expr.msgValue | Expr.selfBalance
| Expr.blockTimestamp | Expr.blockNumber | Expr.blobbasefee
| Expr.calldatasize | Expr.returndataSize | Expr.localVar _ | Expr.arrayLength _
| Expr.memoryArrayLength _
| Expr.storageArrayLength _
| Expr.paramDynamicHeadWord _ _
| Expr.paramDynamicStaticComposite _ _
| Expr.paramDynamicMemberLength _ _
| Expr.paramDynamicMemberDataOffset _ _
| Expr.adtTag _ _ | Expr.adtField _ _ _ _ _ =>
false
termination_by e => sizeOf e
decreasing_by all_goals simp_wf; all_goals omega
def exprListContainsExternalCall : List Expr → Bool
| [] => false
| e :: es => exprContainsExternalCall e || exprListContainsExternalCall es
termination_by es => sizeOf es
decreasing_by all_goals simp_wf; all_goals omega
end
mutual
/-- Conservative expression call detector for annotations such as
`no_external_calls`, where an internal helper expression may itself perform
an external interaction. CEI uses `exprContainsExternalCall` instead so that
local helper reads do not become false interaction barriers. -/
def exprMayContainExternalCall : Expr → Bool
| Expr.internalCall _ _ => true
| Expr.call _ _ _ _ _ _ _ | Expr.staticcall _ _ _ _ _ _
| Expr.delegatecall _ _ _ _ _ _ => true
| Expr.externalCall name args =>
if name == builtinExpName then exprListMayContainExternalCall args else true
| Expr.add a b | Expr.sub a b | Expr.mul a b | Expr.div a b | Expr.sdiv a b
| Expr.mod a b | Expr.smod a b
| Expr.bitAnd a b | Expr.bitOr a b | Expr.bitXor a b | Expr.shl a b | Expr.shr a b | Expr.sar a b
| Expr.byte a b =>
exprMayContainExternalCall a || exprMayContainExternalCall b
| Expr.intrinsic _ _ _ args => exprListMayContainExternalCall args
| Expr.forkIfAtLeast _ thenExpr elseExpr =>
exprMayContainExternalCall thenExpr || exprMayContainExternalCall elseExpr
| Expr.lt a b | Expr.gt a b | Expr.slt a b | Expr.sgt a b | Expr.eq a b
| Expr.ge a b | Expr.le a b | Expr.signextend a b
| Expr.logicalAnd a b | Expr.logicalOr a b
| Expr.wMulDown a b | Expr.wDivUp a b | Expr.min a b | Expr.max a b
| Expr.ceilDiv a b =>
exprMayContainExternalCall a || exprMayContainExternalCall b
| Expr.mulDivDown a b c | Expr.mulDivUp a b c
| Expr.mulDiv512Down a b c | Expr.mulDiv512Up a b c =>
exprMayContainExternalCall a || exprMayContainExternalCall b || exprMayContainExternalCall c
| Expr.bitNot a | Expr.logicalNot a | Expr.extcodesize a =>
exprMayContainExternalCall a
| Expr.ite cond thenVal elseVal =>
exprMayContainExternalCall cond || exprMayContainExternalCall thenVal || exprMayContainExternalCall elseVal
| Expr.mapping _ key | Expr.mappingWord _ key _ | Expr.mappingPackedWord _ key _ _ | Expr.mappingUint _ key
| Expr.structMember _ key _ | Expr.arrayElement _ key | Expr.memoryArrayElement _ key
| Expr.arrayElementWord _ key _ _
| Expr.arrayElementDynamicWord _ key _
| Expr.arrayElementDynamicDataOffset _ key
| Expr.arrayElementDynamicMemberDataOffset _ key _
| Expr.arrayElementDynamicMemberLength _ key _
| Expr.storageArrayElement _ key =>
exprMayContainExternalCall key
| Expr.arrayElementDynamicMemberElement _ key _ innerKey =>
exprMayContainExternalCall key || exprMayContainExternalCall innerKey
| Expr.paramDynamicMemberElement _ _ innerIndex =>
exprMayContainExternalCall innerIndex
| Expr.mappingChain _ keys =>
exprListMayContainExternalCall keys
| Expr.mapping2 _ key1 key2 | Expr.mapping2Word _ key1 key2 _
| Expr.structMember2 _ key1 key2 _ =>
exprMayContainExternalCall key1 || exprMayContainExternalCall key2
| Expr.mload offset | Expr.tload offset | Expr.calldataload offset
| Expr.returndataOptionalBoolAt offset =>
exprMayContainExternalCall offset
| Expr.keccak256 offset size =>
exprMayContainExternalCall offset || exprMayContainExternalCall size
| Expr.adtConstruct _ _ args =>
exprListMayContainExternalCall args
| Expr.dynamicBytesEq _ _ => false
-- Pure leaves: no external call inside. Listed explicitly to avoid
-- `_mutual.eq_def` heartbeat-ceiling failures when new constructors land.
| Expr.literal _ | Expr.param _ | Expr.constructorArg _ | Expr.storage _ | Expr.storageAddr _
| Expr.caller | Expr.contractAddress | Expr.chainid | Expr.msgValue | Expr.selfBalance
| Expr.blockTimestamp | Expr.blockNumber | Expr.blobbasefee
| Expr.calldatasize | Expr.returndataSize | Expr.localVar _ | Expr.arrayLength _
| Expr.memoryArrayLength _
| Expr.storageArrayLength _
| Expr.paramDynamicHeadWord _ _
| Expr.paramDynamicStaticComposite _ _
| Expr.paramDynamicMemberLength _ _
| Expr.paramDynamicMemberDataOffset _ _
| Expr.adtTag _ _ | Expr.adtField _ _ _ _ _ =>
false
termination_by e => sizeOf e
decreasing_by all_goals simp_wf; all_goals omega
def exprListMayContainExternalCall : List Expr → Bool
| [] => false
| e :: es => exprMayContainExternalCall e || exprListMayContainExternalCall es
termination_by es => sizeOf es
decreasing_by all_goals simp_wf; all_goals omega
end
mutual
/-- Check whether a statement contains an external call (externalCallBind, ecm, or
an expression with call/staticcall/delegatecall/externalCall).
Used by `no_external_calls` validation (#1729, Axis 3 Step 1c). -/
def stmtContainsExternalCall : Stmt → Bool
| Stmt.externalCallBind _ _ _ | Stmt.tryExternalCallBind _ _ _ _ => true
| Stmt.ecm _ _ => true
| Stmt.letVar _ value | Stmt.assignVar _ value =>
exprContainsExternalCall value
| Stmt.setStorage _ value | Stmt.setStorageAddr _ value | Stmt.setStorageWord _ _ value
| Stmt.require value _ =>
exprContainsExternalCall value
| Stmt.requireError cond _ args =>
exprContainsExternalCall cond || args.any exprContainsExternalCall
| Stmt.revertError _ args =>
args.any exprContainsExternalCall
| Stmt.return value =>
exprContainsExternalCall value
| Stmt.returnValues values =>
values.any exprContainsExternalCall
| Stmt.storageArrayPush _ value =>
exprContainsExternalCall value
| Stmt.setStorageArrayElement _ index value =>
exprContainsExternalCall index || exprContainsExternalCall value
| Stmt.setMapping _ key value | Stmt.setMappingUint _ key value =>
exprContainsExternalCall key || exprContainsExternalCall value
| Stmt.setMappingWord _ key _ value =>
exprContainsExternalCall key || exprContainsExternalCall value
| Stmt.setMappingPackedWord _ key _ _ value =>
exprContainsExternalCall key || exprContainsExternalCall value
| Stmt.setMappingChain _ keys value =>
keys.any exprContainsExternalCall || exprContainsExternalCall value
| Stmt.setMapping2 _ key1 key2 value =>
exprContainsExternalCall key1 || exprContainsExternalCall key2 || exprContainsExternalCall value
| Stmt.setMapping2Word _ key1 key2 _ value =>
exprContainsExternalCall key1 || exprContainsExternalCall key2 || exprContainsExternalCall value
| Stmt.setStructMember _ key _ value =>
exprContainsExternalCall key || exprContainsExternalCall value
| Stmt.setStructMember2 _ key1 key2 _ value =>
exprContainsExternalCall key1 || exprContainsExternalCall key2 || exprContainsExternalCall value
| Stmt.emit _ args =>
args.any exprContainsExternalCall
| Stmt.rawLog topics dataOffset dataSize =>
topics.any exprContainsExternalCall || exprContainsExternalCall dataOffset || exprContainsExternalCall dataSize
| Stmt.tstore offset value | Stmt.mstore offset value =>
exprContainsExternalCall offset || exprContainsExternalCall value
| Stmt.calldatacopy destOffset sourceOffset size =>
exprContainsExternalCall destOffset || exprContainsExternalCall sourceOffset || exprContainsExternalCall size
| Stmt.returndataCopy destOffset sourceOffset size =>
exprContainsExternalCall destOffset || exprContainsExternalCall sourceOffset || exprContainsExternalCall size
| Stmt.ite cond thenBranch elseBranch =>
exprContainsExternalCall cond || stmtListContainsExternalCall thenBranch || stmtListContainsExternalCall elseBranch
| Stmt.forEach _ count body =>
exprContainsExternalCall count || stmtListContainsExternalCall body
| Stmt.unsafeBlock _ body =>
stmtListContainsExternalCall body
| Stmt.matchAdt _ scrutinee branches =>
exprContainsExternalCall scrutinee ||
matchBranchesContainExternalCall branches
| Stmt.internalCall _ args | Stmt.internalCallAssign _ _ args =>
args.any exprContainsExternalCall
| _ => false
termination_by s => sizeOf s
decreasing_by all_goals simp_wf; all_goals omega
def stmtListContainsExternalCall : List Stmt → Bool
| [] => false
| s :: ss => stmtContainsExternalCall s || stmtListContainsExternalCall ss
termination_by ss => sizeOf ss
decreasing_by all_goals simp_wf; all_goals omega
def matchBranchesContainExternalCall : List (String × List String × List Stmt) → Bool
| [] => false
| (_, _, body) :: rest =>
stmtListContainsExternalCall body || matchBranchesContainExternalCall rest
termination_by bs => sizeOf bs
decreasing_by all_goals simp_wf; all_goals omega
end
mutual
/-- Conservative variant of `stmtContainsExternalCall` for `no_external_calls`
validation. Returns `true` for internal calls anywhere in the statement tree,
because callee bodies may contain external calls that are not visible at
single-function validation scope. -/
def stmtMayContainExternalCall : Stmt → Bool
| Stmt.internalCall _ _ | Stmt.internalCallAssign _ _ _ => true
| Stmt.ite cond thenBranch elseBranch =>
exprMayContainExternalCall cond ||
stmtListMayContainExternalCall thenBranch ||
stmtListMayContainExternalCall elseBranch
| Stmt.forEach _ count body =>
exprMayContainExternalCall count || stmtListMayContainExternalCall body
| Stmt.unsafeBlock _ body =>
stmtListMayContainExternalCall body
| Stmt.matchAdt _ scrutinee branches =>
exprMayContainExternalCall scrutinee || matchBranchesMayContainExternalCall branches
| Stmt.letVar _ value | Stmt.assignVar _ value =>
exprMayContainExternalCall value
| Stmt.setStorage _ value | Stmt.setStorageAddr _ value | Stmt.setStorageWord _ _ value
| Stmt.require value _ =>
exprMayContainExternalCall value
| Stmt.requireError cond _ args =>
exprMayContainExternalCall cond || args.any exprMayContainExternalCall
| Stmt.revertError _ args =>
args.any exprMayContainExternalCall
| Stmt.return value =>
exprMayContainExternalCall value
| Stmt.returnValues values =>
values.any exprMayContainExternalCall
| Stmt.storageArrayPush _ value =>
exprMayContainExternalCall value
| Stmt.setStorageArrayElement _ index value =>
exprMayContainExternalCall index || exprMayContainExternalCall value
| Stmt.setMapping _ key value | Stmt.setMappingUint _ key value =>
exprMayContainExternalCall key || exprMayContainExternalCall value
| Stmt.setMappingWord _ key _ value =>
exprMayContainExternalCall key || exprMayContainExternalCall value
| Stmt.setMappingPackedWord _ key _ _ value =>
exprMayContainExternalCall key || exprMayContainExternalCall value
| Stmt.setMappingChain _ keys value =>
keys.any exprMayContainExternalCall || exprMayContainExternalCall value
| Stmt.setMapping2 _ key1 key2 value =>
exprMayContainExternalCall key1 || exprMayContainExternalCall key2 || exprMayContainExternalCall value
| Stmt.setMapping2Word _ key1 key2 _ value =>
exprMayContainExternalCall key1 || exprMayContainExternalCall key2 || exprMayContainExternalCall value
| Stmt.setStructMember _ key _ value =>
exprMayContainExternalCall key || exprMayContainExternalCall value
| Stmt.setStructMember2 _ key1 key2 _ value =>
exprMayContainExternalCall key1 || exprMayContainExternalCall key2 || exprMayContainExternalCall value
| Stmt.emit _ args =>
args.any exprMayContainExternalCall
| Stmt.rawLog topics dataOffset dataSize =>
topics.any exprMayContainExternalCall || exprMayContainExternalCall dataOffset || exprMayContainExternalCall dataSize
| Stmt.tstore offset value | Stmt.mstore offset value =>
exprMayContainExternalCall offset || exprMayContainExternalCall value
| Stmt.calldatacopy destOffset sourceOffset size =>
exprMayContainExternalCall destOffset || exprMayContainExternalCall sourceOffset || exprMayContainExternalCall size
| Stmt.returndataCopy destOffset sourceOffset size =>
exprMayContainExternalCall destOffset || exprMayContainExternalCall sourceOffset || exprMayContainExternalCall size
| s => stmtContainsExternalCall s
termination_by s => sizeOf s
decreasing_by all_goals simp_wf; all_goals omega
def stmtListMayContainExternalCall : List Stmt → Bool
| [] => false
| s :: ss => stmtMayContainExternalCall s || stmtListMayContainExternalCall ss
termination_by ss => sizeOf ss
decreasing_by all_goals simp_wf; all_goals omega
def matchBranchesMayContainExternalCall : List (String × List String × List Stmt) → Bool
| [] => false
| (_, _, body) :: rest =>
stmtListMayContainExternalCall body || matchBranchesMayContainExternalCall rest
termination_by bs => sizeOf bs
decreasing_by all_goals simp_wf; all_goals omega
end
mutual
def stmtReadsStateOrEnv : Stmt → Bool
| Stmt.letVar _ value | Stmt.assignVar _ value | Stmt.setStorage _ value | Stmt.setStorageAddr _ value
| Stmt.setStorageWord _ _ value |
Stmt.return value | Stmt.require value _ =>
exprReadsStateOrEnv value
| Stmt.storageArrayPush _ value =>
true || exprReadsStateOrEnv value
| Stmt.setStorageArrayElement _ index value =>
true || exprReadsStateOrEnv index || exprReadsStateOrEnv value
| Stmt.storageArrayPop _ =>
true
| Stmt.requireError cond _ args =>
exprReadsStateOrEnv cond || args.any exprReadsStateOrEnv
| Stmt.revertError _ args | Stmt.emit _ args | Stmt.returnValues args =>