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

Test the code identity between the Lockup contracts #771

Closed
3 tasks
PaulRBerg opened this issue Dec 28, 2023 · 6 comments
Closed
3 tasks

Test the code identity between the Lockup contracts #771

PaulRBerg opened this issue Dec 28, 2023 · 6 comments
Labels
effort: medium Default level of effort. priority: 1 This is important. It should be dealt with shortly. type: test Adding, updating, or removing tests. work: complicated Sense-analyze-respond. The relationship between cause and effect requires analysis or expertise.

Comments

@PaulRBerg
Copy link
Member

Problem

Because Solidity does not have anything equivalent to Rust traits, we are forced to duplicate any logic that touches upon different structs despite the logic being completely identical.

Technically speaking, there is a way out, but it's ugly and not worth it. We could implement a setter for every struct field (e.g., setIsCancelable, setWasCanceled, etc.), but:

  1. Doing so would double the number of lines of code written for implementing the cancel and withdraw functionality
  2. The gas cost would increase (there would be more internal calls, some of which may not be inlined)
  3. There would still be a lot of duplicated code.

Solution

Introduce a new class of tests, i.e. differential tests, that perform the following checks:

  1. Read the LockupLinear and the LockupDynamic contracts (and potentially LockupTranched in the future)
  2. Compare the implementations of the _cancel and the _withdraw functions. They should be 100% identical, down to the English comments.

The tests would use Bash scripting and the ffi cheat code to read the file contents.

Alternatively, if implementing this in Forge is too complicated, we can write a CI check. There should be GitHub Actions for performing diffs between files.

References

@PaulRBerg PaulRBerg added testing priority: 1 This is important. It should be dealt with shortly. type: test Adding, updating, or removing tests. work: complicated Sense-analyze-respond. The relationship between cause and effect requires analysis or expertise. effort: medium Default level of effort. and removed testing labels Dec 28, 2023
@andreivladbrg
Copy link
Member

Given how our test structure is designed (shared dir) is the code identity test really needed?

We are running the same tests for these functions:cancel, renounce and withdraw in both contracts, so if there were to be difference between the implementations, the tests would fail for that contract (ofc assuming we have a 100% coverage for each function and we are testing what each line does).

I believe this test would be helpful only if we want to check if the comments are identical, thus we should lower the priority.

Regarding the test method, using forge seems to be an overkill. Adding a Bash script in the shell directory, which should be run during CI, would be more appropriate.

@PaulRBerg
Copy link
Member Author

Yes, performing these code identity checks would still be helpful even if we run the same tests for these contracts.

  1. Code identity is different from functional logic. The same test can pass for two different implementations. E.g., 1 + 3 = 2 +2. So it's not just about the comments.
  2. Fallibilism leads us to assume that we have made errors in our test suites (which are large and complicated). These code identity checks would be comparatively smaller, providing us additional confidence that everything works as expected.

Regarding the test method, using forge seems to be an overkill. Adding a Bash script in the shell directory, which should be run during CI, would be more appropriate

Fair enough

@andreivladbrg
Copy link
Member

@PaulRBerg since we implemented #813 should we close this issue?

@PaulRBerg
Copy link
Member Author

Not sure. Isn't there duplicated code still?

If there is, we should keep this issue open (though revise the description in light of the new spec).

@andreivladbrg
Copy link
Member

andreivladbrg commented Feb 10, 2024

Just these lines, but I don't think they count

Linear:

// Safe Interactions: query the protocol fee. This is safe because it's a known Sablier contract that does
// not call other unknown contracts.
UD60x18 protocolFee = comptroller.protocolFees(params.asset);
// Checks: check the fees and calculate the fee amounts.
Lockup.CreateAmounts memory createAmounts =
Helpers.checkAndCalculateFees(params.totalAmount, protocolFee, params.broker.fee, MAX_FEE);

Dynamic:

// Safe Interactions: query the protocol fee. This is safe because it's a known Sablier contract that does
// not call other unknown contracts.
UD60x18 protocolFee = comptroller.protocolFees(params.asset);
// Checks: check the fees and calculate the fee amounts.
Lockup.CreateAmounts memory createAmounts =
Helpers.checkAndCalculateFees(params.totalAmount, protocolFee, params.broker.fee, MAX_FEE);

Everything else is completely different

@PaulRBerg
Copy link
Member Author

Yeah, they don't count, especially since we might end up removing protocol fees.

@PaulRBerg PaulRBerg closed this as not planned Won't fix, can't repro, duplicate, stale Feb 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
effort: medium Default level of effort. priority: 1 This is important. It should be dealt with shortly. type: test Adding, updating, or removing tests. work: complicated Sense-analyze-respond. The relationship between cause and effect requires analysis or expertise.
Projects
None yet
Development

No branches or pull requests

2 participants