Rewrite difficulty adjustment algorithm. Based on Digibyte's Digishield V3

This commit is contained in:
Julian Yap 2016-12-04 23:30:39 -10:00
parent a542816c86
commit f5f7b50afd
2 changed files with 106 additions and 71 deletions

View file

@ -24,6 +24,7 @@ import (
"github.com/ubiq/go-ubiq/common" "github.com/ubiq/go-ubiq/common"
"github.com/ubiq/go-ubiq/core/state" "github.com/ubiq/go-ubiq/core/state"
"github.com/ubiq/go-ubiq/core/types" "github.com/ubiq/go-ubiq/core/types"
"github.com/ubiq/go-ubiq/logger"
"github.com/ubiq/go-ubiq/logger/glog" "github.com/ubiq/go-ubiq/logger/glog"
"github.com/ubiq/go-ubiq/params" "github.com/ubiq/go-ubiq/params"
"github.com/ubiq/go-ubiq/pow" "github.com/ubiq/go-ubiq/pow"
@ -31,11 +32,41 @@ import (
) )
var ( var (
ExpDiffPeriod = big.NewInt(100000) ExpDiffPeriod = big.NewInt(100000)
big10 = big.NewInt(10) big88 = big.NewInt(88)
bigMinus99 = big.NewInt(-99) bigMinus99 = big.NewInt(-99)
nPowAveragingWindow = big.NewInt(21)
nPowMaxAdjustDown = big.NewInt(16) // 16% adjustment down
nPowMaxAdjustUp = big.NewInt(8) // 8% adjustment up
) )
func AveragingWindowTimespan() *big.Int {
x := new(big.Int)
return x.Mul(nPowAveragingWindow, big88)
}
func MinActualTimespan() *big.Int {
// (AveragingWindowTimespan() * (100 - nPowMaxAdjustUp )) / 100
x := new(big.Int)
y := new(big.Int)
z := new(big.Int)
x.Sub(big.NewInt(100), nPowMaxAdjustUp)
y.Mul(AveragingWindowTimespan(), x)
z.Div(y, big.NewInt(100))
return z
}
func MaxActualTimespan() *big.Int {
// (AveragingWindowTimespan() * (100 + nPowMaxAdjustDown)) / 100
x := new(big.Int)
y := new(big.Int)
z := new(big.Int)
x.Add(big.NewInt(100), nPowMaxAdjustDown)
y.Mul(AveragingWindowTimespan(), x)
z.Div(y, big.NewInt(100))
return z
}
// BlockValidator is responsible for validating block headers, uncles and // BlockValidator is responsible for validating block headers, uncles and
// processed state. // processed state.
// //
@ -310,87 +341,100 @@ func ValidateHeaderHeaderChain(config *params.ChainConfig, pow pow.PoW, header *
// CalcDifficulty is the difficulty adjustment algorithm. It returns // CalcDifficulty is the difficulty adjustment algorithm. It returns
// the difficulty that a new block should have when created at time // the difficulty that a new block should have when created at time
// given the parent block's time and difficulty. // given the parent block's time and difficulty.
// Rewritten to be based on Digibyte's Digishield v3 retargeting
func CalcDifficulty(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int, bc *BlockChain) *big.Int { func CalcDifficulty(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int, bc *BlockChain) *big.Int {
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.mediawiki
// algorithm:
// diff = (parent_diff +
// (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
// ) + 2^(periodCount - 2)
bigTime := new(big.Int).SetUint64(time)
bigParentTime := new(big.Int).SetUint64(parentTime)
// holds intermediate values to make the algo easier to read & audit // holds intermediate values to make the algo easier to read & audit
x := new(big.Int) x := new(big.Int)
y := new(big.Int) nFirstBlock := new(big.Int)
nFirstBlock.Sub(parentNumber, nPowAveragingWindow)
// 1 - (block_timestamp -parent_timestamp) // 10 glog.V(logger.Debug).Infof("CalcDifficulty parentNumber: %v parentDiff: %v\n", parentNumber, parentDiff)
x.Sub(bigTime, bigParentTime)
x.Div(x, big10)
x.Sub(common.Big1, x)
// max(1 - (block_timestamp - parent_timestamp) // 10, -99))) // Check we have enough blocks
if x.Cmp(bigMinus99) < 0 { if parentNumber.Cmp(nPowAveragingWindow) < 1 {
x.Set(bigMinus99) glog.V(logger.Debug).Infof("CalcDifficulty: parentNumber(%+x) < nPowAveragingWindow(%+x)\n", parentNumber, nPowAveragingWindow)
x.Set(parentDiff)
return x
} }
// (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)) // Limit adjustment step
y.Div(parentDiff, params.DifficultyBoundDivisor) // Use medians to prevent time-warp attacks
x.Mul(y, x) // nActualTimespan := nLastBlockTime - nFirstBlockTime
x.Add(parentDiff, x) nLastBlockTime := bc.CalcPastMedianTime(parentNumber.Uint64())
nFirstBlockTime := bc.CalcPastMedianTime(nFirstBlock.Uint64())
nActualTimespan := new(big.Int)
nActualTimespan.Sub(nLastBlockTime, nFirstBlockTime)
glog.V(logger.Debug).Infof("CalcDifficulty nActualTimespan = %v before dampening\n", nActualTimespan)
// nActualTimespan = AveragingWindowTimespan() + (nActualTimespan-AveragingWindowTimespan())/4
y := new(big.Int)
y.Sub(nActualTimespan, AveragingWindowTimespan())
y.Div(y, big.NewInt(4))
nActualTimespan.Add(y, AveragingWindowTimespan())
glog.V(logger.Debug).Infof("CalcDifficulty nActualTimespan = %v before bounds\n", nActualTimespan)
if nActualTimespan.Cmp(MinActualTimespan()) < 0 {
nActualTimespan.Set(MinActualTimespan())
glog.V(logger.Debug).Infoln("CalcDifficulty Minimum Timespan set")
} else if nActualTimespan.Cmp(MaxActualTimespan()) > 0 {
nActualTimespan.Set(MaxActualTimespan())
glog.V(logger.Debug).Infoln("CalcDifficulty Maximum Timespan set")
}
glog.V(logger.Debug).Infof("CalcDifficulty nActualTimespan = %v final\n", nActualTimespan)
// Retarget
x.Mul(parentDiff, AveragingWindowTimespan())
glog.V(logger.Debug).Infoln("CalcDifficulty parentDiff * AveragingWindowTimespan:", x)
x.Div(x, nActualTimespan)
glog.V(logger.Debug).Infoln("CalcDifficulty x / nActualTimespan:", x)
// minimum difficulty can ever be (before exponential factor) // minimum difficulty can ever be (before exponential factor)
if x.Cmp(params.MinimumDifficulty) < 0 { if x.Cmp(params.MinimumDifficulty) < 0 {
x.Set(params.MinimumDifficulty) x.Set(params.MinimumDifficulty)
} }
// for the exponential factor
periodCount := new(big.Int).Add(parentNumber, common.Big1)
periodCount.Div(periodCount, ExpDiffPeriod)
// the exponential factor, commonly referred to as "the bomb"
// diff = diff + 2^(periodCount - 2)
if periodCount.Cmp(common.Big1) > 0 {
y.Sub(periodCount, common.Big2)
y.Exp(common.Big2, y, nil)
x.Add(x, y)
}
return x return x
} }
func CalcDifficultyHeaderChain(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int, hc *HeaderChain) *big.Int { func CalcDifficultyHeaderChain(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int, hc *HeaderChain) *big.Int {
bigTime := new(big.Int).SetUint64(time) // holds intermediate values to make the algo easier to read & audit
bigParentTime := new(big.Int).SetUint64(parentTime)
x := new(big.Int) x := new(big.Int)
y := new(big.Int) nFirstBlock := new(big.Int)
nFirstBlock.Sub(parentNumber, nPowAveragingWindow)
x.Sub(bigTime, bigParentTime) // Check we have enough blocks
x.Div(x, big10) if parentNumber.Cmp(nPowAveragingWindow) < 1 {
x.Sub(common.Big1, x) x.Set(parentDiff)
return x
if x.Cmp(bigMinus99) < 0 {
x.Set(bigMinus99)
} }
y.Div(parentDiff, params.DifficultyBoundDivisor) nLastBlockTime := hc.CalcPastMedianTime(parentNumber.Uint64())
x.Mul(y, x) nFirstBlockTime := hc.CalcPastMedianTime(nFirstBlock.Uint64())
x.Add(parentDiff, x) nActualTimespan := new(big.Int)
nActualTimespan.Sub(nLastBlockTime, nFirstBlockTime)
y := new(big.Int)
y.Sub(nActualTimespan, AveragingWindowTimespan())
y.Div(y, big.NewInt(4))
nActualTimespan.Add(y, AveragingWindowTimespan())
if nActualTimespan.Cmp(MinActualTimespan()) < 0 {
nActualTimespan.Set(MinActualTimespan())
} else if nActualTimespan.Cmp(MaxActualTimespan()) > 0 {
nActualTimespan.Set(MaxActualTimespan())
}
// Retarget
x.Mul(parentDiff, AveragingWindowTimespan())
x.Div(x, nActualTimespan)
// minimum difficulty can ever be (before exponential factor)
if x.Cmp(params.MinimumDifficulty) < 0 { if x.Cmp(params.MinimumDifficulty) < 0 {
x.Set(params.MinimumDifficulty) x.Set(params.MinimumDifficulty)
} }
periodCount := new(big.Int).Add(parentNumber, common.Big1)
periodCount.Div(periodCount, ExpDiffPeriod)
if periodCount.Cmp(common.Big1) > 0 {
y.Sub(periodCount, common.Big2)
y.Exp(common.Big2, y, nil)
x.Add(x, y)
}
return x return x
} }
@ -402,7 +446,7 @@ func CalcDifficultyLegacy(config *params.ChainConfig, time, parentTime uint64, p
y := new(big.Int) y := new(big.Int)
x.Sub(bigTime, bigParentTime) x.Sub(bigTime, bigParentTime)
x.Div(x, big10) x.Div(x, big88)
x.Sub(common.Big1, x) x.Sub(common.Big1, x)
if x.Cmp(bigMinus99) < 0 { if x.Cmp(bigMinus99) < 0 {
@ -417,15 +461,6 @@ func CalcDifficultyLegacy(config *params.ChainConfig, time, parentTime uint64, p
x.Set(params.MinimumDifficulty) x.Set(params.MinimumDifficulty)
} }
periodCount := new(big.Int).Add(parentNumber, common.Big1)
periodCount.Div(periodCount, ExpDiffPeriod)
if periodCount.Cmp(common.Big1) > 0 {
y.Sub(periodCount, common.Big2)
y.Exp(common.Big2, y, nil)
x.Add(x, y)
}
return x return x
} }

View file

@ -359,7 +359,7 @@ func (hc *HeaderChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []co
func (hc *HeaderChain) GetBlockHeadersFromHash(hash common.Hash, max uint64) []*types.Header { func (hc *HeaderChain) GetBlockHeadersFromHash(hash common.Hash, max uint64) []*types.Header {
// Get the origin header from which to fetch // Get the origin header from which to fetch
header := hc.GetHeader(hash) header := hc.GetHeaderByHash(hash)
if header == nil { if header == nil {
return nil return nil
} }
@ -367,10 +367,10 @@ func (hc *HeaderChain) GetBlockHeadersFromHash(hash common.Hash, max uint64) []*
chain := make([]*types.Header, 0, max) chain := make([]*types.Header, 0, max)
for i := uint64(0); i < max; i++ { for i := uint64(0); i < max; i++ {
next := header.ParentHash next := header.ParentHash
if header = hc.GetHeader(next); header == nil { if header = hc.GetHeaderByHash(next); header == nil {
break break
} }
chain = append(chain, hc.GetHeader(next)) chain = append(chain, hc.GetHeaderByHash(next))
if header.Number.Cmp(common.Big0) == 0 { if header.Number.Cmp(common.Big0) == 0 {
break break
} }