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