decred.org/dcrdex@v1.0.5/server/asset/btc/cache.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org/license/1.0.0.
     3  
     4  package btc
     5  
     6  import (
     7  	"fmt"
     8  	"sync"
     9  
    10  	"github.com/btcsuite/btcd/chaincfg/chainhash"
    11  )
    12  
    13  // The cachedBlock structure should hold a minimal amount of information about a
    14  // block needed to verify UTXO validity.
    15  type cachedBlock struct {
    16  	hash     chainhash.Hash
    17  	height   uint32
    18  	orphaned bool
    19  }
    20  
    21  // The blockCache caches block information to prevent repeated calls to
    22  // rpcclient.GetblockVerbose.
    23  type blockCache struct {
    24  	mtx       sync.RWMutex
    25  	blocks    map[chainhash.Hash]*cachedBlock
    26  	mainchain map[uint32]*cachedBlock
    27  	best      cachedBlock
    28  }
    29  
    30  // Constructor for a blockCache.
    31  func newBlockCache() *blockCache {
    32  	return &blockCache{
    33  		blocks:    make(map[chainhash.Hash]*cachedBlock),
    34  		mainchain: make(map[uint32]*cachedBlock),
    35  	}
    36  }
    37  
    38  // Getter for a block by it's hash.
    39  func (cache *blockCache) block(h *chainhash.Hash) (*cachedBlock, bool) {
    40  	cache.mtx.RLock()
    41  	defer cache.mtx.RUnlock()
    42  	blk, found := cache.blocks[*h]
    43  	return blk, found
    44  }
    45  
    46  // Getter for a mainchain block by its height. This method does not attempt
    47  // to load the block from the blockchain if it is not found. If that is required
    48  // use (*Backend).getMainchaincachedBlock.
    49  func (cache *blockCache) atHeight(height uint32) (*cachedBlock, bool) {
    50  	cache.mtx.RLock()
    51  	defer cache.mtx.RUnlock()
    52  	blk, found := cache.mainchain[height]
    53  	return blk, found
    54  }
    55  
    56  // Add a block to the blockCache. This method will translate the RPC result
    57  // to a cachedBlock, returning the cachedBlock. If the block is not orphaned, it
    58  // will be added to the mainchain.
    59  func (cache *blockCache) add(block *GetBlockVerboseResult) (*cachedBlock, error) {
    60  	cache.mtx.Lock()
    61  	defer cache.mtx.Unlock()
    62  	hash, err := chainhash.NewHashFromStr(block.Hash)
    63  	if err != nil {
    64  		return nil, fmt.Errorf("error decoding block hash %s: %w", block.Hash, err)
    65  	}
    66  	blk := &cachedBlock{
    67  		hash:     *hash,
    68  		height:   uint32(block.Height),
    69  		orphaned: block.Confirmations == -1,
    70  	}
    71  	cache.blocks[*hash] = blk
    72  
    73  	// Orphaned blocks will have -1 confirmations. Don't add them to mainchain.
    74  	if !blk.orphaned {
    75  		cache.mainchain[uint32(block.Height)] = blk
    76  		if block.Height >= int64(cache.best.height) {
    77  			cache.best.height = uint32(block.Height)
    78  			cache.best.hash = *hash
    79  			cache.best.orphaned = false // should not be needed, but be safe
    80  		}
    81  	}
    82  	return blk, nil
    83  }
    84  
    85  // Get the best known block height in the blockCache.
    86  func (cache *blockCache) tipHeight() uint32 {
    87  	cache.mtx.RLock()
    88  	defer cache.mtx.RUnlock()
    89  	return cache.best.height
    90  }
    91  
    92  // Get the best known block hash in the blockCache.
    93  func (cache *blockCache) tipHash() chainhash.Hash {
    94  	cache.mtx.RLock()
    95  	defer cache.mtx.RUnlock()
    96  	return cache.best.hash
    97  }
    98  
    99  // Get the best known block height in the blockCache.
   100  func (cache *blockCache) tip() cachedBlock {
   101  	cache.mtx.RLock()
   102  	defer cache.mtx.RUnlock()
   103  	return cache.best
   104  }
   105  
   106  // Trigger a reorg, setting any blocks at or above the provided height as
   107  // orphaned and removing them from mainchain, but not the blocks map. reorg
   108  // clears the best block, so should always be followed with the addition of a
   109  // new mainchain block.
   110  func (cache *blockCache) reorg(from int64) {
   111  	cache.mtx.Lock()
   112  	defer cache.mtx.Unlock()
   113  	if from < 0 {
   114  		return
   115  	}
   116  	for height := uint32(from); height <= cache.best.height; height++ {
   117  		block, found := cache.mainchain[height]
   118  		if !found {
   119  			// OK if not found.
   120  			continue
   121  		}
   122  		// Delete the block from mainchain.
   123  		delete(cache.mainchain, block.height)
   124  		// Store an orphaned block in the blocks cache.
   125  		cache.blocks[block.hash] = &cachedBlock{
   126  			hash:     block.hash,
   127  			height:   block.height,
   128  			orphaned: true,
   129  		}
   130  	}
   131  	// Set this to a dummy block with just the height. Caller will add mainchain
   132  	// block after call to reorg.
   133  	cache.best = cachedBlock{height: uint32(from)}
   134  }