diff --git a/blockchain/blockindex.go b/blockchain/blockindex.go index 45ec24c7..716153cc 100644 --- a/blockchain/blockindex.go +++ b/blockchain/blockindex.go @@ -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, diff --git a/blockchain/chainio_test.go b/blockchain/chainio_test.go index c70602c2..aa1de01d 100644 --- a/blockchain/chainio_test.go +++ b/blockchain/chainio_test.go @@ -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, }, diff --git a/blockchain/difficulty.go b/blockchain/difficulty.go index cb9cd338..1a99dc29 100644 --- a/blockchain/difficulty.go +++ b/blockchain/difficulty.go @@ -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 } diff --git a/blockchain/process.go b/blockchain/process.go index 693b4b8f..4a8ddccd 100644 --- a/blockchain/process.go +++ b/blockchain/process.go @@ -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 "+ diff --git a/blockchain/upgrade.go b/blockchain/upgrade.go index d117466b..2663e5c2 100644 --- a/blockchain/upgrade.go +++ b/blockchain/upgrade.go @@ -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 { diff --git a/blockchain/validate.go b/blockchain/validate.go index a2ed1b9d..10c00805 100644 --- a/blockchain/validate.go +++ b/blockchain/validate.go @@ -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) }