github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/core/chaincode/chaincode_support.go (about) 1 /* 2 Copyright IBM Corp. 2016 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 chaincode 18 19 import ( 20 "fmt" 21 "io" 22 "path/filepath" 23 "strconv" 24 "sync" 25 "time" 26 27 "github.com/golang/protobuf/proto" 28 logging "github.com/op/go-logging" 29 "github.com/spf13/viper" 30 "golang.org/x/net/context" 31 32 "strings" 33 34 "github.com/hyperledger/fabric/common/flogging" 35 "github.com/hyperledger/fabric/core/chaincode/platforms" 36 "github.com/hyperledger/fabric/core/chaincode/shim" 37 "github.com/hyperledger/fabric/core/common/ccprovider" 38 "github.com/hyperledger/fabric/core/config" 39 "github.com/hyperledger/fabric/core/container" 40 "github.com/hyperledger/fabric/core/container/api" 41 "github.com/hyperledger/fabric/core/container/ccintf" 42 "github.com/hyperledger/fabric/core/ledger" 43 pb "github.com/hyperledger/fabric/protos/peer" 44 ) 45 46 type key string 47 48 const ( 49 // DevModeUserRunsChaincode property allows user to run chaincode in development environment 50 DevModeUserRunsChaincode string = "dev" 51 chaincodeStartupTimeoutDefault int = 5000 52 peerAddressDefault string = "0.0.0.0:7051" 53 54 //TXSimulatorKey is used to attach ledger simulation context 55 TXSimulatorKey key = "txsimulatorkey" 56 57 //HistoryQueryExecutorKey is used to attach ledger history query executor context 58 HistoryQueryExecutorKey key = "historyqueryexecutorkey" 59 ) 60 61 //this is basically the singleton that supports the 62 //entire chaincode framework. It does NOT know about 63 //chains. Chains are per-proposal entities that are 64 //setup as part of "join" and go through this object 65 //via calls to Execute and Deploy chaincodes. 66 var theChaincodeSupport *ChaincodeSupport 67 68 //use this for ledger access and make sure TXSimulator is being used 69 func getTxSimulator(context context.Context) ledger.TxSimulator { 70 if txsim, ok := context.Value(TXSimulatorKey).(ledger.TxSimulator); ok { 71 return txsim 72 } 73 //chaincode will not allow state operations 74 return nil 75 } 76 77 //use this for ledger access and make sure HistoryQueryExecutor is being used 78 func getHistoryQueryExecutor(context context.Context) ledger.HistoryQueryExecutor { 79 if historyQueryExecutor, ok := context.Value(HistoryQueryExecutorKey).(ledger.HistoryQueryExecutor); ok { 80 return historyQueryExecutor 81 } 82 //chaincode will not allow state operations 83 return nil 84 } 85 86 // 87 //chaincode runtime environment encapsulates handler and container environment 88 //This is where the VM that's running the chaincode would hook in 89 type chaincodeRTEnv struct { 90 handler *Handler 91 } 92 93 // runningChaincodes contains maps of chaincodeIDs to their chaincodeRTEs 94 type runningChaincodes struct { 95 sync.RWMutex 96 // chaincode environment for each chaincode 97 chaincodeMap map[string]*chaincodeRTEnv 98 } 99 100 //GetChain returns the chaincode framework support object 101 func GetChain() *ChaincodeSupport { 102 return theChaincodeSupport 103 } 104 105 func (chaincodeSupport *ChaincodeSupport) preLaunchSetup(chaincode string) chan bool { 106 chaincodeSupport.runningChaincodes.Lock() 107 defer chaincodeSupport.runningChaincodes.Unlock() 108 //register placeholder Handler. This will be transferred in registerHandler 109 //NOTE: from this point, existence of handler for this chaincode means the chaincode 110 //is in the process of getting started (or has been started) 111 notfy := make(chan bool, 1) 112 chaincodeSupport.runningChaincodes.chaincodeMap[chaincode] = &chaincodeRTEnv{handler: &Handler{readyNotify: notfy}} 113 return notfy 114 } 115 116 //call this under lock 117 func (chaincodeSupport *ChaincodeSupport) chaincodeHasBeenLaunched(chaincode string) (*chaincodeRTEnv, bool) { 118 chrte, hasbeenlaunched := chaincodeSupport.runningChaincodes.chaincodeMap[chaincode] 119 return chrte, hasbeenlaunched 120 } 121 122 // NewChaincodeSupport creates a new ChaincodeSupport instance 123 func NewChaincodeSupport(getCCEndpoint func() (*pb.PeerEndpoint, error), userrunsCC bool, ccstartuptimeout time.Duration) *ChaincodeSupport { 124 ccprovider.SetChaincodesPath(config.GetPath("peer.fileSystemPath") + string(filepath.Separator) + "chaincodes") 125 126 pnid := viper.GetString("peer.networkId") 127 pid := viper.GetString("peer.id") 128 129 theChaincodeSupport = &ChaincodeSupport{runningChaincodes: &runningChaincodes{chaincodeMap: make(map[string]*chaincodeRTEnv)}, peerNetworkID: pnid, peerID: pid} 130 131 //initialize global chain 132 133 ccEndpoint, err := getCCEndpoint() 134 if err != nil { 135 chaincodeLogger.Errorf("Error getting chaincode endpoint, using chaincode.peerAddress: %s", err) 136 theChaincodeSupport.peerAddress = viper.GetString("chaincode.peerAddress") 137 } else { 138 theChaincodeSupport.peerAddress = ccEndpoint.Address 139 } 140 chaincodeLogger.Infof("Chaincode support using peerAddress: %s\n", theChaincodeSupport.peerAddress) 141 //peerAddress = viper.GetString("peer.address") 142 if theChaincodeSupport.peerAddress == "" { 143 theChaincodeSupport.peerAddress = peerAddressDefault 144 } 145 146 theChaincodeSupport.userRunsCC = userrunsCC 147 148 theChaincodeSupport.ccStartupTimeout = ccstartuptimeout 149 150 theChaincodeSupport.peerTLS = viper.GetBool("peer.tls.enabled") 151 if theChaincodeSupport.peerTLS { 152 theChaincodeSupport.peerTLSCertFile = config.GetPath("peer.tls.cert.file") 153 theChaincodeSupport.peerTLSKeyFile = config.GetPath("peer.tls.key.file") 154 theChaincodeSupport.peerTLSSvrHostOrd = viper.GetString("peer.tls.serverhostoverride") 155 } 156 157 kadef := 0 158 if ka := viper.GetString("chaincode.keepalive"); ka == "" { 159 theChaincodeSupport.keepalive = time.Duration(kadef) * time.Second 160 } else { 161 t, terr := strconv.Atoi(ka) 162 if terr != nil { 163 chaincodeLogger.Errorf("Invalid keepalive value %s (%s) defaulting to %d", ka, terr, kadef) 164 t = kadef 165 } else if t <= 0 { 166 chaincodeLogger.Debugf("Turn off keepalive(value %s)", ka) 167 t = kadef 168 } 169 theChaincodeSupport.keepalive = time.Duration(t) * time.Second 170 } 171 172 //default chaincode execute timeout is 30 secs 173 execto := time.Duration(30) * time.Second 174 if eto := viper.GetDuration("chaincode.executetimeout"); eto <= time.Duration(1)*time.Second { 175 chaincodeLogger.Errorf("Invalid execute timeout value %s (should be at least 1s); defaulting to %s", eto, execto) 176 } else { 177 chaincodeLogger.Debugf("Setting execute timeout value to %s", eto) 178 execto = eto 179 } 180 181 theChaincodeSupport.executetimeout = execto 182 183 viper.SetEnvPrefix("CORE") 184 viper.AutomaticEnv() 185 replacer := strings.NewReplacer(".", "_") 186 viper.SetEnvKeyReplacer(replacer) 187 188 theChaincodeSupport.chaincodeLogLevel = getLogLevelFromViper("level") 189 theChaincodeSupport.shimLogLevel = getLogLevelFromViper("shim") 190 theChaincodeSupport.logFormat = viper.GetString("chaincode.logging.format") 191 192 return theChaincodeSupport 193 } 194 195 // getLogLevelFromViper gets the chaincode container log levels from viper 196 func getLogLevelFromViper(module string) string { 197 levelString := viper.GetString("chaincode.logging." + module) 198 _, err := logging.LogLevel(levelString) 199 200 if err == nil { 201 chaincodeLogger.Debugf("CORE_CHAINCODE_%s set to level %s", strings.ToUpper(module), levelString) 202 } else { 203 chaincodeLogger.Warningf("CORE_CHAINCODE_%s has invalid log level %s. defaulting to %s", strings.ToUpper(module), levelString, flogging.DefaultLevel()) 204 levelString = flogging.DefaultLevel() 205 } 206 return levelString 207 } 208 209 // // ChaincodeStream standard stream for ChaincodeMessage type. 210 // type ChaincodeStream interface { 211 // Send(*pb.ChaincodeMessage) error 212 // Recv() (*pb.ChaincodeMessage, error) 213 // } 214 215 // ChaincodeSupport responsible for providing interfacing with chaincodes from the Peer. 216 type ChaincodeSupport struct { 217 runningChaincodes *runningChaincodes 218 peerAddress string 219 ccStartupTimeout time.Duration 220 peerNetworkID string 221 peerID string 222 peerTLSCertFile string 223 peerTLSKeyFile string 224 peerTLSSvrHostOrd string 225 keepalive time.Duration 226 chaincodeLogLevel string 227 shimLogLevel string 228 logFormat string 229 executetimeout time.Duration 230 userRunsCC bool 231 peerTLS bool 232 } 233 234 // DuplicateChaincodeHandlerError returned if attempt to register same chaincodeID while a stream already exists. 235 type DuplicateChaincodeHandlerError struct { 236 ChaincodeID *pb.ChaincodeID 237 } 238 239 func (d *DuplicateChaincodeHandlerError) Error() string { 240 return fmt.Sprintf("Duplicate chaincodeID error: %s", d.ChaincodeID) 241 } 242 243 func newDuplicateChaincodeHandlerError(chaincodeHandler *Handler) error { 244 return &DuplicateChaincodeHandlerError{ChaincodeID: chaincodeHandler.ChaincodeID} 245 } 246 247 func (chaincodeSupport *ChaincodeSupport) registerHandler(chaincodehandler *Handler) error { 248 key := chaincodehandler.ChaincodeID.Name 249 250 chaincodeSupport.runningChaincodes.Lock() 251 defer chaincodeSupport.runningChaincodes.Unlock() 252 253 chrte2, ok := chaincodeSupport.chaincodeHasBeenLaunched(key) 254 if ok && chrte2.handler.registered == true { 255 chaincodeLogger.Debugf("duplicate registered handler(key:%s) return error", key) 256 // Duplicate, return error 257 return newDuplicateChaincodeHandlerError(chaincodehandler) 258 } 259 //a placeholder, unregistered handler will be setup by transaction processing that comes 260 //through via consensus. In this case we swap the handler and give it the notify channel 261 if chrte2 != nil { 262 chaincodehandler.readyNotify = chrte2.handler.readyNotify 263 chrte2.handler = chaincodehandler 264 } else { 265 if chaincodeSupport.userRunsCC == false { 266 //this chaincode was not launched by the peer and is attempting 267 //to register. Don't allow this. 268 return fmt.Errorf("peer will not accepting external chaincode connection %v (except in dev mode)", chaincodehandler.ChaincodeID) 269 } 270 chaincodeSupport.runningChaincodes.chaincodeMap[key] = &chaincodeRTEnv{handler: chaincodehandler} 271 } 272 273 chaincodehandler.registered = true 274 275 //now we are ready to receive messages and send back responses 276 chaincodehandler.txCtxs = make(map[string]*transactionContext) 277 chaincodehandler.txidMap = make(map[string]bool) 278 279 chaincodeLogger.Debugf("registered handler complete for chaincode %s", key) 280 281 return nil 282 } 283 284 func (chaincodeSupport *ChaincodeSupport) deregisterHandler(chaincodehandler *Handler) error { 285 286 // clean up queryIteratorMap 287 for _, context := range chaincodehandler.txCtxs { 288 for _, v := range context.queryIteratorMap { 289 v.Close() 290 } 291 } 292 293 key := chaincodehandler.ChaincodeID.Name 294 chaincodeLogger.Debugf("Deregister handler: %s", key) 295 chaincodeSupport.runningChaincodes.Lock() 296 defer chaincodeSupport.runningChaincodes.Unlock() 297 if _, ok := chaincodeSupport.chaincodeHasBeenLaunched(key); !ok { 298 // Handler NOT found 299 return fmt.Errorf("Error deregistering handler, could not find handler with key: %s", key) 300 } 301 delete(chaincodeSupport.runningChaincodes.chaincodeMap, key) 302 chaincodeLogger.Debugf("Deregistered handler with key: %s", key) 303 return nil 304 } 305 306 // send ready to move to ready state 307 func (chaincodeSupport *ChaincodeSupport) sendReady(context context.Context, cccid *ccprovider.CCContext, timeout time.Duration) error { 308 canName := cccid.GetCanonicalName() 309 chaincodeSupport.runningChaincodes.Lock() 310 //if its in the map, there must be a connected stream...nothing to do 311 var chrte *chaincodeRTEnv 312 var ok bool 313 if chrte, ok = chaincodeSupport.chaincodeHasBeenLaunched(canName); !ok { 314 chaincodeSupport.runningChaincodes.Unlock() 315 chaincodeLogger.Debugf("handler not found for chaincode %s", canName) 316 return fmt.Errorf("handler not found for chaincode %s", canName) 317 } 318 chaincodeSupport.runningChaincodes.Unlock() 319 320 var notfy chan *pb.ChaincodeMessage 321 var err error 322 if notfy, err = chrte.handler.ready(context, cccid.ChainID, cccid.TxID, cccid.SignedProposal, cccid.Proposal); err != nil { 323 return fmt.Errorf("Error sending %s: %s", pb.ChaincodeMessage_READY, err) 324 } 325 if notfy != nil { 326 select { 327 case ccMsg := <-notfy: 328 if ccMsg.Type == pb.ChaincodeMessage_ERROR { 329 err = fmt.Errorf("Error initializing container %s: %s", canName, string(ccMsg.Payload)) 330 } 331 if ccMsg.Type == pb.ChaincodeMessage_COMPLETED { 332 res := &pb.Response{} 333 _ = proto.Unmarshal(ccMsg.Payload, res) 334 if res.Status != shim.OK { 335 err = fmt.Errorf("Error initializing container %s: %s", canName, string(res.Message)) 336 } 337 // TODO 338 // return res so that endorser can anylyze it. 339 } 340 case <-time.After(timeout): 341 err = fmt.Errorf("Timeout expired while executing send init message") 342 } 343 } 344 345 //if initOrReady succeeded, our responsibility to delete the context 346 chrte.handler.deleteTxContext(cccid.TxID) 347 348 return err 349 } 350 351 //get args and env given chaincodeID 352 func (chaincodeSupport *ChaincodeSupport) getArgsAndEnv(cccid *ccprovider.CCContext, cLang pb.ChaincodeSpec_Type) (args []string, envs []string, err error) { 353 canName := cccid.GetCanonicalName() 354 envs = []string{"CORE_CHAINCODE_ID_NAME=" + canName} 355 356 // ---------------------------------------------------------------------------- 357 // Pass TLS options to chaincode 358 // ---------------------------------------------------------------------------- 359 // Note: The peer certificate is only baked into the image during the build 360 // phase (see core/chaincode/platforms). This logic below merely assumes the 361 // image is already configured appropriately and is simply toggling the feature 362 // on or off. If the peer's x509 has changed since the chaincode was deployed, 363 // the image may be stale and the admin will need to remove the current containers 364 // before restarting the peer. 365 // ---------------------------------------------------------------------------- 366 if chaincodeSupport.peerTLS { 367 envs = append(envs, "CORE_PEER_TLS_ENABLED=true") 368 if chaincodeSupport.peerTLSSvrHostOrd != "" { 369 envs = append(envs, "CORE_PEER_TLS_SERVERHOSTOVERRIDE="+chaincodeSupport.peerTLSSvrHostOrd) 370 } 371 } else { 372 envs = append(envs, "CORE_PEER_TLS_ENABLED=false") 373 } 374 375 if chaincodeSupport.chaincodeLogLevel != "" { 376 envs = append(envs, "CORE_CHAINCODE_LOGGING_LEVEL="+chaincodeSupport.chaincodeLogLevel) 377 } 378 379 if chaincodeSupport.shimLogLevel != "" { 380 envs = append(envs, "CORE_CHAINCODE_LOGGING_SHIM="+chaincodeSupport.shimLogLevel) 381 } 382 383 if chaincodeSupport.logFormat != "" { 384 envs = append(envs, "CORE_CHAINCODE_LOGGING_FORMAT="+chaincodeSupport.logFormat) 385 } 386 switch cLang { 387 case pb.ChaincodeSpec_GOLANG, pb.ChaincodeSpec_CAR: 388 args = []string{"chaincode", fmt.Sprintf("-peer.address=%s", chaincodeSupport.peerAddress)} 389 case pb.ChaincodeSpec_JAVA: 390 args = []string{"java", "-jar", "chaincode.jar", "--peerAddress", chaincodeSupport.peerAddress} 391 default: 392 return nil, nil, fmt.Errorf("Unknown chaincodeType: %s", cLang) 393 } 394 chaincodeLogger.Debugf("Executable is %s", args[0]) 395 chaincodeLogger.Debugf("Args %v", args) 396 return args, envs, nil 397 } 398 399 // launchAndWaitForRegister will launch container if not already running. Use the targz to create the image if not found 400 func (chaincodeSupport *ChaincodeSupport) launchAndWaitForRegister(ctxt context.Context, cccid *ccprovider.CCContext, cds *pb.ChaincodeDeploymentSpec, cLang pb.ChaincodeSpec_Type, builder api.BuildSpecFactory) error { 401 canName := cccid.GetCanonicalName() 402 if canName == "" { 403 return fmt.Errorf("chaincode name not set") 404 } 405 406 chaincodeSupport.runningChaincodes.Lock() 407 //if its in the map, its either up or being launched. Either case break the 408 //multiple launch by failing 409 if _, hasBeenLaunched := chaincodeSupport.chaincodeHasBeenLaunched(canName); hasBeenLaunched { 410 chaincodeSupport.runningChaincodes.Unlock() 411 return fmt.Errorf("Error chaincode is being launched: %s", canName) 412 } 413 414 chaincodeSupport.runningChaincodes.Unlock() 415 416 //launch the chaincode 417 418 args, env, err := chaincodeSupport.getArgsAndEnv(cccid, cLang) 419 if err != nil { 420 return err 421 } 422 423 chaincodeLogger.Debugf("start container: %s(networkid:%s,peerid:%s)", canName, chaincodeSupport.peerNetworkID, chaincodeSupport.peerID) 424 chaincodeLogger.Debugf("start container with args: %s", strings.Join(args, " ")) 425 chaincodeLogger.Debugf("start container with env:\n\t%s", strings.Join(env, "\n\t")) 426 427 vmtype, _ := chaincodeSupport.getVMType(cds) 428 429 //set up the shadow handler JIT before container launch to 430 //reduce window of when an external chaincode can sneak in 431 //and use the launching context and make it its own 432 var notfy chan bool 433 preLaunchFunc := func() error { 434 notfy = chaincodeSupport.preLaunchSetup(canName) 435 return nil 436 } 437 438 sir := container.StartImageReq{CCID: ccintf.CCID{ChaincodeSpec: cds.ChaincodeSpec, NetworkID: chaincodeSupport.peerNetworkID, PeerID: chaincodeSupport.peerID, Version: cccid.Version}, Builder: builder, Args: args, Env: env, PrelaunchFunc: preLaunchFunc} 439 440 ipcCtxt := context.WithValue(ctxt, ccintf.GetCCHandlerKey(), chaincodeSupport) 441 442 resp, err := container.VMCProcess(ipcCtxt, vmtype, sir) 443 if err != nil || (resp != nil && resp.(container.VMCResp).Err != nil) { 444 if err == nil { 445 err = resp.(container.VMCResp).Err 446 } 447 err = fmt.Errorf("Error starting container: %s", err) 448 chaincodeSupport.runningChaincodes.Lock() 449 delete(chaincodeSupport.runningChaincodes.chaincodeMap, canName) 450 chaincodeSupport.runningChaincodes.Unlock() 451 return err 452 } 453 454 //wait for REGISTER state 455 select { 456 case ok := <-notfy: 457 if !ok { 458 err = fmt.Errorf("registration failed for %s(networkid:%s,peerid:%s,tx:%s)", canName, chaincodeSupport.peerNetworkID, chaincodeSupport.peerID, cccid.TxID) 459 } 460 case <-time.After(chaincodeSupport.ccStartupTimeout): 461 err = fmt.Errorf("Timeout expired while starting chaincode %s(networkid:%s,peerid:%s,tx:%s)", canName, chaincodeSupport.peerNetworkID, chaincodeSupport.peerID, cccid.TxID) 462 } 463 if err != nil { 464 chaincodeLogger.Debugf("stopping due to error while launching %s", err) 465 errIgnore := chaincodeSupport.Stop(ctxt, cccid, cds) 466 if errIgnore != nil { 467 chaincodeLogger.Debugf("error on stop %s(%s)", errIgnore, err) 468 } 469 } 470 return err 471 } 472 473 //Stop stops a chaincode if running 474 func (chaincodeSupport *ChaincodeSupport) Stop(context context.Context, cccid *ccprovider.CCContext, cds *pb.ChaincodeDeploymentSpec) error { 475 canName := cccid.GetCanonicalName() 476 if canName == "" { 477 return fmt.Errorf("chaincode name not set") 478 } 479 480 //stop the chaincode 481 sir := container.StopImageReq{CCID: ccintf.CCID{ChaincodeSpec: cds.ChaincodeSpec, NetworkID: chaincodeSupport.peerNetworkID, PeerID: chaincodeSupport.peerID, Version: cccid.Version}, Timeout: 0} 482 // The line below is left for debugging. It replaces the line above to keep 483 // the chaincode container around to give you a chance to get data 484 //sir := container.StopImageReq{CCID: ccintf.CCID{ChaincodeSpec: cds.ChaincodeSpec, NetworkID: chaincodeSupport.peerNetworkID, PeerID: chaincodeSupport.peerID, ChainID: cccid.ChainID, Version: cccid.Version}, Timeout: 0, Dontremove: true} 485 486 vmtype, _ := chaincodeSupport.getVMType(cds) 487 488 _, err := container.VMCProcess(context, vmtype, sir) 489 if err != nil { 490 err = fmt.Errorf("Error stopping container: %s", err) 491 //but proceed to cleanup 492 } 493 494 chaincodeSupport.runningChaincodes.Lock() 495 if _, ok := chaincodeSupport.chaincodeHasBeenLaunched(canName); !ok { 496 //nothing to do 497 chaincodeSupport.runningChaincodes.Unlock() 498 return nil 499 } 500 501 delete(chaincodeSupport.runningChaincodes.chaincodeMap, canName) 502 503 chaincodeSupport.runningChaincodes.Unlock() 504 505 return err 506 } 507 508 // Launch will launch the chaincode if not running (if running return nil) and will wait for handler of the chaincode to get into FSM ready state. 509 func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, cccid *ccprovider.CCContext, spec interface{}) (*pb.ChaincodeID, *pb.ChaincodeInput, error) { 510 //build the chaincode 511 var cID *pb.ChaincodeID 512 var cMsg *pb.ChaincodeInput 513 514 var cds *pb.ChaincodeDeploymentSpec 515 var ci *pb.ChaincodeInvocationSpec 516 if cds, _ = spec.(*pb.ChaincodeDeploymentSpec); cds == nil { 517 if ci, _ = spec.(*pb.ChaincodeInvocationSpec); ci == nil { 518 panic("Launch should be called with deployment or invocation spec") 519 } 520 } 521 if cds != nil { 522 cID = cds.ChaincodeSpec.ChaincodeId 523 cMsg = cds.ChaincodeSpec.Input 524 } else { 525 cID = ci.ChaincodeSpec.ChaincodeId 526 cMsg = ci.ChaincodeSpec.Input 527 } 528 529 canName := cccid.GetCanonicalName() 530 chaincodeSupport.runningChaincodes.Lock() 531 var chrte *chaincodeRTEnv 532 var ok bool 533 var err error 534 //if its in the map, there must be a connected stream...nothing to do 535 if chrte, ok = chaincodeSupport.chaincodeHasBeenLaunched(canName); ok { 536 if !chrte.handler.registered { 537 chaincodeSupport.runningChaincodes.Unlock() 538 chaincodeLogger.Debugf("premature execution - chaincode (%s) is being launched", canName) 539 err = fmt.Errorf("premature execution - chaincode (%s) is being launched", canName) 540 return cID, cMsg, err 541 } 542 if chrte.handler.isRunning() { 543 if chaincodeLogger.IsEnabledFor(logging.DEBUG) { 544 chaincodeLogger.Debugf("chaincode is running(no need to launch) : %s", canName) 545 } 546 chaincodeSupport.runningChaincodes.Unlock() 547 return cID, cMsg, nil 548 } 549 chaincodeLogger.Debugf("Container not in READY state(%s)...send init/ready", chrte.handler.FSM.Current()) 550 } 551 chaincodeSupport.runningChaincodes.Unlock() 552 553 if cds == nil { 554 if cccid.Syscc { 555 return cID, cMsg, fmt.Errorf("a syscc should be running (it cannot be launched) %s", canName) 556 } 557 558 if chaincodeSupport.userRunsCC { 559 chaincodeLogger.Error("You are attempting to perform an action other than Deploy on Chaincode that is not ready and you are in developer mode. Did you forget to Deploy your chaincode?") 560 } 561 562 var depPayload []byte 563 564 //hopefully we are restarting from existing image and the deployed transaction exists 565 //this will also validate the ID from the LSCC 566 depPayload, err = GetCDSFromLSCC(context, cccid.TxID, cccid.SignedProposal, cccid.Proposal, cccid.ChainID, cID.Name) 567 if err != nil { 568 return cID, cMsg, fmt.Errorf("Could not get deployment transaction from LSCC for %s - %s", canName, err) 569 } 570 if depPayload == nil { 571 return cID, cMsg, fmt.Errorf("failed to get deployment payload %s - %s", canName, err) 572 } 573 574 cds = &pb.ChaincodeDeploymentSpec{} 575 576 //Get lang from original deployment 577 err = proto.Unmarshal(depPayload, cds) 578 if err != nil { 579 return cID, cMsg, fmt.Errorf("failed to unmarshal deployment transactions for %s - %s", canName, err) 580 } 581 } 582 583 //from here on : if we launch the container and get an error, we need to stop the container 584 585 //launch container if it is a System container or not in dev mode 586 if (!chaincodeSupport.userRunsCC || cds.ExecEnv == pb.ChaincodeDeploymentSpec_SYSTEM) && (chrte == nil || chrte.handler == nil) { 587 //NOTE-We need to streamline code a bit so the data from LSCC gets passed to this thus 588 //avoiding the need to go to the FS. In particular, we should use cdsfs completely. It is 589 //just a vestige of old protocol that we continue to use ChaincodeDeploymentSpec for 590 //anything other than Install. In particular, instantiate, invoke, upgrade should be using 591 //just some form of ChaincodeInvocationSpec. 592 // 593 //But for now, if we are invoking we have gone through the LSCC path above. If instantiating 594 //or upgrading currently we send a CDS with nil CodePackage. In this case the codepath 595 //in the endorser has gone through LSCC validation. Just get the code from the FS. 596 if cds.CodePackage == nil { 597 //no code bytes for these situations 598 if !(chaincodeSupport.userRunsCC || cds.ExecEnv == pb.ChaincodeDeploymentSpec_SYSTEM) { 599 ccpack, err := ccprovider.GetChaincodeFromFS(cID.Name, cID.Version) 600 if err != nil { 601 return cID, cMsg, err 602 } 603 604 cds = ccpack.GetDepSpec() 605 chaincodeLogger.Debugf("launchAndWaitForRegister fetched %d bytes from file system", len(cds.CodePackage)) 606 } 607 } 608 609 builder := func() (io.Reader, error) { return platforms.GenerateDockerBuild(cds) } 610 611 cLang := cds.ChaincodeSpec.Type 612 err = chaincodeSupport.launchAndWaitForRegister(context, cccid, cds, cLang, builder) 613 if err != nil { 614 chaincodeLogger.Errorf("launchAndWaitForRegister failed %s", err) 615 return cID, cMsg, err 616 } 617 } 618 619 if err == nil { 620 //launch will set the chaincode in Ready state 621 err = chaincodeSupport.sendReady(context, cccid, chaincodeSupport.ccStartupTimeout) 622 if err != nil { 623 chaincodeLogger.Errorf("sending init failed(%s)", err) 624 err = fmt.Errorf("Failed to init chaincode(%s)", err) 625 errIgnore := chaincodeSupport.Stop(context, cccid, cds) 626 if errIgnore != nil { 627 chaincodeLogger.Errorf("stop failed %s(%s)", errIgnore, err) 628 } 629 } 630 chaincodeLogger.Debug("sending init completed") 631 } 632 633 chaincodeLogger.Debug("LaunchChaincode complete") 634 635 return cID, cMsg, err 636 } 637 638 //getVMType - just returns a string for now. Another possibility is to use a factory method to 639 //return a VM executor 640 func (chaincodeSupport *ChaincodeSupport) getVMType(cds *pb.ChaincodeDeploymentSpec) (string, error) { 641 if cds.ExecEnv == pb.ChaincodeDeploymentSpec_SYSTEM { 642 return container.SYSTEM, nil 643 } 644 return container.DOCKER, nil 645 } 646 647 // HandleChaincodeStream implements ccintf.HandleChaincodeStream for all vms to call with appropriate stream 648 func (chaincodeSupport *ChaincodeSupport) HandleChaincodeStream(ctxt context.Context, stream ccintf.ChaincodeStream) error { 649 return HandleChaincodeStream(chaincodeSupport, ctxt, stream) 650 } 651 652 // Register the bidi stream entry point called by chaincode to register with the Peer. 653 func (chaincodeSupport *ChaincodeSupport) Register(stream pb.ChaincodeSupport_RegisterServer) error { 654 return chaincodeSupport.HandleChaincodeStream(stream.Context(), stream) 655 } 656 657 // createCCMessage creates a transaction message. 658 func createCCMessage(typ pb.ChaincodeMessage_Type, txid string, cMsg *pb.ChaincodeInput) (*pb.ChaincodeMessage, error) { 659 payload, err := proto.Marshal(cMsg) 660 if err != nil { 661 fmt.Printf(err.Error()) 662 return nil, err 663 } 664 return &pb.ChaincodeMessage{Type: typ, Payload: payload, Txid: txid}, nil 665 } 666 667 // Execute executes a transaction and waits for it to complete until a timeout value. 668 func (chaincodeSupport *ChaincodeSupport) Execute(ctxt context.Context, cccid *ccprovider.CCContext, msg *pb.ChaincodeMessage, timeout time.Duration) (*pb.ChaincodeMessage, error) { 669 chaincodeLogger.Debugf("Entry") 670 defer chaincodeLogger.Debugf("Exit") 671 canName := cccid.GetCanonicalName() 672 chaincodeLogger.Debugf("chaincode canonical name: %s", canName) 673 chaincodeSupport.runningChaincodes.Lock() 674 //we expect the chaincode to be running... sanity check 675 chrte, ok := chaincodeSupport.chaincodeHasBeenLaunched(canName) 676 if !ok { 677 chaincodeSupport.runningChaincodes.Unlock() 678 chaincodeLogger.Debugf("cannot execute-chaincode is not running: %s", canName) 679 return nil, fmt.Errorf("Cannot execute transaction for %s", canName) 680 } 681 chaincodeSupport.runningChaincodes.Unlock() 682 683 var notfy chan *pb.ChaincodeMessage 684 var err error 685 if notfy, err = chrte.handler.sendExecuteMessage(ctxt, cccid.ChainID, msg, cccid.SignedProposal, cccid.Proposal); err != nil { 686 return nil, fmt.Errorf("Error sending %s: %s", msg.Type.String(), err) 687 } 688 var ccresp *pb.ChaincodeMessage 689 select { 690 case ccresp = <-notfy: 691 //response is sent to user or calling chaincode. ChaincodeMessage_ERROR 692 //are typically treated as error 693 case <-time.After(timeout): 694 err = fmt.Errorf("Timeout expired while executing transaction") 695 } 696 697 //our responsibility to delete transaction context if sendExecuteMessage succeeded 698 chrte.handler.deleteTxContext(msg.Txid) 699 700 return ccresp, err 701 } 702 703 // IsDevMode returns true if the peer was configured with development-mode enabled 704 func IsDevMode() bool { 705 mode := viper.GetString("chaincode.mode") 706 707 return mode == DevModeUserRunsChaincode 708 }