github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/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/osdi23p228/fabric/common/util" 22 "github.com/osdi23p228/fabric/integration/nwo/commands" 23 "github.com/osdi23p228/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 EventuallyWithOffset(1, 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 EventuallyWithOffset(1, sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 218 ExpectWithOffset(1, 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(fmt.Sprintf(`\Qcommitted with status (VALID) at %s\E`, n.PeerAddress(p, ListenPort)))) 250 } 251 } 252 } 253 254 func EnsureChaincodeApproved(n *Network, peer *Peer, channel, name, sequence string) { 255 sequenceInt, err := strconv.ParseInt(sequence, 10, 64) 256 Expect(err).NotTo(HaveOccurred()) 257 Eventually(queryApproved(n, peer, channel, name, sequence), n.EventuallyTimeout).Should( 258 MatchFields(IgnoreExtras, Fields{ 259 "Sequence": Equal(sequenceInt), 260 }), 261 ) 262 } 263 264 func CheckCommitReadinessUntilReady(n *Network, channel string, chaincode Chaincode, checkOrgs []*Organization, peers ...*Peer) { 265 for _, p := range peers { 266 keys := Keys{} 267 for _, org := range checkOrgs { 268 keys[org.MSPID] = BeTrue() 269 } 270 Eventually(checkCommitReadiness(n, p, channel, chaincode), n.EventuallyTimeout).Should(MatchKeys(IgnoreExtras, keys)) 271 } 272 } 273 274 func CommitChaincode(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peer *Peer, checkPeers ...*Peer) { 275 // commit using one peer per org 276 commitOrgs := map[string]bool{} 277 var peerAddresses []string 278 for _, p := range checkPeers { 279 if exists := commitOrgs[p.Organization]; !exists { 280 peerAddresses = append(peerAddresses, n.PeerAddress(p, ListenPort)) 281 commitOrgs[p.Organization] = true 282 } 283 } 284 285 sess, err := n.PeerAdminSession(peer, commands.ChaincodeCommit{ 286 ChannelID: channel, 287 Orderer: n.OrdererAddress(orderer, ListenPort), 288 Name: chaincode.Name, 289 Version: chaincode.Version, 290 Sequence: chaincode.Sequence, 291 EndorsementPlugin: chaincode.EndorsementPlugin, 292 ValidationPlugin: chaincode.ValidationPlugin, 293 SignaturePolicy: chaincode.SignaturePolicy, 294 ChannelConfigPolicy: chaincode.ChannelConfigPolicy, 295 InitRequired: chaincode.InitRequired, 296 CollectionsConfig: chaincode.CollectionsConfig, 297 PeerAddresses: peerAddresses, 298 ClientAuth: n.ClientAuthRequired, 299 }) 300 Expect(err).NotTo(HaveOccurred()) 301 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 302 for i := 0; i < len(peerAddresses); i++ { 303 Eventually(sess.Err, n.EventuallyTimeout).Should(gbytes.Say(`\Qcommitted with status (VALID)\E`)) 304 } 305 checkOrgs := []*Organization{} 306 for org := range commitOrgs { 307 checkOrgs = append(checkOrgs, n.Organization(org)) 308 } 309 EnsureChaincodeCommitted(n, channel, chaincode.Name, chaincode.Version, chaincode.Sequence, checkOrgs, checkPeers...) 310 } 311 312 // EnsureChaincodeCommitted polls each supplied peer until the chaincode definition 313 // has been committed to the peer's ledger. 314 func EnsureChaincodeCommitted(n *Network, channel, name, version, sequence string, checkOrgs []*Organization, peers ...*Peer) { 315 for _, p := range peers { 316 sequenceInt, err := strconv.ParseInt(sequence, 10, 64) 317 Expect(err).NotTo(HaveOccurred()) 318 approvedKeys := Keys{} 319 for _, org := range checkOrgs { 320 approvedKeys[org.MSPID] = BeTrue() 321 } 322 Eventually(listCommitted(n, p, channel, name), n.EventuallyTimeout).Should( 323 MatchFields(IgnoreExtras, Fields{ 324 "Version": Equal(version), 325 "Sequence": Equal(sequenceInt), 326 "Approvals": MatchKeys(IgnoreExtras, approvedKeys), 327 }), 328 ) 329 } 330 } 331 332 func InitChaincode(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peers ...*Peer) { 333 // init using one peer per org 334 initOrgs := map[string]bool{} 335 var peerAddresses []string 336 for _, p := range peers { 337 if exists := initOrgs[p.Organization]; !exists { 338 peerAddresses = append(peerAddresses, n.PeerAddress(p, ListenPort)) 339 initOrgs[p.Organization] = true 340 } 341 } 342 343 sess, err := n.PeerUserSession(peers[0], "User1", commands.ChaincodeInvoke{ 344 ChannelID: channel, 345 Orderer: n.OrdererAddress(orderer, ListenPort), 346 Name: chaincode.Name, 347 Ctor: chaincode.Ctor, 348 PeerAddresses: peerAddresses, 349 WaitForEvent: true, 350 IsInit: true, 351 ClientAuth: n.ClientAuthRequired, 352 }) 353 Expect(err).NotTo(HaveOccurred()) 354 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 355 for i := 0; i < len(peerAddresses); i++ { 356 Eventually(sess.Err, n.EventuallyTimeout).Should(gbytes.Say(`\Qcommitted with status (VALID)\E`)) 357 } 358 Expect(sess.Err).To(gbytes.Say("Chaincode invoke successful. result: status:200")) 359 } 360 361 func InstantiateChaincodeLegacy(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peer *Peer, checkPeers ...*Peer) { 362 sess, err := n.PeerAdminSession(peer, commands.ChaincodeInstantiateLegacy{ 363 ChannelID: channel, 364 Orderer: n.OrdererAddress(orderer, ListenPort), 365 Name: chaincode.Name, 366 Version: chaincode.Version, 367 Ctor: chaincode.Ctor, 368 Policy: chaincode.Policy, 369 Lang: chaincode.Lang, 370 CollectionsConfig: chaincode.CollectionsConfig, 371 ClientAuth: n.ClientAuthRequired, 372 }) 373 Expect(err).NotTo(HaveOccurred()) 374 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 375 376 EnsureInstantiatedLegacy(n, channel, chaincode.Name, chaincode.Version, checkPeers...) 377 } 378 379 func EnsureInstantiatedLegacy(n *Network, channel, name, version string, peers ...*Peer) { 380 for _, p := range peers { 381 Eventually(listInstantiatedLegacy(n, p, channel), n.EventuallyTimeout).Should( 382 gbytes.Say(fmt.Sprintf("Name: %s, Version: %s,", name, version)), 383 ) 384 } 385 } 386 387 func UpgradeChaincodeLegacy(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peers ...*Peer) { 388 if len(peers) == 0 { 389 peers = n.PeersWithChannel(channel) 390 } 391 if len(peers) == 0 { 392 return 393 } 394 395 // install on all peers 396 InstallChaincodeLegacy(n, chaincode, peers...) 397 398 // upgrade from the first peer 399 sess, err := n.PeerAdminSession(peers[0], commands.ChaincodeUpgradeLegacy{ 400 ChannelID: channel, 401 Orderer: n.OrdererAddress(orderer, ListenPort), 402 Name: chaincode.Name, 403 Version: chaincode.Version, 404 Ctor: chaincode.Ctor, 405 Policy: chaincode.Policy, 406 CollectionsConfig: chaincode.CollectionsConfig, 407 ClientAuth: n.ClientAuthRequired, 408 }) 409 Expect(err).NotTo(HaveOccurred()) 410 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 411 412 EnsureInstantiatedLegacy(n, channel, chaincode.Name, chaincode.Version, peers...) 413 } 414 415 func EnsureInstalled(n *Network, label, packageID string, peers ...*Peer) { 416 for _, p := range peers { 417 Eventually(QueryInstalled(n, p), n.EventuallyTimeout).Should( 418 ContainElement(MatchFields(IgnoreExtras, 419 Fields{ 420 "Label": Equal(label), 421 "PackageId": Equal(packageID), 422 }, 423 )), 424 ) 425 } 426 } 427 428 func QueryInstalledReferences(n *Network, channel, label, packageID string, checkPeer *Peer, nameVersions ...[]string) { 429 chaincodes := make([]*lifecycle.QueryInstalledChaincodesResult_Chaincode, len(nameVersions)) 430 for i, nameVersion := range nameVersions { 431 chaincodes[i] = &lifecycle.QueryInstalledChaincodesResult_Chaincode{ 432 Name: nameVersion[0], 433 Version: nameVersion[1], 434 } 435 } 436 437 Expect(QueryInstalled(n, checkPeer)()).To( 438 ContainElement(MatchFields(IgnoreExtras, 439 Fields{ 440 "Label": Equal(label), 441 "PackageId": Equal(packageID), 442 "References": HaveKeyWithValue(channel, PointTo(MatchFields(IgnoreExtras, 443 Fields{ 444 "Chaincodes": ConsistOf(chaincodes), 445 }, 446 ))), 447 }, 448 )), 449 ) 450 } 451 452 type queryInstalledOutput struct { 453 InstalledChaincodes []lifecycle.QueryInstalledChaincodesResult_InstalledChaincode `json:"installed_chaincodes"` 454 } 455 456 func QueryInstalled(n *Network, peer *Peer) func() []lifecycle.QueryInstalledChaincodesResult_InstalledChaincode { 457 return func() []lifecycle.QueryInstalledChaincodesResult_InstalledChaincode { 458 sess, err := n.PeerAdminSession(peer, commands.ChaincodeQueryInstalled{ 459 ClientAuth: n.ClientAuthRequired, 460 }) 461 Expect(err).NotTo(HaveOccurred()) 462 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 463 output := &queryInstalledOutput{} 464 err = json.Unmarshal(sess.Out.Contents(), output) 465 Expect(err).NotTo(HaveOccurred()) 466 return output.InstalledChaincodes 467 } 468 } 469 470 type checkCommitReadinessOutput struct { 471 Approvals map[string]bool `json:"approvals"` 472 } 473 474 func checkCommitReadiness(n *Network, peer *Peer, channel string, chaincode Chaincode) func() map[string]bool { 475 return func() map[string]bool { 476 sess, err := n.PeerAdminSession(peer, commands.ChaincodeCheckCommitReadiness{ 477 ChannelID: channel, 478 Name: chaincode.Name, 479 Version: chaincode.Version, 480 Sequence: chaincode.Sequence, 481 EndorsementPlugin: chaincode.EndorsementPlugin, 482 ValidationPlugin: chaincode.ValidationPlugin, 483 SignaturePolicy: chaincode.SignaturePolicy, 484 ChannelConfigPolicy: chaincode.ChannelConfigPolicy, 485 InitRequired: chaincode.InitRequired, 486 CollectionsConfig: chaincode.CollectionsConfig, 487 ClientAuth: n.ClientAuthRequired, 488 }) 489 Expect(err).NotTo(HaveOccurred()) 490 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 491 output := &checkCommitReadinessOutput{} 492 err = json.Unmarshal(sess.Out.Contents(), output) 493 Expect(err).NotTo(HaveOccurred()) 494 return output.Approvals 495 } 496 } 497 498 type queryApprovedOutput struct { 499 Sequence int64 `json:"sequence"` 500 } 501 502 // queryApproved returns the result of the queryApproved command. 503 // If the command fails for any reason, it will return an empty output object. 504 func queryApproved(n *Network, peer *Peer, channel, name, sequence string) func() queryApprovedOutput { 505 return func() queryApprovedOutput { 506 sess, err := n.PeerAdminSession(peer, commands.ChaincodeQueryApproved{ 507 ChannelID: channel, 508 Name: name, 509 Sequence: sequence, 510 PeerAddresses: []string{n.PeerAddress(peer, ListenPort)}, 511 ClientAuth: n.ClientAuthRequired, 512 }) 513 Expect(err).NotTo(HaveOccurred()) 514 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit()) 515 output := &queryApprovedOutput{} 516 if sess.ExitCode() == 1 { 517 // don't try to unmarshal the output as JSON if the query failed 518 return *output 519 } 520 err = json.Unmarshal(sess.Out.Contents(), output) 521 Expect(err).NotTo(HaveOccurred()) 522 return *output 523 } 524 } 525 526 type queryCommittedOutput struct { 527 Sequence int64 `json:"sequence"` 528 Version string `json:"version"` 529 Approvals map[string]bool `json:"approvals"` 530 } 531 532 // listCommitted returns the result of the queryCommitted command. 533 // If the command fails for any reason (e.g. namespace not defined 534 // or a database access issue), it will return an empty output object. 535 func listCommitted(n *Network, peer *Peer, channel, name string) func() queryCommittedOutput { 536 return func() queryCommittedOutput { 537 sess, err := n.PeerAdminSession(peer, commands.ChaincodeListCommitted{ 538 ChannelID: channel, 539 Name: name, 540 ClientAuth: n.ClientAuthRequired, 541 }) 542 Expect(err).NotTo(HaveOccurred()) 543 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit()) 544 output := &queryCommittedOutput{} 545 if sess.ExitCode() == 1 { 546 // don't try to unmarshal the output as JSON if the query failed 547 return *output 548 } 549 err = json.Unmarshal(sess.Out.Contents(), output) 550 Expect(err).NotTo(HaveOccurred()) 551 return *output 552 } 553 } 554 555 func listInstantiatedLegacy(n *Network, peer *Peer, channel string) func() *gbytes.Buffer { 556 return func() *gbytes.Buffer { 557 sess, err := n.PeerAdminSession(peer, commands.ChaincodeListInstantiatedLegacy{ 558 ChannelID: channel, 559 ClientAuth: n.ClientAuthRequired, 560 }) 561 Expect(err).NotTo(HaveOccurred()) 562 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 563 return sess.Buffer() 564 } 565 } 566 567 // EnableCapabilities enables a specific capabilities flag for a running network. 568 // It generates the config update using the first peer, signs the configuration 569 // with the subsequent peers, and then submits the config update using the 570 // first peer. 571 func EnableCapabilities(network *Network, channel, capabilitiesGroup, capabilitiesVersion string, orderer *Orderer, peers ...*Peer) { 572 if len(peers) == 0 { 573 return 574 } 575 576 config := GetConfig(network, peers[0], orderer, channel) 577 updatedConfig := proto.Clone(config).(*common.Config) 578 579 updatedConfig.ChannelGroup.Groups[capabilitiesGroup].Values["Capabilities"] = &common.ConfigValue{ 580 ModPolicy: "Admins", 581 Value: protoutil.MarshalOrPanic( 582 &common.Capabilities{ 583 Capabilities: map[string]*common.Capability{ 584 capabilitiesVersion: {}, 585 }, 586 }, 587 ), 588 } 589 590 UpdateConfig(network, orderer, channel, config, updatedConfig, false, peers[0], peers...) 591 } 592 593 // WaitUntilEqualLedgerHeight waits until all specified peers have the 594 // provided ledger height on a channel 595 func WaitUntilEqualLedgerHeight(n *Network, channel string, height int, peers ...*Peer) { 596 for _, peer := range peers { 597 Eventually(func() int { 598 return GetLedgerHeight(n, peer, channel) 599 }, n.EventuallyTimeout).Should(Equal(height)) 600 } 601 } 602 603 // GetLedgerHeight returns the current ledger height for a peer on 604 // a channel 605 func GetLedgerHeight(n *Network, peer *Peer, channel string) int { 606 sess, err := n.PeerUserSession(peer, "User1", commands.ChannelInfo{ 607 ChannelID: channel, 608 ClientAuth: n.ClientAuthRequired, 609 }) 610 Expect(err).NotTo(HaveOccurred()) 611 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit()) 612 613 if sess.ExitCode() == 1 { 614 // if org is not yet member of channel, peer will return error 615 return -1 616 } 617 618 channelInfoStr := strings.TrimPrefix(string(sess.Buffer().Contents()[:]), "Blockchain info:") 619 var channelInfo = common.BlockchainInfo{} 620 json.Unmarshal([]byte(channelInfoStr), &channelInfo) 621 return int(channelInfo.Height) 622 } 623 624 // GetMaxLedgerHeight returns the maximum ledger height for the 625 // peers on a channel 626 func GetMaxLedgerHeight(n *Network, channel string, peers ...*Peer) int { 627 var maxHeight int 628 for _, peer := range peers { 629 peerHeight := GetLedgerHeight(n, peer, channel) 630 if peerHeight > maxHeight { 631 maxHeight = peerHeight 632 } 633 } 634 return maxHeight 635 }