Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to catch exceptions #1505

Closed
chriseth opened this issue Dec 13, 2016 · 8 comments
Closed

Ability to catch exceptions #1505

chriseth opened this issue Dec 13, 2016 · 8 comments
Labels
language design :rage4: Any changes to the language, e.g. new features

Comments

@chriseth
Copy link
Contributor

A general try { ... } catch-statement should be available to catch exceptions.

How to implement it: The "invalid label" has to become dynamic and somehow either the stack height has to be stored or it will be impossible to access local variables. Furthermore, we have to decide whether local effects should be reverted or not.

@federicobond
Copy link
Contributor

Would it be possible to attach a label to the catch statement? Too general catch clauses have proven to be a constant source of bugs in many programs.

Perhaps these tags could be translated to simple errno-like integers to be stored in a special location and the catch label {} could check against this number before running the catch clause, or continue the stack unwinding otherwise.

Sure, you don't need language support to do this, but given how difficult it is to get this kind of error handling right, I think it would be a welcome addition.

@chriseth
Copy link
Contributor Author

chriseth commented Jan 9, 2017

The main problem to overcome is how to handle cross-call exceptions.

@pirapira
Copy link
Member

@chriseth This can be solved by passing two return pointers on the stack. One for normal returning, and the other for exception throwing.

@axic
Copy link
Member

axic commented Jul 13, 2017

With REVERT once the CALL opcode is able to return 3 states (success, exception, revert) we could consider introducing exception catching for methods:

try {
  Wallet(addr1).transferTo(addr2);
}
catch (e)
{
  if (e == "nofunds")
    // yay, we ran out of monez
}

This would be backwards compatible, because the higher level method still throws an exception, but the try clause will execute if the CALL failed with REVERT.

@chriseth
Copy link
Contributor Author

@axic Do you think cross-internal-call exception catching can be done in ewasm?

@axic axic added the language design :rage4: Any changes to the language, e.g. new features label Jul 28, 2018
@axic axic added this to To discuss in Backlog (breaking) via automation Jul 28, 2018
@axic axic added feature and removed accepted labels Jul 28, 2018
@fulldecent
Copy link
Contributor

I am a fan of the MVP:

try {
  Wallet(addr1).transferTo(addr2);
} catch {
  // something didn't work
}

In other news, I am not a fan of storing English-language error messages inside the blockchain. So I think this MVP is a final product.

@SilentCicero
Copy link

SilentCicero commented Jan 22, 2019

Hey all,

I'm trying to impliment this for the Gnosis MultiSend, such that if one of the transactions fails, the MultiSend skips and continues with the next transaction, thoughts?

I suppose I just remove the revert line, and that should do it right?

pragma solidity ^0.5.0;


/// @title Multi Send - Allows to batch multiple transactions into one.
/// @author Nick Dodson - <nick.dodson@consensys.net>
/// @author Gonçalo Sá - <goncalo.sa@consensys.net>
/// @author Stefan George - <stefan@gnosis.pm>
contract MultiSend {

    /// @dev Sends multiple transactions and reverts all if one fails.
    /// @param transactions Encoded transactions. Each transaction is encoded as a 
    ///                     tuple(operation,address,uint256,bytes), where operation 
    ///                     can be 0 for a call or 1 for a delegatecall. The bytes 
    ///                     of all encoded transactions are concatenated to form the input.
    function multiSend(bytes memory transactions)
        public
    {
        // solium-disable-next-line security/no-inline-assembly
        assembly {
            let length := mload(transactions)
            let i := 0x20
            for { } lt(i, length) { } {
                let operation := mload(add(transactions, i))
                let to := mload(add(transactions, add(i, 0x20)))
                let value := mload(add(transactions, add(i, 0x40)))
                let dataLength := mload(add(transactions, add(i, 0x80)))
                let data := add(transactions, add(i, 0xa0))
                let success := 0
                switch operation 
                case 0 { success := call(gas, to, value, data, dataLength, 0, 0) }
                case 1 { success := delegatecall(gas, to, data, dataLength, 0, 0) }
                if eq(success, 0) { revert(0, 0) }
                i := add(i, add(0xa0, mul(div(add(dataLength, 0x1f), 0x20), 0x20)))
            }
        }
    }
}

@chriseth
Copy link
Contributor Author

chriseth commented Dec 3, 2019

Has been implemented for external calls in 0.6.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
language design :rage4: Any changes to the language, e.g. new features
Projects
No open projects
Development

No branches or pull requests

6 participants