github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/core/common/ccprovider/ccprovider.go (about) 1 /* 2 Copyright IBM Corp. 2017 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package ccprovider 18 19 import ( 20 "context" 21 "fmt" 22 "io/ioutil" 23 "os" 24 "path/filepath" 25 "strings" 26 27 "github.com/golang/protobuf/proto" 28 29 "bytes" 30 31 "github.com/hyperledger/fabric/common/flogging" 32 "github.com/hyperledger/fabric/core/ledger" 33 pb "github.com/hyperledger/fabric/protos/peer" 34 ) 35 36 var ccproviderLogger = flogging.MustGetLogger("ccprovider") 37 38 var chaincodeInstallPath string 39 40 //CCPackage encapsulates a chaincode package which can be 41 // raw ChaincodeDeploymentSpec 42 // SignedChaincodeDeploymentSpec 43 // Attempt to keep the interface at a level with minimal 44 // interface for possible generalization. 45 type CCPackage interface { 46 //InitFromBuffer initialize the package from bytes 47 InitFromBuffer(buf []byte) (*ChaincodeData, error) 48 49 // InitFromFS gets the chaincode from the filesystem (includes the raw bytes too) 50 InitFromFS(ccname string, ccversion string) ([]byte, *pb.ChaincodeDeploymentSpec, error) 51 52 // PutChaincodeToFS writes the chaincode to the filesystem 53 PutChaincodeToFS() error 54 55 // GetDepSpec gets the ChaincodeDeploymentSpec from the package 56 GetDepSpec() *pb.ChaincodeDeploymentSpec 57 58 // GetDepSpecBytes gets the serialized ChaincodeDeploymentSpec from the package 59 GetDepSpecBytes() []byte 60 61 // ValidateCC validates and returns the chaincode deployment spec corresponding to 62 // ChaincodeData. The validation is based on the metadata from ChaincodeData 63 // One use of this method is to validate the chaincode before launching 64 ValidateCC(ccdata *ChaincodeData) error 65 66 // GetPackageObject gets the object as a proto.Message 67 GetPackageObject() proto.Message 68 69 // GetChaincodeData gets the ChaincodeData 70 GetChaincodeData() *ChaincodeData 71 72 // GetId gets the fingerprint of the chaincode based on package computation 73 GetId() []byte 74 } 75 76 //SetChaincodesPath sets the chaincode path for this peer 77 func SetChaincodesPath(path string) { 78 if s, err := os.Stat(path); err != nil { 79 if os.IsNotExist(err) { 80 if err := os.Mkdir(path, 0755); err != nil { 81 panic(fmt.Sprintf("Could not create chaincodes install path: %s", err)) 82 } 83 } else { 84 panic(fmt.Sprintf("Could not stat chaincodes install path: %s", err)) 85 } 86 } else if !s.IsDir() { 87 panic(fmt.Errorf("chaincode path exists but not a dir: %s", path)) 88 } 89 90 chaincodeInstallPath = path 91 } 92 93 //GetChaincodePackage returns the chaincode package from the file system 94 func GetChaincodePackage(ccname string, ccversion string) ([]byte, error) { 95 path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion) 96 var ccbytes []byte 97 var err error 98 if ccbytes, err = ioutil.ReadFile(path); err != nil { 99 return nil, err 100 } 101 return ccbytes, nil 102 } 103 104 //ChaincodePackageExists returns whether the chaincode package exists in the file system 105 func ChaincodePackageExists(ccname string, ccversion string) (bool, error) { 106 path := filepath.Join(chaincodeInstallPath, ccname+"."+ccversion) 107 _, err := os.Stat(path) 108 if err == nil { 109 // chaincodepackage already exists 110 return true, nil 111 } 112 return false, err 113 } 114 115 type CCCacheSupport interface { 116 //GetChaincode is needed by the cache to get chaincode data 117 GetChaincode(ccname string, ccversion string) (CCPackage, error) 118 } 119 120 // CCInfoFSImpl provides the implementation for CC on the FS and the access to it 121 // It implements CCCacheSupport 122 type CCInfoFSImpl struct{} 123 124 // GetChaincodeFromFS this is a wrapper for hiding package implementation. 125 func (*CCInfoFSImpl) GetChaincode(ccname string, ccversion string) (CCPackage, error) { 126 //try raw CDS 127 cccdspack := &CDSPackage{} 128 _, _, err := cccdspack.InitFromFS(ccname, ccversion) 129 if err != nil { 130 //try signed CDS 131 ccscdspack := &SignedCDSPackage{} 132 _, _, err = ccscdspack.InitFromFS(ccname, ccversion) 133 if err != nil { 134 return nil, err 135 } 136 return ccscdspack, nil 137 } 138 return cccdspack, nil 139 } 140 141 // PutChaincodeIntoFS is a wrapper for putting raw ChaincodeDeploymentSpec 142 //using CDSPackage. This is only used in UTs 143 func (*CCInfoFSImpl) PutChaincode(depSpec *pb.ChaincodeDeploymentSpec) (CCPackage, error) { 144 buf, err := proto.Marshal(depSpec) 145 if err != nil { 146 return nil, err 147 } 148 cccdspack := &CDSPackage{} 149 if _, err := cccdspack.InitFromBuffer(buf); err != nil { 150 return nil, err 151 } 152 err = cccdspack.PutChaincodeToFS() 153 if err != nil { 154 return nil, err 155 } 156 157 return cccdspack, nil 158 } 159 160 // The following lines create the cache of CCPackage data that sits 161 // on top of the file system and avoids a trip to the file system 162 // every time. The cache is disabled by default and only enabled 163 // if EnableCCInfoCache is called. This is an unfortunate hack 164 // required by some legacy tests that remove chaincode packages 165 // from the file system as a means of simulating particular test 166 // conditions. This way of testing is incompatible with the 167 // immutable nature of chaincode packages that is assumed by hlf v1 168 // and implemented by this cache. For this reason, tests are for now 169 // allowed to run with the cache disabled (unless they enable it) 170 // until a later time in which they are fixed. The peer process on 171 // the other hand requires the benefits of this cache and therefore 172 // enables it. 173 // TODO: (post v1) enable cache by default as soon as https://jira.hyperledger.org/browse/FAB-3785 is completed 174 175 // ccInfoFSStorageMgr is the storage manager used either by the cache or if the 176 // cache is bypassed 177 var ccInfoFSProvider = &CCInfoFSImpl{} 178 179 // ccInfoCache is the cache instance itself 180 var ccInfoCache = NewCCInfoCache(ccInfoFSProvider) 181 182 // ccInfoCacheEnabled keeps track of whether the cache is enable 183 // (it is disabled by default) 184 var ccInfoCacheEnabled bool 185 186 // EnableCCInfoCache can be called to enable the cache 187 func EnableCCInfoCache() { 188 ccInfoCacheEnabled = true 189 } 190 191 // GetChaincodeFromFS retrieves chaincode information from the file system 192 func GetChaincodeFromFS(ccname string, ccversion string) (CCPackage, error) { 193 return ccInfoFSProvider.GetChaincode(ccname, ccversion) 194 } 195 196 // PutChaincodeIntoFS puts chaincode information in the file system (and 197 // also in the cache to prime it) if the cache is enabled, or directly 198 // from the file system otherwise 199 func PutChaincodeIntoFS(depSpec *pb.ChaincodeDeploymentSpec) error { 200 _, err := ccInfoFSProvider.PutChaincode(depSpec) 201 return err 202 } 203 204 // GetChaincodeData gets chaincode data from cache if there's one 205 func GetChaincodeData(ccname string, ccversion string) (*ChaincodeData, error) { 206 if ccInfoCacheEnabled { 207 ccproviderLogger.Debugf("Getting chaincode data for <%s, %s> from cache", ccname, ccversion) 208 return ccInfoCache.GetChaincodeData(ccname, ccversion) 209 } 210 if ccpack, err := ccInfoFSProvider.GetChaincode(ccname, ccversion); err != nil { 211 return nil, err 212 } else { 213 ccproviderLogger.Infof("Putting chaincode data for <%s, %s> into cache", ccname, ccversion) 214 return ccpack.GetChaincodeData(), nil 215 } 216 } 217 218 func CheckInsantiationPolicy(name, version string, cdLedger *ChaincodeData) error { 219 ccdata, err := GetChaincodeData(name, version) 220 if err != nil { 221 return err 222 } 223 224 // we have the info from the fs, check that the policy 225 // matches the one on the file system if one was specified; 226 // this check is required because the admin of this peer 227 // might have specified instantiation policies for their 228 // chaincode, for example to make sure that the chaincode 229 // is only instantiated on certain channels; a malicious 230 // peer on the other hand might have created a deploy 231 // transaction that attempts to bypass the instantiation 232 // policy. This check is there to ensure that this will not 233 // happen, i.e. that the peer will refuse to invoke the 234 // chaincode under these conditions. More info on 235 // https://jira.hyperledger.org/browse/FAB-3156 236 if ccdata.InstantiationPolicy != nil { 237 if !bytes.Equal(ccdata.InstantiationPolicy, cdLedger.InstantiationPolicy) { 238 return fmt.Errorf("Instantiation policy mismatch for cc %s/%s", name, version) 239 } 240 } 241 242 return nil 243 } 244 245 // GetCCPackage tries each known package implementation one by one 246 // till the right package is found 247 func GetCCPackage(buf []byte) (CCPackage, error) { 248 //try raw CDS 249 cccdspack := &CDSPackage{} 250 if _, err := cccdspack.InitFromBuffer(buf); err != nil { 251 //try signed CDS 252 ccscdspack := &SignedCDSPackage{} 253 if _, err := ccscdspack.InitFromBuffer(buf); err != nil { 254 return nil, err 255 } 256 return ccscdspack, nil 257 } 258 return cccdspack, nil 259 } 260 261 // GetInstalledChaincodes returns a map whose key is the chaincode id and 262 // value is the ChaincodeDeploymentSpec struct for that chaincodes that have 263 // been installed (but not necessarily instantiated) on the peer by searching 264 // the chaincode install path 265 func GetInstalledChaincodes() (*pb.ChaincodeQueryResponse, error) { 266 files, err := ioutil.ReadDir(chaincodeInstallPath) 267 if err != nil { 268 return nil, err 269 } 270 271 // array to store info for all chaincode entries from LSCC 272 var ccInfoArray []*pb.ChaincodeInfo 273 274 for _, file := range files { 275 // split at first period as chaincode versions can contain periods while 276 // chaincode names cannot 277 fileNameArray := strings.SplitN(file.Name(), ".", 2) 278 279 // check that length is 2 as expected, otherwise skip to next cc file 280 if len(fileNameArray) == 2 { 281 ccname := fileNameArray[0] 282 ccversion := fileNameArray[1] 283 ccpack, err := GetChaincodeFromFS(ccname, ccversion) 284 if err != nil { 285 // either chaincode on filesystem has been tampered with or 286 // a non-chaincode file has been found in the chaincodes directory 287 ccproviderLogger.Errorf("Unreadable chaincode file found on filesystem: %s", file.Name()) 288 continue 289 } 290 291 cdsfs := ccpack.GetDepSpec() 292 293 name := cdsfs.GetChaincodeSpec().GetChaincodeId().Name 294 version := cdsfs.GetChaincodeSpec().GetChaincodeId().Version 295 if name != ccname || version != ccversion { 296 // chaincode name/version in the chaincode file name has been modified 297 // by an external entity 298 ccproviderLogger.Errorf("Chaincode file's name/version has been modified on the filesystem: %s", file.Name()) 299 continue 300 } 301 302 path := cdsfs.GetChaincodeSpec().ChaincodeId.Path 303 // since this is just an installed chaincode these should be blank 304 input, escc, vscc := "", "", "" 305 306 ccInfo := &pb.ChaincodeInfo{Name: name, Version: version, Path: path, Input: input, Escc: escc, Vscc: vscc} 307 308 // add this specific chaincode's metadata to the array of all chaincodes 309 ccInfoArray = append(ccInfoArray, ccInfo) 310 } 311 } 312 // add array with info about all instantiated chaincodes to the query 313 // response proto 314 cqr := &pb.ChaincodeQueryResponse{Chaincodes: ccInfoArray} 315 316 return cqr, nil 317 } 318 319 //CCContext pass this around instead of string of args 320 type CCContext struct { 321 //ChainID chain id 322 ChainID string 323 324 //Name chaincode name 325 Name string 326 327 //Version used to construct the chaincode image and register 328 Version string 329 330 //TxID is the transaction id for the proposal (if any) 331 TxID string 332 333 //Syscc is this a system chaincode 334 Syscc bool 335 336 //SignedProposal for this invoke (if any) 337 //this is kept here for access control and in case we need to pass something 338 //from this to the chaincode 339 SignedProposal *pb.SignedProposal 340 341 //Proposal for this invoke (if any) 342 //this is kept here just in case we need to pass something 343 //from this to the chaincode 344 Proposal *pb.Proposal 345 346 //this is not set but computed (note that this is not exported. use GetCanonicalName) 347 canonicalName string 348 } 349 350 //NewCCContext just construct a new struct with whatever args 351 func NewCCContext(cid, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) *CCContext { 352 //version CANNOT be empty. The chaincode namespace has to use version and chain name. 353 //All system chaincodes share the same version given by utils.GetSysCCVersion. Note 354 //that neither Chain Name or Version are stored in a chaincodes state on the ledger 355 if version == "" { 356 panic(fmt.Sprintf("---empty version---(chain=%s,chaincode=%s,version=%s,txid=%s,syscc=%t,proposal=%p", cid, name, version, txid, syscc, prop)) 357 } 358 359 canName := name + ":" + version 360 361 cccid := &CCContext{cid, name, version, txid, syscc, signedProp, prop, canName} 362 363 ccproviderLogger.Debugf("NewCCCC (chain=%s,chaincode=%s,version=%s,txid=%s,syscc=%t,proposal=%p,canname=%s", cid, name, version, txid, syscc, prop, cccid.canonicalName) 364 365 return cccid 366 } 367 368 //GetCanonicalName returns the canonical name associated with the proposal context 369 func (cccid *CCContext) GetCanonicalName() string { 370 if cccid.canonicalName == "" { 371 panic(fmt.Sprintf("cccid not constructed using NewCCContext(chain=%s,chaincode=%s,version=%s,txid=%s,syscc=%t)", cccid.ChainID, cccid.Name, cccid.Version, cccid.TxID, cccid.Syscc)) 372 } 373 374 return cccid.canonicalName 375 } 376 377 //-------- ChaincodeData is stored on the LSCC ------- 378 379 //ChaincodeData defines the datastructure for chaincodes to be serialized by proto 380 //Type provides an additional check by directing to use a specific package after instantiation 381 //Data is Type specifc (see CDSPackage and SignedCDSPackage) 382 type ChaincodeData struct { 383 //Name of the chaincode 384 Name string `protobuf:"bytes,1,opt,name=name"` 385 386 //Version of the chaincode 387 Version string `protobuf:"bytes,2,opt,name=version"` 388 389 //Escc for the chaincode instance 390 Escc string `protobuf:"bytes,3,opt,name=escc"` 391 392 //Vscc for the chaincode instance 393 Vscc string `protobuf:"bytes,4,opt,name=vscc"` 394 395 //Policy endorsement policy for the chaincode instance 396 Policy []byte `protobuf:"bytes,5,opt,name=policy,proto3"` 397 398 //Data data specific to the package 399 Data []byte `protobuf:"bytes,6,opt,name=data,proto3"` 400 401 //Id of the chaincode that's the unique fingerprint for the CC 402 //This is not currently used anywhere but serves as a good 403 //eyecatcher 404 Id []byte `protobuf:"bytes,7,opt,name=id,proto3"` 405 406 //InstantiationPolicy for the chaincode 407 InstantiationPolicy []byte `protobuf:"bytes,8,opt,name=instantiation_policy,proto3"` 408 } 409 410 //implement functions needed from proto.Message for proto's mar/unmarshal functions 411 412 //Reset resets 413 func (cd *ChaincodeData) Reset() { *cd = ChaincodeData{} } 414 415 //String converts to string 416 func (cd *ChaincodeData) String() string { return proto.CompactTextString(cd) } 417 418 //ProtoMessage just exists to make proto happy 419 func (*ChaincodeData) ProtoMessage() {} 420 421 // ChaincodeProvider provides an abstraction layer that is 422 // used for different packages to interact with code in the 423 // chaincode package without importing it; more methods 424 // should be added below if necessary 425 type ChaincodeProvider interface { 426 // GetContext returns a ledger context 427 GetContext(ledger ledger.PeerLedger) (context.Context, error) 428 // GetCCContext returns an opaque chaincode context 429 GetCCContext(cid, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) interface{} 430 // GetCCValidationInfoFromLSCC returns the VSCC and the policy listed by LSCC for the supplied chaincode 431 GetCCValidationInfoFromLSCC(ctxt context.Context, txid string, signedProp *pb.SignedProposal, prop *pb.Proposal, chainID string, chaincodeID string) (string, []byte, error) 432 // ExecuteChaincode executes the chaincode given context and args 433 ExecuteChaincode(ctxt context.Context, cccid interface{}, args [][]byte) (*pb.Response, *pb.ChaincodeEvent, error) 434 // Execute executes the chaincode given context and spec (invocation or deploy) 435 Execute(ctxt context.Context, cccid interface{}, spec interface{}) (*pb.Response, *pb.ChaincodeEvent, error) 436 // ExecuteWithErrorFilder executes the chaincode given context and spec and returns payload 437 ExecuteWithErrorFilter(ctxt context.Context, cccid interface{}, spec interface{}) ([]byte, *pb.ChaincodeEvent, error) 438 // Stop stops the chaincode given context and deployment spec 439 Stop(ctxt context.Context, cccid interface{}, spec *pb.ChaincodeDeploymentSpec) error 440 // ReleaseContext releases the context returned previously by GetContext 441 ReleaseContext() 442 } 443 444 var ccFactory ChaincodeProviderFactory 445 446 // ChaincodeProviderFactory defines a factory interface so 447 // that the actual implementation can be injected 448 type ChaincodeProviderFactory interface { 449 NewChaincodeProvider() ChaincodeProvider 450 } 451 452 // RegisterChaincodeProviderFactory is to be called once to set 453 // the factory that will be used to obtain instances of ChaincodeProvider 454 func RegisterChaincodeProviderFactory(ccfact ChaincodeProviderFactory) { 455 ccFactory = ccfact 456 } 457 458 // GetChaincodeProvider returns instances of ChaincodeProvider; 459 // the actual implementation is controlled by the factory that 460 // is registered via RegisterChaincodeProviderFactory 461 func GetChaincodeProvider() ChaincodeProvider { 462 if ccFactory == nil { 463 panic("The factory must be set first via RegisterChaincodeProviderFactory") 464 } 465 return ccFactory.NewChaincodeProvider() 466 }