Skip to content

Commit

Permalink
[FABG-982] Remove legacy endorser status parsing (#89)
Browse files Browse the repository at this point in the history
Remove handling of endorser status messages from Fabric v1.1

Signed-off-by: Troy Ronda <troy@troyronda.com>
  • Loading branch information
troyronda authored Jul 3, 2020
1 parent 57e60e6 commit d44e5d6
Show file tree
Hide file tree
Showing 3 changed files with 6 additions and 227 deletions.
118 changes: 3 additions & 115 deletions pkg/fab/peer/peerendorser.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import (
reqContext "context"
"crypto/x509"
"regexp"
"strconv"
"strings"
"time"

"github.com/pkg/errors"
Expand All @@ -36,10 +34,9 @@ const (
// GRPC max message size (same as Fabric)
maxCallRecvMsgSize = 100 * 1024 * 1024
maxCallSendMsgSize = 100 * 1024 * 1024
statusCodeUnknown = "Unknown"
)

var chaincodeNotFoundPattern = regexp.MustCompile(`chaincode [^ ]+ not found`)
var chaincodeNotFoundPattern = regexp.MustCompile(`(chaincode [^ ]+ not found)|(could not find chaincode with name)|(cannot get package for chaincode)`)

// peerEndorser enables access to a GRPC-based endorser for running transaction proposal simulations
type peerEndorser struct {
Expand Down Expand Up @@ -160,36 +157,12 @@ 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)

if ok {
code, message, extractErr := extractChaincodeError(rpcStatus)
if extractErr != nil {

code, message1, extractErr := extractPrematureExecutionError(rpcStatus)

if extractErr != nil {
//if not premature execution error, then look for chaincode already launching error
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 {
err = status.New(status.EndorserClientStatus, code, message1, nil)
}

} else {
err = status.NewFromExtractedChaincodeError(code, message)
}
err = status.NewFromGRPCStatus(rpcStatus)
}
} else {
//check error from response (for :fabric v1.2 and later)
Expand All @@ -199,103 +172,18 @@ func (p *peerEndorser) sendProposal(ctx reqContext.Context, proposal fab.Process
return resp, err
}

func extractChaincodeError(status *grpcstatus.Status) (int, string, error) {
var code int
var message string
if status.Code().String() != statusCodeUnknown || status.Message() == "" {
return 0, "", errors.New("Unable to parse GRPC status message")
}
statusLength := len("status:")
messageLength := len("message:")
if strings.Contains(status.Message(), "status:") {
i := strings.Index(status.Message(), "status:")
if i >= 0 {
j := strings.Index(status.Message()[i:], ",")
if j > statusLength {
i1, err := strconv.Atoi(strings.TrimSpace(status.Message()[i+statusLength : i+j]))
if err != nil {
return 0, "", errors.Errorf("Non-number returned as GRPC status [%s] ", strings.TrimSpace(status.Message()[i1+statusLength:i1+j]))
}
code = i1
}
}
}
message = checkMessage(status, messageLength, message)
if code != 0 && message != "" {
return code, message, nil
}
return code, message, errors.Errorf("Unable to parse GRPC Status Message Code: %v Message: %v", code, message)
}

//extractChaincodeErrorFromResponse extracts chaincode error from proposal response
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 chaincodeNotFoundPattern.MatchString(resp.Response.Message) {
return status.New(status.EndorserClientStatus, int32(status.ChaincodeNameNotFound), resp.Response.Message, details)
} else if strings.Contains(resp.Response.Message, "cannot get package for chaincode") {
if chaincodeNotFoundPattern.MatchString(resp.Response.Message) {
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
}

func checkMessage(status *grpcstatus.Status, messageLength int, message string) string {
if strings.Contains(status.Message(), "message:") {
i := strings.Index(status.Message(), "message:")
if i >= 0 {
j := strings.LastIndex(status.Message()[i:], ")")
if j > messageLength {
message = strings.TrimSpace(status.Message()[i+messageLength : i+j])
}
}
}
return message
}

func extractPrematureExecutionError(grpcstat *grpcstatus.Status) (int32, string, error) {
if grpcstat.Code().String() != statusCodeUnknown || grpcstat.Message() == "" {
return 0, "", errors.New("not a premature execution error")
}
index := strings.Index(grpcstat.Message(), "premature execution")
if index == -1 {
return 0, "", errors.New("not a premature execution error")
}
return int32(status.PrematureChaincodeExecution), grpcstat.Message()[index:], nil
}

func extractChaincodeAlreadyLaunchingError(grpcstat *grpcstatus.Status) (int32, string, error) {
if grpcstat.Code().String() != statusCodeUnknown || grpcstat.Message() == "" {
return 0, "", errors.New("not a chaincode already launching error")
}
index := strings.Index(grpcstat.Message(), "error chaincode is already launching:")
if index == -1 {
return 0, "", errors.New("not a chaincode already launching error")
}
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, error) {
if response.Payload != nil {
Expand Down
113 changes: 2 additions & 111 deletions pkg/fab/peer/peerendorser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,14 @@ import (
"time"

"github.com/golang/mock/gomock"
pb "github.com/hyperledger/fabric-protos-go/peer"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc"
grpcCodes "google.golang.org/grpc/codes"
"google.golang.org/grpc/keepalive"
grpcstatus "google.golang.org/grpc/status"

"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
pb "github.com/hyperledger/fabric-protos-go/peer"

"github.com/hyperledger/fabric-sdk-go/pkg/common/errors/status"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/test/mockfab"
"github.com/hyperledger/fabric-sdk-go/pkg/fab/mocks"
)
Expand Down Expand Up @@ -256,80 +254,6 @@ func TestEndorserRPCError(t *testing.T) {
assert.Equal(t, grpcCodes.Unknown, grpcCode)
}

func TestExtractChainCodeError(t *testing.T) {
expectedMsg := "Chaincode error(status: 500, message: Invalid function (dummy) call)"
error := grpcstatus.New(grpcCodes.Unknown, expectedMsg)
code, message, _ := extractChaincodeError(error)
if code != 500 {
t.Fatal("Expected code to be 500")
}
if message != "Invalid function (dummy) call" {
t.Fatal("Expected message not found")
}
}

func TestExtractPrematureExecError(t *testing.T) {

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

err = grpcstatus.New(grpcCodes.Unknown, "transaction returned with failure: premature execution - chaincode (somecc:v1) is being launched")
code, message, _ := extractPrematureExecutionError(err)
assert.EqualValues(t, int32(status.PrematureChaincodeExecution), code, "Expected premature execution error")
assert.EqualValues(t, "premature execution - chaincode (somecc:v1) is being launched", message, "Invalid message")

err = grpcstatus.New(grpcCodes.Unknown, "transaction returned with failure: premature execution - chaincode (somecc:v1) launched and waiting for registration")
code, message, _ = extractPrematureExecutionError(err)
assert.EqualValues(t, int32(status.PrematureChaincodeExecution), code, "Expected premature execution error")
assert.EqualValues(t, "premature execution - chaincode (somecc:v1) launched and waiting for registration", message, "Invalid message")
}

func TestExtractChaincodeAlreadyLaunchingError(t *testing.T) {

err := grpcstatus.New(grpcCodes.Unknown, "some 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)
assert.EqualValues(t, int32(status.ChaincodeAlreadyLaunching), code, "Expected chaincode already launching error")
assert.EqualValues(t, "error chaincode is already launching: somecc:v1", message, "Invalid message")
assert.Nil(t, extractErr)

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

}

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 @@ -359,39 +283,6 @@ func TestChaincodeStatusFromResponse(t *testing.T) {
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 (v1)
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'"},
Expand Down
2 changes: 1 addition & 1 deletion test/integration/negative/revoked/revoked_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ func queryCC(t *testing.T, channelClientContext contextAPI.ChannelProvider, ccID
}

resp, err := chClientOrg1User.Query(channel.Request{ChaincodeID: ccID, Fcn: "invoke", Args: integration.ExampleCCDefaultQueryArgs()},
channel.WithRetry(retry.DefaultChannelOpts), channel.WithTargetEndpoints("peer1.org1.example.com"))
channel.WithRetry(retry.TestRetryOpts), channel.WithTargetEndpoints("peer1.org1.example.com"))

if success {
require.NoError(t, err)
Expand Down

0 comments on commit d44e5d6

Please sign in to comment.