Skip to content

Commit

Permalink
internal/ethapi: EstimateGas and Call handle revert error(#173) (#200)
Browse files Browse the repository at this point in the history
hot fix for EstimateGas and Call handle revert error #173
  • Loading branch information
gzliudan authored Oct 27, 2022
1 parent be3e9eb commit 42379f5
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 47 deletions.
26 changes: 26 additions & 0 deletions accounts/abi/abi.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ package abi
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"

"github.com/XinFinOrg/XDPoSChain/crypto"
)

// The ABI holds information about a contract's context and available
Expand Down Expand Up @@ -144,3 +147,26 @@ func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
}
return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
}


// revertSelector is a special function selector for revert reason unpacking.
var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]

// UnpackRevert resolves the abi-encoded revert reason. According to the solidity
// spec https://solidity.readthedocs.io/en/latest/control-structures.html#revert,
// the provided revert reason is abi-encoded as if it were a call to a function
// `Error(string)`. So it's a special tool for it.
func UnpackRevert(data []byte) (string, error) {
if len(data) < 4 {
return "", errors.New("invalid data for unpacking")
}
if !bytes.Equal(data[:4], revertSelector) {
return "", errors.New("invalid data for unpacking")
}
typ, _ := NewType("string")
unpacked, err := (Arguments{{Type: typ}}).Unpack2(data[4:])
if err != nil {
return "", err
}
return unpacked[0].(string), nil
}
12 changes: 12 additions & 0 deletions accounts/abi/argument.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package abi

import (
"encoding/json"
"errors"
"fmt"
"reflect"
"strings"
Expand Down Expand Up @@ -100,6 +101,17 @@ func (arguments Arguments) Unpack(v interface{}, data []byte) error {
return arguments.unpackAtomic(v, marshalledValues)
}

// Unpack2 performs the operation hexdata -> Go format.
func (arguments Arguments) Unpack2(data []byte) ([]interface{}, error) {
if len(data) == 0 {
if len(arguments.NonIndexed()) != 0 {
return nil, errors.New("abi: attempting to unmarshall an empty string while arguments are expected")
}
return make([]interface{}, 0), nil
}
return arguments.UnpackValues(data)
}

func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {

var (
Expand Down
5 changes: 3 additions & 2 deletions accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call XDPoSChain.Call

// callContract implements common code between normal and pending contract calls.
// state is modified during execution, make sure to copy it if necessary.
func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, uint64, bool, error) {
func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.CallMsg, block *types.Block, statedb *state.StateDB) (ret []byte, usedGas uint64, failed bool, err error) {
// Ensure message is initialized properly.
if call.GasPrice == nil {
call.GasPrice = big.NewInt(1)
Expand Down Expand Up @@ -353,7 +353,8 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.Cal
vmenv := vm.NewEVM(evmContext, statedb, nil, b.config, vm.Config{})
gaspool := new(core.GasPool).AddGas(math.MaxUint64)
owner := common.Address{}
return core.NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner)
ret, usedGas, failed, err, _ = core.NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner)
return
}

// SendTransaction updates the pending block to include the given transaction.
Expand Down
2 changes: 1 addition & 1 deletion core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
// End Bypass blacklist address

// Apply the transaction to the current state (included in the env)
_, gas, failed, err := ApplyMessage(vmenv, msg, gp, coinbaseOwner)
_, gas, failed, err, _ := ApplyMessage(vmenv, msg, gp, coinbaseOwner)

if err != nil {
return nil, 0, err, false
Expand Down
12 changes: 6 additions & 6 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition
// the gas used (which includes gas refunds) and an error if it failed. An error always
// indicates a core error meaning that the message would always fail for that particular
// state and would never be accepted within a block.
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool, owner common.Address) ([]byte, uint64, bool, error) {
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool, owner common.Address) ([]byte, uint64, bool, error, error) {
return NewStateTransition(evm, msg, gp).TransitionDb(owner)
}

Expand Down Expand Up @@ -215,7 +215,7 @@ func (st *StateTransition) preCheck() error {
// TransitionDb will transition the state by applying the current message and
// returning the result including the the used gas. It returns an error if it
// failed. An error indicates a consensus issue.
func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedGas uint64, failed bool, err error) {
func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedGas uint64, failed bool, err error, vmErr error) {
if err = st.preCheck(); err != nil {
return
}
Expand All @@ -228,10 +228,10 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
// Pay intrinsic gas
gas, err := IntrinsicGas(st.data, contractCreation, homestead)
if err != nil {
return nil, 0, false, err
return nil, 0, false, err, nil
}
if err = st.useGas(gas); err != nil {
return nil, 0, false, err
return nil, 0, false, err, nil
}

var (
Expand Down Expand Up @@ -261,7 +261,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
// sufficient balance to make the transfer happen. The first
// balance transfer may never fail.
if vmerr == vm.ErrInsufficientBalance {
return nil, 0, false, vmerr
return nil, 0, false, vmerr, nil
}
}
st.refundGas()
Expand All @@ -274,7 +274,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice))
}

return ret, st.gasUsed(), vmerr != nil, err
return ret, st.gasUsed(), vmerr != nil, err, vmerr
}

func (st *StateTransition) refundGas() {
Expand Down
2 changes: 1 addition & 1 deletion core/token_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func CallContractWithState(call ethereum.CallMsg, chain consensus.ChainContext,
vmenv := vm.NewEVM(evmContext, statedb, nil, chain.Config(), vm.Config{})
gaspool := new(GasPool).AddGas(1000000)
owner := common.Address{}
rval, _, _, err := NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner)
rval, _, _, err, _ := NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner)
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions eth/api_tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,

vmenv := vm.NewEVM(vmctx, statedb, XDCxState, api.config, vm.Config{})
owner := common.Address{}
if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner); err != nil {
if _, _, _, err, _ := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner); err != nil {
failed = err
break
}
Expand Down Expand Up @@ -631,7 +631,7 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, v
vmenv := vm.NewEVM(vmctx, statedb, nil, api.config, vm.Config{Debug: true, Tracer: tracer})

owner := common.Address{}
ret, gas, failed, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), owner)
ret, gas, failed, err, _ := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), owner)
if err != nil {
return nil, fmt.Errorf("tracing failed: %v", err)
}
Expand Down
17 changes: 9 additions & 8 deletions eth/tracers/tracers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ import (
"crypto/ecdsa"
"crypto/rand"
"encoding/json"
"io/ioutil"
"math/big"
"path/filepath"
"reflect"
"strings"
"testing"

"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/common/math"
Expand All @@ -31,12 +38,6 @@ import (
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/rlp"
"github.com/XinFinOrg/XDPoSChain/tests"
"io/ioutil"
"math/big"
"path/filepath"
"reflect"
"strings"
"testing"
)

// To generate a new callTracer test, copy paste the makeTest method below into
Expand Down Expand Up @@ -183,7 +184,7 @@ func TestPrestateTracerCreate2(t *testing.T) {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if _, _, _, err = st.TransitionDb(common.Address{}); err != nil {
if _, _, _, err, _ = st.TransitionDb(common.Address{}); err != nil {
t.Fatalf("failed to execute transaction: %v", err)
}
// Retrieve the trace result and compare against the etalon
Expand Down Expand Up @@ -258,7 +259,7 @@ func TestCallTracer(t *testing.T) {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if _, _, _, err = st.TransitionDb(common.Address{}); err != nil {
if _, _, _, err, _ = st.TransitionDb(common.Address{}); err != nil {
t.Fatalf("failed to execute transaction: %v", err)
}
// Retrieve the trace result and compare against the etalon
Expand Down
Loading

0 comments on commit 42379f5

Please sign in to comment.