¿Por qué `throw` y `revert()` crean diferentes bytecodes?

throwy revert()ambos están compilando la revertoperación (opcode 0xfd). Según los documentos :

La palabra clave throw también se puede utilizar como alternativa a revert().

¿Por qué entonces el siguiente contrato

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

produce un código de bytes diferente si lo uso revert()en lugar del throwejemplo anterior? Estoy usando Remix con Solidity 0.4.13+commit.0fb4cb1a.Emscripten.clang. Estos son los códigos de operación resultantes:

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 

Vemos que las últimas instrucciones son diferentes. Mientras que la versión con throwtermina en

0x627A7A723058
KECCAK256
0x4a
PUSH32
0x5473469C00A2379987B5AF61DFF50F8FABE3B71589C4D2BBB53073BEB9D10029

la versión con revert()extremos en

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
No estoy seguro si este es el motivo, pero los documentos de Solidity también dicen de revert(), en el futuro podría ser posible incluir también detalles sobre el error en una llamada para revertir. Y hay un problema abierto sobre la desaprobación total throw: github.com/ethereum/solidity/issues/1793 . Podría ser un andamiaje para el manejo de errores en el futuro. ¡Interesante pregunta!

Respuestas (3)

Dejaré la interpretación precisa de los OpCodes a otra persona y solo señalaré que son instrucciones diferentes, por lo que se esperan diferentes implementaciones.

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

Empezando por Metropolis, revert;devolverá el gas no utilizado mientras throwque seguirá consumiendo todo el gas disponible. En términos de cambios de estado del contrato, son lo mismo: todo se revierte.

Espero eso ayude.

Gracias. Mi comprensión de esos documentos es, sin embargo, que ambos revert()deben throwcompilarse en un REVERTcódigo de operación. Entonces esperaría que Metropolis maneje este código de operación de manera correspondiente (con un reembolso de gasolina). No veo por qué esto conduciría a un código compilado diferente ahora o nunca.
Puedo estar equivocado. Mi intuición dice que no podemos obtener resultados diferentes de un código idéntico. Dado que revert y throw do (will) realizan operaciones ligeramente diferentes con respecto al gas, es de esperar una diferencia en el código. Dicho de otra manera, si el código compilado fuera idéntico, ¿cómo sabría el EVM qué hacer con el gas?
ok, gracias, ahora llegué a mi malentendido: la solidez revert()devolverá el gas, no el REVERTcódigo de operación que usan ambos revert()y throwen Solidity, por lo tanto, necesitamos la diferencia en el código de bytes, ¡gracias!
¿Cómo throwseguirán consumiendo todo el gas disponible? Entonces, ¿ podemos concluir que throwno devolverá el gas no utilizado al remitente?@RobHitchens

A revertmenudo se denomina cheap throwcomo que reembolsa el gas no utilizado al remitente.

Si está interesado en el diseño detallado de esta función, consulte la discusión original de EIP-140 .

No se debe ejecutar el final del bytecode después de STOP. Son argumentos de constructor. ¿Qué hace exactamente Solidity con ellos y qué almacena allí? Solo Gavin puede saberlo. Supongo que es un parche para tener todos tus fondos.