Безопасное добавление утверждений функций SafeMath против переполнения

Глядя на следующую функцию safeAdd, распространенную во многих смарт-контрактах, кажется, что сравниваются только a и c. Но не может ли быть так, что b будет тем uint, который вызовет переполнение?

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

Почему достаточно сравнить только a и c?

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

Ответы (3)

Во-первых, спасибо библиотеке OpenZeppelin safeMath, части которой я использовал в своем коде.

Учитывая, что сложение является коммутативным, на самом деле не имеет значения, что вы используете. Ответ будет либо равен, либо больше обоих и, следовательно, действителен, либо меньше обоих, недействителен.

Скажем, у нас есть uint3(для простоты, но не допустимый тип Solidity), где переполнение mod 8:

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

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

переполнен

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

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

-редактировать-

Помимо вопроса, я мог бы добавить, что я использую этот шаблон в Solidity 0.4.10 (или выше) для разделения побочных эффектов и проверки, а не для более дорогого вызова функции.

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

Куча примеров, чтобы отвлечься от головной боли. Я в основном переписываю ответ o0ragman0o. Если мой ответ имеет смысл, возможно, вместо этого примите его ответ :)

Первый пример без переполнения:

uint8 a = 255;
uint8 b = 0;

Then c = 255 + 0 which is 255.

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

Увеличьте значение bдля создания переполнения:

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.

Поменяйте местами a, bчтобы показать, что он все еще работает:

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.

Увеличьте количество, на которое мы переполняемся - увеличьте aна 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.

Этот третий пример помогает показать, как число, созданное переполнением, всегда будет меньше, чем любая из составных частей, то есть нам нужно проверить только одну из них.

Не пытаясь доказать, что любой способ работает абсолютно надежно (для уверенности потребуется больше времени и кофеина), я могу сказать, что протестировал альтернативу, assert(c >= b && c >= a);и она приводит к несколько более высокой стоимости газа в solc 0.4.10. Это очень небольшая разница (12 газов), но она указывает на причину. Возможно, предлагаемый код является наиболее газосберегающим известным способом.

Это, конечно, очень веская причина, но все же кажется, что цель отличается. Если мы хотим удостовериться, что переполнения не происходит (и для начала нам не терпится доплатить за газ, иначе мы бы просто использовали + op), почему достаточно проверить только на a, переполнение может быть вызвано по б тоже.
Я уже видел этого кролика. У меня болит мозг, когда я думаю об этом. Есть ли комбинация a, b, где assertне будет переполнения? В прошлый раз, когда я был там, ответ был "Нет". Это еще вопрос. Если вы можете найти такую ​​комбинацию, я уверен, что многие люди должны знать об этом. Может быть, кто-то вмешается с формальным объяснением.
В пределе х + 255 => х-1 х + 254 => х-2 х + 253 => х-3 ... правая часть всегда должна быть ниже любой из левой части, если она переполняется