diff --git a/common/cache.go b/common/cache.go index ade36ba..d3eda6c 100644 --- a/common/cache.go +++ b/common/cache.go @@ -51,20 +51,27 @@ func (c *BlockCache) HashMismatch(prevhash []byte) bool { return c.latestHash != nil && !bytes.Equal(c.latestHash, prevhash) } +// Make the block at the given height the lowest height that we don't have. +// In other words, wipe out this height and beyond. +// This should never increase the size of the cache, only decrease. +// Caller should hold c.mutex.Lock(). func (c *BlockCache) setDbFiles(height int) { - index := height - c.firstBlock - if err := c.lengthsFile.Truncate(int64(index * 4)); err != nil { - Log.Fatal("truncate lengths file failed: ", err) + if height < c.nextBlock { + index := height - c.firstBlock + if err := c.lengthsFile.Truncate(int64(index * 4)); err != nil { + Log.Fatal("truncate lengths file failed: ", err) + } + if err := c.blocksFile.Truncate(c.starts[index]); err != nil { + Log.Fatal("truncate blocks file failed: ", err) + } + c.Sync() + c.starts = c.starts[:index+1] + c.nextBlock = height + c.setLatestHash() } - if err := c.blocksFile.Truncate(c.starts[index]); err != nil { - Log.Fatal("truncate blocks file failed: ", err) - } - c.Sync() - c.starts = c.starts[:index+1] - c.nextBlock = height - c.setLatestHash() } +// Caller should hold c.mutex.Lock(). func (c *BlockCache) recoverFromCorruption(height int) { Log.Warning("CORRUPTION detected in db blocks-cache files, height ", height, " redownloading") c.setDbFiles(height) @@ -86,6 +93,7 @@ func checksum(height int, b []byte) []byte { return cs.Sum(nil) } +// Caller should hold (at least) c.mutex.RLock(). func (c *BlockCache) readBlock(height int) *walletrpc.CompactBlock { blockLen := c.blockLength(height) b := make([]byte, blockLen+8) @@ -116,6 +124,7 @@ func (c *BlockCache) readBlock(height int) *walletrpc.CompactBlock { return block } +// Caller should hold c.mutex.Lock(). func (c *BlockCache) setLatestHash() { c.latestHash = nil // There is at least one block; get the last block's hash @@ -132,6 +141,7 @@ func (c *BlockCache) setLatestHash() { } // NewBlockCache returns an instance of a block cache object. +// (No locking here, we assume this is single-threaded.) func NewBlockCache(dbPath string, chainName string, startHeight int, redownload bool) *BlockCache { c := &BlockCache{} c.firstBlock = startHeight @@ -298,7 +308,12 @@ func (c *BlockCache) Get(height int) *walletrpc.CompactBlock { } block := c.readBlock(height) if block == nil { - c.recoverFromCorruption(height) + go func() { + // We hold only the read lock, need the exclusive lock. + c.mutex.Lock() + c.recoverFromCorruption(height) + c.mutex.Unlock() + }() return nil } return block