github.com/lbryio/lbcd@v0.22.119/database/ffldb/blockio.go (about)

     1  // Copyright (c) 2015-2016 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  // This file contains the implementation functions for reading, writing, and
     6  // otherwise working with the flat files that house the actual blocks.
     7  
     8  package ffldb
     9  
    10  import (
    11  	"container/list"
    12  	"encoding/binary"
    13  	"fmt"
    14  	"hash/crc32"
    15  	"io"
    16  	"os"
    17  	"path/filepath"
    18  	"sync"
    19  
    20  	"github.com/lbryio/lbcd/chaincfg/chainhash"
    21  	"github.com/lbryio/lbcd/database"
    22  	"github.com/lbryio/lbcd/wire"
    23  )
    24  
    25  const (
    26  	// The Bitcoin protocol encodes block height as int32, so max number of
    27  	// blocks is 2^31.  Max block size per the protocol is 32MiB per block.
    28  	// So the theoretical max at the time this comment was written is 64PiB
    29  	// (pebibytes).  With files @ 512MiB each, this would require a maximum
    30  	// of 134,217,728 files.  Thus, choose 9 digits of precision for the
    31  	// filenames.  An additional benefit is 9 digits provides 10^9 files @
    32  	// 512MiB each for a total of ~476.84PiB (roughly 7.4 times the current
    33  	// theoretical max), so there is room for the max block size to grow in
    34  	// the future.
    35  	blockFilenameTemplate = "%09d.fdb"
    36  
    37  	// maxOpenFiles is the max number of open files to maintain in the
    38  	// open blocks cache.  Note that this does not include the current
    39  	// write file, so there will typically be one more than this value open.
    40  	maxOpenFiles = 40
    41  
    42  	// maxBlockFileSize is the maximum size for each file used to store
    43  	// blocks.
    44  	//
    45  	// NOTE: The current code uses uint32 for all offsets, so this value
    46  	// must be less than 2^32 (4 GiB).  This is also why it's a typed
    47  	// constant.
    48  	maxBlockFileSize uint32 = 512 * 1024 * 1024 // 512 MiB
    49  
    50  	// blockLocSize is the number of bytes the serialized block location
    51  	// data that is stored in the block index.
    52  	//
    53  	// The serialized block location format is:
    54  	//
    55  	//  [0:4]  Block file (4 bytes)
    56  	//  [4:8]  File offset (4 bytes)
    57  	//  [8:12] Block length (4 bytes)
    58  	blockLocSize = 12
    59  )
    60  
    61  var (
    62  	// castagnoli houses the Catagnoli polynomial used for CRC-32 checksums.
    63  	castagnoli = crc32.MakeTable(crc32.Castagnoli)
    64  )
    65  
    66  // filer is an interface which acts very similar to a *os.File and is typically
    67  // implemented by it.  It exists so the test code can provide mock files for
    68  // properly testing corruption and file system issues.
    69  type filer interface {
    70  	io.Closer
    71  	io.WriterAt
    72  	io.ReaderAt
    73  	Truncate(size int64) error
    74  	Sync() error
    75  }
    76  
    77  // lockableFile represents a block file on disk that has been opened for either
    78  // read or read/write access.  It also contains a read-write mutex to support
    79  // multiple concurrent readers.
    80  type lockableFile struct {
    81  	sync.RWMutex
    82  	file filer
    83  }
    84  
    85  // writeCursor represents the current file and offset of the block file on disk
    86  // for performing all writes. It also contains a read-write mutex to support
    87  // multiple concurrent readers which can reuse the file handle.
    88  type writeCursor struct {
    89  	sync.RWMutex
    90  
    91  	// curFile is the current block file that will be appended to when
    92  	// writing new blocks.
    93  	curFile *lockableFile
    94  
    95  	// curFileNum is the current block file number and is used to allow
    96  	// readers to use the same open file handle.
    97  	curFileNum uint32
    98  
    99  	// curOffset is the offset in the current write block file where the
   100  	// next new block will be written.
   101  	curOffset uint32
   102  }
   103  
   104  // blockStore houses information used to handle reading and writing blocks (and
   105  // part of blocks) into flat files with support for multiple concurrent readers.
   106  type blockStore struct {
   107  	// network is the specific network to use in the flat files for each
   108  	// block.
   109  	network wire.BitcoinNet
   110  
   111  	// basePath is the base path used for the flat block files and metadata.
   112  	basePath string
   113  
   114  	// maxBlockFileSize is the maximum size for each file used to store
   115  	// blocks.  It is defined on the store so the whitebox tests can
   116  	// override the value.
   117  	maxBlockFileSize uint32
   118  
   119  	// The following fields are related to the flat files which hold the
   120  	// actual blocks.   The number of open files is limited by maxOpenFiles.
   121  	//
   122  	// obfMutex protects concurrent access to the openBlockFiles map.  It is
   123  	// a RWMutex so multiple readers can simultaneously access open files.
   124  	//
   125  	// openBlockFiles houses the open file handles for existing block files
   126  	// which have been opened read-only along with an individual RWMutex.
   127  	// This scheme allows multiple concurrent readers to the same file while
   128  	// preventing the file from being closed out from under them.
   129  	//
   130  	// lruMutex protects concurrent access to the least recently used list
   131  	// and lookup map.
   132  	//
   133  	// openBlocksLRU tracks how the open files are refenced by pushing the
   134  	// most recently used files to the front of the list thereby trickling
   135  	// the least recently used files to end of the list.  When a file needs
   136  	// to be closed due to exceeding the the max number of allowed open
   137  	// files, the one at the end of the list is closed.
   138  	//
   139  	// fileNumToLRUElem is a mapping between a specific block file number
   140  	// and the associated list element on the least recently used list.
   141  	//
   142  	// Thus, with the combination of these fields, the database supports
   143  	// concurrent non-blocking reads across multiple and individual files
   144  	// along with intelligently limiting the number of open file handles by
   145  	// closing the least recently used files as needed.
   146  	//
   147  	// NOTE: The locking order used throughout is well-defined and MUST be
   148  	// followed.  Failure to do so could lead to deadlocks.  In particular,
   149  	// the locking order is as follows:
   150  	//   1) obfMutex
   151  	//   2) lruMutex
   152  	//   3) writeCursor mutex
   153  	//   4) specific file mutexes
   154  	//
   155  	// None of the mutexes are required to be locked at the same time, and
   156  	// often aren't.  However, if they are to be locked simultaneously, they
   157  	// MUST be locked in the order previously specified.
   158  	//
   159  	// Due to the high performance and multi-read concurrency requirements,
   160  	// write locks should only be held for the minimum time necessary.
   161  	obfMutex         sync.RWMutex
   162  	lruMutex         sync.Mutex
   163  	openBlocksLRU    *list.List // Contains uint32 block file numbers.
   164  	fileNumToLRUElem map[uint32]*list.Element
   165  	openBlockFiles   map[uint32]*lockableFile
   166  
   167  	// writeCursor houses the state for the current file and location that
   168  	// new blocks are written to.
   169  	writeCursor *writeCursor
   170  
   171  	// These functions are set to openFile, openWriteFile, and deleteFile by
   172  	// default, but are exposed here to allow the whitebox tests to replace
   173  	// them when working with mock files.
   174  	openFileFunc      func(fileNum uint32) (*lockableFile, error)
   175  	openWriteFileFunc func(fileNum uint32) (filer, error)
   176  	deleteFileFunc    func(fileNum uint32) error
   177  }
   178  
   179  // blockLocation identifies a particular block file and location.
   180  type blockLocation struct {
   181  	blockFileNum uint32
   182  	fileOffset   uint32
   183  	blockLen     uint32
   184  }
   185  
   186  // deserializeBlockLoc deserializes the passed serialized block location
   187  // information.  This is data stored into the block index metadata for each
   188  // block.  The serialized data passed to this function MUST be at least
   189  // blockLocSize bytes or it will panic.  The error check is avoided here because
   190  // this information will always be coming from the block index which includes a
   191  // checksum to detect corruption.  Thus it is safe to use this unchecked here.
   192  func deserializeBlockLoc(serializedLoc []byte) blockLocation {
   193  	// The serialized block location format is:
   194  	//
   195  	//  [0:4]  Block file (4 bytes)
   196  	//  [4:8]  File offset (4 bytes)
   197  	//  [8:12] Block length (4 bytes)
   198  	return blockLocation{
   199  		blockFileNum: byteOrder.Uint32(serializedLoc[0:4]),
   200  		fileOffset:   byteOrder.Uint32(serializedLoc[4:8]),
   201  		blockLen:     byteOrder.Uint32(serializedLoc[8:12]),
   202  	}
   203  }
   204  
   205  // serializeBlockLoc returns the serialization of the passed block location.
   206  // This is data to be stored into the block index metadata for each block.
   207  func serializeBlockLoc(loc blockLocation) []byte {
   208  	// The serialized block location format is:
   209  	//
   210  	//  [0:4]  Block file (4 bytes)
   211  	//  [4:8]  File offset (4 bytes)
   212  	//  [8:12] Block length (4 bytes)
   213  	var serializedData [12]byte
   214  	byteOrder.PutUint32(serializedData[0:4], loc.blockFileNum)
   215  	byteOrder.PutUint32(serializedData[4:8], loc.fileOffset)
   216  	byteOrder.PutUint32(serializedData[8:12], loc.blockLen)
   217  	return serializedData[:]
   218  }
   219  
   220  // blockFilePath return the file path for the provided block file number.
   221  func blockFilePath(dbPath string, fileNum uint32) string {
   222  	fileName := fmt.Sprintf(blockFilenameTemplate, fileNum)
   223  	return filepath.Join(dbPath, fileName)
   224  }
   225  
   226  // openWriteFile returns a file handle for the passed flat file number in
   227  // read/write mode.  The file will be created if needed.  It is typically used
   228  // for the current file that will have all new data appended.  Unlike openFile,
   229  // this function does not keep track of the open file and it is not subject to
   230  // the maxOpenFiles limit.
   231  func (s *blockStore) openWriteFile(fileNum uint32) (filer, error) {
   232  	// The current block file needs to be read-write so it is possible to
   233  	// append to it.  Also, it shouldn't be part of the least recently used
   234  	// file.
   235  	filePath := blockFilePath(s.basePath, fileNum)
   236  	file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0666)
   237  	if err != nil {
   238  		str := fmt.Sprintf("failed to open file %q: %v", filePath, err)
   239  		return nil, makeDbErr(database.ErrDriverSpecific, str, err)
   240  	}
   241  
   242  	return file, nil
   243  }
   244  
   245  // openFile returns a read-only file handle for the passed flat file number.
   246  // The function also keeps track of the open files, performs least recently
   247  // used tracking, and limits the number of open files to maxOpenFiles by closing
   248  // the least recently used file as needed.
   249  //
   250  // This function MUST be called with the overall files mutex (s.obfMutex) locked
   251  // for WRITES.
   252  func (s *blockStore) openFile(fileNum uint32) (*lockableFile, error) {
   253  	// Open the appropriate file as read-only.
   254  	filePath := blockFilePath(s.basePath, fileNum)
   255  	file, err := os.Open(filePath)
   256  	if err != nil {
   257  		return nil, makeDbErr(database.ErrDriverSpecific, err.Error(),
   258  			err)
   259  	}
   260  	blockFile := &lockableFile{file: file}
   261  
   262  	// Close the least recently used file if the file exceeds the max
   263  	// allowed open files.  This is not done until after the file open in
   264  	// case the file fails to open, there is no need to close any files.
   265  	//
   266  	// A write lock is required on the LRU list here to protect against
   267  	// modifications happening as already open files are read from and
   268  	// shuffled to the front of the list.
   269  	//
   270  	// Also, add the file that was just opened to the front of the least
   271  	// recently used list to indicate it is the most recently used file and
   272  	// therefore should be closed last.
   273  	s.lruMutex.Lock()
   274  	lruList := s.openBlocksLRU
   275  	if lruList.Len() >= maxOpenFiles {
   276  		lruFileNum := lruList.Remove(lruList.Back()).(uint32)
   277  		oldBlockFile := s.openBlockFiles[lruFileNum]
   278  
   279  		// Close the old file under the write lock for the file in case
   280  		// any readers are currently reading from it so it's not closed
   281  		// out from under them.
   282  		oldBlockFile.Lock()
   283  		_ = oldBlockFile.file.Close()
   284  		oldBlockFile.Unlock()
   285  
   286  		delete(s.openBlockFiles, lruFileNum)
   287  		delete(s.fileNumToLRUElem, lruFileNum)
   288  	}
   289  	s.fileNumToLRUElem[fileNum] = lruList.PushFront(fileNum)
   290  	s.lruMutex.Unlock()
   291  
   292  	// Store a reference to it in the open block files map.
   293  	s.openBlockFiles[fileNum] = blockFile
   294  
   295  	return blockFile, nil
   296  }
   297  
   298  // deleteFile removes the block file for the passed flat file number.  The file
   299  // must already be closed and it is the responsibility of the caller to do any
   300  // other state cleanup necessary.
   301  func (s *blockStore) deleteFile(fileNum uint32) error {
   302  	filePath := blockFilePath(s.basePath, fileNum)
   303  	if err := os.Remove(filePath); err != nil {
   304  		return makeDbErr(database.ErrDriverSpecific, err.Error(), err)
   305  	}
   306  
   307  	return nil
   308  }
   309  
   310  // blockFile attempts to return an existing file handle for the passed flat file
   311  // number if it is already open as well as marking it as most recently used.  It
   312  // will also open the file when it's not already open subject to the rules
   313  // described in openFile.
   314  //
   315  // NOTE: The returned block file will already have the read lock acquired and
   316  // the caller MUST call .RUnlock() to release it once it has finished all read
   317  // operations.  This is necessary because otherwise it would be possible for a
   318  // separate goroutine to close the file after it is returned from here, but
   319  // before the caller has acquired a read lock.
   320  func (s *blockStore) blockFile(fileNum uint32) (*lockableFile, error) {
   321  	// When the requested block file is open for writes, return it.
   322  	wc := s.writeCursor
   323  	wc.RLock()
   324  	if fileNum == wc.curFileNum && wc.curFile.file != nil {
   325  		obf := wc.curFile
   326  		obf.RLock()
   327  		wc.RUnlock()
   328  		return obf, nil
   329  	}
   330  	wc.RUnlock()
   331  
   332  	// Try to return an open file under the overall files read lock.
   333  	s.obfMutex.RLock()
   334  	if obf, ok := s.openBlockFiles[fileNum]; ok {
   335  		s.lruMutex.Lock()
   336  		s.openBlocksLRU.MoveToFront(s.fileNumToLRUElem[fileNum])
   337  		s.lruMutex.Unlock()
   338  
   339  		obf.RLock()
   340  		s.obfMutex.RUnlock()
   341  		return obf, nil
   342  	}
   343  	s.obfMutex.RUnlock()
   344  
   345  	// Since the file isn't open already, need to check the open block files
   346  	// map again under write lock in case multiple readers got here and a
   347  	// separate one is already opening the file.
   348  	s.obfMutex.Lock()
   349  	if obf, ok := s.openBlockFiles[fileNum]; ok {
   350  		obf.RLock()
   351  		s.obfMutex.Unlock()
   352  		return obf, nil
   353  	}
   354  
   355  	// The file isn't open, so open it while potentially closing the least
   356  	// recently used one as needed.
   357  	obf, err := s.openFileFunc(fileNum)
   358  	if err != nil {
   359  		s.obfMutex.Unlock()
   360  		return nil, err
   361  	}
   362  	obf.RLock()
   363  	s.obfMutex.Unlock()
   364  	return obf, nil
   365  }
   366  
   367  // writeData is a helper function for writeBlock which writes the provided data
   368  // at the current write offset and updates the write cursor accordingly.  The
   369  // field name parameter is only used when there is an error to provide a nicer
   370  // error message.
   371  //
   372  // The write cursor will be advanced the number of bytes actually written in the
   373  // event of failure.
   374  //
   375  // NOTE: This function MUST be called with the write cursor current file lock
   376  // held and must only be called during a write transaction so it is effectively
   377  // locked for writes.  Also, the write cursor current file must NOT be nil.
   378  func (s *blockStore) writeData(data []byte, fieldName string) error {
   379  	wc := s.writeCursor
   380  	n, err := wc.curFile.file.WriteAt(data, int64(wc.curOffset))
   381  	wc.curOffset += uint32(n)
   382  	if err != nil {
   383  		str := fmt.Sprintf("failed to write %s to file %d at "+
   384  			"offset %d: %v", fieldName, wc.curFileNum,
   385  			wc.curOffset-uint32(n), err)
   386  		return makeDbErr(database.ErrDriverSpecific, str, err)
   387  	}
   388  
   389  	return nil
   390  }
   391  
   392  // writeBlock appends the specified raw block bytes to the store's write cursor
   393  // location and increments it accordingly.  When the block would exceed the max
   394  // file size for the current flat file, this function will close the current
   395  // file, create the next file, update the write cursor, and write the block to
   396  // the new file.
   397  //
   398  // The write cursor will also be advanced the number of bytes actually written
   399  // in the event of failure.
   400  //
   401  // Format: <network><block length><serialized block><checksum>
   402  func (s *blockStore) writeBlock(rawBlock []byte) (blockLocation, error) {
   403  	// Compute how many bytes will be written.
   404  	// 4 bytes each for block network + 4 bytes for block length +
   405  	// length of raw block + 4 bytes for checksum.
   406  	blockLen := uint32(len(rawBlock))
   407  	fullLen := blockLen + 12
   408  
   409  	// Move to the next block file if adding the new block would exceed the
   410  	// max allowed size for the current block file.  Also detect overflow
   411  	// to be paranoid, even though it isn't possible currently, numbers
   412  	// might change in the future to make it possible.
   413  	//
   414  	// NOTE: The writeCursor.offset field isn't protected by the mutex
   415  	// since it's only read/changed during this function which can only be
   416  	// called during a write transaction, of which there can be only one at
   417  	// a time.
   418  	wc := s.writeCursor
   419  	finalOffset := wc.curOffset + fullLen
   420  	if finalOffset < wc.curOffset || finalOffset > s.maxBlockFileSize {
   421  		// This is done under the write cursor lock since the curFileNum
   422  		// field is accessed elsewhere by readers.
   423  		//
   424  		// Close the current write file to force a read-only reopen
   425  		// with LRU tracking.  The close is done under the write lock
   426  		// for the file to prevent it from being closed out from under
   427  		// any readers currently reading from it.
   428  		wc.Lock()
   429  		wc.curFile.Lock()
   430  		if wc.curFile.file != nil {
   431  			_ = wc.curFile.file.Close()
   432  			wc.curFile.file = nil
   433  		}
   434  		wc.curFile.Unlock()
   435  
   436  		// Start writes into next file.
   437  		wc.curFileNum++
   438  		wc.curOffset = 0
   439  		wc.Unlock()
   440  	}
   441  
   442  	// All writes are done under the write lock for the file to ensure any
   443  	// readers are finished and blocked first.
   444  	wc.curFile.Lock()
   445  	defer wc.curFile.Unlock()
   446  
   447  	// Open the current file if needed.  This will typically only be the
   448  	// case when moving to the next file to write to or on initial database
   449  	// load.  However, it might also be the case if rollbacks happened after
   450  	// file writes started during a transaction commit.
   451  	if wc.curFile.file == nil {
   452  		file, err := s.openWriteFileFunc(wc.curFileNum)
   453  		if err != nil {
   454  			return blockLocation{}, err
   455  		}
   456  		wc.curFile.file = file
   457  	}
   458  
   459  	// Bitcoin network.
   460  	origOffset := wc.curOffset
   461  	hasher := crc32.New(castagnoli)
   462  	var scratch [4]byte
   463  	byteOrder.PutUint32(scratch[:], uint32(s.network))
   464  	if err := s.writeData(scratch[:], "network"); err != nil {
   465  		return blockLocation{}, err
   466  	}
   467  	_, _ = hasher.Write(scratch[:])
   468  
   469  	// Block length.
   470  	byteOrder.PutUint32(scratch[:], blockLen)
   471  	if err := s.writeData(scratch[:], "block length"); err != nil {
   472  		return blockLocation{}, err
   473  	}
   474  	_, _ = hasher.Write(scratch[:])
   475  
   476  	// Serialized block.
   477  	if err := s.writeData(rawBlock, "block"); err != nil {
   478  		return blockLocation{}, err
   479  	}
   480  	_, _ = hasher.Write(rawBlock)
   481  
   482  	// Castagnoli CRC-32 as a checksum of all the previous.
   483  	if err := s.writeData(hasher.Sum(nil), "checksum"); err != nil {
   484  		return blockLocation{}, err
   485  	}
   486  
   487  	loc := blockLocation{
   488  		blockFileNum: wc.curFileNum,
   489  		fileOffset:   origOffset,
   490  		blockLen:     fullLen,
   491  	}
   492  	return loc, nil
   493  }
   494  
   495  // readBlock reads the specified block record and returns the serialized block.
   496  // It ensures the integrity of the block data by checking that the serialized
   497  // network matches the current network associated with the block store and
   498  // comparing the calculated checksum against the one stored in the flat file.
   499  // This function also automatically handles all file management such as opening
   500  // and closing files as necessary to stay within the maximum allowed open files
   501  // limit.
   502  //
   503  // Returns ErrDriverSpecific if the data fails to read for any reason and
   504  // ErrCorruption if the checksum of the read data doesn't match the checksum
   505  // read from the file.
   506  //
   507  // Format: <network><block length><serialized block><checksum>
   508  func (s *blockStore) readBlock(hash *chainhash.Hash, loc blockLocation) ([]byte, error) {
   509  	// Get the referenced block file handle opening the file as needed.  The
   510  	// function also handles closing files as needed to avoid going over the
   511  	// max allowed open files.
   512  	blockFile, err := s.blockFile(loc.blockFileNum)
   513  	if err != nil {
   514  		return nil, err
   515  	}
   516  
   517  	serializedData := make([]byte, loc.blockLen)
   518  	n, err := blockFile.file.ReadAt(serializedData, int64(loc.fileOffset))
   519  	blockFile.RUnlock()
   520  	if err != nil {
   521  		str := fmt.Sprintf("failed to read block %s from file %d, "+
   522  			"offset %d: %v", hash, loc.blockFileNum, loc.fileOffset,
   523  			err)
   524  		return nil, makeDbErr(database.ErrDriverSpecific, str, err)
   525  	}
   526  
   527  	// Calculate the checksum of the read data and ensure it matches the
   528  	// serialized checksum.  This will detect any data corruption in the
   529  	// flat file without having to do much more expensive merkle root
   530  	// calculations on the loaded block.
   531  	serializedChecksum := binary.BigEndian.Uint32(serializedData[n-4:])
   532  	calculatedChecksum := crc32.Checksum(serializedData[:n-4], castagnoli)
   533  	if serializedChecksum != calculatedChecksum {
   534  		str := fmt.Sprintf("block data for block %s checksum "+
   535  			"does not match - got %x, want %x", hash,
   536  			calculatedChecksum, serializedChecksum)
   537  		return nil, makeDbErr(database.ErrCorruption, str, nil)
   538  	}
   539  
   540  	// The network associated with the block must match the current active
   541  	// network, otherwise somebody probably put the block files for the
   542  	// wrong network in the directory.
   543  	serializedNet := byteOrder.Uint32(serializedData[:4])
   544  	if serializedNet != uint32(s.network) {
   545  		str := fmt.Sprintf("block data for block %s is for the "+
   546  			"wrong network - got %d, want %d", hash, serializedNet,
   547  			uint32(s.network))
   548  		return nil, makeDbErr(database.ErrDriverSpecific, str, nil)
   549  	}
   550  
   551  	// The raw block excludes the network, length of the block, and
   552  	// checksum.
   553  	return serializedData[8 : n-4], nil
   554  }
   555  
   556  // readBlockRegion reads the specified amount of data at the provided offset for
   557  // a given block location.  The offset is relative to the start of the
   558  // serialized block (as opposed to the beginning of the block record).  This
   559  // function automatically handles all file management such as opening and
   560  // closing files as necessary to stay within the maximum allowed open files
   561  // limit.
   562  //
   563  // Returns ErrDriverSpecific if the data fails to read for any reason.
   564  func (s *blockStore) readBlockRegion(loc blockLocation, offset, numBytes uint32) ([]byte, error) {
   565  	// Get the referenced block file handle opening the file as needed.  The
   566  	// function also handles closing files as needed to avoid going over the
   567  	// max allowed open files.
   568  	blockFile, err := s.blockFile(loc.blockFileNum)
   569  	if err != nil {
   570  		return nil, err
   571  	}
   572  
   573  	// Regions are offsets into the actual block, however the serialized
   574  	// data for a block includes an initial 4 bytes for network + 4 bytes
   575  	// for block length.  Thus, add 8 bytes to adjust.
   576  	readOffset := loc.fileOffset + 8 + offset
   577  	serializedData := make([]byte, numBytes)
   578  	_, err = blockFile.file.ReadAt(serializedData, int64(readOffset))
   579  	blockFile.RUnlock()
   580  	if err != nil {
   581  		str := fmt.Sprintf("failed to read region from block file %d, "+
   582  			"offset %d, len %d: %v", loc.blockFileNum, readOffset,
   583  			numBytes, err)
   584  		return nil, makeDbErr(database.ErrDriverSpecific, str, err)
   585  	}
   586  
   587  	return serializedData, nil
   588  }
   589  
   590  // syncBlocks performs a file system sync on the flat file associated with the
   591  // store's current write cursor.  It is safe to call even when there is not a
   592  // current write file in which case it will have no effect.
   593  //
   594  // This is used when flushing cached metadata updates to disk to ensure all the
   595  // block data is fully written before updating the metadata.  This ensures the
   596  // metadata and block data can be properly reconciled in failure scenarios.
   597  func (s *blockStore) syncBlocks() error {
   598  	wc := s.writeCursor
   599  	wc.RLock()
   600  	defer wc.RUnlock()
   601  
   602  	// Nothing to do if there is no current file associated with the write
   603  	// cursor.
   604  	wc.curFile.RLock()
   605  	defer wc.curFile.RUnlock()
   606  	if wc.curFile.file == nil {
   607  		return nil
   608  	}
   609  
   610  	// Sync the file to disk.
   611  	if err := wc.curFile.file.Sync(); err != nil {
   612  		str := fmt.Sprintf("failed to sync file %d: %v", wc.curFileNum,
   613  			err)
   614  		return makeDbErr(database.ErrDriverSpecific, str, err)
   615  	}
   616  
   617  	return nil
   618  }
   619  
   620  // handleRollback rolls the block files on disk back to the provided file number
   621  // and offset.  This involves potentially deleting and truncating the files that
   622  // were partially written.
   623  //
   624  // There are effectively two scenarios to consider here:
   625  //  1. Transient write failures from which recovery is possible
   626  //  2. More permanent failures such as hard disk death and/or removal
   627  //
   628  // In either case, the write cursor will be repositioned to the old block file
   629  // offset regardless of any other errors that occur while attempting to undo
   630  // writes.
   631  //
   632  // For the first scenario, this will lead to any data which failed to be undone
   633  // being overwritten and thus behaves as desired as the system continues to run.
   634  //
   635  // For the second scenario, the metadata which stores the current write cursor
   636  // position within the block files will not have been updated yet and thus if
   637  // the system eventually recovers (perhaps the hard drive is reconnected), it
   638  // will also lead to any data which failed to be undone being overwritten and
   639  // thus behaves as desired.
   640  //
   641  // Therefore, any errors are simply logged at a warning level rather than being
   642  // returned since there is nothing more that could be done about it anyways.
   643  func (s *blockStore) handleRollback(oldBlockFileNum, oldBlockOffset uint32) {
   644  	// Grab the write cursor mutex since it is modified throughout this
   645  	// function.
   646  	wc := s.writeCursor
   647  	wc.Lock()
   648  	defer wc.Unlock()
   649  
   650  	// Nothing to do if the rollback point is the same as the current write
   651  	// cursor.
   652  	if wc.curFileNum == oldBlockFileNum && wc.curOffset == oldBlockOffset {
   653  		return
   654  	}
   655  
   656  	// Regardless of any failures that happen below, reposition the write
   657  	// cursor to the old block file and offset.
   658  	defer func() {
   659  		wc.curFileNum = oldBlockFileNum
   660  		wc.curOffset = oldBlockOffset
   661  	}()
   662  
   663  	log.Debugf("ROLLBACK: Rolling back to file %d, offset %d",
   664  		oldBlockFileNum, oldBlockOffset)
   665  
   666  	// Close the current write file if it needs to be deleted.  Then delete
   667  	// all files that are newer than the provided rollback file while
   668  	// also moving the write cursor file backwards accordingly.
   669  	if wc.curFileNum > oldBlockFileNum {
   670  		wc.curFile.Lock()
   671  		if wc.curFile.file != nil {
   672  			_ = wc.curFile.file.Close()
   673  			wc.curFile.file = nil
   674  		}
   675  		wc.curFile.Unlock()
   676  	}
   677  	for ; wc.curFileNum > oldBlockFileNum; wc.curFileNum-- {
   678  		if err := s.deleteFileFunc(wc.curFileNum); err != nil {
   679  			log.Warnf("ROLLBACK: Failed to delete block file "+
   680  				"number %d: %v", wc.curFileNum, err)
   681  			return
   682  		}
   683  	}
   684  
   685  	// Open the file for the current write cursor if needed.
   686  	wc.curFile.Lock()
   687  	if wc.curFile.file == nil {
   688  		obf, err := s.openWriteFileFunc(wc.curFileNum)
   689  		if err != nil {
   690  			wc.curFile.Unlock()
   691  			log.Warnf("ROLLBACK: %v", err)
   692  			return
   693  		}
   694  		wc.curFile.file = obf
   695  	}
   696  
   697  	// Truncate the to the provided rollback offset.
   698  	if err := wc.curFile.file.Truncate(int64(oldBlockOffset)); err != nil {
   699  		wc.curFile.Unlock()
   700  		log.Warnf("ROLLBACK: Failed to truncate file %d: %v",
   701  			wc.curFileNum, err)
   702  		return
   703  	}
   704  
   705  	// Sync the file to disk.
   706  	err := wc.curFile.file.Sync()
   707  	wc.curFile.Unlock()
   708  	if err != nil {
   709  		log.Warnf("ROLLBACK: Failed to sync file %d: %v",
   710  			wc.curFileNum, err)
   711  		return
   712  	}
   713  }
   714  
   715  // scanBlockFiles searches the database directory for all flat block files to
   716  // find the end of the most recent file.  This position is considered the
   717  // current write cursor which is also stored in the metadata.  Thus, it is used
   718  // to detect unexpected shutdowns in the middle of writes so the block files
   719  // can be reconciled.
   720  func scanBlockFiles(dbPath string) (int, uint32) {
   721  	lastFile := -1
   722  	fileLen := uint32(0)
   723  	for i := 0; ; i++ {
   724  		filePath := blockFilePath(dbPath, uint32(i))
   725  		st, err := os.Stat(filePath)
   726  		if err != nil {
   727  			break
   728  		}
   729  		lastFile = i
   730  
   731  		fileLen = uint32(st.Size())
   732  	}
   733  
   734  	log.Tracef("Scan found latest block file #%d with length %d", lastFile,
   735  		fileLen)
   736  	return lastFile, fileLen
   737  }
   738  
   739  // newBlockStore returns a new block store with the current block file number
   740  // and offset set and all fields initialized.
   741  func newBlockStore(basePath string, network wire.BitcoinNet) *blockStore {
   742  	// Look for the end of the latest block to file to determine what the
   743  	// write cursor position is from the viewpoing of the block files on
   744  	// disk.
   745  	fileNum, fileOff := scanBlockFiles(basePath)
   746  	if fileNum == -1 {
   747  		fileNum = 0
   748  		fileOff = 0
   749  	}
   750  
   751  	store := &blockStore{
   752  		network:          network,
   753  		basePath:         basePath,
   754  		maxBlockFileSize: maxBlockFileSize,
   755  		openBlockFiles:   make(map[uint32]*lockableFile),
   756  		openBlocksLRU:    list.New(),
   757  		fileNumToLRUElem: make(map[uint32]*list.Element),
   758  
   759  		writeCursor: &writeCursor{
   760  			curFile:    &lockableFile{},
   761  			curFileNum: uint32(fileNum),
   762  			curOffset:  fileOff,
   763  		},
   764  	}
   765  	store.openFileFunc = store.openFile
   766  	store.openWriteFileFunc = store.openWriteFile
   767  	store.deleteFileFunc = store.deleteFile
   768  	return store
   769  }