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

PRBMath SD59x18 and UD60x18 properties #30

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open

Conversation

tuturu-tech
Copy link
Contributor

Used the ABDKMath properties as a base to create properties for SD59x18 (118 properties) and UD60x18 (90 properties), covering all math functions except for: gm, floor, ceil, frac.

@tuturu-tech tuturu-tech marked this pull request as ready for review May 16, 2023 10:34
@tuturu-tech
Copy link
Contributor Author

Hey @PaulRBerg, we've created a draft set of properties testing mathematical invariants of your PRBMath v3 library and wanted to get your input on a couple of failing properties.

Both the SD59x18 and UD60x18 libraries have properties that are failing (full list below), however, without performing a full audit of the code it is difficult to ascertain if the failures are due to incorrectly defined allowed error bounds in the properties, or if they indicate a bug in the library implementation.

It would be useful if you could help us define the correct allowed error bounds for the various operations so that the properties are not over- or under-constraining the allowed errors.

Failing properties

SD59x18

  • mul_test_associative - The associative property of multiplication, (x * y) * z == x * (y * z), constrained to a 0.1% allowed error
  • log10_test_power - log10(x ** y) == y * log10(x), constrained to a 1% allowed error
  • pow_test_product_same_base - x ** a * x ** b == x ** (a+b), constrained to 9 digits of allowed error
  • pow_test_power_of_an_exponentiation - (x ** a) ** b == x ** (a * b), constrained to 9 digits of allowed error
  • inv_test_division_noncommutativity - x / y == 1 / (y / x), constrained to a 0.1% allowed error
  • inv_test_identity - (1 / x) * x == 1, constrained to a 0.1% allowed error
  • log2_test_power - log2(x ** y) == y * log2(x), constrained to a 1% allowed error

UD60x18

  • mul_test_associative - The associative property of multiplication, (x * y) * z == x * (y * z), constrained to a 0.1% allowed error
  • inv_test_division_noncommutativity - x / y == 1 / (y / x), constrained to a 0.1% allowed error
  • inv_test_identity - (1 / x) * x == 1, constrained to a 0.1% allowed error
  • pow_test_product_same_base - x ** a * x ** b == x ** (a+b), constrained to 9 digits of allowed error

If you would like to try these yourself you can do so by cloning the dev-prb-v3 branch, installing the dependencies, and running npm run echidna-prb-sd and npm run echidna-prb-ud , it should catch these failures within 100000 runs.

@PaulRBerg
Copy link

PaulRBerg commented May 18, 2023

Thank you SO MUCH for doing this, guys!

I will review this in detail in a few weeks, but I want to mention upfront that it would be super duper helpful to target V4 instead of V3. As per the release notes, V4 contains a fix for an important bug that affected all V3 releases.

There have been few API changes between V3 and V4, so upgrading should be easy.

@tuturu-tech
Copy link
Contributor Author

Thank you for the quick reply!

We opted for targeting v3 since Slither does not (yet) work with user defined operators used in v4. That said, I've just added v4 properties with the caveat that they only work with the Slither no_fail flag turned on, which will ignore Slither failures. Apart from that the properties are exactly the same between v3 and v4.

@PaulRBerg
Copy link

Oh, yes, I forgot that Slither doesn't support operators just yet.

In this case, it should be fine to keep targeting V3 for a while, though we should keep in mind that a bug exists in Common.exp2, which affects other functions, e.g. UD60x18.pow.

Copy link

@broccolirob broccolirob left a comment

Choose a reason for hiding this comment

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

I tried going through a few of the failure cases I saw using echidna 2.2.0. Some of these differed from the ones reported in an earlier comment:

  • inv_test_double_inverse
  • pow_test_high_exponent
  • mul_test_associative
  • sqrt_test_square
  • inv_test_multiplication
  • log10_test_power
  • inv_test_identity
  • pow_test_product_same_base
  • pow_test_power_of_an_exponentiation
  • inv_test_division_noncommutativity
  • log2_test_power
    There's a learning curve to getting ramped up on this stuff. I felt like I needed more time to provide more insight. On my next IRAD, perhaps I can come back to this review and actually solve some of these failures.

int256 la = convert(floor(log10(abs(a))));
int256 lb = convert(floor(log10(abs(b))));
// digits left
int256 prec = la + lb;

Choose a reason for hiding this comment

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

This prec is assigned but never used?

Choose a reason for hiding this comment

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

This function is implicated in several of the failures I reviewed. Needs more investigation.

SD59x18 double_inv_x = inv(inv(x));

// The maximum loss of precision will be 2 * log2(x) bits rounded up
uint256 loss = 2 * significant_digits_lost_in_mult(x, x)+ 2;

Choose a reason for hiding this comment

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

This property is failing in 2.2.0 echinda. The expectation that significant_digits_lost_in_mult(x, x) is going to calculate the expected precision, given the input, appears to be wrong. Maybe the function isn't working as expected because I think it's just returning 0 every time (or at least in cases where it's not supposed to), which means it's always expecting 2 digits of precision. I changed the end of the equation to add 3, and then 4, and it took longer and longer to find a failure, but always reported it was looking for 2, 3, 4 digits of precision depending on what I changed that value to before the run.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The issue might be that we're not taking into consideration the precision loss of the division, so doing significant_digits_lost_in_mult(x, inv(x)) might solve this


SD59x18 result = pow(x, a);

assertEq(result, ZERO_FP);

Choose a reason for hiding this comment

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

This fails because it's returning 1e18, so I suspect it's rounding up instead of down. This might have something to do with a bug in the exp2 function of PRBMath v3.


uint256 digitsLost = significant_digits_lost_in_mult(x, y);
digitsLost += significant_digits_lost_in_mult(x, z);

Choose a reason for hiding this comment

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

What about the digits lost multiplying y and z? We might need to add another digitsLost += significant_digits_lost_in_mult(y, z); to get this to work.

contracts/Math/PRBMath/v3/PRBMathSD59x18PropertyTests.sol Outdated Show resolved Hide resolved
contracts/Math/PRBMath/v3/PRBMathSD59x18PropertyTests.sol Outdated Show resolved Hide resolved
// log10(x ** y) = y * log10(x)
function log10_test_power(SD59x18 x, SD59x18 y) public {
require(x.gt(ZERO_FP) && y.gt(ZERO_FP));
SD59x18 x_y = pow(x, y);

Choose a reason for hiding this comment

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

Failing test case was very close to what was expected. Had the tolerance to be off by 1, but was off by 2. Could be an issue with the exp2 function bug that effects pow in v3.

@PaulRBerg
Copy link

Guys just wanted to say that unfortunately I am tied up with my work (on Sablier V2), and I cannot find any time to provide a proper review here.

I'm sorry, but I am also very grateful that you wrote these fuzz tests for PRBMath. Thank you!

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.

3 participants