github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/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 ) 35 36 func TestInit(t *testing.T) { 37 e := new(EndorserOneValidSignature) 38 stub := shim.NewMockStub("endorseronevalidsignature", e) 39 40 args := [][]byte{[]byte("DEFAULT"), []byte("PEER")} 41 if res := stub.MockInit("1", args); res.Status != shim.OK { 42 fmt.Println("Init failed", string(res.Message)) 43 t.FailNow() 44 } 45 } 46 47 func TestInvoke(t *testing.T) { 48 e := new(EndorserOneValidSignature) 49 stub := shim.NewMockStub("endorseronevalidsignature", e) 50 successResponse := &pb.Response{Status: 200, Payload: []byte("payload")} 51 failResponse := &pb.Response{Status: 500, Message: "error"} 52 successRes, _ := putils.GetBytesResponse(successResponse) 53 failRes, _ := putils.GetBytesResponse(failResponse) 54 ccid := &pb.ChaincodeID{Name: "foo", Version: "v1"} 55 ccidBytes, err := proto.Marshal(ccid) 56 if err != nil { 57 t.Fail() 58 t.Fatalf("couldn't marshal ChaincodeID: err %s", err) 59 return 60 } 61 62 // Initialize ESCC supplying the identity of the signer 63 args := [][]byte{[]byte("DEFAULT"), []byte("PEER")} 64 if res := stub.MockInit("1", args); res.Status != shim.OK { 65 fmt.Println("Init failed", string(res.Message)) 66 t.FailNow() 67 } 68 69 // Failed path: Not enough parameters 70 args = [][]byte{[]byte("test")} 71 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 72 t.Fatalf("escc invoke should have failed with invalid number of args: %v", args) 73 } 74 75 // Failed path: Not enough parameters 76 args = [][]byte{[]byte("test"), []byte("test")} 77 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 78 t.Fatalf("escc invoke should have failed with invalid number of args: %v", args) 79 } 80 81 // Failed path: Not enough parameters 82 args = [][]byte{[]byte("test"), []byte("test"), []byte("test")} 83 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 84 t.Fatalf("escc invoke should have failed with invalid number of args: %v", args) 85 } 86 87 // Failed path: Not enough parameters 88 args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), []byte("test")} 89 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 90 t.Fatalf("escc invoke should have failed with invalid number of args: %v", args) 91 } 92 93 // Failed path: Not enough parameters 94 args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), []byte("test"), []byte("test")} 95 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 96 t.Fatalf("escc invoke should have failed with invalid number of args: %v", args) 97 } 98 99 // Failed path: header is null 100 args = [][]byte{[]byte("test"), nil, []byte("test"), ccidBytes, successRes, []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 header. args: %v", args) 104 } 105 106 // Failed path: payload is null 107 args = [][]byte{[]byte("test"), []byte("test"), nil, ccidBytes, successRes, []byte("test")} 108 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 109 fmt.Println("Invoke", args, "failed", string(res.Message)) 110 t.Fatalf("escc invoke should have failed with a null payload. args: %v", args) 111 } 112 113 // Failed path: response is null 114 args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, nil, []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 120 // Failed path: action struct is null 121 args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, successRes, nil} 122 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 123 fmt.Println("Invoke", args, "failed", string(res.Message)) 124 t.Fatalf("escc invoke should have failed with a null action struct. args: %v", args) 125 } 126 127 // Failed path: status code >=500 128 args = [][]byte{[]byte("test"), []byte("test"), []byte("test"), ccidBytes, failRes, []byte("test")} 129 if res := stub.MockInvoke("1", args); res.Status == shim.OK { 130 fmt.Println("Invoke", args, "failed", string(res.Message)) 131 t.Fatalf("escc invoke should have failed with a null response. args: %v", args) 132 } 133 134 // Successful path - create a proposal 135 cs := &pb.ChaincodeSpec{ 136 ChaincodeId: ccid, 137 Type: pb.ChaincodeSpec_GOLANG, 138 Input: &pb.ChaincodeInput{Args: [][]byte{[]byte("some"), []byte("args")}}} 139 140 cis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: cs} 141 142 sId, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity() 143 if err != nil { 144 t.Fail() 145 t.Fatalf("couldn't obtain identity: err %s", err) 146 return 147 } 148 149 sIdBytes, err := sId.Serialize() 150 if err != nil { 151 t.Fail() 152 t.Fatalf("couldn't serialize identity: err %s", err) 153 return 154 } 155 156 proposal, _, err := putils.CreateChaincodeProposal(common.HeaderType_ENDORSER_TRANSACTION, util.GetTestChainID(), cis, sIdBytes) 157 if err != nil { 158 t.Fail() 159 t.Fatalf("couldn't generate chaincode proposal: err %s", err) 160 return 161 } 162 163 // success test 1: invocation with mandatory args only 164 simRes := []byte("simulation_result") 165 166 args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, successRes, simRes} 167 res := stub.MockInvoke("1", args) 168 if res.Status != shim.OK { 169 t.Fail() 170 t.Fatalf("escc invoke failed with: %s", res.Message) 171 return 172 } 173 174 err = validateProposalResponse(res.Payload, proposal, cs.ChaincodeId, nil, successResponse, simRes, nil) 175 if err != nil { 176 t.Fail() 177 t.Fatalf("%s", err) 178 return 179 } 180 181 // success test 2: invocation with mandatory args + events 182 events := []byte("events") 183 184 args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, successRes, simRes, events} 185 res = stub.MockInvoke("1", args) 186 if res.Status != shim.OK { 187 t.Fail() 188 t.Fatalf("escc invoke failed with: %s", res.Message) 189 return 190 } 191 192 err = validateProposalResponse(res.Payload, proposal, cs.ChaincodeId, nil, successResponse, simRes, events) 193 if err != nil { 194 t.Fail() 195 t.Fatalf("%s", err) 196 return 197 } 198 199 // success test 3: invocation with mandatory args + events and visibility 200 args = [][]byte{[]byte(""), proposal.Header, proposal.Payload, ccidBytes, successRes, simRes, events, nil} 201 res = stub.MockInvoke("1", args) 202 if res.Status != shim.OK { 203 t.Fail() 204 t.Fatalf("escc invoke failed with: %s", res.Message) 205 return 206 } 207 208 err = validateProposalResponse(res.Payload, proposal, cs.ChaincodeId, []byte{}, successResponse, simRes, events) 209 if err != nil { 210 t.Fail() 211 t.Fatalf("%s", err) 212 return 213 } 214 } 215 216 func validateProposalResponse(prBytes []byte, proposal *pb.Proposal, ccid *pb.ChaincodeID, visibility []byte, response *pb.Response, simRes []byte, events []byte) error { 217 if visibility == nil { 218 // TODO: set visibility to the default visibility mode once modes are defined 219 } 220 221 pResp, err := putils.GetProposalResponse(prBytes) 222 if err != nil { 223 return err 224 } 225 226 // check the version 227 if pResp.Version != 1 { 228 return fmt.Errorf("invalid version: %d", pResp.Version) 229 } 230 231 // check the response status 232 if pResp.Response.Status != 200 { 233 return fmt.Errorf("invalid response status: %d", pResp.Response.Status) 234 } 235 236 // extract ProposalResponsePayload 237 prp, err := putils.GetProposalResponsePayload(pResp.Payload) 238 if err != nil { 239 return fmt.Errorf("could not unmarshal the proposal response structure: err %s", err) 240 } 241 242 // TODO: validate the epoch 243 244 hdr, err := putils.GetHeader(proposal.Header) 245 if err != nil { 246 return fmt.Errorf("could not unmarshal the proposal header structure: err %s", err) 247 } 248 249 // recompute proposal hash 250 pHash, err := putils.GetProposalHash1(hdr, proposal.Payload, visibility) 251 if err != nil { 252 return fmt.Errorf("could not obtain proposalHash: err %s", err) 253 } 254 255 // validate that proposal hash matches 256 if bytes.Compare(pHash, prp.ProposalHash) != 0 { 257 return errors.New("proposal hash does not match") 258 } 259 260 // extract the chaincode action 261 cact, err := putils.GetChaincodeAction(prp.Extension) 262 if err != nil { 263 return fmt.Errorf("could not unmarshal the chaincode action structure: err %s", err) 264 } 265 266 // validate that the response match 267 if cact.Response.Status != response.Status { 268 return errors.New("response status do not match") 269 } 270 if cact.Response.Message != response.Message { 271 return errors.New("response message do not match") 272 } 273 if bytes.Compare(cact.Response.Payload, response.Payload) != 0 { 274 return errors.New("response payload do not match") 275 } 276 277 // validate that the results match 278 if bytes.Compare(cact.Results, simRes) != 0 { 279 return errors.New("results do not match") 280 } 281 282 // validate that the events match 283 if bytes.Compare(cact.Events, events) != 0 { 284 return errors.New("events do not match") 285 } 286 287 // validate that the ChaincodeID match 288 if cact.ChaincodeId.Name != ccid.Name { 289 return errors.New("ChaincodeID name do not match") 290 } 291 if cact.ChaincodeId.Version != ccid.Version { 292 return errors.New("ChaincodeID version do not match") 293 } 294 if cact.ChaincodeId.Path != ccid.Path { 295 return errors.New("ChaincodeID path do not match") 296 } 297 298 // get the identity of the endorser 299 endorser, err := mspmgmt.GetManagerForChain(util.GetTestChainID()).DeserializeIdentity(pResp.Endorsement.Endorser) 300 if err != nil { 301 return fmt.Errorf("Failed to deserialize endorser identity, err %s", err) 302 } 303 304 // ensure that endorser has a valid certificate 305 err = endorser.Validate() 306 if err != nil { 307 return fmt.Errorf("The endorser certificate is not valid, err %s", err) 308 } 309 310 err = endorser.Verify(append(pResp.Payload, pResp.Endorsement.Endorser...), pResp.Endorsement.Signature) 311 if err != nil { 312 return fmt.Errorf("The endorser's signature over the proposal response is not valid, err %s", err) 313 } 314 315 // as extra, we assemble a transaction, sign it and then validate it 316 317 // obtain signer for the transaction 318 sId, err := mspmgmt.GetLocalMSP().GetDefaultSigningIdentity() 319 if err != nil { 320 return fmt.Errorf("couldn't obtain identity: err %s", err) 321 } 322 323 // generate a transaction 324 tx, err := putils.CreateSignedTx(proposal, sId, pResp) 325 if err != nil { 326 return err 327 } 328 329 // validate the transaction 330 _, txResult := validation.ValidateTransaction(tx) 331 if txResult != pb.TxValidationCode_VALID { 332 return err 333 } 334 335 return nil 336 } 337 338 func TestMain(m *testing.M) { 339 msptesttools.LoadMSPSetupForTesting() 340 341 os.Exit(m.Run()) 342 }