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

internal/ethapi: EstimateGas and Call handle revert error(#173) #200

Merged
merged 1 commit into from
Oct 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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