github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/integration/pvtdata/pvtdata_test.go (about)

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