Skip to content

Commit

Permalink
[FAB-10947] chaincode error code handling for v1.2
Browse files Browse the repository at this point in the history
Below scenarios are handled,

*	PrematureChaincodeExecution indicates that an attempt was made
to invoke a chaincode that's in the process of being launched.
*	ChaincodeAlreadyLaunching indicates that an attempt for multiple
simultaneous invokes was made to launch chaincode
*	ChaincodeNameNotFound indicates that an that an attempt
was made to invoke a chaincode that's not yet initialized


Change-Id: I126170d7b363ebca3ce043b05af2048ffadd840e
Signed-off-by: Sudesh Shetty <sudesh.shetty@securekey.com>
  • Loading branch information
sudeshrshetty committed Jun 29, 2018
1 parent 3ae9e66 commit 7a9d5ba
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 2 deletions.
3 changes: 3 additions & 0 deletions pkg/common/errors/retry/defaults.go
Expand Up @@ -72,6 +72,7 @@ var DefaultRetryableCodes = map[status.Group][]status.Code{
status.EndorsementMismatch,
status.PrematureChaincodeExecution,
status.ChaincodeAlreadyLaunching,
status.ChaincodeNameNotFound,
},
status.EndorserServerStatus: {
status.Code(common.Status_SERVICE_UNAVAILABLE),
Expand Down Expand Up @@ -101,6 +102,7 @@ var ResMgmtDefaultRetryableCodes = map[status.Group][]status.Code{
status.EndorsementMismatch,
status.PrematureChaincodeExecution,
status.ChaincodeAlreadyLaunching,
status.ChaincodeNameNotFound,
},
status.EndorserServerStatus: {
status.Code(common.Status_SERVICE_UNAVAILABLE),
Expand Down Expand Up @@ -135,6 +137,7 @@ var ChannelClientRetryableCodes = map[status.Group][]status.Code{
status.ConnectionFailed, status.EndorsementMismatch,
status.PrematureChaincodeExecution,
status.ChaincodeAlreadyLaunching,
status.ChaincodeNameNotFound,
},
status.EndorserServerStatus: {
status.Code(common.Status_SERVICE_UNAVAILABLE),
Expand Down
3 changes: 3 additions & 0 deletions pkg/common/errors/status/codes.go
Expand Up @@ -61,6 +61,9 @@ const (

// GenericTransient is generally used by tests to indicate that a retry is possible
GenericTransient Code = 12

// ChaincodeNameNotFound indicates that an that an attempt was made to invoke a chaincode that's not yet initialized
ChaincodeNameNotFound Code = 23
)

// CodeName maps the codes in this packages to human-readable strings
Expand Down
29 changes: 29 additions & 0 deletions pkg/fab/peer/peerendorser.go
Expand Up @@ -152,6 +152,7 @@ func (p *peerEndorser) sendProposal(ctx reqContext.Context, proposal fab.Process
endorserClient := pb.NewEndorserClient(conn)
resp, err := endorserClient.ProcessProposal(ctx, proposal.SignedProposal)

//TODO separate check for stable & devstable error messages should be refactored
if err != nil {
logger.Errorf("process proposal failed [%s]", err)
rpcStatus, ok := grpcstatus.FromError(err)
Expand All @@ -167,6 +168,11 @@ func (p *peerEndorser) sendProposal(ctx reqContext.Context, proposal fab.Process
code, message1, extractErr = extractChaincodeAlreadyLaunchingError(rpcStatus)
}

if extractErr != nil {
//if not chaincode already launching error, then look for chaincode name not found error
code, message1, extractErr = extractChaincodeNameNotFoundError(rpcStatus)
}

if extractErr != nil {
err = status.NewFromGRPCStatus(rpcStatus)
} else {
Expand Down Expand Up @@ -217,6 +223,15 @@ func extractChaincodeError(status *grpcstatus.Status) (int, string, error) {
func extractChaincodeErrorFromResponse(resp *pb.ProposalResponse) error {
if resp.Response.Status < int32(common.Status_SUCCESS) || resp.Response.Status >= int32(common.Status_BAD_REQUEST) {
details := []interface{}{resp.Endorsement, resp.Response.Payload}
if strings.Contains(resp.Response.Message, "premature execution") {
return status.New(status.EndorserClientStatus, int32(status.PrematureChaincodeExecution), resp.Response.Message, details)
} else if strings.Contains(resp.Response.Message, "chaincode is already launching") {
return status.New(status.EndorserClientStatus, int32(status.ChaincodeAlreadyLaunching), resp.Response.Message, details)
} else if strings.Contains(resp.Response.Message, "could not find chaincode with name") {
return status.New(status.EndorserClientStatus, int32(status.ChaincodeNameNotFound), resp.Response.Message, details)
} else if strings.Contains(resp.Response.Message, "cannot get package for chaincode") {
return status.New(status.EndorserClientStatus, int32(status.ChaincodeNameNotFound), resp.Response.Message, details)
}
return status.New(status.ChaincodeStatus, resp.Response.Status, resp.Response.Message, details)
}
return nil
Expand Down Expand Up @@ -257,6 +272,20 @@ func extractChaincodeAlreadyLaunchingError(grpcstat *grpcstatus.Status) (int32,
return int32(status.ChaincodeAlreadyLaunching), grpcstat.Message()[index:], nil
}

func extractChaincodeNameNotFoundError(grpcstat *grpcstatus.Status) (int32, string, error) {
if grpcstat.Code().String() != statusCodeUnknown || grpcstat.Message() == "" {
return 0, "", errors.New("not a 'could not find chaincode with name' error")
}
index := strings.Index(grpcstat.Message(), "could not find chaincode with name")
if index == -1 {
index = strings.Index(grpcstat.Message(), "cannot get package for chaincode")
if index == -1 {
return 0, "", errors.New("not a 'could not find chaincode with name' error")
}
}
return int32(status.ChaincodeNameNotFound), grpcstat.Message()[index:], nil
}

// getChaincodeResponseStatus gets the actual response status from response.Payload.extension.Response.status, as fabric always returns actual 200
func getChaincodeResponseStatus(response *pb.ProposalResponse) int32 {
if response.Payload != nil {
Expand Down
85 changes: 83 additions & 2 deletions pkg/fab/peer/peerendorser_test.go
Expand Up @@ -297,8 +297,8 @@ func TestExtractPrematureExecError(t *testing.T) {
func TestExtractChaincodeAlreadyLaunchingError(t *testing.T) {

err := grpcstatus.New(grpcCodes.Unknown, "some error")
_, _, e := extractPrematureExecutionError(err)
assert.EqualError(t, e, "not a premature execution error")
_, _, e := extractChaincodeAlreadyLaunchingError(err)
assert.EqualError(t, e, "not a chaincode already launching error")

err = grpcstatus.New(grpcCodes.Unknown, "error executing chaincode: error chaincode is already launching: somecc:v1")
code, message, extractErr := extractChaincodeAlreadyLaunchingError(err)
Expand All @@ -314,6 +314,32 @@ func TestExtractChaincodeAlreadyLaunchingError(t *testing.T) {

}

func TestExtractChaincodeNameNotFoundError(t *testing.T) {

err := grpcstatus.New(grpcCodes.Unknown, "some error")
_, _, e := extractChaincodeNameNotFoundError(err)
assert.EqualError(t, e, "not a 'could not find chaincode with name' error")

err = grpcstatus.New(grpcCodes.Unknown, "make sure the chaincode uq7q9y7lu7 has been successfully instantiated and try again: getccdata mychannel/uq7q9y7lu7 responded with error: could not find chaincode with name 'uq7q9y7lu7'")
code, message, extractErr := extractChaincodeNameNotFoundError(err)
assert.EqualValues(t, int32(status.ChaincodeNameNotFound), code, "Expected chaincode name not found error")
assert.EqualValues(t, "could not find chaincode with name 'uq7q9y7lu7'", message, "Invalid message")
assert.Nil(t, extractErr)

err = grpcstatus.New(grpcCodes.Unknown, "cannot get package for chaincode (vl5knffa37:v0)")
code, message, extractErr = extractChaincodeNameNotFoundError(err)
assert.EqualValues(t, int32(status.ChaincodeNameNotFound), code, "Expected chaincode name not found error")
assert.EqualValues(t, "cannot get package for chaincode (vl5knffa37:v0)", message, "Invalid message")
assert.Nil(t, extractErr)

err = grpcstatus.New(grpcCodes.Unknown, "error executing chaincode: some random error: somecc:v1")
code, message, extractErr = extractChaincodeNameNotFoundError(err)
assert.NotNil(t, extractErr)
assert.EqualValues(t, 0, code)
assert.Empty(t, message)

}

func TestChaincodeStatusFromResponse(t *testing.T) {
//For error response
response := &pb.ProposalResponse{
Expand Down Expand Up @@ -342,4 +368,59 @@ func TestChaincodeStatusFromResponse(t *testing.T) {
err = extractChaincodeErrorFromResponse(response)
assert.True(t, ok)
assert.Nil(t, err)

//For error response - premature execution
response = &pb.ProposalResponse{
Response: &pb.Response{Status: 500, Payload: []byte("Unknown Description"), Message: "transaction returned with failure: premature execution - chaincode (somecc:v1) is being launched"},
}
err = extractChaincodeErrorFromResponse(response)
s, ok = status.FromError(err)
assert.True(t, ok)
assert.Equal(t, "transaction returned with failure: premature execution - chaincode (somecc:v1) is being launched", s.Message)
assert.Equal(t, int32(status.PrematureChaincodeExecution), s.Code)
assert.Equal(t, status.EndorserClientStatus, s.Group)

//For error response - premature execution
response = &pb.ProposalResponse{
Response: &pb.Response{Status: 500, Payload: []byte("Unknown Description"), Message: "transaction returned with failure: premature execution - chaincode (somecc:v1) is being launched"},
}
err = extractChaincodeErrorFromResponse(response)
s, ok = status.FromError(err)
assert.True(t, ok)
assert.Equal(t, "transaction returned with failure: premature execution - chaincode (somecc:v1) is being launched", s.Message)
assert.Equal(t, int32(status.PrematureChaincodeExecution), s.Code)
assert.Equal(t, status.EndorserClientStatus, s.Group)

//For error response - chaincode already launching
response = &pb.ProposalResponse{
Response: &pb.Response{Status: 500, Payload: []byte("Unknown Description"), Message: "error executing chaincode: error chaincode is already launching: somecc:v1"},
}
err = extractChaincodeErrorFromResponse(response)
s, ok = status.FromError(err)
assert.True(t, ok)
assert.Equal(t, "error executing chaincode: error chaincode is already launching: somecc:v1", s.Message)
assert.Equal(t, int32(status.ChaincodeAlreadyLaunching), s.Code)
assert.Equal(t, status.EndorserClientStatus, s.Group)

//For error response - chaincode name not found
response = &pb.ProposalResponse{
Response: &pb.Response{Status: 500, Payload: []byte("Unknown Description"), Message: "make sure the chaincode uq7q9y7lu7 has been successfully instantiated and try again: getccdata mychannel/uq7q9y7lu7 responded with error: could not find chaincode with name 'uq7q9y7lu7'"},
}
err = extractChaincodeErrorFromResponse(response)
s, ok = status.FromError(err)
assert.True(t, ok)
assert.Equal(t, "make sure the chaincode uq7q9y7lu7 has been successfully instantiated and try again: getccdata mychannel/uq7q9y7lu7 responded with error: could not find chaincode with name 'uq7q9y7lu7'", s.Message)
assert.Equal(t, int32(status.ChaincodeNameNotFound), s.Code)
assert.Equal(t, status.EndorserClientStatus, s.Group)

//For error response - chaincode package not found
response = &pb.ProposalResponse{
Response: &pb.Response{Status: 500, Payload: []byte("Unknown Description"), Message: "cannot get package for chaincode (vl5knffa37:v0)"},
}
err = extractChaincodeErrorFromResponse(response)
s, ok = status.FromError(err)
assert.True(t, ok)
assert.Equal(t, "cannot get package for chaincode (vl5knffa37:v0)", s.Message)
assert.Equal(t, int32(status.ChaincodeNameNotFound), s.Code)
assert.Equal(t, status.EndorserClientStatus, s.Group)
}

0 comments on commit 7a9d5ba

Please sign in to comment.