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

     1  /*
     2  Copyright hechain All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package ledger
     8  
     9  import (
    10  	"context"
    11  	"encoding/base64"
    12  	"encoding/json"
    13  	"fmt"
    14  	"io/ioutil"
    15  	"math"
    16  	"os"
    17  	"path/filepath"
    18  	"strconv"
    19  	"strings"
    20  	"syscall"
    21  	"time"
    22  
    23  	docker "github.com/fsouza/go-dockerclient"
    24  	"github.com/hechain20/hechain/core/ledger/util"
    25  	"github.com/hechain20/hechain/integration/chaincode/kvexecutor"
    26  	"github.com/hechain20/hechain/integration/nwo"
    27  	"github.com/hechain20/hechain/integration/nwo/commands"
    28  	"github.com/hechain20/hechain/integration/nwo/runner"
    29  	"github.com/hechain20/hechain/integration/pvtdata/marblechaincodeutil"
    30  	"github.com/hechain20/hechain/protoutil"
    31  	cb "github.com/hyperledger/fabric-protos-go/common"
    32  	ab "github.com/hyperledger/fabric-protos-go/orderer"
    33  	pb "github.com/hyperledger/fabric-protos-go/peer"
    34  	. "github.com/onsi/ginkgo"
    35  	. "github.com/onsi/gomega"
    36  	"github.com/onsi/gomega/gbytes"
    37  	"github.com/onsi/gomega/gexec"
    38  	"github.com/tedsuo/ifrit"
    39  )
    40  
    41  const testchannelID = "testchannel"
    42  
    43  var _ = Describe("Snapshot Generation and Bootstrap", func() {
    44  	var (
    45  		setup                 *setup
    46  		helper                *marblesTestHelper
    47  		couchProcess          []ifrit.Process
    48  		legacyChaincode       nwo.Chaincode
    49  		newlifecycleChaincode nwo.Chaincode
    50  	)
    51  
    52  	BeforeEach(func() {
    53  		By("initializing and starting the network")
    54  		setup = initAndStartFourOrgsNetwork()
    55  
    56  		helper = &marblesTestHelper{
    57  			networkHelper: &networkHelper{
    58  				Network:   setup.network,
    59  				orderer:   setup.orderer,
    60  				peers:     setup.peers,
    61  				testDir:   setup.testDir,
    62  				channelID: setup.channelID,
    63  			},
    64  		}
    65  	})
    66  
    67  	AfterEach(func() {
    68  		setup.cleanup()
    69  		for _, proc := range couchProcess {
    70  			proc.Signal(syscall.SIGTERM)
    71  			Eventually(proc.Wait(), setup.network.EventuallyTimeout).Should(Receive())
    72  		}
    73  		os.RemoveAll(setup.network.RootDir)
    74  	})
    75  
    76  	When("chaincode has no private data collections", func() {
    77  		BeforeEach(func() {
    78  			legacyChaincode = nwo.Chaincode{
    79  				Name:        "marbles",
    80  				Version:     "0.0",
    81  				Path:        chaincodePathWithIndex,
    82  				Ctor:        `{"Args":[]}`,
    83  				Policy:      `OR ('Org1MSP.member','Org2MSP.member','Org3MSP.member','Org4MSP.member')`,
    84  				PackageFile: filepath.Join(setup.testDir, "marbles_legacy.tar.gz"),
    85  			}
    86  
    87  			newlifecycleChaincode = nwo.Chaincode{
    88  				Name:            "marbles",
    89  				Version:         "0.0",
    90  				Path:            components.Build(chaincodePathWithIndex),
    91  				Lang:            "binary",
    92  				CodeFiles:       filesWithIndex,
    93  				PackageFile:     filepath.Join(setup.testDir, "marbles.tar.gz"),
    94  				SignaturePolicy: `OR ('Org1MSP.member','Org2MSP.member','Org3MSP.member','Org4MSP.member')`,
    95  				Sequence:        "1",
    96  				Label:           "marbles",
    97  			}
    98  
    99  			By("deploying legacy chaincode on initial peers (stateleveldb)")
   100  			nwo.DeployChaincodeLegacy(setup.network, testchannelID, setup.orderer, legacyChaincode)
   101  		})
   102  
   103  		// Below test does the following when peers are using either leveldb or couchdb.
   104  		// Note that we do not support a channel with mixed DBs. However, for testing,
   105  		// it would be fine to use mixed DBs to test with both couchdb and leveldb
   106  		// - create snapshots on the 2 peers and verify they are same
   107  		// - bootstrap a peer (couchdb) in an existing org from the snapshot
   108  		// - bootstrap a peer (leveldb) in a new org from the snapshot
   109  		// - verify couchdb index exists
   110  		// - verify chaincode invocation, history, qscc, channel config update
   111  		// - upgrade to new lifecycle chaincode
   112  		// - create a new snapshot again from a peer (couchdb) bootstrapped from a snapshot
   113  		// - bootstrap peers (couchdb) in existing org and new org from the new snapshot
   114  		// - verify couchdb index exists
   115  		// - verify chaincode invocation, history, qscc
   116  		// - verify chaincode upgrade and new chaincode install on all peers
   117  		It("generates snapshot and bootstraps from snapshots", func() {
   118  			org1peer0 := setup.network.Peer("Org1", "peer0")
   119  			org2peer0 := setup.network.Peer("Org2", "peer0")
   120  
   121  			By("invoking marbles chaincode")
   122  			testKey := "marble-0"
   123  			helper.invokeMarblesChaincode(legacyChaincode.Name, org1peer0, "initMarble", "marble-0", "blue", "35", "tom")
   124  			helper.invokeMarblesChaincode(legacyChaincode.Name, org1peer0, "initMarble", "marble-1", "red", "100", "tom")
   125  
   126  			By("getting an existing transaction from a block before snapshot is generated")
   127  			txenvBeforeSnapshot, txidBeforeSnapshot := getTxFromLastBlock(setup.network, org1peer0)
   128  
   129  			// verify snapshot commands with different parameters")
   130  			blockNum := nwo.GetLedgerHeight(setup.network, org1peer0, testchannelID) - 1
   131  			verifySnapshotRequestCmds(setup.network, org1peer0, testchannelID, blockNum)
   132  
   133  			// test 1: generate snapshots on 2 peers for the same blockNum and verify they are same
   134  			_, snapshotDir := generateAndCompareSnapshots(setup.network, org1peer0, org2peer0, blockNum)
   135  
   136  			// test 2: bootstrap a peer in an existing org from snapshot and verify
   137  			By("starting new peer org2peer1 in existing org2 (couchdb)")
   138  			org2peer1, couchProc := startPeer(setup, "Org2", "peer1", testchannelID, true)
   139  			couchProcess = append(couchProcess, couchProc)
   140  
   141  			By("installing legacy chaincode on new peer org2peer1")
   142  			nwo.InstallChaincodeLegacy(setup.network, legacyChaincode, org2peer1)
   143  
   144  			By("joining new peer org2peer1 to the channel")
   145  			joinBySnapshot(setup.network, setup.orderer, org2peer1, testchannelID, snapshotDir, blockNum)
   146  
   147  			By("verifying index created on org2peer1")
   148  			verifySizeIndexExists(setup.network, testchannelID, setup.orderer, org2peer1, "marbles")
   149  
   150  			By("invoking marbles chaincode on bootstrapped peer org2peer1")
   151  			helper.invokeMarblesChaincode(legacyChaincode.Name, org2peer1, "transferMarble", testKey, "newowner2")
   152  
   153  			By("verifying history on peer org2peer1")
   154  			expectedHistory := []*marbleHistoryResult{
   155  				{IsDelete: "false", Value: newMarble(testKey, "blue", 35, "newowner2")},
   156  			}
   157  			helper.assertGetHistoryForMarble(legacyChaincode.Name, org2peer1, expectedHistory, testKey)
   158  
   159  			verifyQSCC(setup.network, org2peer1, testchannelID, blockNum, txidBeforeSnapshot)
   160  
   161  			// test 3: bootstrap a peer in a new org from snapshot and verify
   162  			By("starting a peer Org3.peer0 in new org3 (stateleveldb)")
   163  			org3peer0, _ := startPeer(setup, "Org3", "peer0", testchannelID, false)
   164  
   165  			By("installing legacy chaincode on new peer org3peer0")
   166  			nwo.InstallChaincodeLegacy(setup.network, legacyChaincode, org3peer0)
   167  
   168  			By("joining new peer org3peer0 to the channel")
   169  			joinBySnapshot(setup.network, setup.orderer, org3peer0, testchannelID, snapshotDir, blockNum)
   170  
   171  			By("invoking marbles chaincode on bootstrapped peer org3peer0")
   172  			helper.invokeMarblesChaincode(legacyChaincode.Name, org3peer0, "transferMarble", testKey, "newowner3")
   173  
   174  			By("getting an existing transaction from a block after snapshot is generated")
   175  			txenvAfterSnapshot, txidAfterSnapshot := getTxFromLastBlock(setup.network, org1peer0)
   176  
   177  			By("verifying history on peer org3peer0")
   178  			expectedHistory = []*marbleHistoryResult{
   179  				{IsDelete: "false", Value: newMarble(testKey, "blue", 35, "newowner3")},
   180  				{IsDelete: "false", Value: newMarble(testKey, "blue", 35, "newowner2")},
   181  			}
   182  			helper.assertGetHistoryForMarble(legacyChaincode.Name, org3peer0, expectedHistory, testKey)
   183  
   184  			verifyQSCC(setup.network, org3peer0, testchannelID, blockNum, txidBeforeSnapshot)
   185  
   186  			// verify DUPLICATE_TXID error when resubmitting old tx on a peer bootstrapped from snapshot (v1_4 capability)
   187  			By("resubmitting an old transaction committed before snapshot, expecting duplicated txid error")
   188  			err := commitTx(setup.network, setup.orderer, org3peer0, testchannelID, txenvBeforeSnapshot, txidBeforeSnapshot)
   189  			Expect(err.Error()).To(ContainSubstring("transaction invalidated with status (DUPLICATE_TXID)"))
   190  			By("resubmitting an old transaction committed after snapshot, expecting duplicated txid error")
   191  			err = commitTx(setup.network, setup.orderer, org3peer0, testchannelID, txenvAfterSnapshot, txidAfterSnapshot)
   192  			Expect(err.Error()).To(Equal("transaction invalidated with status (DUPLICATE_TXID)"))
   193  
   194  			// test 4: upgrade legacy chaincode to new lifecycle
   195  			By("enabling V2_0 capabilities")
   196  			channelPeers := setup.network.PeersWithChannel(testchannelID)
   197  			nwo.EnableCapabilities(setup.network, testchannelID, "Application", "V2_0", setup.orderer, channelPeers...)
   198  
   199  			By("upgrading legacy chaincode to new lifecycle chaincode")
   200  			nwo.DeployChaincode(setup.network, testchannelID, setup.orderer, newlifecycleChaincode, channelPeers...)
   201  
   202  			By("invoking chaincode after upgraded to new lifecycle chaincode")
   203  			helper.invokeMarblesChaincode(newlifecycleChaincode.Name, org1peer0, "initMarble", "marble-upgrade", "blue", "35", "tom")
   204  
   205  			// test 5: generate snapshot again on a peer bootstrapped from a snapshot and upgraded to new lifecycle chaincode
   206  			blockNumForNextSnapshot := nwo.GetLedgerHeight(setup.network, org2peer1, testchannelID)
   207  			By(fmt.Sprintf("generating a snapshot at blockNum %d on org2peer1 that was bootstrapped by a snapshot", blockNumForNextSnapshot))
   208  			submitSnapshotRequest(setup.network, testchannelID, blockNumForNextSnapshot, org2peer1, false, "Snapshot request submitted successfully")
   209  
   210  			// invoke chaincode to trigger snapshot generation
   211  			// 1st call should be committed before snapshot generation, 2nd call should be committed after snapshot generation
   212  			helper.invokeMarblesChaincode(newlifecycleChaincode.Name, org2peer1, "transferMarble", testKey, "newowner_beforesnapshot")
   213  			helper.invokeMarblesChaincode(newlifecycleChaincode.Name, org2peer1, "transferMarble", testKey, "newowner_aftersnapshot")
   214  
   215  			By("verifying snapshot completed on org2peer1")
   216  			verifyNoPendingSnapshotRequest(setup.network, org2peer1, testchannelID)
   217  			nextSnapshotDir := filepath.Join(setup.network.PeerDir(org2peer1), "filesystem", "snapshots", "completed", testchannelID, strconv.Itoa(blockNumForNextSnapshot))
   218  
   219  			By("getting an existing transaction from a block after new snapshot is generated")
   220  			helper.invokeMarblesChaincode(legacyChaincode.Name, org2peer1, "initMarble", "marble-3", "red", "100", "tom")
   221  			txenvAfterSnapshot, txidAfterSnapshot = getTxFromLastBlock(setup.network, org1peer0)
   222  
   223  			// test 6: bootstrap a peer in a different org from the new snapshot
   224  			By("starting a peer (org1peer1) in existing org1 (couchdb)")
   225  			org1peer1, couchProc := startPeer(setup, "Org1", "peer1", testchannelID, true)
   226  			couchProcess = append(couchProcess, couchProc)
   227  
   228  			By("installing new lifecycle chaincode on peer org1peer1")
   229  			nwo.InstallChaincode(setup.network, newlifecycleChaincode, org1peer1)
   230  
   231  			By("joining new peer org1peer1 to the channel")
   232  			joinBySnapshot(setup.network, setup.orderer, org1peer1, testchannelID, nextSnapshotDir, blockNumForNextSnapshot)
   233  
   234  			By("verifying index created on org1peer1")
   235  			verifySizeIndexExists(setup.network, testchannelID, setup.orderer, org1peer1, "marbles")
   236  
   237  			By("verifying history on peer org1peer1")
   238  			expectedHistory = []*marbleHistoryResult{
   239  				{IsDelete: "false", Value: newMarble(testKey, "blue", 35, "newowner_aftersnapshot")},
   240  			}
   241  			helper.assertGetHistoryForMarble(newlifecycleChaincode.Name, org1peer1, expectedHistory, testKey)
   242  
   243  			verifyQSCC(setup.network, org1peer1, testchannelID, blockNumForNextSnapshot, txidBeforeSnapshot)
   244  
   245  			// test 7: bootstrap a peer in a new org from the new snapshot
   246  			By("starting a peer (org4peer0) in new org4 (couchdb)")
   247  			org4peer0, couchProc := startPeer(setup, "Org4", "peer0", testchannelID, true)
   248  			couchProcess = append(couchProcess, couchProc)
   249  
   250  			By("joining new peer org4peer0 to the channel")
   251  			joinBySnapshot(setup.network, setup.orderer, org4peer0, testchannelID, nextSnapshotDir, blockNumForNextSnapshot)
   252  
   253  			By("installing and approving chaincode on new peer org4peer0")
   254  			installAndApproveChaincode(setup.network, setup.orderer, org4peer0, testchannelID, newlifecycleChaincode, []string{"Org1", "Org2", "Org3", "Org4"})
   255  
   256  			By("verifying index created on org4peer0")
   257  			verifySizeIndexExists(setup.network, testchannelID, setup.orderer, org2peer1, "marbles")
   258  
   259  			By("invoking chaincode on bootstrapped peer org4peer0")
   260  			helper.invokeMarblesChaincode(newlifecycleChaincode.Name, org4peer0, "delete", testKey)
   261  
   262  			By("verifying history on peer org4peer0")
   263  			expectedHistory = []*marbleHistoryResult{
   264  				{IsDelete: "true"},
   265  				{IsDelete: "false", Value: newMarble(testKey, "blue", 35, "newowner_aftersnapshot")},
   266  			}
   267  			helper.assertGetHistoryForMarble(newlifecycleChaincode.Name, org4peer0, expectedHistory, testKey)
   268  
   269  			verifyQSCC(setup.network, org4peer0, testchannelID, blockNumForNextSnapshot, txidBeforeSnapshot)
   270  
   271  			// verify DUPLICATE_TXID error when resubmitting old tx on a peer bootstrapped from snapshot (v_20 capability)
   272  			By("resubmitting an old transaction committed before snapshot, expecting duplicated txid error")
   273  			err = commitTx(setup.network, setup.orderer, org4peer0, testchannelID, txenvBeforeSnapshot, txidBeforeSnapshot)
   274  			Expect(err.Error()).To(ContainSubstring("transaction invalidated with status (DUPLICATE_TXID)"))
   275  			By("resubmitting an old transaction committed after snapshot, expecting duplicated txid error")
   276  			err = commitTx(setup.network, setup.orderer, org4peer0, testchannelID, txenvAfterSnapshot, txidAfterSnapshot)
   277  			Expect(err.Error()).To(Equal("transaction invalidated with status (DUPLICATE_TXID)"))
   278  
   279  			// test 8: verify cscc works correctly to get an orderer endpoint from the channel config
   280  			// even if the peer does not have a channel config block when bootstrapped from snapshot
   281  			By("invoking chaincode without passing orderer endpoint on org4peer0")
   282  			invokeWithoutPassingOrdererEndPoint(setup.network, org4peer0, testchannelID, newlifecycleChaincode.Name, "initMarble", "marble-cscctest", "blue", "35", "tom")
   283  
   284  			// test 9: verify chaincode upgrade and install after bootstrapping
   285  			By("upgrading chaincode to version 2.0 on all peers after bootstrapping from snapshot")
   286  			newlifecycleChaincode.Version = "2.0"
   287  			newlifecycleChaincode.Sequence = "2"
   288  			nwo.DeployChaincode(setup.network, testchannelID, setup.orderer, newlifecycleChaincode)
   289  
   290  			By("deploying a new chaincode on all the peers after bootstrapping from snapshot")
   291  			cc2 := nwo.Chaincode{
   292  				Name:            "kvexecutor",
   293  				Version:         "1.0",
   294  				Path:            components.Build("github.com/hechain20/hechain/integration/chaincode/kvexecutor/cmd"),
   295  				Lang:            "binary",
   296  				SignaturePolicy: `OR ('Org1MSP.member','Org2MSP.member', 'Org3MSP.member', 'Org4MSP.member')`,
   297  				PackageFile:     filepath.Join(setup.testDir, "kvexecutor20.tar.gz"),
   298  				Label:           "kvexecutor-20",
   299  				Sequence:        "1",
   300  			}
   301  			nwo.DeployChaincode(setup.network, testchannelID, setup.orderer, cc2)
   302  
   303  			By("invoking the new chaincode")
   304  			kvdata := []kvexecutor.KVData{
   305  				{Key: "key1", Value: "value1"},
   306  				{Key: "key2", Value: "value2"},
   307  			}
   308  			invokeAndQueryKVExecutorChaincode(setup.network, setup.orderer, testchannelID, cc2, kvdata, setup.network.PeersWithChannel(testchannelID)...)
   309  		})
   310  	})
   311  
   312  	When("chaincode has private data collections", func() {
   313  		BeforeEach(func() {
   314  			newlifecycleChaincode = nwo.Chaincode{
   315  				Name:              "marblesp",
   316  				Version:           "1.0",
   317  				Path:              components.Build("github.com/hechain20/hechain/integration/chaincode/marbles_private/cmd"),
   318  				Lang:              "binary",
   319  				PackageFile:       filepath.Join(setup.testDir, "marbles-pvtdata.tar.gz"),
   320  				Label:             "marbles-private-20",
   321  				SignaturePolicy:   `OR ('Org1MSP.member','Org2MSP.member')`,
   322  				CollectionsConfig: filepath.Join("testdata", "collection_configs", "collections_config1.json"),
   323  				Sequence:          "1",
   324  			}
   325  
   326  			// start org3peer0 so that we have majority number of orgs (3 out of 4) to satify the channel config update policy
   327  			org3peer0, _ := startPeer(setup, "Org3", "peer0", testchannelID, false)
   328  			setup.network.JoinChannel(testchannelID, setup.orderer, org3peer0)
   329  
   330  			By("enabling V2_0 capabilities")
   331  			channelPeers := setup.network.PeersWithChannel(testchannelID)
   332  			nwo.EnableCapabilities(setup.network, testchannelID, "Application", "V2_0", setup.orderer, channelPeers...)
   333  
   334  			By("deploying newlifecycle chaincode on initial peers (leveldb)")
   335  			nwo.DeployChaincode(setup.network, testchannelID, setup.orderer, newlifecycleChaincode)
   336  		})
   337  
   338  		// This test verifies the following:
   339  		// bootstrapped peer can pull private data
   340  		// bootstrapped peer can supply private data to other bootstrapped peer
   341  		It("generates snapshot and bootstraps from snapshots", func() {
   342  			org1peer0 := setup.network.Peer("Org1", "peer0")
   343  			org2peer0 := setup.network.Peer("Org2", "peer0")
   344  			channelPeers := setup.network.PeersWithChannel(testchannelID)
   345  
   346  			// prepare test data: add and delete marble1, add and transfer marble1
   347  			By("adding marble1")
   348  			marblechaincodeutil.AddMarble(setup.network, setup.orderer, testchannelID, newlifecycleChaincode.Name,
   349  				`{"name":"marble1", "color":"blue", "size":35, "owner":"tom", "price":99}`, org2peer0)
   350  			By("deleting marble1")
   351  			marblechaincodeutil.DeleteMarble(setup.network, setup.orderer, testchannelID, newlifecycleChaincode.Name,
   352  				`{"name":"marble1"}`, org2peer0)
   353  
   354  			By("verifying the deletion of marble1")
   355  			marblechaincodeutil.AssertDoesNotExistInCollectionM(setup.network, testchannelID, newlifecycleChaincode.Name, "marble1", channelPeers...)
   356  			marblechaincodeutil.AssertDoesNotExistInCollectionMPD(setup.network, testchannelID, newlifecycleChaincode.Name, "marble1", channelPeers...)
   357  
   358  			By("adding marble2")
   359  			marblechaincodeutil.AddMarble(setup.network, setup.orderer, testchannelID, newlifecycleChaincode.Name,
   360  				`{"name":"marble2", "color":"blue", "size":35, "owner":"tom", "price":99}`, org2peer0)
   361  			By("transferring marble2")
   362  			marblechaincodeutil.TransferMarble(setup.network, setup.orderer, testchannelID, newlifecycleChaincode.Name,
   363  				`{"name":"marble2", "owner":"jerry"}`, org2peer0)
   364  
   365  			assertPvtdataPresencePerCollectionConfig1(setup.network, newlifecycleChaincode.Name, "marble2")
   366  
   367  			By("verifying the new ownership of marble2")
   368  			marblechaincodeutil.AssertOwnershipInCollectionM(setup.network, testchannelID, newlifecycleChaincode.Name, "marble2", "jerry", org1peer0, org2peer0)
   369  
   370  			// test 1: generate snapshots on 2 peers for the same blockNum and verify they are same
   371  			blockNum := nwo.GetLedgerHeight(setup.network, org2peer0, testchannelID) - 1
   372  			_, snapshotDir := generateAndCompareSnapshots(setup.network, org1peer0, org2peer0, blockNum)
   373  
   374  			// test 2: bootstrap a new peer org2peer1 from snapshot and verify pvtdata
   375  			By("starting new peer org2peer1 (couchdb)")
   376  			org2peer1, couchProc := startPeer(setup, "Org2", "peer1", testchannelID, true)
   377  			couchProcess = append(couchProcess, couchProc)
   378  
   379  			By("installing chaincode on peer org2peer1")
   380  			nwo.InstallChaincode(setup.network, newlifecycleChaincode, org2peer1)
   381  
   382  			By("joining peer org2peer1 to the channel by snapshot")
   383  			joinBySnapshot(setup.network, setup.orderer, org2peer1, testchannelID, snapshotDir, blockNum)
   384  
   385  			By("waiting for pvtdata to be reconciled on org2peer1")
   386  			waitForMarblePvtdataReconciliation(setup.network, org2peer1, testchannelID, newlifecycleChaincode.Name, []string{"marble2"})
   387  
   388  			// verify pvtdata reconciliation after joinbysnapshot
   389  			By("verifying marble2 pvtdata reconciliation on org2peer1")
   390  			assertPvtdataPresencePerCollectionConfig1(setup.network, newlifecycleChaincode.Name, "marble2", org2peer1)
   391  			By("verifying the new ownership of marble2")
   392  			marblechaincodeutil.AssertOwnershipInCollectionM(setup.network, testchannelID, newlifecycleChaincode.Name, "marble2", "jerry", org2peer1)
   393  
   394  			By("verifying marble1 does not exist")
   395  			marblechaincodeutil.AssertDoesNotExistInCollectionM(setup.network, testchannelID, newlifecycleChaincode.Name, "marble1", org2peer1)
   396  			marblechaincodeutil.AssertDoesNotExistInCollectionMPD(setup.network, testchannelID, newlifecycleChaincode.Name, "marble1", org2peer1)
   397  
   398  			// test 3: submit a request to generate snapshot again on a peer (org2peer1) bootstrapped from a snapshot
   399  			blockNumForNextSnapshot := nwo.GetLedgerHeight(setup.network, org2peer1, testchannelID)
   400  			By(fmt.Sprintf("generating a snapshot at blockNum %d on org2peer1 that was bootstrapped by a snapshot", blockNumForNextSnapshot))
   401  			submitSnapshotRequest(setup.network, testchannelID, blockNumForNextSnapshot, org2peer1, false, "Snapshot request submitted successfully")
   402  
   403  			// block for marble3 tx is in snapshot, but block for marble4 tx is post snapshot
   404  			By("adding marble3")
   405  			marblechaincodeutil.AddMarble(setup.network, setup.orderer, testchannelID, newlifecycleChaincode.Name,
   406  				`{"name":"marble3", "color":"blue", "size":35, "owner":"tom", "price":99}`, org2peer1)
   407  			assertPvtdataPresencePerCollectionConfig1(setup.network, newlifecycleChaincode.Name, "marble3")
   408  
   409  			By("adding marble4")
   410  			marblechaincodeutil.AddMarble(setup.network, setup.orderer, testchannelID, newlifecycleChaincode.Name,
   411  				`{"name":"marble4", "color":"blue", "size":35, "owner":"tom", "price":99}`, org2peer1)
   412  			assertPvtdataPresencePerCollectionConfig1(setup.network, newlifecycleChaincode.Name, "marble4")
   413  
   414  			By("verifying snapshot completed on org2peer1")
   415  			verifyNoPendingSnapshotRequest(setup.network, org2peer1, testchannelID)
   416  			nextSnapshotDir := filepath.Join(setup.network.PeerDir(org2peer1), "filesystem", "snapshots", "completed", testchannelID, strconv.Itoa(blockNumForNextSnapshot))
   417  
   418  			// stop all the peers and only restart org2peer1
   419  			setup.stopPeers()
   420  			setup.startPeer(org2peer1)
   421  			setup.peers = []*nwo.Peer{org2peer1}
   422  			setup.network.Peers = setup.peers
   423  
   424  			// test 4: bootstrap a new peer org2peer2 by snapshot and verify pvtdata reconciliation
   425  			By("starting a peer (org2peer2) in existing org (leveldb)")
   426  			org2peer2, _ := startPeer(setup, "Org2", "peer2", testchannelID, false)
   427  
   428  			By("installing new lifecycle chaincode2 on peer org2peer2")
   429  			nwo.InstallChaincode(setup.network, newlifecycleChaincode, org2peer2)
   430  
   431  			By("joining peer org2peer2 to the channel by snapshot")
   432  			joinBySnapshot(setup.network, setup.orderer, org2peer2, testchannelID, nextSnapshotDir, blockNumForNextSnapshot)
   433  
   434  			By("waiting for pvtdata to be reconciled on org2peer2")
   435  			waitForMarblePvtdataReconciliation(setup.network, org2peer2, testchannelID, newlifecycleChaincode.Name, []string{"marble2", "marble3", "marble4"})
   436  
   437  			By("verifying marble4 pvtdata reconciliation on org2peer2")
   438  			assertPvtdataPresencePerCollectionConfig1(setup.network, newlifecycleChaincode.Name, "marble4", org2peer2)
   439  			By("verifying marble3 pvtdata reconciliation on org2peer2")
   440  			assertPvtdataPresencePerCollectionConfig1(setup.network, newlifecycleChaincode.Name, "marble3", org2peer2)
   441  			By("verifying marble2 pvtdata reconciliation on org2peer2")
   442  			assertPvtdataPresencePerCollectionConfig1(setup.network, newlifecycleChaincode.Name, "marble2", org2peer2)
   443  			By("verifying the new ownership of marble2")
   444  			marblechaincodeutil.AssertOwnershipInCollectionM(setup.network, testchannelID, newlifecycleChaincode.Name, "marble2", "jerry", org2peer2)
   445  			By("verifying marble1 does not exist")
   446  			marblechaincodeutil.AssertDoesNotExistInCollectionM(setup.network, testchannelID, newlifecycleChaincode.Name, "marble1", org2peer2)
   447  			marblechaincodeutil.AssertDoesNotExistInCollectionMPD(setup.network, testchannelID, newlifecycleChaincode.Name, "marble1", org2peer2)
   448  
   449  			// test 5: bootstrap a new peer Org2peer3 from genesis block to verify pvtdata reconciliation
   450  			By("startinging a peer Org2peer3 in an new org (leveldb)")
   451  			org2peer3, _ := startPeer(setup, "Org2", "peer3", testchannelID, false)
   452  
   453  			By("installing newlifecycleChaincode on new peer Org2peer3")
   454  			nwo.InstallChaincode(setup.network, newlifecycleChaincode, org2peer3)
   455  
   456  			By("joining peer Org2peer3 to the channel by genesis block")
   457  			setup.network.JoinChannel(testchannelID, setup.orderer, org2peer3)
   458  
   459  			By("waiting for the new peer to have the same ledger height")
   460  			channelHeight := nwo.GetMaxLedgerHeight(setup.network, testchannelID, org2peer1)
   461  			nwo.WaitUntilEqualLedgerHeight(setup.network, testchannelID, channelHeight, org2peer3)
   462  
   463  			By("waiting for pvtdata to be reconciled on org2peer3")
   464  			waitForMarblePvtdataReconciliation(setup.network, org2peer3, testchannelID, newlifecycleChaincode.Name, []string{"marble2", "marble3", "marble4"})
   465  
   466  			By("verifying marble4 pvtdata reconciliation on org2peer3")
   467  			assertPvtdataPresencePerCollectionConfig1(setup.network, newlifecycleChaincode.Name, "marble4", org2peer3)
   468  			By("verifying marble3 pvtdata reconciliation on org2peer3")
   469  			assertPvtdataPresencePerCollectionConfig1(setup.network, newlifecycleChaincode.Name, "marble3", org2peer3)
   470  			By("verifying marble2 pvtdata reconciliation on org2peer3")
   471  			assertPvtdataPresencePerCollectionConfig1(setup.network, newlifecycleChaincode.Name, "marble2", org2peer3)
   472  			By("verifying the new ownership of marble2")
   473  			marblechaincodeutil.AssertOwnershipInCollectionM(setup.network, testchannelID, newlifecycleChaincode.Name, "marble2", "jerry", org2peer3)
   474  			By("verifying marble1 does not exist")
   475  			marblechaincodeutil.AssertDoesNotExistInCollectionM(setup.network, testchannelID, newlifecycleChaincode.Name, "marble1", org2peer3)
   476  			marblechaincodeutil.AssertDoesNotExistInCollectionMPD(setup.network, testchannelID, newlifecycleChaincode.Name, "marble1", org2peer3)
   477  
   478  			// verify pvtdata hash on bootstrapped peers
   479  			peers := []*nwo.Peer{org2peer1, org2peer2, org2peer3}
   480  			for i := 2; i <= 4; i++ {
   481  				name := fmt.Sprintf("marble%d", i)
   482  				owner := "tom"
   483  				if name == "marble2" {
   484  					owner = "jerry"
   485  				}
   486  
   487  				By(fmt.Sprintf("verifying getMarbleHash for %s from all peers that has the chaincode instantiated", name))
   488  				expectedBytes := util.ComputeStringHash(fmt.Sprintf(`{"docType":"marble","name":"%s","color":"blue","size":35,"owner":"%s"}`, name, owner))
   489  				marblechaincodeutil.AssertMarblesPrivateHashM(setup.network, testchannelID, newlifecycleChaincode.Name, name, expectedBytes, peers)
   490  
   491  				By(fmt.Sprintf("verifying getMarblePrivateDetailsHash for %s from all peers that has the chaincode instantiated", name))
   492  				expectedBytes = util.ComputeStringHash(fmt.Sprintf(`{"docType":"marblePrivateDetails","name":"%s","price":99}`, name))
   493  				marblechaincodeutil.AssertMarblesPrivateDetailsHashMPD(setup.network, testchannelID, newlifecycleChaincode.Name, name, expectedBytes, peers)
   494  			}
   495  		})
   496  	})
   497  })
   498  
   499  func configPeerWithCouchDB(s *setup, peer *nwo.Peer) ifrit.Process {
   500  	couchDB := &runner.CouchDB{}
   501  	couchProc := ifrit.Invoke(couchDB)
   502  	Eventually(couchProc.Ready(), runner.DefaultStartTimeout).Should(BeClosed())
   503  	Consistently(couchProc.Wait()).ShouldNot(Receive())
   504  
   505  	core := s.network.ReadPeerConfig(peer)
   506  	core.Ledger.State.StateDatabase = "CouchDB"
   507  	core.Ledger.State.CouchDBConfig.CouchDBAddress = couchDB.Address()
   508  
   509  	By("configuring peer to couchdb address " + couchDB.Address())
   510  	s.network.WritePeerConfig(peer, core)
   511  
   512  	return couchProc
   513  }
   514  
   515  // initAndStartFourOrgsNetwork creates a network with multiple orgs.
   516  // Initially only start Org1.peer0 and Org2.peer0 are started and join the channel.
   517  func initAndStartFourOrgsNetwork() *setup {
   518  	var err error
   519  	testDir, err := ioutil.TempDir("", "snapshot")
   520  	Expect(err).NotTo(HaveOccurred())
   521  
   522  	client, err := docker.NewClientFromEnv()
   523  	Expect(err).NotTo(HaveOccurred())
   524  
   525  	config := nwo.BasicSolo()
   526  
   527  	config.Channels = []*nwo.Channel{
   528  		{Name: testchannelID, Profile: "TwoOrgsChannel"},
   529  	}
   530  
   531  	for _, peer := range config.Peers {
   532  		peer.Channels = []*nwo.PeerChannel{
   533  			{Name: testchannelID, Anchor: true},
   534  		}
   535  	}
   536  
   537  	// add more peers to Org1 and Org2
   538  	config.Peers = append(
   539  		config.Peers,
   540  		&nwo.Peer{
   541  			Name:         "peer1",
   542  			Organization: "Org1",
   543  			Channels:     []*nwo.PeerChannel{},
   544  		},
   545  		&nwo.Peer{
   546  			Name:         "peer1",
   547  			Organization: "Org2",
   548  			Channels:     []*nwo.PeerChannel{},
   549  		},
   550  		&nwo.Peer{
   551  			Name:         "peer2",
   552  			Organization: "Org2",
   553  			Channels:     []*nwo.PeerChannel{},
   554  		},
   555  		&nwo.Peer{
   556  			Name:         "peer3",
   557  			Organization: "Org2",
   558  			Channels:     []*nwo.PeerChannel{},
   559  		},
   560  	)
   561  
   562  	// add org3 with one peer
   563  	config.Organizations = append(config.Organizations, &nwo.Organization{
   564  		Name:          "Org3",
   565  		MSPID:         "Org3MSP",
   566  		Domain:        "org3.example.com",
   567  		EnableNodeOUs: true,
   568  		Users:         2,
   569  		CA:            &nwo.CA{Hostname: "ca"},
   570  	})
   571  	config.Consortiums[0].Organizations = append(config.Consortiums[0].Organizations, "Org3")
   572  	config.Profiles[1].Organizations = append(config.Profiles[1].Organizations, "Org3")
   573  	config.Peers = append(config.Peers, &nwo.Peer{
   574  		Name:         "peer0",
   575  		Organization: "Org3",
   576  		Channels: []*nwo.PeerChannel{
   577  			{Name: testchannelID, Anchor: true},
   578  		},
   579  	})
   580  
   581  	// add org4 with one peer
   582  	config.Organizations = append(config.Organizations, &nwo.Organization{
   583  		Name:          "Org4",
   584  		MSPID:         "Org4MSP",
   585  		Domain:        "org4.example.com",
   586  		EnableNodeOUs: true,
   587  		Users:         2,
   588  		CA:            &nwo.CA{Hostname: "ca"},
   589  	})
   590  	config.Consortiums[0].Organizations = append(config.Consortiums[0].Organizations, "Org4")
   591  	config.Profiles[1].Organizations = append(config.Profiles[1].Organizations, "Org4")
   592  	config.Peers = append(config.Peers, &nwo.Peer{
   593  		Name:         "peer0",
   594  		Organization: "Org4",
   595  		Channels: []*nwo.PeerChannel{
   596  			{Name: testchannelID, Anchor: true},
   597  		},
   598  	})
   599  
   600  	n := nwo.New(config, testDir, client, StartPort(), components)
   601  	n.GenerateConfigTree()
   602  	n.Bootstrap()
   603  
   604  	// set ReconcileSleepInterval to 1 second to reconcile pvtdata faster
   605  	// set DeprioritizedDataReconcilerInterval to 2 seconds to resume reconciliation quickly
   606  	// to prevent CI flake in case peer connection is temporarily lost.
   607  	for _, p := range n.Peers {
   608  		core := n.ReadPeerConfig(p)
   609  		core.Peer.Gossip.PvtData.ReconcileSleepInterval = 1 * time.Second
   610  		core.Ledger.PvtdataStore.DeprioritizedDataReconcilerInterval = 2 * time.Second
   611  		n.WritePeerConfig(p, core)
   612  	}
   613  
   614  	// set org2peer2 and org2peer3's gossip bootstrap endpoints pointing to org2peer1
   615  	org2peer1 := n.Peer("Org2", "peer1")
   616  	for _, p := range []*nwo.Peer{n.Peer("Org2", "peer2"), n.Peer("Org2", "peer3")} {
   617  		core := n.ReadPeerConfig(p)
   618  		core.Peer.Gossip.Bootstrap = n.PeerAddress(org2peer1, nwo.ListenPort)
   619  		n.WritePeerConfig(p, core)
   620  	}
   621  
   622  	// only keep Org1.peer0 and Org2.peer0 so we can add other peers back later to test join channel by snapshot
   623  	peers := []*nwo.Peer{}
   624  	for _, p := range n.Peers {
   625  		if p.ID() == "Org1.peer0" || p.ID() == "Org2.peer0" {
   626  			peers = append(peers, p)
   627  		}
   628  	}
   629  	n.Peers = peers
   630  
   631  	setup := &setup{
   632  		testDir:   testDir,
   633  		network:   n,
   634  		peers:     peers,
   635  		channelID: testchannelID,
   636  	}
   637  	Expect(setup.testDir).To(Equal(setup.network.RootDir))
   638  
   639  	By("starting broker and orderer")
   640  	setup.startBrokerAndOrderer()
   641  
   642  	By("starting peers")
   643  	setup.startPeers()
   644  
   645  	orderer := n.Orderer("orderer")
   646  	setup.orderer = orderer
   647  
   648  	By("creating and joining testchannel")
   649  	n.CreateAndJoinChannel(orderer, testchannelID)
   650  	n.UpdateChannelAnchors(orderer, testchannelID)
   651  
   652  	By("verifying membership for testchannel")
   653  	n.VerifyMembership(n.PeersWithChannel(testchannelID), testchannelID)
   654  
   655  	return setup
   656  }
   657  
   658  // verifySnapshotRequestCmds invokes snapshot commands and verify the expected rersults.
   659  // At the end, there will be no pending request.
   660  func verifySnapshotRequestCmds(n *nwo.Network, peer *nwo.Peer, channel string, blockNum int) {
   661  	By("submitting snaphost request for a future blockNum, expecting success")
   662  	submitSnapshotRequest(n, channel, blockNum+10, peer, false, "Snapshot request submitted successfully")
   663  	By("submitting snaphost request at same blockNum again, expecting error")
   664  	submitSnapshotRequest(n, channel, blockNum+10, peer, true,
   665  		fmt.Sprintf("duplicate snapshot request for block number %d", blockNum+10))
   666  	By("submitting snaphost request for a previous blockNum, expecting error")
   667  	submitSnapshotRequest(n, channel, blockNum-1, peer, true,
   668  		fmt.Sprintf("requested snapshot for block number %d cannot be less than the last committed block number %d", blockNum-1, blockNum))
   669  	By("listing pending snaphost requests, expecting success")
   670  	pendingRequests := listPendingSnapshotRequests(n, channel, peer, n.PeerAddress(peer, nwo.ListenPort), false)
   671  	Expect(pendingRequests).To(ContainSubstring(fmt.Sprintf("Successfully got pending snapshot requests: [%d]\n", blockNum+10)))
   672  	By("canceling a pending snaphost request, expecting success")
   673  	cancelSnapshotRequest(n, channel, blockNum+10, peer, n.PeerAddress(peer, nwo.ListenPort), false, "Snapshot request cancelled successfully")
   674  	By("canceling the same snaphost request, expecting error")
   675  	cancelSnapshotRequest(n, channel, blockNum+10, peer, n.PeerAddress(peer, nwo.ListenPort), true,
   676  		fmt.Sprintf("no snapshot request exists for block number %d", blockNum+10))
   677  	By("listing pending snaphost requests, expecting success")
   678  	pendingRequests = listPendingSnapshotRequests(n, channel, peer, n.PeerAddress(peer, nwo.ListenPort), false)
   679  	Expect(pendingRequests).To(ContainSubstring("Successfully got pending snapshot requests: []\n"))
   680  }
   681  
   682  func generateAndCompareSnapshots(n *nwo.Network, peer1, peer2 *nwo.Peer, blockNumForSnapshot int) (string, string) {
   683  	By(fmt.Sprintf("submitting snapshot request at blockNum %d on peer %s", blockNumForSnapshot, peer1.ID()))
   684  	submitSnapshotRequest(n, testchannelID, blockNumForSnapshot, peer1, false, "Snapshot request submitted successfully")
   685  
   686  	By(fmt.Sprintf("submitting snaphost request at blockNum %d on peer %s", blockNumForSnapshot, peer2.ID()))
   687  	submitSnapshotRequest(n, testchannelID, blockNumForSnapshot, peer2, false, "Snapshot request submitted successfully")
   688  
   689  	By("verifying snapshot completed on peer1")
   690  	verifyNoPendingSnapshotRequest(n, peer1, testchannelID)
   691  
   692  	By("verifying snapshot completed on peer2")
   693  	verifyNoPendingSnapshotRequest(n, peer2, testchannelID)
   694  
   695  	By("comparing snapshot metadata generated on different peers for the same block number")
   696  	snapshotDir1 := filepath.Join(n.PeerDir(peer1), "filesystem", "snapshots", "completed", testchannelID, strconv.Itoa(blockNumForSnapshot))
   697  	snapshotDir2 := filepath.Join(n.PeerDir(peer2), "filesystem", "snapshots", "completed", testchannelID, strconv.Itoa(blockNumForSnapshot))
   698  	compareSnapshotMetadata(snapshotDir1, snapshotDir2)
   699  
   700  	return snapshotDir1, snapshotDir2
   701  }
   702  
   703  func verifyNoPendingSnapshotRequest(n *nwo.Network, peer *nwo.Peer, channelID string) {
   704  	checkPending := func() []byte {
   705  		return listPendingSnapshotRequests(n, channelID, peer, n.PeerAddress(peer, nwo.ListenPort), false)
   706  	}
   707  	Eventually(checkPending, n.EventuallyTimeout, 10*time.Second).Should(ContainSubstring("Successfully got pending snapshot requests: []\n"))
   708  }
   709  
   710  func submitSnapshotRequest(n *nwo.Network, channel string, blockNum int, peer *nwo.Peer, expectedError bool, expectedMsg string) {
   711  	sess, err := n.PeerAdminSession(peer, commands.SnapshotSubmitRequest{
   712  		ChannelID:   channel,
   713  		BlockNumber: strconv.Itoa(blockNum),
   714  		ClientAuth:  n.ClientAuthRequired,
   715  		PeerAddress: n.PeerAddress(peer, nwo.ListenPort),
   716  	})
   717  	Expect(err).NotTo(HaveOccurred())
   718  	if !expectedError {
   719  		Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   720  		Expect(sess).To(gbytes.Say(expectedMsg))
   721  	} else {
   722  		Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(1))
   723  		Expect(sess.Err).To(gbytes.Say(expectedMsg))
   724  	}
   725  }
   726  
   727  func cancelSnapshotRequest(n *nwo.Network, channel string, blockNum int, peer *nwo.Peer, peerAddress string, expectedError bool, expectedMsg string) {
   728  	sess, err := n.PeerAdminSession(peer, commands.SnapshotCancelRequest{
   729  		ChannelID:   channel,
   730  		BlockNumber: strconv.Itoa(blockNum),
   731  		ClientAuth:  n.ClientAuthRequired,
   732  		PeerAddress: peerAddress,
   733  	})
   734  	Expect(err).NotTo(HaveOccurred())
   735  	if !expectedError {
   736  		Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   737  		Expect(sess).To(gbytes.Say(expectedMsg))
   738  	} else {
   739  		Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(1))
   740  		Expect(sess.Err).To(gbytes.Say(expectedMsg))
   741  	}
   742  }
   743  
   744  func listPendingSnapshotRequests(n *nwo.Network, channel string, peer *nwo.Peer, peerAddress string, expectedError bool) []byte {
   745  	sess, err := n.PeerAdminSession(peer, commands.SnapshotListPending{
   746  		ChannelID:   channel,
   747  		ClientAuth:  n.ClientAuthRequired,
   748  		PeerAddress: peerAddress,
   749  	})
   750  	Expect(err).NotTo(HaveOccurred())
   751  
   752  	if expectedError {
   753  		Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(1))
   754  		return sess.Err.Contents()
   755  	}
   756  	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   757  	return sess.Buffer().Contents()
   758  }
   759  
   760  func compareSnapshotMetadata(snapshotDir1, snapshotDir2 string) {
   761  	for _, snapshotDir := range []string{snapshotDir1, snapshotDir2} {
   762  		By("verifying snapshot dir exists: " + snapshotDir)
   763  		Expect(snapshotDir).To(BeADirectory())
   764  	}
   765  
   766  	// compare metadata files
   767  	for _, file := range []string{"_snapshot_signable_metadata.json", "_snapshot_additional_metadata.json"} {
   768  		By("comparing metadata file from snapshots on multiple peers: " + file)
   769  		fileContent1, err := ioutil.ReadFile(filepath.Join(snapshotDir1, file))
   770  		Expect(err).NotTo(HaveOccurred())
   771  		fileContent2, err := ioutil.ReadFile(filepath.Join(snapshotDir2, file))
   772  		Expect(err).NotTo(HaveOccurred())
   773  		Expect(fileContent1).To(Equal(fileContent2))
   774  	}
   775  }
   776  
   777  // startPeer starts a peer to prepare for join channel test
   778  func startPeer(s *setup, orgName, peerName, channelID string, useCouchDB bool) (*nwo.Peer, ifrit.Process) {
   779  	peer := &nwo.Peer{
   780  		Name:         peerName,
   781  		Organization: orgName,
   782  		Channels: []*nwo.PeerChannel{
   783  			{Name: channelID},
   784  		},
   785  	}
   786  	s.network.Peers = append(s.network.Peers, peer)
   787  	s.peers = append(s.peers, peer)
   788  
   789  	var couchProc ifrit.Process
   790  	if useCouchDB {
   791  		By("starting couch process and configuring it for peer " + peer.ID())
   792  		couchProc = configPeerWithCouchDB(s, peer)
   793  	}
   794  
   795  	By("starting the new peer " + peer.ID())
   796  	s.startPeer(peer)
   797  
   798  	return peer, couchProc
   799  }
   800  
   801  func joinBySnapshot(n *nwo.Network, orderer *nwo.Orderer, peer *nwo.Peer, channelID string, snapshotDir string, lastBlockInSnapshot int) {
   802  	channelHeight := nwo.GetMaxLedgerHeight(n, channelID, n.PeersWithChannel(channelID)...)
   803  
   804  	By(fmt.Sprintf("joining a peer via snapshot %s", snapshotDir))
   805  	n.JoinChannelBySnapshot(snapshotDir, peer)
   806  
   807  	By("calling JoinBySnapshotStatus")
   808  	checkStatus := func() string { return n.JoinBySnapshotStatus(peer) }
   809  	Eventually(checkStatus, n.EventuallyTimeout, 10*time.Second).Should(ContainSubstring("No joinbysnapshot operation is in progress"))
   810  
   811  	By("waiting for the new peer to have the same ledger height")
   812  	nwo.WaitUntilEqualLedgerHeight(n, channelID, channelHeight, peer)
   813  
   814  	By("verifying blockchain info on peer " + peer.ID())
   815  	sess, err := n.PeerUserSession(peer, "Admin", commands.ChannelInfo{
   816  		ChannelID: channelID,
   817  	})
   818  	Expect(err).NotTo(HaveOccurred())
   819  	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   820  	channelInfoStr := strings.TrimPrefix(string(sess.Buffer().Contents()[:]), "Blockchain info:")
   821  	bcInfo := cb.BlockchainInfo{}
   822  	err = json.Unmarshal([]byte(channelInfoStr), &bcInfo)
   823  	Expect(err).NotTo(HaveOccurred())
   824  	Expect(bcInfo.Height).To(Equal(uint64(channelHeight)))
   825  	Expect(bcInfo.BootstrappingSnapshotInfo.LastBlockInSnapshot).To(Equal(uint64(lastBlockInSnapshot)))
   826  }
   827  
   828  func verifyQSCC(n *nwo.Network, peer *nwo.Peer, channelID string, lastBlockInSnapshot int, txidBeforeSnapshot string) {
   829  	peerID := peer.ID()
   830  	By("verifying qscc GetBlockByNumber returns an error for block number before snapshot on peer " + peerID)
   831  	resp := callQSCC(n, peer, "qscc", "GetBlockByNumber", 1, channelID, strconv.Itoa(lastBlockInSnapshot))
   832  	Expect(resp).To(ContainSubstring(fmt.Sprintf("The ledger is bootstrapped from a snapshot. First available block = [%d]", lastBlockInSnapshot+1)))
   833  
   834  	By("verifying qscc GetBlockByNumber succeeds for a block number after snapshot on peer " + peerID)
   835  	callQSCC(n, peer, "qscc", "GetBlockByNumber", 0, channelID, fmt.Sprintf("%d", lastBlockInSnapshot+1))
   836  
   837  	By("verifying qscc GetBlockByTxID returns an error for a txid before snapshot on peer " + peerID)
   838  	resp = callQSCC(n, peer, "qscc", "GetBlockByTxID", 1, channelID, txidBeforeSnapshot)
   839  	Expect(resp).To(ContainSubstring(fmt.Sprintf("Failed to get block for txID %s, error details for the TXID [%s] not available. Ledger bootstrapped from a snapshot. First available block = [%d]",
   840  		txidBeforeSnapshot, txidBeforeSnapshot, lastBlockInSnapshot+1)))
   841  
   842  	By("verifying qscc GetBlockByTxID succeeds for a txid after snapshot on peer " + peerID)
   843  	_, newTxid := getTxFromLastBlock(n, peer)
   844  	callQSCC(n, peer, "qscc", "GetBlockByTxID", 0, channelID, newTxid)
   845  
   846  	By("verifying qscc GetTransactionByID returns an error for a txid before snapshot on peer " + peerID)
   847  	resp = callQSCC(n, peer, "qscc", "GetTransactionByID", 1, channelID, txidBeforeSnapshot)
   848  	Expect(resp).To(ContainSubstring(fmt.Sprintf("Failed to get transaction with id %s, error details for the TXID [%s] not available. Ledger bootstrapped from a snapshot. First available block = [%d]",
   849  		txidBeforeSnapshot, txidBeforeSnapshot, lastBlockInSnapshot+1)))
   850  
   851  	By("verifying qscc GetTransactionByID succeeds for a txid after snapshot on peer " + peerID)
   852  	callQSCC(n, peer, "qscc", "GetTransactionByID", 0, channelID, newTxid)
   853  }
   854  
   855  func installAndApproveChaincode(n *nwo.Network, orderer *nwo.Orderer, peer *nwo.Peer, channelID string, chaincode nwo.Chaincode, orgNames []string) {
   856  	nwo.InstallChaincode(n, chaincode, peer)
   857  	checkOrgs := make([]*nwo.Organization, len(orgNames))
   858  	for i, orgName := range orgNames {
   859  		checkOrgs[i] = n.Organization(orgName)
   860  	}
   861  	nwo.ApproveChaincodeForMyOrg(n, channelID, orderer, chaincode, n.PeersInOrg(peer.Organization)...)
   862  	nwo.EnsureChaincodeCommitted(n, channelID, chaincode.Name, chaincode.Version, chaincode.Sequence, checkOrgs, peer)
   863  }
   864  
   865  // getTxFromLastBlock gets a transaction id from the latest block that has been
   866  // marshaled and stored on the filesystem
   867  func getTxFromLastBlock(n *nwo.Network, peer *nwo.Peer) (*cb.Envelope, string) {
   868  	blockfile := filepath.Join(n.RootDir, "newest_block.pb")
   869  	fetchNewest := commands.ChannelFetch{
   870  		ChannelID:  "testchannel",
   871  		Block:      "newest",
   872  		OutputFile: blockfile,
   873  	}
   874  	sess, err := n.PeerAdminSession(peer, fetchNewest)
   875  	Expect(err).NotTo(HaveOccurred())
   876  	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   877  	Expect(sess.Err).To(gbytes.Say("Received block: "))
   878  
   879  	block := nwo.UnmarshalBlockFromFile(blockfile)
   880  	txEnvelope, err := protoutil.UnmarshalEnvelope(block.Data.Data[0])
   881  	Expect(err).NotTo(HaveOccurred())
   882  	txID, err := protoutil.GetOrComputeTxIDFromEnvelope(block.Data.Data[0])
   883  	Expect(err).NotTo(HaveOccurred())
   884  
   885  	return txEnvelope, txID
   886  }
   887  
   888  func invokeAndQueryKVExecutorChaincode(n *nwo.Network, orderer *nwo.Orderer, channelID string, chaincode nwo.Chaincode, kvdata []kvexecutor.KVData, peers ...*nwo.Peer) {
   889  	By("invoking kvexecutor chaincode")
   890  	writeInputBytes, err := json.Marshal(kvdata)
   891  	Expect(err).NotTo(HaveOccurred())
   892  	writeInputBase64 := base64.StdEncoding.EncodeToString(writeInputBytes)
   893  
   894  	peerAddresses := make([]string, 0)
   895  	for _, peer := range peers {
   896  		peerAddresses = append(peerAddresses, n.PeerAddress(peer, nwo.ListenPort))
   897  	}
   898  
   899  	invokeCommand := commands.ChaincodeInvoke{
   900  		ChannelID:     channelID,
   901  		Orderer:       n.OrdererAddress(orderer, nwo.ListenPort),
   902  		Name:          chaincode.Name,
   903  		Ctor:          fmt.Sprintf(`{"Args":["readWriteKVs","%s","%s"]}`, "", writeInputBase64),
   904  		PeerAddresses: peerAddresses,
   905  		WaitForEvent:  true,
   906  	}
   907  	invokeChaincode(n, peers[0], invokeCommand)
   908  
   909  	channelPeers := n.PeersWithChannel(channelID)
   910  	nwo.WaitUntilEqualLedgerHeight(n, channelID, nwo.GetLedgerHeight(n, peers[0], channelID), channelPeers...)
   911  
   912  	By("querying kvexecutor chaincode")
   913  	expectedMsg, err := json.Marshal(kvdata)
   914  	Expect(err).NotTo(HaveOccurred())
   915  
   916  	readInputBytes, err := json.Marshal(kvdata)
   917  	Expect(err).NotTo(HaveOccurred())
   918  	readInputBase64 := base64.StdEncoding.EncodeToString(readInputBytes)
   919  
   920  	querycommand := commands.ChaincodeQuery{
   921  		ChannelID: channelID,
   922  		Name:      chaincode.Name,
   923  		Ctor:      fmt.Sprintf(`{"Args":["readWriteKVs","%s","%s"]}`, readInputBase64, ""),
   924  	}
   925  	queryChaincode(n, peers[0], querycommand, string(expectedMsg), true)
   926  }
   927  
   928  func invokeChaincode(n *nwo.Network, peer *nwo.Peer, command commands.ChaincodeInvoke) {
   929  	sess, err := n.PeerUserSession(peer, "User1", command)
   930  	Expect(err).NotTo(HaveOccurred())
   931  	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   932  	Expect(sess.Err).To(gbytes.Say("Chaincode invoke successful."))
   933  }
   934  
   935  func queryChaincode(n *nwo.Network, peer *nwo.Peer, command commands.ChaincodeQuery, expectedMessage string, expectSuccess bool) {
   936  	sess, err := n.PeerUserSession(peer, "User1", command)
   937  	Expect(err).NotTo(HaveOccurred())
   938  	if expectSuccess {
   939  		Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   940  		Expect(sess).To(gbytes.Say(expectedMessage))
   941  	} else {
   942  		Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit())
   943  		Expect(sess.Err).To(gbytes.Say(expectedMessage))
   944  	}
   945  }
   946  
   947  func callQSCC(n *nwo.Network, peer *nwo.Peer, scc, operation string, retCode int, args ...string) []byte {
   948  	args = append([]string{operation}, args...)
   949  	chaincodeQuery := commands.ChaincodeQuery{
   950  		ChannelID: testchannelID,
   951  		Name:      scc,
   952  		Ctor:      toCLIChaincodeArgs(args...),
   953  	}
   954  
   955  	sess, err := n.PeerAdminSession(peer, chaincodeQuery)
   956  	Expect(err).NotTo(HaveOccurred())
   957  	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(retCode))
   958  	if retCode != 0 {
   959  		return sess.Err.Contents()
   960  	}
   961  	return sess.Out.Contents()
   962  }
   963  
   964  func toCLIChaincodeArgs(args ...string) string {
   965  	type cliArgs struct {
   966  		Args []string
   967  	}
   968  	cArgs := &cliArgs{Args: args}
   969  	cArgsJSON, err := json.Marshal(cArgs)
   970  	Expect(err).NotTo(HaveOccurred())
   971  	return string(cArgsJSON)
   972  }
   973  
   974  // waitForMarblePvtdataReconciliation queries the chaincode until it returns a success exit code, which means the data is available.
   975  func waitForMarblePvtdataReconciliation(n *nwo.Network, peer *nwo.Peer, channelID, chaincodeName string, marbleNames []string) {
   976  	for _, marbleName := range marbleNames {
   977  		for _, funcName := range []string{"readMarble", "readMarblePrivateDetails"} {
   978  			query := fmt.Sprintf(`{"Args":["%s","%s"]}`, funcName, marbleName)
   979  			command := commands.ChaincodeQuery{
   980  				ChannelID: channelID,
   981  				Name:      chaincodeName,
   982  				Ctor:      query,
   983  			}
   984  			queryData := func() int {
   985  				sess, err := n.PeerUserSession(peer, "User1", command)
   986  				Expect(err).NotTo(HaveOccurred())
   987  				return sess.Wait(n.EventuallyTimeout).ExitCode()
   988  			}
   989  			Eventually(queryData, n.EventuallyTimeout).Should(Equal(0))
   990  		}
   991  	}
   992  }
   993  
   994  func assertPvtdataPresencePerCollectionConfig1(n *nwo.Network, chaincodeName, marbleName string, peers ...*nwo.Peer) {
   995  	if len(peers) == 0 {
   996  		peers = n.Peers
   997  	}
   998  	for _, peer := range peers {
   999  		switch peer.Organization {
  1000  		case "Org1":
  1001  			By("asserting collection data M in org1 peer " + peer.ID() + " for " + marbleName)
  1002  			marblechaincodeutil.AssertPresentInCollectionM(n, testchannelID, chaincodeName, marbleName, peer)
  1003  			By("asserting no collection data MPD in org1 peer " + peer.ID() + " for " + marbleName)
  1004  			marblechaincodeutil.AssertNotPresentInCollectionMPD(n, testchannelID, chaincodeName, marbleName, peer)
  1005  
  1006  		case "Org2":
  1007  			By("asserting collection data M in org2 peer " + peer.ID() + " for " + marbleName)
  1008  			marblechaincodeutil.AssertPresentInCollectionM(n, testchannelID, chaincodeName, marbleName, peer)
  1009  			By("asserting collection data MPD in org2 peer " + peer.ID() + " for " + marbleName)
  1010  			marblechaincodeutil.AssertPresentInCollectionMPD(n, testchannelID, chaincodeName, marbleName, peer)
  1011  
  1012  		case "Org3":
  1013  			By("asserting no collection data M in org3 peer " + peer.ID() + " for " + marbleName)
  1014  			marblechaincodeutil.AssertNotPresentInCollectionM(n, testchannelID, chaincodeName, marbleName, peer)
  1015  			By("asserting collection data MPD in org3 peer " + peer.ID() + " for " + marbleName)
  1016  			marblechaincodeutil.AssertPresentInCollectionMPD(n, testchannelID, chaincodeName, marbleName, peer)
  1017  		}
  1018  	}
  1019  }
  1020  
  1021  // invokeWithoutPassingOrdererEndPoint does not pass orderer endpoint to a chaincode invoke command.
  1022  // As a result, the command will send a cscc query to the peer and cscc will return the orderer endpoint from the channel config.
  1023  func invokeWithoutPassingOrdererEndPoint(n *nwo.Network, peer *nwo.Peer, channelID, chaincodeName string, funcAndArgs ...string) {
  1024  	command := commands.ChaincodeInvoke{
  1025  		ChannelID: channelID,
  1026  		Name:      chaincodeName,
  1027  		Ctor:      prepareChaincodeInvokeArgs(funcAndArgs...),
  1028  		PeerAddresses: []string{
  1029  			n.PeerAddress(peer, nwo.ListenPort),
  1030  		},
  1031  		WaitForEvent: true,
  1032  	}
  1033  	invokeChaincode(n, peer, command)
  1034  	nwo.WaitUntilEqualLedgerHeight(n, channelID, nwo.GetLedgerHeight(n, peer, channelID), n.PeersWithChannel(channelID)...)
  1035  }
  1036  
  1037  // commitTx commits a transaction for a given transaction envelope
  1038  func commitTx(n *nwo.Network, orderer *nwo.Orderer, peer *nwo.Peer, channelID string, tx *cb.Envelope, txid string) error {
  1039  	By("getting the signer for user1 on peer " + peer.ID())
  1040  	signer := n.PeerUserSigner(peer, "User1")
  1041  
  1042  	By("creating the deliver client to peer " + peer.ID())
  1043  	pcc := n.PeerClientConn(peer)
  1044  	defer pcc.Close()
  1045  	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  1046  	defer cancel()
  1047  	df, err := pb.NewDeliverClient(pcc).DeliverFiltered(ctx)
  1048  	Expect(err).NotTo(HaveOccurred())
  1049  	defer df.CloseSend()
  1050  
  1051  	By("starting filtered delivery on peer " + peer.ID())
  1052  	deliverEnvelope, err := protoutil.CreateSignedEnvelope(
  1053  		cb.HeaderType_DELIVER_SEEK_INFO,
  1054  		channelID,
  1055  		signer,
  1056  		&ab.SeekInfo{
  1057  			Behavior: ab.SeekInfo_BLOCK_UNTIL_READY,
  1058  			Start: &ab.SeekPosition{
  1059  				Type: &ab.SeekPosition_Newest{Newest: &ab.SeekNewest{}},
  1060  			},
  1061  			Stop: &ab.SeekPosition{
  1062  				Type: &ab.SeekPosition_Specified{
  1063  					Specified: &ab.SeekSpecified{Number: math.MaxUint64},
  1064  				},
  1065  			},
  1066  		},
  1067  		0,
  1068  		0,
  1069  	)
  1070  	Expect(err).NotTo(HaveOccurred())
  1071  	err = df.Send(deliverEnvelope)
  1072  	Expect(err).NotTo(HaveOccurred())
  1073  
  1074  	By("creating orderer client and send transaction to the orderer" + orderer.ID())
  1075  	occ := n.OrdererClientConn(orderer)
  1076  	defer occ.Close()
  1077  	broadcastClient, err := ab.NewAtomicBroadcastClient(occ).Broadcast(context.Background())
  1078  	Expect(err).NotTo(HaveOccurred())
  1079  
  1080  	err = broadcastClient.Send(tx)
  1081  	Expect(err).NotTo(HaveOccurred())
  1082  
  1083  	By("waiting for deliver event on peer " + peer.ID())
  1084  	for {
  1085  		resp, err := df.Recv()
  1086  		if err != nil {
  1087  			return err
  1088  		}
  1089  		fb, ok := resp.Type.(*pb.DeliverResponse_FilteredBlock)
  1090  		if !ok {
  1091  			return fmt.Errorf("unexpected filtered block, received %T", resp.Type)
  1092  		}
  1093  		for _, tx := range fb.FilteredBlock.FilteredTransactions {
  1094  			if tx.Txid != txid {
  1095  				continue
  1096  			}
  1097  			if tx.TxValidationCode != pb.TxValidationCode_VALID {
  1098  				return fmt.Errorf("transaction invalidated with status (%s)", tx.TxValidationCode)
  1099  			}
  1100  			return nil
  1101  		}
  1102  	}
  1103  }