github.com/kaituanwang/hyperledger@v2.0.1+incompatible/common/ledger/blkstorage/fsblkstorage/reset.go (about)

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