Skip to content

Commit 00ba748

Browse files
obscurenkaralabe
authored andcommitted
[release/1.4.18] core, core/vm: added gas price variance table
This implements 1b & 1c of EIP150 by adding a new GasTable which must be returned from the RuleSet config method. This table is used to determine the gas prices for the current epoch. Please note that when the CreateBySuicide gas price is set it is assumed that we're in the new epoch phase. In addition this PR will serve as temporary basis while refactorisation in being done in the EVM64 PR, which will substentially overhaul the gas price code. (cherry picked from commit 64af2aa)
1 parent 8d81eb9 commit 00ba748

40 files changed

+77182
-67
lines changed

cmd/ethtest/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func runTestWithReader(test string, r io.Reader) error {
7474
var err error
7575
switch strings.ToLower(test) {
7676
case "bk", "block", "blocktest", "blockchaintest", "blocktests", "blockchaintests":
77-
err = tests.RunBlockTestWithReader(params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, r, skipTests)
77+
err = tests.RunBlockTestWithReader(params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, params.MainNetHomesteadGasRepriceBlock, r, skipTests)
7878
case "st", "state", "statetest", "statetests":
7979
rs := tests.RuleSet{HomesteadBlock: params.MainNetHomesteadBlock, DAOForkBlock: params.MainNetDAOForkBlock, DAOForkSupport: true}
8080
err = tests.RunStateTestWithReader(rs, r, skipTests)

cmd/evm/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"github.com/ethereum/go-ethereum/crypto"
3434
"github.com/ethereum/go-ethereum/ethdb"
3535
"github.com/ethereum/go-ethereum/logger/glog"
36+
"github.com/ethereum/go-ethereum/params"
3637
"gopkg.in/urfave/cli.v1"
3738
)
3839

@@ -222,6 +223,9 @@ func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg
222223
type ruleSet struct{}
223224

224225
func (ruleSet) IsHomestead(*big.Int) bool { return true }
226+
func (ruleSet) GasTable(*big.Int) params.GasTable {
227+
return params.GasTableHomesteadGasRepriceFork
228+
}
225229

226230
func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} }
227231
func (self *VMEnv) Vm() vm.Vm { return self.evm }

cmd/utils/flags.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,13 @@ func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainC
809809
}
810810
config.DAOForkSupport = true
811811
}
812+
if config.HomesteadGasRepriceBlock == nil {
813+
if ctx.GlobalBool(TestNetFlag.Name) {
814+
config.HomesteadGasRepriceBlock = params.TestNetHomesteadGasRepriceBlock
815+
} else {
816+
config.HomesteadGasRepriceBlock = params.MainNetHomesteadGasRepriceBlock
817+
}
818+
}
812819
// Force override any existing configs if explicitly requested
813820
switch {
814821
case ctx.GlobalBool(SupportDAOFork.Name):

core/config.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"math/big"
2222

2323
"github.com/ethereum/go-ethereum/core/vm"
24+
"github.com/ethereum/go-ethereum/params"
2425
)
2526

2627
var ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error
@@ -35,6 +36,8 @@ type ChainConfig struct {
3536
DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork switch block (nil = no fork)
3637
DAOForkSupport bool `json:"daoForkSupport"` // Whether the nodes supports or opposes the DAO hard-fork
3738

39+
HomesteadGasRepriceBlock *big.Int `json:"homesteadGasRepriceBlock"` // Homestead gas reprice switch block (nil = no fork)
40+
3841
VmConfig vm.Config `json:"-"`
3942
}
4043

@@ -45,3 +48,14 @@ func (c *ChainConfig) IsHomestead(num *big.Int) bool {
4548
}
4649
return num.Cmp(c.HomesteadBlock) >= 0
4750
}
51+
52+
// GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice).
53+
//
54+
// The returned GasTable's fields shouldn't, under any circumstances, be changed.
55+
func (c *ChainConfig) GasTable(num *big.Int) params.GasTable {
56+
if c.HomesteadGasRepriceBlock == nil || num == nil || num.Cmp(c.HomesteadGasRepriceBlock) < 0 {
57+
return params.GasTableHomestead
58+
}
59+
60+
return params.GasTableHomesteadGasRepriceFork
61+
}

core/vm/environment.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,16 @@ import (
2020
"math/big"
2121

2222
"github.com/ethereum/go-ethereum/common"
23+
"github.com/ethereum/go-ethereum/params"
2324
)
2425

2526
// RuleSet is an interface that defines the current rule set during the
2627
// execution of the EVM instructions (e.g. whether it's homestead)
2728
type RuleSet interface {
2829
IsHomestead(*big.Int) bool
30+
// GasTable returns the gas prices for this phase, which is based on
31+
// block number passed in.
32+
GasTable(*big.Int) params.GasTable
2933
}
3034

3135
// Environment is an EVM requirement and helper which allows access to outside

core/vm/gas.go

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,27 @@ var (
3535
GasStop = big.NewInt(0)
3636

3737
GasContractByte = big.NewInt(200)
38+
39+
n64 = big.NewInt(64)
3840
)
3941

42+
// calcGas returns the actual gas cost of the call.
43+
//
44+
// The cost of gas was changed during the homestead price change HF. To allow for EIP150
45+
// to be implemented. The returned gas is gas - base * 63 / 64.
46+
func callGas(gasTable params.GasTable, availableGas, base, callCost *big.Int) *big.Int {
47+
if gasTable.CreateBySuicide != nil {
48+
availableGas = new(big.Int).Sub(availableGas, base)
49+
g := new(big.Int).Div(availableGas, n64)
50+
g.Sub(availableGas, g)
51+
52+
if g.Cmp(callCost) < 0 {
53+
return g
54+
}
55+
}
56+
return callCost
57+
}
58+
4059
// baseCheck checks for any stack error underflows
4160
func baseCheck(op OpCode, stack *stack, gas *big.Int) error {
4261
// PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit
@@ -127,18 +146,19 @@ var _baseCheck = map[OpCode]req{
127146
MSIZE: {0, GasQuickStep, 1},
128147
GAS: {0, GasQuickStep, 1},
129148
BLOCKHASH: {1, GasExtStep, 1},
130-
BALANCE: {1, GasExtStep, 1},
131-
EXTCODESIZE: {1, GasExtStep, 1},
132-
EXTCODECOPY: {4, GasExtStep, 0},
149+
BALANCE: {1, Zero, 1},
150+
EXTCODESIZE: {1, Zero, 1},
151+
EXTCODECOPY: {4, Zero, 0},
133152
SLOAD: {1, params.SloadGas, 1},
134153
SSTORE: {2, Zero, 0},
135154
SHA3: {2, params.Sha3Gas, 1},
136155
CREATE: {3, params.CreateGas, 1},
137-
CALL: {7, params.CallGas, 1},
138-
CALLCODE: {7, params.CallGas, 1},
139-
DELEGATECALL: {6, params.CallGas, 1},
140-
JUMPDEST: {0, params.JumpdestGas, 0},
156+
// Zero is calculated in the gasSwitch
157+
CALL: {7, Zero, 1},
158+
CALLCODE: {7, Zero, 1},
159+
DELEGATECALL: {6, Zero, 1},
141160
SUICIDE: {1, Zero, 0},
161+
JUMPDEST: {0, params.JumpdestGas, 0},
142162
RETURN: {2, Zero, 0},
143163
PUSH1: {0, GasFastestStep, 1},
144164
DUP1: {0, Zero, 1},

core/vm/instructions.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,12 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract
514514
input = memory.Get(offset.Int64(), size.Int64())
515515
gas = new(big.Int).Set(contract.Gas)
516516
)
517-
contract.UseGas(contract.Gas)
517+
if env.RuleSet().GasTable(env.BlockNumber()).CreateBySuicide != nil {
518+
gas.Div(gas, n64)
519+
gas = gas.Sub(contract.Gas, gas)
520+
}
521+
522+
contract.UseGas(gas)
518523
_, addr, suberr := env.Create(contract, input, gas, contract.Price, value)
519524
// Push item on the stack based on the returned error. If the ruleset is
520525
// homestead we must check for CodeStoreOutOfGasError (homestead only

core/vm/runtime/runtime.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,16 @@ import (
2525
"github.com/ethereum/go-ethereum/core/vm"
2626
"github.com/ethereum/go-ethereum/crypto"
2727
"github.com/ethereum/go-ethereum/ethdb"
28+
"github.com/ethereum/go-ethereum/params"
2829
)
2930

3031
// The default, always homestead, rule set for the vm env
3132
type ruleSet struct{}
3233

3334
func (ruleSet) IsHomestead(*big.Int) bool { return true }
35+
func (ruleSet) GasTable(*big.Int) params.GasTable {
36+
return params.GasTableHomesteadGasRepriceFork
37+
}
3438

3539
// Config is a basic type specifying certain configuration flags for running
3640
// the EVM.

core/vm/util_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,17 @@
1616

1717
package vm
1818

19-
import "math/big"
19+
import (
20+
"math/big"
21+
22+
"github.com/ethereum/go-ethereum/params"
23+
)
2024

2125
type ruleSet struct {
2226
hs *big.Int
2327
}
2428

2529
func (r ruleSet) IsHomestead(n *big.Int) bool { return n.Cmp(r.hs) >= 0 }
30+
func (r ruleSet) GasTable(*big.Int) params.GasTable {
31+
return params.GasTableHomestead
32+
}

core/vm/vm.go

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ type EVM struct {
4444
env Environment
4545
jumpTable vmJumpTable
4646
cfg Config
47-
48-
logger *Logger
47+
logger *Logger
48+
gasTable params.GasTable
4949
}
5050

5151
// New returns a new instance of the EVM.
@@ -60,6 +60,7 @@ func New(env Environment, cfg Config) *EVM {
6060
jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber()),
6161
cfg: cfg,
6262
logger: logger,
63+
gasTable: env.RuleSet().GasTable(env.BlockNumber()),
6364
}
6465
}
6566

@@ -177,7 +178,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
177178
// Get the memory location of pc
178179
op = contract.GetOp(pc)
179180
// calculate the new memory size and gas price for the current executing opcode
180-
newMemSize, cost, err = calculateGasAndSize(evm.env, contract, caller, op, statedb, mem, stack)
181+
newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, statedb, mem, stack)
181182
if err != nil {
182183
return nil, err
183184
}
@@ -242,7 +243,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
242243

243244
// calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for
244245
// the operation. This does not reduce gas or resizes the memory.
245-
func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
246+
func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
246247
var (
247248
gas = new(big.Int)
248249
newMemSize *big.Int = new(big.Int)
@@ -254,6 +255,24 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
254255

255256
// stack Check, memory resize & gas phase
256257
switch op {
258+
case SUICIDE:
259+
// if suicide is not nil: homestead gas fork
260+
if gasTable.CreateBySuicide != nil {
261+
gas.Set(gasTable.Suicide)
262+
if !env.Db().Exist(common.BigToAddress(stack.data[len(stack.data)-1])) {
263+
gas.Add(gas, gasTable.CreateBySuicide)
264+
}
265+
}
266+
267+
if !statedb.HasSuicided(contract.Address()) {
268+
statedb.AddRefund(params.SuicideRefundGas)
269+
}
270+
case EXTCODESIZE:
271+
gas.Set(gasTable.ExtcodeSize)
272+
case BALANCE:
273+
gas.Set(gasTable.Balance)
274+
case SLOAD:
275+
gas.Set(gasTable.SLoad)
257276
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
258277
n := int(op - SWAP1 + 2)
259278
err := stack.require(n)
@@ -282,6 +301,8 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
282301
gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas))
283302

284303
newMemSize = calcMemSize(mStart, mSize)
304+
305+
quadMemGas(mem, newMemSize, gas)
285306
case EXP:
286307
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas))
287308
case SSTORE:
@@ -310,67 +331,100 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
310331
g = params.SstoreClearGas
311332
}
312333
gas.Set(g)
313-
case SUICIDE:
314-
if !statedb.HasSuicided(contract.Address()) {
315-
statedb.AddRefund(params.SuicideRefundGas)
316-
}
317334
case MLOAD:
318335
newMemSize = calcMemSize(stack.peek(), u256(32))
336+
quadMemGas(mem, newMemSize, gas)
319337
case MSTORE8:
320338
newMemSize = calcMemSize(stack.peek(), u256(1))
339+
quadMemGas(mem, newMemSize, gas)
321340
case MSTORE:
322341
newMemSize = calcMemSize(stack.peek(), u256(32))
342+
quadMemGas(mem, newMemSize, gas)
323343
case RETURN:
324344
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
345+
quadMemGas(mem, newMemSize, gas)
325346
case SHA3:
326347
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
327348

328349
words := toWordSize(stack.data[stack.len()-2])
329350
gas.Add(gas, words.Mul(words, params.Sha3WordGas))
351+
352+
quadMemGas(mem, newMemSize, gas)
330353
case CALLDATACOPY:
331354
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
332355

333356
words := toWordSize(stack.data[stack.len()-3])
334357
gas.Add(gas, words.Mul(words, params.CopyGas))
358+
359+
quadMemGas(mem, newMemSize, gas)
335360
case CODECOPY:
336361
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
337362

338363
words := toWordSize(stack.data[stack.len()-3])
339364
gas.Add(gas, words.Mul(words, params.CopyGas))
365+
366+
quadMemGas(mem, newMemSize, gas)
340367
case EXTCODECOPY:
368+
gas.Set(gasTable.ExtcodeCopy)
369+
341370
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4])
342371

343372
words := toWordSize(stack.data[stack.len()-4])
344373
gas.Add(gas, words.Mul(words, params.CopyGas))
345374

375+
quadMemGas(mem, newMemSize, gas)
346376
case CREATE:
347377
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3])
378+
379+
quadMemGas(mem, newMemSize, gas)
348380
case CALL, CALLCODE:
349-
gas.Add(gas, stack.data[stack.len()-1])
381+
gas.Set(gasTable.Calls)
350382

351383
if op == CALL {
352384
if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
353385
gas.Add(gas, params.CallNewAccountGas)
354386
}
355387
}
356-
357388
if len(stack.data[stack.len()-3].Bytes()) > 0 {
358389
gas.Add(gas, params.CallValueTransferGas)
359390
}
360-
361391
x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7])
362392
y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5])
363393

364394
newMemSize = common.BigMax(x, y)
395+
396+
quadMemGas(mem, newMemSize, gas)
397+
398+
cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1])
399+
// Replace the stack item with the new gas calculation. This means that
400+
// either the original item is left on the stack or the item is replaced by:
401+
// (availableGas - gas) * 63 / 64
402+
// We replace the stack item so that it's available when the opCall instruction is
403+
// called. This information is otherwise lost due to the dependency on *current*
404+
// available gas.
405+
stack.data[stack.len()-1] = cg
406+
gas.Add(gas, cg)
407+
365408
case DELEGATECALL:
366-
gas.Add(gas, stack.data[stack.len()-1])
409+
gas.Set(gasTable.Calls)
367410

368411
x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6])
369412
y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4])
370413

371414
newMemSize = common.BigMax(x, y)
415+
416+
quadMemGas(mem, newMemSize, gas)
417+
418+
cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1])
419+
// Replace the stack item with the new gas calculation. This means that
420+
// either the original item is left on the stack or the item is replaced by:
421+
// (availableGas - gas) * 63 / 64
422+
// We replace the stack item so that it's available when the opCall instruction is
423+
// called.
424+
stack.data[stack.len()-1] = cg
425+
gas.Add(gas, cg)
426+
372427
}
373-
quadMemGas(mem, newMemSize, gas)
374428

375429
return newMemSize, gas, nil
376430
}

0 commit comments

Comments
 (0)