func getTimeout(cID *pb.ChaincodeID) (time.Duration, error) { ledger, err := ledger.GetLedger() if err == nil { chaincodeID := cID.Name txUUID, err := ledger.GetState(chaincodeID, "github.com_openblockchain_obc-peer_chaincode_id", true) if err == nil { tx, err := ledger.GetTransactionByUUID(string(txUUID)) 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 }
// LaunchChaincode 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) LaunchChaincode(context context.Context, t *pb.Transaction) (*pb.ChaincodeID, *pb.ChaincodeInput, error) { //build the chaincode var cID *pb.ChaincodeID var cMsg *pb.ChaincodeInput var f *string var initargs []string if t.Type == pb.Transaction_CHAINCODE_NEW { cds := &pb.ChaincodeDeploymentSpec{} err := proto.Unmarshal(t.Payload, cds) if err != nil { return nil, nil, err } cID = cds.ChaincodeSpec.ChaincodeID cMsg = cds.ChaincodeSpec.CtorMsg f = &cMsg.Function initargs = cMsg.Args } else if t.Type == pb.Transaction_CHAINCODE_EXECUTE || 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.handlerMap.Unlock() return nil, nil, fmt.Errorf("invalid transaction type: %d", t.Type) } chaincode := cID.Name chaincodeSupport.handlerMap.Lock() var handler *Handler var ok bool var err error //if its in the map, there must be a connected stream...nothing to do if handler, ok = chaincodeSupport.chaincodeHasBeenLaunched(chaincode); ok { if !handler.registered { chaincodeSupport.handlerMap.Unlock() chaincodeLog.Debug("premature execution - chaincode (%s) is being launched", chaincode) err = fmt.Errorf("premature execution - chaincode (%s) is being launched", chaincode) return cID, cMsg, err } if handler.isRunning() { chaincodeLog.Debug("chaincode is running(no need to launch) : %s", chaincode) chaincodeSupport.handlerMap.Unlock() return cID, cMsg, nil } chaincodeLog.Debug("Container not in READY state(%s)...send init/ready", handler.FSM.Current()) } chaincodeSupport.handlerMap.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_NEW { ledger, ledgerErr := ledger.GetLedger() 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.GetTransactionByUUID(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) } } } //from here on : if we launch the container and get an error, we need to stop the container if !chaincodeSupport.userRunsCC && handler == nil { _, err = chaincodeSupport.launchAndWaitForRegister(context, cID, t.Uuid) if err != nil { chaincodeLog.Debug("launchAndWaitForRegister failed %s", err) return cID, cMsg, err } } if err == nil { //send init (if (f,args)) and wait for ready state err = chaincodeSupport.sendInitOrReady(context, t.Uuid, chaincode, f, initargs, chaincodeSupport.ccStartupTimeout, t, depTx) if err != nil { chaincodeLog.Debug("sending init failed(%s)", err) err = fmt.Errorf("Failed to init chaincode(%s)", err) errIgnore := chaincodeSupport.stopChaincode(context, cID) if errIgnore != nil { chaincodeLog.Debug("stop failed %s(%s)", errIgnore, err) } } chaincodeLog.Debug("sending init completed") } chaincodeLog.Debug("LaunchChaincode complete") return cID, cMsg, err }