github.com/kaituanwang/hyperledger@v2.0.1+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 "encoding/json" 14 "fmt" 15 "io/ioutil" 16 "os" 17 "os/exec" 18 "path/filepath" 19 "strconv" 20 "strings" 21 "syscall" 22 "time" 23 24 "github.com/golang/protobuf/proto" 25 "github.com/golang/protobuf/ptypes" 26 . "github.com/onsi/ginkgo" 27 . "github.com/onsi/gomega" 28 "github.com/pkg/errors" 29 "google.golang.org/grpc" 30 31 docker "github.com/fsouza/go-dockerclient" 32 cb "github.com/hyperledger/fabric-protos-go/common" 33 "github.com/hyperledger/fabric-protos-go/ledger/rwset" 34 "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 35 mspp "github.com/hyperledger/fabric-protos-go/msp" 36 ab "github.com/hyperledger/fabric-protos-go/orderer" 37 pb "github.com/hyperledger/fabric-protos-go/peer" 38 "github.com/hyperledger/fabric/bccsp/sw" 39 "github.com/hyperledger/fabric/common/crypto" 40 "github.com/hyperledger/fabric/core/comm" 41 "github.com/hyperledger/fabric/core/ledger/util" 42 "github.com/hyperledger/fabric/integration/nwo" 43 "github.com/hyperledger/fabric/integration/nwo/commands" 44 "github.com/hyperledger/fabric/msp" 45 "github.com/hyperledger/fabric/protoutil" 46 "github.com/onsi/gomega/gbytes" 47 "github.com/onsi/gomega/gexec" 48 "github.com/tedsuo/ifrit" 49 "github.com/tedsuo/ifrit/grouper" 50 ) 51 52 // The chaincode used in these tests has two collections defined: 53 // collectionMarbles and collectionMarblePrivateDetails 54 // when calling QueryChaincode with first arg "readMarble", it will query collectionMarbles 55 // when calling QueryChaincode with first arg "readMarblePrivateDetails", it will query collectionMarblePrivateDetails 56 57 const channelID = "testchannel" 58 59 var _ bool = Describe("PrivateData", func() { 60 var ( 61 network *nwo.Network 62 process ifrit.Process 63 64 orderer *nwo.Orderer 65 ) 66 67 AfterEach(func() { 68 testCleanup(network, process) 69 }) 70 71 Describe("Dissemination when pulling is disabled", func() { 72 BeforeEach(func() { 73 By("setting up the network") 74 network = initThreeOrgsSetup(true) 75 76 By("setting the pull retry threshold to 0 on all peers") 77 // set pull retry threshold to 0 78 for _, p := range network.Peers { 79 core := network.ReadPeerConfig(p) 80 core.Peer.Gossip.PvtData.PullRetryThreshold = 0 81 network.WritePeerConfig(p, core) 82 } 83 84 By("starting the network") 85 process, orderer = startNetwork(network) 86 }) 87 88 It("disseminates private data per collections_config1 (positive test) and collections_config8 (negative test)", func() { 89 90 By("deploying legacy chaincode and adding marble1") 91 testChaincode := chaincode{ 92 Chaincode: nwo.Chaincode{ 93 Name: "marblesp", 94 Version: "1.0", 95 Path: "github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd", 96 Ctor: `{"Args":["init"]}`, 97 Policy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`, 98 // collections_config1.json defines the access as follows: 99 // 1. collectionMarbles - Org1, Org2 have access to this collection 100 // 2. collectionMarblePrivateDetails - Org2 and Org3 have access to this collection 101 CollectionsConfig: collectionConfig("collections_config1.json"), 102 }, 103 isLegacy: true, 104 } 105 deployChaincode(network, orderer, testChaincode) 106 addMarble(network, orderer, testChaincode.Name, 107 `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`, 108 network.Peer("Org1", "peer0"), 109 ) 110 111 assertPvtdataPresencePerCollectionConfig1(network, testChaincode.Name, "marble1") 112 113 By("deploying chaincode with RequiredPeerCount greater than number of peers, endorsement will fail") 114 testChaincodeHighRequiredPeerCount := chaincode{ 115 Chaincode: nwo.Chaincode{ 116 Name: "marblespHighRequiredPeerCount", 117 Version: "1.0", 118 Path: "github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd", 119 Ctor: `{"Args":["init"]}`, 120 Policy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`, 121 CollectionsConfig: collectionConfig("collections_config8_high_requiredPeerCount.json"), 122 }, 123 isLegacy: true, 124 } 125 deployChaincode(network, orderer, testChaincodeHighRequiredPeerCount) 126 127 // attempt to add a marble with insufficient dissemination to meet RequiredPeerCount 128 marbleDetailsBase64 := base64.StdEncoding.EncodeToString([]byte(`{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`)) 129 130 command := commands.ChaincodeInvoke{ 131 ChannelID: channelID, 132 Orderer: network.OrdererAddress(orderer, nwo.ListenPort), 133 Name: testChaincodeHighRequiredPeerCount.Name, 134 Ctor: fmt.Sprintf(`{"Args":["initMarble"]}`), 135 Transient: fmt.Sprintf(`{"marble":"%s"}`, marbleDetailsBase64), 136 PeerAddresses: []string{ 137 network.PeerAddress(network.Peer("Org1", "peer0"), nwo.ListenPort), 138 }, 139 WaitForEvent: true, 140 } 141 expectedErrMsg := `Error: endorsement failure during invoke. response: status:500 message:"error in simulation: failed to distribute private collection` 142 invokeChaincodeExpectErr(network, network.Peer("Org1", "peer0"), command, expectedErrMsg) 143 }) 144 145 When("collection config does not have maxPeerCount or requiredPeerCount", func() { 146 It("disseminates private data per collections_config7 with default maxPeerCount and requiredPeerCount", func() { 147 By("deploying legacy chaincode and adding marble1") 148 testChaincode := chaincode{ 149 Chaincode: 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_config7.json"), 159 }, 160 isLegacy: true, 161 } 162 deployChaincode(network, orderer, testChaincode) 163 peer := network.Peer("Org1", "peer0") 164 addMarble(network, orderer, testChaincode.Name, 165 `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`, 166 peer, 167 ) 168 169 assertPvtdataPresencePerCollectionConfig7(network, testChaincode.Name, "marble1", peer) 170 }) 171 }) 172 }) 173 174 Describe("Pvtdata behavior when a peer with new certs joins the network", func() { 175 var ( 176 peerProcesses map[string]ifrit.Process 177 ) 178 179 BeforeEach(func() { 180 By("setting up the network") 181 network = initThreeOrgsSetup(true) 182 183 By("starting the network") 184 peerProcesses = make(map[string]ifrit.Process) 185 network.Bootstrap() 186 187 members := grouper.Members{ 188 {Name: "brokers", Runner: network.BrokerGroupRunner()}, 189 {Name: "orderers", Runner: network.OrdererGroupRunner()}, 190 } 191 networkRunner := grouper.NewOrdered(syscall.SIGTERM, members) 192 process = ifrit.Invoke(networkRunner) 193 Eventually(process.Ready()).Should(BeClosed()) 194 195 org1peer0 := network.Peer("Org1", "peer0") 196 org2peer0 := network.Peer("Org2", "peer0") 197 org3peer0 := network.Peer("Org3", "peer0") 198 199 testPeers := []*nwo.Peer{org1peer0, org2peer0, org3peer0} 200 for _, peer := range testPeers { 201 pr := network.PeerRunner(peer) 202 p := ifrit.Invoke(pr) 203 peerProcesses[peer.ID()] = p 204 Eventually(p.Ready(), network.EventuallyTimeout).Should(BeClosed()) 205 } 206 207 orderer = network.Orderer("orderer") 208 network.CreateAndJoinChannel(orderer, channelID) 209 network.UpdateChannelAnchors(orderer, channelID) 210 211 By("verifying membership") 212 network.VerifyMembership(network.Peers, channelID) 213 214 By("installing and instantiating chaincode on all peers") 215 testChaincode := chaincode{ 216 Chaincode: nwo.Chaincode{ 217 Name: "marblesp", 218 Version: "1.0", 219 Path: "github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd", 220 Ctor: `{"Args":["init"]}`, 221 Policy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`, 222 CollectionsConfig: filepath.Join("testdata", "collection_configs", "collections_config1.json"), 223 }, 224 isLegacy: true, 225 } 226 deployChaincode(network, orderer, testChaincode) 227 228 By("adding marble1 with an org 1 peer as endorser") 229 peer := network.Peer("Org1", "peer0") 230 marbleDetails := `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}` 231 addMarble(network, orderer, testChaincode.Name, marbleDetails, peer) 232 233 By("waiting for block to propagate") 234 nwo.WaitUntilEqualLedgerHeight(network, channelID, nwo.GetLedgerHeight(network, network.Peers[0], channelID), network.Peers...) 235 236 org2Peer1 := &nwo.Peer{ 237 Name: "peer1", 238 Organization: "Org2", 239 Channels: []*nwo.PeerChannel{}, // Don't set channels here so the UpdateConfig call doesn't try to fetch blocks for org2Peer1 with the default Admin user 240 } 241 network.Peers = append(network.Peers, org2Peer1) 242 }) 243 244 AfterEach(func() { 245 for _, peerProcess := range peerProcesses { 246 if peerProcess != nil { 247 peerProcess.Signal(syscall.SIGTERM) 248 Eventually(peerProcess.Wait(), network.EventuallyTimeout).Should(Receive()) 249 } 250 } 251 }) 252 253 It("verifies private data is pulled when joining a new peer with new certs", func() { 254 By("generating new certs for org2Peer1") 255 org2Peer1 := network.Peer("Org2", "peer1") 256 tempCryptoDir, err := ioutil.TempDir("", "crypto") 257 Expect(err).NotTo(HaveOccurred()) 258 defer os.RemoveAll(tempCryptoDir) 259 generateNewCertsForPeer(network, tempCryptoDir, org2Peer1) 260 261 By("updating the channel config with the new certs") 262 updateConfigWithNewCertsForPeer(network, tempCryptoDir, orderer, org2Peer1) 263 264 By("starting the peer1.org2 process") 265 pr := network.PeerRunner(org2Peer1) 266 p := ifrit.Invoke(pr) 267 peerProcesses[org2Peer1.ID()] = p 268 Eventually(p.Ready(), network.EventuallyTimeout).Should(BeClosed()) 269 270 By("joining peer1.org2 to the channel with its Admin2 user") 271 tempFile, err := ioutil.TempFile("", "genesis-block") 272 Expect(err).NotTo(HaveOccurred()) 273 tempFile.Close() 274 defer os.Remove(tempFile.Name()) 275 276 sess, err := network.PeerUserSession(org2Peer1, "Admin2", commands.ChannelFetch{ 277 Block: "0", 278 ChannelID: channelID, 279 Orderer: network.OrdererAddress(orderer, nwo.ListenPort), 280 OutputFile: tempFile.Name(), 281 }) 282 Expect(err).NotTo(HaveOccurred()) 283 Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0)) 284 285 sess, err = network.PeerUserSession(org2Peer1, "Admin2", commands.ChannelJoin{ 286 BlockPath: tempFile.Name(), 287 }) 288 Expect(err).NotTo(HaveOccurred()) 289 Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0)) 290 291 org2Peer1.Channels = append(org2Peer1.Channels, &nwo.PeerChannel{Name: channelID, Anchor: false}) 292 293 ledgerHeight := nwo.GetLedgerHeight(network, network.Peers[0], channelID) 294 295 By("fetching latest blocks to peer1.org2") 296 // Retry channel fetch until peer1.org2 retrieves latest block 297 // Channel Fetch will repeatedly fail until org2Peer1 commits the config update adding its new cert 298 Eventually(fetchBlocksForPeer(network, org2Peer1, "Admin2"), network.EventuallyTimeout).Should(gbytes.Say(fmt.Sprintf("Received block: %d", ledgerHeight-1))) 299 300 By("installing chaincode on peer1.org2 to be able to query it") 301 chaincode := nwo.Chaincode{ 302 Name: "marblesp", 303 Version: "1.0", 304 Path: "github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd", 305 Ctor: `{"Args":["init"]}`, 306 Policy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`, 307 CollectionsConfig: filepath.Join("testdata", "collection_configs", "collections_config1.json")} 308 309 sess, err = network.PeerUserSession(org2Peer1, "Admin2", commands.ChaincodeInstallLegacy{ 310 Name: chaincode.Name, 311 Version: chaincode.Version, 312 Path: chaincode.Path, 313 Lang: chaincode.Lang, 314 PackageFile: chaincode.PackageFile, 315 ClientAuth: network.ClientAuthRequired, 316 }) 317 Expect(err).NotTo(HaveOccurred()) 318 Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0)) 319 320 sess, err = network.PeerUserSession(org2Peer1, "Admin2", commands.ChaincodeListInstalledLegacy{ 321 ClientAuth: network.ClientAuthRequired, 322 }) 323 Expect(err).NotTo(HaveOccurred()) 324 Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0)) 325 Expect(sess).To(gbytes.Say(fmt.Sprintf("Name: %s, Version: %s,", chaincode.Name, chaincode.Version))) 326 327 expectedPeers := []*nwo.Peer{ 328 network.Peer("Org1", "peer0"), 329 network.Peer("Org2", "peer0"), 330 network.Peer("Org2", "peer1"), 331 network.Peer("Org3", "peer0"), 332 } 333 334 By("making sure all peers have the same ledger height") 335 for _, peer := range expectedPeers { 336 Eventually(func() int { 337 var ( 338 sess *gexec.Session 339 err error 340 ) 341 if peer.ID() == "Org2.peer1" { 342 // use Admin2 user for peer1.org2 343 sess, err = network.PeerUserSession(peer, "Admin2", commands.ChannelInfo{ 344 ChannelID: channelID, 345 }) 346 Expect(err).NotTo(HaveOccurred()) 347 Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0)) 348 channelInfoStr := strings.TrimPrefix(string(sess.Buffer().Contents()[:]), "Blockchain info:") 349 var channelInfo = cb.BlockchainInfo{} 350 err = json.Unmarshal([]byte(channelInfoStr), &channelInfo) 351 Expect(err).NotTo(HaveOccurred()) 352 return int(channelInfo.Height) 353 } 354 355 // If not Org2.peer1, just use regular getLedgerHeight call with User1 356 return nwo.GetLedgerHeight(network, peer, channelID) 357 }(), network.EventuallyTimeout).Should(Equal(ledgerHeight)) 358 } 359 360 By("verifying membership") 361 expectedDiscoveredPeers := make([]nwo.DiscoveredPeer, 0, len(expectedPeers)) 362 for _, peer := range expectedPeers { 363 expectedDiscoveredPeers = append(expectedDiscoveredPeers, network.DiscoveredPeer(peer, "_lifecycle", "marblesp")) 364 } 365 for _, peer := range expectedPeers { 366 By(fmt.Sprintf("checking expected peers for peer: %s", peer.ID())) 367 if peer.ID() == "Org2.peer1" { 368 // use Admin2 user for peer1.org2 369 Eventually(nwo.DiscoverPeers(network, peer, "Admin2", channelID), network.EventuallyTimeout).Should(ConsistOf(expectedDiscoveredPeers)) 370 } else { 371 Eventually(nwo.DiscoverPeers(network, peer, "User1", channelID), network.EventuallyTimeout).Should(ConsistOf(expectedDiscoveredPeers)) 372 } 373 } 374 375 By("verifying peer1.org2 got the private data that was created historically") 376 sess, err = network.PeerUserSession(org2Peer1, "Admin2", commands.ChaincodeQuery{ 377 ChannelID: channelID, 378 Name: "marblesp", 379 Ctor: `{"Args":["readMarble","marble1"]}`, 380 }) 381 Expect(err).NotTo(HaveOccurred()) 382 Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0)) 383 Expect(sess).To(gbytes.Say(`{"docType":"marble","name":"marble1","color":"blue","size":35,"owner":"tom"}`)) 384 385 sess, err = network.PeerUserSession(org2Peer1, "Admin2", commands.ChaincodeQuery{ 386 ChannelID: channelID, 387 Name: "marblesp", 388 Ctor: `{"Args":["readMarblePrivateDetails","marble1"]}`, 389 }) 390 Expect(err).NotTo(HaveOccurred()) 391 Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0)) 392 Expect(sess).To(gbytes.Say(`{"docType":"marblePrivateDetails","name":"marble1","price":99}`)) 393 }) 394 }) 395 396 Describe("Pvtdata behavior with untouched peer configs", func() { 397 var ( 398 legacyChaincode nwo.Chaincode 399 newLifecycleChaincode nwo.Chaincode 400 testChaincode chaincode 401 402 org1Peer1, org2Peer1 *nwo.Peer 403 ) 404 405 BeforeEach(func() { 406 By("setting up the network") 407 network = initThreeOrgsSetup(true) 408 legacyChaincode = nwo.Chaincode{ 409 Name: "marblesp", 410 Version: "1.0", 411 Path: "github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd", 412 Ctor: `{"Args":["init"]}`, 413 Policy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`, 414 // collections_config1.json defines the access as follows: 415 // 1. collectionMarbles - Org1, Org2 have access to this collection 416 // 2. collectionMarblePrivateDetails - Org2 and Org3 have access to this collection 417 CollectionsConfig: collectionConfig("collections_config1.json"), 418 } 419 420 newLifecycleChaincode = nwo.Chaincode{ 421 Name: "marblesp", 422 Version: "1.0", 423 Path: components.Build("github.com/hyperledger/fabric/integration/chaincode/marbles_private/cmd"), 424 Lang: "binary", 425 PackageFile: filepath.Join(network.RootDir, "marbles-pvtdata.tar.gz"), 426 Label: "marbles-private-20", 427 SignaturePolicy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member')`, 428 CollectionsConfig: collectionConfig("collections_config1.json"), 429 Sequence: "1", 430 } 431 org1Peer1 = &nwo.Peer{ 432 Name: "peer1", 433 Organization: "Org1", 434 Channels: []*nwo.PeerChannel{ 435 {Name: channelID}, 436 }, 437 } 438 org2Peer1 = &nwo.Peer{ 439 Name: "peer1", 440 Organization: "Org2", 441 Channels: []*nwo.PeerChannel{ 442 {Name: channelID}, 443 }, 444 } 445 446 process, orderer = startNetwork(network) 447 }) 448 449 Describe("Reconciliation and pulling", func() { 450 var newPeerProcess ifrit.Process 451 452 BeforeEach(func() { 453 By("deploying legacy chaincode and adding marble1") 454 testChaincode = chaincode{ 455 Chaincode: legacyChaincode, 456 isLegacy: true, 457 } 458 deployChaincode(network, orderer, testChaincode) 459 addMarble(network, orderer, testChaincode.Name, 460 `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`, 461 network.Peer("Org1", "peer0"), 462 ) 463 }) 464 465 AfterEach(func() { 466 if newPeerProcess != nil { 467 newPeerProcess.Signal(syscall.SIGTERM) 468 Eventually(newPeerProcess.Wait(), network.EventuallyTimeout).Should(Receive()) 469 } 470 }) 471 472 assertReconcileBehavior := func() { 473 It("disseminates private data per collections_config1", func() { 474 assertPvtdataPresencePerCollectionConfig1(network, testChaincode.Name, "marble1") 475 }) 476 477 When("org3 is added to collectionMarbles via chaincode upgrade with collections_config2", func() { 478 It("distributes and allows access to newly added private data per collections_config2", func() { 479 By("upgrading the chaincode and adding marble2") 480 // collections_config2.json defines the access as follows: 481 // 1. collectionMarbles - Org1, Org2 and Org3 have access to this collection 482 // 2. collectionMarblePrivateDetails - Org2 and Org3 have access to this collection 483 // the change from collections_config1 - org3 was added to collectionMarbles 484 testChaincode.Version = "1.1" 485 testChaincode.CollectionsConfig = collectionConfig("collections_config2.json") 486 if !testChaincode.isLegacy { 487 testChaincode.Sequence = "2" 488 } 489 upgradeChaincode(network, orderer, testChaincode) 490 addMarble(network, orderer, testChaincode.Name, 491 `{"name":"marble2", "color":"yellow", "size":53, "owner":"jerry", "price":22}`, 492 network.Peer("Org2", "peer0"), 493 ) 494 495 assertPvtdataPresencePerCollectionConfig2(network, testChaincode.Name, "marble2") 496 }) 497 }) 498 499 When("a new peer in org1 joins the channel", func() { 500 It("causes the new peer to pull the existing private data only for collectionMarbles", func() { 501 By("adding a new peer") 502 newPeerProcess = addPeer(network, orderer, org1Peer1) 503 installChaincode(network, testChaincode, org1Peer1) 504 network.VerifyMembership(network.Peers, channelID, "marblesp") 505 506 assertPvtdataPresencePerCollectionConfig1(network, testChaincode.Name, "marble1", org1Peer1) 507 }) 508 }) 509 } 510 511 When("chaincode in legacy lifecycle", func() { 512 assertReconcileBehavior() 513 }) 514 515 When("chaincode is migrated from legacy to new lifecycle with same collection config", func() { 516 BeforeEach(func() { 517 testChaincode = chaincode{ 518 Chaincode: newLifecycleChaincode, 519 isLegacy: false, 520 } 521 nwo.EnableCapabilities(network, channelID, "Application", "V2_0", orderer, network.Peers...) 522 upgradeChaincode(network, orderer, testChaincode) 523 }) 524 assertReconcileBehavior() 525 }) 526 }) 527 528 Describe("BlockToLive", func() { 529 var newPeerProcess ifrit.Process 530 531 AfterEach(func() { 532 if newPeerProcess != nil { 533 newPeerProcess.Signal(syscall.SIGTERM) 534 Eventually(newPeerProcess.Wait(), network.EventuallyTimeout).Should(Receive()) 535 } 536 }) 537 538 assertBlockToLiveBehavior := func() { 539 eligiblePeer := network.Peer("Org2", "peer0") 540 ccName := testChaincode.Name 541 By("adding three blocks") 542 for i := 0; i < 3; i++ { 543 addMarble(network, orderer, ccName, fmt.Sprintf(`{"name":"test-marble-%d", "color":"blue", "size":35, "owner":"tom", "price":99}`, i), eligiblePeer) 544 } 545 546 By("verifying that marble1 still not purged in collection MarblesPD") 547 assertPresentInCollectionMPD(network, ccName, "marble1", eligiblePeer) 548 549 By("adding one more block") 550 addMarble(network, orderer, ccName, `{"name":"fun-marble-3", "color":"blue", "size":35, "owner":"tom", "price":99}`, eligiblePeer) 551 552 By("verifying that marble1 purged in collection MarblesPD") 553 assertDoesNotExistInCollectionMPD(network, ccName, "marble1", eligiblePeer) 554 555 By("verifying that marble1 still not purged in collection Marbles") 556 assertPresentInCollectionM(network, ccName, "marble1", eligiblePeer) 557 558 By("adding new peer that is eligible to receive data") 559 newPeerProcess = addPeer(network, orderer, org2Peer1) 560 installChaincode(network, testChaincode, org2Peer1) 561 network.VerifyMembership(network.Peers, channelID, ccName) 562 assertPresentInCollectionM(network, ccName, "marble1", org2Peer1) 563 assertDoesNotExistInCollectionMPD(network, ccName, "marble1", org2Peer1) 564 } 565 566 When("chaincode in legacy lifecycle", func() { 567 It("purges private data after BTL and causes new peer not to pull the purged private data", func() { 568 By("deploying legacy chaincode and adding marble1") 569 testChaincode = chaincode{ 570 Chaincode: legacyChaincode, 571 isLegacy: true, 572 } 573 574 testChaincode.CollectionsConfig = collectionConfig("short_btl_config.json") 575 deployChaincode(network, orderer, testChaincode) 576 addMarble(network, orderer, testChaincode.Name, `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`, network.Peer("Org2", "peer0")) 577 578 assertBlockToLiveBehavior() 579 }) 580 }) 581 582 When("chaincode in new lifecycle", func() { 583 It("purges private data after BTL and causes new peer not to pull the purged private data", func() { 584 By("deploying new lifecycle chaincode and adding marble1") 585 testChaincode = chaincode{ 586 Chaincode: newLifecycleChaincode, 587 isLegacy: false, 588 } 589 nwo.EnableCapabilities(network, channelID, "Application", "V2_0", orderer, network.Peers...) 590 591 testChaincode.CollectionsConfig = collectionConfig("short_btl_config.json") 592 deployChaincode(network, orderer, testChaincode) 593 addMarble(network, orderer, testChaincode.Name, `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`, network.Peer("Org2", "peer0")) 594 595 assertBlockToLiveBehavior() 596 }) 597 }) 598 }) 599 600 Describe("Org removal from collection", func() { 601 assertOrgRemovalBehavior := func() { 602 By("upgrading chaincode to remove org3 from collectionMarbles") 603 testChaincode.CollectionsConfig = collectionConfig("collections_config1.json") 604 testChaincode.Version = "1.1" 605 if !testChaincode.isLegacy { 606 testChaincode.Sequence = "2" 607 } 608 upgradeChaincode(network, orderer, testChaincode) 609 addMarble(network, orderer, testChaincode.Name, `{"name":"marble2", "color":"yellow", "size":53, "owner":"jerry", "price":22}`, network.Peer("Org2", "peer0")) 610 assertPvtdataPresencePerCollectionConfig1(network, testChaincode.Name, "marble2") 611 } 612 613 When("chaincode in legacy lifecycle", func() { 614 It("causes removed org not to get new data", func() { 615 By("deploying legacy chaincode and adding marble1") 616 testChaincode = chaincode{ 617 Chaincode: legacyChaincode, 618 isLegacy: true, 619 } 620 testChaincode.CollectionsConfig = collectionConfig("collections_config2.json") 621 deployChaincode(network, orderer, testChaincode) 622 addMarble(network, orderer, testChaincode.Name, `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`, network.Peer("Org2", "peer0")) 623 assertPvtdataPresencePerCollectionConfig2(network, testChaincode.Name, "marble1") 624 625 assertOrgRemovalBehavior() 626 }) 627 }) 628 629 When("chaincode in new lifecycle", func() { 630 It("causes removed org not to get new data", func() { 631 By("deploying new lifecycle chaincode and adding marble1") 632 testChaincode = chaincode{ 633 Chaincode: newLifecycleChaincode, 634 isLegacy: false, 635 } 636 nwo.EnableCapabilities(network, channelID, "Application", "V2_0", orderer, network.Peers...) 637 testChaincode.CollectionsConfig = collectionConfig("collections_config2.json") 638 deployChaincode(network, orderer, testChaincode) 639 addMarble(network, orderer, testChaincode.Name, `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`, network.Peer("Org2", "peer0")) 640 assertPvtdataPresencePerCollectionConfig2(network, testChaincode.Name, "marble1") 641 642 assertOrgRemovalBehavior() 643 }) 644 }) 645 }) 646 647 When("migrating a chaincode from legacy lifecycle to new lifecycle", func() { 648 It("performs check against collection config from legacy lifecycle", func() { 649 By("deploying legacy chaincode") 650 testChaincode = chaincode{ 651 Chaincode: legacyChaincode, 652 isLegacy: true, 653 } 654 deployChaincode(network, orderer, testChaincode) 655 nwo.EnableCapabilities(network, channelID, "Application", "V2_0", orderer, network.Peers...) 656 657 newLifecycleChaincode.CollectionsConfig = collectionConfig("short_btl_config.json") 658 newLifecycleChaincode.PackageID = "test-package-id" 659 660 approveChaincodeForMyOrgExpectErr( 661 network, 662 orderer, 663 newLifecycleChaincode, 664 `the BlockToLive in an existing collection \[collectionMarblePrivateDetails\] modified. Existing value \[1000000\]`, 665 network.Peer("Org2", "peer0")) 666 }) 667 }) 668 669 Describe("marble APIs invocation and private data delivery", func() { 670 // call marble APIs: getMarblesByRange, transferMarble, delete, getMarbleHash, getMarblePrivateDetailsHash and verify ACL Behavior 671 assertMarbleAPIs := func() { 672 eligiblePeer := network.Peer("Org2", "peer0") 673 ccName := testChaincode.Name 674 675 // Verifies marble private chaincode APIs: getMarblesByRange, transferMarble, delete 676 677 By("adding five marbles") 678 for i := 0; i < 5; i++ { 679 addMarble(network, orderer, ccName, fmt.Sprintf(`{"name":"test-marble-%d", "color":"blue", "size":35, "owner":"tom", "price":99}`, i), eligiblePeer) 680 } 681 682 By("getting marbles by range") 683 assertGetMarblesByRange(network, ccName, `"test-marble-0", "test-marble-2"`, eligiblePeer) 684 685 By("transferring test-marble-0 to jerry") 686 transferMarble(network, orderer, ccName, `{"name":"test-marble-0", "owner":"jerry"}`, eligiblePeer) 687 688 By("verifying the new ownership of test-marble-0") 689 assertOwnershipInCollectionM(network, ccName, `test-marble-0`, eligiblePeer) 690 691 By("deleting test-marble-0") 692 deleteMarble(network, orderer, ccName, `{"name":"test-marble-0"}`, eligiblePeer) 693 694 By("verifying the deletion of test-marble-0") 695 assertDoesNotExistInCollectionM(network, ccName, `test-marble-0`, eligiblePeer) 696 697 // This section verifies that chaincode can return private data hash. 698 // Unlike private data that can only be accessed from authorized peers as defined in the collection config, 699 // private data hash can be queried on any peer in the channel that has the chaincode instantiated. 700 // When calling QueryChaincode with "getMarbleHash", the cc will return the private data hash in collectionMarbles. 701 // When calling QueryChaincode with "getMarblePrivateDetailsHash", the cc will return the private data hash in collectionMarblePrivateDetails. 702 703 peerList := []*nwo.Peer{ 704 network.Peer("Org1", "peer0"), 705 network.Peer("Org2", "peer0"), 706 network.Peer("Org3", "peer0")} 707 708 By("verifying getMarbleHash is accessible from all peers that has the chaincode instantiated") 709 expectedBytes := util.ComputeStringHash(`{"docType":"marble","name":"test-marble-1","color":"blue","size":35,"owner":"tom"}`) 710 assertMarblesPrivateHashM(network, ccName, "test-marble-1", expectedBytes, peerList) 711 712 By("verifying getMarblePrivateDetailsHash is accessible from all peers that has the chaincode instantiated") 713 expectedBytes = util.ComputeStringHash(`{"docType":"marblePrivateDetails","name":"test-marble-1","price":99}`) 714 assertMarblesPrivateDetailsHashMPD(network, ccName, "test-marble-1", expectedBytes, peerList) 715 716 // collection ACL while reading private data: not allowed to non-members 717 // collections_config3: collectionMarblePrivateDetails - member_only_read is set to true 718 719 By("querying collectionMarblePrivateDetails on org1-peer0 by org1-user1, shouldn't have read access") 720 assertNoReadAccessToCollectionMPD(network, testChaincode.Name, "test-marble-1", network.Peer("Org1", "peer0")) 721 } 722 723 // verify DeliverWithPrivateData sends private data based on the ACL in collection config 724 // before and after upgrade. 725 assertDeliverWithPrivateDataACLBehavior := func() { 726 By("getting signing identity for a user in org1") 727 signingIdentity := getSigningIdentity(network, "Org1", "User1", "Org1MSP", "bccsp") 728 729 By("adding a marble") 730 peer := network.Peer("Org2", "peer0") 731 addMarble(network, orderer, testChaincode.Name, `{"name":"marble11", "color":"blue", "size":35, "owner":"tom", "price":99}`, peer) 732 733 By("getting the deliver event for newest block") 734 event := getEventFromDeliverService(network, peer, channelID, signingIdentity, 0) 735 736 By("verifying private data in deliver event contains 'collectionMarbles' only") 737 // it should receive pvtdata for 'collectionMarbles' only because memberOnlyRead is true 738 expectedKVWritesMap := map[string]map[string][]byte{ 739 "collectionMarbles": { 740 "\000color~name\000blue\000marble11\000": []byte("\000"), 741 "marble11": getValueForCollectionMarbles("marble11", "blue", "tom", 35), 742 }, 743 } 744 assertPrivateDataAsExpected(event.BlockAndPvtData.PrivateDataMap, expectedKVWritesMap) 745 746 By("upgrading chaincode with collections_config1.json where isMemberOnlyRead is false") 747 testChaincode.CollectionsConfig = collectionConfig("collections_config1.json") 748 testChaincode.Version = "1.1" 749 if !testChaincode.isLegacy { 750 testChaincode.Sequence = "2" 751 } 752 upgradeChaincode(network, orderer, testChaincode) 753 754 By("getting the deliver event for an old block committed before upgrade") 755 event = getEventFromDeliverService(network, peer, channelID, signingIdentity, event.BlockNum) 756 757 By("verifying the deliver event for the old block uses old config") 758 assertPrivateDataAsExpected(event.BlockAndPvtData.PrivateDataMap, expectedKVWritesMap) 759 760 By("adding a new marble after upgrade") 761 addMarble(network, orderer, testChaincode.Name, 762 `{"name":"marble12", "color":"blue", "size":35, "owner":"tom", "price":99}`, 763 network.Peer("Org1", "peer0"), 764 ) 765 By("getting the deliver event for a new block committed after upgrade") 766 event = getEventFromDeliverService(network, peer, channelID, signingIdentity, 0) 767 768 // it should receive pvtdata for both collections because memberOnlyRead is false 769 By("verifying the deliver event for the new block uses new config") 770 expectedKVWritesMap = map[string]map[string][]byte{ 771 "collectionMarbles": { 772 "\000color~name\000blue\000marble12\000": []byte("\000"), 773 "marble12": getValueForCollectionMarbles("marble12", "blue", "tom", 35), 774 }, 775 "collectionMarblePrivateDetails": { 776 "marble12": getValueForCollectionMarblePrivateDetails("marble12", 99), 777 }, 778 } 779 assertPrivateDataAsExpected(event.BlockAndPvtData.PrivateDataMap, expectedKVWritesMap) 780 } 781 782 When("chaincode in legacy lifecycle", func() { 783 It("calls marbles APIs and delivers private data", func() { 784 By("deploying legacy chaincode") 785 testChaincode = chaincode{ 786 Chaincode: legacyChaincode, 787 isLegacy: true, 788 } 789 testChaincode.CollectionsConfig = collectionConfig("collections_config3.json") 790 deployChaincode(network, orderer, testChaincode) 791 792 By("attempting to invoke chaincode from a user (org1) not in any collection member orgs (org2 and org3)") 793 peer2 := network.Peer("Org2", "peer0") 794 marbleDetailsBase64 := base64.StdEncoding.EncodeToString([]byte(`{"name":"memberonly-marble", "color":"blue", "size":35, "owner":"tom", "price":99}`)) 795 command := commands.ChaincodeInvoke{ 796 ChannelID: channelID, 797 Orderer: network.OrdererAddress(orderer, nwo.ListenPort), 798 Name: "marblesp", 799 Ctor: fmt.Sprintf(`{"Args":["initMarble"]}`), 800 Transient: fmt.Sprintf(`{"marble":"%s"}`, marbleDetailsBase64), 801 PeerAddresses: []string{network.PeerAddress(peer2, nwo.ListenPort)}, 802 WaitForEvent: true, 803 } 804 peer1 := network.Peer("Org1", "peer0") 805 expectedErrMsg := "tx creator does not have write access permission" 806 invokeChaincodeExpectErr(network, peer1, command, expectedErrMsg) 807 808 assertMarbleAPIs() 809 assertDeliverWithPrivateDataACLBehavior() 810 }) 811 }) 812 813 When("chaincode in new lifecycle", func() { 814 It("calls marbles APIs and delivers private data", func() { 815 By("deploying new lifecycle chaincode") 816 testChaincode = chaincode{ 817 Chaincode: newLifecycleChaincode, 818 isLegacy: false, 819 } 820 nwo.EnableCapabilities(network, channelID, "Application", "V2_0", orderer, network.Peers...) 821 testChaincode.CollectionsConfig = collectionConfig("collections_config3.json") 822 deployChaincode(network, orderer, testChaincode) 823 824 By("attempting to invoke chaincode from a user (org1) not in any collection member orgs (org2 and org3)") 825 peer2 := network.Peer("Org2", "peer0") 826 marbleDetailsBase64 := base64.StdEncoding.EncodeToString([]byte(`{"name":"memberonly-marble", "color":"blue", "size":35, "owner":"tom", "price":99}`)) 827 command := commands.ChaincodeInvoke{ 828 ChannelID: channelID, 829 Orderer: network.OrdererAddress(orderer, nwo.ListenPort), 830 Name: "marblesp", 831 Ctor: fmt.Sprintf(`{"Args":["initMarble"]}`), 832 Transient: fmt.Sprintf(`{"marble":"%s"}`, marbleDetailsBase64), 833 PeerAddresses: []string{network.PeerAddress(peer2, nwo.ListenPort)}, 834 WaitForEvent: true, 835 } 836 peer1 := network.Peer("Org1", "peer0") 837 expectedErrMsg := "tx creator does not have write access permission" 838 invokeChaincodeExpectErr(network, peer1, command, expectedErrMsg) 839 840 assertMarbleAPIs() 841 assertDeliverWithPrivateDataACLBehavior() 842 }) 843 }) 844 }) 845 846 Describe("Collection Config Endorsement Policy", func() { 847 When("using legacy lifecycle chaincode", func() { 848 It("ignores the collection config endorsement policy and successfully invokes the chaincode", func() { 849 testChaincode = chaincode{ 850 Chaincode: legacyChaincode, 851 isLegacy: true, 852 } 853 By("setting the collection config endorsement policy to org2 or org3 peers") 854 testChaincode.CollectionsConfig = collectionConfig("collections_config4.json") 855 856 By("deploying legacy chaincode") 857 deployChaincode(network, orderer, testChaincode) 858 859 By("adding marble1 with an org 1 peer as endorser") 860 peer := network.Peer("Org1", "peer0") 861 marbleDetails := `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}` 862 addMarble(network, orderer, testChaincode.Name, marbleDetails, peer) 863 }) 864 }) 865 866 When("using new lifecycle chaincode", func() { 867 BeforeEach(func() { 868 testChaincode = chaincode{ 869 Chaincode: newLifecycleChaincode, 870 isLegacy: false, 871 } 872 nwo.EnableCapabilities(network, "testchannel", "Application", "V2_0", orderer, network.Peers...) 873 }) 874 875 When("a peer specified in the chaincode endorsement policy but not in the collection config endorsement policy is used to invoke the chaincode", func() { 876 It("fails validation", func() { 877 By("setting the collection config endorsement policy to org2 or org3 peers") 878 testChaincode.CollectionsConfig = collectionConfig("collections_config4.json") 879 880 By("deploying new lifecycle chaincode") 881 // set collection endorsement policy to org2 or org3 882 deployChaincode(network, orderer, testChaincode) 883 884 By("adding marble1 with an org1 peer as endorser") 885 peer := network.Peer("Org1", "peer0") 886 marbleDetails := `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}` 887 marbleDetailsBase64 := base64.StdEncoding.EncodeToString([]byte(marbleDetails)) 888 889 command := commands.ChaincodeInvoke{ 890 ChannelID: channelID, 891 Orderer: network.OrdererAddress(orderer, nwo.ListenPort), 892 Name: testChaincode.Name, 893 Ctor: fmt.Sprintf(`{"Args":["initMarble"]}`), 894 Transient: fmt.Sprintf(`{"marble":"%s"}`, marbleDetailsBase64), 895 PeerAddresses: []string{ 896 network.PeerAddress(peer, nwo.ListenPort), 897 }, 898 WaitForEvent: true, 899 } 900 901 sess, err := network.PeerUserSession(peer, "User1", command) 902 Expect(err).NotTo(HaveOccurred()) 903 Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit()) 904 Expect(sess.Err).To(gbytes.Say("ENDORSEMENT_POLICY_FAILURE")) 905 }) 906 }) 907 908 When("a peer specified in the collection endorsement policy but not in the chaincode endorsement policy is used to invoke the chaincode", func() { 909 When("the collection endorsement policy is a signature policy", func() { 910 It("successfully invokes the chaincode", func() { 911 // collection config endorsement policy specifies org2 or org3 peers for endorsement 912 By("setting the collection config endorsement policy to use a signature policy") 913 testChaincode.CollectionsConfig = collectionConfig("collections_config4.json") 914 915 By("setting the chaincode endorsement policy to org1 or org2 peers") 916 testChaincode.SignaturePolicy = `OR ('Org1MSP.member','Org2MSP.member')` 917 918 By("deploying new lifecycle chaincode") 919 // set collection endorsement policy to org2 or org3 920 deployChaincode(network, orderer, testChaincode) 921 922 By("adding marble1 with an org3 peer as endorser") 923 peer := network.Peer("Org3", "peer0") 924 marbleDetails := `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}` 925 addMarble(network, orderer, testChaincode.Name, marbleDetails, peer) 926 }) 927 }) 928 929 When("the collection endorsement policy is a channel config policy reference", func() { 930 It("successfully invokes the chaincode", func() { 931 // collection config endorsement policy specifies channel config policy reference /Channel/Application/Readers 932 By("setting the collection config endorsement policy to use a channel config policy reference") 933 testChaincode.CollectionsConfig = collectionConfig("collections_config5.json") 934 935 By("setting the channel endorsement policy to org1 or org2 peers") 936 testChaincode.SignaturePolicy = `OR ('Org1MSP.member','Org2MSP.member')` 937 938 By("deploying new lifecycle chaincode") 939 deployChaincode(network, orderer, testChaincode) 940 941 By("adding marble1 with an org3 peer as endorser") 942 peer := network.Peer("Org3", "peer0") 943 marbleDetails := `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}` 944 addMarble(network, orderer, testChaincode.Name, marbleDetails, peer) 945 }) 946 }) 947 }) 948 949 When("the collection config endorsement policy specifies a semantically wrong, but well formed signature policy", func() { 950 It("fails to invoke the chaincode with an endorsement policy failure", func() { 951 By("setting the collection config endorsement policy to non existent org4 peers") 952 testChaincode.CollectionsConfig = collectionConfig("collections_config6.json") 953 954 By("deploying new lifecycle chaincode") 955 deployChaincode(network, orderer, testChaincode) 956 957 By("adding marble1 with an org1 peer as endorser") 958 peer := network.Peer("Org1", "peer0") 959 marbleDetails := `{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}` 960 marbleDetailsBase64 := base64.StdEncoding.EncodeToString([]byte(marbleDetails)) 961 962 command := commands.ChaincodeInvoke{ 963 ChannelID: channelID, 964 Orderer: network.OrdererAddress(orderer, nwo.ListenPort), 965 Name: testChaincode.Name, 966 Ctor: fmt.Sprintf(`{"Args":["initMarble"]}`), 967 Transient: fmt.Sprintf(`{"marble":"%s"}`, marbleDetailsBase64), 968 PeerAddresses: []string{ 969 network.PeerAddress(peer, nwo.ListenPort), 970 }, 971 WaitForEvent: true, 972 } 973 974 sess, err := network.PeerUserSession(peer, "User1", command) 975 Expect(err).NotTo(HaveOccurred()) 976 Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit()) 977 Expect(sess.Err).To(gbytes.Say("ENDORSEMENT_POLICY_FAILURE")) 978 }) 979 }) 980 }) 981 }) 982 }) 983 }) 984 985 func initThreeOrgsSetup(removePeer1 bool) *nwo.Network { 986 var err error 987 testDir, err := ioutil.TempDir("", "e2e-pvtdata") 988 Expect(err).NotTo(HaveOccurred()) 989 990 client, err := docker.NewClientFromEnv() 991 Expect(err).NotTo(HaveOccurred()) 992 993 config := nwo.FullSolo() 994 995 // add org3 with one peer 996 config.Organizations = append(config.Organizations, &nwo.Organization{ 997 Name: "Org3", 998 MSPID: "Org3MSP", 999 Domain: "org3.example.com", 1000 EnableNodeOUs: true, 1001 Users: 2, 1002 CA: &nwo.CA{Hostname: "ca"}, 1003 }) 1004 config.Consortiums[0].Organizations = append(config.Consortiums[0].Organizations, "Org3") 1005 config.Profiles[1].Organizations = append(config.Profiles[1].Organizations, "Org3") 1006 config.Peers = append(config.Peers, &nwo.Peer{ 1007 Name: "peer0", 1008 Organization: "Org3", 1009 Channels: []*nwo.PeerChannel{ 1010 {Name: channelID, Anchor: true}, 1011 }, 1012 }) 1013 1014 n := nwo.New(config, testDir, client, StartPort(), components) 1015 n.GenerateConfigTree() 1016 1017 if !removePeer1 { 1018 Expect(n.Peers).To(HaveLen(5)) 1019 return n 1020 } 1021 1022 // remove peer1 from org1 and org2 so we can add it back later, we generate the config tree above 1023 // with the two peers so the config files exist later when adding the peer back 1024 peers := []*nwo.Peer{} 1025 for _, p := range n.Peers { 1026 if p.Name != "peer1" { 1027 peers = append(peers, p) 1028 } 1029 } 1030 n.Peers = peers 1031 Expect(n.Peers).To(HaveLen(3)) 1032 1033 return n 1034 } 1035 1036 func startNetwork(n *nwo.Network) (ifrit.Process, *nwo.Orderer) { 1037 n.Bootstrap() 1038 networkRunner := n.NetworkGroupRunner() 1039 process := ifrit.Invoke(networkRunner) 1040 Eventually(process.Ready(), n.EventuallyTimeout).Should(BeClosed()) 1041 1042 orderer := n.Orderer("orderer") 1043 n.CreateAndJoinChannel(orderer, channelID) 1044 n.UpdateChannelAnchors(orderer, channelID) 1045 1046 By("verifying membership") 1047 n.VerifyMembership(n.Peers, channelID) 1048 1049 return process, orderer 1050 } 1051 1052 func testCleanup(network *nwo.Network, process ifrit.Process) { 1053 if process != nil { 1054 process.Signal(syscall.SIGTERM) 1055 Eventually(process.Wait(), network.EventuallyTimeout).Should(Receive()) 1056 } 1057 if network != nil { 1058 network.Cleanup() 1059 } 1060 os.RemoveAll(network.RootDir) 1061 } 1062 1063 func collectionConfig(collConfigFile string) string { 1064 return filepath.Join("testdata", "collection_configs", collConfigFile) 1065 } 1066 1067 type chaincode struct { 1068 nwo.Chaincode 1069 isLegacy bool 1070 } 1071 1072 func addPeer(n *nwo.Network, orderer *nwo.Orderer, peer *nwo.Peer) ifrit.Process { 1073 process := ifrit.Invoke(n.PeerRunner(peer)) 1074 Eventually(process.Ready(), n.EventuallyTimeout).Should(BeClosed()) 1075 1076 n.JoinChannel(channelID, orderer, peer) 1077 ledgerHeight := nwo.GetLedgerHeight(n, n.Peers[0], channelID) 1078 sess, err := n.PeerAdminSession( 1079 peer, 1080 commands.ChannelFetch{ 1081 Block: "newest", 1082 ChannelID: channelID, 1083 Orderer: n.OrdererAddress(orderer, nwo.ListenPort), 1084 OutputFile: filepath.Join(n.RootDir, "newest_block.pb"), 1085 }, 1086 ) 1087 Expect(err).NotTo(HaveOccurred()) 1088 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 1089 Expect(sess.Err).To(gbytes.Say(fmt.Sprintf("Received block: %d", ledgerHeight-1))) 1090 1091 n.Peers = append(n.Peers, peer) 1092 nwo.WaitUntilEqualLedgerHeight(n, channelID, nwo.GetLedgerHeight(n, n.Peers[0], channelID), n.Peers...) 1093 1094 return process 1095 } 1096 1097 func deployChaincode(n *nwo.Network, orderer *nwo.Orderer, chaincode chaincode) { 1098 if chaincode.isLegacy { 1099 nwo.DeployChaincodeLegacy(n, channelID, orderer, chaincode.Chaincode) 1100 } else { 1101 nwo.DeployChaincode(n, channelID, orderer, chaincode.Chaincode) 1102 } 1103 } 1104 1105 func upgradeChaincode(n *nwo.Network, orderer *nwo.Orderer, chaincode chaincode) { 1106 if chaincode.isLegacy { 1107 nwo.UpgradeChaincodeLegacy(n, channelID, orderer, chaincode.Chaincode) 1108 } else { 1109 nwo.DeployChaincode(n, channelID, orderer, chaincode.Chaincode) 1110 } 1111 } 1112 1113 func installChaincode(n *nwo.Network, chaincode chaincode, peer *nwo.Peer) { 1114 if chaincode.isLegacy { 1115 nwo.InstallChaincodeLegacy(n, chaincode.Chaincode, peer) 1116 } else { 1117 nwo.PackageAndInstallChaincode(n, chaincode.Chaincode, peer) 1118 } 1119 } 1120 1121 func queryChaincode(n *nwo.Network, peer *nwo.Peer, command commands.ChaincodeQuery, expectedMessage string, expectSuccess bool) { 1122 sess, err := n.PeerUserSession(peer, "User1", command) 1123 Expect(err).NotTo(HaveOccurred()) 1124 if expectSuccess { 1125 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 1126 Expect(sess).To(gbytes.Say(expectedMessage)) 1127 } else { 1128 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit()) 1129 Expect(sess.Err).To(gbytes.Say(expectedMessage)) 1130 } 1131 } 1132 1133 func invokeChaincode(n *nwo.Network, peer *nwo.Peer, command commands.ChaincodeInvoke) { 1134 sess, err := n.PeerUserSession(peer, "User1", command) 1135 Expect(err).NotTo(HaveOccurred()) 1136 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 1137 Expect(sess.Err).To(gbytes.Say("Chaincode invoke successful.")) 1138 } 1139 1140 func invokeChaincodeExpectErr(n *nwo.Network, peer *nwo.Peer, command commands.ChaincodeInvoke, expectedErrMsg string) { 1141 sess, err := n.PeerUserSession(peer, "User1", command) 1142 Expect(err).NotTo(HaveOccurred()) 1143 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(1)) 1144 Expect(sess.Err).To(gbytes.Say(expectedErrMsg)) 1145 } 1146 1147 func approveChaincodeForMyOrgExpectErr(n *nwo.Network, orderer *nwo.Orderer, chaincode nwo.Chaincode, expectedErrMsg string, peers ...*nwo.Peer) { 1148 // used to ensure we only approve once per org 1149 approvedOrgs := map[string]bool{} 1150 for _, p := range peers { 1151 if _, ok := approvedOrgs[p.Organization]; !ok { 1152 sess, err := n.PeerAdminSession(p, commands.ChaincodeApproveForMyOrg{ 1153 ChannelID: channelID, 1154 Orderer: n.OrdererAddress(orderer, nwo.ListenPort), 1155 Name: chaincode.Name, 1156 Version: chaincode.Version, 1157 PackageID: chaincode.PackageID, 1158 Sequence: chaincode.Sequence, 1159 EndorsementPlugin: chaincode.EndorsementPlugin, 1160 ValidationPlugin: chaincode.ValidationPlugin, 1161 SignaturePolicy: chaincode.SignaturePolicy, 1162 ChannelConfigPolicy: chaincode.ChannelConfigPolicy, 1163 InitRequired: chaincode.InitRequired, 1164 CollectionsConfig: chaincode.CollectionsConfig, 1165 }) 1166 Expect(err).NotTo(HaveOccurred()) 1167 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit()) 1168 approvedOrgs[p.Organization] = true 1169 Eventually(sess.Err, n.EventuallyTimeout).Should(gbytes.Say(expectedErrMsg)) 1170 } 1171 } 1172 } 1173 1174 func addMarble(n *nwo.Network, orderer *nwo.Orderer, chaincodeName, marbleDetails string, peer *nwo.Peer) { 1175 marbleDetailsBase64 := base64.StdEncoding.EncodeToString([]byte(marbleDetails)) 1176 1177 command := commands.ChaincodeInvoke{ 1178 ChannelID: channelID, 1179 Orderer: n.OrdererAddress(orderer, nwo.ListenPort), 1180 Name: chaincodeName, 1181 Ctor: fmt.Sprintf(`{"Args":["initMarble"]}`), 1182 Transient: fmt.Sprintf(`{"marble":"%s"}`, marbleDetailsBase64), 1183 PeerAddresses: []string{ 1184 n.PeerAddress(peer, nwo.ListenPort), 1185 }, 1186 WaitForEvent: true, 1187 } 1188 invokeChaincode(n, peer, command) 1189 nwo.WaitUntilEqualLedgerHeight(n, channelID, nwo.GetLedgerHeight(n, peer, channelID), n.Peers...) 1190 } 1191 1192 func deleteMarble(n *nwo.Network, orderer *nwo.Orderer, chaincodeName, marbleDelete string, peer *nwo.Peer) { 1193 marbleDeleteBase64 := base64.StdEncoding.EncodeToString([]byte(marbleDelete)) 1194 1195 command := commands.ChaincodeInvoke{ 1196 ChannelID: channelID, 1197 Orderer: n.OrdererAddress(orderer, nwo.ListenPort), 1198 Name: chaincodeName, 1199 Ctor: fmt.Sprintf(`{"Args":["delete"]}`), 1200 Transient: fmt.Sprintf(`{"marble_delete":"%s"}`, marbleDeleteBase64), 1201 PeerAddresses: []string{ 1202 n.PeerAddress(peer, nwo.ListenPort), 1203 }, 1204 WaitForEvent: true, 1205 } 1206 invokeChaincode(n, peer, command) 1207 nwo.WaitUntilEqualLedgerHeight(n, channelID, nwo.GetLedgerHeight(n, peer, channelID), n.Peers...) 1208 } 1209 1210 func transferMarble(n *nwo.Network, orderer *nwo.Orderer, chaincodeName, marbleOwner string, peer *nwo.Peer) { 1211 marbleOwnerBase64 := base64.StdEncoding.EncodeToString([]byte(marbleOwner)) 1212 1213 command := commands.ChaincodeInvoke{ 1214 ChannelID: channelID, 1215 Orderer: n.OrdererAddress(orderer, nwo.ListenPort), 1216 Name: chaincodeName, 1217 Ctor: fmt.Sprintf(`{"Args":["transferMarble"]}`), 1218 Transient: fmt.Sprintf(`{"marble_owner":"%s"}`, marbleOwnerBase64), 1219 PeerAddresses: []string{ 1220 n.PeerAddress(peer, nwo.ListenPort), 1221 }, 1222 WaitForEvent: true, 1223 } 1224 invokeChaincode(n, peer, command) 1225 nwo.WaitUntilEqualLedgerHeight(n, channelID, nwo.GetLedgerHeight(n, peer, channelID), n.Peers...) 1226 } 1227 1228 func assertPvtdataPresencePerCollectionConfig1(n *nwo.Network, chaincodeName, marbleName string, peers ...*nwo.Peer) { 1229 if len(peers) == 0 { 1230 peers = n.Peers 1231 } 1232 for _, peer := range peers { 1233 switch peer.Organization { 1234 1235 case "Org1": 1236 assertPresentInCollectionM(n, chaincodeName, marbleName, peer) 1237 assertNotPresentInCollectionMPD(n, chaincodeName, marbleName, peer) 1238 1239 case "Org2": 1240 assertPresentInCollectionM(n, chaincodeName, marbleName, peer) 1241 assertPresentInCollectionMPD(n, chaincodeName, marbleName, peer) 1242 1243 case "Org3": 1244 assertNotPresentInCollectionM(n, chaincodeName, marbleName, peer) 1245 assertPresentInCollectionMPD(n, chaincodeName, marbleName, peer) 1246 } 1247 } 1248 } 1249 1250 func assertPvtdataPresencePerCollectionConfig2(n *nwo.Network, chaincodeName, marbleName string, peers ...*nwo.Peer) { 1251 if len(peers) == 0 { 1252 peers = n.Peers 1253 } 1254 for _, peer := range peers { 1255 switch peer.Organization { 1256 1257 case "Org1": 1258 assertPresentInCollectionM(n, chaincodeName, marbleName, peer) 1259 assertNotPresentInCollectionMPD(n, chaincodeName, marbleName, peer) 1260 1261 case "Org2", "Org3": 1262 assertPresentInCollectionM(n, chaincodeName, marbleName, peer) 1263 assertPresentInCollectionMPD(n, chaincodeName, marbleName, peer) 1264 } 1265 } 1266 } 1267 1268 func assertPvtdataPresencePerCollectionConfig7(n *nwo.Network, chaincodeName, marbleName string, excludedPeer *nwo.Peer, peers ...*nwo.Peer) { 1269 if len(peers) == 0 { 1270 peers = n.Peers 1271 } 1272 collectionMPresence := 0 1273 collectionMPDPresence := 0 1274 for _, peer := range peers { 1275 // exclude the peer that invoked originally and count number of peers disseminated to 1276 if peer != excludedPeer { 1277 switch peer.Organization { 1278 1279 case "Org1": 1280 collectionMPresence += checkPresentInCollectionM(n, chaincodeName, marbleName, peer) 1281 assertNotPresentInCollectionMPD(n, chaincodeName, marbleName, peer) 1282 1283 case "Org2": 1284 collectionMPresence += checkPresentInCollectionM(n, chaincodeName, marbleName, peer) 1285 collectionMPDPresence += checkPresentInCollectionMPD(n, chaincodeName, marbleName, peer) 1286 1287 case "Org3": 1288 assertNotPresentInCollectionM(n, chaincodeName, marbleName, peer) 1289 collectionMPDPresence += checkPresentInCollectionMPD(n, chaincodeName, marbleName, peer) 1290 } 1291 } 1292 } 1293 Expect(collectionMPresence).To(Equal(1)) 1294 Expect(collectionMPDPresence).To(Equal(1)) 1295 1296 } 1297 1298 // assertGetMarblesByRange asserts that 1299 func assertGetMarblesByRange(n *nwo.Network, chaincodeName, marbleRange string, peer *nwo.Peer) { 1300 query := fmt.Sprintf(`{"Args":["getMarblesByRange", %s]}`, marbleRange) 1301 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` 1302 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, true, peer) 1303 } 1304 1305 // assertPresentInCollectionM asserts that the private data for given marble is present in collection 1306 // 'readMarble' at the given peers 1307 func assertPresentInCollectionM(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) { 1308 query := fmt.Sprintf(`{"Args":["readMarble","%s"]}`, marbleName) 1309 expectedMsg := fmt.Sprintf(`{"docType":"marble","name":"%s"`, marbleName) 1310 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, true, peerList...) 1311 } 1312 1313 // assertPresentInCollectionMPD asserts that the private data for given marble is present 1314 // in collection 'readMarblePrivateDetails' at the given peers 1315 func assertPresentInCollectionMPD(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) { 1316 query := fmt.Sprintf(`{"Args":["readMarblePrivateDetails","%s"]}`, marbleName) 1317 expectedMsg := fmt.Sprintf(`{"docType":"marblePrivateDetails","name":"%s"`, marbleName) 1318 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, true, peerList...) 1319 } 1320 1321 // checkPresentInCollectionM checks then number of peers that have the private data for given marble 1322 // in collection 'readMarble' 1323 func checkPresentInCollectionM(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) int { 1324 query := fmt.Sprintf(`{"Args":["readMarble","%s"]}`, marbleName) 1325 expectedMsg := fmt.Sprintf(`{"docType":"marble","name":"%s"`, marbleName) 1326 command := commands.ChaincodeQuery{ 1327 ChannelID: channelID, 1328 Name: chaincodeName, 1329 Ctor: query, 1330 } 1331 present := 0 1332 for _, peer := range peerList { 1333 sess, err := n.PeerUserSession(peer, "User1", command) 1334 Expect(err).NotTo(HaveOccurred()) 1335 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit()) 1336 if bytes.Contains(sess.Buffer().Contents(), []byte(expectedMsg)) { 1337 present++ 1338 } 1339 } 1340 return present 1341 } 1342 1343 // checkPresentInCollectionMPD checks the number of peers that have the private data for given marble 1344 // in collection 'readMarblePrivateDetails' 1345 func checkPresentInCollectionMPD(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) int { 1346 query := fmt.Sprintf(`{"Args":["readMarblePrivateDetails","%s"]}`, marbleName) 1347 expectedMsg := fmt.Sprintf(`{"docType":"marblePrivateDetails","name":"%s"`, marbleName) 1348 command := commands.ChaincodeQuery{ 1349 ChannelID: channelID, 1350 Name: chaincodeName, 1351 Ctor: query, 1352 } 1353 present := 0 1354 for _, peer := range peerList { 1355 sess, err := n.PeerUserSession(peer, "User1", command) 1356 Expect(err).NotTo(HaveOccurred()) 1357 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit()) 1358 if bytes.Contains(sess.Buffer().Contents(), []byte(expectedMsg)) { 1359 present++ 1360 } 1361 } 1362 return present 1363 } 1364 1365 // assertNotPresentInCollectionM asserts that the private data for given marble is NOT present 1366 // in collection 'readMarble' at the given peers 1367 func assertNotPresentInCollectionM(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) { 1368 query := fmt.Sprintf(`{"Args":["readMarble","%s"]}`, marbleName) 1369 expectedMsg := "private data matching public hash version is not available" 1370 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, false, peerList...) 1371 } 1372 1373 // assertNotPresentInCollectionMPD asserts that the private data for given marble is NOT present 1374 // in collection 'readMarblePrivateDetails' at the given peers 1375 func assertNotPresentInCollectionMPD(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) { 1376 query := fmt.Sprintf(`{"Args":["readMarblePrivateDetails","%s"]}`, marbleName) 1377 expectedMsg := "private data matching public hash version is not available" 1378 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, false, peerList...) 1379 } 1380 1381 // assertDoesNotExistInCollectionM asserts that the private data for given marble 1382 // does not exist in collection 'readMarble' (i.e., is never created/has been deleted/has been purged) 1383 func assertDoesNotExistInCollectionM(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) { 1384 query := fmt.Sprintf(`{"Args":["readMarble","%s"]}`, marbleName) 1385 expectedMsg := "Marble does not exist" 1386 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, false, peerList...) 1387 } 1388 1389 // assertDoesNotExistInCollectionMPD asserts that the private data for given marble 1390 // does not exist in collection 'readMarblePrivateDetails' (i.e., is never created/has been deleted/has been purged) 1391 func assertDoesNotExistInCollectionMPD(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) { 1392 query := fmt.Sprintf(`{"Args":["readMarblePrivateDetails","%s"]}`, marbleName) 1393 expectedMsg := "Marble private details does not exist" 1394 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, false, peerList...) 1395 } 1396 1397 // assertOwnershipInCollectionM asserts that the private data for given marble is present 1398 // in collection 'readMarble' at the given peers 1399 func assertOwnershipInCollectionM(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) { 1400 query := fmt.Sprintf(`{"Args":["readMarble","%s"]}`, marbleName) 1401 expectedMsg := fmt.Sprintf(`{"docType":"marble","name":"test-marble-0","color":"blue","size":35,"owner":"jerry"}`) 1402 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, true, peerList...) 1403 } 1404 1405 // assertNoReadAccessToCollectionMPD asserts that the orgs of the given peers do not have 1406 // read access to private data for the collection readMarblePrivateDetails 1407 func assertNoReadAccessToCollectionMPD(n *nwo.Network, chaincodeName, marbleName string, peerList ...*nwo.Peer) { 1408 query := fmt.Sprintf(`{"Args":["readMarblePrivateDetails","%s"]}`, marbleName) 1409 expectedMsg := "tx creator does not have read access permission" 1410 queryChaincodePerPeer(n, query, chaincodeName, expectedMsg, false, peerList...) 1411 } 1412 1413 func queryChaincodePerPeer(n *nwo.Network, query, chaincodeName, expectedMsg string, expectSuccess bool, peerList ...*nwo.Peer) { 1414 command := commands.ChaincodeQuery{ 1415 ChannelID: channelID, 1416 Name: chaincodeName, 1417 Ctor: query, 1418 } 1419 for _, peer := range peerList { 1420 queryChaincode(n, peer, command, expectedMsg, expectSuccess) 1421 } 1422 } 1423 1424 // assertMarblesPrivateHashM asserts that getMarbleHash is accessible from all peers that has the chaincode instantiated 1425 func assertMarblesPrivateHashM(n *nwo.Network, chaincodeName, marbleName string, expectedBytes []byte, peerList []*nwo.Peer) { 1426 query := fmt.Sprintf(`{"Args":["getMarbleHash","%s"]}`, marbleName) 1427 verifyPvtdataHash(n, query, chaincodeName, peerList, expectedBytes) 1428 } 1429 1430 // assertMarblesPrivateDetailsHashMPD asserts that getMarblePrivateDetailsHash is accessible from all peers that has the chaincode instantiated 1431 func assertMarblesPrivateDetailsHashMPD(n *nwo.Network, chaincodeName, marbleName string, expectedBytes []byte, peerList []*nwo.Peer) { 1432 query := fmt.Sprintf(`{"Args":["getMarblePrivateDetailsHash","%s"]}`, marbleName) 1433 verifyPvtdataHash(n, query, chaincodeName, peerList, expectedBytes) 1434 } 1435 1436 // verifyPvtdataHash verifies the private data hash matches the expected bytes. 1437 // Cannot reuse verifyAccess because the hash bytes are not valid utf8 causing gbytes.Say to fail. 1438 func verifyPvtdataHash(n *nwo.Network, query, chaincodeName string, peers []*nwo.Peer, expected []byte) { 1439 command := commands.ChaincodeQuery{ 1440 ChannelID: channelID, 1441 Name: chaincodeName, 1442 Ctor: query, 1443 } 1444 1445 for _, peer := range peers { 1446 sess, err := n.PeerUserSession(peer, "User1", command) 1447 Expect(err).NotTo(HaveOccurred()) 1448 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 1449 actual := sess.Buffer().Contents() 1450 // verify actual bytes contain expected bytes - cannot use equal because session may contain extra bytes 1451 Expect(bytes.Contains(actual, expected)).To(Equal(true)) 1452 } 1453 } 1454 1455 // deliverEvent contains the response and related info from a DeliverWithPrivateData call 1456 type deliverEvent struct { 1457 BlockAndPvtData *pb.BlockAndPrivateData 1458 BlockNum uint64 1459 Err error 1460 } 1461 1462 // getEventFromDeliverService send a request to DeliverWithPrivateData grpc service 1463 // and receive the response 1464 func getEventFromDeliverService(network *nwo.Network, peer *nwo.Peer, channelID string, signingIdentity msp.SigningIdentity, blockNum uint64) *deliverEvent { 1465 ctx, cancelFunc1 := context.WithTimeout(context.Background(), network.EventuallyTimeout) 1466 defer cancelFunc1() 1467 eventCh, conn := registerForDeliverEvent(ctx, network, peer, channelID, signingIdentity, blockNum) 1468 defer conn.Close() 1469 event := &deliverEvent{} 1470 Eventually(eventCh, network.EventuallyTimeout).Should(Receive(event)) 1471 Expect(event.Err).NotTo(HaveOccurred()) 1472 return event 1473 } 1474 1475 func registerForDeliverEvent( 1476 ctx context.Context, 1477 network *nwo.Network, 1478 peer *nwo.Peer, 1479 channelID string, 1480 signingIdentity msp.SigningIdentity, 1481 blockNum uint64, 1482 ) (<-chan deliverEvent, *grpc.ClientConn) { 1483 // create a comm.GRPCClient 1484 tlsRootCertFile := filepath.Join(network.PeerLocalTLSDir(peer), "ca.crt") 1485 caPEM, err := ioutil.ReadFile(tlsRootCertFile) 1486 Expect(err).NotTo(HaveOccurred()) 1487 clientConfig := comm.ClientConfig{Timeout: 10 * time.Second} 1488 clientConfig.SecOpts = comm.SecureOptions{ 1489 UseTLS: true, 1490 ServerRootCAs: [][]byte{caPEM}, 1491 RequireClientCert: false, 1492 } 1493 grpcClient, err := comm.NewGRPCClient(clientConfig) 1494 Expect(err).NotTo(HaveOccurred()) 1495 // create a client for DeliverWithPrivateData 1496 address := network.PeerAddress(peer, nwo.ListenPort) 1497 conn, err := grpcClient.NewConnection(address) 1498 Expect(err).NotTo(HaveOccurred()) 1499 dp, err := pb.NewDeliverClient(conn).DeliverWithPrivateData(ctx) 1500 Expect(err).NotTo(HaveOccurred()) 1501 // send a deliver request 1502 envelope, err := createDeliverEnvelope(channelID, signingIdentity, blockNum) 1503 Expect(err).NotTo(HaveOccurred()) 1504 err = dp.Send(envelope) 1505 dp.CloseSend() 1506 Expect(err).NotTo(HaveOccurred()) 1507 // create a goroutine to receive the response in a separate thread 1508 eventCh := make(chan deliverEvent, 1) 1509 go receiveDeliverResponse(dp, address, eventCh) 1510 1511 return eventCh, conn 1512 } 1513 1514 func getSigningIdentity(network *nwo.Network, org, user, mspID, mspType string) msp.SigningIdentity { 1515 peerForOrg := network.Peer(org, "peer0") 1516 mspConfigPath := network.PeerUserMSPDir(peerForOrg, user) 1517 mspInstance, err := loadLocalMSPAt(mspConfigPath, mspID, mspType) 1518 Expect(err).NotTo(HaveOccurred()) 1519 1520 signingIdentity, err := mspInstance.GetDefaultSigningIdentity() 1521 Expect(err).NotTo(HaveOccurred()) 1522 return signingIdentity 1523 } 1524 1525 // loadLocalMSPAt loads an MSP whose configuration is stored at 'dir', and whose 1526 // id and type are the passed as arguments. 1527 func loadLocalMSPAt(dir, id, mspType string) (msp.MSP, error) { 1528 if mspType != "bccsp" { 1529 return nil, errors.Errorf("invalid msp type, expected 'bccsp', got %s", mspType) 1530 } 1531 conf, err := msp.GetLocalMspConfig(dir, nil, id) 1532 if err != nil { 1533 return nil, err 1534 } 1535 ks, err := sw.NewFileBasedKeyStore(nil, filepath.Join(dir, "keystore"), true) 1536 if err != nil { 1537 return nil, err 1538 } 1539 cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) 1540 if err != nil { 1541 return nil, err 1542 } 1543 thisMSP, err := msp.NewBccspMspWithKeyStore(msp.MSPv1_0, ks, cryptoProvider) 1544 if err != nil { 1545 return nil, err 1546 } 1547 err = thisMSP.Setup(conf) 1548 if err != nil { 1549 return nil, err 1550 } 1551 return thisMSP, nil 1552 } 1553 1554 // receiveDeliverResponse expects to receive the BlockAndPrivateData response for the requested block. 1555 func receiveDeliverResponse(dp pb.Deliver_DeliverWithPrivateDataClient, address string, eventCh chan<- deliverEvent) error { 1556 event := deliverEvent{} 1557 1558 resp, err := dp.Recv() 1559 if err != nil { 1560 event.Err = errors.WithMessagef(err, "error receiving deliver response from peer %s\n", address) 1561 } 1562 switch r := resp.Type.(type) { 1563 case *pb.DeliverResponse_BlockAndPrivateData: 1564 event.BlockAndPvtData = r.BlockAndPrivateData 1565 event.BlockNum = r.BlockAndPrivateData.Block.Header.Number 1566 case *pb.DeliverResponse_Status: 1567 event.Err = errors.Errorf("deliver completed with status (%s) before DeliverResponse_BlockAndPrivateData received from peer %s", r.Status, address) 1568 default: 1569 event.Err = errors.Errorf("received unexpected response type (%T) from peer %s", r, address) 1570 } 1571 1572 select { 1573 case eventCh <- event: 1574 default: 1575 } 1576 return nil 1577 } 1578 1579 // createDeliverEnvelope creates a deliver request based on the block number. 1580 // blockNum=0 means newest block 1581 func createDeliverEnvelope(channelID string, signingIdentity msp.SigningIdentity, blockNum uint64) (*cb.Envelope, error) { 1582 creator, err := signingIdentity.Serialize() 1583 if err != nil { 1584 return nil, err 1585 } 1586 header, err := createHeader(cb.HeaderType_DELIVER_SEEK_INFO, channelID, creator) 1587 if err != nil { 1588 return nil, err 1589 } 1590 1591 // if blockNum is not greater than 0, seek the newest block 1592 var seekInfo *ab.SeekInfo 1593 if blockNum > 0 { 1594 seekInfo = &ab.SeekInfo{ 1595 Start: &ab.SeekPosition{ 1596 Type: &ab.SeekPosition_Specified{ 1597 Specified: &ab.SeekSpecified{Number: blockNum}, 1598 }, 1599 }, 1600 Stop: &ab.SeekPosition{ 1601 Type: &ab.SeekPosition_Specified{ 1602 Specified: &ab.SeekSpecified{Number: blockNum}, 1603 }, 1604 }, 1605 } 1606 } else { 1607 seekInfo = &ab.SeekInfo{ 1608 Start: &ab.SeekPosition{ 1609 Type: &ab.SeekPosition_Newest{ 1610 Newest: &ab.SeekNewest{}, 1611 }, 1612 }, 1613 Stop: &ab.SeekPosition{ 1614 Type: &ab.SeekPosition_Newest{ 1615 Newest: &ab.SeekNewest{}, 1616 }, 1617 }, 1618 } 1619 } 1620 1621 // create the envelope 1622 raw := protoutil.MarshalOrPanic(seekInfo) 1623 payload := &cb.Payload{ 1624 Header: header, 1625 Data: raw, 1626 } 1627 payloadBytes := protoutil.MarshalOrPanic(payload) 1628 signature, err := signingIdentity.Sign(payloadBytes) 1629 if err != nil { 1630 return nil, err 1631 } 1632 return &cb.Envelope{ 1633 Payload: payloadBytes, 1634 Signature: signature, 1635 }, nil 1636 } 1637 1638 func createHeader(txType cb.HeaderType, channelID string, creator []byte) (*cb.Header, error) { 1639 ts, err := ptypes.TimestampProto(time.Now()) 1640 if err != nil { 1641 return nil, err 1642 } 1643 nonce, err := crypto.GetRandomNonce() 1644 if err != nil { 1645 return nil, err 1646 } 1647 chdr := &cb.ChannelHeader{ 1648 Type: int32(txType), 1649 ChannelId: channelID, 1650 TxId: protoutil.ComputeTxID(nonce, creator), 1651 Epoch: 0, 1652 Timestamp: ts, 1653 } 1654 chdrBytes := protoutil.MarshalOrPanic(chdr) 1655 1656 shdr := &cb.SignatureHeader{ 1657 Creator: creator, 1658 Nonce: nonce, 1659 } 1660 shdrBytes := protoutil.MarshalOrPanic(shdr) 1661 header := &cb.Header{ 1662 ChannelHeader: chdrBytes, 1663 SignatureHeader: shdrBytes, 1664 } 1665 return header, nil 1666 } 1667 1668 // verify collection names and pvtdataMap match expectedKVWritesMap 1669 func assertPrivateDataAsExpected(pvtdataMap map[uint64]*rwset.TxPvtReadWriteSet, expectedKVWritesMap map[string]map[string][]byte) { 1670 // In the test, each block has only 1 tx, so txSeqInBlock is 0 1671 txPvtRwset := pvtdataMap[uint64(0)] 1672 Expect(txPvtRwset.NsPvtRwset).To(HaveLen(1)) 1673 Expect(txPvtRwset.NsPvtRwset[0].Namespace).To(Equal("marblesp")) 1674 Expect(txPvtRwset.NsPvtRwset[0].CollectionPvtRwset).To(HaveLen(len(expectedKVWritesMap))) 1675 1676 // verify the collections returned in private data have expected collection names and kvRwset.Writes 1677 for _, col := range txPvtRwset.NsPvtRwset[0].CollectionPvtRwset { 1678 Expect(expectedKVWritesMap).To(HaveKey(col.CollectionName)) 1679 expectedKvWrites := expectedKVWritesMap[col.CollectionName] 1680 kvRwset := kvrwset.KVRWSet{} 1681 err := proto.Unmarshal(col.GetRwset(), &kvRwset) 1682 Expect(err).NotTo(HaveOccurred()) 1683 Expect(kvRwset.Writes).To(HaveLen(len(expectedKvWrites))) 1684 for _, kvWrite := range kvRwset.Writes { 1685 Expect(expectedKvWrites).To(HaveKey(kvWrite.Key)) 1686 Expect(kvWrite.Value).To(Equal(expectedKvWrites[kvWrite.Key])) 1687 } 1688 } 1689 } 1690 1691 func getValueForCollectionMarbles(marbleName, color, owner string, size int) []byte { 1692 marbleJSONasString := `{"docType":"marble","name":"` + marbleName + `","color":"` + color + `","size":` + strconv.Itoa(size) + `,"owner":"` + owner + `"}` 1693 return []byte(marbleJSONasString) 1694 } 1695 1696 func getValueForCollectionMarblePrivateDetails(marbleName string, price int) []byte { 1697 marbleJSONasString := `{"docType":"marblePrivateDetails","name":"` + marbleName + `","price":` + strconv.Itoa(price) + `}` 1698 return []byte(marbleJSONasString) 1699 } 1700 1701 // fetchBlocksForPeer attempts to fetch the newest block on the given peer. 1702 // It skips the orderer and returns the session's Err buffer for parsing. 1703 func fetchBlocksForPeer(n *nwo.Network, peer *nwo.Peer, user string) func() *gbytes.Buffer { 1704 return func() *gbytes.Buffer { 1705 sess, err := n.PeerUserSession(peer, user, commands.ChannelFetch{ 1706 Block: "newest", 1707 ChannelID: channelID, 1708 OutputFile: filepath.Join(n.RootDir, "newest_block.pb"), 1709 }) 1710 Expect(err).NotTo(HaveOccurred()) 1711 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit()) 1712 return sess.Err 1713 } 1714 } 1715 1716 // updateConfigWithNewCertsForPeer updates the channel config with new certs for the designated peer 1717 func updateConfigWithNewCertsForPeer(network *nwo.Network, tempCryptoDir string, orderer *nwo.Orderer, peer *nwo.Peer) { 1718 org := network.Organization(peer.Organization) 1719 1720 By("fetching the channel policy") 1721 currentConfig := nwo.GetConfig(network, network.Peers[0], orderer, channelID) 1722 updatedConfig := proto.Clone(currentConfig).(*cb.Config) 1723 1724 By("parsing the old and new MSP configs") 1725 oldConfig := &mspp.MSPConfig{} 1726 err := proto.Unmarshal( 1727 updatedConfig.ChannelGroup.Groups["Application"].Groups[org.Name].Values["MSP"].Value, 1728 oldConfig) 1729 Expect(err).NotTo(HaveOccurred()) 1730 1731 tempOrgMSPPath := filepath.Join(tempCryptoDir, "peerOrganizations", org.Domain, "msp") 1732 newConfig, err := msp.GetVerifyingMspConfig(tempOrgMSPPath, org.MSPID, "bccsp") 1733 Expect(err).NotTo(HaveOccurred()) 1734 oldMspConfig := &mspp.FabricMSPConfig{} 1735 newMspConfig := &mspp.FabricMSPConfig{} 1736 err = proto.Unmarshal(oldConfig.Config, oldMspConfig) 1737 Expect(err).NotTo(HaveOccurred()) 1738 err = proto.Unmarshal(newConfig.Config, newMspConfig) 1739 Expect(err).NotTo(HaveOccurred()) 1740 1741 By("merging the two MSP configs") 1742 updateOldMspConfigWithNewMspConfig(oldMspConfig, newMspConfig) 1743 1744 By("updating the channel config") 1745 updatedConfig.ChannelGroup.Groups["Application"].Groups[org.Name].Values["MSP"].Value = protoutil.MarshalOrPanic( 1746 &mspp.MSPConfig{ 1747 Type: oldConfig.Type, 1748 Config: protoutil.MarshalOrPanic(oldMspConfig), 1749 }) 1750 nwo.UpdateConfig(network, orderer, channelID, currentConfig, updatedConfig, false, network.Peer(org.Name, "peer0")) 1751 } 1752 1753 // updateOldMspConfigWithNewMspConfig updates the oldMspConfig with certs from the newMspConfig 1754 func updateOldMspConfigWithNewMspConfig(oldMspConfig, newMspConfig *mspp.FabricMSPConfig) { 1755 oldMspConfig.RootCerts = append(oldMspConfig.RootCerts, newMspConfig.RootCerts...) 1756 oldMspConfig.TlsRootCerts = append(oldMspConfig.TlsRootCerts, newMspConfig.TlsRootCerts...) 1757 oldMspConfig.FabricNodeOus.PeerOuIdentifier.Certificate = nil 1758 oldMspConfig.FabricNodeOus.ClientOuIdentifier.Certificate = nil 1759 oldMspConfig.FabricNodeOus.AdminOuIdentifier.Certificate = nil 1760 } 1761 1762 // generateNewCertsForPeer generates new certs with cryptogen for the designated peer and copies 1763 // the necessary certs to the original crypto dir as well as creating an Admin2 user to use for 1764 // any peer operations involving the peer 1765 func generateNewCertsForPeer(network *nwo.Network, tempCryptoDir string, peer *nwo.Peer) { 1766 sess, err := network.Cryptogen(commands.Generate{ 1767 Config: network.CryptoConfigPath(), 1768 Output: tempCryptoDir, 1769 }) 1770 Expect(err).NotTo(HaveOccurred()) 1771 Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0)) 1772 1773 By("copying the new msp certs for the peer to the original crypto dir") 1774 oldPeerMSPPath := network.PeerLocalMSPDir(peer) 1775 org := network.Organization(peer.Organization) 1776 tempPeerMSPPath := filepath.Join( 1777 tempCryptoDir, 1778 "peerOrganizations", 1779 org.Domain, 1780 "peers", 1781 fmt.Sprintf("%s.%s", peer.Name, org.Domain), 1782 "msp", 1783 ) 1784 os.RemoveAll(oldPeerMSPPath) 1785 err = exec.Command("cp", "-r", tempPeerMSPPath, oldPeerMSPPath).Run() 1786 Expect(err).NotTo(HaveOccurred()) 1787 1788 // This lets us keep the old user certs for the org for any peers still remaining in the org 1789 // using the old certs 1790 By("copying the new Admin user cert to the original user certs dir as Admin2") 1791 oldAdminUserPath := filepath.Join( 1792 network.RootDir, 1793 "crypto", 1794 "peerOrganizations", 1795 org.Domain, 1796 "users", 1797 fmt.Sprintf("Admin2@%s", org.Domain), 1798 ) 1799 tempAdminUserPath := filepath.Join( 1800 tempCryptoDir, 1801 "peerOrganizations", 1802 org.Domain, 1803 "users", 1804 fmt.Sprintf("Admin@%s", org.Domain), 1805 ) 1806 os.RemoveAll(oldAdminUserPath) 1807 err = exec.Command("cp", "-r", tempAdminUserPath, oldAdminUserPath).Run() 1808 Expect(err).NotTo(HaveOccurred()) 1809 // We need to rename the signcert from Admin to Admin2 as well 1810 err = os.Rename( 1811 filepath.Join(oldAdminUserPath, "msp", "signcerts", fmt.Sprintf("Admin@%s-cert.pem", org.Domain)), 1812 filepath.Join(oldAdminUserPath, "msp", "signcerts", fmt.Sprintf("Admin2@%s-cert.pem", org.Domain)), 1813 ) 1814 Expect(err).NotTo(HaveOccurred()) 1815 }