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 }