Unverified Commit 29569375 authored by Martin Holst Swende's avatar Martin Holst Swende Committed by GitHub

core/vm: less allocations for various call variants (#21222)

* core/vm/runtime/tests: add more benchmarks

* core/vm: initial work on improving alloc count for calls to precompiles

name                                  old time/op    new time/op    delta
SimpleLoop/identity-precompile-10M-6     117ms ±75%      43ms ± 1%  -63.09%  (p=0.008 n=5+5)
SimpleLoop/loop-10M-6                   79.6ms ± 4%    70.5ms ± 1%  -11.42%  (p=0.008 n=5+5)

name                                  old alloc/op   new alloc/op   delta
SimpleLoop/identity-precompile-10M-6    24.4MB ± 0%     4.9MB ± 0%  -79.94%  (p=0.008 n=5+5)
SimpleLoop/loop-10M-6                   13.2kB ± 0%    13.2kB ± 0%     ~     (p=0.357 n=5+5)

name                                  old allocs/op  new allocs/op  delta
SimpleLoop/identity-precompile-10M-6      382k ± 0%      153k ± 0%  -59.99%  (p=0.000 n=5+4)
SimpleLoop/loop-10M-6                     40.0 ± 0%      40.0 ± 0%     ~     (all equal)

* core/vm: don't allocate big.int for touch

name                                  old time/op    new time/op    delta
SimpleLoop/identity-precompile-10M-6    43.3ms ± 1%    42.4ms ± 7%     ~     (p=0.151 n=5+5)
SimpleLoop/loop-10M-6                   70.5ms ± 1%    76.7ms ± 1%   +8.67%  (p=0.008 n=5+5)

name                                  old alloc/op   new alloc/op   delta
SimpleLoop/identity-precompile-10M-6    4.90MB ± 0%    2.46MB ± 0%  -49.83%  (p=0.008 n=5+5)
SimpleLoop/loop-10M-6                   13.2kB ± 0%    13.2kB ± 1%     ~     (p=0.571 n=5+5)

name                                  old allocs/op  new allocs/op  delta
SimpleLoop/identity-precompile-10M-6      153k ± 0%       76k ± 0%  -49.98%  (p=0.029 n=4+4)
SimpleLoop/loop-10M-6                     40.0 ± 0%      40.0 ± 0%     ~     (all equal)

* core/vm: reduce allocs in staticcall

name                                  old time/op    new time/op    delta
SimpleLoop/identity-precompile-10M-6    42.4ms ± 7%    37.5ms ± 6%  -11.68%  (p=0.008 n=5+5)
SimpleLoop/loop-10M-6                   76.7ms ± 1%    69.1ms ± 1%   -9.82%  (p=0.008 n=5+5)

name                                  old alloc/op   new alloc/op   delta
SimpleLoop/identity-precompile-10M-6    2.46MB ± 0%    0.02MB ± 0%  -99.35%  (p=0.008 n=5+5)
SimpleLoop/loop-10M-6                   13.2kB ± 1%    13.2kB ± 0%     ~     (p=0.143 n=5+5)

name                                  old allocs/op  new allocs/op  delta
SimpleLoop/identity-precompile-10M-6     76.4k ± 0%      0.1k ± 0%     ~     (p=0.079 n=4+5)
SimpleLoop/loop-10M-6                     40.0 ± 0%      40.0 ± 0%     ~     (all equal)

* trie: better use of hasher keccakState

* core/state/statedb: reduce allocations in getDeletedStateObject

* core/vm: reduce allocations in all call derivates

* core/vm: reduce allocations in call variants

- Make returnstack `uint32`
- Use a `sync.Pool` of `stack`s

* core/vm: fix tests

* core/vm: goimports

* core/vm: tracer fix + staticcall gas fix

* core/vm: add back snapshot to staticcall

* core/vm: review concerns + make returnstack pooled + enable returndata in traces

* core/vm: fix some test tracer method signatures

* core/vm: run gencodec, minor comment polish
Co-authored-by: 's avatarPéter Szilágyi <peterke@gmail.com>
parent 240d1851
...@@ -38,6 +38,10 @@ var ( ...@@ -38,6 +38,10 @@ var (
Name: "trace.nostack", Name: "trace.nostack",
Usage: "Disable stack output in traces", Usage: "Disable stack output in traces",
} }
TraceDisableReturnDataFlag = cli.BoolFlag{
Name: "trace.noreturndata",
Usage: "Disable return data output in traces",
}
OutputAllocFlag = cli.StringFlag{ OutputAllocFlag = cli.StringFlag{
Name: "output.alloc", Name: "output.alloc",
Usage: "Determines where to put the `alloc` of the post-state.\n" + Usage: "Determines where to put the `alloc` of the post-state.\n" +
......
...@@ -85,6 +85,7 @@ func Main(ctx *cli.Context) error { ...@@ -85,6 +85,7 @@ func Main(ctx *cli.Context) error {
logConfig := &vm.LogConfig{ logConfig := &vm.LogConfig{
DisableStack: ctx.Bool(TraceDisableStackFlag.Name), DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
DisableMemory: ctx.Bool(TraceDisableMemoryFlag.Name), DisableMemory: ctx.Bool(TraceDisableMemoryFlag.Name),
DisableReturnData: ctx.Bool(TraceDisableReturnDataFlag.Name),
Debug: true, Debug: true,
} }
var prevFile *os.File var prevFile *os.File
......
...@@ -121,6 +121,14 @@ var ( ...@@ -121,6 +121,14 @@ var (
Name: "nostack", Name: "nostack",
Usage: "disable stack output", Usage: "disable stack output",
} }
DisableStorageFlag = cli.BoolFlag{
Name: "nostorage",
Usage: "disable storage output",
}
DisableReturnDataFlag = cli.BoolFlag{
Name: "noreturndata",
Usage: "disable return data output",
}
EVMInterpreterFlag = cli.StringFlag{ EVMInterpreterFlag = cli.StringFlag{
Name: "vm.evm", Name: "vm.evm",
Usage: "External EVM configuration (default = built-in interpreter)", Usage: "External EVM configuration (default = built-in interpreter)",
...@@ -137,6 +145,7 @@ var stateTransitionCommand = cli.Command{ ...@@ -137,6 +145,7 @@ var stateTransitionCommand = cli.Command{
t8ntool.TraceFlag, t8ntool.TraceFlag,
t8ntool.TraceDisableMemoryFlag, t8ntool.TraceDisableMemoryFlag,
t8ntool.TraceDisableStackFlag, t8ntool.TraceDisableStackFlag,
t8ntool.TraceDisableReturnDataFlag,
t8ntool.OutputAllocFlag, t8ntool.OutputAllocFlag,
t8ntool.OutputResultFlag, t8ntool.OutputResultFlag,
t8ntool.InputAllocFlag, t8ntool.InputAllocFlag,
...@@ -172,6 +181,8 @@ func init() { ...@@ -172,6 +181,8 @@ func init() {
ReceiverFlag, ReceiverFlag,
DisableMemoryFlag, DisableMemoryFlag,
DisableStackFlag, DisableStackFlag,
DisableStorageFlag,
DisableReturnDataFlag,
EVMInterpreterFlag, EVMInterpreterFlag,
} }
app.Commands = []cli.Command{ app.Commands = []cli.Command{
......
...@@ -110,6 +110,8 @@ func runCmd(ctx *cli.Context) error { ...@@ -110,6 +110,8 @@ func runCmd(ctx *cli.Context) error {
logconfig := &vm.LogConfig{ logconfig := &vm.LogConfig{
DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name), DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
DisableStack: ctx.GlobalBool(DisableStackFlag.Name), DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name),
DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name),
Debug: ctx.GlobalBool(DebugFlag.Name), Debug: ctx.GlobalBool(DebugFlag.Name),
} }
......
...@@ -61,6 +61,8 @@ func stateTestCmd(ctx *cli.Context) error { ...@@ -61,6 +61,8 @@ func stateTestCmd(ctx *cli.Context) error {
config := &vm.LogConfig{ config := &vm.LogConfig{
DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name), DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
DisableStack: ctx.GlobalBool(DisableStackFlag.Name), DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name),
DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name),
} }
var ( var (
tracer vm.Tracer tracer vm.Tracer
......
...@@ -505,7 +505,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { ...@@ -505,7 +505,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
} }
// If no live objects are available, attempt to use snapshots // If no live objects are available, attempt to use snapshots
var ( var (
data Account data *Account
err error err error
) )
if s.snap != nil { if s.snap != nil {
...@@ -517,11 +517,15 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { ...@@ -517,11 +517,15 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
if acc == nil { if acc == nil {
return nil return nil
} }
data.Nonce, data.Balance, data.CodeHash = acc.Nonce, acc.Balance, acc.CodeHash data = &Account{
Nonce: acc.Nonce,
Balance: acc.Balance,
CodeHash: acc.CodeHash,
Root: common.BytesToHash(acc.Root),
}
if len(data.CodeHash) == 0 { if len(data.CodeHash) == 0 {
data.CodeHash = emptyCodeHash data.CodeHash = emptyCodeHash
} }
data.Root = common.BytesToHash(acc.Root)
if data.Root == (common.Hash{}) { if data.Root == (common.Hash{}) {
data.Root = emptyRoot data.Root = emptyRoot
} }
...@@ -540,13 +544,14 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { ...@@ -540,13 +544,14 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
if len(enc) == 0 { if len(enc) == 0 {
return nil return nil
} }
if err := rlp.DecodeBytes(enc, &data); err != nil { data = new(Account)
if err := rlp.DecodeBytes(enc, data); err != nil {
log.Error("Failed to decode state object", "addr", addr, "err", err) log.Error("Failed to decode state object", "addr", addr, "err", err)
return nil return nil
} }
} }
// Insert into the live set // Insert into the live set
obj := newObject(s, addr, data) obj := newObject(s, addr, *data)
s.setStateObject(obj) s.setStateObject(obj)
return obj return obj
} }
......
...@@ -102,12 +102,18 @@ var PrecompiledContractsYoloV1 = map[common.Address]PrecompiledContract{ ...@@ -102,12 +102,18 @@ var PrecompiledContractsYoloV1 = map[common.Address]PrecompiledContract{
} }
// RunPrecompiledContract runs and evaluates the output of a precompiled contract. // RunPrecompiledContract runs and evaluates the output of a precompiled contract.
func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) { // It returns
gas := p.RequiredGas(input) // - the returned bytes,
if contract.UseGas(gas) { // - the _remaining_ gas,
return p.Run(input) // - any error that occurred
func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
gasCost := p.RequiredGas(input)
if suppliedGas < gasCost {
return nil, 0, ErrOutOfGas
} }
return nil, ErrOutOfGas suppliedGas -= gasCost
output, err := p.Run(input)
return output, suppliedGas, err
} }
// ECRECOVER implemented as a native contract. // ECRECOVER implemented as a native contract.
...@@ -197,6 +203,7 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) { ...@@ -197,6 +203,7 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) {
type bigModExp struct{} type bigModExp struct{}
var ( var (
big0 = big.NewInt(0)
big1 = big.NewInt(1) big1 = big.NewInt(1)
big4 = big.NewInt(4) big4 = big.NewInt(4)
big8 = big.NewInt(8) big8 = big.NewInt(8)
......
...@@ -21,7 +21,6 @@ import ( ...@@ -21,7 +21,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/big"
"testing" "testing"
"time" "time"
...@@ -72,10 +71,9 @@ var blake2FMalformedInputTests = []precompiledFailureTest{ ...@@ -72,10 +71,9 @@ var blake2FMalformedInputTests = []precompiledFailureTest{
func testPrecompiled(addr string, test precompiledTest, t *testing.T) { func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
p := allPrecompiles[common.HexToAddress(addr)] p := allPrecompiles[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.Input) in := common.Hex2Bytes(test.Input)
contract := NewContract(AccountRef(common.HexToAddress("1337")), gas := p.RequiredGas(in)
nil, new(big.Int), p.RequiredGas(in)) t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(t *testing.T) { if res, _, err := RunPrecompiledContract(p, in, gas); err != nil {
if res, err := RunPrecompiledContract(p, in, contract); err != nil {
t.Error(err) t.Error(err)
} else if common.Bytes2Hex(res) != test.Expected { } else if common.Bytes2Hex(res) != test.Expected {
t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res)) t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))
...@@ -91,10 +89,10 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) { ...@@ -91,10 +89,10 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
p := allPrecompiles[common.HexToAddress(addr)] p := allPrecompiles[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.Input) in := common.Hex2Bytes(test.Input)
contract := NewContract(AccountRef(common.HexToAddress("1337")), gas := p.RequiredGas(in) - 1
nil, new(big.Int), p.RequiredGas(in)-1)
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(t *testing.T) { t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
_, err := RunPrecompiledContract(p, in, contract) _, _, err := RunPrecompiledContract(p, in, gas)
if err.Error() != "out of gas" { if err.Error() != "out of gas" {
t.Errorf("Expected error [out of gas], got [%v]", err) t.Errorf("Expected error [out of gas], got [%v]", err)
} }
...@@ -109,11 +107,9 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { ...@@ -109,11 +107,9 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) { func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) {
p := allPrecompiles[common.HexToAddress(addr)] p := allPrecompiles[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.Input) in := common.Hex2Bytes(test.Input)
contract := NewContract(AccountRef(common.HexToAddress("31337")), gas := p.RequiredGas(in)
nil, new(big.Int), p.RequiredGas(in))
t.Run(test.Name, func(t *testing.T) { t.Run(test.Name, func(t *testing.T) {
_, err := RunPrecompiledContract(p, in, contract) _, _, err := RunPrecompiledContract(p, in, gas)
if err.Error() != test.ExpectedError { if err.Error() != test.ExpectedError {
t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err) t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err)
} }
...@@ -132,8 +128,6 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { ...@@ -132,8 +128,6 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
p := allPrecompiles[common.HexToAddress(addr)] p := allPrecompiles[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.Input) in := common.Hex2Bytes(test.Input)
reqGas := p.RequiredGas(in) reqGas := p.RequiredGas(in)
contract := NewContract(AccountRef(common.HexToAddress("1337")),
nil, new(big.Int), reqGas)
var ( var (
res []byte res []byte
...@@ -141,14 +135,13 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { ...@@ -141,14 +135,13 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
data = make([]byte, len(in)) data = make([]byte, len(in))
) )
bench.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(bench *testing.B) { bench.Run(fmt.Sprintf("%s-Gas=%d", test.Name, reqGas), func(bench *testing.B) {
bench.ReportAllocs() bench.ReportAllocs()
start := time.Now().Nanosecond() start := time.Now().Nanosecond()
bench.ResetTimer() bench.ResetTimer()
for i := 0; i < bench.N; i++ { for i := 0; i < bench.N; i++ {
contract.Gas = reqGas
copy(data, in) copy(data, in)
res, err = RunPrecompiledContract(p, data, contract) res, _, err = RunPrecompiledContract(p, data, reqGas)
} }
bench.StopTimer() bench.StopTimer()
elapsed := float64(time.Now().Nanosecond() - start) elapsed := float64(time.Now().Nanosecond() - start)
......
This diff is collapsed.
...@@ -24,6 +24,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) { ...@@ -24,6 +24,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
MemorySize int `json:"memSize"` MemorySize int `json:"memSize"`
Stack []*math.HexOrDecimal256 `json:"stack"` Stack []*math.HexOrDecimal256 `json:"stack"`
ReturnStack []math.HexOrDecimal64 `json:"returnStack"` ReturnStack []math.HexOrDecimal64 `json:"returnStack"`
ReturnData []byte `json:"returnData"`
Storage map[common.Hash]common.Hash `json:"-"` Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"` Depth int `json:"depth"`
RefundCounter uint64 `json:"refund"` RefundCounter uint64 `json:"refund"`
...@@ -50,6 +51,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) { ...@@ -50,6 +51,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
enc.ReturnStack[k] = math.HexOrDecimal64(v) enc.ReturnStack[k] = math.HexOrDecimal64(v)
} }
} }
enc.ReturnData = s.ReturnData
enc.Storage = s.Storage enc.Storage = s.Storage
enc.Depth = s.Depth enc.Depth = s.Depth
enc.RefundCounter = s.RefundCounter enc.RefundCounter = s.RefundCounter
...@@ -70,6 +72,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { ...@@ -70,6 +72,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
MemorySize *int `json:"memSize"` MemorySize *int `json:"memSize"`
Stack []*math.HexOrDecimal256 `json:"stack"` Stack []*math.HexOrDecimal256 `json:"stack"`
ReturnStack []math.HexOrDecimal64 `json:"returnStack"` ReturnStack []math.HexOrDecimal64 `json:"returnStack"`
ReturnData []byte `json:"returnData"`
Storage map[common.Hash]common.Hash `json:"-"` Storage map[common.Hash]common.Hash `json:"-"`
Depth *int `json:"depth"` Depth *int `json:"depth"`
RefundCounter *uint64 `json:"refund"` RefundCounter *uint64 `json:"refund"`
...@@ -104,11 +107,14 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { ...@@ -104,11 +107,14 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
} }
} }
if dec.ReturnStack != nil { if dec.ReturnStack != nil {
s.ReturnStack = make([]uint64, len(dec.ReturnStack)) s.ReturnStack = make([]uint32, len(dec.ReturnStack))
for k, v := range dec.ReturnStack { for k, v := range dec.ReturnStack {
s.ReturnStack[k] = uint64(v) s.ReturnStack[k] = uint32(v)
} }
} }
if dec.ReturnData != nil {
s.ReturnData = dec.ReturnData
}
if dec.Storage != nil { if dec.Storage != nil {
s.Storage = dec.Storage s.Storage = dec.Storage
} }
......
...@@ -563,7 +563,7 @@ func opJumpSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ ...@@ -563,7 +563,7 @@ func opJumpSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
if !callContext.contract.validJumpSubdest(posU64) { if !callContext.contract.validJumpSubdest(posU64) {
return nil, ErrInvalidJump return nil, ErrInvalidJump
} }
callContext.rstack.push(*pc) callContext.rstack.push(uint32(*pc))
*pc = posU64 + 1 *pc = posU64 + 1
return nil, nil return nil, nil
} }
...@@ -575,7 +575,7 @@ func opReturnSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ...@@ -575,7 +575,7 @@ func opReturnSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
// Other than the check that the return stack is not empty, there is no // Other than the check that the return stack is not empty, there is no
// need to validate the pc from 'returns', since we only ever push valid // need to validate the pc from 'returns', since we only ever push valid
//values onto it via jumpsub. //values onto it via jumpsub.
*pc = callContext.rstack.pop() + 1 *pc = uint64(callContext.rstack.pop()) + 1
return nil, nil return nil, nil
} }
...@@ -608,7 +608,13 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] ...@@ -608,7 +608,13 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
stackvalue := size stackvalue := size
callContext.contract.UseGas(gas) callContext.contract.UseGas(gas)
res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, value.ToBig()) //TODO: use uint256.Int instead of converting with toBig()
var bigVal = big0
if !value.IsZero() {
bigVal = value.ToBig()
}
res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, bigVal)
// Push item on the stack based on the returned error. If the ruleset is // Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only // homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must // rule) and treat as an error, if the ruleset is frontier we must
...@@ -643,8 +649,13 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ ...@@ -643,8 +649,13 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
callContext.contract.UseGas(gas) callContext.contract.UseGas(gas)
// reuse size int for stackvalue // reuse size int for stackvalue
stackvalue := size stackvalue := size
//TODO: use uint256.Int instead of converting with toBig()
bigEndowment := big0
if !endowment.IsZero() {
bigEndowment = endowment.ToBig()
}
res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas, res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas,
endowment.ToBig(), salt.ToBig()) bigEndowment, &salt)
// Push item on the stack based on the returned error. // Push item on the stack based on the returned error.
if suberr != nil { if suberr != nil {
stackvalue.Clear() stackvalue.Clear()
...@@ -672,10 +683,17 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by ...@@ -672,10 +683,17 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
// Get the arguments from the memory. // Get the arguments from the memory.
args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
var bigVal = big0
//TODO: use uint256.Int instead of converting with toBig()
// By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls),
// but it would make more sense to extend the usage of uint256.Int
if !value.IsZero() { if !value.IsZero() {
gas += params.CallStipend gas += params.CallStipend
bigVal = value.ToBig()
} }
ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, value.ToBig())
ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, bigVal)
if err != nil { if err != nil {
temp.Clear() temp.Clear()
} else { } else {
...@@ -702,10 +720,14 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ( ...@@ -702,10 +720,14 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
// Get arguments from the memory. // Get arguments from the memory.
args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
//TODO: use uint256.Int instead of converting with toBig()
var bigVal = big0
if !value.IsZero() { if !value.IsZero() {
gas += params.CallStipend gas += params.CallStipend
bigVal = value.ToBig()
} }
ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, value.ToBig())
ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, bigVal)
if err != nil { if err != nil {
temp.Clear() temp.Clear()
} else { } else {
......
...@@ -182,13 +182,20 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( ...@@ -182,13 +182,20 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
logged bool // deferred Tracer should ignore already logged steps logged bool // deferred Tracer should ignore already logged steps
res []byte // result of the opcode execution function res []byte // result of the opcode execution function
) )
// Don't move this deferrred function, it's placed before the capturestate-deferred method,
// so that it get's executed _after_: the capturestate needs the stacks before
// they are returned to the pools
defer func() {
returnStack(stack)
returnRStack(returns)
}()
contract.Input = input contract.Input = input
if in.cfg.Debug { if in.cfg.Debug {
defer func() { defer func() {
if err != nil { if err != nil {
if !logged { if !logged {
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, contract, in.evm.depth, err) in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err)
} else { } else {
in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, contract, in.evm.depth, err) in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, contract, in.evm.depth, err)
} }
...@@ -272,7 +279,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( ...@@ -272,7 +279,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
} }
if in.cfg.Debug { if in.cfg.Debug {
in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, returns, contract, in.evm.depth, err) in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err)
logged = true logged = true
} }
...@@ -281,7 +288,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( ...@@ -281,7 +288,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// if the operation clears the return data (e.g. it has returning data) // if the operation clears the return data (e.g. it has returning data)
// set the last return to the result of the operation. // set the last return to the result of the operation.
if operation.returns { if operation.returns {
in.returnData = res in.returnData = common.CopyBytes(res)
} }
switch { switch {
......
...@@ -50,6 +50,7 @@ type LogConfig struct { ...@@ -50,6 +50,7 @@ type LogConfig struct {
DisableMemory bool // disable memory capture DisableMemory bool // disable memory capture
DisableStack bool // disable stack capture DisableStack bool // disable stack capture
DisableStorage bool // disable storage capture DisableStorage bool // disable storage capture
DisableReturnData bool // disable return data capture
Debug bool // print output during capture end Debug bool // print output during capture end
Limit int // maximum length of output, but zero means unlimited Limit int // maximum length of output, but zero means unlimited
} }
...@@ -66,7 +67,8 @@ type StructLog struct { ...@@ -66,7 +67,8 @@ type StructLog struct {
Memory []byte `json:"memory"` Memory []byte `json:"memory"`
MemorySize int `json:"memSize"` MemorySize int `json:"memSize"`
Stack []*big.Int `json:"stack"` Stack []*big.Int `json:"stack"`
ReturnStack []uint64 `json:"returnStack"` ReturnStack []uint32 `json:"returnStack"`
ReturnData []byte `json:"returnData"`
Storage map[common.Hash]common.Hash `json:"-"` Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"` Depth int `json:"depth"`
RefundCounter uint64 `json:"refund"` RefundCounter uint64 `json:"refund"`
...@@ -104,7 +106,7 @@ func (s *StructLog) ErrorString() string { ...@@ -104,7 +106,7 @@ func (s *StructLog) ErrorString() string {
// if you need to retain them beyond the current call. // if you need to retain them beyond the current call.
type Tracer interface { type Tracer interface {
CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error
CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
} }
...@@ -142,7 +144,7 @@ func (l *StructLogger) CaptureStart(from common.Address, to common.Address, crea ...@@ -142,7 +144,7 @@ func (l *StructLogger) CaptureStart(from common.Address, to common.Address, crea
// CaptureState logs a new structured log message and pushes it out to the environment // CaptureState logs a new structured log message and pushes it out to the environment
// //
// CaptureState also tracks SLOAD/SSTORE ops to track storage change. // CaptureState also tracks SLOAD/SSTORE ops to track storage change.
func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error { func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error {
// check if already accumulated the specified number of logs // check if already accumulated the specified number of logs
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
return errTraceLimitReached return errTraceLimitReached
...@@ -161,9 +163,9 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui ...@@ -161,9 +163,9 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
stck[i] = new(big.Int).Set(item.ToBig()) stck[i] = new(big.Int).Set(item.ToBig())
} }
} }
var rstack []uint64 var rstack []uint32
if !l.cfg.DisableStack && rStack != nil { if !l.cfg.DisableStack && rStack != nil {
rstck := make([]uint64, len(rStack.data)) rstck := make([]uint32, len(rStack.data))
copy(rstck, rStack.data) copy(rstck, rStack.data)
} }
// Copy a snapshot of the current storage to a new container // Copy a snapshot of the current storage to a new container
...@@ -192,8 +194,13 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui ...@@ -192,8 +194,13 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
} }
storage = l.storage[contract.Address()].Copy() storage = l.storage[contract.Address()].Copy()
} }
var rdata []byte
if !l.cfg.DisableReturnData {
rdata = make([]byte, len(rData))
copy(rdata, rData)
}
// create a new snapshot of the EVM. // create a new snapshot of the EVM.
log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rstack, storage, depth, env.StateDB.GetRefund(), err} log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rstack, rdata, storage, depth, env.StateDB.GetRefund(), err}
l.logs = append(l.logs, log) l.logs = append(l.logs, log)
return nil return nil
} }
...@@ -257,6 +264,10 @@ func WriteTrace(writer io.Writer, logs []StructLog) { ...@@ -257,6 +264,10 @@ func WriteTrace(writer io.Writer, logs []StructLog) {
fmt.Fprintf(writer, "%x: %x\n", h, item) fmt.Fprintf(writer, "%x: %x\n", h, item)
} }
} }
if len(log.ReturnData) > 0 {
fmt.Fprintln(writer, "ReturnData:")
fmt.Fprint(writer, hex.Dump(log.ReturnData))
}
fmt.Fprintln(writer) fmt.Fprintln(writer)
} }
} }
...@@ -308,7 +319,7 @@ func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create b ...@@ -308,7 +319,7 @@ func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create b
return nil return nil
} }
func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error { func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error {
fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost) fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost)
if !t.cfg.DisableStack { // format stack if !t.cfg.DisableStack { // format stack
......
...@@ -46,7 +46,7 @@ func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create ...@@ -46,7 +46,7 @@ func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create
} }
// CaptureState outputs state information on the logger. // CaptureState outputs state information on the logger.
func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error { func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error {
log := StructLog{ log := StructLog{
Pc: pc, Pc: pc,
Op: op, Op: op,
...@@ -70,6 +70,9 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint ...@@ -70,6 +70,9 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
log.Stack = logstack log.Stack = logstack
log.ReturnStack = rStack.data log.ReturnStack = rStack.data
} }
if !l.cfg.DisableReturnData {
log.ReturnData = rData
}
return l.encoder.Encode(log) return l.encoder.Encode(log)
} }
......
...@@ -61,7 +61,7 @@ func TestStoreCapture(t *testing.T) { ...@@ -61,7 +61,7 @@ func TestStoreCapture(t *testing.T) {
stack.push(uint256.NewInt().SetUint64(1)) stack.push(uint256.NewInt().SetUint64(1))
stack.push(uint256.NewInt()) stack.push(uint256.NewInt())
var index common.Hash var index common.Hash
logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, rstack, contract, 0, nil) logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, rstack, nil, contract, 0, nil)
if len(logger.storage[contract.Address()]) == 0 { if len(logger.storage[contract.Address()]) == 0 {
t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.storage[contract.Address()])) t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.storage[contract.Address()]))
} }
......
...@@ -57,8 +57,15 @@ func setDefaults(cfg *Config) { ...@@ -57,8 +57,15 @@ func setDefaults(cfg *Config) {
DAOForkBlock: new(big.Int), DAOForkBlock: new(big.Int),
DAOForkSupport: false, DAOForkSupport: false,
EIP150Block: new(big.Int), EIP150Block: new(big.Int),
EIP150Hash: common.Hash{},
EIP155Block: new(big.Int), EIP155Block: new(big.Int),
EIP158Block: new(big.Int), EIP158Block: new(big.Int),
ByzantiumBlock: new(big.Int),
ConstantinopleBlock: new(big.Int),
PetersburgBlock: new(big.Int),
IstanbulBlock: new(big.Int),
MuirGlacierBlock: new(big.Int),
YoloV1Block: nil,
} }
} }
......
...@@ -321,34 +321,6 @@ func TestBlockhash(t *testing.T) { ...@@ -321,34 +321,6 @@ func TestBlockhash(t *testing.T) {
} }
} }
// BenchmarkSimpleLoop test a pretty simple loop which loops
// 1M (1 048 575) times.
// Takes about 200 ms
func BenchmarkSimpleLoop(b *testing.B) {
// 0xfffff = 1048575 loops
code := []byte{
byte(vm.PUSH3), 0x0f, 0xff, 0xff,
byte(vm.JUMPDEST), // [ count ]
byte(vm.PUSH1), 1, // [count, 1]
byte(vm.SWAP1), // [1, count]
byte(vm.SUB), // [ count -1 ]
byte(vm.DUP1), // [ count -1 , count-1]
byte(vm.PUSH1), 4, // [count-1, count -1, label]
byte(vm.JUMPI), // [ 0 ]
byte(vm.STOP),
}
//tracer := vm.NewJSONLogger(nil, os.Stdout)
//Execute(code, nil, &Config{
// EVMConfig: vm.Config{
// Debug: true,
// Tracer: tracer,
// }})
for i := 0; i < b.N; i++ {
Execute(code, nil, nil)
}
}
type stepCounter struct { type stepCounter struct {
inner *vm.JSONLogger inner *vm.JSONLogger
steps int steps int
...@@ -358,7 +330,7 @@ func (s *stepCounter) CaptureStart(from common.Address, to common.Address, creat ...@@ -358,7 +330,7 @@ func (s *stepCounter) CaptureStart(from common.Address, to common.Address, creat
return nil return nil
} }
func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, contract *vm.Contract, depth int, err error) error { func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, rData []byte, contract *vm.Contract, depth int, err error) error {
s.steps++ s.steps++
// Enable this for more output // Enable this for more output
//s.inner.CaptureState(env, pc, op, gas, cost, memory, stack, rStack, contract, depth, err) //s.inner.CaptureState(env, pc, op, gas, cost, memory, stack, rStack, contract, depth, err)
...@@ -593,3 +565,160 @@ func DisabledTestEipExampleCases(t *testing.T) { ...@@ -593,3 +565,160 @@ func DisabledTestEipExampleCases(t *testing.T) {
"allowed, and causes an error", code) "allowed, and causes an error", code)
} }
} }
// benchmarkNonModifyingCode benchmarks code, but if the code modifies the
// state, this should not be used, since it does not reset the state between runs.
func benchmarkNonModifyingCode(gas uint64, code []byte, name string, b *testing.B) {
cfg := new(Config)
setDefaults(cfg)
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
cfg.GasLimit = gas
var (
destination = common.BytesToAddress([]byte("contract"))
vmenv = NewEnv(cfg)
sender = vm.AccountRef(cfg.Origin)
)
cfg.State.CreateAccount(destination)
eoa := common.HexToAddress("E0")
{
cfg.State.CreateAccount(eoa)
cfg.State.SetNonce(eoa, 100)
}
reverting := common.HexToAddress("EE")
{
cfg.State.CreateAccount(reverting)
cfg.State.SetCode(reverting, []byte{
byte(vm.PUSH1), 0x00,
byte(vm.PUSH1), 0x00,
byte(vm.REVERT),
})
}
//cfg.State.CreateAccount(cfg.Origin)
// set the receiver's (the executing contract) code for execution.
cfg.State.SetCode(destination, code)
vmenv.Call(sender, destination, nil, gas, cfg.Value)
b.Run(name, func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
vmenv.Call(sender, destination, nil, gas, cfg.Value)
}
})
}
// BenchmarkSimpleLoop test a pretty simple loop which loops until OOG
// 55 ms
func BenchmarkSimpleLoop(b *testing.B) {
staticCallIdentity := []byte{
byte(vm.JUMPDEST), // [ count ]
// push args for the call
byte(vm.PUSH1), 0, // out size
byte(vm.DUP1), // out offset
byte(vm.DUP1), // out insize
byte(vm.DUP1), // in offset
byte(vm.PUSH1), 0x4, // address of identity
byte(vm.GAS), // gas
byte(vm.STATICCALL),
byte(vm.POP), // pop return value
byte(vm.PUSH1), 0, // jumpdestination
byte(vm.JUMP),
}
callIdentity := []byte{
byte(vm.JUMPDEST), // [ count ]
// push args for the call
byte(vm.PUSH1), 0, // out size
byte(vm.DUP1), // out offset
byte(vm.DUP1), // out insize
byte(vm.DUP1), // in offset
byte(vm.DUP1), // value
byte(vm.PUSH1), 0x4, // address of identity
byte(vm.GAS), // gas
byte(vm.CALL),
byte(vm.POP), // pop return value
byte(vm.PUSH1), 0, // jumpdestination
byte(vm.JUMP),
}
callInexistant := []byte{
byte(vm.JUMPDEST), // [ count ]
// push args for the call
byte(vm.PUSH1), 0, // out size
byte(vm.DUP1), // out offset
byte(vm.DUP1), // out insize
byte(vm.DUP1), // in offset
byte(vm.DUP1), // value
byte(vm.PUSH1), 0xff, // address of existing contract
byte(vm.GAS), // gas
byte(vm.CALL),
byte(vm.POP), // pop return value
byte(vm.PUSH1), 0, // jumpdestination
byte(vm.JUMP),
}
callEOA := []byte{
byte(vm.JUMPDEST), // [ count ]
// push args for the call
byte(vm.PUSH1), 0, // out size
byte(vm.DUP1), // out offset
byte(vm.DUP1), // out insize
byte(vm.DUP1), // in offset
byte(vm.DUP1), // value
byte(vm.PUSH1), 0xE0, // address of EOA
byte(vm.GAS), // gas
byte(vm.CALL),
byte(vm.POP), // pop return value
byte(vm.PUSH1), 0, // jumpdestination
byte(vm.JUMP),
}
loopingCode := []byte{
byte(vm.JUMPDEST), // [ count ]
// push args for the call
byte(vm.PUSH1), 0, // out size
byte(vm.DUP1), // out offset
byte(vm.DUP1), // out insize
byte(vm.DUP1), // in offset
byte(vm.PUSH1), 0x4, // address of identity
byte(vm.GAS), // gas
byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP),
byte(vm.PUSH1), 0, // jumpdestination
byte(vm.JUMP),
}
calllRevertingContractWithInput := []byte{
byte(vm.JUMPDEST), //
// push args for the call
byte(vm.PUSH1), 0, // out size
byte(vm.DUP1), // out offset
byte(vm.PUSH1), 0x20, // in size
byte(vm.PUSH1), 0x00, // in offset
byte(vm.PUSH1), 0x00, // value
byte(vm.PUSH1), 0xEE, // address of reverting contract
byte(vm.GAS), // gas
byte(vm.CALL),
byte(vm.POP), // pop return value
byte(vm.PUSH1), 0, // jumpdestination
byte(vm.JUMP),
}
//tracer := vm.NewJSONLogger(nil, os.Stdout)
//Execute(loopingCode, nil, &Config{
// EVMConfig: vm.Config{
// Debug: true,
// Tracer: tracer,
// }})
// 100M gas
benchmarkNonModifyingCode(100000000, staticCallIdentity, "staticcall-identity-100M", b)
benchmarkNonModifyingCode(100000000, callIdentity, "call-identity-100M", b)
benchmarkNonModifyingCode(100000000, loopingCode, "loop-100M", b)
benchmarkNonModifyingCode(100000000, callInexistant, "call-nonexist-100M", b)
benchmarkNonModifyingCode(100000000, callEOA, "call-EOA-100M", b)
benchmarkNonModifyingCode(100000000, calllRevertingContractWithInput, "call-reverting-100M", b)
//benchmarkNonModifyingCode(10000000, staticCallIdentity, "staticcall-identity-10M", b)
//benchmarkNonModifyingCode(10000000, loopingCode, "loop-10M", b)
}
...@@ -18,10 +18,17 @@ package vm ...@@ -18,10 +18,17 @@ package vm
import ( import (
"fmt" "fmt"
"sync"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
var stackPool = sync.Pool{
New: func() interface{} {
return &Stack{data: make([]uint256.Int, 0, 16)}
},
}
// Stack is an object for basic stack operations. Items popped to the stack are // Stack is an object for basic stack operations. Items popped to the stack are
// expected to be changed and modified. stack does not take care of adding newly // expected to be changed and modified. stack does not take care of adding newly
// initialised objects. // initialised objects.
...@@ -30,7 +37,12 @@ type Stack struct { ...@@ -30,7 +37,12 @@ type Stack struct {
} }
func newstack() *Stack { func newstack() *Stack {
return &Stack{data: make([]uint256.Int, 0, 16)} return stackPool.Get().(*Stack)
}
func returnStack(s *Stack) {
s.data = s.data[:0]
stackPool.Put(s)
} }
// Data returns the underlying uint256.Int array. // Data returns the underlying uint256.Int array.
...@@ -87,20 +99,32 @@ func (st *Stack) Print() { ...@@ -87,20 +99,32 @@ func (st *Stack) Print() {
fmt.Println("#############") fmt.Println("#############")
} }
var rStackPool = sync.Pool{
New: func() interface{} {
return &ReturnStack{data: make([]uint32, 0, 10)}
},
}
// ReturnStack is an object for basic return stack operations. // ReturnStack is an object for basic return stack operations.
type ReturnStack struct { type ReturnStack struct {
data []uint64 data []uint32
} }
func newReturnStack() *ReturnStack { func newReturnStack() *ReturnStack {
return &ReturnStack{data: make([]uint64, 0, 1024)} return rStackPool.Get().(*ReturnStack)
}
func returnRStack(rs *ReturnStack) {
rs.data = rs.data[:0]
rStackPool.Put(rs)
} }
func (st *ReturnStack) push(d uint64) { func (st *ReturnStack) push(d uint32) {
st.data = append(st.data, d) st.data = append(st.data, d)
} }
func (st *ReturnStack) pop() (ret uint64) { // A uint32 is sufficient as for code below 4.2G
func (st *ReturnStack) pop() (ret uint32) {
ret = st.data[len(st.data)-1] ret = st.data[len(st.data)-1]
st.data = st.data[:len(st.data)-1] st.data = st.data[:len(st.data)-1]
return return
......
...@@ -541,7 +541,7 @@ func (jst *Tracer) CaptureStart(from common.Address, to common.Address, create b ...@@ -541,7 +541,7 @@ func (jst *Tracer) CaptureStart(from common.Address, to common.Address, create b
} }
// CaptureState implements the Tracer interface to trace a single step of VM execution. // CaptureState implements the Tracer interface to trace a single step of VM execution.
func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, contract *vm.Contract, depth int, err error) error { func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, rdata []byte, contract *vm.Contract, depth int, err error) error {
if jst.err == nil { if jst.err == nil {
// Initialize the context if it wasn't done yet // Initialize the context if it wasn't done yet
if !jst.inited { if !jst.inited {
......
...@@ -169,10 +169,10 @@ func TestHaltBetweenSteps(t *testing.T) { ...@@ -169,10 +169,10 @@ func TestHaltBetweenSteps(t *testing.T) {
env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0) contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0)
tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, contract, 0, nil) tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, nil, contract, 0, nil)
timeout := errors.New("stahp") timeout := errors.New("stahp")
tracer.Stop(timeout) tracer.Stop(timeout)
tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, contract, 0, nil) tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, nil, contract, 0, nil)
if _, err := tracer.GetResult(); err.Error() != timeout.Error() { if _, err := tracer.GetResult(); err.Error() != timeout.Error() {
t.Errorf("Expected timeout error, got %v", err) t.Errorf("Expected timeout error, got %v", err)
......
...@@ -179,9 +179,9 @@ func (t *SecureTrie) hashKey(key []byte) []byte { ...@@ -179,9 +179,9 @@ func (t *SecureTrie) hashKey(key []byte) []byte {
h := newHasher(false) h := newHasher(false)
h.sha.Reset() h.sha.Reset()
h.sha.Write(key) h.sha.Write(key)
buf := h.sha.Sum(t.hashKeyBuf[:0]) h.sha.Read(t.hashKeyBuf[:])
returnHasherToPool(h) returnHasherToPool(h)
return buf return t.hashKeyBuf[:]
} }
// getSecKeyCache returns the current secure key cache, creating a new one if // getSecKeyCache returns the current secure key cache, creating a new one if
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment