miner: zero-diff polishes + suspend if difficulty increases

This commit is contained in:
Péter Szilágyi 2016-12-22 11:18:36 +02:00
parent 6001b04de0
commit d53486746f
No known key found for this signature in database
GPG key ID: 119A76381CCB7DD2
2 changed files with 62 additions and 4 deletions

View file

@ -27,11 +27,19 @@ import (
"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
// may implement to influence the behavior of the miner.
type Strategy struct {
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 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
// 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
// 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
// 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
// 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
// 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
// the difficulty, never increases.
// * 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
// 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
// goal of mining is not to secure the network, rather to provide a stable but
// resource-light testbed.
@ -65,10 +84,36 @@ func NewZeroDiffStrategy(instantTxs bool, minBalance *big.Int) *Strategy {
return &Strategy{
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 {
// A new block was mined, calculate the required delay
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 delay <= 0 {

View file

@ -370,9 +370,22 @@ func (self *worker) wait() {
// push sends a new work task to currently live miner agents.
func (self *worker) push(work *Work) {
// If there are no agents mining, bail out
if atomic.LoadInt32(&self.mining) != 1 {
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 {
atomic.AddInt32(&self.atWork, 1)
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))
self.unconfirmed.Shift(work.Block.NumberU64() - 1)
}
self.push(work)
go self.push(work)
}
func (self *worker) commitUncle(work *Work, uncle *types.Header) error {