mirror of
https://github.com/btcsuite/btcd.git
synced 2024-11-19 01:40:07 +01:00
Merge pull request #1931 from Crypt-iQ/export_header_funcs
blockchain: refactor and export header validation checks
This commit is contained in:
commit
f5eeb10d03
@ -169,6 +169,60 @@ func (node *blockNode) Ancestor(height int32) *blockNode {
|
||||
return n
|
||||
}
|
||||
|
||||
// Height returns the blockNode's height in the chain.
|
||||
//
|
||||
// NOTE: Part of the HeaderCtx interface.
|
||||
func (node *blockNode) Height() int32 {
|
||||
return node.height
|
||||
}
|
||||
|
||||
// Bits returns the blockNode's nBits.
|
||||
//
|
||||
// NOTE: Part of the HeaderCtx interface.
|
||||
func (node *blockNode) Bits() uint32 {
|
||||
return node.bits
|
||||
}
|
||||
|
||||
// Timestamp returns the blockNode's timestamp.
|
||||
//
|
||||
// NOTE: Part of the HeaderCtx interface.
|
||||
func (node *blockNode) Timestamp() int64 {
|
||||
return node.timestamp
|
||||
}
|
||||
|
||||
// Parent returns the blockNode's parent.
|
||||
//
|
||||
// NOTE: Part of the HeaderCtx interface.
|
||||
func (node *blockNode) Parent() HeaderCtx {
|
||||
if node.parent == nil {
|
||||
// This is required since node.parent is a *blockNode and if we
|
||||
// do not explicitly return nil here, the caller may fail when
|
||||
// nil-checking this.
|
||||
return nil
|
||||
}
|
||||
|
||||
return node.parent
|
||||
}
|
||||
|
||||
// RelativeAncestorCtx returns the blockNode's ancestor that is distance blocks
|
||||
// before it in the chain. This is equivalent to the RelativeAncestor function
|
||||
// below except that the return type is different.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
//
|
||||
// NOTE: Part of the HeaderCtx interface.
|
||||
func (node *blockNode) RelativeAncestorCtx(distance int32) HeaderCtx {
|
||||
ancestor := node.RelativeAncestor(distance)
|
||||
if ancestor == nil {
|
||||
// This is required since RelativeAncestor returns a *blockNode
|
||||
// and if we do not explicitly return nil here, the caller may
|
||||
// fail when nil-checking this.
|
||||
return nil
|
||||
}
|
||||
|
||||
return ancestor
|
||||
}
|
||||
|
||||
// RelativeAncestor returns the ancestor block node a relative 'distance' blocks
|
||||
// before this node. This is equivalent to calling Ancestor with the node's
|
||||
// height minus provided distance.
|
||||
@ -182,17 +236,17 @@ func (node *blockNode) RelativeAncestor(distance int32) *blockNode {
|
||||
// prior to, and including, the block node.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (node *blockNode) CalcPastMedianTime() time.Time {
|
||||
func CalcPastMedianTime(node HeaderCtx) time.Time {
|
||||
// Create a slice of the previous few block timestamps used to calculate
|
||||
// the median per the number defined by the constant medianTimeBlocks.
|
||||
timestamps := make([]int64, medianTimeBlocks)
|
||||
numNodes := 0
|
||||
iterNode := node
|
||||
for i := 0; i < medianTimeBlocks && iterNode != nil; i++ {
|
||||
timestamps[i] = iterNode.timestamp
|
||||
timestamps[i] = iterNode.Timestamp()
|
||||
numNodes++
|
||||
|
||||
iterNode = iterNode.parent
|
||||
iterNode = iterNode.Parent()
|
||||
}
|
||||
|
||||
// Prune the slice to the actual number of available timestamps which
|
||||
@ -217,6 +271,10 @@ func (node *blockNode) CalcPastMedianTime() time.Time {
|
||||
return time.Unix(medianTimestamp, 0)
|
||||
}
|
||||
|
||||
// A compile-time assertion to ensure blockNode implements the HeaderCtx
|
||||
// interface.
|
||||
var _ HeaderCtx = (*blockNode)(nil)
|
||||
|
||||
// blockIndex provides facilities for keeping track of an in-memory index of the
|
||||
// block chain. Although the name block chain suggests a single chain of
|
||||
// blocks, it is actually a tree-shaped structure where any node can have
|
||||
|
@ -437,7 +437,7 @@ func (b *BlockChain) calcSequenceLock(node *blockNode, tx *btcutil.Tx, utxoView
|
||||
prevInputHeight = 0
|
||||
}
|
||||
blockNode := node.Ancestor(prevInputHeight)
|
||||
medianTime := blockNode.CalcPastMedianTime()
|
||||
medianTime := CalcPastMedianTime(blockNode)
|
||||
|
||||
// Time based relative time-locks as defined by BIP 68
|
||||
// have a time granularity of RelativeLockSeconds, so
|
||||
@ -595,7 +595,8 @@ func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block,
|
||||
blockSize := uint64(block.MsgBlock().SerializeSize())
|
||||
blockWeight := uint64(GetBlockWeight(block))
|
||||
state := newBestState(node, blockSize, blockWeight, numTxns,
|
||||
curTotalTxns+numTxns, node.CalcPastMedianTime())
|
||||
curTotalTxns+numTxns, CalcPastMedianTime(node),
|
||||
)
|
||||
|
||||
// Atomically insert info into the database.
|
||||
err = b.db.Update(func(dbTx database.Tx) error {
|
||||
@ -708,7 +709,7 @@ func (b *BlockChain) disconnectBlock(node *blockNode, block *btcutil.Block, view
|
||||
blockWeight := uint64(GetBlockWeight(prevBlock))
|
||||
newTotalTxns := curTotalTxns - uint64(len(block.MsgBlock().Transactions))
|
||||
state := newBestState(prevNode, blockSize, blockWeight, numTxns,
|
||||
newTotalTxns, prevNode.CalcPastMedianTime())
|
||||
newTotalTxns, CalcPastMedianTime(prevNode))
|
||||
|
||||
err = b.db.Update(func(dbTx database.Tx) error {
|
||||
// Update best block state.
|
||||
|
@ -163,13 +163,13 @@ func TestCalcSequenceLock(t *testing.T) {
|
||||
// Obtain the median time past from the PoV of the input created above.
|
||||
// The MTP for the input is the MTP from the PoV of the block *prior*
|
||||
// to the one that included it.
|
||||
medianTime := node.RelativeAncestor(5).CalcPastMedianTime().Unix()
|
||||
medianTime := CalcPastMedianTime(node.RelativeAncestor(5)).Unix()
|
||||
|
||||
// The median time calculated from the PoV of the best block in the
|
||||
// test chain. For unconfirmed inputs, this value will be used since
|
||||
// the MTP will be calculated from the PoV of the yet-to-be-mined
|
||||
// block.
|
||||
nextMedianTime := node.CalcPastMedianTime().Unix()
|
||||
nextMedianTime := CalcPastMedianTime(node).Unix()
|
||||
nextBlockHeight := int32(numBlocksToActivate) + 1
|
||||
|
||||
// Add an additional transaction which will serve as our unconfirmed
|
||||
|
@ -1236,7 +1236,7 @@ func (b *BlockChain) initChainState() error {
|
||||
blockWeight := uint64(GetBlockWeight(btcutil.NewBlock(&block)))
|
||||
numTxns := uint64(len(block.Transactions))
|
||||
b.stateSnapshot = newBestState(tip, blockSize, blockWeight,
|
||||
numTxns, state.totalTxns, tip.CalcPastMedianTime())
|
||||
numTxns, state.totalTxns, CalcPastMedianTime(tip))
|
||||
|
||||
return nil
|
||||
})
|
||||
|
@ -193,88 +193,87 @@ func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration)
|
||||
|
||||
// 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 {
|
||||
func findPrevTestNetDifficulty(startNode HeaderCtx, c ChainCtx) uint32 {
|
||||
// Search backwards through the chain for the last block without
|
||||
// the special rule applied.
|
||||
iterNode := startNode
|
||||
for iterNode != nil && iterNode.height%b.blocksPerRetarget != 0 &&
|
||||
iterNode.bits == b.chainParams.PowLimitBits {
|
||||
for iterNode != nil && iterNode.Height()%c.BlocksPerRetarget() != 0 &&
|
||||
iterNode.Bits() == c.ChainParams().PowLimitBits {
|
||||
|
||||
iterNode = iterNode.parent
|
||||
iterNode = iterNode.Parent()
|
||||
}
|
||||
|
||||
// Return the found difficulty or the minimum difficulty if no
|
||||
// appropriate block was found.
|
||||
lastBits := b.chainParams.PowLimitBits
|
||||
lastBits := c.ChainParams().PowLimitBits
|
||||
if iterNode != nil {
|
||||
lastBits = iterNode.bits
|
||||
lastBits = iterNode.Bits()
|
||||
}
|
||||
return lastBits
|
||||
}
|
||||
|
||||
// calcNextRequiredDifficulty calculates the required difficulty for the block
|
||||
// after the passed previous block node based on the difficulty retarget rules.
|
||||
// after the passed previous HeaderCtx 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.
|
||||
func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode,
|
||||
newBlockTime time.Time) (uint32, error) {
|
||||
// the exported version uses the current best chain as the previous HeaderCtx
|
||||
// while this function accepts any block node. This function accepts a ChainCtx
|
||||
// parameter that gives the necessary difficulty context variables.
|
||||
func calcNextRequiredDifficulty(lastNode HeaderCtx, newBlockTime time.Time,
|
||||
c ChainCtx) (uint32, error) {
|
||||
|
||||
// Emulate the same behavior as Bitcoin Core that for regtest there is
|
||||
// no difficulty retargeting.
|
||||
if b.chainParams.PoWNoRetargeting {
|
||||
return b.chainParams.PowLimitBits, nil
|
||||
if c.ChainParams().PoWNoRetargeting {
|
||||
return c.ChainParams().PowLimitBits, nil
|
||||
}
|
||||
|
||||
// Genesis block.
|
||||
if lastNode == nil {
|
||||
return b.chainParams.PowLimitBits, nil
|
||||
return c.ChainParams().PowLimitBits, nil
|
||||
}
|
||||
|
||||
// Return the previous block's difficulty requirements if this block
|
||||
// is not at a difficulty retarget interval.
|
||||
if (lastNode.height+1)%b.blocksPerRetarget != 0 {
|
||||
if (lastNode.Height()+1)%c.BlocksPerRetarget() != 0 {
|
||||
// For networks that support it, allow special reduction of the
|
||||
// required difficulty once too much time has elapsed without
|
||||
// mining a block.
|
||||
if b.chainParams.ReduceMinDifficulty {
|
||||
if c.ChainParams().ReduceMinDifficulty {
|
||||
// Return minimum difficulty when more than the desired
|
||||
// amount of time has elapsed without mining a block.
|
||||
reductionTime := int64(b.chainParams.MinDiffReductionTime /
|
||||
reductionTime := int64(c.ChainParams().MinDiffReductionTime /
|
||||
time.Second)
|
||||
allowMinTime := lastNode.timestamp + reductionTime
|
||||
allowMinTime := lastNode.Timestamp() + reductionTime
|
||||
if newBlockTime.Unix() > allowMinTime {
|
||||
return b.chainParams.PowLimitBits, nil
|
||||
return c.ChainParams().PowLimitBits, 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.
|
||||
return b.findPrevTestNetDifficulty(lastNode), nil
|
||||
return findPrevTestNetDifficulty(lastNode, c), nil
|
||||
}
|
||||
|
||||
// For the main network (or any unrecognized networks), simply
|
||||
// return the previous block's difficulty requirements.
|
||||
return lastNode.bits, nil
|
||||
return lastNode.Bits(), nil
|
||||
}
|
||||
|
||||
// Get the block node at the previous retarget (targetTimespan days
|
||||
// worth of blocks).
|
||||
firstNode := lastNode.RelativeAncestor(b.blocksPerRetarget - 1)
|
||||
firstNode := lastNode.RelativeAncestorCtx(c.BlocksPerRetarget() - 1)
|
||||
if firstNode == nil {
|
||||
return 0, AssertError("unable to obtain previous retarget block")
|
||||
}
|
||||
|
||||
// Limit the amount of adjustment that can occur to the previous
|
||||
// difficulty.
|
||||
actualTimespan := lastNode.timestamp - firstNode.timestamp
|
||||
actualTimespan := lastNode.Timestamp() - firstNode.Timestamp()
|
||||
adjustedTimespan := actualTimespan
|
||||
if actualTimespan < b.minRetargetTimespan {
|
||||
adjustedTimespan = b.minRetargetTimespan
|
||||
} else if actualTimespan > b.maxRetargetTimespan {
|
||||
adjustedTimespan = b.maxRetargetTimespan
|
||||
if actualTimespan < c.MinRetargetTimespan() {
|
||||
adjustedTimespan = c.MinRetargetTimespan()
|
||||
} else if actualTimespan > c.MaxRetargetTimespan() {
|
||||
adjustedTimespan = c.MaxRetargetTimespan()
|
||||
}
|
||||
|
||||
// Calculate new target difficulty as:
|
||||
@ -282,14 +281,14 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode,
|
||||
// The result uses integer division which means it will be slightly
|
||||
// rounded down. Bitcoind also uses integer division to calculate this
|
||||
// result.
|
||||
oldTarget := CompactToBig(lastNode.bits)
|
||||
oldTarget := CompactToBig(lastNode.Bits())
|
||||
newTarget := new(big.Int).Mul(oldTarget, big.NewInt(adjustedTimespan))
|
||||
targetTimeSpan := int64(b.chainParams.TargetTimespan / time.Second)
|
||||
targetTimeSpan := int64(c.ChainParams().TargetTimespan / time.Second)
|
||||
newTarget.Div(newTarget, big.NewInt(targetTimeSpan))
|
||||
|
||||
// Limit new value to the proof of work limit.
|
||||
if newTarget.Cmp(b.chainParams.PowLimit) > 0 {
|
||||
newTarget.Set(b.chainParams.PowLimit)
|
||||
if newTarget.Cmp(c.ChainParams().PowLimit) > 0 {
|
||||
newTarget.Set(c.ChainParams().PowLimit)
|
||||
}
|
||||
|
||||
// Log new target difficulty and return it. The new target logging is
|
||||
@ -297,13 +296,13 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode,
|
||||
// newTarget since conversion to the compact representation loses
|
||||
// precision.
|
||||
newTargetBits := BigToCompact(newTarget)
|
||||
log.Debugf("Difficulty retarget at block height %d", lastNode.height+1)
|
||||
log.Debugf("Old target %08x (%064x)", lastNode.bits, oldTarget)
|
||||
log.Debugf("Difficulty retarget at block height %d", lastNode.Height()+1)
|
||||
log.Debugf("Old target %08x (%064x)", lastNode.Bits(), oldTarget)
|
||||
log.Debugf("New target %08x (%064x)", newTargetBits, CompactToBig(newTargetBits))
|
||||
log.Debugf("Actual timespan %v, adjusted timespan %v, target timespan %v",
|
||||
time.Duration(actualTimespan)*time.Second,
|
||||
time.Duration(adjustedTimespan)*time.Second,
|
||||
b.chainParams.TargetTimespan)
|
||||
c.ChainParams().TargetTimespan)
|
||||
|
||||
return newTargetBits, nil
|
||||
}
|
||||
@ -315,7 +314,7 @@ func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode,
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) CalcNextRequiredDifficulty(timestamp time.Time) (uint32, error) {
|
||||
b.chainLock.Lock()
|
||||
difficulty, err := b.calcNextRequiredDifficulty(b.bestChain.Tip(), timestamp)
|
||||
difficulty, err := calcNextRequiredDifficulty(b.bestChain.Tip(), timestamp, b)
|
||||
b.chainLock.Unlock()
|
||||
return difficulty, err
|
||||
}
|
||||
|
55
blockchain/interfaces.go
Normal file
55
blockchain/interfaces.go
Normal file
@ -0,0 +1,55 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
// ChainCtx is an interface that abstracts away blockchain parameters.
|
||||
type ChainCtx interface {
|
||||
// ChainParams returns the chain's configured chaincfg.Params.
|
||||
ChainParams() *chaincfg.Params
|
||||
|
||||
// BlocksPerRetarget returns the number of blocks before retargeting
|
||||
// occurs.
|
||||
BlocksPerRetarget() int32
|
||||
|
||||
// MinRetargetTimespan returns the minimum amount of time to use in the
|
||||
// difficulty calculation.
|
||||
MinRetargetTimespan() int64
|
||||
|
||||
// MaxRetargetTimespan returns the maximum amount of time to use in the
|
||||
// difficulty calculation.
|
||||
MaxRetargetTimespan() int64
|
||||
|
||||
// VerifyCheckpoint returns whether the passed height and hash match
|
||||
// the checkpoint data. Not all instances of VerifyCheckpoint will use
|
||||
// this function for validation.
|
||||
VerifyCheckpoint(height int32, hash *chainhash.Hash) bool
|
||||
|
||||
// FindPreviousCheckpoint returns the most recent checkpoint that we
|
||||
// have validated. Not all instances of FindPreviousCheckpoint will use
|
||||
// this function for validation.
|
||||
FindPreviousCheckpoint() (HeaderCtx, error)
|
||||
}
|
||||
|
||||
// HeaderCtx is an interface that describes information about a block. This is
|
||||
// used so that external libraries can provide their own context (the header's
|
||||
// parent, bits, etc.) when attempting to contextually validate a header.
|
||||
type HeaderCtx interface {
|
||||
// Height returns the header's height.
|
||||
Height() int32
|
||||
|
||||
// Bits returns the header's bits.
|
||||
Bits() uint32
|
||||
|
||||
// Timestamp returns the header's timestamp.
|
||||
Timestamp() int64
|
||||
|
||||
// Parent returns the header's parent.
|
||||
Parent() HeaderCtx
|
||||
|
||||
// RelativeAncestorCtx returns the header's ancestor that is distance
|
||||
// blocks before it in the chain.
|
||||
RelativeAncestorCtx(distance int32) HeaderCtx
|
||||
}
|
@ -153,7 +153,7 @@ func (b *BlockChain) PastMedianTime(blockHeader *wire.BlockHeader) (time.Time, e
|
||||
|
||||
blockNode := newBlockNode(blockHeader, prevNode)
|
||||
|
||||
return blockNode.CalcPastMedianTime(), nil
|
||||
return CalcPastMedianTime(blockNode), nil
|
||||
}
|
||||
|
||||
// thresholdStateTransition given a state, a previous node, and a toeholds
|
||||
|
@ -421,13 +421,15 @@ func CountP2SHSigOps(tx *btcutil.Tx, isCoinBaseTx bool, utxoView *UtxoViewpoint)
|
||||
return totalSigOps, nil
|
||||
}
|
||||
|
||||
// checkBlockHeaderSanity performs some preliminary checks on a block header to
|
||||
// CheckBlockHeaderSanity performs some preliminary checks on a block header to
|
||||
// ensure it is sane before continuing with processing. These checks are
|
||||
// context free.
|
||||
//
|
||||
// The flags do not modify the behavior of this function directly, however they
|
||||
// are needed to pass along to checkProofOfWork.
|
||||
func checkBlockHeaderSanity(header *wire.BlockHeader, powLimit *big.Int, timeSource MedianTimeSource, flags BehaviorFlags) error {
|
||||
func CheckBlockHeaderSanity(header *wire.BlockHeader, powLimit *big.Int,
|
||||
timeSource MedianTimeSource, flags BehaviorFlags) error {
|
||||
|
||||
// Ensure the proof of work bits in the block header is in min/max range
|
||||
// and the block hash is less than the target value described by the
|
||||
// bits.
|
||||
@ -467,7 +469,7 @@ func checkBlockHeaderSanity(header *wire.BlockHeader, powLimit *big.Int, timeSou
|
||||
func checkBlockSanity(block *btcutil.Block, powLimit *big.Int, timeSource MedianTimeSource, flags BehaviorFlags) error {
|
||||
msgBlock := block.MsgBlock()
|
||||
header := &msgBlock.Header
|
||||
err := checkBlockHeaderSanity(header, powLimit, timeSource, flags)
|
||||
err := CheckBlockHeaderSanity(header, powLimit, timeSource, flags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -633,22 +635,30 @@ func checkSerializedHeight(coinbaseTx *btcutil.Tx, wantHeight int32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkBlockHeaderContext performs several validation checks on the block header
|
||||
// CheckBlockHeaderContext performs several validation checks on the block header
|
||||
// which depend on its position within the block chain.
|
||||
//
|
||||
// The flags modify the behavior of this function as follows:
|
||||
// - BFFastAdd: All checks except those involving comparing the header against
|
||||
// the checkpoints are not performed.
|
||||
//
|
||||
// The skipCheckpoint boolean is used so that libraries can skip the checkpoint
|
||||
// sanity checks.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes).
|
||||
func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, prevNode *blockNode, flags BehaviorFlags) error {
|
||||
// NOTE: Ignore the above lock requirement if this function is not passed a
|
||||
// *Blockchain instance as the ChainCtx argument.
|
||||
func CheckBlockHeaderContext(header *wire.BlockHeader, prevNode HeaderCtx,
|
||||
flags BehaviorFlags, c ChainCtx, skipCheckpoint bool) error {
|
||||
|
||||
fastAdd := flags&BFFastAdd == BFFastAdd
|
||||
if !fastAdd {
|
||||
// Ensure the difficulty specified in the block header matches
|
||||
// the calculated difficulty based on the previous block and
|
||||
// difficulty retarget rules.
|
||||
expectedDifficulty, err := b.calcNextRequiredDifficulty(prevNode,
|
||||
header.Timestamp)
|
||||
expectedDifficulty, err := calcNextRequiredDifficulty(
|
||||
prevNode, header.Timestamp, c,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -661,7 +671,7 @@ func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, prevNode
|
||||
|
||||
// Ensure the timestamp for the block header is after the
|
||||
// median time of the last several blocks (medianTimeBlocks).
|
||||
medianTime := prevNode.CalcPastMedianTime()
|
||||
medianTime := CalcPastMedianTime(prevNode)
|
||||
if !header.Timestamp.After(medianTime) {
|
||||
str := "block timestamp of %v is not after expected %v"
|
||||
str = fmt.Sprintf(str, header.Timestamp, medianTime)
|
||||
@ -671,11 +681,30 @@ func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, prevNode
|
||||
|
||||
// The height of this block is one more than the referenced previous
|
||||
// block.
|
||||
blockHeight := prevNode.height + 1
|
||||
blockHeight := prevNode.Height() + 1
|
||||
|
||||
// Reject outdated block versions once a majority of the network
|
||||
// has upgraded. These were originally voted on by BIP0034,
|
||||
// BIP0065, and BIP0066.
|
||||
params := c.ChainParams()
|
||||
if header.Version < 2 && blockHeight >= params.BIP0034Height ||
|
||||
header.Version < 3 && blockHeight >= params.BIP0066Height ||
|
||||
header.Version < 4 && blockHeight >= params.BIP0065Height {
|
||||
|
||||
str := "new blocks with version %d are no longer valid"
|
||||
str = fmt.Sprintf(str, header.Version)
|
||||
return ruleError(ErrBlockVersionTooOld, str)
|
||||
}
|
||||
|
||||
if skipCheckpoint {
|
||||
// If the caller wants us to skip the checkpoint checks, we'll
|
||||
// return early.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ensure chain matches up to predetermined checkpoints.
|
||||
blockHash := header.BlockHash()
|
||||
if !b.verifyCheckpoint(blockHeight, &blockHash) {
|
||||
if !c.VerifyCheckpoint(blockHeight, &blockHash) {
|
||||
str := fmt.Sprintf("block at height %d does not match "+
|
||||
"checkpoint hash", blockHeight)
|
||||
return ruleError(ErrBadCheckpoint, str)
|
||||
@ -685,30 +714,17 @@ func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, prevNode
|
||||
// chain before it. This prevents storage of new, otherwise valid,
|
||||
// blocks which build off of old blocks that are likely at a much easier
|
||||
// difficulty and therefore could be used to waste cache and disk space.
|
||||
checkpointNode, err := b.findPreviousCheckpoint()
|
||||
checkpointNode, err := c.FindPreviousCheckpoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if checkpointNode != nil && blockHeight < checkpointNode.height {
|
||||
if checkpointNode != nil && blockHeight < checkpointNode.Height() {
|
||||
str := fmt.Sprintf("block at height %d forks the main chain "+
|
||||
"before the previous checkpoint at height %d",
|
||||
blockHeight, checkpointNode.height)
|
||||
blockHeight, checkpointNode.Height())
|
||||
return ruleError(ErrForkTooOld, str)
|
||||
}
|
||||
|
||||
// Reject outdated block versions once a majority of the network
|
||||
// has upgraded. These were originally voted on by BIP0034,
|
||||
// BIP0065, and BIP0066.
|
||||
params := b.chainParams
|
||||
if header.Version < 2 && blockHeight >= params.BIP0034Height ||
|
||||
header.Version < 3 && blockHeight >= params.BIP0066Height ||
|
||||
header.Version < 4 && blockHeight >= params.BIP0065Height {
|
||||
|
||||
str := "new blocks with version %d are no longer valid"
|
||||
str = fmt.Sprintf(str, header.Version)
|
||||
return ruleError(ErrBlockVersionTooOld, str)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -726,7 +742,7 @@ func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, prevNode
|
||||
func (b *BlockChain) checkBlockContext(block *btcutil.Block, prevNode *blockNode, flags BehaviorFlags) error {
|
||||
// Perform all block header related validation checks.
|
||||
header := &block.MsgBlock().Header
|
||||
err := b.checkBlockHeaderContext(header, prevNode, flags)
|
||||
err := CheckBlockHeaderContext(header, prevNode, flags, b, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -746,7 +762,7 @@ func (b *BlockChain) checkBlockContext(block *btcutil.Block, prevNode *blockNode
|
||||
// timestamps for all lock-time based checks.
|
||||
blockTime := header.Timestamp
|
||||
if csvState == ThresholdActive {
|
||||
blockTime = prevNode.CalcPastMedianTime()
|
||||
blockTime = CalcPastMedianTime(prevNode)
|
||||
}
|
||||
|
||||
// The height of this block is one more than the referenced
|
||||
@ -1186,7 +1202,7 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block, vi
|
||||
|
||||
// We obtain the MTP of the *previous* block in order to
|
||||
// determine if transactions in the current block are final.
|
||||
medianTime := node.parent.CalcPastMedianTime()
|
||||
medianTime := CalcPastMedianTime(node.parent)
|
||||
|
||||
// Additionally, if the CSV soft-fork package is now active,
|
||||
// then we also enforce the relative sequence number based
|
||||
@ -1288,3 +1304,68 @@ func (b *BlockChain) CheckConnectBlockTemplate(block *btcutil.Block) error {
|
||||
newNode := newBlockNode(&header, tip)
|
||||
return b.checkConnectBlock(newNode, block, view, nil)
|
||||
}
|
||||
|
||||
// ChainParams returns the Blockchain's configured chaincfg.Params.
|
||||
//
|
||||
// NOTE: Part of the ChainCtx interface.
|
||||
func (b *BlockChain) ChainParams() *chaincfg.Params {
|
||||
return b.chainParams
|
||||
}
|
||||
|
||||
// BlocksPerRetarget returns the number of blocks before retargeting occurs.
|
||||
//
|
||||
// NOTE: Part of the ChainCtx interface.
|
||||
func (b *BlockChain) BlocksPerRetarget() int32 {
|
||||
return b.blocksPerRetarget
|
||||
}
|
||||
|
||||
// MinRetargetTimespan returns the minimum amount of time to use in the
|
||||
// difficulty calculation.
|
||||
//
|
||||
// NOTE: Part of the ChainCtx interface.
|
||||
func (b *BlockChain) MinRetargetTimespan() int64 {
|
||||
return b.minRetargetTimespan
|
||||
}
|
||||
|
||||
// MaxRetargetTimespan returns the maximum amount of time to use in the
|
||||
// difficulty calculation.
|
||||
//
|
||||
// NOTE: Part of the ChainCtx interface.
|
||||
func (b *BlockChain) MaxRetargetTimespan() int64 {
|
||||
return b.maxRetargetTimespan
|
||||
}
|
||||
|
||||
// VerifyCheckpoint checks that the height and hash match the stored
|
||||
// checkpoints.
|
||||
//
|
||||
// NOTE: Part of the ChainCtx interface.
|
||||
func (b *BlockChain) VerifyCheckpoint(height int32,
|
||||
hash *chainhash.Hash) bool {
|
||||
|
||||
return b.verifyCheckpoint(height, hash)
|
||||
}
|
||||
|
||||
// FindPreviousCheckpoint finds the checkpoint we've encountered during
|
||||
// validation.
|
||||
//
|
||||
// NOTE: Part of the ChainCtx interface.
|
||||
func (b *BlockChain) FindPreviousCheckpoint() (HeaderCtx, error) {
|
||||
checkpoint, err := b.findPreviousCheckpoint()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if checkpoint == nil {
|
||||
// This check is necessary because if we just return the nil
|
||||
// blockNode as a HeaderCtx, a caller performing a nil-check
|
||||
// will fail. This is a quirk of go where a nil value stored in
|
||||
// an interface is different from the actual nil interface.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return checkpoint, err
|
||||
}
|
||||
|
||||
// A compile-time assertion to ensure BlockChain implements the ChainCtx
|
||||
// interface.
|
||||
var _ ChainCtx = (*BlockChain)(nil)
|
||||
|
Loading…
Reference in New Issue
Block a user