Warum erzeugen `throw` und `revert()` unterschiedliche Bytecodes?

throwund revert()kompilieren beide zur revertOperation (opcode 0xfd). Laut den Dokumenten :

Alternativ zu revert() kann auch das Schlüsselwort throw verwendet werden.

Warum kommt es dann zu folgendem Vertrag

contract test {
    function () {
        throw;
        //revert();
    }
}

erzeugt anderen Bytecode, wenn ich revert()anstelle von throwim obigen Beispiel verwende? Ich verwende Remix mit Solidity 0.4.13+commit.0fb4cb1a.Emscripten.clang. Dies sind die resultierenden Opcodes:

using throw:    PUSH1 0x60 PUSH1 0x40 MSTORE CALLVALUE ISZERO PUSH1 0xE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST JUMPDEST PUSH1 0x47 DUP1 PUSH1 0x1C PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x60 PUSH1 0x40 MSTORE JUMPDEST CALLVALUE ISZERO PUSH1 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x19 JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST JUMP JUMPDEST STOP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0x4a PUSH32 0x5473469C00A2379987B5AF61DFF50F8FABE3B71589C4D2BBB53073BEB9D10029 
using revert(): PUSH1 0x60 PUSH1 0x40 MSTORE CALLVALUE ISZERO PUSH1 0xE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST JUMPDEST PUSH1 0x47 DUP1 PUSH1 0x1C PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x60 PUSH1 0x40 MSTORE JUMPDEST CALLVALUE ISZERO PUSH1 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x19 JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST JUMP JUMPDEST STOP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0x5f 0xef 0xc4 0xac 0x4c STATICCALL DUP15 TIMESTAMP CODECOPY PUSH10 0x17070269221C62F6F5D4 0xba 0xe1 BALANCE BYTE 0xf7 EQ 0xca SWAP6 SWAP13 0xd0 0x27 0x24 STOP 0x29 

Wir sehen, dass die letzten Anweisungen anders sind. Während die Version mit throwauf endet

0x627A7A723058
KECCAK256
0x4a
PUSH32
0x5473469C00A2379987B5AF61DFF50F8FABE3B71589C4D2BBB53073BEB9D10029

die Version mit revert()endet auf

0x627A7A723058
KECCAK256
0x5f
0xef
0xc4
0xac
0x4c
STATICCALL
DUP15
TIMESTAMP
CODECOPY
PUSH10
0x17070269221C62F6F5D4
0xba
0xe1
BALANCE
BYTE
0xf7
EQ
0xca
SWAP6
SWAP13
0xd0
0x27
0x24
STOP
0x29
Ich bin mir nicht sicher, ob dies der Grund ist, aber die Solidity-Dokumentation sagt auch von revert(), dass es in Zukunft möglich sein könnte, auch Details über den Fehler in einen Aufruf von revert aufzunehmen. Und es gibt ein offenes Problem zum vollständigen Verwerfen von throw: github.com/ethereum/solidity/issues/1793 . Es könnte ein Gerüst für die spätere Fehlerbehandlung sein. Interessante Frage!

Antworten (3)

Ich überlasse die genaue Interpretation der OpCodes jemand anderem und weise nur darauf hin, dass es sich um unterschiedliche Anweisungen handelt, sodass unterschiedliche Implementierungen zu erwarten sind.

Danach: http://solidity.readthedocs.io/en/develop/control-structures.html#error-handling-assert-require-revert-and-exceptions

Beginnend mit Metropolis revert;wird ungenutztes Gas zurückgegeben, während throwweiterhin das gesamte verfügbare Gas verbraucht wird. In Bezug auf Vertragsstatusänderungen sind sie gleich – alles wird rückgängig gemacht.

Ich hoffe es hilft.

Vielen Dank. Mein Verständnis dieser Dokumente ist jedoch, dass beide revert()und in einen Opcode throwkompiliert werden sollten . REVERTDann würde ich erwarten, dass Metropolis diesen Opcode entsprechend behandelt (mit einer Gasrückerstattung). Ich verstehe nicht, warum dies jetzt oder jemals zu einem anderen kompilierten Code führen würde.
Ich kann mich irren. Meine Intuition sagt, dass wir mit identischem Code keine unterschiedlichen Ergebnisse erzielen können. Da Revert und Throw in Bezug auf das Gas leicht unterschiedliche Operationen ausführen (werden), ist ein Unterschied im Code zu erwarten. Anders ausgedrückt: Wenn der kompilierte Code identisch wäre, wie würde das EVM dann wissen, was mit dem Gas zu tun ist?
ok danke, jetzt bin ich zu meinem Missverständnis gekommen: Solidity gibt revert()das Gas zurück, nicht den REVERTOpcode, der sowohl von Solidity als revert()auch verwendet wird throw, daher brauchen wir den Unterschied im Bytecode, danke!
Wie throwwird weiterhin alles verfügbare Gas verbraucht? Können wir also schlussfolgern, dass throwdas ungenutzte Gas nicht an den Absender zurückgeschickt wird? @RobHitchens

Das revertwird oft als bezeichnet, cheap throwda es dem Absender nicht verbrauchtes Gas zurückerstattet.

Wenn Sie am detaillierten Design dieser Funktion interessiert sind, sehen Sie sich bitte die ursprüngliche EIP-140- Diskussion an.

Ende des Bytecodes nach STOP soll nicht ausgeführt werden. Es sind Konstruktorargumente. Was genau macht Solidity damit und was speichert es dort? Nur Gavin kann es wissen. Ich vermute, es ist ein Patch, um alle Ihre Gelder zu haben