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