github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/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/core/ledger" 29 pb "github.com/hyperledger/fabric/protos/peer" 30 31 logging "github.com/op/go-logging" 32 ) 33 34 var ccproviderLogger = logging.MustGetLogger("ccprovider") 35 36 var chaincodeInstallPath string 37 38 //SetChaincodesPath sets the chaincode path for this peer 39 func SetChaincodesPath(path string) { 40 if s, err := os.Stat(path); err != nil { 41 if os.IsNotExist(err) { 42 if err := os.Mkdir(path, 0755); err != nil { 43 panic(fmt.Sprintf("Could not create chaincodes install path: %s", err)) 44 } 45 } else { 46 panic(fmt.Sprintf("Could not stat chaincodes install path: %s", err)) 47 } 48 } else if !s.IsDir() { 49 panic(fmt.Errorf("chaincode path exists but not a dir: %s", path)) 50 } 51 52 chaincodeInstallPath = path 53 } 54 55 //GetChaincodePackage returns the chaincode package from the file system 56 func GetChaincodePackage(ccname string, ccversion string) ([]byte, error) { 57 path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion) 58 var ccbytes []byte 59 var err error 60 if ccbytes, err = ioutil.ReadFile(path); err != nil { 61 return nil, err 62 } 63 return ccbytes, nil 64 } 65 66 //GetChaincodeFromFS returns the chaincode and its package from the file system 67 func GetChaincodeFromFS(ccname string, ccversion string) ([]byte, *pb.ChaincodeDeploymentSpec, error) { 68 //NOTE- this is the only place from where we get code from file system 69 //this API needs to be modified to take other params for security. 70 //this implementation needs to be enhanced to do those security checks 71 ccbytes, err := GetChaincodePackage(ccname, ccversion) 72 if err != nil { 73 return nil, nil, err 74 } 75 76 cdsfs := &pb.ChaincodeDeploymentSpec{} 77 err = proto.Unmarshal(ccbytes, cdsfs) 78 if err != nil { 79 return nil, nil, fmt.Errorf("failed to unmarshal fs deployment spec for %s, %s", ccname, ccversion) 80 } 81 82 return ccbytes, cdsfs, nil 83 } 84 85 //PutChaincodeIntoFS - serializes chaincode to a package on the file system 86 func PutChaincodeIntoFS(depSpec *pb.ChaincodeDeploymentSpec) error { 87 //NOTE- this is only place from where we put code into file system 88 //this API needs to be modified to take other params for security. 89 //this implementation needs to be enhanced to do those security checks 90 ccname := depSpec.ChaincodeSpec.ChaincodeId.Name 91 ccversion := depSpec.ChaincodeSpec.ChaincodeId.Version 92 93 //return error if chaincode exists 94 path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion) 95 if _, err := os.Stat(path); err == nil { 96 return fmt.Errorf("chaincode %s exists", path) 97 } 98 99 b, err := proto.Marshal(depSpec) 100 if err != nil { 101 return fmt.Errorf("failed to marshal fs deployment spec for %s, %s", ccname, ccversion) 102 } 103 104 if err = ioutil.WriteFile(path, b, 0644); err != nil { 105 return err 106 } 107 108 return nil 109 } 110 111 // GetInstalledChaincodes returns a map whose key is the chaincode id and 112 // value is the ChaincodeDeploymentSpec struct for that chaincodes that have 113 // been installed (but not necessarily instantiated) on the peer by searching 114 // the chaincode install path 115 func GetInstalledChaincodes() (*pb.ChaincodeQueryResponse, error) { 116 files, err := ioutil.ReadDir(chaincodeInstallPath) 117 if err != nil { 118 return nil, err 119 } 120 121 // array to store info for all chaincode entries from LCCC 122 var ccInfoArray []*pb.ChaincodeInfo 123 124 for _, file := range files { 125 fileNameArray := strings.Split(file.Name(), ".") 126 127 // check that length is 2 as expected, otherwise skip to next cc file 128 if len(fileNameArray) == 2 { 129 ccname := fileNameArray[0] 130 ccversion := fileNameArray[1] 131 _, cdsfs, err := GetChaincodeFromFS(ccname, ccversion) 132 if err != nil { 133 // either chaincode on filesystem has been tampered with or 134 // a non-chaincode file has been found in the chaincodes directory 135 ccproviderLogger.Errorf("Unreadable chaincode file found on filesystem: %s", file.Name()) 136 continue 137 } 138 139 name := cdsfs.GetChaincodeSpec().GetChaincodeId().Name 140 version := cdsfs.GetChaincodeSpec().GetChaincodeId().Version 141 if name != ccname || version != ccversion { 142 // chaincode name/version in the chaincode file name has been modified 143 // by an external entity 144 ccproviderLogger.Errorf("Chaincode file's name/version has been modified on the filesystem: %s", file.Name()) 145 continue 146 } 147 148 path := cdsfs.GetChaincodeSpec().ChaincodeId.Path 149 // since this is just an installed chaincode these should be blank 150 input, escc, vscc := "", "", "" 151 152 ccInfo := &pb.ChaincodeInfo{Name: name, Version: version, Path: path, Input: input, Escc: escc, Vscc: vscc} 153 154 // add this specific chaincode's metadata to the array of all chaincodes 155 ccInfoArray = append(ccInfoArray, ccInfo) 156 } 157 } 158 // add array with info about all instantiated chaincodes to the query 159 // response proto 160 cqr := &pb.ChaincodeQueryResponse{Chaincodes: ccInfoArray} 161 162 return cqr, nil 163 } 164 165 //CCContext pass this around instead of string of args 166 type CCContext struct { 167 //ChainID chain id 168 ChainID string 169 170 //Name chaincode name 171 Name string 172 173 //Version used to construct the chaincode image and register 174 Version string 175 176 //TxID is the transaction id for the proposal (if any) 177 TxID string 178 179 //Syscc is this a system chaincode 180 Syscc bool 181 182 //SignedProposal for this invoke (if any) 183 //this is kept here for access control and in case we need to pass something 184 //from this to the chaincode 185 SignedProposal *pb.SignedProposal 186 187 //Proposal for this invoke (if any) 188 //this is kept here just in case we need to pass something 189 //from this to the chaincode 190 Proposal *pb.Proposal 191 192 //this is not set but computed (note that this is not exported. use GetCanonicalName) 193 canonicalName string 194 } 195 196 //NewCCContext just construct a new struct with whatever args 197 func NewCCContext(cid, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) *CCContext { 198 //version CANNOT be empty. The chaincode namespace has to use version and chain name. 199 //All system chaincodes share the same version given by utils.GetSysCCVersion. Note 200 //that neither Chain Name or Version are stored in a chaincodes state on the ledger 201 if version == "" { 202 panic(fmt.Sprintf("---empty version---(chain=%s,chaincode=%s,version=%s,txid=%s,syscc=%t,proposal=%p", cid, name, version, txid, syscc, prop)) 203 } 204 205 canName := name + ":" + version 206 207 cccid := &CCContext{cid, name, version, txid, syscc, signedProp, prop, canName} 208 209 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) 210 211 return cccid 212 } 213 214 //GetCanonicalName returns the canonical name associated with the proposal context 215 func (cccid *CCContext) GetCanonicalName() string { 216 if cccid.canonicalName == "" { 217 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)) 218 } 219 220 return cccid.canonicalName 221 } 222 223 //ChaincodeData defines the datastructure for chaincodes to be serialized by proto 224 type ChaincodeData struct { 225 Name string `protobuf:"bytes,1,opt,name=name"` 226 Version string `protobuf:"bytes,2,opt,name=version"` 227 DepSpec []byte `protobuf:"bytes,3,opt,name=depSpec,proto3"` 228 Escc string `protobuf:"bytes,4,opt,name=escc"` 229 Vscc string `protobuf:"bytes,5,opt,name=vscc"` 230 Policy []byte `protobuf:"bytes,6,opt,name=policy"` 231 } 232 233 //implement functions needed from proto.Message for proto's mar/unmarshal functions 234 235 //Reset resets 236 func (cd *ChaincodeData) Reset() { *cd = ChaincodeData{} } 237 238 //String convers to string 239 func (cd *ChaincodeData) String() string { return proto.CompactTextString(cd) } 240 241 //ProtoMessage just exists to make proto happy 242 func (*ChaincodeData) ProtoMessage() {} 243 244 // ChaincodeProvider provides an abstraction layer that is 245 // used for different packages to interact with code in the 246 // chaincode package without importing it; more methods 247 // should be added below if necessary 248 type ChaincodeProvider interface { 249 // GetContext returns a ledger context 250 GetContext(ledger ledger.PeerLedger) (context.Context, error) 251 // GetCCContext returns an opaque chaincode context 252 GetCCContext(cid, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) interface{} 253 // GetCCValidationInfoFromLCCC returns the VSCC and the policy listed by LCCC for the supplied chaincode 254 GetCCValidationInfoFromLCCC(ctxt context.Context, txid string, signedProp *pb.SignedProposal, prop *pb.Proposal, chainID string, chaincodeID string) (string, []byte, error) 255 // ExecuteChaincode executes the chaincode given context and args 256 ExecuteChaincode(ctxt context.Context, cccid interface{}, args [][]byte) (*pb.Response, *pb.ChaincodeEvent, error) 257 // Execute executes the chaincode given context and spec (invocation or deploy) 258 Execute(ctxt context.Context, cccid interface{}, spec interface{}) (*pb.Response, *pb.ChaincodeEvent, error) 259 // ExecuteWithErrorFilder executes the chaincode given context and spec and returns payload 260 ExecuteWithErrorFilter(ctxt context.Context, cccid interface{}, spec interface{}) ([]byte, *pb.ChaincodeEvent, error) 261 // Stop stops the chaincode given context and deployment spec 262 Stop(ctxt context.Context, cccid interface{}, spec *pb.ChaincodeDeploymentSpec) error 263 // ReleaseContext releases the context returned previously by GetContext 264 ReleaseContext() 265 } 266 267 var ccFactory ChaincodeProviderFactory 268 269 // ChaincodeProviderFactory defines a factory interface so 270 // that the actual implementation can be injected 271 type ChaincodeProviderFactory interface { 272 NewChaincodeProvider() ChaincodeProvider 273 } 274 275 // RegisterChaincodeProviderFactory is to be called once to set 276 // the factory that will be used to obtain instances of ChaincodeProvider 277 func RegisterChaincodeProviderFactory(ccfact ChaincodeProviderFactory) { 278 ccFactory = ccfact 279 } 280 281 // GetChaincodeProvider returns instances of ChaincodeProvider; 282 // the actual implementation is controlled by the factory that 283 // is registered via RegisterChaincodeProviderFactory 284 func GetChaincodeProvider() ChaincodeProvider { 285 if ccFactory == nil { 286 panic("The factory must be set first via RegisterChaincodeProviderFactory") 287 } 288 return ccFactory.NewChaincodeProvider() 289 }