Unverified Commit cf667453 authored by Paweł Bylica's avatar Paweł Bylica Committed by GitHub

core/vm: use uint256 in EVM implementation (#20787)

* core/vm: use fixed uint256 library instead of big

* core/vm: remove intpools

* core/vm: upgrade uint256, fixes uint256.NewFromBig

* core/vm: use uint256.Int by value in Stack

* core/vm: upgrade uint256 to v1.0.0

* core/vm: don't preallocate space for 1024 stack items (only 16)
Co-authored-by: 's avatarMartin Holst Swende <martin@swende.se>
parent 39abd92c
...@@ -17,15 +17,14 @@ ...@@ -17,15 +17,14 @@
package vm package vm
import ( import (
"math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
"github.com/holiman/uint256"
) )
// calcMemSize64 calculates the required memory size, and returns // calcMemSize64 calculates the required memory size, and returns
// the size and whether the result overflowed uint64 // 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() { if !l.IsUint64() {
return 0, true return 0, true
} }
...@@ -35,16 +34,16 @@ func calcMemSize64(off, l *big.Int) (uint64, bool) { ...@@ -35,16 +34,16 @@ func calcMemSize64(off, l *big.Int) (uint64, bool) {
// calcMemSize64WithUint calculates the required memory size, and returns // calcMemSize64WithUint calculates the required memory size, and returns
// the size and whether the result overflowed uint64 // the size and whether the result overflowed uint64
// Identical to calcMemSize64, but length is a 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 length is zero, memsize is always zero, regardless of offset
if length64 == 0 { if length64 == 0 {
return 0, false return 0, false
} }
// Check that offset doesn't overflow // Check that offset doesn't overflow
if !off.IsUint64() { offset64, overflow := off.Uint64WithOverflow()
if overflow {
return 0, true return 0, true
} }
offset64 := off.Uint64()
val := offset64 + length64 val := offset64 + length64
// if value < either of it's parts, then it overflowed // if value < either of it's parts, then it overflowed
return val, val < offset64 return val, val < offset64
...@@ -64,22 +63,6 @@ func getData(data []byte, start uint64, size uint64) []byte { ...@@ -64,22 +63,6 @@ func getData(data []byte, start uint64, size uint64) []byte {
return common.RightPadBytes(data[start:end], int(size)) 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. // toWordSize returns the ceiled word size required for memory expansion.
func toWordSize(size uint64) uint64 { func toWordSize(size uint64) uint64 {
if size > math.MaxUint64-31 { if size > math.MaxUint64-31 {
......
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
) )
// ContractRef is a reference to the contract's backing object // 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 ...@@ -81,11 +82,11 @@ func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uin
return c return c
} }
func (c *Contract) validJumpdest(dest *big.Int) bool { func (c *Contract) validJumpdest(dest *uint256.Int) bool {
udest := dest.Uint64() udest, overflow := dest.Uint64WithOverflow()
// PC cannot go beyond len(code) and certainly can't be bigger than 63 bits. // PC cannot go beyond len(code) and certainly can't be bigger than 63bits.
// Don't bother checking for JUMPDEST in that case. // 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 return false
} }
// Only JUMPDESTs allowed for destinations // Only JUMPDESTs allowed for destinations
......
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
) )
// EnableEIP enables the given EIP on the config. // EnableEIP enables the given EIP on the config.
...@@ -63,7 +64,7 @@ func enable1884(jt *JumpTable) { ...@@ -63,7 +64,7 @@ func enable1884(jt *JumpTable) {
} }
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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) callContext.stack.push(balance)
return nil, nil return nil, nil
} }
...@@ -83,7 +84,7 @@ func enable1344(jt *JumpTable) { ...@@ -83,7 +84,7 @@ func enable1344(jt *JumpTable) {
// opChainID implements CHAINID opcode // opChainID implements CHAINID opcode
func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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) callContext.stack.push(chainId)
return nil, nil return nil, nil
} }
......
...@@ -352,7 +352,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte ...@@ -352,7 +352,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, // 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 // but is the correct thing to do and matters on other networks, in tests, and potential
// future scenarios // 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 // 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 // above we revert to the snapshot and consume any gas remaining. Additionally
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
package vm package vm
import ( import (
"math/big" "github.com/holiman/uint256"
) )
// Gas costs // Gas costs
...@@ -34,7 +34,7 @@ const ( ...@@ -34,7 +34,7 @@ const (
// //
// The cost of gas was changed during the homestead price change HF. // 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. // 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 { if isEip150 {
availableGas = availableGas - base availableGas = availableGas - base
gas := availableGas - availableGas/64 gas := availableGas - availableGas/64
......
...@@ -70,7 +70,7 @@ func memoryCopierGas(stackpos int) gasFunc { ...@@ -70,7 +70,7 @@ func memoryCopierGas(stackpos int) gasFunc {
return 0, err return 0, err
} }
// And gas for copying data, charged per word at param.CopyGas // 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 { if overflow {
return 0, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
...@@ -96,7 +96,7 @@ var ( ...@@ -96,7 +96,7 @@ var (
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var ( var (
y, x = stack.Back(1), stack.Back(0) 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 // 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) // Legacy rules should be applied if we are in Petersburg (removal of EIP-1283)
...@@ -131,11 +131,11 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi ...@@ -131,11 +131,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. 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.1. If original value is 0, add 19800 gas to refund counter.
// 2.2.2.2. Otherwise, add 4800 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) if current == value { // noop (1)
return params.NetSstoreNoopGas, nil 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 == current {
if original == (common.Hash{}) { // create slot (2.1.1) if original == (common.Hash{}) { // create slot (2.1.1)
return params.NetSstoreInitGas, nil return params.NetSstoreInitGas, nil
...@@ -183,14 +183,14 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m ...@@ -183,14 +183,14 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
// Gas sentry honoured, do the actual gas calculation based on the stored value // Gas sentry honoured, do the actual gas calculation based on the stored value
var ( var (
y, x = stack.Back(1), stack.Back(0) 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) if current == value { // noop (1)
return params.SstoreNoopGasEIP2200, nil return params.SstoreNoopGasEIP2200, nil
} }
original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x)) original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
if original == current { if original == current {
if original == (common.Hash{}) { // create slot (2.1.1) if original == (common.Hash{}) { // create slot (2.1.1)
return params.SstoreInitGasEIP2200, nil return params.SstoreInitGasEIP2200, nil
...@@ -219,7 +219,7 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m ...@@ -219,7 +219,7 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
func makeGasLog(n uint64) gasFunc { func makeGasLog(n uint64) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { 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 { if overflow {
return 0, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
...@@ -252,7 +252,7 @@ func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize ...@@ -252,7 +252,7 @@ func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
if err != nil { if err != nil {
return 0, err return 0, err
} }
wordGas, overflow := bigUint64(stack.Back(1)) wordGas, overflow := stack.Back(1).Uint64WithOverflow()
if overflow { if overflow {
return 0, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
...@@ -286,7 +286,7 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS ...@@ -286,7 +286,7 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
if err != nil { if err != nil {
return 0, err return 0, err
} }
wordGas, overflow := bigUint64(stack.Back(2)) wordGas, overflow := stack.Back(2).Uint64WithOverflow()
if overflow { if overflow {
return 0, ErrGasUintOverflow return 0, ErrGasUintOverflow
} }
...@@ -328,8 +328,8 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor ...@@ -328,8 +328,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) { func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var ( var (
gas uint64 gas uint64
transfersValue = stack.Back(2).Sign() != 0 transfersValue = !stack.Back(2).IsZero()
address = common.BigToAddress(stack.Back(1)) address = common.Address(stack.Back(1).Bytes20())
) )
if evm.chainRules.IsEIP158 { if evm.chainRules.IsEIP158 {
if transfersValue && evm.StateDB.Empty(address) { if transfersValue && evm.StateDB.Empty(address) {
...@@ -422,7 +422,7 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me ...@@ -422,7 +422,7 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
// EIP150 homestead gas reprice fork: // EIP150 homestead gas reprice fork:
if evm.chainRules.IsEIP150 { if evm.chainRules.IsEIP150 {
gas = params.SelfdestructGasEIP150 gas = params.SelfdestructGasEIP150
var address = common.BigToAddress(stack.Back(0)) var address = common.Address(stack.Back(0).Bytes20())
if evm.chainRules.IsEIP158 { if evm.chainRules.IsEIP158 {
// if empty and transfers value // if empty and transfers value
......
...@@ -17,302 +17,170 @@ ...@@ -17,302 +17,170 @@
package vm package vm
import ( import (
"math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
"golang.org/x/crypto/sha3" "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) { func opAdd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := callContext.stack.pop(), callContext.stack.peek()
math.U256(y.Add(x, y)) y.Add(&x, y)
interpreter.intPool.putOne(x)
return nil, nil return nil, nil
} }
func opSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := callContext.stack.pop(), callContext.stack.peek()
math.U256(y.Sub(x, y)) y.Sub(&x, y)
interpreter.intPool.putOne(x)
return nil, nil return nil, nil
} }
func opMul(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opMul(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.pop() x, y := callContext.stack.pop(), callContext.stack.peek()
callContext.stack.push(math.U256(x.Mul(x, y))) y.Mul(&x, y)
interpreter.intPool.putOne(y)
return nil, nil return nil, nil
} }
func opDiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opDiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := callContext.stack.pop(), callContext.stack.peek()
if y.Sign() != 0 { y.Div(&x, y)
math.U256(y.Div(x, y))
} else {
y.SetUint64(0)
}
interpreter.intPool.putOne(x)
return nil, nil return nil, nil
} }
func opSdiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSdiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := math.S256(callContext.stack.pop()), math.S256(callContext.stack.pop()) x, y := callContext.stack.pop(), callContext.stack.peek()
res := interpreter.intPool.getZero() y.SDiv(&x, y)
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)
return nil, nil return nil, nil
} }
func opMod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opMod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.pop() x, y := callContext.stack.pop(), callContext.stack.peek()
if y.Sign() == 0 { y.Mod(&x, y)
callContext.stack.push(x.SetUint64(0))
} else {
callContext.stack.push(math.U256(x.Mod(x, y)))
}
interpreter.intPool.putOne(y)
return nil, nil return nil, nil
} }
func opSmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := math.S256(callContext.stack.pop()), math.S256(callContext.stack.pop()) x, y := callContext.stack.pop(), callContext.stack.peek()
res := interpreter.intPool.getZero() y.SMod(&x, y)
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)
return nil, nil return nil, nil
} }
func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
base, exponent := callContext.stack.pop(), callContext.stack.pop() base, exponent := callContext.stack.pop(), callContext.stack.peek()
// some shortcuts exponent.Exp(&base, exponent)
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)
return nil, nil return nil, nil
} }
func opSignExtend(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSignExtend(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
back := callContext.stack.pop() back, num := callContext.stack.pop(), callContext.stack.peek()
if back.Cmp(big.NewInt(31)) < 0 { num.ExtendSign(num, &back)
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)
return nil, nil return nil, nil
} }
func opNot(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opNot(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x := callContext.stack.peek() x := callContext.stack.peek()
math.U256(x.Not(x)) x.Not(x)
return nil, nil return nil, nil
} }
func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := callContext.stack.pop(), callContext.stack.peek()
if x.Cmp(y) < 0 { if x.Lt(y) {
y.SetUint64(1) y.SetOne()
} else { } else {
y.SetUint64(0) y.Clear()
} }
interpreter.intPool.putOne(x)
return nil, nil return nil, nil
} }
func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := callContext.stack.pop(), callContext.stack.peek()
if x.Cmp(y) > 0 { if x.Gt(y) {
y.SetUint64(1) y.SetOne()
} else { } else {
y.SetUint64(0) y.Clear()
} }
interpreter.intPool.putOne(x)
return nil, nil return nil, nil
} }
func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := callContext.stack.pop(), callContext.stack.peek()
if x.Slt(y) {
xSign := x.Cmp(tt255) y.SetOne()
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 { } else {
y.SetUint64(0) y.Clear()
}
} }
interpreter.intPool.putOne(x)
return nil, nil return nil, nil
} }
func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := callContext.stack.pop(), callContext.stack.peek()
if x.Sgt(y) {
xSign := x.Cmp(tt255) y.SetOne()
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 { } else {
y.SetUint64(0) y.Clear()
} }
}
interpreter.intPool.putOne(x)
return nil, nil return nil, nil
} }
func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := callContext.stack.pop(), callContext.stack.peek()
if x.Cmp(y) == 0 { if x.Eq(y) {
y.SetUint64(1) y.SetOne()
} else { } else {
y.SetUint64(0) y.Clear()
} }
interpreter.intPool.putOne(x)
return nil, nil return nil, nil
} }
func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x := callContext.stack.peek() x := callContext.stack.peek()
if x.Sign() > 0 { if x.IsZero() {
x.SetUint64(0) x.SetOne()
} else { } else {
x.SetUint64(1) x.Clear()
} }
return nil, nil return nil, nil
} }
func opAnd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opAnd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.pop() x, y := callContext.stack.pop(), callContext.stack.peek()
callContext.stack.push(x.And(x, y)) y.And(&x, y)
interpreter.intPool.putOne(y)
return nil, nil return nil, nil
} }
func opOr(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opOr(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := callContext.stack.pop(), callContext.stack.peek()
y.Or(x, y) y.Or(&x, y)
interpreter.intPool.putOne(x)
return nil, nil return nil, nil
} }
func opXor(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opXor(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek() x, y := callContext.stack.pop(), callContext.stack.peek()
y.Xor(x, y) y.Xor(&x, y)
interpreter.intPool.putOne(x)
return nil, nil return nil, nil
} }
func opByte(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opByte(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
th, val := callContext.stack.pop(), callContext.stack.peek() th, val := callContext.stack.pop(), callContext.stack.peek()
if th.Cmp(common.Big32) < 0 { val.Byte(&th)
b := math.Byte(val, 32, int(th.Int64()))
val.SetUint64(uint64(b))
} else {
val.SetUint64(0)
}
interpreter.intPool.putOne(th)
return nil, nil return nil, nil
} }
func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop() x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek()
if z.Cmp(bigZero) > 0 { if z.IsZero() {
x.Add(x, y) z.Clear()
x.Mod(x, z)
callContext.stack.push(math.U256(x))
} else { } else {
callContext.stack.push(x.SetUint64(0)) z.AddMod(&x, &y, z)
} }
interpreter.intPool.put(y, z)
return nil, nil return nil, nil
} }
func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop() x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek()
if z.Cmp(bigZero) > 0 { z.MulMod(&x, &y, z)
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)
return nil, nil return nil, nil
} }
...@@ -321,16 +189,12 @@ func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] ...@@ -321,16 +189,12 @@ func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
// and pushes on the stack arg2 shifted to the left by arg1 number of bits. // 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) { 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 // 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()) shift, value := callContext.stack.pop(), callContext.stack.peek()
defer interpreter.intPool.putOne(shift) // First operand back into the pool if shift.LtUint64(256) {
value.Lsh(value, uint(shift.Uint64()))
if shift.Cmp(common.Big256) >= 0 { } else {
value.SetUint64(0) value.Clear()
return nil, nil
} }
n := uint(shift.Uint64())
math.U256(value.Lsh(value, n))
return nil, nil return nil, nil
} }
...@@ -339,16 +203,12 @@ func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt ...@@ -339,16 +203,12 @@ func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
// and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill. // 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) { 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 // 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()) shift, value := callContext.stack.pop(), callContext.stack.peek()
defer interpreter.intPool.putOne(shift) // First operand back into the pool if shift.LtUint64(256) {
value.Rsh(value, uint(shift.Uint64()))
if shift.Cmp(common.Big256) >= 0 { } else {
value.SetUint64(0) value.Clear()
return nil, nil
} }
n := uint(shift.Uint64())
math.U256(value.Rsh(value, n))
return nil, nil return nil, nil
} }
...@@ -356,29 +216,24 @@ func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt ...@@ -356,29 +216,24 @@ func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
// The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2, // 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. // 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) { 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 := callContext.stack.pop(), callContext.stack.peek()
shift, value := math.U256(callContext.stack.pop()), math.S256(callContext.stack.pop()) if shift.GtUint64(256) {
defer interpreter.intPool.putOne(shift) // First operand back into the pool
if shift.Cmp(common.Big256) >= 0 {
if value.Sign() >= 0 { if value.Sign() >= 0 {
value.SetUint64(0) value.Clear()
} else { } else {
value.SetInt64(-1) // Max negative shift: all bits set
value.SetAllOne()
} }
callContext.stack.push(math.U256(value))
return nil, nil return nil, nil
} }
n := uint(shift.Uint64()) n := uint(shift.Uint64())
value.Rsh(value, n) value.SRsh(value, n)
callContext.stack.push(math.U256(value))
return nil, nil return nil, nil
} }
func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
offset, size := callContext.stack.pop(), callContext.stack.pop() offset, size := callContext.stack.pop(), callContext.stack.peek()
data := callContext.memory.GetPtr(offset.Int64(), size.Int64()) data := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
if interpreter.hasher == nil { if interpreter.hasher == nil {
interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState) interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState)
...@@ -392,45 +247,50 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by ...@@ -392,45 +247,50 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
if evm.vmConfig.EnablePreimageRecording { if evm.vmConfig.EnablePreimageRecording {
evm.StateDB.AddPreimage(interpreter.hasherBuf, data) 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 return nil, nil
} }
func opAddress(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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 return nil, nil
} }
func opBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
slot := callContext.stack.peek() 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 return nil, nil
} }
func opOrigin(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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 return nil, nil
} }
func opCaller(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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 return nil, nil
} }
func opCallValue(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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 return nil, nil
} }
func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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 return nil, nil
} }
func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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 return nil, nil
} }
...@@ -440,14 +300,20 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCt ...@@ -440,14 +300,20 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
dataOffset = callContext.stack.pop() dataOffset = callContext.stack.pop()
length = 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 return nil, nil
} }
func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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 return nil, nil
} }
...@@ -456,30 +322,33 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *call ...@@ -456,30 +322,33 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *call
memOffset = callContext.stack.pop() memOffset = callContext.stack.pop()
dataOffset = callContext.stack.pop() dataOffset = callContext.stack.pop()
length = 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 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 return nil, nil
} }
func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
slot := callContext.stack.peek() 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 return nil, nil
} }
func opCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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) callContext.stack.push(l)
return nil, nil return nil, nil
} }
...@@ -489,24 +358,32 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ( ...@@ -489,24 +358,32 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
codeOffset = callContext.stack.pop() codeOffset = callContext.stack.pop()
length = 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) callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
interpreter.intPool.put(memOffset, codeOffset, length)
return nil, nil return nil, nil
} }
func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var ( var (
addr = common.BigToAddress(callContext.stack.pop()) stack = callContext.stack
memOffset = callContext.stack.pop() a = stack.pop()
codeOffset = callContext.stack.pop() memOffset = stack.pop()
length = callContext.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) callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
interpreter.intPool.put(memOffset, codeOffset, length)
return nil, nil return nil, nil
} }
...@@ -538,9 +415,9 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx ...@@ -538,9 +415,9 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
// this account should be regarded as a non-existent account and zero should be returned. // 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) { func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
slot := callContext.stack.peek() slot := callContext.stack.peek()
address := common.BigToAddress(slot) address := common.Address(slot.Bytes20())
if interpreter.evm.StateDB.Empty(address) { if interpreter.evm.StateDB.Empty(address) {
slot.SetUint64(0) slot.Clear()
} else { } else {
slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes()) slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes())
} }
...@@ -548,56 +425,69 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx ...@@ -548,56 +425,69 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
} }
func opGasprice(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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 return nil, nil
} }
func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
num := callContext.stack.pop() num := callContext.stack.peek()
num64, overflow := num.Uint64WithOverflow()
n := interpreter.intPool.get().Sub(interpreter.evm.BlockNumber, common.Big257) if overflow {
if num.Cmp(n) > 0 && num.Cmp(interpreter.evm.BlockNumber) < 0 { num.Clear()
callContext.stack.push(interpreter.evm.GetHash(num.Uint64()).Big()) return nil, nil
}
var upper, lower uint64
upper = interpreter.evm.BlockNumber.Uint64()
if upper < 257 {
lower = 0
} else { } else {
callContext.stack.push(interpreter.intPool.getZero()) lower = upper - 256
}
if num64 >= lower && num64 < upper {
num.SetBytes(interpreter.evm.GetHash(num64).Bytes())
} else {
num.Clear()
} }
interpreter.intPool.put(num, n)
return nil, nil return nil, nil
} }
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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 return nil, nil
} }
func opTimestamp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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 return nil, nil
} }
func opNumber(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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 return nil, nil
} }
func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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 return nil, nil
} }
func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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 return nil, nil
} }
func opPop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opPop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
interpreter.intPool.putOne(callContext.stack.pop()) callContext.stack.pop()
return nil, nil return nil, nil
} }
func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
v := callContext.stack.peek() v := callContext.stack.peek()
offset := v.Int64() offset := int64(v.Uint64())
v.SetBytes(callContext.memory.GetPtr(offset, 32)) v.SetBytes(callContext.memory.GetPtr(offset, 32))
return nil, nil return nil, nil
} }
...@@ -605,58 +495,51 @@ func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]b ...@@ -605,58 +495,51 @@ func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]b
func opMstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opMstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// pop value of the stack // pop value of the stack
mStart, val := callContext.stack.pop(), callContext.stack.pop() mStart, val := callContext.stack.pop(), callContext.stack.pop()
callContext.memory.Set32(mStart.Uint64(), val) callContext.memory.Set32(mStart.Uint64(), &val)
interpreter.intPool.put(mStart, val)
return nil, nil return nil, nil
} }
func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
off, val := callContext.stack.pop().Int64(), callContext.stack.pop().Int64() off, val := callContext.stack.pop(), callContext.stack.pop()
callContext.memory.store[off] = byte(val & 0xff) callContext.memory.store[off.Uint64()] = byte(val.Uint64())
return nil, nil return nil, nil
} }
func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
loc := callContext.stack.peek() 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()) loc.SetBytes(val.Bytes())
return nil, nil return nil, nil
} }
func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
loc := common.BigToHash(callContext.stack.pop()) loc := callContext.stack.pop()
val := callContext.stack.pop() val := callContext.stack.pop()
interpreter.evm.StateDB.SetState(callContext.contract.Address(), loc, common.BigToHash(val)) interpreter.evm.StateDB.SetState(callContext.contract.Address(),
common.Hash(loc.Bytes32()), common.Hash(val.Bytes32()))
interpreter.intPool.putOne(val)
return nil, nil return nil, nil
} }
func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
pos := callContext.stack.pop() pos := callContext.stack.pop()
if !callContext.contract.validJumpdest(pos) { if !callContext.contract.validJumpdest(&pos) {
return nil, ErrInvalidJump return nil, ErrInvalidJump
} }
*pc = pos.Uint64() *pc = pos.Uint64()
interpreter.intPool.putOne(pos)
return nil, nil return nil, nil
} }
func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
pos, cond := callContext.stack.pop(), callContext.stack.pop() pos, cond := callContext.stack.pop(), callContext.stack.pop()
if cond.Sign() != 0 { if !cond.IsZero() {
if !callContext.contract.validJumpdest(pos) { if !callContext.contract.validJumpdest(&pos) {
return nil, ErrInvalidJump return nil, ErrInvalidJump
} }
*pc = pos.Uint64() *pc = pos.Uint64()
} else { } else {
*pc++ *pc++
} }
interpreter.intPool.put(pos, cond)
return nil, nil return nil, nil
} }
...@@ -682,7 +565,6 @@ func opJumpSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ ...@@ -682,7 +565,6 @@ func opJumpSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
} }
callContext.rstack.push(*pc) callContext.rstack.push(*pc)
*pc = posU64 + 1 *pc = posU64 + 1
interpreter.intPool.put(pos)
return nil, nil return nil, nil
} }
...@@ -698,17 +580,17 @@ func opReturnSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ...@@ -698,17 +580,17 @@ func opReturnSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
} }
func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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 return nil, nil
} }
func opMsize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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 return nil, nil
} }
func opGas(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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 return nil, nil
} }
...@@ -716,28 +598,30 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([] ...@@ -716,28 +598,30 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
var ( var (
value = callContext.stack.pop() value = callContext.stack.pop()
offset, size = callContext.stack.pop(), 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 gas = callContext.contract.Gas
) )
if interpreter.evm.chainRules.IsEIP150 { if interpreter.evm.chainRules.IsEIP150 {
gas -= gas / 64 gas -= gas / 64
} }
// reuse size int for stackvalue
stackvalue := size
callContext.contract.UseGas(gas) 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 // Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only // homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must // rule) and treat as an error, if the ruleset is frontier we must
// ignore this error and pretend the operation was successful. // ignore this error and pretend the operation was successful.
if interpreter.evm.chainRules.IsHomestead && suberr == ErrCodeStoreOutOfGas { if interpreter.evm.chainRules.IsHomestead && suberr == ErrCodeStoreOutOfGas {
callContext.stack.push(interpreter.intPool.getZero()) stackvalue.Clear()
} else if suberr != nil && suberr != ErrCodeStoreOutOfGas { } else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
callContext.stack.push(interpreter.intPool.getZero()) stackvalue.Clear()
} else { } else {
callContext.stack.push(interpreter.intPool.get().SetBytes(addr.Bytes())) stackvalue.SetBytes(addr.Bytes())
} }
callContext.stack.push(&stackvalue)
callContext.contract.Gas += returnGas callContext.contract.Gas += returnGas
interpreter.intPool.put(value, offset, size)
if suberr == ErrExecutionReverted { if suberr == ErrExecutionReverted {
return res, nil return res, nil
...@@ -750,22 +634,25 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ ...@@ -750,22 +634,25 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
endowment = callContext.stack.pop() endowment = callContext.stack.pop()
offset, size = callContext.stack.pop(), callContext.stack.pop() offset, size = callContext.stack.pop(), callContext.stack.pop()
salt = 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 gas = callContext.contract.Gas
) )
// Apply EIP150 // Apply EIP150
gas -= gas / 64 gas -= gas / 64
callContext.contract.UseGas(gas) 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. // Push item on the stack based on the returned error.
if suberr != nil { if suberr != nil {
callContext.stack.push(interpreter.intPool.getZero()) stackvalue.Clear()
} else { } else {
callContext.stack.push(interpreter.intPool.get().SetBytes(addr.Bytes())) stackvalue.SetBytes(addr.Bytes())
} }
callContext.stack.push(&stackvalue)
callContext.contract.Gas += returnGas callContext.contract.Gas += returnGas
interpreter.intPool.put(endowment, offset, size, salt)
if suberr == ErrExecutionReverted { if suberr == ErrExecutionReverted {
return res, nil return res, nil
...@@ -774,126 +661,130 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ ...@@ -774,126 +661,130 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
} }
func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
stack := callContext.stack
// Pop gas. The actual gas in interpreter.evm.callGasTemp. // 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 gas := interpreter.evm.callGasTemp
// Pop other call parameters. // 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() addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.BigToAddress(addr) toAddr := common.Address(addr.Bytes20())
value = math.U256(value)
// Get the arguments from the memory. // Get the arguments from the memory.
args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64()) args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
if value.Sign() != 0 { if !value.IsZero() {
gas += params.CallStipend 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 { if err != nil {
callContext.stack.push(interpreter.intPool.getZero()) temp.Clear()
} else { } else {
callContext.stack.push(interpreter.intPool.get().SetUint64(1)) temp.SetOne()
} }
stack.push(&temp)
if err == nil || err == ErrExecutionReverted { if err == nil || err == ErrExecutionReverted {
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
} }
callContext.contract.Gas += returnGas callContext.contract.Gas += returnGas
interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize)
return ret, nil return ret, nil
} }
func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp. // 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 gas := interpreter.evm.callGasTemp
// Pop other call parameters. // 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() addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.BigToAddress(addr) toAddr := common.Address(addr.Bytes20())
value = math.U256(value)
// Get arguments from the memory. // 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 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 { if err != nil {
callContext.stack.push(interpreter.intPool.getZero()) temp.Clear()
} else { } else {
callContext.stack.push(interpreter.intPool.get().SetUint64(1)) temp.SetOne()
} }
stack.push(&temp)
if err == nil || err == ErrExecutionReverted { if err == nil || err == ErrExecutionReverted {
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
} }
callContext.contract.Gas += returnGas callContext.contract.Gas += returnGas
interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize)
return ret, nil return ret, nil
} }
func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
stack := callContext.stack
// Pop gas. The actual gas is in interpreter.evm.callGasTemp. // 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 gas := interpreter.evm.callGasTemp
// Pop other call parameters. // Pop other call parameters.
addr, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop() addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.BigToAddress(addr) toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory. // 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) ret, returnGas, err := interpreter.evm.DelegateCall(callContext.contract, toAddr, args, gas)
if err != nil { if err != nil {
callContext.stack.push(interpreter.intPool.getZero()) temp.Clear()
} else { } else {
callContext.stack.push(interpreter.intPool.get().SetUint64(1)) temp.SetOne()
} }
stack.push(&temp)
if err == nil || err == ErrExecutionReverted { if err == nil || err == ErrExecutionReverted {
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
} }
callContext.contract.Gas += returnGas callContext.contract.Gas += returnGas
interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize)
return ret, nil return ret, nil
} }
func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp. // 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 gas := interpreter.evm.callGasTemp
// Pop other call parameters. // Pop other call parameters.
addr, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop() addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.BigToAddress(addr) toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory. // 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) ret, returnGas, err := interpreter.evm.StaticCall(callContext.contract, toAddr, args, gas)
if err != nil { if err != nil {
callContext.stack.push(interpreter.intPool.getZero()) temp.Clear()
} else { } else {
callContext.stack.push(interpreter.intPool.get().SetUint64(1)) temp.SetOne()
} }
stack.push(&temp)
if err == nil || err == ErrExecutionReverted { if err == nil || err == ErrExecutionReverted {
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
} }
callContext.contract.Gas += returnGas callContext.contract.Gas += returnGas
interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize)
return ret, nil return ret, nil
} }
func opReturn(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opReturn(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
offset, size := callContext.stack.pop(), callContext.stack.pop() 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, nil
} }
func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
offset, size := callContext.stack.pop(), callContext.stack.pop() 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, nil
} }
...@@ -902,9 +793,9 @@ func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by ...@@ -902,9 +793,9 @@ func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
} }
func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
beneficiary := callContext.stack.pop()
balance := interpreter.evm.StateDB.GetBalance(callContext.contract.Address()) 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()) interpreter.evm.StateDB.Suicide(callContext.contract.Address())
return nil, nil return nil, nil
} }
...@@ -915,12 +806,14 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([ ...@@ -915,12 +806,14 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
func makeLog(size int) executionFunc { func makeLog(size int) executionFunc {
return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
topics := make([]common.Hash, size) 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++ { 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{ interpreter.evm.StateDB.AddLog(&types.Log{
Address: callContext.contract.Address(), Address: callContext.contract.Address(),
Topics: topics, Topics: topics,
...@@ -930,7 +823,6 @@ func makeLog(size int) executionFunc { ...@@ -930,7 +823,6 @@ func makeLog(size int) executionFunc {
BlockNumber: interpreter.evm.BlockNumber.Uint64(), BlockNumber: interpreter.evm.BlockNumber.Uint64(),
}) })
interpreter.intPool.put(mStart, mSize)
return nil, nil return nil, nil
} }
} }
...@@ -939,13 +831,13 @@ func makeLog(size int) executionFunc { ...@@ -939,13 +831,13 @@ func makeLog(size int) executionFunc {
func opPush1(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { func opPush1(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var ( var (
codeLen = uint64(len(callContext.contract.Code)) codeLen = uint64(len(callContext.contract.Code))
integer = interpreter.intPool.get() integer = new(uint256.Int)
) )
*pc += 1 *pc += 1
if *pc < codeLen { if *pc < codeLen {
callContext.stack.push(integer.SetUint64(uint64(callContext.contract.Code[*pc]))) callContext.stack.push(integer.SetUint64(uint64(callContext.contract.Code[*pc])))
} else { } else {
callContext.stack.push(integer.SetUint64(0)) callContext.stack.push(integer.Clear())
} }
return nil, nil return nil, nil
} }
...@@ -965,8 +857,9 @@ func makePush(size uint64, pushByteSize int) executionFunc { ...@@ -965,8 +857,9 @@ func makePush(size uint64, pushByteSize int) executionFunc {
endMin = startMin + pushByteSize endMin = startMin + pushByteSize
} }
integer := interpreter.intPool.get() integer := new(uint256.Int)
callContext.stack.push(integer.SetBytes(common.RightPadBytes(callContext.contract.Code[startMin:endMin], pushByteSize))) callContext.stack.push(integer.SetBytes(common.RightPadBytes(
callContext.contract.Code[startMin:endMin], pushByteSize)))
*pc += size *pc += size
return nil, nil return nil, nil
...@@ -976,7 +869,7 @@ func makePush(size uint64, pushByteSize int) executionFunc { ...@@ -976,7 +869,7 @@ func makePush(size uint64, pushByteSize int) executionFunc {
// make dup instruction function // make dup instruction function
func makeDup(size int64) executionFunc { func makeDup(size int64) executionFunc {
return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) { 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 return nil, nil
} }
} }
......
...@@ -21,12 +21,12 @@ import ( ...@@ -21,12 +21,12 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/big"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
) )
type TwoOperandTestcase struct { type TwoOperandTestcase struct {
...@@ -98,42 +98,23 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu ...@@ -98,42 +98,23 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
pc = uint64(0) pc = uint64(0)
evmInterpreter = env.interpreter.(*EVMInterpreter) 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 { for i, test := range tests {
x := new(big.Int).SetBytes(common.Hex2Bytes(test.X)) x := new(uint256.Int).SetBytes(common.Hex2Bytes(test.X))
y := new(big.Int).SetBytes(common.Hex2Bytes(test.Y)) y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Y))
expected := new(big.Int).SetBytes(common.Hex2Bytes(test.Expected)) expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected))
stack.push(x) stack.push(x)
stack.push(y) stack.push(y)
opFn(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil}) opFn(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil})
if len(stack.data) != 1 {
t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data))
}
actual := stack.pop() actual := stack.pop()
if actual.Cmp(expected) != 0 { if actual.Cmp(expected) != 0 {
t.Errorf("Testcase %v %d, %v(%x, %x): expected %x, got %x", name, i, name, x, y, expected, actual) 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) { func TestByteOp(t *testing.T) {
...@@ -209,6 +190,44 @@ func TestSAR(t *testing.T) { ...@@ -209,6 +190,44 @@ func TestSAR(t *testing.T) {
testTwoOperandOp(t, tests, opSAR, "sar") testTwoOperandOp(t, tests, opSAR, "sar")
} }
func TestAddMod(t *testing.T) {
var (
env = NewEVM(Context{}, 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, 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 // getResult is a convenience function to generate the expected values
func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase { func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
var ( var (
...@@ -217,11 +236,10 @@ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcas ...@@ -217,11 +236,10 @@ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcas
pc = uint64(0) pc = uint64(0)
interpreter = env.interpreter.(*EVMInterpreter) interpreter = env.interpreter.(*EVMInterpreter)
) )
interpreter.intPool = poolOfIntPools.get()
result := make([]TwoOperandTestcase, len(args)) result := make([]TwoOperandTestcase, len(args))
for i, param := range args { for i, param := range args {
x := new(big.Int).SetBytes(common.Hex2Bytes(param.x)) x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x))
y := new(big.Int).SetBytes(common.Hex2Bytes(param.y)) y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
stack.push(x) stack.push(x)
stack.push(y) stack.push(y)
opFn(&pc, interpreter, &callCtx{nil, stack, rstack, nil}) opFn(&pc, interpreter, &callCtx{nil, stack, rstack, nil})
...@@ -269,7 +287,6 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { ...@@ -269,7 +287,6 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
) )
env.interpreter = evmInterpreter env.interpreter = evmInterpreter
evmInterpreter.intPool = poolOfIntPools.get()
// convert args // convert args
byteArgs := make([][]byte, len(args)) byteArgs := make([][]byte, len(args))
for i, arg := range args { for i, arg := range args {
...@@ -279,13 +296,13 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) { ...@@ -279,13 +296,13 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
bench.ResetTimer() bench.ResetTimer()
for i := 0; i < bench.N; i++ { for i := 0; i < bench.N; i++ {
for _, arg := range byteArgs { for _, arg := range byteArgs {
a := new(big.Int).SetBytes(arg) a := new(uint256.Int)
a.SetBytes(arg)
stack.push(a) stack.push(a)
} }
op(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil}) op(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil})
stack.pop() stack.pop()
} }
poolOfIntPools.put(evmInterpreter.intPool)
} }
func BenchmarkOpAdd64(b *testing.B) { func BenchmarkOpAdd64(b *testing.B) {
...@@ -505,21 +522,19 @@ func TestOpMstore(t *testing.T) { ...@@ -505,21 +522,19 @@ func TestOpMstore(t *testing.T) {
) )
env.interpreter = evmInterpreter env.interpreter = evmInterpreter
evmInterpreter.intPool = poolOfIntPools.get()
mem.Resize(64) mem.Resize(64)
pc := uint64(0) pc := uint64(0)
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
stack.pushN(new(big.Int).SetBytes(common.Hex2Bytes(v)), big.NewInt(0)) stack.pushN(*new(uint256.Int).SetBytes(common.Hex2Bytes(v)), *new(uint256.Int))
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil}) opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v { if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v {
t.Fatalf("Mstore fail, got %v, expected %v", got, v) t.Fatalf("Mstore fail, got %v, expected %v", got, v)
} }
stack.pushN(big.NewInt(0x1), big.NewInt(0)) stack.pushN(*new(uint256.Int).SetUint64(0x1), *new(uint256.Int))
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil}) opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" { if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
t.Fatalf("Mstore failed to overwrite previous value") t.Fatalf("Mstore failed to overwrite previous value")
} }
poolOfIntPools.put(evmInterpreter.intPool)
} }
func BenchmarkOpMstore(bench *testing.B) { func BenchmarkOpMstore(bench *testing.B) {
...@@ -531,18 +546,16 @@ func BenchmarkOpMstore(bench *testing.B) { ...@@ -531,18 +546,16 @@ func BenchmarkOpMstore(bench *testing.B) {
) )
env.interpreter = evmInterpreter env.interpreter = evmInterpreter
evmInterpreter.intPool = poolOfIntPools.get()
mem.Resize(64) mem.Resize(64)
pc := uint64(0) pc := uint64(0)
memStart := big.NewInt(0) memStart := new(uint256.Int)
value := big.NewInt(0x1337) value := new(uint256.Int).SetUint64(0x1337)
bench.ResetTimer() bench.ResetTimer()
for i := 0; i < bench.N; i++ { for i := 0; i < bench.N; i++ {
stack.pushN(value, memStart) stack.pushN(*value, *memStart)
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil}) opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
} }
poolOfIntPools.put(evmInterpreter.intPool)
} }
func BenchmarkOpSHA3(bench *testing.B) { func BenchmarkOpSHA3(bench *testing.B) {
...@@ -553,17 +566,15 @@ func BenchmarkOpSHA3(bench *testing.B) { ...@@ -553,17 +566,15 @@ func BenchmarkOpSHA3(bench *testing.B) {
evmInterpreter = NewEVMInterpreter(env, env.vmConfig) evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
) )
env.interpreter = evmInterpreter env.interpreter = evmInterpreter
evmInterpreter.intPool = poolOfIntPools.get()
mem.Resize(32) mem.Resize(32)
pc := uint64(0) pc := uint64(0)
start := big.NewInt(0) start := uint256.NewInt()
bench.ResetTimer() bench.ResetTimer()
for i := 0; i < bench.N; i++ { for i := 0; i < bench.N; i++ {
stack.pushN(big.NewInt(32), start) stack.pushN(*uint256.NewInt().SetUint64(32), *start)
opSha3(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil}) opSha3(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
} }
poolOfIntPools.put(evmInterpreter.intPool)
} }
func TestCreate2Addreses(t *testing.T) { func TestCreate2Addreses(t *testing.T) {
...@@ -637,6 +648,5 @@ func TestCreate2Addreses(t *testing.T) { ...@@ -637,6 +648,5 @@ func TestCreate2Addreses(t *testing.T) {
if !bytes.Equal(expected.Bytes(), address.Bytes()) { if !bytes.Equal(expected.Bytes(), address.Bytes()) {
t.Errorf("test %d: expected %s, got %s", i, expected.String(), address.String()) t.Errorf("test %d: expected %s, got %s", i, expected.String(), address.String())
} }
} }
} }
// 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 <http://www.gnu.org/licenses/>.
// +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))
}
}
}
// 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 <http://www.gnu.org/licenses/>.
// +build !VERIFY_EVM_INTEGER_POOL
package vm
const verifyPool = false
func verifyIntegerPool(ip *intPool) {}
...@@ -84,8 +84,6 @@ type EVMInterpreter struct { ...@@ -84,8 +84,6 @@ type EVMInterpreter struct {
evm *EVM evm *EVM
cfg Config cfg Config
intPool *intPool
hasher keccakState // Keccak256 hasher instance shared across opcodes hasher keccakState // Keccak256 hasher instance shared across opcodes
hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes
...@@ -141,13 +139,6 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { ...@@ -141,13 +139,6 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
// considered a revert-and-consume-all-gas operation except for // considered a revert-and-consume-all-gas operation except for
// ErrExecutionReverted which means revert-and-keep-gas-left. // ErrExecutionReverted which means revert-and-keep-gas-left.
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) { 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 // Increment the call depth which is restricted to 1024
in.evm.depth++ in.evm.depth++
...@@ -193,9 +184,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( ...@@ -193,9 +184,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
) )
contract.Input = input 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 { if in.cfg.Debug {
defer func() { defer func() {
if err != nil { if err != nil {
...@@ -290,11 +278,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( ...@@ -290,11 +278,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// execute the operation // execute the operation
res, err = operation.execute(&pc, in, callContext) 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) // if the operation clears the return data (e.g. it has returning data)
// set the last return to the result of the operation. // set the last return to the result of the operation.
if operation.returns { if operation.returns {
......
// 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 <http://www.gnu.org/licenses/>.
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)
}
}
// 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 <http://www.gnu.org/licenses/>.
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)
}
}
...@@ -159,8 +159,8 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui ...@@ -159,8 +159,8 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
// it in the local storage container. // it in the local storage container.
if op == SSTORE && stack.len() >= 2 { if op == SSTORE && stack.len() >= 2 {
var ( var (
value = common.BigToHash(stack.data[stack.len()-2]) value = common.Hash(stack.data[stack.len()-2].Bytes32())
address = common.BigToHash(stack.data[stack.len()-1]) address = common.Hash(stack.data[stack.len()-1].Bytes32())
) )
l.changedValues[contract.Address()][address] = value l.changedValues[contract.Address()][address] = value
} }
...@@ -175,7 +175,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui ...@@ -175,7 +175,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
if !l.cfg.DisableStack { if !l.cfg.DisableStack {
stck = make([]*big.Int, len(stack.Data())) stck = make([]*big.Int, len(stack.Data()))
for i, item := range 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 // Copy a snapshot of the current storage to a new container
......
...@@ -62,7 +62,12 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint ...@@ -62,7 +62,12 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
log.Memory = memory.Data() log.Memory = memory.Data()
} }
if !l.cfg.DisableStack { 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
log.ReturnStack = rStack.data log.ReturnStack = rStack.data
} }
return l.encoder.Encode(log) return l.encoder.Encode(log)
......
...@@ -23,6 +23,7 @@ import ( ...@@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
) )
type dummyContractRef struct { type dummyContractRef struct {
...@@ -57,8 +58,8 @@ func TestStoreCapture(t *testing.T) { ...@@ -57,8 +58,8 @@ func TestStoreCapture(t *testing.T) {
rstack = newReturnStack() rstack = newReturnStack()
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0) contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0)
) )
stack.push(big.NewInt(1)) stack.push(uint256.NewInt().SetUint64(1))
stack.push(big.NewInt(0)) stack.push(uint256.NewInt())
var index common.Hash var index common.Hash
logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, rstack, contract, 0, nil) logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, rstack, contract, 0, nil)
if len(logger.changedValues[contract.Address()]) == 0 { if len(logger.changedValues[contract.Address()]) == 0 {
......
...@@ -18,9 +18,8 @@ package vm ...@@ -18,9 +18,8 @@ package vm
import ( import (
"fmt" "fmt"
"math/big"
"github.com/ethereum/go-ethereum/common/math" "github.com/holiman/uint256"
) )
// Memory implements a simple memory model for the ethereum virtual machine. // Memory implements a simple memory model for the ethereum virtual machine.
...@@ -50,7 +49,7 @@ func (m *Memory) Set(offset, size uint64, value []byte) { ...@@ -50,7 +49,7 @@ func (m *Memory) Set(offset, size uint64, value []byte) {
// Set32 sets the 32 bytes starting at offset to the value of val, left-padded with zeroes to // Set32 sets the 32 bytes starting at offset to the value of val, left-padded with zeroes to
// 32 bytes. // 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. // length of store may never be less than offset + size.
// The store should be resized PRIOR to setting the memory // The store should be resized PRIOR to setting the memory
if offset+32 > uint64(len(m.store)) { if offset+32 > uint64(len(m.store)) {
...@@ -59,7 +58,7 @@ func (m *Memory) Set32(offset uint64, val *big.Int) { ...@@ -59,7 +58,7 @@ func (m *Memory) Set32(offset uint64, val *big.Int) {
// Zero the memory area // 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}) 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 // Fill in relevant bits
math.ReadBits(val, m.store[offset:offset+32]) val.WriteToSlice(m.store[offset:])
} }
// Resize resizes the memory to size // Resize resizes the memory to size
......
...@@ -18,36 +18,36 @@ package vm ...@@ -18,36 +18,36 @@ package vm
import ( import (
"fmt" "fmt"
"math/big"
"github.com/holiman/uint256"
) )
// Stack is an object for basic stack operations. Items popped to the stack are // Stack is an object for basic stack operations. Items popped to the stack are
// expected to be changed and modified. stack does not take care of adding newly // expected to be changed and modified. stack does not take care of adding newly
// initialised objects. // initialised objects.
type Stack struct { type Stack struct {
data []*big.Int data []uint256.Int
} }
func newstack() *Stack { 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. // Data returns the underlying uint256.Int array.
func (st *Stack) Data() []*big.Int { func (st *Stack) Data() []uint256.Int {
return st.data 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 // NOTE push limit (1024) is checked in baseCheck
//stackItem := new(big.Int).Set(d) st.data = append(st.data, *d)
//st.data = append(st.data, stackItem)
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...) 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] ret = st.data[len(st.data)-1]
st.data = st.data[:len(st.data)-1] st.data = st.data[:len(st.data)-1]
return return
...@@ -61,17 +61,17 @@ func (st *Stack) swap(n int) { ...@@ -61,17 +61,17 @@ func (st *Stack) swap(n int) {
st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n] 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) { func (st *Stack) dup(n int) {
st.push(pool.get().Set(st.data[st.len()-n])) st.push(&st.data[st.len()-n])
} }
func (st *Stack) peek() *big.Int { func (st *Stack) peek() *uint256.Int {
return st.data[st.len()-1] return &st.data[st.len()-1]
} }
// Back returns the n'th item in stack // Back returns the n'th item in stack
func (st *Stack) Back(n int) *big.Int { func (st *Stack) Back(n int) *uint256.Int {
return st.data[st.len()-n-1] return &st.data[st.len()-n-1]
} }
// Print dumps the content of the stack // Print dumps the content of the stack
......
...@@ -162,7 +162,7 @@ func (sw *stackWrapper) peek(idx int) *big.Int { ...@@ -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) log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx)
return new(big.Int) 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 // pushObject assembles a JSVM object wrapping a swappable stack and pushes it
......
...@@ -31,6 +31,7 @@ require ( ...@@ -31,6 +31,7 @@ require (
github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989 github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277 github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277
github.com/hashicorp/golang-lru v0.5.4 github.com/hashicorp/golang-lru v0.5.4
github.com/holiman/uint256 v1.0.0
github.com/huin/goupnp v1.0.0 github.com/huin/goupnp v1.0.0
github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883 github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458
......
...@@ -93,6 +93,10 @@ github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277 h1:E0whKx ...@@ -93,6 +93,10 @@ github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277 h1:E0whKx
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/holiman/uint256 v0.0.0-20200319132535-a4d16d7dba8d h1:GHyibjawOjlV6CVRfc7nAD2fFiwadHU2p87viNQtG/c=
github.com/holiman/uint256 v0.0.0-20200319132535-a4d16d7dba8d/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
github.com/holiman/uint256 v1.0.0 h1:GV654hUWgO9gVQwgjMyup5bkUCWI9t87+LSjB9fAo5c=
github.com/holiman/uint256 v1.0.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo=
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment