@@ -21,17 +21,85 @@ data Fallback(payability, args, rets, fn) = Fallback(payability, args, rets, fn)
21
21
22
22
// --- Method Selectors ---
23
23
24
- // For each method in a contract the compiler generates a unique type and
25
- // produces a `Selector` instance for that type that returns the selector hash
26
- forall nm . class nm:Selector {
27
- function hash(prx: Proxy(nm)) -> word;
24
+ // The string representation of abi types used during selector computation
25
+ forall ty . class ty:ABIString {
26
+ function append(head : word, tail : word, prx : Proxy(ty)) -> word;
28
27
}
29
28
30
- // Method has a Selector if its name has a Selector
31
- forall name payability args rets fn . name:Selector => instance Method(name,payability,args,rets,fn):Selector {
32
- function hash(prx: Proxy(Method(name,payability,args,rets,fn))) -> word {
33
- return Selector.hash(Proxy : Proxy(name));
34
- }
29
+ instance uint256:ABIString {
30
+ function append(head : word, tail : word, prx : Proxy(uint256)) -> word {
31
+ let size : word = 7;
32
+ assembly {
33
+ mstore(head, add(mload(head), size))
34
+ mstore(tail, 0x75696e7432353600000000000000000000000000000000000000000000000000)
35
+ }
36
+ return Add.add(tail, size);
37
+ }
38
+ }
39
+
40
+ instance ():ABIString {
41
+ function append(head : word, tail : word, prx : Proxy(())) -> word {
42
+ return(tail);
43
+ }
44
+ }
45
+
46
+ function append_left_bracket(head : word, tail : word) -> word {
47
+ let size = 1;
48
+ assembly {
49
+ mstore(head, add(mload(head), size));
50
+ mstore(tail, 0x2800000000000000000000000000000000000000000000000000000000000000);
51
+ }
52
+ return Add.add(tail, size);
53
+ }
54
+
55
+ function append_right_bracket(head : word, tail : word) -> word {
56
+ let size = 1;
57
+ assembly {
58
+ mstore(head, add(mload(head), size));
59
+ mstore(tail, 0x2900000000000000000000000000000000000000000000000000000000000000);
60
+ }
61
+ return Add.add(tail, size);
62
+ }
63
+
64
+ function append_comma(head : word, tail : word) -> word {
65
+ let size = 1;
66
+ assembly {
67
+ mstore(head, add(mload(head), size));
68
+ mstore(tail, 0x2c00000000000000000000000000000000000000000000000000000000000000);
69
+ }
70
+ return Add.add(tail, size);
71
+ }
72
+
73
+ forall l r . l:ABIString, r:ABIString => instance (l,r):ABIString {
74
+ function append(head : word, tail : word, prx : Proxy((l,r))) -> word {
75
+ let with_l = ABIString.append(head, tail, Proxy : Proxy(l));
76
+ let with_comma = append_comma(head, with_l);
77
+ return ABIString.append(head, with_comma, Proxy : Proxy(r));
78
+ }
79
+ }
80
+
81
+ forall ty . class ty:Selector {
82
+ function compute(prx : Proxy(ty)) -> bytes4;
83
+ }
84
+
85
+ // Computes the selector hash for a given method
86
+ // this is a class with a single instance since it made some of the downstream definitions a bit cleaner to define
87
+ forall name payability args rets fn
88
+ . name:ABIString
89
+ , args:ABIString
90
+ => instance Method(name,payability,Proxy(args),Proxy(rets),fn):Selector {
91
+ function compute(prx : Proxy(Method(name,payability,Proxy(args),Proxy(rets),fn))) -> bytes4 {
92
+ let head = get_free_memory();
93
+ let tail = Add.add(head, 32);
94
+ tail = ABIString.append(head, tail, Proxy : Proxy(name));
95
+ tail = append_left_bracket(head, tail);
96
+ tail = ABIString.append(head, tail, Proxy : Proxy(args));
97
+ tail = append_right_bracket(head, tail);
98
+
99
+ let res : word;
100
+ assembly { res := shr(224, keccak256(add(head, 32), mload(head))) }
101
+ return bytes4(res);
102
+ }
35
103
}
36
104
37
105
// --- Method Execution ---
@@ -162,13 +230,13 @@ forall n m . n:ExecMethod, n:Selector, m:RunDispatch => instance (n,m):RunDispat
162
230
}
163
231
164
232
// TODO: we only wanna do the calldataload once
165
- // Given evidence of a name with a known selector, we can check if it matches the selector in the first four bytes of calldata
166
- forall name . name :Selector => function selector_matches(prx : Proxy(name )) -> Bool {
167
- let hash = Selector.hash (prx);
233
+ // Given evidence of a type with a known selector, we can check if it matches the selector in the first four bytes of calldata
234
+ forall ty . ty :Selector => function selector_matches(prx : Proxy(ty )) -> Bool {
235
+ let candidate = Typedef.rep( Selector.compute (prx) );
168
236
let res : word;
169
237
assembly {
170
238
let sel := shr(224, calldataload(0));
171
- res := eq(sel, hash );
239
+ res := eq(sel, candidate );
172
240
}
173
241
match res {
174
242
| 0 => return False;
@@ -264,14 +332,17 @@ function revert_handler() -> () {
264
332
assembly { revert(0,0) }
265
333
}
266
334
267
- data C_Add2_Selector = C_Add2_Selector ;
335
+ data C_Add2_Name = C_Add2_Name ;
268
336
269
- instance C_Add2_Selector:Selector {
270
- function hash(prx: Proxy(C_Add2_Selector)) -> word {
271
- // This would be keccak256("add2(uint256,uint256)") >> 224
272
- // Compiler computes this at compile time
273
- return 0x29fcda33; // placeholder value
274
- }
337
+ instance C_Add2_Name:ABIString {
338
+ function append(head : word, tail : word, prx : Proxy(C_Add2_Name)) -> word {
339
+ let size = 4;
340
+ assembly {
341
+ mstore(head, add(mload(head), size));
342
+ mstore(tail, 0x6164643200000000000000000000000000000000000000000000000000000000);
343
+ }
344
+ return Add.add(tail, size);
345
+ }
275
346
}
276
347
277
348
// transform
@@ -281,13 +352,12 @@ contract C {
281
352
return Add.add(x,y);
282
353
}
283
354
284
- function main() -> word {
355
+ function main() -> () {
285
356
let c = Contract(
286
- Method(C_Add2_Selector , Proxy : Proxy(NonPayable), Proxy : Proxy((uint256,uint256)), Proxy : Proxy(uint256), add2),
357
+ Method(C_Add2_Name , Proxy : Proxy(NonPayable), Proxy : Proxy((uint256,uint256)), Proxy : Proxy(uint256), add2),
287
358
Fallback(Proxy : Proxy(NonPayable), Proxy : Proxy(()), Proxy : Proxy(()),revert_handler)
288
359
);
289
360
290
361
RunContract.exec(c);
291
- return 0;
292
362
}
293
363
}
0 commit comments