github.com/BlockABC/godash@v0.0.0-20191112120524-f4aa3a32c566/blockchain/blocklocator.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  	"github.com/BlockABC/godash/database"
    10  	"github.com/BlockABC/godash/wire"
    11  )
    12  
    13  // BlockLocator is used to help locate a specific block.  The algorithm for
    14  // building the block locator is to add the hashes in reverse order until
    15  // the genesis block is reached.  In order to keep the list of locator hashes
    16  // to a reasonable number of entries, first the most recent previous 10 block
    17  // hashes are added, then the step is doubled each loop iteration to
    18  // exponentially decrease the number of hashes as a function of the distance
    19  // from the block being located.
    20  //
    21  // For example, assume you have a block chain with a side chain as depicted
    22  // below:
    23  // 	genesis -> 1 -> 2 -> ... -> 15 -> 16  -> 17  -> 18
    24  // 	                              \-> 16a -> 17a
    25  //
    26  // The block locator for block 17a would be the hashes of blocks:
    27  // [17a 16a 15 14 13 12 11 10 9 8 6 2 genesis]
    28  type BlockLocator []*wire.ShaHash
    29  
    30  // blockLocatorFromHash returns a block locator for the passed block hash.
    31  // See BlockLocator for details on the algotirhm used to create a block locator.
    32  //
    33  // In addition to the general algorithm referenced above, there are a couple of
    34  // special cases which are handled:
    35  //
    36  //  - If the genesis hash is passed, there are no previous hashes to add and
    37  //    therefore the block locator will only consist of the genesis hash
    38  //  - If the passed hash is not currently known, the block locator will only
    39  //    consist of the passed hash
    40  //
    41  // This function MUST be called with the chain state lock held (for reads).
    42  func (b *BlockChain) blockLocatorFromHash(hash *wire.ShaHash) BlockLocator {
    43  	// The locator contains the requested hash at the very least.
    44  	locator := make(BlockLocator, 0, wire.MaxBlockLocatorsPerMsg)
    45  	locator = append(locator, hash)
    46  
    47  	// Nothing more to do if a locator for the genesis hash was requested.
    48  	if hash.IsEqual(b.chainParams.GenesisHash) {
    49  		return locator
    50  	}
    51  
    52  	// Attempt to find the height of the block that corresponds to the
    53  	// passed hash, and if it's on a side chain, also find the height at
    54  	// which it forks from the main chain.
    55  	blockHeight := int32(-1)
    56  	forkHeight := int32(-1)
    57  	node, exists := b.index[*hash]
    58  	if !exists {
    59  		// Try to look up the height for passed block hash.  Assume an
    60  		// error means it doesn't exist and just return the locator for
    61  		// the block itself.
    62  		var height int32
    63  		err := b.db.View(func(dbTx database.Tx) error {
    64  			var err error
    65  			height, err = dbFetchHeightByHash(dbTx, hash)
    66  			return err
    67  		})
    68  		if err != nil {
    69  			return locator
    70  		}
    71  
    72  		blockHeight = height
    73  	} else {
    74  		blockHeight = node.height
    75  
    76  		// Find the height at which this node forks from the main chain
    77  		// if the node is on a side chain.
    78  		if !node.inMainChain {
    79  			for n := node; n.parent != nil; n = n.parent {
    80  				if n.inMainChain {
    81  					forkHeight = n.height
    82  					break
    83  				}
    84  			}
    85  		}
    86  	}
    87  
    88  	// Generate the block locators according to the algorithm described in
    89  	// in the BlockLocator comment and make sure to leave room for the final
    90  	// genesis hash.
    91  	//
    92  	// The error is intentionally ignored here since the only way the code
    93  	// could fail is if there is something wrong with the database which
    94  	// will be caught in short order anyways and it's also safe to ignore
    95  	// block locators.
    96  	_ = b.db.View(func(dbTx database.Tx) error {
    97  		iterNode := node
    98  		increment := int32(1)
    99  		for len(locator) < wire.MaxBlockLocatorsPerMsg-1 {
   100  			// Once there are 10 locators, exponentially increase
   101  			// the distance between each block locator.
   102  			if len(locator) > 10 {
   103  				increment *= 2
   104  			}
   105  			blockHeight -= increment
   106  			if blockHeight < 1 {
   107  				break
   108  			}
   109  
   110  			// As long as this is still on the side chain, walk
   111  			// backwards along the side chain nodes to each block
   112  			// height.
   113  			if forkHeight != -1 && blockHeight > forkHeight {
   114  				// Intentionally use parent field instead of the
   115  				// getPrevNodeFromNode function since we don't
   116  				// want to dynamically load nodes when building
   117  				// block locators.  Side chain blocks should
   118  				// always be in memory already, and if they
   119  				// aren't for some reason it's ok to skip them.
   120  				for iterNode != nil && blockHeight > iterNode.height {
   121  					iterNode = iterNode.parent
   122  				}
   123  				if iterNode != nil && iterNode.height == blockHeight {
   124  					locator = append(locator, iterNode.hash)
   125  				}
   126  				continue
   127  			}
   128  
   129  			// The desired block height is in the main chain, so
   130  			// look it up from the main chain database.
   131  			h, err := dbFetchHashByHeight(dbTx, blockHeight)
   132  			if err != nil {
   133  				// This shouldn't happen and it's ok to ignore
   134  				// block locators, so just continue to the next
   135  				// one.
   136  				log.Warnf("Lookup of known valid height failed %v",
   137  					blockHeight)
   138  				continue
   139  			}
   140  			locator = append(locator, h)
   141  		}
   142  
   143  		return nil
   144  	})
   145  
   146  	// Append the appropriate genesis block.
   147  	locator = append(locator, b.chainParams.GenesisHash)
   148  	return locator
   149  }
   150  
   151  // BlockLocatorFromHash returns a block locator for the passed block hash.
   152  // See BlockLocator for details on the algorithm used to create a block locator.
   153  //
   154  // In addition to the general algorithm referenced above, there are a couple of
   155  // special cases which are handled:
   156  //
   157  //  - If the genesis hash is passed, there are no previous hashes to add and
   158  //    therefore the block locator will only consist of the genesis hash
   159  //  - If the passed hash is not currently known, the block locator will only
   160  //    consist of the passed hash
   161  //
   162  // This function is safe for concurrent access.
   163  func (b *BlockChain) BlockLocatorFromHash(hash *wire.ShaHash) BlockLocator {
   164  	b.chainLock.RLock()
   165  	locator := b.blockLocatorFromHash(hash)
   166  	b.chainLock.RUnlock()
   167  	return locator
   168  }
   169  
   170  // LatestBlockLocator returns a block locator for the latest known tip of the
   171  // main (best) chain.
   172  //
   173  // This function is safe for concurrent access.
   174  func (b *BlockChain) LatestBlockLocator() (BlockLocator, error) {
   175  	b.chainLock.RLock()
   176  	locator := b.blockLocatorFromHash(b.bestNode.hash)
   177  	b.chainLock.RUnlock()
   178  	return locator, nil
   179  }