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  }