github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/core/container/inproccontroller/inproccontroller.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 inproccontroller
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  
    23  	"github.com/hyperledger/fabric/common/flogging"
    24  	"github.com/hyperledger/fabric/core/chaincode/shim"
    25  	container "github.com/hyperledger/fabric/core/container/api"
    26  	"github.com/hyperledger/fabric/core/container/ccintf"
    27  	pb "github.com/hyperledger/fabric/protos/peer"
    28  
    29  	"golang.org/x/net/context"
    30  )
    31  
    32  type inprocContainer struct {
    33  	chaincode shim.Chaincode
    34  	running   bool
    35  	args      []string
    36  	env       []string
    37  	stopChan  chan struct{}
    38  }
    39  
    40  var (
    41  	inprocLogger = flogging.MustGetLogger("inproccontroller")
    42  	typeRegistry = make(map[string]*inprocContainer)
    43  	instRegistry = make(map[string]*inprocContainer)
    44  )
    45  
    46  // errors
    47  
    48  //SysCCRegisteredErr registered error
    49  type SysCCRegisteredErr string
    50  
    51  func (s SysCCRegisteredErr) Error() string {
    52  	return fmt.Sprintf("%s already registered", string(s))
    53  }
    54  
    55  //Register registers system chaincode with given path. The deploy should be called to initialize
    56  func Register(path string, cc shim.Chaincode) error {
    57  	tmp := typeRegistry[path]
    58  	if tmp != nil {
    59  		return SysCCRegisteredErr(path)
    60  	}
    61  
    62  	typeRegistry[path] = &inprocContainer{chaincode: cc}
    63  	return nil
    64  }
    65  
    66  //InprocVM is a vm. It is identified by a executable name
    67  type InprocVM struct {
    68  	id string
    69  }
    70  
    71  func (vm *InprocVM) getInstance(ctxt context.Context, ipctemplate *inprocContainer, instName string, args []string, env []string) (*inprocContainer, error) {
    72  	ipc := instRegistry[instName]
    73  	if ipc != nil {
    74  		inprocLogger.Warningf("chaincode instance exists for %s", instName)
    75  		return ipc, nil
    76  	}
    77  	ipc = &inprocContainer{args: args, env: env, chaincode: ipctemplate.chaincode, stopChan: make(chan struct{})}
    78  	instRegistry[instName] = ipc
    79  	inprocLogger.Debugf("chaincode instance created for %s", instName)
    80  	return ipc, nil
    81  }
    82  
    83  //Deploy verifies chaincode is registered and creates an instance for it. Currently only one instance can be created
    84  func (vm *InprocVM) Deploy(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, reader io.Reader) error {
    85  	path := ccid.ChaincodeSpec.ChaincodeId.Path
    86  
    87  	ipctemplate := typeRegistry[path]
    88  	if ipctemplate == nil {
    89  		return fmt.Errorf(fmt.Sprintf("%s not registered. Please register the system chaincode in inprocinstances.go", path))
    90  	}
    91  
    92  	if ipctemplate.chaincode == nil {
    93  		return fmt.Errorf(fmt.Sprintf("%s system chaincode does not contain chaincode instance", path))
    94  	}
    95  
    96  	instName, _ := vm.GetVMName(ccid, nil)
    97  	_, err := vm.getInstance(ctxt, ipctemplate, instName, args, env)
    98  
    99  	//FUTURE ... here is where we might check code for safety
   100  	inprocLogger.Debugf("registered : %s", path)
   101  
   102  	return err
   103  }
   104  
   105  func (ipc *inprocContainer) launchInProc(ctxt context.Context, id string, args []string, env []string, ccSupport ccintf.CCSupport) error {
   106  	peerRcvCCSend := make(chan *pb.ChaincodeMessage)
   107  	ccRcvPeerSend := make(chan *pb.ChaincodeMessage)
   108  	var err error
   109  	ccchan := make(chan struct{}, 1)
   110  	ccsupportchan := make(chan struct{}, 1)
   111  	go func() {
   112  		defer close(ccchan)
   113  		inprocLogger.Debugf("chaincode started for %s", id)
   114  		if args == nil {
   115  			args = ipc.args
   116  		}
   117  		if env == nil {
   118  			env = ipc.env
   119  		}
   120  		err := shim.StartInProc(env, args, ipc.chaincode, ccRcvPeerSend, peerRcvCCSend)
   121  		if err != nil {
   122  			err = fmt.Errorf("chaincode-support ended with err: %s", err)
   123  			inprocLogger.Errorf("%s", err)
   124  		}
   125  		inprocLogger.Debugf("chaincode ended with for  %s with err: %s", id, err)
   126  	}()
   127  
   128  	go func() {
   129  		defer close(ccsupportchan)
   130  		inprocStream := newInProcStream(peerRcvCCSend, ccRcvPeerSend)
   131  		inprocLogger.Debugf("chaincode-support started for  %s", id)
   132  		err := ccSupport.HandleChaincodeStream(ctxt, inprocStream)
   133  		if err != nil {
   134  			err = fmt.Errorf("chaincode ended with err: %s", err)
   135  			inprocLogger.Errorf("%s", err)
   136  		}
   137  		inprocLogger.Debugf("chaincode-support ended with for  %s with err: %s", id, err)
   138  	}()
   139  
   140  	select {
   141  	case <-ccchan:
   142  		close(peerRcvCCSend)
   143  		inprocLogger.Debugf("chaincode %s quit", id)
   144  	case <-ccsupportchan:
   145  		close(ccRcvPeerSend)
   146  		inprocLogger.Debugf("chaincode support %s quit", id)
   147  	case <-ipc.stopChan:
   148  		close(ccRcvPeerSend)
   149  		close(peerRcvCCSend)
   150  		inprocLogger.Debugf("chaincode %s stopped", id)
   151  	}
   152  
   153  	return err
   154  }
   155  
   156  //Start starts a previously registered system codechain
   157  func (vm *InprocVM) Start(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, builder container.BuildSpecFactory, prelaunchFunc container.PrelaunchFunc) error {
   158  	path := ccid.ChaincodeSpec.ChaincodeId.Path
   159  
   160  	ipctemplate := typeRegistry[path]
   161  
   162  	if ipctemplate == nil {
   163  		return fmt.Errorf(fmt.Sprintf("%s not registered", path))
   164  	}
   165  
   166  	instName, _ := vm.GetVMName(ccid, nil)
   167  
   168  	ipc, err := vm.getInstance(ctxt, ipctemplate, instName, args, env)
   169  
   170  	if err != nil {
   171  		return fmt.Errorf(fmt.Sprintf("could not create instance for %s", instName))
   172  	}
   173  
   174  	if ipc.running {
   175  		return fmt.Errorf(fmt.Sprintf("chaincode running %s", path))
   176  	}
   177  
   178  	//TODO VALIDITY CHECKS ?
   179  
   180  	ccSupport, ok := ctxt.Value(ccintf.GetCCHandlerKey()).(ccintf.CCSupport)
   181  	if !ok || ccSupport == nil {
   182  		return fmt.Errorf("in-process communication generator not supplied")
   183  	}
   184  
   185  	if prelaunchFunc != nil {
   186  		if err = prelaunchFunc(); err != nil {
   187  			return err
   188  		}
   189  	}
   190  
   191  	ipc.running = true
   192  
   193  	go func() {
   194  		defer func() {
   195  			if r := recover(); r != nil {
   196  				inprocLogger.Criticalf("caught panic from chaincode  %s", instName)
   197  			}
   198  		}()
   199  		ipc.launchInProc(ctxt, instName, args, env, ccSupport)
   200  	}()
   201  
   202  	return nil
   203  }
   204  
   205  //Stop stops a system codechain
   206  func (vm *InprocVM) Stop(ctxt context.Context, ccid ccintf.CCID, timeout uint, dontkill bool, dontremove bool) error {
   207  	path := ccid.ChaincodeSpec.ChaincodeId.Path
   208  
   209  	ipctemplate := typeRegistry[path]
   210  	if ipctemplate == nil {
   211  		return fmt.Errorf("%s not registered", path)
   212  	}
   213  
   214  	instName, _ := vm.GetVMName(ccid, nil)
   215  
   216  	ipc := instRegistry[instName]
   217  
   218  	if ipc == nil {
   219  		return fmt.Errorf("%s not found", instName)
   220  	}
   221  
   222  	if !ipc.running {
   223  		return fmt.Errorf("%s not running", instName)
   224  	}
   225  
   226  	ipc.stopChan <- struct{}{}
   227  
   228  	delete(instRegistry, instName)
   229  	//TODO stop
   230  	return nil
   231  }
   232  
   233  //Destroy destroys an image
   234  func (vm *InprocVM) Destroy(ctxt context.Context, ccid ccintf.CCID, force bool, noprune bool) error {
   235  	//not implemented
   236  	return nil
   237  }
   238  
   239  // GetVMName ignores the peer and network name as it just needs to be unique in
   240  // process.  It accepts a format function parameter to allow different
   241  // formatting based on the desired use of the name.
   242  func (vm *InprocVM) GetVMName(ccid ccintf.CCID, format func(string) (string, error)) (string, error) {
   243  	name := ccid.GetName()
   244  	if format != nil {
   245  		formattedName, err := format(name)
   246  		if err != nil {
   247  			return formattedName, err
   248  		}
   249  		name = formattedName
   250  	}
   251  	return name, nil
   252  }