github.com/palcoin-project/palcd@v1.0.0/blockchain/process.go (about)

     1  // Copyright (c) 2013-2017 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package blockchain
     6  
     7  import (
     8  	"fmt"
     9  	"time"
    10  
    11  	"github.com/palcoin-project/palcd/chaincfg/chainhash"
    12  	"github.com/palcoin-project/palcd/database"
    13  	"github.com/palcoin-project/palcutil"
    14  )
    15  
    16  // BehaviorFlags is a bitmask defining tweaks to the normal behavior when
    17  // performing chain processing and consensus rules checks.
    18  type BehaviorFlags uint32
    19  
    20  const (
    21  	// BFFastAdd may be set to indicate that several checks can be avoided
    22  	// for the block since it is already known to fit into the chain due to
    23  	// already proving it correct links into the chain up to a known
    24  	// checkpoint.  This is primarily used for headers-first mode.
    25  	BFFastAdd BehaviorFlags = 1 << iota
    26  
    27  	// BFNoPoWCheck may be set to indicate the proof of work check which
    28  	// ensures a block hashes to a value less than the required target will
    29  	// not be performed.
    30  	BFNoPoWCheck
    31  
    32  	// BFNone is a convenience value to specifically indicate no flags.
    33  	BFNone BehaviorFlags = 0
    34  )
    35  
    36  // blockExists determines whether a block with the given hash exists either in
    37  // the main chain or any side chains.
    38  //
    39  // This function is safe for concurrent access.
    40  func (b *BlockChain) blockExists(hash *chainhash.Hash) (bool, error) {
    41  	// Check block index first (could be main chain or side chain blocks).
    42  	if b.index.HaveBlock(hash) {
    43  		return true, nil
    44  	}
    45  
    46  	// Check in the database.
    47  	var exists bool
    48  	err := b.db.View(func(dbTx database.Tx) error {
    49  		var err error
    50  		exists, err = dbTx.HasBlock(hash)
    51  		if err != nil || !exists {
    52  			return err
    53  		}
    54  
    55  		// Ignore side chain blocks in the database.  This is necessary
    56  		// because there is not currently any record of the associated
    57  		// block index data such as its block height, so it's not yet
    58  		// possible to efficiently load the block and do anything useful
    59  		// with it.
    60  		//
    61  		// Ultimately the entire block index should be serialized
    62  		// instead of only the current main chain so it can be consulted
    63  		// directly.
    64  		_, err = dbFetchHeightByHash(dbTx, hash)
    65  		if isNotInMainChainErr(err) {
    66  			exists = false
    67  			return nil
    68  		}
    69  		return err
    70  	})
    71  	return exists, err
    72  }
    73  
    74  // processOrphans determines if there are any orphans which depend on the passed
    75  // block hash (they are no longer orphans if true) and potentially accepts them.
    76  // It repeats the process for the newly accepted blocks (to detect further
    77  // orphans which may no longer be orphans) until there are no more.
    78  //
    79  // The flags do not modify the behavior of this function directly, however they
    80  // are needed to pass along to maybeAcceptBlock.
    81  //
    82  // This function MUST be called with the chain state lock held (for writes).
    83  func (b *BlockChain) processOrphans(hash *chainhash.Hash, flags BehaviorFlags) error {
    84  	// Start with processing at least the passed hash.  Leave a little room
    85  	// for additional orphan blocks that need to be processed without
    86  	// needing to grow the array in the common case.
    87  	processHashes := make([]*chainhash.Hash, 0, 10)
    88  	processHashes = append(processHashes, hash)
    89  	for len(processHashes) > 0 {
    90  		// Pop the first hash to process from the slice.
    91  		processHash := processHashes[0]
    92  		processHashes[0] = nil // Prevent GC leak.
    93  		processHashes = processHashes[1:]
    94  
    95  		// Look up all orphans that are parented by the block we just
    96  		// accepted.  This will typically only be one, but it could
    97  		// be multiple if multiple blocks are mined and broadcast
    98  		// around the same time.  The one with the most proof of work
    99  		// will eventually win out.  An indexing for loop is
   100  		// intentionally used over a range here as range does not
   101  		// reevaluate the slice on each iteration nor does it adjust the
   102  		// index for the modified slice.
   103  		for i := 0; i < len(b.prevOrphans[*processHash]); i++ {
   104  			orphan := b.prevOrphans[*processHash][i]
   105  			if orphan == nil {
   106  				log.Warnf("Found a nil entry at index %d in the "+
   107  					"orphan dependency list for block %v", i,
   108  					processHash)
   109  				continue
   110  			}
   111  
   112  			// Remove the orphan from the orphan pool.
   113  			orphanHash := orphan.block.Hash()
   114  			b.removeOrphanBlock(orphan)
   115  			i--
   116  
   117  			// Potentially accept the block into the block chain.
   118  			_, err := b.maybeAcceptBlock(orphan.block, flags)
   119  			if err != nil {
   120  				return err
   121  			}
   122  
   123  			// Add this block to the list of blocks to process so
   124  			// any orphan blocks that depend on this block are
   125  			// handled too.
   126  			processHashes = append(processHashes, orphanHash)
   127  		}
   128  	}
   129  	return nil
   130  }
   131  
   132  // ProcessBlock is the main workhorse for handling insertion of new blocks into
   133  // the block chain.  It includes functionality such as rejecting duplicate
   134  // blocks, ensuring blocks follow all rules, orphan handling, and insertion into
   135  // the block chain along with best chain selection and reorganization.
   136  //
   137  // When no errors occurred during processing, the first return value indicates
   138  // whether or not the block is on the main chain and the second indicates
   139  // whether or not the block is an orphan.
   140  //
   141  // This function is safe for concurrent access.
   142  func (b *BlockChain) ProcessBlock(block *palcutil.Block, flags BehaviorFlags) (bool, bool, error) {
   143  	b.chainLock.Lock()
   144  	defer b.chainLock.Unlock()
   145  
   146  	fastAdd := flags&BFFastAdd == BFFastAdd
   147  
   148  	blockHash := block.Hash()
   149  	log.Tracef("Processing block %v", blockHash)
   150  
   151  	// The block must not already exist in the main chain or side chains.
   152  	exists, err := b.blockExists(blockHash)
   153  	if err != nil {
   154  		return false, false, err
   155  	}
   156  	if exists {
   157  		str := fmt.Sprintf("already have block %v", blockHash)
   158  		return false, false, ruleError(ErrDuplicateBlock, str)
   159  	}
   160  
   161  	// The block must not already exist as an orphan.
   162  	if _, exists := b.orphans[*blockHash]; exists {
   163  		str := fmt.Sprintf("already have block (orphan) %v", blockHash)
   164  		return false, false, ruleError(ErrDuplicateBlock, str)
   165  	}
   166  
   167  	// Perform preliminary sanity checks on the block and its transactions.
   168  	err = checkBlockSanity(block, b.chainParams.PowLimit, b.timeSource, flags)
   169  	if err != nil {
   170  		return false, false, err
   171  	}
   172  
   173  	// Find the previous checkpoint and perform some additional checks based
   174  	// on the checkpoint.  This provides a few nice properties such as
   175  	// preventing old side chain blocks before the last checkpoint,
   176  	// rejecting easy to mine, but otherwise bogus, blocks that could be
   177  	// used to eat memory, and ensuring expected (versus claimed) proof of
   178  	// work requirements since the previous checkpoint are met.
   179  	blockHeader := &block.MsgBlock().Header
   180  	checkpointNode, err := b.findPreviousCheckpoint()
   181  	if err != nil {
   182  		return false, false, err
   183  	}
   184  	if checkpointNode != nil {
   185  		// Ensure the block timestamp is after the checkpoint timestamp.
   186  		checkpointTime := time.Unix(checkpointNode.timestamp, 0)
   187  		if blockHeader.Timestamp.Before(checkpointTime) {
   188  			str := fmt.Sprintf("block %v has timestamp %v before "+
   189  				"last checkpoint timestamp %v", blockHash,
   190  				blockHeader.Timestamp, checkpointTime)
   191  			return false, false, ruleError(ErrCheckpointTimeTooOld, str)
   192  		}
   193  		if !fastAdd {
   194  			// Even though the checks prior to now have already ensured the
   195  			// proof of work exceeds the claimed amount, the claimed amount
   196  			// is a field in the block header which could be forged.  This
   197  			// check ensures the proof of work is at least the minimum
   198  			// expected based on elapsed time since the last checkpoint and
   199  			// maximum adjustment allowed by the retarget rules.
   200  			duration := blockHeader.Timestamp.Sub(checkpointTime)
   201  			requiredTarget := CompactToBig(b.calcEasiestDifficulty(
   202  				checkpointNode.bits, duration))
   203  			currentTarget := CompactToBig(blockHeader.Bits)
   204  			if currentTarget.Cmp(requiredTarget) > 0 {
   205  				str := fmt.Sprintf("block target difficulty of %064x "+
   206  					"is too low when compared to the previous "+
   207  					"checkpoint", currentTarget)
   208  				return false, false, ruleError(ErrDifficultyTooLow, str)
   209  			}
   210  		}
   211  	}
   212  
   213  	// Handle orphan blocks.
   214  	prevHash := &blockHeader.PrevBlock
   215  	prevHashExists, err := b.blockExists(prevHash)
   216  	if err != nil {
   217  		return false, false, err
   218  	}
   219  	if !prevHashExists {
   220  		log.Infof("Adding orphan block %v with parent %v", blockHash, prevHash)
   221  		b.addOrphanBlock(block)
   222  
   223  		return false, true, nil
   224  	}
   225  
   226  	// The block has passed all context independent checks and appears sane
   227  	// enough to potentially accept it into the block chain.
   228  	isMainChain, err := b.maybeAcceptBlock(block, flags)
   229  	if err != nil {
   230  		return false, false, err
   231  	}
   232  
   233  	// Accept any orphan blocks that depend on this block (they are
   234  	// no longer orphans) and repeat for those accepted blocks until
   235  	// there are no more.
   236  	err = b.processOrphans(blockHash, flags)
   237  	if err != nil {
   238  		return false, false, err
   239  	}
   240  
   241  	log.Debugf("Accepted block %v", blockHash)
   242  
   243  	return isMainChain, false, nil
   244  }