dcrd/blockchain/difficulty.go
Dave Collins b6d426241d blockchain: Rework to use new db interface.
This commit is the first stage of several that are planned to convert
the blockchain package into a concurrent safe package that will
ultimately allow support for multi-peer download and concurrent chain
processing.  The goal is to update btcd proper after each step so it can
take advantage of the enhancements as they are developed.

In addition to the aforementioned benefit, this staged approach has been
chosen since it is absolutely critical to maintain consensus.
Separating the changes into several stages makes it easier for reviewers
to logically follow what is happening and therefore helps prevent
consensus bugs.  Naturally there are significant automated tests to help
prevent consensus issues as well.

The main focus of this stage is to convert the blockchain package to use
the new database interface and implement the chain-related functionality
which it no longer handles.  It also aims to improve efficiency in
various areas by making use of the new database and chain capabilities.

The following is an overview of the chain changes:

- Update to use the new database interface
- Add chain-related functionality that the old database used to handle
  - Main chain structure and state
  - Transaction spend tracking
- Implement a new pruned unspent transaction output (utxo) set
  - Provides efficient direct access to the unspent transaction outputs
  - Uses a domain specific compression algorithm that understands the
    standard transaction scripts in order to significantly compress them
  - Removes reliance on the transaction index and paves the way toward
    eventually enabling block pruning
- Modify the New function to accept a Config struct instead of
  inidividual parameters
- Replace the old TxStore type with a new UtxoViewpoint type that makes
  use of the new pruned utxo set
- Convert code to treat the new UtxoViewpoint as a rolling view that is
  used between connects and disconnects to improve efficiency
- Make best chain state always set when the chain instance is created
  - Remove now unnecessary logic for dealing with unset best state
- Make all exported functions concurrent safe
  - Currently using a single chain state lock as it provides a straight
    forward and easy to review path forward however this can be improved
    with more fine grained locking
- Optimize various cases where full blocks were being loaded when only
  the header is needed to help reduce the I/O load
- Add the ability for callers to get a snapshot of the current best
  chain stats in a concurrent safe fashion
  - Does not block callers while new blocks are being processed
- Make error messages that reference transaction outputs consistently
  use <transaction hash>:<output index>
- Introduce a new AssertError type an convert internal consistency
  checks to use it
- Update tests and examples to reflect the changes
- Add a full suite of tests to ensure correct functionality of the new
  code

The following is an overview of the btcd changes:

- Update to use the new database and chain interfaces
- Temporarily remove all code related to the transaction index
- Temporarily remove all code related to the address index
- Convert all code that uses transaction stores to use the new utxo
  view
- Rework several calls that required the block manager for safe
  concurrency to use the chain package directly now that it is
  concurrent safe
- Change all calls to obtain the best hash to use the new best state
  snapshot capability from the chain package
- Remove workaround for limits on fetching height ranges since the new
  database interface no longer imposes them
- Correct the gettxout RPC handler to return the best chain hash as
  opposed the hash the txout was found in
- Optimize various RPC handlers:
  - Change several of the RPC handlers to use the new chain snapshot
    capability to avoid needlessly loading data
  - Update several handlers to use new functionality to avoid accessing
    the block manager so they are able to return the data without
    blocking when the server is busy processing blocks
  - Update non-verbose getblock to avoid deserialization and
    serialization overhead
  - Update getblockheader to request the block height directly from
    chain and only load the header
  - Update getdifficulty to use the new cached data from chain
  - Update getmininginfo to use the new cached data from chain
  - Update non-verbose getrawtransaction to avoid deserialization and
    serialization overhead
  - Update gettxout to use the new utxo store versus loading
    full transactions using the transaction index

The following is an overview of the utility changes:
- Update addblock to use the new database and chain interfaces
- Update findcheckpoint to use the new database and chain interfaces
- Remove the dropafter utility which is no longer supported

NOTE: The transaction index and address index will be reimplemented in
another commit.
2016-08-18 15:42:18 -04:00

1074 lines
37 KiB
Go

// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
import (
"fmt"
"math/big"
"time"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/wire"
)
var (
// bigZero is 0 represented as a big.Int. It is defined here to avoid
// the overhead of creating it multiple times.
bigZero = big.NewInt(0)
// bigOne is 1 represented as a big.Int. It is defined here to avoid
// the overhead of creating it multiple times.
bigOne = big.NewInt(1)
// oneLsh256 is 1 shifted left 256 bits. It is defined here to avoid
// the overhead of creating it multiple times.
oneLsh256 = new(big.Int).Lsh(bigOne, 256)
// maxShift is the maximum shift for a difficulty that resets (e.g.
// testnet difficulty).
maxShift = uint(256)
)
// ShaHashToBig converts a wire.ShaHash into a big.Int that can be used to
// perform math comparisons.
func ShaHashToBig(hash *chainhash.Hash) *big.Int {
// A ShaHash is in little-endian, but the big package wants the bytes
// in big-endian, so reverse them.
buf := *hash
blen := len(buf)
for i := 0; i < blen/2; i++ {
buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i]
}
return new(big.Int).SetBytes(buf[:])
}
// CompactToBig converts a compact representation of a whole number N to an
// unsigned 32-bit number. The representation is similar to IEEE754 floating
// point numbers.
//
// 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
//
// -------------------------------------------------
// | 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)
//
// This compact form is only used in decred to encode unsigned 256-bit numbers
// which represent difficulty targets, thus there really is not a need for a
// sign bit, but it is implemented here to stay consistent with bitcoind.
func CompactToBig(compact uint32) *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)
var bn *big.Int
if exponent <= 3 {
mantissa >>= 8 * (3 - exponent)
bn = big.NewInt(int64(mantissa))
} else {
bn = big.NewInt(int64(mantissa))
bn.Lsh(bn, 8*(exponent-3))
}
// Make it negative if the sign bit is set.
if isNegative {
bn = bn.Neg(bn)
}
return bn
}
// BigToCompact converts a whole number N to a compact representation using
// an unsigned 32-bit number. The compact representation only provides 23 bits
// of precision, so values larger than (2^23 - 1) only encode the most
// significant digits of the number. See CompactToBig for details.
func BigToCompact(n *big.Int) uint32 {
// No need to do any work if it's zero.
if n.Sign() == 0 {
return 0
}
// Since the base for the exponent is 256, the exponent can be treated
// as the number of bytes. So, shift the number right or left
// accordingly. This is equivalent to:
// mantissa = mantissa / 256^(exponent-3)
var mantissa uint32
exponent := uint(len(n.Bytes()))
if exponent <= 3 {
mantissa = uint32(n.Bits()[0])
mantissa <<= 8 * (3 - exponent)
} else {
// Use a copy to avoid modifying the caller's original number.
tn := new(big.Int).Set(n)
mantissa = uint32(tn.Rsh(tn, 8*(exponent-3)).Bits()[0])
}
// When the mantissa already has the sign bit set, the number is too
// large to fit into the available 23-bits, so divide the number by 256
// and increment the exponent accordingly.
if mantissa&0x00800000 != 0 {
mantissa >>= 8
exponent++
}
// Pack the exponent, sign bit, and mantissa into an unsigned 32-bit
// int and return it.
compact := uint32(exponent<<24) | mantissa
if n.Sign() < 0 {
compact |= 0x00800000
}
return compact
}
// CalcWork calculates a work value from difficulty bits. Decred increases
// the difficulty for generating a block by decreasing the value which the
// generated hash must be less than. This difficulty target is stored in each
// block header using a compact representation as described in the documentation
// for CompactToBig. The main chain is selected by choosing the chain that has
// the most proof of work (highest difficulty). Since a lower target difficulty
// value equates to higher actual difficulty, the work value which will be
// accumulated must be the inverse of the difficulty. Also, in order to avoid
// potential division by zero and really small floating point numbers, the
// result adds 1 to the denominator and multiplies the numerator by 2^256.
func CalcWork(bits uint32) *big.Int {
// Return a work value of zero if the passed difficulty bits represent
// a negative number. Note this should not happen in practice with valid
// blocks, but an invalid block could trigger it.
difficultyNum := CompactToBig(bits)
if difficultyNum.Sign() <= 0 {
return big.NewInt(0)
}
// (1 << 256) / (difficultyNum + 1)
denominator := new(big.Int).Add(difficultyNum, bigOne)
return new(big.Int).Div(oneLsh256, denominator)
}
// 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) uint32 {
// Convert types used in the calculations below.
durationVal := int64(duration)
adjustmentFactor := big.NewInt(b.chainParams.RetargetAdjustmentFactor)
maxRetargetTimespan := int64(b.chainParams.TargetTimespan) *
b.chainParams.RetargetAdjustmentFactor
// The test network rules allow minimum difficulty blocks after more
// than twice the desired amount of time needed to generate a block has
// elapsed.
if b.chainParams.ResetMinDifficulty {
if durationVal > int64(b.chainParams.TimePerBlock)*2 {
return b.chainParams.PowLimitBits
}
}
// 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 := CompactToBig(bits)
for durationVal > 0 && newTarget.Cmp(b.chainParams.PowLimit) < 0 {
newTarget.Mul(newTarget, adjustmentFactor)
durationVal -= maxRetargetTimespan
}
// Limit new value to the proof of work limit.
if newTarget.Cmp(b.chainParams.PowLimit) > 0 {
newTarget.Set(b.chainParams.PowLimit)
}
return 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 (b *BlockChain) findPrevTestNetDifficulty(startNode *blockNode) (uint32,
error) {
// Search backwards through the chain for the last block without
// the special rule applied.
blocksPerRetarget := b.chainParams.WorkDiffWindowSize *
b.chainParams.WorkDiffWindows
iterNode := startNode
for iterNode != nil && iterNode.height%blocksPerRetarget != 0 &&
iterNode.header.Bits == b.chainParams.PowLimitBits {
// Get the previous block node. This function is used over
// simply accessing iterNode.parent directly as it will
// dynamically create previous block nodes as needed. This
// helps allow only the pieces of the chain that are needed
// to remain in memory.
var err error
iterNode, err = b.getPrevNodeFromNode(iterNode)
if err != nil {
log.Errorf("getPrevNodeFromNode: %v", err)
return 0, err
}
}
// Return the found difficulty or the minimum difficulty if no
// appropriate block was found.
lastBits := b.chainParams.PowLimitBits
if iterNode != nil {
lastBits = iterNode.header.Bits
}
return lastBits, nil
}
// 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.
//
// This function MUST be called with the chain state lock held (for writes).
func (b *BlockChain) calcNextRequiredDifficulty(curNode *blockNode,
newBlockTime time.Time) (uint32, error) {
// Genesis block.
if curNode == nil {
return b.chainParams.PowLimitBits, nil
}
// Get the old difficulty; if we aren't at a block height where it changes,
// just return this.
oldDiff := curNode.header.Bits
oldDiffBig := CompactToBig(curNode.header.Bits)
// We're not at a retarget point, return the oldDiff.
if (curNode.height+1)%b.chainParams.WorkDiffWindowSize != 0 {
// The test network rules allow minimum difficulty blocks after
// more than twice the desired amount of time needed to generate
// a block has elapsed.
if b.chainParams.ResetMinDifficulty {
// Return minimum difficulty when more than twice the
// desired amount of time needed to generate a block has
// elapsed.
allowMinTime := curNode.header.Timestamp.Add(
b.chainParams.TimePerBlock * b.chainParams.MinDiffResetTimeFactor)
// For every extra target timespan that passes, we halve the
// difficulty.
if newBlockTime.After(allowMinTime) {
timePassed := newBlockTime.Sub(curNode.header.Timestamp)
timePassed -= (b.chainParams.TimePerBlock *
b.chainParams.MinDiffResetTimeFactor)
shifts := uint((timePassed / b.chainParams.TimePerBlock) + 1)
// Scale the difficulty with time passed.
oldTarget := CompactToBig(curNode.header.Bits)
newTarget := new(big.Int)
if shifts < maxShift {
newTarget.Lsh(oldTarget, shifts)
} else {
newTarget.Set(oneLsh256)
}
// Limit new value to the proof of work limit.
if newTarget.Cmp(b.chainParams.PowLimit) > 0 {
newTarget.Set(b.chainParams.PowLimit)
}
return 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.
prevBits, err := b.findPrevTestNetDifficulty(curNode)
if err != nil {
return 0, err
}
return prevBits, nil
}
return oldDiff, nil
}
// Declare some useful variables.
RAFBig := big.NewInt(b.chainParams.RetargetAdjustmentFactor)
nextDiffBigMin := CompactToBig(curNode.header.Bits)
nextDiffBigMin.Div(nextDiffBigMin, RAFBig)
nextDiffBigMax := CompactToBig(curNode.header.Bits)
nextDiffBigMax.Mul(nextDiffBigMax, RAFBig)
alpha := b.chainParams.WorkDiffAlpha
// Number of nodes to traverse while calculating difficulty.
nodesToTraverse := (b.chainParams.WorkDiffWindowSize *
b.chainParams.WorkDiffWindows)
// Initialize bigInt slice for the percentage changes for each window period
// above or below the target.
windowChanges := make([]*big.Int, b.chainParams.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.
oldNode := curNode
windowPeriod := int64(0)
weights := uint64(0)
recentTime := curNode.header.Timestamp.UnixNano()
olderTime := int64(0)
for i := int64(0); ; i++ {
// Store and reset after reaching the end of every window period.
if i%b.chainParams.WorkDiffWindowSize == 0 && i != 0 {
olderTime = oldNode.header.Timestamp.UnixNano()
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 oldNode.height == 0 {
timeDifference = int64(b.chainParams.TargetTimespan)
}
timeDifBig := big.NewInt(timeDifference)
timeDifBig.Lsh(timeDifBig, 32) // Add padding
targetTemp := big.NewInt(int64(b.chainParams.TargetTimespan))
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.chainParams.WorkDiffWindows-windowPeriod)*alpha))
// Sum up all the different weights incrementally.
weights += 1 << uint64((b.chainParams.WorkDiffWindows-windowPeriod)*
alpha)
// Store it in the slice.
windowChanges[windowPeriod] = windowAdjusted
windowPeriod++
recentTime = olderTime
}
if i == nodesToTraverse {
break // Exit for loop when we hit the end.
}
// Get the previous block node.
var err error
tempNode := oldNode
oldNode, err = b.getPrevNodeFromNode(oldNode)
if err != nil {
return 0, err
}
// If we're at the genesis block, reset the oldNode
// so that it stays at the genesis block.
if oldNode == nil {
oldNode = tempNode
}
}
// Sum up the weighted window periods.
weightedSum := big.NewInt(0)
for i := int64(0); i < b.chainParams.WorkDiffWindows; i++ {
weightedSum.Add(weightedSum, windowChanges[i])
}
// Divide by the sum of all weights.
weightsBig := big.NewInt(int64(weights))
weightedSumDiv := weightedSum.Div(weightedSum, weightsBig)
// Multiply by the old diff.
nextDiffBig := weightedSumDiv.Mul(weightedSumDiv, oldDiffBig)
// Right shift to restore the original padding (restore non-fixed point).
nextDiffBig = nextDiffBig.Rsh(nextDiffBig, 32)
// 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(b.chainParams.PowLimit)
} 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 nextDiffBig.Cmp(b.chainParams.PowLimit) > 0 {
nextDiffBig.Set(b.chainParams.PowLimit)
}
// 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 := BigToCompact(nextDiffBig)
log.Debugf("Difficulty retarget at block height %d", curNode.height+1)
log.Debugf("Old target %08x (%064x)", curNode.header.Bits, oldDiffBig)
log.Debugf("New target %08x (%064x)", nextDiffBits, CompactToBig(nextDiffBits))
return nextDiffBits, nil
}
// 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 *chainhash.Hash,
timestamp time.Time) (uint32, error) {
// Fetch the block to get the difficulty for.
node, err := b.findNode(hash)
if err != nil {
return 0, err
}
return b.calcNextRequiredDifficulty(node, timestamp)
}
// 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) (uint32,
error) {
b.chainLock.Lock()
difficulty, err := b.calcNextRequiredDifficulty(b.bestNode, timestamp)
b.chainLock.Unlock()
return difficulty, err
}
// mergeDifficulty takes an original stake difficulty and two new, scaled
// stake difficulties, merges the new difficulties, and outputs a new
// merged stake difficulty.
func mergeDifficulty(oldDiff int64, newDiff1 int64, newDiff2 int64) int64 {
newDiff1Big := big.NewInt(newDiff1)
newDiff2Big := big.NewInt(newDiff2)
newDiff2Big.Lsh(newDiff2Big, 32)
oldDiffBig := big.NewInt(oldDiff)
oldDiffBigLSH := big.NewInt(oldDiff)
oldDiffBigLSH.Lsh(oldDiffBig, 32)
newDiff1Big.Div(oldDiffBigLSH, newDiff1Big)
newDiff2Big.Div(newDiff2Big, oldDiffBig)
// Combine the two changes in difficulty.
summedChange := big.NewInt(0)
summedChange.Set(newDiff2Big)
summedChange.Lsh(summedChange, 32)
summedChange.Div(summedChange, newDiff1Big)
summedChange.Mul(summedChange, oldDiffBig)
summedChange.Rsh(summedChange, 32)
return summedChange.Int64()
}
// calcNextRequiredStakeDifficulty calculates the exponentially weighted average
// and then uses it to determine the next stake difficulty.
// TODO: You can combine the first and second for loops below for a speed up
// if you'd like, I'm not sure how much it matters.
func (b *BlockChain) calcNextRequiredStakeDifficulty(curNode *blockNode) (int64,
error) {
alpha := b.chainParams.StakeDiffAlpha
stakeDiffStartHeight := int64(b.chainParams.CoinbaseMaturity) +
1
maxRetarget := int64(b.chainParams.RetargetAdjustmentFactor)
TicketPoolWeight := int64(b.chainParams.TicketPoolSizeWeight)
// Number of nodes to traverse while calculating difficulty.
nodesToTraverse := (b.chainParams.StakeDiffWindowSize *
b.chainParams.StakeDiffWindows)
// Genesis block. Block at height 1 has these parameters.
// Additionally, if we're before the time when people generally begin
// purchasing tickets, just use the MinimumStakeDiff.
// This is sort of sloppy and coded with the hopes that generally by
// stakeDiffStartHeight people will be submitting lots of SStx over the
// past nodesToTraverse many nodes. It should be okay with the default
// Decred parameters, but might do weird things if you use custom
// parameters.
if curNode == nil ||
curNode.height < stakeDiffStartHeight {
return b.chainParams.MinimumStakeDiff, nil
}
// Get the old difficulty; if we aren't at a block height where it changes,
// just return this.
oldDiff := curNode.header.SBits
if (curNode.height+1)%b.chainParams.StakeDiffWindowSize != 0 {
return oldDiff, nil
}
// The target size of the ticketPool in live tickets. Recast these as int64
// to avoid possible overflows for large sizes of either variable in
// params.
targetForTicketPool := int64(b.chainParams.TicketsPerBlock) *
int64(b.chainParams.TicketPoolSize)
// Initialize bigInt slice for the percentage changes for each window period
// above or below the target.
windowChanges := make([]*big.Int, b.chainParams.StakeDiffWindows)
// Regress through all of the previous blocks and store the percent changes
// per window period; use bigInts to emulate 64.32 bit fixed point.
oldNode := curNode
windowPeriod := int64(0)
weights := uint64(0)
for i := int64(0); ; i++ {
// Store and reset after reaching the end of every window period.
if (i+1)%b.chainParams.StakeDiffWindowSize == 0 {
// First adjust based on ticketPoolSize. Skew the difference
// in ticketPoolSize by max adjustment factor to help
// weight ticket pool size versus tickets per block.
poolSizeSkew := (int64(oldNode.header.PoolSize)-
targetForTicketPool)*TicketPoolWeight + targetForTicketPool
// Don't let this be negative or zero.
if poolSizeSkew <= 0 {
poolSizeSkew = 1
}
curPoolSizeTemp := big.NewInt(poolSizeSkew)
curPoolSizeTemp.Lsh(curPoolSizeTemp, 32) // Add padding
targetTemp := big.NewInt(targetForTicketPool)
windowAdjusted := curPoolSizeTemp.Div(curPoolSizeTemp, 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.chainParams.StakeDiffWindows-windowPeriod)*alpha))
// Sum up all the different weights incrementally.
weights += 1 << uint64((b.chainParams.StakeDiffWindows-windowPeriod)*
alpha)
// Store it in the slice.
windowChanges[windowPeriod] = windowAdjusted
// windowFreshStake = 0
windowPeriod++
}
if (i + 1) == nodesToTraverse {
break // Exit for loop when we hit the end.
}
// Get the previous block node.
var err error
tempNode := oldNode
oldNode, err = b.getPrevNodeFromNode(oldNode)
if err != nil {
return 0, err
}
// If we're at the genesis block, reset the oldNode
// so that it stays at the genesis block.
if oldNode == nil {
oldNode = tempNode
}
}
// Sum up the weighted window periods.
weightedSum := big.NewInt(0)
for i := int64(0); i < b.chainParams.StakeDiffWindows; i++ {
weightedSum.Add(weightedSum, windowChanges[i])
}
// Divide by the sum of all weights.
weightsBig := big.NewInt(int64(weights))
weightedSumDiv := weightedSum.Div(weightedSum, weightsBig)
// Multiply by the old stake diff.
oldDiffBig := big.NewInt(oldDiff)
nextDiffBig := weightedSumDiv.Mul(weightedSumDiv, oldDiffBig)
// Right shift to restore the original padding (restore non-fixed point).
nextDiffBig = nextDiffBig.Rsh(nextDiffBig, 32)
nextDiffTicketPool := nextDiffBig.Int64()
// 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 oldDiff == 0 { // This should never really happen, but in case it does...
return nextDiffTicketPool, nil
} else if nextDiffTicketPool == 0 {
nextDiffTicketPool = oldDiff / maxRetarget
} else if (nextDiffTicketPool / oldDiff) > (maxRetarget - 1) {
nextDiffTicketPool = oldDiff * maxRetarget
} else if (oldDiff / nextDiffTicketPool) > (maxRetarget - 1) {
nextDiffTicketPool = oldDiff / maxRetarget
}
// The target number of new SStx per block for any given window period.
targetForWindow := b.chainParams.StakeDiffWindowSize *
int64(b.chainParams.TicketsPerBlock)
// Regress through all of the previous blocks and store the percent changes
// per window period; use bigInts to emulate 64.32 bit fixed point.
oldNode = curNode
windowFreshStake := int64(0)
windowPeriod = int64(0)
weights = uint64(0)
for i := int64(0); ; i++ {
// Add the fresh stake into the store for this window period.
windowFreshStake += int64(oldNode.header.FreshStake)
// Store and reset after reaching the end of every window period.
if (i+1)%b.chainParams.StakeDiffWindowSize == 0 {
// Don't let fresh stake be zero.
if windowFreshStake <= 0 {
windowFreshStake = 1
}
freshTemp := big.NewInt(windowFreshStake)
freshTemp.Lsh(freshTemp, 32) // Add padding
targetTemp := big.NewInt(targetForWindow)
// Get the percentage change.
windowAdjusted := freshTemp.Div(freshTemp, 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.chainParams.StakeDiffWindows-windowPeriod)*alpha))
// Sum up all the different weights incrementally.
weights += 1 <<
uint64((b.chainParams.StakeDiffWindows-windowPeriod)*alpha)
// Store it in the slice.
windowChanges[windowPeriod] = windowAdjusted
windowFreshStake = 0
windowPeriod++
}
if (i + 1) == nodesToTraverse {
break // Exit for loop when we hit the end.
}
// Get the previous block node.
var err error
tempNode := oldNode
oldNode, err = b.getPrevNodeFromNode(oldNode)
if err != nil {
return 0, err
}
// If we're at the genesis block, reset the oldNode
// so that it stays at the genesis block.
if oldNode == nil {
oldNode = tempNode
}
}
// Sum up the weighted window periods.
weightedSum = big.NewInt(0)
for i := int64(0); i < b.chainParams.StakeDiffWindows; i++ {
weightedSum.Add(weightedSum, windowChanges[i])
}
// Divide by the sum of all weights.
weightsBig = big.NewInt(int64(weights))
weightedSumDiv = weightedSum.Div(weightedSum, weightsBig)
// Multiply by the old stake diff.
oldDiffBig = big.NewInt(oldDiff)
nextDiffBig = weightedSumDiv.Mul(weightedSumDiv, oldDiffBig)
// Right shift to restore the original padding (restore non-fixed point).
nextDiffBig = nextDiffBig.Rsh(nextDiffBig, 32)
nextDiffFreshStake := nextDiffBig.Int64()
// 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 oldDiff == 0 { // This should never really happen, but in case it does...
return nextDiffFreshStake, nil
} else if nextDiffFreshStake == 0 {
nextDiffFreshStake = oldDiff / maxRetarget
} else if (nextDiffFreshStake / oldDiff) > (maxRetarget - 1) {
nextDiffFreshStake = oldDiff * maxRetarget
} else if (oldDiff / nextDiffFreshStake) > (maxRetarget - 1) {
nextDiffFreshStake = oldDiff / maxRetarget
}
// Average the two differences using scaled multiplication.
nextDiff := mergeDifficulty(oldDiff, nextDiffTicketPool, nextDiffFreshStake)
// 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 oldDiff == 0 { // This should never really happen, but in case it does...
return oldDiff, nil
} else if nextDiff == 0 {
nextDiff = oldDiff / maxRetarget
} else if (nextDiff / oldDiff) > (maxRetarget - 1) {
nextDiff = oldDiff * maxRetarget
} else if (oldDiff / nextDiff) > (maxRetarget - 1) {
nextDiff = oldDiff / maxRetarget
}
// If the next diff is below the network minimum, set the required stake
// difficulty to the minimum.
if nextDiff < b.chainParams.MinimumStakeDiff {
return b.chainParams.MinimumStakeDiff, nil
}
return nextDiff, nil
}
// CalcNextRequiredStakeDifficulty is the exported version of the above function.
// This function is NOT safe for concurrent access.
func (b *BlockChain) CalcNextRequiredStakeDifficulty() (int64, error) {
return b.calcNextRequiredStakeDifficulty(b.bestNode)
}
// estimateNextStakeDifficulty returns a user-specified estimate for the next
// stake difficulty, with the passed ticketsInWindow indicating the number of
// fresh stake to pretend exists within this window. Optionally the user can
// also override this variable with useMaxTickets, which simply plugs in the
// maximum number of tickets the user can try.
func (b *BlockChain) estimateNextStakeDifficulty(curNode *blockNode,
ticketsInWindow int64, useMaxTickets bool) (int64, error) {
alpha := b.chainParams.StakeDiffAlpha
stakeDiffStartHeight := int64(b.chainParams.CoinbaseMaturity) +
1
maxRetarget := int64(b.chainParams.RetargetAdjustmentFactor)
TicketPoolWeight := int64(b.chainParams.TicketPoolSizeWeight)
// Number of nodes to traverse while calculating difficulty.
nodesToTraverse := (b.chainParams.StakeDiffWindowSize *
b.chainParams.StakeDiffWindows)
// Genesis block. Block at height 1 has these parameters.
if curNode == nil ||
curNode.height < stakeDiffStartHeight {
return b.chainParams.MinimumStakeDiff, nil
}
// Create a fake blockchain on top of the current best node with
// the number of freshly purchased tickets as indicated by the
// user.
oldDiff := curNode.header.SBits
topNode := curNode
if (curNode.height+1)%b.chainParams.StakeDiffWindowSize != 0 {
nextAdjHeight := ((curNode.height /
b.chainParams.StakeDiffWindowSize) + 1) *
b.chainParams.StakeDiffWindowSize
maxTickets := (nextAdjHeight - curNode.height) *
int64(b.chainParams.MaxFreshStakePerBlock)
// If the user has indicated that the automatically
// calculated maximum amount of tickets should be
// used, plug that in here.
if useMaxTickets {
ticketsInWindow = maxTickets
}
// Double check to make sure there isn't too much.
if ticketsInWindow > maxTickets {
return 0, fmt.Errorf("too much fresh stake to be used "+
"in evaluation requested; max %v, got %v", maxTickets,
ticketsInWindow)
}
// Insert all the tickets into bogus nodes that will be
// used to calculate the next difficulty below.
ticketsToInsert := ticketsInWindow
for i := curNode.height + 1; i < nextAdjHeight; i++ {
emptyHeader := new(wire.BlockHeader)
emptyHeader.Height = uint32(i)
// User a constant pool size for estimate, since
// this has much less fluctuation than freshStake.
// TODO Use a better pool size estimate?
emptyHeader.PoolSize = curNode.header.PoolSize
// Insert the fake fresh stake into each block,
// decrementing the amount we need to use each
// time until we hit 0.
freshStake := b.chainParams.MaxFreshStakePerBlock
if int64(freshStake) > ticketsToInsert {
freshStake = uint8(ticketsToInsert)
ticketsToInsert -= ticketsToInsert
} else {
ticketsToInsert -= int64(b.chainParams.MaxFreshStakePerBlock)
}
emptyHeader.FreshStake = freshStake
// Connect the header.
emptyHeader.PrevBlock = *topNode.hash
// Make up a node hash.
hB, err := emptyHeader.Bytes()
if err != nil {
return 0, err
}
emptyHeaderHash := chainhash.HashFuncH(hB)
thisNode := new(blockNode)
thisNode.header = *emptyHeader
thisNode.hash = &emptyHeaderHash
thisNode.height = i
thisNode.parent = topNode
topNode = thisNode
}
}
// The target size of the ticketPool in live tickets. Recast these as int64
// to avoid possible overflows for large sizes of either variable in
// params.
targetForTicketPool := int64(b.chainParams.TicketsPerBlock) *
int64(b.chainParams.TicketPoolSize)
// Initialize bigInt slice for the percentage changes for each window period
// above or below the target.
windowChanges := make([]*big.Int, b.chainParams.StakeDiffWindows)
// Regress through all of the previous blocks and store the percent changes
// per window period; use bigInts to emulate 64.32 bit fixed point.
oldNode := topNode
windowPeriod := int64(0)
weights := uint64(0)
for i := int64(0); ; i++ {
// Store and reset after reaching the end of every window period.
if (i+1)%b.chainParams.StakeDiffWindowSize == 0 {
// First adjust based on ticketPoolSize. Skew the difference
// in ticketPoolSize by max adjustment factor to help
// weight ticket pool size versus tickets per block.
poolSizeSkew := (int64(oldNode.header.PoolSize)-
targetForTicketPool)*TicketPoolWeight + targetForTicketPool
// Don't let this be negative or zero.
if poolSizeSkew <= 0 {
poolSizeSkew = 1
}
curPoolSizeTemp := big.NewInt(poolSizeSkew)
curPoolSizeTemp.Lsh(curPoolSizeTemp, 32) // Add padding
targetTemp := big.NewInt(targetForTicketPool)
windowAdjusted := curPoolSizeTemp.Div(curPoolSizeTemp, 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.chainParams.StakeDiffWindows-windowPeriod)*alpha))
// Sum up all the different weights incrementally.
weights += 1 << uint64((b.chainParams.StakeDiffWindows-windowPeriod)*
alpha)
// Store it in the slice.
windowChanges[windowPeriod] = windowAdjusted
// windowFreshStake = 0
windowPeriod++
}
if (i + 1) == nodesToTraverse {
break // Exit for loop when we hit the end.
}
// Get the previous block node.
var err error
tempNode := oldNode
oldNode, err = b.getPrevNodeFromNode(oldNode)
if err != nil {
return 0, err
}
// If we're at the genesis block, reset the oldNode
// so that it stays at the genesis block.
if oldNode == nil {
oldNode = tempNode
}
}
// Sum up the weighted window periods.
weightedSum := big.NewInt(0)
for i := int64(0); i < b.chainParams.StakeDiffWindows; i++ {
weightedSum.Add(weightedSum, windowChanges[i])
}
// Divide by the sum of all weights.
weightsBig := big.NewInt(int64(weights))
weightedSumDiv := weightedSum.Div(weightedSum, weightsBig)
// Multiply by the old stake diff.
oldDiffBig := big.NewInt(oldDiff)
nextDiffBig := weightedSumDiv.Mul(weightedSumDiv, oldDiffBig)
// Right shift to restore the original padding (restore non-fixed point).
nextDiffBig = nextDiffBig.Rsh(nextDiffBig, 32)
nextDiffTicketPool := nextDiffBig.Int64()
// 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 oldDiff == 0 { // This should never really happen, but in case it does...
return nextDiffTicketPool, nil
} else if nextDiffTicketPool == 0 {
nextDiffTicketPool = oldDiff / maxRetarget
} else if (nextDiffTicketPool / oldDiff) > (maxRetarget - 1) {
nextDiffTicketPool = oldDiff * maxRetarget
} else if (oldDiff / nextDiffTicketPool) > (maxRetarget - 1) {
nextDiffTicketPool = oldDiff / maxRetarget
}
// The target number of new SStx per block for any given window period.
targetForWindow := b.chainParams.StakeDiffWindowSize *
int64(b.chainParams.TicketsPerBlock)
// Regress through all of the previous blocks and store the percent changes
// per window period; use bigInts to emulate 64.32 bit fixed point.
oldNode = topNode
windowFreshStake := int64(0)
windowPeriod = int64(0)
weights = uint64(0)
for i := int64(0); ; i++ {
// Add the fresh stake into the store for this window period.
windowFreshStake += int64(oldNode.header.FreshStake)
// Store and reset after reaching the end of every window period.
if (i+1)%b.chainParams.StakeDiffWindowSize == 0 {
// Don't let fresh stake be zero.
if windowFreshStake <= 0 {
windowFreshStake = 1
}
freshTemp := big.NewInt(windowFreshStake)
freshTemp.Lsh(freshTemp, 32) // Add padding
targetTemp := big.NewInt(targetForWindow)
// Get the percentage change.
windowAdjusted := freshTemp.Div(freshTemp, 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.chainParams.StakeDiffWindows-windowPeriod)*alpha))
// Sum up all the different weights incrementally.
weights += 1 <<
uint64((b.chainParams.StakeDiffWindows-windowPeriod)*alpha)
// Store it in the slice.
windowChanges[windowPeriod] = windowAdjusted
windowFreshStake = 0
windowPeriod++
}
if (i + 1) == nodesToTraverse {
break // Exit for loop when we hit the end.
}
// Get the previous block node.
var err error
tempNode := oldNode
oldNode, err = b.getPrevNodeFromNode(oldNode)
if err != nil {
return 0, err
}
// If we're at the genesis block, reset the oldNode
// so that it stays at the genesis block.
if oldNode == nil {
oldNode = tempNode
}
}
// Sum up the weighted window periods.
weightedSum = big.NewInt(0)
for i := int64(0); i < b.chainParams.StakeDiffWindows; i++ {
weightedSum.Add(weightedSum, windowChanges[i])
}
// Divide by the sum of all weights.
weightsBig = big.NewInt(int64(weights))
weightedSumDiv = weightedSum.Div(weightedSum, weightsBig)
// Multiply by the old stake diff.
oldDiffBig = big.NewInt(oldDiff)
nextDiffBig = weightedSumDiv.Mul(weightedSumDiv, oldDiffBig)
// Right shift to restore the original padding (restore non-fixed point).
nextDiffBig = nextDiffBig.Rsh(nextDiffBig, 32)
nextDiffFreshStake := nextDiffBig.Int64()
// 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 oldDiff == 0 { // This should never really happen, but in case it does...
return nextDiffFreshStake, nil
} else if nextDiffFreshStake == 0 {
nextDiffFreshStake = oldDiff / maxRetarget
} else if (nextDiffFreshStake / oldDiff) > (maxRetarget - 1) {
nextDiffFreshStake = oldDiff * maxRetarget
} else if (oldDiff / nextDiffFreshStake) > (maxRetarget - 1) {
nextDiffFreshStake = oldDiff / maxRetarget
}
// Average the two differences using scaled multiplication.
nextDiff := mergeDifficulty(oldDiff, nextDiffTicketPool, nextDiffFreshStake)
// 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 oldDiff == 0 { // This should never really happen, but in case it does...
return oldDiff, nil
} else if nextDiff == 0 {
nextDiff = oldDiff / maxRetarget
} else if (nextDiff / oldDiff) > (maxRetarget - 1) {
nextDiff = oldDiff * maxRetarget
} else if (oldDiff / nextDiff) > (maxRetarget - 1) {
nextDiff = oldDiff / maxRetarget
}
// If the next diff is below the network minimum, set the required stake
// difficulty to the minimum.
if nextDiff < b.chainParams.MinimumStakeDiff {
return b.chainParams.MinimumStakeDiff, nil
}
return nextDiff, nil
}
// EstimateNextStakeDifficulty is the exported version of the above function.
// This function is NOT safe for concurrent access.
func (b *BlockChain) EstimateNextStakeDifficulty(ticketsInWindow int64,
useMaxTickets bool) (int64, error) {
return b.estimateNextStakeDifficulty(b.bestNode, ticketsInWindow,
useMaxTickets)
}