Skip to content

Commit

Permalink
Embed the precompile error in the error message returned in the ccr (#…
Browse files Browse the repository at this point in the history
…231)

* Embed the precompile error (if any) in the error message returned in the ccr

* Fix panic

* Add bound check

* Small fix
  • Loading branch information
ferranbt committed Apr 4, 2024
1 parent 277c65b commit 9413e91
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 1 deletion.
2 changes: 1 addition & 1 deletion cmd/geth/forgecmd_test.go
Expand Up @@ -41,7 +41,7 @@ func TestForgeReadConfig(t *testing.T) {
require.NoError(t, err)

require.Len(t, sCtx.Backend.ExternalWhitelist, 0)
require.Len(t, sCtx.Backend.DnsRegistry, 0)
require.Len(t, sCtx.Backend.ServiceAliasRegistry, 0)

// read context from config toml file
ctx.Set("config", "./testdata/forge.toml")
Expand Down
42 changes: 42 additions & 0 deletions internal/ethapi/api.go
Expand Up @@ -17,6 +17,7 @@
package ethapi

import (
"bytes"
"context"
"encoding/hex"
"encoding/json"
Expand Down Expand Up @@ -2037,6 +2038,14 @@ func runMEVM(ctx context.Context, b Backend, state *state.StateDB, header *types
// If the execution fails we do not return an error but the transaction result itself
// since some callers of this function (i.e. estimate gas) differentiate between
// unrecoverable errors (i.e. nonce is incorrect) and failed executions.

// If the error comes from a precompile, return data contains the event PeekerReverted(address, bytes).
// If so, parse it and return the error message in a more informative way.
precompileErr := parseSuavePrecompileError(result.ReturnData)
if precompileErr != nil {
result.Err = fmt.Errorf("%v: %v", result.Err, precompileErr)
}

return nil, result, nil, nil
}

Expand Down Expand Up @@ -2094,6 +2103,39 @@ func runMEVM(ctx context.Context, b Backend, state *state.StateDB, header *types
return signed, result, storeFinalize, nil
}

var (
peekerRevertedPrefix = []byte{0x75, 0xff, 0xf4, 0x67}
)

func parseSuavePrecompileError(ret []byte) error {
if !bytes.HasPrefix(ret, peekerRevertedPrefix) {
return nil
}

ret = ret[4:]

if len(ret) < 96 {
log.Debug("invalid precompile reverted message")
return nil
}

// In ABI, the PeekerReverted(address, bytes) error is encoded as slots of 32 bytes:
// 0-32 bytes: address. 20 bytes padded with zeros to the left.
// 32-64 bytes: offset of the dynamic type bytes It is always 0x...040.
// 64-96 bytes: length of the dynamic type bytes.
// 96-end bytes: the dynamic type bytes.
precompileAddr := common.BytesToAddress(ret[:32])

size := new(big.Int).SetBytes(ret[64:96]).Uint64()
if 96+size > uint64(len(ret)) {
log.Debug("invalid precompile reverted message")
return nil
}

msg := ret[96 : 96+size]
return fmt.Errorf("precompile '%s' reverted: '%s'", precompileAddr, string(msg))
}

// Sign calculates an ECDSA signature for:
// keccak256("\x19Ethereum Signed Message:\n" + len(message) + message).
//
Expand Down
14 changes: 14 additions & 0 deletions internal/ethapi/api_test.go
Expand Up @@ -20,6 +20,7 @@ import (
"bytes"
"context"
"crypto/ecdsa"
"encoding/hex"
"encoding/json"
"errors"
"hash"
Expand Down Expand Up @@ -47,6 +48,7 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
suave "github.com/ethereum/go-ethereum/suave/core"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/sha3"
)

Expand Down Expand Up @@ -740,3 +742,15 @@ func TestRPCMarshalBlock(t *testing.T) {
}
}
}

// --- suave specific ---

func TestSuave_ParsePrecompileError(t *testing.T) {
txt := "75fff46700000000000000000000000000000000000000000000000000000000432000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002d47657420227365706f6c6961223a20756e737570706f727465642070726f746f636f6c20736368656d6520222200000000000000000000000000000000000000"
txtB, _ := hex.DecodeString(txt)

err := parseSuavePrecompileError(txtB)
require.Equal(t, err.Error(), `precompile '0x0000000000000000000000000000000043200002' reverted: 'Get "sepolia": unsupported protocol scheme ""'`)
}

// --- end of suave specific ---
22 changes: 22 additions & 0 deletions suave/e2e/workflow_test.go
Expand Up @@ -1385,6 +1385,28 @@ func TestE2E_EmptyAddress(t *testing.T) {
require.Error(t, err)
}

func TestE2E_PrecompileInternalError(t *testing.T) {
// if the precompile fails, return a more informative error
fr := newFramework(t)
defer fr.Close()

clt := fr.NewSDKClient()

contractAddr := common.Address{0x3}
contract := sdk.GetContract(contractAddr, exampleCallSourceContract.Abi, clt)

req := &types.HttpRequest{
Method: "GET",
Url: "sepolia",
}
_, err := contract.SendTransaction("remoteCall", []interface{}{req}, nil)
require.Error(t, err)

if !strings.Contains(err.Error(), `precompile '0x0000000000000000000000000000000043200002' reverted`) {
t.Fatal("bad")
}
}

type clientWrapper struct {
t *testing.T

Expand Down

0 comments on commit 9413e91

Please sign in to comment.