2015-08-26 06:03:18 +02:00
|
|
|
// Copyright (c) 2013-2016 The btcsuite developers
|
2013-07-18 16:49:28 +02:00
|
|
|
// Use of this source code is governed by an ISC
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2015-01-30 21:54:30 +01:00
|
|
|
package blockchain
|
2013-07-18 16:49:28 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2014-07-02 18:04:59 +02:00
|
|
|
|
2015-02-06 06:18:27 +01:00
|
|
|
"github.com/btcsuite/btcd/chaincfg"
|
2016-08-08 21:04:33 +02:00
|
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
2015-08-26 11:54:55 +02:00
|
|
|
"github.com/btcsuite/btcd/database"
|
2015-01-30 19:08:47 +01:00
|
|
|
"github.com/btcsuite/btcd/txscript"
|
2015-01-15 17:23:47 +01:00
|
|
|
"github.com/btcsuite/btcutil"
|
2013-07-18 16:49:28 +02:00
|
|
|
)
|
|
|
|
|
2013-07-27 23:45:59 +02:00
|
|
|
// CheckpointConfirmations is the number of blocks before the end of the current
|
|
|
|
// best block chain that a good checkpoint candidate must be.
|
2013-07-22 10:20:00 +02:00
|
|
|
const CheckpointConfirmations = 2016
|
2013-07-18 16:49:28 +02:00
|
|
|
|
2016-08-08 21:04:33 +02:00
|
|
|
// newHashFromStr converts the passed big-endian hex string into a
|
|
|
|
// chainhash.Hash. It only differs from the one available in chainhash in that
|
2013-07-18 16:49:28 +02:00
|
|
|
// it ignores the error since it will only (and must only) be called with
|
|
|
|
// hard-coded, and therefore known good, hashes.
|
2016-08-08 21:04:33 +02:00
|
|
|
func newHashFromStr(hexStr string) *chainhash.Hash {
|
|
|
|
hash, _ := chainhash.NewHashFromStr(hexStr)
|
|
|
|
return hash
|
2013-07-18 16:49:28 +02:00
|
|
|
}
|
|
|
|
|
2014-02-01 02:51:24 +01:00
|
|
|
// Checkpoints returns a slice of checkpoints (regardless of whether they are
|
2017-01-18 23:58:38 +01:00
|
|
|
// already known). When there are no checkpoints for the chain, it will return
|
|
|
|
// nil.
|
2015-08-26 06:03:18 +02:00
|
|
|
//
|
|
|
|
// This function is safe for concurrent access.
|
2015-02-06 06:18:27 +01:00
|
|
|
func (b *BlockChain) Checkpoints() []chaincfg.Checkpoint {
|
2016-12-02 00:54:41 +01:00
|
|
|
return b.checkpoints
|
2014-02-01 02:51:24 +01:00
|
|
|
}
|
|
|
|
|
2016-12-02 00:54:41 +01:00
|
|
|
// HasCheckpoints returns whether this BlockChain has checkpoints defined.
|
2015-08-26 06:03:18 +02:00
|
|
|
//
|
2016-12-02 00:54:41 +01:00
|
|
|
// This function is safe for concurrent access.
|
|
|
|
func (b *BlockChain) HasCheckpoints() bool {
|
|
|
|
return len(b.checkpoints) > 0
|
2013-07-18 16:49:28 +02:00
|
|
|
}
|
|
|
|
|
2017-01-18 23:58:38 +01:00
|
|
|
// LatestCheckpoint returns the most recent checkpoint (regardless of whether it
|
|
|
|
// is already known). When there are no defined checkpoints for the active chain
|
2016-12-02 00:54:41 +01:00
|
|
|
// instance, it will return nil.
|
2015-08-26 06:03:18 +02:00
|
|
|
//
|
|
|
|
// This function is safe for concurrent access.
|
|
|
|
func (b *BlockChain) LatestCheckpoint() *chaincfg.Checkpoint {
|
2016-12-02 00:54:41 +01:00
|
|
|
if !b.HasCheckpoints() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return &b.checkpoints[len(b.checkpoints)-1]
|
2015-08-26 06:03:18 +02:00
|
|
|
}
|
|
|
|
|
2013-07-18 16:49:28 +02:00
|
|
|
// verifyCheckpoint returns whether the passed block height and hash combination
|
2017-01-18 23:58:38 +01:00
|
|
|
// match the checkpoint data. It also returns true if there is no checkpoint
|
2016-12-02 00:54:41 +01:00
|
|
|
// data for the passed block height.
|
2016-08-08 21:04:33 +02:00
|
|
|
func (b *BlockChain) verifyCheckpoint(height int32, hash *chainhash.Hash) bool {
|
2016-12-02 00:54:41 +01:00
|
|
|
if !b.HasCheckpoints() {
|
2013-07-18 16:49:28 +02:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Nothing to check if there is no checkpoint data for the block height.
|
2014-05-26 17:27:50 +02:00
|
|
|
checkpoint, exists := b.checkpointsByHeight[height]
|
2013-07-18 16:49:28 +02:00
|
|
|
if !exists {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2014-01-17 07:07:02 +01:00
|
|
|
if !checkpoint.Hash.IsEqual(hash) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2014-01-22 19:17:41 +01:00
|
|
|
log.Infof("Verified checkpoint at height %d/block %s", checkpoint.Height,
|
2014-01-17 07:07:02 +01:00
|
|
|
checkpoint.Hash)
|
|
|
|
return true
|
2013-07-18 16:49:28 +02:00
|
|
|
}
|
|
|
|
|
2014-02-21 22:03:44 +01:00
|
|
|
// findPreviousCheckpoint finds the most recent checkpoint that is already
|
2013-07-18 16:49:28 +02:00
|
|
|
// available in the downloaded portion of the block chain and returns the
|
2017-02-04 04:18:39 +01:00
|
|
|
// associated block node. It returns nil if a checkpoint can't be found (this
|
|
|
|
// should really only happen for blocks before the first checkpoint).
|
2015-08-26 06:03:18 +02:00
|
|
|
//
|
|
|
|
// This function MUST be called with the chain lock held (for reads).
|
2017-02-04 04:18:39 +01:00
|
|
|
func (b *BlockChain) findPreviousCheckpoint() (*blockNode, error) {
|
2016-12-02 00:54:41 +01:00
|
|
|
if !b.HasCheckpoints() {
|
2013-07-18 16:49:28 +02:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2014-01-17 07:07:02 +01:00
|
|
|
// Perform the initial search to find and cache the latest known
|
|
|
|
// checkpoint if the best chain is not known yet or we haven't already
|
|
|
|
// previously searched.
|
2017-02-04 04:18:39 +01:00
|
|
|
checkpoints := b.checkpoints
|
|
|
|
numCheckpoints := len(checkpoints)
|
|
|
|
if b.checkpointNode == nil && b.nextCheckpoint == nil {
|
2014-01-17 07:07:02 +01:00
|
|
|
// Loop backwards through the available checkpoints to find one
|
2015-08-26 06:03:18 +02:00
|
|
|
// that is already available.
|
2017-02-04 04:18:39 +01:00
|
|
|
for i := numCheckpoints - 1; i >= 0; i-- {
|
|
|
|
node := b.index.LookupNode(checkpoints[i].Hash)
|
|
|
|
if node == nil || !node.inMainChain {
|
|
|
|
continue
|
2013-07-18 16:49:28 +02:00
|
|
|
}
|
2017-02-04 04:18:39 +01:00
|
|
|
|
|
|
|
// Checkpoint found. Cache it for future lookups and
|
|
|
|
// set the next expected checkpoint accordingly.
|
|
|
|
b.checkpointNode = node
|
|
|
|
if i < numCheckpoints-1 {
|
|
|
|
b.nextCheckpoint = &checkpoints[i+1]
|
|
|
|
}
|
|
|
|
return b.checkpointNode, nil
|
2013-07-18 16:49:28 +02:00
|
|
|
}
|
2014-01-17 07:07:02 +01:00
|
|
|
|
|
|
|
// No known latest checkpoint. This will only happen on blocks
|
|
|
|
// before the first known checkpoint. So, set the next expected
|
|
|
|
// checkpoint to the first checkpoint and return the fact there
|
|
|
|
// is no latest known checkpoint block.
|
2017-02-04 04:18:39 +01:00
|
|
|
b.nextCheckpoint = &checkpoints[0]
|
|
|
|
return nil, nil
|
2014-01-17 07:07:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// At this point we've already searched for the latest known checkpoint,
|
|
|
|
// so when there is no next checkpoint, the current checkpoint lockin
|
|
|
|
// will always be the latest known checkpoint.
|
|
|
|
if b.nextCheckpoint == nil {
|
2017-02-04 04:18:39 +01:00
|
|
|
return b.checkpointNode, nil
|
2013-07-18 16:49:28 +02:00
|
|
|
}
|
2014-01-17 07:07:02 +01:00
|
|
|
|
|
|
|
// When there is a next checkpoint and the height of the current best
|
|
|
|
// chain does not exceed it, the current checkpoint lockin is still
|
|
|
|
// the latest known checkpoint.
|
2015-08-26 06:03:18 +02:00
|
|
|
if b.bestNode.height < b.nextCheckpoint.Height {
|
2017-02-04 04:18:39 +01:00
|
|
|
return b.checkpointNode, nil
|
2014-01-17 07:07:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// We've reached or exceeded the next checkpoint height. Note that
|
|
|
|
// once a checkpoint lockin has been reached, forks are prevented from
|
|
|
|
// any blocks before the checkpoint, so we don't have to worry about the
|
|
|
|
// checkpoint going away out from under us due to a chain reorganize.
|
|
|
|
|
2017-02-04 04:18:39 +01:00
|
|
|
// Cache the latest known checkpoint for future lookups. Note that if
|
|
|
|
// this lookup fails something is very wrong since the chain has already
|
|
|
|
// passed the checkpoint which was verified as accurate before inserting
|
|
|
|
// it.
|
|
|
|
checkpointNode := b.index.LookupNode(b.nextCheckpoint.Hash)
|
|
|
|
if checkpointNode == nil {
|
|
|
|
return nil, AssertError(fmt.Sprintf("findPreviousCheckpoint "+
|
|
|
|
"failed lookup of known good block node %s",
|
|
|
|
b.nextCheckpoint.Hash))
|
2014-01-17 07:07:02 +01:00
|
|
|
}
|
2017-02-04 04:18:39 +01:00
|
|
|
b.checkpointNode = checkpointNode
|
2014-01-17 07:07:02 +01:00
|
|
|
|
|
|
|
// Set the next expected checkpoint.
|
|
|
|
checkpointIndex := -1
|
|
|
|
for i := numCheckpoints - 1; i >= 0; i-- {
|
|
|
|
if checkpoints[i].Hash.IsEqual(b.nextCheckpoint.Hash) {
|
|
|
|
checkpointIndex = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
b.nextCheckpoint = nil
|
|
|
|
if checkpointIndex != -1 && checkpointIndex < numCheckpoints-1 {
|
|
|
|
b.nextCheckpoint = &checkpoints[checkpointIndex+1]
|
|
|
|
}
|
|
|
|
|
2017-02-04 04:18:39 +01:00
|
|
|
return b.checkpointNode, nil
|
2013-07-18 16:49:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// isNonstandardTransaction determines whether a transaction contains any
|
|
|
|
// scripts which are not one of the standard types.
|
2013-10-28 21:17:53 +01:00
|
|
|
func isNonstandardTransaction(tx *btcutil.Tx) bool {
|
2013-07-18 16:49:28 +02:00
|
|
|
// Check all of the output public key scripts for non-standard scripts.
|
2013-10-28 21:17:53 +01:00
|
|
|
for _, txOut := range tx.MsgTx().TxOut {
|
2015-01-30 19:08:47 +01:00
|
|
|
scriptClass := txscript.GetScriptClass(txOut.PkScript)
|
|
|
|
if scriptClass == txscript.NonStandardTy {
|
2013-07-18 16:49:28 +02:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsCheckpointCandidate returns whether or not the passed block is a good
|
|
|
|
// checkpoint candidate.
|
|
|
|
//
|
|
|
|
// The factors used to determine a good checkpoint are:
|
2013-07-19 17:08:09 +02:00
|
|
|
// - The block must be in the main chain
|
|
|
|
// - The block must be at least 'CheckpointConfirmations' blocks prior to the
|
|
|
|
// current end of the main chain
|
|
|
|
// - The timestamps for the blocks before and after the checkpoint must have
|
|
|
|
// timestamps which are also before and after the checkpoint, respectively
|
|
|
|
// (due to the median time allowance this is not always the case)
|
|
|
|
// - The block must not contain any strange transaction such as those with
|
|
|
|
// nonstandard scripts
|
2014-05-26 17:27:50 +02:00
|
|
|
//
|
|
|
|
// The intent is that candidates are reviewed by a developer to make the final
|
|
|
|
// decision and then manually added to the list of checkpoints for a network.
|
2015-08-26 06:03:18 +02:00
|
|
|
//
|
|
|
|
// This function is safe for concurrent access.
|
2013-07-18 16:49:28 +02:00
|
|
|
func (b *BlockChain) IsCheckpointCandidate(block *btcutil.Block) (bool, error) {
|
2015-08-26 06:03:18 +02:00
|
|
|
b.chainLock.RLock()
|
|
|
|
defer b.chainLock.RUnlock()
|
|
|
|
|
|
|
|
var isCandidate bool
|
|
|
|
err := b.db.View(func(dbTx database.Tx) error {
|
|
|
|
// A checkpoint must be in the main chain.
|
2016-08-08 21:04:33 +02:00
|
|
|
blockHeight, err := dbFetchHeightByHash(dbTx, block.Hash())
|
2015-08-26 06:03:18 +02:00
|
|
|
if err != nil {
|
|
|
|
// Only return an error if it's not due to the block not
|
|
|
|
// being in the main chain.
|
|
|
|
if !isNotInMainChainErr(err) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2013-07-18 16:49:28 +02:00
|
|
|
|
2015-08-26 06:03:18 +02:00
|
|
|
// Ensure the height of the passed block and the entry for the
|
|
|
|
// block in the main chain match. This should always be the
|
|
|
|
// case unless the caller provided an invalid block.
|
|
|
|
if blockHeight != block.Height() {
|
|
|
|
return fmt.Errorf("passed block height of %d does not "+
|
|
|
|
"match the main chain height of %d",
|
|
|
|
block.Height(), blockHeight)
|
|
|
|
}
|
2013-07-18 16:49:28 +02:00
|
|
|
|
2015-08-26 06:03:18 +02:00
|
|
|
// A checkpoint must be at least CheckpointConfirmations blocks
|
|
|
|
// before the end of the main chain.
|
|
|
|
mainChainHeight := b.bestNode.height
|
|
|
|
if blockHeight > (mainChainHeight - CheckpointConfirmations) {
|
|
|
|
return nil
|
|
|
|
}
|
2013-07-18 16:49:28 +02:00
|
|
|
|
2015-08-26 06:03:18 +02:00
|
|
|
// Get the previous block header.
|
|
|
|
prevHash := &block.MsgBlock().Header.PrevBlock
|
|
|
|
prevHeader, err := dbFetchHeaderByHash(dbTx, prevHash)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2013-07-18 16:49:28 +02:00
|
|
|
|
2015-08-26 06:03:18 +02:00
|
|
|
// Get the next block header.
|
|
|
|
nextHeader, err := dbFetchHeaderByHeight(dbTx, blockHeight+1)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2013-07-18 16:49:28 +02:00
|
|
|
|
2015-08-26 06:03:18 +02:00
|
|
|
// A checkpoint must have timestamps for the block and the
|
|
|
|
// blocks on either side of it in order (due to the median time
|
|
|
|
// allowance this is not always the case).
|
|
|
|
prevTime := prevHeader.Timestamp
|
|
|
|
curTime := block.MsgBlock().Header.Timestamp
|
|
|
|
nextTime := nextHeader.Timestamp
|
|
|
|
if prevTime.After(curTime) || nextTime.Before(curTime) {
|
|
|
|
return nil
|
2013-07-18 16:49:28 +02:00
|
|
|
}
|
|
|
|
|
2015-08-26 06:03:18 +02:00
|
|
|
// A checkpoint must have transactions that only contain
|
|
|
|
// standard scripts.
|
|
|
|
for _, tx := range block.Transactions() {
|
|
|
|
if isNonstandardTransaction(tx) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// All of the checks passed, so the block is a candidate.
|
|
|
|
isCandidate = true
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return isCandidate, err
|
2013-07-18 16:49:28 +02:00
|
|
|
}
|