github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/integration/ledger/couchdb_indexes_test.go (about)

     1  /*
     2  Copyright IBM Corp All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package ledger
     8  
     9  import (
    10  	"encoding/json"
    11  	"io/ioutil"
    12  	"os"
    13  	"path/filepath"
    14  	"syscall"
    15  
    16  	docker "github.com/fsouza/go-dockerclient"
    17  	"github.com/osdi23p228/fabric/integration/nwo"
    18  	"github.com/osdi23p228/fabric/integration/nwo/commands"
    19  	"github.com/osdi23p228/fabric/integration/nwo/fabricconfig"
    20  	"github.com/osdi23p228/fabric/integration/runner"
    21  	. "github.com/onsi/ginkgo"
    22  	. "github.com/onsi/gomega"
    23  	"github.com/onsi/gomega/gbytes"
    24  	"github.com/onsi/gomega/gexec"
    25  	"github.com/tedsuo/ifrit"
    26  )
    27  
    28  const (
    29  	chaincodePathWithNoIndex = "github.com/osdi23p228/fabric/integration/chaincode/marbles/cmd"
    30  	chaincodePathWithIndex   = "github.com/osdi23p228/fabric/integration/chaincode/marbles/cmdwithindexspec"
    31  	chaincodePathWithIndexes = "github.com/osdi23p228/fabric/integration/chaincode/marbles/cmdwithindexspecs"
    32  )
    33  
    34  var (
    35  	filesWithIndex = map[string]string{
    36  		"../chaincode/marbles/cmdwithindexspec/META-INF/statedb/couchdb/indexes/indexSizeSortDoc.json": "metadata/statedb/couchdb/indexes/indexSizeSortDoc.json",
    37  	}
    38  
    39  	filesWithIndices = map[string]string{
    40  		"../chaincode/marbles/cmdwithindexspecs/META-INF/statedb/couchdb/indexes/indexSizeSortDoc.json":  "metadata/statedb/couchdb/indexes/indexSizeSortDoc.json",
    41  		"../chaincode/marbles/cmdwithindexspecs/META-INF/statedb/couchdb/indexes/indexColorSortDoc.json": "metadata/statedb/couchdb/indexes/indexColorSortDoc.json",
    42  	}
    43  )
    44  
    45  var _ = Describe("CouchDB indexes", func() {
    46  	var (
    47  		testDir string
    48  		client  *docker.Client
    49  		network *nwo.Network
    50  		orderer *nwo.Orderer
    51  		process ifrit.Process
    52  
    53  		couchAddr    string
    54  		couchDB      *runner.CouchDB
    55  		couchProcess ifrit.Process
    56  
    57  		legacyChaincode       nwo.Chaincode
    58  		newlifecycleChaincode nwo.Chaincode
    59  	)
    60  
    61  	BeforeEach(func() {
    62  		var err error
    63  		testDir, err = ioutil.TempDir("", "ledger")
    64  		Expect(err).NotTo(HaveOccurred())
    65  		client, err = docker.NewClientFromEnv()
    66  		Expect(err).NotTo(HaveOccurred())
    67  
    68  		network = nwo.New(nwo.FullSolo(), testDir, client, StartPort(), components)
    69  
    70  		cwd, err := os.Getwd()
    71  		Expect(err).NotTo(HaveOccurred())
    72  		network.ExternalBuilders = append(network.ExternalBuilders, fabricconfig.ExternalBuilder{
    73  			Path:                 filepath.Join(cwd, "..", "externalbuilders", "golang"),
    74  			Name:                 "external-golang",
    75  			PropagateEnvironment: []string{"GOPATH", "GOCACHE", "GOPROXY", "HOME", "PATH"},
    76  		})
    77  
    78  		network.GenerateConfigTree()
    79  
    80  		// configure only one of four peers (Org1, peer0) to use couchdb.
    81  		// Note that we do not support a channel with mixed DBs.
    82  		// However, for testing, it would be fine to use couchdb for one
    83  		// peer and sending all the couchdb related test queries to this peer
    84  		couchDB = &runner.CouchDB{}
    85  		couchProcess = ifrit.Invoke(couchDB)
    86  		Eventually(couchProcess.Ready(), runner.DefaultStartTimeout).Should(BeClosed())
    87  		Consistently(couchProcess.Wait()).ShouldNot(Receive())
    88  		couchAddr = couchDB.Address()
    89  		peer := network.Peer("Org1", "peer0")
    90  		core := network.ReadPeerConfig(peer)
    91  		core.Ledger.State.StateDatabase = "CouchDB"
    92  		core.Ledger.State.CouchDBConfig.CouchDBAddress = couchAddr
    93  		network.WritePeerConfig(peer, core)
    94  
    95  		// start the network
    96  		network.Bootstrap()
    97  		networkRunner := network.NetworkGroupRunner()
    98  		process = ifrit.Invoke(networkRunner)
    99  		Eventually(process.Ready(), network.EventuallyTimeout).Should(BeClosed())
   100  		orderer = network.Orderer("orderer")
   101  		network.CreateAndJoinChannel(orderer, "testchannel")
   102  		network.UpdateChannelAnchors(orderer, "testchannel")
   103  		network.VerifyMembership(network.PeersWithChannel("testchannel"), "testchannel")
   104  
   105  		legacyChaincode = nwo.Chaincode{
   106  			Name:        "marbles",
   107  			Version:     "0.0",
   108  			Path:        chaincodePathWithIndex,
   109  			Ctor:        `{"Args":[]}`,
   110  			Policy:      `OR ('Org1MSP.member','Org2MSP.member')`,
   111  			PackageFile: filepath.Join(testDir, "marbles_legacy.tar.gz"),
   112  		}
   113  
   114  		newlifecycleChaincode = nwo.Chaincode{
   115  			Name:            "marbles",
   116  			Version:         "0.0",
   117  			Path:            components.Build(chaincodePathWithIndex),
   118  			Lang:            "binary",
   119  			CodeFiles:       filesWithIndex,
   120  			PackageFile:     filepath.Join(testDir, "marbles.tar.gz"),
   121  			SignaturePolicy: `OR ('Org1MSP.member','Org2MSP.member')`,
   122  			Sequence:        "1",
   123  			Label:           "marbles",
   124  		}
   125  	})
   126  
   127  	AfterEach(func() {
   128  		process.Signal(syscall.SIGTERM)
   129  		Eventually(process.Wait(), network.EventuallyTimeout).Should(Receive())
   130  		couchProcess.Signal(syscall.SIGTERM)
   131  		Eventually(couchProcess.Wait(), network.EventuallyTimeout).Should(Receive())
   132  		network.Cleanup()
   133  		os.RemoveAll(testDir)
   134  	})
   135  
   136  	When("chaincode is installed and instantiated via legacy lifecycle", func() {
   137  		It("creates indexes", func() {
   138  			nwo.PackageChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer0"))
   139  			nwo.InstallChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer0"))
   140  			nwo.InstantiateChaincodeLegacy(network, "testchannel", orderer, legacyChaincode, network.Peer("Org1", "peer0"), network.Peers...)
   141  			initMarble(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles", "marble_indexed")
   142  			verifySizeIndexExists(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles")
   143  		})
   144  	})
   145  
   146  	When("chaincode is deployed via new lifecycle (using the docker chaincode build) ", func() {
   147  		BeforeEach(func() {
   148  			newlifecycleChaincode.Path = chaincodePathWithIndex
   149  			newlifecycleChaincode.Lang = "golang"
   150  		})
   151  
   152  		It("creates indexes", func() {
   153  			nwo.EnableCapabilities(network, "testchannel", "Application", "V2_0", orderer, network.Peer("Org1", "peer0"), network.Peer("Org2", "peer0"))
   154  			nwo.DeployChaincode(network, "testchannel", orderer, newlifecycleChaincode, network.Peers...)
   155  			initMarble(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles", "marble_indexed")
   156  			verifySizeIndexExists(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles")
   157  		})
   158  	})
   159  
   160  	When("chaincode is defined and installed via new lifecycle and then upgraded with an additional index", func() {
   161  		It("creates indexes", func() {
   162  			nwo.EnableCapabilities(network, "testchannel", "Application", "V2_0", orderer, network.Peer("Org1", "peer0"), network.Peer("Org2", "peer0"))
   163  			nwo.DeployChaincode(network, "testchannel", orderer, newlifecycleChaincode, network.Peers...)
   164  			initMarble(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles", "marble_indexed")
   165  			verifySizeIndexExists(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles")
   166  			verifyColorIndexDoesNotExist(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles")
   167  
   168  			By("upgrading the chaincode to include an additional index")
   169  			newlifecycleChaincode.Sequence = "2"
   170  			newlifecycleChaincode.CodeFiles = filesWithIndices
   171  			newlifecycleChaincode.PackageFile = filepath.Join(testDir, "marbles-two-indexes.tar.gz")
   172  			newlifecycleChaincode.Label = "marbles-two-indexes"
   173  
   174  			nwo.PackageChaincodeBinary(newlifecycleChaincode)
   175  			nwo.InstallChaincode(network, newlifecycleChaincode, network.Peers...)
   176  			nwo.ApproveChaincodeForMyOrg(network, "testchannel", orderer, newlifecycleChaincode, network.Peers...)
   177  			nwo.CheckCommitReadinessUntilReady(network, "testchannel", newlifecycleChaincode, network.PeerOrgs(), network.Peers...)
   178  			nwo.CommitChaincode(network, "testchannel", orderer, newlifecycleChaincode, network.Peers[0], network.Peers...)
   179  
   180  			verifySizeIndexExists(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles")
   181  			verifyColorIndexExists(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles")
   182  		})
   183  	})
   184  
   185  	When("chaincode is installed and instantiated via legacy lifecycle and then defined and installed via new lifecycle", func() {
   186  		BeforeEach(func() {
   187  			legacyChaincode.Path = chaincodePathWithNoIndex
   188  			newlifecycleChaincode.CodeFiles = filesWithIndex
   189  		})
   190  
   191  		It("creates indexes from the new lifecycle package", func() {
   192  			By("instantiating and installing legacy chaincode")
   193  			nwo.PackageChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer0"))
   194  			nwo.InstallChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer0"))
   195  			nwo.InstantiateChaincodeLegacy(network, "testchannel", orderer, legacyChaincode, network.Peer("Org1", "peer0"), network.Peers...)
   196  			initMarble(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles", "marble_not_indexed")
   197  			verifySizeIndexDoesNotExist(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles")
   198  
   199  			By("installing and defining chaincode using new lifecycle")
   200  			nwo.EnableCapabilities(network, "testchannel", "Application", "V2_0", orderer, network.Peer("Org1", "peer0"), network.Peer("Org2", "peer0"))
   201  			nwo.DeployChaincode(network, "testchannel", orderer, newlifecycleChaincode)
   202  			initMarble(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles", "marble_indexed")
   203  			verifySizeIndexExists(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles")
   204  		})
   205  	})
   206  
   207  	When("chaincode is installed and instantiated via legacy lifecycle with an external builder", func() {
   208  		BeforeEach(func() {
   209  			// This covers the legacy lifecycle + external builder scenario
   210  			legacyChaincode.Path = chaincodePathWithIndexes
   211  			legacyChaincode.Name = "marbles-external"
   212  		})
   213  
   214  		It("creates indexes from the new lifecycle package", func() {
   215  			peer := network.Peer("Org1", "peer0")
   216  
   217  			By("installing with the external chaincode builder")
   218  			nwo.PackageChaincodeLegacy(network, legacyChaincode, peer)
   219  			nwo.InstallChaincodeLegacy(network, legacyChaincode, peer)
   220  			nwo.InstantiateChaincodeLegacy(network, "testchannel", orderer, legacyChaincode, peer, peer)
   221  			initMarble(network, "testchannel", orderer, peer, legacyChaincode.Name, "marble_indexed")
   222  			verifySizeIndexExists(network, "testchannel", orderer, peer, legacyChaincode.Name)
   223  		})
   224  	})
   225  
   226  	When("chaincode is instantiated via legacy lifecycle, then defined and installed via new lifecycle and, finally installed via legacy lifecycle", func() {
   227  		BeforeEach(func() {
   228  			legacyChaincode.Path = chaincodePathWithIndex
   229  			newlifecycleChaincode.CodeFiles = nil
   230  		})
   231  
   232  		It("does not create indexes upon final installation of legacy chaincode", func() {
   233  			By("instantiating legacy chaincode")
   234  			// lscc requires the chaincode to be installed before a instantiate transaction can be simulated
   235  			// doing so in Org1.peer1 so that chaincode is not installed on "Org1.peer0" i.e., only instantiated
   236  			// via legacy lifecycle
   237  			nwo.PackageChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer1"))
   238  			nwo.InstallChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer1"))
   239  			nwo.InstantiateChaincodeLegacy(network, "testchannel", orderer, legacyChaincode, network.Peer("Org1", "peer1"), network.Peers...)
   240  
   241  			By("installing and defining chaincode using new lifecycle")
   242  			nwo.EnableCapabilities(network, "testchannel", "Application", "V2_0", orderer, network.Peer("Org1", "peer0"), network.Peer("Org2", "peer0"))
   243  			nwo.DeployChaincode(network, "testchannel", orderer, newlifecycleChaincode)
   244  			initMarble(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles", "marble_not_indexed")
   245  
   246  			By("installing legacy chaincode on Org1.peer0")
   247  			nwo.InstallChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer0"))
   248  
   249  			By("verifying that the index should not have been created on (Org1, peer0) - though the legacy package contains indexes")
   250  			verifySizeIndexDoesNotExist(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles")
   251  		})
   252  	})
   253  
   254  	When("chaincode is installed using legacy lifecycle, then defined and installed using new lifecycle", func() {
   255  		BeforeEach(func() {
   256  			legacyChaincode.Path = chaincodePathWithIndex
   257  			newlifecycleChaincode.CodeFiles = nil
   258  		})
   259  
   260  		It("does not use legacy package to create indexes", func() {
   261  			By("installing legacy chaincode (with an index included)")
   262  			nwo.PackageChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer0"))
   263  			nwo.InstallChaincodeLegacy(network, legacyChaincode, network.Peer("Org1", "peer0"))
   264  
   265  			By("installing and defining chaincode (without an index included) using new lifecycle")
   266  			nwo.EnableCapabilities(network, "testchannel", "Application", "V2_0", orderer, network.Peer("Org1", "peer0"), network.Peer("Org2", "peer0"))
   267  			nwo.DeployChaincode(network, "testchannel", orderer, newlifecycleChaincode)
   268  			initMarble(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles", "marble_not_indexed")
   269  
   270  			By("verifying that the index should not have been created - though the legacy package contains indexes")
   271  			verifySizeIndexDoesNotExist(network, "testchannel", orderer, network.Peer("Org1", "peer0"), "marbles")
   272  		})
   273  	})
   274  })
   275  
   276  func initMarble(n *nwo.Network, channel string, orderer *nwo.Orderer, peer *nwo.Peer, ccName, marbleName string) {
   277  	By("invoking initMarble function of the chaincode")
   278  	sess, err := n.PeerUserSession(peer, "User1", commands.ChaincodeInvoke{
   279  		ChannelID: channel,
   280  		Orderer:   n.OrdererAddress(orderer, nwo.ListenPort),
   281  		Name:      ccName,
   282  		Ctor:      prepareChaincodeInvokeArgs("initMarble", marbleName, "blue", "35", "tom"),
   283  		PeerAddresses: []string{
   284  			n.PeerAddress(peer, nwo.ListenPort),
   285  		},
   286  		WaitForEvent: true,
   287  	})
   288  	Expect(err).NotTo(HaveOccurred())
   289  	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit())
   290  	Expect(sess.Err).To(gbytes.Say("Chaincode invoke successful."))
   291  }
   292  
   293  func prepareChaincodeInvokeArgs(args ...string) string {
   294  	m, err := json.Marshal(map[string][]string{
   295  		"Args": args,
   296  	})
   297  	Expect(err).NotTo(HaveOccurred())
   298  	return string(m)
   299  }
   300  
   301  func verifySizeIndexExists(n *nwo.Network, channel string, orderer *nwo.Orderer, peer *nwo.Peer, ccName string) {
   302  	verifySizeIndexPresence(n, channel, orderer, peer, ccName, true)
   303  }
   304  
   305  func verifySizeIndexDoesNotExist(n *nwo.Network, channel string, orderer *nwo.Orderer, peer *nwo.Peer, ccName string) {
   306  	verifySizeIndexPresence(n, channel, orderer, peer, ccName, false)
   307  }
   308  
   309  func verifySizeIndexPresence(n *nwo.Network, channel string, orderer *nwo.Orderer, peer *nwo.Peer, ccName string, expectIndexPresent bool) {
   310  	query := `{
   311  		"selector":{
   312  			"docType":{
   313  				"$eq":"marble"
   314  			},
   315  			"owner":{
   316  				"$eq":"tom"
   317  			},
   318  			"size":{
   319  				"$gt":0
   320  			}
   321  		},
   322  		"fields":["docType","owner","size"],
   323  		"sort":[{"size":"desc"}],
   324  		"use_index":"_design/indexSizeSortDoc"
   325  	}`
   326  	verifyIndexPresence(n, channel, orderer, peer, ccName, expectIndexPresent, query)
   327  }
   328  
   329  func verifyColorIndexExists(n *nwo.Network, channel string, orderer *nwo.Orderer, peer *nwo.Peer, ccName string) {
   330  	verifyColorIndexPresence(n, channel, orderer, peer, ccName, true)
   331  }
   332  
   333  func verifyColorIndexDoesNotExist(n *nwo.Network, channel string, orderer *nwo.Orderer, peer *nwo.Peer, ccName string) {
   334  	verifyColorIndexPresence(n, channel, orderer, peer, ccName, false)
   335  }
   336  
   337  func verifyColorIndexPresence(n *nwo.Network, channel string, orderer *nwo.Orderer, peer *nwo.Peer, ccName string, expectIndexPresent bool) {
   338  	query := `{
   339  		"selector":{
   340  			"docType":{
   341  				"$eq":"marble"
   342  			},
   343  			"owner":{
   344  				"$eq":"tom"
   345  			},
   346  			"color":{
   347  				"$eq":"blue"
   348  			}
   349  		},
   350  		"fields":["docType","owner","size"],
   351  		"sort":[{"color":"desc"}],
   352  		"use_index":"_design/indexColorSortDoc"
   353  	}`
   354  	verifyIndexPresence(n, channel, orderer, peer, ccName, expectIndexPresent, query)
   355  }
   356  
   357  func verifyIndexPresence(n *nwo.Network, channel string, orderer *nwo.Orderer, peer *nwo.Peer, ccName string, expectIndexPresent bool, indexQuery string) {
   358  	By("invoking queryMarbles function with a user constructed query that requires an index due to a sort")
   359  	sess, err := n.PeerUserSession(peer, "User1", commands.ChaincodeInvoke{
   360  		ChannelID: channel,
   361  		Name:      ccName,
   362  		Ctor:      prepareChaincodeInvokeArgs("queryMarbles", indexQuery),
   363  	})
   364  	Expect(err).NotTo(HaveOccurred())
   365  	if expectIndexPresent {
   366  		Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   367  		Expect(sess.Err).To(gbytes.Say("Chaincode invoke successful."))
   368  	} else {
   369  		Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit())
   370  		Expect(sess.Err).To(gbytes.Say("Error:no_usable_index"))
   371  	}
   372  }