forked from cerc-io/plugeth
8b57c49490
Reworked the EVM gas instructions to use 64bit integers rather than arbitrary size big ints. All gas operations, be it additions, multiplications or divisions, are checked and guarded against 64 bit integer overflows. In additon, most of the protocol paramaters in the params package have been converted to uint64 and are now constants rather than variables. * common/math: added overflow check ops * core: vmenv, env renamed to evm * eth, internal/ethapi, les: unmetered eth_call and cancel methods * core/vm: implemented big.Int pool for evm instructions * core/vm: unexported intPool methods & verification methods * core/vm: added memoryGasCost overflow check and test
417 lines
12 KiB
Go
417 lines
12 KiB
Go
package vm
|
|
|
|
import (
|
|
gmath "math"
|
|
"math/big"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/common/math"
|
|
"github.com/ethereum/go-ethereum/params"
|
|
)
|
|
|
|
// memoryGasCosts calculates the quadratic gas for memory expansion. It does so
|
|
// only for the memory region that is expanded, not the total memory.
|
|
func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
|
|
// The maximum that will fit in a uint64 is max_word_count - 1
|
|
// anything above that will result in an overflow.
|
|
if newMemSize > gmath.MaxUint64-32 {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
|
|
if newMemSize == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
newMemSizeWords := toWordSize(newMemSize)
|
|
newMemSize = newMemSizeWords * 32
|
|
|
|
if newMemSize > uint64(mem.Len()) {
|
|
square := newMemSizeWords * newMemSizeWords
|
|
linCoef := newMemSizeWords * params.MemoryGas
|
|
quadCoef := square / params.QuadCoeffDiv
|
|
newTotalFee := linCoef + quadCoef
|
|
|
|
fee := newTotalFee - mem.lastGasCost
|
|
mem.lastGasCost = newTotalFee
|
|
|
|
return fee, nil
|
|
}
|
|
return 0, nil
|
|
}
|
|
|
|
func constGasFunc(gas uint64) gasFunc {
|
|
return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
return gas, nil
|
|
}
|
|
}
|
|
|
|
func gasCalldataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
gas, err := memoryGasCost(mem, memorySize)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
var overflow bool
|
|
if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
|
|
words, overflow := bigUint64(stack.Back(2))
|
|
if overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
|
|
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
|
|
if gas, overflow = math.SafeAdd(gas, words); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
return gas, nil
|
|
}
|
|
|
|
func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
var (
|
|
y, x = stack.Back(1), stack.Back(0)
|
|
val = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
|
|
)
|
|
// This checks for 3 scenario's and calculates gas accordingly
|
|
// 1. From a zero-value address to a non-zero value (NEW VALUE)
|
|
// 2. From a non-zero value address to a zero-value address (DELETE)
|
|
// 3. From a non-zero to a non-zero (CHANGE)
|
|
if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
|
|
// 0 => non 0
|
|
return params.SstoreSetGas, nil
|
|
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
|
|
evm.StateDB.AddRefund(new(big.Int).SetUint64(params.SstoreRefundGas))
|
|
|
|
return params.SstoreClearGas, nil
|
|
} else {
|
|
// non 0 => non 0 (or 0 => 0)
|
|
return params.SstoreResetGas, nil
|
|
}
|
|
}
|
|
|
|
func makeGasLog(n uint64) gasFunc {
|
|
return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
requestedSize, overflow := bigUint64(stack.Back(1))
|
|
if overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
|
|
gas, err := memoryGasCost(mem, memorySize)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
|
|
var memorySizeGas uint64
|
|
if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
return gas, nil
|
|
}
|
|
}
|
|
|
|
func gasSha3(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
var overflow bool
|
|
gas, err := memoryGasCost(mem, memorySize)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if gas, overflow = math.SafeAdd(gas, params.Sha3Gas); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
|
|
wordGas, overflow := bigUint64(stack.Back(1))
|
|
if overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
return gas, nil
|
|
}
|
|
|
|
func gasCodeCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
gas, err := memoryGasCost(mem, memorySize)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
var overflow bool
|
|
if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
|
|
wordGas, overflow := bigUint64(stack.Back(2))
|
|
if overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.CopyGas); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
return gas, nil
|
|
}
|
|
|
|
func gasExtCodeCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
gas, err := memoryGasCost(mem, memorySize)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
var overflow bool
|
|
if gas, overflow = math.SafeAdd(gas, gt.ExtcodeCopy); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
|
|
wordGas, overflow := bigUint64(stack.Back(3))
|
|
if overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
|
|
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.CopyGas); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
|
|
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
return gas, nil
|
|
}
|
|
|
|
func gasMLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
var overflow bool
|
|
gas, err := memoryGasCost(mem, memorySize)
|
|
if err != nil {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
return gas, nil
|
|
}
|
|
|
|
func gasMStore8(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
var overflow bool
|
|
gas, err := memoryGasCost(mem, memorySize)
|
|
if err != nil {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
return gas, nil
|
|
}
|
|
|
|
func gasMStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
var overflow bool
|
|
gas, err := memoryGasCost(mem, memorySize)
|
|
if err != nil {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
return gas, nil
|
|
}
|
|
|
|
func gasCreate(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
var overflow bool
|
|
gas, err := memoryGasCost(mem, memorySize)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if gas, overflow = math.SafeAdd(gas, params.CreateGas); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
return gas, nil
|
|
}
|
|
|
|
func gasBalance(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
return gt.Balance, nil
|
|
}
|
|
|
|
func gasExtCodeSize(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
return gt.ExtcodeSize, nil
|
|
}
|
|
|
|
func gasSLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
return gt.SLoad, nil
|
|
}
|
|
|
|
func gasExp(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
|
|
|
var (
|
|
gas = expByteLen * gt.ExpByte // no overflow check required. Max is 256 * ExpByte gas
|
|
overflow bool
|
|
)
|
|
if gas, overflow = math.SafeAdd(gas, GasSlowStep); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
return gas, nil
|
|
}
|
|
|
|
func gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
var (
|
|
gas = gt.Calls
|
|
transfersValue = stack.Back(2).BitLen() > 0
|
|
address = common.BigToAddress(stack.Back(1))
|
|
eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber)
|
|
)
|
|
if eip158 {
|
|
if evm.StateDB.Empty(address) && transfersValue {
|
|
gas += params.CallNewAccountGas
|
|
}
|
|
} else if !evm.StateDB.Exist(address) {
|
|
gas += params.CallNewAccountGas
|
|
}
|
|
if transfersValue {
|
|
gas += params.CallValueTransferGas
|
|
}
|
|
memoryGas, err := memoryGasCost(mem, memorySize)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
var overflow bool
|
|
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
|
|
cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
// Replace the stack item with the new gas calculation. This means that
|
|
// either the original item is left on the stack or the item is replaced by:
|
|
// (availableGas - gas) * 63 / 64
|
|
// We replace the stack item so that it's available when the opCall instruction is
|
|
// called. This information is otherwise lost due to the dependency on *current*
|
|
// available gas.
|
|
stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
|
|
|
|
if gas, overflow = math.SafeAdd(gas, cg); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
return gas, nil
|
|
}
|
|
|
|
func gasCallCode(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
gas := gt.Calls
|
|
if stack.Back(2).BitLen() > 0 {
|
|
gas += params.CallValueTransferGas
|
|
}
|
|
memoryGas, err := memoryGasCost(mem, memorySize)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
var overflow bool
|
|
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
|
|
cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
// Replace the stack item with the new gas calculation. This means that
|
|
// either the original item is left on the stack or the item is replaced by:
|
|
// (availableGas - gas) * 63 / 64
|
|
// We replace the stack item so that it's available when the opCall instruction is
|
|
// called. This information is otherwise lost due to the dependency on *current*
|
|
// available gas.
|
|
stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
|
|
|
|
if gas, overflow = math.SafeAdd(gas, cg); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
return gas, nil
|
|
}
|
|
|
|
func gasReturn(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
return memoryGasCost(mem, memorySize)
|
|
}
|
|
|
|
func gasSuicide(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
var gas uint64
|
|
// EIP150 homestead gas reprice fork:
|
|
if evm.ChainConfig().IsEIP150(evm.BlockNumber) {
|
|
gas = gt.Suicide
|
|
var (
|
|
address = common.BigToAddress(stack.Back(0))
|
|
eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber)
|
|
)
|
|
|
|
if eip158 {
|
|
// if empty and transfers value
|
|
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).BitLen() > 0 {
|
|
gas += gt.CreateBySuicide
|
|
}
|
|
} else if !evm.StateDB.Exist(address) {
|
|
gas += gt.CreateBySuicide
|
|
}
|
|
}
|
|
|
|
if !evm.StateDB.HasSuicided(contract.Address()) {
|
|
evm.StateDB.AddRefund(new(big.Int).SetUint64(params.SuicideRefundGas))
|
|
}
|
|
return gas, nil
|
|
}
|
|
|
|
func gasDelegateCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
gas, err := memoryGasCost(mem, memorySize)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
var overflow bool
|
|
if gas, overflow = math.SafeAdd(gas, gt.Calls); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
|
|
cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
// Replace the stack item with the new gas calculation. This means that
|
|
// either the original item is left on the stack or the item is replaced by:
|
|
// (availableGas - gas) * 63 / 64
|
|
// We replace the stack item so that it's available when the opCall instruction is
|
|
// called.
|
|
stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
|
|
|
|
if gas, overflow = math.SafeAdd(gas, cg); overflow {
|
|
return 0, errGasUintOverflow
|
|
}
|
|
return gas, nil
|
|
}
|
|
|
|
func gasPush(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
return GasFastestStep, nil
|
|
}
|
|
|
|
func gasSwap(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
return GasFastestStep, nil
|
|
}
|
|
|
|
func gasDup(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
|
return GasFastestStep, nil
|
|
}
|