diff --git a/consensus/ubqhash/consensus.go b/consensus/ubqhash/consensus.go index 4107fd1c51..d9ddabb80d 100644 --- a/consensus/ubqhash/consensus.go +++ b/consensus/ubqhash/consensus.go @@ -36,10 +36,30 @@ import ( // Ubqhash proof-of-work protocol constants. var ( - blockReward *big.Int = big.NewInt(5e+18) // Block reward in wei for successfully mining a block + blockReward *big.Int = big.NewInt(8e+18) // Block reward in wei for successfully mining a block maxUncles = 2 // Maximum number of uncles allowed in a single block ) +// Diff algo constants. +var ( + big88 = big.NewInt(88) + bigMinus99 = big.NewInt(-99) + nPowAveragingWindow = big.NewInt(21) + nPowMaxAdjustDown = big.NewInt(16) // 16% adjustment down + nPowMaxAdjustUp = big.NewInt(8) // 8% adjustment up + + diffChangeBlock = big.NewInt(4088) + nPowAveragingWindow88 = big.NewInt(88) + nPowMaxAdjustDown2 = big.NewInt(3) // 3% adjustment down + nPowMaxAdjustUp2 = big.NewInt(2) // 2% adjustment up + + // Flux + fluxChangeBlock = big.NewInt(8000) + nPowMaxAdjustDownFlux = big.NewInt(5) // 0.5% adjustment down + nPowMaxAdjustUpFlux = big.NewInt(3) // 0.3% adjustment up + nPowDampFlux = big.NewInt(1) // 0.1% +) + // Various error messages to mark blocks invalid. These should be private to // prevent engine specific errors from being referenced in the remainder of the // codebase, inherently breaking if the engine is swapped out. Please put common @@ -238,7 +258,7 @@ func (ubqhash *Ubqhash) verifyHeader(chain consensus.ChainReader, header, parent return errZeroBlockTime } // Verify the block's difficulty based in it's timestamp and parent's difficulty - expected := CalcDifficulty(chain.Config(), header.Time.Uint64(), parent) + expected := CalcDifficulty(chain, header.Time.Uint64(), parent) if expected.Cmp(header.Difficulty) != 0 { return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, expected) } @@ -279,17 +299,108 @@ func (ubqhash *Ubqhash) verifyHeader(chain consensus.ChainReader, header, parent return nil } -// CalcDifficulty is the difficulty adjustment algorithm. It returns -// the difficulty that a new block should have when created at time -// given the parent block's time and difficulty. +// Difficulty timespans +func averagingWindowTimespan() *big.Int { + x := new(big.Int) + return x.Mul(nPowAveragingWindow, big88) +} + +func minActualTimespan() *big.Int { + 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 { + 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 +} + +func averagingWindowTimespan88() *big.Int { + x := new(big.Int) + return x.Mul(nPowAveragingWindow88, big88) +} + +func minActualTimespan2() *big.Int { + x := new(big.Int) + y := new(big.Int) + z := new(big.Int) + x.Sub(big.NewInt(100), nPowMaxAdjustUp2) + y.Mul(averagingWindowTimespan88(), x) + z.Div(y, big.NewInt(100)) + return z +} + +func maxActualTimespan2() *big.Int { + x := new(big.Int) + y := new(big.Int) + z := new(big.Int) + x.Add(big.NewInt(100), nPowMaxAdjustDown2) + y.Mul(averagingWindowTimespan88(), x) + z.Div(y, big.NewInt(100)) + return z +} + +func minActualTimespanFlux(dampen bool) *big.Int { + x := new(big.Int) + y := new(big.Int) + z := new(big.Int) + if dampen { + x.Sub(big.NewInt(1000), nPowDampFlux) + y.Mul(averagingWindowTimespan88(), x) + z.Div(y, big.NewInt(1000)) + } else { + x.Sub(big.NewInt(1000), nPowMaxAdjustUpFlux) + y.Mul(averagingWindowTimespan88(), x) + z.Div(y, big.NewInt(1000)) + } + return z +} + +func maxActualTimespanFlux(dampen bool) *big.Int { + x := new(big.Int) + y := new(big.Int) + z := new(big.Int) + if dampen { + x.Add(big.NewInt(1000), nPowDampFlux) + y.Mul(averagingWindowTimespan88(), x) + z.Div(y, big.NewInt(1000)) + } else { + x.Add(big.NewInt(1000), nPowMaxAdjustDownFlux) + y.Mul(averagingWindowTimespan88(), x) + z.Div(y, big.NewInt(1000)) + } + return z +} + +// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty +// that a new block should have when created at time given the parent block's time +// and difficulty. +// // TODO (karalabe): Move the chain maker into this package and make this private! -func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int { - next := new(big.Int).Add(parent.Number, common.Big1) - switch { - case config.IsHomestead(next): - return calcDifficultyHomestead(time, parent) - default: - return calcDifficultyFrontier(time, parent) +func CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int { + parentTime := parent.Time + parentNumber := parent.Number + parentDiff := parent.Difficulty + + if parentNumber.Cmp(diffChangeBlock) < 0 { + return calcDifficultyOrig(chain, parentNumber, parentDiff, parent) + } + if parentNumber.Cmp(fluxChangeBlock) < 0 { + // (chain consensus.ChainReader, parentNumber, parentDiff *big.Int, parent *types.Header) + return calcDifficulty2(chain, parentNumber, parentDiff, parent) + } else { + // (chain consensus.ChainReader, time, parentTime, parentNumber, parentDiff *big.Int, parent *types.Header) + return fluxDifficulty(chain, big.NewInt(int64(time)), parentTime, parentNumber, parentDiff, parent) } } @@ -297,21 +408,14 @@ func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Heade var ( expDiffPeriod = big.NewInt(100000) big10 = big.NewInt(10) - bigMinus99 = big.NewInt(-99) ) -// calcDifficultyHomestead is the difficulty adjustment algorithm. It returns +// calcDifficultyLegacy is the difficulty adjustment algorithm. It returns // the difficulty that a new block should have when created at time given the -// parent block's time and difficulty. The calculation uses the Homestead rules. -func calcDifficultyHomestead(time uint64, parent *types.Header) *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) - +// parent block's time and difficulty. The calculation uses the Legacy rules. +func CalcDifficultyLegacy(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { bigTime := new(big.Int).SetUint64(time) - bigParentTime := new(big.Int).Set(parent.Time) + bigParentTime := new(big.Int).SetUint64(parentTime) // holds intermediate values to make the algo easier to read & audit x := new(big.Int) @@ -319,7 +423,7 @@ func calcDifficultyHomestead(time uint64, parent *types.Header) *big.Int { // 1 - (block_timestamp -parent_timestamp) // 10 x.Sub(bigTime, bigParentTime) - x.Div(x, big10) + x.Div(x, big88) x.Sub(common.Big1, x) // max(1 - (block_timestamp - parent_timestamp) // 10, -99))) @@ -327,59 +431,149 @@ func calcDifficultyHomestead(time uint64, parent *types.Header) *big.Int { x.Set(bigMinus99) } // (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)) - y.Div(parent.Difficulty, params.DifficultyBoundDivisor) + y.Div(parentDiff, params.DifficultyBoundDivisor) x.Mul(y, x) - x.Add(parent.Difficulty, x) + x.Add(parentDiff, x) // minimum difficulty can ever be (before exponential factor) if x.Cmp(params.MinimumDifficulty) < 0 { x.Set(params.MinimumDifficulty) } - // for the exponential factor - periodCount := new(big.Int).Add(parent.Number, 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 } -// calcDifficultyFrontier is the difficulty adjustment algorithm. It returns the -// difficulty that a new block should have when created at time given the parent -// block's time and difficulty. The calculation uses the Frontier rules. -func calcDifficultyFrontier(time uint64, parent *types.Header) *big.Int { - diff := new(big.Int) - adjust := new(big.Int).Div(parent.Difficulty, params.DifficultyBoundDivisor) - bigTime := new(big.Int) - bigParentTime := new(big.Int) +// CalcDifficulty is the difficulty adjustment algorithm. It returns +// the difficulty that a new block should have when created at time +// given the parent block's time and difficulty. +// Rewritten to be based on Digibyte's Digishield v3 retargeting +func calcDifficultyOrig(chain consensus.ChainReader, parentNumber, parentDiff *big.Int, parent *types.Header) *big.Int { + // holds intermediate values to make the algo easier to read & audit + x := new(big.Int) + nFirstBlock := new(big.Int) + nFirstBlock.Sub(parentNumber, nPowAveragingWindow) - bigTime.SetUint64(time) - bigParentTime.Set(parent.Time) + log.Debug(fmt.Sprintf("CalcDifficulty parentNumber: %v parentDiff: %v", parentNumber, parentDiff)) - if bigTime.Sub(bigTime, bigParentTime).Cmp(params.DurationLimit) < 0 { - diff.Add(parent.Difficulty, adjust) - } else { - diff.Sub(parent.Difficulty, adjust) - } - if diff.Cmp(params.MinimumDifficulty) < 0 { - diff.Set(params.MinimumDifficulty) + // Check we have enough blocks + if parentNumber.Cmp(nPowAveragingWindow) < 1 { + log.Debug(fmt.Sprintf("CalcDifficulty: parentNumber(%+x) < nPowAveragingWindow(%+x)", parentNumber, nPowAveragingWindow)) + x.Set(parentDiff) + return x } - periodCount := new(big.Int).Add(parent.Number, common.Big1) - periodCount.Div(periodCount, expDiffPeriod) - if periodCount.Cmp(common.Big1) > 0 { - // diff = diff + 2^(periodCount - 2) - expDiff := periodCount.Sub(periodCount, common.Big2) - expDiff.Exp(common.Big2, expDiff, nil) - diff.Add(diff, expDiff) - diff = math.BigMax(diff, params.MinimumDifficulty) + // Limit adjustment step + // Use medians to prevent time-warp attacks + // nActualTimespan := nLastBlockTime - nFirstBlockTime + nLastBlockTime := chain.CalcPastMedianTime(parentNumber.Uint64(), parent) + nFirstBlockTime := chain.CalcPastMedianTime(nFirstBlock.Uint64(), parent) + nActualTimespan := new(big.Int) + nActualTimespan.Sub(nLastBlockTime, nFirstBlockTime) + log.Debug(fmt.Sprintf("CalcDifficulty nActualTimespan = %v before dampening", nActualTimespan)) + + // nActualTimespan = AveragingWindowTimespan() + (nActualTimespan-AveragingWindowTimespan())/4 + y := new(big.Int) + y.Sub(nActualTimespan, averagingWindowTimespan()) + y.Div(y, big.NewInt(4)) + nActualTimespan.Add(y, averagingWindowTimespan()) + log.Debug(fmt.Sprintf("CalcDifficulty nActualTimespan = %v before bounds", nActualTimespan)) + + if nActualTimespan.Cmp(minActualTimespan()) < 0 { + nActualTimespan.Set(minActualTimespan()) + log.Debug("CalcDifficulty Minimum Timespan set") + } else if nActualTimespan.Cmp(maxActualTimespan()) > 0 { + nActualTimespan.Set(maxActualTimespan()) + log.Debug("CalcDifficulty Maximum Timespan set") } - return diff + + log.Debug(fmt.Sprintf("CalcDifficulty nActualTimespan = %v final\n", nActualTimespan)) + + // Retarget + x.Mul(parentDiff, averagingWindowTimespan()) + log.Debug(fmt.Sprintf("CalcDifficulty parentDiff * AveragingWindowTimespan: %v", x)) + + x.Div(x, nActualTimespan) + log.Debug(fmt.Sprintf("CalcDifficulty x / nActualTimespan: %v", x)) + + return x +} + +func calcDifficulty2(chain consensus.ChainReader, parentNumber, parentDiff *big.Int, parent *types.Header) *big.Int { + x := new(big.Int) + nFirstBlock := new(big.Int) + nFirstBlock.Sub(parentNumber, nPowAveragingWindow88) + + nLastBlockTime := chain.CalcPastMedianTime(parentNumber.Uint64(), parent) + nFirstBlockTime := chain.CalcPastMedianTime(nFirstBlock.Uint64(), parent) + + nActualTimespan := new(big.Int) + nActualTimespan.Sub(nLastBlockTime, nFirstBlockTime) + + y := new(big.Int) + y.Sub(nActualTimespan, averagingWindowTimespan88()) + y.Div(y, big.NewInt(4)) + nActualTimespan.Add(y, averagingWindowTimespan88()) + + if nActualTimespan.Cmp(minActualTimespan2()) < 0 { + nActualTimespan.Set(minActualTimespan2()) + } else if nActualTimespan.Cmp(maxActualTimespan2()) > 0 { + nActualTimespan.Set(maxActualTimespan2()) + } + + x.Mul(parentDiff, averagingWindowTimespan88()) + x.Div(x, nActualTimespan) + + if x.Cmp(params.MinimumDifficulty) < 0 { + x.Set(params.MinimumDifficulty) + } + + return x +} + +func fluxDifficulty(chain consensus.ChainReader, time, parentTime, parentNumber, parentDiff *big.Int, parent *types.Header) *big.Int { + x := new(big.Int) + nFirstBlock := new(big.Int) + nFirstBlock.Sub(parentNumber, nPowAveragingWindow88) + + diffTime := new(big.Int) + diffTime.Sub(time, parentTime) + + nLastBlockTime := chain.CalcPastMedianTime(parentNumber.Uint64(), parent) + nFirstBlockTime := chain.CalcPastMedianTime(nFirstBlock.Uint64(), parent) + nActualTimespan := new(big.Int) + nActualTimespan.Sub(nLastBlockTime, nFirstBlockTime) + + y := new(big.Int) + y.Sub(nActualTimespan, averagingWindowTimespan88()) + y.Div(y, big.NewInt(4)) + nActualTimespan.Add(y, averagingWindowTimespan88()) + + if nActualTimespan.Cmp(minActualTimespanFlux(false)) < 0 { + doubleBig88 := new(big.Int) + doubleBig88.Mul(big88, big.NewInt(2)) + if diffTime.Cmp(doubleBig88) > 0 { + nActualTimespan.Set(minActualTimespanFlux(true)) + } else { + nActualTimespan.Set(minActualTimespanFlux(false)) + } + } else if nActualTimespan.Cmp(maxActualTimespanFlux(false)) > 0 { + halfBig88 := new(big.Int) + halfBig88.Div(big88, big.NewInt(2)) + if diffTime.Cmp(halfBig88) < 0 { + nActualTimespan.Set(maxActualTimespanFlux(true)) + } else { + nActualTimespan.Set(maxActualTimespanFlux(false)) + } + } + + x.Mul(parentDiff, averagingWindowTimespan88()) + x.Div(x, nActualTimespan) + + if x.Cmp(params.MinimumDifficulty) < 0 { + x.Set(params.MinimumDifficulty) + } + + return x } // VerifySeal implements consensus.Engine, checking whether the given block satisfies @@ -450,6 +644,7 @@ func (ubqhash *Ubqhash) Finalize(chain consensus.ChainReader, header *types.Head // Some weird constants to avoid constant memory allocs for them. var ( + big2 = big.NewInt(2) big8 = big.NewInt(8) big32 = big.NewInt(32) ) @@ -460,15 +655,50 @@ var ( // TODO (karalabe): Move the chain maker into this package and make this private! func AccumulateRewards(state *state.StateDB, header *types.Header, uncles []*types.Header) { reward := new(big.Int).Set(blockReward) + + if header.Number.Cmp(big.NewInt(358363)) > 0 { + reward = big.NewInt(7e+18) + } + if header.Number.Cmp(big.NewInt(716727)) > 0 { + reward = big.NewInt(6e+18) + } + if header.Number.Cmp(big.NewInt(1075090)) > 0 { + reward = big.NewInt(5e+18) + } + if header.Number.Cmp(big.NewInt(1433454)) > 0 { + reward = big.NewInt(4e+18) + } + if header.Number.Cmp(big.NewInt(1791818)) > 0 { + reward = big.NewInt(3e+18) + } + if header.Number.Cmp(big.NewInt(2150181)) > 0 { + reward = big.NewInt(2e+18) + } + if header.Number.Cmp(big.NewInt(2508545)) > 0 { + reward = big.NewInt(1e+18) + } + r := new(big.Int) for _, uncle := range uncles { - r.Add(uncle.Number, big8) + r.Add(uncle.Number, big2) r.Sub(r, header.Number) r.Mul(r, blockReward) - r.Div(r, big8) - state.AddBalance(uncle.Coinbase, r) + r.Div(r, big2) + + if header.Number.Cmp(big.NewInt(10)) < 0 { + state.AddBalance(uncle.Coinbase, r) + r.Div(blockReward, big32) + if r.Cmp(big.NewInt(0)) < 0 { + r = big.NewInt(0) + } + } else { + if r.Cmp(big.NewInt(0)) < 0 { + r = big.NewInt(0) + } + state.AddBalance(uncle.Coinbase, r) + r.Div(blockReward, big32) + } - r.Div(blockReward, big32) reward.Add(reward, r) } state.AddBalance(header.Coinbase, reward) diff --git a/core/chain_makers.go b/core/chain_makers.go index 7435e139b2..05eb496558 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -142,7 +142,7 @@ func (b *BlockGen) OffsetTime(seconds int64) { if b.header.Time.Cmp(b.parent.Header().Time) <= 0 { panic("block time out of range") } - b.header.Difficulty = ubqhash.CalcDifficulty(b.config, b.header.Time.Uint64(), b.parent.Header()) + b.header.Difficulty = ubqhash.CalcDifficultyLegacy(b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty()) } // GenerateChain creates a chain of n blocks. The first block's @@ -206,11 +206,7 @@ func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.St Root: state.IntermediateRoot(config.IsEIP158(parent.Number())), ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), - Difficulty: ubqhash.CalcDifficulty(config, time.Uint64(), &types.Header{ - Number: parent.Number(), - Time: new(big.Int).Sub(time, big.NewInt(10)), - Difficulty: parent.Difficulty(), - }), + Difficulty: ubqhash.CalcDifficultyLegacy(time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()), GasLimit: CalcGasLimit(parent), GasUsed: new(big.Int), Number: new(big.Int).Add(parent.Number(), common.Big1),