diff --git a/core/store/ledgerstore/ledger_store.go b/core/store/ledgerstore/ledger_store.go
index a2fdaede7..f29a071d3 100644
--- a/core/store/ledgerstore/ledger_store.go
+++ b/core/store/ledgerstore/ledger_store.go
@@ -16,6 +16,7 @@
* along with The ontology. If not, see .
*/
//Storage of ledger
+
package ledgerstore
import (
@@ -1447,7 +1448,15 @@ func (this *LedgerStoreImp) PreExecuteContract(tx *types.Transaction) (*sstate.P
return this.PreExecuteContractWithParam(tx, param)
}
+func (this *LedgerStoreImp) TraceEip155Tx(msg types3.Message, tracer evm2.Tracer) (*types5.ExecutionResult, error) {
+ return this.executeEip155Tx(msg, evm2.Config{Debug: true, Tracer: tracer})
+}
+
func (this *LedgerStoreImp) PreExecuteEip155Tx(msg types3.Message) (*types5.ExecutionResult, error) {
+ return this.executeEip155Tx(msg, evm2.Config{})
+}
+
+func (this *LedgerStoreImp) executeEip155Tx(msg types3.Message, conf evm2.Config) (*types5.ExecutionResult, error) {
height := this.GetCurrentBlockHeight()
// use previous block time to make it predictable for easy test
blockTime := uint32(time.Now().Unix())
@@ -1466,7 +1475,7 @@ func (this *LedgerStoreImp) PreExecuteEip155Tx(msg types3.Message) (*types5.Exec
blockContext := evm.NewEVMBlockContext(height, blockTime, this)
cache := this.GetCacheDB()
statedb := storage.NewStateDB(cache, common2.Hash{}, common2.Hash(ctx.BlockHash), ong.OngBalanceHandle{})
- vmenv := evm2.NewEVM(blockContext, txContext, statedb, config, evm2.Config{})
+ vmenv := evm2.NewEVM(blockContext, txContext, statedb, config, conf)
res, err := evm.ApplyMessage(vmenv, msg, common2.Address(utils.GovernanceContractAddress))
return res, err
}
diff --git a/core/store/store.go b/core/store/store.go
index c04acdec1..e2449c187 100644
--- a/core/store/store.go
+++ b/core/store/store.go
@@ -32,6 +32,7 @@ import (
types3 "github.com/ontio/ontology/smartcontract/service/evm/types"
cstates "github.com/ontio/ontology/smartcontract/states"
"github.com/ontio/ontology/smartcontract/storage"
+ "github.com/ontio/ontology/vm/evm"
)
type ExecuteResult struct {
@@ -78,6 +79,7 @@ type LedgerStore interface {
PreExecuteContract(tx *types.Transaction) (*cstates.PreExecResult, error)
PreExecuteContractBatch(txes []*types.Transaction, atomic bool) ([]*cstates.PreExecResult, uint32, error)
PreExecuteEip155Tx(msg types2.Message) (*types3.ExecutionResult, error)
+ TraceEip155Tx(msg types2.Message, tracer evm.Tracer) (*types3.ExecutionResult, error)
GetEventNotifyByTx(tx common.Uint256) (*event.ExecuteNotify, error)
GetEventNotifyByBlock(height uint32) ([]*event.ExecuteNotify, error)
GetEthCode(hash common2.Hash) ([]byte, error)
diff --git a/http/ethrpc/debug/tracer.go b/http/ethrpc/debug/tracer.go
new file mode 100644
index 000000000..2d9c371c9
--- /dev/null
+++ b/http/ethrpc/debug/tracer.go
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 The ontology Authors
+ * This file is part of The ontology library.
+ *
+ * The ontology 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 ontology 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 ontology. If not, see .
+ */
+
+package debug
+
+import (
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/ontio/ontology/core/ledger"
+ "github.com/ontio/ontology/http/ethrpc/eth"
+ types2 "github.com/ontio/ontology/http/ethrpc/types"
+ "github.com/ontio/ontology/vm/evm"
+ "github.com/ontio/ontology/vm/evm/tracers"
+)
+
+// DebugAPI is the collection of tracing APIs exposed over the private debugging endpoint.
+type DebugAPI struct {
+}
+
+// NewDebugAPI creates a new DebugAPI definition for the tracing methods of the Ethereum service.
+func NewDebugAPI() *DebugAPI {
+ return &DebugAPI{}
+}
+
+// TraceConfig holds extra parameters to trace functions.
+type TraceConfig struct {
+ *evm.LogConfig
+ Tracer *string
+ Timeout *string
+ Reexec *uint64
+}
+
+// TraceCallConfig is the config for traceCall DebugAPI. It holds one more
+// field to override the state for tracing.
+type TraceCallConfig struct {
+ *evm.LogConfig
+ Tracer *string
+ Timeout *string
+ Reexec *uint64
+ //StateOverrides *ethapi.StateOverride
+}
+
+// TraceCall lets you trace a given eth_call. It collects the structured logs
+// created during the execution of EVM if the given transaction was added on
+// top of the provided block and returns them as a JSON object.
+// You can provide -2 as a block number to trace on top of the pending block.
+func (api *DebugAPI) TraceCall(args types2.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) {
+ // Execute the trace
+ msg := args.AsMessage(eth.RPCGasCap)
+
+ var traceConfig *TraceConfig
+ if config != nil {
+ traceConfig = &TraceConfig{
+ LogConfig: config.LogConfig,
+ Tracer: config.Tracer,
+ Timeout: config.Timeout,
+ Reexec: config.Reexec,
+ }
+ }
+ return api.traceTx(msg, traceConfig)
+}
+
+// traceTx configures a new tracer according to the provided configuration, and
+// executes the given message in the provided environment. The return value will
+// be tracer dependent.
+func (api *DebugAPI) traceTx(message types.Message, config *TraceConfig) (interface{}, error) {
+ // Assemble the structured logger or the JavaScript tracer
+ var (
+ tracer evm.Tracer
+ err error
+ )
+ switch {
+ case config == nil:
+ tracer = evm.NewStructLogger(nil)
+ case config.Tracer != nil:
+ switch *config.Tracer {
+ case "callTracer":
+ tracer = tracers.NewCallTracer()
+ default:
+ return nil, fmt.Errorf("unkown tracer type: %s", *config.Tracer)
+ }
+ default:
+ tracer = evm.NewStructLogger(config.LogConfig)
+ }
+
+ result, err := ledger.DefLedger.TraceEip155Tx(message, nil)
+ if err != nil {
+ return nil, fmt.Errorf("tracing failed: %w", err)
+ }
+
+ // Depending on the tracer type, format and return the output.
+ switch tracer := tracer.(type) {
+ case *evm.StructLogger:
+ // If the result contains a revert reason, return it.
+ returnVal := fmt.Sprintf("%x", result.Return())
+ if len(result.Revert()) > 0 {
+ returnVal = fmt.Sprintf("%x", result.Revert())
+ }
+ return &ExecutionResult{
+ Gas: result.UsedGas,
+ Failed: result.Failed(),
+ ReturnValue: returnVal,
+ StructLogs: FormatLogs(tracer.StructLogs()),
+ }, nil
+
+ case *tracers.CallTracer:
+ return tracer.GetResult()
+
+ default:
+ panic(fmt.Sprintf("bad tracer type %T", tracer))
+ }
+}
diff --git a/http/ethrpc/debug/types.go b/http/ethrpc/debug/types.go
new file mode 100644
index 000000000..630eddcf8
--- /dev/null
+++ b/http/ethrpc/debug/types.go
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 The ontology Authors
+ * This file is part of The ontology library.
+ *
+ * The ontology 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 ontology 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 ontology. If not, see .
+ */
+
+package debug
+
+import (
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/common/math"
+ "github.com/ontio/ontology/vm/evm"
+)
+
+// ExecutionResult groups all structured logs emitted by the EVM
+// while replaying a transaction in debug mode as well as transaction
+// execution status, the amount of gas used and the return value
+type ExecutionResult struct {
+ Gas uint64 `json:"gas"`
+ Failed bool `json:"failed"`
+ ReturnValue string `json:"returnValue"`
+ StructLogs []StructLogRes `json:"structLogs"`
+}
+
+// StructLogRes stores a structured log emitted by the EVM while replaying a
+// transaction in debug mode
+type StructLogRes struct {
+ Pc uint64 `json:"pc"`
+ Op string `json:"op"`
+ Gas uint64 `json:"gas"`
+ GasCost uint64 `json:"gasCost"`
+ Depth int `json:"depth"`
+ Error error `json:"error,omitempty"`
+ Stack *[]string `json:"stack,omitempty"`
+ Memory *[]string `json:"memory,omitempty"`
+ Storage *map[string]string `json:"storage,omitempty"`
+}
+
+// FormatLogs formats EVM returned structured logs for json output
+func FormatLogs(logs []evm.StructLog) []StructLogRes {
+ formatted := make([]StructLogRes, len(logs))
+ for index, trace := range logs {
+ formatted[index] = StructLogRes{
+ Pc: trace.Pc,
+ Op: trace.Op.String(),
+ Gas: trace.Gas,
+ GasCost: trace.GasCost,
+ Depth: trace.Depth,
+ Error: trace.Err,
+ }
+ if trace.Stack != nil {
+ stack := make([]string, len(trace.Stack))
+ for i, stackValue := range trace.Stack {
+ stack[i] = fmt.Sprintf("%x", math.PaddedBigBytes(stackValue, 32))
+ }
+ formatted[index].Stack = &stack
+ }
+ if trace.Memory != nil {
+ memory := make([]string, 0, (len(trace.Memory)+31)/32)
+ for i := 0; i+32 <= len(trace.Memory); i += 32 {
+ memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32]))
+ }
+ formatted[index].Memory = &memory
+ }
+ if trace.Storage != nil {
+ storage := make(map[string]string)
+ for i, storageValue := range trace.Storage {
+ storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue)
+ }
+ formatted[index].Storage = &storage
+ }
+ }
+ return formatted
+}
diff --git a/http/ethrpc/rpc_server.go b/http/ethrpc/rpc_server.go
index d2771a04a..32880c526 100644
--- a/http/ethrpc/rpc_server.go
+++ b/http/ethrpc/rpc_server.go
@@ -28,6 +28,7 @@ import (
"github.com/ontio/ontology/core/store/ledgerstore"
"github.com/ontio/ontology/http/base/actor"
backend2 "github.com/ontio/ontology/http/ethrpc/backend"
+ "github.com/ontio/ontology/http/ethrpc/debug"
"github.com/ontio/ontology/http/ethrpc/eth"
filters2 "github.com/ontio/ontology/http/ethrpc/filters"
"github.com/ontio/ontology/http/ethrpc/net"
@@ -68,6 +69,9 @@ func StartEthServer(txpool *tp.TXPoolServer) error {
if err := server.RegisterName("web3", web3.NewAPI()); err != nil {
return err
}
+ if err := server.RegisterName("debug", debug.NewDebugAPI()); err != nil {
+ return err
+ }
// add cors wrapper
wrappedCORSHandler := node.NewHTTPHandlerStack(server, cors, vhosts)
diff --git a/vm/evm/evm.go b/vm/evm/evm.go
index d7b5f8d28..cf2baab93 100644
--- a/vm/evm/evm.go
+++ b/vm/evm/evm.go
@@ -25,8 +25,8 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/holiman/uint256"
+ common2 "github.com/ontio/ontology/common"
"github.com/ontio/ontology/core/types"
- "github.com/ontio/ontology/smartcontract/service/native/utils"
"github.com/ontio/ontology/vm/evm/errors"
"github.com/ontio/ontology/vm/evm/params"
)
@@ -554,8 +554,10 @@ func MakeOngTransferLog(stateDB StateDB, from, to common.Address, value *big.Int
topic[1] = common.BytesToHash(from[:])
topic[2] = common.BytesToHash(to[:])
val := common.BytesToHash(value.Bytes())
+ // copied from smartcontract/service/native/utils to avoid cycle dependencies
+ OngContractAddress, _ := common2.AddressParseFromBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02})
sl := &types.StorageLog{
- Address: common.BytesToAddress(utils.OngContractAddress[:]),
+ Address: common.BytesToAddress(OngContractAddress[:]),
Topics: topic,
Data: val[:],
}
diff --git a/vm/evm/gas_table_test.go b/vm/evm/gas_table_test.go
index be54f5f56..ef9b8faf9 100644
--- a/vm/evm/gas_table_test.go
+++ b/vm/evm/gas_table_test.go
@@ -18,18 +18,9 @@
package evm
import (
- "math"
- "math/big"
"testing"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ontio/ontology/core/store/leveldbstore"
- "github.com/ontio/ontology/core/store/overlaydb"
- "github.com/ontio/ontology/smartcontract/service/native/ong"
- "github.com/ontio/ontology/smartcontract/storage"
"github.com/ontio/ontology/vm/evm/errors"
- "github.com/ontio/ontology/vm/evm/params"
)
func TestMemoryGasCost(t *testing.T) {
@@ -51,62 +42,3 @@ func TestMemoryGasCost(t *testing.T) {
}
}
}
-
-var eip2200Tests = []struct {
- original byte
- gaspool uint64
- input string
- used uint64
- refund uint64
- failure error
-}{
- {0, math.MaxUint64, "0x60006000556000600055", 1612, 0, nil}, // 0 -> 0 -> 0
- {0, math.MaxUint64, "0x60006000556001600055", 20812, 0, nil}, // 0 -> 0 -> 1
- {0, math.MaxUint64, "0x60016000556000600055", 20812, 19200, nil}, // 0 -> 1 -> 0
- {0, math.MaxUint64, "0x60016000556002600055", 20812, 0, nil}, // 0 -> 1 -> 2
- {0, math.MaxUint64, "0x60016000556001600055", 20812, 0, nil}, // 0 -> 1 -> 1
- {1, math.MaxUint64, "0x60006000556000600055", 5812, 15000, nil}, // 1 -> 0 -> 0
- {1, math.MaxUint64, "0x60006000556001600055", 5812, 4200, nil}, // 1 -> 0 -> 1
- {1, math.MaxUint64, "0x60006000556002600055", 5812, 0, nil}, // 1 -> 0 -> 2
- {1, math.MaxUint64, "0x60026000556000600055", 5812, 15000, nil}, // 1 -> 2 -> 0
- {1, math.MaxUint64, "0x60026000556003600055", 5812, 0, nil}, // 1 -> 2 -> 3
- {1, math.MaxUint64, "0x60026000556001600055", 5812, 4200, nil}, // 1 -> 2 -> 1
- {1, math.MaxUint64, "0x60026000556002600055", 5812, 0, nil}, // 1 -> 2 -> 2
- {1, math.MaxUint64, "0x60016000556000600055", 5812, 15000, nil}, // 1 -> 1 -> 0
- {1, math.MaxUint64, "0x60016000556002600055", 5812, 0, nil}, // 1 -> 1 -> 2
- {1, math.MaxUint64, "0x60016000556001600055", 1612, 0, nil}, // 1 -> 1 -> 1
- {0, math.MaxUint64, "0x600160005560006000556001600055", 40818, 19200, nil}, // 0 -> 1 -> 0 -> 1
- {1, math.MaxUint64, "0x600060005560016000556000600055", 10818, 19200, nil}, // 1 -> 0 -> 1 -> 0
- {1, 2306, "0x6001600055", 2306, 0, errors.ErrOutOfGas}, // 1 -> 1 (2300 sentry + 2xPUSH)
- {1, 2307, "0x6001600055", 806, 0, nil}, // 1 -> 1 (2301 sentry + 2xPUSH)
-}
-
-func TestEIP2200(t *testing.T) {
- for i, tt := range eip2200Tests {
- address := common.BytesToAddress([]byte("contract"))
-
- db := storage.NewCacheDB(overlaydb.NewOverlayDB(leveldbstore.NewMemLevelDBStore()))
- statedb := storage.NewStateDB(db, common.Hash{}, common.Hash{}, ong.OngBalanceHandle{})
- statedb.CreateAccount(address)
- statedb.SetCode(address, hexutil.MustDecode(tt.input))
- statedb.SetState(address, common.Hash{}, common.BytesToHash([]byte{tt.original}))
- _ = statedb.Commit() // Push the state into the "original" slot
-
- vmctx := BlockContext{
- CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true },
- Transfer: func(StateDB, common.Address, common.Address, *big.Int) {},
- }
- vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
-
- _, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int))
- if err != tt.failure {
- t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure)
- }
- if used := tt.gaspool - gas; used != tt.used {
- t.Errorf("test %d: gas used mismatch: have %v, want %v", i, used, tt.used)
- }
- if refund := vmenv.StateDB.GetRefund(); refund != tt.refund {
- t.Errorf("test %d: gas refund mismatch: have %v, want %v", i, refund, tt.refund)
- }
- }
-}
diff --git a/vm/evm/tests/gas_table_test.go b/vm/evm/tests/gas_table_test.go
new file mode 100644
index 000000000..ea85390ec
--- /dev/null
+++ b/vm/evm/tests/gas_table_test.go
@@ -0,0 +1,93 @@
+// Copyright (C) 2021 The Ontology Authors
+// Copyright 2017 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 .
+
+package tests
+
+import (
+ "math"
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ontio/ontology/core/store/leveldbstore"
+ "github.com/ontio/ontology/core/store/overlaydb"
+ "github.com/ontio/ontology/smartcontract/service/native/ong"
+ "github.com/ontio/ontology/smartcontract/storage"
+ "github.com/ontio/ontology/vm/evm"
+ "github.com/ontio/ontology/vm/evm/errors"
+ "github.com/ontio/ontology/vm/evm/params"
+)
+
+var eip2200Tests = []struct {
+ original byte
+ gaspool uint64
+ input string
+ used uint64
+ refund uint64
+ failure error
+}{
+ {0, math.MaxUint64, "0x60006000556000600055", 1612, 0, nil}, // 0 -> 0 -> 0
+ {0, math.MaxUint64, "0x60006000556001600055", 20812, 0, nil}, // 0 -> 0 -> 1
+ {0, math.MaxUint64, "0x60016000556000600055", 20812, 19200, nil}, // 0 -> 1 -> 0
+ {0, math.MaxUint64, "0x60016000556002600055", 20812, 0, nil}, // 0 -> 1 -> 2
+ {0, math.MaxUint64, "0x60016000556001600055", 20812, 0, nil}, // 0 -> 1 -> 1
+ {1, math.MaxUint64, "0x60006000556000600055", 5812, 15000, nil}, // 1 -> 0 -> 0
+ {1, math.MaxUint64, "0x60006000556001600055", 5812, 4200, nil}, // 1 -> 0 -> 1
+ {1, math.MaxUint64, "0x60006000556002600055", 5812, 0, nil}, // 1 -> 0 -> 2
+ {1, math.MaxUint64, "0x60026000556000600055", 5812, 15000, nil}, // 1 -> 2 -> 0
+ {1, math.MaxUint64, "0x60026000556003600055", 5812, 0, nil}, // 1 -> 2 -> 3
+ {1, math.MaxUint64, "0x60026000556001600055", 5812, 4200, nil}, // 1 -> 2 -> 1
+ {1, math.MaxUint64, "0x60026000556002600055", 5812, 0, nil}, // 1 -> 2 -> 2
+ {1, math.MaxUint64, "0x60016000556000600055", 5812, 15000, nil}, // 1 -> 1 -> 0
+ {1, math.MaxUint64, "0x60016000556002600055", 5812, 0, nil}, // 1 -> 1 -> 2
+ {1, math.MaxUint64, "0x60016000556001600055", 1612, 0, nil}, // 1 -> 1 -> 1
+ {0, math.MaxUint64, "0x600160005560006000556001600055", 40818, 19200, nil}, // 0 -> 1 -> 0 -> 1
+ {1, math.MaxUint64, "0x600060005560016000556000600055", 10818, 19200, nil}, // 1 -> 0 -> 1 -> 0
+ {1, 2306, "0x6001600055", 2306, 0, errors.ErrOutOfGas}, // 1 -> 1 (2300 sentry + 2xPUSH)
+ {1, 2307, "0x6001600055", 806, 0, nil}, // 1 -> 1 (2301 sentry + 2xPUSH)
+}
+
+func TestEIP2200(t *testing.T) {
+ for i, tt := range eip2200Tests {
+ address := common.BytesToAddress([]byte("contract"))
+
+ db := storage.NewCacheDB(overlaydb.NewOverlayDB(leveldbstore.NewMemLevelDBStore()))
+ statedb := storage.NewStateDB(db, common.Hash{}, common.Hash{}, ong.OngBalanceHandle{})
+ statedb.CreateAccount(address)
+ statedb.SetCode(address, hexutil.MustDecode(tt.input))
+ statedb.SetState(address, common.Hash{}, common.BytesToHash([]byte{tt.original}))
+ _ = statedb.Commit() // Push the state into the "original" slot
+
+ vmctx := evm.BlockContext{
+ CanTransfer: func(evm.StateDB, common.Address, *big.Int) bool { return true },
+ Transfer: func(evm.StateDB, common.Address, common.Address, *big.Int) {},
+ }
+ vmenv := evm.NewEVM(vmctx, evm.TxContext{}, statedb, params.AllEthashProtocolChanges, evm.Config{ExtraEips: []int{2200}})
+
+ _, gas, err := vmenv.Call(evm.AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int))
+ if err != tt.failure {
+ t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure)
+ }
+ if used := tt.gaspool - gas; used != tt.used {
+ t.Errorf("test %d: gas used mismatch: have %v, want %v", i, used, tt.used)
+ }
+ if refund := vmenv.StateDB.GetRefund(); refund != tt.refund {
+ t.Errorf("test %d: gas refund mismatch: have %v, want %v", i, refund, tt.refund)
+ }
+ }
+}