Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

consolidations, withdrawals: output request type #22

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 38 additions & 30 deletions src/consolidations/main.eas
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,18 @@
#define FEE_UPDATE_FRACTION 17
#define EXCESS_INHIBITOR 1181

#define INPUT_SIZE 96 ;; the size of (source ++ target)
#define RECORD_SIZE 116 ;; the size of (address ++ source ++ target)
#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.
;; address.
caller ;; [caller]
push20 SYSTEM_ADDR ;; [sysaddr, caller]
eq ;; [sysaddr == caller]
Expand Down Expand Up @@ -141,7 +143,6 @@ check_input:
swap1 ;; [slot, target[16:48], ..]
sstore ;; [..]


;; Assemble log data.
caller ;; [caller, ..]
push1 96 ;; [96, caller, ..]
Expand All @@ -154,7 +155,7 @@ check_input:
calldatacopy ;; [..]

;; Log record.
push1 RECORD_SIZE ;; [size, ..]
push1 LOG_RECORD_SIZE ;; [size, ..]
push0 ;; [idx, size, ..]
log0 ;; [..]

Expand All @@ -174,14 +175,14 @@ check_input:
;; 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 RECORD_SIZE bytes.
;; with each record being exactly 117 bytes.
;;
;; Consolidation request record:
;;
;; Consolidation request record:
;;
;; +------+--------+--------+
;; | addr | source | target |
;; +------+--------+--------+
;; 20 48 48
;; +------+------+--------+--------+
;; | 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.
Expand Down Expand Up @@ -216,17 +217,17 @@ begin_loop:
push0 ;; [i, count, head_idx, tail_idx]

accum_loop:
;; This loop will read each request and byte bang it into a RECORD_SIZE byte chunk.
;; This loop will read each request and byte bang it into a OUT_RECORD_SIZE byte chunk.
lightclient marked this conversation as resolved.
Show resolved Hide resolved

;; 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*RECORD_SIZE.
;; Precompute record_offset = i*OUT_RECORD_SIZE.
dup1 ;; [i, i, count, head_idx, tail_idx]
push RECORD_SIZE ;; [size, 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
Expand Down Expand Up @@ -269,32 +270,39 @@ accum_loop:
;; (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*RECORD_SIZE.

;; 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*RECORD_SIZE + 20.
;; 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 20 ;; [20, record_offset, source[0:32], src[32:48] ++ tgt[0:16], target[16:32], record_offset, i, ..]
add ;; [record_offset+20, 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*RECORD_SIZE + 52.
;; 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 52 ;; [52, record_offset, src[32:48] ++ tgt[0:16], target[16:32], record_offset, i, ..]
add ;; [record_offset+52, 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*RECORD_SIZE + 84.
;; Store target[16:48] at offset = i*OUT_RECORD_SIZE + 85.
swap1 ;; [record_offset, target[16:32], i, ..]
push 84 ;; [84, record_offset, target[16:32], i, ..]
add ;; [record_offset+84, 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.
Expand Down Expand Up @@ -339,7 +347,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]
Expand All @@ -365,11 +373,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:
Expand All @@ -388,7 +396,7 @@ store_excess:
sstore ;; [count]

;; Return the requests.
push RECORD_SIZE ;; [record_size, count]
push OUT_RECORD_SIZE ;; [record_size, count]
mul ;; [size]
push0 ;; [0, size]
return ;; []
Expand Down
55 changes: 32 additions & 23 deletions src/withdrawals/main.eas
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@
#define FEE_UPDATE_FRACTION 17
#define EXCESS_INHIBITOR 1181

#define INPUT_SIZE 56 ;; the size of (pubkey ++ amount)
#define RECORD_SIZE 76 ;; the size of (address ++ pubkey ++ amount)
#define INPUT_SIZE 56 ;; pubkey ++ amount
#define LOG_RECORD_SIZE 76 ;; address ++ pubkey ++ amount
#define OUT_RECORD_SIZE 77 ;; type ++ address ++ pubkey ++ amount
#define OUT_RECORD_TYPE 0x01

;; -----------------------------------------------------------------------------
;; PROGRAM START ---------------------------------------------------------------
Expand Down Expand Up @@ -154,7 +156,7 @@ check_input:
calldatacopy ;; [..]

;; Log record.
push1 RECORD_SIZE ;; [size, ..]
push1 LOG_RECORD_SIZE ;; [size, ..]
push0 ;; [idx, size, ..]
log0 ;; [..]

Expand All @@ -174,14 +176,14 @@ check_input:
;; This is the logic executed by the protocol each block. It reads as many
;; requests as available from the queue, until the max withdrawal request per
;; block is reached. The requests are returned as a contiguous array of bytes
;; with each record being exactly 76 bytes.
;; with each record being exactly 77 bytes.
;;
;; Withdrawal request record:
;; Withdrawal request record:
;;
;; +------+--------+--------+
;; | addr | pubkey | amount |
;; +------+--------+--------+
;; 20 48 8
;; +------+------+--------+--------+
;; | 0x01 | addr | pubkey | amount |
;; +------+------+--------+--------+
;; 1 20 48 8
;;
;; Because the requests are stored across three storage slots, there is some
;; shuffling to align the data.
Expand Down Expand Up @@ -216,17 +218,17 @@ begin_loop:
push0 ;; [i, count, head_idx, tail_idx]

accum_loop:
;; This loop will read each request and byte bang it into a 76 byte chunk.
;; This loop will read each request and stores them to the return buffer.

;; 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*RECORD_SIZE.
;; Precompute record_offset = i*OUT_RECORD_SIZE.
dup1 ;; [i, i, count, head_idx, tail_idx]
push RECORD_SIZE ;; [size, 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
Expand Down Expand Up @@ -279,40 +281,47 @@ accum_loop:
;;
;; (A[12:32] ++ B[0:12], B[12:32] ++ C[0:12], C[12:24])

;; Store request type at offset = i*OUT_RECORD_SIZE.
push OUT_RECORD_TYPE ;; [type, pk2_am, pk[0:32], addr, record_offset, i, ..]
dup5 ;; [record_offset, type, pk2_am, pk[0:32], addr, record_offset, i, ..]
mstore8 ;; [pk2_am, pk[0:32], addr, record_offset, i, ..]

;; Shift addr bytes.
swap2 ;; [addr, pk[0:32], pk2_am, record_offset, i, ..]
push 12*8 ;; [96, addr, pk[0:32], pk2_am, record_offset, i, ..]
shl ;; [addr<<96, pk[0:32], pk2_am, record_offset, i, ..]

;; Store addr at offset = i*RECORD_SIZE.
;; Store addr at offset = i*OUT_RECORD_SIZE + 1.
dup4 ;; [record_offset, addr<<96, pk[0:32], pk2_am, record_offset, i, ..]
push 1 ;; [1, record_offset, addr<<96, pk[0:32], pk2_am, record_offset, i, ..]
add ;; [record_offset+1, addr<<96, pk[0:32], pk2_am, record_offset, i, ..]
mstore ;; [pk[0:32], pk2_am, record_offset, i, ..]

;; Store pk[0:32] at offset = i*RECORD_SIZE + 20.
;; Store pk[0:32] at offset = i*OUT_RECORD_SIZE + 21.
dup3 ;; [record_offset, pk[0:32], pk2_am, record_offset, i, ..]
push 20 ;; [20, record_offset, pk[0:32], pk2_am, record_offset, i, ..]
add ;; [record_offset+20, pk[0:32], pk2_am, record_offset, i, ..]
push 21 ;; [21, record_offset, pk[0:32], pk2_am, record_offset, i, ..]
add ;; [record_offset+21, pk[0:32], pk2_am, record_offset, i, ..]
mstore ;; [pk2_am, record_offset, i, ..]

;; Extract pk2 from pk2_am.
dup1 ;; [pk2_am, pk2_am, record_offset, i, ..]
push pk2_mask ;; [mask, pk2_am, pk2_am, record_offset, i, ..]
and ;; [pk2, pk2_am, record_offset, i, ..]

;; Store pk2 at offset = i*RECORD_SIZE + 52.
;; Store pk2 at offset = i*OUT_RECORD_SIZE + 53.
dup3 ;; [record_offset, pk2, pk2_am, record_offset, i, ..]
push 52 ;; [52, record_offset, pk2, pk2_am, record_offset, i, ..]
add ;; [record_offset+52, pk2, pk2_am, record_offset, i, ..]
push 53 ;; [53, record_offset, pk2, pk2_am, record_offset, i, ..]
add ;; [record_offset+53, pk2, pk2_am, record_offset, i, ..]
mstore ;; [pk2_am, record_offset, i, ..]

;; Extract am from pk2_am.
push 8*8 ;; [shft, pk2_am, record_offset, i, ..]
shr ;; [am, record_offset, i, ..]

;; Store am at offset = i*RECORD_SIZE + 68.
;; Store am at offset = i*OUT_RECORD_SIZE + 69.
swap1 ;; [record_offset, am, i, ..]
push 68 ;; [68, record_offset, am, i, ..]
add ;; [record_offset+68, am, i, ..]
push 69 ;; [69, record_offset, am, i, ..]
add ;; [record_offset+69, am, i, ..]
%mstore_uint64_le() ;; [i, ..]

;; Increment i.
Expand Down Expand Up @@ -406,7 +415,7 @@ store_excess:
sstore ;; [count]

;; Return the withdrawal requests.
push RECORD_SIZE ;; [record_size, count]
push OUT_RECORD_SIZE ;; [record_size, count]
mul ;; [size]
push0 ;; [0, size]
return ;; []
Expand Down
31 changes: 17 additions & 14 deletions test/Consolidation.t.sol.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import "./Test.sol";
uint256 constant target_per_block = 1;
uint256 constant max_per_block = 1;

uint64 constant outputRecordSize = 117;

contract ConsolidationTest is Test {

function setUp() public {
Expand Down Expand Up @@ -49,10 +51,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));
assertEq(req.length, outputRecordSize);
assertEq(uint8(req[0]), 0x02, "wrong request type");
assertEq(bytes20(slice(req, 1, 21)), bytes20(address(this)), "wrong address");
assertEq(slice(req, 21, 117), data, "wrong request data");
assertStorage(count_slot, 0, "unexpected request count");
assertExcess(0);
}
Expand Down Expand Up @@ -119,18 +121,18 @@ contract ConsolidationTest is Test {
uint256 excess = count - target_per_block;

// Attempt to add an invalid request with fee too low or a valid request.
// This should cause the excess requests counter to either decrease by 1
// This should cause the excess requests counter to either decrease by 1
// or remain the same each iteration.
for (uint256 i = 0; i < count; i++) {
assertExcess(excess);

uint256 fee = computeFee(excess);
if (idx % 2 == 0) {
addRequest(address(uint160(idx)), makeConsolidation(idx), fee);
} else {
addFailedRequest(address(uint160(idx)), makeConsolidation(idx), fee-1);
}

uint256 expected = min(idx-read+1, max_per_block);
checkConsolidations(read, expected);

Expand All @@ -140,7 +142,6 @@ contract ConsolidationTest is Test {
read += expected;
idx++;
}

}

// testInhibitorRest verifies that after the first system call the excess
Expand Down Expand Up @@ -192,13 +193,15 @@ 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*outputRecordSize);

for (uint256 i = 0; i < count; i++) {
uint256 offset = i*116;
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");
assertEq(toFixed(requests, offset+84, offset+116), toFixed(makeConsolidation(startIndex+i), 64, 96), "unexpected target[16:48] returned");
uint256 offset = i*outputRecordSize;
assertEq(uint8(requests[offset]), 0x02, "unexpected request type returned");
assertEq(toFixed(requests, offset+1, offset+21) >> 96, uint256(startIndex+i), "unexpected request address returned");
assertEq(toFixed(requests, offset+21, offset+53), toFixed(makeConsolidation(startIndex+i), 0, 32), "unexpected source[0:32] returned");
assertEq(toFixed(requests, offset+53, offset+85), toFixed(makeConsolidation(startIndex+i), 32, 64), "unexpected source[32:48] ++ target[0:16] returned");
assertEq(toFixed(requests, offset+85, offset+117), toFixed(makeConsolidation(startIndex+i), 64, 96), "unexpected target[16:48] returned");
}
return count;
}
Expand Down
11 changes: 11 additions & 0 deletions test/Test.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading
Loading