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

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package nwo
     8  
     9  import (
    10  	"encoding/json"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"os"
    14  	"os/exec"
    15  	"strconv"
    16  	"strings"
    17  
    18  	"github.com/golang/protobuf/proto"
    19  	"github.com/hechain20/hechain/common/util"
    20  	"github.com/hechain20/hechain/integration/nwo/commands"
    21  	"github.com/hechain20/hechain/protoutil"
    22  	"github.com/hyperledger/fabric-protos-go/common"
    23  	"github.com/hyperledger/fabric-protos-go/peer/lifecycle"
    24  	"github.com/onsi/ginkgo"
    25  	. "github.com/onsi/gomega"
    26  	"github.com/onsi/gomega/gbytes"
    27  	"github.com/onsi/gomega/gexec"
    28  	"github.com/onsi/gomega/gstruct"
    29  )
    30  
    31  type Chaincode struct {
    32  	Name                string
    33  	Version             string
    34  	Path                string
    35  	Ctor                string
    36  	Policy              string // only used for legacy lifecycle. For new lifecycle use SignaturePolicy
    37  	Lang                string
    38  	CollectionsConfig   string // optional
    39  	PackageFile         string
    40  	PackageID           string            // if unspecified, chaincode won't be executable. Can use SetPackageIDFromPackageFile() to set.
    41  	CodeFiles           map[string]string // map from paths on the filesystem to code.tar.gz paths
    42  	Sequence            string
    43  	EndorsementPlugin   string
    44  	ValidationPlugin    string
    45  	InitRequired        bool
    46  	Label               string
    47  	SignaturePolicy     string
    48  	ChannelConfigPolicy string
    49  }
    50  
    51  func (c *Chaincode) SetPackageIDFromPackageFile() {
    52  	fileBytes, err := ioutil.ReadFile(c.PackageFile)
    53  	Expect(err).NotTo(HaveOccurred())
    54  	hashStr := fmt.Sprintf("%x", util.ComputeSHA256(fileBytes))
    55  	c.PackageID = c.Label + ":" + hashStr
    56  }
    57  
    58  // DeployChaincode is a helper that will install chaincode to all peers that
    59  // are connected to the specified channel, approve the chaincode on one of the
    60  // peers of each organization in the network, commit the chaincode definition
    61  // on the channel using one of the peers, and wait for the chaincode commit to
    62  // complete on all of the peers. It uses the _lifecycle implementation.
    63  // NOTE V2_0 capabilities must be enabled for this functionality to work.
    64  func DeployChaincode(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peers ...*Peer) {
    65  	if len(peers) == 0 {
    66  		peers = n.PeersWithChannel(channel)
    67  	}
    68  	if len(peers) == 0 {
    69  		return
    70  	}
    71  
    72  	PackageAndInstallChaincode(n, chaincode, peers...)
    73  
    74  	// approve for each org
    75  	ApproveChaincodeForMyOrg(n, channel, orderer, chaincode, peers...)
    76  
    77  	// commit definition
    78  	CheckCommitReadinessUntilReady(n, channel, chaincode, n.PeerOrgs(), peers...)
    79  	CommitChaincode(n, channel, orderer, chaincode, peers[0], peers...)
    80  
    81  	// init the chaincode, if required
    82  	if chaincode.InitRequired {
    83  		InitChaincode(n, channel, orderer, chaincode, peers...)
    84  	}
    85  }
    86  
    87  // DeployChaincodeLegacy is a helper that will install chaincode to all peers
    88  // that are connected to the specified channel, instantiate the chaincode on
    89  // one of the peers, and wait for the instantiation to complete on all of the
    90  // peers. It uses the legacy lifecycle (lscc) implementation.
    91  //
    92  // NOTE: This helper should not be used to deploy the same chaincode on
    93  // multiple channels as the install will fail on subsequent calls. Instead,
    94  // simply use InstantiateChaincode().
    95  func DeployChaincodeLegacy(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peers ...*Peer) {
    96  	if len(peers) == 0 {
    97  		peers = n.PeersWithChannel(channel)
    98  	}
    99  	if len(peers) == 0 {
   100  		return
   101  	}
   102  
   103  	// create temp file for chaincode package if not provided
   104  	if chaincode.PackageFile == "" {
   105  		tempFile, err := ioutil.TempFile("", "chaincode-package")
   106  		Expect(err).NotTo(HaveOccurred())
   107  		tempFile.Close()
   108  		defer os.Remove(tempFile.Name())
   109  		chaincode.PackageFile = tempFile.Name()
   110  	}
   111  
   112  	// only create chaincode package if it doesn't already exist
   113  	if fi, err := os.Stat(chaincode.PackageFile); os.IsNotExist(err) || fi.Size() == 0 {
   114  		PackageChaincodeLegacy(n, chaincode, peers[0])
   115  	}
   116  
   117  	// install on all peers
   118  	InstallChaincodeLegacy(n, chaincode, peers...)
   119  
   120  	// instantiate on the first peer
   121  	InstantiateChaincodeLegacy(n, channel, orderer, chaincode, peers[0], peers...)
   122  }
   123  
   124  func PackageAndInstallChaincode(n *Network, chaincode Chaincode, peers ...*Peer) {
   125  	// create temp file for chaincode package if not provided
   126  	if chaincode.PackageFile == "" {
   127  		tempFile, err := ioutil.TempFile("", "chaincode-package")
   128  		Expect(err).NotTo(HaveOccurred())
   129  		tempFile.Close()
   130  		defer os.Remove(tempFile.Name())
   131  		chaincode.PackageFile = tempFile.Name()
   132  	}
   133  
   134  	// only create chaincode package if it doesn't already exist
   135  	if _, err := os.Stat(chaincode.PackageFile); os.IsNotExist(err) {
   136  		switch chaincode.Lang {
   137  		case "binary", "ccaas":
   138  			PackageChaincodeBinary(chaincode)
   139  		default:
   140  			PackageChaincode(n, chaincode, peers[0])
   141  		}
   142  	}
   143  
   144  	// install on all peers
   145  	InstallChaincode(n, chaincode, peers...)
   146  }
   147  
   148  func PackageChaincode(n *Network, chaincode Chaincode, peer *Peer) {
   149  	sess, err := n.PeerAdminSession(peer, commands.ChaincodePackage{
   150  		Path:       chaincode.Path,
   151  		Lang:       chaincode.Lang,
   152  		Label:      chaincode.Label,
   153  		OutputFile: chaincode.PackageFile,
   154  		ClientAuth: n.ClientAuthRequired,
   155  	})
   156  	Expect(err).NotTo(HaveOccurred())
   157  	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   158  }
   159  
   160  func PackageChaincodeLegacy(n *Network, chaincode Chaincode, peer *Peer) {
   161  	sess, err := n.PeerAdminSession(peer, commands.ChaincodePackageLegacy{
   162  		Name:       chaincode.Name,
   163  		Version:    chaincode.Version,
   164  		Path:       chaincode.Path,
   165  		Lang:       chaincode.Lang,
   166  		OutputFile: chaincode.PackageFile,
   167  		ClientAuth: n.ClientAuthRequired,
   168  	})
   169  	Expect(err).NotTo(HaveOccurred())
   170  	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   171  }
   172  
   173  func CheckPackageID(n *Network, packageFile string, packageID string, peer *Peer) {
   174  	sess, err := n.PeerAdminSession(peer, commands.ChaincodeCalculatePackageID{
   175  		PackageFile: packageFile,
   176  		ClientAuth:  n.ClientAuthRequired,
   177  	})
   178  	Expect(err).NotTo(HaveOccurred())
   179  	Eventually(sess, n.EventuallyTimeout).Should(gbytes.Say(fmt.Sprintf(`\Q%s\E`, packageID)))
   180  }
   181  
   182  func InstallChaincode(n *Network, chaincode Chaincode, peers ...*Peer) {
   183  	// Ensure 'jq' exists in path, because we need it to build chaincode
   184  	if _, err := exec.LookPath("jq"); err != nil {
   185  		ginkgo.Fail("'jq' is needed to build chaincode but it wasn't found in the PATH")
   186  	}
   187  
   188  	if chaincode.PackageID == "" {
   189  		chaincode.SetPackageIDFromPackageFile()
   190  	}
   191  
   192  	for _, p := range peers {
   193  		sess, err := n.PeerAdminSession(p, commands.ChaincodeInstall{
   194  			PackageFile: chaincode.PackageFile,
   195  			ClientAuth:  n.ClientAuthRequired,
   196  		})
   197  		ExpectWithOffset(1, err).NotTo(HaveOccurred())
   198  		EventuallyWithOffset(1, sess, n.EventuallyTimeout).Should(gexec.Exit())
   199  
   200  		EnsureInstalled(n, chaincode.Label, chaincode.PackageID, p)
   201  		CheckPackageID(n, chaincode.PackageFile, chaincode.PackageID, p)
   202  	}
   203  }
   204  
   205  func InstallChaincodeLegacy(n *Network, chaincode Chaincode, peers ...*Peer) {
   206  	// Ensure 'jq' exists in path, because we need it to build chaincode
   207  	if _, err := exec.LookPath("jq"); err != nil {
   208  		ginkgo.Fail("'jq' is needed to build chaincode but it wasn't found in the PATH")
   209  	}
   210  
   211  	for _, p := range peers {
   212  		sess, err := n.PeerAdminSession(p, commands.ChaincodeInstallLegacy{
   213  			Name:        chaincode.Name,
   214  			Version:     chaincode.Version,
   215  			Path:        chaincode.Path,
   216  			Lang:        chaincode.Lang,
   217  			PackageFile: chaincode.PackageFile,
   218  			ClientAuth:  n.ClientAuthRequired,
   219  		})
   220  		Expect(err).NotTo(HaveOccurred())
   221  		EventuallyWithOffset(1, sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   222  
   223  		sess, err = n.PeerAdminSession(p, commands.ChaincodeListInstalledLegacy{
   224  			ClientAuth: n.ClientAuthRequired,
   225  		})
   226  		Expect(err).NotTo(HaveOccurred())
   227  		EventuallyWithOffset(1, sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   228  		ExpectWithOffset(1, sess).To(gbytes.Say(fmt.Sprintf("Name: %s, Version: %s,", chaincode.Name, chaincode.Version)))
   229  	}
   230  }
   231  
   232  func ApproveChaincodeForMyOrg(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peers ...*Peer) {
   233  	if chaincode.PackageID == "" {
   234  		chaincode.SetPackageIDFromPackageFile()
   235  	}
   236  
   237  	// used to ensure we only approve once per org
   238  	approvedOrgs := map[string]bool{}
   239  	for _, p := range peers {
   240  		if _, ok := approvedOrgs[p.Organization]; !ok {
   241  			sess, err := n.PeerAdminSession(p, commands.ChaincodeApproveForMyOrg{
   242  				ChannelID:           channel,
   243  				Orderer:             n.OrdererAddress(orderer, ListenPort),
   244  				Name:                chaincode.Name,
   245  				Version:             chaincode.Version,
   246  				PackageID:           chaincode.PackageID,
   247  				Sequence:            chaincode.Sequence,
   248  				EndorsementPlugin:   chaincode.EndorsementPlugin,
   249  				ValidationPlugin:    chaincode.ValidationPlugin,
   250  				SignaturePolicy:     chaincode.SignaturePolicy,
   251  				ChannelConfigPolicy: chaincode.ChannelConfigPolicy,
   252  				InitRequired:        chaincode.InitRequired,
   253  				CollectionsConfig:   chaincode.CollectionsConfig,
   254  				ClientAuth:          n.ClientAuthRequired,
   255  			})
   256  			Expect(err).NotTo(HaveOccurred())
   257  			Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   258  			approvedOrgs[p.Organization] = true
   259  			Eventually(sess.Err, n.EventuallyTimeout).Should(gbytes.Say(fmt.Sprintf(`\Qcommitted with status (VALID) at %s\E`, n.PeerAddress(p, ListenPort))))
   260  		}
   261  	}
   262  }
   263  
   264  func EnsureChaincodeApproved(n *Network, peer *Peer, channel, name, sequence string) {
   265  	sequenceInt, err := strconv.ParseInt(sequence, 10, 64)
   266  	Expect(err).NotTo(HaveOccurred())
   267  	Eventually(queryApproved(n, peer, channel, name, sequence), n.EventuallyTimeout).Should(
   268  		gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{
   269  			"Sequence": Equal(sequenceInt),
   270  		}),
   271  	)
   272  }
   273  
   274  func CheckCommitReadinessUntilReady(n *Network, channel string, chaincode Chaincode, checkOrgs []*Organization, peers ...*Peer) {
   275  	for _, p := range peers {
   276  		keys := gstruct.Keys{}
   277  		for _, org := range checkOrgs {
   278  			keys[org.MSPID] = BeTrue()
   279  		}
   280  		Eventually(checkCommitReadiness(n, p, channel, chaincode), n.EventuallyTimeout).Should(
   281  			gstruct.MatchKeys(gstruct.IgnoreExtras, keys),
   282  		)
   283  	}
   284  }
   285  
   286  func CommitChaincode(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peer *Peer, checkPeers ...*Peer) {
   287  	// commit using one peer per org
   288  	commitOrgs := map[string]bool{}
   289  	var peerAddresses []string
   290  	for _, p := range checkPeers {
   291  		if exists := commitOrgs[p.Organization]; !exists {
   292  			peerAddresses = append(peerAddresses, n.PeerAddress(p, ListenPort))
   293  			commitOrgs[p.Organization] = true
   294  		}
   295  	}
   296  
   297  	sess, err := n.PeerAdminSession(peer, commands.ChaincodeCommit{
   298  		ChannelID:           channel,
   299  		Orderer:             n.OrdererAddress(orderer, ListenPort),
   300  		Name:                chaincode.Name,
   301  		Version:             chaincode.Version,
   302  		Sequence:            chaincode.Sequence,
   303  		EndorsementPlugin:   chaincode.EndorsementPlugin,
   304  		ValidationPlugin:    chaincode.ValidationPlugin,
   305  		SignaturePolicy:     chaincode.SignaturePolicy,
   306  		ChannelConfigPolicy: chaincode.ChannelConfigPolicy,
   307  		InitRequired:        chaincode.InitRequired,
   308  		CollectionsConfig:   chaincode.CollectionsConfig,
   309  		PeerAddresses:       peerAddresses,
   310  		ClientAuth:          n.ClientAuthRequired,
   311  	})
   312  	Expect(err).NotTo(HaveOccurred())
   313  	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   314  	for i := 0; i < len(peerAddresses); i++ {
   315  		Eventually(sess.Err, n.EventuallyTimeout).Should(gbytes.Say(`\Qcommitted with status (VALID)\E`))
   316  	}
   317  	checkOrgs := []*Organization{}
   318  	for org := range commitOrgs {
   319  		checkOrgs = append(checkOrgs, n.Organization(org))
   320  	}
   321  	EnsureChaincodeCommitted(n, channel, chaincode.Name, chaincode.Version, chaincode.Sequence, checkOrgs, checkPeers...)
   322  }
   323  
   324  // EnsureChaincodeCommitted polls each supplied peer until the chaincode definition
   325  // has been committed to the peer's ledger.
   326  func EnsureChaincodeCommitted(n *Network, channel, name, version, sequence string, checkOrgs []*Organization, peers ...*Peer) {
   327  	for _, p := range peers {
   328  		sequenceInt, err := strconv.ParseInt(sequence, 10, 64)
   329  		Expect(err).NotTo(HaveOccurred())
   330  		approvedKeys := gstruct.Keys{}
   331  		for _, org := range checkOrgs {
   332  			approvedKeys[org.MSPID] = BeTrue()
   333  		}
   334  		Eventually(listCommitted(n, p, channel, name), n.EventuallyTimeout).Should(
   335  			gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{
   336  				"Version":   Equal(version),
   337  				"Sequence":  Equal(sequenceInt),
   338  				"Approvals": gstruct.MatchKeys(gstruct.IgnoreExtras, approvedKeys),
   339  			}),
   340  		)
   341  	}
   342  }
   343  
   344  func InitChaincode(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peers ...*Peer) {
   345  	// init using one peer per org
   346  	initOrgs := map[string]bool{}
   347  	var peerAddresses []string
   348  	for _, p := range peers {
   349  		if exists := initOrgs[p.Organization]; !exists {
   350  			peerAddresses = append(peerAddresses, n.PeerAddress(p, ListenPort))
   351  			initOrgs[p.Organization] = true
   352  		}
   353  	}
   354  
   355  	sess, err := n.PeerUserSession(peers[0], "User1", commands.ChaincodeInvoke{
   356  		ChannelID:     channel,
   357  		Orderer:       n.OrdererAddress(orderer, ListenPort),
   358  		Name:          chaincode.Name,
   359  		Ctor:          chaincode.Ctor,
   360  		PeerAddresses: peerAddresses,
   361  		WaitForEvent:  true,
   362  		IsInit:        true,
   363  		ClientAuth:    n.ClientAuthRequired,
   364  	})
   365  	Expect(err).NotTo(HaveOccurred())
   366  	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   367  	for i := 0; i < len(peerAddresses); i++ {
   368  		Eventually(sess.Err, n.EventuallyTimeout).Should(gbytes.Say(`\Qcommitted with status (VALID)\E`))
   369  	}
   370  	Expect(sess.Err).To(gbytes.Say("Chaincode invoke successful. result: status:200"))
   371  }
   372  
   373  func InstantiateChaincodeLegacy(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peer *Peer, checkPeers ...*Peer) {
   374  	sess, err := n.PeerAdminSession(peer, commands.ChaincodeInstantiateLegacy{
   375  		ChannelID:         channel,
   376  		Orderer:           n.OrdererAddress(orderer, ListenPort),
   377  		Name:              chaincode.Name,
   378  		Version:           chaincode.Version,
   379  		Ctor:              chaincode.Ctor,
   380  		Policy:            chaincode.Policy,
   381  		Lang:              chaincode.Lang,
   382  		CollectionsConfig: chaincode.CollectionsConfig,
   383  		ClientAuth:        n.ClientAuthRequired,
   384  	})
   385  	Expect(err).NotTo(HaveOccurred())
   386  	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   387  
   388  	EnsureInstantiatedLegacy(n, channel, chaincode.Name, chaincode.Version, checkPeers...)
   389  }
   390  
   391  func EnsureInstantiatedLegacy(n *Network, channel, name, version string, peers ...*Peer) {
   392  	for _, p := range peers {
   393  		Eventually(listInstantiatedLegacy(n, p, channel), n.EventuallyTimeout).Should(
   394  			gbytes.Say(fmt.Sprintf("Name: %s, Version: %s,", name, version)),
   395  		)
   396  	}
   397  }
   398  
   399  func UpgradeChaincodeLegacy(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peers ...*Peer) {
   400  	if len(peers) == 0 {
   401  		peers = n.PeersWithChannel(channel)
   402  	}
   403  	if len(peers) == 0 {
   404  		return
   405  	}
   406  
   407  	// install on all peers
   408  	InstallChaincodeLegacy(n, chaincode, peers...)
   409  
   410  	// upgrade from the first peer
   411  	sess, err := n.PeerAdminSession(peers[0], commands.ChaincodeUpgradeLegacy{
   412  		ChannelID:         channel,
   413  		Orderer:           n.OrdererAddress(orderer, ListenPort),
   414  		Name:              chaincode.Name,
   415  		Version:           chaincode.Version,
   416  		Ctor:              chaincode.Ctor,
   417  		Policy:            chaincode.Policy,
   418  		CollectionsConfig: chaincode.CollectionsConfig,
   419  		ClientAuth:        n.ClientAuthRequired,
   420  	})
   421  	Expect(err).NotTo(HaveOccurred())
   422  	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   423  
   424  	EnsureInstantiatedLegacy(n, channel, chaincode.Name, chaincode.Version, peers...)
   425  }
   426  
   427  func EnsureInstalled(n *Network, label, packageID string, peers ...*Peer) {
   428  	for _, p := range peers {
   429  		Eventually(QueryInstalled(n, p), n.EventuallyTimeout).Should(
   430  			ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras,
   431  				gstruct.Fields{
   432  					"Label":     Equal(label),
   433  					"PackageId": Equal(packageID),
   434  				},
   435  			)),
   436  		)
   437  	}
   438  }
   439  
   440  func QueryInstalledReferences(n *Network, channel, label, packageID string, checkPeer *Peer, nameVersions ...[]string) {
   441  	chaincodes := make([]*lifecycle.QueryInstalledChaincodesResult_Chaincode, len(nameVersions))
   442  	for i, nameVersion := range nameVersions {
   443  		chaincodes[i] = &lifecycle.QueryInstalledChaincodesResult_Chaincode{
   444  			Name:    nameVersion[0],
   445  			Version: nameVersion[1],
   446  		}
   447  	}
   448  
   449  	Expect(QueryInstalled(n, checkPeer)()).To(
   450  		ContainElement(gstruct.MatchFields(gstruct.IgnoreExtras,
   451  			gstruct.Fields{
   452  				"Label":     Equal(label),
   453  				"PackageId": Equal(packageID),
   454  				"References": HaveKeyWithValue(channel, gstruct.PointTo(gstruct.MatchFields(gstruct.IgnoreExtras,
   455  					gstruct.Fields{
   456  						"Chaincodes": ConsistOf(chaincodes),
   457  					},
   458  				))),
   459  			},
   460  		)),
   461  	)
   462  }
   463  
   464  type queryInstalledOutput struct {
   465  	InstalledChaincodes []lifecycle.QueryInstalledChaincodesResult_InstalledChaincode `json:"installed_chaincodes"`
   466  }
   467  
   468  func QueryInstalled(n *Network, peer *Peer) func() []lifecycle.QueryInstalledChaincodesResult_InstalledChaincode {
   469  	return func() []lifecycle.QueryInstalledChaincodesResult_InstalledChaincode {
   470  		sess, err := n.PeerAdminSession(peer, commands.ChaincodeQueryInstalled{
   471  			ClientAuth: n.ClientAuthRequired,
   472  		})
   473  		Expect(err).NotTo(HaveOccurred())
   474  		Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   475  		output := &queryInstalledOutput{}
   476  		err = json.Unmarshal(sess.Out.Contents(), output)
   477  		Expect(err).NotTo(HaveOccurred())
   478  		return output.InstalledChaincodes
   479  	}
   480  }
   481  
   482  type checkCommitReadinessOutput struct {
   483  	Approvals map[string]bool `json:"approvals"`
   484  }
   485  
   486  func checkCommitReadiness(n *Network, peer *Peer, channel string, chaincode Chaincode) func() map[string]bool {
   487  	return func() map[string]bool {
   488  		sess, err := n.PeerAdminSession(peer, commands.ChaincodeCheckCommitReadiness{
   489  			ChannelID:           channel,
   490  			Name:                chaincode.Name,
   491  			Version:             chaincode.Version,
   492  			Sequence:            chaincode.Sequence,
   493  			EndorsementPlugin:   chaincode.EndorsementPlugin,
   494  			ValidationPlugin:    chaincode.ValidationPlugin,
   495  			SignaturePolicy:     chaincode.SignaturePolicy,
   496  			ChannelConfigPolicy: chaincode.ChannelConfigPolicy,
   497  			InitRequired:        chaincode.InitRequired,
   498  			CollectionsConfig:   chaincode.CollectionsConfig,
   499  			ClientAuth:          n.ClientAuthRequired,
   500  		})
   501  		Expect(err).NotTo(HaveOccurred())
   502  		Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   503  		output := &checkCommitReadinessOutput{}
   504  		err = json.Unmarshal(sess.Out.Contents(), output)
   505  		Expect(err).NotTo(HaveOccurred())
   506  		return output.Approvals
   507  	}
   508  }
   509  
   510  type queryApprovedOutput struct {
   511  	Sequence int64 `json:"sequence"`
   512  }
   513  
   514  // queryApproved returns the result of the queryApproved command.
   515  // If the command fails for any reason, it will return an empty output object.
   516  func queryApproved(n *Network, peer *Peer, channel, name, sequence string) func() queryApprovedOutput {
   517  	return func() queryApprovedOutput {
   518  		sess, err := n.PeerAdminSession(peer, commands.ChaincodeQueryApproved{
   519  			ChannelID:     channel,
   520  			Name:          name,
   521  			Sequence:      sequence,
   522  			PeerAddresses: []string{n.PeerAddress(peer, ListenPort)},
   523  			ClientAuth:    n.ClientAuthRequired,
   524  		})
   525  		Expect(err).NotTo(HaveOccurred())
   526  		Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit())
   527  		output := &queryApprovedOutput{}
   528  		if sess.ExitCode() == 1 {
   529  			// don't try to unmarshal the output as JSON if the query failed
   530  			return *output
   531  		}
   532  		err = json.Unmarshal(sess.Out.Contents(), output)
   533  		Expect(err).NotTo(HaveOccurred())
   534  		return *output
   535  	}
   536  }
   537  
   538  type queryCommittedOutput struct {
   539  	Sequence  int64           `json:"sequence"`
   540  	Version   string          `json:"version"`
   541  	Approvals map[string]bool `json:"approvals"`
   542  }
   543  
   544  // listCommitted returns the result of the queryCommitted command.
   545  // If the command fails for any reason (e.g. namespace not defined
   546  // or a database access issue), it will return an empty output object.
   547  func listCommitted(n *Network, peer *Peer, channel, name string) func() queryCommittedOutput {
   548  	return func() queryCommittedOutput {
   549  		sess, err := n.PeerAdminSession(peer, commands.ChaincodeListCommitted{
   550  			ChannelID:  channel,
   551  			Name:       name,
   552  			ClientAuth: n.ClientAuthRequired,
   553  		})
   554  		Expect(err).NotTo(HaveOccurred())
   555  		Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit())
   556  		output := &queryCommittedOutput{}
   557  		if sess.ExitCode() == 1 {
   558  			// don't try to unmarshal the output as JSON if the query failed
   559  			return *output
   560  		}
   561  		err = json.Unmarshal(sess.Out.Contents(), output)
   562  		Expect(err).NotTo(HaveOccurred())
   563  		return *output
   564  	}
   565  }
   566  
   567  func listInstantiatedLegacy(n *Network, peer *Peer, channel string) func() *gbytes.Buffer {
   568  	return func() *gbytes.Buffer {
   569  		sess, err := n.PeerAdminSession(peer, commands.ChaincodeListInstantiatedLegacy{
   570  			ChannelID:  channel,
   571  			ClientAuth: n.ClientAuthRequired,
   572  		})
   573  		Expect(err).NotTo(HaveOccurred())
   574  		Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
   575  		return sess.Buffer()
   576  	}
   577  }
   578  
   579  // EnableCapabilities enables a specific capabilities flag for a running network.
   580  // It generates the config update using the first peer, signs the configuration
   581  // with the subsequent peers, and then submits the config update using the
   582  // first peer.
   583  func EnableCapabilities(network *Network, channel, capabilitiesGroup, capabilitiesVersion string, orderer *Orderer, peers ...*Peer) {
   584  	if len(peers) == 0 {
   585  		return
   586  	}
   587  
   588  	config := GetConfig(network, peers[0], orderer, channel)
   589  	updatedConfig := proto.Clone(config).(*common.Config)
   590  
   591  	updatedConfig.ChannelGroup.Groups[capabilitiesGroup].Values["Capabilities"] = &common.ConfigValue{
   592  		ModPolicy: "Admins",
   593  		Value: protoutil.MarshalOrPanic(
   594  			&common.Capabilities{
   595  				Capabilities: map[string]*common.Capability{
   596  					capabilitiesVersion: {},
   597  				},
   598  			},
   599  		),
   600  	}
   601  
   602  	UpdateConfig(network, orderer, channel, config, updatedConfig, false, peers[0], peers...)
   603  }
   604  
   605  // WaitUntilEqualLedgerHeight waits until all specified peers have the
   606  // provided ledger height on a channel
   607  func WaitUntilEqualLedgerHeight(n *Network, channel string, height int, peers ...*Peer) {
   608  	for _, peer := range peers {
   609  		Eventually(func() int {
   610  			return GetLedgerHeight(n, peer, channel)
   611  		}, n.EventuallyTimeout).Should(Equal(height))
   612  	}
   613  }
   614  
   615  // GetLedgerHeight returns the current ledger height for a peer on
   616  // a channel
   617  func GetLedgerHeight(n *Network, peer *Peer, channel string) int {
   618  	sess, err := n.PeerUserSession(peer, "User1", commands.ChannelInfo{
   619  		ChannelID:  channel,
   620  		ClientAuth: n.ClientAuthRequired,
   621  	})
   622  	Expect(err).NotTo(HaveOccurred())
   623  	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit())
   624  
   625  	if sess.ExitCode() == 1 {
   626  		// if org is not yet member of channel, peer will return error
   627  		return -1
   628  	}
   629  
   630  	channelInfoStr := strings.TrimPrefix(string(sess.Buffer().Contents()[:]), "Blockchain info:")
   631  	channelInfo := common.BlockchainInfo{}
   632  	json.Unmarshal([]byte(channelInfoStr), &channelInfo)
   633  	return int(channelInfo.Height)
   634  }
   635  
   636  // GetMaxLedgerHeight returns the maximum ledger height for the
   637  // peers on a channel
   638  func GetMaxLedgerHeight(n *Network, channel string, peers ...*Peer) int {
   639  	var maxHeight int
   640  	for _, peer := range peers {
   641  		peerHeight := GetLedgerHeight(n, peer, channel)
   642  		if peerHeight > maxHeight {
   643  			maxHeight = peerHeight
   644  		}
   645  	}
   646  	return maxHeight
   647  }