From 9b904067b05dcdb09f4aa38900dd911460afc6ff Mon Sep 17 00:00:00 2001 From: zsfelfoldi Date: Sun, 9 Aug 2015 02:42:01 +0200 Subject: [PATCH] moved CalcGasLimit to eth; selectable miner strategies --- cmd/geth/main.go | 5 +++ cmd/utils/flags.go | 36 +++++++++++++-- core/bench_test.go | 4 +- core/block_processor_test.go | 2 +- core/chain_makers.go | 4 +- core/chain_makers_test.go | 2 +- core/chain_manager.go | 10 +++-- core/chain_manager_test.go | 2 +- core/chain_util.go | 33 -------------- core/manager.go | 4 ++ eth/backend.go | 20 ++++++++- eth/gaslimit.go | 86 ++++++++++++++++++++++++++++++++++++ eth/protocol_test.go | 2 +- miner/worker.go | 2 +- 14 files changed, 163 insertions(+), 49 deletions(-) create mode 100644 eth/gaslimit.go diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 895e55b44d..863f16becc 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -333,6 +333,11 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso utils.GpobaseStepDownFlag, utils.GpobaseStepUpFlag, utils.GpobaseCorrectionFactorFlag, + utils.GlsFlag, + utils.GlsTargetFlag, + utils.GlsBlkUtilFlag, + utils.GlsMinFlag, + utils.GlsMaxFlag, } app.Before = func(ctx *cli.Context) error { utils.SetupLogger(ctx) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index e7b30cfa0d..513cc90aae 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -361,6 +361,31 @@ var ( Usage: "Suggested gas price base correction factor (%)", Value: 110, } + GlsFlag = cli.StringFlag{ + Name: "gls", + Usage: "Gas limit adjustment strategy ( none | target | blkutil )", + Value: "target", + } + GlsTargetFlag = cli.StringFlag{ + Name: "glstarget", + Usage: "Constant gas limit target", + Value: "3141592", + } + GlsBlkUtilFlag = cli.IntFlag{ + Name: "glsblkutil", + Usage: "Block utilization target (%)", + Value: 67, + } + GlsMinFlag = cli.StringFlag{ + Name: "glsmin", + Usage: "Gas limit adjustment range minimum (applies to all strategies)", + Value: "1000000", + } + GlsMaxFlag = cli.StringFlag{ + Name: "glsmax", + Usage: "Gas limit adjustment range maximum (applies to all strategies)", + Value: "10000000", + } ) // MakeNAT creates a port mapper from set command line flags. @@ -437,8 +462,13 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config { GpobaseStepDown: ctx.GlobalInt(GpobaseStepDownFlag.Name), GpobaseStepUp: ctx.GlobalInt(GpobaseStepUpFlag.Name), GpobaseCorrectionFactor: ctx.GlobalInt(GpobaseCorrectionFactorFlag.Name), - SolcPath: ctx.GlobalString(SolcPathFlag.Name), - AutoDAG: ctx.GlobalBool(AutoDAGFlag.Name) || ctx.GlobalBool(MiningEnabledFlag.Name), + Gls: ctx.GlobalString(GlsFlag.Name), + GlsTarget: common.String2Big(ctx.GlobalString(GlsTargetFlag.Name)), + GlsBlkUtil: ctx.GlobalInt(GlsBlkUtilFlag.Name), + GlsMin: common.String2Big(ctx.GlobalString(GlsMinFlag.Name)), + GlsMax: common.String2Big(ctx.GlobalString(GlsMaxFlag.Name)), + SolcPath: ctx.GlobalString(SolcPathFlag.Name), + AutoDAG: ctx.GlobalBool(AutoDAGFlag.Name) || ctx.GlobalBool(MiningEnabledFlag.Name), } } @@ -483,7 +513,7 @@ func MakeChain(ctx *cli.Context) (chain *core.ChainManager, blockDB, stateDB, ex eventMux := new(event.TypeMux) pow := ethash.New() //genesis := core.GenesisBlock(uint64(ctx.GlobalInt(GenesisNonceFlag.Name)), blockDB) - chain, err = core.NewChainManager(blockDB, stateDB, extraDB, pow, eventMux) + chain, err = core.NewChainManager(blockDB, stateDB, extraDB, pow, eventMux, nil) if err != nil { Fatalf("Could not start chainmanager: %v", err) } diff --git a/core/bench_test.go b/core/bench_test.go index 67ba15970f..9647b57d35 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -108,7 +108,7 @@ func init() { func genTxRing(naccounts int) func(int, *BlockGen) { from := 0 return func(i int, gen *BlockGen) { - gas := CalcGasLimit(gen.PrevBlock(i - 1)) + gas := gen.PrevBlock(i - 1).GasLimit() // CalcGasLimit(gen.PrevBlock(i - 1)) for { gas.Sub(gas, params.TxGas) if gas.Cmp(params.TxGas) < 0 { @@ -168,7 +168,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { // Time the insertion of the new chain. // State and blocks are stored in the same DB. evmux := new(event.TypeMux) - chainman, _ := NewChainManager(db, db, db, FakePow{}, evmux) + chainman, _ := NewChainManager(db, db, db, FakePow{}, evmux, nil) chainman.SetProcessor(NewBlockProcessor(db, db, FakePow{}, chainman, evmux)) defer chainman.Stop() b.ReportAllocs() diff --git a/core/block_processor_test.go b/core/block_processor_test.go index f48ce9607f..87ddc95783 100644 --- a/core/block_processor_test.go +++ b/core/block_processor_test.go @@ -34,7 +34,7 @@ func proc() (*BlockProcessor, *ChainManager) { var mux event.TypeMux WriteTestNetGenesisBlock(db, db, 0) - chainMan, err := NewChainManager(db, db, db, thePow(), &mux) + chainMan, err := NewChainManager(db, db, db, thePow(), &mux, nil) if err != nil { fmt.Println(err) } diff --git a/core/chain_makers.go b/core/chain_makers.go index 85a6175dc9..6ea7faa85c 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -172,7 +172,7 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header { ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), Difficulty: CalcDifficulty(time, parent.Time(), parent.Number(), parent.Difficulty()), - GasLimit: CalcGasLimit(parent), + GasLimit: parent.GasLimit(), GasUsed: new(big.Int), Number: new(big.Int).Add(parent.Number(), common.Big1), Time: uint64(time), @@ -185,7 +185,7 @@ func newCanonical(n int, db common.Database) (*BlockProcessor, error) { evmux := &event.TypeMux{} WriteTestNetGenesisBlock(db, db, 0) - chainman, _ := NewChainManager(db, db, db, FakePow{}, evmux) + chainman, _ := NewChainManager(db, db, db, FakePow{}, evmux, nil) bman := NewBlockProcessor(db, db, FakePow{}, chainman, evmux) bman.bc.SetProcessor(bman) parent := bman.bc.CurrentBlock() diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index 98a585f9be..0e85d8cc1c 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -77,7 +77,7 @@ func ExampleGenerateChain() { // Import the chain. This runs all block validation rules. evmux := &event.TypeMux{} - chainman, _ := NewChainManager(db, db, db, FakePow{}, evmux) + chainman, _ := NewChainManager(db, db, db, FakePow{}, evmux, nil) chainman.SetProcessor(NewBlockProcessor(db, db, FakePow{}, chainman, evmux)) if i, err := chainman.InsertChain(chain); err != nil { fmt.Printf("insert error (block %d): %v\n", i, err) diff --git a/core/chain_manager.go b/core/chain_manager.go index 1b792933c9..b28eede70d 100644 --- a/core/chain_manager.go +++ b/core/chain_manager.go @@ -54,8 +54,11 @@ const ( checkpointLimit = 200 ) +type GlsFunc func(*types.Block) *big.Int + type ChainManager struct { //eth EthManager + glsfunc GlsFunc blockDb common.Database stateDb common.Database extraDb common.Database @@ -85,9 +88,10 @@ type ChainManager struct { pow pow.PoW } -func NewChainManager(blockDb, stateDb, extraDb common.Database, pow pow.PoW, mux *event.TypeMux) (*ChainManager, error) { +func NewChainManager(blockDb, stateDb, extraDb common.Database, pow pow.PoW, mux *event.TypeMux, glsfunc GlsFunc) (*ChainManager, error) { cache, _ := lru.New(blockCacheLimit) bc := &ChainManager{ + glsfunc: glsfunc, blockDb: blockDb, stateDb: stateDb, extraDb: extraDb, @@ -235,7 +239,7 @@ func (bc *ChainManager) setLastState() error { bc.Reset() } bc.td = bc.currentBlock.Td - bc.currentGasLimit = CalcGasLimit(bc.currentBlock) + bc.currentGasLimit = bc.glsfunc(bc.currentBlock) if glog.V(logger.Info) { glog.Infof("Last block (#%v) %x TD=%v\n", bc.currentBlock.Number(), bc.currentBlock.Hash(), bc.td) @@ -757,7 +761,7 @@ out: // We need some control over the mining operation. Acquiring locks and waiting for the miner to create new block takes too long // and in most cases isn't even necessary. if self.lastBlockHash == event.Hash { - self.currentGasLimit = CalcGasLimit(event.Block) + self.currentGasLimit = self.glsfunc(event.Block) self.eventMux.Post(ChainHeadEvent{event.Block}) } } diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go index f0c097df6c..60dd498eeb 100644 --- a/core/chain_manager_test.go +++ b/core/chain_manager_test.go @@ -49,7 +49,7 @@ func thePow() pow.PoW { func theChainManager(db common.Database, t *testing.T) *ChainManager { var eventMux event.TypeMux WriteTestNetGenesisBlock(db, db, 0) - chainMan, err := NewChainManager(db, db, db, thePow(), &eventMux) + chainMan, err := NewChainManager(db, db, db, thePow(), &eventMux, nil) if err != nil { t.Error("failed creating chainmanager:", err) t.FailNow() diff --git a/core/chain_util.go b/core/chain_util.go index 34f6c8d0aa..9879148d69 100644 --- a/core/chain_util.go +++ b/core/chain_util.go @@ -79,39 +79,6 @@ func CalcTD(block, parent *types.Block) *big.Int { return d } -// CalcGasLimit computes the gas limit of the next block after parent. -// The result may be modified by the caller. -// This is miner strategy, not consensus protocol. -func CalcGasLimit(parent *types.Block) *big.Int { - // contrib = (parentGasUsed * 3 / 2) / 1024 - contrib := new(big.Int).Mul(parent.GasUsed(), big.NewInt(3)) - contrib = contrib.Div(contrib, big.NewInt(2)) - contrib = contrib.Div(contrib, params.GasLimitBoundDivisor) - - // decay = parentGasLimit / 1024 -1 - decay := new(big.Int).Div(parent.GasLimit(), params.GasLimitBoundDivisor) - decay.Sub(decay, big.NewInt(1)) - - /* - strategy: gasLimit of block-to-mine is set based on parent's - gasUsed value. if parentGasUsed > parentGasLimit * (2/3) then we - increase it, otherwise lower it (or leave it unchanged if it's right - at that usage) the amount increased/decreased depends on how far away - from parentGasLimit * (2/3) parentGasUsed is. - */ - gl := new(big.Int).Sub(parent.GasLimit(), decay) - gl = gl.Add(gl, contrib) - gl.Set(common.BigMax(gl, params.MinGasLimit)) - - // however, if we're now below the target (GenesisGasLimit) we increase the - // limit as much as we can (parentGasLimit / 1024 -1) - if gl.Cmp(params.GenesisGasLimit) < 0 { - gl.Add(parent.GasLimit(), decay) - gl.Set(common.BigMin(gl, params.GenesisGasLimit)) - } - return gl -} - // GetBlockByHash returns the block corresponding to the hash or nil if not found func GetBlockByHash(db common.Database, hash common.Hash) *types.Block { data, _ := db.Get(append(blockHashPre, hash[:]...)) diff --git a/core/manager.go b/core/manager.go index a07c326592..558c87d522 100644 --- a/core/manager.go +++ b/core/manager.go @@ -17,8 +17,11 @@ package core import ( + "math/big" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" ) @@ -32,4 +35,5 @@ type Backend interface { StateDb() common.Database ExtraDb() common.Database EventMux() *event.TypeMux + CalcGasLimit(*types.Block) *big.Int } diff --git a/eth/backend.go b/eth/backend.go index ed46a4ab36..2aecb4f525 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -122,6 +122,12 @@ type Config struct { GpobaseStepUp int GpobaseCorrectionFactor int + Gls string + GlsTarget *big.Int + GlsBlkUtil int + GlsMin *big.Int + GlsMax *big.Int + // NewDB is used to create databases. // If nil, the default is to create leveldb databases on disk. NewDB func(path string) (common.Database, error) @@ -232,6 +238,12 @@ type Ethereum struct { GpobaseStepUp int GpobaseCorrectionFactor int + Gls string + GlsTarget *big.Int + GlsBlkUtil int + GlsMin *big.Int + GlsMax *big.Int + net *p2p.Server eventMux *event.TypeMux miner *miner.Miner @@ -350,6 +362,11 @@ func New(config *Config) (*Ethereum, error) { GpobaseStepDown: config.GpobaseStepDown, GpobaseStepUp: config.GpobaseStepUp, GpobaseCorrectionFactor: config.GpobaseCorrectionFactor, + Gls: config.Gls, + GlsTarget: config.GlsTarget, + GlsBlkUtil: config.GlsBlkUtil, + GlsMin: config.GlsMin, + GlsMax: config.GlsMax, } if config.PowTest { @@ -362,7 +379,8 @@ func New(config *Config) (*Ethereum, error) { eth.pow = ethash.New() } //genesis := core.GenesisBlock(uint64(config.GenesisNonce), stateDb) - eth.chainManager, err = core.NewChainManager(blockDb, stateDb, extraDb, eth.pow, eth.EventMux()) + + eth.chainManager, err = core.NewChainManager(blockDb, stateDb, extraDb, eth.pow, eth.EventMux(), eth.CalcGasLimit) if err != nil { if err == core.ErrNoGenesis { return nil, fmt.Errorf(`Genesis block not found. Please supply a genesis block with the "--genesis /path/to/file" argument`) diff --git a/eth/gaslimit.go b/eth/gaslimit.go new file mode 100644 index 0000000000..548f4474a4 --- /dev/null +++ b/eth/gaslimit.go @@ -0,0 +1,86 @@ +// Copyright 2015 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 . + +package eth + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" +) + +// CalcGasLimit computes the gas limit of the next block after parent. +// The result may be modified by the caller. +func (eth *Ethereum) CalcGasLimit(parent *types.Block) *big.Int { + + var limit, step, min, max *big.Int + step = parent.GasLimit() + step = step.Div(step, params.GasLimitBoundDivisor) + step = step.Sub(step, big.NewInt(1)) + min = parent.GasLimit() + min = min.Sub(min, step) + max = parent.GasLimit() + max = max.Add(max, step) + + switch eth.Gls { + case "target": + limit = eth.GlsTarget + case "blkutil": + limit = eth.CalcGlsBlkUtil(parent) + default: + limit = parent.GasLimit() + } + + if limit.Cmp(min) < 0 { + limit = min + } + if limit.Cmp(max) > 0 { + limit = max + } + return limit +} + +func (eth *Ethereum) CalcGlsBlkUtil(parent *types.Block) *big.Int { + // contrib = (parentGasUsed * 100 / GlsBlkUtil) / 1024 + contrib := new(big.Int).Mul(parent.GasUsed(), big.NewInt(100)) + contrib = contrib.Div(contrib, big.NewInt(int64(eth.GlsBlkUtil))) + contrib = contrib.Div(contrib, params.GasLimitBoundDivisor) + + // decay = parentGasLimit / 1024 -1 + decay := new(big.Int).Div(parent.GasLimit(), params.GasLimitBoundDivisor) + decay.Sub(decay, big.NewInt(1)) + + /* + strategy: gasLimit of block-to-mine is set based on parent's + gasUsed value. if parentGasUsed > parentGasLimit * (2/3) then we + increase it, otherwise lower it (or leave it unchanged if it's right + at that usage) the amount increased/decreased depends on how far away + from parentGasLimit * (2/3) parentGasUsed is. + */ + gl := new(big.Int).Sub(parent.GasLimit(), decay) + gl = gl.Add(gl, contrib) + gl.Set(common.BigMax(gl, params.MinGasLimit)) + + // however, if we're now below the target (GenesisGasLimit) we increase the + // limit as much as we can (parentGasLimit / 1024 -1) + if gl.Cmp(params.GenesisGasLimit) < 0 { + gl.Add(parent.GasLimit(), decay) + gl.Set(common.BigMin(gl, params.GenesisGasLimit)) + } + return gl +} diff --git a/eth/protocol_test.go b/eth/protocol_test.go index a24d98f696..da58cc46c9 100644 --- a/eth/protocol_test.go +++ b/eth/protocol_test.go @@ -182,7 +182,7 @@ func newProtocolManagerForTesting(txAdded chan<- []*types.Transaction) *Protocol core.WriteTestNetGenesisBlock(db, db, 0) var ( em = new(event.TypeMux) - chain, _ = core.NewChainManager(db, db, db, core.FakePow{}, em) + chain, _ = core.NewChainManager(db, db, db, core.FakePow{}, em, nil) txpool = &fakeTxPool{added: txAdded} pm = NewProtocolManager(NetworkId, em, txpool, core.FakePow{}, chain) ) diff --git a/miner/worker.go b/miner/worker.go index 535ce51447..b31f3c4fe1 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -446,7 +446,7 @@ func (self *worker) commitNewWork() { ParentHash: parent.Hash(), Number: num.Add(num, common.Big1), Difficulty: core.CalcDifficulty(uint64(tstamp), parent.Time(), parent.Number(), parent.Difficulty()), - GasLimit: core.CalcGasLimit(parent), + GasLimit: self.eth.CalcGasLimit(parent), GasUsed: new(big.Int), Coinbase: self.coinbase, Extra: self.extra,