SafeMath Safe-Add-Funktionszusicherungen gegen Überläufe

Betrachtet man die folgende safeAdd-Funktion, die in vielen Smart Contracts üblich ist, scheint es, dass nur a und c verglichen werden. Aber kann es nicht sein, dass b die uint ist, die einen Überlauf verursacht?

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

Warum reicht es aus, nur a und c zu vergleichen?

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

Antworten (3)

Zunächst ein großes Lob an die SafeMath-Bibliothek von OpenZeppelin, von der ich Teile in meinem Code verwendet habe.

Da die Addition kommutativ ist, spielt es keine Rolle, welche Sie verwenden. Die Antwort ist entweder gleich oder größer als beide und somit gültig, oder kleiner als beide ungültig.

Angenommen, wir haben einen uint3(der Einfachheit halber aber keinen gültigen Solidity-Typ), bei dem der Überlauf lautet mod 8:

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

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

Übergelaufen

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

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

-bearbeiten-

Abgesehen von der Frage möchte ich hinzufügen, dass ich dieses Muster in Solidity 0.4.10 (oder höher) verwende, um Nebeneffekte und Validierung zu trennen, anstatt einen teureren Funktionsaufruf.

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

Eine Reihe von Beispielen, um dem Hirnschmerz zu entkommen. Ich schreibe die Antwort von o0ragman0o im Grunde neu. Wenn meine Antwort Sinn macht, akzeptiere vielleicht stattdessen seine Antwort :)

Erstes Beispiel ohne Überlauf:

uint8 a = 255;
uint8 b = 0;

Then c = 255 + 0 which is 255.

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

Erhöhen Sie den Wert von b, um einen Überlauf zu erzeugen:

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.

Tauschen Sie aund bherum, um zu zeigen, dass es immer noch funktioniert:

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.

Erhöhen Sie die Menge, die wir überlaufen, um - Erhöhung aum 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.

Dieses dritte Beispiel hilft zu zeigen, wie die durch den Überlauf erzeugte Zahl immer kleiner als einer der Bestandteile ist, was bedeutet, dass wir nur einen von ihnen überprüfen müssen.

Ohne zu versuchen zu beweisen, dass beide Wege absolut zuverlässig funktionieren (würde mehr Zeit und Koffein erfordern, um sicher zu sein), kann ich sagen, dass ich die Alternative getestet habe assert(c >= b && c >= a);und sie in Solc 0.4.10 etwas höhere Benzinkosten verursacht. Es ist ein sehr kleiner Unterschied (12 Gas), aber es deutet auf einen Grund hin. Möglicherweise ist der vorgeschlagene Code der spriteffizienteste bekannte Weg.

Dies ist natürlich ein sehr guter Grund, aber es scheint immer noch den Zweck zu unterscheiden. Wenn wir sicherstellen wollen, dass kein Überlaufen entsteht (und wir am Anfang zusätzliches Benzin zahlen müssen, sonst hätten wir einfach die +-Operation verwendet), warum reicht es aus, nur auf ein Überlaufen zu prüfen? auch von b.
Hatte dieses Kaninchen schon einmal. Es tut mir im Gehirn weh, wenn ich darüber nachdenke. Gibt es eine Kombination aus a, b, bei der der assertÜberlauf nicht abgefangen wird? Als ich das letzte Mal dort war, war die Antwort "Nein". Das ist immer noch die Frage. Wenn Sie eine solche Kombination finden , bin ich sicher, dass viele Leute darüber Bescheid wissen müssen. Vielleicht meldet sich jemand mit einer formellen Erklärung.
An der Grenze x + 255 => x-1 x + 254 => x-2 x + 253 => x-3 ... muss die rechte Seite immer niedriger sein als eine der linken, wenn sie überläuft