github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/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 "strings" 25 26 "github.com/golang/protobuf/proto" 27 28 "github.com/hyperledger/fabric/common/flogging" 29 "github.com/hyperledger/fabric/core/ledger" 30 pb "github.com/hyperledger/fabric/protos/peer" 31 ) 32 33 var ccproviderLogger = flogging.MustGetLogger("ccprovider") 34 35 var chaincodeInstallPath string 36 37 //CCPackage encapsulates a chaincode package which can be 38 // raw ChaincodeDeploymentSpec 39 // SignedChaincodeDeploymentSpec 40 // Attempt to keep the interface at a level with minimal 41 // interface for possible generalization. 42 type CCPackage interface { 43 //InitFromBuffer initialize the package from bytes 44 InitFromBuffer(buf []byte) (*ChaincodeData, error) 45 46 // InitFromFS gets the chaincode from the filesystem (includes the raw bytes too) 47 InitFromFS(ccname string, ccversion string) ([]byte, *pb.ChaincodeDeploymentSpec, error) 48 49 // PutChaincodeToFS writes the chaincode to the filesystem 50 PutChaincodeToFS() error 51 52 // GetDepSpec gets the ChaincodeDeploymentSpec from the package 53 GetDepSpec() *pb.ChaincodeDeploymentSpec 54 55 // GetDepSpecBytes gets the serialized ChaincodeDeploymentSpec from the package 56 GetDepSpecBytes() []byte 57 58 // ValidateCC validates and returns the chaincode deployment spec corresponding to 59 // ChaincodeData. The validation is based on the metadata from ChaincodeData 60 // One use of this method is to validate the chaincode before launching 61 ValidateCC(ccdata *ChaincodeData) error 62 63 // GetPackageObject gets the object as a proto.Message 64 GetPackageObject() proto.Message 65 66 // GetChaincodeData gets the ChaincodeData 67 GetChaincodeData() *ChaincodeData 68 69 // GetId gets the fingerprint of the chaincode based on package computation 70 GetId() []byte 71 } 72 73 //SetChaincodesPath sets the chaincode path for this peer 74 func SetChaincodesPath(path string) { 75 if s, err := os.Stat(path); err != nil { 76 if os.IsNotExist(err) { 77 if err := os.Mkdir(path, 0755); err != nil { 78 panic(fmt.Sprintf("Could not create chaincodes install path: %s", err)) 79 } 80 } else { 81 panic(fmt.Sprintf("Could not stat chaincodes install path: %s", err)) 82 } 83 } else if !s.IsDir() { 84 panic(fmt.Errorf("chaincode path exists but not a dir: %s", path)) 85 } 86 87 chaincodeInstallPath = path 88 } 89 90 //GetChaincodePackage returns the chaincode package from the file system 91 func GetChaincodePackage(ccname string, ccversion string) ([]byte, error) { 92 path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion) 93 var ccbytes []byte 94 var err error 95 if ccbytes, err = ioutil.ReadFile(path); err != nil { 96 return nil, err 97 } 98 return ccbytes, nil 99 } 100 101 // GetChaincodeFromFS this is a wrapper for hiding package implementation. 102 func GetChaincodeFromFS(ccname string, ccversion string) (CCPackage, error) { 103 //try raw CDS 104 cccdspack := &CDSPackage{} 105 _, _, err := cccdspack.InitFromFS(ccname, ccversion) 106 if err != nil { 107 //try signed CDS 108 ccscdspack := &SignedCDSPackage{} 109 _, _, err = ccscdspack.InitFromFS(ccname, ccversion) 110 if err != nil { 111 return nil, err 112 } 113 return ccscdspack, nil 114 } 115 return cccdspack, nil 116 } 117 118 // PutChaincodeIntoFS is a wrapper for putting raw ChaincodeDeploymentSpec 119 //using CDSPackage. This is only used in UTs 120 func PutChaincodeIntoFS(depSpec *pb.ChaincodeDeploymentSpec) error { 121 buf, err := proto.Marshal(depSpec) 122 if err != nil { 123 return err 124 } 125 cccdspack := &CDSPackage{} 126 if _, err := cccdspack.InitFromBuffer(buf); err != nil { 127 return err 128 } 129 return cccdspack.PutChaincodeToFS() 130 } 131 132 // GetCCPackage tries each known package implementation one by one 133 // till the right package is found 134 func GetCCPackage(buf []byte) (CCPackage, error) { 135 //try raw CDS 136 cccdspack := &CDSPackage{} 137 if _, err := cccdspack.InitFromBuffer(buf); err != nil { 138 //try signed CDS 139 ccscdspack := &SignedCDSPackage{} 140 if _, err := ccscdspack.InitFromBuffer(buf); err != nil { 141 return nil, err 142 } 143 return ccscdspack, nil 144 } 145 return cccdspack, nil 146 } 147 148 // GetInstalledChaincodes returns a map whose key is the chaincode id and 149 // value is the ChaincodeDeploymentSpec struct for that chaincodes that have 150 // been installed (but not necessarily instantiated) on the peer by searching 151 // the chaincode install path 152 func GetInstalledChaincodes() (*pb.ChaincodeQueryResponse, error) { 153 files, err := ioutil.ReadDir(chaincodeInstallPath) 154 if err != nil { 155 return nil, err 156 } 157 158 // array to store info for all chaincode entries from LSCC 159 var ccInfoArray []*pb.ChaincodeInfo 160 161 for _, file := range files { 162 // split at first period as chaincode versions can contain periods while 163 // chaincode names cannot 164 fileNameArray := strings.SplitN(file.Name(), ".", 2) 165 166 // check that length is 2 as expected, otherwise skip to next cc file 167 if len(fileNameArray) == 2 { 168 ccname := fileNameArray[0] 169 ccversion := fileNameArray[1] 170 ccpack, err := GetChaincodeFromFS(ccname, ccversion) 171 if err != nil { 172 // either chaincode on filesystem has been tampered with or 173 // a non-chaincode file has been found in the chaincodes directory 174 ccproviderLogger.Errorf("Unreadable chaincode file found on filesystem: %s", file.Name()) 175 continue 176 } 177 178 cdsfs := ccpack.GetDepSpec() 179 180 name := cdsfs.GetChaincodeSpec().GetChaincodeId().Name 181 version := cdsfs.GetChaincodeSpec().GetChaincodeId().Version 182 if name != ccname || version != ccversion { 183 // chaincode name/version in the chaincode file name has been modified 184 // by an external entity 185 ccproviderLogger.Errorf("Chaincode file's name/version has been modified on the filesystem: %s", file.Name()) 186 continue 187 } 188 189 path := cdsfs.GetChaincodeSpec().ChaincodeId.Path 190 // since this is just an installed chaincode these should be blank 191 input, escc, vscc := "", "", "" 192 193 ccInfo := &pb.ChaincodeInfo{Name: name, Version: version, Path: path, Input: input, Escc: escc, Vscc: vscc} 194 195 // add this specific chaincode's metadata to the array of all chaincodes 196 ccInfoArray = append(ccInfoArray, ccInfo) 197 } 198 } 199 // add array with info about all instantiated chaincodes to the query 200 // response proto 201 cqr := &pb.ChaincodeQueryResponse{Chaincodes: ccInfoArray} 202 203 return cqr, nil 204 } 205 206 //CCContext pass this around instead of string of args 207 type CCContext struct { 208 //ChainID chain id 209 ChainID string 210 211 //Name chaincode name 212 Name string 213 214 //Version used to construct the chaincode image and register 215 Version string 216 217 //TxID is the transaction id for the proposal (if any) 218 TxID string 219 220 //Syscc is this a system chaincode 221 Syscc bool 222 223 //SignedProposal for this invoke (if any) 224 //this is kept here for access control and in case we need to pass something 225 //from this to the chaincode 226 SignedProposal *pb.SignedProposal 227 228 //Proposal for this invoke (if any) 229 //this is kept here just in case we need to pass something 230 //from this to the chaincode 231 Proposal *pb.Proposal 232 233 //this is not set but computed (note that this is not exported. use GetCanonicalName) 234 canonicalName string 235 } 236 237 //NewCCContext just construct a new struct with whatever args 238 func NewCCContext(cid, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) *CCContext { 239 //version CANNOT be empty. The chaincode namespace has to use version and chain name. 240 //All system chaincodes share the same version given by utils.GetSysCCVersion. Note 241 //that neither Chain Name or Version are stored in a chaincodes state on the ledger 242 if version == "" { 243 panic(fmt.Sprintf("---empty version---(chain=%s,chaincode=%s,version=%s,txid=%s,syscc=%t,proposal=%p", cid, name, version, txid, syscc, prop)) 244 } 245 246 canName := name + ":" + version 247 248 cccid := &CCContext{cid, name, version, txid, syscc, signedProp, prop, canName} 249 250 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) 251 252 return cccid 253 } 254 255 //GetCanonicalName returns the canonical name associated with the proposal context 256 func (cccid *CCContext) GetCanonicalName() string { 257 if cccid.canonicalName == "" { 258 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)) 259 } 260 261 return cccid.canonicalName 262 } 263 264 //-------- ChaincodeData is stored on the LSCC ------- 265 266 //ChaincodeData defines the datastructure for chaincodes to be serialized by proto 267 //Type provides an additional check by directing to use a specific package after instantiation 268 //Data is Type specifc (see CDSPackage and SignedCDSPackage) 269 type ChaincodeData struct { 270 //Name of the chaincode 271 Name string `protobuf:"bytes,1,opt,name=name"` 272 273 //Version of the chaincode 274 Version string `protobuf:"bytes,2,opt,name=version"` 275 276 //Escc for the chaincode instance 277 Escc string `protobuf:"bytes,3,opt,name=escc"` 278 279 //Vscc for the chaincode instance 280 Vscc string `protobuf:"bytes,4,opt,name=vscc"` 281 282 //Policy endorsement policy for the chaincode instance 283 Policy []byte `protobuf:"bytes,5,opt,name=policy,proto3"` 284 285 //Data data specific to the package 286 Data []byte `protobuf:"bytes,6,opt,name=data,proto3"` 287 288 //Id of the chaincode that's the unique fingerprint for the CC 289 //This is not currently used anywhere but serves as a good 290 //eyecatcher 291 Id []byte `protobuf:"bytes,7,opt,name=id,proto3"` 292 293 //InstantiationPolicy for the chaincode 294 InstantiationPolicy []byte `protobuf:"bytes,8,opt,name=instantiation_policy,proto3"` 295 } 296 297 //implement functions needed from proto.Message for proto's mar/unmarshal functions 298 299 //Reset resets 300 func (cd *ChaincodeData) Reset() { *cd = ChaincodeData{} } 301 302 //String convers to string 303 func (cd *ChaincodeData) String() string { return proto.CompactTextString(cd) } 304 305 //ProtoMessage just exists to make proto happy 306 func (*ChaincodeData) ProtoMessage() {} 307 308 // ChaincodeProvider provides an abstraction layer that is 309 // used for different packages to interact with code in the 310 // chaincode package without importing it; more methods 311 // should be added below if necessary 312 type ChaincodeProvider interface { 313 // GetContext returns a ledger context 314 GetContext(ledger ledger.PeerLedger) (context.Context, error) 315 // GetCCContext returns an opaque chaincode context 316 GetCCContext(cid, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) interface{} 317 // GetCCValidationInfoFromLSCC returns the VSCC and the policy listed by LSCC for the supplied chaincode 318 GetCCValidationInfoFromLSCC(ctxt context.Context, txid string, signedProp *pb.SignedProposal, prop *pb.Proposal, chainID string, chaincodeID string) (string, []byte, error) 319 // ExecuteChaincode executes the chaincode given context and args 320 ExecuteChaincode(ctxt context.Context, cccid interface{}, args [][]byte) (*pb.Response, *pb.ChaincodeEvent, error) 321 // Execute executes the chaincode given context and spec (invocation or deploy) 322 Execute(ctxt context.Context, cccid interface{}, spec interface{}) (*pb.Response, *pb.ChaincodeEvent, error) 323 // ExecuteWithErrorFilder executes the chaincode given context and spec and returns payload 324 ExecuteWithErrorFilter(ctxt context.Context, cccid interface{}, spec interface{}) ([]byte, *pb.ChaincodeEvent, error) 325 // Stop stops the chaincode given context and deployment spec 326 Stop(ctxt context.Context, cccid interface{}, spec *pb.ChaincodeDeploymentSpec) error 327 // ReleaseContext releases the context returned previously by GetContext 328 ReleaseContext() 329 } 330 331 var ccFactory ChaincodeProviderFactory 332 333 // ChaincodeProviderFactory defines a factory interface so 334 // that the actual implementation can be injected 335 type ChaincodeProviderFactory interface { 336 NewChaincodeProvider() ChaincodeProvider 337 } 338 339 // RegisterChaincodeProviderFactory is to be called once to set 340 // the factory that will be used to obtain instances of ChaincodeProvider 341 func RegisterChaincodeProviderFactory(ccfact ChaincodeProviderFactory) { 342 ccFactory = ccfact 343 } 344 345 // GetChaincodeProvider returns instances of ChaincodeProvider; 346 // the actual implementation is controlled by the factory that 347 // is registered via RegisterChaincodeProviderFactory 348 func GetChaincodeProvider() ChaincodeProvider { 349 if ccFactory == nil { 350 panic("The factory must be set first via RegisterChaincodeProviderFactory") 351 } 352 return ccFactory.NewChaincodeProvider() 353 }