Commit 21678e30 authored by clabby's avatar clabby

perf: Pack clock & position

parent d2933a35
......@@ -36,9 +36,9 @@ FaultDisputeGame_Test:test_extraData_succeeds() (gas: 17456)
FaultDisputeGame_Test:test_gameData_succeeds() (gas: 17882)
FaultDisputeGame_Test:test_gameStart_succeeds() (gas: 10315)
FaultDisputeGame_Test:test_gameType_succeeds() (gas: 8237)
FaultDisputeGame_Test:test_initialRootClaimData_succeeds() (gas: 19522)
FaultDisputeGame_Test:test_rootClaim_succeeds() (gas: 8225)
FaultDisputeGame_Test:test_simpleAttack_succeeds() (gas: 129033)
FaultDisputeGame_Test:test_initialRootClaimData_succeeds() (gas: 17602)
FaultDisputeGame_Test:test_rootClaim_succeeds() (gas: 8213)
FaultDisputeGame_Test:test_simpleAttack_succeeds() (gas: 105905)
FaultDisputeGame_Test:test_version_succeeds() (gas: 9736)
FeeVault_Test:test_constructor_succeeds() (gas: 18185)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 352135)
......
......@@ -16,7 +16,7 @@ library LibClock {
*/
function wrap(Duration _duration, Timestamp _timestamp) internal pure returns (Clock _clock) {
assembly {
_clock := or(shl(0x80, _duration), _timestamp)
_clock := or(shl(0x40, _duration), _timestamp)
}
}
......@@ -26,9 +26,9 @@ library LibClock {
* @return _duration The `Duration` pulled out of `_clock`.
*/
function duration(Clock _clock) internal pure returns (Duration _duration) {
// Shift the high-order 128 bits into the low-order 128 bits, leaving only the `duration`.
// Shift the high-order 64 bits into the low-order 64 bits, leaving only the `duration`.
assembly {
_duration := shr(0x80, _clock)
_duration := shr(0x40, _clock)
}
}
......@@ -38,10 +38,10 @@ library LibClock {
* @return _timestamp The `Timestamp` pulled out of `_clock`.
*/
function timestamp(Clock _clock) internal pure returns (Timestamp _timestamp) {
// Clean the high-order 128 bits by shifting the clock left and then right again, leaving
// Clean the high-order 192 bits by shifting the clock left and then right again, leaving
// only the `timestamp`.
assembly {
_timestamp := shr(0x80, shl(0x80, _clock))
_timestamp := shr(0xC0, shl(0xC0, _clock))
}
}
}
......@@ -8,30 +8,30 @@ import "../../libraries/DisputeTypes.sol";
* @notice This library contains helper functions for working with the `Position` type.
*/
library LibPosition {
function wrap(uint128 _depth, uint128 _indexAtDepth) internal pure returns (Position _position) {
function wrap(uint64 _depth, uint64 _indexAtDepth) internal pure returns (Position _position) {
assembly {
_position := or(shl(0x80, _depth), _indexAtDepth)
_position := or(shl(0x40, _depth), _indexAtDepth)
}
}
/**
* @notice Pulls the `depth` out of a packed `Position` type.
*/
function depth(Position position) internal pure returns (uint128 _depth) {
// Shift the high-order 128 bits into the low-order 128 bits, leaving only the `depth`.
function depth(Position position) internal pure returns (uint64 _depth) {
// Shift the high-order 64 bits into the low-order 64 bits, leaving only the `depth`.
assembly {
_depth := shr(0x80, position)
_depth := shr(0x40, position)
}
}
/**
* @notice Pulls the `indexAtDepth` out of a packed `Position` type.
*/
function indexAtDepth(Position position) internal pure returns (uint128 _indexAtDepth) {
// Clean the high-order 128 bits by shifting the position left and then right again, leaving
function indexAtDepth(Position position) internal pure returns (uint64 _indexAtDepth) {
// Clean the high-order 192 bits by shifting the position left and then right again, leaving
// only the `indexAtDepth`.
assembly {
_indexAtDepth := shr(0x80, shl(0x80, position))
_indexAtDepth := shr(0xC0, shl(0xC0, position))
}
}
......@@ -41,12 +41,12 @@ library LibPosition {
* @return _left The position to the left of `position`.
*/
function left(Position position) internal pure returns (Position _left) {
uint128 _depth = depth(position);
uint128 _indexAtDepth = indexAtDepth(position);
uint64 _depth = depth(position);
uint64 _indexAtDepth = indexAtDepth(position);
// Left = { depth: position.depth + 1, indexAtDepth: position.indexAtDepth * 2 }
assembly {
_left := or(shl(0x80, add(_depth, 0x01)), shl(0x01, _indexAtDepth))
_left := or(shl(0x40, add(_depth, 0x01)), shl(0x01, _indexAtDepth))
}
}
......@@ -56,12 +56,12 @@ library LibPosition {
* @return _right The position to the right of `position`.
*/
function right(Position position) internal pure returns (Position _right) {
uint128 _depth = depth(position);
uint128 _indexAtDepth = indexAtDepth(position);
uint64 _depth = depth(position);
uint64 _indexAtDepth = indexAtDepth(position);
// Right = { depth: position.depth + 1, indexAtDepth: position.indexAtDepth * 2 + 1 }
assembly {
_right := or(shl(0x80, add(_depth, 0x01)), add(shl(0x01, _indexAtDepth), 0x01))
_right := or(shl(0x40, add(_depth, 0x01)), add(shl(0x01, _indexAtDepth), 0x01))
}
}
......@@ -71,12 +71,12 @@ library LibPosition {
* @return _parent The parent position of `position`.
*/
function parent(Position position) internal pure returns (Position _parent) {
uint128 _depth = depth(position);
uint128 _indexAtDepth = indexAtDepth(position);
uint64 _depth = depth(position);
uint64 _indexAtDepth = indexAtDepth(position);
// Parent = { depth: position.depth - 1, indexAtDepth: position.indexAtDepth / 2 }
assembly {
_parent := or(shl(0x80, sub(_depth, 0x01)), shr(0x01, _indexAtDepth))
_parent := or(shl(0x40, sub(_depth, 0x01)), shr(0x01, _indexAtDepth))
}
}
......@@ -87,12 +87,12 @@ library LibPosition {
* @return _rightIndex The deepest, right most index relative to the `position`.
* TODO: Optimize; No need to update the full position in the sub loop.
*/
function rightIndex(Position position, uint256 maxDepth) internal pure returns (uint128 _rightIndex) {
function rightIndex(Position position, uint256 maxDepth) internal pure returns (uint64 _rightIndex) {
assembly {
_rightIndex := shr(0x80, shl(0x80, position))
_rightIndex := shr(0xC0, shl(0xC0, position))
// Walk down to the max depth by moving right
for { let i := shr(0x80, position) } lt(i, sub(maxDepth, 0x01)) { i := add(i, 0x01) } {
for { let i := shr(0x40, position) } lt(i, sub(maxDepth, 0x01)) { i := add(i, 0x01) } {
_rightIndex := add(0x01, shl(0x01, _rightIndex))
}
}
......@@ -109,12 +109,12 @@ library LibPosition {
* @notice Get the defend position of `position`.
*/
function defend(Position position) internal pure returns (Position _defend) {
uint128 _depth = depth(position);
uint128 _indexAtDepth = indexAtDepth(position);
uint64 _depth = depth(position);
uint64 _indexAtDepth = indexAtDepth(position);
// Defend = { depth: position.depth + 1, indexAtDepth: ((position.indexAtDepth / 2) * 2 + 1) * 2 }
assembly {
_defend := or(shl(0x80, add(_depth, 0x01)), shl(0x01, add(0x01, shl(0x01, shr(0x01, _indexAtDepth)))))
_defend := or(shl(0x40, add(_depth, 0x01)), shl(0x01, add(0x01, shl(0x01, shr(0x01, _indexAtDepth)))))
}
}
}
......@@ -39,11 +39,11 @@ type Duration is uint64;
* ┌────────────┬────────────────┐
* │ Bits │ Value │
* ├────────────┼────────────────┤
* │ [0, 128) │ Duration │
* │ [128, 256) │ Timestamp │
* │ [0, 64) │ Duration │
* │ [64, 128) │ Timestamp │
* └────────────┴────────────────┘
*/
type Clock is uint256;
type Clock is uint128;
/**
* @notice A `Position` represents a position of a claim within the game tree.
......@@ -51,11 +51,11 @@ type Clock is uint256;
* ┌────────────┬────────────────┐
* │ Bits │ Value │
* ├────────────┼────────────────┤
* │ [0, 128) │ Depth │
* │ [128, 256) │ Index at depth │
* │ [0, 64) │ Depth │
* │ [64, 128) │ Index at depth │
* └────────────┴────────────────┘
*/
type Position is uint256;
type Position is uint128;
/**
* @notice A `GameType` represents the type of game being played.
......
......@@ -12,16 +12,16 @@ contract LibClock_Test is Test {
/**
* @notice Tests that the `duration` function correctly shifts out the `Duration` from a packed `Clock` type.
*/
function testFuzz_duration_succeeds(uint64 _duration, uint64 _timestamp) public {
Clock clock = LibClock.wrap(Duration.wrap(_duration), Timestamp.wrap(_timestamp));
assertEq(Duration.unwrap(LibClock.duration(clock)), _duration);
function testFuzz_duration_succeeds(Duration _duration, Timestamp _timestamp) public {
Clock clock = LibClock.wrap(_duration, _timestamp);
assertEq(Duration.unwrap(LibClock.duration(clock)), Duration.unwrap(_duration));
}
/**
* @notice Tests that the `timestamp` function correctly shifts out the `Timestamp` from a packed `Clock` type.
*/
function testFuzz_timestamp_succeeds(uint64 _duration, uint64 _timestamp) public {
Clock clock = LibClock.wrap(Duration.wrap(_duration), Timestamp.wrap(_timestamp));
assertEq(Timestamp.unwrap(LibClock.timestamp(clock)), _timestamp);
function testFuzz_timestamp_succeeds(Duration _duration, Timestamp _timestamp) public {
Clock clock = LibClock.wrap(_duration, _timestamp);
assertEq(Timestamp.unwrap(LibClock.timestamp(clock)), Timestamp.unwrap(_timestamp));
}
}
......@@ -10,16 +10,16 @@ import "../libraries/DisputeTypes.sol";
*/
contract LibPosition_Test is Test {
/**
* @dev Assumes a MAX depth of 127 for the Position type. Any greater depth can cause overflows.
* @dev At the lowest level of the tree, this allows for 2 ** 127 leaves. In reality, the max game depth
* @dev Assumes a MAX depth of 63 for the Position type. Any greater depth can cause overflows.
* @dev At the lowest level of the tree, this allows for 2 ** 63 leaves. In reality, the max game depth
* will likely be much lower.
*/
uint8 internal constant MAX_DEPTH = 127;
uint8 internal constant MAX_DEPTH = 63;
/**
* @notice Tests that the `depth` function correctly shifts out the `depth` from a packed `Position` type.
*/
function testFuzz_depth_correctness(uint128 _depth, uint128 _indexAtDepth) public {
function testFuzz_depth_correctness(uint64 _depth, uint64 _indexAtDepth) public {
Position position = LibPosition.wrap(_depth, _indexAtDepth);
assertEq(LibPosition.depth(position), _depth);
}
......@@ -27,7 +27,7 @@ contract LibPosition_Test is Test {
/**
* @notice Tests that the `indexAtDepth` function correctly shifts out the `indexAtDepth` from a packed `Position` type.
*/
function testFuzz_indexAtDepth_correctness(uint128 _depth, uint128 _indexAtDepth) public {
function testFuzz_indexAtDepth_correctness(uint64 _depth, uint64 _indexAtDepth) public {
Position position = LibPosition.wrap(_depth, _indexAtDepth);
assertEq(LibPosition.indexAtDepth(position), _indexAtDepth);
}
......@@ -35,27 +35,27 @@ contract LibPosition_Test is Test {
/**
* @notice Tests that the `left` function correctly computes the position of the left child.
*/
function testFuzz_left_correctness(uint8 _depth, uint128 _indexAtDepth) public {
// Depth bound: [0, 127]
function testFuzz_left_correctness(uint8 _depth, uint64 _indexAtDepth) public {
// Depth bound: [0, 63]
_depth = uint8(bound(_depth, 0, MAX_DEPTH));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint128(bound(_indexAtDepth, 0, 2**_depth));
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
Position left = LibPosition.left(position);
assertEq(LibPosition.depth(left), uint128(_depth) + 1);
assertEq(LibPosition.depth(left), uint64(_depth) + 1);
assertEq(LibPosition.indexAtDepth(left), _indexAtDepth * 2);
}
/**
* @notice Tests that the `right` function correctly computes the position of the right child.
*/
function testFuzz_right_correctness(uint8 _depth, uint128 _indexAtDepth) public {
// Depth bound: [0, 127]
function testFuzz_right_correctness(uint8 _depth, uint64 _indexAtDepth) public {
// Depth bound: [0, 63]
_depth = uint8(bound(_depth, 0, MAX_DEPTH));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint128(bound(_indexAtDepth, 0, 2**_depth));
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
Position right = LibPosition.right(position);
......@@ -67,11 +67,11 @@ contract LibPosition_Test is Test {
/**
* @notice Tests that the `parent` function correctly computes the position of the parent.
*/
function testFuzz_parent_correctness(uint8 _depth, uint128 _indexAtDepth) public {
// Depth bound: [1, 127]
function testFuzz_parent_correctness(uint8 _depth, uint64 _indexAtDepth) public {
// Depth bound: [1, 63]
_depth = uint8(bound(_depth, 1, MAX_DEPTH));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint128(bound(_indexAtDepth, 0, 2**_depth));
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
Position parent = LibPosition.parent(position);
......@@ -87,24 +87,24 @@ contract LibPosition_Test is Test {
function testFuzz_rightIndex_correctness(
uint8 _maxDepth,
uint8 _depth,
uint128 _indexAtDepth
uint64 _indexAtDepth
) public {
// Max depth bound: [1, 127]
// Max depth bound: [1, 63]
// The max game depth MUST be at least 1.
_maxDepth = uint8(bound(_maxDepth, 1, MAX_DEPTH));
// Depth bound: [0, _maxDepth]
_depth = uint8(bound(_depth, 0, _maxDepth));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint128(bound(_indexAtDepth, 0, 2**_depth));
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
uint128 rightIndex = LibPosition.rightIndex(position, _maxDepth);
uint64 rightIndex = LibPosition.rightIndex(position, _maxDepth);
// Find the deepest, rightmost index in Solidity rather than Yul
for (uint256 i = _depth; i < _maxDepth - 1; ++i) {
position = LibPosition.right(position);
}
uint128 _rightIndex = LibPosition.indexAtDepth(position);
uint64 _rightIndex = LibPosition.indexAtDepth(position);
assertEq(rightIndex, _rightIndex);
}
......@@ -114,11 +114,11 @@ contract LibPosition_Test is Test {
* a given position.
* @dev `attack` is an alias for `left`, but we test it separately for completeness.
*/
function testFuzz_attack_correctness(uint8 _depth, uint128 _indexAtDepth) public {
// Depth bound: [0, 127]
function testFuzz_attack_correctness(uint8 _depth, uint64 _indexAtDepth) public {
// Depth bound: [0, 63]
_depth = uint8(bound(_depth, 0, MAX_DEPTH));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint128(bound(_indexAtDepth, 0, 2**_depth));
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
Position attack = LibPosition.attack(position);
......@@ -133,11 +133,11 @@ contract LibPosition_Test is Test {
* @dev A defense can only be given if the position does not belong to the root claim, hence the bound of [1, 127]
* on the depth.
*/
function testFuzz_defend_correctness(uint8 _depth, uint128 _indexAtDepth) public {
// Depth bound: [1, 127]
function testFuzz_defend_correctness(uint8 _depth, uint64 _indexAtDepth) public {
// Depth bound: [1, 63]
_depth = uint8(bound(_depth, 1, MAX_DEPTH));
// Index at depth bound: [0, 2 ** _depth]
_indexAtDepth = uint128(bound(_indexAtDepth, 0, 2**_depth));
_indexAtDepth = uint64(bound(_indexAtDepth, 0, 2**_depth));
Position position = LibPosition.wrap(_depth, _indexAtDepth);
Position defend = LibPosition.defend(position);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment