github.com/hyperledger-labs/bdls@v2.1.1+incompatible/integration/nwo/deploy.go (about) 1 /* 2 Copyright IBM Corp. 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/hyperledger/fabric-protos-go/common" 20 "github.com/hyperledger/fabric-protos-go/peer/lifecycle" 21 "github.com/hyperledger/fabric/common/util" 22 "github.com/hyperledger/fabric/integration/nwo/commands" 23 "github.com/hyperledger/fabric/protoutil" 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": 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 InstallChaincode(n *Network, chaincode Chaincode, peers ...*Peer) { 174 // Ensure 'jq' exists in path, because we need it to build chaincode 175 if _, err := exec.LookPath("jq"); err != nil { 176 ginkgo.Fail("'jq' is needed to build chaincode but it wasn't found in the PATH") 177 } 178 179 if chaincode.PackageID == "" { 180 chaincode.SetPackageIDFromPackageFile() 181 } 182 183 for _, p := range peers { 184 sess, err := n.PeerAdminSession(p, commands.ChaincodeInstall{ 185 PackageFile: chaincode.PackageFile, 186 ClientAuth: n.ClientAuthRequired, 187 }) 188 ExpectWithOffset(1, err).NotTo(HaveOccurred()) 189 EventuallyWithOffset(1, sess, n.EventuallyTimeout).Should(gexec.Exit()) 190 191 EnsureInstalled(n, chaincode.Label, chaincode.PackageID, p) 192 } 193 } 194 195 func InstallChaincodeLegacy(n *Network, chaincode Chaincode, peers ...*Peer) { 196 // Ensure 'jq' exists in path, because we need it to build chaincode 197 if _, err := exec.LookPath("jq"); err != nil { 198 ginkgo.Fail("'jq' is needed to build chaincode but it wasn't found in the PATH") 199 } 200 201 for _, p := range peers { 202 sess, err := n.PeerAdminSession(p, commands.ChaincodeInstallLegacy{ 203 Name: chaincode.Name, 204 Version: chaincode.Version, 205 Path: chaincode.Path, 206 Lang: chaincode.Lang, 207 PackageFile: chaincode.PackageFile, 208 ClientAuth: n.ClientAuthRequired, 209 }) 210 Expect(err).NotTo(HaveOccurred()) 211 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 212 213 sess, err = n.PeerAdminSession(p, commands.ChaincodeListInstalledLegacy{ 214 ClientAuth: n.ClientAuthRequired, 215 }) 216 Expect(err).NotTo(HaveOccurred()) 217 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 218 Expect(sess).To(gbytes.Say(fmt.Sprintf("Name: %s, Version: %s,", chaincode.Name, chaincode.Version))) 219 } 220 } 221 222 func ApproveChaincodeForMyOrg(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peers ...*Peer) { 223 if chaincode.PackageID == "" { 224 chaincode.SetPackageIDFromPackageFile() 225 } 226 227 // used to ensure we only approve once per org 228 approvedOrgs := map[string]bool{} 229 for _, p := range peers { 230 if _, ok := approvedOrgs[p.Organization]; !ok { 231 sess, err := n.PeerAdminSession(p, commands.ChaincodeApproveForMyOrg{ 232 ChannelID: channel, 233 Orderer: n.OrdererAddress(orderer, ListenPort), 234 Name: chaincode.Name, 235 Version: chaincode.Version, 236 PackageID: chaincode.PackageID, 237 Sequence: chaincode.Sequence, 238 EndorsementPlugin: chaincode.EndorsementPlugin, 239 ValidationPlugin: chaincode.ValidationPlugin, 240 SignaturePolicy: chaincode.SignaturePolicy, 241 ChannelConfigPolicy: chaincode.ChannelConfigPolicy, 242 InitRequired: chaincode.InitRequired, 243 CollectionsConfig: chaincode.CollectionsConfig, 244 ClientAuth: n.ClientAuthRequired, 245 }) 246 Expect(err).NotTo(HaveOccurred()) 247 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 248 approvedOrgs[p.Organization] = true 249 Eventually(sess.Err, n.EventuallyTimeout).Should(gbytes.Say(`\Qcommitted with status (VALID)\E`)) 250 } 251 } 252 } 253 254 func CheckCommitReadinessUntilReady(n *Network, channel string, chaincode Chaincode, checkOrgs []*Organization, peers ...*Peer) { 255 for _, p := range peers { 256 keys := Keys{} 257 for _, org := range checkOrgs { 258 keys[org.MSPID] = BeTrue() 259 } 260 Eventually(checkCommitReadiness(n, p, channel, chaincode), n.EventuallyTimeout).Should(MatchKeys(IgnoreExtras, keys)) 261 } 262 } 263 264 func CommitChaincode(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peer *Peer, checkPeers ...*Peer) { 265 // commit using one peer per org 266 commitOrgs := map[string]bool{} 267 var peerAddresses []string 268 for _, p := range checkPeers { 269 if exists := commitOrgs[p.Organization]; !exists { 270 peerAddresses = append(peerAddresses, n.PeerAddress(p, ListenPort)) 271 commitOrgs[p.Organization] = true 272 } 273 } 274 275 sess, err := n.PeerAdminSession(peer, commands.ChaincodeCommit{ 276 ChannelID: channel, 277 Orderer: n.OrdererAddress(orderer, ListenPort), 278 Name: chaincode.Name, 279 Version: chaincode.Version, 280 Sequence: chaincode.Sequence, 281 EndorsementPlugin: chaincode.EndorsementPlugin, 282 ValidationPlugin: chaincode.ValidationPlugin, 283 SignaturePolicy: chaincode.SignaturePolicy, 284 ChannelConfigPolicy: chaincode.ChannelConfigPolicy, 285 InitRequired: chaincode.InitRequired, 286 CollectionsConfig: chaincode.CollectionsConfig, 287 PeerAddresses: peerAddresses, 288 ClientAuth: n.ClientAuthRequired, 289 }) 290 Expect(err).NotTo(HaveOccurred()) 291 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 292 for i := 0; i < len(peerAddresses); i++ { 293 Eventually(sess.Err, n.EventuallyTimeout).Should(gbytes.Say(`\Qcommitted with status (VALID)\E`)) 294 } 295 checkOrgs := []*Organization{} 296 for org := range commitOrgs { 297 checkOrgs = append(checkOrgs, n.Organization(org)) 298 } 299 EnsureChaincodeCommitted(n, channel, chaincode.Name, chaincode.Version, chaincode.Sequence, checkOrgs, checkPeers...) 300 } 301 302 // EnsureChaincodeCommitted polls each supplied peer until the chaincode definition 303 // has been committed to the peer's ledger. 304 func EnsureChaincodeCommitted(n *Network, channel, name, version, sequence string, checkOrgs []*Organization, peers ...*Peer) { 305 for _, p := range peers { 306 sequenceInt, err := strconv.ParseInt(sequence, 10, 64) 307 Expect(err).NotTo(HaveOccurred()) 308 approvedKeys := Keys{} 309 for _, org := range checkOrgs { 310 approvedKeys[org.MSPID] = BeTrue() 311 } 312 Eventually(listCommitted(n, p, channel, name), n.EventuallyTimeout).Should( 313 MatchFields(IgnoreExtras, Fields{ 314 "Version": Equal(version), 315 "Sequence": Equal(sequenceInt), 316 "Approvals": MatchKeys(IgnoreExtras, approvedKeys), 317 }), 318 ) 319 } 320 } 321 322 func InitChaincode(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peers ...*Peer) { 323 // init using one peer per org 324 initOrgs := map[string]bool{} 325 var peerAddresses []string 326 for _, p := range peers { 327 if exists := initOrgs[p.Organization]; !exists { 328 peerAddresses = append(peerAddresses, n.PeerAddress(p, ListenPort)) 329 initOrgs[p.Organization] = true 330 } 331 } 332 333 sess, err := n.PeerUserSession(peers[0], "User1", commands.ChaincodeInvoke{ 334 ChannelID: channel, 335 Orderer: n.OrdererAddress(orderer, ListenPort), 336 Name: chaincode.Name, 337 Ctor: chaincode.Ctor, 338 PeerAddresses: peerAddresses, 339 WaitForEvent: true, 340 IsInit: true, 341 ClientAuth: n.ClientAuthRequired, 342 }) 343 Expect(err).NotTo(HaveOccurred()) 344 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 345 for i := 0; i < len(peerAddresses); i++ { 346 Eventually(sess.Err, n.EventuallyTimeout).Should(gbytes.Say(`\Qcommitted with status (VALID)\E`)) 347 } 348 Expect(sess.Err).To(gbytes.Say("Chaincode invoke successful. result: status:200")) 349 } 350 351 func InstantiateChaincodeLegacy(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peer *Peer, checkPeers ...*Peer) { 352 sess, err := n.PeerAdminSession(peer, commands.ChaincodeInstantiateLegacy{ 353 ChannelID: channel, 354 Orderer: n.OrdererAddress(orderer, ListenPort), 355 Name: chaincode.Name, 356 Version: chaincode.Version, 357 Ctor: chaincode.Ctor, 358 Policy: chaincode.Policy, 359 Lang: chaincode.Lang, 360 CollectionsConfig: chaincode.CollectionsConfig, 361 ClientAuth: n.ClientAuthRequired, 362 }) 363 Expect(err).NotTo(HaveOccurred()) 364 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 365 366 EnsureInstantiatedLegacy(n, channel, chaincode.Name, chaincode.Version, checkPeers...) 367 } 368 369 func EnsureInstantiatedLegacy(n *Network, channel, name, version string, peers ...*Peer) { 370 for _, p := range peers { 371 Eventually(listInstantiatedLegacy(n, p, channel), n.EventuallyTimeout).Should( 372 gbytes.Say(fmt.Sprintf("Name: %s, Version: %s,", name, version)), 373 ) 374 } 375 } 376 377 func UpgradeChaincodeLegacy(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peers ...*Peer) { 378 if len(peers) == 0 { 379 peers = n.PeersWithChannel(channel) 380 } 381 if len(peers) == 0 { 382 return 383 } 384 385 // install on all peers 386 InstallChaincodeLegacy(n, chaincode, peers...) 387 388 // upgrade from the first peer 389 sess, err := n.PeerAdminSession(peers[0], commands.ChaincodeUpgradeLegacy{ 390 ChannelID: channel, 391 Orderer: n.OrdererAddress(orderer, ListenPort), 392 Name: chaincode.Name, 393 Version: chaincode.Version, 394 Ctor: chaincode.Ctor, 395 Policy: chaincode.Policy, 396 CollectionsConfig: chaincode.CollectionsConfig, 397 ClientAuth: n.ClientAuthRequired, 398 }) 399 Expect(err).NotTo(HaveOccurred()) 400 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 401 402 EnsureInstantiatedLegacy(n, channel, chaincode.Name, chaincode.Version, peers...) 403 } 404 405 func EnsureInstalled(n *Network, label, packageID string, peers ...*Peer) { 406 for _, p := range peers { 407 Eventually(QueryInstalled(n, p), n.EventuallyTimeout).Should( 408 ContainElement(MatchFields(IgnoreExtras, 409 Fields{ 410 "Label": Equal(label), 411 "PackageId": Equal(packageID), 412 }, 413 )), 414 ) 415 } 416 } 417 418 func QueryInstalledReferences(n *Network, channel, label, packageID string, checkPeer *Peer, nameVersions ...[]string) { 419 chaincodes := make([]*lifecycle.QueryInstalledChaincodesResult_Chaincode, len(nameVersions)) 420 for i, nameVersion := range nameVersions { 421 chaincodes[i] = &lifecycle.QueryInstalledChaincodesResult_Chaincode{ 422 Name: nameVersion[0], 423 Version: nameVersion[1], 424 } 425 } 426 427 Expect(QueryInstalled(n, checkPeer)()).To( 428 ContainElement(MatchFields(IgnoreExtras, 429 Fields{ 430 "Label": Equal(label), 431 "PackageId": Equal(packageID), 432 "References": HaveKeyWithValue(channel, PointTo(MatchFields(IgnoreExtras, 433 Fields{ 434 "Chaincodes": ConsistOf(chaincodes), 435 }, 436 ))), 437 }, 438 )), 439 ) 440 } 441 442 func QueryInstalledNoReferences(n *Network, channel, label, packageID string, checkPeer *Peer) { 443 } 444 445 type queryInstalledOutput struct { 446 InstalledChaincodes []lifecycle.QueryInstalledChaincodesResult_InstalledChaincode `json:"installed_chaincodes"` 447 } 448 449 func QueryInstalled(n *Network, peer *Peer) func() []lifecycle.QueryInstalledChaincodesResult_InstalledChaincode { 450 return func() []lifecycle.QueryInstalledChaincodesResult_InstalledChaincode { 451 sess, err := n.PeerAdminSession(peer, commands.ChaincodeQueryInstalled{ 452 ClientAuth: n.ClientAuthRequired, 453 }) 454 Expect(err).NotTo(HaveOccurred()) 455 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 456 output := &queryInstalledOutput{} 457 err = json.Unmarshal(sess.Out.Contents(), output) 458 Expect(err).NotTo(HaveOccurred()) 459 return output.InstalledChaincodes 460 } 461 } 462 463 type checkCommitReadinessOutput struct { 464 Approvals map[string]bool `json:"approvals"` 465 } 466 467 func checkCommitReadiness(n *Network, peer *Peer, channel string, chaincode Chaincode) func() map[string]bool { 468 return func() map[string]bool { 469 sess, err := n.PeerAdminSession(peer, commands.ChaincodeCheckCommitReadiness{ 470 ChannelID: channel, 471 Name: chaincode.Name, 472 Version: chaincode.Version, 473 Sequence: chaincode.Sequence, 474 EndorsementPlugin: chaincode.EndorsementPlugin, 475 ValidationPlugin: chaincode.ValidationPlugin, 476 SignaturePolicy: chaincode.SignaturePolicy, 477 ChannelConfigPolicy: chaincode.ChannelConfigPolicy, 478 InitRequired: chaincode.InitRequired, 479 CollectionsConfig: chaincode.CollectionsConfig, 480 ClientAuth: n.ClientAuthRequired, 481 }) 482 Expect(err).NotTo(HaveOccurred()) 483 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 484 output := &checkCommitReadinessOutput{} 485 err = json.Unmarshal(sess.Out.Contents(), output) 486 Expect(err).NotTo(HaveOccurred()) 487 return output.Approvals 488 } 489 } 490 491 type queryCommittedOutput struct { 492 Sequence int64 `json:"sequence"` 493 Version string `json:"version"` 494 Approvals map[string]bool `json:"approvals"` 495 } 496 497 // listCommitted returns the result of the queryCommitted command. 498 // If the command fails for any reason (e.g. namespace not defined 499 // or a database access issue), it will return an empty output object. 500 func listCommitted(n *Network, peer *Peer, channel, name string) func() queryCommittedOutput { 501 return func() queryCommittedOutput { 502 sess, err := n.PeerAdminSession(peer, commands.ChaincodeListCommitted{ 503 ChannelID: channel, 504 Name: name, 505 ClientAuth: n.ClientAuthRequired, 506 }) 507 Expect(err).NotTo(HaveOccurred()) 508 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit()) 509 output := &queryCommittedOutput{} 510 if sess.ExitCode() == 1 { 511 // don't try to unmarshal the output as JSON if the query failed 512 return *output 513 } 514 err = json.Unmarshal(sess.Out.Contents(), output) 515 Expect(err).NotTo(HaveOccurred()) 516 return *output 517 } 518 } 519 520 func listInstantiatedLegacy(n *Network, peer *Peer, channel string) func() *gbytes.Buffer { 521 return func() *gbytes.Buffer { 522 sess, err := n.PeerAdminSession(peer, commands.ChaincodeListInstantiatedLegacy{ 523 ChannelID: channel, 524 ClientAuth: n.ClientAuthRequired, 525 }) 526 Expect(err).NotTo(HaveOccurred()) 527 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 528 return sess.Buffer() 529 } 530 } 531 532 // EnableCapabilities enables a specific capabilities flag for a running network. 533 // It generates the config update using the first peer, signs the configuration 534 // with the subsequent peers, and then submits the config update using the 535 // first peer. 536 func EnableCapabilities(network *Network, channel, capabilitiesGroup, capabilitiesVersion string, orderer *Orderer, peers ...*Peer) { 537 if len(peers) == 0 { 538 return 539 } 540 541 config := GetConfig(network, peers[0], orderer, channel) 542 updatedConfig := proto.Clone(config).(*common.Config) 543 544 updatedConfig.ChannelGroup.Groups[capabilitiesGroup].Values["Capabilities"] = &common.ConfigValue{ 545 ModPolicy: "Admins", 546 Value: protoutil.MarshalOrPanic( 547 &common.Capabilities{ 548 Capabilities: map[string]*common.Capability{ 549 capabilitiesVersion: {}, 550 }, 551 }, 552 ), 553 } 554 555 UpdateConfig(network, orderer, channel, config, updatedConfig, false, peers[0], peers...) 556 } 557 558 // WaitUntilEqualLedgerHeight waits until all specified peers have the 559 // provided ledger height on a channel 560 func WaitUntilEqualLedgerHeight(n *Network, channel string, height int, peers ...*Peer) { 561 for _, peer := range peers { 562 Eventually(func() int { 563 return GetLedgerHeight(n, peer, channel) 564 }, n.EventuallyTimeout).Should(Equal(height)) 565 } 566 } 567 568 // GetLedgerHeight returns the current ledger height for a peer on 569 // a channel 570 func GetLedgerHeight(n *Network, peer *Peer, channel string) int { 571 sess, err := n.PeerUserSession(peer, "User1", commands.ChannelInfo{ 572 ChannelID: channel, 573 ClientAuth: n.ClientAuthRequired, 574 }) 575 Expect(err).NotTo(HaveOccurred()) 576 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit()) 577 578 if sess.ExitCode() == 1 { 579 // if org is not yet member of channel, peer will return error 580 return -1 581 } 582 583 channelInfoStr := strings.TrimPrefix(string(sess.Buffer().Contents()[:]), "Blockchain info:") 584 var channelInfo = common.BlockchainInfo{} 585 json.Unmarshal([]byte(channelInfoStr), &channelInfo) 586 return int(channelInfo.Height) 587 } 588 589 // GetMaxLedgerHeight returns the maximum ledger height for the 590 // peers on a channel 591 func GetMaxLedgerHeight(n *Network, channel string, peers ...*Peer) int { 592 var maxHeight int 593 for _, peer := range peers { 594 peerHeight := GetLedgerHeight(n, peer, channel) 595 if peerHeight > maxHeight { 596 maxHeight = peerHeight 597 } 598 } 599 return maxHeight 600 }