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?
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 = 0
0 < 2 && 0 < 7
7+1 = 0
0 < 2 && 0 < 7
Übergelaufen
0+7 = 7
7 > 0 && 7 >= 7
7+0 = 7
7 >= 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 a
und b
herum, 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 a
um 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.
schultz
Rob Hitchens
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.David Appleton