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 }