github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/common/ledger/blkstorage/reset.go (about)

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