github.com/dashpay/godash@v0.0.0-20160726055534-e038a21e0e3d/blockchain/checkpoints.go (about)

     1  // Copyright (c) 2013-2016 The btcsuite developers
     2  // Copyright (c) 2016 The Dash developers
     3  // Use of this source code is governed by an ISC
     4  // license that can be found in the LICENSE file.
     5  
     6  package blockchain
     7  
     8  import (
     9  	"fmt"
    10  
    11  	"github.com/dashpay/godash/chaincfg"
    12  	"github.com/dashpay/godash/database"
    13  	"github.com/dashpay/godash/txscript"
    14  	"github.com/dashpay/godash/wire"
    15  	"github.com/dashpay/godashutil"
    16  )
    17  
    18  // CheckpointConfirmations is the number of blocks before the end of the current
    19  // best block chain that a good checkpoint candidate must be.
    20  const CheckpointConfirmations = 2016
    21  
    22  // newShaHashFromStr converts the passed big-endian hex string into a
    23  // wire.ShaHash.  It only differs from the one available in wire in that
    24  // it ignores the error since it will only (and must only) be called with
    25  // hard-coded, and therefore known good, hashes.
    26  func newShaHashFromStr(hexStr string) *wire.ShaHash {
    27  	sha, _ := wire.NewShaHashFromStr(hexStr)
    28  	return sha
    29  }
    30  
    31  // DisableCheckpoints provides a mechanism to disable validation against
    32  // checkpoints which you DO NOT want to do in production.  It is provided only
    33  // for debug purposes.
    34  //
    35  // This function is safe for concurrent access.
    36  func (b *BlockChain) DisableCheckpoints(disable bool) {
    37  	b.chainLock.Lock()
    38  	b.noCheckpoints = disable
    39  	b.chainLock.Unlock()
    40  }
    41  
    42  // Checkpoints returns a slice of checkpoints (regardless of whether they are
    43  // already known).  When checkpoints are disabled or there are no checkpoints
    44  // for the active network, it will return nil.
    45  //
    46  // This function is safe for concurrent access.
    47  func (b *BlockChain) Checkpoints() []chaincfg.Checkpoint {
    48  	b.chainLock.RLock()
    49  	defer b.chainLock.RUnlock()
    50  
    51  	if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 {
    52  		return nil
    53  	}
    54  
    55  	return b.chainParams.Checkpoints
    56  }
    57  
    58  // latestCheckpoint returns the most recent checkpoint (regardless of whether it
    59  // is already known).  When checkpoints are disabled or there are no checkpoints
    60  // for the active network, it will return nil.
    61  //
    62  // This function MUST be called with the chain state lock held (for reads).
    63  func (b *BlockChain) latestCheckpoint() *chaincfg.Checkpoint {
    64  	if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 {
    65  		return nil
    66  	}
    67  
    68  	checkpoints := b.chainParams.Checkpoints
    69  	return &checkpoints[len(checkpoints)-1]
    70  }
    71  
    72  // LatestCheckpoint returns the most recent checkpoint (regardless of whether it
    73  // is already known).  When checkpoints are disabled or there are no checkpoints
    74  // for the active network, it will return nil.
    75  //
    76  // This function is safe for concurrent access.
    77  func (b *BlockChain) LatestCheckpoint() *chaincfg.Checkpoint {
    78  	b.chainLock.RLock()
    79  	checkpoint := b.latestCheckpoint()
    80  	b.chainLock.RUnlock()
    81  	return checkpoint
    82  }
    83  
    84  // verifyCheckpoint returns whether the passed block height and hash combination
    85  // match the hard-coded checkpoint data.  It also returns true if there is no
    86  // checkpoint data for the passed block height.
    87  //
    88  // This function MUST be called with the chain lock held (for reads).
    89  func (b *BlockChain) verifyCheckpoint(height int32, hash *wire.ShaHash) bool {
    90  	if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 {
    91  		return true
    92  	}
    93  
    94  	// Nothing to check if there is no checkpoint data for the block height.
    95  	checkpoint, exists := b.checkpointsByHeight[height]
    96  	if !exists {
    97  		return true
    98  	}
    99  
   100  	if !checkpoint.Hash.IsEqual(hash) {
   101  		return false
   102  	}
   103  
   104  	log.Infof("Verified checkpoint at height %d/block %s", checkpoint.Height,
   105  		checkpoint.Hash)
   106  	return true
   107  }
   108  
   109  // findPreviousCheckpoint finds the most recent checkpoint that is already
   110  // available in the downloaded portion of the block chain and returns the
   111  // associated block.  It returns nil if a checkpoint can't be found (this should
   112  // really only happen for blocks before the first checkpoint).
   113  //
   114  // This function MUST be called with the chain lock held (for reads).
   115  func (b *BlockChain) findPreviousCheckpoint() (*godashutil.Block, error) {
   116  	if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 {
   117  		return nil, nil
   118  	}
   119  
   120  	// No checkpoints.
   121  	checkpoints := b.chainParams.Checkpoints
   122  	numCheckpoints := len(checkpoints)
   123  	if numCheckpoints == 0 {
   124  		return nil, nil
   125  	}
   126  
   127  	// Perform the initial search to find and cache the latest known
   128  	// checkpoint if the best chain is not known yet or we haven't already
   129  	// previously searched.
   130  	if b.checkpointBlock == nil && b.nextCheckpoint == nil {
   131  		// Loop backwards through the available checkpoints to find one
   132  		// that is already available.
   133  		checkpointIndex := -1
   134  		err := b.db.View(func(dbTx database.Tx) error {
   135  			for i := numCheckpoints - 1; i >= 0; i-- {
   136  				if dbMainChainHasBlock(dbTx, checkpoints[i].Hash) {
   137  					checkpointIndex = i
   138  					break
   139  				}
   140  			}
   141  			return nil
   142  		})
   143  		if err != nil {
   144  			return nil, err
   145  		}
   146  
   147  		// No known latest checkpoint.  This will only happen on blocks
   148  		// before the first known checkpoint.  So, set the next expected
   149  		// checkpoint to the first checkpoint and return the fact there
   150  		// is no latest known checkpoint block.
   151  		if checkpointIndex == -1 {
   152  			b.nextCheckpoint = &checkpoints[0]
   153  			return nil, nil
   154  		}
   155  
   156  		// Cache the latest known checkpoint block for future lookups.
   157  		checkpoint := checkpoints[checkpointIndex]
   158  		err = b.db.View(func(dbTx database.Tx) error {
   159  			block, err := dbFetchBlockByHash(dbTx, checkpoint.Hash)
   160  			if err != nil {
   161  				return err
   162  			}
   163  			b.checkpointBlock = block
   164  
   165  			// Set the next expected checkpoint block accordingly.
   166  			b.nextCheckpoint = nil
   167  			if checkpointIndex < numCheckpoints-1 {
   168  				b.nextCheckpoint = &checkpoints[checkpointIndex+1]
   169  			}
   170  
   171  			return nil
   172  		})
   173  		if err != nil {
   174  			return nil, err
   175  		}
   176  
   177  		return b.checkpointBlock, nil
   178  	}
   179  
   180  	// At this point we've already searched for the latest known checkpoint,
   181  	// so when there is no next checkpoint, the current checkpoint lockin
   182  	// will always be the latest known checkpoint.
   183  	if b.nextCheckpoint == nil {
   184  		return b.checkpointBlock, nil
   185  	}
   186  
   187  	// When there is a next checkpoint and the height of the current best
   188  	// chain does not exceed it, the current checkpoint lockin is still
   189  	// the latest known checkpoint.
   190  	if b.bestNode.height < b.nextCheckpoint.Height {
   191  		return b.checkpointBlock, nil
   192  	}
   193  
   194  	// We've reached or exceeded the next checkpoint height.  Note that
   195  	// once a checkpoint lockin has been reached, forks are prevented from
   196  	// any blocks before the checkpoint, so we don't have to worry about the
   197  	// checkpoint going away out from under us due to a chain reorganize.
   198  
   199  	// Cache the latest known checkpoint block for future lookups.  Note
   200  	// that if this lookup fails something is very wrong since the chain
   201  	// has already passed the checkpoint which was verified as accurate
   202  	// before inserting it.
   203  	err := b.db.View(func(tx database.Tx) error {
   204  		block, err := dbFetchBlockByHash(tx, b.nextCheckpoint.Hash)
   205  		if err != nil {
   206  			return err
   207  		}
   208  		b.checkpointBlock = block
   209  		return nil
   210  	})
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  
   215  	// Set the next expected checkpoint.
   216  	checkpointIndex := -1
   217  	for i := numCheckpoints - 1; i >= 0; i-- {
   218  		if checkpoints[i].Hash.IsEqual(b.nextCheckpoint.Hash) {
   219  			checkpointIndex = i
   220  			break
   221  		}
   222  	}
   223  	b.nextCheckpoint = nil
   224  	if checkpointIndex != -1 && checkpointIndex < numCheckpoints-1 {
   225  		b.nextCheckpoint = &checkpoints[checkpointIndex+1]
   226  	}
   227  
   228  	return b.checkpointBlock, nil
   229  }
   230  
   231  // isNonstandardTransaction determines whether a transaction contains any
   232  // scripts which are not one of the standard types.
   233  func isNonstandardTransaction(tx *godashutil.Tx) bool {
   234  	// Check all of the output public key scripts for non-standard scripts.
   235  	for _, txOut := range tx.MsgTx().TxOut {
   236  		scriptClass := txscript.GetScriptClass(txOut.PkScript)
   237  		if scriptClass == txscript.NonStandardTy {
   238  			return true
   239  		}
   240  	}
   241  	return false
   242  }
   243  
   244  // IsCheckpointCandidate returns whether or not the passed block is a good
   245  // checkpoint candidate.
   246  //
   247  // The factors used to determine a good checkpoint are:
   248  //  - The block must be in the main chain
   249  //  - The block must be at least 'CheckpointConfirmations' blocks prior to the
   250  //    current end of the main chain
   251  //  - The timestamps for the blocks before and after the checkpoint must have
   252  //    timestamps which are also before and after the checkpoint, respectively
   253  //    (due to the median time allowance this is not always the case)
   254  //  - The block must not contain any strange transaction such as those with
   255  //    nonstandard scripts
   256  //
   257  // The intent is that candidates are reviewed by a developer to make the final
   258  // decision and then manually added to the list of checkpoints for a network.
   259  //
   260  // This function is safe for concurrent access.
   261  func (b *BlockChain) IsCheckpointCandidate(block *godashutil.Block) (bool, error) {
   262  	b.chainLock.RLock()
   263  	defer b.chainLock.RUnlock()
   264  
   265  	// Checkpoints must be enabled.
   266  	if b.noCheckpoints {
   267  		return false, fmt.Errorf("checkpoints are disabled")
   268  	}
   269  
   270  	var isCandidate bool
   271  	err := b.db.View(func(dbTx database.Tx) error {
   272  		// A checkpoint must be in the main chain.
   273  		blockHeight, err := dbFetchHeightByHash(dbTx, block.Sha())
   274  		if err != nil {
   275  			// Only return an error if it's not due to the block not
   276  			// being in the main chain.
   277  			if !isNotInMainChainErr(err) {
   278  				return err
   279  			}
   280  			return nil
   281  		}
   282  
   283  		// Ensure the height of the passed block and the entry for the
   284  		// block in the main chain match.  This should always be the
   285  		// case unless the caller provided an invalid block.
   286  		if blockHeight != block.Height() {
   287  			return fmt.Errorf("passed block height of %d does not "+
   288  				"match the main chain height of %d",
   289  				block.Height(), blockHeight)
   290  		}
   291  
   292  		// A checkpoint must be at least CheckpointConfirmations blocks
   293  		// before the end of the main chain.
   294  		mainChainHeight := b.bestNode.height
   295  		if blockHeight > (mainChainHeight - CheckpointConfirmations) {
   296  			return nil
   297  		}
   298  
   299  		// Get the previous block header.
   300  		prevHash := &block.MsgBlock().Header.PrevBlock
   301  		prevHeader, err := dbFetchHeaderByHash(dbTx, prevHash)
   302  		if err != nil {
   303  			return err
   304  		}
   305  
   306  		// Get the next block header.
   307  		nextHeader, err := dbFetchHeaderByHeight(dbTx, blockHeight+1)
   308  		if err != nil {
   309  			return err
   310  		}
   311  
   312  		// A checkpoint must have timestamps for the block and the
   313  		// blocks on either side of it in order (due to the median time
   314  		// allowance this is not always the case).
   315  		prevTime := prevHeader.Timestamp
   316  		curTime := block.MsgBlock().Header.Timestamp
   317  		nextTime := nextHeader.Timestamp
   318  		if prevTime.After(curTime) || nextTime.Before(curTime) {
   319  			return nil
   320  		}
   321  
   322  		// A checkpoint must have transactions that only contain
   323  		// standard scripts.
   324  		for _, tx := range block.Transactions() {
   325  			if isNonstandardTransaction(tx) {
   326  				return nil
   327  			}
   328  		}
   329  
   330  		// All of the checks passed, so the block is a candidate.
   331  		isCandidate = true
   332  		return nil
   333  	})
   334  	return isCandidate, err
   335  }