github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/kvledger/snapshot.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package kvledger 8 9 import ( 10 "encoding/hex" 11 "encoding/json" 12 "fmt" 13 "hash" 14 "io/ioutil" 15 "os" 16 "path/filepath" 17 18 "github.com/pkg/errors" 19 ) 20 21 const ( 22 snapshotMetadataFileName = "_snapshot_signable_metadata.json" 23 snapshotMetadataHashFileName = "_snapshot_additional_info.json" 24 jsonFileIndent = " " 25 ) 26 27 // snapshotSignableMetadata is used to build a JSON that represents a unique snapshot and 28 // can be signed by the peer. Hashsum of the resultant JSON is intended to be used as a single 29 // hash of the snapshot, if need be. 30 type snapshotSignableMetadata struct { 31 ChannelName string `json:"channel_name"` 32 ChannelHeight uint64 `json:"channel_height"` 33 LastBlockHashInHex string `json:"last_block_hash"` 34 FilesAndHashes map[string]string `json:"snapshot_files_raw_hashes"` 35 } 36 37 type snapshotAdditionalInfo struct { 38 SnapshotHashInHex string `json:"snapshot_hash"` 39 LastBlockCommitHashInHex string `json:"last_block_commit_hash"` 40 } 41 42 // generateSnapshot generates a snapshot. This function should be invoked when commit on the kvledger are paused 43 // after committing the last block fully and further the commits should not be resumed till this function finishes 44 func (l *kvLedger) generateSnapshot() error { 45 snapshotsRootDir := l.snapshotsConfig.RootDir 46 bcInfo, err := l.GetBlockchainInfo() 47 if err != nil { 48 return err 49 } 50 snapshotTempDir, err := ioutil.TempDir( 51 InProgressSnapshotsPath(snapshotsRootDir), 52 fmt.Sprintf("%s-%d-", l.ledgerID, bcInfo.Height), 53 ) 54 if err != nil { 55 return errors.Wrapf(err, "error while creating temp dir [%s]", snapshotTempDir) 56 } 57 newHashFunc := func() (hash.Hash, error) { 58 return l.hashProvider.GetHash(snapshotHashOpts) 59 } 60 txIDsExportSummary, err := l.blockStore.ExportTxIds(snapshotTempDir, newHashFunc) 61 if err != nil { 62 return err 63 } 64 configsHistoryExportSummary, err := l.configHistoryRetriever.ExportConfigHistory(snapshotTempDir, newHashFunc) 65 if err != nil { 66 return err 67 } 68 stateDBExportSummary, err := l.txmgr.ExportPubStateAndPvtStateHashes(snapshotTempDir, newHashFunc) 69 if err != nil { 70 return err 71 } 72 73 if err := l.generateSnapshotMetadataFiles( 74 snapshotTempDir, txIDsExportSummary, 75 configsHistoryExportSummary, stateDBExportSummary, 76 ); err != nil { 77 return err 78 } 79 if err := syncDir(snapshotTempDir); err != nil { 80 return err 81 } 82 slgr := SnapshotsDirForLedger(snapshotsRootDir, l.ledgerID) 83 if err := os.MkdirAll(slgr, 0755); err != nil { 84 return errors.Wrapf(err, "error while creating final dir for snapshot:%s", slgr) 85 } 86 if err := syncParentDir(slgr); err != nil { 87 return err 88 } 89 slgrht := SnapshotDirForLedgerHeight(l.snapshotsConfig.RootDir, l.ledgerID, bcInfo.Height) 90 if err := os.Rename(snapshotTempDir, slgrht); err != nil { 91 return errors.Wrapf(err, "error while renaming dir [%s] to [%s]:", snapshotTempDir, slgrht) 92 } 93 return syncParentDir(slgrht) 94 } 95 96 func (l *kvLedger) generateSnapshotMetadataFiles( 97 dir string, 98 txIDsExportSummary, 99 configsHistoryExportSummary, 100 stateDBExportSummary map[string][]byte) error { 101 // generate metadata file 102 filesAndHashes := map[string]string{} 103 for fileName, hashsum := range txIDsExportSummary { 104 filesAndHashes[fileName] = hex.EncodeToString(hashsum) 105 } 106 for fileName, hashsum := range configsHistoryExportSummary { 107 filesAndHashes[fileName] = hex.EncodeToString(hashsum) 108 } 109 for fileName, hashsum := range stateDBExportSummary { 110 filesAndHashes[fileName] = hex.EncodeToString(hashsum) 111 } 112 bcInfo, err := l.GetBlockchainInfo() 113 if err != nil { 114 return err 115 } 116 metadata, err := json.MarshalIndent( 117 &snapshotSignableMetadata{ 118 ChannelName: l.ledgerID, 119 ChannelHeight: bcInfo.Height, 120 LastBlockHashInHex: hex.EncodeToString(bcInfo.CurrentBlockHash), 121 FilesAndHashes: filesAndHashes, 122 }, 123 "", 124 jsonFileIndent, 125 ) 126 if err != nil { 127 return errors.Wrap(err, "error while marshelling snapshot metadata to JSON") 128 } 129 if err := createAndSyncFile(filepath.Join(dir, snapshotMetadataFileName), metadata); err != nil { 130 return err 131 } 132 133 // generate metadata hash file 134 hash, err := l.hashProvider.GetHash(snapshotHashOpts) 135 if err != nil { 136 return err 137 } 138 if _, err := hash.Write(metadata); err != nil { 139 return err 140 } 141 metadataAdditionalInfo, err := json.MarshalIndent( 142 &snapshotAdditionalInfo{ 143 SnapshotHashInHex: hex.EncodeToString(hash.Sum(nil)), 144 LastBlockCommitHashInHex: hex.EncodeToString(l.commitHash), 145 }, 146 "", 147 jsonFileIndent, 148 ) 149 if err != nil { 150 return errors.Wrap(err, "error while marshalling snapshot additional info to JSON") 151 } 152 return createAndSyncFile(filepath.Join(dir, snapshotMetadataHashFileName), metadataAdditionalInfo) 153 } 154 155 func createAndSyncFile(filePath string, content []byte) error { 156 file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0444) 157 if err != nil { 158 return errors.Wrapf(err, "error while creating file:%s", filePath) 159 } 160 _, err = file.Write(content) 161 if err != nil { 162 file.Close() 163 return errors.Wrapf(err, "error while writing to file:%s", filePath) 164 } 165 if err = file.Sync(); err != nil { 166 file.Close() 167 return errors.Wrapf(err, "error while synching the file:%s", filePath) 168 } 169 if err := file.Close(); err != nil { 170 return errors.Wrapf(err, "error while closing the file:%s", filePath) 171 } 172 return nil 173 } 174 175 func syncParentDir(path string) error { 176 return syncDir(filepath.Dir(path)) 177 } 178 179 func syncDir(dirPath string) error { 180 dir, err := os.Open(dirPath) 181 if err != nil { 182 return errors.Wrapf(err, "error while opening dir:%s", dirPath) 183 } 184 if err := dir.Sync(); err != nil { 185 dir.Close() 186 return errors.Wrapf(err, "error while synching dir:%s", dirPath) 187 } 188 if err := dir.Close(); err != nil { 189 return errors.Wrapf(err, "error while closing dir:%s", dirPath) 190 } 191 return err 192 }