blockchain: Use standalone module for work funcs.

This updates blockchain to make use of the new work functions in the
standalone module.

Finally, it also deprecates the related exported functions in the
blockchain module so they can be removed in the next major version and
rewrites them in terms of the standalone module in the mean time.
This commit is contained in:
Dave Collins 2019-08-06 08:42:04 -05:00
parent e172790d98
commit 4133596ddb
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2
6 changed files with 68 additions and 121 deletions

View File

@ -13,6 +13,7 @@ import (
"time"
"github.com/decred/dcrd/blockchain/stake"
"github.com/decred/dcrd/blockchain/standalone"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/database"
"github.com/decred/dcrd/wire"
@ -137,7 +138,7 @@ type blockNode struct {
func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parent *blockNode) {
*node = blockNode{
hash: blockHeader.BlockHash(),
workSum: CalcWork(blockHeader.Bits),
workSum: standalone.CalcWork(blockHeader.Bits),
height: int64(blockHeader.Height),
blockVersion: blockHeader.Version,
voteBits: blockHeader.VoteBits,

View File

@ -15,6 +15,7 @@ import (
"time"
"github.com/decred/dcrd/blockchain/stake"
"github.com/decred/dcrd/blockchain/standalone"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/database"
"github.com/decred/dcrd/wire"
@ -1385,7 +1386,7 @@ func TestBestChainStateSerialization(t *testing.T) {
totalTxns: 1,
totalSubsidy: 0,
workSum: func() *big.Int {
workSum.Add(workSum, CalcWork(486604799))
workSum.Add(workSum, standalone.CalcWork(486604799))
return new(big.Int).Set(workSum)
}(), // 0x0100010001
},
@ -1399,7 +1400,7 @@ func TestBestChainStateSerialization(t *testing.T) {
totalTxns: 2,
totalSubsidy: 123456789,
workSum: func() *big.Int {
workSum.Add(workSum, CalcWork(486604799))
workSum.Add(workSum, standalone.CalcWork(486604799))
return new(big.Int).Set(workSum)
}(), // 0x0200020002,
},

View File

@ -10,6 +10,7 @@ import (
"math/big"
"time"
"github.com/decred/dcrd/blockchain/standalone"
"github.com/decred/dcrd/chaincfg"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/wire"
@ -31,16 +32,10 @@ var (
// HashToBig converts a chainhash.Hash into a big.Int that can be used to
// perform math comparisons.
//
// Deprecated: Use standalone.HashToBig instead.
func HashToBig(hash *chainhash.Hash) *big.Int {
// A Hash 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[:])
return standalone.HashToBig(hash)
}
// CompactToBig converts a compact representation of a whole number N to an
@ -66,74 +61,20 @@ func HashToBig(hash *chainhash.Hash) *big.Int {
// 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.
//
// Deprecated: Use standalone.CompactToBig instead.
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
return standalone.CompactToBig(compact)
}
// 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.
//
// Deprecated: Use standalone.BigToCompact instead.
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
return standalone.BigToCompact(n)
}
// CalcWork calculates a work value from difficulty bits. Decred increases
@ -146,18 +87,10 @@ func BigToCompact(n *big.Int) uint32 {
// 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.
//
// Deprecated: Use standalone.CalcWork instead.
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)
return standalone.CalcWork(bits)
}
// calcEasiestDifficulty calculates the easiest possible difficulty that a block
@ -183,7 +116,7 @@ func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration)
// 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)
newTarget := standalone.CompactToBig(bits)
for durationVal > 0 && newTarget.Cmp(b.chainParams.PowLimit) < 0 {
newTarget.Mul(newTarget, adjustmentFactor)
durationVal -= maxRetargetTimespan
@ -194,7 +127,7 @@ func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration)
newTarget.Set(b.chainParams.PowLimit)
}
return BigToCompact(newTarget)
return standalone.BigToCompact(newTarget)
}
// findPrevTestNetDifficulty returns the difficulty of the previous block which
@ -231,7 +164,7 @@ func (b *BlockChain) calcNextRequiredDifficulty(curNode *blockNode, newBlockTime
// Get the old difficulty; if we aren't at a block height where it changes,
// just return this.
oldDiff := curNode.bits
oldDiffBig := CompactToBig(curNode.bits)
oldDiffBig := standalone.CompactToBig(curNode.bits)
// We're not at a retarget point, return the oldDiff.
if (curNode.height+1)%b.chainParams.WorkDiffWindowSize != 0 {
@ -259,9 +192,9 @@ func (b *BlockChain) calcNextRequiredDifficulty(curNode *blockNode, newBlockTime
// Declare some useful variables.
RAFBig := big.NewInt(b.chainParams.RetargetAdjustmentFactor)
nextDiffBigMin := CompactToBig(curNode.bits)
nextDiffBigMin := standalone.CompactToBig(curNode.bits)
nextDiffBigMin.Div(nextDiffBigMin, RAFBig)
nextDiffBigMax := CompactToBig(curNode.bits)
nextDiffBigMax := standalone.CompactToBig(curNode.bits)
nextDiffBigMax.Mul(nextDiffBigMax, RAFBig)
alpha := b.chainParams.WorkDiffAlpha
@ -367,10 +300,11 @@ func (b *BlockChain) calcNextRequiredDifficulty(curNode *blockNode, newBlockTime
// intentionally converting the bits back to a number instead of using
// newTarget since conversion to the compact representation loses
// precision.
nextDiffBits := BigToCompact(nextDiffBig)
nextDiffBits := standalone.BigToCompact(nextDiffBig)
log.Debugf("Difficulty retarget at block height %d", curNode.height+1)
log.Debugf("Old target %08x (%064x)", curNode.bits, oldDiffBig)
log.Debugf("New target %08x (%064x)", nextDiffBits, CompactToBig(nextDiffBits))
log.Debugf("New target %08x (%064x)", nextDiffBits, standalone.CompactToBig(
nextDiffBits))
return nextDiffBits, nil
}

View File

@ -9,6 +9,7 @@ import (
"fmt"
"time"
"github.com/decred/dcrd/blockchain/standalone"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/dcrutil"
)
@ -167,9 +168,9 @@ func (b *BlockChain) ProcessBlock(block *dcrutil.Block, flags BehaviorFlags) (in
// expected based on elapsed time since the last checkpoint and
// maximum adjustment allowed by the retarget rules.
duration := blockHeader.Timestamp.Sub(checkpointTime)
requiredTarget := CompactToBig(b.calcEasiestDifficulty(
requiredTarget := standalone.CompactToBig(b.calcEasiestDifficulty(
checkpointNode.bits, duration))
currentTarget := CompactToBig(blockHeader.Bits)
currentTarget := standalone.CompactToBig(blockHeader.Bits)
if currentTarget.Cmp(requiredTarget) > 0 {
str := fmt.Sprintf("block target difficulty of %064x "+
"is too low when compared to the previous "+

View File

@ -14,6 +14,7 @@ import (
"github.com/decred/dcrd/blockchain/internal/progresslog"
"github.com/decred/dcrd/blockchain/stake"
"github.com/decred/dcrd/blockchain/standalone"
"github.com/decred/dcrd/chaincfg"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/database"
@ -609,7 +610,7 @@ func upgradeToVersion5(db database.DB, chainParams *chaincfg.Params, dbInfo *dat
height: 0,
totalTxns: numTxns,
totalSubsidy: 0,
workSum: CalcWork(genesisBlock.Header.Bits),
workSum: standalone.CalcWork(genesisBlock.Header.Bits),
})
err = meta.Put(chainStateKeyName, serializedData)
if err != nil {

View File

@ -433,6 +433,25 @@ func CheckProofOfStake(block *dcrutil.Block, posLimit int64) error {
return checkProofOfStake(block, posLimit)
}
// standaloneToChainRuleError attempts to convert the passed error from a
// standalone.RuleError to a blockchain.RuleError with the equivalent code. The
// error is simply passed through without modification if it is not a
// standalone.RuleError, not one of the specifically recognized error codes, or
// nil.
func standaloneToChainRuleError(err error) error {
// Convert standalone package rule errors to blockchain rule errors.
if rErr, ok := err.(standalone.RuleError); ok {
switch rErr.ErrorCode {
case standalone.ErrUnexpectedDifficulty:
return ruleError(ErrUnexpectedDifficulty, rErr.Description)
case standalone.ErrHighHash:
return ruleError(ErrHighHash, rErr.Description)
}
}
return err
}
// checkProofOfWork ensures the block header bits which indicate the target
// difficulty is in min/max range and that the block hash is less than the
// target difficulty as claimed.
@ -441,40 +460,30 @@ func CheckProofOfStake(block *dcrutil.Block, posLimit int64) error {
// - BFNoPoWCheck: The check to ensure the block hash is less than the target
// difficulty is not performed.
func checkProofOfWork(header *wire.BlockHeader, powLimit *big.Int, flags BehaviorFlags) error {
// The target difficulty must be larger than zero.
target := CompactToBig(header.Bits)
if target.Sign() <= 0 {
str := fmt.Sprintf("block target difficulty of %064x is too "+
"low", target)
return ruleError(ErrUnexpectedDifficulty, str)
// Only ensure the target difficulty bits are in the valid range when the
// the flag to avoid proof of work checks is set.
if flags&BFNoPoWCheck == BFNoPoWCheck {
err := standalone.CheckProofOfWorkRange(header.Bits, powLimit)
return standaloneToChainRuleError(err)
}
// The target difficulty must be less than the maximum allowed.
if target.Cmp(powLimit) > 0 {
str := fmt.Sprintf("block target difficulty of %064x is "+
"higher than max of %064x", target, powLimit)
return ruleError(ErrUnexpectedDifficulty, str)
}
// The block hash must be less than the claimed target unless the flag
// to avoid proof of work checks is set.
if flags&BFNoPoWCheck != BFNoPoWCheck {
// The block hash must be less than the claimed target.
hash := header.BlockHash()
hashNum := HashToBig(&hash)
if hashNum.Cmp(target) > 0 {
str := fmt.Sprintf("block hash of %064x is higher than"+
" expected max of %064x", hashNum, target)
return ruleError(ErrHighHash, str)
}
}
return nil
// Perform all proof of work checks when the flag is not set:
//
// - The target difficulty must be larger than zero.
// - The target difficulty must be less than the maximum allowed.
// - The block hash must be less than the claimed target.
blockHash := header.BlockHash()
err := standalone.CheckProofOfWork(&blockHash, header.Bits, powLimit)
return standaloneToChainRuleError(err)
}
// CheckProofOfWork ensures the block header bits which indicate the target
// difficulty is in min/max range and that the block hash is less than the
// target difficulty as claimed.
// target difficulty as claimed. This is equivalent to the function in the
// standalone package with the exception that that any error is converted to a
// RuleError.
//
// Deprecated: Use standalone.CheckProofOfWork instead.
func CheckProofOfWork(header *wire.BlockHeader, powLimit *big.Int) error {
return checkProofOfWork(header, powLimit, BFNone)
}