Skip to content

Commit

Permalink
all: restructure project to better support multiple system contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
lightclient committed Jul 5, 2024
1 parent 37b4eca commit f03673e
Show file tree
Hide file tree
Showing 12 changed files with 155 additions and 143 deletions.
7 changes: 3 additions & 4 deletions build-wrapper
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ set -euf -o pipefail

SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )";

WITHDRAWAL_BYTECODE="$(geas "src/withdrawal.eas")"
FAKE_EXPO_BYTECODE="$(geas "src/fake_expo_test.eas")"

CONSOLODATION_BYTECODE="$(geas "src/consolidation.eas")"
WITHDRAWAL_BYTECODE="$(geas "src/withdrawals/main.eas")"
CONSOLODATION_BYTECODE="$(geas "src/consolidations/main.eas")"
FAKE_EXPO_BYTECODE="$(geas "src/common/fake_expo_test.eas")"

sed \
-e "s/@bytecode@/$WITHDRAWAL_BYTECODE/" \
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion src/consolidation.eas → src/consolidations/main.eas
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ check_input:
push SLOT_EXCESS ;; [excess_slot, update_fraction]
sload ;; [excess, update_fraction]
push MIN_FEE ;; [min_fee, excess, update_fraction]
#include "fake_expo.eas"
#include "../common/fake_expo.eas"

;; Determine if the fee provided is enough to cover the request fee.
callvalue ;; [callvalue, req_fee]
Expand Down
2 changes: 1 addition & 1 deletion src/withdrawal_ctor.eas → src/withdrawals/ctor.eas
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ push0
return

.start:
#assemble "withdrawal.eas"
#assemble "main.eas"
.end:
2 changes: 1 addition & 1 deletion src/withdrawal.eas → src/withdrawals/main.eas
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ check_input:
push SLOT_EXCESS ;; [excess_slot, update_fraction]
sload ;; [excess, update_fraction]
push MIN_FEE ;; [min_fee, excess, update_fraction]
#include "fake_expo.eas"
#include "../common/fake_expo.eas"

;; Determine if the fee provided is enough to cover the withdrawal request fee.
callvalue ;; [callvalue, req_fee]
Expand Down
51 changes: 25 additions & 26 deletions test/Consolidation.t.sol.in
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "./TestHelper.sol";
import "./Test.sol";

uint256 constant target_per_block = 1;
uint256 constant max_per_block = 1;

contract ConsolidationTest is TestHelper {
contract ConsolidationTest is Test {

function setUp() public {
vm.etch(addr, hex"@bytecode@");
Expand All @@ -28,9 +28,9 @@ contract ConsolidationTest is TestHelper {
assertEq(ret, false);
}

// testRequest verifies a single request below the target request
// count is accepted and read successfully.
function testRequest() public {
// testConsolidation verifies a single consolidation request below the target
// request count is accepted and read successfully.
function testConsolidation() public {
bytes memory data = hex"111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222";
(bool ret,) = addr.call{value: 2}(data);
assertEq(ret, true);
Expand All @@ -53,26 +53,26 @@ contract ConsolidationTest is TestHelper {
// Add more requests than the max per block (1) so that the queue is not
// immediately emptied.
for (uint256 i = 0; i < max_per_block+1; i++) {
addRequest(address(uint160(i)), makeRequest(i), 2);
addRequest(address(uint160(i)), makeConsolidation(i), 2);
}
assertStorage(count_slot, max_per_block+1, "unexpected request count");

// Simulate syscall, check that max requests per block are read.
checkRequests(0, max_per_block);
checkConsolidations(0, max_per_block);
assertExcess(1);

// Add another batch of max requests per block (1) so the next read leaves a
// single request in the queue.
for (uint256 i = 2; i < 3; i++) {
addRequest(address(uint160(i)), makeRequest(i), 2);
addRequest(address(uint160(i)), makeConsolidation(i), 2);
}
assertStorage(count_slot, max_per_block, "unexpected request count");

// Simulate syscall. Verify first that max per block are read. Then
// verify only the single final requst is read.
checkRequests(1, max_per_block);
checkConsolidations(1, max_per_block);
assertExcess(1);
checkRequests(2, 1);
checkConsolidations(2, 1);
assertExcess(0);

// Now ensure the queue is empty and has reset to zero.
Expand All @@ -82,12 +82,12 @@ contract ConsolidationTest is TestHelper {
// Add five (5) more requests to check that new requests can be added after the queue
// is reset.
for (uint256 i = 3; i < 8; i++) {
addRequest(address(uint160(i)), makeRequest(i), 4);
addRequest(address(uint160(i)), makeConsolidation(i), 4);
}
assertStorage(count_slot, 5, "unexpected request count");

// Simulate syscall, read only the max requests per block.
checkRequests(3, 1);
checkConsolidations(3, 1);
assertExcess(4);
}

Expand All @@ -99,10 +99,10 @@ contract ConsolidationTest is TestHelper {

// Add a bunch of requests.
for (; idx < count; idx++) {
addRequest(address(uint160(idx)), makeRequest(idx), 1);
addRequest(address(uint160(idx)), makeConsolidation(idx), 1);
}
assertStorage(count_slot, count, "unexpected request count");
checkRequests(0, max_per_block);
checkConsolidations(0, max_per_block);

uint256 read = max_per_block;
uint256 excess = count - target_per_block;
Expand All @@ -115,13 +115,13 @@ contract ConsolidationTest is TestHelper {

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

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

if (excess > 0 && idx % 2 != 0) {
excess--;
Expand Down Expand Up @@ -174,27 +174,27 @@ contract ConsolidationTest is TestHelper {
assertStorage(idx+3, toFixed(req, 64, 96), "target[16:48] not written to queue");
}

// checkRequest will simulate a system call to the system contract and verify
// the expected requests are returned.
// checkConsolidations will simulate a system call to the system contract and
// verify the expected consolidation requests are returned.
//
// It assumes that addresses are stored as uint256(index) and pubkeys are
// uint8(index), repeating.
function checkRequests(uint256 startIndex, uint256 count) internal returns (uint256) {
function checkConsolidations(uint256 startIndex, uint256 count) internal returns (uint256) {
bytes memory requests = getRequests();
assertEq(requests.length, count*116);
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(makeRequest(startIndex+i), 0, 32), "unexpected source[0:32] returned");
assertEq(toFixed(requests, offset+52, offset+84), toFixed(makeRequest(startIndex+i), 32, 64), "unexpected source[32:48] ++ target[0:16] returned");
assertEq(toFixed(requests, offset+84, offset+116), toFixed(makeRequest(startIndex+i), 64, 96), "unexpected target[16:48] 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");
}
return count;
}

function makeRequest(uint256 x) internal pure returns (bytes memory) {
// makeWithdrawal constructs a withdrawal request with a base of x.
function makeConsolidation(uint256 x) internal pure returns (bytes memory) {
bytes memory out = new bytes(96);

// source
for (uint256 i = 0; i < 48; i++) {
out[i] = bytes1(uint8(x));
Expand All @@ -203,7 +203,6 @@ contract ConsolidationTest is TestHelper {
for (uint256 i = 0; i < 48; i++) {
out[48 + i] = bytes1(uint8(x+1));
}

return out;
}
}
4 changes: 2 additions & 2 deletions test/FakeExpo.t.sol.in
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "./TestHelper.sol";
import "./Test.sol";

contract FakeExpoTest is TestHelper {
contract FakeExpoTest is Test {
function setUp() public {
vm.etch(fakeExpo, hex"@bytecode@");
}
Expand Down
87 changes: 87 additions & 0 deletions test/Test.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test as StdTest} from "forge-std/Test.sol";

address constant fakeExpo = 0x000000000000000000000000000000000000BbBB;
address constant addr = 0x000000000000000000000000000000000000aaaa;
address constant sysaddr = 0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE;

uint256 constant excess_slot = 0;
uint256 constant count_slot = 1;
uint256 constant queue_head_slot = 2;
uint256 constant queue_tail_slot = 3;
uint256 constant queue_storage_offset = 4;

// Test has some helper functions used by multiple system contract test suites.
abstract contract Test is StdTest {
// getRequests makes a call to the system contract as the system address in
// order to trigger a dequeue action.
function getRequests() internal returns (bytes memory) {
vm.prank(sysaddr);
(bool ret, bytes memory data) = addr.call("");
assertEq(ret, true);
return data;
}

// addFailedRequest submits a request to the system contract and expects it to
// fail.
function addFailedRequest(address from, bytes memory req, uint256 value) internal {
vm.deal(from, value);
vm.prank(from);
(bool ret,) = addr.call{value: value}(req);
assertEq(ret, false, "expected request to fail");
}

// load is a helper function to read a specific storage slot in the system
// contract.
function load(uint256 slot) internal view returns (uint256) {
return uint256(vm.load(addr, bytes32(slot)));
}

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

// assertExcess verifies the excess returned from storage and by calling the
// system contract matches count.
function assertExcess(uint256 count) internal {
assertStorage(excess_slot, count, "unexpected excess requests");
(, bytes memory data) = addr.call("");
assertEq(toFixed(data, 0, 32), count, "unexpected excess requests");
}
}

// min returns the minimum value of x and y.
function min(uint256 x, uint256 y) pure returns (uint256) {
if (x < y) {
return x;
}
return y;
}

// toFixed copys data from memory into a uint256. If the length is less than 32,
// the output is right-padded with zeros.
function toFixed(bytes memory data, uint256 start, uint256 end) pure returns (uint256) {
require(end-start <= 32, "range cannot be larger than 32 bytes");
bytes memory out = new bytes(32);
for (uint256 i = start; i < end; i++) {
out[i-start] = data[i];
}
return uint256(bytes32(out));
}

// computeFee calls the fake exponentiation contract with the specified
// parameters to determine the correctt fee value.
function computeFee(uint256 excess) returns (uint256) {
return callFakeExpo(1, int(excess), 17);
}

// callFakeExpo makes a raw call to the fake exponentiation contract.
function callFakeExpo(int factor, int numerator, int denominator) returns (uint256) {
(, bytes memory data) = fakeExpo.call(bytes.concat(bytes32(uint256(factor)), bytes32(uint256(numerator)), bytes32(uint256(denominator))));
return toFixed(data, 0, 32);
}
72 changes: 0 additions & 72 deletions test/TestHelper.sol

This file was deleted.

Loading

0 comments on commit f03673e

Please sign in to comment.