Skip to content

Commit 005f2fe

Browse files
authored
Merge pull request #20 from fjl/withdrawals-le
withdrawals: return amount as little-endian
2 parents 982a735 + d841b22 commit 005f2fe

File tree

3 files changed

+130
-33
lines changed

3 files changed

+130
-33
lines changed

src/withdrawals/main.eas

Lines changed: 100 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
;; ██╔╝ ████╔╝██║████╔╝██║██╔═══╝ ██╔══██║╚════██║██║╚██╔╝██║
55
;; ██║ ╚██████╔╝╚██████╔╝███████╗ ██║ ██║███████║██║ ╚═╝ ██║
66
;; ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
7-
;;
7+
;;
88
;; This is an implementation of EIP-7002's pre-deploy contract. It implements an
99
;; unvalidated withdrawal requests queue for beacon chain validators. The queue
1010
;; is tracked using head and tail index pointers. After the queue is emptied,
@@ -45,7 +45,7 @@
4545

4646
.start:
4747
;; Protect the system subroutine by checking if the caller is the system
48-
;; address.
48+
;; address.
4949
caller ;; [caller]
5050
push20 SYSTEM_ADDR ;; [sysaddr, caller]
5151
eq ;; [sysaddr == caller]
@@ -177,7 +177,7 @@ check_input:
177177
;; with each record being exactly 76 bytes.
178178
;;
179179
;; Withdrawal request record:
180-
;;
180+
;;
181181
;; +------+--------+--------+
182182
;; | addr | pubkey | amount |
183183
;; +------+--------+--------+
@@ -240,7 +240,7 @@ accum_loop:
240240
push QUEUE_OFFSET ;; [offset, 3*(i+head_idx), record_offset, i, ..]
241241
add ;; [addr_offset, record_offset, i, ..]
242242

243-
;; Read address.
243+
;; Read address.
244244
dup1 ;; [addr_offset, addr_offset, record_offset, i, ..]
245245
sload ;; [addr, addr_offset, record_offset, i, ..]
246246

@@ -259,13 +259,13 @@ accum_loop:
259259

260260
;; Write values to memory flat and contiguously. This require combining the
261261
;; three storage elements (addr, pk[0:32], pk2_am) so there is no padding.
262-
;;
262+
;;
263263
;; Each stack element has the following layout:
264264
;;
265265
;; A: addr
266-
;; 0x00 | 00 00 00 00 00 00 00 00 00 00 00 00 aa aa aa aa
266+
;; 0x00 | 00 00 00 00 00 00 00 00 00 00 00 00 aa aa aa aa
267267
;; 0x10 | aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
268-
;;
268+
;;
269269
;; B: pk[0:32]
270270
;; 0x00 | bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb
271271
;; 0x10 | bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb
@@ -281,9 +281,9 @@ accum_loop:
281281

282282
;; Shift addr bytes.
283283
swap2 ;; [addr, pk[0:32], pk2_am, record_offset, i, ..]
284-
push 12*8 ;; [96, addr, pk0:32], pk2_am, record_offset, i, ..]
284+
push 12*8 ;; [96, addr, pk[0:32], pk2_am, record_offset, i, ..]
285285
shl ;; [addr<<96, pk[0:32], pk2_am, record_offset, i, ..]
286-
286+
287287
;; Store addr at offset = i*RECORD_SIZE.
288288
dup4 ;; [record_offset, addr<<96, pk[0:32], pk2_am, record_offset, i, ..]
289289
mstore ;; [pk[0:32], pk2_am, record_offset, i, ..]
@@ -294,11 +294,26 @@ accum_loop:
294294
add ;; [record_offset+20, pk[0:32], pk2_am, record_offset, i, ..]
295295
mstore ;; [pk2_am, record_offset, i, ..]
296296

297-
;; Store pk2_am at offset = i*RECORD_SIZE + 52.
298-
swap1 ;; [record_offset, pk2_am, i, ..]
299-
push 52 ;; [52, record_offset, pk2_am, i, ..]
300-
add ;; [record_offset+52, pk2_am, i, ..]
301-
mstore ;; [i, ..]
297+
;; Extract pk2 from pk2_am.
298+
dup1 ;; [pk2_am, pk2_am, record_offset, i, ..]
299+
push pk2_mask ;; [mask, pk2_am, pk2_am, record_offset, i, ..]
300+
and ;; [pk2, pk2_am, record_offset, i, ..]
301+
302+
;; Store pk2 at offset = i*RECORD_SIZE + 52.
303+
dup3 ;; [record_offset, pk2, pk2_am, record_offset, i, ..]
304+
push 52 ;; [52, record_offset, pk2, pk2_am, record_offset, i, ..]
305+
add ;; [record_offset+52, pk2, pk2_am, record_offset, i, ..]
306+
mstore ;; [pk2_am, record_offset, i, ..]
307+
308+
;; Extract am from pk2_am.
309+
push 8*8 ;; [shft, pk2_am, record_offset, i, ..]
310+
shr ;; [am, record_offset, i, ..]
311+
312+
;; Store am at offset = i*RECORD_SIZE + 68.
313+
swap1 ;; [record_offset, am, i, ..]
314+
push 68 ;; [68, record_offset, am, i, ..]
315+
add ;; [record_offset+68, am, i, ..]
316+
%mstore_uint64_le() ;; [i, ..]
302317

303318
;; Increment i.
304319
push 1 ;; [1, i, ..]
@@ -342,7 +357,7 @@ update_excess:
342357
;; Update the new excess withdrawal requests.
343358
push SLOT_EXCESS ;; [excess_slot, count]
344359
sload ;; [excess, count]
345-
360+
346361
;; Check if excess needs to be reset to 0 for first iteration after
347362
;; activation.
348363
dup1 ;; [excess, excess, count, count]
@@ -368,11 +383,11 @@ skip_reset:
368383
add ;; [count+excess, target, count, excess, count]
369384
gt ;; [count+excess > target, count, excess, count]
370385
jumpi @compute_excess ;; [count, excess, count]
371-
386+
372387
;; Zero out excess.
373388
pop ;; [excess, count]
374389
pop ;; [count]
375-
push0
390+
push0
376391
jump @store_excess
377392

378393
compute_excess:
@@ -401,3 +416,71 @@ revert:
401416
push0
402417
push0
403418
revert
419+
420+
;; -----------------------------------------------------------------------------
421+
;; MACROS ----------------------------------------------------------------------
422+
;; -----------------------------------------------------------------------------
423+
424+
;; This defines a mask for accessing the top 16 bytes of a number.
425+
#define pk2_mask 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000
426+
427+
;; Helper for storing little-endian amount.
428+
#define %mstore_uint64_le() { ;; [offset, value]
429+
dup2 ;; [value, offset, value]
430+
push 7*8 ;; [56, value, offset, value]
431+
shr ;; [value>>56, offset, value]
432+
dup2 ;; [offset, value>>56, offset, value]
433+
push 7 ;; [7, offset, value>>56, offset, value]
434+
add ;; [offset+7, value>>56, offset, value]
435+
mstore8 ;; [offset, value]
436+
437+
dup2 ;; [value, offset, value]
438+
push 6*8 ;; [48, value, offset, value]
439+
shr ;; [value>>48, offset, value]
440+
dup2 ;; [offset, value>>48, offset, value]
441+
push 6 ;; [6, offset, value>>48, offset, value]
442+
add ;; [offset+6, value>>48, offset, value]
443+
mstore8 ;; [offset, value]
444+
445+
dup2 ;; [value, offset, value]
446+
push 5*8 ;; [40, value, offset, value]
447+
shr ;; [value>>40, offset, value]
448+
dup2 ;; [offset, value>>40, offset, value]
449+
push 5 ;; [2, offset, value>>40, offset, value]
450+
add ;; [offset+5, value>>40, offset, value]
451+
mstore8 ;; [offset, value]
452+
453+
dup2 ;; [value, offset, value]
454+
push 4*8 ;; [32, value, offset, value]
455+
shr ;; [value>>32, offset, value]
456+
dup2 ;; [offset, value>>32, offset, value]
457+
push 4 ;; [4, offset, value>>32, offset, value]
458+
add ;; [offset+4, value>>32, offset, value]
459+
mstore8 ;; [offset, value]
460+
461+
dup2 ;; [value, offset, value]
462+
push 3*8 ;; [24, value, offset, value]
463+
shr ;; [value>>24, offset, value]
464+
dup2 ;; [offset, value>>24, offset, value]
465+
push 3 ;; [3, offset, value>>24, offset, value]
466+
add ;; [offset+3, value>>24, offset, value]
467+
mstore8 ;; [offset, value]
468+
469+
dup2 ;; [value, offset, value]
470+
push 2*8 ;; [16, value, offset, value]
471+
shr ;; [value>>16, offset, value]
472+
dup2 ;; [offset, value>>16, offset, value]
473+
push 2 ;; [2, offset, value>>16, offset, value]
474+
add ;; [offset+2, value>>16, offset, value]
475+
mstore8 ;; [offset, value]
476+
477+
dup2 ;; [value, offset, value]
478+
push 1*8 ;; [8, value, offset, value]
479+
shr ;; [value>>8, offset, value]
480+
dup2 ;; [offset, value>>8, offset, value]
481+
push 1 ;; [1, offset, value>>8, offset, value]
482+
add ;; [offset+1, value>>8, offset, value]
483+
mstore8 ;; [offset, value]
484+
485+
mstore8 ;; []
486+
}

test/Test.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ abstract contract Test is StdTest {
4141

4242
// assertStorage reads a value from the system contract and asserts it is
4343
// equal to the provided value.
44-
function assertStorage(uint256 slot, uint256 value, string memory err) internal {
44+
function assertStorage(uint256 slot, uint256 value, string memory err) internal view {
4545
bytes32 got = vm.load(addr, bytes32(slot));
4646
assertEq(got, bytes32(value), err);
4747
}

test/Withdrawal.t.sol.in

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ contract WithdrawalsTest is Test {
3333
// testWithdrawal verifies a single withdrawal request below the target request
3434
// count is accepted and read successfully.
3535
function testWithdrawal() public {
36-
bytes memory data = hex"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112222222222222222";
36+
bytes memory data = hex"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110203040506070809";
37+
bytes memory exp_req = hex"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110908070605040302";
3738

3839
vm.expectEmitAnonymous(false, false, false, false, true);
3940
assembly {
@@ -51,8 +52,10 @@ contract WithdrawalsTest is Test {
5152

5253
bytes memory req = getRequests();
5354
assertEq(req.length, 76);
54-
assertEq(toFixed(req, 20, 52), toFixed(data, 0, 32));
55-
assertEq(toFixed(req, 52, 76), toFixed(data, 32, 56));
55+
assertEq(bytes20(req), bytes20(address(this))); // check addr
56+
assertEq(toFixed(req, 20, 52), toFixed(exp_req, 0, 32)); // check pk1
57+
assertEq(toFixed(req, 52, 68), toFixed(exp_req, 32, 48)); // check pk2
58+
assertEq(toFixed(req, 68, 76), toFixed(exp_req, 48, 56)); // check amt
5659
assertStorage(count_slot, 0, "unexpected request count");
5760
assertExcess(0);
5861
}
@@ -124,7 +127,7 @@ contract WithdrawalsTest is Test {
124127
// counter to decrease by 1 each iteration.
125128
for (uint256 i = 0; i < count; i++) {
126129
assertExcess(excess);
127-
130+
128131
uint256 fee = computeFee(excess);
129132
addFailedRequest(address(uint160(idx)), makeWithdrawal(idx), fee-1);
130133
addRequest(address(uint160(idx)), makeWithdrawal(idx), fee);
@@ -188,29 +191,40 @@ contract WithdrawalsTest is Test {
188191
// It assumes that addresses are stored as uint256(index) and pubkeys are
189192
// uint8(index), repeating.
190193
function checkWithdrawals(uint256 startIndex, uint256 count) internal returns (uint256) {
194+
bytes memory amountBuffer = new bytes(8);
191195
bytes memory requests = getRequests();
192196
assertEq(requests.length, count*76);
197+
193198
for (uint256 i = 0; i < count; i++) {
194199
uint256 offset = i*76;
195-
assertEq(toFixed(requests, offset, offset+20) >> 96, uint256(startIndex+i), "unexpected request address returned");
196-
assertEq(toFixed(requests, offset+20, offset+52), toFixed(makeWithdrawal(startIndex+i), 0, 32), "unexpected request pk returned");
197-
assertEq(toFixed(requests, offset+52, offset+68), toFixed(makeWithdrawal(startIndex+i), 32, 48), "unexpected request pk returned");
198-
assertEq(toFixed(requests, offset+68, offset+76), toFixed(makeWithdrawal(startIndex+i), 48, 56), "unexpected request amount returned");
200+
uint256 wdIndex = startIndex + i;
201+
bytes memory wd = makeWithdrawal(wdIndex);
202+
203+
// Check address, pubkey.
204+
assertEq(toFixed(requests, offset, offset+20) >> 96, uint256(wdIndex), "unexpected request address returned");
205+
assertEq(toFixed(requests, offset+20, offset+52), toFixed(wd, 0, 32), "unexpected request pk1 returned");
206+
assertEq(toFixed(requests, offset+52, offset+68), toFixed(wd, 32, 48), "unexpected request pk2 returned");
207+
208+
// Check amount.
209+
for (uint j = 0; j < 8; j++) {
210+
amountBuffer[j] = requests[offset+68+j];
211+
}
212+
bytes memory wantAmount = hex"de852726f6fb9f2d";
213+
assertEq(amountBuffer, wantAmount, "unexpected request amount returned");
199214
}
215+
200216
return count;
201217
}
202218

203219
// makeWithdrawal constructs a withdrawal request with a base of x.
204220
function makeWithdrawal(uint256 x) internal pure returns (bytes memory) {
205-
bytes memory out = new bytes(56);
206-
// pubkey
221+
bytes memory pk = new bytes(48);
207222
for (uint256 i = 0; i < 48; i++) {
208-
out[i] = bytes1(uint8(x));
209-
}
210-
// amount
211-
for (uint256 i = 0; i < 8; i++) {
212-
out[48 + i] = bytes1(uint8(x+1));
223+
pk[i] = bytes1(uint8(x));
213224
}
225+
bytes memory amt = hex"2d9ffbf6262785de";
226+
bytes memory out = bytes.concat(pk, amt);
227+
require(out.length == 56);
214228
return out;
215229
}
216230
}

0 commit comments

Comments
 (0)