github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/chaincode/persistence/persistence.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package persistence 8 9 import ( 10 "encoding/hex" 11 "fmt" 12 "io/ioutil" 13 "os" 14 "path/filepath" 15 "regexp" 16 "strings" 17 18 "github.com/hechain20/hechain/common/chaincode" 19 "github.com/hechain20/hechain/common/flogging" 20 "github.com/hechain20/hechain/common/util" 21 22 "github.com/pkg/errors" 23 ) 24 25 var logger = flogging.MustGetLogger("chaincode.persistence") 26 27 // IOReadWriter defines the interface needed for reading, writing, removing, and 28 // checking for existence of a specified file 29 type IOReadWriter interface { 30 ReadDir(string) ([]os.FileInfo, error) 31 ReadFile(string) ([]byte, error) 32 Remove(name string) error 33 WriteFile(string, string, []byte) error 34 MakeDir(string, os.FileMode) error 35 Exists(path string) (bool, error) 36 } 37 38 // FilesystemIO is the production implementation of the IOWriter interface 39 type FilesystemIO struct{} 40 41 // WriteFile writes a file to the filesystem; it does so atomically 42 // by first writing to a temp file and then renaming the file so that 43 // if the operation crashes midway we're not stuck with a bad package 44 func (f *FilesystemIO) WriteFile(path, name string, data []byte) error { 45 if path == "" { 46 return errors.New("empty path not allowed") 47 } 48 tmpFile, err := ioutil.TempFile(path, ".ccpackage.") 49 if err != nil { 50 return errors.Wrapf(err, "error creating temp file in directory '%s'", path) 51 } 52 defer os.Remove(tmpFile.Name()) 53 54 if n, err := tmpFile.Write(data); err != nil || n != len(data) { 55 if err == nil { 56 err = errors.Errorf( 57 "failed to write the entire content of the file, expected %d, wrote %d", 58 len(data), n) 59 } 60 return errors.Wrapf(err, "error writing to temp file '%s'", tmpFile.Name()) 61 } 62 63 if err := tmpFile.Close(); err != nil { 64 return errors.Wrapf(err, "error closing temp file '%s'", tmpFile.Name()) 65 } 66 67 if err := os.Rename(tmpFile.Name(), filepath.Join(path, name)); err != nil { 68 return errors.Wrapf(err, "error renaming temp file '%s'", tmpFile.Name()) 69 } 70 71 return nil 72 } 73 74 // Remove removes a file from the filesystem - used for rolling back an in-flight 75 // Save operation upon a failure 76 func (f *FilesystemIO) Remove(name string) error { 77 return os.Remove(name) 78 } 79 80 // ReadFile reads a file from the filesystem 81 func (f *FilesystemIO) ReadFile(filename string) ([]byte, error) { 82 return ioutil.ReadFile(filename) 83 } 84 85 // ReadDir reads a directory from the filesystem 86 func (f *FilesystemIO) ReadDir(dirname string) ([]os.FileInfo, error) { 87 return ioutil.ReadDir(dirname) 88 } 89 90 // MakeDir makes a directory on the filesystem (and any 91 // necessary parent directories). 92 func (f *FilesystemIO) MakeDir(dirname string, mode os.FileMode) error { 93 return os.MkdirAll(dirname, mode) 94 } 95 96 // Exists checks whether a file exists 97 func (*FilesystemIO) Exists(path string) (bool, error) { 98 _, err := os.Stat(path) 99 if err == nil { 100 return true, nil 101 } 102 if os.IsNotExist(err) { 103 return false, nil 104 } 105 106 return false, errors.Wrapf(err, "could not determine whether file '%s' exists", path) 107 } 108 109 // Store holds the information needed for persisting a chaincode install package 110 type Store struct { 111 Path string 112 ReadWriter IOReadWriter 113 } 114 115 // NewStore creates a new chaincode persistence store using 116 // the provided path on the filesystem. 117 func NewStore(path string) *Store { 118 store := &Store{ 119 Path: path, 120 ReadWriter: &FilesystemIO{}, 121 } 122 store.Initialize() 123 return store 124 } 125 126 // Initialize checks for the existence of the _lifecycle chaincodes 127 // directory and creates it if it has not yet been created. 128 func (s *Store) Initialize() { 129 var ( 130 exists bool 131 err error 132 ) 133 if exists, err = s.ReadWriter.Exists(s.Path); exists { 134 return 135 } 136 if err != nil { 137 panic(fmt.Sprintf("Initialization of chaincode store failed: %s", err)) 138 } 139 if err = s.ReadWriter.MakeDir(s.Path, 0o750); err != nil { 140 panic(fmt.Sprintf("Could not create _lifecycle chaincodes install path: %s", err)) 141 } 142 } 143 144 // Save persists chaincode install package bytes. It returns 145 // the hash of the chaincode install package 146 func (s *Store) Save(label string, ccInstallPkg []byte) (string, error) { 147 packageID := PackageID(label, ccInstallPkg) 148 149 ccInstallPkgFileName := CCFileName(packageID) 150 ccInstallPkgFilePath := filepath.Join(s.Path, ccInstallPkgFileName) 151 152 if exists, _ := s.ReadWriter.Exists(ccInstallPkgFilePath); exists { 153 // chaincode install package was already installed 154 return packageID, nil 155 } 156 157 if err := s.ReadWriter.WriteFile(s.Path, ccInstallPkgFileName, ccInstallPkg); err != nil { 158 err = errors.Wrapf(err, "error writing chaincode install package to %s", ccInstallPkgFilePath) 159 logger.Error(err.Error()) 160 return "", err 161 } 162 163 return packageID, nil 164 } 165 166 // Load loads a persisted chaincode install package bytes with 167 // the given packageID. 168 func (s *Store) Load(packageID string) ([]byte, error) { 169 ccInstallPkgPath := filepath.Join(s.Path, CCFileName(packageID)) 170 171 exists, err := s.ReadWriter.Exists(ccInstallPkgPath) 172 if err != nil { 173 return nil, errors.Wrapf(err, "could not determine whether chaincode install package '%s' exists", packageID) 174 } 175 if !exists { 176 return nil, &CodePackageNotFoundErr{ 177 PackageID: packageID, 178 } 179 } 180 181 ccInstallPkg, err := s.ReadWriter.ReadFile(ccInstallPkgPath) 182 if err != nil { 183 err = errors.Wrapf(err, "error reading chaincode install package at %s", ccInstallPkgPath) 184 return nil, err 185 } 186 187 return ccInstallPkg, nil 188 } 189 190 // Delete deletes a persisted chaincode. Note, there is no locking, 191 // so this should only be performed if the chaincode has already 192 // been marked built. 193 func (s *Store) Delete(packageID string) error { 194 ccInstallPkgPath := filepath.Join(s.Path, CCFileName(packageID)) 195 return s.ReadWriter.Remove(ccInstallPkgPath) 196 } 197 198 // CodePackageNotFoundErr is the error returned when a code package cannot 199 // be found in the persistence store 200 type CodePackageNotFoundErr struct { 201 PackageID string 202 } 203 204 func (e CodePackageNotFoundErr) Error() string { 205 return fmt.Sprintf("chaincode install package '%s' not found", e.PackageID) 206 } 207 208 // ListInstalledChaincodes returns an array with information about the 209 // chaincodes installed in the persistence store 210 func (s *Store) ListInstalledChaincodes() ([]chaincode.InstalledChaincode, error) { 211 files, err := s.ReadWriter.ReadDir(s.Path) 212 if err != nil { 213 return nil, errors.Wrapf(err, "error reading chaincode directory at %s", s.Path) 214 } 215 216 installedChaincodes := []chaincode.InstalledChaincode{} 217 for _, file := range files { 218 if instCC, isInstCC := installedChaincodeFromFilename(file.Name()); isInstCC { 219 installedChaincodes = append(installedChaincodes, instCC) 220 } 221 } 222 return installedChaincodes, nil 223 } 224 225 // GetChaincodeInstallPath returns the path where chaincodes 226 // are installed 227 func (s *Store) GetChaincodeInstallPath() string { 228 return s.Path 229 } 230 231 // PackageID returns the package ID with the label and hash of the chaincode install package 232 func PackageID(label string, ccInstallPkg []byte) string { 233 hash := util.ComputeSHA256(ccInstallPkg) 234 return packageID(label, hash) 235 } 236 237 func packageID(label string, hash []byte) string { 238 return fmt.Sprintf("%s:%x", label, hash) 239 } 240 241 func CCFileName(packageID string) string { 242 return strings.Replace(packageID, ":", ".", 1) + ".tar.gz" 243 } 244 245 var packageFileMatcher = regexp.MustCompile("^(.+)[.]([0-9a-f]{64})[.]tar[.]gz$") 246 247 func installedChaincodeFromFilename(fileName string) (chaincode.InstalledChaincode, bool) { 248 matches := packageFileMatcher.FindStringSubmatch(fileName) 249 if len(matches) == 3 { 250 label := matches[1] 251 hash, _ := hex.DecodeString(matches[2]) 252 packageID := packageID(label, hash) 253 254 return chaincode.InstalledChaincode{ 255 Label: label, 256 Hash: hash, 257 PackageID: packageID, 258 }, true 259 } 260 261 return chaincode.InstalledChaincode{}, false 262 }