From 248ddd66ed7b5409eb20cc4a8092ed72b0b6bdbd Mon Sep 17 00:00:00 2001 From: frankcrypto Date: Sun, 10 Dec 2023 20:39:52 +0800 Subject: [PATCH] restruct difficulty modules --- consensus/consensus.go | 3 +- consensus/model/block.go | 1 + consensus/model/block_chain.go | 2 + consensus/model/meer_dag.go | 11 + core/blockchain/process.go | 6 +- core/blockchain/validate.go | 9 +- core/types/common.go | 5 + core/types/pow/config.go | 4 +- .../difficultymanager/difficultymanager.go | 77 ------- .../interface_difficultymanager.go | 39 ++++ .../interface_processes_difficultymanager.go | 60 ----- core/types/pow/difficultymanager/kaspad.go | 214 ++++++++++++++++++ .../pow/difficultymanager/meer.go} | 202 ++++++----------- node/api.go | 10 +- params/params_mixnet.go | 3 +- services/mining/newblocktemplate.go | 8 +- 16 files changed, 371 insertions(+), 283 deletions(-) create mode 100644 consensus/model/meer_dag.go delete mode 100644 core/types/pow/difficultymanager/difficultymanager.go create mode 100644 core/types/pow/difficultymanager/interface_difficultymanager.go delete mode 100644 core/types/pow/difficultymanager/interface_processes_difficultymanager.go create mode 100644 core/types/pow/difficultymanager/kaspad.go rename core/{blockchain/difficulty.go => types/pow/difficultymanager/meer.go} (65%) diff --git a/consensus/consensus.go b/consensus/consensus.go index dd0d4d33..0cb71ec6 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -1,6 +1,8 @@ package consensus import ( + "sync" + "github.com/Qitmeer/qng/common/hash" "github.com/Qitmeer/qng/config" "github.com/Qitmeer/qng/consensus/model" @@ -13,7 +15,6 @@ import ( "github.com/Qitmeer/qng/node/service" "github.com/Qitmeer/qng/params" "github.com/Qitmeer/qng/services/index" - "sync" ) type consensus struct { diff --git a/consensus/model/block.go b/consensus/model/block.go index 395e049d..e0e1f8d3 100644 --- a/consensus/model/block.go +++ b/consensus/model/block.go @@ -9,4 +9,5 @@ type Block interface { GetOrder() uint HasParents() bool GetMainParent() uint + GetHeight() uint } diff --git a/consensus/model/block_chain.go b/consensus/model/block_chain.go index f46724ed..b5cdc197 100644 --- a/consensus/model/block_chain.go +++ b/consensus/model/block_chain.go @@ -26,4 +26,6 @@ type BlockChain interface { GetBlockOrderByHash(hash *hash.Hash) (uint, error) GetBlockHeader(block Block) *types.BlockHeader ForeachBlueBlocks(start Block, depth uint, powType pow.PowType, fn func(block Block, header *types.BlockHeader) error) error + ChainRLock() + ChainRUnlock() } diff --git a/consensus/model/meer_dag.go b/consensus/model/meer_dag.go new file mode 100644 index 00000000..a2bf9fa4 --- /dev/null +++ b/consensus/model/meer_dag.go @@ -0,0 +1,11 @@ +package model + +import ( + "github.com/Qitmeer/qng/common/hash" + "github.com/Qitmeer/qng/rpc/api" +) + +type MeerDag interface { + RegisterAPIs(apis []api.API) + GetBlockIDByTxHash(txhash *hash.Hash) uint64 +} diff --git a/core/blockchain/process.go b/core/blockchain/process.go index 4d8bdb94..84f4a1ff 100644 --- a/core/blockchain/process.go +++ b/core/blockchain/process.go @@ -8,16 +8,18 @@ package blockchain import ( "container/list" "fmt" + "time" + "github.com/Qitmeer/qng/common/hash" "github.com/Qitmeer/qng/consensus/model" "github.com/Qitmeer/qng/core/blockchain/utxo" "github.com/Qitmeer/qng/core/state" "github.com/Qitmeer/qng/core/types" "github.com/Qitmeer/qng/core/types/pow" + "github.com/Qitmeer/qng/core/types/pow/difficultymanager" "github.com/Qitmeer/qng/engine/txscript" l "github.com/Qitmeer/qng/log" "github.com/Qitmeer/qng/meerdag" - "time" ) // ProcessBlock is the main workhorse for handling insertion of new blocks into @@ -153,7 +155,7 @@ func (b *BlockChain) preProcessBlock(block *types.SerializedBlock, flags Behavio // expected based on elapsed time since the last checkpoint and // maximum adjustment allowed by the retarget rules. duration := blockHeader.Timestamp.Sub(checkpointTime) - requiredTarget := pow.CompactToBig(b.calcEasiestDifficulty( + requiredTarget := pow.CompactToBig(difficultymanager.NewDiffManager(b.consensus.BlockChain(), b.params).CalcEasiestDifficulty( checkpointNode.Difficulty, duration, block.Block().Header.Pow)) currentTarget := pow.CompactToBig(blockHeader.Difficulty) if !block.Block().Header.Pow.CompareDiff(currentTarget, requiredTarget) { diff --git a/core/blockchain/validate.go b/core/blockchain/validate.go index b83c1dbf..b8588b20 100644 --- a/core/blockchain/validate.go +++ b/core/blockchain/validate.go @@ -10,6 +10,9 @@ import ( "encoding/binary" "encoding/hex" "fmt" + "math" + "time" + "github.com/Qitmeer/qng/common/hash" "github.com/Qitmeer/qng/consensus/forks" "github.com/Qitmeer/qng/consensus/model" @@ -22,11 +25,10 @@ import ( "github.com/Qitmeer/qng/core/state" "github.com/Qitmeer/qng/core/types" "github.com/Qitmeer/qng/core/types/pow" + "github.com/Qitmeer/qng/core/types/pow/difficultymanager" "github.com/Qitmeer/qng/engine/txscript" "github.com/Qitmeer/qng/meerdag" "github.com/Qitmeer/qng/params" - "math" - "time" ) const ( @@ -758,7 +760,8 @@ func (b *BlockChain) checkBlockHeaderContext(block *types.SerializedBlock, prevN // 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, + + expDiff, err := difficultymanager.NewDiffManager(b.consensus.BlockChain(), b.params).RequiredDifficulty(prevNode, header.Timestamp, instance) if err != nil { return err diff --git a/core/types/common.go b/core/types/common.go index 19a0e31c..0ec8c9f3 100644 --- a/core/types/common.go +++ b/core/types/common.go @@ -18,4 +18,9 @@ const ( // MaxAmount is the maximum transaction amount allowed in atoms. // TODO, relocate the coin related item to chain's params MaxAmount = 21e6 * AtomsPerCoin + + // MEER difficulty adjustment + DIFFICULTY_MODE_MEER = 0 + // KASPAD difficulty adjustment + DIFFICULTY_MODE_KASPAD ) diff --git a/core/types/pow/config.go b/core/types/pow/config.go index 52e7b0a4..34a1a1a3 100644 --- a/core/types/pow/config.go +++ b/core/types/pow/config.go @@ -48,9 +48,11 @@ type PowConfig struct { //is init init bool + + DifficultyMode int } -//global cache +// global cache func GetPowConfig() *PowConfig { if PowConfigInstance != nil { return PowConfigInstance diff --git a/core/types/pow/difficultymanager/difficultymanager.go b/core/types/pow/difficultymanager/difficultymanager.go deleted file mode 100644 index 84f96149..00000000 --- a/core/types/pow/difficultymanager/difficultymanager.go +++ /dev/null @@ -1,77 +0,0 @@ -package difficultymanager - -import ( - "math/big" - "time" - - "github.com/Qitmeer/qng/common/math" - "github.com/Qitmeer/qng/core/types/pow" - "github.com/Qitmeer/qng/params" -) - -// DifficultyManager provides a method to resolve the -// difficulty value of a block -type difficultyManager struct { - powMax *big.Int - difficultyAdjustmentWindowSize int - disableDifficultyAdjustment bool - targetTimePerBlock time.Duration - genesisBits uint32 -} - -// New instantiates a new DifficultyManager -func New(cfg *params.Params) DifficultyManager { - return &difficultyManager{ - powMax: cfg.PowConfig.MeerXKeccakV1PowLimit, - difficultyAdjustmentWindowSize: int(cfg.WorkDiffWindowSize), - disableDifficultyAdjustment: false, - targetTimePerBlock: cfg.TargetTimePerBlock, - genesisBits: cfg.PowConfig.MeerXKeccakV1PowLimitBits, - } -} - -// RequiredDifficulty returns the difficulty required for some block -func (dm *difficultyManager) RequiredDifficulty(targetsWindow BlockWindow, powInstance pow.IPow) (uint32, error) { - if powInstance.GetPowType() != pow.MEERXKECCAKV1 || len(targetsWindow) < 1 { - return dm.genesisBits, nil - } - return dm.requiredDifficultyFromTargetsWindow(targetsWindow) -} - -func (dm *difficultyManager) requiredDifficultyFromTargetsWindow(targetsWindow BlockWindow) (uint32, error) { - if dm.disableDifficultyAdjustment { - return dm.genesisBits, nil - } - - // in the past this was < 2 as the comment explains, we changed it to under the window size to - // make the hashrate(which is ~1.5GH/s) constant in the first 2641 blocks so that we won't have a lot of tips - - // We need at least 2 blocks to get a timestamp interval - // We could instead clamp the timestamp difference to `targetTimePerBlock`, - // but then everything will cancel out and we'll get the target from the last block, which will be the same as genesis. - // We add 64 as a safety margin - if len(targetsWindow) < 2 || len(targetsWindow) < dm.difficultyAdjustmentWindowSize { - return dm.genesisBits, nil - } - - windowMinTimestamp, windowMaxTimeStamp, windowMinIndex := targetsWindow.MinMaxTimestamps() - // Remove the last block from the window so to calculate the average target of dag.difficultyAdjustmentWindowSize blocks - targetsWindow.Remove(windowMinIndex) - - // Calculate new target difficulty as: - // averageWindowTarget * (windowMinTimestamp / (targetTimePerBlock * windowSize)) - // The result uses integer division which means it will be slightly - // rounded down. - div := new(big.Int) - newTarget := targetsWindow.AverageTarget() - newTarget. - // We need to clamp the timestamp difference to 1 so that we'll never get a 0 target. - Mul(newTarget, div.SetInt64(math.MaxInt64Val(windowMaxTimeStamp-windowMinTimestamp, 1))). - Div(newTarget, div.SetInt64(dm.targetTimePerBlock.Milliseconds())). - Div(newTarget, div.SetUint64(uint64(len(targetsWindow)))) - if newTarget.Cmp(dm.powMax) > 0 { - return pow.BigToCompact(dm.powMax), nil - } - newTargetBits := pow.BigToCompact(newTarget) - return newTargetBits, nil -} diff --git a/core/types/pow/difficultymanager/interface_difficultymanager.go b/core/types/pow/difficultymanager/interface_difficultymanager.go new file mode 100644 index 00000000..05889613 --- /dev/null +++ b/core/types/pow/difficultymanager/interface_difficultymanager.go @@ -0,0 +1,39 @@ +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" +) + +// DifficultyManager provides a method to resolve the +// difficulty value of a block +type DifficultyManager interface { + CalcNextRequiredDifficulty(timestamp time.Time, powType pow.PowType) (uint32, error) + RequiredDifficulty(block model.Block, newBlockTime time.Time, powInstance pow.IPow) (uint32, error) + CalcEasiestDifficulty(bits uint32, duration time.Duration, powInstance pow.IPow) uint32 + GetCurrentPowDiff(ib model.Block, powType pow.PowType) *big.Int +} + +func NewDiffManager(b model.BlockChain, cfg *params.Params) DifficultyManager { + switch cfg.PowConfig.DifficultyMode { + case types.DIFFICULTY_MODE_KASPAD: + return &kaspadDiff{ + b: b, + powMax: cfg.PowConfig.MeerXKeccakV1PowLimit, + difficultyAdjustmentWindowSize: int(cfg.WorkDiffWindowSize), + disableDifficultyAdjustment: false, + targetTimePerBlock: cfg.TargetTimePerBlock, + genesisBits: cfg.PowConfig.MeerXKeccakV1PowLimitBits, + cfg: cfg, + } + } + return &meerDiff{ + b: b, + cfg: cfg, + } +} diff --git a/core/types/pow/difficultymanager/interface_processes_difficultymanager.go b/core/types/pow/difficultymanager/interface_processes_difficultymanager.go deleted file mode 100644 index 887e43e0..00000000 --- a/core/types/pow/difficultymanager/interface_processes_difficultymanager.go +++ /dev/null @@ -1,60 +0,0 @@ -package difficultymanager - -import ( - "math" - "math/big" - - "github.com/Qitmeer/qng/common/hash" - "github.com/Qitmeer/qng/core/types/pow" -) - -type DifficultyBlock struct { - TimeInMilliseconds int64 - Bits uint32 - Hash hash.Hash - BlueWork bool -} -type BlockWindow []DifficultyBlock - -func ghostdagLess(blockA *DifficultyBlock, blockB *DifficultyBlock) bool { - return blockA.BlueWork == blockB.BlueWork -} - -func (window BlockWindow) MinMaxTimestamps() (min, max int64, minIndex int) { - min = math.MaxInt64 - minIndex = 0 - max = 0 - for i, block := range window { - // If timestamps are equal we ghostdag compare in order to reach consensus on `minIndex` - if block.TimeInMilliseconds < min || - (block.TimeInMilliseconds == min && ghostdagLess(&block, &window[minIndex])) { - min = block.TimeInMilliseconds - minIndex = i - } - if block.TimeInMilliseconds > max { - max = block.TimeInMilliseconds - } - } - return -} - -func (window *BlockWindow) Remove(n int) { - (*window)[n] = (*window)[len(*window)-1] - *window = (*window)[:len(*window)-1] -} - -func (window BlockWindow) AverageTarget() *big.Int { - averageTarget := new(big.Int) - targetTmp := new(big.Int) - for _, block := range window { - pow.CompactToBigWithDestination(block.Bits, targetTmp) - averageTarget.Add(averageTarget, targetTmp) - } - return averageTarget.Div(averageTarget, big.NewInt(int64(len(window)))) -} - -// DifficultyManager provides a method to resolve the -// difficulty value of a block -type DifficultyManager interface { - RequiredDifficulty(blocks BlockWindow, powInstance pow.IPow) (uint32, error) -} diff --git a/core/types/pow/difficultymanager/kaspad.go b/core/types/pow/difficultymanager/kaspad.go new file mode 100644 index 00000000..7ff733e7 --- /dev/null +++ b/core/types/pow/difficultymanager/kaspad.go @@ -0,0 +1,214 @@ +package difficultymanager + +import ( + "math/big" + "time" + + "github.com/Qitmeer/qng/common/hash" + "github.com/Qitmeer/qng/common/math" + "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 DifficultyBlock struct { + TimeInMilliseconds int64 + Bits uint32 + Hash hash.Hash + BlueWork bool +} +type blockWindow []DifficultyBlock + +func ghostdagLess(blockA *DifficultyBlock, blockB *DifficultyBlock) bool { + return blockA.BlueWork == blockB.BlueWork +} + +func (window blockWindow) MinMaxTimestamps() (min, max int64, minIndex int) { + min = math.MaxInt64 + minIndex = 0 + max = 0 + for i, block := range window { + // If timestamps are equal we ghostdag compare in order to reach consensus on `minIndex` + if block.TimeInMilliseconds < min || + (block.TimeInMilliseconds == min && ghostdagLess(&block, &window[minIndex])) { + min = block.TimeInMilliseconds + minIndex = i + } + if block.TimeInMilliseconds > max { + max = block.TimeInMilliseconds + } + } + return +} + +func (window *blockWindow) Remove(n int) { + (*window)[n] = (*window)[len(*window)-1] + *window = (*window)[:len(*window)-1] +} + +func (window blockWindow) AverageTarget() *big.Int { + averageTarget := new(big.Int) + targetTmp := new(big.Int) + for _, block := range window { + pow.CompactToBigWithDestination(block.Bits, targetTmp) + averageTarget.Add(averageTarget, targetTmp) + } + return averageTarget.Div(averageTarget, big.NewInt(int64(len(window)))) +} + +// DifficultyManager provides a method to resolve the +// difficulty value of a block +type kaspadDiff struct { + powMax *big.Int + difficultyAdjustmentWindowSize int + disableDifficultyAdjustment bool + targetTimePerBlock time.Duration + genesisBits uint32 + 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 *kaspadDiff) 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) +} + +func (m *kaspadDiff) CalcNextRequiredDifficulty(timestamp time.Time, powType pow.PowType) (uint32, error) { + m.b.ChainRLock() + block := m.b.GetMainChainTip() + instance := pow.GetInstance(powType, 0, []byte{}) + instance.SetParams(m.cfg.PowConfig) + instance.SetMainHeight(pow.MainHeight(block.GetHeight() + 1)) + difficulty, err := m.RequiredDifficultyByWindows(m.getblockWindows(block, instance.GetPowType(), int(m.cfg.WorkDiffWindowSize))) + m.b.ChainRUnlock() + return difficulty, err +} + +func (m *kaspadDiff) RequiredDifficulty(block model.Block, newBlockTime time.Time, powInstance pow.IPow) (uint32, error) { + return m.RequiredDifficultyByWindows(m.getblockWindows(block, powInstance.GetPowType(), int(m.cfg.WorkDiffWindowSize))) +} + +// RequiredDifficultyByWindows returns the difficulty required for some block +func (dm *kaspadDiff) RequiredDifficultyByWindows(targetsWindow blockWindow) (uint32, error) { + if len(targetsWindow) < 1 { + return dm.genesisBits, nil + } + return dm.requiredDifficultyFromTargetsWindow(targetsWindow) +} + +func (dm *kaspadDiff) requiredDifficultyFromTargetsWindow(targetsWindow blockWindow) (uint32, error) { + if dm.disableDifficultyAdjustment { + return dm.genesisBits, nil + } + + // in the past this was < 2 as the comment explains, we changed it to under the window size to + // make the hashrate(which is ~1.5GH/s) constant in the first 2641 blocks so that we won't have a lot of tips + + // We need at least 2 blocks to get a timestamp interval + // We could instead clamp the timestamp difference to `targetTimePerBlock`, + // but then everything will cancel out and we'll get the target from the last block, which will be the same as genesis. + // We add 64 as a safety margin + if len(targetsWindow) < 2 || len(targetsWindow) < dm.difficultyAdjustmentWindowSize { + return dm.genesisBits, nil + } + + windowMinTimestamp, windowMaxTimeStamp, windowMinIndex := targetsWindow.MinMaxTimestamps() + // Remove the last block from the window so to calculate the average target of dag.difficultyAdjustmentWindowSize blocks + targetsWindow.Remove(windowMinIndex) + + // Calculate new target difficulty as: + // averageWindowTarget * (windowMinTimestamp / (targetTimePerBlock * windowSize)) + // The result uses integer division which means it will be slightly + // rounded down. + div := new(big.Int) + newTarget := targetsWindow.AverageTarget() + newTarget. + // We need to clamp the timestamp difference to 1 so that we'll never get a 0 target. + Mul(newTarget, div.SetInt64(math.MaxInt64Val(windowMaxTimeStamp-windowMinTimestamp, 1))). + Div(newTarget, div.SetInt64(dm.targetTimePerBlock.Milliseconds())). + Div(newTarget, div.SetUint64(uint64(len(targetsWindow)))) + if newTarget.Cmp(dm.powMax) > 0 { + return pow.BigToCompact(dm.powMax), nil + } + newTargetBits := pow.BigToCompact(newTarget) + return newTargetBits, nil +} + +// blockWindow returns a blockWindow of the given size that contains the +// blocks in the past of startingNode, the sorting is unspecified. +// If the number of blocks in the past of startingNode is less then windowSize, +// the window will be padded by genesis blocks to achieve a size of windowSize. +func (dm *kaspadDiff) getblockWindows(oldBlock model.Block, powType pow.PowType, windowSize int) blockWindow { + windows := make(blockWindow, 0, windowSize) + dm.b.ForeachBlueBlocks(oldBlock, uint(windowSize), powType, func(block model.Block, header *types.BlockHeader) error { + windows = append(windows, DifficultyBlock{ + TimeInMilliseconds: header.Timestamp.UnixMilli(), + Bits: header.Difficulty, + Hash: header.BlockHash(), + BlueWork: true, + }) + return nil + }) + + return windows +} + +// find block node by pow type +func (m *kaspadDiff) GetCurrentPowDiff(ib model.Block, powType pow.PowType) *big.Int { + instance := pow.GetInstance(powType, 0, []byte{}) + instance.SetParams(m.cfg.PowConfig) + safeBigDiff := instance.GetSafeDiff(0) + for { + curNode := m.b.GetBlockHeader(ib) + if curNode == nil { + return safeBigDiff + } + if curNode.Pow.GetPowType() == powType { + return pow.CompactToBig(curNode.Difficulty) + } + + if !ib.HasParents() { + return safeBigDiff + } + + ib = m.b.GetBlockById(ib.GetMainParent()) + if ib == nil { + return safeBigDiff + } + } +} diff --git a/core/blockchain/difficulty.go b/core/types/pow/difficultymanager/meer.go similarity index 65% rename from core/blockchain/difficulty.go rename to core/types/pow/difficultymanager/meer.go index 25c243c6..28dfbcde 100644 --- a/core/blockchain/difficulty.go +++ b/core/types/pow/difficultymanager/meer.go @@ -4,19 +4,18 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package blockchain +package difficultymanager import ( "fmt" "math/big" "time" - "github.com/Qitmeer/qng/common/hash" + "github.com/Qitmeer/qng/consensus/model" "github.com/Qitmeer/qng/core/types" "github.com/Qitmeer/qng/core/types/pow" - - "github.com/Qitmeer/qng/core/types/pow/difficultymanager" - "github.com/Qitmeer/qng/meerdag" + "github.com/Qitmeer/qng/log" + "github.com/Qitmeer/qng/params" ) // bigZero is 0 represented as a big.Int. It is defined here to avoid @@ -27,21 +26,26 @@ var bigZero = big.NewInt(0) // testnet difficulty). const maxShift = uint(256) -// calcEasiestDifficulty calculates the easiest possible difficulty that a block +type meerDiff 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 (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration, powInstance pow.IPow) uint32 { +func (m *meerDiff) CalcEasiestDifficulty(bits uint32, duration time.Duration, powInstance pow.IPow) uint32 { // Convert types used in the calculations below. durationVal := int64(duration) - adjustmentFactor := big.NewInt(b.params.RetargetAdjustmentFactor) - maxRetargetTimespan := int64(b.params.TargetTimespan) * - b.params.RetargetAdjustmentFactor + 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 b.params.ReduceMinDifficulty { - if durationVal > int64(b.params.MinDiffReductionTime) { + if m.cfg.ReduceMinDifficulty { + if durationVal > int64(m.cfg.MinDiffReductionTime) { return pow.BigToCompact(target) } } @@ -70,34 +74,31 @@ func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration, // did not have the special testnet minimum difficulty rule applied. // // This function MUST be called with the chain state lock held (for writes). -func (b *BlockChain) findPrevTestNetDifficulty(startBlock meerdag.IBlock, powInstance pow.IPow) uint32 { +func (m *meerDiff) 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(b.params.WorkDiffWindowSize * b.params.WorkDiffWindows) + 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 = b.GetBlockHeader(iterBlock) + iterNode = m.b.GetBlockHeader(iterBlock) if iterNode.Difficulty != pow.BigToCompact(target) { return lastBits } return iterNode.Difficulty } -// calcNextRequiredDifficulty calculates the required difficulty for the block +// 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 CalcNextRequiredDifficulty in that +// 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 (b *BlockChain) calcNextRequiredDifficulty(block meerdag.IBlock, newBlockTime time.Time, powInstance pow.IPow) (uint32, error) { - if powInstance.GetPowType() == pow.MEERXKECCAKV1 { - return difficultymanager.New(b.params).RequiredDifficulty(b.getBlockWindows(block, powInstance.GetPowType(), int(b.params.WorkDiffWindowSize)), powInstance) - } +func (m *meerDiff) RequiredDifficulty(block model.Block, newBlockTime time.Time, powInstance pow.IPow) (uint32, error) { baseTarget := powInstance.GetSafeDiff(0) originCurrentBlock := block // Genesis block. @@ -105,11 +106,11 @@ func (b *BlockChain) calcNextRequiredDifficulty(block meerdag.IBlock, newBlockTi return pow.BigToCompact(baseTarget), nil } - block = b.getPowTypeNode(block, powInstance.GetPowType()) + block = m.getPowTypeNode(block, powInstance.GetPowType()) if block == nil { return pow.BigToCompact(baseTarget), nil } - curNode := b.GetBlockHeader(block) + curNode := m.b.GetBlockHeader(block) if curNode == nil { return pow.BigToCompact(baseTarget), nil } @@ -117,21 +118,21 @@ func (b *BlockChain) calcNextRequiredDifficulty(block meerdag.IBlock, newBlockTi // just return this. oldDiff := curNode.Difficulty oldDiffBig := pow.CompactToBig(curNode.Difficulty) - windowsSizeBig := big.NewInt(b.params.WorkDiffWindowSize) + windowsSizeBig := big.NewInt(m.cfg.WorkDiffWindowSize) // percent is *100 * 2^32 windowsSizeBig.Mul(windowsSizeBig, powInstance.PowPercent()) windowsSizeBig.Div(windowsSizeBig, big.NewInt(100)) windowsSizeBig.Rsh(windowsSizeBig, 32) needAjustCount := int64(windowsSizeBig.Uint64()) // We're not at a retarget point, return the oldDiff. - if !b.needAjustPowDifficulty(block, powInstance.GetPowType(), needAjustCount) { + if !m.needAjustPowDifficulty(block, powInstance.GetPowType(), needAjustCount) { // For networks that support it, allow special reduction of the // required difficulty once too much time has elapsed without // mining a block. - if b.params.ReduceMinDifficulty { + if m.cfg.ReduceMinDifficulty { // Return minimum difficulty when more than the desired // amount of time has elapsed without mining a block. - reductionTime := int64(b.params.MinDiffReductionTime / + reductionTime := int64(m.cfg.MinDiffReductionTime / time.Second) allowMinTime := curNode.Timestamp.Unix() + reductionTime @@ -140,7 +141,7 @@ func (b *BlockChain) calcNextRequiredDifficulty(block meerdag.IBlock, newBlockTi if newBlockTime.Unix() > allowMinTime { timePassed := newBlockTime.Unix() - curNode.Timestamp.Unix() timePassed -= reductionTime - shifts := uint((timePassed / int64(b.params.TargetTimePerBlock/ + shifts := uint((timePassed / int64(m.cfg.TargetTimePerBlock/ time.Second)) + 1) // Scale the difficulty with time passed. @@ -163,29 +164,29 @@ func (b *BlockChain) calcNextRequiredDifficulty(block meerdag.IBlock, newBlockTi // The block was mined within the desired timeframe, so // return the difficulty for the last block which did // not have the special minimum difficulty rule applied. - return b.findPrevTestNetDifficulty(block, powInstance), nil + return m.findPrevTestNetDifficulty(block, powInstance), nil } return oldDiff, nil } // Declare some useful variables. - RAFBig := big.NewInt(b.params.RetargetAdjustmentFactor) + RAFBig := big.NewInt(m.cfg.RetargetAdjustmentFactor) nextDiffBigMin := pow.CompactToBig(curNode.Difficulty) nextDiffBigMin.Div(nextDiffBigMin, RAFBig) nextDiffBigMax := pow.CompactToBig(curNode.Difficulty) nextDiffBigMax.Mul(nextDiffBigMax, RAFBig) - alpha := b.params.WorkDiffAlpha + alpha := m.cfg.WorkDiffAlpha // Number of nodes to traverse while calculating difficulty. - nodesToTraverse := needAjustCount * b.params.WorkDiffWindows - percentStatsRecentCount := b.params.WorkDiffWindowSize * b.params.WorkDiffWindows + nodesToTraverse := needAjustCount * m.cfg.WorkDiffWindows + percentStatsRecentCount := m.cfg.WorkDiffWindowSize * m.cfg.WorkDiffWindows //calc pow block count in last nodesToTraverse blocks - currentPowBlockCount := b.calcCurrentPowCount(originCurrentBlock, percentStatsRecentCount, powInstance.GetPowType()) + currentPowBlockCount := m.calcCurrentPowCount(originCurrentBlock, percentStatsRecentCount, powInstance.GetPowType()) // Initialize bigInt slice for the percentage changes for each window period // above or below the target. - windowChanges := make([]*big.Int, b.params.WorkDiffWindows) + windowChanges := make([]*big.Int, m.cfg.WorkDiffWindows) // Regress through all of the previous blocks and store the percent changes // per window period; use bigInts to emulate 64.32 bit fixed point. @@ -205,22 +206,22 @@ func (b *BlockChain) calcNextRequiredDifficulty(block meerdag.IBlock, newBlockTi // Just assume we're at the target (no change) if we've // gone all the way back to the genesis block. if oldBlockOrder == 0 { - timeDifference = int64(b.params.TargetTimespan / + timeDifference = int64(m.cfg.TargetTimespan / time.Second) } timeDifBig := big.NewInt(timeDifference) timeDifBig.Lsh(timeDifBig, 32) // Add padding - targetTemp := big.NewInt(int64(b.params.TargetTimespan / + targetTemp := big.NewInt(int64(m.cfg.TargetTimespan / time.Second)) windowAdjusted := targetTemp.Div(timeDifBig, targetTemp) // Weight it exponentially. Be aware that this could at some point // overflow if alpha or the number of blocks used is really large. windowAdjusted = windowAdjusted.Lsh(windowAdjusted, - uint((b.params.WorkDiffWindows-windowPeriod)*alpha)) + uint((m.cfg.WorkDiffWindows-windowPeriod)*alpha)) // Sum up all the different weights incrementally. - weights += 1 << uint64((b.params.WorkDiffWindows-windowPeriod)* + weights += 1 << uint64((m.cfg.WorkDiffWindows-windowPeriod)* alpha) // Store it in the slice. @@ -237,17 +238,17 @@ func (b *BlockChain) calcNextRequiredDifficulty(block meerdag.IBlock, newBlockTi // Get the previous node while staying at the genesis block as // needed. if oldBlock != nil && oldBlock.HasParents() { - oldBlock = b.bd.GetBlockById(oldBlock.GetMainParent()) + oldBlock = m.b.GetBlockById(oldBlock.GetMainParent()) if oldBlock == nil { continue } - oldBlock = b.getPowTypeNode(oldBlock, powInstance.GetPowType()) + oldBlock = m.getPowTypeNode(oldBlock, powInstance.GetPowType()) if oldBlock == nil { oldNodeTimestamp = 0 oldBlockOrder = 0 continue } - on := b.GetBlockHeader(oldBlock) + on := m.b.GetBlockHeader(oldBlock) if on == nil { continue } @@ -257,7 +258,7 @@ func (b *BlockChain) calcNextRequiredDifficulty(block meerdag.IBlock, newBlockTi } // Sum up the weighted window periods. weightedSum := big.NewInt(0) - for i := int64(0); i < b.params.WorkDiffWindows; i++ { + for i := int64(0); i < m.cfg.WorkDiffWindows; i++ { weightedSum.Add(weightedSum, windowChanges[i]) } @@ -308,53 +309,8 @@ func (b *BlockChain) calcNextRequiredDifficulty(block meerdag.IBlock, newBlockTi return nextDiffBits, nil } -// blockWindow returns a blockWindow of the given size that contains the -// blocks in the past of startingNode, the sorting is unspecified. -// If the number of blocks in the past of startingNode is less then windowSize, -// the window will be padded by genesis blocks to achieve a size of windowSize. -func (b *BlockChain) getBlockWindows(oldBlock meerdag.IBlock, powType pow.PowType, windowSize int) difficultymanager.BlockWindow { - windows := make(difficultymanager.BlockWindow, 0, windowSize) - count := 0 - for i := uint64(0); ; i++ { - // Get the previous node while staying at the genesis block as - // needed. - if oldBlock == nil || !oldBlock.HasParents() { - break - } - ids := oldBlock.GetParents().SortList(false) - for i := 0; i < len(ids); i++ { - id := ids[i] - if count >= windowSize { - return windows - } - oldBlock = b.bd.GetBlockById(id) - if oldBlock == nil { - continue - } - oldBlock = b.getPowTypeNode(oldBlock, powType) - if oldBlock == nil { - continue - } - - on := b.GetBlockHeader(oldBlock) - if on == nil { - continue - } - windows = append(windows, difficultymanager.DifficultyBlock{ - TimeInMilliseconds: on.Timestamp.UnixMilli(), - Bits: on.Difficulty, - Hash: on.BlockHash(), - BlueWork: b.BlockDAG().IsBlue(oldBlock.GetID()), - }) - count++ - } - - } - return windows -} - // stats current pow count in nodesToTraverse -func (b *BlockChain) calcCurrentPowCount(block meerdag.IBlock, nodesToTraverse int64, powType pow.PowType) int64 { +func (m *meerDiff) calcCurrentPowCount(block model.Block, nodesToTraverse int64, powType pow.PowType) int64 { // Genesis block. if block == nil { return 0 @@ -370,9 +326,9 @@ func (b *BlockChain) calcCurrentPowCount(block meerdag.IBlock, nodesToTraverse i currentPowBlockCount-- } if oldBlock.HasParents() { - ob := b.bd.GetBlockById(oldBlock.GetMainParent()) + ob := m.b.GetBlockById(oldBlock.GetMainParent()) if ob != nil { - oldNode := b.GetBlockHeader(ob) + oldNode := m.b.GetBlockHeader(ob) if oldNode == nil { continue } @@ -388,21 +344,21 @@ func (b *BlockChain) calcCurrentPowCount(block meerdag.IBlock, nodesToTraverse i } // whether need ajust Pow Difficulty -// recent b.params.WorkDiffWindowSize blocks +// recent m.cfg.WorkDiffWindowSize blocks // if current count arrived target block count . need ajustment difficulty -func (b *BlockChain) needAjustPowDifficulty(block meerdag.IBlock, powType pow.PowType, needAjustCount int64) bool { - countFromLastAdjustment := b.getDistanceFromLastAdjustment(block, powType, needAjustCount) - // countFromLastAdjustment stats b.params.WorkDiffWindows Multiple count - countFromLastAdjustment /= b.params.WorkDiffWindows +func (m *meerDiff) needAjustPowDifficulty(block model.Block, powType pow.PowType, needAjustCount int64) bool { + countFromLastAdjustment := m.getDistanceFromLastAdjustment(block, powType, needAjustCount) + // countFromLastAdjustment stats m.b.params.WorkDiffWindows Multiple count + countFromLastAdjustment /= m.cfg.WorkDiffWindows return countFromLastAdjustment > 0 && countFromLastAdjustment%needAjustCount == 0 } // Distance block count from last adjustment -func (b *BlockChain) getDistanceFromLastAdjustment(block meerdag.IBlock, powType pow.PowType, needAjustCount int64) int64 { +func (m *meerDiff) getDistanceFromLastAdjustment(block model.Block, powType pow.PowType, needAjustCount int64) int64 { if block == nil { return 0 } - curNode := b.GetBlockHeader(block) + curNode := m.b.GetBlockHeader(block) if curNode == nil { return 0 } @@ -423,59 +379,43 @@ func (b *BlockChain) getDistanceFromLastAdjustment(block meerdag.IBlock, powType } // if TargetTimespan have only one pow block need ajustment difficulty // or count >= needAjustCount - if (count > 1 && currentTime-curNode.Timestamp.Unix() > (count-1)*int64(b.params.TargetTimespan/time.Second)) || + if (count > 1 && currentTime-curNode.Timestamp.Unix() > (count-1)*int64(m.cfg.TargetTimespan/time.Second)) || count >= needAjustCount { - return needAjustCount * b.params.WorkDiffWindows + return needAjustCount * m.cfg.WorkDiffWindows } if !block.HasParents() { return count } - block = b.bd.GetBlockById(block.GetMainParent()) + block = m.b.GetBlockById(block.GetMainParent()) if block != nil { - curNode = b.GetBlockHeader(block) + curNode = m.b.GetBlockHeader(block) } else { return count } } } -// CalcNextRequiredDiffFromNode calculates the required difficulty for the block -// given with the passed hash along with the given timestamp. -// -// This function is NOT safe for concurrent access. -func (b *BlockChain) CalcNextRequiredDiffFromNode(hash *hash.Hash, timestamp time.Time, powType pow.PowType) (uint32, error) { - ib := b.bd.GetBlock(hash) - if ib == nil { - return 0, fmt.Errorf("block %s is not known", hash) - } - - instance := pow.GetInstance(powType, 0, []byte{}) - instance.SetParams(b.params.PowConfig) - instance.SetMainHeight(pow.MainHeight(ib.GetHeight() + 1)) - return b.calcNextRequiredDifficulty(ib, timestamp, instance) -} - // CalcNextRequiredDifficulty calculates the required difficulty for the block // after the end of the current best chain based on the difficulty retarget // rules. // // This function is safe for concurrent access. -func (b *BlockChain) CalcNextRequiredDifficulty(timestamp time.Time, powType pow.PowType) (uint32, error) { - b.ChainRLock() - block := b.bd.GetMainChainTip() +func (m *meerDiff) CalcNextRequiredDifficulty(timestamp time.Time, powType pow.PowType) (uint32, error) { + m.b.ChainRLock() + block := m.b.GetMainChainTip() instance := pow.GetInstance(powType, 0, []byte{}) - instance.SetParams(b.params.PowConfig) + instance.SetParams(m.cfg.PowConfig) instance.SetMainHeight(pow.MainHeight(block.GetHeight() + 1)) - difficulty, err := b.calcNextRequiredDifficulty(block, timestamp, instance) - b.ChainRUnlock() + difficulty, err := m.RequiredDifficulty(block, timestamp, instance) + m.b.ChainRUnlock() return difficulty, err } // find block node by pow type -func (b *BlockChain) getPowTypeNode(block meerdag.IBlock, powType pow.PowType) meerdag.IBlock { +func (m *meerDiff) getPowTypeNode(block model.Block, powType pow.PowType) model.Block { for { - curNode := b.GetBlockHeader(block) + curNode := m.b.GetBlockHeader(block) if curNode == nil { return nil } @@ -486,7 +426,7 @@ func (b *BlockChain) getPowTypeNode(block meerdag.IBlock, powType pow.PowType) m if !block.HasParents() { return nil } - block = b.bd.GetBlockById(block.GetMainParent()) + block = m.b.GetBlockById(block.GetMainParent()) if block == nil { return nil } @@ -494,12 +434,12 @@ func (b *BlockChain) getPowTypeNode(block meerdag.IBlock, powType pow.PowType) m } // find block node by pow type -func (b *BlockChain) GetCurrentPowDiff(ib meerdag.IBlock, powType pow.PowType) *big.Int { +func (m *meerDiff) GetCurrentPowDiff(ib model.Block, powType pow.PowType) *big.Int { instance := pow.GetInstance(powType, 0, []byte{}) - instance.SetParams(b.params.PowConfig) + instance.SetParams(m.cfg.PowConfig) safeBigDiff := instance.GetSafeDiff(0) for { - curNode := b.GetBlockHeader(ib) + curNode := m.b.GetBlockHeader(ib) if curNode == nil { return safeBigDiff } @@ -511,7 +451,7 @@ func (b *BlockChain) GetCurrentPowDiff(ib meerdag.IBlock, powType pow.PowType) * return safeBigDiff } - ib = b.bd.GetBlockById(ib.GetMainParent()) + ib = m.b.GetBlockById(ib.GetMainParent()) if ib == nil { return safeBigDiff } diff --git a/node/api.go b/node/api.go index 018f5006..aa889af8 100644 --- a/node/api.go +++ b/node/api.go @@ -8,12 +8,17 @@ package node import ( js "encoding/json" "fmt" + "math/big" + "strconv" + "time" + "github.com/Qitmeer/qng/common/marshal" "github.com/Qitmeer/qng/common/roughtime" "github.com/Qitmeer/qng/consensus/forks" "github.com/Qitmeer/qng/core/json" "github.com/Qitmeer/qng/core/protocol" "github.com/Qitmeer/qng/core/types/pow" + "github.com/Qitmeer/qng/core/types/pow/difficultymanager" "github.com/Qitmeer/qng/meerdag" "github.com/Qitmeer/qng/meerevm/eth" "github.com/Qitmeer/qng/params" @@ -21,9 +26,6 @@ import ( "github.com/Qitmeer/qng/rpc/client/cmds" "github.com/Qitmeer/qng/services/common" "github.com/Qitmeer/qng/version" - "math/big" - "strconv" - "time" ) func (nf *QitmeerFull) apis() []api.API { @@ -58,7 +60,7 @@ func NewPublicBlockChainAPI(node *QitmeerFull) *PublicBlockChainAPI { func (api *PublicBlockChainAPI) GetNodeInfo() (interface{}, error) { best := api.node.GetBlockChain().BestSnapshot() node := api.node.GetBlockChain().BlockDAG().GetBlock(&best.Hash) - powNodes := api.node.GetBlockChain().GetCurrentPowDiff(node, pow.MEERXKECCAKV1) + powNodes := difficultymanager.NewDiffManager(api.node.GetBlockChain().Consensus().BlockChain(), api.node.GetBlockChain().ChainParams()).GetCurrentPowDiff(node, pow.MEERXKECCAKV1) ret := &json.InfoNodeResult{ ID: api.node.GetPeerServer().PeerID().String(), Version: int32(1000000*version.Major + 10000*version.Minor + 100*version.Patch), diff --git a/params/params_mixnet.go b/params/params_mixnet.go index c8c78df9..fd3cc58e 100644 --- a/params/params_mixnet.go +++ b/params/params_mixnet.go @@ -86,6 +86,7 @@ var MixNetParams = Params{ }, // after this height the big graph will be the main pow graph AdjustmentStartMainHeight: 1440 * 15 / mixTargetTimePerBlock, + DifficultyMode: types.DIFFICULTY_MODE_KASPAD, }, WorkDiffAlpha: 1, @@ -127,7 +128,7 @@ var MixNetParams = Params{ SLIP0044CoinType: 813, LegacyCoinType: 223, - CoinbaseMaturity: 720, + CoinbaseMaturity: 16, OrganizationPkScript: hexMustDecode("76a91429209320e66d96839785dd07e643a7f1592edc5a88ac"), TokenAdminPkScript: hexMustDecode("00000000c96d6d76a914b8834294977b26a44094fe2216f8a7d59af1130888ac"), } diff --git a/services/mining/newblocktemplate.go b/services/mining/newblocktemplate.go index 0eb9356e..ca7d94f8 100644 --- a/services/mining/newblocktemplate.go +++ b/services/mining/newblocktemplate.go @@ -2,6 +2,8 @@ package mining import ( "fmt" + "time" + "github.com/Qitmeer/qng/common/hash" "github.com/Qitmeer/qng/consensus/model" "github.com/Qitmeer/qng/core/address" @@ -11,13 +13,13 @@ import ( s "github.com/Qitmeer/qng/core/serialization" "github.com/Qitmeer/qng/core/types" "github.com/Qitmeer/qng/core/types/pow" + "github.com/Qitmeer/qng/core/types/pow/difficultymanager" "github.com/Qitmeer/qng/engine/txscript" "github.com/Qitmeer/qng/log" "github.com/Qitmeer/qng/meerdag" "github.com/Qitmeer/qng/params" "github.com/Qitmeer/qng/services/mempool" "golang.org/x/net/context" - "time" ) // NewBlockTemplate returns a new block template that is ready to be solved @@ -504,7 +506,7 @@ mempool: ts := MedianAdjustedTime(bc, timeSource) // - reqCompactDifficulty, err := bc.CalcNextRequiredDifficulty(ts, powType) + reqCompactDifficulty, err := difficultymanager.NewDiffManager(bc.Consensus().BlockChain(), bc.ChainParams()).CalcNextRequiredDifficulty(ts, powType) if err != nil { return nil, miningRuleError(ErrGettingDifficulty, err.Error()) } @@ -597,7 +599,7 @@ func UpdateBlockTime(msgBlock *types.Block, chain *blockchain.BlockChain, timeSo // If running on a network that requires recalculating the difficulty, // do so now. if activeNetParams.ReduceMinDifficulty { - difficulty, err := chain.CalcNextRequiredDifficulty( + difficulty, err := difficultymanager.NewDiffManager(chain.Consensus().BlockChain(), chain.ChainParams()).CalcNextRequiredDifficulty( newTimestamp, msgBlock.Header.Pow.GetPowType()) if err != nil { return miningRuleError(ErrGettingDifficulty, err.Error())