diff --git a/src/consolidations/main.eas b/src/consolidations/main.eas index a37ec01..d976907 100644 --- a/src/consolidations/main.eas +++ b/src/consolidations/main.eas @@ -29,13 +29,15 @@ #define INPUT_SIZE 96 ;; the size of (source ++ target) #define RECORD_SIZE 116 ;; the size of (address ++ source ++ target) +#define REQUEST_TYPE 0x02 + ;; ----------------------------------------------------------------------------- ;; PROGRAM START --------------------------------------------------------------- ;; ----------------------------------------------------------------------------- .start: ;; Protect the system subroutine by checking if the caller is the system - ;; address. + ;; address. caller ;; [caller] push20 SYSTEM_ADDR ;; [sysaddr, caller] eq ;; [sysaddr == caller] @@ -184,7 +186,7 @@ check_input: ;; with each record being exactly RECORD_SIZE bytes. ;; ;; Consolidation request record: -;; +;; ;; +------+--------+--------+ ;; | addr | source | target | ;; +------+--------+--------+ @@ -220,7 +222,13 @@ read_requests: push MAX_PER_BLOCK ;; [count, head_idx, tail_idx] begin_loop: - push0 ;; [i, count, head_idx, tail_idx] + ;; Store request type prefix into the output buffer. + push1 REQUEST_TYPE ;; [type, count, head_idx, tail_idx] + push 0 ;; [offset, type, count, head_idx, tail_idx] + mstore8 ;; [count, head_idx, tail_idx] + + ;; Push initial loop index. + push 0 ;; [i, count, head_idx, tail_idx] accum_loop: ;; This loop will read each request and byte bang it into a RECORD_SIZE byte chunk. @@ -234,7 +242,9 @@ accum_loop: ;; Precompute record_offset = i*RECORD_SIZE. dup1 ;; [i, i, count, head_idx, tail_idx] push RECORD_SIZE ;; [size, i, i, count, head_idx, tail_idx] - mul ;; [record_offset, i, count, head_idx, tail_idx] + mul ;; [offset, i, count, head_idx, tail_idx] + push 1 ;; [1, offset, i, count, head_idx, tail_idx] + add ;; [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 @@ -280,7 +290,7 @@ accum_loop: 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*RECORD_SIZE. dup5 ;; [record_offset, 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, ..] @@ -346,7 +356,7 @@ 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] @@ -372,11 +382,11 @@ skip_reset: 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 + push0 jump @store_excess compute_excess: @@ -396,7 +406,9 @@ store_excess: ;; Return the requests. push RECORD_SIZE ;; [record_size, count] - mul ;; [size] + mul ;; [size, count] + push 1 ;; [1, size, count] + add ;; [size+1, count] push0 ;; [0, size] return ;; [] diff --git a/src/withdrawals/main.eas b/src/withdrawals/main.eas index ba90e2e..66808e5 100644 --- a/src/withdrawals/main.eas +++ b/src/withdrawals/main.eas @@ -39,6 +39,8 @@ #define INPUT_SIZE 56 ;; the size of (pubkey ++ amount) #define RECORD_SIZE 76 ;; the size of (address ++ pubkey ++ amount) +#define REQUEST_TYPE 0x01 + ;; ----------------------------------------------------------------------------- ;; PROGRAM START --------------------------------------------------------------- ;; ----------------------------------------------------------------------------- @@ -220,7 +222,13 @@ read_requests: push MAX_PER_BLOCK ;; [count, head_idx, tail_idx] begin_loop: - push0 ;; [i, count, head_idx, tail_idx] + ;; Store request type prefix into the output buffer. + push1 REQUEST_TYPE ;; [type, count, head_idx, tail_idx] + push 0 ;; [offset, type, count, head_idx, tail_idx] + mstore8 ;; [count, head_idx, tail_idx] + + ;; Push initial loop index. + push 0 ;; [i, count, head_idx, tail_idx] accum_loop: ;; This loop will read each request and byte bang it into a 76 byte chunk. @@ -231,10 +239,12 @@ accum_loop: eq ;; [i == count, i, count, head_idx, tail_idx] jumpi @update_head ;; [i, count, head_idx, tail_idx] - ;; Precompute record_offset = i*RECORD_SIZE. + ;; Precompute record_offset = i*RECORD_SIZE + 1. dup1 ;; [i, i, count, head_idx, tail_idx] push RECORD_SIZE ;; [size, i, i, count, head_idx, tail_idx] - mul ;; [record_offset, i, count, head_idx, tail_idx] + mul ;; [offset, i, count, head_idx, tail_idx] + push 1 ;; [1, offset, i, count, head_idx, tail_idx] + add ;; [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 two storage slots containing the public key and @@ -414,7 +424,9 @@ store_excess: ;; Return the withdrawal requests. push RECORD_SIZE ;; [record_size, count] - mul ;; [size] + mul ;; [size, count] + push 1 ;; [1, size, count] + add ;; [size+1, count] push0 ;; [0, size] return ;; [] diff --git a/test/Consolidation.t.sol.in b/test/Consolidation.t.sol.in index 8678b0b..1f94b17 100644 --- a/test/Consolidation.t.sol.in +++ b/test/Consolidation.t.sol.in @@ -50,11 +50,10 @@ contract ConsolidationTest is Test { assertExcess(0); bytes memory req = getRequests(); - assertEq(req.length, 116); - assertEq(toFixed(req, 20, 52), toFixed(data, 0, 32)); - assertEq(toFixed(req, 52, 84), toFixed(data, 32, 64)); - assertEq(toFixed(req, 84, 116), toFixed(data, 64, 96)); - assertStorage(count_slot, 0, "unexpected request count"); + assertEq(req.length, 117); + assertEq(uint8(req[0]), 0x02); + assertEq(slice(req, 21, 117), data, "wrong output request data"); + assertStorage(count_slot, 0, "unexpected request count in storage"); assertExcess(0); } @@ -196,9 +195,9 @@ contract ConsolidationTest is Test { // uint8(index), repeating. function checkConsolidations(uint256 startIndex, uint256 count) internal returns (uint256) { bytes memory requests = getRequests(); - assertEq(requests.length, count*116); + assertEq(requests.length, count*116 + 1); for (uint256 i = 0; i < count; i++) { - uint256 offset = i*116; + uint256 offset = i*116 + 1; assertEq(toFixed(requests, offset, offset+20) >> 96, uint256(startIndex+i), "unexpected request address returned"); assertEq(toFixed(requests, offset+20, offset+52), toFixed(makeConsolidation(startIndex+i), 0, 32), "unexpected source[0:32] returned"); assertEq(toFixed(requests, offset+52, offset+84), toFixed(makeConsolidation(startIndex+i), 32, 64), "unexpected source[32:48] ++ target[0:16] returned"); diff --git a/test/Test.sol b/test/Test.sol index 87a3455..cbeeba9 100644 --- a/test/Test.sol +++ b/test/Test.sol @@ -74,6 +74,17 @@ function toFixed(bytes memory data, uint256 start, uint256 end) pure returns (ui return uint256(bytes32(out)); } +// slice copys data from memory. If the length is less than 32, +// the output is right-padded with zeros. +function slice(bytes memory data, uint start, uint end) pure returns (bytes memory) { + require(end > start, "invalid range (end > start)"); + bytes memory out = new bytes(end-start); + for (uint i = start; i < end; i++) { + out[i-start] = data[i]; + } + return out; +} + // computeFee calls the fake exponentiation contract with the specified // parameters to determine the correctt fee value. function computeFee(uint256 excess) returns (uint256) { diff --git a/test/Withdrawal.t.sol.in b/test/Withdrawal.t.sol.in index e229901..49c8f71 100644 --- a/test/Withdrawal.t.sol.in +++ b/test/Withdrawal.t.sol.in @@ -52,11 +52,12 @@ contract WithdrawalsTest is Test { assertExcess(0); bytes memory req = getRequests(); - assertEq(req.length, 76); - assertEq(bytes20(req), bytes20(address(this))); // check addr - assertEq(toFixed(req, 20, 52), toFixed(exp_req, 0, 32)); // check pk1 - assertEq(toFixed(req, 52, 68), toFixed(exp_req, 32, 48)); // check pk2 - assertEq(toFixed(req, 68, 76), toFixed(exp_req, 48, 56)); // check amt + assertEq(req.length, 77); + assertEq(uint8(req[0]), 0x01, "wrong request type prefix"); + assertEq(bytes20(slice(req, 1, 21)), bytes20(address(this)), "wrong address"); + assertEq(toFixed(req, 21, 53), toFixed(exp_req, 0, 32), "wrong pk1"); + assertEq(toFixed(req, 53, 69), toFixed(exp_req, 32, 48), "wrong pk2"); + assertEq(toFixed(req, 69, 77), toFixed(exp_req, 48, 56), "wrong amount"); assertStorage(count_slot, 0, "unexpected request count"); assertExcess(0); } @@ -197,10 +198,11 @@ contract WithdrawalsTest is Test { function checkWithdrawals(uint256 startIndex, uint256 count) internal returns (uint256) { bytes memory amountBuffer = new bytes(8); bytes memory requests = getRequests(); - assertEq(requests.length, count*76); + assertEq(requests.length, count*76 + 1); + assertEq(uint8(requests[0]), 0x01); for (uint256 i = 0; i < count; i++) { - uint256 offset = i*76; + uint256 offset = i*76 + 1; uint256 wdIndex = startIndex + i; bytes memory wd = makeWithdrawal(wdIndex);