github.com/sykesm/fabric@v1.1.0-preview.0.20200129034918-2aa12b1a0181/core/common/ccprovider/ccprovider.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package ccprovider 8 9 import ( 10 "fmt" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 "strings" 15 "unicode" 16 17 "github.com/golang/protobuf/proto" 18 pb "github.com/hyperledger/fabric-protos-go/peer" 19 "github.com/hyperledger/fabric/bccsp" 20 "github.com/hyperledger/fabric/bccsp/factory" 21 "github.com/hyperledger/fabric/common/chaincode" 22 "github.com/hyperledger/fabric/common/flogging" 23 "github.com/hyperledger/fabric/core/common/privdata" 24 "github.com/hyperledger/fabric/core/ledger" 25 "github.com/pkg/errors" 26 ) 27 28 var ccproviderLogger = flogging.MustGetLogger("ccprovider") 29 30 var chaincodeInstallPath string 31 32 // CCPackage encapsulates a chaincode package which can be 33 // raw ChaincodeDeploymentSpec 34 // SignedChaincodeDeploymentSpec 35 // Attempt to keep the interface at a level with minimal 36 // interface for possible generalization. 37 type CCPackage interface { 38 //InitFromBuffer initialize the package from bytes 39 InitFromBuffer(buf []byte) (*ChaincodeData, error) 40 41 // PutChaincodeToFS writes the chaincode to the filesystem 42 PutChaincodeToFS() error 43 44 // GetDepSpec gets the ChaincodeDeploymentSpec from the package 45 GetDepSpec() *pb.ChaincodeDeploymentSpec 46 47 // GetDepSpecBytes gets the serialized ChaincodeDeploymentSpec from the package 48 GetDepSpecBytes() []byte 49 50 // ValidateCC validates and returns the chaincode deployment spec corresponding to 51 // ChaincodeData. The validation is based on the metadata from ChaincodeData 52 // One use of this method is to validate the chaincode before launching 53 ValidateCC(ccdata *ChaincodeData) error 54 55 // GetPackageObject gets the object as a proto.Message 56 GetPackageObject() proto.Message 57 58 // GetChaincodeData gets the ChaincodeData 59 GetChaincodeData() *ChaincodeData 60 61 // GetId gets the fingerprint of the chaincode based on package computation 62 GetId() []byte 63 } 64 65 // SetChaincodesPath sets the chaincode path for this peer 66 func SetChaincodesPath(path string) { 67 if s, err := os.Stat(path); err != nil { 68 if os.IsNotExist(err) { 69 if err := os.Mkdir(path, 0755); err != nil { 70 panic(fmt.Sprintf("Could not create chaincodes install path: %s", err)) 71 } 72 } else { 73 panic(fmt.Sprintf("Could not stat chaincodes install path: %s", err)) 74 } 75 } else if !s.IsDir() { 76 panic(fmt.Errorf("chaincode path exists but not a dir: %s", path)) 77 } 78 79 chaincodeInstallPath = path 80 } 81 82 // isPrintable is used by CDSPackage and SignedCDSPackage validation to 83 // detect garbage strings in unmarshaled proto fields where printable 84 // characters are expected. 85 func isPrintable(name string) bool { 86 notASCII := func(r rune) bool { 87 return !unicode.IsPrint(r) 88 } 89 return strings.IndexFunc(name, notASCII) == -1 90 } 91 92 // GetChaincodePackage returns the chaincode package from the file system 93 func GetChaincodePackageFromPath(ccNameVersion string, ccInstallPath string) ([]byte, error) { 94 path := fmt.Sprintf("%s/%s", ccInstallPath, strings.ReplaceAll(ccNameVersion, ":", ".")) 95 var ccbytes []byte 96 var err error 97 if ccbytes, err = ioutil.ReadFile(path); err != nil { 98 return nil, err 99 } 100 return ccbytes, nil 101 } 102 103 // ChaincodePackageExists returns whether the chaincode package exists in the file system 104 func ChaincodePackageExists(ccname string, ccversion string) (bool, error) { 105 path := filepath.Join(chaincodeInstallPath, ccname+"."+ccversion) 106 _, err := os.Stat(path) 107 if err == nil { 108 // chaincodepackage already exists 109 return true, nil 110 } 111 return false, err 112 } 113 114 type CCCacheSupport interface { 115 // GetChaincode is needed by the cache to get chaincode data 116 GetChaincode(ccNameVersion string) (CCPackage, error) 117 } 118 119 // CCInfoFSImpl provides the implementation for CC on the FS and the access to it 120 // It implements CCCacheSupport 121 type CCInfoFSImpl struct { 122 GetHasher GetHasher 123 } 124 125 // GetChaincodeFromFS this is a wrapper for hiding package implementation. 126 // It calls GetChaincodeFromPath with the chaincodeInstallPath 127 func (cifs *CCInfoFSImpl) GetChaincode(ccNameVersion string) (CCPackage, error) { 128 return cifs.GetChaincodeFromPath(ccNameVersion, chaincodeInstallPath) 129 } 130 131 func (cifs *CCInfoFSImpl) GetChaincodeCodePackage(ccNameVersion string) ([]byte, error) { 132 ccpack, err := cifs.GetChaincode(ccNameVersion) 133 if err != nil { 134 return nil, err 135 } 136 return ccpack.GetDepSpec().CodePackage, nil 137 } 138 139 func (cifs *CCInfoFSImpl) GetChaincodeDepSpec(ccNameVersion string) (*pb.ChaincodeDeploymentSpec, error) { 140 ccpack, err := cifs.GetChaincode(ccNameVersion) 141 if err != nil { 142 return nil, err 143 } 144 return ccpack.GetDepSpec(), nil 145 } 146 147 // GetChaincodeFromPath this is a wrapper for hiding package implementation. 148 func (cifs *CCInfoFSImpl) GetChaincodeFromPath(ccNameVersion string, path string) (CCPackage, error) { 149 // try raw CDS 150 cccdspack := &CDSPackage{GetHasher: cifs.GetHasher} 151 _, _, err := cccdspack.InitFromPath(ccNameVersion, path) 152 if err != nil { 153 // try signed CDS 154 ccscdspack := &SignedCDSPackage{GetHasher: cifs.GetHasher} 155 _, _, err = ccscdspack.InitFromPath(ccNameVersion, path) 156 if err != nil { 157 return nil, err 158 } 159 return ccscdspack, nil 160 } 161 return cccdspack, nil 162 } 163 164 // GetChaincodeInstallPath returns the path to the installed chaincodes 165 func (*CCInfoFSImpl) GetChaincodeInstallPath() string { 166 return chaincodeInstallPath 167 } 168 169 // PutChaincode is a wrapper for putting raw ChaincodeDeploymentSpec 170 //using CDSPackage. This is only used in UTs 171 func (cifs *CCInfoFSImpl) PutChaincode(depSpec *pb.ChaincodeDeploymentSpec) (CCPackage, error) { 172 buf, err := proto.Marshal(depSpec) 173 if err != nil { 174 return nil, err 175 } 176 cccdspack := &CDSPackage{GetHasher: cifs.GetHasher} 177 if _, err := cccdspack.InitFromBuffer(buf); err != nil { 178 return nil, err 179 } 180 err = cccdspack.PutChaincodeToFS() 181 if err != nil { 182 return nil, err 183 } 184 185 return cccdspack, nil 186 } 187 188 // DirEnumerator enumerates directories 189 type DirEnumerator func(string) ([]os.FileInfo, error) 190 191 // ChaincodeExtractor extracts chaincode from a given path 192 type ChaincodeExtractor func(ccNameVersion string, path string, getHasher GetHasher) (CCPackage, error) 193 194 // ListInstalledChaincodes retrieves the installed chaincodes 195 func (cifs *CCInfoFSImpl) ListInstalledChaincodes(dir string, ls DirEnumerator, ccFromPath ChaincodeExtractor) ([]chaincode.InstalledChaincode, error) { 196 var chaincodes []chaincode.InstalledChaincode 197 if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) { 198 return nil, nil 199 } 200 files, err := ls(dir) 201 if err != nil { 202 return nil, errors.Wrapf(err, "failed reading directory %s", dir) 203 } 204 205 for _, f := range files { 206 // Skip directories, we're only interested in normal files 207 if f.IsDir() { 208 continue 209 } 210 // A chaincode file name is of the type "name.version" 211 // We're only interested in the name. 212 // Skip files that don't adhere to the file naming convention of "A.B" 213 i := strings.Index(f.Name(), ".") 214 if i == -1 { 215 ccproviderLogger.Info("Skipping", f.Name(), "because of missing separator '.'") 216 continue 217 } 218 ccName := f.Name()[:i] // Everything before the separator 219 ccVersion := f.Name()[i+1:] // Everything after the separator 220 221 ccPackage, err := ccFromPath(ccName+":"+ccVersion, dir, cifs.GetHasher) 222 if err != nil { 223 ccproviderLogger.Warning("Failed obtaining chaincode information about", ccName, ccVersion, ":", err) 224 return nil, errors.Wrapf(err, "failed obtaining information about %s, version %s", ccName, ccVersion) 225 } 226 227 chaincodes = append(chaincodes, chaincode.InstalledChaincode{ 228 Name: ccName, 229 Version: ccVersion, 230 Hash: ccPackage.GetId(), 231 }) 232 } 233 ccproviderLogger.Debug("Returning", chaincodes) 234 return chaincodes, nil 235 } 236 237 // ccInfoFSStorageMgr is the storage manager used either by the cache or if the 238 // cache is bypassed 239 var ccInfoFSProvider = &CCInfoFSImpl{GetHasher: factory.GetDefault()} 240 241 // ccInfoCache is the cache instance itself 242 var ccInfoCache = NewCCInfoCache(ccInfoFSProvider) 243 244 // GetChaincodeFromFS retrieves chaincode information from the file system 245 func GetChaincodeFromFS(ccNameVersion string) (CCPackage, error) { 246 return ccInfoFSProvider.GetChaincode(ccNameVersion) 247 } 248 249 // GetChaincodeData gets chaincode data from cache if there's one 250 func GetChaincodeData(ccNameVersion string) (*ChaincodeData, error) { 251 ccproviderLogger.Debugf("Getting chaincode data for <%s> from cache", ccNameVersion) 252 return ccInfoCache.GetChaincodeData(ccNameVersion) 253 } 254 255 // GetCCPackage tries each known package implementation one by one 256 // till the right package is found 257 func GetCCPackage(buf []byte, bccsp bccsp.BCCSP) (CCPackage, error) { 258 // try raw CDS 259 cds := &CDSPackage{GetHasher: bccsp} 260 if ccdata, err := cds.InitFromBuffer(buf); err != nil { 261 cds = nil 262 } else { 263 err = cds.ValidateCC(ccdata) 264 if err != nil { 265 cds = nil 266 } 267 } 268 269 // try signed CDS 270 scds := &SignedCDSPackage{GetHasher: bccsp} 271 if ccdata, err := scds.InitFromBuffer(buf); err != nil { 272 scds = nil 273 } else { 274 err = scds.ValidateCC(ccdata) 275 if err != nil { 276 scds = nil 277 } 278 } 279 280 if cds != nil && scds != nil { 281 // Both were unmarshaled successfully, this is exactly why the approach of 282 // hoping proto fails for bad inputs is fatally flawed. 283 ccproviderLogger.Errorf("Could not determine chaincode package type, guessing SignedCDS") 284 return scds, nil 285 } 286 287 if cds != nil { 288 return cds, nil 289 } 290 291 if scds != nil { 292 return scds, nil 293 } 294 295 return nil, errors.New("could not unmarshal chaincode package to CDS or SignedCDS") 296 } 297 298 // GetInstalledChaincodes returns a map whose key is the chaincode id and 299 // value is the ChaincodeDeploymentSpec struct for that chaincodes that have 300 // been installed (but not necessarily instantiated) on the peer by searching 301 // the chaincode install path 302 func GetInstalledChaincodes() (*pb.ChaincodeQueryResponse, error) { 303 files, err := ioutil.ReadDir(chaincodeInstallPath) 304 if err != nil { 305 return nil, err 306 } 307 308 // array to store info for all chaincode entries from LSCC 309 var ccInfoArray []*pb.ChaincodeInfo 310 311 for _, file := range files { 312 // split at first period as chaincode versions can contain periods while 313 // chaincode names cannot 314 fileNameArray := strings.SplitN(file.Name(), ".", 2) 315 316 // check that length is 2 as expected, otherwise skip to next cc file 317 if len(fileNameArray) == 2 { 318 ccname := fileNameArray[0] 319 ccversion := fileNameArray[1] 320 ccpack, err := GetChaincodeFromFS(ccname + ":" + ccversion) 321 if err != nil { 322 // either chaincode on filesystem has been tampered with or 323 // _lifecycle chaincode files exist in the chaincodes directory. 324 continue 325 } 326 327 cdsfs := ccpack.GetDepSpec() 328 329 name := cdsfs.GetChaincodeSpec().GetChaincodeId().Name 330 version := cdsfs.GetChaincodeSpec().GetChaincodeId().Version 331 if name != ccname || version != ccversion { 332 // chaincode name/version in the chaincode file name has been modified 333 // by an external entity 334 ccproviderLogger.Errorf("Chaincode file's name/version has been modified on the filesystem: %s", file.Name()) 335 continue 336 } 337 338 path := cdsfs.GetChaincodeSpec().ChaincodeId.Path 339 // since this is just an installed chaincode these should be blank 340 input, escc, vscc := "", "", "" 341 342 ccInfo := &pb.ChaincodeInfo{Name: name, Version: version, Path: path, Input: input, Escc: escc, Vscc: vscc, Id: ccpack.GetId()} 343 344 // add this specific chaincode's metadata to the array of all chaincodes 345 ccInfoArray = append(ccInfoArray, ccInfo) 346 } 347 } 348 // add array with info about all instantiated chaincodes to the query 349 // response proto 350 cqr := &pb.ChaincodeQueryResponse{Chaincodes: ccInfoArray} 351 352 return cqr, nil 353 } 354 355 //-------- ChaincodeData is stored on the LSCC ------- 356 357 // ChaincodeData defines the datastructure for chaincodes to be serialized by proto 358 // Type provides an additional check by directing to use a specific package after instantiation 359 // Data is Type specific (see CDSPackage and SignedCDSPackage) 360 type ChaincodeData struct { 361 // Name of the chaincode 362 Name string `protobuf:"bytes,1,opt,name=name"` 363 364 // Version of the chaincode 365 Version string `protobuf:"bytes,2,opt,name=version"` 366 367 // Escc for the chaincode instance 368 Escc string `protobuf:"bytes,3,opt,name=escc"` 369 370 // Vscc for the chaincode instance 371 Vscc string `protobuf:"bytes,4,opt,name=vscc"` 372 373 // Policy endorsement policy for the chaincode instance 374 Policy []byte `protobuf:"bytes,5,opt,name=policy,proto3"` 375 376 // Data data specific to the package 377 Data []byte `protobuf:"bytes,6,opt,name=data,proto3"` 378 379 // Id of the chaincode that's the unique fingerprint for the CC This is not 380 // currently used anywhere but serves as a good eyecatcher 381 Id []byte `protobuf:"bytes,7,opt,name=id,proto3"` 382 383 // InstantiationPolicy for the chaincode 384 InstantiationPolicy []byte `protobuf:"bytes,8,opt,name=instantiation_policy,proto3"` 385 } 386 387 // ChaincodeID is the name by which the chaincode will register itself. 388 func (cd *ChaincodeData) ChaincodeID() string { 389 return cd.Name + ":" + cd.Version 390 } 391 392 // implement functions needed from proto.Message for proto's mar/unmarshal functions 393 394 // Reset resets 395 func (cd *ChaincodeData) Reset() { *cd = ChaincodeData{} } 396 397 // String converts to string 398 func (cd *ChaincodeData) String() string { return proto.CompactTextString(cd) } 399 400 // ProtoMessage just exists to make proto happy 401 func (*ChaincodeData) ProtoMessage() {} 402 403 // TransactionParams are parameters which are tied to a particular transaction 404 // and which are required for invoking chaincode. 405 type TransactionParams struct { 406 TxID string 407 ChannelID string 408 NamespaceID string 409 SignedProp *pb.SignedProposal 410 Proposal *pb.Proposal 411 TXSimulator ledger.TxSimulator 412 HistoryQueryExecutor ledger.HistoryQueryExecutor 413 CollectionStore privdata.CollectionStore 414 IsInitTransaction bool 415 416 // this is additional data passed to the chaincode 417 ProposalDecorations map[string][]byte 418 }