4

Looking at the following safeAdd function, which is common in many smart contracts out there, it seems that only a and c are compared. But can't it be that b will be the uint that will cause an overflow?

  function safeAdd(uint a, uint b) internal returns (uint) {
    uint c = a + b;
    assert(c >= a);
    return c;
  }

Why is compering only a and c is sufficient?

assert(c >= b && c >= a);  <- Why not like this?
4

Firstly, kudos to the OpenZeppelin safeMath library parts of which I've used in my code.

Given that addition is commutative, it doesn't really matter which you use. The answer will either be equal or greater than both and so valid, or less than both, invalid.

Say we have an uint3 (for simplicity but not a valid Solidity type) where overflow is mod 8:

1+7 = 0 0 < 2 && 0 < 7

7+1 = 0 0 < 2 && 0 < 7

Overflowed

0+7 = 7 7 > 0 && 7 >= 7

7+0 = 7 7 >= 7 && 7 > 0

-edit-

Aside from the question, I might add that I use this pattern in solidity 0.4.10 (or above) to separate side effects and validation rather than a more expensive function call.

uint _check = c; // where c is a variable being updated
    c = a + b;
assert(c >= _check);
2

Without attempting to prove that either way works completely reliably (would require more time and caffeine to be sure), I can say I tested the alternative, assert(c >= b && c >= a); and it causes a slightly higher gas cost in solc 0.4.10. It's a very small difference (12 gas) but it suggests a reason. Possibly the suggested code is the most gas-efficient way known.

  • 1
    This is of course a very good reason, but it still seems to differ the purpose. If we want to make sure no overflowing is accruing (and we're welling to pay extra gas to begin with, otherwise we would've just used the + op), why is it enough to check only for a, overflow can be caused by b as well. – shultz Apr 17 '17 at 16:31
  • Been down this rabbit before. It makes my brain hurt thinking it through. Is there a combination of a, b where the assert won't catch an overflow? Last time I went there, the answer was "No." That's still the question. If you can find such a combination I'm sure a lot of people need to know about it. Maybe someone will chime in with a formal explanation. – Rob Hitchens - B9lab Apr 17 '17 at 17:12
  • 2
    At the limit x + 255 => x-1 x + 254 => x-2 x + 253 => x-3 ... the RHS must always be lower than either of the LHS if it overflows – Dave Appleton Sep 10 '17 at 9:53
2

A bunch of examples to get away from the brain ache. I'm basically re-writing o0ragman0o's answer. If my answer makes sense, maybe accept his answer instead :)

First example, with no overflow occurring:

uint8 a = 255;
uint8 b = 0;

Then c = 255 + 0 which is 255.

assert(c >= a);  // SUCCESS as 255 is equal to a.

Increase the value of b to create an overflow:

uint8 a = 255;
uint8 b = 1;

Then c = 255 + 1 which is 0.

assert(c >= a);  // FAILS because 0 is not greater or equal to 255.

Swap a and b around to show it still works:

uint8 a = 1;
uint8 b = 255;

Then c = 1 + 255 which, again, is 0.

assert(c >= a);  // FAILS because 0 is not greater or equal to 1.

Increase the amount we're overflowing by - increase a by 1:

uint8 a = 2;
uint8 b = 255;

Then c = 2 + 255 which is 1.

assert(c >= a);  // FAILS because 1 is not greater or equal to 2.

This third example helps show how the number created by the overflow will always be less than either of the constituent parts, meaning we need check only one of them.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.