diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 175db51a87aa..0d8595bc1480 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -135,12 +135,29 @@ func ActivePrecompiles(rules params.Rules) []common.Address { } // RunPrecompiledContract runs and evaluates the output of a precompiled contract. -func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) { - gas := p.RequiredGas(input) - if contract.UseGas(gas) { - return p.Run(input) +// It returns +// - the returned bytes, +// - the _remaining_ gas, +// - any error that occurred +func RunPrecompiledContract(evm *EVM, p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) { + if evm != nil { + if evm.chainConfig.IsTIPXDCXReceiver(evm.BlockNumber) { + switch p := p.(type) { + case *XDCxEpochPrice: + p.SetTradingState(evm.tradingStateDB) + case *XDCxLastPrice: + p.SetTradingState(evm.tradingStateDB) + } + } + } + + 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. @@ -230,6 +247,7 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) { type bigModExp struct{} var ( + big0 = big.NewInt(0) big1 = big.NewInt(1) big4 = big.NewInt(4) big8 = big.NewInt(8) diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index 4d8a4c762fc1..ed915683c7cb 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -23,10 +23,6 @@ import ( "reflect" "testing" - "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate" - "github.com/XinFinOrg/XDPoSChain/core/rawdb" - "github.com/XinFinOrg/XDPoSChain/params" - "github.com/XinFinOrg/XDPoSChain/common" ) @@ -496,10 +492,9 @@ var blake2FTests = []precompiledTest{ func testPrecompiled(addr string, test precompiledTest, t *testing.T) { p := PrecompiledContractsIstanbul[common.HexToAddress(addr)] in := common.Hex2Bytes(test.input) - contract := NewContract(AccountRef(common.HexToAddress("1337")), - nil, new(big.Int), p.RequiredGas(in)) - t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) { - if res, err := RunPrecompiledContract(p, in, contract); err != nil { + gas := p.RequiredGas(in) + t.Run(fmt.Sprintf("%s-Gas=%d", test.name, gas), func(t *testing.T) { + if res, _, err := RunPrecompiledContract(nil, p, in, gas); err != nil { t.Error(err) } else if common.Bytes2Hex(res) != test.expected { t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res)) @@ -513,21 +508,12 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) { } func testXDCxPrecompiled(addr string, test precompiledTest, t *testing.T) { - db := rawdb.NewMemoryDatabase() - stateCache := tradingstate.NewDatabase(db) - tradingStateDB, _ := tradingstate.New(common.Hash{}, stateCache) - tradingStateDB.SetLastPrice(tradingstate.GetTradingOrderBookHash(common.HexToAddress(BTCAddress), common.HexToAddress(USDTAddress)), BTCUSDTLastPrice) - tradingStateDB.SetMediumPriceBeforeEpoch(tradingstate.GetTradingOrderBookHash(common.HexToAddress(BTCAddress), common.HexToAddress(USDTAddress)), BTCUSDTEpochPrice) - - evm := NewEVM(Context{BlockNumber: common.Big1}, nil, tradingStateDB, ¶ms.ChainConfig{ByzantiumBlock: common.Big0}, Config{}) contractAddr := common.HexToAddress(addr) p := PrecompiledContractsByzantium[contractAddr] in := common.Hex2Bytes(test.input) - contract := NewContract(AccountRef(common.HexToAddress("1337")), - nil, new(big.Int), p.RequiredGas(in)) - contract.SetCallCode(&contractAddr, common.Hash{}, []byte{}) - t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) { - if res, err := run(evm, contract, in, false); err != nil { + gas := p.RequiredGas(in) + t.Run(fmt.Sprintf("%s-Gas=%d", test.name, gas), func(t *testing.T) { + if res, _, err := RunPrecompiledContract(nil, p, in, gas); err != nil { t.Error(err) } else if common.Bytes2Hex(res) != test.expected { t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res)) @@ -536,16 +522,12 @@ func testXDCxPrecompiled(addr string, test precompiledTest, t *testing.T) { } func testPrecompiledWithEmptyTradingState(addr string, test precompiledTest, t *testing.T) { - evm := NewEVM(Context{BlockNumber: common.Big1}, nil, nil, ¶ms.ChainConfig{ByzantiumBlock: common.Big0}, Config{}) - contractAddr := common.HexToAddress(addr) p := PrecompiledContractsByzantium[contractAddr] in := common.Hex2Bytes(test.input) - contract := NewContract(AccountRef(common.HexToAddress("1337")), - nil, new(big.Int), p.RequiredGas(in)) - contract.SetCallCode(&contractAddr, common.Hash{}, []byte{}) - t.Run(fmt.Sprintf("testPrecompiledWithEmptyTradingState-%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) { - if res, err := run(evm, contract, in, false); err != nil { + gas := p.RequiredGas(in) + t.Run(fmt.Sprintf("testPrecompiledWithEmptyTradingState-%s-Gas=%d", test.name, gas), func(t *testing.T) { + if res, _, err := RunPrecompiledContract(nil, p, in, gas); err != nil { t.Error(err) } else if common.Bytes2Hex(res) != test.expected { t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res)) @@ -556,10 +538,10 @@ func testPrecompiledWithEmptyTradingState(addr string, test precompiledTest, t * func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { p := PrecompiledContractsIstanbul[common.HexToAddress(addr)] in := common.Hex2Bytes(test.input) - contract := NewContract(AccountRef(common.HexToAddress("1337")), - nil, new(big.Int), p.RequiredGas(in)-1) - t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) { - _, err := RunPrecompiledContract(p, in, contract) + gas := p.RequiredGas(in) - 1 + + t.Run(fmt.Sprintf("%s-Gas=%d", test.name, gas), func(t *testing.T) { + _, _, err := RunPrecompiledContract(nil, p, in, gas) if err.Error() != "out of gas" { t.Errorf("Expected error [out of gas], got [%v]", err) } @@ -574,11 +556,9 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) { p := PrecompiledContractsIstanbul[common.HexToAddress(addr)] in := common.Hex2Bytes(test.input) - contract := NewContract(AccountRef(common.HexToAddress("31337")), - nil, new(big.Int), p.RequiredGas(in)) - + gas := p.RequiredGas(in) t.Run(test.name, func(t *testing.T) { - _, err := RunPrecompiledContract(p, in, contract) + _, _, err := RunPrecompiledContract(nil, p, in, gas) if !reflect.DeepEqual(err, test.expectedError) { t.Errorf("Expected error [%v], got [%v]", test.expectedError, err) } @@ -597,8 +577,6 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { p := PrecompiledContractsIstanbul[common.HexToAddress(addr)] in := common.Hex2Bytes(test.input) reqGas := p.RequiredGas(in) - contract := NewContract(AccountRef(common.HexToAddress("1337")), - nil, new(big.Int), reqGas) var ( res []byte @@ -606,12 +584,11 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { 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.ResetTimer() for i := 0; i < bench.N; i++ { - contract.Gas = reqGas copy(data, in) - res, err = RunPrecompiledContract(p, data, contract) + res, _, err = RunPrecompiledContract(nil, p, data, reqGas) } bench.StopTimer() //Check if it is correct diff --git a/core/vm/evm.go b/core/vm/evm.go index 4f61fc3c80a0..10da4a3297f5 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -26,6 +26,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/params" + "github.com/holiman/uint256" ) // emptyCodeHash is used by create to ensure deployment is disallowed to already @@ -76,19 +77,6 @@ func (evm *EVM) precompile2(addr common.Address) (PrecompiledContract, bool) { // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter. func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) { - if contract.CodeAddr != nil { - if p, isPrecompile := evm.precompile(*contract.CodeAddr); isPrecompile { - if evm.chainConfig.IsTIPXDCXReceiver(evm.BlockNumber) { - switch p := p.(type) { - case *XDCxEpochPrice: - p.SetTradingState(evm.tradingStateDB) - case *XDCxLastPrice: - p.SetTradingState(evm.tradingStateDB) - } - } - return RunPrecompiledContract(p, input, contract) - } - } if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber) { for _, interpreter := range evm.interpreters { if interpreter.CanRun(contract.Code) { @@ -221,15 +209,13 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas return nil, gas, ErrDepth } // Fail if we're trying to transfer more than the available balance - if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { + if value.Sign() != 0 && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { return nil, gas, ErrInsufficientBalance } - var ( - to = AccountRef(addr) - snapshot = evm.StateDB.Snapshot() - ) + snapshot := evm.StateDB.Snapshot() + p, isPrecompile := evm.precompile2(addr) + if !evm.StateDB.Exist(addr) { - _, isPrecompile := evm.precompile2(addr) if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 { // Calling a non existing account, don't do anything, but ping the tracer if evm.vmConfig.Debug { @@ -245,7 +231,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } evm.StateDB.CreateAccount(addr) } - evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value) + evm.Transfer(evm.StateDB, caller.Address(), addr, value) // Capture the tracer start/end events in debug mode if evm.vmConfig.Debug { @@ -264,16 +250,24 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } } - // Initialise a new contract and set the code that is to be used by the EVM. - // The contract is a scoped environment for this execution context only. - contract := NewContract(caller, to, value, gas) - contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - - // Even if the account has no code, we need to continue because it might be a precompile - - ret, err = run(evm, contract, input, false) - gas = contract.Gas - + if isPrecompile { + ret, gas, err = RunPrecompiledContract(evm, p, input, gas) + } else { + // Initialise a new contract and set the code that is to be used by the EVM. + // The contract is a scoped environment for this execution context only. + code := evm.StateDB.GetCode(addr) + if len(code) == 0 { + ret, err = nil, nil // gas is unchanged + } else { + addrCopy := addr + // If the account has no code, we can abort here + // The depth-check is already done, and precompiles handled above + contract := NewContract(caller, AccountRef(addrCopy), value, gas) + contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code) + ret, err = run(evm, contract, input, false) + gas = contract.Gas + } + } // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in homestead this also counts for code storage gas errors. @@ -282,6 +276,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas if err != ErrExecutionReverted { gas = 0 } + // TODO: consider clearing up unused snapshots: + //} else { + // evm.StateDB.DiscardSnapshot(snapshot) } return ret, gas, err } @@ -305,10 +302,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { return nil, gas, ErrInsufficientBalance } - var ( - snapshot = evm.StateDB.Snapshot() - to = AccountRef(caller.Address()) - ) + var snapshot = evm.StateDB.Snapshot() // Invoke tracer hooks that signal entering/exiting a call frame if evm.vmConfig.Debug { @@ -318,13 +312,18 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, }(gas) } - // Initialise a new contract and set the code that is to be used by the EVM. - // The contract is a scoped environment for this execution context only. - contract := NewContract(caller, to, value, gas) - contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - - ret, err = run(evm, contract, input, false) - gas = contract.Gas + // It is allowed to call precompiles, even via delegatecall + if p, isPrecompile := evm.precompile(addr); isPrecompile { + ret, gas, err = RunPrecompiledContract(evm, p, input, gas) + } else { + addrCopy := addr + // Initialise a new contract and set the code that is to be used by the EVM. + // The contract is a scoped environment for this execution context only. + contract := NewContract(caller, AccountRef(caller.Address()), value, gas) + contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + ret, err = run(evm, contract, input, false) + gas = contract.Gas + } if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { @@ -344,10 +343,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth } - var ( - snapshot = evm.StateDB.Snapshot() - to = AccountRef(caller.Address()) - ) + var snapshot = evm.StateDB.Snapshot() // Invoke tracer hooks that signal entering/exiting a call frame if evm.vmConfig.Debug { @@ -357,12 +353,17 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by }(gas) } - // Initialise a new contract and make initialise the delegate values - contract := NewContract(caller, to, nil, gas).AsDelegate() - contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - - ret, err = run(evm, contract, input, false) - gas = contract.Gas + // It is allowed to call precompiles, even via delegatecall + if p, isPrecompile := evm.precompile(addr); isPrecompile { + ret, gas, err = RunPrecompiledContract(evm, p, input, gas) + } else { + addrCopy := addr + // Initialise a new contract and make initialise the delegate values + contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate() + contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + ret, err = run(evm, contract, input, false) + gas = contract.Gas + } if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { @@ -381,22 +382,18 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth } - var ( - to = AccountRef(addr) - snapshot = evm.StateDB.Snapshot() - ) - // Initialise a new contract and set the code that is to be used by the EVM. - // The contract is a scoped environment for this execution context only. - contract := NewContract(caller, to, new(big.Int), gas) - contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + // We take a snapshot here. This is a bit counter-intuitive, and could probably be skipped. + // However, even a staticcall is considered a 'touch'. On mainnet, static calls were introduced + // after all empty accounts were deleted, so this is not required. However, if we omit this, + // then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json. + // We could change this, but for now it's left for legacy reasons + var snapshot = evm.StateDB.Snapshot() - if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber) { - // We do an AddBalance of zero here, just in order to trigger a touch. - // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, - // but is the correct thing to do and matters on other networks, in tests, and potential - // future scenarios - evm.StateDB.AddBalance(addr, big.NewInt(0)) - } + // We do an AddBalance of zero here, just in order to trigger a touch. + // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, + // but is the correct thing to do and matters on other networks, in tests, and potential + // future scenarios + evm.StateDB.AddBalance(addr, big0) // Invoke tracer hooks that signal entering/exiting a call frame if evm.vmConfig.Debug { @@ -406,11 +403,24 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte }(gas) } - // When an error was returned by the EVM or when setting the creation code - // above we revert to the snapshot and consume any gas remaining. Additionally - // when we're in Homestead this also counts for code storage gas errors. - ret, err = run(evm, contract, input, evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber)) - gas = contract.Gas + if p, isPrecompile := evm.precompile(addr); isPrecompile { + ret, gas, err = RunPrecompiledContract(evm, p, input, gas) + } else { + // At this point, we use a copy of address. If we don't, the go compiler will + // leak the 'contract' to the outer scope, and make allocation for 'contract' + // even if the actual execution ends on RunPrecompiled above. + addrCopy := addr + // Initialise a new contract and set the code that is to be used by the EVM. + // The contract is a scoped environment for this execution context only. + contract := NewContract(caller, AccountRef(addrCopy), new(big.Int), gas) + contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) + // When an error was returned by the EVM or when setting the creation code + // above we revert to the snapshot and consume any gas remaining. Additionally + // when we're in Homestead this also counts for code storage gas errors. + readOnly := evm.ChainConfig().IsTIPXDCXCancellationFee(evm.BlockNumber) + ret, err = run(evm, contract, input, readOnly) + gas = contract.Gas + } if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { @@ -534,9 +544,9 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I // // The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:] // instead of the usual sender-and-nonce-hash as the address where the contract is initialized at. -func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { +func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { codeAndHash := &codeAndHash{code: code} - contractAddr = crypto.CreateAddress2(caller.Address(), common.BigToHash(salt), codeAndHash.Hash().Bytes()) + contractAddr = crypto.CreateAddress2(caller.Address(), common.Hash(salt.Bytes32()), codeAndHash.Hash().Bytes()) return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2) } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index b2bf296c1faf..47e87e723d98 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -606,7 +606,13 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b stackvalue := size scope.Contract.UseGas(gas) - res, addr, returnGas, suberr := interpreter.evm.Create(scope.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(scope.Contract, input, gas, bigVal) // Push item on the stack based on the returned error. If the ruleset is // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must @@ -646,8 +652,13 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] scope.Contract.UseGas(gas) // reuse size int for stackvalue 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(scope.Contract, input, gas, - endowment.ToBig(), salt.ToBig()) + bigEndowment, &salt) // Push item on the stack based on the returned error. if suberr != nil { stackvalue.Clear() @@ -680,10 +691,18 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt if interpreter.readOnly && !value.IsZero() { return nil, ErrWriteProtection } + + 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() { gas += params.CallStipend + bigVal = value.ToBig() } - ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, value.ToBig()) + + ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, bigVal) + if err != nil { temp.Clear() } else { @@ -712,10 +731,14 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ // Get arguments from the memory. args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) + //TODO: use uint256.Int instead of converting with toBig() + var bigVal = big0 if !value.IsZero() { gas += params.CallStipend + bigVal = value.ToBig() } - ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, value.ToBig()) + + ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, bigVal) if err != nil { temp.Clear() } else { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 6e71411ac143..7d8351f70278 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -186,6 +186,12 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( logged bool // deferred EVMLogger should ignore already logged steps 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) + }() contract.Input = input if in.cfg.Debug { diff --git a/core/vm/stack.go b/core/vm/stack.go index a389c04b725d..e1a957e2445a 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -17,9 +17,17 @@ package vm import ( + "sync" + "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 // expected to be changed and modified. stack does not take care of adding newly // initialised objects. @@ -28,7 +36,12 @@ type Stack struct { } 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. diff --git a/trie/secure_trie.go b/trie/secure_trie.go index 532d541436ab..eb79f8dedbce 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -179,9 +179,9 @@ func (t *SecureTrie) hashKey(key []byte) []byte { h := newHasher(false) h.sha.Reset() h.sha.Write(key) - buf := h.sha.Sum(t.hashKeyBuf[:0]) + h.sha.Read(t.hashKeyBuf[:]) returnHasherToPool(h) - return buf + return t.hashKeyBuf[:] } // getSecKeyCache returns the current secure key Cache, creating a new one if