forked from ethereum/sys-asm
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.eas
More file actions
408 lines (340 loc) · 17.5 KB
/
Copy pathmain.eas
File metadata and controls
408 lines (340 loc) · 17.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
;; This is an implementation of EIP-7002 style contract handling EL triggerred
;; consolidations. It leverages on the fee mechanism and the queue design of
;; the original EIP-7002 smart contract implementation. Therefore, the subroutines
;; logic of the original smart contract remains.
;;
;; The difference from the EIP-7002 smart contract is in input data, the size
;; of the queue item, target and max block parameters.
;; -----------------------------------------------------------------------------
;; CONSTANTS -------------------------------------------------------------------
;; -----------------------------------------------------------------------------
#define SYSTEM_ADDR 0xfffffffffffffffffffffffffffffffffffffffe
#define SLOT_EXCESS 0
#define SLOT_COUNT 1
#define QUEUE_HEAD 2
#define QUEUE_TAIL 3
#define QUEUE_OFFSET 4
#define SLOTS_PER_ITEM 4 ;; (address, source[0:32], source[32:48] ++ target[0:16], target[16::48])
#define MIN_FEE 1
#define TARGET_PER_BLOCK 1
#define MAX_PER_BLOCK 1
#define FEE_UPDATE_FRACTION 17
#define EXCESS_INHIBITOR 1181
#define INPUT_SIZE 96 ;; source ++ target
#define LOG_RECORD_SIZE 116 ;; address ++ source ++ target
#define OUT_RECORD_SIZE 117 ;; type ++ address ++ source ++ target
#define OUT_RECORD_TYPE 0x02
;; -----------------------------------------------------------------------------
;; PROGRAM START ---------------------------------------------------------------
;; -----------------------------------------------------------------------------
.start:
;; Protect the system subroutine by checking if the caller is the system
;; address.
caller ;; [caller]
push20 SYSTEM_ADDR ;; [sysaddr, caller]
eq ;; [sysaddr == caller]
push1 @read_requests ;; [read_lbl, sysaddr == caller]
jumpi ;; []
;; ---------------------------------------------------------------------------
;; USER SUBROUTINE -----------------------------------------------------------
;; ---------------------------------------------------------------------------
;;
;; Record new request ~~
;; This is the default code path. It will attempt to record a user's request
;; so long as they pay the required fee.
;; If calldatasize == 0, return the current excess requests.
calldatasize ;; [calldatasize]
iszero ;; [calldatasize == 0]
iszero ;; [calldatasize != 0]
jumpi @check_input
;; Load excess requests and return the value.
push SLOT_EXCESS ;; [excess_reqs_slot]
sload ;; [excess_reqs]
push0 ;; [0, excess_reqs]
mstore ;; []
push 32 ;; [32]
push 0 ;; [0, 32]
return ;; []
check_input:
;; Input data has the following layout:
;;
;; +--------+--------+
;; | source | target |
;; +--------+--------+
;; 48 48
;; Verify the input is exactly INPUT_SIZE bytes.
calldatasize ;; [calldatasize]
push1 INPUT_SIZE ;; [INPUT_SIZE, calldatasize]
eq ;; [INPUT_SIZE == calldatasize]
iszero ;; [INPUT_SIZE != calldatasize]
jumpi @revert ;; []
;; Compute the fee using fake expo and the current excess requests.
push FEE_UPDATE_FRACTION
push SLOT_EXCESS ;; [excess_slot, update_fraction]
sload ;; [excess, update_fraction]
push MIN_FEE ;; [min_fee, excess, update_fraction]
#include "../common/fake_expo.eas"
;; Determine if the fee provided is enough to cover the request fee.
callvalue ;; [callvalue, req_fee]
lt ;; [callvalue < req_fee]
jumpi @revert ;; []
;; The request can pay, increment request count.
push SLOT_COUNT
sload ;; [req_count]
push1 1 ;; [1, req_count]
add ;; [req_count+1]
push SLOT_COUNT
sstore ;; []
;; Now insert request into queue. First, compute the base storage slot
push QUEUE_TAIL ;; [tail_idx_slot]
sload ;; [tail_idx]
dup1 ;; [tail_idx, tail_idx]
push1 SLOTS_PER_ITEM ;; [SLOTS_PER_ITEM, tail_idx, tail_idx]
mul ;; [SLOTS_PER_ITEM*tail_idx, tail_idx]
push QUEUE_OFFSET
add ;; [slot, tail_idx]
;; Write address to queue.
caller ;; [caller, slot, ..]
dup2 ;; [slot, caller, slot, ..]
sstore ;; [slot, ..]
push1 1 ;; [1, slot, ..]
add ;; [slot, ..]
;; Store source[0:32] to queue.
push0 ;; [0, slot, ..]
calldataload ;; [source[0:32], slot, ..]
dup2 ;; [slot, source[0:32], slot, ..]
sstore ;; [slot, ..]
push1 1 ;; [1, slot, ..]
add ;; [slot, ..]
;; Store source[32:48] ++ target[0:16] to queue.
push1 32 ;; [32, slot, ..]
calldataload ;; [source[32:48] ++ target[0:16], slot, ..]
dup2 ;; [slot, source[32:48] ++ target[0:16], slot, ..]
sstore ;; [slot, ..]
push1 1 ;; [1, slot, ..]
add ;; [slot, ..]
;; Store target[16:48] to queue.
push1 64 ;; [64, slot, ..]
calldataload ;; [target[16:48], slot, ..]
swap1 ;; [slot, target[16:48], ..]
sstore ;; [..]
;; Assemble log data.
caller ;; [caller, ..]
push1 96 ;; [96, caller, ..]
shl ;; [caller, ..]
push0 ;; [0, caller, ..]
mstore ;; [..]
push1 INPUT_SIZE ;; [size, ..]
push0 ;; [ost, size, ..]
push1 20 ;; [dest, ost, size, ..]
calldatacopy ;; [..]
;; Log record.
push1 LOG_RECORD_SIZE ;; [size, ..]
push0 ;; [idx, size, ..]
log0 ;; [..]
;; Increment queue tail over last and write to storage.
push1 1 ;; [1, tail_idx]
add ;; [tail_idx+1]
push QUEUE_TAIL ;; [tail_idx_slot]
sstore ;; []
stop
;; -----------------------------------------------------------------------------
;; SYSTEM SUBROUTINE -----------------------------------------------------------
;; -----------------------------------------------------------------------------
;;
;; Pop request from queue, update fee accumulator ~~
;; This is the logic executed by the protocol each block. It reads as many
;; requests as available from the queue, until the max request per
;; block is reached. The requests are returned as a contiguous array of bytes
;; with each record being exactly 117 bytes.
;;
;; Consolidation request record:
;;
;; +------+------+--------+--------+
;; | 0x02 | addr | source | target |
;; +------+------+--------+--------+
;; 1 20 48 48
;;
;; Because the requests are stored across SLOTS_PER_ITEM storage slots, there is some
;; shuffling to align the data.
;;
;; After reading the requests, they are removed from the queue by
;; modifying the queue's head index. The excess requests accumulator is updated
;; so that the new cost of submitting a request is reflected. Finally, the
;; request count is reset.
read_requests:
;; Determine the size of the queue by calculating tail - head.
push QUEUE_TAIL ;; [tail_idx_slot, head_idx, head_idx]
sload ;; [tail_idx]
push QUEUE_HEAD ;; [head_idx_slot, tail_idx]
sload ;; [head_idx, tail_idx]
;; Now compute the count.
dup1 ;; [head_idx, head_idx, tail_idx]
dup3 ;; [tail_idx, head_idx, head_idx, tail_idx]
sub ;; [count, head_idx, tail_idx]
;; Determine if count is greater than the max requests.
dup1 ;; [count, count, head_idx, tail_idx]
push MAX_PER_BLOCK ;; [reqs_per_block, count, count, head_idx, tail_idx]
gt ;; [reqs_per_block > count, count, head_idx, tail_idx]
jumpi @begin_loop ;; [count, head_idx, tail_idx]
;; Discard count, use the max requests per block.
pop ;; [head_idx, tail_idx]
push MAX_PER_BLOCK ;; [count, head_idx, tail_idx]
begin_loop:
push0 ;; [i, count, head_idx, tail_idx]
accum_loop:
;; This loop will read each request and byte bang it into an OUT_RECORD_SIZE byte chunk.
;; Bounds check, ensure i < count.
dup2 ;; [count, i, count, head_idx, tail_idx]
dup2 ;; [i, count, i, count, head_idx, tail_idx]
eq ;; [i == count, i, count, head_idx, tail_idx]
jumpi @update_head ;; [i, count, head_idx, tail_idx]
;; Precompute record_offset = i*OUT_RECORD_SIZE.
dup1 ;; [i, i, count, head_idx, tail_idx]
push OUT_RECORD_SIZE ;; [size, i, i, count, head_idx, tail_idx]
mul ;; [record_offset, i, count, head_idx, tail_idx]
;; Determine the storage slot of the address for this iteration. This value is
;; also the base for the other storage slots containing the source and the target
;; public keys. The base slot will be (queue_offset + (queue_head + i)*SLOTS_PER_ITEM).
dup4 ;; [head_idx, record_offset, i, ..]
dup3 ;; [i, head_idx, record_offset, i, ..]
add ;; [i+head_idx, record_offset, i, ..]
push SLOTS_PER_ITEM ;; [SLOTS_PER_ITEM, i+head_idx, record_offset, i, ..]
mul ;; [SLOTS_PER_ITEM*(i+head_idx), record_offset, i, ..]
push QUEUE_OFFSET ;; [offset, SLOTS_PER_ITEM*(i+head_idx), record_offset, i, ..]
add ;; [addr_offset, record_offset, i, ..]
;; Read address from slot 0.
dup1 ;; [addr_offset, addr_offset, record_offset, i, ..]
sload ;; [addr, addr_offset, record_offset, i, ..]
;; Read source[0:32] from slot 1.
swap1 ;; [addr_offset, addr, record_offset, i, ..]
push 1 ;; [1, addr_offset, addr, record_offset, i, ..]
add ;; [slot1_offset, addr, record_offset, i, ..]
dup1 ;; [slot1_offset, slot1_offset, addr, record_offset, i, ..]
sload ;; [source[0:32], slot1_offset, addr, record_offset, i, ..]
;; Read source[32:48] and target[0:16] from slot 2.
swap1 ;; [slot1_offset, source[0:32], addr, record_offset, i, ..]
push 1 ;; [1, slot1_offset, source[0:32], addr, record_offset, i, ..]
add ;; [slot2_offset, source[0:32], addr, record_offset, i, ..]
dup1 ;; [slot2_offset, slot2_offset, source[0:32], addr, record_offset, i, ..]
sload ;; [src[32:48] ++ tgt[0:16], slot2_offset, source[0:32], addr, record_offset, i, ..]
;; Read target[16:48] from slot 3.
swap1 ;; [slot2_offset, src[32:48] ++ tgt[0:16], source[0:32], addr, record_offset, i, ..]
push 1 ;; [1, slot2_offset, src[32:48] ++ tgt[0:16], source[0:32], addr, record_offset, i, ..]
add ;; [slot3_offset, src[32:48] ++ tgt[0:16], source[0:32], addr, record_offset, i, ..]
sload ;; [target[16:32], src[32:48] ++ tgt[0:16], source[0:32], addr, record_offset, i, ..]
;; Write values to memory flat and contiguously. This require combining the
;; four storage elements
;; (addr, source[0:32], source[32:48] ++ target[0:16], target[16:48])
;; so there is no padding.
;; Store request type to output.
push OUT_RECORD_TYPE ;; [type, target[16:32], src[32:48] ++ tgt[0:16], source[0:32], addr, record_offset, i, ..]
dup6 ;; [record_offset, type, target[16:32], src[32:48] ++ tgt[0:16], source[0:32], addr, record_offset, i, ..]
mstore8 ;; [target[16:32], src[32:48] ++ tgt[0:16], source[0:32], addr, record_offset, i, ..]
;; Shift addr bytes.
swap3 ;; [addr, src[32:48] ++ tgt[0:16], source[0:32], target[16:32], record_offset, i, ..]
push 12*8 ;; [96, addr, src[32:48] ++ tgt[0:16], source[0:32], target[16:32], record_offset, i, ..]
shl ;; [addr<<96, src[32:48] ++ tgt[0:16], source[0:32], target[16:32], record_offset, i, ..]
;; Store addr at offset = i*OUT_RECORD_SIZE + 1.
dup5 ;; [record_offset, addr<<96, src[32:48] ++ tgt[0:16], source[0:32], target[16:32], record_offset, i, ..]
push 1 ;; [1, record_offset, addr<<96, src[32:48] ++ tgt[0:16], source[0:32], target[16:32], record_offset, i, ..]
add ;; [record_offset+1, addr<<96, src[32:48] ++ tgt[0:16], source[0:32], target[16:32], record_offset, i, ..]
mstore ;; [src[32:48] ++ tgt[0:16], source[0:32], target[16:32], record_offset, i, ..]
;; Store source[0:32] at offset = i*OUT_RECORD_SIZE + 21.
swap1 ;; [source[0:32], src[32:48] ++ tgt[0:16], target[16:32], record_offset, i, ..]
dup4 ;; [record_offset, source[0:32], src[32:48] ++ tgt[0:16], target[16:32], record_offset, i, ..]
push 21 ;; [21, record_offset, source[0:32], src[32:48] ++ tgt[0:16], target[16:32], record_offset, i, ..]
add ;; [record_offset+21, source[0:32], src[32:48] ++ tgt[0:16], target[16:32], record_offset, i, ..]
mstore ;; [src[32:48] ++ tgt[0:16], target[16:32], record_offset, i, ..]
;; Store src[32:48] ++ tgt[0:16] at offset = i*OUT_RECORD_SIZE + 53.
dup3 ;; [record_offset, src[32:48] ++ tgt[0:16], target[16:32], record_offset, i, ..]
push 53 ;; [53, record_offset, src[32:48] ++ tgt[0:16], target[16:32], record_offset, i, ..]
add ;; [record_offset+53, src[32:48] ++ tgt[0:16], target[16:32], record_offset, i, ..]
mstore ;; [target[16:32], record_offset, i, ..]
;; Store target[16:48] at offset = i*OUT_RECORD_SIZE + 85.
swap1 ;; [record_offset, target[16:32], i, ..]
push 85 ;; [85, record_offset, target[16:32], i, ..]
add ;; [record_offset+85, target[16:32], i, ..]
mstore ;; [i, ..]
;; Increment i.
push 1 ;; [1, i, ..]
add ;; [i+1, ..]
jump @accum_loop ;; [i, count, head_idx, tail_idx]
update_head:
;; All requests have been read, update queue by adding the count read.
;; to the current head index.
swap2 ;; [head_idx, count, count, tail_idx]
add ;; [new_head_idx, count, tail_idx]
;; If the new head is equal to the tail, reset the queue by zeroing them both.
dup1 ;; [new_head_idx, new_head_idx, count, tail_idx]
swap3 ;; [tail_idx, new_head_idx, count, new_head_idx]
eq ;; [new_head_idx == tail_idx, count, new_head_idx]
jumpi @reset_queue ;; [count, new_head_idx]
;; Otherwise, write the new head to storage.
swap1 ;; [new_head_idx, count]
push QUEUE_HEAD ;; [head_idx_slot, new_head_idx, count]
sstore ;; [count]
jump @update_excess ;; [count]
reset_queue:
;; Since the queue is empty, both the head and tail indexes can be zeroed.
swap1 ;; [new_head_idx, count]
pop ;; [count]
push0 ;; [0, count]
push QUEUE_HEAD ;; [head_slot, 0, count]
sstore ;; [count]
push0 ;; [0, count]
push QUEUE_TAIL ;; [tail_slot, 0, count]
sstore ;; [count]
update_excess:
;; Update the new excess withdrawal requests.
push SLOT_EXCESS ;; [excess_slot, count]
sload ;; [excess, count]
;; Check if excess needs to be reset to 0 for first iteration after
;; activation.
dup1 ;; [excess, excess, count]
push EXCESS_INHIBITOR ;; [inhibitor, excess, excess, count]
eq ;; [inhibitor == excess, excess, count]
iszero ;; [inhibitor != excess, excess, count]
jumpi @skip_reset ;; [excess, count]
;; Drop the excess from storage and use 0.
pop ;; [count]
push0 ;; [reset_excess, count]
skip_reset:
push SLOT_COUNT ;; [count_slot, excess, count]
sload ;; [count, excess, count]
;; If the sum of the previous excess requests and requests added in the
;; current block is greater than the target, subtract the target from the sum
;; and set it as the new excess requests value.
push TARGET_PER_BLOCK ;; [target, count, excess, count]
dup3 ;; [excess, target, count, excess, count]
dup3 ;; [count, excess, target, count, excess, count]
add ;; [count+excess, target, count, excess, count]
gt ;; [count+excess > target, count, excess, count]
jumpi @compute_excess ;; [count, excess, count]
;; Zero out excess.
pop ;; [excess, count]
pop ;; [count]
push0
jump @store_excess
compute_excess:
add ;; [count+excess, count]
push TARGET_PER_BLOCK ;; [target, count+excess, count]
swap1 ;; [count+excess, target, count]
sub ;; [new_excess, count]
store_excess:
push SLOT_EXCESS ;; [excess_slot, new_excess, count]
sstore ;; [count]
;; Reset request count.
push0 ;; [0, count]
push SLOT_COUNT ;; [count_slot, 0, count]
sstore ;; [count]
;; Return the requests.
push OUT_RECORD_SIZE ;; [record_size, count]
mul ;; [size]
push0 ;; [0, size]
return ;; []
;; Revert subroutine.
revert:
push0
push0
revert