diff options
author | Péter Szilágyi <peterke@gmail.com> | 2016-10-15 00:32:11 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-10-15 00:32:11 +0800 |
commit | 81b01f1c2bba8340f9cf2bf1da8ef131e3decd50 (patch) | |
tree | 0b2269cb3dfa9719cf3bf7a828c49b0c1ffccab8 /core | |
parent | a4d9e63d12c80a14294c0d085de6bf711ceec779 (diff) | |
parent | 64af2aafdaf16d0bab4c2b89573324b076602bab (diff) | |
download | go-tangerine-81b01f1c2bba8340f9cf2bf1da8ef131e3decd50.tar.gz go-tangerine-81b01f1c2bba8340f9cf2bf1da8ef131e3decd50.tar.zst go-tangerine-81b01f1c2bba8340f9cf2bf1da8ef131e3decd50.zip |
Merge pull request #3111 from obscuren/gas-price-fork
core, core/vm: added gas price variance table (EIP #150)
Diffstat (limited to 'core')
-rw-r--r-- | core/config.go | 14 | ||||
-rw-r--r-- | core/vm/environment.go | 4 | ||||
-rw-r--r-- | core/vm/gas.go | 34 | ||||
-rw-r--r-- | core/vm/instructions.go | 7 | ||||
-rw-r--r-- | core/vm/runtime/runtime.go | 4 | ||||
-rw-r--r-- | core/vm/util_test.go | 9 | ||||
-rw-r--r-- | core/vm/vm.go | 77 |
7 files changed, 129 insertions, 20 deletions
diff --git a/core/config.go b/core/config.go index c0d065a57..3ab04e520 100644 --- a/core/config.go +++ b/core/config.go @@ -21,6 +21,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/params" ) var ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error @@ -35,6 +36,8 @@ type ChainConfig struct { DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork switch block (nil = no fork) DAOForkSupport bool `json:"daoForkSupport"` // Whether the nodes supports or opposes the DAO hard-fork + HomesteadGasRepriceBlock *big.Int `json:"homesteadGasRepriceBlock"` // Homestead gas reprice switch block (nil = no fork) + VmConfig vm.Config `json:"-"` } @@ -45,3 +48,14 @@ func (c *ChainConfig) IsHomestead(num *big.Int) bool { } return num.Cmp(c.HomesteadBlock) >= 0 } + +// GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice). +// +// The returned GasTable's fields shouldn't, under any circumstances, be changed. +func (c *ChainConfig) GasTable(num *big.Int) params.GasTable { + if c.HomesteadGasRepriceBlock == nil || num == nil || num.Cmp(c.HomesteadGasRepriceBlock) < 0 { + return params.GasTableHomestead + } + + return params.GasTableHomesteadGasRepriceFork +} diff --git a/core/vm/environment.go b/core/vm/environment.go index a4b2ac196..f8996e648 100644 --- a/core/vm/environment.go +++ b/core/vm/environment.go @@ -20,12 +20,16 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" ) // RuleSet is an interface that defines the current rule set during the // execution of the EVM instructions (e.g. whether it's homestead) type RuleSet interface { IsHomestead(*big.Int) bool + // GasTable returns the gas prices for this phase, which is based on + // block number passed in. + GasTable(*big.Int) params.GasTable } // Environment is an EVM requirement and helper which allows access to outside diff --git a/core/vm/gas.go b/core/vm/gas.go index eb2c16346..fdbc0df7f 100644 --- a/core/vm/gas.go +++ b/core/vm/gas.go @@ -35,8 +35,27 @@ var ( GasStop = big.NewInt(0) GasContractByte = big.NewInt(200) + + n64 = big.NewInt(64) ) +// calcGas returns the actual gas cost of the call. +// +// The cost of gas was changed during the homestead price change HF. To allow for EIP150 +// to be implemented. The returned gas is gas - base * 63 / 64. +func callGas(gasTable params.GasTable, availableGas, base, callCost *big.Int) *big.Int { + if gasTable.CreateBySuicide != nil { + availableGas = new(big.Int).Sub(availableGas, base) + g := new(big.Int).Div(availableGas, n64) + g.Sub(availableGas, g) + + if g.Cmp(callCost) < 0 { + return g + } + } + return callCost +} + // baseCheck checks for any stack error underflows func baseCheck(op OpCode, stack *Stack, gas *big.Int) error { // 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{ MSIZE: {0, GasQuickStep, 1}, GAS: {0, GasQuickStep, 1}, BLOCKHASH: {1, GasExtStep, 1}, - BALANCE: {1, GasExtStep, 1}, - EXTCODESIZE: {1, GasExtStep, 1}, - EXTCODECOPY: {4, GasExtStep, 0}, + BALANCE: {1, Zero, 1}, + EXTCODESIZE: {1, Zero, 1}, + EXTCODECOPY: {4, Zero, 0}, SLOAD: {1, params.SloadGas, 1}, SSTORE: {2, Zero, 0}, SHA3: {2, params.Sha3Gas, 1}, CREATE: {3, params.CreateGas, 1}, - CALL: {7, params.CallGas, 1}, - CALLCODE: {7, params.CallGas, 1}, - DELEGATECALL: {6, params.CallGas, 1}, - JUMPDEST: {0, params.JumpdestGas, 0}, + // Zero is calculated in the gasSwitch + CALL: {7, Zero, 1}, + CALLCODE: {7, Zero, 1}, + DELEGATECALL: {6, Zero, 1}, SUICIDE: {1, Zero, 0}, + JUMPDEST: {0, params.JumpdestGas, 0}, RETURN: {2, Zero, 0}, PUSH1: {0, GasFastestStep, 1}, DUP1: {0, Zero, 1}, diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 79aee60d2..3352877c1 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -514,7 +514,12 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract input = memory.Get(offset.Int64(), size.Int64()) gas = new(big.Int).Set(contract.Gas) ) - contract.UseGas(contract.Gas) + if env.RuleSet().GasTable(env.BlockNumber()).CreateBySuicide != nil { + gas.Div(gas, n64) + gas = gas.Sub(contract.Gas, gas) + } + + contract.UseGas(gas) _, addr, suberr := env.Create(contract, input, gas, contract.Price, value) // Push item on the stack based on the returned error. If the ruleset is // homestead we must check for CodeStoreOutOfGasError (homestead only diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 6d980cb32..343aee514 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -25,12 +25,16 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/params" ) // The default, always homestead, rule set for the vm env type ruleSet struct{} func (ruleSet) IsHomestead(*big.Int) bool { return true } +func (ruleSet) GasTable(*big.Int) params.GasTable { + return params.GasTableHomesteadGasRepriceFork +} // Config is a basic type specifying certain configuration flags for running // the EVM. diff --git a/core/vm/util_test.go b/core/vm/util_test.go index f0d825555..5783ee015 100644 --- a/core/vm/util_test.go +++ b/core/vm/util_test.go @@ -16,10 +16,17 @@ package vm -import "math/big" +import ( + "math/big" + + "github.com/ethereum/go-ethereum/params" +) type ruleSet struct { hs *big.Int } func (r ruleSet) IsHomestead(n *big.Int) bool { return n.Cmp(r.hs) >= 0 } +func (r ruleSet) GasTable(*big.Int) params.GasTable { + return params.GasTableHomestead +} diff --git a/core/vm/vm.go b/core/vm/vm.go index 033ada21c..fcffcf317 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -44,6 +44,7 @@ type EVM struct { env Environment jumpTable vmJumpTable cfg Config + gasTable params.GasTable } // New returns a new instance of the EVM. @@ -52,6 +53,7 @@ func New(env Environment, cfg Config) *EVM { env: env, jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber()), cfg: cfg, + gasTable: env.RuleSet().GasTable(env.BlockNumber()), } } @@ -169,7 +171,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { // Get the memory location of pc op = contract.GetOp(pc) // calculate the new memory size and gas price for the current executing opcode - newMemSize, cost, err = calculateGasAndSize(evm.env, contract, caller, op, statedb, mem, stack) + newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, statedb, mem, stack) if err != nil { return nil, err } @@ -234,7 +236,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { // calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for // the operation. This does not reduce gas or resizes the memory. -func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) { +func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) { var ( gas = new(big.Int) newMemSize *big.Int = new(big.Int) @@ -246,6 +248,24 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef // stack Check, memory resize & gas phase switch op { + case SUICIDE: + // if suicide is not nil: homestead gas fork + if gasTable.CreateBySuicide != nil { + gas.Set(gasTable.Suicide) + if !env.Db().Exist(common.BigToAddress(stack.data[len(stack.data)-1])) { + gas.Add(gas, gasTable.CreateBySuicide) + } + } + + if !statedb.HasSuicided(contract.Address()) { + statedb.AddRefund(params.SuicideRefundGas) + } + case EXTCODESIZE: + gas.Set(gasTable.ExtcodeSize) + case BALANCE: + gas.Set(gasTable.Balance) + case SLOAD: + gas.Set(gasTable.SLoad) case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16: n := int(op - SWAP1 + 2) err := stack.require(n) @@ -274,6 +294,8 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas)) newMemSize = calcMemSize(mStart, mSize) + + quadMemGas(mem, newMemSize, gas) case EXP: gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas)) case SSTORE: @@ -302,67 +324,100 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef g = params.SstoreResetGas } gas.Set(g) - case SUICIDE: - if !statedb.HasSuicided(contract.Address()) { - statedb.AddRefund(params.SuicideRefundGas) - } case MLOAD: newMemSize = calcMemSize(stack.peek(), u256(32)) + quadMemGas(mem, newMemSize, gas) case MSTORE8: newMemSize = calcMemSize(stack.peek(), u256(1)) + quadMemGas(mem, newMemSize, gas) case MSTORE: newMemSize = calcMemSize(stack.peek(), u256(32)) + quadMemGas(mem, newMemSize, gas) case RETURN: newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2]) + quadMemGas(mem, newMemSize, gas) case SHA3: newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2]) words := toWordSize(stack.data[stack.len()-2]) gas.Add(gas, words.Mul(words, params.Sha3WordGas)) + + quadMemGas(mem, newMemSize, gas) case CALLDATACOPY: newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) words := toWordSize(stack.data[stack.len()-3]) gas.Add(gas, words.Mul(words, params.CopyGas)) + + quadMemGas(mem, newMemSize, gas) case CODECOPY: newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) words := toWordSize(stack.data[stack.len()-3]) gas.Add(gas, words.Mul(words, params.CopyGas)) + + quadMemGas(mem, newMemSize, gas) case EXTCODECOPY: + gas.Set(gasTable.ExtcodeCopy) + newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4]) words := toWordSize(stack.data[stack.len()-4]) gas.Add(gas, words.Mul(words, params.CopyGas)) + quadMemGas(mem, newMemSize, gas) case CREATE: newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3]) + + quadMemGas(mem, newMemSize, gas) case CALL, CALLCODE: - gas.Add(gas, stack.data[stack.len()-1]) + gas.Set(gasTable.Calls) if op == CALL { if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) { gas.Add(gas, params.CallNewAccountGas) } } - if len(stack.data[stack.len()-3].Bytes()) > 0 { gas.Add(gas, params.CallValueTransferGas) } - x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7]) y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5]) newMemSize = common.BigMax(x, y) + + quadMemGas(mem, newMemSize, gas) + + cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1]) + // 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] = cg + gas.Add(gas, cg) + case DELEGATECALL: - gas.Add(gas, stack.data[stack.len()-1]) + gas.Set(gasTable.Calls) x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6]) y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4]) newMemSize = common.BigMax(x, y) + + quadMemGas(mem, newMemSize, gas) + + cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1]) + // 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] = cg + gas.Add(gas, cg) + } - quadMemGas(mem, newMemSize, gas) return newMemSize, gas, nil } |