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 }