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