From 192edc0c63ccc62203abe9c40a38c2fe04ff6b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 8 Jun 2020 14:24:40 +0200 Subject: [PATCH 01/31] core/vm: use uint256 in EVM implementation (#20787) * core/vm: use fixed uint256 library instead of big * core/vm: remove intpools * core/vm: upgrade uint256, fixes uint256.NewFromBig * core/vm: use uint256.Int by value in Stack * core/vm: upgrade uint256 to v1.0.0 * core/vm: don't preallocate space for 1024 stack items (only 16) Co-authored-by: Martin Holst Swende --- cmd/evm/json_logger.go | 7 +- core/vm/common.go | 27 +- core/vm/contract.go | 7 +- core/vm/eips.go | 5 +- core/vm/evm.go | 2 +- core/vm/gas.go | 4 +- core/vm/gas_table.go | 26 +- core/vm/instructions.go | 579 ++++++++++++----------------- core/vm/instructions_test.go | 149 ++++---- core/vm/int_pool_verifier.go | 31 -- core/vm/int_pool_verifier_empty.go | 23 -- core/vm/interpreter.go | 17 - core/vm/intpool.go | 117 ------ core/vm/intpool_test.go | 55 --- core/vm/logger.go | 6 +- core/vm/logger_json.go | 7 +- core/vm/logger_test.go | 8 +- core/vm/memory.go | 7 +- core/vm/stack.go | 34 +- eth/tracers/tracer.go | 2 +- go.mod | 2 +- go.sum | 2 + 22 files changed, 386 insertions(+), 731 deletions(-) delete mode 100644 core/vm/int_pool_verifier.go delete mode 100644 core/vm/int_pool_verifier_empty.go delete mode 100644 core/vm/intpool.go delete mode 100644 core/vm/intpool_test.go diff --git a/cmd/evm/json_logger.go b/cmd/evm/json_logger.go index 458d56494b30..90e44f9c4ae5 100644 --- a/cmd/evm/json_logger.go +++ b/cmd/evm/json_logger.go @@ -56,7 +56,12 @@ func (l *JSONLogger) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cos log.Memory = memory.Data() } if !l.cfg.DisableStack { - log.Stack = stack.Data() + //TODO(@holiman) improve this + logstack := make([]*big.Int, len(stack.Data())) + for i, item := range stack.Data() { + logstack[i] = item.ToBig() + } + log.Stack = logstack } return l.encoder.Encode(log) } diff --git a/core/vm/common.go b/core/vm/common.go index 194e3897f8c3..bb11f4a393e5 100644 --- a/core/vm/common.go +++ b/core/vm/common.go @@ -17,15 +17,14 @@ package vm import ( - "math/big" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/math" + "github.com/holiman/uint256" ) // calcMemSize64 calculates the required memory size, and returns // the size and whether the result overflowed uint64 -func calcMemSize64(off, l *big.Int) (uint64, bool) { +func calcMemSize64(off, l *uint256.Int) (uint64, bool) { if !l.IsUint64() { return 0, true } @@ -35,16 +34,16 @@ func calcMemSize64(off, l *big.Int) (uint64, bool) { // calcMemSize64WithUint calculates the required memory size, and returns // the size and whether the result overflowed uint64 // Identical to calcMemSize64, but length is a uint64 -func calcMemSize64WithUint(off *big.Int, length64 uint64) (uint64, bool) { +func calcMemSize64WithUint(off *uint256.Int, length64 uint64) (uint64, bool) { // if length is zero, memsize is always zero, regardless of offset if length64 == 0 { return 0, false } // Check that offset doesn't overflow - if !off.IsUint64() { + offset64, overflow := off.Uint64WithOverflow() + if overflow { return 0, true } - offset64 := off.Uint64() val := offset64 + length64 // if value < either of it's parts, then it overflowed return val, val < offset64 @@ -64,22 +63,6 @@ func getData(data []byte, start uint64, size uint64) []byte { return common.RightPadBytes(data[start:end], int(size)) } -// getDataBig returns a slice from the data based on the start and size and pads -// up to size with zero's. This function is overflow safe. -func getDataBig(data []byte, start *big.Int, size *big.Int) []byte { - dlen := big.NewInt(int64(len(data))) - - s := math.BigMin(start, dlen) - e := math.BigMin(new(big.Int).Add(s, size), dlen) - return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64())) -} - -// bigUint64 returns the integer casted to a uint64 and returns whether it -// overflowed in the process. -func bigUint64(v *big.Int) (uint64, bool) { - return v.Uint64(), !v.IsUint64() -} - // toWordSize returns the ceiled word size required for memory expansion. func toWordSize(size uint64) uint64 { if size > math.MaxUint64-31 { diff --git a/core/vm/contract.go b/core/vm/contract.go index cc9d59cf2deb..5378267dad03 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -20,6 +20,7 @@ import ( "math/big" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/holiman/uint256" ) // ContractRef is a reference to the contract's backing object @@ -81,11 +82,11 @@ func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uin return c } -func (c *Contract) validJumpdest(dest *big.Int) bool { - udest := dest.Uint64() +func (c *Contract) validJumpdest(dest *uint256.Int) bool { + udest, overflow := dest.Uint64WithOverflow() // PC cannot go beyond len(code) and certainly can't be bigger than 63bits. // Don't bother checking for JUMPDEST in that case. - if dest.BitLen() >= 63 || udest >= uint64(len(c.Code)) { + if overflow || udest >= uint64(len(c.Code)) { return false } // Only JUMPDESTs allowed for destinations diff --git a/core/vm/eips.go b/core/vm/eips.go index 40da1f88fafa..65829f1d568a 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -20,6 +20,7 @@ import ( "fmt" "github.com/XinFinOrg/XDPoSChain/params" + "github.com/holiman/uint256" ) // EnableEIP enables the given EIP on the config. @@ -61,7 +62,7 @@ func enable1884(jt *JumpTable) { } func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - balance := interpreter.intPool.get().Set(interpreter.evm.StateDB.GetBalance(callContext.contract.Address())) + balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(callContext.contract.Address())) callContext.stack.push(balance) return nil, nil } @@ -81,7 +82,7 @@ func enable1344(jt *JumpTable) { // opChainID implements CHAINID opcode func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - chainId := interpreter.intPool.get().Set(interpreter.evm.chainConfig.ChainId) + chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainId) callContext.stack.push(chainId) return nil, nil } diff --git a/core/vm/evm.go b/core/vm/evm.go index f6f6a29a87d0..e8e5c1ca9419 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -353,7 +353,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // 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, bigZero) + evm.StateDB.AddBalance(addr, big.NewInt(0)) } // When an error was returned by the EVM or when setting the creation code diff --git a/core/vm/gas.go b/core/vm/gas.go index bda326cdc7ab..5cf1d852d24a 100644 --- a/core/vm/gas.go +++ b/core/vm/gas.go @@ -17,7 +17,7 @@ package vm import ( - "math/big" + "github.com/holiman/uint256" ) // Gas costs @@ -34,7 +34,7 @@ const ( // // The cost of gas was changed during the homestead price change HF. // As part of EIP 150 (TangerineWhistle), the returned gas is gas - base * 63 / 64. -func callGas(isEip150 bool, availableGas, base uint64, callCost *big.Int) (uint64, error) { +func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (uint64, error) { if isEip150 { availableGas = availableGas - base gas := availableGas - availableGas/64 diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 987dca32384d..719613f91227 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -71,7 +71,7 @@ func memoryCopierGas(stackpos int) gasFunc { return 0, err } // And gas for copying data, charged per word at param.CopyGas - words, overflow := bigUint64(stack.Back(stackpos)) + words, overflow := stack.Back(stackpos).Uint64WithOverflow() if overflow { return 0, ErrGasUintOverflow } @@ -97,7 +97,7 @@ var ( func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var ( y, x = stack.Back(1), stack.Back(0) - current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) + current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32())) ) // The legacy gas metering only takes into consideration the current state // Legacy rules should be applied if we are in Petersburg (removal of EIP-1283) @@ -132,11 +132,11 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi // 2.2.2. If original value equals new value (this storage slot is reset) // 2.2.2.1. If original value is 0, add 19800 gas to refund counter. // 2.2.2.2. Otherwise, add 4800 gas to refund counter. - value := common.BigToHash(y) + value := common.Hash(y.Bytes32()) if current == value { // noop (1) return params.NetSstoreNoopGas, nil } - original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x)) + original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32())) if original == current { if original == (common.Hash{}) { // create slot (2.1.1) return params.NetSstoreInitGas, nil @@ -184,14 +184,14 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m // Gas sentry honoured, do the actual gas calculation based on the stored value var ( y, x = stack.Back(1), stack.Back(0) - current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) + current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32())) ) - value := common.BigToHash(y) + value := common.Hash(y.Bytes32()) if current == value { // noop (1) return params.SstoreNoopGasEIP2200, nil } - original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x)) + original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32())) if original == current { if original == (common.Hash{}) { // create slot (2.1.1) return params.SstoreInitGasEIP2200, nil @@ -220,7 +220,7 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m func makeGasLog(n uint64) gasFunc { return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - requestedSize, overflow := bigUint64(stack.Back(1)) + requestedSize, overflow := stack.Back(1).Uint64WithOverflow() if overflow { return 0, ErrGasUintOverflow } @@ -253,7 +253,7 @@ func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize if err != nil { return 0, err } - wordGas, overflow := bigUint64(stack.Back(1)) + wordGas, overflow := stack.Back(1).Uint64WithOverflow() if overflow { return 0, ErrGasUintOverflow } @@ -287,7 +287,7 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS if err != nil { return 0, err } - wordGas, overflow := bigUint64(stack.Back(2)) + wordGas, overflow := stack.Back(2).Uint64WithOverflow() if overflow { return 0, ErrGasUintOverflow } @@ -329,8 +329,8 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var ( gas uint64 - transfersValue = stack.Back(2).Sign() != 0 - address = common.BigToAddress(stack.Back(1)) + transfersValue = !stack.Back(2).IsZero() + address = common.Address(stack.Back(1).Bytes20()) ) if evm.chainRules.IsEIP158 { if transfersValue && evm.StateDB.Empty(address) { @@ -423,7 +423,7 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me // EIP150 homestead gas reprice fork: if evm.chainRules.IsEIP150 { gas = params.SelfdestructGasEIP150 - var address = common.BigToAddress(stack.Back(0)) + var address = common.Address(stack.Back(0).Bytes20()) if evm.chainRules.IsEIP158 { // if empty and transfers value diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 4f1d12291132..92b3b9ae88c0 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -17,303 +17,170 @@ package vm import ( - "math/big" - - "github.com/XinFinOrg/XDPoSChain/params" - "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/params" + "github.com/holiman/uint256" "golang.org/x/crypto/sha3" ) -var ( - bigZero = new(big.Int) - tt255 = math.BigPow(2, 255) -) - func opAdd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - math.U256(y.Add(x, y)) - - interpreter.intPool.putOne(x) + y.Add(&x, y) return nil, nil } func opSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - math.U256(y.Sub(x, y)) - - interpreter.intPool.putOne(x) + y.Sub(&x, y) return nil, nil } func opMul(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.pop() - callContext.stack.push(math.U256(x.Mul(x, y))) - - interpreter.intPool.putOne(y) - + x, y := callContext.stack.pop(), callContext.stack.peek() + y.Mul(&x, y) return nil, nil } func opDiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - if y.Sign() != 0 { - math.U256(y.Div(x, y)) - } else { - y.SetUint64(0) - } - interpreter.intPool.putOne(x) + y.Div(&x, y) return nil, nil } func opSdiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := math.S256(callContext.stack.pop()), math.S256(callContext.stack.pop()) - res := interpreter.intPool.getZero() - - if y.Sign() == 0 || x.Sign() == 0 { - callContext.stack.push(res) - } else { - if x.Sign() != y.Sign() { - res.Div(x.Abs(x), y.Abs(y)) - res.Neg(res) - } else { - res.Div(x.Abs(x), y.Abs(y)) - } - callContext.stack.push(math.U256(res)) - } - interpreter.intPool.put(x, y) + x, y := callContext.stack.pop(), callContext.stack.peek() + y.SDiv(&x, y) return nil, nil } func opMod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.pop() - if y.Sign() == 0 { - callContext.stack.push(x.SetUint64(0)) - } else { - callContext.stack.push(math.U256(x.Mod(x, y))) - } - interpreter.intPool.putOne(y) + x, y := callContext.stack.pop(), callContext.stack.peek() + y.Mod(&x, y) return nil, nil } func opSmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := math.S256(callContext.stack.pop()), math.S256(callContext.stack.pop()) - res := interpreter.intPool.getZero() - - if y.Sign() == 0 { - callContext.stack.push(res) - } else { - if x.Sign() < 0 { - res.Mod(x.Abs(x), y.Abs(y)) - res.Neg(res) - } else { - res.Mod(x.Abs(x), y.Abs(y)) - } - callContext.stack.push(math.U256(res)) - } - interpreter.intPool.put(x, y) + x, y := callContext.stack.pop(), callContext.stack.peek() + y.SMod(&x, y) return nil, nil } func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - base, exponent := callContext.stack.pop(), callContext.stack.pop() - // some shortcuts - cmpToOne := exponent.Cmp(big1) - if cmpToOne < 0 { // Exponent is zero - // x ^ 0 == 1 - callContext.stack.push(base.SetUint64(1)) - } else if base.Sign() == 0 { - // 0 ^ y, if y != 0, == 0 - callContext.stack.push(base.SetUint64(0)) - } else if cmpToOne == 0 { // Exponent is one - // x ^ 1 == x - callContext.stack.push(base) - } else { - callContext.stack.push(math.Exp(base, exponent)) - interpreter.intPool.putOne(base) - } - interpreter.intPool.putOne(exponent) + base, exponent := callContext.stack.pop(), callContext.stack.peek() + exponent.Exp(&base, exponent) return nil, nil } func opSignExtend(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - back := callContext.stack.pop() - if back.Cmp(big.NewInt(31)) < 0 { - bit := uint(back.Uint64()*8 + 7) - num := callContext.stack.pop() - mask := back.Lsh(common.Big1, bit) - mask.Sub(mask, common.Big1) - if num.Bit(int(bit)) > 0 { - num.Or(num, mask.Not(mask)) - } else { - num.And(num, mask) - } - - callContext.stack.push(math.U256(num)) - } - - interpreter.intPool.putOne(back) + back, num := callContext.stack.pop(), callContext.stack.peek() + num.ExtendSign(num, &back) return nil, nil } func opNot(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x := callContext.stack.peek() - math.U256(x.Not(x)) + x.Not(x) return nil, nil } func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - if x.Cmp(y) < 0 { - y.SetUint64(1) + if x.Lt(y) { + y.SetOne() } else { - y.SetUint64(0) + y.Clear() } - interpreter.intPool.putOne(x) return nil, nil } func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - if x.Cmp(y) > 0 { - y.SetUint64(1) + if x.Gt(y) { + y.SetOne() } else { - y.SetUint64(0) + y.Clear() } - interpreter.intPool.putOne(x) return nil, nil } func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - - xSign := x.Cmp(tt255) - ySign := y.Cmp(tt255) - - switch { - case xSign >= 0 && ySign < 0: - y.SetUint64(1) - - case xSign < 0 && ySign >= 0: - y.SetUint64(0) - - default: - if x.Cmp(y) < 0 { - y.SetUint64(1) - } else { - y.SetUint64(0) - } + if x.Slt(y) { + y.SetOne() + } else { + y.Clear() } - interpreter.intPool.putOne(x) return nil, nil } func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - - xSign := x.Cmp(tt255) - ySign := y.Cmp(tt255) - - switch { - case xSign >= 0 && ySign < 0: - y.SetUint64(0) - - case xSign < 0 && ySign >= 0: - y.SetUint64(1) - - default: - if x.Cmp(y) > 0 { - y.SetUint64(1) - } else { - y.SetUint64(0) - } + if x.Sgt(y) { + y.SetOne() + } else { + y.Clear() } - interpreter.intPool.putOne(x) return nil, nil } func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - if x.Cmp(y) == 0 { - y.SetUint64(1) + if x.Eq(y) { + y.SetOne() } else { - y.SetUint64(0) + y.Clear() } - interpreter.intPool.putOne(x) return nil, nil } func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x := callContext.stack.peek() - if x.Sign() > 0 { - x.SetUint64(0) + if x.IsZero() { + x.SetOne() } else { - x.SetUint64(1) + x.Clear() } return nil, nil } func opAnd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y := callContext.stack.pop(), callContext.stack.pop() - callContext.stack.push(x.And(x, y)) - - interpreter.intPool.putOne(y) + x, y := callContext.stack.pop(), callContext.stack.peek() + y.And(&x, y) return nil, nil } func opOr(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - y.Or(x, y) - - interpreter.intPool.putOne(x) + y.Or(&x, y) return nil, nil } func opXor(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { x, y := callContext.stack.pop(), callContext.stack.peek() - y.Xor(x, y) - - interpreter.intPool.putOne(x) + y.Xor(&x, y) return nil, nil } func opByte(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { th, val := callContext.stack.pop(), callContext.stack.peek() - if th.Cmp(common.Big32) < 0 { - b := math.Byte(val, 32, int(th.Int64())) - val.SetUint64(uint64(b)) - } else { - val.SetUint64(0) - } - interpreter.intPool.putOne(th) + val.Byte(&th) return nil, nil } func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop() - if z.Cmp(bigZero) > 0 { - x.Add(x, y) - x.Mod(x, z) - callContext.stack.push(math.U256(x)) + x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek() + if z.IsZero() { + z.Clear() } else { - callContext.stack.push(x.SetUint64(0)) + z.AddMod(&x, &y, z) } - interpreter.intPool.put(y, z) return nil, nil } func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop() - if z.Cmp(bigZero) > 0 { - x.Mul(x, y) - x.Mod(x, z) - callContext.stack.push(math.U256(x)) - } else { - callContext.stack.push(x.SetUint64(0)) - } - interpreter.intPool.put(y, z) + x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek() + z.MulMod(&x, &y, z) return nil, nil } @@ -322,16 +189,12 @@ func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] // and pushes on the stack arg2 shifted to the left by arg1 number of bits. func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards - shift, value := math.U256(callContext.stack.pop()), math.U256(callContext.stack.peek()) - defer interpreter.intPool.putOne(shift) // First operand back into the pool - - if shift.Cmp(common.Big256) >= 0 { - value.SetUint64(0) - return nil, nil + shift, value := callContext.stack.pop(), callContext.stack.peek() + if shift.LtUint64(256) { + value.Lsh(value, uint(shift.Uint64())) + } else { + value.Clear() } - n := uint(shift.Uint64()) - math.U256(value.Lsh(value, n)) - return nil, nil } @@ -340,16 +203,12 @@ func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt // and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill. func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards - shift, value := math.U256(callContext.stack.pop()), math.U256(callContext.stack.peek()) - defer interpreter.intPool.putOne(shift) // First operand back into the pool - - if shift.Cmp(common.Big256) >= 0 { - value.SetUint64(0) - return nil, nil + shift, value := callContext.stack.pop(), callContext.stack.peek() + if shift.LtUint64(256) { + value.Rsh(value, uint(shift.Uint64())) + } else { + value.Clear() } - n := uint(shift.Uint64()) - math.U256(value.Rsh(value, n)) - return nil, nil } @@ -357,29 +216,24 @@ func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt // The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2, // and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension. func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - // Note, S256 returns (potentially) a new bigint, so we're popping, not peeking this one - shift, value := math.U256(callContext.stack.pop()), math.S256(callContext.stack.pop()) - defer interpreter.intPool.putOne(shift) // First operand back into the pool - - if shift.Cmp(common.Big256) >= 0 { + shift, value := callContext.stack.pop(), callContext.stack.peek() + if shift.GtUint64(256) { if value.Sign() >= 0 { - value.SetUint64(0) + value.Clear() } else { - value.SetInt64(-1) + // Max negative shift: all bits set + value.SetAllOne() } - callContext.stack.push(math.U256(value)) return nil, nil } n := uint(shift.Uint64()) - value.Rsh(value, n) - callContext.stack.push(math.U256(value)) - + value.SRsh(value, n) return nil, nil } func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - offset, size := callContext.stack.pop(), callContext.stack.pop() - data := callContext.memory.GetPtr(offset.Int64(), size.Int64()) + offset, size := callContext.stack.pop(), callContext.stack.peek() + data := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) if interpreter.hasher == nil { interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState) @@ -393,45 +247,50 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by if evm.vmConfig.EnablePreimageRecording { evm.StateDB.AddPreimage(interpreter.hasherBuf, data) } - callContext.stack.push(interpreter.intPool.get().SetBytes(interpreter.hasherBuf[:])) - interpreter.intPool.put(offset, size) + size.SetBytes(interpreter.hasherBuf[:]) return nil, nil } - func opAddress(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetBytes(callContext.contract.Address().Bytes())) + callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Address().Bytes())) return nil, nil } func opBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { slot := callContext.stack.peek() - slot.Set(interpreter.evm.StateDB.GetBalance(common.BigToAddress(slot))) + address := common.Address(slot.Bytes20()) + slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address)) return nil, nil } func opOrigin(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Origin.Bytes())) + callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes())) return nil, nil } - func opCaller(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetBytes(callContext.contract.Caller().Bytes())) + callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Caller().Bytes())) return nil, nil } func opCallValue(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().Set(callContext.contract.value)) + v, _ := uint256.FromBig(callContext.contract.value) + callContext.stack.push(v) return nil, nil } func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetBytes(getDataBig(callContext.contract.Input, callContext.stack.pop(), big32))) + x := callContext.stack.peek() + if offset, overflow := x.Uint64WithOverflow(); !overflow { + data := getData(callContext.contract.Input, offset, 32) + x.SetBytes(data) + } else { + x.Clear() + } return nil, nil } func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetInt64(int64(len(callContext.contract.Input)))) + callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(callContext.contract.Input)))) return nil, nil } @@ -441,14 +300,20 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCt dataOffset = callContext.stack.pop() length = callContext.stack.pop() ) - callContext.memory.Set(memOffset.Uint64(), length.Uint64(), getDataBig(callContext.contract.Input, dataOffset, length)) + dataOffset64, overflow := dataOffset.Uint64WithOverflow() + if overflow { + dataOffset64 = 0xffffffffffffffff + } + // These values are checked for overflow during gas cost calculation + memOffset64 := memOffset.Uint64() + length64 := length.Uint64() + callContext.memory.Set(memOffset64, length64, getData(callContext.contract.Input, dataOffset64, length64)) - interpreter.intPool.put(memOffset, dataOffset, length) return nil, nil } func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetUint64(uint64(len(interpreter.returnData)))) + callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData)))) return nil, nil } @@ -457,30 +322,33 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *call memOffset = callContext.stack.pop() dataOffset = callContext.stack.pop() length = callContext.stack.pop() - - end = interpreter.intPool.get().Add(dataOffset, length) ) - defer interpreter.intPool.put(memOffset, dataOffset, length, end) - if !end.IsUint64() || uint64(len(interpreter.returnData)) < end.Uint64() { + offset64, overflow := dataOffset.Uint64WithOverflow() + if overflow { return nil, ErrReturnDataOutOfBounds } - callContext.memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()]) - + // we can reuse dataOffset now (aliasing it for clarity) + var end = dataOffset + end.Add(&dataOffset, &length) + end64, overflow := end.Uint64WithOverflow() + if overflow || uint64(len(interpreter.returnData)) < end64 { + return nil, ErrReturnDataOutOfBounds + } + callContext.memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[offset64:end64]) return nil, nil } func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { slot := callContext.stack.peek() - slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.BigToAddress(slot)))) - + slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.Address(slot.Bytes20())))) return nil, nil } func opCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - l := interpreter.intPool.get().SetInt64(int64(len(callContext.contract.Code))) + l := new(uint256.Int) + l.SetUint64(uint64(len(callContext.contract.Code))) callContext.stack.push(l) - return nil, nil } @@ -490,24 +358,32 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ( codeOffset = callContext.stack.pop() length = callContext.stack.pop() ) - codeCopy := getDataBig(callContext.contract.Code, codeOffset, length) + uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() + if overflow { + uint64CodeOffset = 0xffffffffffffffff + } + codeCopy := getData(callContext.contract.Code, uint64CodeOffset, length.Uint64()) callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) - interpreter.intPool.put(memOffset, codeOffset, length) return nil, nil } func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { var ( - addr = common.BigToAddress(callContext.stack.pop()) - memOffset = callContext.stack.pop() - codeOffset = callContext.stack.pop() - length = callContext.stack.pop() + stack = callContext.stack + a = stack.pop() + memOffset = stack.pop() + codeOffset = stack.pop() + length = stack.pop() ) - codeCopy := getDataBig(interpreter.evm.StateDB.GetCode(addr), codeOffset, length) + uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() + if overflow { + uint64CodeOffset = 0xffffffffffffffff + } + addr := common.Address(a.Bytes20()) + codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64()) callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) - interpreter.intPool.put(memOffset, codeOffset, length) return nil, nil } @@ -539,9 +415,9 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx // this account should be regarded as a non-existent account and zero should be returned. func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { slot := callContext.stack.peek() - address := common.BigToAddress(slot) + address := common.Address(slot.Bytes20()) if interpreter.evm.StateDB.Empty(address) { - slot.SetUint64(0) + slot.Clear() } else { slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes()) } @@ -549,56 +425,69 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx } func opGasprice(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().Set(interpreter.evm.GasPrice)) + v, _ := uint256.FromBig(interpreter.evm.GasPrice) + callContext.stack.push(v) return nil, nil } func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - num := callContext.stack.pop() - - n := interpreter.intPool.get().Sub(interpreter.evm.BlockNumber, common.Big257) - if num.Cmp(n) > 0 && num.Cmp(interpreter.evm.BlockNumber) < 0 { - callContext.stack.push(interpreter.evm.GetHash(num.Uint64()).Big()) + num := callContext.stack.peek() + num64, overflow := num.Uint64WithOverflow() + if overflow { + num.Clear() + return nil, nil + } + var upper, lower uint64 + upper = interpreter.evm.BlockNumber.Uint64() + if upper < 257 { + lower = 0 + } else { + lower = upper - 256 + } + if num64 >= lower && num64 < upper { + num.SetBytes(interpreter.evm.GetHash(num64).Bytes()) } else { - callContext.stack.push(interpreter.intPool.getZero()) + num.Clear() } - interpreter.intPool.put(num, n) return nil, nil } func opCoinbase(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Coinbase.Bytes())) + callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Coinbase.Bytes())) return nil, nil } func opTimestamp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.Time))) + v, _ := uint256.FromBig(interpreter.evm.Time) + callContext.stack.push(v) return nil, nil } func opNumber(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.BlockNumber))) + v, _ := uint256.FromBig(interpreter.evm.BlockNumber) + callContext.stack.push(v) return nil, nil } func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.Difficulty))) + v, _ := uint256.FromBig(interpreter.evm.Difficulty) + callContext.stack.push(v) return nil, nil } func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(math.U256(interpreter.intPool.get().SetUint64(interpreter.evm.GasLimit))) + callContext.stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit)) return nil, nil } func opPop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - interpreter.intPool.putOne(callContext.stack.pop()) + callContext.stack.pop() return nil, nil } func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { v := callContext.stack.peek() - offset := v.Int64() + offset := int64(v.Uint64()) v.SetBytes(callContext.memory.GetPtr(offset, 32)) return nil, nil } @@ -606,58 +495,51 @@ func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]b func opMstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { // pop value of the stack mStart, val := callContext.stack.pop(), callContext.stack.pop() - callContext.memory.Set32(mStart.Uint64(), val) - - interpreter.intPool.put(mStart, val) + callContext.memory.Set32(mStart.Uint64(), &val) return nil, nil } func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - off, val := callContext.stack.pop().Int64(), callContext.stack.pop().Int64() - callContext.memory.store[off] = byte(val & 0xff) - + off, val := callContext.stack.pop(), callContext.stack.pop() + callContext.memory.store[off.Uint64()] = byte(val.Uint64()) return nil, nil } func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { loc := callContext.stack.peek() - val := interpreter.evm.StateDB.GetState(callContext.contract.Address(), common.BigToHash(loc)) + hash := common.Hash(loc.Bytes32()) + val := interpreter.evm.StateDB.GetState(callContext.contract.Address(), hash) loc.SetBytes(val.Bytes()) return nil, nil } func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - loc := common.BigToHash(callContext.stack.pop()) + loc := callContext.stack.pop() val := callContext.stack.pop() - interpreter.evm.StateDB.SetState(callContext.contract.Address(), loc, common.BigToHash(val)) - - interpreter.intPool.putOne(val) + interpreter.evm.StateDB.SetState(callContext.contract.Address(), + common.Hash(loc.Bytes32()), common.Hash(val.Bytes32())) return nil, nil } func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { pos := callContext.stack.pop() - if !callContext.contract.validJumpdest(pos) { + if !callContext.contract.validJumpdest(&pos) { return nil, ErrInvalidJump } *pc = pos.Uint64() - - interpreter.intPool.putOne(pos) return nil, nil } func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { pos, cond := callContext.stack.pop(), callContext.stack.pop() - if cond.Sign() != 0 { - if !callContext.contract.validJumpdest(pos) { + if !cond.IsZero() { + if !callContext.contract.validJumpdest(&pos) { return nil, ErrInvalidJump } *pc = pos.Uint64() } else { *pc++ } - - interpreter.intPool.put(pos, cond) return nil, nil } @@ -666,17 +548,17 @@ func opJumpdest(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ( } func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetUint64(*pc)) + callContext.stack.push(new(uint256.Int).SetUint64(*pc)) return nil, nil } func opMsize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetInt64(int64(callContext.memory.Len()))) + callContext.stack.push(new(uint256.Int).SetUint64(uint64(callContext.memory.Len()))) return nil, nil } func opGas(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.push(interpreter.intPool.get().SetUint64(callContext.contract.Gas)) + callContext.stack.push(new(uint256.Int).SetUint64(callContext.contract.Gas)) return nil, nil } @@ -684,28 +566,30 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] var ( value = callContext.stack.pop() offset, size = callContext.stack.pop(), callContext.stack.pop() - input = callContext.memory.GetCopy(offset.Int64(), size.Int64()) + input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) gas = callContext.contract.Gas ) if interpreter.evm.chainRules.IsEIP150 { gas -= gas / 64 } + // reuse size int for stackvalue + stackvalue := size callContext.contract.UseGas(gas) - res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, value) + res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, value.ToBig()) // 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 // ignore this error and pretend the operation was successful. if interpreter.evm.chainRules.IsHomestead && suberr == ErrCodeStoreOutOfGas { - callContext.stack.push(interpreter.intPool.getZero()) + stackvalue.Clear() } else if suberr != nil && suberr != ErrCodeStoreOutOfGas { - callContext.stack.push(interpreter.intPool.getZero()) + stackvalue.Clear() } else { - callContext.stack.push(interpreter.intPool.get().SetBytes(addr.Bytes())) + stackvalue.SetBytes(addr.Bytes()) } + callContext.stack.push(&stackvalue) callContext.contract.Gas += returnGas - interpreter.intPool.put(value, offset, size) if suberr == ErrExecutionReverted { return res, nil @@ -718,22 +602,25 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ endowment = callContext.stack.pop() offset, size = callContext.stack.pop(), callContext.stack.pop() salt = callContext.stack.pop() - input = callContext.memory.GetCopy(offset.Int64(), size.Int64()) + input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) gas = callContext.contract.Gas ) // Apply EIP150 gas -= gas / 64 callContext.contract.UseGas(gas) - res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas, endowment, salt) + // reuse size int for stackvalue + stackvalue := size + res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas, + endowment.ToBig(), salt.ToBig()) // Push item on the stack based on the returned error. if suberr != nil { - callContext.stack.push(interpreter.intPool.getZero()) + stackvalue.Clear() } else { - callContext.stack.push(interpreter.intPool.get().SetBytes(addr.Bytes())) + stackvalue.SetBytes(addr.Bytes()) } + callContext.stack.push(&stackvalue) callContext.contract.Gas += returnGas - interpreter.intPool.put(endowment, offset, size, salt) if suberr == ErrExecutionReverted { return res, nil @@ -742,130 +629,134 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ } func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + stack := callContext.stack // Pop gas. The actual gas in interpreter.evm.callGasTemp. - interpreter.intPool.putOne(callContext.stack.pop()) + // We can use this as a temporary value + temp := stack.pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. - addr, value, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop() - toAddr := common.BigToAddress(addr) - value = math.U256(value) + addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + toAddr := common.Address(addr.Bytes20()) // Get the arguments from the memory. - args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64()) + args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) - if value.Sign() != 0 { + if !value.IsZero() { gas += params.CallStipend } - ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, value) + ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, value.ToBig()) if err != nil { - callContext.stack.push(interpreter.intPool.getZero()) + temp.Clear() } else { - callContext.stack.push(interpreter.intPool.get().SetUint64(1)) + temp.SetOne() } + stack.push(&temp) if err == nil || err == ErrExecutionReverted { ret = common.CopyBytes(ret) callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } callContext.contract.Gas += returnGas - interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) return ret, nil } func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { // Pop gas. The actual gas is in interpreter.evm.callGasTemp. - interpreter.intPool.putOne(callContext.stack.pop()) + stack := callContext.stack + // We use it as a temporary value + temp := stack.pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. - addr, value, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop() - toAddr := common.BigToAddress(addr) - value = math.U256(value) + addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + toAddr := common.Address(addr.Bytes20()) // Get arguments from the memory. - args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64()) + args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) - if value.Sign() != 0 { + if !value.IsZero() { gas += params.CallStipend } - ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, value) + ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, value.ToBig()) if err != nil { - callContext.stack.push(interpreter.intPool.getZero()) + temp.Clear() } else { - callContext.stack.push(interpreter.intPool.get().SetUint64(1)) + temp.SetOne() } + stack.push(&temp) if err == nil || err == ErrExecutionReverted { ret = common.CopyBytes(ret) callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } callContext.contract.Gas += returnGas - interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) return ret, nil } func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + stack := callContext.stack // Pop gas. The actual gas is in interpreter.evm.callGasTemp. - interpreter.intPool.putOne(callContext.stack.pop()) + // We use it as a temporary value + temp := stack.pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. - addr, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop() - toAddr := common.BigToAddress(addr) + addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + toAddr := common.Address(addr.Bytes20()) // Get arguments from the memory. - args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64()) + args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) ret, returnGas, err := interpreter.evm.DelegateCall(callContext.contract, toAddr, args, gas) if err != nil { - callContext.stack.push(interpreter.intPool.getZero()) + temp.Clear() } else { - callContext.stack.push(interpreter.intPool.get().SetUint64(1)) + temp.SetOne() } + stack.push(&temp) if err == nil || err == ErrExecutionReverted { ret = common.CopyBytes(ret) callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } callContext.contract.Gas += returnGas - interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize) return ret, nil } func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { // Pop gas. The actual gas is in interpreter.evm.callGasTemp. - interpreter.intPool.putOne(callContext.stack.pop()) + stack := callContext.stack + // We use it as a temporary value + temp := stack.pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. - addr, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop() - toAddr := common.BigToAddress(addr) + addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + toAddr := common.Address(addr.Bytes20()) // Get arguments from the memory. - args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64()) + args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) ret, returnGas, err := interpreter.evm.StaticCall(callContext.contract, toAddr, args, gas) if err != nil { - callContext.stack.push(interpreter.intPool.getZero()) + temp.Clear() } else { - callContext.stack.push(interpreter.intPool.get().SetUint64(1)) + temp.SetOne() } + stack.push(&temp) if err == nil || err == ErrExecutionReverted { ret = common.CopyBytes(ret) callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } callContext.contract.Gas += returnGas - interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize) return ret, nil } func opReturn(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { offset, size := callContext.stack.pop(), callContext.stack.pop() - ret := callContext.memory.GetPtr(offset.Int64(), size.Int64()) + ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) - interpreter.intPool.put(offset, size) return ret, nil } func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { offset, size := callContext.stack.pop(), callContext.stack.pop() - ret := callContext.memory.GetPtr(offset.Int64(), size.Int64()) + ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) - interpreter.intPool.put(offset, size) return ret, nil } @@ -874,9 +765,9 @@ func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by } func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + beneficiary := callContext.stack.pop() balance := interpreter.evm.StateDB.GetBalance(callContext.contract.Address()) - interpreter.evm.StateDB.AddBalance(common.BigToAddress(callContext.stack.pop()), balance) - + interpreter.evm.StateDB.AddBalance(common.Address(beneficiary.Bytes20()), balance) interpreter.evm.StateDB.Suicide(callContext.contract.Address()) return nil, nil } @@ -887,12 +778,14 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ func makeLog(size int) executionFunc { return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { topics := make([]common.Hash, size) - mStart, mSize := callContext.stack.pop(), callContext.stack.pop() + stack := callContext.stack + mStart, mSize := stack.pop(), stack.pop() for i := 0; i < size; i++ { - topics[i] = common.BigToHash(callContext.stack.pop()) + addr := stack.pop() + topics[i] = common.Hash(addr.Bytes32()) } - d := callContext.memory.GetCopy(mStart.Int64(), mSize.Int64()) + d := callContext.memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64())) interpreter.evm.StateDB.AddLog(&types.Log{ Address: callContext.contract.Address(), Topics: topics, @@ -902,7 +795,6 @@ func makeLog(size int) executionFunc { BlockNumber: interpreter.evm.BlockNumber.Uint64(), }) - interpreter.intPool.put(mStart, mSize) return nil, nil } } @@ -911,13 +803,13 @@ func makeLog(size int) executionFunc { func opPush1(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { var ( codeLen = uint64(len(callContext.contract.Code)) - integer = interpreter.intPool.get() + integer = new(uint256.Int) ) *pc += 1 if *pc < codeLen { callContext.stack.push(integer.SetUint64(uint64(callContext.contract.Code[*pc]))) } else { - callContext.stack.push(integer.SetUint64(0)) + callContext.stack.push(integer.Clear()) } return nil, nil } @@ -937,8 +829,9 @@ func makePush(size uint64, pushByteSize int) executionFunc { endMin = startMin + pushByteSize } - integer := interpreter.intPool.get() - callContext.stack.push(integer.SetBytes(common.RightPadBytes(callContext.contract.Code[startMin:endMin], pushByteSize))) + integer := new(uint256.Int) + callContext.stack.push(integer.SetBytes(common.RightPadBytes( + callContext.contract.Code[startMin:endMin], pushByteSize))) *pc += size return nil, nil @@ -948,7 +841,7 @@ func makePush(size uint64, pushByteSize int) executionFunc { // make dup instruction function func makeDup(size int64) executionFunc { return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - callContext.stack.dup(interpreter.intPool, int(size)) + callContext.stack.dup(int(size)) return nil, nil } } diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index f439b643a5dd..13dfa960d1ec 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -21,14 +21,13 @@ import ( "encoding/json" "fmt" "log" - "math/big" "os" "testing" - "github.com/XinFinOrg/XDPoSChain/params" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/params" + "github.com/holiman/uint256" ) type TwoOperandTestcase struct { @@ -91,31 +90,6 @@ func init() { } } -// getResult is a convenience function to generate the expected values -func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase { - var ( - env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) - stack = newstack() - pc = uint64(0) - interpreter = env.interpreter.(*EVMInterpreter) - ) - interpreter.intPool = poolOfIntPools.get() - result := make([]TwoOperandTestcase, len(args)) - for i, param := range args { - x := new(big.Int).SetBytes(common.Hex2Bytes(param.x)) - y := new(big.Int).SetBytes(common.Hex2Bytes(param.y)) - stack.push(x) - stack.push(y) - _, err := opFn(&pc, interpreter, &callCtx{nil, stack, nil}) - if err != nil { - log.Fatalln(err) - } - actual := stack.pop() - result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} - } - return result -} - func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) { var ( @@ -124,42 +98,23 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu pc = uint64(0) evmInterpreter = env.interpreter.(*EVMInterpreter) ) - // Stuff a couple of nonzero bigints into pool, to ensure that ops do not rely on pooled integers to be zero - evmInterpreter.intPool = poolOfIntPools.get() - evmInterpreter.intPool.put(big.NewInt(-1337)) - evmInterpreter.intPool.put(big.NewInt(-1337)) - evmInterpreter.intPool.put(big.NewInt(-1337)) for i, test := range tests { - x := new(big.Int).SetBytes(common.Hex2Bytes(test.X)) - y := new(big.Int).SetBytes(common.Hex2Bytes(test.Y)) - expected := new(big.Int).SetBytes(common.Hex2Bytes(test.Expected)) + x := new(uint256.Int).SetBytes(common.Hex2Bytes(test.X)) + y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Y)) + expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected)) stack.push(x) stack.push(y) opFn(&pc, evmInterpreter, &callCtx{nil, stack, nil}) + if len(stack.data) != 1 { + t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data)) + } actual := stack.pop() if actual.Cmp(expected) != 0 { t.Errorf("Testcase %v %d, %v(%x, %x): expected %x, got %x", name, i, name, x, y, expected, actual) } - // Check pool usage - // 1.pool is not allowed to contain anything on the stack - // 2.pool is not allowed to contain the same pointers twice - if evmInterpreter.intPool.pool.len() > 0 { - - poolvals := make(map[*big.Int]struct{}) - poolvals[actual] = struct{}{} - - for evmInterpreter.intPool.pool.len() > 0 { - key := evmInterpreter.intPool.get() - if _, exist := poolvals[key]; exist { - t.Errorf("Testcase %v %d, pool contains double-entry", name, i) - } - poolvals[key] = struct{}{} - } - } } - poolOfIntPools.put(evmInterpreter.intPool) } func TestByteOp(t *testing.T) { @@ -235,6 +190,68 @@ func TestSAR(t *testing.T) { testTwoOperandOp(t, tests, opSAR, "sar") } +func TestAddMod(t *testing.T) { + var ( + env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + stack = newstack() + evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + pc = uint64(0) + ) + tests := []struct { + x string + y string + z string + expected string + }{ + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + }, + } + // x + y = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd + // in 256 bit repr, fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd + + for i, test := range tests { + x := new(uint256.Int).SetBytes(common.Hex2Bytes(test.x)) + y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.y)) + z := new(uint256.Int).SetBytes(common.Hex2Bytes(test.z)) + expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.expected)) + stack.push(z) + stack.push(y) + stack.push(x) + opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil}) + actual := stack.pop() + if actual.Cmp(expected) != 0 { + t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual) + } + } +} + +// getResult is a convenience function to generate the expected values +func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase { + var ( + env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) + stack = newstack() + pc = uint64(0) + interpreter = env.interpreter.(*EVMInterpreter) + ) + result := make([]TwoOperandTestcase, len(args)) + for i, param := range args { + x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x)) + y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y)) + stack.push(x) + stack.push(y) + _, err := opFn(&pc, interpreter, &callCtx{nil, stack, nil}) + if err != nil { + log.Fatalln(err) + } + actual := stack.pop() + result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} + } + return result +} + // utility function to fill the json-file with testcases // Enable this test to generate the 'testcases_xx.json' files func TestWriteExpectedValues(t *testing.T) { @@ -276,7 +293,6 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { ) env.interpreter = evmInterpreter - evmInterpreter.intPool = poolOfIntPools.get() // convert args byteArgs := make([][]byte, len(args)) for i, arg := range args { @@ -286,13 +302,13 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { bench.ResetTimer() for i := 0; i < bench.N; i++ { for _, arg := range byteArgs { - a := new(big.Int).SetBytes(arg) + a := new(uint256.Int) + a.SetBytes(arg) stack.push(a) } op(&pc, evmInterpreter, &callCtx{nil, stack, nil}) stack.pop() } - poolOfIntPools.put(evmInterpreter.intPool) } func BenchmarkOpAdd64(b *testing.B) { @@ -512,21 +528,19 @@ func TestOpMstore(t *testing.T) { ) env.interpreter = evmInterpreter - evmInterpreter.intPool = poolOfIntPools.get() mem.Resize(64) pc := uint64(0) v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" - stack.pushN(new(big.Int).SetBytes(common.Hex2Bytes(v)), big.NewInt(0)) + stack.pushN(*new(uint256.Int).SetBytes(common.Hex2Bytes(v)), *new(uint256.Int)) opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil}) if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v { t.Fatalf("Mstore fail, got %v, expected %v", got, v) } - stack.pushN(big.NewInt(0x1), big.NewInt(0)) + stack.pushN(*new(uint256.Int).SetUint64(0x1), *new(uint256.Int)) opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil}) if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" { t.Fatalf("Mstore failed to overwrite previous value") } - poolOfIntPools.put(evmInterpreter.intPool) } func BenchmarkOpMstore(bench *testing.B) { @@ -538,18 +552,16 @@ func BenchmarkOpMstore(bench *testing.B) { ) env.interpreter = evmInterpreter - evmInterpreter.intPool = poolOfIntPools.get() mem.Resize(64) pc := uint64(0) - memStart := big.NewInt(0) - value := big.NewInt(0x1337) + memStart := new(uint256.Int) + value := new(uint256.Int).SetUint64(0x1337) bench.ResetTimer() for i := 0; i < bench.N; i++ { - stack.pushN(value, memStart) + stack.pushN(*value, *memStart) opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil}) } - poolOfIntPools.put(evmInterpreter.intPool) } func BenchmarkOpSHA3(bench *testing.B) { @@ -560,17 +572,15 @@ func BenchmarkOpSHA3(bench *testing.B) { evmInterpreter = NewEVMInterpreter(env, env.vmConfig) ) env.interpreter = evmInterpreter - evmInterpreter.intPool = poolOfIntPools.get() mem.Resize(32) pc := uint64(0) - start := big.NewInt(0) + start := new(uint256.Int) bench.ResetTimer() for i := 0; i < bench.N; i++ { - stack.pushN(big.NewInt(32), start) + stack.pushN(*uint256.NewInt(32), *start) opSha3(&pc, evmInterpreter, &callCtx{mem, stack, nil}) } - poolOfIntPools.put(evmInterpreter.intPool) } func TestCreate2Addreses(t *testing.T) { @@ -644,6 +654,5 @@ func TestCreate2Addreses(t *testing.T) { if !bytes.Equal(expected.Bytes(), address.Bytes()) { t.Errorf("test %d: expected %s, got %s", i, expected.String(), address.String()) } - } } diff --git a/core/vm/int_pool_verifier.go b/core/vm/int_pool_verifier.go deleted file mode 100644 index 82fbfed699de..000000000000 --- a/core/vm/int_pool_verifier.go +++ /dev/null @@ -1,31 +0,0 @@ -// 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 . - -// +build VERIFY_EVM_INTEGER_POOL - -package vm - -import "fmt" - -const verifyPool = true - -func verifyIntegerPool(ip *intPool) { - for i, item := range ip.pool.data { - if item.Cmp(checkVal) != 0 { - panic(fmt.Sprintf("%d'th item failed aggressive pool check. Value was modified", i)) - } - } -} diff --git a/core/vm/int_pool_verifier_empty.go b/core/vm/int_pool_verifier_empty.go deleted file mode 100644 index a5f1dc02b7fe..000000000000 --- a/core/vm/int_pool_verifier_empty.go +++ /dev/null @@ -1,23 +0,0 @@ -// 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 . - -// +build !VERIFY_EVM_INTEGER_POOL - -package vm - -const verifyPool = false - -func verifyIntegerPool(ip *intPool) {} diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 2662a1922ec8..e857a2169c19 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -83,8 +83,6 @@ type EVMInterpreter struct { evm *EVM cfg Config - intPool *intPool - hasher keccakState // Keccak256 hasher instance shared across opcodes hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes @@ -138,13 +136,6 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { // considered a revert-and-consume-all-gas operation except for // ErrExecutionReverted which means revert-and-keep-gas-left. func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) { - if in.intPool == nil { - in.intPool = poolOfIntPools.get() - defer func() { - poolOfIntPools.put(in.intPool) - in.intPool = nil - }() - } // Increment the call depth which is restricted to 1024 in.evm.depth++ @@ -188,9 +179,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( ) contract.Input = input - // Reclaim the stack as an int pool when the execution stops - defer func() { in.intPool.put(stack.data...) }() - if in.cfg.Debug { defer func() { if err != nil { @@ -285,11 +273,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // execute the operation res, err = operation.execute(&pc, in, callContext) - // verifyPool is a build flag. Pool verification makes sure the integrity - // of the integer pool by comparing values to a default value. - if verifyPool { - verifyIntegerPool(in.intPool) - } // if the operation clears the return data (e.g. it has returning data) // set the last return to the result of the operation. if operation.returns { diff --git a/core/vm/intpool.go b/core/vm/intpool.go deleted file mode 100644 index eed074b07338..000000000000 --- a/core/vm/intpool.go +++ /dev/null @@ -1,117 +0,0 @@ -// 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 vm - -import ( - "math/big" - "sync" -) - -var checkVal = big.NewInt(-42) - -const poolLimit = 256 - -// intPool is a pool of big integers that -// can be reused for all big.Int operations. -type intPool struct { - pool *Stack -} - -func newIntPool() *intPool { - return &intPool{pool: newstack()} -} - -// get retrieves a big int from the pool, allocating one if the pool is empty. -// Note, the returned int's value is arbitrary and will not be zeroed! -func (p *intPool) get() *big.Int { - if p.pool.len() > 0 { - return p.pool.pop() - } - return new(big.Int) -} - -// getZero retrieves a big int from the pool, setting it to zero or allocating -// a new one if the pool is empty. -func (p *intPool) getZero() *big.Int { - if p.pool.len() > 0 { - return p.pool.pop().SetUint64(0) - } - return new(big.Int) -} - -// putOne returns an allocated big int to the pool to be later reused by get calls. -// Note, the values as saved as is; neither put nor get zeroes the ints out! -// As opposed to 'put' with variadic args, this method becomes inlined by the -// go compiler -func (p *intPool) putOne(i *big.Int) { - if len(p.pool.data) > poolLimit { - return - } - p.pool.push(i) -} - -// put returns an allocated big int to the pool to be later reused by get calls. -// Note, the values as saved as is; neither put nor get zeroes the ints out! -func (p *intPool) put(is ...*big.Int) { - if len(p.pool.data) > poolLimit { - return - } - for _, i := range is { - // verifyPool is a build flag. Pool verification makes sure the integrity - // of the integer pool by comparing values to a default value. - if verifyPool { - i.Set(checkVal) - } - p.pool.push(i) - } -} - -// The intPool pool's default capacity -const poolDefaultCap = 25 - -// intPoolPool manages a pool of intPools. -type intPoolPool struct { - pools []*intPool - lock sync.Mutex -} - -var poolOfIntPools = &intPoolPool{ - pools: make([]*intPool, 0, poolDefaultCap), -} - -// get is looking for an available pool to return. -func (ipp *intPoolPool) get() *intPool { - ipp.lock.Lock() - defer ipp.lock.Unlock() - - if len(poolOfIntPools.pools) > 0 { - ip := ipp.pools[len(ipp.pools)-1] - ipp.pools = ipp.pools[:len(ipp.pools)-1] - return ip - } - return newIntPool() -} - -// put a pool that has been allocated with get. -func (ipp *intPoolPool) put(ip *intPool) { - ipp.lock.Lock() - defer ipp.lock.Unlock() - - if len(ipp.pools) < cap(ipp.pools) { - ipp.pools = append(ipp.pools, ip) - } -} diff --git a/core/vm/intpool_test.go b/core/vm/intpool_test.go deleted file mode 100644 index 6c0d00f3ce5c..000000000000 --- a/core/vm/intpool_test.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2018 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 vm - -import ( - "testing" -) - -func TestIntPoolPoolGet(t *testing.T) { - poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap) - - nip := poolOfIntPools.get() - if nip == nil { - t.Fatalf("Invalid pool allocation") - } -} - -func TestIntPoolPoolPut(t *testing.T) { - poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap) - - nip := poolOfIntPools.get() - if len(poolOfIntPools.pools) != 0 { - t.Fatalf("Pool got added to list when none should have been") - } - - poolOfIntPools.put(nip) - if len(poolOfIntPools.pools) == 0 { - t.Fatalf("Pool did not get added to list when one should have been") - } -} - -func TestIntPoolPoolReUse(t *testing.T) { - poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap) - nip := poolOfIntPools.get() - poolOfIntPools.put(nip) - poolOfIntPools.get() - - if len(poolOfIntPools.pools) != 0 { - t.Fatalf("Invalid number of pools. Got %d, expected %d", len(poolOfIntPools.pools), 0) - } -} diff --git a/core/vm/logger.go b/core/vm/logger.go index 970ff90fb36e..6cdb7a7c5bed 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -156,8 +156,8 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui // it in the local storage container. if op == SSTORE && stack.len() >= 2 { var ( - value = common.BigToHash(stack.data[stack.len()-2]) - address = common.BigToHash(stack.data[stack.len()-1]) + value = common.Hash(stack.data[stack.len()-2].Bytes32()) + address = common.Hash(stack.data[stack.len()-1].Bytes32()) ) l.changedValues[contract.Address()][address] = value } @@ -172,7 +172,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui if !l.cfg.DisableStack { stck = make([]*big.Int, len(stack.Data())) for i, item := range stack.Data() { - stck[i] = new(big.Int).Set(item) + stck[i] = new(big.Int).Set(item.ToBig()) } } // Copy a snapshot of the current storage to a new container diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go index 2eeba8dfacc3..593626eda2e0 100644 --- a/core/vm/logger_json.go +++ b/core/vm/logger_json.go @@ -62,7 +62,12 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint log.Memory = memory.Data() } if !l.cfg.DisableStack { - log.Stack = stack.Data() + //TODO(@holiman) improve this + logstack := make([]*big.Int, len(stack.Data())) + for i, item := range stack.Data() { + logstack[i] = item.ToBig() + } + log.Stack = logstack } return l.encoder.Encode(log) } diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index e3e0a085b998..9ee02b97c028 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -20,10 +20,10 @@ import ( "math/big" "testing" - "github.com/XinFinOrg/XDPoSChain/params" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/state" + "github.com/XinFinOrg/XDPoSChain/params" + "github.com/holiman/uint256" ) type dummyContractRef struct { @@ -57,8 +57,8 @@ func TestStoreCapture(t *testing.T) { stack = newstack() contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0) ) - stack.push(big.NewInt(1)) - stack.push(big.NewInt(0)) + stack.push(uint256.NewInt(1)) + stack.push(new(uint256.Int)) var index common.Hash logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, contract, 0, nil) if len(logger.changedValues[contract.Address()]) == 0 { diff --git a/core/vm/memory.go b/core/vm/memory.go index 7fc44a096188..ba5f8485dc95 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -18,9 +18,8 @@ package vm import ( "fmt" - "math/big" - "github.com/XinFinOrg/XDPoSChain/common/math" + "github.com/holiman/uint256" ) // Memory implements a simple memory model for the ethereum virtual machine. @@ -50,7 +49,7 @@ func (m *Memory) Set(offset, size uint64, value []byte) { // Set32 sets the 32 bytes starting at offset to the value of val, left-padded with zeroes to // 32 bytes. -func (m *Memory) Set32(offset uint64, val *big.Int) { +func (m *Memory) Set32(offset uint64, val *uint256.Int) { // length of store may never be less than offset + size. // The store should be resized PRIOR to setting the memory if offset+32 > uint64(len(m.store)) { @@ -59,7 +58,7 @@ func (m *Memory) Set32(offset uint64, val *big.Int) { // Zero the memory area copy(m.store[offset:offset+32], []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) // Fill in relevant bits - math.ReadBits(val, m.store[offset:offset+32]) + val.WriteToSlice(m.store[offset:]) } // Resize resizes the memory to size diff --git a/core/vm/stack.go b/core/vm/stack.go index c9c3d07f4b6f..2367da1d7c97 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -18,36 +18,36 @@ package vm import ( "fmt" - "math/big" + + "github.com/holiman/uint256" ) // 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. type Stack struct { - data []*big.Int + data []uint256.Int } func newstack() *Stack { - return &Stack{data: make([]*big.Int, 0, 1024)} + return &Stack{data: make([]uint256.Int, 0, 16)} } -// Data returns the underlying big.Int array. -func (st *Stack) Data() []*big.Int { +// Data returns the underlying uint256.Int array. +func (st *Stack) Data() []uint256.Int { return st.data } -func (st *Stack) push(d *big.Int) { +func (st *Stack) push(d *uint256.Int) { // NOTE push limit (1024) is checked in baseCheck - //stackItem := new(big.Int).Set(d) - //st.data = append(st.data, stackItem) - st.data = append(st.data, d) + st.data = append(st.data, *d) } -func (st *Stack) pushN(ds ...*big.Int) { +func (st *Stack) pushN(ds ...uint256.Int) { + // FIXME: Is there a way to pass args by pointers. st.data = append(st.data, ds...) } -func (st *Stack) pop() (ret *big.Int) { +func (st *Stack) pop() (ret uint256.Int) { ret = st.data[len(st.data)-1] st.data = st.data[:len(st.data)-1] return @@ -61,17 +61,17 @@ func (st *Stack) swap(n int) { st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n] } -func (st *Stack) dup(pool *intPool, n int) { - st.push(pool.get().Set(st.data[st.len()-n])) +func (st *Stack) dup(n int) { + st.push(&st.data[st.len()-n]) } -func (st *Stack) peek() *big.Int { - return st.data[st.len()-1] +func (st *Stack) peek() *uint256.Int { + return &st.data[st.len()-1] } // Back returns the n'th item in stack -func (st *Stack) Back(n int) *big.Int { - return st.data[st.len()-n-1] +func (st *Stack) Back(n int) *uint256.Int { + return &st.data[st.len()-n-1] } // Print dumps the content of the stack diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go index 94c828fc900e..235f752bac0a 100644 --- a/eth/tracers/tracer.go +++ b/eth/tracers/tracer.go @@ -162,7 +162,7 @@ func (sw *stackWrapper) peek(idx int) *big.Int { log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx) return new(big.Int) } - return sw.stack.Data()[len(sw.stack.Data())-idx-1] + return sw.stack.Back(idx).ToBig() } // pushObject assembles a JSVM object wrapping a swappable stack and pushes it diff --git a/go.mod b/go.mod index e2065745fde8..8d19bc4956fc 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb github.com/gorilla/websocket v1.4.2 github.com/hashicorp/golang-lru v0.5.3 + github.com/holiman/uint256 v1.2.4 github.com/huin/goupnp v1.3.0 github.com/influxdata/influxdb v1.7.9 github.com/jackpal/go-nat-pmp v1.0.2 @@ -61,7 +62,6 @@ require ( github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/holiman/uint256 v1.2.3 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/maruel/panicparse v0.0.0-20160720141634-ad661195ed0e // indirect diff --git a/go.sum b/go.sum index 70a3ac6c9348..19df00ede131 100644 --- a/go.sum +++ b/go.sum @@ -140,6 +140,8 @@ github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8 github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= From 06d5da016c6729fad9cfb4348929be3553e68177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Thu, 16 Jul 2020 15:32:01 +0300 Subject: [PATCH 02/31] core/vm: use pointers to operations vs. copy by value (#21336) --- core/vm/eips.go | 6 +- core/vm/interpreter.go | 6 +- core/vm/jump_table.go | 163 +++-------------------------------------- 3 files changed, 16 insertions(+), 159 deletions(-) diff --git a/core/vm/eips.go b/core/vm/eips.go index 65829f1d568a..6ba7bbd6ec7a 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -52,12 +52,11 @@ func enable1884(jt *JumpTable) { jt[EXTCODEHASH].constantGas = params.ExtcodeHashGasEIP1884 // New opcode - jt[SELFBALANCE] = operation{ + jt[SELFBALANCE] = &operation{ execute: opSelfBalance, constantGas: GasFastStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, } } @@ -71,12 +70,11 @@ func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx // - Adds an opcode that returns the current chain’s EIP-155 unique identifier func enable1344(jt *JumpTable) { // New opcode - jt[CHAINID] = operation{ + jt[CHAINID] = &operation{ execute: opChainID, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, } } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index e857a2169c19..aa70d7c1020e 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -32,7 +32,7 @@ type Config struct { NoRecursion bool // Disables call, callcode, delegate call and create EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages - JumpTable [256]operation // EVM instruction table, automatically populated if unset + JumpTable [256]*operation // EVM instruction table, automatically populated if unset EWASMInterpreter string // External EWASM interpreter options EVMInterpreter string // External EVM interpreter options @@ -95,7 +95,7 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { // We use the STOP instruction whether to see // the jump table was initialised. If it was not // we'll set the default jump table. - if !cfg.JumpTable[STOP].valid { + if cfg.JumpTable[STOP] == nil { var jt JumpTable switch { case evm.chainRules.IsIstanbul: @@ -209,7 +209,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // enough stack items available to perform the operation. op = contract.GetOp(pc) operation := in.cfg.JumpTable[op] - if !operation.valid { + if operation == nil { return nil, &ErrInvalidOpCode{opcode: op} } // Validate stack diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index b3a0f7ff7ce9..7580544ba2ca 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -44,7 +44,6 @@ type operation struct { halts bool // indicates whether the operation should halt further execution jumps bool // indicates whether the program counter should not increment writes bool // determines whether this a state modifying operation - valid bool // indication whether the retrieved operation is valid and known reverts bool // determines whether the operation reverts state (implicitly halts) returns bool // determines whether the operations sets the return data content } @@ -60,7 +59,7 @@ var ( ) // JumpTable contains the EVM opcodes supported at a given fork. -type JumpTable [256]operation +type JumpTable [256]*operation // newIstanbulInstructionSet returns the frontier, homestead // byzantium, contantinople and petersburg instructions. @@ -78,42 +77,37 @@ func newIstanbulInstructionSet() JumpTable { // byzantium and contantinople instructions. func newConstantinopleInstructionSet() JumpTable { instructionSet := newByzantiumInstructionSet() - instructionSet[SHL] = operation{ + instructionSet[SHL] = &operation{ execute: opSHL, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, } - instructionSet[SHR] = operation{ + instructionSet[SHR] = &operation{ execute: opSHR, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, } - instructionSet[SAR] = operation{ + instructionSet[SAR] = &operation{ execute: opSAR, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, } - instructionSet[EXTCODEHASH] = operation{ + instructionSet[EXTCODEHASH] = &operation{ execute: opExtCodeHash, constantGas: params.ExtcodeHashGasConstantinople, minStack: minStack(1, 1), maxStack: maxStack(1, 1), - valid: true, } - instructionSet[CREATE2] = operation{ + instructionSet[CREATE2] = &operation{ execute: opCreate2, constantGas: params.Create2Gas, dynamicGas: gasCreate2, minStack: minStack(4, 1), maxStack: maxStack(4, 1), memorySize: memoryCreate2, - valid: true, writes: true, returns: true, } @@ -124,39 +118,35 @@ func newConstantinopleInstructionSet() JumpTable { // byzantium instructions. func newByzantiumInstructionSet() JumpTable { instructionSet := newSpuriousDragonInstructionSet() - instructionSet[STATICCALL] = operation{ + instructionSet[STATICCALL] = &operation{ execute: opStaticCall, constantGas: params.CallGasEIP150, dynamicGas: gasStaticCall, minStack: minStack(6, 1), maxStack: maxStack(6, 1), memorySize: memoryStaticCall, - valid: true, returns: true, } - instructionSet[RETURNDATASIZE] = operation{ + instructionSet[RETURNDATASIZE] = &operation{ execute: opReturnDataSize, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, } - instructionSet[RETURNDATACOPY] = operation{ + instructionSet[RETURNDATACOPY] = &operation{ execute: opReturnDataCopy, constantGas: GasFastestStep, dynamicGas: gasReturnDataCopy, minStack: minStack(3, 0), maxStack: maxStack(3, 0), memorySize: memoryReturnDataCopy, - valid: true, } - instructionSet[REVERT] = operation{ + instructionSet[REVERT] = &operation{ execute: opRevert, dynamicGas: gasRevert, minStack: minStack(2, 0), maxStack: maxStack(2, 0), memorySize: memoryRevert, - valid: true, reverts: true, returns: true, } @@ -188,14 +178,13 @@ func newTangerineWhistleInstructionSet() JumpTable { // instructions that can be executed during the homestead phase. func newHomesteadInstructionSet() JumpTable { instructionSet := newFrontierInstructionSet() - instructionSet[DELEGATECALL] = operation{ + instructionSet[DELEGATECALL] = &operation{ execute: opDelegateCall, dynamicGas: gasDelegateCall, constantGas: params.CallGasFrontier, minStack: minStack(6, 1), maxStack: maxStack(6, 1), memorySize: memoryDelegateCall, - valid: true, returns: true, } return instructionSet @@ -211,161 +200,138 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(0, 0), maxStack: maxStack(0, 0), halts: true, - valid: true, }, ADD: { execute: opAdd, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, MUL: { execute: opMul, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, SUB: { execute: opSub, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, DIV: { execute: opDiv, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, SDIV: { execute: opSdiv, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, MOD: { execute: opMod, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, SMOD: { execute: opSmod, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, ADDMOD: { execute: opAddmod, constantGas: GasMidStep, minStack: minStack(3, 1), maxStack: maxStack(3, 1), - valid: true, }, MULMOD: { execute: opMulmod, constantGas: GasMidStep, minStack: minStack(3, 1), maxStack: maxStack(3, 1), - valid: true, }, EXP: { execute: opExp, dynamicGas: gasExpFrontier, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, SIGNEXTEND: { execute: opSignExtend, constantGas: GasFastStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, LT: { execute: opLt, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, GT: { execute: opGt, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, SLT: { execute: opSlt, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, SGT: { execute: opSgt, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, EQ: { execute: opEq, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, ISZERO: { execute: opIszero, constantGas: GasFastestStep, minStack: minStack(1, 1), maxStack: maxStack(1, 1), - valid: true, }, AND: { execute: opAnd, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, XOR: { execute: opXor, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, OR: { execute: opOr, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, NOT: { execute: opNot, constantGas: GasFastestStep, minStack: minStack(1, 1), maxStack: maxStack(1, 1), - valid: true, }, BYTE: { execute: opByte, constantGas: GasFastestStep, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - valid: true, }, SHA3: { execute: opSha3, @@ -374,56 +340,48 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 1), maxStack: maxStack(2, 1), memorySize: memorySha3, - valid: true, }, ADDRESS: { execute: opAddress, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, BALANCE: { execute: opBalance, constantGas: params.BalanceGasFrontier, minStack: minStack(1, 1), maxStack: maxStack(1, 1), - valid: true, }, ORIGIN: { execute: opOrigin, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, CALLER: { execute: opCaller, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, CALLVALUE: { execute: opCallValue, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, CALLDATALOAD: { execute: opCallDataLoad, constantGas: GasFastestStep, minStack: minStack(1, 1), maxStack: maxStack(1, 1), - valid: true, }, CALLDATASIZE: { execute: opCallDataSize, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, CALLDATACOPY: { execute: opCallDataCopy, @@ -432,14 +390,12 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(3, 0), maxStack: maxStack(3, 0), memorySize: memoryCallDataCopy, - valid: true, }, CODESIZE: { execute: opCodeSize, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, CODECOPY: { execute: opCodeCopy, @@ -448,21 +404,18 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(3, 0), maxStack: maxStack(3, 0), memorySize: memoryCodeCopy, - valid: true, }, GASPRICE: { execute: opGasprice, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, EXTCODESIZE: { execute: opExtCodeSize, constantGas: params.ExtcodeSizeGasFrontier, minStack: minStack(1, 1), maxStack: maxStack(1, 1), - valid: true, }, EXTCODECOPY: { execute: opExtCodeCopy, @@ -471,56 +424,48 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(4, 0), maxStack: maxStack(4, 0), memorySize: memoryExtCodeCopy, - valid: true, }, BLOCKHASH: { execute: opBlockhash, constantGas: GasExtStep, minStack: minStack(1, 1), maxStack: maxStack(1, 1), - valid: true, }, COINBASE: { execute: opCoinbase, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, TIMESTAMP: { execute: opTimestamp, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, NUMBER: { execute: opNumber, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, DIFFICULTY: { execute: opDifficulty, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, GASLIMIT: { execute: opGasLimit, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, POP: { execute: opPop, constantGas: GasQuickStep, minStack: minStack(1, 0), maxStack: maxStack(1, 0), - valid: true, }, MLOAD: { execute: opMload, @@ -529,7 +474,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(1, 1), maxStack: maxStack(1, 1), memorySize: memoryMLoad, - valid: true, }, MSTORE: { execute: opMstore, @@ -538,7 +482,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 0), maxStack: maxStack(2, 0), memorySize: memoryMStore, - valid: true, }, MSTORE8: { execute: opMstore8, @@ -547,22 +490,18 @@ func newFrontierInstructionSet() JumpTable { memorySize: memoryMStore8, minStack: minStack(2, 0), maxStack: maxStack(2, 0), - - valid: true, }, SLOAD: { execute: opSload, constantGas: params.SloadGasFrontier, minStack: minStack(1, 1), maxStack: maxStack(1, 1), - valid: true, }, SSTORE: { execute: opSstore, dynamicGas: gasSStore, minStack: minStack(2, 0), maxStack: maxStack(2, 0), - valid: true, writes: true, }, JUMP: { @@ -571,7 +510,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(1, 0), maxStack: maxStack(1, 0), jumps: true, - valid: true, }, JUMPI: { execute: opJumpi, @@ -579,483 +517,414 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 0), maxStack: maxStack(2, 0), jumps: true, - valid: true, }, PC: { execute: opPc, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, MSIZE: { execute: opMsize, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, GAS: { execute: opGas, constantGas: GasQuickStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, JUMPDEST: { execute: opJumpdest, constantGas: params.JumpdestGas, minStack: minStack(0, 0), maxStack: maxStack(0, 0), - valid: true, }, PUSH1: { execute: opPush1, constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH2: { execute: makePush(2, 2), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH3: { execute: makePush(3, 3), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH4: { execute: makePush(4, 4), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH5: { execute: makePush(5, 5), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH6: { execute: makePush(6, 6), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH7: { execute: makePush(7, 7), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH8: { execute: makePush(8, 8), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH9: { execute: makePush(9, 9), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH10: { execute: makePush(10, 10), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH11: { execute: makePush(11, 11), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH12: { execute: makePush(12, 12), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH13: { execute: makePush(13, 13), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH14: { execute: makePush(14, 14), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH15: { execute: makePush(15, 15), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH16: { execute: makePush(16, 16), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH17: { execute: makePush(17, 17), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH18: { execute: makePush(18, 18), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH19: { execute: makePush(19, 19), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH20: { execute: makePush(20, 20), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH21: { execute: makePush(21, 21), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH22: { execute: makePush(22, 22), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH23: { execute: makePush(23, 23), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH24: { execute: makePush(24, 24), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH25: { execute: makePush(25, 25), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH26: { execute: makePush(26, 26), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH27: { execute: makePush(27, 27), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH28: { execute: makePush(28, 28), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH29: { execute: makePush(29, 29), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH30: { execute: makePush(30, 30), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH31: { execute: makePush(31, 31), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, PUSH32: { execute: makePush(32, 32), constantGas: GasFastestStep, minStack: minStack(0, 1), maxStack: maxStack(0, 1), - valid: true, }, DUP1: { execute: makeDup(1), constantGas: GasFastestStep, minStack: minDupStack(1), maxStack: maxDupStack(1), - valid: true, }, DUP2: { execute: makeDup(2), constantGas: GasFastestStep, minStack: minDupStack(2), maxStack: maxDupStack(2), - valid: true, }, DUP3: { execute: makeDup(3), constantGas: GasFastestStep, minStack: minDupStack(3), maxStack: maxDupStack(3), - valid: true, }, DUP4: { execute: makeDup(4), constantGas: GasFastestStep, minStack: minDupStack(4), maxStack: maxDupStack(4), - valid: true, }, DUP5: { execute: makeDup(5), constantGas: GasFastestStep, minStack: minDupStack(5), maxStack: maxDupStack(5), - valid: true, }, DUP6: { execute: makeDup(6), constantGas: GasFastestStep, minStack: minDupStack(6), maxStack: maxDupStack(6), - valid: true, }, DUP7: { execute: makeDup(7), constantGas: GasFastestStep, minStack: minDupStack(7), maxStack: maxDupStack(7), - valid: true, }, DUP8: { execute: makeDup(8), constantGas: GasFastestStep, minStack: minDupStack(8), maxStack: maxDupStack(8), - valid: true, }, DUP9: { execute: makeDup(9), constantGas: GasFastestStep, minStack: minDupStack(9), maxStack: maxDupStack(9), - valid: true, }, DUP10: { execute: makeDup(10), constantGas: GasFastestStep, minStack: minDupStack(10), maxStack: maxDupStack(10), - valid: true, }, DUP11: { execute: makeDup(11), constantGas: GasFastestStep, minStack: minDupStack(11), maxStack: maxDupStack(11), - valid: true, }, DUP12: { execute: makeDup(12), constantGas: GasFastestStep, minStack: minDupStack(12), maxStack: maxDupStack(12), - valid: true, }, DUP13: { execute: makeDup(13), constantGas: GasFastestStep, minStack: minDupStack(13), maxStack: maxDupStack(13), - valid: true, }, DUP14: { execute: makeDup(14), constantGas: GasFastestStep, minStack: minDupStack(14), maxStack: maxDupStack(14), - valid: true, }, DUP15: { execute: makeDup(15), constantGas: GasFastestStep, minStack: minDupStack(15), maxStack: maxDupStack(15), - valid: true, }, DUP16: { execute: makeDup(16), constantGas: GasFastestStep, minStack: minDupStack(16), maxStack: maxDupStack(16), - valid: true, }, SWAP1: { execute: makeSwap(1), constantGas: GasFastestStep, minStack: minSwapStack(2), maxStack: maxSwapStack(2), - valid: true, }, SWAP2: { execute: makeSwap(2), constantGas: GasFastestStep, minStack: minSwapStack(3), maxStack: maxSwapStack(3), - valid: true, }, SWAP3: { execute: makeSwap(3), constantGas: GasFastestStep, minStack: minSwapStack(4), maxStack: maxSwapStack(4), - valid: true, }, SWAP4: { execute: makeSwap(4), constantGas: GasFastestStep, minStack: minSwapStack(5), maxStack: maxSwapStack(5), - valid: true, }, SWAP5: { execute: makeSwap(5), constantGas: GasFastestStep, minStack: minSwapStack(6), maxStack: maxSwapStack(6), - valid: true, }, SWAP6: { execute: makeSwap(6), constantGas: GasFastestStep, minStack: minSwapStack(7), maxStack: maxSwapStack(7), - valid: true, }, SWAP7: { execute: makeSwap(7), constantGas: GasFastestStep, minStack: minSwapStack(8), maxStack: maxSwapStack(8), - valid: true, }, SWAP8: { execute: makeSwap(8), constantGas: GasFastestStep, minStack: minSwapStack(9), maxStack: maxSwapStack(9), - valid: true, }, SWAP9: { execute: makeSwap(9), constantGas: GasFastestStep, minStack: minSwapStack(10), maxStack: maxSwapStack(10), - valid: true, }, SWAP10: { execute: makeSwap(10), constantGas: GasFastestStep, minStack: minSwapStack(11), maxStack: maxSwapStack(11), - valid: true, }, SWAP11: { execute: makeSwap(11), constantGas: GasFastestStep, minStack: minSwapStack(12), maxStack: maxSwapStack(12), - valid: true, }, SWAP12: { execute: makeSwap(12), constantGas: GasFastestStep, minStack: minSwapStack(13), maxStack: maxSwapStack(13), - valid: true, }, SWAP13: { execute: makeSwap(13), constantGas: GasFastestStep, minStack: minSwapStack(14), maxStack: maxSwapStack(14), - valid: true, }, SWAP14: { execute: makeSwap(14), constantGas: GasFastestStep, minStack: minSwapStack(15), maxStack: maxSwapStack(15), - valid: true, }, SWAP15: { execute: makeSwap(15), constantGas: GasFastestStep, minStack: minSwapStack(16), maxStack: maxSwapStack(16), - valid: true, }, SWAP16: { execute: makeSwap(16), constantGas: GasFastestStep, minStack: minSwapStack(17), maxStack: maxSwapStack(17), - valid: true, }, LOG0: { execute: makeLog(0), @@ -1063,7 +932,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 0), maxStack: maxStack(2, 0), memorySize: memoryLog, - valid: true, writes: true, }, LOG1: { @@ -1072,7 +940,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(3, 0), maxStack: maxStack(3, 0), memorySize: memoryLog, - valid: true, writes: true, }, LOG2: { @@ -1081,7 +948,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(4, 0), maxStack: maxStack(4, 0), memorySize: memoryLog, - valid: true, writes: true, }, LOG3: { @@ -1090,7 +956,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(5, 0), maxStack: maxStack(5, 0), memorySize: memoryLog, - valid: true, writes: true, }, LOG4: { @@ -1099,7 +964,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(6, 0), maxStack: maxStack(6, 0), memorySize: memoryLog, - valid: true, writes: true, }, CREATE: { @@ -1109,7 +973,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(3, 1), maxStack: maxStack(3, 1), memorySize: memoryCreate, - valid: true, writes: true, returns: true, }, @@ -1120,7 +983,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(7, 1), maxStack: maxStack(7, 1), memorySize: memoryCall, - valid: true, returns: true, }, CALLCODE: { @@ -1130,7 +992,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(7, 1), maxStack: maxStack(7, 1), memorySize: memoryCall, - valid: true, returns: true, }, RETURN: { @@ -1140,7 +1001,6 @@ func newFrontierInstructionSet() JumpTable { maxStack: maxStack(2, 0), memorySize: memoryReturn, halts: true, - valid: true, }, SELFDESTRUCT: { execute: opSuicide, @@ -1148,7 +1008,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(1, 0), maxStack: maxStack(1, 0), halts: true, - valid: true, writes: true, }, } From 446b9e86f4e742c16529424af136e6a1d17a5030 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 28 Sep 2020 14:14:45 +0200 Subject: [PATCH 03/31] core/vm, params: make 2200 in line with spec (#21605) --- core/vm/gas_table.go | 34 +++++++++++++++++----------------- params/protocol_params.go | 12 ++++-------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 719613f91227..f25ba9e4cdfb 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -164,18 +164,18 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi } // 0. If *gasleft* is less than or equal to 2300, fail the current call. -// 1. If current value equals new value (this is a no-op), SSTORE_NOOP_GAS gas is deducted. +// 1. If current value equals new value (this is a no-op), SLOAD_GAS is deducted. // 2. If current value does not equal new value: // 2.1. If original value equals current value (this storage slot has not been changed by the current execution context): -// 2.1.1. If original value is 0, SSTORE_INIT_GAS gas is deducted. -// 2.1.2. Otherwise, SSTORE_CLEAN_GAS gas is deducted. If new value is 0, add SSTORE_CLEAR_REFUND to refund counter. -// 2.2. If original value does not equal current value (this storage slot is dirty), SSTORE_DIRTY_GAS gas is deducted. Apply both of the following clauses: +// 2.1.1. If original value is 0, SSTORE_SET_GAS (20K) gas is deducted. +// 2.1.2. Otherwise, SSTORE_RESET_GAS gas is deducted. If new value is 0, add SSTORE_CLEARS_SCHEDULE to refund counter. +// 2.2. If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses: // 2.2.1. If original value is not 0: -// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEAR_REFUND gas from refund counter. We can prove that refund counter will never go below 0. -// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEAR_REFUND gas to refund counter. +// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter. +// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter. // 2.2.2. If original value equals new value (this storage slot is reset): -// 2.2.2.1. If original value is 0, add SSTORE_INIT_REFUND to refund counter. -// 2.2.2.2. Otherwise, add SSTORE_CLEAN_REFUND gas to refund counter. +// 2.2.2.1. If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter. +// 2.2.2.2. Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter. func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { // If we fail the minimum gas availability invariant, fail (0) if contract.Gas <= params.SstoreSentryGasEIP2200 { @@ -189,33 +189,33 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m value := common.Hash(y.Bytes32()) if current == value { // noop (1) - return params.SstoreNoopGasEIP2200, nil + return params.SloadGasEIP2200, nil } original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32())) if original == current { if original == (common.Hash{}) { // create slot (2.1.1) - return params.SstoreInitGasEIP2200, nil + return params.SstoreSetGasEIP2200, nil } if value == (common.Hash{}) { // delete slot (2.1.2b) - evm.StateDB.AddRefund(params.SstoreClearRefundEIP2200) + evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200) } - return params.SstoreCleanGasEIP2200, nil // write existing slot (2.1.2) + return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2) } if original != (common.Hash{}) { if current == (common.Hash{}) { // recreate slot (2.2.1.1) - evm.StateDB.SubRefund(params.SstoreClearRefundEIP2200) + evm.StateDB.SubRefund(params.SstoreClearsScheduleRefundEIP2200) } else if value == (common.Hash{}) { // delete slot (2.2.1.2) - evm.StateDB.AddRefund(params.SstoreClearRefundEIP2200) + evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200) } } if original == value { if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1) - evm.StateDB.AddRefund(params.SstoreInitRefundEIP2200) + evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200) } else { // reset to original existing slot (2.2.2.2) - evm.StateDB.AddRefund(params.SstoreCleanRefundEIP2200) + evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200) } } - return params.SstoreDirtyGasEIP2200, nil // dirty update (2.2) + return params.SloadGasEIP2200, nil // dirty update (2.2) } func makeGasLog(n uint64) gasFunc { diff --git a/params/protocol_params.go b/params/protocol_params.go index f7ab3cdd1046..d9272981d63e 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -98,14 +98,10 @@ const ( NetSstoreResetRefund uint64 = 4800 // Once per SSTORE operation for resetting to the original non-zero value NetSstoreResetClearRefund uint64 = 19800 // Once per SSTORE operation for resetting to the original zero value - SstoreSentryGasEIP2200 uint64 = 2300 // Minimum gas required to be present for an SSTORE call, not consumed - SstoreNoopGasEIP2200 uint64 = 800 // Once per SSTORE operation if the value doesn't change. - SstoreDirtyGasEIP2200 uint64 = 800 // Once per SSTORE operation if a dirty value is changed. - SstoreInitGasEIP2200 uint64 = 20000 // Once per SSTORE operation from clean zero to non-zero - SstoreInitRefundEIP2200 uint64 = 19200 // Once per SSTORE operation for resetting to the original zero value - SstoreCleanGasEIP2200 uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else - SstoreCleanRefundEIP2200 uint64 = 4200 // Once per SSTORE operation for resetting to the original non-zero value - SstoreClearRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot + SstoreSentryGasEIP2200 uint64 = 2300 // Minimum gas required to be present for an SSTORE call, not consumed + SstoreSetGasEIP2200 uint64 = 20000 // Once per SSTORE operation from clean zero to non-zero + SstoreResetGasEIP2200 uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else + SstoreClearsScheduleRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot Create2Gas uint64 = 32000 // Once per CREATE2 operation SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation. From 6077ecb19531cc06159dcef1a2f35f54106844d1 Mon Sep 17 00:00:00 2001 From: aaronbuchwald Date: Fri, 30 Apr 2021 06:49:13 -0400 Subject: [PATCH 04/31] core/vm: replace repeated string with variable in tests (#22774) --- core/vm/instructions_test.go | 69 ++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 13dfa960d1ec..35f3865bf54a 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -41,6 +41,7 @@ type twoOperandParams struct { y string } +var alphabetSoup = "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" var commonParams []*twoOperandParams var twoOpMethods map[string]executionFunc @@ -354,8 +355,8 @@ func BenchmarkOpSub256(b *testing.B) { } func BenchmarkOpMul(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opMul, x, y) } @@ -386,64 +387,64 @@ func BenchmarkOpSdiv(b *testing.B) { } func BenchmarkOpMod(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opMod, x, y) } func BenchmarkOpSmod(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opSmod, x, y) } func BenchmarkOpExp(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opExp, x, y) } func BenchmarkOpSignExtend(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opSignExtend, x, y) } func BenchmarkOpLt(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opLt, x, y) } func BenchmarkOpGt(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opGt, x, y) } func BenchmarkOpSlt(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opSlt, x, y) } func BenchmarkOpSgt(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opSgt, x, y) } func BenchmarkOpEq(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opEq, x, y) } @@ -453,45 +454,45 @@ func BenchmarkOpEq2(b *testing.B) { opBenchmark(b, opEq, x, y) } func BenchmarkOpAnd(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opAnd, x, y) } func BenchmarkOpOr(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opOr, x, y) } func BenchmarkOpXor(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opXor, x, y) } func BenchmarkOpByte(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup opBenchmark(b, opByte, x, y) } func BenchmarkOpAddmod(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - z := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup + z := alphabetSoup opBenchmark(b, opAddmod, x, y, z) } func BenchmarkOpMulmod(b *testing.B) { - x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" - z := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" + x := alphabetSoup + y := alphabetSoup + z := alphabetSoup opBenchmark(b, opMulmod, x, y, z) } From 9c8d228dda156a10ef6e3b0eb60bf4ce5bc111ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Sat, 1 May 2021 13:19:24 +0200 Subject: [PATCH 05/31] core/vm: clean up contract creation error handling (#22766) Do not keep separate flag for "max code size exceeded" case, but instead assign appropriate error for it sooner. --- core/vm/evm.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index e8e5c1ca9419..dfed7f7e226e 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -423,13 +423,16 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, ret, err := run(evm, contract, nil, false) - // check whether the max code size has been exceeded - maxCodeSizeExceeded := evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize + // Check whether the max code size has been exceeded, assign err if the case. + if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize { + err = ErrMaxCodeSizeExceeded + } + // if the contract creation ran successfully and no errors were returned // calculate the gas required to store the code. If the code could not // be stored due to not enough gas set an error and let it be handled // by the error checking condition below. - if err == nil && !maxCodeSizeExceeded { + if err == nil { createDataGas := uint64(len(ret)) * params.CreateDataGas if contract.UseGas(createDataGas) { evm.StateDB.SetCode(address, ret) @@ -441,21 +444,17 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // 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. - if maxCodeSizeExceeded || (err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas)) { + if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { contract.UseGas(contract.Gas) } } - // Assign err if contract code size exceeds the max while the err is still empty. - if maxCodeSizeExceeded && err == nil { - err = ErrMaxCodeSizeExceeded - } + if evm.vmConfig.Debug && evm.depth == 0 { evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) } return ret, address, contract.Gas, err - } // Create creates a new contract using code as deployment code. From 7c6908b959fc670ff38af6a35cda3085cbbba44e Mon Sep 17 00:00:00 2001 From: aaronbuchwald Date: Mon, 3 May 2021 04:58:00 -0400 Subject: [PATCH 06/31] core/vm: fix interpreter comments (#22797) * Fix interpreter comment * Fix comment --- core/vm/interpreter.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index aa70d7c1020e..abf2178984db 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -142,7 +142,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( defer func() { in.evm.depth-- }() // Make sure the readOnly is only set if we aren't in readOnly yet. - // This makes also sure that the readOnly flag isn't removed for child calls. + // This also makes sure that the readOnly flag isn't removed for child calls. if readOnly && !in.readOnly { in.readOnly = true defer func() { in.readOnly = false }() @@ -218,7 +218,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } else if sLen > operation.maxStack { return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} } - // If the operation is valid, enforce and write restrictions + // If the operation is valid, enforce write restrictions if in.readOnly && in.evm.chainRules.IsByzantium { // If the interpreter is operating in readonly mode, make sure no // state-modifying operation is performed. The 3rd stack item From 21bbe5f568ee86a5688a378734a771b97da7a476 Mon Sep 17 00:00:00 2001 From: Evgeny Danilenko <6655321@bk.ru> Date: Thu, 6 May 2021 11:46:27 +0300 Subject: [PATCH 07/31] core/vm: avoid duplicate log in json logger (#22825) --- core/vm/logger_json.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go index 593626eda2e0..4fbd4fd75f12 100644 --- a/core/vm/logger_json.go +++ b/core/vm/logger_json.go @@ -85,8 +85,9 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, Time time.Duration `json:"time"` Err string `json:"error,omitempty"` } + var errMsg string if err != nil { - return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, err.Error()}) + errMsg = err.Error() } - return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, ""}) + return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, errMsg}) } From 7a55b9f7882abe14b11132c72f8bfc916c02f0d1 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Thu, 22 Feb 2024 19:53:37 +0800 Subject: [PATCH 08/31] core/state: remove unused methods ReturnGas (#23092) --- core/state/state_object.go | 3 --- core/vm/logger_test.go | 1 - eth/tracers/tracer_test.go | 1 - 3 files changed, 5 deletions(-) diff --git a/core/state/state_object.go b/core/state/state_object.go index 53ea774d6194..6d50461f1587 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -306,9 +306,6 @@ func (self *stateObject) setBalance(amount *big.Int) { } } -// Return the gas back to the origin. Used by the Virtual machine or Closures -func (c *stateObject) ReturnGas(gas *big.Int) {} - func (self *stateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *stateObject { stateObject := newObject(db, self.address, self.data, onDirty) if self.trie != nil { diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index 9ee02b97c028..f6c15d9cbb0f 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -30,7 +30,6 @@ type dummyContractRef struct { calledForEach bool } -func (dummyContractRef) ReturnGas(*big.Int) {} func (dummyContractRef) Address() common.Address { return common.Address{} } func (dummyContractRef) Value() *big.Int { return new(big.Int) } func (dummyContractRef) SetCode(common.Hash, []byte) {} diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go index a7266c178880..577fd1e576c3 100644 --- a/eth/tracers/tracer_test.go +++ b/eth/tracers/tracer_test.go @@ -40,7 +40,6 @@ func (account) SetBalance(*big.Int) {} func (account) SetNonce(uint64) {} func (account) Balance() *big.Int { return nil } func (account) Address() common.Address { return common.Address{} } -func (account) ReturnGas(*big.Int) {} func (account) SetCode(common.Hash, []byte) {} func (account) ForEachStorage(cb func(key, value common.Hash) bool) {} From f05fa009e28dd9f7b8e32d114f9993b9543a7674 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 24 Aug 2021 13:57:05 +0200 Subject: [PATCH 09/31] core/vm: fix typo in comment (#23450) --- core/vm/gas_table.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index f25ba9e4cdfb..c2f8e385c7c6 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -61,7 +61,7 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) { // as argument: // CALLDATACOPY (stack position 2) // CODECOPY (stack position 2) -// EXTCODECOPY (stack poition 3) +// EXTCODECOPY (stack position 3) // RETURNDATACOPY (stack position 2) func memoryCopierGas(stackpos int) gasFunc { return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { From dc8cdf3e62b23dfa520743415dbda88f5c3b8043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 30 Aug 2021 14:13:06 +0200 Subject: [PATCH 10/31] core/vm: rework jumpdest analysis benchmarks (#23499) * core/vm: rework jumpdest analysis benchmarks For BenchmarkJumpdestOpAnalysis use fixed code size of ~1.2MB and classic benchmark loop. * core/vm: clear bitvec in jumpdest analysis benchmark --- core/vm/analysis_test.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/core/vm/analysis_test.go b/core/vm/analysis_test.go index 99e4e386b404..b68794736f61 100644 --- a/core/vm/analysis_test.go +++ b/core/vm/analysis_test.go @@ -55,9 +55,12 @@ func TestJumpDestAnalysis(t *testing.T) { } } +const analysisCodeSize = 1200 * 1024 + func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) { // 1.4 ms - code := make([]byte, 1200000) + code := make([]byte, analysisCodeSize) + bench.SetBytes(analysisCodeSize) bench.ResetTimer() for i := 0; i < bench.N; i++ { codeBitmap(code) @@ -66,7 +69,8 @@ func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) { } func BenchmarkJumpdestHashing_1200k(bench *testing.B) { // 4 ms - code := make([]byte, 1200000) + code := make([]byte, analysisCodeSize) + bench.SetBytes(analysisCodeSize) bench.ResetTimer() for i := 0; i < bench.N; i++ { crypto.Keccak256Hash(code) @@ -77,13 +81,19 @@ func BenchmarkJumpdestHashing_1200k(bench *testing.B) { func BenchmarkJumpdestOpAnalysis(bench *testing.B) { var op OpCode bencher := func(b *testing.B) { - code := make([]byte, 32*b.N) + code := make([]byte, analysisCodeSize) + b.SetBytes(analysisCodeSize) for i := range code { code[i] = byte(op) } bits := make(bitvec, len(code)/8+1+4) b.ResetTimer() - codeBitmapInternal(code, bits) + for i := 0; i < b.N; i++ { + for j := range bits { + bits[j] = 0 + } + codeBitmapInternal(code, bits) + } } for op = PUSH1; op <= PUSH32; op++ { bench.Run(op.String(), bencher) From f9e14af6fd285956bc15637e4d2b723cf3b56cc9 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 18 Nov 2021 09:50:52 +0100 Subject: [PATCH 11/31] core/vm: don't use iota for opcode definitions (#23928) --- core/vm/opcodes.go | 123 ++++++++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 57 deletions(-) diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 322e01d17c99..0376e1f27ec3 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -39,68 +39,68 @@ func (op OpCode) IsStaticJump() bool { // 0x0 range - arithmetic ops. const ( - STOP OpCode = iota - ADD - MUL - SUB - DIV - SDIV - MOD - SMOD - ADDMOD - MULMOD - EXP - SIGNEXTEND + STOP OpCode = 0x0 + ADD OpCode = 0x1 + MUL OpCode = 0x2 + SUB OpCode = 0x3 + DIV OpCode = 0x4 + SDIV OpCode = 0x5 + MOD OpCode = 0x6 + SMOD OpCode = 0x7 + ADDMOD OpCode = 0x8 + MULMOD OpCode = 0x9 + EXP OpCode = 0xa + SIGNEXTEND OpCode = 0xb ) // 0x10 range - comparison ops. const ( - LT OpCode = iota + 0x10 - GT - SLT - SGT - EQ - ISZERO - AND - OR - XOR - NOT - BYTE - SHL - SHR - SAR + LT OpCode = 0x10 + GT OpCode = 0x11 + SLT OpCode = 0x12 + SGT OpCode = 0x13 + EQ OpCode = 0x14 + ISZERO OpCode = 0x15 + AND OpCode = 0x16 + OR OpCode = 0x17 + XOR OpCode = 0x18 + NOT OpCode = 0x19 + BYTE OpCode = 0x1a + SHL OpCode = 0x1b + SHR OpCode = 0x1c + SAR OpCode = 0x1d SHA3 OpCode = 0x20 ) // 0x30 range - closure state. const ( - ADDRESS OpCode = 0x30 + iota - BALANCE - ORIGIN - CALLER - CALLVALUE - CALLDATALOAD - CALLDATASIZE - CALLDATACOPY - CODESIZE - CODECOPY - GASPRICE - EXTCODESIZE - EXTCODECOPY - RETURNDATASIZE - RETURNDATACOPY - EXTCODEHASH + ADDRESS OpCode = 0x30 + BALANCE OpCode = 0x31 + ORIGIN OpCode = 0x32 + CALLER OpCode = 0x33 + CALLVALUE OpCode = 0x34 + CALLDATALOAD OpCode = 0x35 + CALLDATASIZE OpCode = 0x36 + CALLDATACOPY OpCode = 0x37 + CODESIZE OpCode = 0x38 + CODECOPY OpCode = 0x39 + GASPRICE OpCode = 0x3a + EXTCODESIZE OpCode = 0x3b + EXTCODECOPY OpCode = 0x3c + RETURNDATASIZE OpCode = 0x3d + RETURNDATACOPY OpCode = 0x3e + EXTCODEHASH OpCode = 0x3f ) // 0x40 range - block operations. const ( - BLOCKHASH OpCode = 0x40 + iota - COINBASE - TIMESTAMP - NUMBER - DIFFICULTY - GASLIMIT + BLOCKHASH OpCode = 0x40 + COINBASE OpCode = 0x41 + TIMESTAMP OpCode = 0x42 + NUMBER OpCode = 0x43 + DIFFICULTY OpCode = 0x44 + GASLIMIT OpCode = 0x45 CHAINID OpCode = 0x46 SELFBALANCE OpCode = 0x47 ) @@ -121,7 +121,7 @@ const ( JUMPDEST ) -// 0x60 range. +// 0x60 range - pushes. const ( PUSH1 OpCode = 0x60 + iota PUSH2 @@ -155,7 +155,11 @@ const ( PUSH30 PUSH31 PUSH32 - DUP1 +) + +// 0x80 range - dups. +const ( + DUP1 = 0x80 + iota DUP2 DUP3 DUP4 @@ -171,7 +175,11 @@ const ( DUP14 DUP15 DUP16 - SWAP1 +) + +// 0x90 range - swaps. +const ( + SWAP1 = 0x90 + iota SWAP2 SWAP3 SWAP4 @@ -207,12 +215,13 @@ const ( // 0xf0 range - closures. const ( - CREATE OpCode = 0xf0 + iota - CALL - CALLCODE - RETURN - DELEGATECALL - CREATE2 + CREATE OpCode = 0xf0 + CALL OpCode = 0xf1 + CALLCODE OpCode = 0xf2 + RETURN OpCode = 0xf3 + DELEGATECALL OpCode = 0xf4 + CREATE2 OpCode = 0xf5 + STATICCALL OpCode = 0xfa REVERT OpCode = 0xfd SELFDESTRUCT OpCode = 0xff From 3d3cc6c6d74323a6f5f7df1f9fad1d157df8932a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 24 Nov 2021 16:02:12 +0100 Subject: [PATCH 12/31] core/vm: use proper JumpTable type (#23967) --- core/vm/interpreter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index abf2178984db..2571429b2972 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -32,7 +32,7 @@ type Config struct { NoRecursion bool // Disables call, callcode, delegate call and create EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages - JumpTable [256]*operation // EVM instruction table, automatically populated if unset + JumpTable JumpTable // EVM instruction table, automatically populated if unset EWASMInterpreter string // External EWASM interpreter options EVMInterpreter string // External EVM interpreter options From f7c6b1abd488c3bbe7bc5c085bffe05ee8b64aaa Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 25 Nov 2021 09:37:47 +0100 Subject: [PATCH 13/31] core/vm, core/state/snapshot: remove unused code (#23956) * core/state/snapshot: remove wiper functionality * core/vm: remove unused 'unofficial' opcodes --- core/vm/opcodes.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 0376e1f27ec3..d6d2a9bcd55e 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -206,13 +206,6 @@ const ( LOG4 ) -// unofficial opcodes used for parsing. -const ( - PUSH OpCode = 0xb0 + iota - DUP - SWAP -) - // 0xf0 range - closures. const ( CREATE OpCode = 0xf0 @@ -389,10 +382,6 @@ var opCodeToString = map[OpCode]string{ STATICCALL: "STATICCALL", REVERT: "REVERT", SELFDESTRUCT: "SELFDESTRUCT", - - PUSH: "PUSH", - DUP: "DUP", - SWAP: "SWAP", } func (op OpCode) String() string { From 6415934da9843f87c03cf7b5bee78e6435c1c364 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Thu, 25 Nov 2021 20:10:01 +0100 Subject: [PATCH 14/31] core/vm: simplify op lookup in contract (#23974) --- core/vm/contract.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/core/vm/contract.go b/core/vm/contract.go index 5378267dad03..2be61b51d0ce 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -132,16 +132,11 @@ func (c *Contract) AsDelegate() *Contract { // GetOp returns the n'th element in the contract's byte array func (c *Contract) GetOp(n uint64) OpCode { - return OpCode(c.GetByte(n)) -} - -// GetByte returns the n'th byte in the contract's byte array -func (c *Contract) GetByte(n uint64) byte { if n < uint64(len(c.Code)) { - return c.Code[n] + return OpCode(c.Code[n]) } - return 0 + return STOP } // Caller returns the caller of the contract. From 823ec33484165c2ff0247e23607b10b545d29057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 29 Nov 2021 14:46:24 +0100 Subject: [PATCH 15/31] core/vm: simplify error handling in interpreter loop (#23952) * core/vm: break loop on any error * core/vm: move ErrExecutionReverted to opRevert() * core/vm: use "stop token" to stop the loop * core/vm: unconditionally pc++ in the loop * core/vm: set return data in instruction impls --- core/vm/errors.go | 4 ++++ core/vm/instructions.go | 23 +++++++++++++++-------- core/vm/interpreter.go | 25 +++++++++---------------- core/vm/jump_table.go | 19 +------------------ 4 files changed, 29 insertions(+), 42 deletions(-) diff --git a/core/vm/errors.go b/core/vm/errors.go index c813aa36af36..236e22568b58 100644 --- a/core/vm/errors.go +++ b/core/vm/errors.go @@ -34,6 +34,10 @@ var ( ErrWriteProtection = errors.New("write protection") ErrReturnDataOutOfBounds = errors.New("return data out of bounds") ErrGasUintOverflow = errors.New("gas uint64 overflow") + + // errStopToken is an internal token indicating interpreter loop termination, + // never returned to outside callers. + errStopToken = errors.New("stop token") ) // ErrStackUnderflow wraps an evm error when the items on the stack less diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 92b3b9ae88c0..987fdcb177d4 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -526,7 +526,7 @@ func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by if !callContext.contract.validJumpdest(&pos) { return nil, ErrInvalidJump } - *pc = pos.Uint64() + *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop return nil, nil } @@ -536,9 +536,7 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]b if !callContext.contract.validJumpdest(&pos) { return nil, ErrInvalidJump } - *pc = pos.Uint64() - } else { - *pc++ + *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop } return nil, nil } @@ -592,8 +590,10 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] callContext.contract.Gas += returnGas if suberr == ErrExecutionReverted { + interpreter.returnData = res // set REVERT data to return data buffer return res, nil } + interpreter.returnData = nil // clear dirty return data buffer return nil, nil } @@ -623,8 +623,10 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ callContext.contract.Gas += returnGas if suberr == ErrExecutionReverted { + interpreter.returnData = res // set REVERT data to return data buffer return res, nil } + interpreter.returnData = nil // clear dirty return data buffer return nil, nil } @@ -656,6 +658,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by } callContext.contract.Gas += returnGas + interpreter.returnData = ret return ret, nil } @@ -687,6 +690,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ( } callContext.contract.Gas += returnGas + interpreter.returnData = ret return ret, nil } @@ -715,6 +719,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCt } callContext.contract.Gas += returnGas + interpreter.returnData = ret return ret, nil } @@ -743,6 +748,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) } callContext.contract.Gas += returnGas + interpreter.returnData = ret return ret, nil } @@ -750,18 +756,19 @@ func opReturn(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] offset, size := callContext.stack.pop(), callContext.stack.pop() ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) - return ret, nil + return ret, errStopToken } func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { offset, size := callContext.stack.pop(), callContext.stack.pop() ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) - return ret, nil + interpreter.returnData = ret + return ret, ErrExecutionReverted } func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { - return nil, nil + return nil, errStopToken } func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { @@ -769,7 +776,7 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ balance := interpreter.evm.StateDB.GetBalance(callContext.contract.Address()) interpreter.evm.StateDB.AddBalance(common.Address(beneficiary.Bytes20()), balance) interpreter.evm.StateDB.Suicide(callContext.contract.Address()) - return nil, nil + return nil, errStopToken } // following functions are used by the instruction jump table diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 2571429b2972..5d094c4eea40 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -273,24 +273,17 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // execute the operation res, err = operation.execute(&pc, in, callContext) - // if the operation clears the return data (e.g. it has returning data) - // set the last return to the result of the operation. - if operation.returns { - in.returnData = res - } - switch { - case err != nil: - return nil, err - case operation.reverts: - log.Debug("ErrExecutionReverted", "pc", pc, "reverts", operation.reverts, "err", err) - return res, ErrExecutionReverted - case operation.halts: - return res, nil - case !operation.jumps: - pc++ + if err != nil { + break } + pc++ } - return nil, nil + + if err == errStopToken { + err = nil // clear stop token error + } + + return res, err } // CanRun tells if the contract, passed as an argument, can be diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 7580544ba2ca..f56dc89d5655 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -41,11 +41,7 @@ type operation struct { // memorySize returns the memory size required for the operation memorySize memorySizeFunc - halts bool // indicates whether the operation should halt further execution - jumps bool // indicates whether the program counter should not increment - writes bool // determines whether this a state modifying operation - reverts bool // determines whether the operation reverts state (implicitly halts) - returns bool // determines whether the operations sets the return data content + writes bool // determines whether this a state modifying operation } var ( @@ -109,7 +105,6 @@ func newConstantinopleInstructionSet() JumpTable { maxStack: maxStack(4, 1), memorySize: memoryCreate2, writes: true, - returns: true, } return instructionSet } @@ -125,7 +120,6 @@ func newByzantiumInstructionSet() JumpTable { minStack: minStack(6, 1), maxStack: maxStack(6, 1), memorySize: memoryStaticCall, - returns: true, } instructionSet[RETURNDATASIZE] = &operation{ execute: opReturnDataSize, @@ -147,8 +141,6 @@ func newByzantiumInstructionSet() JumpTable { minStack: minStack(2, 0), maxStack: maxStack(2, 0), memorySize: memoryRevert, - reverts: true, - returns: true, } return instructionSet } @@ -185,7 +177,6 @@ func newHomesteadInstructionSet() JumpTable { minStack: minStack(6, 1), maxStack: maxStack(6, 1), memorySize: memoryDelegateCall, - returns: true, } return instructionSet } @@ -199,7 +190,6 @@ func newFrontierInstructionSet() JumpTable { constantGas: 0, minStack: minStack(0, 0), maxStack: maxStack(0, 0), - halts: true, }, ADD: { execute: opAdd, @@ -509,14 +499,12 @@ func newFrontierInstructionSet() JumpTable { constantGas: GasMidStep, minStack: minStack(1, 0), maxStack: maxStack(1, 0), - jumps: true, }, JUMPI: { execute: opJumpi, constantGas: GasSlowStep, minStack: minStack(2, 0), maxStack: maxStack(2, 0), - jumps: true, }, PC: { execute: opPc, @@ -974,7 +962,6 @@ func newFrontierInstructionSet() JumpTable { maxStack: maxStack(3, 1), memorySize: memoryCreate, writes: true, - returns: true, }, CALL: { execute: opCall, @@ -983,7 +970,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(7, 1), maxStack: maxStack(7, 1), memorySize: memoryCall, - returns: true, }, CALLCODE: { execute: opCallCode, @@ -992,7 +978,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(7, 1), maxStack: maxStack(7, 1), memorySize: memoryCall, - returns: true, }, RETURN: { execute: opReturn, @@ -1000,14 +985,12 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 0), maxStack: maxStack(2, 0), memorySize: memoryReturn, - halts: true, }, SELFDESTRUCT: { execute: opSuicide, dynamicGas: gasSelfdestruct, minStack: minStack(1, 0), maxStack: maxStack(1, 0), - halts: true, writes: true, }, } From c097e565fd8cb9a03b3deb0ed1779bf27424e14a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 30 Nov 2021 09:34:34 +0000 Subject: [PATCH 16/31] core/vm: rename SHA3 instruction to KECCAK256 (#23976) This was proposed in 2016, Solidity uses this since 2017, and evmone and other VMs use the keccak256 name. This brings geth in line with those. --- core/vm/evm.go | 2 +- core/vm/gas_table.go | 6 +++--- core/vm/instructions.go | 2 +- core/vm/instructions_test.go | 4 ++-- core/vm/jump_table.go | 10 +++++----- core/vm/memory_table.go | 2 +- core/vm/opcodes.go | 6 +++--- params/protocol_params.go | 4 ++-- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index dfed7f7e226e..413c74874ec9 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -465,7 +465,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I // Create2 creates a new contract using code as deployment code. // -// The different between Create2 with Create is Create2 uses sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code))[12:] +// 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) { codeAndHash := &codeAndHash{code: code} diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index c2f8e385c7c6..45589157bb00 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -248,7 +248,7 @@ func makeGasLog(n uint64) gasFunc { } } -func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { +func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { gas, err := memoryGasCost(mem, memorySize) if err != nil { return 0, err @@ -257,7 +257,7 @@ func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize if overflow { return 0, ErrGasUintOverflow } - if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow { + if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow { return 0, ErrGasUintOverflow } if gas, overflow = math.SafeAdd(gas, wordGas); overflow { @@ -291,7 +291,7 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS if overflow { return 0, ErrGasUintOverflow } - if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow { + if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow { return 0, ErrGasUintOverflow } if gas, overflow = math.SafeAdd(gas, wordGas); overflow { diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 987fdcb177d4..4f181a6e6950 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -231,7 +231,7 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt return nil, nil } -func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opKeccak256(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { offset, size := callContext.stack.pop(), callContext.stack.peek() data := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 35f3865bf54a..88107a69c40d 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -565,7 +565,7 @@ func BenchmarkOpMstore(bench *testing.B) { } } -func BenchmarkOpSHA3(bench *testing.B) { +func BenchmarkOpKeccak256(bench *testing.B) { var ( env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() @@ -580,7 +580,7 @@ func BenchmarkOpSHA3(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { stack.pushN(*uint256.NewInt(32), *start) - opSha3(&pc, evmInterpreter, &callCtx{mem, stack, nil}) + opKeccak256(&pc, evmInterpreter, &callCtx{mem, stack, nil}) } } diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index f56dc89d5655..a5b5445d804a 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -323,13 +323,13 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 1), maxStack: maxStack(2, 1), }, - SHA3: { - execute: opSha3, - constantGas: params.Sha3Gas, - dynamicGas: gasSha3, + KECCAK256: { + execute: opKeccak256, + constantGas: params.Keccak256Gas, + dynamicGas: gasKeccak256, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - memorySize: memorySha3, + memorySize: memoryKeccak256, }, ADDRESS: { execute: opAddress, diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go index 4fcb41442c4e..e35ca84e0efa 100644 --- a/core/vm/memory_table.go +++ b/core/vm/memory_table.go @@ -16,7 +16,7 @@ package vm -func memorySha3(stack *Stack) (uint64, bool) { +func memoryKeccak256(stack *Stack) (uint64, bool) { return calcMemSize64(stack.Back(0), stack.Back(1)) } diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index d6d2a9bcd55e..224c8789210b 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -70,7 +70,7 @@ const ( SHR OpCode = 0x1c SAR OpCode = 0x1d - SHA3 OpCode = 0x20 + KECCAK256 OpCode = 0x20 ) // 0x30 range - closure state. @@ -253,7 +253,7 @@ var opCodeToString = map[OpCode]string{ MULMOD: "MULMOD", // 0x20 range - crypto. - SHA3: "SHA3", + KECCAK256: "KECCAK256", // 0x30 range - closure state. ADDRESS: "ADDRESS", @@ -420,7 +420,7 @@ var stringToOp = map[string]OpCode{ "SAR": SAR, "ADDMOD": ADDMOD, "MULMOD": MULMOD, - "SHA3": SHA3, + "KECCAK256": KECCAK256, "ADDRESS": ADDRESS, "BALANCE": BALANCE, "ORIGIN": ORIGIN, diff --git a/params/protocol_params.go b/params/protocol_params.go index d9272981d63e..f15530649750 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -41,8 +41,8 @@ const ( LogDataGas uint64 = 8 // Per byte in a LOG* operation's data. CallStipend uint64 = 2300 // Free gas given at beginning of call. - Sha3Gas uint64 = 30 // Once per SHA3 operation. - Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data. + Keccak256Gas uint64 = 30 // Once per KECCAK256 operation. + Keccak256WordGas uint64 = 6 // Once per word of the KECCAK256 operation's data. SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero. SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change. SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero. From 002be52ae9622ec7fa33be50cb70ccd4ee940cfc Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Tue, 30 Nov 2021 13:21:40 +0100 Subject: [PATCH 17/31] core/vm: don't copy JumpTable when no EIP mods are needed (#23977) --- core/vm/interpreter.go | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 5d094c4eea40..582957262b9f 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -32,7 +32,7 @@ type Config struct { NoRecursion bool // Disables call, callcode, delegate call and create EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages - JumpTable JumpTable // EVM instruction table, automatically populated if unset + JumpTable *JumpTable // EVM instruction table, automatically populated if unset EWASMInterpreter string // External EWASM interpreter options EVMInterpreter string // External EVM interpreter options @@ -92,35 +92,33 @@ type EVMInterpreter struct { // NewEVMInterpreter returns a new instance of the Interpreter. func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { - // We use the STOP instruction whether to see - // the jump table was initialised. If it was not - // we'll set the default jump table. - if cfg.JumpTable[STOP] == nil { - var jt JumpTable + // If jump table was not initialised we set the default one. + if cfg.JumpTable == nil { switch { case evm.chainRules.IsIstanbul: - jt = istanbulInstructionSet + cfg.JumpTable = &istanbulInstructionSet case evm.chainRules.IsConstantinople: - jt = constantinopleInstructionSet + cfg.JumpTable = &constantinopleInstructionSet case evm.chainRules.IsByzantium: - jt = byzantiumInstructionSet + cfg.JumpTable = &byzantiumInstructionSet case evm.chainRules.IsEIP158: - jt = spuriousDragonInstructionSet + cfg.JumpTable = &spuriousDragonInstructionSet case evm.chainRules.IsEIP150: - jt = tangerineWhistleInstructionSet + cfg.JumpTable = &tangerineWhistleInstructionSet case evm.chainRules.IsHomestead: - jt = homesteadInstructionSet + cfg.JumpTable = &homesteadInstructionSet default: - jt = frontierInstructionSet + cfg.JumpTable = &frontierInstructionSet } for i, eip := range cfg.ExtraEips { - if err := EnableEIP(eip, &jt); err != nil { + copy := *cfg.JumpTable + if err := EnableEIP(eip, ©); err != nil { // Disable it, so caller can check if it's activated or not cfg.ExtraEips = append(cfg.ExtraEips[:i], cfg.ExtraEips[i+1:]...) log.Error("EIP activation failed", "eip", eip, "error", err) } + cfg.JumpTable = © } - cfg.JumpTable = jt } return &EVMInterpreter{ From bfbb6783098c69defb95b78b28ec22239a1fdcf2 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 1 Dec 2021 09:21:21 +0000 Subject: [PATCH 18/31] core/vm: move interpreter.ReadOnly check into the opcode implementations (#23970) * core/vm: Move interpreter.ReadOnly check into the opcode implementations Also remove the same check from the interpreter inner loop. * core/vm: Remove obsolete operation.writes flag * core/vm: Capture fault states in logger Co-authored-by: Martin Holst Swende * core/vm: Remove panic added for testing Co-authored-by: Martin Holst Swende --- core/vm/instructions.go | 18 ++++++++++++++++++ core/vm/interpreter.go | 11 ----------- core/vm/jump_table.go | 11 ----------- core/vm/logger_json.go | 2 +- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 4f181a6e6950..ccc3e383b298 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -514,6 +514,9 @@ func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]b } func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + if interpreter.readOnly { + return nil, ErrWriteProtection + } loc := callContext.stack.pop() val := callContext.stack.pop() interpreter.evm.StateDB.SetState(callContext.contract.Address(), @@ -561,6 +564,9 @@ func opGas(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt } func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + if interpreter.readOnly { + return nil, ErrWriteProtection + } var ( value = callContext.stack.pop() offset, size = callContext.stack.pop(), callContext.stack.pop() @@ -598,6 +604,9 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] } func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + if interpreter.readOnly { + return nil, ErrWriteProtection + } var ( endowment = callContext.stack.pop() offset, size = callContext.stack.pop(), callContext.stack.pop() @@ -642,6 +651,9 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by // Get the arguments from the memory. args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) + if interpreter.readOnly && !value.IsZero() { + return nil, ErrWriteProtection + } if !value.IsZero() { gas += params.CallStipend } @@ -772,6 +784,9 @@ func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by } func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + if interpreter.readOnly { + return nil, ErrWriteProtection + } beneficiary := callContext.stack.pop() balance := interpreter.evm.StateDB.GetBalance(callContext.contract.Address()) interpreter.evm.StateDB.AddBalance(common.Address(beneficiary.Bytes20()), balance) @@ -784,6 +799,9 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ // make log instruction function func makeLog(size int) executionFunc { return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + if interpreter.readOnly { + return nil, ErrWriteProtection + } topics := make([]common.Hash, size) stack := callContext.stack mStart, mSize := stack.pop(), stack.pop() diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 582957262b9f..eabf1dce19ef 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -216,17 +216,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } else if sLen > operation.maxStack { return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} } - // If the operation is valid, enforce write restrictions - if in.readOnly && in.evm.chainRules.IsByzantium { - // If the interpreter is operating in readonly mode, make sure no - // state-modifying operation is performed. The 3rd stack item - // for a call operation is the value. Transferring value from one - // account to the others means the state is modified and should also - // return with an error. - if operation.writes || (op == CALL && stack.Back(2).Sign() != 0) { - return nil, ErrWriteProtection - } - } // Static portion of gas cost = operation.constantGas // For tracing if !contract.UseGas(operation.constantGas) { diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index a5b5445d804a..c1b9fd8470b6 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -40,8 +40,6 @@ type operation struct { // memorySize returns the memory size required for the operation memorySize memorySizeFunc - - writes bool // determines whether this a state modifying operation } var ( @@ -104,7 +102,6 @@ func newConstantinopleInstructionSet() JumpTable { minStack: minStack(4, 1), maxStack: maxStack(4, 1), memorySize: memoryCreate2, - writes: true, } return instructionSet } @@ -492,7 +489,6 @@ func newFrontierInstructionSet() JumpTable { dynamicGas: gasSStore, minStack: minStack(2, 0), maxStack: maxStack(2, 0), - writes: true, }, JUMP: { execute: opJump, @@ -920,7 +916,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 0), maxStack: maxStack(2, 0), memorySize: memoryLog, - writes: true, }, LOG1: { execute: makeLog(1), @@ -928,7 +923,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(3, 0), maxStack: maxStack(3, 0), memorySize: memoryLog, - writes: true, }, LOG2: { execute: makeLog(2), @@ -936,7 +930,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(4, 0), maxStack: maxStack(4, 0), memorySize: memoryLog, - writes: true, }, LOG3: { execute: makeLog(3), @@ -944,7 +937,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(5, 0), maxStack: maxStack(5, 0), memorySize: memoryLog, - writes: true, }, LOG4: { execute: makeLog(4), @@ -952,7 +944,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(6, 0), maxStack: maxStack(6, 0), memorySize: memoryLog, - writes: true, }, CREATE: { execute: opCreate, @@ -961,7 +952,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(3, 1), maxStack: maxStack(3, 1), memorySize: memoryCreate, - writes: true, }, CALL: { execute: opCall, @@ -991,7 +981,6 @@ func newFrontierInstructionSet() JumpTable { dynamicGas: gasSelfdestruct, minStack: minStack(1, 0), maxStack: maxStack(1, 0), - writes: true, }, } } diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go index 4fbd4fd75f12..4a63120a3df4 100644 --- a/core/vm/logger_json.go +++ b/core/vm/logger_json.go @@ -74,7 +74,7 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint // CaptureFault outputs state information on the logger. func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error { - return nil + return l.CaptureState(env, pc, op, gas, cost, memory, stack, contract, depth, err) } // CaptureEnd is triggered at end of execution. From b1c03864e60a830f342fab9071997b3173afdc9b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 1 Dec 2021 09:33:29 +0000 Subject: [PATCH 19/31] core/vm: rename opSuicide to opSelfdestruct (#24022) The opcode was renamed in the codebase in 2017, but the functions were kept unchanged. --- core/vm/instructions.go | 2 +- core/vm/jump_table.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index ccc3e383b298..3a361eb0e8c4 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -783,7 +783,7 @@ func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by return nil, errStopToken } -func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { +func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { if interpreter.readOnly { return nil, ErrWriteProtection } diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index c1b9fd8470b6..1b6f6d1d4559 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -977,7 +977,7 @@ func newFrontierInstructionSet() JumpTable { memorySize: memoryReturn, }, SELFDESTRUCT: { - execute: opSuicide, + execute: opSelfdestruct, dynamicGas: gasSelfdestruct, minStack: minStack(1, 0), maxStack: maxStack(1, 0), From ae267d3da7cb4442bc6ed67129265716ab715431 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 3 Dec 2021 09:16:49 +0000 Subject: [PATCH 20/31] core/vm: remove stack.pushN (#24040) --- core/vm/instructions_test.go | 12 ++++++++---- core/vm/stack.go | 4 ---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 88107a69c40d..1f63d2b040a1 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -532,12 +532,14 @@ func TestOpMstore(t *testing.T) { mem.Resize(64) pc := uint64(0) v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" - stack.pushN(*new(uint256.Int).SetBytes(common.Hex2Bytes(v)), *new(uint256.Int)) + stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v))) + stack.push(new(uint256.Int)) opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil}) if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v { t.Fatalf("Mstore fail, got %v, expected %v", got, v) } - stack.pushN(*new(uint256.Int).SetUint64(0x1), *new(uint256.Int)) + stack.push(new(uint256.Int).SetUint64(0x1)) + stack.push(new(uint256.Int)) opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil}) if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" { t.Fatalf("Mstore failed to overwrite previous value") @@ -560,7 +562,8 @@ func BenchmarkOpMstore(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { - stack.pushN(*value, *memStart) + stack.push(value) + stack.push(memStart) opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil}) } } @@ -579,7 +582,8 @@ func BenchmarkOpKeccak256(bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { - stack.pushN(*uint256.NewInt(32), *start) + stack.push(uint256.NewInt(32)) + stack.push(start) opKeccak256(&pc, evmInterpreter, &callCtx{mem, stack, nil}) } } diff --git a/core/vm/stack.go b/core/vm/stack.go index 2367da1d7c97..cc6037f23b3c 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -42,10 +42,6 @@ func (st *Stack) push(d *uint256.Int) { // NOTE push limit (1024) is checked in baseCheck st.data = append(st.data, *d) } -func (st *Stack) pushN(ds ...uint256.Int) { - // FIXME: Is there a way to pass args by pointers. - st.data = append(st.data, ds...) -} func (st *Stack) pop() (ret uint256.Int) { ret = st.data[len(st.data)-1] From 7d3c783bb9db041447b2ce9ef60de722f62f7cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 3 Dec 2021 11:04:54 +0100 Subject: [PATCH 21/31] core/vm: fill gaps in jump table with opUndefined (#24031) --- core/vm/instructions.go | 4 ++++ core/vm/interpreter.go | 3 --- core/vm/jump_table.go | 11 ++++++++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 3a361eb0e8c4..5dcf90b77094 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -779,6 +779,10 @@ func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] return ret, ErrExecutionReverted } +func opUndefined(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + return nil, &ErrInvalidOpCode{opcode: OpCode(callContext.contract.Code[*pc])} +} + func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { return nil, errStopToken } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index eabf1dce19ef..51551678db5d 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -207,9 +207,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // enough stack items available to perform the operation. op = contract.GetOp(pc) operation := in.cfg.JumpTable[op] - if operation == nil { - return nil, &ErrInvalidOpCode{opcode: op} - } // Validate stack if sLen := stack.len(); sLen < operation.minStack { return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack} diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 1b6f6d1d4559..55ad78adc53f 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -181,7 +181,7 @@ func newHomesteadInstructionSet() JumpTable { // newFrontierInstructionSet returns the frontier instructions // that can be executed during the frontier phase. func newFrontierInstructionSet() JumpTable { - return JumpTable{ + tbl := JumpTable{ STOP: { execute: opStop, constantGas: 0, @@ -983,4 +983,13 @@ func newFrontierInstructionSet() JumpTable { maxStack: maxStack(1, 0), }, } + + // Fill all unassigned slots with opUndefined. + for i, entry := range tbl { + if entry == nil { + tbl[i] = &operation{execute: opUndefined, maxStack: maxStack(0, 0)} + } + } + + return tbl } From 4c27910a7706fa74b7cfef20e84f4d9c0647beeb Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Fri, 3 Dec 2021 11:10:26 +0100 Subject: [PATCH 22/31] core/vm: move interpreter interruption check to jump instructions (#24026) * core/vm: Remove interpreter loop interruption check * core/vm: Unit test for interpreter loop interruption * core/vm: Check for interpreter loop abort on every jump --- core/vm/instructions.go | 8 ++++++++ core/vm/interpreter.go | 6 ------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 5dcf90b77094..73173b903981 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -17,6 +17,8 @@ package vm import ( + "sync/atomic" + "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/params" @@ -525,6 +527,9 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] } func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + if atomic.LoadInt32(&interpreter.evm.abort) != 0 { + return nil, errStopToken + } pos := callContext.stack.pop() if !callContext.contract.validJumpdest(&pos) { return nil, ErrInvalidJump @@ -534,6 +539,9 @@ func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by } func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { + if atomic.LoadInt32(&interpreter.evm.abort) != 0 { + return nil, errStopToken + } pos, cond := callContext.stack.pop(), callContext.stack.pop() if !cond.IsZero() { if !callContext.contract.validJumpdest(&pos) { diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 51551678db5d..40a814bfd4f2 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -18,7 +18,6 @@ package vm import ( "hash" - "sync/atomic" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/math" @@ -192,12 +191,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during // the execution of one of the operations or until the done flag is set by the // parent context. - steps := 0 for { - steps++ - if steps%1000 == 0 && atomic.LoadInt32(&in.evm.abort) != 0 { - break - } if in.cfg.Debug { // Capture pre-execution values for tracing. logged, pcCopy, gasCopy = false, pc, contract.Gas From fbc1cc11ba3656cdd217ab2ffef532d93128b783 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 6 Dec 2021 09:55:50 +0100 Subject: [PATCH 23/31] core/vm: remove no-recursion option from config (24066) --- core/vm/evm.go | 16 ---------------- core/vm/interpreter.go | 1 - tests/vm_test_util.go | 3 +-- 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index 413c74874ec9..284ea6768c96 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -189,9 +189,6 @@ func (evm *EVM) Interpreter() Interpreter { // the necessary steps to create accounts and reverses the state in case of an // execution error or failed value transfer. func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { - if evm.vmConfig.NoRecursion && evm.depth > 0 { - return nil, gas, nil - } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth @@ -263,9 +260,6 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // CallCode differs from Call in the sense that it executes the given address' // code with the caller as context. func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { - if evm.vmConfig.NoRecursion && evm.depth > 0 { - return nil, gas, nil - } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth @@ -302,9 +296,6 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // DelegateCall differs from CallCode in the sense that it executes the given address' // code with the caller as context and the caller is set to the caller of the caller. func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { - if evm.vmConfig.NoRecursion && evm.depth > 0 { - return nil, gas, nil - } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth @@ -332,9 +323,6 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // Opcodes that attempt to perform such modifications will result in exceptions // instead of performing the modifications. func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { - if evm.vmConfig.NoRecursion && evm.depth > 0 { - return nil, gas, nil - } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth @@ -412,10 +400,6 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, contract := NewContract(caller, AccountRef(address), value, gas) contract.SetCodeOptionalHash(&address, codeAndHash) - if evm.vmConfig.NoRecursion && evm.depth > 0 { - return nil, address, gas, nil - } - if evm.vmConfig.Debug && evm.depth == 0 { evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value) } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 40a814bfd4f2..91bb8dcfaa25 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -28,7 +28,6 @@ import ( type Config struct { Debug bool // Enables debugging Tracer Tracer // Opcode logger - NoRecursion bool // Disables call, callcode, delegate call and create EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages JumpTable *JumpTable // EVM instruction table, automatically populated if unset diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index 509022bf5149..bd516a182721 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -20,13 +20,13 @@ import ( "bytes" "encoding/json" "fmt" - "github.com/XinFinOrg/XDPoSChain/core/rawdb" "math/big" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/hexutil" "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/crypto" @@ -143,7 +143,6 @@ func (t *VMTest) newEVM(statedb *state.StateDB, vmconfig vm.Config) *vm.EVM { Difficulty: t.json.Env.Difficulty, GasPrice: t.json.Exec.GasPrice, } - vmconfig.NoRecursion = true return vm.NewEVM(context, statedb, nil, params.MainnetChainConfig, vmconfig) } From 2ce30010bc3fa3b890a8b8ab673c77583cbc985b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 9 Dec 2021 12:55:06 +0000 Subject: [PATCH 24/31] core/vm: remove unused code (IsStaticJump) (#24085) --- core/vm/opcodes.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 224c8789210b..b39232b78abe 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -32,11 +32,6 @@ func (op OpCode) IsPush() bool { return false } -// IsStaticJump specifies if an opcode is JUMP. -func (op OpCode) IsStaticJump() bool { - return op == JUMP -} - // 0x0 range - arithmetic ops. const ( STOP OpCode = 0x0 From 6e9248629611ecea4e9d452bc8de3532caf74914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 17 Dec 2021 10:32:00 +0100 Subject: [PATCH 25/31] core/vm: reverse bit order in bytes of code bitmap (#24120) * core/vm: reverse bit order in bytes of code bitmap This bit order is more natural for bit manipulation operations and we can eliminate some small number of CPU instructions. * core/vm: drop lookup table --- core/vm/analysis.go | 32 +++++++++++++------------------- core/vm/analysis_test.go | 40 ++++++++++++++++++++++------------------ 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/core/vm/analysis.go b/core/vm/analysis.go index 449cded2a896..3733bab6a7c0 100644 --- a/core/vm/analysis.go +++ b/core/vm/analysis.go @@ -17,12 +17,12 @@ package vm const ( - set2BitsMask = uint16(0b1100_0000_0000_0000) - set3BitsMask = uint16(0b1110_0000_0000_0000) - set4BitsMask = uint16(0b1111_0000_0000_0000) - set5BitsMask = uint16(0b1111_1000_0000_0000) - set6BitsMask = uint16(0b1111_1100_0000_0000) - set7BitsMask = uint16(0b1111_1110_0000_0000) + set2BitsMask = uint16(0b11) + set3BitsMask = uint16(0b111) + set4BitsMask = uint16(0b1111) + set5BitsMask = uint16(0b1_1111) + set6BitsMask = uint16(0b11_1111) + set7BitsMask = uint16(0b111_1111) ) // bitvec is a bit vector which maps bytes in a program. @@ -30,32 +30,26 @@ const ( // it's data (i.e. argument of PUSHxx). type bitvec []byte -var lookup = [8]byte{ - 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1, -} - func (bits bitvec) set1(pos uint64) { - bits[pos/8] |= lookup[pos%8] + bits[pos/8] |= 1 << (pos % 8) } func (bits bitvec) setN(flag uint16, pos uint64) { - a := flag >> (pos % 8) - bits[pos/8] |= byte(a >> 8) - if b := byte(a); b != 0 { - // If the bit-setting affects the neighbouring byte, we can assign - no need to OR it, - // since it's the first write to that byte + a := flag << (pos % 8) + bits[pos/8] |= byte(a) + if b := byte(a >> 8); b != 0 { bits[pos/8+1] = b } } func (bits bitvec) set8(pos uint64) { - a := byte(0xFF >> (pos % 8)) + a := byte(0xFF << (pos % 8)) bits[pos/8] |= a bits[pos/8+1] = ^a } func (bits bitvec) set16(pos uint64) { - a := byte(0xFF >> (pos % 8)) + a := byte(0xFF << (pos % 8)) bits[pos/8] |= a bits[pos/8+1] = 0xFF bits[pos/8+2] = ^a @@ -63,7 +57,7 @@ func (bits bitvec) set16(pos uint64) { // codeSegment checks if the position is in a code segment. func (bits *bitvec) codeSegment(pos uint64) bool { - return ((*bits)[pos/8] & (0x80 >> (pos % 8))) == 0 + return (((*bits)[pos/8] >> (pos % 8)) & 1) == 0 } // codeBitmap collects data locations in code. diff --git a/core/vm/analysis_test.go b/core/vm/analysis_test.go index b68794736f61..20674bf919d3 100644 --- a/core/vm/analysis_test.go +++ b/core/vm/analysis_test.go @@ -17,6 +17,7 @@ package vm import ( + "math/bits" "testing" "github.com/XinFinOrg/XDPoSChain/crypto" @@ -28,24 +29,27 @@ func TestJumpDestAnalysis(t *testing.T) { exp byte which int }{ - {[]byte{byte(PUSH1), 0x01, 0x01, 0x01}, 0x40, 0}, - {[]byte{byte(PUSH1), byte(PUSH1), byte(PUSH1), byte(PUSH1)}, 0x50, 0}, - {[]byte{byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), 0x01, 0x01, 0x01}, 0x7F, 0}, - {[]byte{byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x80, 1}, - {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), byte(PUSH2), byte(PUSH2), 0x01, 0x01, 0x01}, 0x03, 0}, - {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), 0x01, 0x01, 0x01, 0x01, 0x01}, 0x00, 1}, - {[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x74, 0}, - {[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x00, 1}, - {[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x3F, 0}, - {[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0xC0, 1}, - {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x7F, 0}, - {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0xFF, 1}, - {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0x80, 2}, - {[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0x7f, 0}, - {[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0xA0, 1}, - {[]byte{byte(PUSH32)}, 0x7F, 0}, - {[]byte{byte(PUSH32)}, 0xFF, 1}, - {[]byte{byte(PUSH32)}, 0xFF, 2}, + {[]byte{byte(PUSH1), 0x01, 0x01, 0x01}, 0b0000_0010, 0}, + {[]byte{byte(PUSH1), byte(PUSH1), byte(PUSH1), byte(PUSH1)}, 0b0000_1010, 0}, + {[]byte{0x00, byte(PUSH1), 0x00, byte(PUSH1), 0x00, byte(PUSH1), 0x00, byte(PUSH1)}, 0b0101_0100, 0}, + {[]byte{byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), byte(PUSH8), 0x01, 0x01, 0x01}, bits.Reverse8(0x7F), 0}, + {[]byte{byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0001, 1}, + {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), byte(PUSH2), byte(PUSH2), 0x01, 0x01, 0x01}, 0b1100_0000, 0}, + {[]byte{0x01, 0x01, 0x01, 0x01, 0x01, byte(PUSH2), 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0000, 1}, + {[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0010_1110, 0}, + {[]byte{byte(PUSH3), 0x01, 0x01, 0x01, byte(PUSH1), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0000, 1}, + {[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b1111_1100, 0}, + {[]byte{0x01, byte(PUSH8), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0011, 1}, + {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b1111_1110, 0}, + {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b1111_1111, 1}, + {[]byte{byte(PUSH16), 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}, 0b0000_0001, 2}, + {[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0b1111_1110, 0}, + {[]byte{byte(PUSH8), 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, byte(PUSH1), 0x01}, 0b0000_0101, 1}, + {[]byte{byte(PUSH32)}, 0b1111_1110, 0}, + {[]byte{byte(PUSH32)}, 0b1111_1111, 1}, + {[]byte{byte(PUSH32)}, 0b1111_1111, 2}, + {[]byte{byte(PUSH32)}, 0b1111_1111, 3}, + {[]byte{byte(PUSH32)}, 0b0000_0001, 4}, } for i, test := range tests { ret := codeBitmap(test.code) From b022ba2c5ddae2b825b60ec017377afd76c22048 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Fri, 17 Dec 2021 13:44:05 +0100 Subject: [PATCH 26/31] core/vm: make INVALID a defined opcode (#24017) * core/vm: Define 0xfe opcode as INVALID * core/vm: Remove opInvalid as opUndefined handles it Co-authored-by: Alex Beregszaszi --- core/vm/opcodes.go | 3 +++ eth/tracers/testdata/call_tracer_inner_throw_outer_revert.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index b39232b78abe..b861a62d6de0 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -212,6 +212,7 @@ const ( STATICCALL OpCode = 0xfa REVERT OpCode = 0xfd + INVALID OpCode = 0xfe SELFDESTRUCT OpCode = 0xff ) @@ -376,6 +377,7 @@ var opCodeToString = map[OpCode]string{ CREATE2: "CREATE2", STATICCALL: "STATICCALL", REVERT: "REVERT", + INVALID: "INVALID", SELFDESTRUCT: "SELFDESTRUCT", } @@ -529,6 +531,7 @@ var stringToOp = map[string]OpCode{ "RETURN": RETURN, "CALLCODE": CALLCODE, "REVERT": REVERT, + "INVALID": INVALID, "SELFDESTRUCT": SELFDESTRUCT, } diff --git a/eth/tracers/testdata/call_tracer_inner_throw_outer_revert.json b/eth/tracers/testdata/call_tracer_inner_throw_outer_revert.json index 7627c8c23d68..ec2ceb426fda 100644 --- a/eth/tracers/testdata/call_tracer_inner_throw_outer_revert.json +++ b/eth/tracers/testdata/call_tracer_inner_throw_outer_revert.json @@ -59,7 +59,7 @@ "result": { "calls": [ { - "error": "invalid opcode: opcode 0xfe not defined", + "error": "invalid opcode: INVALID", "from": "0x33056b5dcac09a9b4becad0e1dcf92c19bd0af76", "gas": "0x75fe3", "gasUsed": "0x75fe3", From ac35f0ecec87eb15ffba104b413da5291a7f8909 Mon Sep 17 00:00:00 2001 From: s7v7nislands Date: Wed, 11 May 2022 13:03:35 +0800 Subject: [PATCH 27/31] core/vm: clean up some dead functions (#24851) --- core/vm/contracts.go | 2 +- core/vm/memory.go | 19 +------------------ core/vm/stack.go | 15 --------------- 3 files changed, 2 insertions(+), 34 deletions(-) diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 28394d4dd23c..b07cce9fc29b 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -521,7 +521,7 @@ func (c *blake2F) Run(input []byte) ([]byte, error) { // Parse the input into the Blake2b call parameters var ( rounds = binary.BigEndian.Uint32(input[0:4]) - final = (input[212] == blake2FFinalBlockBytes) + final = input[212] == blake2FFinalBlockBytes h [8]uint64 m [16]uint64 diff --git a/core/vm/memory.go b/core/vm/memory.go index ba5f8485dc95..7db2308187bf 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -17,8 +17,6 @@ package vm import ( - "fmt" - "github.com/holiman/uint256" ) @@ -68,7 +66,7 @@ func (m *Memory) Resize(size uint64) { } } -// Get returns offset + size as a new slice +// GetCopy returns offset + size as a new slice func (m *Memory) GetCopy(offset, size int64) (cpy []byte) { if size == 0 { return nil @@ -106,18 +104,3 @@ func (m *Memory) Len() int { func (m *Memory) Data() []byte { return m.store } - -// Print dumps the content of the memory. -func (m *Memory) Print() { - fmt.Printf("### mem %d bytes ###\n", len(m.store)) - if len(m.store) > 0 { - addr := 0 - for i := 0; i+32 <= len(m.store); i += 32 { - fmt.Printf("%03d: % x\n", addr, m.store[i:i+32]) - addr++ - } - } else { - fmt.Println("-- empty --") - } - fmt.Println("####################") -} diff --git a/core/vm/stack.go b/core/vm/stack.go index cc6037f23b3c..a389c04b725d 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -17,8 +17,6 @@ package vm import ( - "fmt" - "github.com/holiman/uint256" ) @@ -69,16 +67,3 @@ func (st *Stack) peek() *uint256.Int { func (st *Stack) Back(n int) *uint256.Int { return &st.data[st.len()-n-1] } - -// Print dumps the content of the stack -func (st *Stack) Print() { - fmt.Println("### stack ###") - if len(st.data) > 0 { - for i, val := range st.data { - fmt.Printf("%-3d %v\n", i, val) - } - } else { - fmt.Println("-- empty --") - } - fmt.Println("#############") -} From 62b62da730204d458cfeb0f7c76202274250ac1a Mon Sep 17 00:00:00 2001 From: s7v7nislands Date: Wed, 11 May 2022 13:04:16 +0800 Subject: [PATCH 28/31] core/vm: separate opcode group for 0x20 range (#24850) --- core/vm/opcodes.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index b861a62d6de0..0917b252ae8b 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -64,7 +64,10 @@ const ( SHL OpCode = 0x1b SHR OpCode = 0x1c SAR OpCode = 0x1d +) +// 0x20 range - crypto. +const ( KECCAK256 OpCode = 0x20 ) From 032b98e942e58ce926f185ad9a7f17f59f7d74a3 Mon Sep 17 00:00:00 2001 From: Qian Bin Date: Wed, 11 May 2022 17:00:29 +0800 Subject: [PATCH 29/31] core/vm: optimize Memory.Set32 (#24847) * core/vm: remove unnecessary memset for Memory.Set32 * core/vm: optimize Memory.Set32 --- core/vm/memory.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/vm/memory.go b/core/vm/memory.go index 7db2308187bf..35b729996075 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -53,10 +53,9 @@ func (m *Memory) Set32(offset uint64, val *uint256.Int) { if offset+32 > uint64(len(m.store)) { panic("invalid memory: store empty") } - // Zero the memory area - copy(m.store[offset:offset+32], []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) // Fill in relevant bits - val.WriteToSlice(m.store[offset:]) + b32 := val.Bytes32() + copy(m.store[offset:], b32[:]) } // Resize resizes the memory to size From 8b6db6d87a94edcfd844e77cad65bc897cb0d2a9 Mon Sep 17 00:00:00 2001 From: Leon <316032931@qq.com> Date: Mon, 26 Sep 2022 19:20:38 +0800 Subject: [PATCH 30/31] core/vm: better handle error on eip activation check (#25131) * core/vm: correct logic for eip check of NewEVMInterpreter * refactor --- core/vm/interpreter.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 91bb8dcfaa25..59fbf21f7c5a 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -108,15 +108,18 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { default: cfg.JumpTable = &frontierInstructionSet } - for i, eip := range cfg.ExtraEips { + var extraEips []int + for _, eip := range cfg.ExtraEips { copy := *cfg.JumpTable if err := EnableEIP(eip, ©); err != nil { // Disable it, so caller can check if it's activated or not - cfg.ExtraEips = append(cfg.ExtraEips[:i], cfg.ExtraEips[i+1:]...) log.Error("EIP activation failed", "eip", eip, "error", err) + } else { + extraEips = append(extraEips, eip) } cfg.JumpTable = © } + cfg.ExtraEips = extraEips } return &EVMInterpreter{ From b36678f7adf6b93d7416e89ae2b4f92a397370f8 Mon Sep 17 00:00:00 2001 From: lmittmann <3458786+lmittmann@users.noreply.github.com> Date: Thu, 2 Nov 2023 07:54:28 +0100 Subject: [PATCH 31/31] core/vm: performance tweak of `OpCode.String()` (#28453) make `opCodeToString` a `[256]string` array Co-authored-by: lmittmann --- core/vm/opcodes.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 0917b252ae8b..b9001138b05a 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -219,8 +219,7 @@ const ( SELFDESTRUCT OpCode = 0xff ) -// Since the opcodes aren't all in order we can't use a regular slice. -var opCodeToString = map[OpCode]string{ +var opCodeToString = [256]string{ // 0x0 range - arithmetic ops. STOP: "STOP", ADD: "ADD", @@ -385,12 +384,11 @@ var opCodeToString = map[OpCode]string{ } func (op OpCode) String() string { - str := opCodeToString[op] - if len(str) == 0 { - return fmt.Sprintf("opcode 0x%x not defined", int(op)) + if s := opCodeToString[op]; s != "" { + return s } - return str + return fmt.Sprintf("opcode %#x not defined", int(op)) } var stringToOp = map[string]OpCode{