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