github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/core/scc/escc/endorser_onevalidsignature_test.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 package escc 17 18 import ( 19 "bytes" 20 "errors" 21 "fmt" 22 "os" 23 "testing" 24 25 "github.com/golang/protobuf/proto" 26 "github.com/hyperledger/fabric/common/util" 27 "github.com/hyperledger/fabric/core/chaincode/shim" 28 "github.com/hyperledger/fabric/core/common/validation" 29 mspmgmt "github.com/hyperledger/fabric/msp/mgmt" 30 "github.com/hyperledger/fabric/msp/mgmt/testtools" 31 "github.com/hyperledger/fabric/protos/common" 32 pb "github.com/hyperledger/fabric/protos/peer" 33 putils "github.com/hyperledger/fabric/protos/utils" 34 "github.com/stretchr/testify/assert" 35 ) 36 37 func TestInit(t *testing.T) { 38 e := new(EndorserOneValidSignature) 39 stub := shim.NewMockStub("endorseronevalidsignature", e) 40 41 args := [][]byte{[]byte("DEFAULT"), []byte("PEER")} 42 if res := stub.MockInit("1", args); res.Status != shim.OK { 43 fmt.Println("Init failed", string(res.Message)) 44 t.FailNow() 45 } 46 } 47 48 func TestInvoke(t *testing.T) { 49 e := new(EndorserOneValidSignature) 50 stub := shim.NewMockStub("endorseronevalidsignature", e) 51 successResponse := &pb.Response{Status: 200, Payload: []byte("payload")} 52 failResponse := &pb.Response{Status: 500, Message: "error"} 53 ccFailResponse := &pb.Response{Status: 400, Message: "chaincode error"} 54 successRes, _ := putils.GetBytesResponse(successResponse) 55 failRes, _ := putils.GetBytesResponse(failResponse) 56 ccFailRes, _ := putils.GetBytesResponse(ccFailResponse) 57 ccid := &pb.ChaincodeID{Name: "foo", Version: "v1"} 58 ccidBytes, err := proto.Marshal(ccid) 59 if err != nil { 60 t.Fail() 61 t.Fatalf("couldn't marshal ChaincodeID: err %s", err) 62 return 63 } 64 65 // Initialize ESCC supplying the identity of the signer 66 args := [][]byte{[]byte("DEFAULT"), []byte("PEER")} 67 if res := stub.MockInit("1", args); res.Status != shim.OK { 68 fmt.Println("Init failed", string(res.Message)) 69 t.FailNow() 70 } 71 72 // Failed path: Not enough parameters 73 args = [][]byte{[]byte("test")} 74 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 75 t.Fatalf("escc invoke should have failed with invalid number of args: %v", args) 76 } 77 78 // Failed path: Not enough parameters 79 args = [][]byte{[]byte("test"), []byte("test")} 80 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 81 t.Fatalf("escc invoke should have failed with invalid number of args: %v", args) 82 } 83 84 // Failed path: Not enough parameters 85 args = [][]byte{[]byte("test"), []byte("test"), []byte("test")} 86 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 87 t.Fatalf("escc invoke should have failed with invalid number of args: %v", args) 88 } 89 90 // Failed path: Not enough parameters 91 args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), []byte("test")} 92 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 93 t.Fatalf("escc invoke should have failed with invalid number of args: %v", args) 94 } 95 96 // Failed path: Not enough parameters 97 args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), []byte("test"), []byte("test")} 98 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 99 t.Fatalf("escc invoke should have failed with invalid number of args: %v", args) 100 } 101 102 // Too many parameters 103 a := []byte("test") 104 args = [][]byte{a, a, a, a, a, a, a, a, a} 105 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 106 t.Fatalf("escc invoke should have failed with invalid number of args: %v", args) 107 } 108 109 // Failed path: ccid is null 110 args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), nil, successRes, []byte("test")} 111 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 112 fmt.Println("Invoke", args, "failed", string(res.Message)) 113 t.Fatalf("escc invoke should have failed with a null header. args: %v", args) 114 } 115 116 // Failed path: ccid is bogus 117 args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), []byte("barf"), successRes, []byte("test")} 118 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 119 fmt.Println("Invoke", args, "failed", string(res.Message)) 120 t.Fatalf("escc invoke should have failed with a null header. args: %v", args) 121 } 122 123 // Failed path: response is bogus 124 args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, []byte("barf"), []byte("test")} 125 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 126 fmt.Println("Invoke", args, "failed", string(res.Message)) 127 t.Fatalf("escc invoke should have failed with a null header. args: %v", args) 128 } 129 130 // Failed path: header is null 131 args = [][]byte{[]byte("test"), nil, []byte("test"), ccidBytes, successRes, []byte("test")} 132 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 133 fmt.Println("Invoke", args, "failed", string(res.Message)) 134 t.Fatalf("escc invoke should have failed with a null header. args: %v", args) 135 } 136 137 // Failed path: payload is null 138 args = [][]byte{[]byte("test"), []byte("test"), nil, ccidBytes, successRes, []byte("test")} 139 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 140 fmt.Println("Invoke", args, "failed", string(res.Message)) 141 t.Fatalf("escc invoke should have failed with a null payload. args: %v", args) 142 } 143 144 // Failed path: response is null 145 args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, nil, []byte("test")} 146 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 147 fmt.Println("Invoke", args, "failed", string(res.Message)) 148 t.Fatalf("escc invoke should have failed with a null response. args: %v", args) 149 } 150 151 // Failed path: action struct is null 152 args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, successRes, nil} 153 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 154 fmt.Println("Invoke", args, "failed", string(res.Message)) 155 t.Fatalf("escc invoke should have failed with a null action struct. args: %v", args) 156 } 157 158 // Failed path: status code = 500 159 args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, failRes, []byte("test")} 160 res := stub.MockInvoke("1", args) 161 assert.NotEqual(t, res.Status, shim.OK, "Invoke should have failed with status code: %d", ccFailResponse.Status) 162 assert.Contains(t, res.Message, fmt.Sprintf("Status code less than %d will be endorsed", shim.ERRORTHRESHOLD)) 163 164 // Failed path: status code = 400 165 args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, ccFailRes, []byte("test")} 166 res = stub.MockInvoke("1", args) 167 assert.NotEqual(t, res.Status, shim.OK, "Invoke should have failed with status code: %d", ccFailResponse.Status) 168 assert.Contains(t, res.Message, fmt.Sprintf("Status code less than %d will be endorsed", shim.ERRORTHRESHOLD)) 169 170 // Successful path - create a proposal 171 cs := &pb.ChaincodeSpec{ 172 ChaincodeId: ccid, 173 Type: pb.ChaincodeSpec_GOLANG, 174 Input: &pb.ChaincodeInput{Args: [][]byte{[]byte("some"), []byte("args")}}} 175 176 cis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: cs} 177 178 sId, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity() 179 if err != nil { 180 t.Fail() 181 t.Fatalf("couldn't obtain identity: err %s", err) 182 return 183 } 184 185 sIdBytes, err := sId.Serialize() 186 if err != nil { 187 t.Fail() 188 t.Fatalf("couldn't serialize identity: err %s", err) 189 return 190 } 191 192 proposal, _, err := putils.CreateChaincodeProposal(common.HeaderType_ENDORSER_TRANSACTION, util.GetTestChainID(), cis, sIdBytes) 193 if err != nil { 194 t.Fail() 195 t.Fatalf("couldn't generate chaincode proposal: err %s", err) 196 return 197 } 198 199 simRes := []byte("simulation_result") 200 201 // bogus header 202 args = [][]byte{[]byte(""), []byte("barf"), proposal.Payload, ccidBytes, successRes, simRes} 203 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 204 fmt.Println("Invoke", args, "failed", string(res.Message)) 205 t.Fatalf("escc invoke should have failed with a null response. args: %v", args) 206 } 207 208 // success test 1: invocation with mandatory args only 209 args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, successRes, simRes} 210 res = stub.MockInvoke("1", args) 211 if res.Status != shim.OK { 212 t.Fail() 213 t.Fatalf("escc invoke failed with: %s", res.Message) 214 return 215 } 216 217 err = validateProposalResponse(res.Payload, proposal, cs.ChaincodeId, nil, successResponse, simRes, nil) 218 if err != nil { 219 t.Fail() 220 t.Fatalf("%s", err) 221 return 222 } 223 224 // success test 2: invocation with mandatory args + events 225 events := []byte("events") 226 227 args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, successRes, simRes, events} 228 res = stub.MockInvoke("1", args) 229 if res.Status != shim.OK { 230 t.Fail() 231 t.Fatalf("escc invoke failed with: %s", res.Message) 232 return 233 } 234 235 err = validateProposalResponse(res.Payload, proposal, cs.ChaincodeId, nil, successResponse, simRes, events) 236 if err != nil { 237 t.Fail() 238 t.Fatalf("%s", err) 239 return 240 } 241 242 // success test 3: invocation with mandatory args + events and visibility 243 args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, successRes, simRes, events, nil} 244 res = stub.MockInvoke("1", args) 245 if res.Status != shim.OK { 246 t.Fail() 247 t.Fatalf("escc invoke failed with: %s", res.Message) 248 return 249 } 250 251 err = validateProposalResponse(res.Payload, proposal, cs.ChaincodeId, []byte{}, successResponse, simRes, events) 252 if err != nil { 253 t.Fail() 254 t.Fatalf("%s", err) 255 return 256 } 257 } 258 259 func validateProposalResponse(prBytes []byte, proposal *pb.Proposal, ccid *pb.ChaincodeID, visibility []byte, response *pb.Response, simRes []byte, events []byte) error { 260 if visibility == nil { 261 // TODO: set visibility to the default visibility mode once modes are defined 262 } 263 264 pResp, err := putils.GetProposalResponse(prBytes) 265 if err != nil { 266 return err 267 } 268 269 // check the version 270 if pResp.Version != 1 { 271 return fmt.Errorf("invalid version: %d", pResp.Version) 272 } 273 274 // check the response status 275 if pResp.Response.Status != 200 { 276 return fmt.Errorf("invalid response status: %d", pResp.Response.Status) 277 } 278 279 // extract ProposalResponsePayload 280 prp, err := putils.GetProposalResponsePayload(pResp.Payload) 281 if err != nil { 282 return fmt.Errorf("could not unmarshal the proposal response structure: err %s", err) 283 } 284 285 // TODO: validate the epoch 286 287 hdr, err := putils.GetHeader(proposal.Header) 288 if err != nil { 289 return fmt.Errorf("could not unmarshal the proposal header structure: err %s", err) 290 } 291 292 // recompute proposal hash 293 pHash, err := putils.GetProposalHash1(hdr, proposal.Payload, visibility) 294 if err != nil { 295 return fmt.Errorf("could not obtain proposalHash: err %s", err) 296 } 297 298 // validate that proposal hash matches 299 if bytes.Compare(pHash, prp.ProposalHash) != 0 { 300 return errors.New("proposal hash does not match") 301 } 302 303 // extract the chaincode action 304 cact, err := putils.GetChaincodeAction(prp.Extension) 305 if err != nil { 306 return fmt.Errorf("could not unmarshal the chaincode action structure: err %s", err) 307 } 308 309 // validate that the response match 310 if cact.Response.Status != response.Status { 311 return errors.New("response status do not match") 312 } 313 if cact.Response.Message != response.Message { 314 return errors.New("response message do not match") 315 } 316 if bytes.Compare(cact.Response.Payload, response.Payload) != 0 { 317 return errors.New("response payload do not match") 318 } 319 320 // validate that the results match 321 if bytes.Compare(cact.Results, simRes) != 0 { 322 return errors.New("results do not match") 323 } 324 325 // validate that the events match 326 if bytes.Compare(cact.Events, events) != 0 { 327 return errors.New("events do not match") 328 } 329 330 // validate that the ChaincodeID match 331 if cact.ChaincodeId.Name != ccid.Name { 332 return errors.New("ChaincodeID name do not match") 333 } 334 if cact.ChaincodeId.Version != ccid.Version { 335 return errors.New("ChaincodeID version do not match") 336 } 337 if cact.ChaincodeId.Path != ccid.Path { 338 return errors.New("ChaincodeID path do not match") 339 } 340 341 // get the identity of the endorser 342 endorser, err := mspmgmt.GetManagerForChain(util.GetTestChainID()).DeserializeIdentity(pResp.Endorsement.Endorser) 343 if err != nil { 344 return fmt.Errorf("Failed to deserialize endorser identity, err %s", err) 345 } 346 347 // ensure that endorser has a valid certificate 348 err = endorser.Validate() 349 if err != nil { 350 return fmt.Errorf("The endorser certificate is not valid, err %s", err) 351 } 352 353 err = endorser.Verify(append(pResp.Payload, pResp.Endorsement.Endorser...), pResp.Endorsement.Signature) 354 if err != nil { 355 return fmt.Errorf("The endorser's signature over the proposal response is not valid, err %s", err) 356 } 357 358 // as extra, we assemble a transaction, sign it and then validate it 359 360 // obtain signer for the transaction 361 sId, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity() 362 if err != nil { 363 return fmt.Errorf("couldn't obtain identity: err %s", err) 364 } 365 366 // generate a transaction 367 tx, err := putils.CreateSignedTx(proposal, sId, pResp) 368 if err != nil { 369 return err 370 } 371 372 // validate the transaction 373 _, txResult := validation.ValidateTransaction(tx) 374 if txResult != pb.TxValidationCode_VALID { 375 return err 376 } 377 378 return nil 379 } 380 381 func TestMain(m *testing.M) { 382 msptesttools.LoadMSPSetupForTesting() 383 384 os.Exit(m.Run()) 385 }