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/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/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 99e4e386b404..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) @@ -55,9 +59,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 +73,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 +85,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) 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..2be61b51d0ce 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 @@ -131,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. 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/eips.go b/core/vm/eips.go index 40da1f88fafa..6ba7bbd6ec7a 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. @@ -51,17 +52,16 @@ 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, } } 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 } @@ -70,18 +70,17 @@ 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, } } // 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/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/evm.go b/core/vm/evm.go index f6f6a29a87d0..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 @@ -353,7 +341,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 @@ -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) } @@ -423,13 +407,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 +428,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. @@ -466,7 +449,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.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..45589157bb00 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) { @@ -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 @@ -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 { @@ -184,43 +184,43 @@ 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 + return params.SloadGasEIP2200, 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 + 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 { 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 } @@ -248,16 +248,16 @@ 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 } - wordGas, overflow := bigUint64(stack.Back(1)) + wordGas, overflow := stack.Back(1).Uint64WithOverflow() 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 { @@ -287,11 +287,11 @@ 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 } - 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 { @@ -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..73173b903981 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -17,303 +17,172 @@ package vm import ( - "math/big" - - "github.com/XinFinOrg/XDPoSChain/params" + "sync/atomic" "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 +191,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 +205,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 +218,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()) +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())) if interpreter.hasher == nil { interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState) @@ -393,45 +249,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 +302,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 +324,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 +360,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 +417,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 +427,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 +497,58 @@ 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()) + if interpreter.readOnly { + return nil, ErrWriteProtection + } + 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) { + if atomic.LoadInt32(&interpreter.evm.abort) != 0 { + return nil, errStopToken + } pos := callContext.stack.pop() - if !callContext.contract.validJumpdest(pos) { + if !callContext.contract.validJumpdest(&pos) { return nil, ErrInvalidJump } - *pc = pos.Uint64() - - interpreter.intPool.putOne(pos) + *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop return nil, nil } 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.Sign() != 0 { - if !callContext.contract.validJumpdest(pos) { + if !cond.IsZero() { + 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 } - - interpreter.intPool.put(pos, cond) return nil, nil } @@ -666,219 +557,253 @@ 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 } 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() - 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 { + interpreter.returnData = res // set REVERT data to return data buffer return res, nil } + interpreter.returnData = nil // clear dirty return data buffer return nil, nil } 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() 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 { + interpreter.returnData = res // set REVERT data to return data buffer return res, nil } + interpreter.returnData = nil // clear dirty return data buffer return nil, nil } 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 interpreter.readOnly && !value.IsZero() { + return nil, ErrWriteProtection + } + 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) + interpreter.returnData = ret 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) + interpreter.returnData = ret 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) + interpreter.returnData = ret 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) + interpreter.returnData = ret 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 + 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(offset.Int64(), size.Int64()) + ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) - interpreter.intPool.put(offset, size) - return ret, nil + interpreter.returnData = ret + 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, nil + 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 + } + 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 + return nil, errStopToken } // following functions are used by the instruction jump table @@ -886,13 +811,18 @@ 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) - 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 +832,6 @@ func makeLog(size int) executionFunc { BlockNumber: interpreter.evm.BlockNumber.Uint64(), }) - interpreter.intPool.put(mStart, mSize) return nil, nil } } @@ -911,13 +840,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 +866,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 +878,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..1f63d2b040a1 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 { @@ -42,6 +41,7 @@ type twoOperandParams struct { y string } +var alphabetSoup = "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" var commonParams []*twoOperandParams var twoOpMethods map[string]executionFunc @@ -91,31 +91,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 +99,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 +191,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 +294,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 +303,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) { @@ -338,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) } @@ -370,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) } @@ -437,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) } @@ -512,21 +529,21 @@ 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.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(big.NewInt(0x1), big.NewInt(0)) + 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") } - poolOfIntPools.put(evmInterpreter.intPool) } func BenchmarkOpMstore(bench *testing.B) { @@ -538,21 +555,20 @@ 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.push(value) + stack.push(memStart) opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil}) } - poolOfIntPools.put(evmInterpreter.intPool) } -func BenchmarkOpSHA3(bench *testing.B) { +func BenchmarkOpKeccak256(bench *testing.B) { var ( env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{}) stack = newstack() @@ -560,17 +576,16 @@ 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) - opSha3(&pc, evmInterpreter, &callCtx{mem, stack, nil}) + stack.push(uint256.NewInt(32)) + stack.push(start) + opKeccak256(&pc, evmInterpreter, &callCtx{mem, stack, nil}) } - poolOfIntPools.put(evmInterpreter.intPool) } func TestCreate2Addreses(t *testing.T) { @@ -644,6 +659,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..59fbf21f7c5a 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" @@ -29,10 +28,9 @@ 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 [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 @@ -83,8 +81,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 @@ -94,35 +90,36 @@ 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].valid { - 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 { + 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.JumpTable = jt + cfg.ExtraEips = extraEips } return &EVMInterpreter{ @@ -138,20 +135,13 @@ 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++ 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 }() @@ -188,9 +178,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 { @@ -206,12 +193,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 @@ -221,26 +203,12 @@ 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 { - return nil, &ErrInvalidOpCode{opcode: op} - } // Validate stack if sLen := stack.len(); sLen < operation.minStack { return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack} } else if sLen > operation.maxStack { return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} } - // If the operation is valid, enforce and 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) { @@ -285,29 +253,17 @@ 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 { - 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/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/jump_table.go b/core/vm/jump_table.go index b3a0f7ff7ce9..55ad78adc53f 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -40,13 +40,6 @@ 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 - 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 } var ( @@ -60,7 +53,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,44 +71,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, } return instructionSet } @@ -124,41 +110,34 @@ 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, } return instructionSet } @@ -188,15 +167,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 } @@ -204,226 +181,194 @@ 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, 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, - constantGas: params.Sha3Gas, - dynamicGas: gasSha3, + KECCAK256: { + execute: opKeccak256, + constantGas: params.Keccak256Gas, + dynamicGas: gasKeccak256, minStack: minStack(2, 1), maxStack: maxStack(2, 1), - memorySize: memorySha3, - valid: true, + memorySize: memoryKeccak256, }, 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 +377,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 +391,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 +411,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 +461,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(1, 1), maxStack: maxStack(1, 1), memorySize: memoryMLoad, - valid: true, }, MSTORE: { execute: opMstore, @@ -538,7 +469,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 0), maxStack: maxStack(2, 0), memorySize: memoryMStore, - valid: true, }, MSTORE8: { execute: opMstore8, @@ -547,515 +477,438 @@ 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: { execute: opJump, constantGas: GasMidStep, minStack: minStack(1, 0), maxStack: maxStack(1, 0), - jumps: true, - valid: true, }, JUMPI: { execute: opJumpi, constantGas: GasSlowStep, 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,8 +916,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 0), maxStack: maxStack(2, 0), memorySize: memoryLog, - valid: true, - writes: true, }, LOG1: { execute: makeLog(1), @@ -1072,8 +923,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(3, 0), maxStack: maxStack(3, 0), memorySize: memoryLog, - valid: true, - writes: true, }, LOG2: { execute: makeLog(2), @@ -1081,8 +930,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(4, 0), maxStack: maxStack(4, 0), memorySize: memoryLog, - valid: true, - writes: true, }, LOG3: { execute: makeLog(3), @@ -1090,8 +937,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(5, 0), maxStack: maxStack(5, 0), memorySize: memoryLog, - valid: true, - writes: true, }, LOG4: { execute: makeLog(4), @@ -1099,8 +944,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(6, 0), maxStack: maxStack(6, 0), memorySize: memoryLog, - valid: true, - writes: true, }, CREATE: { execute: opCreate, @@ -1109,9 +952,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(3, 1), maxStack: maxStack(3, 1), memorySize: memoryCreate, - valid: true, - writes: true, - returns: true, }, CALL: { execute: opCall, @@ -1120,8 +960,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(7, 1), maxStack: maxStack(7, 1), memorySize: memoryCall, - valid: true, - returns: true, }, CALLCODE: { execute: opCallCode, @@ -1130,8 +968,6 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(7, 1), maxStack: maxStack(7, 1), memorySize: memoryCall, - valid: true, - returns: true, }, RETURN: { execute: opReturn, @@ -1139,17 +975,21 @@ func newFrontierInstructionSet() JumpTable { minStack: minStack(2, 0), maxStack: maxStack(2, 0), memorySize: memoryReturn, - halts: true, - valid: true, }, SELFDESTRUCT: { - execute: opSuicide, + execute: opSelfdestruct, dynamicGas: gasSelfdestruct, minStack: minStack(1, 0), maxStack: maxStack(1, 0), - halts: true, - valid: true, - writes: true, }, } + + // 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 } 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..4a63120a3df4 100644 --- a/core/vm/logger_json.go +++ b/core/vm/logger_json.go @@ -62,14 +62,19 @@ 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) } // 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. @@ -80,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}) } diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index e3e0a085b998..f6c15d9cbb0f 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -20,17 +20,16 @@ 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 { 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) {} @@ -57,8 +56,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..35b729996075 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -17,10 +17,7 @@ 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,16 +47,15 @@ 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)) { 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 - math.ReadBits(val, m.store[offset:offset+32]) + b32 := val.Bytes32() + copy(m.store[offset:], b32[:]) } // Resize resizes the memory to size @@ -69,7 +65,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 @@ -107,18 +103,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/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 322e01d17c99..b9001138b05a 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -32,75 +32,73 @@ 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 = 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 +// 0x20 range - crypto. +const ( + KECCAK256 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 +119,7 @@ const ( JUMPDEST ) -// 0x60 range. +// 0x60 range - pushes. const ( PUSH1 OpCode = 0x60 + iota PUSH2 @@ -155,7 +153,11 @@ const ( PUSH30 PUSH31 PUSH32 - DUP1 +) + +// 0x80 range - dups. +const ( + DUP1 = 0x80 + iota DUP2 DUP3 DUP4 @@ -171,7 +173,11 @@ const ( DUP14 DUP15 DUP16 - SWAP1 +) + +// 0x90 range - swaps. +const ( + SWAP1 = 0x90 + iota SWAP2 SWAP3 SWAP4 @@ -198,28 +204,22 @@ const ( LOG4 ) -// unofficial opcodes used for parsing. -const ( - PUSH OpCode = 0xb0 + iota - DUP - SWAP -) - // 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 + INVALID OpCode = 0xfe 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", @@ -251,7 +251,7 @@ var opCodeToString = map[OpCode]string{ MULMOD: "MULMOD", // 0x20 range - crypto. - SHA3: "SHA3", + KECCAK256: "KECCAK256", // 0x30 range - closure state. ADDRESS: "ADDRESS", @@ -379,20 +379,16 @@ var opCodeToString = map[OpCode]string{ CREATE2: "CREATE2", STATICCALL: "STATICCALL", REVERT: "REVERT", + INVALID: "INVALID", SELFDESTRUCT: "SELFDESTRUCT", - - PUSH: "PUSH", - DUP: "DUP", - SWAP: "SWAP", } 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{ @@ -422,7 +418,7 @@ var stringToOp = map[string]OpCode{ "SAR": SAR, "ADDMOD": ADDMOD, "MULMOD": MULMOD, - "SHA3": SHA3, + "KECCAK256": KECCAK256, "ADDRESS": ADDRESS, "BALANCE": BALANCE, "ORIGIN": ORIGIN, @@ -536,6 +532,7 @@ var stringToOp = map[string]OpCode{ "RETURN": RETURN, "CALLCODE": CALLCODE, "REVERT": REVERT, + "INVALID": INVALID, "SELFDESTRUCT": SELFDESTRUCT, } diff --git a/core/vm/stack.go b/core/vm/stack.go index c9c3d07f4b6f..a389c04b725d 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -17,37 +17,31 @@ 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) -} -func (st *Stack) pushN(ds ...*big.Int) { - st.data = append(st.data, ds...) + st.data = append(st.data, *d) } -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,28 +55,15 @@ 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] -} - -// 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("#############") +func (st *Stack) Back(n int) *uint256.Int { + return &st.data[st.len()-n-1] } 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", 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/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) {} 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= diff --git a/params/protocol_params.go b/params/protocol_params.go index f7ab3cdd1046..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. @@ -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. 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) }