github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/integration/pvtdata/pvtdata_test.go (about)

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