github.com/defanghe/fabric@v2.1.1+incompatible/core/chaincode/lifecycle/endorsment_info.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package lifecycle 8 9 import ( 10 "github.com/hyperledger/fabric/core/ledger" 11 "github.com/hyperledger/fabric/core/scc" 12 13 "github.com/pkg/errors" 14 ) 15 16 //go:generate counterfeiter -o mock/legacy_lifecycle.go --fake-name LegacyLifecycle . Lifecycle 17 18 // Lifecycle is the interface which the core/chaincode package and core/endorser package requires that lifecycle satisfy. 19 type Lifecycle interface { 20 ChaincodeEndorsementInfo(channelID, chaincodeName string, qe ledger.SimpleQueryExecutor) (*ChaincodeEndorsementInfo, error) 21 } 22 23 //go:generate counterfeiter -o mock/chaincode_info_cache.go --fake-name ChaincodeInfoCache . ChaincodeInfoCache 24 type ChaincodeInfoCache interface { 25 ChaincodeInfo(channelID, chaincodeName string) (definition *LocalChaincodeInfo, err error) 26 } 27 28 // ChaincodeEndorsementInfo contains the information necessary to handle a chaincode invoke request. 29 type ChaincodeEndorsementInfo struct { 30 // Version is the version from the definition in this particular channel and namespace context. 31 Version string 32 33 // EnforceInit is set to true for definitions which require the chaincode package to enforce 34 // 'init exactly once' semantics. 35 EnforceInit bool 36 37 // ChaincodeID is the name by which to look up or launch the underlying chaincode. 38 ChaincodeID string 39 40 // EndorsementPlugin is the name of the plugin to use when endorsing. 41 EndorsementPlugin string 42 } 43 44 type ChaincodeEndorsementInfoSource struct { 45 Resources *Resources 46 Cache ChaincodeInfoCache 47 LegacyImpl Lifecycle 48 BuiltinSCCs scc.BuiltinSCCs 49 } 50 51 func (cei *ChaincodeEndorsementInfoSource) CachedChaincodeInfo(channelID, chaincodeName string, qe ledger.SimpleQueryExecutor) (*LocalChaincodeInfo, bool, error) { 52 var qes ReadableState = &SimpleQueryExecutorShim{ 53 Namespace: LifecycleNamespace, 54 SimpleQueryExecutor: qe, 55 } 56 57 if qe == nil { 58 // NOTE: the core/chaincode package inconsistently sets the 59 // query executor depending on whether the call has a channel 60 // context or not. We use this dummy shim which always returns 61 // an error for GetState calls to avoid a peer panic. 62 qes = &DummyQueryExecutorShim{} 63 } 64 65 currentSequence, err := cei.Resources.Serializer.DeserializeFieldAsInt64(NamespacesName, chaincodeName, "Sequence", qes) 66 if err != nil { 67 return nil, false, errors.WithMessagef(err, "could not get current sequence for chaincode '%s' on channel '%s'", chaincodeName, channelID) 68 } 69 70 // Committed sequences begin at 1 71 if currentSequence == 0 { 72 return nil, false, nil 73 } 74 75 chaincodeInfo, err := cei.Cache.ChaincodeInfo(channelID, chaincodeName) 76 if err != nil { 77 return nil, false, errors.WithMessage(err, "could not get approved chaincode info from cache") 78 } 79 80 if chaincodeInfo.Definition.Sequence != currentSequence { 81 // TODO this is a transient error which indicates that this query executor is executing against a chaincode 82 // whose definition has already changed (the cache may be ahead of the committed state, but never behind). In this 83 // case, we should simply abort the tx, and re-acquire a query executor and re-execute. There is no reason this 84 // error needs to be returned to the client. 85 return nil, false, errors.Errorf("chaincode cache at sequence %d but current sequence is %d, chaincode definition for '%s' changed during invoke", chaincodeInfo.Definition.Sequence, currentSequence, chaincodeName) 86 } 87 88 if !chaincodeInfo.Approved { 89 return nil, false, errors.Errorf("chaincode definition for '%s' at sequence %d on channel '%s' has not yet been approved by this org", chaincodeName, currentSequence, channelID) 90 } 91 92 if chaincodeInfo.InstallInfo == nil { 93 return nil, false, errors.Errorf("chaincode definition for '%s' exists, but chaincode is not installed", chaincodeName) 94 } 95 96 return chaincodeInfo, true, nil 97 98 } 99 100 // ChaincodeEndorsementInfo returns the information necessary to handle a chaincode invocation request, as well as a function to 101 // enforce security checks on the chaincode (in case the definition is from the legacy lscc). 102 func (cei *ChaincodeEndorsementInfoSource) ChaincodeEndorsementInfo(channelID, chaincodeName string, qe ledger.SimpleQueryExecutor) (*ChaincodeEndorsementInfo, error) { 103 if cei.BuiltinSCCs.IsSysCC(chaincodeName) { 104 return &ChaincodeEndorsementInfo{ 105 Version: scc.SysCCVersion, 106 EndorsementPlugin: "escc", 107 EnforceInit: false, 108 ChaincodeID: scc.ChaincodeID(chaincodeName), 109 }, nil 110 } 111 112 // return legacy cc endorsement info if V20 is not enabled 113 channelConfig := cei.Resources.ChannelConfigSource.GetStableChannelConfig(channelID) 114 if channelConfig == nil { 115 return nil, errors.Errorf("could not get channel config for channel '%s'", channelID) 116 } 117 ac, ok := channelConfig.ApplicationConfig() 118 if !ok { 119 return nil, errors.Errorf("could not get application config for channel '%s'", channelID) 120 } 121 if !ac.Capabilities().LifecycleV20() { 122 return cei.LegacyImpl.ChaincodeEndorsementInfo(channelID, chaincodeName, qe) 123 } 124 125 chaincodeInfo, ok, err := cei.CachedChaincodeInfo(channelID, chaincodeName, qe) 126 if err != nil { 127 return nil, err 128 } 129 if !ok { 130 return cei.LegacyImpl.ChaincodeEndorsementInfo(channelID, chaincodeName, qe) 131 } 132 133 return &ChaincodeEndorsementInfo{ 134 Version: chaincodeInfo.Definition.EndorsementInfo.Version, 135 EnforceInit: chaincodeInfo.Definition.EndorsementInfo.InitRequired, 136 EndorsementPlugin: chaincodeInfo.Definition.EndorsementInfo.EndorsementPlugin, 137 ChaincodeID: chaincodeInfo.InstallInfo.PackageID, // Local packages use package ID for ccid 138 }, nil 139 }