github.com/yous1230/fabric@v2.0.0-beta.0.20191224111736-74345bee6ac2+incompatible/core/chaincode/chaincode_support.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package chaincode
     8  
     9  import (
    10  	"bytes"
    11  	"time"
    12  	"unicode/utf8"
    13  
    14  	"github.com/golang/protobuf/proto"
    15  	pb "github.com/hyperledger/fabric-protos-go/peer"
    16  	"github.com/hyperledger/fabric/common/util"
    17  	"github.com/hyperledger/fabric/core/chaincode/extcc"
    18  	"github.com/hyperledger/fabric/core/chaincode/lifecycle"
    19  	"github.com/hyperledger/fabric/core/common/ccprovider"
    20  	"github.com/hyperledger/fabric/core/container/ccintf"
    21  	"github.com/hyperledger/fabric/core/ledger"
    22  	"github.com/hyperledger/fabric/core/peer"
    23  	"github.com/hyperledger/fabric/core/scc"
    24  	"github.com/pkg/errors"
    25  )
    26  
    27  const (
    28  	// InitializedKeyName is the reserved key in a chaincode's namespace which
    29  	// records the ID of the chaincode which initialized the namespace.
    30  	// In this way, we can enforce Init exactly once semantics, whenever
    31  	// the backing chaincode bytes change (but not be required to re-initialize
    32  	// the chaincode say, when endorsement policy changes).
    33  	InitializedKeyName = "\x00" + string(utf8.MaxRune) + "initialized"
    34  )
    35  
    36  // Runtime is used to manage chaincode runtime instances.
    37  type Runtime interface {
    38  	Build(ccid string) (*ccintf.ChaincodeServerInfo, error)
    39  	Start(ccid string, ccinfo *ccintf.PeerConnection) error
    40  	Stop(ccid string) error
    41  	Wait(ccid string) (int, error)
    42  }
    43  
    44  // Launcher is used to launch chaincode runtimes.
    45  type Launcher interface {
    46  	Launch(ccid string, streamHandler extcc.StreamHandler) error
    47  }
    48  
    49  // Lifecycle provides a way to retrieve chaincode definitions and the packages necessary to run them
    50  type Lifecycle interface {
    51  	// ChaincodeEndorsementInfo looks up the chaincode info in the given channel.  It is the responsibility
    52  	// of the implementation to add appropriate read dependencies for the information returned.
    53  	ChaincodeEndorsementInfo(channelID, chaincodeName string, qe ledger.SimpleQueryExecutor) (*lifecycle.ChaincodeEndorsementInfo, error)
    54  }
    55  
    56  // ChaincodeSupport responsible for providing interfacing with chaincodes from the Peer.
    57  type ChaincodeSupport struct {
    58  	ACLProvider            ACLProvider
    59  	AppConfig              ApplicationConfigRetriever
    60  	BuiltinSCCs            scc.BuiltinSCCs
    61  	DeployedCCInfoProvider ledger.DeployedChaincodeInfoProvider
    62  	ExecuteTimeout         time.Duration
    63  	InstallTimeout         time.Duration
    64  	HandlerMetrics         *HandlerMetrics
    65  	HandlerRegistry        *HandlerRegistry
    66  	Keepalive              time.Duration
    67  	Launcher               Launcher
    68  	Lifecycle              Lifecycle
    69  	Peer                   *peer.Peer
    70  	Runtime                Runtime
    71  	TotalQueryLimit        int
    72  	UserRunsCC             bool
    73  }
    74  
    75  // Launch starts executing chaincode if it is not already running. This method
    76  // blocks until the peer side handler gets into ready state or encounters a fatal
    77  // error. If the chaincode is already running, it simply returns.
    78  func (cs *ChaincodeSupport) Launch(ccid string) (*Handler, error) {
    79  	if h := cs.HandlerRegistry.Handler(ccid); h != nil {
    80  		return h, nil
    81  	}
    82  
    83  	if err := cs.Launcher.Launch(ccid, cs); err != nil {
    84  		return nil, errors.Wrapf(err, "could not launch chaincode %s", ccid)
    85  	}
    86  
    87  	h := cs.HandlerRegistry.Handler(ccid)
    88  	if h == nil {
    89  		return nil, errors.Errorf("claimed to start chaincode container for %s but could not find handler", ccid)
    90  	}
    91  
    92  	return h, nil
    93  }
    94  
    95  // LaunchInProc is a stopgap solution to be called by the inproccontroller to allow system chaincodes to register
    96  func (cs *ChaincodeSupport) LaunchInProc(ccid string) <-chan struct{} {
    97  	launchStatus, ok := cs.HandlerRegistry.Launching(ccid)
    98  	if ok {
    99  		chaincodeLogger.Panicf("attempted to launch a system chaincode which has already been launched")
   100  	}
   101  
   102  	return launchStatus.Done()
   103  }
   104  
   105  // HandleChaincodeStream implements ccintf.HandleChaincodeStream for all vms to call with appropriate stream
   106  func (cs *ChaincodeSupport) HandleChaincodeStream(stream ccintf.ChaincodeStream) error {
   107  	handler := &Handler{
   108  		Invoker:                cs,
   109  		Keepalive:              cs.Keepalive,
   110  		Registry:               cs.HandlerRegistry,
   111  		ACLProvider:            cs.ACLProvider,
   112  		TXContexts:             NewTransactionContexts(),
   113  		ActiveTransactions:     NewActiveTransactions(),
   114  		BuiltinSCCs:            cs.BuiltinSCCs,
   115  		QueryResponseBuilder:   &QueryResponseGenerator{MaxResultLimit: 100},
   116  		UUIDGenerator:          UUIDGeneratorFunc(util.GenerateUUID),
   117  		LedgerGetter:           cs.Peer,
   118  		DeployedCCInfoProvider: cs.DeployedCCInfoProvider,
   119  		AppConfig:              cs.AppConfig,
   120  		Metrics:                cs.HandlerMetrics,
   121  		TotalQueryLimit:        cs.TotalQueryLimit,
   122  	}
   123  
   124  	return handler.ProcessStream(stream)
   125  }
   126  
   127  // Register the bidi stream entry point called by chaincode to register with the Peer.
   128  func (cs *ChaincodeSupport) Register(stream pb.ChaincodeSupport_RegisterServer) error {
   129  	return cs.HandleChaincodeStream(stream)
   130  }
   131  
   132  // ExecuteLegacyInit is a temporary method which should be removed once the old style lifecycle
   133  // is entirely deprecated.  Ideally one release after the introduction of the new lifecycle.
   134  // It does not attempt to start the chaincode based on the information from lifecycle, but instead
   135  // accepts the container information directly in the form of a ChaincodeDeploymentSpec.
   136  func (cs *ChaincodeSupport) ExecuteLegacyInit(txParams *ccprovider.TransactionParams, ccName, ccVersion string, input *pb.ChaincodeInput) (*pb.Response, *pb.ChaincodeEvent, error) {
   137  	// FIXME: this is a hack, we shouldn't construct the
   138  	// ccid manually but rather let lifecycle construct it
   139  	// for us. However this is legacy code that will disappear
   140  	// so it is acceptable for now (FAB-14627)
   141  	ccid := ccName + ":" + ccVersion
   142  
   143  	h, err := cs.Launch(ccid)
   144  	if err != nil {
   145  		return nil, nil, err
   146  	}
   147  
   148  	resp, err := cs.execute(pb.ChaincodeMessage_INIT, txParams, ccName, input, h)
   149  	return processChaincodeExecutionResult(txParams.TxID, ccName, resp, err)
   150  }
   151  
   152  // Execute invokes chaincode and returns the original response.
   153  func (cs *ChaincodeSupport) Execute(txParams *ccprovider.TransactionParams, chaincodeName string, input *pb.ChaincodeInput) (*pb.Response, *pb.ChaincodeEvent, error) {
   154  	resp, err := cs.Invoke(txParams, chaincodeName, input)
   155  	return processChaincodeExecutionResult(txParams.TxID, chaincodeName, resp, err)
   156  }
   157  
   158  func processChaincodeExecutionResult(txid, ccName string, resp *pb.ChaincodeMessage, err error) (*pb.Response, *pb.ChaincodeEvent, error) {
   159  	if err != nil {
   160  		return nil, nil, errors.Wrapf(err, "failed to execute transaction %s", txid)
   161  	}
   162  	if resp == nil {
   163  		return nil, nil, errors.Errorf("nil response from transaction %s", txid)
   164  	}
   165  
   166  	if resp.ChaincodeEvent != nil {
   167  		resp.ChaincodeEvent.ChaincodeId = ccName
   168  		resp.ChaincodeEvent.TxId = txid
   169  	}
   170  
   171  	switch resp.Type {
   172  	case pb.ChaincodeMessage_COMPLETED:
   173  		res := &pb.Response{}
   174  		err := proto.Unmarshal(resp.Payload, res)
   175  		if err != nil {
   176  			return nil, nil, errors.Wrapf(err, "failed to unmarshal response for transaction %s", txid)
   177  		}
   178  		return res, resp.ChaincodeEvent, nil
   179  
   180  	case pb.ChaincodeMessage_ERROR:
   181  		return nil, resp.ChaincodeEvent, errors.Errorf("transaction returned with failure: %s", resp.Payload)
   182  
   183  	default:
   184  		return nil, nil, errors.Errorf("unexpected response type %d for transaction %s", resp.Type, txid)
   185  	}
   186  }
   187  
   188  // Invoke will invoke chaincode and return the message containing the response.
   189  // The chaincode will be launched if it is not already running.
   190  func (cs *ChaincodeSupport) Invoke(txParams *ccprovider.TransactionParams, chaincodeName string, input *pb.ChaincodeInput) (*pb.ChaincodeMessage, error) {
   191  	ccid, cctype, err := cs.CheckInvocation(txParams, chaincodeName, input)
   192  	if err != nil {
   193  		return nil, errors.WithMessage(err, "invalid invocation")
   194  	}
   195  
   196  	h, err := cs.Launch(ccid)
   197  	if err != nil {
   198  		return nil, err
   199  	}
   200  
   201  	return cs.execute(cctype, txParams, chaincodeName, input, h)
   202  }
   203  
   204  // CheckInvocation inspects the parameters of an invocation and determines if, how, and to where a that invocation should be routed.
   205  // First, we ensure that the target namespace is defined on the channel and invokable on this peer, according to the lifecycle implementation.
   206  // Then, if the chaincode definition requires it, this function enforces 'init exactly once' semantics.
   207  // Finally, it returns the chaincode ID to route to and the message type of the request (normal transation, or init).
   208  func (cs *ChaincodeSupport) CheckInvocation(txParams *ccprovider.TransactionParams, chaincodeName string, input *pb.ChaincodeInput) (ccid string, cctype pb.ChaincodeMessage_Type, err error) {
   209  	chaincodeLogger.Debugf("[%s] getting chaincode data for %s on channel %s", shorttxid(txParams.TxID), chaincodeName, txParams.ChannelID)
   210  	cii, err := cs.Lifecycle.ChaincodeEndorsementInfo(txParams.ChannelID, chaincodeName, txParams.TXSimulator)
   211  	if err != nil {
   212  		logDevModeError(cs.UserRunsCC)
   213  		return "", 0, errors.Wrapf(err, "[channel %s] failed to get chaincode container info for %s", txParams.ChannelID, chaincodeName)
   214  	}
   215  
   216  	needsInitialization := false
   217  	if cii.EnforceInit {
   218  
   219  		value, err := txParams.TXSimulator.GetState(chaincodeName, InitializedKeyName)
   220  		if err != nil {
   221  			return "", 0, errors.WithMessage(err, "could not get 'initialized' key")
   222  		}
   223  
   224  		needsInitialization = !bytes.Equal(value, []byte(cii.Version))
   225  	}
   226  
   227  	// Note, IsInit is a new field for v2.0 and should only be set for invocations of non-legacy chaincodes.
   228  	// Any invocation of a legacy chaincode with IsInit set will fail.  This is desirable, as the old
   229  	// InstantiationPolicy contract enforces which users may call init.
   230  	if input.IsInit {
   231  		if !cii.EnforceInit {
   232  			return "", 0, errors.Errorf("chaincode '%s' does not require initialization but called as init", chaincodeName)
   233  		}
   234  
   235  		if !needsInitialization {
   236  			return "", 0, errors.Errorf("chaincode '%s' is already initialized but called as init", chaincodeName)
   237  		}
   238  
   239  		err = txParams.TXSimulator.SetState(chaincodeName, InitializedKeyName, []byte(cii.Version))
   240  		if err != nil {
   241  			return "", 0, errors.WithMessage(err, "could not set 'initialized' key")
   242  		}
   243  
   244  		return cii.ChaincodeID, pb.ChaincodeMessage_INIT, nil
   245  	}
   246  
   247  	if needsInitialization {
   248  		return "", 0, errors.Errorf("chaincode '%s' has not been initialized for this version, must call as init first", chaincodeName)
   249  	}
   250  
   251  	return cii.ChaincodeID, pb.ChaincodeMessage_TRANSACTION, nil
   252  }
   253  
   254  // execute executes a transaction and waits for it to complete until a timeout value.
   255  func (cs *ChaincodeSupport) execute(cctyp pb.ChaincodeMessage_Type, txParams *ccprovider.TransactionParams, namespace string, input *pb.ChaincodeInput, h *Handler) (*pb.ChaincodeMessage, error) {
   256  	input.Decorations = txParams.ProposalDecorations
   257  
   258  	payload, err := proto.Marshal(input)
   259  	if err != nil {
   260  		return nil, errors.WithMessage(err, "failed to create chaincode message")
   261  	}
   262  
   263  	ccMsg := &pb.ChaincodeMessage{
   264  		Type:      cctyp,
   265  		Payload:   payload,
   266  		Txid:      txParams.TxID,
   267  		ChannelId: txParams.ChannelID,
   268  	}
   269  
   270  	timeout := cs.executeTimeout(namespace, input)
   271  	ccresp, err := h.Execute(txParams, namespace, ccMsg, timeout)
   272  	if err != nil {
   273  		return nil, errors.WithMessage(err, "error sending")
   274  	}
   275  
   276  	return ccresp, nil
   277  }
   278  
   279  func (cs *ChaincodeSupport) executeTimeout(namespace string, input *pb.ChaincodeInput) time.Duration {
   280  	operation := chaincodeOperation(input.Args)
   281  	switch {
   282  	case namespace == "lscc" && operation == "install":
   283  		return maxDuration(cs.InstallTimeout, cs.ExecuteTimeout)
   284  	case namespace == lifecycle.LifecycleNamespace && operation == lifecycle.InstallChaincodeFuncName:
   285  		return maxDuration(cs.InstallTimeout, cs.ExecuteTimeout)
   286  	default:
   287  		return cs.ExecuteTimeout
   288  	}
   289  }
   290  
   291  func maxDuration(durations ...time.Duration) time.Duration {
   292  	var result time.Duration
   293  	for _, d := range durations {
   294  		if d > result {
   295  			result = d
   296  		}
   297  	}
   298  	return result
   299  }
   300  
   301  func chaincodeOperation(args [][]byte) string {
   302  	if len(args) == 0 {
   303  		return ""
   304  	}
   305  	return string(args[0])
   306  }
   307  
   308  func logDevModeError(userRunsCC bool) {
   309  	if userRunsCC {
   310  		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?")
   311  	}
   312  }