2017-04-14 03:29:00 -05:00
// 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/>.
2017-01-05 04:52:10 -06:00
package vm
import (
"github.com/ethereum/go-ethereum/common"
2017-01-04 13:17:24 -06:00
"github.com/ethereum/go-ethereum/common/math"
2017-01-05 04:52:10 -06:00
"github.com/ethereum/go-ethereum/params"
)
2019-01-24 05:14:02 -06:00
// memoryGasCost calculates the quadratic gas for memory expansion. It does so
2017-01-04 13:17:24 -06:00
// only for the memory region that is expanded, not the total memory.
func memoryGasCost ( mem * Memory , newMemSize uint64 ) ( uint64 , error ) {
if newMemSize == 0 {
return 0 , nil
2017-02-02 08:25:42 -06:00
}
2019-04-01 06:56:43 -05:00
// The maximum that will fit in a uint64 is max_word_count - 1. Anything above
// that will result in an overflow. Additionally, a newMemSize which results in
// a newMemSizeWords larger than 0xFFFFFFFF will cause the square operation to
// overflow. The constant 0x1FFFFFFFE0 is the highest number that can be used
// without overflowing the gas calculation.
2019-03-28 16:04:31 -05:00
if newMemSize > 0x1FFFFFFFE0 {
2017-06-28 02:51:31 -05:00
return 0 , errGasUintOverflow
}
2017-01-04 13:17:24 -06:00
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
2017-01-05 04:52:10 -06:00
}
2019-08-05 03:01:02 -05:00
// memoryCopierGas creates the gas functions for the following opcodes, and takes
// the stack position of the operand which determines the size of the data to copy
// as argument:
// CALLDATACOPY (stack position 2)
// CODECOPY (stack position 2)
// EXTCODECOPY (stack poition 3)
// RETURNDATACOPY (stack position 2)
func memoryCopierGas ( stackpos int ) gasFunc {
return func ( evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) ( uint64 , error ) {
// Gas for expanding the memory
gas , err := memoryGasCost ( mem , memorySize )
if err != nil {
return 0 , err
}
// And gas for copying data, charged per word at param.CopyGas
words , overflow := bigUint64 ( stack . Back ( stackpos ) )
if overflow {
return 0 , errGasUintOverflow
}
2017-08-16 05:07:33 -05:00
2019-08-05 03:01:02 -05:00
if words , overflow = math . SafeMul ( toWordSize ( words ) , params . CopyGas ) ; overflow {
return 0 , errGasUintOverflow
}
2017-08-16 05:07:33 -05:00
2019-08-05 03:01:02 -05:00
if gas , overflow = math . SafeAdd ( gas , words ) ; overflow {
return 0 , errGasUintOverflow
}
return gas , nil
2017-08-16 05:07:33 -05:00
}
}
2019-08-05 03:01:02 -05:00
var (
gasCallDataCopy = memoryCopierGas ( 2 )
gasCodeCopy = memoryCopierGas ( 2 )
gasExtCodeCopy = memoryCopierGas ( 3 )
gasReturnDataCopy = memoryCopierGas ( 2 )
)
2017-01-05 04:52:10 -06:00
2019-08-05 03:01:02 -05:00
func gasSStore ( evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) ( uint64 , error ) {
2018-08-11 16:03:54 -05:00
var (
2018-08-12 07:47:03 -05:00
y , x = stack . Back ( 1 ) , stack . Back ( 0 )
current = evm . StateDB . GetState ( contract . Address ( ) , common . BigToHash ( x ) )
2018-08-11 16:03:54 -05:00
)
2018-09-18 08:24:35 -05:00
// The legacy gas metering only takes into consideration the current state
2019-01-24 04:36:30 -06:00
// Legacy rules should be applied if we are in Petersburg (removal of EIP-1283)
// OR Constantinople is not active
if evm . chainRules . IsPetersburg || ! evm . chainRules . IsConstantinople {
2018-09-18 08:24:35 -05:00
// 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)
switch {
case current == ( common . Hash { } ) && y . Sign ( ) != 0 : // 0 => non 0
return params . SstoreSetGas , nil
case current != ( common . Hash { } ) && y . Sign ( ) == 0 : // non 0 => 0
evm . StateDB . AddRefund ( params . SstoreRefundGas )
return params . SstoreClearGas , nil
default : // non 0 => non 0 (or 0 => 0)
return params . SstoreResetGas , nil
}
}
// The new gas metering is based on net gas costs (EIP-1283):
//
// 1. If current value equals new value (this is a no-op), 200 gas is deducted.
// 2. If current value does not equal new value
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
// 2.1.1. If original value is 0, 20000 gas is deducted.
// 2.1.2. Otherwise, 5000 gas is deducted. If new value is 0, add 15000 gas to refund counter.
// 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
// 2.2.1. If original value is not 0
// 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
// 2.2.2. If original value equals new value (this storage slot is reset)
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
value := common . BigToHash ( y )
if current == value { // noop (1)
return params . NetSstoreNoopGas , nil
}
original := evm . StateDB . GetCommittedState ( contract . Address ( ) , common . BigToHash ( x ) )
if original == current {
if original == ( common . Hash { } ) { // create slot (2.1.1)
return params . NetSstoreInitGas , nil
2018-08-11 16:03:54 -05:00
}
2018-09-18 08:24:35 -05:00
if value == ( common . Hash { } ) { // delete slot (2.1.2b)
evm . StateDB . AddRefund ( params . NetSstoreClearRefund )
2018-08-11 16:03:54 -05:00
}
2018-09-18 08:24:35 -05:00
return params . NetSstoreCleanGas , nil // write existing slot (2.1.2)
}
if original != ( common . Hash { } ) {
if current == ( common . Hash { } ) { // recreate slot (2.2.1.1)
evm . StateDB . SubRefund ( params . NetSstoreClearRefund )
} else if value == ( common . Hash { } ) { // delete slot (2.2.1.2)
evm . StateDB . AddRefund ( params . NetSstoreClearRefund )
2018-08-11 16:03:54 -05:00
}
}
2018-09-18 08:24:35 -05:00
if original == value {
if original == ( common . Hash { } ) { // reset to original inexistent slot (2.2.2.1)
evm . StateDB . AddRefund ( params . NetSstoreResetClearRefund )
} else { // reset to original existing slot (2.2.2.2)
evm . StateDB . AddRefund ( params . NetSstoreResetRefund )
2018-08-11 16:03:54 -05:00
}
}
2018-09-18 08:24:35 -05:00
return params . NetSstoreDirtyGas , nil
2018-08-11 16:03:54 -05:00
}
2017-01-04 13:17:24 -06:00
func makeGasLog ( n uint64 ) gasFunc {
2019-08-05 03:01:02 -05:00
return func ( evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) ( uint64 , error ) {
2017-01-04 13:17:24 -06:00
requestedSize , overflow := bigUint64 ( stack . Back ( 1 ) )
if overflow {
return 0 , errGasUintOverflow
}
2017-01-05 04:52:10 -06:00
2017-01-04 13:17:24 -06:00
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
2017-01-05 04:52:10 -06:00
}
}
2019-08-05 03:01:02 -05:00
func gasSha3 ( evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) ( uint64 , error ) {
2017-01-04 13:17:24 -06:00
gas , err := memoryGasCost ( mem , memorySize )
if err != nil {
return 0 , err
}
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
2017-01-05 04:52:10 -06:00
}
2019-08-05 03:01:02 -05:00
// pureMemoryGascost is used by several operations, which aside from their
// static cost have a dynamic cost which is solely based on the memory
// expansion
func pureMemoryGascost ( evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) ( uint64 , error ) {
return memoryGasCost ( mem , memorySize )
2017-01-05 04:52:10 -06:00
}
2019-08-05 03:01:02 -05:00
var (
gasReturn = pureMemoryGascost
gasRevert = pureMemoryGascost
gasMLoad = pureMemoryGascost
gasMStore8 = pureMemoryGascost
gasMStore = pureMemoryGascost
gasCreate = pureMemoryGascost
)
2017-01-05 04:52:10 -06:00
2019-08-05 03:01:02 -05:00
func gasCreate2 ( evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) ( uint64 , error ) {
2018-07-24 09:22:03 -05:00
gas , err := memoryGasCost ( mem , memorySize )
if err != nil {
return 0 , err
}
2018-10-05 02:32:35 -05:00
wordGas , overflow := bigUint64 ( stack . Back ( 2 ) )
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
}
2018-07-24 09:22:03 -05:00
return gas , nil
}
2019-08-05 03:01:02 -05:00
func gasExpFrontier ( evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) ( uint64 , error ) {
expByteLen := uint64 ( ( stack . data [ stack . len ( ) - 2 ] . BitLen ( ) + 7 ) / 8 )
2017-01-05 04:52:10 -06:00
2019-08-05 03:01:02 -05:00
var (
gas = expByteLen * params . ExpByteFrontier // no overflow check required. Max is 256 * ExpByte gas
overflow bool
)
if gas , overflow = math . SafeAdd ( gas , params . ExpGas ) ; overflow {
return 0 , errGasUintOverflow
}
return gas , nil
2017-02-02 08:25:42 -06:00
}
2019-08-05 03:01:02 -05:00
func gasExpEIP158 ( evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) ( uint64 , error ) {
2017-01-04 13:17:24 -06:00
expByteLen := uint64 ( ( stack . data [ stack . len ( ) - 2 ] . BitLen ( ) + 7 ) / 8 )
2017-02-08 06:39:26 -06:00
2017-02-02 08:25:42 -06:00
var (
2019-08-05 03:01:02 -05:00
gas = expByteLen * params . ExpByteEIP158 // no overflow check required. Max is 256 * ExpByte gas
2017-01-04 13:17:24 -06:00
overflow bool
)
2019-01-24 05:14:02 -06:00
if gas , overflow = math . SafeAdd ( gas , params . ExpGas ) ; overflow {
2017-01-04 13:17:24 -06:00
return 0 , errGasUintOverflow
}
return gas , nil
}
2019-08-05 03:01:02 -05:00
func gasCall ( evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) ( uint64 , error ) {
2017-01-04 13:17:24 -06:00
var (
2019-08-05 03:01:02 -05:00
gas uint64
2017-02-28 08:09:11 -06:00
transfersValue = stack . Back ( 2 ) . Sign ( ) != 0
2017-01-04 13:17:24 -06:00
address = common . BigToAddress ( stack . Back ( 1 ) )
2017-01-05 04:52:10 -06:00
)
2019-08-05 03:01:02 -05:00
if evm . chainRules . IsEIP158 {
2017-09-14 02:07:31 -05:00
if transfersValue && evm . StateDB . Empty ( address ) {
2017-01-04 13:17:24 -06:00
gas += params . CallNewAccountGas
2017-01-05 04:52:10 -06:00
}
2017-01-04 13:17:24 -06:00
} else if ! evm . StateDB . Exist ( address ) {
gas += params . CallNewAccountGas
2017-01-05 04:52:10 -06:00
}
if transfersValue {
2017-01-04 13:17:24 -06:00
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
2017-01-05 04:52:10 -06:00
}
2019-08-05 03:01:02 -05:00
evm . callGasTemp , err = callGas ( evm . chainRules . IsEIP150 , contract . Gas , gas , stack . Back ( 0 ) )
2017-01-04 13:17:24 -06:00
if err != nil {
return 0 , err
}
2017-11-28 13:05:49 -06:00
if gas , overflow = math . SafeAdd ( gas , evm . callGasTemp ) ; overflow {
2017-01-04 13:17:24 -06:00
return 0 , errGasUintOverflow
}
return gas , nil
2017-01-05 04:52:10 -06:00
}
2019-08-05 03:01:02 -05:00
func gasCallCode ( evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) ( uint64 , error ) {
2017-01-04 13:17:24 -06:00
memoryGas , err := memoryGasCost ( mem , memorySize )
if err != nil {
return 0 , err
}
2019-08-05 03:01:02 -05:00
var (
gas uint64
overflow bool
)
if stack . Back ( 2 ) . Sign ( ) != 0 {
gas += params . CallValueTransferGas
}
2017-01-04 13:17:24 -06:00
if gas , overflow = math . SafeAdd ( gas , memoryGas ) ; overflow {
return 0 , errGasUintOverflow
2017-01-05 04:52:10 -06:00
}
2019-08-05 03:01:02 -05:00
evm . callGasTemp , err = callGas ( evm . chainRules . IsEIP150 , contract . Gas , gas , stack . Back ( 0 ) )
2017-01-04 13:17:24 -06:00
if err != nil {
return 0 , err
}
2017-11-28 13:05:49 -06:00
if gas , overflow = math . SafeAdd ( gas , evm . callGasTemp ) ; overflow {
2017-01-04 13:17:24 -06:00
return 0 , errGasUintOverflow
}
return gas , nil
2017-01-05 04:52:10 -06:00
}
2019-08-05 03:01:02 -05:00
func gasDelegateCall ( evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) ( uint64 , error ) {
2017-01-04 13:17:24 -06:00
gas , err := memoryGasCost ( mem , memorySize )
if err != nil {
return 0 , err
}
2019-08-05 03:01:02 -05:00
evm . callGasTemp , err = callGas ( evm . chainRules . IsEIP150 , contract . Gas , gas , stack . Back ( 0 ) )
2017-08-15 03:23:23 -05:00
if err != nil {
return 0 , err
}
2019-08-05 03:01:02 -05:00
var overflow bool
2017-11-28 13:05:49 -06:00
if gas , overflow = math . SafeAdd ( gas , evm . callGasTemp ) ; overflow {
2017-08-15 03:23:23 -05:00
return 0 , errGasUintOverflow
}
return gas , nil
}
2019-08-05 03:01:02 -05:00
func gasStaticCall ( evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) ( uint64 , error ) {
2017-08-15 03:23:23 -05:00
gas , err := memoryGasCost ( mem , memorySize )
if err != nil {
return 0 , err
}
2019-08-05 03:01:02 -05:00
evm . callGasTemp , err = callGas ( evm . chainRules . IsEIP150 , contract . Gas , gas , stack . Back ( 0 ) )
2017-01-04 13:17:24 -06:00
if err != nil {
return 0 , err
}
2019-08-05 03:01:02 -05:00
var overflow bool
2017-11-28 13:05:49 -06:00
if gas , overflow = math . SafeAdd ( gas , evm . callGasTemp ) ; overflow {
2017-01-04 13:17:24 -06:00
return 0 , errGasUintOverflow
}
return gas , nil
2017-01-05 04:52:10 -06:00
}
2019-08-05 03:01:02 -05:00
func gasSelfdestruct ( evm * EVM , contract * Contract , stack * Stack , mem * Memory , memorySize uint64 ) ( uint64 , error ) {
var gas uint64
// EIP150 homestead gas reprice fork:
if evm . chainRules . IsEIP150 {
gas = params . SelfdestructGasEIP150
var address = common . BigToAddress ( stack . Back ( 0 ) )
if evm . chainRules . IsEIP158 {
// if empty and transfers value
if evm . StateDB . Empty ( address ) && evm . StateDB . GetBalance ( contract . Address ( ) ) . Sign ( ) != 0 {
gas += params . CreateBySelfdestructGas
}
} else if ! evm . StateDB . Exist ( address ) {
gas += params . CreateBySelfdestructGas
}
}
if ! evm . StateDB . HasSuicided ( contract . Address ( ) ) {
evm . StateDB . AddRefund ( params . SelfdestructRefundGas )
}
return gas , nil
}