Skip to content

Commit

Permalink
[FAB-1829] Add Response to ProposalResponse
Browse files Browse the repository at this point in the history
Add Response to ChaincodeAction. Reject proposal response that
status code >= 500 in endorse system chaincode.

Change-Id: Id441c3575941be3f11c044e083e482fb653740e5
Signed-off-by: jiangyaoguo <jiangyaoguo@gmail.com>
  • Loading branch information
jiangyaoguo committed Jan 25, 2017
1 parent 1b53e6e commit a762607
Show file tree
Hide file tree
Showing 16 changed files with 201 additions and 78 deletions.
2 changes: 1 addition & 1 deletion core/chaincode/exectransaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func endTxSimulation(chainID string, txsim ledger.TxSimulator, payload []byte, c
}

// assemble a (signed) proposal response message
resp, err := putils.CreateProposalResponse(prop.Header, prop.Payload, txSimulationResults, nil, nil, signer)
resp, err := putils.CreateProposalResponse(prop.Header, prop.Payload, &pb.Response{Status: 200}, txSimulationResults, nil, nil, signer)
if err != nil {
return err
}
Expand Down
18 changes: 12 additions & 6 deletions core/common/validation/fullflow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,11 @@ func TestGoodPath(t *testing.T) {
return
}

response := &peer.Response{Status: 200}
simRes := []byte("simulation_result")

// endorse it to get a proposal response
presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, simRes, nil, nil, signer)
presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response, simRes, nil, nil, signer)
if err != nil {
t.Fatalf("CreateProposalResponse failed, err %s", err)
return
Expand Down Expand Up @@ -198,10 +199,11 @@ func TestBadTx(t *testing.T) {
return
}

response := &peer.Response{Status: 200}
simRes := []byte("simulation_result")

// endorse it to get a proposal response
presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, simRes, nil, nil, signer)
presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response, simRes, nil, nil, signer)
if err != nil {
t.Fatalf("CreateProposalResponse failed, err %s", err)
return
Expand Down Expand Up @@ -250,19 +252,21 @@ func Test2EndorsersAgree(t *testing.T) {
return
}

response1 := &peer.Response{Status: 200}
simRes1 := []byte("simulation_result")

// endorse it to get a proposal response
presp1, err := utils.CreateProposalResponse(prop.Header, prop.Payload, simRes1, nil, nil, signer)
presp1, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response1, simRes1, nil, nil, signer)
if err != nil {
t.Fatalf("CreateProposalResponse failed, err %s", err)
return
}

response2 := &peer.Response{Status: 200}
simRes2 := []byte("simulation_result")

// endorse it to get a proposal response
presp2, err := utils.CreateProposalResponse(prop.Header, prop.Payload, simRes2, nil, nil, signer)
presp2, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response2, simRes2, nil, nil, signer)
if err != nil {
t.Fatalf("CreateProposalResponse failed, err %s", err)
return
Expand Down Expand Up @@ -291,19 +295,21 @@ func Test2EndorsersDisagree(t *testing.T) {
return
}

response1 := &peer.Response{Status: 200}
simRes1 := []byte("simulation_result1")

// endorse it to get a proposal response
presp1, err := utils.CreateProposalResponse(prop.Header, prop.Payload, simRes1, nil, nil, signer)
presp1, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response1, simRes1, nil, nil, signer)
if err != nil {
t.Fatalf("CreateProposalResponse failed, err %s", err)
return
}

response2 := &peer.Response{Status: 200}
simRes2 := []byte("simulation_result2")

// endorse it to get a proposal response
presp2, err := utils.CreateProposalResponse(prop.Header, prop.Payload, simRes2, nil, nil, signer)
presp2, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response2, simRes2, nil, nil, signer)
if err != nil {
t.Fatalf("CreateProposalResponse failed, err %s", err)
return
Expand Down
18 changes: 12 additions & 6 deletions core/endorser/endorser.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ func (e *Endorser) getCDSFromLCCC(ctx context.Context, chainID string, txid stri
}

//endorse the proposal by calling the ESCC
func (e *Endorser) endorseProposal(ctx context.Context, chainID string, txid string, proposal *pb.Proposal, simRes []byte, event *pb.ChaincodeEvent, visibility []byte, ccid *pb.ChaincodeID, txsim ledger.TxSimulator, cd *chaincode.ChaincodeData) (*pb.ProposalResponse, error) {
func (e *Endorser) endorseProposal(ctx context.Context, chainID string, txid string, proposal *pb.Proposal, response *pb.Response, simRes []byte, event *pb.ChaincodeEvent, visibility []byte, ccid *pb.ChaincodeID, txsim ledger.TxSimulator, cd *chaincode.ChaincodeData) (*pb.ProposalResponse, error) {
endorserLogger.Infof("endorseProposal starts for chainID %s, ccid %s", chainID, ccid)

// 1) extract the chaincodeDeploymentSpec for the chaincode we are invoking; we need it to get the escc
Expand Down Expand Up @@ -249,15 +249,21 @@ func (e *Endorser) endorseProposal(ctx context.Context, chainID string, txid str
}
}

resBytes, err := putils.GetBytesResponse(response)
if err != nil {
return nil, fmt.Errorf("failed to marshal response bytes - %s", err)
}

// 3) call the ESCC we've identified
// arguments:
// args[0] - function name (not used now)
// args[1] - serialized Header object
// args[2] - serialized ChaincodeProposalPayload object
// args[3] - binary blob of simulation results
// args[4] - serialized events
// args[5] - payloadVisibility
args := [][]byte{[]byte(""), proposal.Header, proposal.Payload, simRes, eventBytes, visibility}
// args[3] - result of executing chaincode
// args[4] - binary blob of simulation results
// args[5] - serialized events
// args[6] - payloadVisibility
args := [][]byte{[]byte(""), proposal.Header, proposal.Payload, resBytes, simRes, eventBytes, visibility}
version := util.GetSysCCVersion()
ecccis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeID: &pb.ChaincodeID{Name: escc}, Input: &pb.ChaincodeInput{Args: args}}}
res, _, err := e.callChaincode(ctx, chainID, version, txid, proposal, ecccis, &pb.ChaincodeID{Name: escc}, txsim)
Expand Down Expand Up @@ -356,7 +362,7 @@ func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedPro
if ischainless {
pResp = &pb.ProposalResponse{Response: &pb.Response{}}
} else {
pResp, err = e.endorseProposal(ctx, chainID, txid, prop, simulationResult, ccevent, hdrExt.PayloadVisibility, hdrExt.ChaincodeID, txsim, cd)
pResp, err = e.endorseProposal(ctx, chainID, txid, prop, res, simulationResult, ccevent, hdrExt.PayloadVisibility, hdrExt.ChaincodeID, txsim, cd)
if err != nil {
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
}
Expand Down
4 changes: 3 additions & 1 deletion core/ledger/kvledger/example/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric/protos/peer"
ptestutils "github.com/hyperledger/fabric/protos/testutils"
)

Expand Down Expand Up @@ -114,7 +115,8 @@ func (app *App) QueryBalances(accounts []string) ([]int, error) {
}

func constructTransaction(simulationResults []byte) *common.Envelope {
txEnv, _ := ptestutils.ConstructSingedTxEnvWithDefaultSigner(util.GenerateUUID(), util.GetTestChainID(), "foo", simulationResults, nil, nil)
response := &pb.Response{Status: 200}
txEnv, _ := ptestutils.ConstructSingedTxEnvWithDefaultSigner(util.GenerateUUID(), util.GetTestChainID(), "foo", response, simulationResults, nil, nil)
return txEnv
}

Expand Down
6 changes: 4 additions & 2 deletions core/ledger/testutil/test_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric/protos/peer"
ptestutils "github.com/hyperledger/fabric/protos/testutils"
)

Expand Down Expand Up @@ -92,13 +93,14 @@ func ConstructTestBlocks(t *testing.T, numBlocks int) []*common.Block {
// ConstructTransaction constructs a transaction for testing
func ConstructTransaction(t *testing.T, simulationResults []byte, sign bool) (*common.Envelope, string, error) {
ccName := "foo"
response := &pb.Response{Status: 200}
txID := util.GenerateUUID()
var txEnv *common.Envelope
var err error
if sign {
txEnv, err = ptestutils.ConstructSingedTxEnvWithDefaultSigner(txID, util.GetTestChainID(), ccName, simulationResults, nil, nil)
txEnv, err = ptestutils.ConstructSingedTxEnvWithDefaultSigner(txID, util.GetTestChainID(), ccName, response, simulationResults, nil, nil)
} else {
txEnv, err = ptestutils.ConstructUnsingedTxEnv(txID, util.GetTestChainID(), ccName, simulationResults, nil, nil)
txEnv, err = ptestutils.ConstructUnsingedTxEnv(txID, util.GetTestChainID(), ccName, response, simulationResults, nil, nil)
}
return txEnv, txID, err
}
Expand Down
40 changes: 28 additions & 12 deletions core/system_chaincode/escc/endorser_onevalidsignature.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"
"github.com/hyperledger/fabric/protos/utils"
putils "github.com/hyperledger/fabric/protos/utils"
"github.com/op/go-logging"

"github.com/hyperledger/fabric/core/peer/msp"
Expand Down Expand Up @@ -62,10 +63,10 @@ func (e *EndorserOneValidSignature) Init(stub shim.ChaincodeStubInterface) pb.Re
// definition can't be a state change of our own.
func (e *EndorserOneValidSignature) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
args := stub.GetArgs()
if len(args) < 4 {
return shim.Error(fmt.Sprintf("Incorrect number of arguments (expected a minimum of 4, provided %d)", len(args)))
} else if len(args) > 6 {
return shim.Error(fmt.Sprintf("Incorrect number of arguments (expected a maximum of 6, provided %d)", len(args)))
if len(args) < 5 {
return shim.Error(fmt.Sprintf("Incorrect number of arguments (expected a minimum of 5, provided %d)", len(args)))
} else if len(args) > 7 {
return shim.Error(fmt.Sprintf("Incorrect number of arguments (expected a maximum of 7, provided %d)", len(args)))
}

logger.Infof("ESCC starts: %d args", len(args))
Expand All @@ -86,29 +87,44 @@ func (e *EndorserOneValidSignature) Invoke(stub shim.ChaincodeStubInterface) pb.

payl = args[2]

// handle executing chaincode result
// Status code < 500 can be endorsed
if args[3] == nil {
return shim.Error("Response of chaincode executing is null")
}

response, err := putils.GetResponse(args[3])
if err != nil {
return shim.Error(fmt.Sprintf("Failed to get Response of executing chaincode: %s", err.Error()))
}

if response.Status >= shim.ERROR {
return shim.Error(fmt.Sprintf("Status code less than 500 will be endorsed, get status code: %d", response.Status))
}

// handle simulation results
var results []byte
if args[3] == nil {
if args[4] == nil {
return shim.Error("simulation results are null")
}

results = args[3]
results = args[4]

// Handle serialized events if they have been provided
// they might be nil in case there's no events but there
// is a visibility field specified as the next arg
events := []byte("")
if len(args) > 4 && args[4] != nil {
events = args[4]
if len(args) > 5 && args[5] != nil {
events = args[5]
}

// Handle payload visibility (it's an optional argument)
visibility := []byte("") // TODO: when visibility is properly defined, replace with the default
if len(args) > 5 {
if args[5] == nil {
if len(args) > 6 {
if args[6] == nil {
return shim.Error("serialized events are null")
}
visibility = args[5]
visibility = args[6]
}

// obtain the default signing identity for this peer; it will be used to sign this proposal response
Expand All @@ -123,7 +139,7 @@ func (e *EndorserOneValidSignature) Invoke(stub shim.ChaincodeStubInterface) pb.
}

// obtain a proposal response
presp, err := utils.CreateProposalResponse(hdr, payl, results, events, visibility, signingEndorser)
presp, err := utils.CreateProposalResponse(hdr, payl, response, results, events, visibility, signingEndorser)
if err != nil {
return shim.Error(err.Error())
}
Expand Down
55 changes: 45 additions & 10 deletions core/system_chaincode/escc/endorser_onevalidsignature_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ func TestInit(t *testing.T) {
func TestInvoke(t *testing.T) {
e := new(EndorserOneValidSignature)
stub := shim.NewMockStub("endorseronevalidsignature", e)
successResponse := &pb.Response{Status: 200, Payload: []byte("payload")}
failResponse := &pb.Response{Status: 500, Message: "error"}
successRes, _ := putils.GetBytesResponse(successResponse)
failRes, _ := putils.GetBytesResponse(failResponse)

// Initialize ESCC supplying the identity of the signer
args := [][]byte{[]byte("DEFAULT"), []byte("PEER")}
Expand All @@ -72,27 +76,47 @@ func TestInvoke(t *testing.T) {
t.Fatalf("escc invoke should have failed with invalid number of args: %v", args)
}

// Failed path: Not enough parameters
args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), []byte("test")}
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
t.Fatalf("escc invoke should have failed with invalid number of args: %v", args)
}

// Failed path: header is null
args = [][]byte{[]byte("test"), nil, []byte("test"), []byte("test")}
args = [][]byte{[]byte("test"), nil, []byte("test"), successRes, []byte("test")}
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
fmt.Println("Invoke", args, "failed", string(res.Message))
t.Fatalf("escc invoke should have failed with a null header. args: %v", args)
}

// Failed path: payload is null
args = [][]byte{[]byte("test"), []byte("test"), nil, []byte("test")}
args = [][]byte{[]byte("test"), []byte("test"), nil, successRes, []byte("test")}
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
fmt.Println("Invoke", args, "failed", string(res.Message))
t.Fatalf("escc invoke should have failed with a null payload. args: %v", args)
}

// Failed path: response is null
args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), nil, []byte("test")}
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
fmt.Println("Invoke", args, "failed", string(res.Message))
t.Fatalf("escc invoke should have failed with a null response. args: %v", args)
}

// Failed path: action struct is null
args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), nil}
args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), successRes, nil}
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
fmt.Println("Invoke", args, "failed", string(res.Message))
t.Fatalf("escc invoke should have failed with a null action struct. args: %v", args)
}

// Failed path: status code >=500
args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), failRes, []byte("test")}
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
fmt.Println("Invoke", args, "failed", string(res.Message))
t.Fatalf("escc invoke should have failed with a null response. args: %v", args)
}

// Successful path - create a proposal
cs := &pb.ChaincodeSpec{
ChaincodeID: &pb.ChaincodeID{Name: "foo"},
Expand Down Expand Up @@ -127,15 +151,15 @@ func TestInvoke(t *testing.T) {
// success test 1: invocation with mandatory args only
simRes := []byte("simulation_result")

args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, simRes}
args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, successRes, simRes}
res := stub.MockInvoke("1", args)
if res.Status != shim.OK {
t.Fail()
t.Fatalf("escc invoke failed with: %s", res.Message)
return
}

err = validateProposalResponse(res.Payload, proposal, nil, simRes, nil)
err = validateProposalResponse(res.Payload, proposal, nil, successResponse, simRes, nil)
if err != nil {
t.Fail()
t.Fatalf("%s", err)
Expand All @@ -145,15 +169,15 @@ func TestInvoke(t *testing.T) {
// success test 2: invocation with mandatory args + events
events := []byte("events")

args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, simRes, events}
args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, successRes, simRes, events}
res = stub.MockInvoke("1", args)
if res.Status != shim.OK {
t.Fail()
t.Fatalf("escc invoke failed with: %s", res.Message)
return
}

err = validateProposalResponse(res.Payload, proposal, nil, simRes, events)
err = validateProposalResponse(res.Payload, proposal, nil, successResponse, simRes, events)
if err != nil {
t.Fail()
t.Fatalf("%s", err)
Expand All @@ -163,23 +187,23 @@ func TestInvoke(t *testing.T) {
// success test 3: invocation with mandatory args + events and visibility
visibility := []byte("visibility")

args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, simRes, events, visibility}
args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, successRes, simRes, events, visibility}
res = stub.MockInvoke("1", args)
if res.Status != shim.OK {
t.Fail()
t.Fatalf("escc invoke failed with: %s", res.Message)
return
}

err = validateProposalResponse(res.Payload, proposal, visibility, simRes, events)
err = validateProposalResponse(res.Payload, proposal, visibility, successResponse, simRes, events)
if err != nil {
t.Fail()
t.Fatalf("%s", err)
return
}
}

func validateProposalResponse(prBytes []byte, proposal *pb.Proposal, visibility []byte, simRes []byte, events []byte) error {
func validateProposalResponse(prBytes []byte, proposal *pb.Proposal, visibility []byte, response *pb.Response, simRes []byte, events []byte) error {
if visibility == nil {
// TODO: set visibility to the default visibility mode once modes are defined
}
Expand Down Expand Up @@ -224,6 +248,17 @@ func validateProposalResponse(prBytes []byte, proposal *pb.Proposal, visibility
return fmt.Errorf("could not unmarshal the chaincode action structure: err %s", err)
}

// validate that the response match
if cact.Response.Status != response.Status {
return fmt.Errorf("response status do not match")
}
if cact.Response.Message != response.Message {
return fmt.Errorf("response message do not match")
}
if bytes.Compare(cact.Response.Payload, response.Payload) != 0 {
return fmt.Errorf("response payload do not match")
}

// validate that the results match
if bytes.Compare(cact.Results, simRes) != 0 {
return fmt.Errorf("results do not match")
Expand Down
Loading

0 comments on commit a762607

Please sign in to comment.