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 }