Skip to content

Commit

Permalink
Clear bits before OR'ing new values
Browse files Browse the repository at this point in the history
  • Loading branch information
0xfornax committed Aug 26, 2024
1 parent 3a55a4f commit ea4f086
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 44 deletions.
92 changes: 56 additions & 36 deletions contracts/contract/util/AddressLinkedQueueStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ contract AddressLinkedQueueStorage is RocketBase, AddressLinkedQueueStorageInter
uint256 constant internal receiverOffset = 256 - 160;
uint256 constant internal indexOffset = 256 - 160 - 32;
uint256 constant internal suppliedOffset = 256 - 160 - 32 - 32;

uint64 constant internal ones64Bits = 0xFFFFFFFFFFFFFFFF;

// Construct
constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) {
Expand All @@ -26,26 +28,26 @@ contract AddressLinkedQueueStorage is RocketBase, AddressLinkedQueueStorageInter

/// @notice The number of items in the queue
/// @param _namespace defines the queue to be used
function getLength(bytes32 _namespace) override public view returns (uint) {
return getUint(keccak256(abi.encodePacked(_namespace, ".data"))) >> lengthOffset;
function getLength(bytes32 _namespace) override public view returns (uint256) {
return uint256(uint64(getUint(keccak256(abi.encodePacked(_namespace, ".data"))) >> lengthOffset));
}

/// @notice The item in a queue by index
/// @param _namespace defines the queue to be used
/// @param _index the item index
function getItem(bytes32 _namespace, uint _index) override external view returns (DepositQueueValue memory) {
uint index = getUint(keccak256(abi.encodePacked(_namespace, ".data"))) >> startOffset + _index;
uint packedValue = getUint(keccak256(abi.encodePacked(_namespace, ".item", index)));
function getItem(bytes32 _namespace, uint256 _index) override external view returns (DepositQueueValue memory) {
uint256 index = getUint(keccak256(abi.encodePacked(_namespace, ".data"))) >> startOffset + _index;
uint256 packedValue = getUint(keccak256(abi.encodePacked(_namespace, ".item", index)));
return unpackDepositQueueValue(packedValue);
}

/// @notice The index of an item in a queue. Returns -1 if the value is not found
/// @param _namespace defines the queue to be used
/// @param _value the deposit queue value
function getIndexOf(bytes32 _namespace, DepositQueueValue memory _value) override external view returns (int) {
uint index = getUint(keccak256(abi.encodePacked(_namespace, ".index", _value.receiver, _value.validatorIndex)));
uint256 index = getUint(keccak256(abi.encodePacked(_namespace, ".index", _value.receiver, _value.validatorId)));
if (index > 0) {
return int(index);
return int256(index);
}
return -1;
}
Expand All @@ -54,9 +56,9 @@ contract AddressLinkedQueueStorage is RocketBase, AddressLinkedQueueStorageInter
/// @param _namespace defines the queue to be used
/// @param _value the deposit queue value
function getPreviousItem(bytes32 _namespace, DepositQueueValue memory _value) external view returns (DepositQueueValue memory previousItem) {
uint index = getUint(keccak256(abi.encodePacked(_namespace, ".index", _value.receiver, _value.validatorIndex)));
uint256 index = getUint(keccak256(abi.encodePacked(_namespace, ".index", _value.receiver, _value.validatorId)));
if (index > 0) {
uint previousIndex = getUint(keccak256(abi.encodePacked(_namespace, ".prev", index)));
uint256 previousIndex = getUint(keccak256(abi.encodePacked(_namespace, ".prev", index)));
previousItem = unpackDepositQueueValue(getUint(keccak256(abi.encodePacked(_namespace, ".item", previousIndex))));
}
}
Expand All @@ -65,9 +67,9 @@ contract AddressLinkedQueueStorage is RocketBase, AddressLinkedQueueStorageInter
/// @param _namespace defines the queue to be used
/// @param _value the deposit queue value
function getNextItem(bytes32 _namespace, DepositQueueValue memory _value) external view returns (DepositQueueValue memory nextItem) {
uint index = getUint(keccak256(abi.encodePacked(_namespace, ".index", _value.receiver, _value.validatorIndex)));
uint256 index = getUint(keccak256(abi.encodePacked(_namespace, ".index", _value.receiver, _value.validatorId)));
if (index > 0) {
uint nextIndex = getUint(keccak256(abi.encodePacked(_namespace, ".next", index)));
uint256 nextIndex = getUint(keccak256(abi.encodePacked(_namespace, ".next", index)));
nextItem = unpackDepositQueueValue(getUint(keccak256(abi.encodePacked(_namespace, ".item", nextIndex))));
}
}
Expand All @@ -77,49 +79,61 @@ contract AddressLinkedQueueStorage is RocketBase, AddressLinkedQueueStorageInter
/// @param _value the deposit queue value
function enqueueItem(bytes32 _namespace, DepositQueueValue memory _value) virtual override external {
// onlyLatestContract("addressLinkedListStorage", address(this)) onlyLatestNetworkContract {
require(getUint(keccak256(abi.encodePacked(_namespace, ".index", _value.receiver, _value.validatorIndex))) == 0, "Item already exists in queue");
uint data = getUint(keccak256(abi.encodePacked(_namespace, ".data")));
uint endIndex = data >> endOffset;
uint newIndex = endIndex + 1;
require(getUint(keccak256(abi.encodePacked(_namespace, ".index", _value.receiver, _value.validatorId))) == 0, "Item already exists in queue");
uint256 data = getUint(keccak256(abi.encodePacked(_namespace, ".data")));
uint256 endIndex = uint64(data >> endOffset);
uint256 newIndex = endIndex + 1;

if (endIndex > 0) {
setUint(keccak256(abi.encodePacked(_namespace, ".next", endIndex)), newIndex);
setUint(keccak256(abi.encodePacked(_namespace, ".prev", newIndex)), endIndex);
} else {
// clear the 64 bits used to stored the 'start' pointer
data &= ~(uint256(ones64Bits) << startOffset);
data |= newIndex << startOffset;
}

setUint(keccak256(abi.encodePacked(_namespace, ".item", newIndex)), packDepositQueueValue(_value));
setUint(keccak256(abi.encodePacked(_namespace, ".index", _value.receiver, _value.validatorIndex)), newIndex);
setUint(keccak256(abi.encodePacked(_namespace, ".index", _value.receiver, _value.validatorId)), newIndex);
// clear the 64 bits used to stored the 'end' pointer
data &= ~(uint256(ones64Bits) << endOffset);
data |= newIndex << endOffset;

// Update the length of the queue
uint currentLength = data >> lengthOffset;
uint256 currentLength = uint64(data >> lengthOffset);
// clear the 64 bits used to stored the 'length' information
data &= ~(uint256(ones64Bits) << lengthOffset);
data |= (currentLength + 1) << lengthOffset;
setUint(keccak256(abi.encodePacked(_namespace, ".data")), data);
}

/// @notice Remove an item from the start of a queue and return it. Requires that the queue is not empty
/// @param _namespace defines the queue to be used
function dequeueItem(bytes32 _namespace) public virtual override onlyLatestContract("addressLinkedListStorage", address(this)) onlyLatestNetworkContract returns (address) {
function dequeueItem(bytes32 _namespace) public virtual override onlyLatestContract("addressLinkedListStorage", address(this)) onlyLatestNetworkContract returns (DepositQueueValue memory item) {
require(getLength(_namespace) > 0, "Queue is empty");
uint data = getUint(keccak256(abi.encodePacked(_namespace, ".data")));
uint start = data >> startOffset;
address item = getAddress(keccak256(abi.encodePacked(_namespace, ".item", start)));
uint256 data = getUint(keccak256(abi.encodePacked(_namespace, ".data")));
uint256 start = uint64(data >> startOffset);
uint256 packedItem = getUint(keccak256(abi.encodePacked(_namespace, ".item", start)));
item = unpackDepositQueueValue(packedItem);

uint nextItem = getUint(keccak256(abi.encodePacked(_namespace, ".next", item)));
uint256 nextItem = getUint(keccak256(abi.encodePacked(_namespace, ".next", start)));
// clear the 64 bits used to stored the 'start' pointer
data &= ~(uint256(ones64Bits) << startOffset);
data |= nextItem << startOffset;
setUint(keccak256(abi.encodePacked(_namespace, ".index", item)), 0);
setUint(keccak256(abi.encodePacked(_namespace, ".index", item.receiver, item.validatorId)), 0);

if (nextItem > 0) {
setUint(keccak256(abi.encodePacked(_namespace, ".prev", nextItem)), 0);
} else {
data |= 0 << endOffset;
// zero the 64 bits storing the 'end' pointer
data &= ~(uint256(ones64Bits) << endOffset);
}

// Update the length of the queue
uint currentLength = data >> lengthOffset;
data |= (currentLength + 1) << lengthOffset;
uint256 currentLength = uint64(data >> lengthOffset);
// clear the 64 bits used to stored the 'length' information
data &= ~(uint256(ones64Bits) << lengthOffset);
data |= (currentLength -1) << lengthOffset;
setUint(keccak256(abi.encodePacked(_namespace, ".data")), data);

return item;
Expand All @@ -128,17 +142,19 @@ contract AddressLinkedQueueStorage is RocketBase, AddressLinkedQueueStorageInter
/// @notice Removes an item from a queue. Requires that the item exists in the queue
/// @param _namespace defines the queue to be used
function removeItem(bytes32 _namespace, DepositQueueValue memory _value) public virtual override onlyLatestContract("addressLinkedListStorage", address(this)) onlyLatestNetworkContract {
uint index = getUint(keccak256(abi.encodePacked(_namespace, ".index", _value.receiver, _value.validatorIndex)));
uint data = getUint(keccak256(abi.encodePacked(_namespace, ".data")));
uint256 index = getUint(keccak256(abi.encodePacked(_namespace, ".index", _value.receiver, _value.validatorId)));
uint256 data = getUint(keccak256(abi.encodePacked(_namespace, ".data")));
require(index > 0, "Item does not exist in queue");

uint prevIndex = getUint(keccak256(abi.encodePacked(_namespace, ".prev", index)));
uint nextIndex = getUint(keccak256(abi.encodePacked(_namespace, ".next", index)));
uint256 prevIndex = getUint(keccak256(abi.encodePacked(_namespace, ".prev", index)));
uint256 nextIndex = getUint(keccak256(abi.encodePacked(_namespace, ".next", index)));
if (prevIndex > 0) {
// Not the first item
setUint(keccak256(abi.encodePacked(_namespace, ".next", prevIndex)), nextIndex);
} else {
// First item
// clear the 64 bits used to stored the 'start' pointer
data &= ~(uint256(ones64Bits) << startOffset);
data |= nextIndex << startOffset;
setUint(keccak256(abi.encodePacked(_namespace, ".prev", nextIndex)), 0);
}
Expand All @@ -148,24 +164,28 @@ contract AddressLinkedQueueStorage is RocketBase, AddressLinkedQueueStorageInter
setUint(keccak256(abi.encodePacked(_namespace, ".prev", nextIndex)), prevIndex);
} else {
// Last item
// clear the 64 bits used to stored the 'end' pointer
data &= ~(uint256(ones64Bits) << endOffset);
data |= prevIndex << endOffset;
}

setUint(keccak256(abi.encodePacked(_namespace, ".index", _value.receiver, _value.validatorIndex)), 0);
setUint(keccak256(abi.encodePacked(_namespace, ".index", _value.receiver, _value.validatorId)), 0);
setUint(keccak256(abi.encodePacked(_namespace, ".next", index)), 0);
setUint(keccak256(abi.encodePacked(_namespace, ".prev", index)), 0);

// Update the length of the queue
uint currentLength = data >> lengthOffset;
data |= (currentLength + 1) << lengthOffset;
uint256 currentLength = uint64(data >> lengthOffset);
// clear the 64 bits used to stored the 'length' information
data &= ~(uint256(ones64Bits) << lengthOffset);
data |= (currentLength - 1) << lengthOffset;
setUint(keccak256(abi.encodePacked(_namespace, ".data")), data);
}

/// @notice packs a deposit queue value into a single uint256
/// @param _struct the deposit queue value to be packed
function packDepositQueueValue(DepositQueueValue memory _struct) internal pure returns (uint256 packed) {
packed |= uint256(uint160(_struct.receiver)) << receiverOffset;
packed |= uint256(_struct.validatorIndex) << indexOffset;
packed |= uint256(_struct.validatorId) << indexOffset;
packed |= uint256(_struct.suppliedValue) << suppliedOffset;
packed |= uint256(_struct.requestedValue);
}
Expand All @@ -174,7 +194,7 @@ contract AddressLinkedQueueStorage is RocketBase, AddressLinkedQueueStorageInter
/// @param _packedValue the packed deposit queue value
function unpackDepositQueueValue(uint256 _packedValue) internal pure returns (DepositQueueValue memory value) {
value.receiver = address(uint160(_packedValue >> receiverOffset));
value.validatorIndex = uint32(_packedValue >> indexOffset);
value.validatorId = uint32(_packedValue >> indexOffset);
value.suppliedValue = uint32(_packedValue >> suppliedOffset);
value.requestedValue = uint32(_packedValue);
}
Expand Down
16 changes: 8 additions & 8 deletions contracts/interface/util/AddressLinkedQueueStorageInterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ pragma abicoder v2;
// SPDX-License-Identifier: GPL-3.0-only

struct DepositQueueValue {
address receiver;
uint32 validatorIndex;
address receiver; // the megapool address
uint32 validatorId; // internal validator id
uint32 suppliedValue; // in milliether
uint32 requestedValue; // in milliether
}

interface AddressLinkedQueueStorageInterface {
function getLength(bytes32 _key) external view returns (uint);
function getItem(bytes32 _key, uint _index) external view returns (DepositQueueValue memory);
function getIndexOf(bytes32 _key, DepositQueueValue memory _value) external view returns (int);
function enqueueItem(bytes32 _key, DepositQueueValue memory _value) external;
function dequeueItem(bytes32 _key) external returns (address);
function removeItem(bytes32 _key, DepositQueueValue memory _value) external;
function getLength(bytes32 _namespace) external view returns (uint);
function getItem(bytes32 _namespace, uint _index) external view returns (DepositQueueValue memory);
function getIndexOf(bytes32 _namespace, DepositQueueValue memory _value) external view returns (int);
function enqueueItem(bytes32 _namespace, DepositQueueValue memory _value) external;
function dequeueItem(bytes32 _namespace) external returns (DepositQueueValue memory);
function removeItem(bytes32 _namespace, DepositQueueValue memory _value) external;
}

0 comments on commit ea4f086

Please sign in to comment.