mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
miner: zero-diff polishes + suspend if difficulty increases
This commit is contained in:
parent
6001b04de0
commit
d53486746f
2 changed files with 62 additions and 4 deletions
|
|
@ -27,11 +27,19 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// zerodiffBlocktime block time for the zero-diff miner to target. Note, this
|
||||||
|
// needs to be in sync with the difficulty adjustment algorithm so chaning this
|
||||||
|
// or making it configurable doesn't make sense.
|
||||||
|
const zerodiffBlocktime = 15 * time.Second
|
||||||
|
|
||||||
// Strategy is a collection of optional callback methods that miner strategies
|
// Strategy is a collection of optional callback methods that miner strategies
|
||||||
// may implement to influence the behavior of the miner.
|
// may implement to influence the behavior of the miner.
|
||||||
type Strategy struct {
|
type Strategy struct {
|
||||||
Name string // Name of the strategy for logging purposes
|
Name string // Name of the strategy for logging purposes
|
||||||
|
|
||||||
|
// OnNewWork is invoked when a new block is starting to be mined by the miner.
|
||||||
|
OnNewWork func(emux *event.TypeMux, chain *core.BlockChain, block *types.Block) error
|
||||||
|
|
||||||
// OnMinedBlock is invoked when a new block is mined by the miner.
|
// OnMinedBlock is invoked when a new block is mined by the miner.
|
||||||
OnMinedBlock func(emux *event.TypeMux, chain *core.BlockChain, pool *core.TxPool, block *types.Block) error
|
OnMinedBlock func(emux *event.TypeMux, chain *core.BlockChain, pool *core.TxPool, block *types.Block) error
|
||||||
}
|
}
|
||||||
|
|
@ -39,7 +47,8 @@ type Strategy struct {
|
||||||
// NewZeroDiffStrategy creates a mining strategy that aims to push the difficulty
|
// NewZeroDiffStrategy creates a mining strategy that aims to push the difficulty
|
||||||
// of the blocks down to zero. It tries to achive this by delaying found blocks
|
// of the blocks down to zero. It tries to achive this by delaying found blocks
|
||||||
// to always be above the configured block time limit, pushing the difficulty
|
// to always be above the configured block time limit, pushing the difficulty
|
||||||
// down a bit after every block.
|
// down a bit after every block. Similarly if it notices the difficulty going up,
|
||||||
|
// it aborts mining altogether.
|
||||||
//
|
//
|
||||||
// The strategy has a few nice properties that is orthogonal both to multiple
|
// The strategy has a few nice properties that is orthogonal both to multiple
|
||||||
// zero-diff miners as well as other plain miners:
|
// zero-diff miners as well as other plain miners:
|
||||||
|
|
@ -50,7 +59,7 @@ type Strategy struct {
|
||||||
// pushed up by a rouge miner and abandoned, the multiple threads will allow
|
// pushed up by a rouge miner and abandoned, the multiple threads will allow
|
||||||
// pulling the difficulty down faster until it reaches sub-target times.
|
// pulling the difficulty down faster until it reaches sub-target times.
|
||||||
// * The zero-diff miner can play along nicely with non zero-diff miners, since
|
// * The zero-diff miner can play along nicely with non zero-diff miners, since
|
||||||
// it will either delay it's block to above-target times, or outright discard
|
// it will either delay its block to above-target times, or outright discard
|
||||||
// its own block if another is found, thereby ensuring it only ever reduces
|
// its own block if another is found, thereby ensuring it only ever reduces
|
||||||
// the difficulty, never increases.
|
// the difficulty, never increases.
|
||||||
// * The zero-diff miner can also play along nicely with other zero-diff miners
|
// * The zero-diff miner can also play along nicely with other zero-diff miners
|
||||||
|
|
@ -58,6 +67,16 @@ type Strategy struct {
|
||||||
// multiple zero-diff miners to co-exist and share blocks, without racing each
|
// multiple zero-diff miners to co-exist and share blocks, without racing each
|
||||||
// other for blocks and leading to a high uncle rate.
|
// other for blocks and leading to a high uncle rate.
|
||||||
//
|
//
|
||||||
|
// There are two short-circuits built in that can cause the zero-diff miner to
|
||||||
|
// temporarilly turn off and mine at full capacity:
|
||||||
|
//
|
||||||
|
// * If instant-transactions are requested, then blocks containing transactions
|
||||||
|
// will not be delayed at all; furthermore empty blocks will be fast-tracked
|
||||||
|
// too if the transaction pool contains pending transactions ready to be added
|
||||||
|
// to the next block.
|
||||||
|
// * If a minimum balance threshold is set for the miner, no delays will come
|
||||||
|
// into effect until that minimum threshold is reached.
|
||||||
|
//
|
||||||
// Note, this strategy is only meaningful in trusted private networks where the
|
// Note, this strategy is only meaningful in trusted private networks where the
|
||||||
// goal of mining is not to secure the network, rather to provide a stable but
|
// goal of mining is not to secure the network, rather to provide a stable but
|
||||||
// resource-light testbed.
|
// resource-light testbed.
|
||||||
|
|
@ -65,10 +84,36 @@ func NewZeroDiffStrategy(instantTxs bool, minBalance *big.Int) *Strategy {
|
||||||
return &Strategy{
|
return &Strategy{
|
||||||
Name: "zero-diff",
|
Name: "zero-diff",
|
||||||
|
|
||||||
|
OnNewWork: func(emux *event.TypeMux, chain *core.BlockChain, block *types.Block) error {
|
||||||
|
// If the difficulty is increasing, give a change for others to waste resources
|
||||||
|
if parent := chain.GetBlock(block.ParentHash(), block.NumberU64()-1); parent.Difficulty().Cmp(block.Difficulty()) < 0 {
|
||||||
|
// Before bailing out, do check if balance threshold needs to be reached
|
||||||
|
if minBalance != nil {
|
||||||
|
if state, _ := chain.State(); state.GetBalance(block.Coinbase()).Cmp(minBalance) < 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Someone seems to be mining, listen for events though to avoid hangs if the other leaves
|
||||||
|
head := emux.Subscribe(core.ChainHeadEvent{})
|
||||||
|
defer head.Unsubscribe()
|
||||||
|
|
||||||
|
if chain.CurrentHeader().Hash() != block.ParentHash() {
|
||||||
|
return errors.New("stale work") // Another block arrived already, drop this
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-time.After(zerodiffBlocktime):
|
||||||
|
return nil // Ok, nobody else seems to be mining, fire up the CPU
|
||||||
|
case <-head.Chan():
|
||||||
|
return errors.New("concurrent miner") // Meh, someone's mining, let them burn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
|
||||||
OnMinedBlock: func(emux *event.TypeMux, chain *core.BlockChain, pool *core.TxPool, block *types.Block) error {
|
OnMinedBlock: func(emux *event.TypeMux, chain *core.BlockChain, pool *core.TxPool, block *types.Block) error {
|
||||||
// A new block was mined, calculate the required delay
|
// A new block was mined, calculate the required delay
|
||||||
elapsed := time.Since(time.Unix(block.Time().Int64(), 0))
|
elapsed := time.Since(time.Unix(block.Time().Int64(), 0))
|
||||||
delay := float64(15*time.Second)*(1.0+rand.Float64()/10.0) - float64(elapsed)
|
delay := float64(zerodiffBlocktime)*(1.0+rand.Float64()/10.0) - float64(elapsed)
|
||||||
|
|
||||||
// If the delay is negative, block times are way over the target already, release
|
// If the delay is negative, block times are way over the target already, release
|
||||||
if delay <= 0 {
|
if delay <= 0 {
|
||||||
|
|
|
||||||
|
|
@ -370,9 +370,22 @@ func (self *worker) wait() {
|
||||||
|
|
||||||
// push sends a new work task to currently live miner agents.
|
// push sends a new work task to currently live miner agents.
|
||||||
func (self *worker) push(work *Work) {
|
func (self *worker) push(work *Work) {
|
||||||
|
// If there are no agents mining, bail out
|
||||||
if atomic.LoadInt32(&self.mining) != 1 {
|
if atomic.LoadInt32(&self.mining) != 1 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Iterate over all miner strategies and ensure none denies this block
|
||||||
|
for _, strat := range self.strategies {
|
||||||
|
if strat.OnMinedBlock != nil {
|
||||||
|
glog.V(logger.Debug).Infof("Passing mining request #%d [%x…] to strategy %s", work.Block.Number(), work.Block.Hash().Bytes()[:4], strat.Name)
|
||||||
|
if err := strat.OnNewWork(self.mux, self.chain, work.Block); err != nil {
|
||||||
|
glog.V(logger.Debug).Infof("Mining request #%d [%x…] denied by strategy %s: %v", work.Block.Number(), work.Block.Hash().Bytes()[:4], strat.Name, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
glog.V(logger.Debug).Infof("Mining request #%d [%x…] accepted by strategy %s", work.Block.Number(), work.Block.Hash().Bytes()[:4], strat.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Block accepted for mining, proceed
|
||||||
for agent := range self.agents {
|
for agent := range self.agents {
|
||||||
atomic.AddInt32(&self.atWork, 1)
|
atomic.AddInt32(&self.atWork, 1)
|
||||||
if ch := agent.Work(); ch != nil {
|
if ch := agent.Work(); ch != nil {
|
||||||
|
|
@ -534,7 +547,7 @@ func (self *worker) commitNewWork() {
|
||||||
glog.V(logger.Info).Infof("commit new work on block %v with %d txs & %d uncles. Took %v\n", work.Block.Number(), work.tcount, len(uncles), time.Since(tstart))
|
glog.V(logger.Info).Infof("commit new work on block %v with %d txs & %d uncles. Took %v\n", work.Block.Number(), work.tcount, len(uncles), time.Since(tstart))
|
||||||
self.unconfirmed.Shift(work.Block.NumberU64() - 1)
|
self.unconfirmed.Shift(work.Block.NumberU64() - 1)
|
||||||
}
|
}
|
||||||
self.push(work)
|
go self.push(work)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *worker) commitUncle(work *Work, uncle *types.Header) error {
|
func (self *worker) commitUncle(work *Work, uncle *types.Header) error {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue