github.com/kaituanwang/hyperledger@v2.0.1+incompatible/discovery/endorsement/endorsement_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package endorsement 8 9 import ( 10 "fmt" 11 "testing" 12 13 "github.com/golang/protobuf/proto" 14 discoveryprotos "github.com/hyperledger/fabric-protos-go/discovery" 15 "github.com/hyperledger/fabric-protos-go/gossip" 16 "github.com/hyperledger/fabric-protos-go/msp" 17 "github.com/hyperledger/fabric-protos-go/peer" 18 "github.com/hyperledger/fabric/common/chaincode" 19 "github.com/hyperledger/fabric/common/policies" 20 "github.com/hyperledger/fabric/common/policies/inquire" 21 "github.com/hyperledger/fabric/gossip/api" 22 "github.com/hyperledger/fabric/gossip/common" 23 "github.com/hyperledger/fabric/gossip/discovery" 24 "github.com/hyperledger/fabric/protoutil" 25 "github.com/pkg/errors" 26 "github.com/stretchr/testify/assert" 27 "github.com/stretchr/testify/mock" 28 ) 29 30 var pkiID2MSPID = map[string]string{ 31 "p0": "Org0MSP", 32 "p1": "Org1MSP", 33 "p2": "Org2MSP", 34 "p3": "Org3MSP", 35 "p4": "Org4MSP", 36 "p5": "Org5MSP", 37 "p6": "Org6MSP", 38 "p7": "Org7MSP", 39 "p8": "Org8MSP", 40 "p9": "Org9MSP", 41 "p10": "Org10MSP", 42 "p11": "Org11MSP", 43 "p12": "Org12MSP", 44 "p13": "Org13MSP", 45 "p14": "Org14MSP", 46 "p15": "Org15MSP", 47 } 48 49 func TestPeersForEndorsement(t *testing.T) { 50 extractPeers := func(desc *discoveryprotos.EndorsementDescriptor) map[string]struct{} { 51 res := map[string]struct{}{} 52 for _, endorsers := range desc.EndorsersByGroups { 53 for _, p := range endorsers.Peers { 54 res[string(p.Identity)] = struct{}{} 55 assert.Equal(t, string(p.Identity), string(p.MembershipInfo.Payload)) 56 assert.Equal(t, string(p.Identity), string(p.StateInfo.Payload)) 57 } 58 } 59 return res 60 } 61 cc := "chaincode" 62 g := &gossipMock{} 63 pf := &policyFetcherMock{} 64 ccWithMissingPolicy := "chaincodeWithMissingPolicy" 65 channel := common.ChannelID("test") 66 alivePeers := peerSet{ 67 newPeer(0), 68 newPeer(2), 69 newPeer(4), 70 newPeer(6), 71 newPeer(8), 72 newPeer(10), 73 newPeer(11), 74 newPeer(12), 75 } 76 77 identities := identitySet(pkiID2MSPID) 78 79 chanPeers := peerSet{ 80 newPeer(0).withChaincode(cc, "1.0"), 81 newPeer(3).withChaincode(cc, "1.0"), 82 newPeer(6).withChaincode(cc, "1.0"), 83 newPeer(9).withChaincode(cc, "1.0"), 84 newPeer(11).withChaincode(cc, "1.0"), 85 newPeer(12).withChaincode(cc, "1.0"), 86 } 87 g.On("Peers").Return(alivePeers.toMembers()) 88 g.On("IdentityInfo").Return(identities) 89 90 // Scenario I: Policy isn't found 91 t.Run("PolicyNotFound", func(t *testing.T) { 92 pf.On("PoliciesByChaincode", ccWithMissingPolicy).Return(nil).Once() 93 g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once() 94 mf := &metadataFetcher{} 95 mf.On("Metadata").Return(&chaincode.Metadata{ 96 Name: cc, 97 Version: "1.0", 98 }).Once() 99 analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf) 100 desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{ 101 Chaincodes: []*discoveryprotos.ChaincodeCall{ 102 { 103 Name: ccWithMissingPolicy, 104 }, 105 }, 106 }) 107 assert.Nil(t, desc) 108 assert.Equal(t, "policy not found", err.Error()) 109 }) 110 111 t.Run("NotEnoughPeers", func(t *testing.T) { 112 // Scenario II: Policy is found but not enough peers to satisfy the policy. 113 // The policy requires a signature from: 114 // p1 and p6, or 115 // p11 x2 (twice), but we only have a single peer in the alive view for p11 116 pb := principalBuilder{} 117 policy := pb.newSet().addPrincipal(peerRole("p1")).addPrincipal(peerRole("p6")). 118 newSet().addPrincipal(peerRole("p11")).addPrincipal(peerRole("p11")).buildPolicy() 119 g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once() 120 mf := &metadataFetcher{} 121 mf.On("Metadata").Return(&chaincode.Metadata{Name: cc, Version: "1.0"}).Once() 122 analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf) 123 pf.On("PoliciesByChaincode", cc).Return(policy).Once() 124 desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{ 125 Chaincodes: []*discoveryprotos.ChaincodeCall{ 126 { 127 Name: cc, 128 }, 129 }, 130 }) 131 assert.Nil(t, desc) 132 assert.Equal(t, err.Error(), "cannot satisfy any principal combination") 133 }) 134 135 t.Run("DisjointViews", func(t *testing.T) { 136 pb := principalBuilder{} 137 // Scenario III: Policy is found and there are enough peers to satisfy 138 // only 1 type of principal combination: p0 and p6. 139 // However, the combination of a signature from p10 and p12 140 // cannot be satisfied because p10 is not in the channel view but only in the alive view 141 policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p6")). 142 newSet().addPrincipal(peerRole("p10")).addPrincipal(peerRole("p12")).buildPolicy() 143 g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once() 144 mf := &metadataFetcher{} 145 mf.On("Metadata").Return(&chaincode.Metadata{ 146 Name: cc, 147 Version: "1.0", 148 }).Once() 149 analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf) 150 pf.On("PoliciesByChaincode", cc).Return(policy).Once() 151 desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{ 152 Chaincodes: []*discoveryprotos.ChaincodeCall{ 153 { 154 Name: cc, 155 }, 156 }, 157 }) 158 assert.NoError(t, err) 159 assert.NotNil(t, desc) 160 assert.Len(t, desc.Layouts, 1) 161 assert.Len(t, desc.Layouts[0].QuantitiesByGroup, 2) 162 assert.Equal(t, map[string]struct{}{ 163 peerIdentityString("p0"): {}, 164 peerIdentityString("p6"): {}, 165 }, extractPeers(desc)) 166 }) 167 168 t.Run("MultipleCombinations", func(t *testing.T) { 169 // Scenario IV: Policy is found and there are enough peers to satisfy 170 // 2 principal combinations: 171 // p0 and p6, or 172 // p12 alone 173 pb := principalBuilder{} 174 policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p6")). 175 newSet().addPrincipal(peerRole("p12")).buildPolicy() 176 g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once() 177 mf := &metadataFetcher{} 178 mf.On("Metadata").Return(&chaincode.Metadata{ 179 Name: cc, 180 Version: "1.0", 181 }).Once() 182 analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf) 183 pf.On("PoliciesByChaincode", cc).Return(policy).Once() 184 desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{ 185 Chaincodes: []*discoveryprotos.ChaincodeCall{ 186 { 187 Name: cc, 188 }, 189 }, 190 }) 191 assert.NoError(t, err) 192 assert.NotNil(t, desc) 193 assert.Len(t, desc.Layouts, 2) 194 assert.Len(t, desc.Layouts[0].QuantitiesByGroup, 2) 195 assert.Len(t, desc.Layouts[1].QuantitiesByGroup, 1) 196 assert.Equal(t, map[string]struct{}{ 197 peerIdentityString("p0"): {}, 198 peerIdentityString("p6"): {}, 199 peerIdentityString("p12"): {}, 200 }, extractPeers(desc)) 201 }) 202 203 t.Run("WrongVersionInstalled", func(t *testing.T) { 204 // Scenario V: Policy is found, and there are enough peers to satisfy policy combinations, 205 // but all peers have the wrong version installed on them. 206 mf := &metadataFetcher{} 207 mf.On("Metadata").Return(&chaincode.Metadata{ 208 Name: cc, 209 Version: "1.1", 210 }).Once() 211 pb := principalBuilder{} 212 policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p6")). 213 newSet().addPrincipal(peerRole("p12")).buildPolicy() 214 g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once() 215 pf.On("PoliciesByChaincode", cc).Return(policy).Once() 216 analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf) 217 desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{ 218 Chaincodes: []*discoveryprotos.ChaincodeCall{ 219 { 220 Name: cc, 221 }, 222 }, 223 }) 224 assert.Nil(t, desc) 225 assert.Equal(t, "cannot satisfy any principal combination", err.Error()) 226 227 // Scenario VI: Policy is found, there are enough peers to satisfy policy combinations, 228 // but some peers have the wrong chaincode version, and some don't even have it installed. 229 chanPeers := peerSet{ 230 newPeer(0).withChaincode(cc, "0.6"), 231 newPeer(3).withChaincode(cc, "1.0"), 232 newPeer(6).withChaincode(cc, "1.0"), 233 newPeer(9).withChaincode(cc, "1.0"), 234 newPeer(12), 235 } 236 g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once() 237 pf.On("PoliciesByChaincode", cc).Return(policy).Once() 238 mf.On("Metadata").Return(&chaincode.Metadata{ 239 Name: cc, 240 Version: "1.0", 241 }).Once() 242 desc, err = analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{ 243 Chaincodes: []*discoveryprotos.ChaincodeCall{ 244 { 245 Name: cc, 246 }, 247 }, 248 }) 249 assert.Nil(t, desc) 250 assert.Equal(t, "cannot satisfy any principal combination", err.Error()) 251 }) 252 253 t.Run("NoChaincodeMetadataFromLedger", func(t *testing.T) { 254 // Scenario VII: Policy is found, there are enough peers to satisfy the policy, 255 // but the chaincode metadata cannot be fetched from the ledger. 256 pb := principalBuilder{} 257 policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p6")). 258 newSet().addPrincipal(peerRole("p12")).buildPolicy() 259 g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once() 260 pf.On("PoliciesByChaincode", cc).Return(policy).Once() 261 mf := &metadataFetcher{} 262 mf.On("Metadata").Return(nil).Once() 263 analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf) 264 desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{ 265 Chaincodes: []*discoveryprotos.ChaincodeCall{ 266 { 267 Name: cc, 268 }, 269 }, 270 }) 271 assert.Nil(t, desc) 272 assert.Equal(t, "No metadata was found for chaincode chaincode in channel test", err.Error()) 273 }) 274 275 t.Run("Collections", func(t *testing.T) { 276 // Scenario VIII: Policy is found and there are enough peers to satisfy 277 // 2 principal combinations: p0 and p6, or p12 alone. 278 // However, the query contains a collection which has a policy that permits only p0 and p12, 279 // and thus - the combination of p0 and p6 is filtered out and we're left with p12 only. 280 collectionOrgs := []*msp.MSPPrincipal{ 281 peerRole("p0"), 282 peerRole("p12"), 283 } 284 col2principals := map[string][]*msp.MSPPrincipal{ 285 "collection": collectionOrgs, 286 } 287 mf := &metadataFetcher{} 288 mf.On("Metadata").Return(&chaincode.Metadata{ 289 Name: cc, 290 Version: "1.0", 291 CollectionsConfig: buildCollectionConfig(col2principals), 292 }).Once() 293 pb := principalBuilder{} 294 policy := pb.newSet().addPrincipal(peerRole("p0")). 295 addPrincipal(peerRole("p6")).newSet(). 296 addPrincipal(peerRole("p12")).buildPolicy() 297 g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once() 298 pf.On("PoliciesByChaincode", cc).Return(policy).Once() 299 analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf) 300 desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{ 301 Chaincodes: []*discoveryprotos.ChaincodeCall{ 302 { 303 Name: cc, 304 CollectionNames: []string{"collection"}, 305 }, 306 }, 307 }) 308 assert.NoError(t, err) 309 assert.NotNil(t, desc) 310 assert.Len(t, desc.Layouts, 1) 311 assert.Len(t, desc.Layouts[0].QuantitiesByGroup, 1) 312 assert.Equal(t, map[string]struct{}{ 313 peerIdentityString("p12"): {}, 314 }, extractPeers(desc)) 315 }) 316 317 t.Run("Chaincode2Chaincode I", func(t *testing.T) { 318 // Scenario IX: A chaincode-to-chaincode query is made. 319 // Total organizations are 0, 2, 4, 6, 10, 12 320 // and the endorsement policies of the chaincodes are as follows: 321 // cc1: OR(AND(0, 2), AND(6, 10)) 322 // cc2: AND(6, 10, 12) 323 // cc3: AND(4, 12) 324 // Therefore, the result should be: 4, 6, 10, 12 325 326 chanPeers := peerSet{} 327 for _, id := range []int{0, 2, 4, 6, 10, 12} { 328 peer := newPeer(id).withChaincode("cc1", "1.0").withChaincode("cc2", "1.0").withChaincode("cc3", "1.0") 329 chanPeers = append(chanPeers, peer) 330 } 331 332 g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once() 333 334 mf := &metadataFetcher{} 335 mf.On("Metadata").Return(&chaincode.Metadata{ 336 Name: "cc1", 337 Version: "1.0", 338 }).Once() 339 mf.On("Metadata").Return(&chaincode.Metadata{ 340 Name: "cc2", 341 Version: "1.0", 342 }).Once() 343 mf.On("Metadata").Return(&chaincode.Metadata{ 344 Name: "cc3", 345 Version: "1.0", 346 }).Once() 347 348 pb := principalBuilder{} 349 cc1policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p2")). 350 newSet().addPrincipal(peerRole("p6")).addPrincipal(peerRole("p10")).buildPolicy() 351 352 pf.On("PoliciesByChaincode", "cc1").Return(cc1policy).Once() 353 354 cc2policy := pb.newSet().addPrincipal(peerRole("p6")). 355 addPrincipal(peerRole("p10")).addPrincipal(peerRole("p12")).buildPolicy() 356 pf.On("PoliciesByChaincode", "cc2").Return(cc2policy).Once() 357 358 cc3policy := pb.newSet().addPrincipal(peerRole("p4")). 359 addPrincipal(peerRole("p12")).buildPolicy() 360 pf.On("PoliciesByChaincode", "cc3").Return(cc3policy).Once() 361 362 analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf) 363 desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{ 364 Chaincodes: []*discoveryprotos.ChaincodeCall{ 365 { 366 Name: "cc1", 367 }, 368 { 369 Name: "cc2", 370 }, 371 { 372 Name: "cc3", 373 }, 374 }, 375 }) 376 assert.NoError(t, err) 377 assert.NotNil(t, desc) 378 assert.Len(t, desc.Layouts, 1) 379 assert.Len(t, desc.Layouts[0].QuantitiesByGroup, 4) 380 assert.Equal(t, map[string]struct{}{ 381 peerIdentityString("p4"): {}, 382 peerIdentityString("p6"): {}, 383 peerIdentityString("p10"): {}, 384 peerIdentityString("p12"): {}, 385 }, extractPeers(desc)) 386 }) 387 388 t.Run("Chaincode2Chaincode II", func(t *testing.T) { 389 // Scenario X: A chaincode-to-chaincode query is made. 390 // and the endorsement policies of the chaincodes are as follows: 391 // cc1: OR(0, 1) 392 // cc2: AND(0, 1) 393 // Therefore, the result should be: (0, 1) 394 395 cc1 := "cc1" 396 cc2 := "cc2" 397 chanPeers := peerSet{ 398 newPeer(0).withChaincode(cc1, "1.0").withChaincode(cc2, "1.0"), 399 newPeer(1).withChaincode(cc1, "1.0").withChaincode(cc2, "1.0"), 400 }.toMembers() 401 402 alivePeers := peerSet{ 403 newPeer(0), 404 newPeer(1), 405 }.toMembers() 406 407 g := &gossipMock{} 408 g.On("Peers").Return(alivePeers) 409 g.On("IdentityInfo").Return(identities) 410 g.On("PeersOfChannel").Return(chanPeers).Once() 411 412 mf := &metadataFetcher{} 413 mf.On("Metadata").Return(&chaincode.Metadata{ 414 Name: "cc1", 415 Version: "1.0", 416 }) 417 mf.On("Metadata").Return(&chaincode.Metadata{ 418 Name: "cc2", 419 Version: "1.0", 420 }) 421 422 pb := principalBuilder{} 423 cc1policy := pb.newSet().addPrincipal(peerRole("p0")). 424 newSet().addPrincipal(peerRole("p1")).buildPolicy() 425 pf.On("PoliciesByChaincode", "cc1").Return(cc1policy).Once() 426 427 cc2policy := pb.newSet().addPrincipal(peerRole("p0")). 428 addPrincipal(peerRole("p1")).buildPolicy() 429 pf.On("PoliciesByChaincode", "cc2").Return(cc2policy).Once() 430 431 analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf) 432 desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{ 433 Chaincodes: []*discoveryprotos.ChaincodeCall{ 434 { 435 Name: "cc1", 436 }, 437 { 438 Name: "cc2", 439 }, 440 }, 441 }) 442 assert.NoError(t, err) 443 assert.NotNil(t, desc) 444 assert.Len(t, desc.Layouts, 1) 445 assert.Len(t, desc.Layouts[0].QuantitiesByGroup, 2) 446 assert.Equal(t, map[string]struct{}{ 447 peerIdentityString("p0"): {}, 448 peerIdentityString("p1"): {}, 449 }, extractPeers(desc)) 450 }) 451 452 t.Run("Collection specific EP", func(t *testing.T) { 453 // Scenario XI: Policy is found and there are enough peers to satisfy 454 // 2 principal combinations: p0 and p6, or p12 alone. 455 // The collection has p0, p6, and p12 in it. 456 // The chaincode EP is (p0 and p6) or p12. 457 // However, the the chaincode has a collection level EP that requires p6 and p12. 458 // Thus, the only combination that can satisfy would be p6 and p12. 459 collectionOrgs := []*msp.MSPPrincipal{ 460 peerRole("p0"), 461 peerRole("p6"), 462 peerRole("p12"), 463 } 464 col2principals := map[string][]*msp.MSPPrincipal{ 465 "collection": collectionOrgs, 466 } 467 468 mf := &metadataFetcher{} 469 mf.On("Metadata").Return(&chaincode.Metadata{ 470 Name: cc, 471 Version: "1.0", 472 CollectionsConfig: buildCollectionConfig(col2principals), 473 }).Once() 474 pb := principalBuilder{} 475 chaincodeEP := pb.newSet().addPrincipal(peerRole("p0")). 476 addPrincipal(peerRole("p6")).newSet(). 477 addPrincipal(peerRole("p12")).buildPolicy() 478 collectionEP := pb.newSet().addPrincipal(peerRole("p6")). 479 addPrincipal(peerRole("p12")).buildPolicy() 480 g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once() 481 pf := &policyFetcherMock{} 482 pf.On("PoliciesByChaincode", cc).Return([]policies.InquireablePolicy{chaincodeEP, collectionEP}).Once() 483 analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf) 484 desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{ 485 Chaincodes: []*discoveryprotos.ChaincodeCall{ 486 { 487 Name: cc, 488 CollectionNames: []string{"collection"}, 489 }, 490 }, 491 }) 492 assert.NoError(t, err) 493 assert.NotNil(t, desc) 494 assert.Len(t, desc.Layouts, 1) 495 assert.Len(t, desc.Layouts[0].QuantitiesByGroup, 2) 496 assert.Equal(t, map[string]struct{}{ 497 peerIdentityString("p6"): {}, 498 peerIdentityString("p12"): {}, 499 }, extractPeers(desc)) 500 }) 501 } 502 503 func TestPeersAuthorizedByCriteria(t *testing.T) { 504 cc1 := "cc1" 505 cc2 := "cc2" 506 members := peerSet{ 507 newPeer(0).withChaincode(cc1, "1.0"), 508 newPeer(3).withChaincode(cc1, "1.0"), 509 newPeer(6).withChaincode(cc1, "1.0"), 510 newPeer(9).withChaincode(cc1, "1.0"), 511 newPeer(12).withChaincode(cc1, "1.0"), 512 }.toMembers() 513 514 members2 := append(discovery.Members{}, members...) 515 members2 = append(members2, peerSet{newPeer(13).withChaincode(cc1, "1.1").withChaincode(cc2, "1.0")}.toMembers()...) 516 members2 = append(members2, peerSet{newPeer(14).withChaincode(cc1, "1.1")}.toMembers()...) 517 members2 = append(members2, peerSet{newPeer(15).withChaincode(cc2, "1.0")}.toMembers()...) 518 519 alivePeers := peerSet{ 520 newPeer(0), 521 newPeer(2), 522 newPeer(4), 523 newPeer(6), 524 newPeer(8), 525 newPeer(10), 526 newPeer(11), 527 newPeer(12), 528 newPeer(13), 529 newPeer(14), 530 newPeer(15), 531 }.toMembers() 532 533 identities := identitySet(pkiID2MSPID) 534 535 for _, tst := range []struct { 536 name string 537 arguments *discoveryprotos.ChaincodeInterest 538 totalExistingMembers discovery.Members 539 metadata []*chaincode.Metadata 540 expected discovery.Members 541 }{ 542 { 543 name: "Nil interest", 544 arguments: nil, 545 totalExistingMembers: members, 546 expected: members, 547 }, 548 { 549 name: "Empty interest invocation chain", 550 arguments: &discoveryprotos.ChaincodeInterest{}, 551 totalExistingMembers: members, 552 expected: members, 553 }, 554 { 555 name: "Chaincodes only installed on some peers", 556 arguments: &discoveryprotos.ChaincodeInterest{ 557 Chaincodes: []*discoveryprotos.ChaincodeCall{ 558 {Name: cc1}, 559 {Name: cc2}, 560 }, 561 }, 562 totalExistingMembers: members2, 563 metadata: []*chaincode.Metadata{ 564 { 565 Name: "cc1", 566 Version: "1.1", 567 }, 568 { 569 Name: "cc2", 570 Version: "1.0", 571 }, 572 }, 573 expected: peerSet{newPeer(13).withChaincode(cc1, "1.1").withChaincode(cc2, "1.0")}.toMembers(), 574 }, 575 { 576 name: "Only some peers authorized by collection", 577 arguments: &discoveryprotos.ChaincodeInterest{ 578 Chaincodes: []*discoveryprotos.ChaincodeCall{ 579 {Name: cc1, CollectionNames: []string{"collection"}}, 580 }, 581 }, 582 totalExistingMembers: members, 583 metadata: []*chaincode.Metadata{ 584 { 585 Name: cc1, 586 Version: "1.0", 587 CollectionsConfig: buildCollectionConfig(map[string][]*msp.MSPPrincipal{ 588 "collection": { 589 peerRole("p0"), 590 peerRole("p12"), 591 }, 592 }), 593 }, 594 { 595 Name: cc1, 596 Version: "1.0", 597 CollectionsConfig: buildCollectionConfig(map[string][]*msp.MSPPrincipal{ 598 "collection": { 599 peerRole("p3"), 600 peerRole("p9"), 601 }, 602 }), 603 }, 604 }, 605 expected: peerSet{ 606 newPeer(0).withChaincode(cc1, "1.0"), 607 newPeer(12).withChaincode(cc1, "1.0")}.toMembers(), 608 }, 609 } { 610 t.Run(tst.name, func(t *testing.T) { 611 g := &gossipMock{} 612 pf := &policyFetcherMock{} 613 mf := &metadataFetcher{} 614 g.On("Peers").Return(alivePeers) 615 g.On("IdentityInfo").Return(identities) 616 g.On("PeersOfChannel").Return(tst.totalExistingMembers).Once() 617 for _, md := range tst.metadata { 618 mf.On("Metadata").Return(md).Once() 619 } 620 621 analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf) 622 actualMembers, err := analyzer.PeersAuthorizedByCriteria(common.ChannelID("mychannel"), tst.arguments) 623 assert.NoError(t, err) 624 assert.Equal(t, tst.expected, actualMembers) 625 }) 626 } 627 } 628 629 func TestPop(t *testing.T) { 630 slice := []inquire.ComparablePrincipalSets{{}, {}} 631 assert.Len(t, slice, 2) 632 _, slice, err := popComparablePrincipalSets(slice) 633 assert.NoError(t, err) 634 assert.Len(t, slice, 1) 635 _, slice, err = popComparablePrincipalSets(slice) 636 assert.Len(t, slice, 0) 637 _, slice, err = popComparablePrincipalSets(slice) 638 assert.Error(t, err) 639 assert.Equal(t, "no principal sets remained after filtering", err.Error()) 640 } 641 642 func TestMergePrincipalSetsNilInput(t *testing.T) { 643 _, err := mergePrincipalSets(nil) 644 assert.Error(t, err) 645 assert.Equal(t, "no principal sets remained after filtering", err.Error()) 646 } 647 648 func TestComputePrincipalSetsNoPolicies(t *testing.T) { 649 // Tests a hypothetical case where no chaincodes populate the chaincode interest. 650 651 interest := &discoveryprotos.ChaincodeInterest{ 652 Chaincodes: []*discoveryprotos.ChaincodeCall{}, 653 } 654 ea := &endorsementAnalyzer{} 655 _, err := ea.computePrincipalSets(common.ChannelID("mychannel"), interest) 656 assert.Error(t, err) 657 assert.Contains(t, err.Error(), "no principal sets remained after filtering") 658 } 659 660 func TestLoadMetadataAndFiltersCollectionNotPresentInConfig(t *testing.T) { 661 interest := &discoveryprotos.ChaincodeInterest{ 662 Chaincodes: []*discoveryprotos.ChaincodeCall{ 663 { 664 Name: "mycc", 665 CollectionNames: []string{"bar"}, 666 }, 667 }, 668 } 669 670 org1AndOrg2 := []*msp.MSPPrincipal{ 671 orgPrincipal("Org1MSP"), 672 orgPrincipal("Org2MSP"), 673 } 674 col2principals := map[string][]*msp.MSPPrincipal{ 675 "foo": org1AndOrg2, 676 } 677 config := buildCollectionConfig(col2principals) 678 679 mdf := &metadataFetcher{} 680 mdf.On("Metadata").Return(&chaincode.Metadata{ 681 Name: "mycc", 682 CollectionsConfig: config, 683 Policy: []byte{1, 2, 3}, 684 }) 685 686 _, err := loadMetadataAndFilters(metadataAndFilterContext{ 687 identityInfoByID: nil, 688 evaluator: nil, 689 chainID: common.ChannelID("mychannel"), 690 fetch: mdf, 691 interest: interest, 692 }) 693 694 assert.Equal(t, "collection bar doesn't exist in collection config for chaincode mycc", err.Error()) 695 } 696 697 func TestLoadMetadataAndFiltersInvalidCollectionData(t *testing.T) { 698 interest := &discoveryprotos.ChaincodeInterest{ 699 Chaincodes: []*discoveryprotos.ChaincodeCall{ 700 { 701 Name: "mycc", 702 CollectionNames: []string{"col1"}, 703 }, 704 }, 705 } 706 mdf := &metadataFetcher{} 707 mdf.On("Metadata").Return(&chaincode.Metadata{ 708 Name: "mycc", 709 CollectionsConfig: &peer.CollectionConfigPackage{}, 710 Policy: []byte{1, 2, 3}, 711 }) 712 713 _, err := loadMetadataAndFilters(metadataAndFilterContext{ 714 identityInfoByID: nil, 715 evaluator: nil, 716 chainID: common.ChannelID("mychannel"), 717 fetch: mdf, 718 interest: interest, 719 }) 720 assert.Error(t, err) 721 assert.Contains(t, err.Error(), "collection col1 doesn't exist in collection config for chaincode mycc") 722 } 723 724 type peerSet []*peerInfo 725 726 func (p peerSet) toMembers() discovery.Members { 727 var members discovery.Members 728 for _, peer := range p { 729 members = append(members, peer.NetworkMember) 730 } 731 return members 732 } 733 734 func identitySet(pkiID2MSPID map[string]string) api.PeerIdentitySet { 735 var res api.PeerIdentitySet 736 for pkiID, mspID := range pkiID2MSPID { 737 sID := &msp.SerializedIdentity{ 738 Mspid: pkiID2MSPID[pkiID], 739 IdBytes: []byte(pkiID), 740 } 741 res = append(res, api.PeerIdentityInfo{ 742 Identity: api.PeerIdentityType(protoutil.MarshalOrPanic(sID)), 743 PKIId: common.PKIidType(pkiID), 744 Organization: api.OrgIdentityType(mspID), 745 }) 746 } 747 return res 748 } 749 750 type peerInfo struct { 751 identity api.PeerIdentityType 752 pkiID common.PKIidType 753 discovery.NetworkMember 754 } 755 756 func peerIdentityString(id string) string { 757 return string(protoutil.MarshalOrPanic(&msp.SerializedIdentity{ 758 Mspid: pkiID2MSPID[id], 759 IdBytes: []byte(id), 760 })) 761 } 762 763 func newPeer(i int) *peerInfo { 764 p := fmt.Sprintf("p%d", i) 765 identity := protoutil.MarshalOrPanic(&msp.SerializedIdentity{ 766 Mspid: pkiID2MSPID[p], 767 IdBytes: []byte(p), 768 }) 769 return &peerInfo{ 770 pkiID: common.PKIidType(p), 771 identity: api.PeerIdentityType(identity), 772 NetworkMember: discovery.NetworkMember{ 773 PKIid: common.PKIidType(p), 774 Endpoint: p, 775 InternalEndpoint: p, 776 Envelope: &gossip.Envelope{ 777 Payload: []byte(identity), 778 }, 779 }, 780 } 781 } 782 783 func peerRole(pkiID string) *msp.MSPPrincipal { 784 return &msp.MSPPrincipal{ 785 PrincipalClassification: msp.MSPPrincipal_ROLE, 786 Principal: protoutil.MarshalOrPanic(&msp.MSPRole{ 787 MspIdentifier: pkiID2MSPID[pkiID], 788 Role: msp.MSPRole_PEER, 789 }), 790 } 791 } 792 793 func (pi *peerInfo) withChaincode(name, version string) *peerInfo { 794 if pi.Properties == nil { 795 pi.Properties = &gossip.Properties{} 796 } 797 pi.Properties.Chaincodes = append(pi.Properties.Chaincodes, &gossip.Chaincode{ 798 Name: name, 799 Version: version, 800 }) 801 return pi 802 } 803 804 type gossipMock struct { 805 mock.Mock 806 } 807 808 func (g *gossipMock) IdentityInfo() api.PeerIdentitySet { 809 return g.Called().Get(0).(api.PeerIdentitySet) 810 } 811 812 func (g *gossipMock) PeersOfChannel(_ common.ChannelID) discovery.Members { 813 members := g.Called().Get(0) 814 return members.(discovery.Members) 815 } 816 817 func (g *gossipMock) Peers() discovery.Members { 818 members := g.Called().Get(0) 819 return members.(discovery.Members) 820 } 821 822 type policyFetcherMock struct { 823 mock.Mock 824 } 825 826 func (pf *policyFetcherMock) PoliciesByChaincode(channel string, chaincode string, collections ...string) []policies.InquireablePolicy { 827 arg := pf.Called(chaincode) 828 if arg.Get(0) == nil { 829 return nil 830 } 831 832 singlePolicy, isSinglePolicy := arg.Get(0).(policies.InquireablePolicy) 833 if isSinglePolicy { 834 return []policies.InquireablePolicy{singlePolicy} 835 } 836 837 return arg.Get(0).([]policies.InquireablePolicy) 838 } 839 840 type principalBuilder struct { 841 ip inquireablePolicy 842 } 843 844 func (pb *principalBuilder) buildPolicy() inquireablePolicy { 845 defer func() { 846 pb.ip = nil 847 }() 848 return pb.ip 849 } 850 851 func (pb *principalBuilder) newSet() *principalBuilder { 852 pb.ip = append(pb.ip, make(policies.PrincipalSet, 0)) 853 return pb 854 } 855 856 func (pb *principalBuilder) addPrincipal(principal *msp.MSPPrincipal) *principalBuilder { 857 pb.ip[len(pb.ip)-1] = append(pb.ip[len(pb.ip)-1], principal) 858 return pb 859 } 860 861 type inquireablePolicy []policies.PrincipalSet 862 863 func (ip inquireablePolicy) SatisfiedBy() []policies.PrincipalSet { 864 return ip 865 } 866 867 type principalEvaluatorMock struct { 868 } 869 870 func (pe *principalEvaluatorMock) SatisfiesPrincipal(channel string, identity []byte, principal *msp.MSPPrincipal) error { 871 peerRole := &msp.MSPRole{} 872 if err := proto.Unmarshal(principal.Principal, peerRole); err != nil { 873 return err 874 } 875 sId := &msp.SerializedIdentity{} 876 if err := proto.Unmarshal(identity, sId); err != nil { 877 return err 878 } 879 if peerRole.MspIdentifier == sId.Mspid { 880 return nil 881 } 882 return errors.New("bingo") 883 } 884 885 type metadataFetcher struct { 886 mock.Mock 887 } 888 889 func (mf *metadataFetcher) Metadata(channel string, cc string, _ bool) *chaincode.Metadata { 890 arg := mf.Called().Get(0) 891 if arg == nil { 892 return nil 893 } 894 return arg.(*chaincode.Metadata) 895 }