Skip to content

Commit a762607

Browse files
committed
[FAB-1829] Add Response to ProposalResponse
Add Response to ChaincodeAction. Reject proposal response that status code >= 500 in endorse system chaincode. Change-Id: Id441c3575941be3f11c044e083e482fb653740e5 Signed-off-by: jiangyaoguo <[email protected]>
1 parent 1b53e6e commit a762607

16 files changed

+201
-78
lines changed

core/chaincode/exectransaction_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ func endTxSimulation(chainID string, txsim ledger.TxSimulator, payload []byte, c
184184
}
185185

186186
// assemble a (signed) proposal response message
187-
resp, err := putils.CreateProposalResponse(prop.Header, prop.Payload, txSimulationResults, nil, nil, signer)
187+
resp, err := putils.CreateProposalResponse(prop.Header, prop.Payload, &pb.Response{Status: 200}, txSimulationResults, nil, nil, signer)
188188
if err != nil {
189189
return err
190190
}

core/common/validation/fullflow_test.go

+12-6
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,11 @@ func TestGoodPath(t *testing.T) {
6565
return
6666
}
6767

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

7071
// endorse it to get a proposal response
71-
presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, simRes, nil, nil, signer)
72+
presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response, simRes, nil, nil, signer)
7273
if err != nil {
7374
t.Fatalf("CreateProposalResponse failed, err %s", err)
7475
return
@@ -198,10 +199,11 @@ func TestBadTx(t *testing.T) {
198199
return
199200
}
200201

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

203205
// endorse it to get a proposal response
204-
presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, simRes, nil, nil, signer)
206+
presp, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response, simRes, nil, nil, signer)
205207
if err != nil {
206208
t.Fatalf("CreateProposalResponse failed, err %s", err)
207209
return
@@ -250,19 +252,21 @@ func Test2EndorsersAgree(t *testing.T) {
250252
return
251253
}
252254

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

255258
// endorse it to get a proposal response
256-
presp1, err := utils.CreateProposalResponse(prop.Header, prop.Payload, simRes1, nil, nil, signer)
259+
presp1, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response1, simRes1, nil, nil, signer)
257260
if err != nil {
258261
t.Fatalf("CreateProposalResponse failed, err %s", err)
259262
return
260263
}
261264

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

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

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

296301
// endorse it to get a proposal response
297-
presp1, err := utils.CreateProposalResponse(prop.Header, prop.Payload, simRes1, nil, nil, signer)
302+
presp1, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response1, simRes1, nil, nil, signer)
298303
if err != nil {
299304
t.Fatalf("CreateProposalResponse failed, err %s", err)
300305
return
301306
}
302307

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

305311
// endorse it to get a proposal response
306-
presp2, err := utils.CreateProposalResponse(prop.Header, prop.Payload, simRes2, nil, nil, signer)
312+
presp2, err := utils.CreateProposalResponse(prop.Header, prop.Payload, response2, simRes2, nil, nil, signer)
307313
if err != nil {
308314
t.Fatalf("CreateProposalResponse failed, err %s", err)
309315
return

core/endorser/endorser.go

+12-6
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ func (e *Endorser) getCDSFromLCCC(ctx context.Context, chainID string, txid stri
217217
}
218218

219219
//endorse the proposal by calling the ESCC
220-
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) {
220+
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) {
221221
endorserLogger.Infof("endorseProposal starts for chainID %s, ccid %s", chainID, ccid)
222222

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

252+
resBytes, err := putils.GetBytesResponse(response)
253+
if err != nil {
254+
return nil, fmt.Errorf("failed to marshal response bytes - %s", err)
255+
}
256+
252257
// 3) call the ESCC we've identified
253258
// arguments:
254259
// args[0] - function name (not used now)
255260
// args[1] - serialized Header object
256261
// args[2] - serialized ChaincodeProposalPayload object
257-
// args[3] - binary blob of simulation results
258-
// args[4] - serialized events
259-
// args[5] - payloadVisibility
260-
args := [][]byte{[]byte(""), proposal.Header, proposal.Payload, simRes, eventBytes, visibility}
262+
// args[3] - result of executing chaincode
263+
// args[4] - binary blob of simulation results
264+
// args[5] - serialized events
265+
// args[6] - payloadVisibility
266+
args := [][]byte{[]byte(""), proposal.Header, proposal.Payload, resBytes, simRes, eventBytes, visibility}
261267
version := util.GetSysCCVersion()
262268
ecccis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeID: &pb.ChaincodeID{Name: escc}, Input: &pb.ChaincodeInput{Args: args}}}
263269
res, _, err := e.callChaincode(ctx, chainID, version, txid, proposal, ecccis, &pb.ChaincodeID{Name: escc}, txsim)
@@ -356,7 +362,7 @@ func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedPro
356362
if ischainless {
357363
pResp = &pb.ProposalResponse{Response: &pb.Response{}}
358364
} else {
359-
pResp, err = e.endorseProposal(ctx, chainID, txid, prop, simulationResult, ccevent, hdrExt.PayloadVisibility, hdrExt.ChaincodeID, txsim, cd)
365+
pResp, err = e.endorseProposal(ctx, chainID, txid, prop, res, simulationResult, ccevent, hdrExt.PayloadVisibility, hdrExt.ChaincodeID, txsim, cd)
360366
if err != nil {
361367
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err
362368
}

core/ledger/kvledger/example/app.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424

2525
"github.com/hyperledger/fabric/common/util"
2626
"github.com/hyperledger/fabric/protos/common"
27+
pb "github.com/hyperledger/fabric/protos/peer"
2728
ptestutils "github.com/hyperledger/fabric/protos/testutils"
2829
)
2930

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

116117
func constructTransaction(simulationResults []byte) *common.Envelope {
117-
txEnv, _ := ptestutils.ConstructSingedTxEnvWithDefaultSigner(util.GenerateUUID(), util.GetTestChainID(), "foo", simulationResults, nil, nil)
118+
response := &pb.Response{Status: 200}
119+
txEnv, _ := ptestutils.ConstructSingedTxEnvWithDefaultSigner(util.GenerateUUID(), util.GetTestChainID(), "foo", response, simulationResults, nil, nil)
118120
return txEnv
119121
}
120122

core/ledger/testutil/test_helper.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/golang/protobuf/proto"
2323
"github.com/hyperledger/fabric/common/util"
2424
"github.com/hyperledger/fabric/protos/common"
25+
pb "github.com/hyperledger/fabric/protos/peer"
2526
ptestutils "github.com/hyperledger/fabric/protos/testutils"
2627
)
2728

@@ -92,13 +93,14 @@ func ConstructTestBlocks(t *testing.T, numBlocks int) []*common.Block {
9293
// ConstructTransaction constructs a transaction for testing
9394
func ConstructTransaction(t *testing.T, simulationResults []byte, sign bool) (*common.Envelope, string, error) {
9495
ccName := "foo"
96+
response := &pb.Response{Status: 200}
9597
txID := util.GenerateUUID()
9698
var txEnv *common.Envelope
9799
var err error
98100
if sign {
99-
txEnv, err = ptestutils.ConstructSingedTxEnvWithDefaultSigner(txID, util.GetTestChainID(), ccName, simulationResults, nil, nil)
101+
txEnv, err = ptestutils.ConstructSingedTxEnvWithDefaultSigner(txID, util.GetTestChainID(), ccName, response, simulationResults, nil, nil)
100102
} else {
101-
txEnv, err = ptestutils.ConstructUnsingedTxEnv(txID, util.GetTestChainID(), ccName, simulationResults, nil, nil)
103+
txEnv, err = ptestutils.ConstructUnsingedTxEnv(txID, util.GetTestChainID(), ccName, response, simulationResults, nil, nil)
102104
}
103105
return txEnv, txID, err
104106
}

core/system_chaincode/escc/endorser_onevalidsignature.go

+28-12
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/hyperledger/fabric/core/chaincode/shim"
2323
pb "github.com/hyperledger/fabric/protos/peer"
2424
"github.com/hyperledger/fabric/protos/utils"
25+
putils "github.com/hyperledger/fabric/protos/utils"
2526
"github.com/op/go-logging"
2627

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

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

8788
payl = args[2]
8889

90+
// handle executing chaincode result
91+
// Status code < 500 can be endorsed
92+
if args[3] == nil {
93+
return shim.Error("Response of chaincode executing is null")
94+
}
95+
96+
response, err := putils.GetResponse(args[3])
97+
if err != nil {
98+
return shim.Error(fmt.Sprintf("Failed to get Response of executing chaincode: %s", err.Error()))
99+
}
100+
101+
if response.Status >= shim.ERROR {
102+
return shim.Error(fmt.Sprintf("Status code less than 500 will be endorsed, get status code: %d", response.Status))
103+
}
104+
89105
// handle simulation results
90106
var results []byte
91-
if args[3] == nil {
107+
if args[4] == nil {
92108
return shim.Error("simulation results are null")
93109
}
94110

95-
results = args[3]
111+
results = args[4]
96112

97113
// Handle serialized events if they have been provided
98114
// they might be nil in case there's no events but there
99115
// is a visibility field specified as the next arg
100116
events := []byte("")
101-
if len(args) > 4 && args[4] != nil {
102-
events = args[4]
117+
if len(args) > 5 && args[5] != nil {
118+
events = args[5]
103119
}
104120

105121
// Handle payload visibility (it's an optional argument)
106122
visibility := []byte("") // TODO: when visibility is properly defined, replace with the default
107-
if len(args) > 5 {
108-
if args[5] == nil {
123+
if len(args) > 6 {
124+
if args[6] == nil {
109125
return shim.Error("serialized events are null")
110126
}
111-
visibility = args[5]
127+
visibility = args[6]
112128
}
113129

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

125141
// obtain a proposal response
126-
presp, err := utils.CreateProposalResponse(hdr, payl, results, events, visibility, signingEndorser)
142+
presp, err := utils.CreateProposalResponse(hdr, payl, response, results, events, visibility, signingEndorser)
127143
if err != nil {
128144
return shim.Error(err.Error())
129145
}

core/system_chaincode/escc/endorser_onevalidsignature_test.go

+45-10
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ func TestInit(t *testing.T) {
4646
func TestInvoke(t *testing.T) {
4747
e := new(EndorserOneValidSignature)
4848
stub := shim.NewMockStub("endorseronevalidsignature", e)
49+
successResponse := &pb.Response{Status: 200, Payload: []byte("payload")}
50+
failResponse := &pb.Response{Status: 500, Message: "error"}
51+
successRes, _ := putils.GetBytesResponse(successResponse)
52+
failRes, _ := putils.GetBytesResponse(failResponse)
4953

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

79+
// Failed path: Not enough parameters
80+
args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), []byte("test")}
81+
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
82+
t.Fatalf("escc invoke should have failed with invalid number of args: %v", args)
83+
}
84+
7585
// Failed path: header is null
76-
args = [][]byte{[]byte("test"), nil, []byte("test"), []byte("test")}
86+
args = [][]byte{[]byte("test"), nil, []byte("test"), successRes, []byte("test")}
7787
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
7888
fmt.Println("Invoke", args, "failed", string(res.Message))
7989
t.Fatalf("escc invoke should have failed with a null header. args: %v", args)
8090
}
8191

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

99+
// Failed path: response is null
100+
args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), nil, []byte("test")}
101+
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
102+
fmt.Println("Invoke", args, "failed", string(res.Message))
103+
t.Fatalf("escc invoke should have failed with a null response. args: %v", args)
104+
}
105+
89106
// Failed path: action struct is null
90-
args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), nil}
107+
args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), successRes, nil}
91108
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
92109
fmt.Println("Invoke", args, "failed", string(res.Message))
93110
t.Fatalf("escc invoke should have failed with a null action struct. args: %v", args)
94111
}
95112

113+
// Failed path: status code >=500
114+
args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), failRes, []byte("test")}
115+
if res := stub.MockInvoke("1", args); res.Status == shim.OK {
116+
fmt.Println("Invoke", args, "failed", string(res.Message))
117+
t.Fatalf("escc invoke should have failed with a null response. args: %v", args)
118+
}
119+
96120
// Successful path - create a proposal
97121
cs := &pb.ChaincodeSpec{
98122
ChaincodeID: &pb.ChaincodeID{Name: "foo"},
@@ -127,15 +151,15 @@ func TestInvoke(t *testing.T) {
127151
// success test 1: invocation with mandatory args only
128152
simRes := []byte("simulation_result")
129153

130-
args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, simRes}
154+
args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, successRes, simRes}
131155
res := stub.MockInvoke("1", args)
132156
if res.Status != shim.OK {
133157
t.Fail()
134158
t.Fatalf("escc invoke failed with: %s", res.Message)
135159
return
136160
}
137161

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

148-
args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, simRes, events}
172+
args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, successRes, simRes, events}
149173
res = stub.MockInvoke("1", args)
150174
if res.Status != shim.OK {
151175
t.Fail()
152176
t.Fatalf("escc invoke failed with: %s", res.Message)
153177
return
154178
}
155179

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

166-
args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, simRes, events, visibility}
190+
args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, successRes, simRes, events, visibility}
167191
res = stub.MockInvoke("1", args)
168192
if res.Status != shim.OK {
169193
t.Fail()
170194
t.Fatalf("escc invoke failed with: %s", res.Message)
171195
return
172196
}
173197

174-
err = validateProposalResponse(res.Payload, proposal, visibility, simRes, events)
198+
err = validateProposalResponse(res.Payload, proposal, visibility, successResponse, simRes, events)
175199
if err != nil {
176200
t.Fail()
177201
t.Fatalf("%s", err)
178202
return
179203
}
180204
}
181205

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

251+
// validate that the response match
252+
if cact.Response.Status != response.Status {
253+
return fmt.Errorf("response status do not match")
254+
}
255+
if cact.Response.Message != response.Message {
256+
return fmt.Errorf("response message do not match")
257+
}
258+
if bytes.Compare(cact.Response.Payload, response.Payload) != 0 {
259+
return fmt.Errorf("response payload do not match")
260+
}
261+
227262
// validate that the results match
228263
if bytes.Compare(cact.Results, simRes) != 0 {
229264
return fmt.Errorf("results do not match")

0 commit comments

Comments
 (0)