SafeMath aserciones de función de adición segura contra desbordamientos

Mirando la siguiente función safeAdd, que es común en muchos contratos inteligentes, parece que solo se comparan a y c. ¿Pero no puede ser que b sea la uint que provoque un desbordamiento?

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

¿Por qué es suficiente comparar solo a y c?

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

Respuestas (3)

En primer lugar, felicitaciones a las partes de la biblioteca SafeMath de OpenZeppelin que he usado en mi código.

Dado que la suma es conmutativa, realmente no importa cuál uses. La respuesta será igual o mayor que ambas y, por lo tanto, válida, o menor que ambas, inválida.

Digamos que tenemos un uint3(por simplicidad pero no un tipo de solidez válido) donde el desbordamiento es mod 8:

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

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

desbordado

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

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

-editar-

Aparte de la pregunta, podría agregar que uso este patrón en solidity 0.4.10 (o superior) para separar los efectos secundarios y la validación en lugar de una llamada de función más costosa.

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

Un montón de ejemplos para alejarse del dolor de cabeza. Básicamente estoy reescribiendo la respuesta de o0ragman0o. Si mi respuesta tiene sentido, tal vez acepte su respuesta en su lugar :)

Primer ejemplo, sin que se produzca desbordamiento:

uint8 a = 255;
uint8 b = 0;

Then c = 255 + 0 which is 255.

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

Aumente el valor de bpara crear un desbordamiento:

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.

Cambia ay bgira para mostrar que todavía funciona:

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.

Aumente la cantidad que estamos desbordando - aumente aen 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.

Este tercer ejemplo ayuda a mostrar cómo el número creado por el desbordamiento siempre será menor que cualquiera de las partes constituyentes, lo que significa que solo debemos verificar una de ellas.

Sin intentar probar que cualquiera de las dos formas funciona de manera completamente confiable (requiere más tiempo y cafeína para estar seguro), puedo decir que probé la alternativa assert(c >= b && c >= a);y provoca un costo de gasolina ligeramente más alto en solc 0.4.10. Es una diferencia muy pequeña (12 gases) pero sugiere una razón. Posiblemente, el código sugerido es la forma más eficiente de gas conocida.

Esta es, por supuesto, una muy buena razón, pero todavía parece diferir el propósito. Si queremos asegurarnos de que no se acumule un desbordamiento (y, para empezar, estamos dispuestos a pagar gasolina adicional, de lo contrario, simplemente habríamos usado el + op), ¿por qué es suficiente verificar solo un desbordamiento? por b también.
He estado en este conejo antes. Hace que mi cerebro duela pensando en ello. ¿Hay una combinación de a, b en la assertque no se produzca un desbordamiento? La última vez que fui allí, la respuesta fue "No". Esa sigue siendo la pregunta. Si puede encontrar esa combinación, estoy seguro de que mucha gente necesita saberlo. Tal vez alguien intervenga con una explicación formal.
En el límite x + 255 => x-1 x + 254 => x-2 x + 253 => x-3 ... el RHS siempre debe estar más bajo que cualquiera de los LHS si se desborda