Wird eine gesamte Transaktion zurückgesetzt, wenn ein Throw auftritt?

Ich habe einen Vertrag, der einen anderen Vertrag aufruft, der einen anderen aufruft. Wenn irgendwo im zweiten Kontrakt ein Wurf (oder kein Gasfehler) auftritt, wird die gesamte Transaktion rückgängig gemacht oder ist es nur das Zeug im zweiten und dritten Kontrakt, das dies tut, während die Ausführung vom ersten Kontrakt erfolgt noch steht?

Antworten (3)

In Solidity standardmäßig ja.

Auf der EVM-Ebene kehrt ein Wurf (schlechter Sprung, kein Gas oder jede andere Ausnahme) nur den Anruf zurück, in dem er sich befindet. Solidity setzt die Ausnahme hilfreicherweise im Stack fort, bis alles rückgängig gemacht ist.

Es ist möglich, dies mit Code auf niedrigerer Ebene (insbesondere address.call()) zu verhindern. Hier ist ein Beispiel dafür, wie dies als improvisiertes Try-Catch-Konstrukt verwendet wird.

Wenn ich innerhalb meines Vertrags einen unbekannten Vertrag anrufe, kann ich immer noch sicher sein, dass der unbekannte Vertrag nicht in der Lage ist, meinen Vertrag in einem Zwischenzustand zu belassen.
Das ist richtig. Sofern Sie address.call() nicht absichtlich verwenden, wird die Ausnahme automatisch nach unten weitergegeben. Das heißt, Sie möchten die Situation vielleicht eingehend untersuchen. Ein unbekannter Vertrag kann Ihren Vertrag später erneut aufrufen (der Wiedereintrittsangriff), und Ihr Vertrag befindet sich für diesen Moment in einem Zwischenzustand.
Ich muss address.call() verwenden, weil es sich um einen „unbekannten“ Vertrag handelt, aber es sieht so aus, als wäre ich immer noch sicher, dass mein Code nur halb fertig bleibt. Wenn sie werfen, sind die einzigen Optionen: 1 Total Revert. Oder 2 der Aufruf von address.call() wird rückgängig gemacht und mein Code fährt fort, als ob nichts passiert wäre. (Ich ignoriere den Wiedereintritt, gegen den ich mich auf andere Weise geschützt habe)

Um die Antwort von @Matthew zu ergänzen, hängt es davon ab, wie der Aufruf in Solidity erfolgt.

Wenn Caufgerufen D.foo()wird und fooein ausgeführt throwwird, wird die gesamte Transaktion rückgängig gemacht.

Wenn Cein "Rohaufruf auf niedrigerer Ebene" wie D.call(bytes4(sha3('foo()')))und fooein ausgeführt throwwird, werden nur foound seine Unteraufrufe zurückgesetzt. Dies liegt daran, dass ein Rohaufruf keine Ausnahmen weitergibt: Ein Rohaufruf wie D.callgibt nur einen booleschen Wert zurück, der angibt, ob der Aufruf erfolgreich war oder auf eine Ausnahme gestoßen ist.


Mehr Details

In Solidity throwverursacht a eine Ausnahme, indem es Bytecode generiert, der zu einem ungültigen Sprungziel führt . (Für die anderen Fälle siehe Alle Fälle, in denen Solidity zu einem ungültigen Sprungziel kompiliert wird .)

Aus der Solidity-Perspektive führt eine Ausnahme, die nicht geschluckt oder nicht erfasst/nicht behandelt wird, dazu, dass die gesamte Transaktion rückgängig gemacht wird.

Es gibt derzeit keine Möglichkeit, eine Ausnahme in Solidity abzufangen, daher führt eine Ausnahme, wie z. B. throw, dazu, dass die gesamte Transaktion rückgängig gemacht wird.

Raw-Aufrufe "schlucken" jedoch Ausnahmen: Raw-Aufrufe verbreiten keine Ausnahmen, und deshalb werden nur die Unteraufrufe zurückgesetzt.

Es ist die gesamte Transaktion, die rückgängig gemacht wird.

Außerdem geht das gesamte mit der Transaktion verbundene Gas (einschließlich des verbleibenden) an den Miner.

Normalerweise wird die gesamte Transaktion rückgängig gemacht, aber Sie können dies auch verhindern. Siehe die Antwort von Matthew.