9

Following an issue I opened in github, this bothers me. The operation described in the issue, which happened during following avsa's tutorial on creating a DAO, generated two internals transactions as evident here: tx Shortly, the internals transactions are:

  • Change the status of the requested operation in the DAO to "completed"
  • Actually perform the operation by sending a transaction to another contract

The first internal transaction completed successfully - that means updating the operation in the DAO to "completed" status. The second one fails, most likely because it went out of gas (multiple attempts produced the same results, while adding 100,000 gas made it work multiple times).

I think the implications of something like this happening in a real DAO are clear. I (mistakenly) assumed the if the second transaction ran out of gas, then the entire operation (including the first transaction) will revert.

As suggested by eth's comment, I will ask a specific question about this situation - how can I detect it inside the contract? Will changing the order of the operations help?

  • 2
    Thanks for your first question and it has the makings of quite a good one. Questions and answers should be able to stand on their own without having to click on links (because links go dead). As such, I suggest revising it. Your question also has quite a few parts and splitting it may be better. Q1 about changing the order is quite specific to your example, but the others could be asked separately. – eth Jan 24 '16 at 8:57
6

The entire operation did not revert because enough gas was provided by the transaction origin of the overall transaction. If insufficient gas was provided, then you would observe the behavior you were expecting, where both internal transactions would be reverted.

The second internal transaction was reverted because it ran out of gas. To detect this, the function that was called could include a true return value (use a tuple if needed). The caller can explicitly check for this true value, because an out of gas would return the default value which is 0 (false).

Here's an example of what happened. First (internal) transaction requires 200 gas and second transaction requires 700 gas. Overall transaction was provided with 1000 gas. The gas that was made available to the second transaction, was less than 700. All gas was not fully provided to the second internal transaction. See bottom NOTE for explanation.

Here are some things that may help:

  • changing the order of the operations, as you suggest, may help because the transaction that ran out of gas, will now be first and have all available gas to start with (instead of requiring refunded gas)
  • keeping the operations in the same order, but for the first transaction limit the gas sent with the function call via .gas(amount) syntax. This could then allow the second transaction more gas. (In the example above, it could be explicitly limiting the first internal transaction to 220 gas, so that refunded gas is only 20.)

The simplest solution is for the transaction origin to provide more gas in the transaction. There is little downside since all unused gas will be refunded, and with plenty of gas potentially available for internal transactions, edge cases may be avoided.

NOTE why "all" gas is not provided to a call (internal transaction):

chriseth (Solidity author) said:

This is one of the quirky places of the EVM. You cannot just say "send all my gas along with the call", you always have to give an explicit number. We do have the GAS opcode which gives us the amount of gas still available, but the problem is that performing the call itself also costs some gas, so we have to subtract the amount of gas the call costs from the value provided by GAS, and we also have to subtract the amount of gas it costs to perform the subtraction...

EDIT: To clarify possible misinterpretations 1) contracts never pay gas -- they can reserve/delegate/limit the gas for another contract call. 2) Reverted transactions are still included in blocks and pay gas.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.