-
Notifications
You must be signed in to change notification settings - Fork 976
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
use custom storage for lockData #353
Changes from all commits
75769dd
ed5b1db
b26114a
d7146a0
bfb45fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
96005 | ||
95828 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
153358 | ||
152890 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
58562 | ||
58722 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
38009 | ||
37964 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
307152 | ||
306997 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
275102 | ||
274947 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
293760 | ||
293605 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
49720 | ||
49856 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
124982 | ||
124514 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
109692 | ||
109224 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
91502 | ||
91638 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
49691 | ||
49827 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
49720 | ||
49856 |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
pragma solidity ^0.8.20; | ||
|
||
/// @notice The LockData holds global information for the lock data structure: the length and nonzeroDeltaCount. | ||
/// @dev The left most 128 bits is the length, or total number of active lockers. | ||
/// @dev The right most 128 bits is the nonzeroDeltaCount, or total number of nonzero deltas over all active + completed lockers. | ||
/// @dev Located in transient storage. | ||
/// TODO: With transient storage experiment writing length and nonzeroDeltaCount to two separate slots and compare gas. | ||
type LockData is uint256; | ||
|
||
using LockDataLibrary for LockData global; | ||
|
||
function toLockData(uint128 _length, uint128 _nonzeroDeltaCount) pure returns (LockData lockData) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given its going to be transient storage, should we consider just putting these in different slots? Every function that writes to this slot, only updates 1 of the 2 values - they never both get updated. It feels like it would be easier to just separate them? Imo it might make more sense to have
where
this is more like how actual arrays are encoded in storage There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. interesting, yeah we could do that and compare gas. tload/tstore still 100 gas so its not crazy to pack though |
||
/// @solidity memory-safe-assembly | ||
assembly { | ||
lockData := | ||
or( | ||
shl(128, _length), | ||
and(0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff, _nonzeroDeltaCount) | ||
) | ||
} | ||
} | ||
|
||
/// @dev This library manages a custom storage implementation for a queue | ||
/// that tracks current lockers. The LOCK_DATA storage slot for this data structure, | ||
/// passed in as a LockData custom type that is a uint256, stores not just the current | ||
/// length of the queue but also the global count of non-zero deltas across all lockers. | ||
/// The values of the data structure start at LOCKERS, and each value is a locker address. | ||
library LockDataLibrary { | ||
snreynolds marked this conversation as resolved.
Show resolved
Hide resolved
|
||
using LockDataLibrary for LockData; | ||
|
||
uint256 private constant LOCK_DATA_SLOT = uint256(keccak256("LockData")); | ||
uint256 private constant LOCKERS_SLOT = uint256(keccak256("Lockers")); | ||
|
||
function length(LockData lockData) internal pure returns (uint128 _length) { | ||
/// @solidity memory-safe-assembly | ||
assembly { | ||
_length := shr(128, lockData) | ||
} | ||
} | ||
|
||
function nonzeroDeltaCount(LockData lockData) internal pure returns (uint128 _nonzeroDeltaCount) { | ||
/// @solidity memory-safe-assembly | ||
assembly { | ||
_nonzeroDeltaCount := lockData | ||
} | ||
} | ||
|
||
/// @dev Pushes a locker onto the end of the queue at the LOCKERS_SLOT + length storage slot, and updates the length at the LOCK_DATA storage slot. | ||
function push(LockData lockData, address locker) internal { | ||
// read current length from the LOCK_DATA storage slot | ||
uint128 _length = lockData.length(); | ||
unchecked { | ||
uint256 nextLockerSlot = LOCKERS_SLOT + _length; // not in assembly because assembly cannot access constants | ||
|
||
/// @solidity memory-safe-assembly | ||
assembly { | ||
// in the next storage slot, write the locker | ||
sstore(nextLockerSlot, locker) | ||
} | ||
LockData newLockData = toLockData(_length + 1, lockData.nonzeroDeltaCount()); | ||
newLockData.update(); | ||
} | ||
} | ||
|
||
/// @dev Pops a locker off the end of the queue. Note that no storage gets cleared. | ||
function pop(LockData lockData) internal { | ||
unchecked { | ||
LockData newLockData = toLockData(lockData.length() - 1, lockData.nonzeroDeltaCount()); | ||
newLockData.update(); | ||
} | ||
} | ||
|
||
/// @dev Decreases the nonzero delta count by 1. | ||
function decrementNonzeroDeltaCount(LockData lockData) internal { | ||
uint256 newLockData; | ||
unchecked { | ||
newLockData = LockData.unwrap(lockData) - 1; | ||
} | ||
LockData.wrap(newLockData).update(); | ||
} | ||
|
||
/// @dev Increases the nonzero delta count by 1. | ||
function incrementNonzeroDeltaCount(LockData lockData) internal { | ||
uint256 newLockData; | ||
unchecked { | ||
newLockData = LockData.unwrap(lockData) + 1; | ||
} | ||
LockData.wrap(newLockData).update(); | ||
} | ||
|
||
/// @dev Clears the length and nonzero delta count from the lockData. | ||
function clear() internal { | ||
LockData lockData = LockData.wrap(uint256(0)); | ||
lockData.update(); | ||
} | ||
|
||
/// @dev Writes the new length and nonzeroDeltaCount to storage. | ||
/// TODO: Use transient storage. | ||
function update(LockData lockData) internal { | ||
uint256 lockDataSlot = LOCK_DATA_SLOT; | ||
assembly { | ||
sstore(lockDataSlot, lockData) | ||
} | ||
} | ||
|
||
/// @dev Returns the locker at the ith index. | ||
/// TODO: Use transient storage. | ||
function getLock(uint256 i) internal view returns (address locker) { | ||
unchecked { | ||
uint256 position = LOCKERS_SLOT + i; // not in assembly because assembly can't read constants | ||
/// @solidity memory-safe-assembly | ||
assembly { | ||
locker := sload(position) | ||
} | ||
} | ||
} | ||
|
||
/// @dev Returns the current locker. | ||
function getActiveLock(LockData lockData) internal view returns (address) { | ||
uint128 _length; | ||
unchecked { | ||
_length = lockData.length() - 1; | ||
} | ||
return getLock(_length); | ||
} | ||
|
||
/// @dev Returns the current length and nonzeroDeltaCount. | ||
/// TODO: Use transient storage. | ||
function getLockData() internal view returns (LockData lockData) { | ||
uint256 lockDataSlot = LOCK_DATA_SLOT; | ||
assembly { | ||
lockData := sload(lockDataSlot) | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
something definitely feels a bit weird to me about using pop/push (write functionality) on a type memory uint256 that actually does storage writes. I wish there was a nice way to return a storage reference/pointer here instead of recaching below. Feels a bit off but can't think of any great solutions...this + update() feel a big "loosey goosey" if you will lol
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it is a bit wonky..let me know if you can think of another way, but I had to move away from a library for storage pointers because we don't yet have the
transient
keyword.We can rewrite the lib to use the transient keyword when there is solidity support.