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