Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize mining #624

Merged
merged 5 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/blockchain/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func (b *BlockChain) Init() error {
log.Info(fmt.Sprintf("hash=%s,order=%s,height=%d", v.GetHash(), meerdag.GetOrderLogStr(v.GetOrder()), v.GetHeight()))
}

b.difficultyManager = difficultymanager.NewDiffManager(b.Consensus().BlockChain(), b.params)
b.difficultyManager = difficultymanager.NewDiffManager(b.Consensus(), b.params)
return nil
}

Expand Down
41 changes: 21 additions & 20 deletions core/blockchain/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ func checkProofOfWork(header *types.BlockHeader, powConfig *pow.PowConfig, flags

// The block hash must be less than the claimed target unless the flag
// to avoid proof of work checks is set.
if !flags.Has(BFNoPoWCheck) {
if !flags.Has(BFNoPoWCheck) && !params.ActiveNetParams.Params.IsDevelopDiff() {
header.Pow.SetParams(powConfig)
header.Pow.SetMainHeight(pow.MainHeight(mHeight))
// The block hash must be less than the claimed target.
Expand Down Expand Up @@ -751,26 +751,27 @@ func (b *BlockChain) checkBlockHeaderContext(block *types.SerializedBlock, prevN

header := &block.Block().Header
if !flags.Has(BFFastAdd) {
instance := pow.GetInstance(header.Pow.GetPowType(), 0, []byte{})
instance.SetMainHeight(pow.MainHeight(prevNode.GetHeight() + 1))
instance.SetParams(b.params.PowConfig)
// Ensure the difficulty specified in the block header matches
// the calculated difficulty based on the previous block and
// difficulty retarget rules.

expDiff, err := b.calcNextRequiredDifficulty(prevNode,
header.Timestamp, instance)
if err != nil {
return err
}
blockDifficulty := header.Difficulty
if blockDifficulty != expDiff {
str := fmt.Sprintf("block difficulty of %d is not the"+
" expected value of %d", blockDifficulty,
expDiff)
return ruleError(ErrUnexpectedDifficulty, str)
if !b.params.IsDevelopDiff() {
instance := pow.GetInstance(header.Pow.GetPowType(), 0, []byte{})
instance.SetMainHeight(pow.MainHeight(prevNode.GetHeight() + 1))
instance.SetParams(b.params.PowConfig)
// Ensure the difficulty specified in the block header matches
// the calculated difficulty based on the previous block and
// difficulty retarget rules.

expDiff, err := b.calcNextRequiredDifficulty(prevNode,
header.Timestamp, instance)
if err != nil {
return err
}
blockDifficulty := header.Difficulty
if blockDifficulty != expDiff {
str := fmt.Sprintf("block difficulty of %d is not the"+
" expected value of %d", blockDifficulty,
expDiff)
return ruleError(ErrUnexpectedDifficulty, str)
}
}

// Ensure the timestamp for the block header is after the
// median time of the last several blocks (medianTimeBlocks).
medianTime := b.CalcPastMedianTime(prevNode)
Expand Down
2 changes: 2 additions & 0 deletions core/types/pow/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const (
DIFFICULTY_MODE_MEER = 0
// KASPAD difficulty adjustment
DIFFICULTY_MODE_KASPAD = 1
// DEVELOP difficulty adjustment
DIFFICULTY_MODE_DEVELOP = 2
)

// HashToBig converts a hash.Hash into a big.Int that can be used to
Expand Down
102 changes: 102 additions & 0 deletions core/types/pow/difficultymanager/develop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright (c) 2017-2018 The qitmeer developers
// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2018 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package difficultymanager

import (
"math/big"
"time"

"github.com/Qitmeer/qng/consensus/model"
"github.com/Qitmeer/qng/core/types"
"github.com/Qitmeer/qng/core/types/pow"
"github.com/Qitmeer/qng/params"
)

type developDiff struct {
b model.BlockChain
cfg *params.Params
}

// CalcEasiestDifficulty calculates the easiest possible difficulty that a block
// can have given starting difficulty bits and a duration. It is mainly used to
// verify that claimed proof of work by a block is sane as compared to a
// known good checkpoint.
func (m *developDiff) CalcEasiestDifficulty(bits uint32, duration time.Duration, powInstance pow.IPow) uint32 {
// Convert types used in the calculations below.
durationVal := int64(duration)
adjustmentFactor := big.NewInt(m.cfg.RetargetAdjustmentFactor)
maxRetargetTimespan := int64(m.cfg.TargetTimespan) *
m.cfg.RetargetAdjustmentFactor
target := powInstance.GetSafeDiff(0)
// The test network rules allow minimum difficulty blocks once too much
// time has elapsed without mining a block.
if m.cfg.ReduceMinDifficulty {
if durationVal > int64(m.cfg.MinDiffReductionTime) {
return pow.BigToCompact(target)
}
}

// Since easier difficulty equates to higher numbers, the easiest
// difficulty for a given duration is the largest value possible given
// the number of retargets for the duration and starting difficulty
// multiplied by the max adjustment factor.
newTarget := pow.CompactToBig(bits)

for durationVal > 0 && powInstance.CompareDiff(newTarget, target) {
newTarget.Mul(newTarget, adjustmentFactor)
newTarget = powInstance.GetNextDiffBig(adjustmentFactor, newTarget, big.NewInt(0))
durationVal -= maxRetargetTimespan
}

// Limit new value to the proof of work limit.
if !powInstance.CompareDiff(newTarget, target) {
newTarget.Set(target)
}

return pow.BigToCompact(newTarget)
}

// findPrevTestNetDifficulty returns the difficulty of the previous block which
// did not have the special testnet minimum difficulty rule applied.
//
// This function MUST be called with the chain state lock held (for writes).
func (m *developDiff) findPrevTestNetDifficulty(startBlock model.Block, powInstance pow.IPow) uint32 {
// Search backwards through the chain for the last block without
// the special rule applied.
target := powInstance.GetSafeDiff(0)
lastBits := pow.BigToCompact(target)
blocksPerRetarget := uint64(m.cfg.WorkDiffWindowSize * m.cfg.WorkDiffWindows)
iterBlock := startBlock
if iterBlock == nil ||
uint64(iterBlock.GetHeight())%blocksPerRetarget == 0 {
return lastBits
}
var iterNode *types.BlockHeader
iterNode = m.b.GetBlockHeader(iterBlock)
if iterNode.Difficulty != pow.BigToCompact(target) {
return lastBits
}
return iterNode.Difficulty
}

// RequiredDifficulty calculates the required difficulty for the block
// after the passed previous block node based on the difficulty retarget rules.
// This function differs from the exported RequiredDifficulty in that
// the exported version uses the current best chain as the previous block node
// while this function accepts any block node.
func (m *developDiff) RequiredDifficulty(block model.Block, newBlockTime time.Time, powInstance pow.IPow) (uint32, error) {
baseTarget := powInstance.GetSafeDiff(0)
return pow.BigToCompact(baseTarget), nil
}

// find block node by pow type
func (m *developDiff) GetCurrentPowDiff(ib model.Block, powType pow.PowType) *big.Int {
instance := pow.GetInstance(powType, 0, []byte{})
instance.SetParams(m.cfg.PowConfig)
safeBigDiff := instance.GetSafeDiff(0)
return safeBigDiff
}
13 changes: 10 additions & 3 deletions core/types/pow/difficultymanager/difficultymanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,28 @@ import (
"github.com/Qitmeer/qng/params"
)

func NewDiffManager(b model.BlockChain, cfg *params.Params) model.DifficultyManager {
func NewDiffManager(con model.Consensus, cfg *params.Params) model.DifficultyManager {
switch cfg.PowConfig.DifficultyMode {
case pow.DIFFICULTY_MODE_KASPAD:
return &kaspadDiff{
b: b,
con: con,
b: con.BlockChain(),
powMax: cfg.PowConfig.MeerXKeccakV1PowLimit,
difficultyAdjustmentWindowSize: int(cfg.WorkDiffWindowSize),
disableDifficultyAdjustment: false,
targetTimePerBlock: cfg.TargetTimePerBlock,
genesisBits: cfg.PowConfig.MeerXKeccakV1PowLimitBits,
cfg: cfg,
}
case pow.DIFFICULTY_MODE_DEVELOP:
return &developDiff{
b: con.BlockChain(),
cfg: cfg,
}
}
return &meerDiff{
b: b,
con: con,
b: con.BlockChain(),
cfg: cfg,
}
}
1 change: 1 addition & 0 deletions core/types/pow/difficultymanager/kaspad.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ type kaspadDiff struct {
genesisBits uint32
b model.BlockChain
cfg *params.Params
con model.Consensus
}

// CalcEasiestDifficulty calculates the easiest possible difficulty that a block
Expand Down
1 change: 1 addition & 0 deletions core/types/pow/difficultymanager/meer.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const maxShift = uint(256)
type meerDiff struct {
b model.BlockChain
cfg *params.Params
con model.Consensus
}

// CalcEasiestDifficulty calculates the easiest possible difficulty that a block
Expand Down
4 changes: 4 additions & 0 deletions params/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,10 @@ func (p *Params) HasTax() bool {
return false
}

func (p *Params) IsDevelopDiff() bool {
return p.PowConfig.DifficultyMode == pow.DIFFICULTY_MODE_DEVELOP
}

var (
// ErrDuplicateNet describes an error where the parameters for a network
// could not be set due to the network already being a standard
Expand Down
88 changes: 40 additions & 48 deletions services/miner/cpuworker.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"github.com/Qitmeer/qng/core/types"
"github.com/Qitmeer/qng/core/types/pow"
"github.com/Qitmeer/qng/params"
"github.com/Qitmeer/qng/services/mining"
"sync"
"sync/atomic"
"time"
Expand Down Expand Up @@ -49,8 +48,7 @@ type CPUWorker struct {
workWg sync.WaitGroup
updateNumWorks chan struct{}
numWorks uint32
updateWork chan struct{}
hasNewWork bool
hasNewWork atomic.Bool

miner *Miner

Expand Down Expand Up @@ -274,15 +272,7 @@ func (w *CPUWorker) Update() {
if atomic.LoadInt32(&w.shutdown) != 0 {
return
}
w.Lock()
defer w.Unlock()

if w.discrete && w.discreteNum <= 0 {
return
}
w.hasNewWork = true
w.updateWork <- struct{}{}
w.hasNewWork = false
w.hasNewWork.Store(true)
}

func (w *CPUWorker) generateDiscrete(num int, block chan *hash.Hash) bool {
Expand All @@ -302,45 +292,52 @@ func (w *CPUWorker) generateDiscrete(num int, block chan *hash.Hash) bool {
}

func (w *CPUWorker) generateBlocks() {
log.Trace(fmt.Sprintf("Starting generate blocks worker:%s", w.GetType()))
log.Info(fmt.Sprintf("Starting generate blocks worker:%s", w.GetType()))
out:
for {
// Quit when the miner is stopped.
select {
case <-w.updateWork:
if w.discrete && w.discreteNum <= 0 {
case <-w.quit:
break out
default:
// Non-blocking select to fall through
}
if w.discrete && w.discreteNum <= 0 || !w.hasNewWork.Load() {
time.Sleep(time.Second)
continue
}
start := time.Now()
if params.ActiveNetParams.Params.IsDevelopDiff() {
time.Sleep(params.ActiveNetParams.Params.TargetTimePerBlock)
}
sb := w.solveBlock()
if sb != nil {
w.hasNewWork.Store(false)
block := types.NewBlock(sb)
info, err := w.miner.submitBlock(block)
if err != nil {
log.Error(fmt.Sprintf("Failed to submit new block:%s ,%v", block.Hash().String(), err))
w.cleanDiscrete()
continue
}
sb := w.solveBlock()
if sb != nil {
block := types.NewBlock(sb)
info, err := w.miner.submitBlock(block)
if err != nil {
log.Error(fmt.Sprintf("Failed to submit new block:%s ,%v", block.Hash().String(), err))
w.cleanDiscrete()
continue
log.Info(fmt.Sprintf("%v", info), "cost", time.Since(start).String())

if w.discrete && w.discreteNum > 0 {
if w.discreteBlock != nil {
w.discreteBlock <- block.Hash()
}
log.Info(fmt.Sprintf("%v", info))

if w.discrete && w.discreteNum > 0 {
if w.discreteBlock != nil {
w.discreteBlock <- block.Hash()
}
w.discreteNum--
if w.discreteNum <= 0 {
w.cleanDiscrete()
}
w.discreteNum--
if w.discreteNum <= 0 {
w.cleanDiscrete()
}
} else {
w.cleanDiscrete()
}
case <-w.quit:
break out
} else {
w.cleanDiscrete()
}
}

w.workWg.Done()
log.Trace(fmt.Sprintf("Generate blocks worker done:%s", w.GetType()))
log.Info(fmt.Sprintf("Generate blocks worker done:%s", w.GetType()))
}

func (w *CPUWorker) cleanDiscrete() {
Expand All @@ -359,7 +356,7 @@ func (w *CPUWorker) solveBlock() *types.Block {
}
// Start a ticker which is used to signal checks for stale work and
// updates to the speed monitor.
ticker := time.NewTicker(333 * time.Millisecond)
ticker := time.NewTicker(time.Second)
defer ticker.Stop()

// Create a couple of convenience variables.
Expand Down Expand Up @@ -405,16 +402,9 @@ func (w *CPUWorker) solveBlock() *types.Block {
// has been updated since the block template was
// generated and it has been at least 3 seconds,
// or if it's been one minute.
if w.hasNewWork || roughtime.Now().After(lastGenerated.Add(gbtRegenerateSeconds*time.Second)) {
if roughtime.Now().After(lastGenerated.Add(gbtRegenerateSeconds * time.Second)) {
return nil
}

err := mining.UpdateBlockTime(block, w.miner.BlockChain(), w.miner.timeSource, params.ActiveNetParams.Params)
if err != nil {
log.Warn(fmt.Sprintf("CPU miner unable to update block template time: %v", err))
return nil
}

default:
// Non-blocking select to fall through
}
Expand All @@ -424,6 +414,9 @@ func (w *CPUWorker) solveBlock() *types.Block {
instance.SetParams(params.ActiveNetParams.Params.PowConfig)
hashesCompleted += 2
header.Pow = instance
if params.ActiveNetParams.Params.IsDevelopDiff() {
return block
}
if header.Pow.FindSolver(header.BlockData(), header.BlockHash(), header.Difficulty) {
w.updateHashes <- hashesCompleted
return block
Expand All @@ -443,7 +436,6 @@ func NewCPUWorker(miner *Miner) *CPUWorker {
queryHashesPerSec: make(chan float64),
updateNumWorks: make(chan struct{}),
numWorks: defaultNumWorkers,
updateWork: make(chan struct{}),
}

return &w
Expand Down
Loading
Loading