With an exception, as with throw
, all effects (including events) of a transaction are reverted, except for the payment to the miner. So there would be no benefit in firing an event before a throw
.
Possible Alternative
If full reversion via an exception isn't required, an error code might be usable.
Example:
contract C {
function foo() returns(int) {
if(msg.sender != owner) {
Error("User not authorized")
return -1111; // some error code
}
// sender is authorized so do stuff
}
}
Events can't be accessed by contracts so the error message is more for the frontend. If the message is just for the frontend, consider using a pattern of eth_call
before an eth_sendTransaction
(call vs sendTx explained here).
The eth_call
before eth_sendTransaction
pattern would look like this frontend Javascript code:
// `contract` is an instance, from web3.eth.contract(...).at(...)
if (contract.foo.call({from: eth.accounts[0]...) === -1111) {
// example only of how error could be dealt with
alert("User not authorized");
return;
}
// user should now be authorized (though it is possible another transaction may have changed `owner` in contract `C` after above eth_call and before this upcoming eth_sendTransaction)
contract.foo.sendTransaction({from: eth.accounts[0]......)
That said, there can be cases where creating an Error event is desired. For example, if your DApp wants analytics on how many errors have been encountered, how many are unauthorized errors, etc an Error event could be the way to go.
EDIT: When https://github.com/ethereum/solidity/issues/1686 is implemented, the example in the question would look like:
require(msg.sender == owner, "User not authorized");
Solidity didn't offer an exception management mechanism as try/catch (for Java/c#).
due to an invalid EVM code, throw consumes all provided gas terminates and reverts all changes to the state made by current contract execution and to Ether balances. this concept intends to prevent spamming the network.
in the Yellow paper it is mentioned that :
Just as with contract creation, if the execution halts in an
exceptional fashion (i.e. due to an exhausted gas supply, stack
underflow, invalid jump destination or invalid instruction), then no
gas is refunded to the caller and the state is reverted to the point
immediately prior to balance transfer (i.e. σ)
as an information for the “intrinsic gas too low” message
Mist checks the amount gas without executing any code and it tells you that the transaction will fail because you didn't provide enough gas to execute the transaction.
Best Answer
All gas is consumed because the EVM essentially only has 1 exception: Out of Gas.
To see this clearer, take a look at the difference between a "pure" exception, and an error due to bad/buggy/invalid EVM code. Out of Gas is the former. Now there are errors such as stack underflow, invalid JUMP, and invalid opcode: they can be called "exceptions" but they are more like errors than an EVM exception, because the developer of the contract has not written valid EVM code according to the rules of the EVM. For example, an invalid opcode is like writing "class" instead of "function" in Javascript: native Javascript does not have "class", unlike a language like Java. With stack underflow, it's like the developer telling the EVM to add 2 numbers, but the developer does not provide the 2nd number.
Note also how "exception" in the EVM, is different from other languages. For example, there is no divide by zero exception in the EVM (though Solidity 0.4+ will generate an exception for this). Assuming valid EVM code, there are no exceptions except for Out of Gas.
Since there's only an Out of Gas exception, these other errors were made to behave as Out of Gas. This is relatively consistent with some Ethereum design principles of generalization and minimizing complexity. In other words, when implementing the behavior of stack underflow, invalid JUMP, and invalid opcode, it's simpler and easier for the protocol to reuse the implementation and behavior of Out of Gas.
Other answers have explained how Solidity compiles
throw
to an invalid JUMP. (Serpent instead uses an invalid opcode, and calls itinvalid
instead ofthrow
.)EDIT: The Byzantium release includes EIP 140, a new opcode named
REVERT
in the EVM that:throw
is being deprecated in favor ofrevert()
,require()
,assert()