func getTimeout(cID *pb.ChaincodeID) (time.Duration, error) { ledger, err := ledger.GetLedger() if err == nil { chaincodeID := cID.Name txID, err := ledger.GetState(chaincodeID, "github.com_openblockchain_obc-peer_chaincode_id", true) if err == nil { tx, err := ledger.GetTransactionByID(string(txID)) if err == nil { chaincodeDeploymentSpec := &pb.ChaincodeDeploymentSpec{} proto.Unmarshal(tx.Payload, chaincodeDeploymentSpec) chaincodeSpec := chaincodeDeploymentSpec.GetChaincodeSpec() timeout := time.Duration(time.Duration(chaincodeSpec.Timeout) * time.Millisecond) return timeout, nil } } } return -1, errFailedToGetChainCodeSpecForTransaction }
// 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. func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, t *pb.Transaction) (*pb.ChaincodeID, *pb.ChaincodeInput, error) { //build the chaincode var cID *pb.ChaincodeID var cMsg *pb.ChaincodeInput var cLang pb.ChaincodeSpec_Type var initargs [][]byte cds := &pb.ChaincodeDeploymentSpec{} if t.Type == pb.Transaction_CHAINCODE_DEPLOY { err := proto.Unmarshal(t.Payload, cds) if err != nil { return nil, nil, err } cID = cds.ChaincodeSpec.ChaincodeID cMsg = cds.ChaincodeSpec.CtorMsg cLang = cds.ChaincodeSpec.Type initargs = cMsg.Args } else if t.Type == pb.Transaction_CHAINCODE_INVOKE || t.Type == pb.Transaction_CHAINCODE_QUERY { ci := &pb.ChaincodeInvocationSpec{} err := proto.Unmarshal(t.Payload, ci) if err != nil { return nil, nil, err } cID = ci.ChaincodeSpec.ChaincodeID cMsg = ci.ChaincodeSpec.CtorMsg } else { chaincodeSupport.runningChaincodes.Unlock() return nil, nil, fmt.Errorf("invalid transaction type: %d", t.Type) } chaincode := cID.Name chaincodeSupport.runningChaincodes.Lock() var chrte *chaincodeRTEnv var ok bool var err error //if its in the map, there must be a connected stream...nothing to do if chrte, ok = chaincodeSupport.chaincodeHasBeenLaunched(chaincode); ok { if !chrte.handler.registered { chaincodeSupport.runningChaincodes.Unlock() chaincodeLogger.Debugf("premature execution - chaincode (%s) is being launched", chaincode) err = fmt.Errorf("premature execution - chaincode (%s) is being launched", chaincode) return cID, cMsg, err } if chrte.handler.isRunning() { chaincodeLogger.Debugf("chaincode is running(no need to launch) : %s", chaincode) chaincodeSupport.runningChaincodes.Unlock() return cID, cMsg, nil } chaincodeLogger.Debugf("Container not in READY state(%s)...send init/ready", chrte.handler.FSM.Current()) } chaincodeSupport.runningChaincodes.Unlock() var depTx *pb.Transaction //extract depTx so we can initialize hander.deployTXSecContext //we need it only after container is launched and only if this is not a deploy tx //NOTE: ideally this section should be moved before just before sendInitOrReady where // where we need depTx. However, as we don't check for ExecuteTransactions failure // in consensus/helper, the following race is not resolved: // 1) deploy creates image // 2) query launches chaincode // 3) deploy returns "premature execution" error // 4) error ignored and deploy committed // 5) query successfully retrives committed tx and calls sendInitOrReady // See issue #710 if t.Type != pb.Transaction_CHAINCODE_DEPLOY { ledger, ledgerErr := ledger.GetLedger() if chaincodeSupport.userRunsCC { 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?") } if ledgerErr != nil { return cID, cMsg, fmt.Errorf("Failed to get handle to ledger (%s)", ledgerErr) } //hopefully we are restarting from existing image and the deployed transaction exists depTx, ledgerErr = ledger.GetTransactionByID(chaincode) if ledgerErr != nil { return cID, cMsg, fmt.Errorf("Could not get deployment transaction for %s - %s", chaincode, ledgerErr) } if depTx == nil { return cID, cMsg, fmt.Errorf("deployment transaction does not exist for %s", chaincode) } if nil != chaincodeSupport.secHelper { var err error depTx, err = chaincodeSupport.secHelper.TransactionPreExecution(depTx) // Note that t is now decrypted and is a deep clone of the original input t if nil != err { return cID, cMsg, fmt.Errorf("failed tx preexecution%s - %s", chaincode, err) } } //Get lang from original deployment err := proto.Unmarshal(depTx.Payload, cds) if err != nil { return cID, cMsg, fmt.Errorf("failed to unmarshal deployment transactions for %s - %s", chaincode, err) } cLang = cds.ChaincodeSpec.Type } //from here on : if we launch the container and get an error, we need to stop the container //launch container if it is a System container or not in dev mode if (!chaincodeSupport.userRunsCC || cds.ExecEnv == pb.ChaincodeDeploymentSpec_SYSTEM) && (chrte == nil || chrte.handler == nil) { var targz io.Reader = bytes.NewBuffer(cds.CodePackage) _, err = chaincodeSupport.launchAndWaitForRegister(context, cds, cID, t.Txid, cLang, targz) if err != nil { chaincodeLogger.Errorf("launchAndWaitForRegister failed %s", err) return cID, cMsg, err } } if err == nil { //send init (if (args)) and wait for ready state err = chaincodeSupport.sendInitOrReady(context, t.Txid, chaincode, initargs, chaincodeSupport.ccStartupTimeout, t, depTx) if err != nil { chaincodeLogger.Errorf("sending init failed(%s)", err) err = fmt.Errorf("Failed to init chaincode(%s)", err) errIgnore := chaincodeSupport.Stop(context, cds) if errIgnore != nil { chaincodeLogger.Errorf("stop failed %s(%s)", errIgnore, err) } } chaincodeLogger.Debug("sending init completed") } chaincodeLogger.Debug("LaunchChaincode complete") return cID, cMsg, err }