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 }