diff --git a/core/vm/common.go b/core/vm/common.go index fbc8866f5..8a9678293 100644 --- a/core/vm/common.go +++ b/core/vm/common.go @@ -17,7 +17,7 @@ package vm import ( - "math/big" + "github.com/core-coin/uint256" "github.com/core-coin/go-core/common" "github.com/core-coin/go-core/common/math" @@ -25,7 +25,7 @@ import ( // 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 +35,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 +64,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 { @@ -88,12 +72,3 @@ func toWordSize(size uint64) uint64 { return (size + 31) / 32 } - -func allZero(b []byte) bool { - for _, byte := range b { - if byte != 0 { - return false - } - } - return true -} diff --git a/core/vm/contract.go b/core/vm/contract.go index f18f77567..f799bba0c 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -17,6 +17,7 @@ package vm import ( + "github.com/core-coin/uint256" "math/big" "github.com/core-coin/go-core/common" @@ -57,8 +58,8 @@ type Contract struct { CodeAddr *common.Address Input []byte - Energy uint64 - value *big.Int + Energy uint64 + value *big.Int } // NewContract returns a new contract environment for the execution of CVM. @@ -81,11 +82,11 @@ func NewContract(caller ContractRef, object ContractRef, value *big.Int, energy return c } -func (c *Contract) validJumpdest(dest *big.Int) bool { - udest := dest.Uint64() +func (c *Contract) validJumpdest(dest *uint256.Int) bool { + udest, overflow := dest.Uint64WithOverflow() // PC cannot go beyond len(code) and certainly can't be bigger than 63bits. // Don't bother checking for JUMPDEST in that case. - if dest.BitLen() >= 63 || udest >= uint64(len(c.Code)) { + if overflow || udest >= uint64(len(c.Code)) { return false } // Only JUMPDESTs allowed for destinations diff --git a/core/vm/cvm.go b/core/vm/cvm.go index 765facda4..0040d766b 100644 --- a/core/vm/cvm.go +++ b/core/vm/cvm.go @@ -76,12 +76,12 @@ type Context struct { GetHash GetHashFunc // Message information - Origin common.Address // Provides information for ORIGIN + Origin common.Address // Provides information for ORIGIN EnergyPrice *big.Int // Provides information for ENERGYPRICE // Block information Coinbase common.Address // Provides information for COINBASE - EnergyLimit uint64 // Provides information for ENERGYLIMIT + EnergyLimit uint64 // Provides information for ENERGYLIMIT BlockNumber *big.Int // Provides information for NUMBER Time *big.Int // Provides information for TIME Difficulty *big.Int // Provides information for DIFFICULTY @@ -338,7 +338,7 @@ func (cvm *CVM) StaticCall(caller ContractRef, addr common.Address, input []byte // We do an AddBalance of zero here, just in order to trigger a touch. // but is the correct thing to do and matters on other networks, in tests, and potential // future scenarios - cvm.StateDB.AddBalance(addr, bigZero) + cvm.StateDB.AddBalance(addr, big.NewInt(0)) // When an error was returned by the CVM or when setting the creation code // above we revert to the snapshot and consume any energy remaining. diff --git a/core/vm/eips.go b/core/vm/eips.go index b24d10377..bc52da4f8 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -16,15 +16,17 @@ package vm +import "github.com/core-coin/uint256" + func opSelfBalance(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - balance := interpreter.intPool.get().Set(interpreter.cvm.StateDB.GetBalance(contract.Address())) + balance, _ := uint256.FromBig(interpreter.cvm.StateDB.GetBalance(contract.Address())) stack.push(balance) return nil, nil } // opNetworkID implements NETWORKID opcode func opNetworkID(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - networkId := interpreter.intPool.get().Set(interpreter.cvm.chainConfig.NetworkID) + networkId, _ := uint256.FromBig(interpreter.cvm.chainConfig.NetworkID) stack.push(networkId) return nil, nil } diff --git a/core/vm/energy.go b/core/vm/energy.go index 798e5b7a5..7a709d810 100644 --- a/core/vm/energy.go +++ b/core/vm/energy.go @@ -17,7 +17,7 @@ package vm import ( - "math/big" + "github.com/core-coin/uint256" ) // Energy costs @@ -33,7 +33,7 @@ const ( // callEnergy returns the actual energy cost of the call. // // The returned energy is energy - base * 63 / 64. -func callEnergy(availableEnergy, base uint64, callCost *big.Int) (uint64, error) { +func callEnergy(availableEnergy, base uint64, callCost *uint256.Int) (uint64, error) { availableEnergy = availableEnergy - base energy := availableEnergy - availableEnergy/64 // If the bit length exceeds 64 bit we know that the newly calculated "energy" for CIP150 diff --git a/core/vm/energy_table.go b/core/vm/energy_table.go index 04909ab06..7e33d90c1 100644 --- a/core/vm/energy_table.go +++ b/core/vm/energy_table.go @@ -70,7 +70,7 @@ func memoryCopierEnergy(stackpos int) energyFunc { return 0, err } // And energy for copying data, charged per word at param.CopyEnergy - words, overflow := bigUint64(stack.Back(stackpos)) + words, overflow := stack.Back(stackpos).Uint64WithOverflow() if overflow { return 0, errEnergyUintOverflow } @@ -114,14 +114,14 @@ func energySStore(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memor // Energy sentry honoured, do the actual energy calculation based on the stored value var ( y, x = stack.Back(1), stack.Back(0) - current = cvm.StateDB.GetState(contract.Address(), common.BigToHash(x)) + current = cvm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32())) ) - value := common.BigToHash(y) + value := common.Hash(y.Bytes32()) if current == value { // noop (1) return params.SstoreNoopEnergy, nil } - original := cvm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x)) + original := cvm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32())) if original == current { if original == (common.Hash{}) { // create slot (2.1.1) return params.SstoreInitEnergy, nil @@ -150,7 +150,7 @@ func energySStore(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memor func makeEnergyLog(n uint64) energyFunc { return func(cvm *CVM, 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, errEnergyUintOverflow } @@ -183,7 +183,7 @@ func energySha3(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memoryS if err != nil { return 0, err } - wordEnergy, overflow := bigUint64(stack.Back(1)) + wordEnergy, overflow := stack.Back(1).Uint64WithOverflow() if overflow { return 0, errEnergyUintOverflow } @@ -217,7 +217,7 @@ func energyCreate2(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memo if err != nil { return 0, err } - wordEnergy, overflow := bigUint64(stack.Back(2)) + wordEnergy, overflow := stack.Back(2).Uint64WithOverflow() if overflow { return 0, errEnergyUintOverflow } @@ -234,7 +234,7 @@ func energyExp(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memorySi expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8) var ( - energy = expByteLen * params.ExpByte // no overflow check required. Max is 256 * ExpByte energy + energy = expByteLen * params.ExpByte // no overflow check required. Max is 256 * ExpByte energy overflow bool ) if energy, overflow = math.SafeAdd(energy, params.ExpEnergy); overflow { @@ -245,9 +245,9 @@ func energyExp(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memorySi func energyCall(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var ( - energy uint64 - transfersValue = stack.Back(2).Sign() != 0 - address = common.BigToAddress(stack.Back(1)) + energy uint64 + transfersValue = !stack.Back(2).IsZero() + address = common.Address(stack.Back(1).Bytes22()) ) if transfersValue && cvm.StateDB.Empty(address) { energy += params.CallNewAccountEnergy @@ -264,7 +264,7 @@ func energyCall(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, memoryS return 0, errEnergyUintOverflow } - cvm.callEnergyTemp, err = callEnergy( contract.Energy, energy, stack.Back(0)) + cvm.callEnergyTemp, err = callEnergy(contract.Energy, energy, stack.Back(0)) if err != nil { return 0, err } @@ -280,7 +280,7 @@ func energyCallCode(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, mem return 0, err } var ( - energy uint64 + energy uint64 overflow bool ) if stack.Back(2).Sign() != 0 { @@ -335,7 +335,7 @@ func energySelfdestruct(cvm *CVM, contract *Contract, stack *Stack, mem *Memory, var energy uint64 energy = params.SelfdestructEnergy - var address = common.BigToAddress(stack.Back(0)) + var address = common.Address(stack.Back(0).Bytes22()) if cvm.StateDB.Empty(address) && cvm.StateDB.GetBalance(contract.Address()).Sign() != 0 { energy += params.CreateBySelfdestructEnergy diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 164f8fe7f..9dce1deee 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -18,18 +18,14 @@ package vm import ( "errors" - "math/big" - "github.com/core-coin/go-core/common" - "github.com/core-coin/go-core/common/math" "github.com/core-coin/go-core/core/types" "github.com/core-coin/go-core/params" + "github.com/core-coin/uint256" "golang.org/x/crypto/sha3" ) var ( - bigZero = new(big.Int) - tt255 = math.BigPow(2, 255) errWriteProtection = errors.New("cvm: write protection") errReturnDataOutOfBounds = errors.New("cvm: return data out of bounds") errExecutionReverted = errors.New("cvm: execution reverted") @@ -39,286 +35,162 @@ var ( func opAdd(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() - math.U256(y.Add(x, y)) - - interpreter.intPool.put(x) + y.Add(&x, y) return nil, nil } func opSub(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() - math.U256(y.Sub(x, y)) - - interpreter.intPool.put(x) + y.Sub(&x, y) return nil, nil } func opMul(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - x, y := stack.pop(), stack.pop() - stack.push(math.U256(x.Mul(x, y))) - - interpreter.intPool.put(y) + x, y := stack.pop(), stack.peek() + y.Mul(&x, y) return nil, nil } func opDiv(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() - if y.Sign() != 0 { - math.U256(y.Div(x, y)) - } else { - y.SetUint64(0) - } - interpreter.intPool.put(x) + y.Div(&x, y) return nil, nil } func opSdiv(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - x, y := math.S256(stack.pop()), math.S256(stack.pop()) - res := interpreter.intPool.getZero() - - if y.Sign() == 0 || x.Sign() == 0 { - 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)) - } - stack.push(math.U256(res)) - } - interpreter.intPool.put(x, y) + x, y := stack.pop(), stack.peek() + y.SDiv(&x, y) return nil, nil } func opMod(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - x, y := stack.pop(), stack.pop() - if y.Sign() == 0 { - stack.push(x.SetUint64(0)) - } else { - stack.push(math.U256(x.Mod(x, y))) - } - interpreter.intPool.put(y) + x, y := stack.pop(), stack.peek() + y.Mod(&x, y) return nil, nil } func opSmod(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - x, y := math.S256(stack.pop()), math.S256(stack.pop()) - res := interpreter.intPool.getZero() - - if y.Sign() == 0 { - 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)) - } - stack.push(math.U256(res)) - } - interpreter.intPool.put(x, y) + x, y := stack.pop(), stack.peek() + y.SMod(&x, y) return nil, nil } func opExp(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - base, exponent := stack.pop(), stack.pop() - // some shortcuts - cmpToOne := exponent.Cmp(big1) - if cmpToOne < 0 { // Exponent is zero - // x ^ 0 == 1 - stack.push(base.SetUint64(1)) - } else if base.Sign() == 0 { - // 0 ^ y, if y != 0, == 0 - stack.push(base.SetUint64(0)) - } else if cmpToOne == 0 { // Exponent is one - // x ^ 1 == x - stack.push(base) - } else { - stack.push(math.Exp(base, exponent)) - interpreter.intPool.put(base) - } - interpreter.intPool.put(exponent) + base, exponent := stack.pop(), stack.peek() + exponent.Exp(&base, exponent) return nil, nil } func opSignExtend(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - back := stack.pop() - if back.Cmp(big.NewInt(31)) < 0 { - bit := uint(back.Uint64()*8 + 7) - num := 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) - } - - stack.push(math.U256(num)) - } - - interpreter.intPool.put(back) + back, num := stack.pop(), stack.peek() + num.ExtendSign(num, &back) return nil, nil } func opNot(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x := stack.peek() - math.U256(x.Not(x)) + x.Not(x) return nil, nil } func opLt(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() - if x.Cmp(y) < 0 { - y.SetUint64(1) + if x.Lt(y) { + y.SetOne() } else { - y.SetUint64(0) + y.Clear() } - interpreter.intPool.put(x) return nil, nil } func opGt(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() - if x.Cmp(y) > 0 { - y.SetUint64(1) + if x.Gt(y) { + y.SetOne() } else { - y.SetUint64(0) + y.Clear() } - interpreter.intPool.put(x) return nil, nil } func opSlt(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), 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.put(x) return nil, nil } func opSgt(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), 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.put(x) return nil, nil } func opEq(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() - if x.Cmp(y) == 0 { - y.SetUint64(1) + if x.Eq(y) { + y.SetOne() } else { - y.SetUint64(0) + y.Clear() } - interpreter.intPool.put(x) return nil, nil } func opIszero(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x := 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 *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - x, y := stack.pop(), stack.pop() - stack.push(x.And(x, y)) - - interpreter.intPool.put(y) + x, y := stack.pop(), stack.peek() + y.And(&x, y) return nil, nil } func opOr(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() - y.Or(x, y) - - interpreter.intPool.put(x) + y.Or(&x, y) return nil, nil } func opXor(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { x, y := stack.pop(), stack.peek() - y.Xor(x, y) - - interpreter.intPool.put(x) + y.Xor(&x, y) return nil, nil } func opByte(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { th, val := stack.pop(), 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.put(th) + val.Byte(&th) return nil, nil } func opAddmod(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - x, y, z := stack.pop(), stack.pop(), stack.pop() - if z.Cmp(bigZero) > 0 { - x.Add(x, y) - x.Mod(x, z) - stack.push(math.U256(x)) + x, y, z := stack.pop(), stack.pop(), stack.peek() + if z.IsZero() { + z.Clear() } else { - stack.push(x.SetUint64(0)) + z.AddMod(&x, &y, z) } - interpreter.intPool.put(y, z) return nil, nil } func opMulmod(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - x, y, z := stack.pop(), stack.pop(), stack.pop() - if z.Cmp(bigZero) > 0 { - x.Mul(x, y) - x.Mod(x, z) - stack.push(math.U256(x)) - } else { - stack.push(x.SetUint64(0)) - } - interpreter.intPool.put(y, z) + x, y, z := stack.pop(), stack.pop(), stack.peek() + z.MulMod(&x, &y, z) return nil, nil } @@ -327,15 +199,12 @@ func opMulmod(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memor // and pushes on the stack arg2 shifted to the left by arg1 number of bits. func opSHL(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]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(stack.pop()), math.U256(stack.peek()) - defer interpreter.intPool.put(shift) // First operand back into the pool - - if shift.Cmp(common.Big256) >= 0 { - value.SetUint64(0) - return nil, nil + shift, value := stack.pop(), 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 } @@ -345,15 +214,12 @@ func opSHL(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory * // and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill. func opSHR(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]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(stack.pop()), math.U256(stack.peek()) - defer interpreter.intPool.put(shift) // First operand back into the pool - - if shift.Cmp(common.Big256) >= 0 { - value.SetUint64(0) - return nil, nil + shift, value := stack.pop(), 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 } @@ -363,28 +229,25 @@ func opSHR(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory * // and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension. func opSAR(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // Note, S256 returns (potentially) a new bigint, so we're popping, not peeking this one - shift, value := math.U256(stack.pop()), math.S256(stack.pop()) - defer interpreter.intPool.put(shift) // First operand back into the pool - - if shift.Cmp(common.Big256) >= 0 { + shift, value := stack.pop(), 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() } - stack.push(math.U256(value)) return nil, nil } n := uint(shift.Uint64()) - value.Rsh(value, n) - stack.push(math.U256(value)) + value.SRsh(value, n) return nil, nil } func opSha3(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - offset, size := stack.pop(), stack.pop() - data := memory.GetPtr(offset.Int64(), size.Int64()) + offset, size := stack.pop(), stack.peek() + data := memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) if interpreter.hasher == nil { interpreter.hasher = sha3.New256().(keccakState) @@ -398,45 +261,51 @@ func opSha3(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory if cvm.vmConfig.EnablePreimageRecording { cvm.StateDB.AddPreimage(interpreter.hasherBuf, data) } - 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 *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(interpreter.intPool.get().SetBytes(contract.Address().Bytes())) + stack.push(new(uint256.Int).SetBytes(contract.Address().Bytes())) return nil, nil } func opBalance(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { slot := stack.peek() - slot.Set(interpreter.cvm.StateDB.GetBalance(common.BigToAddress(slot))) + address := common.Address(slot.Bytes22()) + slot.SetFromBig(interpreter.cvm.StateDB.GetBalance(address)) return nil, nil } func opOrigin(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(interpreter.intPool.get().SetBytes(interpreter.cvm.Origin.Bytes())) + stack.push(new(uint256.Int).SetBytes(interpreter.cvm.Origin.Bytes())) return nil, nil } func opCaller(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(interpreter.intPool.get().SetBytes(contract.Caller().Bytes())) + stack.push(new(uint256.Int).SetBytes(contract.Caller().Bytes())) return nil, nil } func opCallValue(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(interpreter.intPool.get().Set(contract.value)) + v, _ := uint256.FromBig(contract.value) + stack.push(v) return nil, nil } func opCallDataLoad(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(interpreter.intPool.get().SetBytes(getDataBig(contract.Input, stack.pop(), big32))) + x := stack.peek() + if offset, overflow := x.Uint64WithOverflow(); !overflow { + data := getData(contract.Input, offset, 32) + x.SetBytes(data) + } else { + x.Clear() + } return nil, nil } func opCallDataSize(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(interpreter.intPool.get().SetInt64(int64(len(contract.Input)))) + stack.push(new(uint256.Int).SetUint64(uint64(len(contract.Input)))) return nil, nil } @@ -446,14 +315,19 @@ func opCallDataCopy(pc *uint64, interpreter *CVMInterpreter, contract *Contract, dataOffset = stack.pop() length = stack.pop() ) - memory.Set(memOffset.Uint64(), length.Uint64(), getDataBig(contract.Input, dataOffset, length)) - - interpreter.intPool.put(memOffset, dataOffset, length) + dataOffset64, overflow := dataOffset.Uint64WithOverflow() + if overflow { + dataOffset64 = 0xffffffffffffffff + } + // These values are checked for overflow during energy cost calculation + memOffset64 := memOffset.Uint64() + length64 := length.Uint64() + memory.Set(memOffset64, length64, getData(contract.Input, dataOffset64, length64)) return nil, nil } func opReturnDataSize(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(interpreter.intPool.get().SetUint64(uint64(len(interpreter.returnData)))) + stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData)))) return nil, nil } @@ -462,28 +336,33 @@ func opReturnDataCopy(pc *uint64, interpreter *CVMInterpreter, contract *Contrac memOffset = stack.pop() dataOffset = stack.pop() length = 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 } - 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 + } + memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[offset64:end64]) return nil, nil } func opExtCodeSize(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { slot := stack.peek() - slot.SetUint64(uint64(interpreter.cvm.StateDB.GetCodeSize(common.BigToAddress(slot)))) + slot.SetUint64(uint64(interpreter.cvm.StateDB.GetCodeSize(common.Address(slot.Bytes22())))) return nil, nil } func opCodeSize(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - l := interpreter.intPool.get().SetInt64(int64(len(contract.Code))) + l := new(uint256.Int) + l.SetUint64(uint64(len(contract.Code))) stack.push(l) return nil, nil @@ -495,24 +374,31 @@ func opCodeCopy(pc *uint64, interpreter *CVMInterpreter, contract *Contract, mem codeOffset = stack.pop() length = stack.pop() ) - codeCopy := getDataBig(contract.Code, codeOffset, length) + uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() + if overflow { + uint64CodeOffset = 0xffffffffffffffff + } + codeCopy := getData(contract.Code, uint64CodeOffset, length.Uint64()) memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) - interpreter.intPool.put(memOffset, codeOffset, length) return nil, nil } func opExtCodeCopy(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { var ( - addr = common.BigToAddress(stack.pop()) + a = stack.pop() memOffset = stack.pop() codeOffset = stack.pop() length = stack.pop() ) - codeCopy := getDataBig(interpreter.cvm.StateDB.GetCode(addr), codeOffset, length) + uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() + if overflow { + uint64CodeOffset = 0xffffffffffffffff + } + addr := common.Address(a.Bytes22()) + codeCopy := getData(interpreter.cvm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64()) memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) - interpreter.intPool.put(memOffset, codeOffset, length) return nil, nil } @@ -544,9 +430,9 @@ func opExtCodeCopy(pc *uint64, interpreter *CVMInterpreter, contract *Contract, // this account should be regarded as a non-existent account and zero should be returned. func opExtCodeHash(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { slot := stack.peek() - address := common.BigToAddress(slot) + address := common.Address(slot.Bytes22()) if interpreter.cvm.StateDB.Empty(address) { - slot.SetUint64(0) + slot.Clear() } else { slot.SetBytes(interpreter.cvm.StateDB.GetCodeHash(address).Bytes()) } @@ -554,56 +440,69 @@ func opExtCodeHash(pc *uint64, interpreter *CVMInterpreter, contract *Contract, } func opEnergyprice(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(interpreter.intPool.get().Set(interpreter.cvm.EnergyPrice)) + v, _ := uint256.FromBig(interpreter.cvm.EnergyPrice) + stack.push(v) return nil, nil } func opBlockhash(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - num := stack.pop() - - n := interpreter.intPool.get().Sub(interpreter.cvm.BlockNumber, common.Big257) - if num.Cmp(n) > 0 && num.Cmp(interpreter.cvm.BlockNumber) < 0 { - stack.push(interpreter.cvm.GetHash(num.Uint64()).Big()) + num := stack.peek() + num64, overflow := num.Uint64WithOverflow() + if overflow { + num.Clear() + return nil, nil + } + var upper, lower uint64 + upper = interpreter.cvm.BlockNumber.Uint64() + if upper < 257 { + lower = 0 } else { - stack.push(interpreter.intPool.getZero()) + lower = upper - 256 + } + if num64 >= lower && num64 < upper { + num.SetBytes(interpreter.cvm.GetHash(num64).Bytes()) + } else { + num.Clear() } - interpreter.intPool.put(num, n) return nil, nil } func opCoinbase(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(interpreter.intPool.get().SetBytes(interpreter.cvm.Coinbase.Bytes())) + stack.push(new(uint256.Int).SetBytes(interpreter.cvm.Coinbase.Bytes())) return nil, nil } func opTimestamp(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(math.U256(interpreter.intPool.get().Set(interpreter.cvm.Time))) + v, _ := uint256.FromBig(interpreter.cvm.Time) + stack.push(v) return nil, nil } func opNumber(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(math.U256(interpreter.intPool.get().Set(interpreter.cvm.BlockNumber))) + v, _ := uint256.FromBig(interpreter.cvm.BlockNumber) + stack.push(v) return nil, nil } func opDifficulty(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(math.U256(interpreter.intPool.get().Set(interpreter.cvm.Difficulty))) + v, _ := uint256.FromBig(interpreter.cvm.Difficulty) + stack.push(v) return nil, nil } func opEnergyLimit(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(math.U256(interpreter.intPool.get().SetUint64(interpreter.cvm.EnergyLimit))) + stack.push(new(uint256.Int).SetUint64(interpreter.cvm.EnergyLimit)) return nil, nil } func opPop(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - interpreter.intPool.put(stack.pop()) + stack.pop() return nil, nil } func opMload(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { v := stack.peek() - offset := v.Int64() + offset := int64(v.Uint64()) v.SetBytes(memory.GetPtr(offset, 32)) return nil, nil } @@ -611,50 +510,46 @@ func opMload(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory func opMstore(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // pop value of the stack mStart, val := stack.pop(), stack.pop() - memory.Set32(mStart.Uint64(), val) - - interpreter.intPool.put(mStart, val) + memory.Set32(mStart.Uint64(), &val) return nil, nil } func opMstore8(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - off, val := stack.pop().Int64(), stack.pop().Int64() - memory.store[off] = byte(val & 0xff) - + off, val := stack.pop(), stack.pop() + memory.store[off.Uint64()] = byte(val.Uint64()) return nil, nil } func opSload(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { loc := stack.peek() - val := interpreter.cvm.StateDB.GetState(contract.Address(), common.BigToHash(loc)) + hash := common.Hash(loc.Bytes32()) + val := interpreter.cvm.StateDB.GetState(contract.Address(), hash) loc.SetBytes(val.Bytes()) return nil, nil } func opSstore(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - loc := common.BigToHash(stack.pop()) + loc := stack.pop() val := stack.pop() - interpreter.cvm.StateDB.SetState(contract.Address(), loc, common.BigToHash(val)) - - interpreter.intPool.put(val) + interpreter.cvm.StateDB.SetState(contract.Address(), + common.Hash(loc.Bytes32()), common.Hash(val.Bytes32())) return nil, nil } func opJump(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { pos := stack.pop() - if !contract.validJumpdest(pos) { + if !contract.validJumpdest(&pos) { return nil, errInvalidJump } *pc = pos.Uint64() - interpreter.intPool.put(pos) return nil, nil } func opJumpi(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { pos, cond := stack.pop(), stack.pop() - if cond.Sign() != 0 { - if !contract.validJumpdest(pos) { + if !cond.IsZero() { + if !contract.validJumpdest(&pos) { return nil, errInvalidJump } *pc = pos.Uint64() @@ -662,7 +557,6 @@ func opJumpi(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *pc++ } - interpreter.intPool.put(pos, cond) return nil, nil } @@ -671,17 +565,17 @@ func opJumpdest(pc *uint64, interpreter *CVMInterpreter, contract *Contract, mem } func opPc(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(interpreter.intPool.get().SetUint64(*pc)) + stack.push(new(uint256.Int).SetUint64(*pc)) return nil, nil } func opMsize(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(interpreter.intPool.get().SetInt64(int64(memory.Len()))) + stack.push(new(uint256.Int).SetUint64(uint64(memory.Len()))) return nil, nil } func opEnergy(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(interpreter.intPool.get().SetUint64(contract.Energy)) + stack.push(new(uint256.Int).SetUint64(contract.Energy)) return nil, nil } @@ -689,25 +583,27 @@ func opCreate(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memor var ( value = stack.pop() offset, size = stack.pop(), stack.pop() - input = memory.GetCopy(offset.Int64(), size.Int64()) - energy = contract.Energy + input = memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) + energy = contract.Energy ) energy -= energy / 64 + // reuse size int for stackvalue + stackvalue := size contract.UseEnergy(energy) - res, addr, returnEnergy, suberr := interpreter.cvm.Create(contract, input, energy, value) + res, addr, returnEnergy, suberr := interpreter.cvm.Create(contract, input, energy, value.ToBig()) // Push item on the stack based on the returned error. We must // ignore this error and pretend the operation was successful. if suberr == ErrCodeStoreOutOfEnergy { - stack.push(interpreter.intPool.getZero()) + stackvalue.Clear() } else if suberr != nil && suberr != ErrCodeStoreOutOfEnergy { - stack.push(interpreter.intPool.getZero()) + stackvalue.Clear() } else { - stack.push(interpreter.intPool.get().SetBytes(addr.Bytes())) + stackvalue.SetBytes(addr.Bytes()) } + stack.push(&stackvalue) contract.Energy += returnEnergy - interpreter.intPool.put(value, offset, size) if suberr == errExecutionReverted { return res, nil @@ -720,22 +616,25 @@ func opCreate2(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memo endowment = stack.pop() offset, size = stack.pop(), stack.pop() salt = stack.pop() - input = memory.GetCopy(offset.Int64(), size.Int64()) - energy = contract.Energy + input = memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) + energy = contract.Energy ) // Apply CIP150 energy -= energy / 64 contract.UseEnergy(energy) - res, addr, returnEnergy, suberr := interpreter.cvm.Create2(contract, input, energy, endowment, salt) + // reuse size int for stackvalue + stackvalue := size + res, addr, returnEnergy, suberr := interpreter.cvm.Create2(contract, input, energy, + endowment.ToBig(), salt.ToBig()) // Push item on the stack based on the returned error. if suberr != nil { - stack.push(interpreter.intPool.getZero()) + stackvalue.Clear() } else { - stack.push(interpreter.intPool.get().SetBytes(addr.Bytes())) + stackvalue.SetBytes(addr.Bytes()) } + stack.push(&stackvalue) contract.Energy += returnEnergy - interpreter.intPool.put(endowment, offset, size, salt) if suberr == errExecutionReverted { return res, nil @@ -745,125 +644,119 @@ func opCreate2(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memo func opCall(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // Pop energy. The actual energy in interpreter.cvm.callEnergyTemp. - interpreter.intPool.put(stack.pop()) + // We can use this as a temporary value + temp := stack.pop() energy := interpreter.cvm.callEnergyTemp // Pop other call parameters. addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() - toAddr := common.BigToAddress(addr) - value = math.U256(value) + toAddr := common.Address(addr.Bytes22()) // Get the arguments from the memory. - args := memory.GetPtr(inOffset.Int64(), inSize.Int64()) + args := memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) - if value.Sign() != 0 { + if !value.IsZero() { energy += params.CallStipend } - ret, returnEnergy, err := interpreter.cvm.Call(contract, toAddr, args, energy, value) + ret, returnEnergy, err := interpreter.cvm.Call(contract, toAddr, args, energy, value.ToBig()) if err != nil { - stack.push(interpreter.intPool.getZero()) + temp.Clear() } else { - stack.push(interpreter.intPool.get().SetUint64(1)) + temp.SetOne() } + stack.push(&temp) if err == nil || err == errExecutionReverted { memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } contract.Energy += returnEnergy - - interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) return ret, nil } func opCallCode(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // Pop energy. The actual energy is in interpreter.cvm.callEnergyTemp. - interpreter.intPool.put(stack.pop()) + // We use it as a temporary value + temp := stack.pop() energy := interpreter.cvm.callEnergyTemp // Pop other call parameters. addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() - toAddr := common.BigToAddress(addr) - value = math.U256(value) + toAddr := common.Address(addr.Bytes22()) // Get arguments from the memory. - args := memory.GetPtr(inOffset.Int64(), inSize.Int64()) + args := memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) - if value.Sign() != 0 { + if !value.IsZero() { energy += params.CallStipend } - ret, returnEnergy, err := interpreter.cvm.CallCode(contract, toAddr, args, energy, value) + ret, returnEnergy, err := interpreter.cvm.CallCode(contract, toAddr, args, energy, value.ToBig()) if err != nil { - stack.push(interpreter.intPool.getZero()) + temp.Clear() } else { - stack.push(interpreter.intPool.get().SetUint64(1)) + temp.SetOne() } + stack.push(&temp) if err == nil || err == errExecutionReverted { memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } contract.Energy += returnEnergy - - interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize) return ret, nil } func opDelegateCall(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // Pop energy. The actual energy is in interpreter.cvm.callEnergyTemp. - interpreter.intPool.put(stack.pop()) + // We use it as a temporary value + temp := stack.pop() energy := interpreter.cvm.callEnergyTemp // Pop other call parameters. addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() - toAddr := common.BigToAddress(addr) + toAddr := common.Address(addr.Bytes22()) // Get arguments from the memory. - args := memory.GetPtr(inOffset.Int64(), inSize.Int64()) + args := memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) ret, returnEnergy, err := interpreter.cvm.DelegateCall(contract, toAddr, args, energy) if err != nil { - stack.push(interpreter.intPool.getZero()) + temp.Clear() } else { - stack.push(interpreter.intPool.get().SetUint64(1)) + temp.SetOne() } + stack.push(&temp) if err == nil || err == errExecutionReverted { memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } contract.Energy += returnEnergy - - interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize) return ret, nil } func opStaticCall(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { // Pop energy. The actual energy is in interpreter.cvm.callEnergyTemp. - interpreter.intPool.put(stack.pop()) + // We use it as a temporary value + temp := stack.pop() energy := interpreter.cvm.callEnergyTemp // Pop other call parameters. addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() - toAddr := common.BigToAddress(addr) + toAddr := common.Address(addr.Bytes22()) // Get arguments from the memory. - args := memory.GetPtr(inOffset.Int64(), inSize.Int64()) + args := memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) ret, returnEnergy, err := interpreter.cvm.StaticCall(contract, toAddr, args, energy) if err != nil { - stack.push(interpreter.intPool.getZero()) + temp.Clear() } else { - stack.push(interpreter.intPool.get().SetUint64(1)) + temp.SetOne() } + stack.push(&temp) if err == nil || err == errExecutionReverted { memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } contract.Energy += returnEnergy - - interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize) return ret, nil } func opReturn(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { offset, size := stack.pop(), stack.pop() - ret := memory.GetPtr(offset.Int64(), size.Int64()) - - interpreter.intPool.put(offset, size) + ret := memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) return ret, nil } func opRevert(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { offset, size := stack.pop(), stack.pop() - ret := memory.GetPtr(offset.Int64(), size.Int64()) - - interpreter.intPool.put(offset, size) + ret := memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) return ret, nil } @@ -872,8 +765,9 @@ func opStop(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory } func opSuicide(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + beneficiary := stack.pop() balance := interpreter.cvm.StateDB.GetBalance(contract.Address()) - interpreter.cvm.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance) + interpreter.cvm.StateDB.AddBalance(common.Address(beneficiary.Bytes22()), balance) interpreter.cvm.StateDB.Suicide(contract.Address()) return nil, nil @@ -887,10 +781,11 @@ func makeLog(size int) executionFunc { topics := make([]common.Hash, size) mStart, mSize := stack.pop(), stack.pop() for i := 0; i < size; i++ { - topics[i] = common.BigToHash(stack.pop()) + addr := stack.pop() + topics[i] = common.Hash(addr.Bytes32()) } - d := memory.GetCopy(mStart.Int64(), mSize.Int64()) + d := memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64())) interpreter.cvm.StateDB.AddLog(&types.Log{ Address: contract.Address(), Topics: topics, @@ -900,7 +795,6 @@ func makeLog(size int) executionFunc { BlockNumber: interpreter.cvm.BlockNumber.Uint64(), }) - interpreter.intPool.put(mStart, mSize) return nil, nil } } @@ -909,13 +803,13 @@ func makeLog(size int) executionFunc { func opPush1(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { var ( codeLen = uint64(len(contract.Code)) - integer = interpreter.intPool.get() + integer = new(uint256.Int) ) *pc += 1 if *pc < codeLen { stack.push(integer.SetUint64(uint64(contract.Code[*pc]))) } else { - stack.push(integer.SetUint64(0)) + stack.push(integer.Clear()) } return nil, nil } @@ -935,8 +829,9 @@ func makePush(size uint64, pushByteSize int) executionFunc { endMin = startMin + pushByteSize } - integer := interpreter.intPool.get() - stack.push(integer.SetBytes(common.RightPadBytes(contract.Code[startMin:endMin], pushByteSize))) + integer := new(uint256.Int) + stack.push(integer.SetBytes(common.RightPadBytes( + contract.Code[startMin:endMin], pushByteSize))) *pc += size return nil, nil @@ -946,7 +841,7 @@ func makePush(size uint64, pushByteSize int) executionFunc { // make dup instruction function func makeDup(size int64) executionFunc { return func(pc *uint64, interpreter *CVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.dup(interpreter.intPool, int(size)) + stack.dup(int(size)) return nil, nil } } diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 14301c537..70f220c62 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -20,8 +20,8 @@ import ( "bytes" "encoding/json" "fmt" + "github.com/core-coin/uint256" "io/ioutil" - "math/big" "testing" "github.com/core-coin/go-core/common" @@ -97,42 +97,22 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu pc = uint64(0) cvmInterpreter = env.interpreter.(*CVMInterpreter) ) - // Stuff a couple of nonzero bigints into pool, to ensure that ops do not rely on pooled integers to be zero - cvmInterpreter.intPool = poolOfIntPools.get() - cvmInterpreter.intPool.put(big.NewInt(-1337)) - cvmInterpreter.intPool.put(big.NewInt(-1337)) - cvmInterpreter.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, cvmInterpreter, nil, nil, stack) + 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 cvmInterpreter.intPool.pool.len() > 0 { - - poolvals := make(map[*big.Int]struct{}) - poolvals[actual] = struct{}{} - - for cvmInterpreter.intPool.pool.len() > 0 { - key := cvmInterpreter.intPool.get() - if _, exist := poolvals[key]; exist { - t.Errorf("Testcase %v %d, pool contains double-entry", name, i) - } - poolvals[key] = struct{}{} - } - } } - poolOfIntPools.put(cvmInterpreter.intPool) } func TestByteOp(t *testing.T) { @@ -166,6 +146,44 @@ func TestSHL(t *testing.T) { testTwoOperandOp(t, tests, opSHL, "shl") } +func TestAddMod(t *testing.T) { + var ( + env = NewCVM(Context{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + evmInterpreter = NewCVMInterpreter(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, nil, nil, stack) + actual := stack.pop() + if actual.Cmp(expected) != 0 { + t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual) + } + } +} + func TestSHR(t *testing.T) { // Testcases from https://github.com/core-coin/CIPs/blob/master/CIPS/cip-145.md#shr-logical-shift-right tests := []TwoOperandTestcase{ @@ -216,11 +234,10 @@ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcas pc = uint64(0) interpreter = env.interpreter.(*CVMInterpreter) ) - 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)) + x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x)) + y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y)) stack.push(x) stack.push(y) opFn(&pc, interpreter, nil, nil, stack) @@ -268,7 +285,6 @@ func opBenchmark(bench *testing.B, op func(pc *uint64, interpreter *CVMInterpret ) env.interpreter = cvmInterpreter - cvmInterpreter.intPool = poolOfIntPools.get() // convert args byteArgs := make([][]byte, len(args)) for i, arg := range args { @@ -278,13 +294,12 @@ func opBenchmark(bench *testing.B, op func(pc *uint64, interpreter *CVMInterpret bench.ResetTimer() for i := 0; i < bench.N; i++ { for _, arg := range byteArgs { - a := new(big.Int).SetBytes(arg) - stack.push(a) + a := new(uint256.Int) + a.SetBytes(arg) } op(&pc, cvmInterpreter, nil, nil, stack) stack.pop() } - poolOfIntPools.put(cvmInterpreter.intPool) } func BenchmarkOpAdd64(b *testing.B) { @@ -504,21 +519,19 @@ func TestOpMstore(t *testing.T) { ) env.interpreter = cvmInterpreter - cvmInterpreter.intPool = poolOfIntPools.get() mem.Resize(64) pc := uint64(0) v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" - stack.pushN(new(big.Int).SetBytes(common.Hex2Bytes(v)), big.NewInt(0)) + stack.pushN(*new(uint256.Int).SetBytes(common.Hex2Bytes(v)), *new(uint256.Int)) opMstore(&pc, cvmInterpreter, nil, mem, stack) if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v { t.Fatalf("Mstore fail, got %v, expected %v", got, v) } - stack.pushN(big.NewInt(0x1), big.NewInt(0)) + stack.pushN(*new(uint256.Int).SetUint64(0x1), *new(uint256.Int)) opMstore(&pc, cvmInterpreter, nil, mem, stack) if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" { t.Fatalf("Mstore failed to overwrite previous value") } - poolOfIntPools.put(cvmInterpreter.intPool) } func BenchmarkOpMstore(bench *testing.B) { @@ -530,18 +543,16 @@ func BenchmarkOpMstore(bench *testing.B) { ) env.interpreter = cvmInterpreter - cvmInterpreter.intPool = poolOfIntPools.get() mem.Resize(64) pc := uint64(0) - memStart := big.NewInt(0) - value := big.NewInt(0x1337) + memStart := new(uint256.Int) + value := new(uint256.Int).SetUint64(0x1337) bench.ResetTimer() for i := 0; i < bench.N; i++ { - stack.pushN(value, memStart) + stack.pushN(*value, *memStart) opMstore(&pc, cvmInterpreter, nil, mem, stack) } - poolOfIntPools.put(cvmInterpreter.intPool) } func BenchmarkOpSHA3(bench *testing.B) { @@ -552,17 +563,15 @@ func BenchmarkOpSHA3(bench *testing.B) { cvmInterpreter = NewCVMInterpreter(env, env.vmConfig) ) env.interpreter = cvmInterpreter - cvmInterpreter.intPool = poolOfIntPools.get() mem.Resize(32) pc := uint64(0) - start := big.NewInt(0) + start := uint256.NewInt(0) bench.ResetTimer() for i := 0; i < bench.N; i++ { - stack.pushN(big.NewInt(32), start) + stack.pushN(*uint256.NewInt(32), *start) opSha3(&pc, cvmInterpreter, nil, mem, stack) } - poolOfIntPools.put(cvmInterpreter.intPool) } func TestCreate2Addreses(t *testing.T) { //TODO: TEST diff --git a/core/vm/int_pool_verifier.go b/core/vm/int_pool_verifier.go deleted file mode 100644 index 58b1256c6..000000000 --- a/core/vm/int_pool_verifier.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2017 by the Authors -// This file is part of the go-core library. -// -// The go-core 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-core 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-core library. If not, see . - -// +build VERIFY_CVM_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 f20ea3294..000000000 --- a/core/vm/int_pool_verifier_empty.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2017 by the Authors -// This file is part of the go-core library. -// -// The go-core 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-core 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-core library. If not, see . - -// +build !VERIFY_CVM_INTEGER_POOL - -package vm - -const verifyPool = false - -func verifyIntegerPool(ip *intPool) {} diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 646fdeab2..8c779bf74 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -75,8 +75,6 @@ type CVMInterpreter struct { cvm *CVM cfg Config - intPool *intPool - hasher keccakState // SHA3 hasher instance shared across opcodes hasherBuf common.Hash // SHA3 hasher result array shared aross opcodes @@ -111,14 +109,6 @@ func NewCVMInterpreter(cvm *CVM, cfg Config) *CVMInterpreter { // considered a revert-and-consume-all-energy operation except for // errExecutionReverted which means revert-and-keep-energy-left. func (in *CVMInterpreter) 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.cvm.depth++ defer func() { in.cvm.depth-- }() @@ -156,9 +146,6 @@ func (in *CVMInterpreter) 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 { @@ -248,11 +235,6 @@ func (in *CVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( // execute the operation res, err = operation.execute(&pc, in, contract, mem, stack) - // verifyPool is a build flag. Pool verification makes sure the integrity - // of the integer pool by comparing values to a default value. - if verifyPool { - verifyIntegerPool(in.intPool) - } // if the operation clears the return data (e.g. it has returning data) // set the last return to the result of the operation. if operation.returns { diff --git a/core/vm/intpool.go b/core/vm/intpool.go deleted file mode 100644 index e6f5ccc5e..000000000 --- a/core/vm/intpool.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2017 by the Authors -// This file is part of the go-core library. -// -// The go-core 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-core 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-core 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) -} - -// 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 31b85eebd..000000000 --- a/core/vm/intpool_test.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2018 by the Authors -// This file is part of the go-core library. -// -// The go-core 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-core 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-core library. If not, see . - -package vm - -import ( - "testing" -) - -func TestIntPoolPoolGet(t *testing.T) { - poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap) - - nip := poolOfIntPools.get() - if nip == nil { - t.Fatalf("Invalid pool allocation") - } -} - -func TestIntPoolPoolPut(t *testing.T) { - poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap) - - nip := poolOfIntPools.get() - if len(poolOfIntPools.pools) != 0 { - t.Fatalf("Pool got added to list when none should have been") - } - - poolOfIntPools.put(nip) - if len(poolOfIntPools.pools) == 0 { - t.Fatalf("Pool did not get added to list when one should have been") - } -} - -func TestIntPoolPoolReUse(t *testing.T) { - poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap) - nip := poolOfIntPools.get() - poolOfIntPools.put(nip) - poolOfIntPools.get() - - if len(poolOfIntPools.pools) != 0 { - t.Fatalf("Invalid number of pools. Got %d, expected %d", len(poolOfIntPools.pools), 0) - } -} diff --git a/core/vm/logger.go b/core/vm/logger.go index 7e6790717..c5bb829eb 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -58,8 +58,8 @@ type LogConfig struct { type StructLog struct { Pc uint64 `json:"pc"` Op OpCode `json:"op"` - Energy uint64 `json:"energy"` - EnergyCost uint64 `json:"energyCost"` + Energy uint64 `json:"energy"` + EnergyCost uint64 `json:"energyCost"` Memory []byte `json:"memory"` MemorySize int `json:"memSize"` Stack []*big.Int `json:"stack"` @@ -72,8 +72,8 @@ type StructLog struct { // overrides for gencodec type structLogMarshaling struct { Stack []*math.HexOrDecimal256 - Energy math.HexOrDecimal64 - EnergyCost math.HexOrDecimal64 + Energy math.HexOrDecimal64 + EnergyCost math.HexOrDecimal64 Memory hexutil.Bytes OpName string `json:"opName"` // adds call to OpName() in MarshalJSON ErrorString string `json:"error"` // adds call to ErrorString() in MarshalJSON @@ -153,8 +153,8 @@ func (l *StructLogger) CaptureState(env *CVM, pc uint64, op OpCode, energy, cost // 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 } @@ -169,7 +169,7 @@ func (l *StructLogger) CaptureState(env *CVM, pc uint64, op OpCode, energy, cost 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 0c9ad474a..ec7f71ec6 100644 --- a/core/vm/logger_json.go +++ b/core/vm/logger_json.go @@ -50,8 +50,8 @@ func (l *JSONLogger) CaptureState(env *CVM, pc uint64, op OpCode, energy, cost u log := StructLog{ Pc: pc, Op: op, - Energy: energy, - EnergyCost: cost, + Energy: energy, + EnergyCost: cost, MemorySize: memory.Len(), Storage: nil, Depth: depth, @@ -62,7 +62,12 @@ func (l *JSONLogger) CaptureState(env *CVM, pc uint64, op OpCode, energy, cost u 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) } @@ -75,10 +80,10 @@ func (l *JSONLogger) CaptureFault(env *CVM, pc uint64, op OpCode, energy, cost u // CaptureEnd is triggered at end of execution. func (l *JSONLogger) CaptureEnd(output []byte, energyUsed uint64, t time.Duration, err error) error { type endLog struct { - Output string `json:"output"` + Output string `json:"output"` EnergyUsed math.HexOrDecimal64 `json:"energyUsed"` - Time time.Duration `json:"time"` - Err string `json:"error,omitempty"` + Time time.Duration `json:"time"` + Err string `json:"error,omitempty"` } if err != nil { return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(energyUsed), t, err.Error()}) diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index 9eb698364..c1a131468 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -17,6 +17,7 @@ package vm import ( + "github.com/core-coin/uint256" "math/big" "testing" @@ -29,7 +30,7 @@ type dummyContractRef struct { calledForEach bool } -func (dummyContractRef) ReturnEnergy(*big.Int) {} +func (dummyContractRef) ReturnEnergy(*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) {} @@ -56,8 +57,8 @@ func TestStoreCapture(t *testing.T) { stack = newstack() contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0) ) - stack.push(big.NewInt(1)) - stack.push(big.NewInt(0)) + stack.push(uint256.NewInt(1)) + stack.push(new(uint256.Int)) var index common.Hash logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, contract, 0, nil) if len(logger.changedValues[contract.Address()]) == 0 { diff --git a/core/vm/memory.go b/core/vm/memory.go index 144b3481c..5f223bcbd 100644 --- a/core/vm/memory.go +++ b/core/vm/memory.go @@ -18,14 +18,12 @@ package vm import ( "fmt" - "math/big" - - "github.com/core-coin/go-core/common/math" + "github.com/core-coin/uint256" ) // Memory implements a simple memory model for the Core Virtual Machine. type Memory struct { - store []byte + store []byte lastEnergyCost uint64 } @@ -50,7 +48,7 @@ func (m *Memory) Set(offset, size uint64, value []byte) { // Set32 sets the 32 bytes starting at offset to the value of val, left-padded with zeroes to // 32 bytes. -func (m *Memory) Set32(offset uint64, val *big.Int) { +func (m *Memory) Set32(offset uint64, val *uint256.Int) { // length of store may never be less than offset + size. // The store should be resized PRIOR to setting the memory if offset+32 > uint64(len(m.store)) { @@ -59,7 +57,7 @@ func (m *Memory) Set32(offset uint64, val *big.Int) { // Zero the memory area copy(m.store[offset:offset+32], []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) // Fill in relevant bits - math.ReadBits(val, m.store[offset:offset+32]) + val.WriteToSlice(m.store[offset:]) } // Resize resizes the memory to size diff --git a/core/vm/stack.go b/core/vm/stack.go index 90ac3ca5a..6bc2c4c81 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -18,36 +18,35 @@ package vm import ( "fmt" - "math/big" + "github.com/core-coin/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 array. +func (st *Stack) Data() []uint256.Int { return st.data } -func (st *Stack) push(d *big.Int) { +func (st *Stack) push(d *uint256.Int) { // NOTE push limit (1024) is checked in baseCheck - //stackItem := new(big.Int).Set(d) - //st.data = append(st.data, stackItem) - st.data = append(st.data, d) + st.data = append(st.data, *d) } -func (st *Stack) pushN(ds ...*big.Int) { +func (st *Stack) pushN(ds ...uint256.Int) { + // FIXME: Is there a way to pass args by pointers. st.data = append(st.data, ds...) } -func (st *Stack) pop() (ret *big.Int) { +func (st *Stack) pop() (ret uint256.Int) { ret = st.data[len(st.data)-1] st.data = st.data[:len(st.data)-1] return @@ -61,17 +60,17 @@ func (st *Stack) swap(n int) { st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n] } -func (st *Stack) dup(pool *intPool, n int) { - st.push(pool.get().Set(st.data[st.len()-n])) +func (st *Stack) dup(n int) { + st.push(&st.data[st.len()-n]) } -func (st *Stack) peek() *big.Int { - return st.data[st.len()-1] +func (st *Stack) peek() *uint256.Int { + return &st.data[st.len()-1] } // Back returns the n'th item in stack -func (st *Stack) Back(n int) *big.Int { - return st.data[st.len()-n-1] +func (st *Stack) Back(n int) *uint256.Int { + return &st.data[st.len()-n-1] } // Print dumps the content of the stack diff --git a/go.sum b/go.sum index 63337cd78..4abb6f08f 100644 --- a/go.sum +++ b/go.sum @@ -75,6 +75,8 @@ github.com/core-coin/go-goldilocks v1.0.9 h1:NEX5+LXeLpj3yJPKQgEffj2ly33rOvANsWD github.com/core-coin/go-goldilocks v1.0.9/go.mod h1:r6mSidt/OMBXorR8jBJYJttsver3m2EBAcSuf+m2Js0= github.com/core-coin/go-randomy v0.0.14 h1:MIzErlW7djO5IcoMAPNYLOC4YZV47TPmofusu01TGZY= github.com/core-coin/go-randomy v0.0.14/go.mod h1:7YzU3Hrss60CzXlzziTZNAQUN6u+eLAHH1cL1JRGLBE= +github.com/core-coin/uint256 v1.0.0 h1:AzgINl9YCnYDRJBlqFlWsVb4sr814LcvPAvkPyVBFVo= +github.com/core-coin/uint256 v1.0.0/go.mod h1:rrinB/+X6+31MswTK2xwoFGi8i75SH1ILSsRSoOFa1I= github.com/core-coin/go-randomy v0.0.18 h1:m0IW81uirjpT0fCAnS+5PxPrbI+s/zviFK8hfQbhRcs= github.com/core-coin/go-randomy v0.0.18/go.mod h1:7YzU3Hrss60CzXlzziTZNAQUN6u+eLAHH1cL1JRGLBE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= diff --git a/xcb/tracers/tracer.go b/xcb/tracers/tracer.go index 669fa1b53..beb93eb28 100644 --- a/xcb/tracers/tracer.go +++ b/xcb/tracers/tracer.go @@ -163,7 +163,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