HandleTransaction is a middleware function that obtains and verifies a transaction context prior to forwarding the message to the provided delegate. Response messages returned by the delegate are sent to the chat stream. Any errors returned by the delegate are packaged as chaincode error messages.
(msg *pb.ChaincodeMessage, delegate handleFunc)
| 245 | // returned by the delegate are sent to the chat stream. Any errors returned by the |
| 246 | // delegate are packaged as chaincode error messages. |
| 247 | func (h *Handler) HandleTransaction(msg *pb.ChaincodeMessage, delegate handleFunc) { |
| 248 | startTime := time.Now() |
| 249 | meterLabels := []string{ |
| 250 | "type", msg.Type.String(), |
| 251 | "channel", msg.ChannelId, |
| 252 | "chaincode", h.chaincodeID, |
| 253 | } |
| 254 | |
| 255 | // Recover from panics that can occur when the transaction context is cleaned up |
| 256 | // (e.g., iterators closed) due to execution timeout while this goroutine is still |
| 257 | // actively using those resources. This prevents peer crashes from nil pointer |
| 258 | // dereferences in the underlying LevelDB iterator. |
| 259 | // See https://github.com/hyperledger/fabric/issues/5048 |
| 260 | defer func() { |
| 261 | if r := recover(); r != nil { |
| 262 | chaincodeLogger.Errorf("[%s] Recovered from panic handling %s: %v", shorttxid(msg.Txid), msg.Type, r) |
| 263 | resp := &pb.ChaincodeMessage{ |
| 264 | Type: pb.ChaincodeMessage_ERROR, |
| 265 | Payload: []byte(fmt.Sprintf("%s failed: transaction ID: %s: panic during execution", msg.Type, msg.Txid)), |
| 266 | Txid: msg.Txid, |
| 267 | ChannelId: msg.ChannelId, |
| 268 | } |
| 269 | h.ActiveTransactions.Remove(msg.ChannelId, msg.Txid) |
| 270 | h.serialSendAsync(resp) |
| 271 | |
| 272 | meterLabels = append(meterLabels, "success", "false") |
| 273 | h.Metrics.ShimRequestDuration.With(meterLabels...).Observe(time.Since(startTime).Seconds()) |
| 274 | h.Metrics.ShimRequestsCompleted.With(meterLabels...).Add(1) |
| 275 | } |
| 276 | }() |
| 277 | |
| 278 | chaincodeLogger.Debugf("[%s] handling %s from chaincode", shorttxid(msg.Txid), msg.Type.String()) |
| 279 | if !h.registerTxid(msg) { |
| 280 | return |
| 281 | } |
| 282 | |
| 283 | var txContext *TransactionContext |
| 284 | var err error |
| 285 | if msg.Type == pb.ChaincodeMessage_INVOKE_CHAINCODE { |
| 286 | txContext, err = h.getTxContextForInvoke(msg.ChannelId, msg.Txid, msg.Payload, "") |
| 287 | } else { |
| 288 | txContext, err = h.isValidTxSim(msg.ChannelId, msg.Txid, "no ledger context") |
| 289 | } |
| 290 | |
| 291 | h.Metrics.ShimRequestsReceived.With(meterLabels...).Add(1) |
| 292 | |
| 293 | var resp *pb.ChaincodeMessage |
| 294 | if err == nil { |
| 295 | resp, err = delegate(msg, txContext) |
| 296 | } |
| 297 | |
| 298 | if err != nil { |
| 299 | err = errors.Wrapf(err, "%s failed: transaction ID: %s", msg.Type, msg.Txid) |
| 300 | chaincodeLogger.Errorf("[%s] Failed to handle %s. error: %+v", shorttxid(msg.Txid), msg.Type, err) |
| 301 | resp = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: []byte(err.Error()), Txid: msg.Txid, ChannelId: msg.ChannelId} |
| 302 | } |
| 303 | |
| 304 | chaincodeLogger.Debugf("[%s] Completed %s. Sending %s", shorttxid(msg.Txid), msg.Type, resp.Type) |
no test coverage detected