mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 19:00:46 +00:00
all: improve EstimateGas API (#20830)
This commit is contained in:
parent
21b05243b6
commit
7491a7ba74
16 changed files with 404 additions and 121 deletions
|
|
@ -19,6 +19,7 @@ package abi
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -713,3 +714,34 @@ func TestABI_MethodById(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnpackRevert(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var cases = []struct {
|
||||||
|
input string
|
||||||
|
expect string
|
||||||
|
expectErr error
|
||||||
|
}{
|
||||||
|
{"", "", errors.New("invalid data for unpacking")},
|
||||||
|
{"08c379a1", "", errors.New("invalid data for unpacking")},
|
||||||
|
{"08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d72657665727420726561736f6e00000000000000000000000000000000000000", "revert reason", nil},
|
||||||
|
}
|
||||||
|
for index, c := range cases {
|
||||||
|
t.Run(fmt.Sprintf("case %d", index), func(t *testing.T) {
|
||||||
|
got, err := UnpackRevert(common.Hex2Bytes(c.input))
|
||||||
|
if c.expectErr != nil {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected non-nil error")
|
||||||
|
}
|
||||||
|
if err.Error() != c.expectErr.Error() {
|
||||||
|
t.Fatalf("Expected error mismatch, want %v, got %v", c.expectErr, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.expect != got {
|
||||||
|
t.Fatalf("Output mismatch, want %v, got %v", c.expect, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/XDCx"
|
"github.com/XinFinOrg/XDPoSChain/XDCx"
|
||||||
"github.com/XinFinOrg/XDPoSChain/XDCxlending"
|
"github.com/XinFinOrg/XDPoSChain/XDCxlending"
|
||||||
"github.com/XinFinOrg/XDPoSChain/accounts"
|
"github.com/XinFinOrg/XDPoSChain/accounts"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/accounts/abi"
|
||||||
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
|
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
|
||||||
"github.com/XinFinOrg/XDPoSChain/accounts/keystore"
|
"github.com/XinFinOrg/XDPoSChain/accounts/keystore"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
|
@ -54,7 +55,6 @@ import (
|
||||||
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
|
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
|
||||||
|
|
||||||
var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block")
|
var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block")
|
||||||
var errGasEstimationFailed = errors.New("gas required exceeds allowance or always failing transaction")
|
|
||||||
|
|
||||||
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
|
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
|
||||||
// the background. Its main purpose is to allow easily testing contract bindings.
|
// the background. Its main purpose is to allow easily testing contract bindings.
|
||||||
|
|
@ -161,6 +161,12 @@ func NewSimulatedBackend(alloc core.GenesisAlloc) *SimulatedBackend {
|
||||||
return backend
|
return backend
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close terminates the underlying blockchain's update loop.
|
||||||
|
func (b *SimulatedBackend) Close() error {
|
||||||
|
b.blockchain.Stop()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Commit imports all the pending transactions as a single block and starts a
|
// Commit imports all the pending transactions as a single block and starts a
|
||||||
// fresh new state.
|
// fresh new state.
|
||||||
func (b *SimulatedBackend) Commit() {
|
func (b *SimulatedBackend) Commit() {
|
||||||
|
|
@ -290,8 +296,11 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call XDPoSChain.Cal
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rval, _, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
|
res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
|
||||||
return rval, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res.Return(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingCallContract executes a contract call on the pending state.
|
// PendingCallContract executes a contract call on the pending state.
|
||||||
|
|
@ -300,8 +309,11 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call XDPoSCh
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
|
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
|
||||||
|
|
||||||
rval, _, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
||||||
return rval, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res.Return(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
|
// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
|
||||||
|
|
@ -348,22 +360,33 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call XDPoSChain.Call
|
||||||
cap = hi
|
cap = hi
|
||||||
|
|
||||||
// Create a helper to check if a gas allowance results in an executable transaction
|
// Create a helper to check if a gas allowance results in an executable transaction
|
||||||
executable := func(gas uint64) bool {
|
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
|
||||||
call.Gas = gas
|
call.Gas = gas
|
||||||
|
|
||||||
snapshot := b.pendingState.Snapshot()
|
snapshot := b.pendingState.Snapshot()
|
||||||
_, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
||||||
b.pendingState.RevertToSnapshot(snapshot)
|
b.pendingState.RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
if err != nil || failed {
|
if err != nil {
|
||||||
return false
|
if err == core.ErrIntrinsicGas {
|
||||||
|
return true, nil, nil // Special case, raise gas limit
|
||||||
}
|
}
|
||||||
return true
|
return true, nil, err // Bail out
|
||||||
|
}
|
||||||
|
return res.Failed(), res, nil
|
||||||
}
|
}
|
||||||
// Execute the binary search and hone in on an executable gas limit
|
// Execute the binary search and hone in on an executable gas limit
|
||||||
for lo+1 < hi {
|
for lo+1 < hi {
|
||||||
mid := (hi + lo) / 2
|
mid := (hi + lo) / 2
|
||||||
if !executable(mid) {
|
failed, _, err := executable(mid)
|
||||||
|
|
||||||
|
// If the error is not nil(consensus error), it means the provided message
|
||||||
|
// call or transaction will never be accepted no matter how much gas it is
|
||||||
|
// assigned. Return the error directly, don't struggle any more
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if failed {
|
||||||
lo = mid
|
lo = mid
|
||||||
} else {
|
} else {
|
||||||
hi = mid
|
hi = mid
|
||||||
|
|
@ -371,8 +394,25 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call XDPoSChain.Call
|
||||||
}
|
}
|
||||||
// Reject the transaction as invalid if it still fails at the highest allowance
|
// Reject the transaction as invalid if it still fails at the highest allowance
|
||||||
if hi == cap {
|
if hi == cap {
|
||||||
if !executable(hi) {
|
failed, result, err := executable(hi)
|
||||||
return 0, errGasEstimationFailed
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if failed {
|
||||||
|
if result != nil && result.Err != vm.ErrOutOfGas {
|
||||||
|
errMsg := fmt.Sprintf("always failing transaction (%v)", result.Err)
|
||||||
|
if len(result.Revert()) > 0 {
|
||||||
|
ret, err := abi.UnpackRevert(result.Revert())
|
||||||
|
if err != nil {
|
||||||
|
errMsg += fmt.Sprintf(" (%#x)", result.Revert())
|
||||||
|
} else {
|
||||||
|
errMsg += fmt.Sprintf(" (%s)", ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, errors.New(errMsg)
|
||||||
|
}
|
||||||
|
// Otherwise, the specified gas cap is too low
|
||||||
|
return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return hi, nil
|
return hi, nil
|
||||||
|
|
@ -380,10 +420,10 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call XDPoSChain.Call
|
||||||
|
|
||||||
// callContract implements common code between normal and pending contract calls.
|
// callContract implements common code between normal and pending contract calls.
|
||||||
// state is modified during execution, make sure to copy it if necessary.
|
// 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) (ret []byte, usedGas uint64, failed bool, err error) {
|
func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.CallMsg, block *types.Block, statedb *state.StateDB) (*core.ExecutionResult, error) {
|
||||||
// Gas prices post 1559 need to be initialized
|
// Gas prices post 1559 need to be initialized
|
||||||
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
|
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
|
||||||
return nil, 0, false, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
||||||
}
|
}
|
||||||
head := b.blockchain.CurrentHeader()
|
head := b.blockchain.CurrentHeader()
|
||||||
if !b.blockchain.Config().IsEIP1559(head.Number) {
|
if !b.blockchain.Config().IsEIP1559(head.Number) {
|
||||||
|
|
@ -438,8 +478,8 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.Cal
|
||||||
vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, b.config, vm.Config{NoBaseFee: true})
|
vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, b.config, vm.Config{NoBaseFee: true})
|
||||||
gaspool := new(core.GasPool).AddGas(math.MaxUint64)
|
gaspool := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
owner := common.Address{}
|
owner := common.Address{}
|
||||||
ret, usedGas, failed, err, _ = core.NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner)
|
res, err, _ := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner)
|
||||||
return
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendTransaction updates the pending block to include the given transaction.
|
// SendTransaction updates the pending block to include the given transaction.
|
||||||
|
|
|
||||||
154
accounts/abi/bind/backends/simulated_test.go
Normal file
154
accounts/abi/bind/backends/simulated_test.go
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
// Copyright 2019 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package backends
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/accounts/abi"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSimulatedBackend_EstimateGas(t *testing.T) {
|
||||||
|
/*
|
||||||
|
pragma solidity ^0.6.4;
|
||||||
|
contract GasEstimation {
|
||||||
|
function PureRevert() public { revert(); }
|
||||||
|
function Revert() public { revert("revert reason");}
|
||||||
|
function OOG() public { for (uint i = 0; ; i++) {}}
|
||||||
|
function Assert() public { assert(false);}
|
||||||
|
function Valid() public {}
|
||||||
|
}*/
|
||||||
|
const contractAbi = "[{\"inputs\":[],\"name\":\"Assert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OOG\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PureRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Revert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Valid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
|
||||||
|
const contractBin = "0x60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040523480156100115760006000fd5b506004361061005c5760003560e01c806350f6fe3414610062578063aa8b1d301461006c578063b9b046f914610076578063d8b9839114610080578063e09fface1461008a5761005c565b60006000fd5b61006a610094565b005b6100746100ad565b005b61007e6100b5565b005b6100886100c2565b005b610092610135565b005b6000600090505b5b808060010191505061009b565b505b565b60006000fd5b565b600015156100bf57fe5b5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081526020015060200191505060405180910390fd5b565b5b56fea2646970667358221220345bbcbb1a5ecf22b53a78eaebf95f8ee0eceff6d10d4b9643495084d2ec934a64736f6c63430006040033"
|
||||||
|
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
opts := bind.NewKeyedTransactor(key)
|
||||||
|
|
||||||
|
sim := NewXDCSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(params.Ether)}}, 10000000, ¶ms.ChainConfig{
|
||||||
|
ConstantinopleBlock: big.NewInt(0),
|
||||||
|
XDPoS: ¶ms.XDPoSConfig{
|
||||||
|
Epoch: 900,
|
||||||
|
SkipV1Validation: true,
|
||||||
|
V2: ¶ms.V2{
|
||||||
|
SwitchBlock: big.NewInt(900),
|
||||||
|
CurrentConfig: params.UnitTestV2Configs[0],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
defer sim.Close()
|
||||||
|
|
||||||
|
parsed, _ := abi.JSON(strings.NewReader(contractAbi))
|
||||||
|
contractAddr, _, _, _ := bind.DeployContract(opts, parsed, common.FromHex(contractBin), sim)
|
||||||
|
sim.Commit()
|
||||||
|
|
||||||
|
var cases = []struct {
|
||||||
|
name string
|
||||||
|
message XDPoSChain.CallMsg
|
||||||
|
expect uint64
|
||||||
|
expectError error
|
||||||
|
}{
|
||||||
|
{"plain transfer(valid)", XDPoSChain.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &addr,
|
||||||
|
Gas: 0,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: big.NewInt(1),
|
||||||
|
Data: nil,
|
||||||
|
}, params.TxGas, nil},
|
||||||
|
|
||||||
|
{"plain transfer(invalid)", XDPoSChain.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 0,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: big.NewInt(1),
|
||||||
|
Data: nil,
|
||||||
|
}, 0, errors.New("always failing transaction (execution reverted)")},
|
||||||
|
|
||||||
|
{"Revert", XDPoSChain.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 0,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: nil,
|
||||||
|
Data: common.Hex2Bytes("d8b98391"),
|
||||||
|
}, 0, errors.New("always failing transaction (execution reverted) (revert reason)")},
|
||||||
|
|
||||||
|
{"PureRevert", XDPoSChain.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 0,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: nil,
|
||||||
|
Data: common.Hex2Bytes("aa8b1d30"),
|
||||||
|
}, 0, errors.New("always failing transaction (execution reverted)")},
|
||||||
|
|
||||||
|
{"OOG", XDPoSChain.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 100000,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: nil,
|
||||||
|
Data: common.Hex2Bytes("50f6fe34"),
|
||||||
|
}, 0, errors.New("gas required exceeds allowance (100000)")},
|
||||||
|
|
||||||
|
{"Assert", XDPoSChain.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 100000,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: nil,
|
||||||
|
Data: common.Hex2Bytes("b9b046f9"),
|
||||||
|
}, 0, errors.New("always failing transaction (invalid opcode: INVALID)")},
|
||||||
|
|
||||||
|
{"Valid", XDPoSChain.CallMsg{
|
||||||
|
From: addr,
|
||||||
|
To: &contractAddr,
|
||||||
|
Gas: 100000,
|
||||||
|
GasPrice: big.NewInt(0),
|
||||||
|
Value: nil,
|
||||||
|
Data: common.Hex2Bytes("e09fface"),
|
||||||
|
}, 21483, nil},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
got, err := sim.EstimateGas(context.Background(), c.message)
|
||||||
|
if c.expectError != nil {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expect error, got nil")
|
||||||
|
}
|
||||||
|
if c.expectError.Error() != err.Error() {
|
||||||
|
t.Fatalf("Expect error, want %v, got %v", c.expectError, err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if got != c.expect {
|
||||||
|
t.Fatalf("Gas estimation mismatch, want %d, got %d", c.expect, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -82,8 +82,6 @@ var (
|
||||||
blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
|
blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
|
||||||
|
|
||||||
CheckpointCh = make(chan int)
|
CheckpointCh = make(chan int)
|
||||||
|
|
||||||
ErrNoGenesis = errors.New("Genesis not found in chain")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,18 @@ var (
|
||||||
// ErrBlacklistedHash is returned if a block to import is on the blacklist.
|
// ErrBlacklistedHash is returned if a block to import is on the blacklist.
|
||||||
ErrBlacklistedHash = errors.New("blacklisted hash")
|
ErrBlacklistedHash = errors.New("blacklisted hash")
|
||||||
|
|
||||||
|
// ErrNoGenesis is returned when there is no Genesis Block.
|
||||||
|
ErrNoGenesis = errors.New("genesis not found in chain")
|
||||||
|
)
|
||||||
|
|
||||||
|
// List of evm-call-message pre-checking errors. All state transtion messages will
|
||||||
|
// be pre-checked before execution. If any invalidation detected, the corresponding
|
||||||
|
// error should be returned which is defined here.
|
||||||
|
//
|
||||||
|
// - If the pre-checking happens in the miner, then the transaction won't be packed.
|
||||||
|
// - If the pre-checking happens in the block processing procedure, then a "BAD BLOCk"
|
||||||
|
// error should be emitted.
|
||||||
|
var (
|
||||||
// ErrNonceTooLow is returned if the nonce of a transaction is lower than the
|
// ErrNonceTooLow is returned if the nonce of a transaction is lower than the
|
||||||
// one present in the local chain.
|
// one present in the local chain.
|
||||||
ErrNonceTooLow = errors.New("nonce too low")
|
ErrNonceTooLow = errors.New("nonce too low")
|
||||||
|
|
@ -45,6 +57,10 @@ var (
|
||||||
// by a transaction is higher than what's left in the block.
|
// by a transaction is higher than what's left in the block.
|
||||||
ErrGasLimitReached = errors.New("gas limit reached")
|
ErrGasLimitReached = errors.New("gas limit reached")
|
||||||
|
|
||||||
|
// ErrInsufficientFundsForTransfer is returned if the transaction sender doesn't
|
||||||
|
// have enough funds for transfer(topmost call only).
|
||||||
|
ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer")
|
||||||
|
|
||||||
// ErrMaxInitCodeSizeExceeded is returned if creation transaction provides the init code bigger
|
// ErrMaxInitCodeSizeExceeded is returned if creation transaction provides the init code bigger
|
||||||
// than init code size limit.
|
// than init code size limit.
|
||||||
ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
|
ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
|
||||||
|
|
|
||||||
|
|
@ -403,7 +403,7 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
|
||||||
// End Bypass blacklist address
|
// End Bypass blacklist address
|
||||||
|
|
||||||
// Apply the transaction to the current state (included in the env)
|
// Apply the transaction to the current state (included in the env)
|
||||||
_, gas, failed, err, _ := ApplyMessage(evm, msg, gp, coinbaseOwner)
|
result, err, _ := ApplyMessage(evm, msg, gp, coinbaseOwner)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err, false
|
return nil, 0, err, false
|
||||||
|
|
@ -416,18 +416,18 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
|
||||||
} else {
|
} else {
|
||||||
root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes()
|
root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes()
|
||||||
}
|
}
|
||||||
*usedGas += gas
|
*usedGas += result.UsedGas
|
||||||
|
|
||||||
// Create a new receipt for the transaction, storing the intermediate root and gas used
|
// Create a new receipt for the transaction, storing the intermediate root and gas used
|
||||||
// by the tx.
|
// by the tx.
|
||||||
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas}
|
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas}
|
||||||
if failed {
|
if result.Failed() {
|
||||||
receipt.Status = types.ReceiptStatusFailed
|
receipt.Status = types.ReceiptStatusFailed
|
||||||
} else {
|
} else {
|
||||||
receipt.Status = types.ReceiptStatusSuccessful
|
receipt.Status = types.ReceiptStatusSuccessful
|
||||||
}
|
}
|
||||||
receipt.TxHash = tx.Hash()
|
receipt.TxHash = tx.Hash()
|
||||||
receipt.GasUsed = gas
|
receipt.GasUsed = result.UsedGas
|
||||||
|
|
||||||
// If the transaction created a contract, store the creation address in the receipt.
|
// If the transaction created a contract, store the creation address in the receipt.
|
||||||
if msg.To() == nil {
|
if msg.To() == nil {
|
||||||
|
|
@ -440,10 +440,10 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
|
||||||
receipt.BlockHash = blockHash
|
receipt.BlockHash = blockHash
|
||||||
receipt.BlockNumber = blockNumber
|
receipt.BlockNumber = blockNumber
|
||||||
receipt.TransactionIndex = uint(statedb.TxIndex())
|
receipt.TransactionIndex = uint(statedb.TxIndex())
|
||||||
if balanceFee != nil && failed {
|
if balanceFee != nil && result.Failed() {
|
||||||
state.PayFeeWithTRC21TxFail(statedb, msg.From(), *to)
|
state.PayFeeWithTRC21TxFail(statedb, msg.From(), *to)
|
||||||
}
|
}
|
||||||
return receipt, gas, err, balanceFee != nil
|
return receipt, result.UsedGas, err, balanceFee != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCoinbaseOwner(bc *BlockChain, statedb *state.StateDB, header *types.Header, author *common.Address) common.Address {
|
func getCoinbaseOwner(bc *BlockChain, statedb *state.StateDB, header *types.Header, author *common.Address) common.Address {
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
var emptyCodeHash = crypto.Keccak256Hash(nil)
|
var emptyCodeHash = crypto.Keccak256Hash(nil)
|
||||||
|
|
@ -73,7 +73,6 @@ type StateTransition struct {
|
||||||
// Message represents a message sent to a contract.
|
// Message represents a message sent to a contract.
|
||||||
type Message interface {
|
type Message interface {
|
||||||
From() common.Address
|
From() common.Address
|
||||||
//FromFrontier() (common.Address, error)
|
|
||||||
To() *common.Address
|
To() *common.Address
|
||||||
|
|
||||||
GasPrice() *big.Int
|
GasPrice() *big.Int
|
||||||
|
|
@ -89,6 +88,35 @@ type Message interface {
|
||||||
AccessList() types.AccessList
|
AccessList() types.AccessList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExecutionResult includes all output after executing given evm
|
||||||
|
// message no matter the execution itself is successful or not.
|
||||||
|
type ExecutionResult struct {
|
||||||
|
UsedGas uint64 // Total used gas but include the refunded gas
|
||||||
|
Err error // Any error encountered during the execution(listed in core/vm/errors.go)
|
||||||
|
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failed returns the indicator whether the execution is successful or not
|
||||||
|
func (result *ExecutionResult) Failed() bool { return result.Err != nil }
|
||||||
|
|
||||||
|
// Return is a helper function to help caller distinguish between revert reason
|
||||||
|
// and function return. Return returns the data after execution if no error occurs.
|
||||||
|
func (result *ExecutionResult) Return() []byte {
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return common.CopyBytes(result.ReturnData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revert returns the concrete revert reason if the execution is aborted by `REVERT`
|
||||||
|
// opcode. Note the reason can be nil if no data supplied with revert opcode.
|
||||||
|
func (result *ExecutionResult) Revert() []byte {
|
||||||
|
if result.Err != vm.ErrExecutionReverted {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return common.CopyBytes(result.ReturnData)
|
||||||
|
}
|
||||||
|
|
||||||
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
||||||
func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead bool, isEIP3860 bool) (uint64, error) {
|
func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation, isHomestead bool, isEIP3860 bool) (uint64, error) {
|
||||||
// Set the starting gas for the raw transaction
|
// Set the starting gas for the raw transaction
|
||||||
|
|
@ -166,7 +194,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
|
// 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
|
// indicates a core error meaning that the message would always fail for that particular
|
||||||
// state and would never be accepted within a block.
|
// 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, error) {
|
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool, owner common.Address) (*ExecutionResult, error, error) {
|
||||||
return NewStateTransition(evm, msg, gp).TransitionDb(owner)
|
return NewStateTransition(evm, msg, gp).TransitionDb(owner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,7 +241,7 @@ func (st *StateTransition) buyGas() error {
|
||||||
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want)
|
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want)
|
||||||
}
|
}
|
||||||
} else if balanceTokenFee.Cmp(mgval) < 0 {
|
} else if balanceTokenFee.Cmp(mgval) < 0 {
|
||||||
return errInsufficientBalanceForGas
|
return ErrInsufficientFunds
|
||||||
}
|
}
|
||||||
if err := st.gp.SubGas(st.msg.Gas()); err != nil {
|
if err := st.gp.SubGas(st.msg.Gas()); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -289,7 +317,7 @@ func (st *StateTransition) preCheck() error {
|
||||||
//
|
//
|
||||||
// However if any consensus issue encountered, return the error directly with
|
// However if any consensus issue encountered, return the error directly with
|
||||||
// nil evm execution result.
|
// nil evm execution result.
|
||||||
func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedGas uint64, failed bool, err error, vmErr error) {
|
func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult, error, error) {
|
||||||
// First check this message satisfies all consensus rules before
|
// First check this message satisfies all consensus rules before
|
||||||
// applying the message. The rules include these clauses
|
// applying the message. The rules include these clauses
|
||||||
//
|
//
|
||||||
|
|
@ -301,8 +329,8 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
|
||||||
// 6. caller has enough balance to cover asset transfer for **topmost** call
|
// 6. caller has enough balance to cover asset transfer for **topmost** call
|
||||||
|
|
||||||
// Check clauses 1-3, buy gas if everything is correct
|
// Check clauses 1-3, buy gas if everything is correct
|
||||||
if err = st.preCheck(); err != nil {
|
if err := st.preCheck(); err != nil {
|
||||||
return nil, 0, false, err, nil
|
return nil, err, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -317,16 +345,16 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
|
||||||
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
||||||
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, rules.IsEIP1559)
|
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, rules.IsEIP1559)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, false, err, nil
|
return nil, err, nil
|
||||||
}
|
}
|
||||||
if st.gas < gas {
|
if st.gas < gas {
|
||||||
return nil, 0, false, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas), nil
|
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gas, gas), nil
|
||||||
}
|
}
|
||||||
st.gas -= gas
|
st.gas -= gas
|
||||||
|
|
||||||
// Check whether the init code size has been exceeded.
|
// Check whether the init code size has been exceeded.
|
||||||
if rules.IsEIP1559 && contractCreation && len(st.data) > params.MaxInitCodeSize {
|
if rules.IsEIP1559 && contractCreation && len(st.data) > params.MaxInitCodeSize {
|
||||||
return nil, 0, false, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize), nil
|
return nil, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the preparatory steps for state transition which includes:
|
// Execute the preparatory steps for state transition which includes:
|
||||||
|
|
@ -334,35 +362,25 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
|
||||||
// - reset transient storage(eip 1153)
|
// - reset transient storage(eip 1153)
|
||||||
st.state.Prepare(rules, msg.From(), st.evm.Context.Coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
|
st.state.Prepare(rules, msg.From(), st.evm.Context.Coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
|
||||||
|
|
||||||
|
// Check clause 6
|
||||||
|
value, overflow := uint256.FromBig(msg.Value())
|
||||||
|
if overflow {
|
||||||
|
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex()), nil
|
||||||
|
}
|
||||||
|
if !value.IsZero() && !st.evm.Context.CanTransfer(st.state, msg.From(), value.ToBig()) {
|
||||||
|
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex()), nil
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
evm = st.evm
|
ret []byte
|
||||||
// vm errors do not effect consensus and are therefor
|
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
||||||
// not assigned to err, except for insufficient balance
|
|
||||||
// error.
|
|
||||||
vmerr error
|
|
||||||
)
|
)
|
||||||
// for debugging purpose
|
|
||||||
// TODO: clean it after fixing the issue https://github.com/XinFinOrg/XDPoSChain/issues/401
|
|
||||||
var contractAction string
|
|
||||||
nonce := uint64(1)
|
|
||||||
if contractCreation {
|
if contractCreation {
|
||||||
ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
|
ret, _, st.gas, vmerr = st.evm.Create(sender, st.data, st.gas, st.value)
|
||||||
contractAction = "contract creation"
|
|
||||||
} else {
|
} else {
|
||||||
// Increment the nonce for the next transaction
|
// Increment the nonce for the next transaction
|
||||||
nonce = st.state.GetNonce(sender.Address()) + 1
|
st.state.SetNonce(sender.Address(), st.state.GetNonce(sender.Address())+1)
|
||||||
st.state.SetNonce(sender.Address(), nonce)
|
ret, st.gas, vmerr = st.evm.Call(sender, st.to().Address(), st.data, st.gas, st.value)
|
||||||
ret, st.gas, vmerr = evm.Call(sender, st.to().Address(), st.data, st.gas, st.value)
|
|
||||||
contractAction = "contract call"
|
|
||||||
}
|
|
||||||
if vmerr != nil {
|
|
||||||
log.Debug("VM returned with error", "action", contractAction, "contract address", st.to().Address(), "gas", st.gas, "gasPrice", st.gasPrice, "nonce", nonce, "err", vmerr)
|
|
||||||
// The only possible consensus-error would be if there wasn't
|
|
||||||
// sufficient balance to make the transfer happen. The first
|
|
||||||
// balance transfer may never fail.
|
|
||||||
if vmerr == vm.ErrInsufficientBalance {
|
|
||||||
return nil, 0, false, vmerr, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !eip3529 {
|
if !eip3529 {
|
||||||
// Before EIP-3529: refunds were capped to gasUsed / 2
|
// Before EIP-3529: refunds were capped to gasUsed / 2
|
||||||
|
|
@ -384,7 +402,11 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
|
||||||
st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip))
|
st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret, st.gasUsed(), vmerr != nil, nil, vmerr
|
return &ExecutionResult{
|
||||||
|
UsedGas: st.gasUsed(),
|
||||||
|
Err: vmerr,
|
||||||
|
ReturnData: ret,
|
||||||
|
}, nil, vmerr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *StateTransition) refundGas(refundQuotient uint64) {
|
func (st *StateTransition) refundGas(refundQuotient uint64) {
|
||||||
|
|
|
||||||
|
|
@ -117,11 +117,11 @@ func CallContractWithState(call ethereum.CallMsg, chain consensus.ChainContext,
|
||||||
vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, chain.Config(), vm.Config{})
|
vmenv := vm.NewEVM(evmContext, txContext, statedb, nil, chain.Config(), vm.Config{})
|
||||||
gaspool := new(GasPool).AddGas(1000000)
|
gaspool := new(GasPool).AddGas(1000000)
|
||||||
owner := common.Address{}
|
owner := common.Address{}
|
||||||
rval, _, _, err, _ := NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner)
|
result, err, _ := NewStateTransition(vmenv, msg, gaspool).TransitionDb(owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return rval, err
|
return result.Return(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure that balance of token is at slot 0
|
// make sure that balance of token is at slot 0
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -526,7 +526,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
|
||||||
|
|
||||||
vmenv := vm.NewEVM(blockCtx, txContext, statedb, XDCxState, api.config, vm.Config{})
|
vmenv := vm.NewEVM(blockCtx, txContext, statedb, XDCxState, api.config, vm.Config{})
|
||||||
owner := common.Address{}
|
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
|
failed = err
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -750,7 +750,7 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t
|
||||||
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
|
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
|
||||||
|
|
||||||
owner := common.Address{}
|
owner := common.Address{}
|
||||||
ret, gas, failed, err, _ := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), owner)
|
result, err, _ := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()), owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("tracing failed: %v", err)
|
return nil, fmt.Errorf("tracing failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -758,9 +758,9 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, t
|
||||||
switch tracer := tracer.(type) {
|
switch tracer := tracer.(type) {
|
||||||
case *vm.StructLogger:
|
case *vm.StructLogger:
|
||||||
return ðapi.ExecutionResult{
|
return ðapi.ExecutionResult{
|
||||||
Gas: gas,
|
Gas: result.UsedGas,
|
||||||
Failed: failed,
|
Failed: result.Failed(),
|
||||||
ReturnValue: fmt.Sprintf("%x", ret),
|
ReturnValue: fmt.Sprintf("%x", result.Return()),
|
||||||
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
|
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
|
||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
|
||||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
}
|
}
|
||||||
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
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)
|
t.Fatalf("failed to execute transaction: %v", err)
|
||||||
}
|
}
|
||||||
// Retrieve the trace result and compare against the etalon
|
// Retrieve the trace result and compare against the etalon
|
||||||
|
|
@ -231,7 +231,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
|
||||||
evm := vm.NewEVM(context, txContext, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
|
evm := vm.NewEVM(context, txContext, statedb, nil, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
|
||||||
snap := statedb.Snapshot()
|
snap := statedb.Snapshot()
|
||||||
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
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 {
|
||||||
b.Fatalf("failed to execute transaction: %v", err)
|
b.Fatalf("failed to execute transaction: %v", err)
|
||||||
}
|
}
|
||||||
if _, err = tracer.GetResult(); err != nil {
|
if _, err = tracer.GetResult(); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@ func TestZeroValueToNotExitCall(t *testing.T) {
|
||||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
}
|
}
|
||||||
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
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)
|
t.Fatalf("failed to execute transaction: %v", err)
|
||||||
}
|
}
|
||||||
// Retrieve the trace result and compare against the etalon
|
// Retrieve the trace result and compare against the etalon
|
||||||
|
|
@ -244,7 +244,7 @@ func TestPrestateTracerCreate2(t *testing.T) {
|
||||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
}
|
}
|
||||||
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
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)
|
t.Fatalf("failed to execute transaction: %v", err)
|
||||||
}
|
}
|
||||||
// Retrieve the trace result and compare against the etalon
|
// Retrieve the trace result and compare against the etalon
|
||||||
|
|
@ -346,7 +346,7 @@ func BenchmarkTransactionTrace(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
snap := statedb.Snapshot()
|
snap := statedb.Snapshot()
|
||||||
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||||
_, _, _, err, _ = st.TransitionDb(common.Address{})
|
_, err, _ = st.TransitionDb(common.Address{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1322,18 +1322,18 @@ func (s *PublicBlockChainAPI) getCandidatesFromSmartContract() ([]utils.Masterno
|
||||||
return candidatesWithStakeInfo, nil
|
return candidatesWithStakeInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) ([]byte, uint64, bool, error, error) {
|
func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error, error) {
|
||||||
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
||||||
|
|
||||||
statedb, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
statedb, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||||
if statedb == nil || err != nil {
|
if statedb == nil || err != nil {
|
||||||
return nil, 0, false, err, nil
|
return nil, err, nil
|
||||||
}
|
}
|
||||||
if header == nil {
|
if header == nil {
|
||||||
return nil, 0, false, errors.New("nil header in DoCall"), nil
|
return nil, errors.New("nil header in DoCall"), nil
|
||||||
}
|
}
|
||||||
if err := overrides.Apply(statedb); err != nil {
|
if err := overrides.Apply(statedb); err != nil {
|
||||||
return nil, 0, false, err, nil
|
return nil, err, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup context so it may be cancelled the call has completed
|
// Setup context so it may be cancelled the call has completed
|
||||||
|
|
@ -1350,32 +1350,32 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
|
||||||
|
|
||||||
block, err := b.BlockByNumberOrHash(ctx, blockNrOrHash)
|
block, err := b.BlockByNumberOrHash(ctx, blockNrOrHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, false, err, nil
|
return nil, err, nil
|
||||||
}
|
}
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return nil, 0, false, fmt.Errorf("nil block in DoCall: number=%d, hash=%s", header.Number.Uint64(), header.Hash().Hex()), nil
|
return nil, fmt.Errorf("nil block in DoCall: number=%d, hash=%s", header.Number.Uint64(), header.Hash().Hex()), nil
|
||||||
}
|
}
|
||||||
author, err := b.GetEngine().Author(block.Header())
|
author, err := b.GetEngine().Author(block.Header())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, false, err, nil
|
return nil, err, nil
|
||||||
}
|
}
|
||||||
XDCxState, err := b.XDCxService().GetTradingState(block, author)
|
XDCxState, err := b.XDCxService().GetTradingState(block, author)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, false, err, nil
|
return nil, err, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: replace header.BaseFee with blockCtx.BaseFee
|
// TODO: replace header.BaseFee with blockCtx.BaseFee
|
||||||
// reference: https://github.com/ethereum/go-ethereum/pull/29051
|
// reference: https://github.com/ethereum/go-ethereum/pull/29051
|
||||||
msg, err := args.ToMessage(b, header.Number, globalGasCap, header.BaseFee)
|
msg, err := args.ToMessage(b, header.Number, globalGasCap, header.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, false, err, nil
|
return nil, err, nil
|
||||||
}
|
}
|
||||||
msg.SetBalanceTokenFeeForCall()
|
msg.SetBalanceTokenFeeForCall()
|
||||||
|
|
||||||
// Get a new instance of the EVM.
|
// Get a new instance of the EVM.
|
||||||
evm, vmError, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &vm.Config{NoBaseFee: true})
|
evm, vmError, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &vm.Config{NoBaseFee: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, false, err, nil
|
return nil, err, nil
|
||||||
}
|
}
|
||||||
// Wait for the context to be done and cancel the evm. Even if the
|
// Wait for the context to be done and cancel the evm. Even if the
|
||||||
// EVM has finished, cancelling may be done (repeatedly)
|
// EVM has finished, cancelling may be done (repeatedly)
|
||||||
|
|
@ -1387,19 +1387,19 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
|
||||||
// Execute the message.
|
// Execute the message.
|
||||||
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
owner := common.Address{}
|
owner := common.Address{}
|
||||||
res, gas, failed, err, vmErr := core.ApplyMessage(evm, msg, gp, owner)
|
result, err, vmErr := core.ApplyMessage(evm, msg, gp, owner)
|
||||||
if err := vmError(); err != nil {
|
if err := vmError(); err != nil {
|
||||||
return nil, 0, false, err, nil
|
return nil, err, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the timer caused an abort, return an appropriate error message
|
// If the timer caused an abort, return an appropriate error message
|
||||||
if evm.Cancelled() {
|
if evm.Cancelled() {
|
||||||
return nil, 0, false, fmt.Errorf("execution aborted (timeout = %v)", timeout), nil
|
return nil, fmt.Errorf("execution aborted (timeout = %v)", timeout), nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, 0, false, fmt.Errorf("err: %w (supplied gas %d)", err, msg.Gas()), nil
|
return result, fmt.Errorf("err: %w (supplied gas %d)", err, msg.Gas()), nil
|
||||||
}
|
}
|
||||||
return res, gas, failed, err, vmErr
|
return result, err, vmErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRevertError(res []byte) *revertError {
|
func newRevertError(res []byte) *revertError {
|
||||||
|
|
@ -1443,16 +1443,32 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, bl
|
||||||
if args.To != nil && *args.To == common.MasternodeVotingSMCBinary {
|
if args.To != nil && *args.To == common.MasternodeVotingSMCBinary {
|
||||||
timeout = 0
|
timeout = 0
|
||||||
}
|
}
|
||||||
result, _, failed, err, vmErr := DoCall(ctx, s.b, args, *blockNrOrHash, overrides, timeout, s.b.RPCGasCap())
|
result, err, vmErr := DoCall(ctx, s.b, args, *blockNrOrHash, overrides, timeout, s.b.RPCGasCap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// If the result contains a revert reason, try to unpack and return it.
|
// If the result contains a revert reason, try to unpack and return it.
|
||||||
if failed && len(result) > 0 {
|
if result.Failed() && len(result.Return()) > 0 {
|
||||||
return nil, newRevertError(result)
|
return nil, newRevertError(result.Return())
|
||||||
|
}
|
||||||
|
return result.Return(), vmErr
|
||||||
}
|
}
|
||||||
|
|
||||||
return (hexutil.Bytes)(result), vmErr
|
type estimateGasError struct {
|
||||||
|
error string // Concrete error type if it's failed to estimate gas usage
|
||||||
|
vmerr error // Additional field, it's non-nil if the given transaction is invalid
|
||||||
|
revert string // Additional field, it's non-empty if the transaction is reverted and reason is provided
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e estimateGasError) Error() string {
|
||||||
|
errMsg := e.error
|
||||||
|
if e.vmerr != nil {
|
||||||
|
errMsg += fmt.Sprintf(" (%v)", e.vmerr)
|
||||||
|
}
|
||||||
|
if e.revert != "" {
|
||||||
|
errMsg += fmt.Sprintf(" (%s)", e.revert)
|
||||||
|
}
|
||||||
|
return errMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, gasCap uint64) (hexutil.Uint64, error) {
|
func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, gasCap uint64) (hexutil.Uint64, error) {
|
||||||
|
|
@ -1496,21 +1512,17 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
|
||||||
cap = hi
|
cap = hi
|
||||||
|
|
||||||
// Create a helper to check if a gas allowance results in an executable transaction
|
// Create a helper to check if a gas allowance results in an executable transaction
|
||||||
executable := func(gas uint64) (bool, []byte, error, error) {
|
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
|
||||||
args.Gas = (*hexutil.Uint64)(&gas)
|
args.Gas = (*hexutil.Uint64)(&gas)
|
||||||
|
|
||||||
res, _, failed, err, vmErr := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap)
|
result, err, _ := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, vm.ErrOutOfGas) || errors.Is(err, core.ErrIntrinsicGas) {
|
if errors.Is(err, vm.ErrOutOfGas) || errors.Is(err, core.ErrIntrinsicGas) {
|
||||||
return false, nil, nil, nil // Special case, raise gas limit
|
return true, nil, nil // Special case, raise gas limit
|
||||||
}
|
}
|
||||||
return false, nil, err, nil // Bail out
|
return true, nil, err // Bail out
|
||||||
}
|
}
|
||||||
if failed {
|
return result.Failed(), result, nil
|
||||||
return false, res, nil, vmErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil, nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the transaction is a plain value transfer, short circuit estimation and
|
// If the transaction is a plain value transfer, short circuit estimation and
|
||||||
|
|
@ -1519,7 +1531,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
|
||||||
// unused access list items). Ever so slightly wasteful, but safer overall.
|
// unused access list items). Ever so slightly wasteful, but safer overall.
|
||||||
if args.Data == nil || len(*args.Data) == 0 {
|
if args.Data == nil || len(*args.Data) == 0 {
|
||||||
if args.To != nil && state.GetCodeSize(*args.To) == 0 {
|
if args.To != nil && state.GetCodeSize(*args.To) == 0 {
|
||||||
ok, _, err, _ := executable(params.TxGas)
|
ok, _, err := executable(params.TxGas)
|
||||||
if ok && err == nil {
|
if ok && err == nil {
|
||||||
return hexutil.Uint64(params.TxGas), nil
|
return hexutil.Uint64(params.TxGas), nil
|
||||||
}
|
}
|
||||||
|
|
@ -1529,7 +1541,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
|
||||||
// Execute the binary search and hone in on an executable gas limit
|
// Execute the binary search and hone in on an executable gas limit
|
||||||
for lo+1 < hi {
|
for lo+1 < hi {
|
||||||
mid := (hi + lo) / 2
|
mid := (hi + lo) / 2
|
||||||
ok, _, err, _ := executable(mid)
|
failed, _, err := executable(mid)
|
||||||
|
|
||||||
// If the error is not nil(consensus error), it means the provided message
|
// If the error is not nil(consensus error), it means the provided message
|
||||||
// call or transaction will never be accepted no matter how much gas it is
|
// call or transaction will never be accepted no matter how much gas it is
|
||||||
|
|
@ -1538,7 +1550,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok {
|
if failed {
|
||||||
lo = mid
|
lo = mid
|
||||||
} else {
|
} else {
|
||||||
hi = mid
|
hi = mid
|
||||||
|
|
@ -1547,21 +1559,30 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
|
||||||
|
|
||||||
// Reject the transaction as invalid if it still fails at the highest allowance
|
// Reject the transaction as invalid if it still fails at the highest allowance
|
||||||
if hi == cap {
|
if hi == cap {
|
||||||
ok, res, err, vmErr := executable(hi)
|
failed, result, err := executable(hi)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok {
|
if failed {
|
||||||
if vmErr != vm.ErrOutOfGas {
|
if result != nil && result.Err != vm.ErrOutOfGas {
|
||||||
if len(res) > 0 {
|
var revert string
|
||||||
return 0, newRevertError(res)
|
if len(result.Revert()) > 0 {
|
||||||
|
ret, err := abi.UnpackRevert(result.Revert())
|
||||||
|
if err != nil {
|
||||||
|
revert = hexutil.Encode(result.Revert())
|
||||||
|
} else {
|
||||||
|
revert = ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, estimateGasError{
|
||||||
|
error: "always failing transaction",
|
||||||
|
vmerr: result.Err,
|
||||||
|
revert: revert,
|
||||||
}
|
}
|
||||||
return 0, vmErr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, the specified gas cap is too low
|
// Otherwise, the specified gas cap is too low
|
||||||
return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
|
return 0, estimateGasError{error: fmt.Sprintf("gas required exceeds allowance (%d)", cap)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return hexutil.Uint64(hi), nil
|
return hexutil.Uint64(hi), nil
|
||||||
|
|
@ -2075,12 +2096,12 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
|
||||||
return nil, 0, nil, err
|
return nil, 0, nil, err
|
||||||
}
|
}
|
||||||
// TODO: determine the value of owner
|
// TODO: determine the value of owner
|
||||||
_, UsedGas, _, err, vmErr := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner)
|
res, err, _ := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()), owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err)
|
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err)
|
||||||
}
|
}
|
||||||
if tracer.Equal(prevTracer) {
|
if tracer.Equal(prevTracer) {
|
||||||
return accessList, UsedGas, vmErr, nil
|
return accessList, res.UsedGas, res.Err, nil
|
||||||
}
|
}
|
||||||
prevTracer = tracer
|
prevTracer = tracer
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -142,8 +142,8 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
|
||||||
//vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
|
//vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{})
|
||||||
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
owner := common.Address{}
|
owner := common.Address{}
|
||||||
ret, _, _, _, _ := core.ApplyMessage(vmenv, msg, gp, owner)
|
result, _, _ := core.ApplyMessage(vmenv, msg, gp, owner)
|
||||||
res = append(res, ret...)
|
res = append(res, result.Return()...)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
header := lc.GetHeaderByHash(bhash)
|
header := lc.GetHeaderByHash(bhash)
|
||||||
|
|
@ -160,9 +160,9 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
|
||||||
vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{NoBaseFee: true})
|
vmenv := vm.NewEVM(context, txContext, statedb, nil, config, vm.Config{NoBaseFee: true})
|
||||||
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
owner := common.Address{}
|
owner := common.Address{}
|
||||||
ret, _, _, _, _ := core.ApplyMessage(vmenv, msg, gp, owner)
|
result, _, _ := core.ApplyMessage(vmenv, msg, gp, owner)
|
||||||
if statedb.Error() == nil {
|
if statedb.Error() == nil {
|
||||||
res = append(res, ret...)
|
res = append(res, result.Return()...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -191,8 +191,8 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain
|
||||||
vmenv := vm.NewEVM(context, txContext, st, nil, config, vm.Config{NoBaseFee: true})
|
vmenv := vm.NewEVM(context, txContext, st, nil, config, vm.Config{NoBaseFee: true})
|
||||||
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
owner := common.Address{}
|
owner := common.Address{}
|
||||||
ret, _, _, _, _ := core.ApplyMessage(vmenv, msg, gp, owner)
|
result, _, _ := core.ApplyMessage(vmenv, msg, gp, owner)
|
||||||
res = append(res, ret...)
|
res = append(res, result.Return()...)
|
||||||
if st.Error() != nil {
|
if st.Error() != nil {
|
||||||
return res, st.Error()
|
return res, st.Error()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD
|
||||||
gaspool.AddGas(block.GasLimit())
|
gaspool.AddGas(block.GasLimit())
|
||||||
|
|
||||||
coinbase := &t.json.Env.Coinbase
|
coinbase := &t.json.Env.Coinbase
|
||||||
if _, _, _, err, _ := core.ApplyMessage(evm, msg, gaspool, *coinbase); err != nil {
|
if _, err, _ := core.ApplyMessage(evm, msg, gaspool, *coinbase); err != nil {
|
||||||
statedb.RevertToSnapshot(snapshot)
|
statedb.RevertToSnapshot(snapshot)
|
||||||
}
|
}
|
||||||
if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) {
|
if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue