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