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/difficulty_manager.go b/consensus/model/difficulty_manager.go new file mode 100644 index 00000000..0dace629 --- /dev/null +++ b/consensus/model/difficulty_manager.go @@ -0,0 +1,16 @@ +package model + +import ( + "math/big" + "time" + + "github.com/Qitmeer/qng/core/types/pow" +) + +// DifficultyManager provides a method to resolve the +// difficulty value of a block +type DifficultyManager interface { + RequiredDifficulty(block Block, newBlockTime time.Time, powInstance pow.IPow) (uint32, error) + CalcEasiestDifficulty(bits uint32, duration time.Duration, powInstance pow.IPow) uint32 + GetCurrentPowDiff(ib Block, powType pow.PowType) *big.Int +} diff --git a/core/blockchain/blockchain.go b/core/blockchain/blockchain.go index 5e626114..809b2187 100644 --- a/core/blockchain/blockchain.go +++ b/core/blockchain/blockchain.go @@ -5,6 +5,10 @@ package blockchain import ( "container/list" "fmt" + "sort" + "sync" + "time" + "github.com/Qitmeer/qng/common/hash" "github.com/Qitmeer/qng/common/roughtime" "github.com/Qitmeer/qng/common/system" @@ -18,6 +22,7 @@ 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/database/common" "github.com/Qitmeer/qng/engine/txscript" l "github.com/Qitmeer/qng/log" @@ -28,9 +33,6 @@ import ( "github.com/Qitmeer/qng/params" "github.com/Qitmeer/qng/services/progresslog" "github.com/schollz/progressbar/v3" - "sort" - "sync" - "time" ) const ( @@ -135,7 +137,8 @@ type BlockChain struct { wg sync.WaitGroup quit chan struct{} - meerChain *meer.MeerChain + meerChain *meer.MeerChain + difficultyManager model.DifficultyManager } func (b *BlockChain) Init() error { @@ -169,6 +172,8 @@ func (b *BlockChain) Init() error { for _, v := range tips { 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) return nil } diff --git a/core/blockchain/difficulty.go b/core/blockchain/difficulty.go index 12ff8c8a..e0cdebdd 100644 --- a/core/blockchain/difficulty.go +++ b/core/blockchain/difficulty.go @@ -7,402 +7,23 @@ package blockchain import ( - "fmt" - "github.com/Qitmeer/qng/common/hash" - "github.com/Qitmeer/qng/core/types" - "github.com/Qitmeer/qng/core/types/pow" - "github.com/Qitmeer/qng/meerdag" "math/big" "time" -) -// bigZero is 0 represented as a big.Int. It is defined here to avoid -// the overhead of creating it multiple times. -var bigZero = big.NewInt(0) - -// maxShift is the maximum shift for a difficulty that resets (e.g. -// testnet difficulty). -const maxShift = uint(256) + "github.com/Qitmeer/qng/consensus/model" + "github.com/Qitmeer/qng/core/types/pow" +) -// calcEasiestDifficulty calculates the easiest possible difficulty that a block +// 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 { - // Convert types used in the calculations below. - durationVal := int64(duration) - adjustmentFactor := big.NewInt(b.params.RetargetAdjustmentFactor) - maxRetargetTimespan := int64(b.params.TargetTimespan) * - b.params.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) { - 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 *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration, powInstance pow.IPow) uint32 { + return m.difficultyManager.CalcEasiestDifficulty(bits, duration, powInstance) } -// 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 (b *BlockChain) findPrevTestNetDifficulty(startBlock meerdag.IBlock, 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) - iterBlock := startBlock - if iterBlock == nil || - uint64(iterBlock.GetHeight())%blocksPerRetarget == 0 { - return lastBits - } - var iterNode *types.BlockHeader - iterNode = b.GetBlockHeader(iterBlock) - if iterNode.Difficulty != pow.BigToCompact(target) { - return lastBits - } - return iterNode.Difficulty -} - -// calcNextRequiredDifficulty 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 -// 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) { - baseTarget := powInstance.GetSafeDiff(0) - originCurrentBlock := block - // Genesis block. - if block == nil { - return pow.BigToCompact(baseTarget), nil - } - - block = b.getPowTypeNode(block, powInstance.GetPowType()) - if block == nil { - return pow.BigToCompact(baseTarget), nil - } - curNode := b.GetBlockHeader(block) - if curNode == nil { - return pow.BigToCompact(baseTarget), nil - } - // Get the old difficulty; if we aren't at a block height where it changes, - // just return this. - oldDiff := curNode.Difficulty - oldDiffBig := pow.CompactToBig(curNode.Difficulty) - windowsSizeBig := big.NewInt(b.params.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) { - // 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 { - // Return minimum difficulty when more than the desired - // amount of time has elapsed without mining a block. - reductionTime := int64(b.params.MinDiffReductionTime / - time.Second) - allowMinTime := curNode.Timestamp.Unix() + reductionTime - - // For every extra target timespan that passes, we halve the - // difficulty. - if newBlockTime.Unix() > allowMinTime { - timePassed := newBlockTime.Unix() - curNode.Timestamp.Unix() - timePassed -= reductionTime - shifts := uint((timePassed / int64(b.params.TargetTimePerBlock/ - time.Second)) + 1) - - // Scale the difficulty with time passed. - oldTarget := pow.CompactToBig(curNode.Difficulty) - newTarget := new(big.Int) - if shifts < maxShift { - newTarget.Lsh(oldTarget, shifts) - } else { - newTarget.Set(pow.OneLsh256) - } - - // Limit new value to the proof of work limit. - if powInstance.CompareDiff(newTarget, baseTarget) { - newTarget.Set(baseTarget) - } - - return pow.BigToCompact(newTarget), nil - } - - // 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 oldDiff, nil - } - // Declare some useful variables. - RAFBig := big.NewInt(b.params.RetargetAdjustmentFactor) - nextDiffBigMin := pow.CompactToBig(curNode.Difficulty) - nextDiffBigMin.Div(nextDiffBigMin, RAFBig) - nextDiffBigMax := pow.CompactToBig(curNode.Difficulty) - nextDiffBigMax.Mul(nextDiffBigMax, RAFBig) - - alpha := b.params.WorkDiffAlpha - - // Number of nodes to traverse while calculating difficulty. - nodesToTraverse := needAjustCount * b.params.WorkDiffWindows - percentStatsRecentCount := b.params.WorkDiffWindowSize * b.params.WorkDiffWindows - //calc pow block count in last nodesToTraverse blocks - currentPowBlockCount := b.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) - - // Regress through all of the previous blocks and store the percent changes - // per window period; use bigInts to emulate 64.32 bit fixed point. - var olderTime, windowPeriod int64 - var weights uint64 - oldBlock := block - - oldNodeTimestamp := curNode.Timestamp.Unix() - oldBlockOrder := block.GetOrder() - - recentTime := curNode.Timestamp.Unix() - for i := uint64(0); ; i++ { - // Store and reset after reaching the end of every window period. - if i%uint64(needAjustCount) == 0 && i != 0 { - olderTime = oldNodeTimestamp - timeDifference := recentTime - olderTime - // 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 / - time.Second) - } - timeDifBig := big.NewInt(timeDifference) - timeDifBig.Lsh(timeDifBig, 32) // Add padding - targetTemp := big.NewInt(int64(b.params.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)) - - // Sum up all the different weights incrementally. - weights += 1 << uint64((b.params.WorkDiffWindows-windowPeriod)* - alpha) - - // Store it in the slice. - windowChanges[windowPeriod] = windowAdjusted - - windowPeriod++ - - recentTime = olderTime - } - - if i == uint64(nodesToTraverse) { - break // Exit for loop when we hit the end. - } - // Get the previous node while staying at the genesis block as - // needed. - if oldBlock != nil && oldBlock.HasParents() { - oldBlock = b.bd.GetBlockById(oldBlock.GetMainParent()) - if oldBlock == nil { - continue - } - oldBlock = b.getPowTypeNode(oldBlock, powInstance.GetPowType()) - if oldBlock == nil { - oldNodeTimestamp = 0 - oldBlockOrder = 0 - continue - } - on := b.GetBlockHeader(oldBlock) - if on == nil { - continue - } - oldNodeTimestamp = on.Timestamp.Unix() - oldBlockOrder = oldBlock.GetOrder() - } - } - // Sum up the weighted window periods. - weightedSum := big.NewInt(0) - for i := int64(0); i < b.params.WorkDiffWindows; i++ { - weightedSum.Add(weightedSum, windowChanges[i]) - } - - // Divide by the sum of all weights. - weightsBig := big.NewInt(int64(weights)) - weightedSumDiv := weightedSum.Div(weightedSum, weightsBig) - // if current pow count is zero , set 1 min 1 - if currentPowBlockCount <= 0 { - currentPowBlockCount = 1 - } - //percent calculate - currentPowPercent := big.NewInt(int64(currentPowBlockCount)) - currentPowPercent.Lsh(currentPowPercent, 32) - nodesToTraverseBig := big.NewInt(percentStatsRecentCount) - currentPowPercent = currentPowPercent.Div(currentPowPercent, nodesToTraverseBig) - // Multiply by the old diff. - nextDiffBig := powInstance.GetNextDiffBig(weightedSumDiv, oldDiffBig, currentPowPercent) - // Right shift to restore the original padding (restore non-fixed point). - // Check to see if we're over the limits for the maximum allowable retarget; - // if we are, return the maximum or minimum except in the case that oldDiff - // is zero. - if oldDiffBig.Cmp(bigZero) == 0 { // This should never really happen, - nextDiffBig.Set(nextDiffBig) // but in case it does... - } else if nextDiffBig.Cmp(bigZero) == 0 { - nextDiffBig.Set(baseTarget) - } else if nextDiffBig.Cmp(nextDiffBigMax) == 1 { - nextDiffBig.Set(nextDiffBigMax) - } else if nextDiffBig.Cmp(nextDiffBigMin) == -1 { - nextDiffBig.Set(nextDiffBigMin) - } - - // Limit new value to the proof of work limit. - if !powInstance.CompareDiff(nextDiffBig, baseTarget) { - nextDiffBig.Set(baseTarget) - } - // Log new target difficulty and return it. The new target logging is - // intentionally converting the bits back to a number instead of using - // newTarget since conversion to the compact representation loses - // precision. - nextDiffBits := pow.BigToCompact(nextDiffBig) - - log.Debug("Difficulty retarget", "block main height", block.GetHeight()+1) - log.Debug("Old target", "bits", fmt.Sprintf("%08x", curNode.Difficulty), - "diff", fmt.Sprintf("(%064x)", oldDiffBig)) - log.Debug("New target", "bits", fmt.Sprintf("%08x", nextDiffBits), - "diff", fmt.Sprintf("(%064x)", nextDiffBig)) - - return nextDiffBits, nil -} - -// stats current pow count in nodesToTraverse -func (b *BlockChain) calcCurrentPowCount(block meerdag.IBlock, nodesToTraverse int64, powType pow.PowType) int64 { - // Genesis block. - if block == nil { - return 0 - } - currentPowBlockCount := nodesToTraverse - // Regress through all of the previous blocks and store the percent changes - // per window period; use bigInts to emulate 64.32 bit fixed point. - oldBlock := block - for i := int64(0); i < nodesToTraverse; i++ { - // Get the previous node while staying at the genesis block as - // needed. - if oldBlock.GetOrder() == 0 { - currentPowBlockCount-- - } - if oldBlock.HasParents() { - ob := b.bd.GetBlockById(oldBlock.GetMainParent()) - if ob != nil { - oldNode := b.GetBlockHeader(ob) - if oldNode == nil { - continue - } - oldBlock = ob - if oldBlock.GetOrder() != 0 && oldNode.Pow.GetPowType() != powType { - currentPowBlockCount-- - } - - } - } - } - return currentPowBlockCount -} - -// whether need ajust Pow Difficulty -// recent b.params.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 - 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 { - if block == nil { - return 0 - } - curNode := b.GetBlockHeader(block) - if curNode == nil { - return 0 - } - //calculate - oldBits := curNode.Difficulty - count := int64(0) - currentTime := curNode.Timestamp.Unix() - for { - if curNode.Pow.GetPowType() == powType { - if oldBits != curNode.Difficulty { - return count - } - count++ - } - if block.GetOrder() == 0 { - //geniess block - return count - } - // 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)) || - count >= needAjustCount { - return needAjustCount * b.params.WorkDiffWindows - } - if !block.HasParents() { - return count - } - - block = b.bd.GetBlockById(block.GetMainParent()) - if block != nil { - curNode = 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) +func (m *BlockChain) calcNextRequiredDifficulty(block model.Block, newBlockTime time.Time, powInstance pow.IPow) (uint32, error) { + return m.difficultyManager.RequiredDifficulty(block, newBlockTime, powInstance) } // CalcNextRequiredDifficulty calculates the required difficulty for the block @@ -416,53 +37,12 @@ func (b *BlockChain) CalcNextRequiredDifficulty(timestamp time.Time, powType pow instance := pow.GetInstance(powType, 0, []byte{}) instance.SetParams(b.params.PowConfig) instance.SetMainHeight(pow.MainHeight(block.GetHeight() + 1)) - difficulty, err := b.calcNextRequiredDifficulty(block, timestamp, instance) + difficulty, err := b.difficultyManager.RequiredDifficulty(block, timestamp, instance) b.ChainRUnlock() return difficulty, err } // find block node by pow type -func (b *BlockChain) getPowTypeNode(block meerdag.IBlock, powType pow.PowType) meerdag.IBlock { - for { - curNode := b.GetBlockHeader(block) - if curNode == nil { - return nil - } - if curNode.Pow.GetPowType() == powType { - return block - } - - if !block.HasParents() { - return nil - } - block = b.bd.GetBlockById(block.GetMainParent()) - if block == nil { - return nil - } - } -} - -// find block node by pow type -func (b *BlockChain) GetCurrentPowDiff(ib meerdag.IBlock, powType pow.PowType) *big.Int { - instance := pow.GetInstance(powType, 0, []byte{}) - instance.SetParams(b.params.PowConfig) - safeBigDiff := instance.GetSafeDiff(0) - for { - curNode := b.GetBlockHeader(ib) - if curNode == nil { - return safeBigDiff - } - if curNode.Pow.GetPowType() == powType { - return pow.CompactToBig(curNode.Difficulty) - } - - if !ib.HasParents() { - return safeBigDiff - } - - ib = b.bd.GetBlockById(ib.GetMainParent()) - if ib == nil { - return safeBigDiff - } - } +func (b *BlockChain) GetCurrentPowDiff(ib model.Block, powType pow.PowType) *big.Int { + return b.GetCurrentPowDiff(ib, powType) } diff --git a/core/blockchain/process.go b/core/blockchain/process.go index 4d8bdb94..efce5d44 100644 --- a/core/blockchain/process.go +++ b/core/blockchain/process.go @@ -8,6 +8,8 @@ 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" @@ -17,7 +19,6 @@ import ( "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 diff --git a/core/blockchain/validate.go b/core/blockchain/validate.go index b83c1dbf..50c6eb64 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" @@ -25,8 +28,6 @@ import ( "github.com/Qitmeer/qng/engine/txscript" "github.com/Qitmeer/qng/meerdag" "github.com/Qitmeer/qng/params" - "math" - "time" ) const ( @@ -758,6 +759,7 @@ 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, header.Timestamp, instance) if err != nil { 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/diff.go b/core/types/pow/diff.go index d05ff176..86892646 100644 --- a/core/types/pow/diff.go +++ b/core/types/pow/diff.go @@ -5,8 +5,9 @@ package pow import ( "fmt" - "github.com/Qitmeer/qng/common/hash" "math/big" + + "github.com/Qitmeer/qng/common/hash" ) var ( @@ -19,6 +20,13 @@ var ( OneLsh256 = new(big.Int).Lsh(bigOne, 256) ) +const ( + // MEER difficulty adjustment + DIFFICULTY_MODE_MEER = 0 + // KASPAD difficulty adjustment + DIFFICULTY_MODE_KASPAD = 1 +) + // HashToBig converts a hash.Hash into a big.Int that can be used to // perform math comparisons. func HashToBig(hash *hash.Hash) *big.Int { @@ -40,18 +48,21 @@ func HashToBig(hash *hash.Hash) *big.Int { // Like IEEE754 floating point, there are three basic components: the sign, // the exponent, and the mantissa. They are broken out as follows: // -// * the most significant 8 bits represent the unsigned base 256 exponent -// * bit 23 (the 24th bit) represents the sign bit -// * the least significant 23 bits represent the mantissa +// - the most significant 8 bits represent the unsigned base 256 exponent +// +// - bit 23 (the 24th bit) represents the sign bit +// +// - the least significant 23 bits represent the mantissa // -// ------------------------------------------------- -// | Exponent | Sign | Mantissa | -// ------------------------------------------------- -// | 8 bits [31-24] | 1 bit [23] | 23 bits [22-00] | -// ------------------------------------------------- +// ------------------------------------------------- +// | Exponent | Sign | Mantissa | +// ------------------------------------------------- +// | 8 bits [31-24] | 1 bit [23] | 23 bits [22-00] | +// ------------------------------------------------- // // The formula to calculate N is: -// N = (-1^sign) * mantissa * 256^(exponent-3) +// +// N = (-1^sign) * mantissa * 256^(exponent-3) // // This compact form is only used to encode unsigned 256-bit numbers which // represent difficulty targets, thus there really is not a need for a sign @@ -190,7 +201,7 @@ func mergeDifficulty(oldDiff int64, newDiff1 int64, newDiff2 int64) int64 { return summedChange.Int64() } -//calc cuckoo diff +// calc cuckoo diff func CalcCuckooDiff(scale uint64, blockHash hash.Hash) *big.Int { c := HashToBig(&blockHash) max := big.NewInt(1).Lsh(bigOne, 256) @@ -207,7 +218,7 @@ func CalcCuckooDiff(scale uint64, blockHash hash.Hash) *big.Int { return e } -//calc cuckoo diff convert to target hash like 7fff000000000000000000000000000000000000000000000000000000000000 +// calc cuckoo diff convert to target hash like 7fff000000000000000000000000000000000000000000000000000000000000 func CuckooDiffToTarget(scale uint64, diff *big.Int) string { a := &big.Int{} a.SetUint64(scale) @@ -220,3 +231,32 @@ func CuckooDiffToTarget(scale uint64, diff *big.Int) string { b := a.Bytes() return fmt.Sprintf("%064x", b) } + +// CompactToBigWithDestination is a version of CompactToBig that +// takes a destination parameter. This is useful for saving memory, +// as then the destination big.Int can be reused. +// See CompactToBig for further details. +func CompactToBigWithDestination(compact uint32, destination *big.Int) { + // Extract the mantissa, sign bit, and exponent. + mantissa := compact & 0x007fffff + isNegative := compact&0x00800000 != 0 + exponent := uint(compact >> 24) + + // Since the base for the exponent is 256, the exponent can be treated + // as the number of bytes to represent the full 256-bit number. So, + // treat the exponent as the number of bytes and shift the mantissa + // right or left accordingly. This is equivalent to: + // N = mantissa * 256^(exponent-3) + if exponent <= 3 { + mantissa >>= 8 * (3 - exponent) + destination.SetInt64(int64(mantissa)) + } else { + destination.SetInt64(int64(mantissa)) + destination.Lsh(destination, 8*(exponent-3)) + } + + // Make it negative if the sign bit is set. + if isNegative { + destination.Neg(destination) + } +} diff --git a/core/types/pow/difficultymanager/difficultymanager.go b/core/types/pow/difficultymanager/difficultymanager.go new file mode 100644 index 00000000..4bb7135b --- /dev/null +++ b/core/types/pow/difficultymanager/difficultymanager.go @@ -0,0 +1,26 @@ +package difficultymanager + +import ( + "github.com/Qitmeer/qng/consensus/model" + "github.com/Qitmeer/qng/core/types/pow" + "github.com/Qitmeer/qng/params" +) + +func NewDiffManager(b model.BlockChain, cfg *params.Params) model.DifficultyManager { + switch cfg.PowConfig.DifficultyMode { + case pow.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/kaspad.go b/core/types/pow/difficultymanager/kaspad.go new file mode 100644 index 00000000..7727ad4a --- /dev/null +++ b/core/types/pow/difficultymanager/kaspad.go @@ -0,0 +1,203 @@ +package difficultymanager + +import ( + "math" + "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/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) 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(int64(math.Max(float64(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/types/pow/difficultymanager/meer.go b/core/types/pow/difficultymanager/meer.go new file mode 100644 index 00000000..482b08d6 --- /dev/null +++ b/core/types/pow/difficultymanager/meer.go @@ -0,0 +1,442 @@ +// 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 ( + "fmt" + "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/log" + "github.com/Qitmeer/qng/params" +) + +// bigZero is 0 represented as a big.Int. It is defined here to avoid +// the overhead of creating it multiple times. +var bigZero = big.NewInt(0) + +// maxShift is the maximum shift for a difficulty that resets (e.g. +// testnet difficulty). +const maxShift = uint(256) + +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 (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(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 *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(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 *meerDiff) RequiredDifficulty(block model.Block, newBlockTime time.Time, powInstance pow.IPow) (uint32, error) { + baseTarget := powInstance.GetSafeDiff(0) + originCurrentBlock := block + // Genesis block. + if block == nil { + return pow.BigToCompact(baseTarget), nil + } + + block = m.getPowTypeNode(block, powInstance.GetPowType()) + if block == nil { + return pow.BigToCompact(baseTarget), nil + } + curNode := m.b.GetBlockHeader(block) + if curNode == nil { + return pow.BigToCompact(baseTarget), nil + } + // Get the old difficulty; if we aren't at a block height where it changes, + // just return this. + oldDiff := curNode.Difficulty + oldDiffBig := pow.CompactToBig(curNode.Difficulty) + 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 !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 m.cfg.ReduceMinDifficulty { + // Return minimum difficulty when more than the desired + // amount of time has elapsed without mining a block. + reductionTime := int64(m.cfg.MinDiffReductionTime / + time.Second) + allowMinTime := curNode.Timestamp.Unix() + reductionTime + + // For every extra target timespan that passes, we halve the + // difficulty. + if newBlockTime.Unix() > allowMinTime { + timePassed := newBlockTime.Unix() - curNode.Timestamp.Unix() + timePassed -= reductionTime + shifts := uint((timePassed / int64(m.cfg.TargetTimePerBlock/ + time.Second)) + 1) + + // Scale the difficulty with time passed. + oldTarget := pow.CompactToBig(curNode.Difficulty) + newTarget := new(big.Int) + if shifts < maxShift { + newTarget.Lsh(oldTarget, shifts) + } else { + newTarget.Set(pow.OneLsh256) + } + + // Limit new value to the proof of work limit. + if powInstance.CompareDiff(newTarget, baseTarget) { + newTarget.Set(baseTarget) + } + + return pow.BigToCompact(newTarget), nil + } + + // 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 m.findPrevTestNetDifficulty(block, powInstance), nil + } + + return oldDiff, nil + } + // Declare some useful variables. + 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 := m.cfg.WorkDiffAlpha + + // Number of nodes to traverse while calculating difficulty. + nodesToTraverse := needAjustCount * m.cfg.WorkDiffWindows + percentStatsRecentCount := m.cfg.WorkDiffWindowSize * m.cfg.WorkDiffWindows + //calc pow block count in last nodesToTraverse blocks + 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, 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. + var olderTime, windowPeriod int64 + var weights uint64 + oldBlock := block + + oldNodeTimestamp := curNode.Timestamp.Unix() + oldBlockOrder := block.GetOrder() + + recentTime := curNode.Timestamp.Unix() + for i := uint64(0); ; i++ { + // Store and reset after reaching the end of every window period. + if i%uint64(needAjustCount) == 0 && i != 0 { + olderTime = oldNodeTimestamp + timeDifference := recentTime - olderTime + // 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(m.cfg.TargetTimespan / + time.Second) + } + timeDifBig := big.NewInt(timeDifference) + timeDifBig.Lsh(timeDifBig, 32) // Add padding + 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((m.cfg.WorkDiffWindows-windowPeriod)*alpha)) + + // Sum up all the different weights incrementally. + weights += 1 << uint64((m.cfg.WorkDiffWindows-windowPeriod)* + alpha) + + // Store it in the slice. + windowChanges[windowPeriod] = windowAdjusted + + windowPeriod++ + + recentTime = olderTime + } + + if i == uint64(nodesToTraverse) { + break // Exit for loop when we hit the end. + } + // Get the previous node while staying at the genesis block as + // needed. + if oldBlock != nil && oldBlock.HasParents() { + oldBlock = m.b.GetBlockById(oldBlock.GetMainParent()) + if oldBlock == nil { + continue + } + oldBlock = m.getPowTypeNode(oldBlock, powInstance.GetPowType()) + if oldBlock == nil { + oldNodeTimestamp = 0 + oldBlockOrder = 0 + continue + } + on := m.b.GetBlockHeader(oldBlock) + if on == nil { + continue + } + oldNodeTimestamp = on.Timestamp.Unix() + oldBlockOrder = oldBlock.GetOrder() + } + } + // Sum up the weighted window periods. + weightedSum := big.NewInt(0) + for i := int64(0); i < m.cfg.WorkDiffWindows; i++ { + weightedSum.Add(weightedSum, windowChanges[i]) + } + + // Divide by the sum of all weights. + weightsBig := big.NewInt(int64(weights)) + weightedSumDiv := weightedSum.Div(weightedSum, weightsBig) + // if current pow count is zero , set 1 min 1 + if currentPowBlockCount <= 0 { + currentPowBlockCount = 1 + } + //percent calculate + currentPowPercent := big.NewInt(int64(currentPowBlockCount)) + currentPowPercent.Lsh(currentPowPercent, 32) + nodesToTraverseBig := big.NewInt(percentStatsRecentCount) + currentPowPercent = currentPowPercent.Div(currentPowPercent, nodesToTraverseBig) + // Multiply by the old diff. + nextDiffBig := powInstance.GetNextDiffBig(weightedSumDiv, oldDiffBig, currentPowPercent) + // Right shift to restore the original padding (restore non-fixed point). + // Check to see if we're over the limits for the maximum allowable retarget; + // if we are, return the maximum or minimum except in the case that oldDiff + // is zero. + if oldDiffBig.Cmp(bigZero) == 0 { // This should never really happen, + nextDiffBig.Set(nextDiffBig) // but in case it does... + } else if nextDiffBig.Cmp(bigZero) == 0 { + nextDiffBig.Set(baseTarget) + } else if nextDiffBig.Cmp(nextDiffBigMax) == 1 { + nextDiffBig.Set(nextDiffBigMax) + } else if nextDiffBig.Cmp(nextDiffBigMin) == -1 { + nextDiffBig.Set(nextDiffBigMin) + } + + // Limit new value to the proof of work limit. + if !powInstance.CompareDiff(nextDiffBig, baseTarget) { + nextDiffBig.Set(baseTarget) + } + // Log new target difficulty and return it. The new target logging is + // intentionally converting the bits back to a number instead of using + // newTarget since conversion to the compact representation loses + // precision. + nextDiffBits := pow.BigToCompact(nextDiffBig) + + log.Debug("Difficulty retarget", "block main height", block.GetHeight()+1) + log.Debug("Old target", "bits", fmt.Sprintf("%08x", curNode.Difficulty), + "diff", fmt.Sprintf("(%064x)", oldDiffBig)) + log.Debug("New target", "bits", fmt.Sprintf("%08x", nextDiffBits), + "diff", fmt.Sprintf("(%064x)", nextDiffBig)) + return nextDiffBits, nil +} + +// stats current pow count in nodesToTraverse +func (m *meerDiff) calcCurrentPowCount(block model.Block, nodesToTraverse int64, powType pow.PowType) int64 { + // Genesis block. + if block == nil { + return 0 + } + currentPowBlockCount := nodesToTraverse + // Regress through all of the previous blocks and store the percent changes + // per window period; use bigInts to emulate 64.32 bit fixed point. + oldBlock := block + for i := int64(0); i < nodesToTraverse; i++ { + // Get the previous node while staying at the genesis block as + // needed. + if oldBlock.GetOrder() == 0 { + currentPowBlockCount-- + } + if oldBlock.HasParents() { + ob := m.b.GetBlockById(oldBlock.GetMainParent()) + if ob != nil { + oldNode := m.b.GetBlockHeader(ob) + if oldNode == nil { + continue + } + oldBlock = ob + if oldBlock.GetOrder() != 0 && oldNode.Pow.GetPowType() != powType { + currentPowBlockCount-- + } + + } + } + } + return currentPowBlockCount +} + +// whether need ajust Pow Difficulty +// recent m.cfg.WorkDiffWindowSize blocks +// if current count arrived target block count . need ajustment difficulty +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 (m *meerDiff) getDistanceFromLastAdjustment(block model.Block, powType pow.PowType, needAjustCount int64) int64 { + if block == nil { + return 0 + } + curNode := m.b.GetBlockHeader(block) + if curNode == nil { + return 0 + } + //calculate + oldBits := curNode.Difficulty + count := int64(0) + currentTime := curNode.Timestamp.Unix() + for { + if curNode.Pow.GetPowType() == powType { + if oldBits != curNode.Difficulty { + return count + } + count++ + } + if block.GetOrder() == 0 { + //geniess block + return count + } + // if TargetTimespan have only one pow block need ajustment difficulty + // or count >= needAjustCount + if (count > 1 && currentTime-curNode.Timestamp.Unix() > (count-1)*int64(m.cfg.TargetTimespan/time.Second)) || + count >= needAjustCount { + return needAjustCount * m.cfg.WorkDiffWindows + } + if !block.HasParents() { + return count + } + + block = m.b.GetBlockById(block.GetMainParent()) + if block != nil { + curNode = m.b.GetBlockHeader(block) + } else { + return count + } + } +} + +// find block node by pow type +func (m *meerDiff) getPowTypeNode(block model.Block, powType pow.PowType) model.Block { + for { + curNode := m.b.GetBlockHeader(block) + if curNode == nil { + return nil + } + if curNode.Pow.GetPowType() == powType { + return block + } + + if !block.HasParents() { + return nil + } + block = m.b.GetBlockById(block.GetMainParent()) + if block == nil { + return nil + } + } +} + +// find block node by pow type +func (m *meerDiff) 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/node/api.go b/node/api.go index 018f5006..9a25e58d 100644 --- a/node/api.go +++ b/node/api.go @@ -8,6 +8,10 @@ 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" @@ -21,9 +25,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 { diff --git a/params/params_mixnet.go b/params/params_mixnet.go index 4b8cfae6..2a0a252d 100644 --- a/params/params_mixnet.go +++ b/params/params_mixnet.go @@ -7,10 +7,11 @@ package params import ( - "github.com/Qitmeer/qng/core/types" "math/big" "time" + "github.com/Qitmeer/qng/core/types" + "github.com/Qitmeer/qng/common" "github.com/Qitmeer/qng/core/protocol" "github.com/Qitmeer/qng/core/types/pow" @@ -23,10 +24,10 @@ import ( var testMixNetPowLimit = new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 242), common.Big1) // target time per block unit second(s) -const mixTargetTimePerBlock = 15 +const mixTargetTimePerBlock = 1 -// Difficulty check interval is about 30*15 = 7.5 mins -const mixWorkDiffWindowSize = 30 +// The DAA should take the median of 2640 blocks, so in order to do that we need 2641 window size. +const mixWorkDiffWindowSize = 2641 // testPowNetParams defines the network parameters for the test network. var MixNetParams = Params{ @@ -85,6 +86,7 @@ var MixNetParams = Params{ }, // after this height the big graph will be the main pow graph AdjustmentStartMainHeight: 1440 * 15 / mixTargetTimePerBlock, + DifficultyMode: pow.DIFFICULTY_MODE_KASPAD, }, WorkDiffAlpha: 1, @@ -126,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..a83804b0 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" @@ -17,7 +19,6 @@ import ( "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