github.com/kchristidis/fabric@v1.0.4-0.20171028114726-837acd08cde1/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  	//mark the starting of launch of a chaincode so multiple requests
   100  	//do not attempt to start the chaincode at the same time
   101  	launchStarted map[string]bool
   102  }
   103  
   104  //GetChain returns the chaincode framework support object
   105  func GetChain() *ChaincodeSupport {
   106  	return theChaincodeSupport
   107  }
   108  
   109  func (chaincodeSupport *ChaincodeSupport) preLaunchSetup(chaincode string) chan bool {
   110  	chaincodeSupport.runningChaincodes.Lock()
   111  	defer chaincodeSupport.runningChaincodes.Unlock()
   112  	//register placeholder Handler. This will be transferred in registerHandler
   113  	//NOTE: from this point, existence of handler for this chaincode means the chaincode
   114  	//is in the process of getting started (or has been started)
   115  	notfy := make(chan bool, 1)
   116  	chaincodeSupport.runningChaincodes.chaincodeMap[chaincode] = &chaincodeRTEnv{handler: &Handler{readyNotify: notfy}}
   117  	return notfy
   118  }
   119  
   120  //call this under lock
   121  func (chaincodeSupport *ChaincodeSupport) chaincodeHasBeenLaunched(chaincode string) (*chaincodeRTEnv, bool) {
   122  	chrte, hasbeenlaunched := chaincodeSupport.runningChaincodes.chaincodeMap[chaincode]
   123  	return chrte, hasbeenlaunched
   124  }
   125  
   126  //call this under lock
   127  func (chaincodeSupport *ChaincodeSupport) launchStarted(chaincode string) bool {
   128  	if _, launchStarted := chaincodeSupport.runningChaincodes.launchStarted[chaincode]; launchStarted {
   129  		return true
   130  	}
   131  	return false
   132  }
   133  
   134  // NewChaincodeSupport creates a new ChaincodeSupport instance
   135  func NewChaincodeSupport(getCCEndpoint func() (*pb.PeerEndpoint, error), userrunsCC bool, ccstartuptimeout time.Duration) *ChaincodeSupport {
   136  	ccprovider.SetChaincodesPath(config.GetPath("peer.fileSystemPath") + string(filepath.Separator) + "chaincodes")
   137  
   138  	pnid := viper.GetString("peer.networkId")
   139  	pid := viper.GetString("peer.id")
   140  
   141  	theChaincodeSupport = &ChaincodeSupport{runningChaincodes: &runningChaincodes{chaincodeMap: make(map[string]*chaincodeRTEnv), launchStarted: make(map[string]bool)}, peerNetworkID: pnid, peerID: pid}
   142  
   143  	//initialize global chain
   144  
   145  	ccEndpoint, err := getCCEndpoint()
   146  	if err != nil {
   147  		chaincodeLogger.Errorf("Error getting chaincode endpoint, using chaincode.peerAddress: %s", err)
   148  		theChaincodeSupport.peerAddress = viper.GetString("chaincode.peerAddress")
   149  	} else {
   150  		theChaincodeSupport.peerAddress = ccEndpoint.Address
   151  	}
   152  	chaincodeLogger.Infof("Chaincode support using peerAddress: %s\n", theChaincodeSupport.peerAddress)
   153  	//peerAddress = viper.GetString("peer.address")
   154  	if theChaincodeSupport.peerAddress == "" {
   155  		theChaincodeSupport.peerAddress = peerAddressDefault
   156  	}
   157  
   158  	theChaincodeSupport.userRunsCC = userrunsCC
   159  
   160  	theChaincodeSupport.ccStartupTimeout = ccstartuptimeout
   161  
   162  	theChaincodeSupport.peerTLS = viper.GetBool("peer.tls.enabled")
   163  	if theChaincodeSupport.peerTLS {
   164  		theChaincodeSupport.peerTLSCertFile = config.GetPath("peer.tls.cert.file")
   165  		theChaincodeSupport.peerTLSKeyFile = config.GetPath("peer.tls.key.file")
   166  		theChaincodeSupport.peerTLSSvrHostOrd = viper.GetString("peer.tls.serverhostoverride")
   167  	}
   168  
   169  	kadef := 0
   170  	if ka := viper.GetString("chaincode.keepalive"); ka == "" {
   171  		theChaincodeSupport.keepalive = time.Duration(kadef) * time.Second
   172  	} else {
   173  		t, terr := strconv.Atoi(ka)
   174  		if terr != nil {
   175  			chaincodeLogger.Errorf("Invalid keepalive value %s (%s) defaulting to %d", ka, terr, kadef)
   176  			t = kadef
   177  		} else if t <= 0 {
   178  			chaincodeLogger.Debugf("Turn off keepalive(value %s)", ka)
   179  			t = kadef
   180  		}
   181  		theChaincodeSupport.keepalive = time.Duration(t) * time.Second
   182  	}
   183  
   184  	//default chaincode execute timeout is 30 secs
   185  	execto := time.Duration(30) * time.Second
   186  	if eto := viper.GetDuration("chaincode.executetimeout"); eto <= time.Duration(1)*time.Second {
   187  		chaincodeLogger.Errorf("Invalid execute timeout value %s (should be at least 1s); defaulting to %s", eto, execto)
   188  	} else {
   189  		chaincodeLogger.Debugf("Setting execute timeout value to %s", eto)
   190  		execto = eto
   191  	}
   192  
   193  	theChaincodeSupport.executetimeout = execto
   194  
   195  	viper.SetEnvPrefix("CORE")
   196  	viper.AutomaticEnv()
   197  	replacer := strings.NewReplacer(".", "_")
   198  	viper.SetEnvKeyReplacer(replacer)
   199  
   200  	theChaincodeSupport.chaincodeLogLevel = getLogLevelFromViper("level")
   201  	theChaincodeSupport.shimLogLevel = getLogLevelFromViper("shim")
   202  	theChaincodeSupport.logFormat = viper.GetString("chaincode.logging.format")
   203  
   204  	return theChaincodeSupport
   205  }
   206  
   207  // getLogLevelFromViper gets the chaincode container log levels from viper
   208  func getLogLevelFromViper(module string) string {
   209  	levelString := viper.GetString("chaincode.logging." + module)
   210  	_, err := logging.LogLevel(levelString)
   211  
   212  	if err == nil {
   213  		chaincodeLogger.Debugf("CORE_CHAINCODE_%s set to level %s", strings.ToUpper(module), levelString)
   214  	} else {
   215  		chaincodeLogger.Warningf("CORE_CHAINCODE_%s has invalid log level %s. defaulting to %s", strings.ToUpper(module), levelString, flogging.DefaultLevel())
   216  		levelString = flogging.DefaultLevel()
   217  	}
   218  	return levelString
   219  }
   220  
   221  // // ChaincodeStream standard stream for ChaincodeMessage type.
   222  // type ChaincodeStream interface {
   223  // 	Send(*pb.ChaincodeMessage) error
   224  // 	Recv() (*pb.ChaincodeMessage, error)
   225  // }
   226  
   227  // ChaincodeSupport responsible for providing interfacing with chaincodes from the Peer.
   228  type ChaincodeSupport struct {
   229  	runningChaincodes *runningChaincodes
   230  	peerAddress       string
   231  	ccStartupTimeout  time.Duration
   232  	peerNetworkID     string
   233  	peerID            string
   234  	peerTLSCertFile   string
   235  	peerTLSKeyFile    string
   236  	peerTLSSvrHostOrd string
   237  	keepalive         time.Duration
   238  	chaincodeLogLevel string
   239  	shimLogLevel      string
   240  	logFormat         string
   241  	executetimeout    time.Duration
   242  	userRunsCC        bool
   243  	peerTLS           bool
   244  }
   245  
   246  // DuplicateChaincodeHandlerError returned if attempt to register same chaincodeID while a stream already exists.
   247  type DuplicateChaincodeHandlerError struct {
   248  	ChaincodeID *pb.ChaincodeID
   249  }
   250  
   251  func (d *DuplicateChaincodeHandlerError) Error() string {
   252  	return fmt.Sprintf("Duplicate chaincodeID error: %s", d.ChaincodeID)
   253  }
   254  
   255  func newDuplicateChaincodeHandlerError(chaincodeHandler *Handler) error {
   256  	return &DuplicateChaincodeHandlerError{ChaincodeID: chaincodeHandler.ChaincodeID}
   257  }
   258  
   259  func (chaincodeSupport *ChaincodeSupport) registerHandler(chaincodehandler *Handler) error {
   260  	key := chaincodehandler.ChaincodeID.Name
   261  
   262  	chaincodeSupport.runningChaincodes.Lock()
   263  	defer chaincodeSupport.runningChaincodes.Unlock()
   264  
   265  	chrte2, ok := chaincodeSupport.chaincodeHasBeenLaunched(key)
   266  	if ok && chrte2.handler.registered == true {
   267  		chaincodeLogger.Debugf("duplicate registered handler(key:%s) return error", key)
   268  		// Duplicate, return error
   269  		return newDuplicateChaincodeHandlerError(chaincodehandler)
   270  	}
   271  	//a placeholder, unregistered handler will be setup by transaction processing that comes
   272  	//through via consensus. In this case we swap the handler and give it the notify channel
   273  	if chrte2 != nil {
   274  		chaincodehandler.readyNotify = chrte2.handler.readyNotify
   275  		chrte2.handler = chaincodehandler
   276  	} else {
   277  		if chaincodeSupport.userRunsCC == false {
   278  			//this chaincode was not launched by the peer and is attempting
   279  			//to register. Don't allow this.
   280  			return fmt.Errorf("peer will not accepting external chaincode connection %v (except in dev mode)", chaincodehandler.ChaincodeID)
   281  		}
   282  		chaincodeSupport.runningChaincodes.chaincodeMap[key] = &chaincodeRTEnv{handler: chaincodehandler}
   283  	}
   284  
   285  	chaincodehandler.registered = true
   286  
   287  	//now we are ready to receive messages and send back responses
   288  	chaincodehandler.txCtxs = make(map[string]*transactionContext)
   289  	chaincodehandler.txidMap = make(map[string]bool)
   290  
   291  	chaincodeLogger.Debugf("registered handler complete for chaincode %s", key)
   292  
   293  	return nil
   294  }
   295  
   296  func (chaincodeSupport *ChaincodeSupport) deregisterHandler(chaincodehandler *Handler) error {
   297  
   298  	// clean up queryIteratorMap
   299  	for _, context := range chaincodehandler.txCtxs {
   300  		for _, v := range context.queryIteratorMap {
   301  			v.Close()
   302  		}
   303  	}
   304  
   305  	key := chaincodehandler.ChaincodeID.Name
   306  	chaincodeLogger.Debugf("Deregister handler: %s", key)
   307  	chaincodeSupport.runningChaincodes.Lock()
   308  	defer chaincodeSupport.runningChaincodes.Unlock()
   309  	if _, ok := chaincodeSupport.chaincodeHasBeenLaunched(key); !ok {
   310  		// Handler NOT found
   311  		return fmt.Errorf("Error deregistering handler, could not find handler with key: %s", key)
   312  	}
   313  	delete(chaincodeSupport.runningChaincodes.chaincodeMap, key)
   314  	chaincodeLogger.Debugf("Deregistered handler with key: %s", key)
   315  	return nil
   316  }
   317  
   318  // send ready to move to ready state
   319  func (chaincodeSupport *ChaincodeSupport) sendReady(context context.Context, cccid *ccprovider.CCContext, timeout time.Duration) error {
   320  	canName := cccid.GetCanonicalName()
   321  	chaincodeSupport.runningChaincodes.Lock()
   322  	//if its in the map, there must be a connected stream...nothing to do
   323  	var chrte *chaincodeRTEnv
   324  	var ok bool
   325  	if chrte, ok = chaincodeSupport.chaincodeHasBeenLaunched(canName); !ok {
   326  		chaincodeSupport.runningChaincodes.Unlock()
   327  		chaincodeLogger.Debugf("handler not found for chaincode %s", canName)
   328  		return fmt.Errorf("handler not found for chaincode %s", canName)
   329  	}
   330  	chaincodeSupport.runningChaincodes.Unlock()
   331  
   332  	var notfy chan *pb.ChaincodeMessage
   333  	var err error
   334  	if notfy, err = chrte.handler.ready(context, cccid.ChainID, cccid.TxID, cccid.SignedProposal, cccid.Proposal); err != nil {
   335  		return fmt.Errorf("Error sending %s: %s", pb.ChaincodeMessage_READY, err)
   336  	}
   337  	if notfy != nil {
   338  		select {
   339  		case ccMsg := <-notfy:
   340  			if ccMsg.Type == pb.ChaincodeMessage_ERROR {
   341  				err = fmt.Errorf("Error initializing container %s: %s", canName, string(ccMsg.Payload))
   342  			}
   343  			if ccMsg.Type == pb.ChaincodeMessage_COMPLETED {
   344  				res := &pb.Response{}
   345  				_ = proto.Unmarshal(ccMsg.Payload, res)
   346  				if res.Status != shim.OK {
   347  					err = fmt.Errorf("Error initializing container %s: %s", canName, string(res.Message))
   348  				}
   349  				// TODO
   350  				// return res so that endorser can anylyze it.
   351  			}
   352  		case <-time.After(timeout):
   353  			err = fmt.Errorf("Timeout expired while executing send init message")
   354  		}
   355  	}
   356  
   357  	//if initOrReady succeeded, our responsibility to delete the context
   358  	chrte.handler.deleteTxContext(cccid.TxID)
   359  
   360  	return err
   361  }
   362  
   363  //get args and env given chaincodeID
   364  func (chaincodeSupport *ChaincodeSupport) getArgsAndEnv(cccid *ccprovider.CCContext, cLang pb.ChaincodeSpec_Type) (args []string, envs []string, err error) {
   365  	canName := cccid.GetCanonicalName()
   366  	envs = []string{"CORE_CHAINCODE_ID_NAME=" + canName}
   367  
   368  	// ----------------------------------------------------------------------------
   369  	// Pass TLS options to chaincode
   370  	// ----------------------------------------------------------------------------
   371  	// Note: The peer certificate is only baked into the image during the build
   372  	// phase (see core/chaincode/platforms).  This logic below merely assumes the
   373  	// image is already configured appropriately and is simply toggling the feature
   374  	// on or off.  If the peer's x509 has changed since the chaincode was deployed,
   375  	// the image may be stale and the admin will need to remove the current containers
   376  	// before restarting the peer.
   377  	// ----------------------------------------------------------------------------
   378  	if chaincodeSupport.peerTLS {
   379  		envs = append(envs, "CORE_PEER_TLS_ENABLED=true")
   380  		if chaincodeSupport.peerTLSSvrHostOrd != "" {
   381  			envs = append(envs, "CORE_PEER_TLS_SERVERHOSTOVERRIDE="+chaincodeSupport.peerTLSSvrHostOrd)
   382  		}
   383  	} else {
   384  		envs = append(envs, "CORE_PEER_TLS_ENABLED=false")
   385  	}
   386  
   387  	if chaincodeSupport.chaincodeLogLevel != "" {
   388  		envs = append(envs, "CORE_CHAINCODE_LOGGING_LEVEL="+chaincodeSupport.chaincodeLogLevel)
   389  	}
   390  
   391  	if chaincodeSupport.shimLogLevel != "" {
   392  		envs = append(envs, "CORE_CHAINCODE_LOGGING_SHIM="+chaincodeSupport.shimLogLevel)
   393  	}
   394  
   395  	if chaincodeSupport.logFormat != "" {
   396  		envs = append(envs, "CORE_CHAINCODE_LOGGING_FORMAT="+chaincodeSupport.logFormat)
   397  	}
   398  	switch cLang {
   399  	case pb.ChaincodeSpec_GOLANG, pb.ChaincodeSpec_CAR:
   400  		args = []string{"chaincode", fmt.Sprintf("-peer.address=%s", chaincodeSupport.peerAddress)}
   401  	case pb.ChaincodeSpec_JAVA:
   402  		args = []string{"java", "-jar", "chaincode.jar", "--peerAddress", chaincodeSupport.peerAddress}
   403  	default:
   404  		return nil, nil, fmt.Errorf("Unknown chaincodeType: %s", cLang)
   405  	}
   406  	chaincodeLogger.Debugf("Executable is %s", args[0])
   407  	chaincodeLogger.Debugf("Args %v", args)
   408  	return args, envs, nil
   409  }
   410  
   411  //launchAndWaitForRegister will launch container if not already running. Use
   412  //the targz to create the image if not found
   413  func (chaincodeSupport *ChaincodeSupport) launchAndWaitForRegister(ctxt context.Context, cccid *ccprovider.CCContext, cds *pb.ChaincodeDeploymentSpec, cLang pb.ChaincodeSpec_Type, builder api.BuildSpecFactory) error {
   414  	canName := cccid.GetCanonicalName()
   415  	if canName == "" {
   416  		return fmt.Errorf("chaincode name not set")
   417  	}
   418  
   419  	chaincodeSupport.runningChaincodes.Lock()
   420  	//if its in the map, its either up or being launched. Either case break the
   421  	//multiple launch by failing
   422  	if _, hasBeenLaunched := chaincodeSupport.chaincodeHasBeenLaunched(canName); hasBeenLaunched {
   423  		chaincodeSupport.runningChaincodes.Unlock()
   424  		return fmt.Errorf("Error chaincode has been launched: %s", canName)
   425  	}
   426  
   427  	//prohibit multiple simultaneous invokes (for example while flooding the
   428  	//system with invokes as in a stress test scenario) from attempting to launch
   429  	//the chaincode. The first one wins. Others receive an error.
   430  	//NOTE - this transient behavior as the chaincode is being launched is nothing
   431  	//new. All invokes (except the one launching the CC) will fail in any case
   432  	//until the container is up and registered.
   433  	if chaincodeSupport.launchStarted(canName) {
   434  		chaincodeSupport.runningChaincodes.Unlock()
   435  		return fmt.Errorf("Error chaincode is already launching: %s", canName)
   436  	}
   437  
   438  	//Chaincode is not up and is not in the process of being launched. Setup flag
   439  	//for launching so we can proceed to do that undisturbed by other requests on
   440  	//this chaincode
   441  	chaincodeLogger.Debugf("chaincode %s is being launched", canName)
   442  	chaincodeSupport.runningChaincodes.launchStarted[canName] = true
   443  
   444  	//now that chaincode launch sequence is done (whether successful or not),
   445  	//unset launch flag as we get out of this function. If launch was not
   446  	//successful (handler was not created), next invoke will try again.
   447  	defer func() {
   448  		chaincodeSupport.runningChaincodes.Lock()
   449  		defer chaincodeSupport.runningChaincodes.Unlock()
   450  
   451  		delete(chaincodeSupport.runningChaincodes.launchStarted, canName)
   452  		chaincodeLogger.Debugf("chaincode %s launch seq completed", canName)
   453  	}()
   454  
   455  	chaincodeSupport.runningChaincodes.Unlock()
   456  
   457  	//launch the chaincode
   458  
   459  	args, env, err := chaincodeSupport.getArgsAndEnv(cccid, cLang)
   460  	if err != nil {
   461  		return err
   462  	}
   463  
   464  	chaincodeLogger.Debugf("start container: %s(networkid:%s,peerid:%s)", canName, chaincodeSupport.peerNetworkID, chaincodeSupport.peerID)
   465  	chaincodeLogger.Debugf("start container with args: %s", strings.Join(args, " "))
   466  	chaincodeLogger.Debugf("start container with env:\n\t%s", strings.Join(env, "\n\t"))
   467  
   468  	vmtype, _ := chaincodeSupport.getVMType(cds)
   469  
   470  	//set up the shadow handler JIT before container launch to
   471  	//reduce window of when an external chaincode can sneak in
   472  	//and use the launching context and make it its own
   473  	var notfy chan bool
   474  	preLaunchFunc := func() error {
   475  		notfy = chaincodeSupport.preLaunchSetup(canName)
   476  		return nil
   477  	}
   478  
   479  	sir := container.StartImageReq{CCID: ccintf.CCID{ChaincodeSpec: cds.ChaincodeSpec, NetworkID: chaincodeSupport.peerNetworkID, PeerID: chaincodeSupport.peerID, Version: cccid.Version}, Builder: builder, Args: args, Env: env, PrelaunchFunc: preLaunchFunc}
   480  
   481  	ipcCtxt := context.WithValue(ctxt, ccintf.GetCCHandlerKey(), chaincodeSupport)
   482  
   483  	resp, err := container.VMCProcess(ipcCtxt, vmtype, sir)
   484  	if err != nil || (resp != nil && resp.(container.VMCResp).Err != nil) {
   485  		if err == nil {
   486  			err = resp.(container.VMCResp).Err
   487  		}
   488  		err = fmt.Errorf("Error starting container: %s", err)
   489  		chaincodeSupport.runningChaincodes.Lock()
   490  		delete(chaincodeSupport.runningChaincodes.chaincodeMap, canName)
   491  		chaincodeSupport.runningChaincodes.Unlock()
   492  		return err
   493  	}
   494  
   495  	//wait for REGISTER state
   496  	select {
   497  	case ok := <-notfy:
   498  		if !ok {
   499  			err = fmt.Errorf("registration failed for %s(networkid:%s,peerid:%s,tx:%s)", canName, chaincodeSupport.peerNetworkID, chaincodeSupport.peerID, cccid.TxID)
   500  		}
   501  	case <-time.After(chaincodeSupport.ccStartupTimeout):
   502  		err = fmt.Errorf("Timeout expired while starting chaincode %s(networkid:%s,peerid:%s,tx:%s)", canName, chaincodeSupport.peerNetworkID, chaincodeSupport.peerID, cccid.TxID)
   503  	}
   504  	if err != nil {
   505  		chaincodeLogger.Debugf("stopping due to error while launching %s", err)
   506  		errIgnore := chaincodeSupport.Stop(ctxt, cccid, cds)
   507  		if errIgnore != nil {
   508  			chaincodeLogger.Debugf("error on stop %s(%s)", errIgnore, err)
   509  		}
   510  	}
   511  	return err
   512  }
   513  
   514  //Stop stops a chaincode if running
   515  func (chaincodeSupport *ChaincodeSupport) Stop(context context.Context, cccid *ccprovider.CCContext, cds *pb.ChaincodeDeploymentSpec) error {
   516  	canName := cccid.GetCanonicalName()
   517  	if canName == "" {
   518  		return fmt.Errorf("chaincode name not set")
   519  	}
   520  
   521  	//stop the chaincode
   522  	sir := container.StopImageReq{CCID: ccintf.CCID{ChaincodeSpec: cds.ChaincodeSpec, NetworkID: chaincodeSupport.peerNetworkID, PeerID: chaincodeSupport.peerID, Version: cccid.Version}, Timeout: 0}
   523  	// The line below is left for debugging. It replaces the line above to keep
   524  	// the chaincode container around to give you a chance to get data
   525  	//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}
   526  
   527  	vmtype, _ := chaincodeSupport.getVMType(cds)
   528  
   529  	_, err := container.VMCProcess(context, vmtype, sir)
   530  	if err != nil {
   531  		err = fmt.Errorf("Error stopping container: %s", err)
   532  		//but proceed to cleanup
   533  	}
   534  
   535  	chaincodeSupport.runningChaincodes.Lock()
   536  	if _, ok := chaincodeSupport.chaincodeHasBeenLaunched(canName); !ok {
   537  		//nothing to do
   538  		chaincodeSupport.runningChaincodes.Unlock()
   539  		return nil
   540  	}
   541  
   542  	delete(chaincodeSupport.runningChaincodes.chaincodeMap, canName)
   543  
   544  	chaincodeSupport.runningChaincodes.Unlock()
   545  
   546  	return err
   547  }
   548  
   549  // 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.
   550  func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, cccid *ccprovider.CCContext, spec interface{}) (*pb.ChaincodeID, *pb.ChaincodeInput, error) {
   551  	//build the chaincode
   552  	var cID *pb.ChaincodeID
   553  	var cMsg *pb.ChaincodeInput
   554  
   555  	var cds *pb.ChaincodeDeploymentSpec
   556  	var ci *pb.ChaincodeInvocationSpec
   557  	if cds, _ = spec.(*pb.ChaincodeDeploymentSpec); cds == nil {
   558  		if ci, _ = spec.(*pb.ChaincodeInvocationSpec); ci == nil {
   559  			panic("Launch should be called with deployment or invocation spec")
   560  		}
   561  	}
   562  	if cds != nil {
   563  		cID = cds.ChaincodeSpec.ChaincodeId
   564  		cMsg = cds.ChaincodeSpec.Input
   565  	} else {
   566  		cID = ci.ChaincodeSpec.ChaincodeId
   567  		cMsg = ci.ChaincodeSpec.Input
   568  	}
   569  
   570  	canName := cccid.GetCanonicalName()
   571  	chaincodeSupport.runningChaincodes.Lock()
   572  	var chrte *chaincodeRTEnv
   573  	var ok bool
   574  	var err error
   575  	//if its in the map, there must be a connected stream...nothing to do
   576  	if chrte, ok = chaincodeSupport.chaincodeHasBeenLaunched(canName); ok {
   577  		if !chrte.handler.registered {
   578  			chaincodeSupport.runningChaincodes.Unlock()
   579  			chaincodeLogger.Debugf("premature execution - chaincode (%s) launched and waiting for registration", canName)
   580  			err = fmt.Errorf("premature execution - chaincode (%s) launched and waiting for registration", canName)
   581  			return cID, cMsg, err
   582  		}
   583  		if chrte.handler.isRunning() {
   584  			if chaincodeLogger.IsEnabledFor(logging.DEBUG) {
   585  				chaincodeLogger.Debugf("chaincode is running(no need to launch) : %s", canName)
   586  			}
   587  			chaincodeSupport.runningChaincodes.Unlock()
   588  			return cID, cMsg, nil
   589  		}
   590  		chaincodeLogger.Debugf("Container not in READY state(%s)...send init/ready", chrte.handler.FSM.Current())
   591  	} else {
   592  		//chaincode is not up... but is the launch process underway? this is
   593  		//strictly not necessary as the actual launch process will catch this
   594  		//(in launchAndWaitForRegister), just a bit of optimization for thundering
   595  		//herds
   596  		if chaincodeSupport.launchStarted(canName) {
   597  			chaincodeSupport.runningChaincodes.Unlock()
   598  			err = fmt.Errorf("premature execution - chaincode (%s) is being launched", canName)
   599  			return cID, cMsg, err
   600  		}
   601  	}
   602  	chaincodeSupport.runningChaincodes.Unlock()
   603  
   604  	if cds == nil {
   605  		if cccid.Syscc {
   606  			return cID, cMsg, fmt.Errorf("a syscc should be running (it cannot be launched) %s", canName)
   607  		}
   608  
   609  		if chaincodeSupport.userRunsCC {
   610  			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?")
   611  		}
   612  
   613  		var depPayload []byte
   614  
   615  		//hopefully we are restarting from existing image and the deployed transaction exists
   616  		//this will also validate the ID from the LSCC
   617  		depPayload, err = GetCDSFromLSCC(context, cccid.TxID, cccid.SignedProposal, cccid.Proposal, cccid.ChainID, cID.Name)
   618  		if err != nil {
   619  			return cID, cMsg, fmt.Errorf("Could not get deployment transaction from LSCC for %s - %s", canName, err)
   620  		}
   621  		if depPayload == nil {
   622  			return cID, cMsg, fmt.Errorf("failed to get deployment payload %s - %s", canName, err)
   623  		}
   624  
   625  		cds = &pb.ChaincodeDeploymentSpec{}
   626  
   627  		//Get lang from original deployment
   628  		err = proto.Unmarshal(depPayload, cds)
   629  		if err != nil {
   630  			return cID, cMsg, fmt.Errorf("failed to unmarshal deployment transactions for %s - %s", canName, err)
   631  		}
   632  	}
   633  
   634  	//from here on : if we launch the container and get an error, we need to stop the container
   635  
   636  	//launch container if it is a System container or not in dev mode
   637  	if (!chaincodeSupport.userRunsCC || cds.ExecEnv == pb.ChaincodeDeploymentSpec_SYSTEM) && (chrte == nil || chrte.handler == nil) {
   638  		//NOTE-We need to streamline code a bit so the data from LSCC gets passed to this thus
   639  		//avoiding the need to go to the FS. In particular, we should use cdsfs completely. It is
   640  		//just a vestige of old protocol that we continue to use ChaincodeDeploymentSpec for
   641  		//anything other than Install. In particular, instantiate, invoke, upgrade should be using
   642  		//just some form of ChaincodeInvocationSpec.
   643  		//
   644  		//But for now, if we are invoking we have gone through the LSCC path above. If  instantiating
   645  		//or upgrading currently we send a CDS with nil CodePackage. In this case the codepath
   646  		//in the endorser has gone through LSCC validation. Just get the code from the FS.
   647  		if cds.CodePackage == nil {
   648  			//no code bytes for these situations
   649  			if !(chaincodeSupport.userRunsCC || cds.ExecEnv == pb.ChaincodeDeploymentSpec_SYSTEM) {
   650  				ccpack, err := ccprovider.GetChaincodeFromFS(cID.Name, cID.Version)
   651  				if err != nil {
   652  					return cID, cMsg, err
   653  				}
   654  
   655  				cds = ccpack.GetDepSpec()
   656  				chaincodeLogger.Debugf("launchAndWaitForRegister fetched %d bytes from file system", len(cds.CodePackage))
   657  			}
   658  		}
   659  
   660  		builder := func() (io.Reader, error) { return platforms.GenerateDockerBuild(cds) }
   661  
   662  		cLang := cds.ChaincodeSpec.Type
   663  		err = chaincodeSupport.launchAndWaitForRegister(context, cccid, cds, cLang, builder)
   664  		if err != nil {
   665  			chaincodeLogger.Errorf("launchAndWaitForRegister failed %s", err)
   666  			return cID, cMsg, err
   667  		}
   668  	}
   669  
   670  	if err == nil {
   671  		//launch will set the chaincode in Ready state
   672  		err = chaincodeSupport.sendReady(context, cccid, chaincodeSupport.ccStartupTimeout)
   673  		if err != nil {
   674  			chaincodeLogger.Errorf("sending init failed(%s)", err)
   675  			err = fmt.Errorf("Failed to init chaincode(%s)", err)
   676  			errIgnore := chaincodeSupport.Stop(context, cccid, cds)
   677  			if errIgnore != nil {
   678  				chaincodeLogger.Errorf("stop failed %s(%s)", errIgnore, err)
   679  			}
   680  		}
   681  		chaincodeLogger.Debug("sending init completed")
   682  	}
   683  
   684  	chaincodeLogger.Debug("LaunchChaincode complete")
   685  
   686  	return cID, cMsg, err
   687  }
   688  
   689  //getVMType - just returns a string for now. Another possibility is to use a factory method to
   690  //return a VM executor
   691  func (chaincodeSupport *ChaincodeSupport) getVMType(cds *pb.ChaincodeDeploymentSpec) (string, error) {
   692  	if cds.ExecEnv == pb.ChaincodeDeploymentSpec_SYSTEM {
   693  		return container.SYSTEM, nil
   694  	}
   695  	return container.DOCKER, nil
   696  }
   697  
   698  // HandleChaincodeStream implements ccintf.HandleChaincodeStream for all vms to call with appropriate stream
   699  func (chaincodeSupport *ChaincodeSupport) HandleChaincodeStream(ctxt context.Context, stream ccintf.ChaincodeStream) error {
   700  	return HandleChaincodeStream(chaincodeSupport, ctxt, stream)
   701  }
   702  
   703  // Register the bidi stream entry point called by chaincode to register with the Peer.
   704  func (chaincodeSupport *ChaincodeSupport) Register(stream pb.ChaincodeSupport_RegisterServer) error {
   705  	return chaincodeSupport.HandleChaincodeStream(stream.Context(), stream)
   706  }
   707  
   708  // createCCMessage creates a transaction message.
   709  func createCCMessage(typ pb.ChaincodeMessage_Type, txid string, cMsg *pb.ChaincodeInput) (*pb.ChaincodeMessage, error) {
   710  	payload, err := proto.Marshal(cMsg)
   711  	if err != nil {
   712  		fmt.Printf(err.Error())
   713  		return nil, err
   714  	}
   715  	return &pb.ChaincodeMessage{Type: typ, Payload: payload, Txid: txid}, nil
   716  }
   717  
   718  // Execute executes a transaction and waits for it to complete until a timeout value.
   719  func (chaincodeSupport *ChaincodeSupport) Execute(ctxt context.Context, cccid *ccprovider.CCContext, msg *pb.ChaincodeMessage, timeout time.Duration) (*pb.ChaincodeMessage, error) {
   720  	chaincodeLogger.Debugf("Entry")
   721  	defer chaincodeLogger.Debugf("Exit")
   722  	canName := cccid.GetCanonicalName()
   723  	chaincodeLogger.Debugf("chaincode canonical name: %s", canName)
   724  	chaincodeSupport.runningChaincodes.Lock()
   725  	//we expect the chaincode to be running... sanity check
   726  	chrte, ok := chaincodeSupport.chaincodeHasBeenLaunched(canName)
   727  	if !ok {
   728  		chaincodeSupport.runningChaincodes.Unlock()
   729  		chaincodeLogger.Debugf("cannot execute-chaincode is not running: %s", canName)
   730  		return nil, fmt.Errorf("Cannot execute transaction for %s", canName)
   731  	}
   732  	chaincodeSupport.runningChaincodes.Unlock()
   733  
   734  	var notfy chan *pb.ChaincodeMessage
   735  	var err error
   736  	if notfy, err = chrte.handler.sendExecuteMessage(ctxt, cccid.ChainID, msg, cccid.SignedProposal, cccid.Proposal); err != nil {
   737  		return nil, fmt.Errorf("Error sending %s: %s", msg.Type.String(), err)
   738  	}
   739  	var ccresp *pb.ChaincodeMessage
   740  	select {
   741  	case ccresp = <-notfy:
   742  		//response is sent to user or calling chaincode. ChaincodeMessage_ERROR
   743  		//are typically treated as error
   744  	case <-time.After(timeout):
   745  		err = fmt.Errorf("Timeout expired while executing transaction")
   746  	}
   747  
   748  	//our responsibility to delete transaction context if sendExecuteMessage succeeded
   749  	chrte.handler.deleteTxContext(msg.Txid)
   750  
   751  	return ccresp, err
   752  }
   753  
   754  // IsDevMode returns true if the peer was configured with development-mode enabled
   755  func IsDevMode() bool {
   756  	mode := viper.GetString("chaincode.mode")
   757  
   758  	return mode == DevModeUserRunsChaincode
   759  }