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

Add Modulo operation for getting the quotient #915

Merged
merged 7 commits into from
Aug 13, 2018

Conversation

JeanoLee
Copy link
Contributor

@JeanoLee JeanoLee commented Apr 24, 2018

Fixes #

πŸš€ Description

Added modulo operation.

  • πŸ“˜ I've reviewed the OpenZeppelin Contributor Guidelines
  • βœ… I've added tests where applicable to test my new functionality.
  • πŸ“– I've made sure that my contracts are well-documented.
  • 🎨 I've run the JS/Solidity linters and fixed any issues (npm run lint:all:fix).

@OpenZeppelin OpenZeppelin deleted a comment from coveralls Jun 10, 2018
@frangio
Copy link
Contributor

frangio commented Jun 10, 2018

Hi @JeanoLee! Thank you for contributing.

Is this function any different from the built-in operator %?

@nventuro nventuro mentioned this pull request Jul 26, 2018
4 tasks
@nventuro nventuro self-assigned this Jul 28, 2018
@nventuro nventuro added feature New contracts, functions, or helpers. contracts Smart contract code. labels Jul 28, 2018
@nventuro nventuro added this to the v2.0 milestone Jul 28, 2018
Copy link
Contributor

@nventuro nventuro left a comment

Choose a reason for hiding this comment

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

Awesome @JeanoLee, thanks for your contribution! I'm not too familiar with coveralls, but it looks like it's reporting coverage dropped because the assert is (as expected) never hit: I'll look into that.

@@ -88,4 +88,21 @@ contract('SafeMath', () => {
await assertJump(this.safeMath.div(a, b));
});
});

describe('mod', function () {
it('modulo correctly', async function () {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you add a couple more test cases? Since modulo is a bit of a tricky operation, I think it'd be nice to test when a < b, a == b, a > b, and a == b * k (for some integer k).

*/
function mod(uint256 a, uint256 b) internal pure returns (uint256 c) {
// d = a / b
// a = d * b + c
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider adding a small comment here noting that truncation happens when d is calculated. Otherwise it may be confusing, since for a real number d, a = d * b + c = (a / b) * b + c = a + c, which obviously doesn't hold.

// c = a - d * b
uint256 d = div(a,b);
c = sub(a, mul(d,b));
assert(c < b);
Copy link
Contributor

Choose a reason for hiding this comment

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

πŸ‘, this is in line with what's being discussed at #1120 (unless I'm mistaken @leonardoalt?)

Choose a reason for hiding this comment

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

@nventuro Yes! (if the other operations add the necessary requires)

Copy link
Contributor

@nventuro nventuro Aug 1, 2018

Choose a reason for hiding this comment

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

It's more fundamental than that: by definition the result of a modulo operation will always be strictly less than the divisor (it's the remainder of dividing by it), which is why I thought this was an interesting example of 'correct' assert usage.

Choose a reason for hiding this comment

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

Sure, but that's exactly the point. Since here mod is implemented using the other operators, if they overflow etc the assertion might break. By having the other operations ensure their correctness, the corrected of mod follows.

Choose a reason for hiding this comment

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

And you are totally right in saying this is a nice example of the usage of assert: you have the pre-conditions that are true from the other operations, which imply the mod property.

@nventuro
Copy link
Contributor

@frangio this is indeed how % behaves in Solidity (and in any sane languages): differences start to pop up when negative numbers are involved (e.g. (-5) % 2 == -1 in both C and Solidity, but 1 in Python).

@nventuro
Copy link
Contributor

nventuro commented Aug 8, 2018

I went ahead and implemented the changes myself, due to inactivity.

@nventuro nventuro requested a review from frangio August 8, 2018 10:52
require(b != 0);

uint256 c = a % b;
assert(c < b);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd consider removing the assert here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Removed, since we don't yet have a criteria for this.

Copy link
Contributor

@frangio frangio left a comment

Choose a reason for hiding this comment

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

Thanks @JeanoLee and @nventuro!

@frangio frangio merged commit 9aa30e1 into OpenZeppelin:master Aug 13, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
contracts Smart contract code. feature New contracts, functions, or helpers.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants