github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/integration/pvtdata/pvtdata_test.go (about) 1 /* 2 Copyright IBM Corp All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package pvtdata 8 9 import ( 10 "bytes" 11 "context" 12 "encoding/base64" 13 "fmt" 14 "io/ioutil" 15 "os" 16 "path/filepath" 17 "strconv" 18 "syscall" 19 "time" 20 21 "github.com/golang/protobuf/proto" 22 "github.com/golang/protobuf/ptypes" 23 . "github.com/onsi/ginkgo" 24 . "github.com/onsi/gomega" 25 "github.com/pkg/errors" 26 "google.golang.org/grpc" 27 28 docker "github.com/fsouza/go-dockerclient" 29 cb "github.com/hyperledger/fabric-protos-go/common" 30 "github.com/hyperledger/fabric-protos-go/ledger/rwset" 31 "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 32 ab "github.com/hyperledger/fabric-protos-go/orderer" 33 pb "github.com/hyperledger/fabric-protos-go/peer" 34 "github.com/hyperledger/fabric/bccsp/sw" 35 "github.com/hyperledger/fabric/common/crypto" 36 "github.com/hyperledger/fabric/core/comm" 37 "github.com/hyperledger/fabric/core/ledger/util" 38 "github.com/hyperledger/fabric/integration/nwo" 39 "github.com/hyperledger/fabric/integration/nwo/commands" 40 "github.com/hyperledger/fabric/msp" 41 "github.com/hyperledger/fabric/protoutil" 42 "github.com/onsi/gomega/gbytes" 43 "github.com/onsi/gomega/gexec" 44 "github.com/tedsuo/ifrit" 45 ) 46 47 // The chaincode used in these tests has two collections defined: 48 // collectionMarbles and collectionMarblePrivateDetails 49 // when calling QueryChaincode with first arg "readMarble", it will query collectionMarbles 50 // when calling QueryChaincode with first arg "readMarblePrivateDetails", it will query collectionMarblePrivateDetails 51 52 const channelID = "testchannel" 53 54 var _ bool = Describe("PrivateData", func() { 55 var ( 56 network *nwo.Network 57 process ifrit.Process 58 59 orderer *nwo.Orderer 60 ) 61 62 AfterEach(func() { 63 testCleanup(network, process) 64 }) 65 66 Describe("Dissemination when pulling is disabled", func() { 67 It("disseminates private data per collections_config1", func() { 68 By("setting up the network") 69 network = initThreeOrgsSetup() 70 71 By("setting the pull retry threshold to 0 on all peers") 72 // set pull retry threshold to 0 73 for _, p := range network.Peers { 74 core := network.ReadPeerConfig(p) 75 core.Peer.Gossip.PvtData.PullRetryThreshold = 0 76 network.WritePeerConfig(p, core) 77 } 78 79 By("starting the network") 80 process, orderer = startNetwork(network) 81 82 By("deploying legacy chaincode and adding marble1") 83 testChaincode := chaincode{ 84 Chaincode: nwo.Chaincode{ 85 Name: "marblesp", 86 Version: "1.0", 87 Path: "github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd", 88 Ctor: `{"Args":["init"]}`, 89 Policy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`, 90 // collections_config1.json defines the access as follows: 91 // 1. collectionMarbles - Org1, Org2 have access to this collection 92 // 2. collectionMarblePrivateDetails - Org2 and Org3 have access to this collection 93 CollectionsConfig: collectionConfig("collections_config1.json"), 94 }, 95 isLegacy: true, 96 } 97 deployChaincode(network, orderer, testChaincode) 98 addMarble(network, orderer, testChaincode.Name, 99 `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`, 100 network.Peer("Org1", "peer0"), 101 ) 102 103 assertPvtdataPresencePerCollectionConfig1(network, testChaincode.Name, "marble1") 104 }) 105 }) 106 107 Describe("Pvtdata behavior with untouched peer configs", func() { 108 var ( 109 legacyChaincode nwo.Chaincode 110 newLifecycleChaincode nwo.Chaincode 111 testChaincode chaincode 112 113 org1Peer1, org2Peer1 *nwo.Peer 114 ) 115 116 BeforeEach(func() { 117 By("setting up the network") 118 network = initThreeOrgsSetup() 119 legacyChaincode = nwo.Chaincode{ 120 Name: "marblesp", 121 Version: "1.0", 122 Path: "github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd", 123 Ctor: `{"Args":["init"]}`, 124 Policy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`, 125 // collections_config1.json defines the access as follows: 126 // 1. collectionMarbles - Org1, Org2 have access to this collection 127 // 2. collectionMarblePrivateDetails - Org2 and Org3 have access to this collection 128 CollectionsConfig: collectionConfig("collections_config1.json"), 129 } 130 131 newLifecycleChaincode = nwo.Chaincode{ 132 Name: "marblesp", 133 Version: "1.0", 134 Path: components.Build("github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd"), 135 Lang: "binary", 136 PackageFile: filepath.Join(network.RootDir, "marbles-pvtdata.tar.gz"), 137 Label: "marbles-private-20", 138 SignaturePolicy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`, 139 CollectionsConfig: collectionConfig("collections_config1.json"), 140 Sequence: "1", 141 } 142 org1Peer1 = &nwo.Peer{ 143 Name: "peer1", 144 Organization: "Org1", 145 Channels: []*nwo.PeerChannel{ 146 {Name: channelID}, 147 }, 148 } 149 org2Peer1 = &nwo.Peer{ 150 Name: "peer1", 151 Organization: "Org2", 152 Channels: []*nwo.PeerChannel{ 153 {Name: channelID}, 154 }, 155 } 156 157 process, orderer = startNetwork(network) 158 }) 159 160 Describe("Reconciliation and pulling", func() { 161 var newPeerProcess ifrit.Process 162 163 BeforeEach(func() { 164 By("deploying legacy chaincode and adding marble1") 165 testChaincode = chaincode{ 166 Chaincode: legacyChaincode, 167 isLegacy: true, 168 } 169 deployChaincode(network, orderer, testChaincode) 170 addMarble(network, orderer, testChaincode.Name, 171 `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`, 172 network.Peer("Org1", "peer0"), 173 ) 174 }) 175 176 AfterEach(func() { 177 if newPeerProcess != nil { 178 newPeerProcess.Signal(syscall.SIGTERM) 179 Eventually(newPeerProcess.Wait(), network.EventuallyTimeout).Should(Receive()) 180 } 181 }) 182 183 assertReconcileBehavior := func() { 184 It("disseminates private data per collections_config1", func() { 185 assertPvtdataPresencePerCollectionConfig1(network, testChaincode.Name, "marble1") 186 }) 187 188 When("org3 is added to collectionMarbles via chaincode upgrade with collections_config2", func() { 189 It("distributes and allows access to newly added private data per collections_config2", func() { 190 By("upgrading the chaincode and adding marble2") 191 // collections_config2.json defines the access as follows: 192 // 1. collectionMarbles - Org1, Org2 and Org3 have access to this collection 193 // 2. collectionMarblePrivateDetails - Org2 and Org3 have access to this collection 194 // the change from collections_config1 - org3 was added to collectionMarbles 195 testChaincode.Version = "1.1" 196 testChaincode.CollectionsConfig = collectionConfig("collections_config2.json") 197 if !testChaincode.isLegacy { 198 testChaincode.Sequence = "2" 199 } 200 upgradeChaincode(network, orderer, testChaincode) 201 addMarble(network, orderer, testChaincode.Name, 202 `{"name":"marble2", "color":"yellow", "size":53, "owner":"jerry", "price":22}`, 203 network.Peer("Org2", "peer0"), 204 ) 205 206 assertPvtdataPresencePerCollectionConfig2(network, testChaincode.Name, "marble2") 207 }) 208 }) 209 210 When("a new peer in org1 joins the channel", func() { 211 It("causes the new peer to pull the existing private data only for collectionMarbles", func() { 212 By("adding a new peer") 213 newPeerProcess = addPeer(network, orderer, org1Peer1) 214 installChaincode(network, testChaincode, org1Peer1) 215 network.VerifyMembership(network.Peers, channelID, "marblesp") 216 217 assertPvtdataPresencePerCollectionConfig1(network, testChaincode.Name, "marble1", org1Peer1) 218 }) 219 }) 220 } 221 222 When("chaincode in legacy lifecycle", func() { 223 assertReconcileBehavior() 224 }) 225 226 When("chaincode is migrated from legacy to new lifecycle with same collection config", func() { 227 BeforeEach(func() { 228 testChaincode = chaincode{ 229 Chaincode: newLifecycleChaincode, 230 isLegacy: false, 231 } 232 nwo.EnableCapabilities(network, channelID, "Application", "V2_0", orderer, network.Peers...) 233 upgradeChaincode(network, orderer, testChaincode) 234 }) 235 assertReconcileBehavior() 236 }) 237 }) 238 239 Describe("BlockToLive", func() { 240 var newPeerProcess ifrit.Process 241 242 AfterEach(func() { 243 if newPeerProcess != nil { 244 newPeerProcess.Signal(syscall.SIGTERM) 245 Eventually(newPeerProcess.Wait(), network.EventuallyTimeout).Should(Receive()) 246 } 247 }) 248 249 assertBlockToLiveBehavior := func() { 250 eligiblePeer := network.Peer("Org2", "peer0") 251 ccName := testChaincode.Name 252 By("adding three blocks") 253 for i := 0; i < 3; i++ { 254 addMarble(network, orderer, ccName, fmt.Sprintf(`{"name":"test-marble-%d", "color":"blue", "size":35, "owner":"tom", "price":99}`, i), eligiblePeer) 255 } 256 257 By("verifying that marble1 still not purged in collection MarblesPD") 258 assertPresentInCollectionMPD(network, ccName, "marble1", eligiblePeer) 259 260 By("adding one more block") 261 addMarble(network, orderer, ccName, `{"name":"fun-marble-3", "color":"blue", "size":35, "owner":"tom", "price":99}`, eligiblePeer) 262 263 By("verifying that marble1 purged in collection MarblesPD") 264 assertDoesNotExistInCollectionMPD(network, ccName, "marble1", eligiblePeer) 265 266 By("verifying that marble1 still not purged in collection Marbles") 267 assertPresentInCollectionM(network, ccName, "marble1", eligiblePeer) 268 269 By("adding new peer that is eligible to recieve data") 270 newPeerProcess = addPeer(network, orderer, org2Peer1) 271 installChaincode(network, testChaincode, org2Peer1) 272 network.VerifyMembership(network.Peers, channelID, ccName) 273 assertPresentInCollectionM(network, ccName, "marble1", org2Peer1) 274 assertDoesNotExistInCollectionMPD(network, ccName, "marble1", org2Peer1) 275 } 276 277 When("chaincode in legacy lifecycle", func() { 278 It("purges private data after BTL and causes new peer not to pull the purged private data", func() { 279 By("deploying legacy chaincode and adding marble1") 280 testChaincode = chaincode{ 281 Chaincode: legacyChaincode, 282 isLegacy: true, 283 } 284 285 testChaincode.CollectionsConfig = collectionConfig("short_btl_config.json") 286 deployChaincode(network, orderer, testChaincode) 287 addMarble(network, orderer, testChaincode.Name, `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`, network.Peer("Org2", "peer0")) 288 289 assertBlockToLiveBehavior() 290 }) 291 }) 292 293 When("chaincode in new lifecycle", func() { 294 It("purges private data after BTL and causes new peer not to pull the purged private data", func() { 295 By("deploying new lifecycle chaincode and adding marble1") 296 testChaincode = chaincode{ 297 Chaincode: newLifecycleChaincode, 298 isLegacy: false, 299 } 300 nwo.EnableCapabilities(network, channelID, "Application", "V2_0", orderer, network.Peers...) 301 302 testChaincode.CollectionsConfig = collectionConfig("short_btl_config.json") 303 deployChaincode(network, orderer, testChaincode) 304 addMarble(network, orderer, testChaincode.Name, `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`, network.Peer("Org2", "peer0")) 305 306 assertBlockToLiveBehavior() 307 }) 308 }) 309 }) 310 311 Describe("Org removal from collection", func() { 312 assertOrgRemovalBehavior := func() { 313 By("upgrading chaincode to remove org3 from collectionMarbles") 314 testChaincode.CollectionsConfig = collectionConfig("collections_config1.json") 315 testChaincode.Version = "1.1" 316 if !testChaincode.isLegacy { 317 testChaincode.Sequence = "2" 318 } 319 upgradeChaincode(network, orderer, testChaincode) 320 addMarble(network, orderer, testChaincode.Name, `{"name":"marble2", "color":"yellow", "size":53, "owner":"jerry", "price":22}`, network.Peer("Org2", "peer0")) 321 assertPvtdataPresencePerCollectionConfig1(network, testChaincode.Name, "marble2") 322 } 323 324 When("chaincode in legacy lifecycle", func() { 325 It("causes removed org not to get new data", func() { 326 By("deploying legacy chaincode and adding marble1") 327 testChaincode = chaincode{ 328 Chaincode: legacyChaincode, 329 isLegacy: true, 330 } 331 testChaincode.CollectionsConfig = collectionConfig("collections_config2.json") 332 deployChaincode(network, orderer, testChaincode) 333 addMarble(network, orderer, testChaincode.Name, `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`, network.Peer("Org2", "peer0")) 334 assertPvtdataPresencePerCollectionConfig2(network, testChaincode.Name, "marble1") 335 336 assertOrgRemovalBehavior() 337 }) 338 }) 339 340 When("chaincode in new lifecycle", func() { 341 It("causes removed org not to get new data", func() { 342 By("deploying new lifecycle chaincode and adding marble1") 343 testChaincode = chaincode{ 344 Chaincode: newLifecycleChaincode, 345 isLegacy: false, 346 } 347 nwo.EnableCapabilities(network, channelID, "Application", "V2_0", orderer, network.Peers...) 348 testChaincode.CollectionsConfig = collectionConfig("collections_config2.json") 349 deployChaincode(network, orderer, testChaincode) 350 addMarble(network, orderer, testChaincode.Name, `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`, network.Peer("Org2", "peer0")) 351 assertPvtdataPresencePerCollectionConfig2(network, testChaincode.Name, "marble1") 352 353 assertOrgRemovalBehavior() 354 }) 355 }) 356 }) 357 358 When("migrating a chaincode from legacy lifecycle to new lifecycle", func() { 359 It("performs check against collection config from legacy lifecycle", func() { 360 By("deploying legacy chaincode") 361 testChaincode = chaincode{ 362 Chaincode: legacyChaincode, 363 isLegacy: true, 364 } 365 deployChaincode(network, orderer, testChaincode) 366 nwo.EnableCapabilities(network, channelID, "Application", "V2_0", orderer, network.Peers...) 367 368 newLifecycleChaincode.CollectionsConfig = collectionConfig("short_btl_config.json") 369 newLifecycleChaincode.PackageID = "test-package-id" 370 371 approveChaincodeForMyOrgExpectErr( 372 network, 373 orderer, 374 newLifecycleChaincode, 375 `the BlockToLive in an existing collection \[collectionMarblePrivateDetails\] modified. Existing value \[1000000\]`, 376 network.Peer("Org2", "peer0")) 377 }) 378 }) 379 380 Describe("marble APIs invocation and private data delivery", func() { 381 // call marble APIs: getMarblesByRange, transferMarble, delete, getMarbleHash, getMarblePrivateDetailsHash and verify ACL Behavior 382 assertMarbleAPIs := func() { 383 eligiblePeer := network.Peer("Org2", "peer0") 384 ccName := testChaincode.Name 385 386 // Verifies marble private chaincode APIs: getMarblesByRange, transferMarble, delete 387 388 By("adding five marbles") 389 for i := 0; i < 5; i++ { 390 addMarble(network, orderer, ccName, fmt.Sprintf(`{"name":"test-marble-%d", "color":"blue", "size":35, "owner":"tom", "price":99}`, i), eligiblePeer) 391 } 392 393 By("getting marbles by range") 394 assertGetMarblesByRange(network, ccName, `"test-marble-0", "test-marble-2"`, eligiblePeer) 395 396 By("transferring test-marble-0 to jerry") 397 transferMarble(network, orderer, ccName, `{"name":"test-marble-0", "owner":"jerry"}`, eligiblePeer) 398 399 By("verifying the new ownership of test-marble-0") 400 assertOwnershipInCollectionM(network, ccName, `test-marble-0`, eligiblePeer) 401 402 By("deleting test-marble-0") 403 deleteMarble(network, orderer, ccName, `{"name":"test-marble-0"}`, eligiblePeer) 404 405 By("verifying the deletion of test-marble-0") 406 assertDoesNotExistInCollectionM(network, ccName, `test-marble-0`, eligiblePeer) 407 408 // This section verifies that chaincode can return private data hash. 409 // Unlike private data that can only be accessed from authorized peers as defined in the collection config, 410 // private data hash can be queried on any peer in the channel that has the chaincode instantiated. 411 // When calling QueryChaincode with "getMarbleHash", the cc will return the private data hash in collectionMarbles. 412 // When calling QueryChaincode with "getMarblePrivateDetailsHash", the cc will return the private data hash in collectionMarblePrivateDetails. 413 414 peerList := []*nwo.Peer{ 415 network.Peer("Org1", "peer0"), 416 network.Peer("Org2", "peer0"), 417 network.Peer("Org3", "peer0")} 418 419 By("verifying getMarbleHash is accessible from all peers that has the chaincode instantiated") 420 expectedBytes := util.ComputeStringHash(`{"docType":"marble","name":"test-marble-1","color":"blue","size":35,"owner":"tom"}`) 421 assertMarblesPrivateHashM(network, ccName, "test-marble-1", expectedBytes, peerList) 422 423 By("verifying getMarblePrivateDetailsHash is accessible from all peers that has the chaincode instantiated") 424 expectedBytes = util.ComputeStringHash(`{"docType":"marblePrivateDetails","name":"test-marble-1","price":99}`) 425 assertMarblesPrivateDetailsHashMPD(network, ccName, "test-marble-1", expectedBytes, peerList) 426 427 // collection ACL while reading private data: not allowed to non-members 428 // collections_config3: collectionMarblePrivateDetails - member_only_read is set to true 429 430 By("querying collectionMarblePrivateDetails on org1-peer0 by org1-user1, shouldn't have read access") 431 assertNoReadAccessToCollectionMPD(network, testChaincode.Name, "test-marble-1", network.Peer("Org1", "peer0")) 432 } 433 434 // verify DeliverWithPrivateData sends private data based on the ACL in collection config 435 // before and after upgrade. 436 assertDeliverWithPrivateDataACLBehavior := func() { 437 By("getting signing identity for a user in org1") 438 signingIdentity := getSigningIdentity(network, "Org1", "User1", "Org1MSP", "bccsp") 439 440 By("adding a marble") 441 peer := network.Peer("Org2", "peer0") 442 addMarble(network, orderer, testChaincode.Name, `{"name":"marble11", "color":"blue", "size":35, "owner":"tom", "price":99}`, peer) 443 444 By("getting the deliver event for newest block") 445 event := getEventFromDeliverService(network, peer, channelID, signingIdentity, 0) 446 447 By("verifying private data in deliver event contains 'collectionMarbles' only") 448 // it should receive pvtdata for 'collectionMarbles' only because memberOnlyRead is true 449 expectedKVWritesMap := map[string]map[string][]byte{ 450 "collectionMarbles": { 451 "\000color~name\000blue\000marble11\000": []byte("\000"), 452 "marble11": getValueForCollectionMarbles("marble11", "blue", "tom", 35), 453 }, 454 } 455 assertPrivateDataAsExpected(event.BlockAndPvtData.PrivateDataMap, expectedKVWritesMap) 456 457 By("upgrading chaincode with collections_config1.json where isMemberOnlyRead is false") 458 testChaincode.CollectionsConfig = collectionConfig("collections_config1.json") 459 testChaincode.Version = "1.1" 460 if !testChaincode.isLegacy { 461 testChaincode.Sequence = "2" 462 } 463 upgradeChaincode(network, orderer, testChaincode) 464 465 By("getting the deliver event for an old block committed before upgrade") 466 event = getEventFromDeliverService(network, peer, channelID, signingIdentity, event.BlockNum) 467 468 By("verifying the deliver event for the old block uses old config") 469 assertPrivateDataAsExpected(event.BlockAndPvtData.PrivateDataMap, expectedKVWritesMap) 470 471 By("adding a new marble after upgrade") 472 addMarble(network, orderer, testChaincode.Name, 473 `{"name":"marble12", "color":"blue", "size":35, "owner":"tom", "price":99}`, 474 network.Peer("Org1", "peer0"), 475 ) 476 By("getting the deliver event for a new block committed after upgrade") 477 event = getEventFromDeliverService(network, peer, channelID, signingIdentity, 0) 478 479 // it should receive pvtdata for both collections because memberOnlyRead is false 480 By("verifying the deliver event for the new block uses new config") 481 expectedKVWritesMap = map[string]map[string][]byte{ 482 "collectionMarbles": { 483 "\000color~name\000blue\000marble12\000": []byte("\000"), 484 "marble12": getValueForCollectionMarbles("marble12", "blue", "tom", 35), 485 }, 486 "collectionMarblePrivateDetails": { 487 "marble12": getValueForCollectionMarblePrivateDetails("marble12", 99), 488 }, 489 } 490 assertPrivateDataAsExpected(event.BlockAndPvtData.PrivateDataMap, expectedKVWritesMap) 491 } 492 493 When("chaincode in legacy lifecycle", func() { 494 It("calls marbles APIs and delivers private data", func() { 495 By("deploying legacy chaincode") 496 testChaincode = chaincode{ 497 Chaincode: legacyChaincode, 498 isLegacy: true, 499 } 500 testChaincode.CollectionsConfig = collectionConfig("collections_config3.json") 501 deployChaincode(network, orderer, testChaincode) 502 503 assertMarbleAPIs() 504 assertDeliverWithPrivateDataACLBehavior() 505 }) 506 }) 507 508 When("chaincode in new lifecycle", func() { 509 It("calls marbles APIs and delivers private data", func() { 510 By("deploying new lifecycle chaincode") 511 testChaincode = chaincode{ 512 Chaincode: newLifecycleChaincode, 513 isLegacy: false, 514 } 515 nwo.EnableCapabilities(network, channelID, "Application", "V2_0", orderer, network.Peers...) 516 testChaincode.CollectionsConfig = collectionConfig("collections_config3.json") 517 deployChaincode(network, orderer, testChaincode) 518 519 assertMarbleAPIs() 520 assertDeliverWithPrivateDataACLBehavior() 521 }) 522 }) 523 }) 524 525 Describe("Collection Config Endorsement Policy", func() { 526 When("using legacy lifecycle chaincode", func() { 527 It("ignores the collection config endorsement policy and successfully invokes the chaincode", func() { 528 testChaincode = chaincode{ 529 Chaincode: legacyChaincode, 530 isLegacy: true, 531 } 532 By("setting the collection config endorsement policy to org2 or org3 peers") 533 testChaincode.CollectionsConfig = collectionConfig("collections_config4.json") 534 535 By("deploying legacy chaincode") 536 deployChaincode(network, orderer, testChaincode) 537 538 By("adding marble1 with an org 1 peer as endorser") 539 peer := network.Peer("Org1", "peer0") 540 marbleDetails := `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}` 541 addMarble(network, orderer, testChaincode.Name, marbleDetails, peer) 542 }) 543 }) 544 545 When("using new lifecycle chaincode", func() { 546 BeforeEach(func() { 547 testChaincode = chaincode{ 548 Chaincode: newLifecycleChaincode, 549 isLegacy: false, 550 } 551 nwo.EnableCapabilities(network, "testchannel", "Application", "V2_0", orderer, network.Peers...) 552 }) 553 554 When("a peer specified in the chaincode endorsement policy but not in the collection config endorsement policy is used to invoke the chaincode", func() { 555 It("fails validation", func() { 556 By("setting the collection config endorsement policy to org2 or org3 peers") 557 testChaincode.CollectionsConfig = collectionConfig("collections_config4.json") 558 559 By("deploying new lifecycle chaincode") 560 // set collection endorsement policy to org2 or org3 561 deployChaincode(network, orderer, testChaincode) 562 563 By("adding marble1 with an org1 peer as endorser") 564 peer := network.Peer("Org1", "peer0") 565 marbleDetails := `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}` 566 marbleDetailsBase64 := base64.StdEncoding.EncodeToString([]byte(marbleDetails)) 567 568 command := commands.ChaincodeInvoke{ 569 ChannelID: channelID, 570 Orderer: network.OrdererAddress(orderer, nwo.ListenPort), 571 Name: testChaincode.Name, 572 Ctor: fmt.Sprintf(`{"Args":["initMarble"]}`), 573 Transient: fmt.Sprintf(`{"marble":"%s"}`, marbleDetailsBase64), 574 PeerAddresses: []string{ 575 network.PeerAddress(peer, nwo.ListenPort), 576 }, 577 WaitForEvent: true, 578 } 579 580 sess, err := network.PeerUserSession(peer, "User1", command) 581 Expect(err).NotTo(HaveOccurred()) 582 Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit()) 583 Expect(sess.Err).To(gbytes.Say("ENDORSEMENT_POLICY_FAILURE")) 584 }) 585 }) 586 587 When("a peer specified in the collection endorsement policy but not in the chaincode endorsement policy is used to invoke the chaincode", func() { 588 When("the collection endorsement policy is a signature policy", func() { 589 It("successfully invokes the chaincode", func() { 590 // collection config endorsement policy specifies org2 or org3 peers for endorsement 591 By("setting the collection config endorsement policy to use a signature policy") 592 testChaincode.CollectionsConfig = collectionConfig("collections_config4.json") 593 594 By("setting the chaincode endorsement policy to org1 or org2 peers") 595 testChaincode.SignaturePolicy = `OR ('Org1MSP.member','Org2MSP.member')` 596 597 By("deploying new lifecycle chaincode") 598 // set collection endorsement policy to org2 or org3 599 deployChaincode(network, orderer, testChaincode) 600 601 By("adding marble1 with an org3 peer as endorser") 602 peer := network.Peer("Org3", "peer0") 603 marbleDetails := `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}` 604 addMarble(network, orderer, testChaincode.Name, marbleDetails, peer) 605 }) 606 }) 607 608 When("the collection endorsement policy is a channel config policy reference", func() { 609 It("successfully invokes the chaincode", func() { 610 // collection config endorsement policy specifies channel config policy reference /Channel/Application/Readers 611 By("setting the collection config endorsement policy to use a channel config policy reference") 612 testChaincode.CollectionsConfig = collectionConfig("collections_config5.json") 613 614 By("setting the channel endorsement policy to org1 or org2 peers") 615 testChaincode.SignaturePolicy = `OR ('Org1MSP.member','Org2MSP.member')` 616 617 By("deploying new lifecycle chaincode") 618 deployChaincode(network, orderer, testChaincode) 619 620 By("adding marble1 with an org3 peer as endorser") 621 peer := network.Peer("Org3", "peer0") 622 marbleDetails := `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}` 623 addMarble(network, orderer, testChaincode.Name, marbleDetails, peer) 624 }) 625 }) 626 }) 627 628 When("the collection config endorsement policy specifies a semantically wrong, but well formed signature policy", func() { 629 It("fails to invoke the chaincode with an endorsement policy failure", func() { 630 By("setting the collection config endorsement policy to non existent org4 peers") 631 testChaincode.CollectionsConfig = collectionConfig("collections_config6.json") 632 633 By("deploying new lifecycle chaincode") 634 deployChaincode(network, orderer, testChaincode) 635 636 By("adding marble1 with an org1 peer as endorser") 637 peer := network.Peer("Org1", "peer0") 638 marbleDetails := `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}` 639 marbleDetailsBase64 := base64.StdEncoding.EncodeToString([]byte(marbleDetails)) 640 641 command := commands.ChaincodeInvoke{ 642 ChannelID: channelID, 643 Orderer: network.OrdererAddress(orderer, nwo.ListenPort), 644 Name: testChaincode.Name, 645 Ctor: fmt.Sprintf(`{"Args":["initMarble"]}`), 646 Transient: fmt.Sprintf(`{"marble":"%s"}`, marbleDetailsBase64), 647 PeerAddresses: []string{ 648 network.PeerAddress(peer, nwo.ListenPort), 649 }, 650 WaitForEvent: true, 651 } 652 653 sess, err := network.PeerUserSession(peer, "User1", command) 654 Expect(err).NotTo(HaveOccurred()) 655 Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit()) 656 Expect(sess.Err).To(gbytes.Say("ENDORSEMENT_POLICY_FAILURE")) 657 }) 658 }) 659 }) 660 }) 661 }) 662 }) 663 664 func initThreeOrgsSetup() *nwo.Network { 665 var err error 666 testDir, err := ioutil.TempDir("", "e2e-pvtdata") 667 Expect(err).NotTo(HaveOccurred()) 668 669 client, err := docker.NewClientFromEnv() 670 Expect(err).NotTo(HaveOccurred()) 671 672 config := nwo.BasicSolo() 673 674 // add org3 with one peer 675 config.Organizations = append(config.Organizations, &nwo.Organization{ 676 Name: "Org3", 677 MSPID: "Org3MSP", 678 Domain: "org3.example.com", 679 EnableNodeOUs: true, 680 Users: 2, 681 CA: &nwo.CA{Hostname: "ca"}, 682 }) 683 config.Consortiums[0].Organizations = append(config.Consortiums[0].Organizations, "Org3") 684 config.Profiles[1].Organizations = append(config.Profiles[1].Organizations, "Org3") 685 config.Peers = append(config.Peers, &nwo.Peer{ 686 Name: "peer0", 687 Organization: "Org3", 688 Channels: []*nwo.PeerChannel{ 689 {Name: channelID, Anchor: true}, 690 }, 691 }) 692 693 n := nwo.New(config, testDir, client, StartPort(), components) 694 n.GenerateConfigTree() 695 696 // remove peer1 from org1 and org2 so we can add it back later, we generate the config tree above 697 // with the two peers so the config files exist later when adding the peer back 698 peers := []*nwo.Peer{} 699 for _, p := range n.Peers { 700 if p.Name != "peer1" { 701 peers = append(peers, p) 702 } 703 } 704 n.Peers = peers 705 Expect(n.Peers).To(HaveLen(3)) 706 707 return n 708 } 709 710 func startNetwork(n *nwo.Network) (ifrit.Process, *nwo.Orderer) { 711 n.Bootstrap() 712 networkRunner := n.NetworkGroupRunner() 713 process := ifrit.Invoke(networkRunner) 714 Eventually(process.Ready(), n.EventuallyTimeout).Should(BeClosed()) 715 716 orderer := n.Orderer("orderer") 717 n.CreateAndJoinChannel(orderer, channelID) 718 n.UpdateChannelAnchors(orderer, channelID) 719 720 By("verifying membership") 721 n.VerifyMembership(n.Peers, channelID) 722 723 return process, orderer 724 } 725 726 func testCleanup(network *nwo.Network, process ifrit.Process) { 727 if process != nil { 728 process.Signal(syscall.SIGTERM) 729 Eventually(process.Wait(), network.EventuallyTimeout).Should(Receive()) 730 } 731 if network != nil { 732 network.Cleanup() 733 } 734 os.RemoveAll(network.RootDir) 735 } 736 737 func collectionConfig(collConfigFile string) string { 738 return filepath.Join("testdata", "collection_configs", collConfigFile) 739 } 740 741 type chaincode struct { 742 nwo.Chaincode 743 isLegacy bool 744 } 745 746 func addPeer(n *nwo.Network, orderer *nwo.Orderer, peer *nwo.Peer) ifrit.Process { 747 process := ifrit.Invoke(n.PeerRunner(peer)) 748 Eventually(process.Ready(), n.EventuallyTimeout).Should(BeClosed()) 749 750 n.JoinChannel(channelID, orderer, peer) 751 ledgerHeight := nwo.GetLedgerHeight(n, n.Peers[0], channelID) 752 sess, err := n.PeerAdminSession( 753 peer, 754 commands.ChannelFetch{ 755 Block: "newest", 756 ChannelID: channelID, 757 Orderer: n.OrdererAddress(orderer, nwo.ListenPort), 758 OutputFile: filepath.Join(n.RootDir, "newest_block.pb"), 759 }, 760 ) 761 Expect(err).NotTo(HaveOccurred()) 762 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 763 Expect(sess.Err).To(gbytes.Say(fmt.Sprintf("Received block: %d", ledgerHeight-1))) 764 765 n.Peers = append(n.Peers, peer) 766 nwo.WaitUntilEqualLedgerHeight(n, channelID, nwo.GetLedgerHeight(n, n.Peers[0], channelID), n.Peers...) 767 768 return process 769 } 770 771 func deployChaincode(n *nwo.Network, orderer *nwo.Orderer, chaincode chaincode) { 772 if chaincode.isLegacy { 773 nwo.DeployChaincodeLegacy(n, channelID, orderer, chaincode.Chaincode) 774 } else { 775 nwo.DeployChaincode(n, channelID, orderer, chaincode.Chaincode) 776 } 777 } 778 779 func upgradeChaincode(n *nwo.Network, orderer *nwo.Orderer, chaincode chaincode) { 780 if chaincode.isLegacy { 781 nwo.UpgradeChaincodeLegacy(n, channelID, orderer, chaincode.Chaincode) 782 } else { 783 nwo.DeployChaincode(n, channelID, orderer, chaincode.Chaincode) 784 } 785 } 786 787 func installChaincode(n *nwo.Network, chaincode chaincode, peer *nwo.Peer) { 788 if chaincode.isLegacy { 789 nwo.InstallChaincodeLegacy(n, chaincode.Chaincode, peer) 790 } else { 791 nwo.PackageAndInstallChaincode(n, chaincode.Chaincode, peer) 792 } 793 } 794 795 func queryChaincode(n *nwo.Network, peer *nwo.Peer, command commands.ChaincodeQuery, expectedMessage string, expectSuccess bool) { 796 sess, err := n.PeerUserSession(peer, "User1", command) 797 Expect(err).NotTo(HaveOccurred()) 798 if expectSuccess { 799 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 800 Expect(sess).To(gbytes.Say(expectedMessage)) 801 } else { 802 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit()) 803 Expect(sess.Err).To(gbytes.Say(expectedMessage)) 804 } 805 } 806 807 func invokeChaincode(n *nwo.Network, peer *nwo.Peer, command commands.ChaincodeInvoke) { 808 sess, err := n.PeerUserSession(peer, "User1", command) 809 Expect(err).NotTo(HaveOccurred()) 810 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 811 Expect(sess.Err).To(gbytes.Say("Chaincode invoke successful.")) 812 } 813 814 func approveChaincodeForMyOrgExpectErr(n *nwo.Network, orderer *nwo.Orderer, chaincode nwo.Chaincode, expectedErrMsg string, peers ...*nwo.Peer) { 815 // used to ensure we only approve once per org 816 approvedOrgs := map[string]bool{} 817 for _, p := range peers { 818 if _, ok := approvedOrgs[p.Organization]; !ok { 819 sess, err := n.PeerAdminSession(p, commands.ChaincodeApproveForMyOrg{ 820 ChannelID: channelID, 821 Orderer: n.OrdererAddress(orderer, nwo.ListenPort), 822 Name: chaincode.Name, 823 Version: chaincode.Version, 824 PackageID: chaincode.PackageID, 825 Sequence: chaincode.Sequence, 826 EndorsementPlugin: chaincode.EndorsementPlugin, 827 ValidationPlugin: chaincode.ValidationPlugin, 828 SignaturePolicy: chaincode.SignaturePolicy, 829 ChannelConfigPolicy: chaincode.ChannelConfigPolicy, 830 InitRequired: chaincode.InitRequired, 831 CollectionsConfig: chaincode.CollectionsConfig, 832 }) 833 Expect(err).NotTo(HaveOccurred()) 834 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit()) 835 approvedOrgs[p.Organization] = true 836 Eventually(sess.Err, n.EventuallyTimeout).Should(gbytes.Say(expectedErrMsg)) 837 } 838 } 839 } 840 841 func addMarble(n *nwo.Network, orderer *nwo.Orderer, chaincodeName, marbleDetails string, peer *nwo.Peer) { 842 marbleDetailsBase64 := base64.StdEncoding.EncodeToString([]byte(marbleDetails)) 843 844 command := commands.ChaincodeInvoke{ 845 ChannelID: channelID, 846 Orderer: n.OrdererAddress(orderer, nwo.ListenPort), 847 Name: chaincodeName, 848 Ctor: fmt.Sprintf(`{"Args":["initMarble"]}`), 849 Transient: fmt.Sprintf(`{"marble":"%s"}`, marbleDetailsBase64), 850 PeerAddresses: []string{ 851 n.PeerAddress(peer, nwo.ListenPort), 852 }, 853 WaitForEvent: true, 854 } 855 invokeChaincode(n, peer, command) 856 nwo.WaitUntilEqualLedgerHeight(n, channelID, nwo.GetLedgerHeight(n, peer, channelID), n.Peers...) 857 } 858 859 func deleteMarble(n *nwo.Network, orderer *nwo.Orderer, chaincodeName, marbleDelete string, peer *nwo.Peer) { 860 marbleDeleteBase64 := base64.StdEncoding.EncodeToString([]byte(marbleDelete)) 861 862 command := commands.ChaincodeInvoke{ 863 ChannelID: channelID, 864 Orderer: n.OrdererAddress(orderer, nwo.ListenPort), 865 Name: chaincodeName, 866 Ctor: fmt.Sprintf(`{"Args":["delete"]}`), 867 Transient: fmt.Sprintf(`{"marble_delete":"%s"}`, marbleDeleteBase64), 868 PeerAddresses: []string{ 869 n.PeerAddress(peer, nwo.ListenPort), 870 }, 871 WaitForEvent: true, 872 } 873 invokeChaincode(n, peer, command) 874 nwo.WaitUntilEqualLedgerHeight(n, channelID, nwo.GetLedgerHeight(n, peer, channelID), n.Peers...) 875 } 876 877 func transferMarble(n *nwo.Network, orderer *nwo.Orderer, chaincodeName, marbleOwner string, peer *nwo.Peer) { 878 marbleOwnerBase64 := base64.StdEncoding.EncodeToString([]byte(marbleOwner)) 879 880 command := commands.ChaincodeInvoke{ 881 ChannelID: channelID, 882 Orderer: n.OrdererAddress(orderer, nwo.ListenPort), 883 Name: chaincodeName, 884 Ctor: fmt.Sprintf(`{"Args":["transferMarble"]}`), 885 Transient: fmt.Sprintf(`{"marble_owner":"%s"}`, marbleOwnerBase64), 886 PeerAddresses: []string{ 887 n.PeerAddress(peer, nwo.ListenPort), 888 }, 889 WaitForEvent: true, 890 } 891 invokeChaincode(n, peer, command) 892 nwo.WaitUntilEqualLedgerHeight(n, channelID, nwo.GetLedgerHeight(n, peer, channelID), n.Peers...) 893 } 894 895 func assertPvtdataPresencePerCollectionConfig1(n *nwo.Network, chaincodeName, marbleName string, peers ...*nwo.Peer) { 896 if len(peers) == 0 { 897 peers = n.Peers 898 } 899 for _, peer := range peers { 900 switch peer.Organization { 901 902 case "Org1": 903 assertPresentInCollectionM(n, chaincodeName, marbleName, peer) 904 assertNotPresentInCollectionMPD(n, chaincodeName, marbleName, peer) 905 906 case "Org2": 907 assertPresentInCollectionM(n, chaincodeName, marbleName, peer) 908 assertPresentInCollectionMPD(n, chaincodeName, marbleName, peer) 909 910 case "Org3": 911 assertNotPresentInCollectionM(n, chaincodeName, marbleName, peer) 912 assertPresentInCollectionMPD(n, chaincodeName, marbleName, peer) 913 } 914 } 915 } 916 917 func assertPvtdataPresencePerCollectionConfig2(n *nwo.Network, chaincodeName, marbleName string, peers ...*nwo.Peer) { 918 if len(peers) == 0 { 919 peers = n.Peers 920 } 921 for _, peer := range peers { 922 switch peer.Organization { 923 924 case "Org1": 925 assertPresentInCollectionM(n, chaincodeName, marbleName, peer) 926 assertNotPresentInCollectionMPD(n, chaincodeName, marbleName, peer) 927 928 case "Org2", "Org3": 929 assertPresentInCollectionM(n, chaincodeName, marbleName, peer) 930 assertPresentInCollectionMPD(n, chaincodeName, marbleName, peer) 931 } 932 } 933 } 934 935 // assertGetMarblesByRange asserts that 936 func assertGetMarblesByRange(n *nwo.Network, chaincodeName, marbleRange string, peer *nwo.Peer) { 937 query := fmt.Sprintf(`{"Args":["getMarblesByRange", %s]}`, marbleRange) 938 expectedMsg := `\Q[{"Key":"test-marble-0", "Record":{"docType":"marble","name":"test-marble-0","color":"blue","size":35,"owner":"tom"}},{"Key":"test-marble-1", "Record":{"docType":"marble","name":"test-marble-1","color":"blue","size":35,"owner":"tom"}}]\E` 939 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, true, peer) 940 } 941 942 // assertPresentInCollectionM asserts that the private data for given marble is present in collection 943 // 'readMarble' at the given peers 944 func assertPresentInCollectionM(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) { 945 query := fmt.Sprintf(`{"Args":["readMarble","%s"]}`, marbleName) 946 expectedMsg := fmt.Sprintf(`{"docType":"marble","name":"%s"`, marbleName) 947 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, true, peerList...) 948 } 949 950 // assertPresentInCollectionMPD asserts that the private data for given marble is present 951 // in collection 'readMarblePrivateDetails' at the given peers 952 func assertPresentInCollectionMPD(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) { 953 query := fmt.Sprintf(`{"Args":["readMarblePrivateDetails","%s"]}`, marbleName) 954 expectedMsg := fmt.Sprintf(`{"docType":"marblePrivateDetails","name":"%s"`, marbleName) 955 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, true, peerList...) 956 } 957 958 // assertNotPresentInCollectionM asserts that the private data for given marble is NOT present 959 // in collection 'readMarble' at the given peers 960 func assertNotPresentInCollectionM(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) { 961 query := fmt.Sprintf(`{"Args":["readMarble","%s"]}`, marbleName) 962 expectedMsg := "private data matching public hash version is not available" 963 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, false, peerList...) 964 } 965 966 // assertNotPresentInCollectionMPD asserts that the private data for given marble is NOT present 967 // in collection 'readMarblePrivateDetails' at the given peers 968 func assertNotPresentInCollectionMPD(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) { 969 query := fmt.Sprintf(`{"Args":["readMarblePrivateDetails","%s"]}`, marbleName) 970 expectedMsg := "private data matching public hash version is not available" 971 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, false, peerList...) 972 } 973 974 // assertDoesNotExistInCollectionM asserts that the private data for given marble 975 // does not exist in collection 'readMarble' (i.e., is never created/has been deleted/has been purged) 976 func assertDoesNotExistInCollectionM(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) { 977 query := fmt.Sprintf(`{"Args":["readMarble","%s"]}`, marbleName) 978 expectedMsg := "Marble does not exist" 979 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, false, peerList...) 980 } 981 982 // assertDoesNotExistInCollectionMPD asserts that the private data for given marble 983 // does not exist in collection 'readMarblePrivateDetails' (i.e., is never created/has been deleted/has been purged) 984 func assertDoesNotExistInCollectionMPD(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) { 985 query := fmt.Sprintf(`{"Args":["readMarblePrivateDetails","%s"]}`, marbleName) 986 expectedMsg := "Marble private details does not exist" 987 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, false, peerList...) 988 } 989 990 // assertOwnershipInCollectionM asserts that the private data for given marble is present 991 // in collection 'readMarble' at the given peers 992 func assertOwnershipInCollectionM(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) { 993 query := fmt.Sprintf(`{"Args":["readMarble","%s"]}`, marbleName) 994 expectedMsg := fmt.Sprintf(`{"docType":"marble","name":"test-marble-0","color":"blue","size":35,"owner":"jerry"}`) 995 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, true, peerList...) 996 } 997 998 // assertNoReadAccessToCollectionMPD asserts that the orgs of the given peers do not have 999 // read access to private data for the collection readMarblePrivateDetails 1000 func assertNoReadAccessToCollectionMPD(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) { 1001 query := fmt.Sprintf(`{"Args":["readMarblePrivateDetails","%s"]}`, marbleName) 1002 expectedMsg := "tx creator does not have read access permission" 1003 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, false, peerList...) 1004 } 1005 1006 func queryChaincodePerPeer(n *nwo.Network, query, chaincodeName, expectedMsg string, expectSuccess bool, peerList ...*nwo.Peer) { 1007 command := commands.ChaincodeQuery{ 1008 ChannelID: channelID, 1009 Name: chaincodeName, 1010 Ctor: query, 1011 } 1012 for _, peer := range peerList { 1013 queryChaincode(n, peer, command, expectedMsg, expectSuccess) 1014 } 1015 } 1016 1017 // assertMarblesPrivateHashM asserts that getMarbleHash is accessible from all peers that has the chaincode instantiated 1018 func assertMarblesPrivateHashM(n *nwo.Network, chaincodeName, marbleName string, expectedBytes []byte, peerList []*nwo.Peer) { 1019 query := fmt.Sprintf(`{"Args":["getMarbleHash","%s"]}`, marbleName) 1020 verifyPvtdataHash(n, query, chaincodeName, peerList, expectedBytes) 1021 } 1022 1023 // assertMarblesPrivateDetailsHashMPD asserts that getMarblePrivateDetailsHash is accessible from all peers that has the chaincode instantiated 1024 func assertMarblesPrivateDetailsHashMPD(n *nwo.Network, chaincodeName, marbleName string, expectedBytes []byte, peerList []*nwo.Peer) { 1025 query := fmt.Sprintf(`{"Args":["getMarblePrivateDetailsHash","%s"]}`, marbleName) 1026 verifyPvtdataHash(n, query, chaincodeName, peerList, expectedBytes) 1027 } 1028 1029 // verifyPvtdataHash verifies the private data hash matches the expected bytes. 1030 // Cannot reuse verifyAccess because the hash bytes are not valid utf8 causing gbytes.Say to fail. 1031 func verifyPvtdataHash(n *nwo.Network, query, chaincodeName string, peers []*nwo.Peer, expected []byte) { 1032 command := commands.ChaincodeQuery{ 1033 ChannelID: channelID, 1034 Name: chaincodeName, 1035 Ctor: query, 1036 } 1037 1038 for _, peer := range peers { 1039 sess, err := n.PeerUserSession(peer, "User1", command) 1040 Expect(err).NotTo(HaveOccurred()) 1041 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 1042 actual := sess.Buffer().Contents() 1043 // verify actual bytes contain expected bytes - cannot use equal because session may contain extra bytes 1044 Expect(bytes.Contains(actual, expected)).To(Equal(true)) 1045 } 1046 } 1047 1048 // deliverEvent contains the response and related info from a DeliverWithPrivateData call 1049 type deliverEvent struct { 1050 BlockAndPvtData *pb.BlockAndPrivateData 1051 BlockNum uint64 1052 Err error 1053 } 1054 1055 // getEventFromDeliverService send a request to DeliverWithPrivateData grpc service 1056 // and receive the response 1057 func getEventFromDeliverService(network *nwo.Network, peer *nwo.Peer, channelID string, signingIdentity msp.SigningIdentity, blockNum uint64) *deliverEvent { 1058 ctx, cancelFunc1 := context.WithTimeout(context.Background(), network.EventuallyTimeout) 1059 defer cancelFunc1() 1060 eventCh, conn := registerForDeliverEvent(ctx, network, peer, channelID, signingIdentity, blockNum) 1061 defer conn.Close() 1062 event := &deliverEvent{} 1063 Eventually(eventCh, network.EventuallyTimeout).Should(Receive(event)) 1064 Expect(event.Err).NotTo(HaveOccurred()) 1065 return event 1066 } 1067 1068 func registerForDeliverEvent( 1069 ctx context.Context, 1070 network *nwo.Network, 1071 peer *nwo.Peer, 1072 channelID string, 1073 signingIdentity msp.SigningIdentity, 1074 blockNum uint64, 1075 ) (<-chan deliverEvent, *grpc.ClientConn) { 1076 // create a comm.GRPCClient 1077 tlsRootCertFile := filepath.Join(network.PeerLocalTLSDir(peer), "ca.crt") 1078 caPEM, err := ioutil.ReadFile(tlsRootCertFile) 1079 Expect(err).NotTo(HaveOccurred()) 1080 clientConfig := comm.ClientConfig{Timeout: 10 * time.Second} 1081 clientConfig.SecOpts = comm.SecureOptions{ 1082 UseTLS: true, 1083 ServerRootCAs: [][]byte{caPEM}, 1084 RequireClientCert: false, 1085 } 1086 grpcClient, err := comm.NewGRPCClient(clientConfig) 1087 Expect(err).NotTo(HaveOccurred()) 1088 // create a client for DeliverWithPrivateData 1089 address := network.PeerAddress(peer, nwo.ListenPort) 1090 conn, err := grpcClient.NewConnection(address) 1091 Expect(err).NotTo(HaveOccurred()) 1092 dp, err := pb.NewDeliverClient(conn).DeliverWithPrivateData(ctx) 1093 Expect(err).NotTo(HaveOccurred()) 1094 // send a deliver request 1095 envelope, err := createDeliverEnvelope(channelID, signingIdentity, blockNum) 1096 Expect(err).NotTo(HaveOccurred()) 1097 err = dp.Send(envelope) 1098 dp.CloseSend() 1099 Expect(err).NotTo(HaveOccurred()) 1100 // create a goroutine to receive the response in a separate thread 1101 eventCh := make(chan deliverEvent, 1) 1102 go receiveDeliverResponse(dp, address, eventCh) 1103 1104 return eventCh, conn 1105 } 1106 1107 func getSigningIdentity(network *nwo.Network, org, user, mspID, mspType string) msp.SigningIdentity { 1108 peerForOrg := network.Peer(org, "peer0") 1109 mspConfigPath := network.PeerUserMSPDir(peerForOrg, user) 1110 mspInstance, err := loadLocalMSPAt(mspConfigPath, mspID, mspType) 1111 Expect(err).NotTo(HaveOccurred()) 1112 1113 signingIdentity, err := mspInstance.GetDefaultSigningIdentity() 1114 Expect(err).NotTo(HaveOccurred()) 1115 return signingIdentity 1116 } 1117 1118 // loadLocalMSPAt loads an MSP whose configuration is stored at 'dir', and whose 1119 // id and type are the passed as arguments. 1120 func loadLocalMSPAt(dir, id, mspType string) (msp.MSP, error) { 1121 if mspType != "bccsp" { 1122 return nil, errors.Errorf("invalid msp type, expected 'bccsp', got %s", mspType) 1123 } 1124 conf, err := msp.GetLocalMspConfig(dir, nil, id) 1125 if err != nil { 1126 return nil, err 1127 } 1128 ks, err := sw.NewFileBasedKeyStore(nil, filepath.Join(dir, "keystore"), true) 1129 if err != nil { 1130 return nil, err 1131 } 1132 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 1133 if err != nil { 1134 return nil, err 1135 } 1136 thisMSP, err := msp.NewBccspMspWithKeyStore(msp.MSPv1_0, ks, cryptoProvider) 1137 if err != nil { 1138 return nil, err 1139 } 1140 err = thisMSP.Setup(conf) 1141 if err != nil { 1142 return nil, err 1143 } 1144 return thisMSP, nil 1145 } 1146 1147 // receiveDeliverResponse expectes to receive the BlockAndPrivateData response for the requested block. 1148 func receiveDeliverResponse(dp pb.Deliver_DeliverWithPrivateDataClient, address string, eventCh chan<- deliverEvent) error { 1149 event := deliverEvent{} 1150 1151 resp, err := dp.Recv() 1152 if err != nil { 1153 event.Err = errors.WithMessagef(err, "error receiving deliver response from peer %s\n", address) 1154 } 1155 switch r := resp.Type.(type) { 1156 case *pb.DeliverResponse_BlockAndPrivateData: 1157 event.BlockAndPvtData = r.BlockAndPrivateData 1158 event.BlockNum = r.BlockAndPrivateData.Block.Header.Number 1159 case *pb.DeliverResponse_Status: 1160 event.Err = errors.Errorf("deliver completed with status (%s) before DeliverResponse_BlockAndPrivateData received from peer %s", r.Status, address) 1161 default: 1162 event.Err = errors.Errorf("received unexpected response type (%T) from peer %s", r, address) 1163 } 1164 1165 select { 1166 case eventCh <- event: 1167 default: 1168 } 1169 return nil 1170 } 1171 1172 // createDeliverEnvelope creates a deliver request based on the block number. 1173 // blockNum=0 means newest block 1174 func createDeliverEnvelope(channelID string, signingIdentity msp.SigningIdentity, blockNum uint64) (*cb.Envelope, error) { 1175 creator, err := signingIdentity.Serialize() 1176 if err != nil { 1177 return nil, err 1178 } 1179 header, err := createHeader(cb.HeaderType_DELIVER_SEEK_INFO, channelID, creator) 1180 if err != nil { 1181 return nil, err 1182 } 1183 1184 // if blockNum is not greater than 0, seek the newest block 1185 var seekInfo *ab.SeekInfo 1186 if blockNum > 0 { 1187 seekInfo = &ab.SeekInfo{ 1188 Start: &ab.SeekPosition{ 1189 Type: &ab.SeekPosition_Specified{ 1190 Specified: &ab.SeekSpecified{Number: blockNum}, 1191 }, 1192 }, 1193 Stop: &ab.SeekPosition{ 1194 Type: &ab.SeekPosition_Specified{ 1195 Specified: &ab.SeekSpecified{Number: blockNum}, 1196 }, 1197 }, 1198 } 1199 } else { 1200 seekInfo = &ab.SeekInfo{ 1201 Start: &ab.SeekPosition{ 1202 Type: &ab.SeekPosition_Newest{ 1203 Newest: &ab.SeekNewest{}, 1204 }, 1205 }, 1206 Stop: &ab.SeekPosition{ 1207 Type: &ab.SeekPosition_Newest{ 1208 Newest: &ab.SeekNewest{}, 1209 }, 1210 }, 1211 } 1212 } 1213 1214 // create the envelope 1215 raw := protoutil.MarshalOrPanic(seekInfo) 1216 payload := &cb.Payload{ 1217 Header: header, 1218 Data: raw, 1219 } 1220 payloadBytes := protoutil.MarshalOrPanic(payload) 1221 signature, err := signingIdentity.Sign(payloadBytes) 1222 if err != nil { 1223 return nil, err 1224 } 1225 return &cb.Envelope{ 1226 Payload: payloadBytes, 1227 Signature: signature, 1228 }, nil 1229 } 1230 1231 func createHeader(txType cb.HeaderType, channelID string, creator []byte) (*cb.Header, error) { 1232 ts, err := ptypes.TimestampProto(time.Now()) 1233 if err != nil { 1234 return nil, err 1235 } 1236 nonce, err := crypto.GetRandomNonce() 1237 if err != nil { 1238 return nil, err 1239 } 1240 chdr := &cb.ChannelHeader{ 1241 Type: int32(txType), 1242 ChannelId: channelID, 1243 TxId: protoutil.ComputeTxID(nonce, creator), 1244 Epoch: 0, 1245 Timestamp: ts, 1246 } 1247 chdrBytes := protoutil.MarshalOrPanic(chdr) 1248 1249 shdr := &cb.SignatureHeader{ 1250 Creator: creator, 1251 Nonce: nonce, 1252 } 1253 shdrBytes := protoutil.MarshalOrPanic(shdr) 1254 header := &cb.Header{ 1255 ChannelHeader: chdrBytes, 1256 SignatureHeader: shdrBytes, 1257 } 1258 return header, nil 1259 } 1260 1261 // verify collection names and pvtdataMap match expectedKVWritesMap 1262 func assertPrivateDataAsExpected(pvtdataMap map[uint64]*rwset.TxPvtReadWriteSet, expectedKVWritesMap map[string]map[string][]byte) { 1263 // In the test, each block has only 1 tx, so txSeqInBlock is 0 1264 txPvtRwset := pvtdataMap[uint64(0)] 1265 Expect(txPvtRwset.NsPvtRwset).To(HaveLen(1)) 1266 Expect(txPvtRwset.NsPvtRwset[0].Namespace).To(Equal("marblesp")) 1267 Expect(txPvtRwset.NsPvtRwset[0].CollectionPvtRwset).To(HaveLen(len(expectedKVWritesMap))) 1268 1269 // verify the collections returned in private data have expected collection names and kvRwset.Writes 1270 for _, col := range txPvtRwset.NsPvtRwset[0].CollectionPvtRwset { 1271 Expect(expectedKVWritesMap).To(HaveKey(col.CollectionName)) 1272 expectedKvWrites := expectedKVWritesMap[col.CollectionName] 1273 kvRwset := kvrwset.KVRWSet{} 1274 err := proto.Unmarshal(col.GetRwset(), &kvRwset) 1275 Expect(err).NotTo(HaveOccurred()) 1276 Expect(kvRwset.Writes).To(HaveLen(len(expectedKvWrites))) 1277 for _, kvWrite := range kvRwset.Writes { 1278 Expect(expectedKvWrites).To(HaveKey(kvWrite.Key)) 1279 Expect(kvWrite.Value).To(Equal(expectedKvWrites[kvWrite.Key])) 1280 } 1281 } 1282 } 1283 1284 func getValueForCollectionMarbles(marbleName, color, owner string, size int) []byte { 1285 marbleJSONasString := `{"docType":"marble","name":"` + marbleName + `","color":"` + color + `","size":` + strconv.Itoa(size) + `,"owner":"` + owner + `"}` 1286 return []byte(marbleJSONasString) 1287 } 1288 1289 func getValueForCollectionMarblePrivateDetails(marbleName string, price int) []byte { 1290 marbleJSONasString := `{"docType":"marblePrivateDetails","name":"` + marbleName + `","price":` + strconv.Itoa(price) + `}` 1291 return []byte(marbleJSONasString) 1292 }