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

Refactor to remove Head and Tail address #129

Closed
wants to merge 3 commits into from

Conversation

Tristan22400
Copy link
Contributor

Fixes : #85

@Tristan22400 Tristan22400 self-assigned this May 3, 2023
@Tristan22400 Tristan22400 changed the base branch from main to code-style-change May 3, 2023 16:22
@Tristan22400 Tristan22400 changed the title Refactor/dd lheadtail Refactor/ddlheadtail May 3, 2023
@Tristan22400 Tristan22400 changed the title Refactor/ddlheadtail Refactor to remove Head and Tail address May 3, 2023
@Tristan22400 Tristan22400 marked this pull request as ready for review May 3, 2023 16:25
@Tristan22400 Tristan22400 marked this pull request as draft May 3, 2023 16:30
Copy link
Collaborator

@MathisGD MathisGD left a comment

Choose a reason for hiding this comment

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

I think that insertSorted can be completely refactored with this new design. See morpho-org/morpho-optimizers#1255

src/DoubleLinkedList.sol Show resolved Hide resolved
@Tristan22400 Tristan22400 marked this pull request as ready for review May 4, 2023 08:55
@QGarchery
Copy link
Collaborator

While I love the idea, it would also require a big refactor of the verification done with Certora. Is it worth to invest some time into it ? @morpho-org/onchain-team

@MerlinEgalite
Copy link
Contributor

While I love the idea, it would also require a big refactor of the verification done with Certora. Is it worth to invest some time into it ? @morpho-org/onchain-team

If it would take you 1 day or something I'm against and we can forget about it

@QGarchery
Copy link
Collaborator

While I love the idea, it would also require a big refactor of the verification done with Certora. Is it worth to invest some time into it ? @morpho-org/onchain-team

If it would take you 1 day or something I'm against and we can forget about it

Yes probably one or two days to do it: I know well how it works now but it still requires to refactor almost all the invariants

Comment on lines 102 to 107
while (numberOfIterations < maxIterations && next != address(0) && list.accounts[next].value >= value) {
next = list.accounts[next].next;
unchecked {
++numberOfIterations;
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

this should work too, idk what's best

Suggested change
while (numberOfIterations < maxIterations && next != address(0) && list.accounts[next].value >= value) {
next = list.accounts[next].next;
unchecked {
++numberOfIterations;
}
}
unchecked {
while (numberOfIterations++ < maxIterations && next != address(0) && list.accounts[next].value >= value) {
next = list.accounts[next].next;
}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

In this implementation, on the last iteration, numberOfIterations will also be incremented. At the end of the loop, numberOfIterations will be one greater than in the current implementation. To replicate the same behavior as the current implementation, the comparison numberOfIterations == maxIterations would need to be changed to numberOfIterations == maxIterations + 1

Copy link
Contributor

@makcandrov makcandrov May 9, 2023

Choose a reason for hiding this comment

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

To minimize storage reads (which is the heaviest operation), I would have written it like this:

uint256 numberOfIterations;
address prevId = address(0);
address nextId = list.accounts[address(0)].next; // If not added at the end of the list `id` will be inserted before `next`.
Account storage nextAccount = list.accounts[nextId];

unchecked {
    while (true) {
        if (++numberOfIterations == maxIterations) {
            nextAccount = list.accounts[address(0)];
            prevId = nextAccount.prev;
            break;
        } else if (nextId == address(0) || nextAccount.value < value) {
            break;
        } else {
            prevId = nextId;
            nextId = nextAccount.next;
            nextAccount = list.accounts[nextId];
        }
    }
}

list.accounts[id] = Account(prevId, nextId, value);
list.accounts[prevId].next = id;
nextAccount.prev = id;
gas diff

Comment on lines +108 to 110
if (numberOfIterations == maxIterations) {
next = address(0);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

this works too, but maybe less readable

Suggested change
if (numberOfIterations == maxIterations) {
next = address(0);
}
assembly {
next := mul(next, iszero(eq(numberOfIterations, maxIterations)))
}

Comment on lines 102 to 107
while (numberOfIterations < maxIterations && next != address(0) && list.accounts[next].value >= value) {
next = list.accounts[next].next;
unchecked {
++numberOfIterations;
}
}
Copy link
Contributor

@makcandrov makcandrov May 9, 2023

Choose a reason for hiding this comment

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

To minimize storage reads (which is the heaviest operation), I would have written it like this:

uint256 numberOfIterations;
address prevId = address(0);
address nextId = list.accounts[address(0)].next; // If not added at the end of the list `id` will be inserted before `next`.
Account storage nextAccount = list.accounts[nextId];

unchecked {
    while (true) {
        if (++numberOfIterations == maxIterations) {
            nextAccount = list.accounts[address(0)];
            prevId = nextAccount.prev;
            break;
        } else if (nextId == address(0) || nextAccount.value < value) {
            break;
        } else {
            prevId = nextId;
            nextId = nextAccount.next;
            nextAccount = list.accounts[nextId];
        }
    }
}

list.accounts[id] = Account(prevId, nextId, value);
list.accounts[prevId].next = id;
nextAccount.prev = id;
gas diff

@@ -79,12 +77,11 @@ library DoubleLinkedList {
/// @param id The address of the account.
function remove(List storage list, address id) internal {
Copy link
Contributor

@makcandrov makcandrov May 9, 2023

Choose a reason for hiding this comment

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

this function can be optimized:

function remove(List storage list, address id) internal {
    Account storage account = list.accounts[id];
    if (id == address(0)) revert AddressIsZero();
    if (account.value == 0) revert AccountDoesNotExist();

    address prevId = account.prev;
    address nextId = account.next;
    list.accounts[prevId].next = nextId;
    list.accounts[nextId].prev = prevId;

    account.prev = address(0);
    account.next = address(0);
    account.value = 0;
}
gas diff

The line if (id == address(0)) revert AddressIsZero(); can be removed, but the error thrown would no longer be accurate in the case of a null address.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will implement but not sure we will merge the PR as mentioned above

Copy link
Contributor

Choose a reason for hiding this comment

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

can you remove changes to the test @Tristan22400 please?

Copy link
Collaborator

@QGarchery QGarchery left a comment

Choose a reason for hiding this comment

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

I think we do not want to merge because of this discussion

@MerlinEgalite
Copy link
Contributor

Agree with you @QGarchery. Sorry about that @Tristan22400 I'm closing this PR

@MerlinEgalite MerlinEgalite deleted the refactor/DDLheadtail branch May 9, 2023 18:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

DLL: use address(0) instead of head and tail
5 participants