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  }