github.com/sykesm/fabric@v1.1.0-preview.0.20200129034918-2aa12b1a0181/common/ledger/blkstorage/fsblkstorage/reset.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package fsblkstorage
     8  
     9  import (
    10  	"fmt"
    11  	"io/ioutil"
    12  	"os"
    13  	"path"
    14  	"strconv"
    15  
    16  	"github.com/hyperledger/fabric/common/ledger/util"
    17  )
    18  
    19  // ResetBlockStore drops the block storage index and truncates the blocks files for all channels/ledgers to genesis blocks
    20  func ResetBlockStore(blockStorageDir string) error {
    21  	if err := DeleteBlockStoreIndex(blockStorageDir); err != nil {
    22  		return err
    23  	}
    24  	conf := &Conf{blockStorageDir: blockStorageDir}
    25  	chainsDir := conf.getChainsDir()
    26  	chainsDirExists, err := pathExists(chainsDir)
    27  	if err != nil {
    28  		return err
    29  	}
    30  	if !chainsDirExists {
    31  		logger.Infof("Dir [%s] missing... exiting", chainsDir)
    32  		return nil
    33  	}
    34  	ledgerIDs, err := util.ListSubdirs(chainsDir)
    35  	if err != nil {
    36  		return err
    37  	}
    38  	if len(ledgerIDs) == 0 {
    39  		logger.Info("No ledgers found.. exiting")
    40  		return nil
    41  	}
    42  	logger.Infof("Found ledgers - %s", ledgerIDs)
    43  	for _, ledgerID := range ledgerIDs {
    44  		ledgerDir := conf.getLedgerBlockDir(ledgerID)
    45  		if err := recordHeightIfGreaterThanPreviousRecording(ledgerDir); err != nil {
    46  			return err
    47  		}
    48  		if err := resetToGenesisBlk(ledgerDir); err != nil {
    49  			return err
    50  		}
    51  	}
    52  	return nil
    53  }
    54  
    55  // DeleteBlockStoreIndex deletes block store index file
    56  func DeleteBlockStoreIndex(blockStorageDir string) error {
    57  	conf := &Conf{blockStorageDir: blockStorageDir}
    58  	indexDir := conf.getIndexDir()
    59  	logger.Infof("Dropping the index dir [%s]... if present", indexDir)
    60  	return os.RemoveAll(indexDir)
    61  }
    62  
    63  func resetToGenesisBlk(ledgerDir string) error {
    64  	logger.Infof("Resetting ledger [%s] to genesis block", ledgerDir)
    65  	lastFileNum, err := retrieveLastFileSuffix(ledgerDir)
    66  	logger.Infof("lastFileNum = [%d]", lastFileNum)
    67  	if err != nil {
    68  		return err
    69  	}
    70  	if lastFileNum < 0 {
    71  		return nil
    72  	}
    73  	zeroFilePath, genesisBlkEndOffset, err := retrieveGenesisBlkOffsetAndMakeACopy(ledgerDir)
    74  	if err != nil {
    75  		return err
    76  	}
    77  	for lastFileNum > 0 {
    78  		filePath := deriveBlockfilePath(ledgerDir, lastFileNum)
    79  		logger.Infof("Deleting file number = [%d]", lastFileNum)
    80  		if err := os.Remove(filePath); err != nil {
    81  			return err
    82  		}
    83  		lastFileNum--
    84  	}
    85  	logger.Infof("Truncating file [%s] to offset [%d]", zeroFilePath, genesisBlkEndOffset)
    86  	return os.Truncate(zeroFilePath, genesisBlkEndOffset)
    87  }
    88  
    89  func retrieveGenesisBlkOffsetAndMakeACopy(ledgerDir string) (string, int64, error) {
    90  	blockfilePath := deriveBlockfilePath(ledgerDir, 0)
    91  	blockfileStream, err := newBlockfileStream(ledgerDir, 0, 0)
    92  	if err != nil {
    93  		return "", -1, err
    94  	}
    95  	genesisBlockBytes, _, err := blockfileStream.nextBlockBytesAndPlacementInfo()
    96  	if err != nil {
    97  		return "", -1, err
    98  	}
    99  	endOffsetGenesisBlock := blockfileStream.currentOffset
   100  	blockfileStream.close()
   101  
   102  	if err := assertIsGenesisBlock(genesisBlockBytes); err != nil {
   103  		return "", -1, err
   104  	}
   105  	// just for an extra safety make a backup of genesis block
   106  	if err := ioutil.WriteFile(path.Join(ledgerDir, "__backupGenesisBlockBytes"), genesisBlockBytes, 0640); err != nil {
   107  		return "", -1, err
   108  	}
   109  	logger.Infof("Genesis block backed up. Genesis block info file [%s], offset [%d]", blockfilePath, endOffsetGenesisBlock)
   110  	return blockfilePath, endOffsetGenesisBlock, nil
   111  }
   112  
   113  func assertIsGenesisBlock(blockBytes []byte) error {
   114  	block, err := deserializeBlock(blockBytes)
   115  	if err != nil {
   116  		return err
   117  	}
   118  	if block.Header.Number != 0 || block.Header.GetDataHash() == nil {
   119  		return fmt.Errorf("The supplied bytes are not of genesis block. blockNum=%d, blockHash=%x", block.Header.Number, block.Header.GetDataHash())
   120  	}
   121  	return nil
   122  }
   123  
   124  func pathExists(path string) (bool, error) {
   125  	_, err := os.Stat(path)
   126  	if os.IsNotExist(err) {
   127  		return false, nil
   128  	}
   129  	if err != nil {
   130  		return false, err
   131  	}
   132  	return true, nil
   133  }
   134  
   135  const (
   136  	fileNamePreRestHt = "__preResetHeight"
   137  )
   138  
   139  // recordHeightIfGreaterThanPreviousRecording creates a file "__preResetHeight" in the ledger's
   140  // directory. This file contains human readable string for the current block height. This function
   141  // only overwrites this information if the current block height is higher than the one recorded in
   142  // the existing file (if present). This helps in achieving fail-safe behviour of reset utility
   143  func recordHeightIfGreaterThanPreviousRecording(ledgerDir string) error {
   144  	logger.Infof("Preparing to record current height for ledger at [%s]", ledgerDir)
   145  	checkpointInfo, err := constructCheckpointInfoFromBlockFiles(ledgerDir)
   146  	if err != nil {
   147  		return err
   148  	}
   149  	logger.Infof("Loaded current info from blockfiles %#v", checkpointInfo)
   150  	preResetHtFile := path.Join(ledgerDir, fileNamePreRestHt)
   151  	exists, err := pathExists(preResetHtFile)
   152  	logger.Infof("preResetHtFile already exists? = %t", exists)
   153  	if err != nil {
   154  		return err
   155  	}
   156  	previuoslyRecordedHt := uint64(0)
   157  	if exists {
   158  		htBytes, err := ioutil.ReadFile(preResetHtFile)
   159  		if err != nil {
   160  			return err
   161  		}
   162  		if previuoslyRecordedHt, err = strconv.ParseUint(string(htBytes), 10, 64); err != nil {
   163  			return err
   164  		}
   165  		logger.Infof("preResetHtFile contains height = %d", previuoslyRecordedHt)
   166  	}
   167  	currentHt := checkpointInfo.lastBlockNumber + 1
   168  	if currentHt > previuoslyRecordedHt {
   169  		logger.Infof("Recording current height [%d]", currentHt)
   170  		return ioutil.WriteFile(preResetHtFile,
   171  			[]byte(strconv.FormatUint(currentHt, 10)),
   172  			0640,
   173  		)
   174  	}
   175  	logger.Infof("Not recording current height [%d] since this is less than previously recorded height [%d]",
   176  		currentHt, previuoslyRecordedHt)
   177  
   178  	return nil
   179  }
   180  
   181  // LoadPreResetHeight searches the preResetHeight files for the specified ledgers and
   182  // returns a map of channelname to the last recorded block height during one of the reset operations.
   183  func LoadPreResetHeight(blockStorageDir string, ledgerIDs []string) (map[string]uint64, error) {
   184  	logger.Debug("Loading Pre-reset heights")
   185  	preResetFilesMap, err := preResetHtFiles(blockStorageDir, ledgerIDs)
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  	m := map[string]uint64{}
   190  	for ledgerID, filePath := range preResetFilesMap {
   191  		bytes, err := ioutil.ReadFile(filePath)
   192  		if err != nil {
   193  			return nil, err
   194  		}
   195  		previuoslyRecordedHt, err := strconv.ParseUint(string(bytes), 10, 64)
   196  		if err != nil {
   197  			return nil, err
   198  		}
   199  		m[ledgerID] = previuoslyRecordedHt
   200  	}
   201  	if len(m) > 0 {
   202  		logger.Infof("Pre-reset heights loaded: %v", m)
   203  	}
   204  	return m, nil
   205  }
   206  
   207  // ClearPreResetHeight deletes the files that contain the last recorded reset heights for the specified ledgers
   208  func ClearPreResetHeight(blockStorageDir string, ledgerIDs []string) error {
   209  	logger.Info("Clearing Pre-reset heights")
   210  	preResetFilesMap, err := preResetHtFiles(blockStorageDir, ledgerIDs)
   211  	if err != nil {
   212  		return err
   213  	}
   214  	for _, filePath := range preResetFilesMap {
   215  		if err := os.Remove(filePath); err != nil {
   216  			return err
   217  		}
   218  	}
   219  	logger.Info("Cleared off Pre-reset heights")
   220  	return nil
   221  }
   222  
   223  func preResetHtFiles(blockStorageDir string, ledgerIDs []string) (map[string]string, error) {
   224  	if len(ledgerIDs) == 0 {
   225  		logger.Info("No active channels passed")
   226  		return nil, nil
   227  	}
   228  	conf := &Conf{blockStorageDir: blockStorageDir}
   229  	chainsDir := conf.getChainsDir()
   230  	chainsDirExists, err := pathExists(chainsDir)
   231  	if err != nil {
   232  		return nil, err
   233  	}
   234  	if !chainsDirExists {
   235  		logger.Infof("Dir [%s] missing... exiting", chainsDir)
   236  		return nil, err
   237  	}
   238  	m := map[string]string{}
   239  	for _, ledgerID := range ledgerIDs {
   240  		ledgerDir := conf.getLedgerBlockDir(ledgerID)
   241  		file := path.Join(ledgerDir, fileNamePreRestHt)
   242  		exists, err := pathExists(file)
   243  		if err != nil {
   244  			return nil, err
   245  		}
   246  		if !exists {
   247  			continue
   248  		}
   249  		m[ledgerID] = file
   250  	}
   251  	return m, nil
   252  }