-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathCallsTest.lean
More file actions
166 lines (152 loc) · 5.92 KB
/
Copy pathCallsTest.lean
File metadata and controls
166 lines (152 loc) · 5.92 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
import Compiler.Codegen
import Compiler.CompilationModel
import Compiler.CompilationModel.TrustSurface
import Compiler.Modules.Calls
import Compiler.Yul.PrettyPrint
namespace Compiler.Modules.CallsTest
open Compiler
open Compiler.CompilationModel
private def contains (haystack needle : String) : Bool :=
let h := haystack.toList
let n := needle.toList
if n.isEmpty then true
else
let rec go : List Char → Bool
| [] => false
| c :: cs =>
if (c :: cs).take n.length == n then true
else go cs
go h
private def expectTrue (label : String) (ok : Bool) : IO Unit := do
if !ok then
throw (IO.userError s!"✗ {label}")
IO.println s!"✓ {label}"
private def selectorsFor (spec : CompilationModel) : List Nat :=
List.range (spec.functions.filter (fun fn =>
!fn.isInternal && fn.name != "fallback" && fn.name != "receive")).length
private def expectCompileErrorContains (label : String)
(spec : CompilationModel) (needle : String) : IO Unit := do
match Compiler.CompilationModel.compile spec (selectorsFor spec) with
| .ok _ => throw (IO.userError s!"✗ {label}: expected compile error containing '{needle}'")
| .error err =>
if !contains err needle then
throw (IO.userError s!"✗ {label}: expected error containing '{needle}', got '{err}'")
IO.println s!"✓ {label}"
private def expectCompileToYul (label : String) (spec : CompilationModel) : IO String := do
match Compiler.CompilationModel.compile spec (selectorsFor spec) with
| .ok ir =>
IO.println s!"✓ {label}"
pure (Compiler.Yul.render (Compiler.emitYul ir))
| .error err =>
throw (IO.userError s!"✗ {label}: compile failed: {err}")
private def selfDelegateMulticallBytesSmokeSpec : CompilationModel := {
name := "SelfDelegateMulticallBytesSmoke"
fields := []
«constructor» := none
functions := [
{ name := "multicall"
params := [
{ name := "calls", ty := ParamType.array ParamType.bytes }
]
returnType := none
body := [
Compiler.Modules.Calls.selfDelegateMulticallBytes "calls",
Stmt.stop
]
}
]
}
private def selfDelegateMulticallBytesBadAritySpec : CompilationModel := {
name := "SelfDelegateMulticallBytesBadArity"
fields := []
«constructor» := none
functions := [
{ name := "bad"
params := [
{ name := "calls", ty := ParamType.array ParamType.bytes }
]
returnType := none
body := [
Stmt.ecm (Compiler.Modules.Calls.selfDelegateMulticallBytesModule "calls")
[Expr.arrayLength "calls"],
Stmt.stop
]
}
]
}
private def selfDelegateMulticallBytesEmptyParamSpec : CompilationModel := {
name := "SelfDelegateMulticallBytesEmptyParam"
fields := []
«constructor» := none
functions := [
{ name := "bad"
params := [
{ name := "calls", ty := ParamType.array ParamType.bytes }
]
returnType := none
body := [
Compiler.Modules.Calls.selfDelegateMulticallBytes "",
Stmt.stop
]
}
]
}
private def selfDelegateMulticallBytesViewRejectedSpec : CompilationModel := {
name := "SelfDelegateMulticallBytesViewRejected"
fields := []
«constructor» := none
functions := [
{ name := "multicall"
params := [
{ name := "calls", ty := ParamType.array ParamType.bytes }
]
returnType := none
isView := true
body := [
Compiler.Modules.Calls.selfDelegateMulticallBytes "calls",
Stmt.stop
]
}
]
}
unsafe def runTests : IO Unit := do
let yul ←
expectCompileToYul "self-delegate multicall bytes smoke spec" selfDelegateMulticallBytesSmokeSpec
expectTrue "self-delegate multicall walks bytes[] calldata offsets"
(contains yul "for {" &&
contains yul "let __mc_i := 0" &&
contains yul "lt(__mc_i, calls_length)" &&
contains yul "let __mc_rel_offset := calldataload(add(calls_data_offset, mul(__mc_i, 32)))" &&
contains yul "if lt(__mc_rel_offset, mul(calls_length, 32)) {" &&
contains yul "if gt(__mc_rel_offset, sub(not(0), calls_data_offset)) {" &&
contains yul "let __mc_head_offset := add(calls_data_offset, __mc_rel_offset)" &&
contains yul "let __mc_data_size := calldataload(__mc_head_offset)" &&
contains yul "let __mc_data_offset := add(__mc_head_offset, 32)")
expectTrue "self-delegate multicall copies each bytes payload and delegatecalls address()"
(contains yul "calldatacopy(__mc_ptr, __mc_data_offset, __mc_data_size)" &&
contains yul "delegatecall(gas(), address(), __mc_ptr, __mc_data_size, 0, 0)")
expectTrue "self-delegate multicall forwards revert returndata exactly"
(contains yul "let __mc_rds := returndatasize()" &&
contains yul "returndatacopy(0, 0, __mc_rds)" &&
contains yul "revert(0, __mc_rds)")
expectCompileErrorContains
"self-delegate multicall ECM rejects invalid argument counts"
selfDelegateMulticallBytesBadAritySpec
"uses ECM 'selfDelegateMulticallBytes' with 1 arguments but it expects 0"
expectCompileErrorContains
"self-delegate multicall rejects empty parameter names"
selfDelegateMulticallBytesEmptyParamSpec
"selfDelegateMulticallBytes: arrayParam must be non-empty"
expectCompileErrorContains
"self-delegate multicall remains rejected from view functions"
selfDelegateMulticallBytesViewRejectedSpec
"function 'multicall' is marked view but writes state"
let report := emitTrustReportJson [selfDelegateMulticallBytesSmokeSpec]
expectTrue "self-delegate multicall trust report surfaces scoped multicall assumption without proxy boundary"
(contains report "\"module\":\"selfDelegateMulticallBytes\"" &&
contains report "\"assumption\":\"self_delegate_multicall_bytes_revert_bubbling\"" &&
contains report "\"boundaryClass\":\"abiBoundary\"" &&
contains report "\"notModeledProxyUpgradeability\":[]" &&
! (contains report "\"delegatecall\""))
#eval! runTests
end Compiler.Modules.CallsTest