Глядя на следующую функцию 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?
Во-первых, спасибо библиотеке OpenZeppelin safeMath, части которой я использовал в своем коде.
Учитывая, что сложение является коммутативным, на самом деле не имеет значения, что вы используете. Ответ будет либо равен, либо больше обоих и, следовательно, действителен, либо меньше обоих, недействителен.
Скажем, у нас есть uint3
(для простоты, но не допустимый тип Solidity), где переполнение mod 8
:
1+7 = 0
0 < 2 && 0 < 7
7+1 = 0
0 < 2 && 0 < 7
переполнен
0+7 = 7
7 > 0 && 7 >= 7
7+0 = 7
7 >= 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 газов), но она указывает на причину. Возможно, предлагаемый код является наиболее газосберегающим известным способом.
Шульц
Роб Хитченс
assert
не будет переполнения? В прошлый раз, когда я был там, ответ был "Нет". Это еще вопрос. Если вы можете найти такую комбинацию, я уверен, что многие люди должны знать об этом. Может быть, кто-то вмешается с формальным объяснением.Дэйв Эпплтон