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

feat: remediations #6

Merged
merged 3 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
79 changes: 78 additions & 1 deletion src/SentinelList.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Sentinel address
address constant SENTINEL = address(0x1);
// Zero address
address constant ZERO_ADDRESS = address(0x0);

/**
* @title SentinelListLib
* @dev Library for managing a linked list of addresses
* @author Rhinestone
*/
library SentinelListLib {
// Struct to hold the linked list
struct SentinelList {
mapping(address => address) entries;
}
Expand All @@ -14,22 +22,48 @@
error LinkedList_InvalidEntry(address entry);
error LinkedList_EntryAlreadyInList(address entry);

/**
* Initialize the linked list
*
* @param self The linked list
*/
function init(SentinelList storage self) internal {
if (alreadyInitialized(self)) revert LinkedList_AlreadyInitialized();
self.entries[SENTINEL] = SENTINEL;
}

/**
* Check if the linked list is already initialized
*
* @param self The linked list
*
* @return bool True if the linked list is already initialized
*/
function alreadyInitialized(SentinelList storage self) internal view returns (bool) {
return self.entries[SENTINEL] != ZERO_ADDRESS;
}

/**
* Get the next entry in the linked list
*
* @param self The linked list
* @param entry The current entry
*
* @return address The next entry
*/
function getNext(SentinelList storage self, address entry) internal view returns (address) {
if (entry == ZERO_ADDRESS) {
revert LinkedList_InvalidEntry(entry);
}
return self.entries[entry];
}

/**
* Push a new entry to the linked list
*
* @param self The linked list
* @param newEntry The new entry
*/
function push(SentinelList storage self, address newEntry) internal {
if (newEntry == ZERO_ADDRESS || newEntry == SENTINEL) {
revert LinkedList_InvalidEntry(newEntry);
Expand All @@ -39,6 +73,27 @@
self.entries[SENTINEL] = newEntry;
}

/**
* Safe push a new entry to the linked list
* @dev This ensures that the linked list is initialized and initializes it if it is not
*
* @param self The linked list
* @param newEntry The new entry
*/
function safePush(SentinelList storage self, address newEntry) internal {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOTE: fixes L4

if (!alreadyInitialized({ self: self })) {
init({ self: self });
}
push({ self: self, newEntry: newEntry });
}

/**
* Pop an entry from the linked list
*
* @param self The linked list
* @param prevEntry The entry before the entry to pop
* @param popEntry The entry to pop
*/
function pop(SentinelList storage self, address prevEntry, address popEntry) internal {
if (popEntry == ZERO_ADDRESS || popEntry == SENTINEL) {
revert LinkedList_InvalidEntry(prevEntry);
Expand All @@ -48,20 +103,42 @@
self.entries[popEntry] = ZERO_ADDRESS;
}

/**
* Pop all entries from the linked list
*
* @param self The linked list
*/
function popAll(SentinelList storage self) internal {
address next = self.entries[SENTINEL];
while (next != ZERO_ADDRESS) {
address current = next;
next = self.entries[next];
self.entries[current] = ZERO_ADDRESS;
}
self.entries[SENTINEL] = ZERO_ADDRESS;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOTE: fixes I7

}

/**
* Check if the linked list contains an entry
*
* @param self The linked list
* @param entry The entry to check
*
* @return bool True if the linked list contains the entry
*/
function contains(SentinelList storage self, address entry) internal view returns (bool) {
return SENTINEL != entry && self.entries[entry] != ZERO_ADDRESS;
}

/**
* Get all entries in the linked list
*
* @param self The linked list
* @param start The start entry
* @param pageSize The page size
*
* @return array All entries in the linked list
* @return next The next entry
*/
function getEntriesPaginated(
SentinelList storage self,
address start,
Expand Down Expand Up @@ -104,7 +181,7 @@
// Set correct size of returned array
// solhint-disable-next-line no-inline-assembly
/// @solidity memory-safe-assembly
assembly {

Check warning on line 184 in src/SentinelList.sol

View workflow job for this annotation

GitHub Actions / lint / forge-lint

Avoid to use inline assembly. It is acceptable only in rare cases
mstore(array, entryCount)
}
}
Expand Down
91 changes: 87 additions & 4 deletions src/SentinelList4337.sol
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Sentinel address
address constant SENTINEL = address(0x1);
// Zero address
address constant ZERO_ADDRESS = address(0x0);

/**
* Implements a linked list, but adheres to ERC-4337 storage restrictions.
* Intended use: validator modules for modular ERC-4337 smart accounts
* @author kopy-kat | rhinestone.wtf
* @title SentinelListLib
* @dev Library for managing a linked list of addresses that is compliant with the ERC-4337
* validation rules
* @author Rhinestone
*/
library SentinelList4337Lib {
// Struct to hold the linked list
// This linked list has the account address as the inner key so it is ERC-4337 compliant
struct SentinelList {
mapping(address key => mapping(address account => address entry)) entries;
}
Expand All @@ -19,11 +24,25 @@
error LinkedList_InvalidEntry(address entry);
error LinkedList_EntryAlreadyInList(address entry);

/**
* Initialize the linked list
*
* @param self The linked list
* @param account The account to initialize the linked list for
*/
function init(SentinelList storage self, address account) internal {
if (alreadyInitialized(self, account)) revert LinkedList_AlreadyInitialized();
self.entries[SENTINEL][account] = SENTINEL;
}

/**
* Check if the linked list is already initialized
*
* @param self The linked list
* @param account The account to check if the linked list is initialized for
*
* @return bool True if the linked list is already initialized
*/
function alreadyInitialized(
SentinelList storage self,
address account
Expand All @@ -35,6 +54,15 @@
return self.entries[SENTINEL][account] != ZERO_ADDRESS;
}

/**
* Get the next entry in the linked list
*
* @param self The linked list
* @param account The account to get the next entry for
* @param entry The current entry
*
* @return address The next entry
*/
function getNext(
SentinelList storage self,
address account,
Expand All @@ -50,6 +78,13 @@
return self.entries[entry][account];
}

/**
* Push a new entry to the linked list
*
* @param self The linked list
* @param account The account to push the new entry for
* @param newEntry The new entry
*/
function push(SentinelList storage self, address account, address newEntry) internal {
if (newEntry == ZERO_ADDRESS || newEntry == SENTINEL) {
revert LinkedList_InvalidEntry(newEntry);
Expand All @@ -61,6 +96,29 @@
self.entries[SENTINEL][account] = newEntry;
}

/**
* Safe push a new entry to the linked list
* @dev This ensures that the linked list is initialized and initializes it if it is not
*
* @param self The linked list
* @param account The account to push the new entry for
* @param newEntry The new entry
*/
function safePush(SentinelList storage self, address account, address newEntry) internal {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOTE: fixes L4

if (!alreadyInitialized(self, account)) {
init({ self: self, account: account });
}
push({ self: self, account: account, newEntry: newEntry });
}

/**
* Pop an entry from the linked list
*
* @param self The linked list
* @param account The account to pop the entry for
* @param prevEntry The entry before the entry to pop
* @param popEntry The entry to pop
*/
function pop(
SentinelList storage self,
address account,
Expand All @@ -79,16 +137,30 @@
self.entries[popEntry][account] = ZERO_ADDRESS;
}

/**
* Pop all entries from the linked list
*
* @param self The linked list
* @param account The account to pop all entries for
*/
function popAll(SentinelList storage self, address account) internal {
address next = self.entries[SENTINEL][account];
while (next != ZERO_ADDRESS) {
address current = next;
next = self.entries[next][account];
self.entries[current][account] = ZERO_ADDRESS;
}
self.entries[SENTINEL][account] = ZERO_ADDRESS;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOTE: fixes I7

}

/**
* Check if the linked list contains an entry
*
* @param self The linked list
* @param account The account to check if the entry is in the linked list for
* @param entry The entry to check for
*
* @return bool True if the linked list contains the entry
*/
function contains(
SentinelList storage self,
address account,
Expand All @@ -101,6 +173,17 @@
return SENTINEL != entry && self.entries[entry][account] != ZERO_ADDRESS;
}

/**
* Get all entries in the linked list
*
* @param self The linked list
* @param account The account to get the entries for
* @param start The start entry
* @param pageSize The page size
*
* @return array All entries in the linked list
* @return next The next entry
*/
function getEntriesPaginated(
SentinelList storage self,
address account,
Expand Down Expand Up @@ -146,7 +229,7 @@
// Set correct size of returned array
// solhint-disable-next-line no-inline-assembly
/// @solidity memory-safe-assembly
assembly {

Check warning on line 232 in src/SentinelList4337.sol

View workflow job for this annotation

GitHub Actions / lint / forge-lint

Avoid to use inline assembly. It is acceptable only in rare cases
mstore(array, entryCount)
}
}
Expand Down
Loading