From faf1f9977546f4308da1f407a2b3061f4f116652 Mon Sep 17 00:00:00 2001 From: Will Lahti Date: Thu, 31 Jan 2019 11:56:05 -0500 Subject: [PATCH] Add integration "ApproveChaincodeDefinitionForMyOrg" FAB-14017 Change-Id: Iac00ff8368f01c1553448fcd50f2840b5c16a180 Signed-off-by: Will Lahti Signed-off-by: Gari Singh --- integration/discovery/discovery_test.go | 2 +- integration/e2e/acl_test.go | 2 +- integration/nwo/commands/peer.go | 53 ++++++++++++ integration/nwo/configblock.go | 42 +++++++++- integration/nwo/core_template.go | 2 +- integration/nwo/deploy.go | 107 ++++++++++++++++++++---- integration/nwo/network_test.go | 47 +++++++---- 7 files changed, 218 insertions(+), 37 deletions(-) diff --git a/integration/discovery/discovery_test.go b/integration/discovery/discovery_test.go index 0e0e8605ca4..23e165b63c4 100644 --- a/integration/discovery/discovery_test.go +++ b/integration/discovery/discovery_test.go @@ -180,7 +180,7 @@ var _ = Describe("DiscoveryService", func() { currentConfig := nwo.GetConfig(network, network.Peer("org3", "peer0"), orderer, "testchannel") updatedConfig := proto.Clone(currentConfig).(*common.Config) updatedConfig.ChannelGroup.Groups["Application"].Groups["org3"].Policies["Writers"].Policy.Value = protoutil.MarshalOrPanic(cauthdsl.SignedByMspAdmin("Org3MSP")) - nwo.UpdateConfig(network, orderer, "testchannel", currentConfig, updatedConfig, network.Peer("org3", "peer0")) + nwo.UpdateConfig(network, orderer, "testchannel", currentConfig, updatedConfig, true, network.Peer("org3", "peer0")) By("trying to discover peers as an org 3 member") endorsers = commands.Endorsers{ diff --git a/integration/e2e/acl_test.go b/integration/e2e/acl_test.go index c6ed381eaf1..b557e4650aa 100644 --- a/integration/e2e/acl_test.go +++ b/integration/e2e/acl_test.go @@ -261,7 +261,7 @@ func SetACLPolicy(network *nwo.Network, channel, policyName, policy string, orde }), } - nwo.UpdateConfig(network, orderer, channel, config, updatedConfig, submitter, signer) + nwo.UpdateConfig(network, orderer, channel, config, updatedConfig, true, submitter, signer) } // GetTxIDFromBlock gets a transaction id from a block that has been diff --git a/integration/nwo/commands/peer.go b/integration/nwo/commands/peer.go index bf87ab999a8..ec0c867c0b2 100644 --- a/integration/nwo/commands/peer.go +++ b/integration/nwo/commands/peer.go @@ -196,6 +196,59 @@ func (c ChaincodeInstall) Args() []string { return args } +type ChaincodeApproveForMyOrg struct { + ChannelID string + Orderer string + Name string + Version string + Hash string + Sequence string + EndorsementPlugin string + ValidationPlugin string + Policy string + InitRequired bool + CollectionsConfig string + PeerAddresses []string + WaitForEvent bool +} + +func (c ChaincodeApproveForMyOrg) SessionName() string { + return "peer-chaincode-approveformyorg" +} + +func (c ChaincodeApproveForMyOrg) Args() []string { + args := []string{ + "chaincode", "approveformyorg", + "--channelID", c.ChannelID, + "--orderer", c.Orderer, + "--name", c.Name, + "--version", c.Version, + "--hash", c.Hash, + "--sequence", c.Sequence, + "--escc", c.EndorsementPlugin, + "--vscc", c.ValidationPlugin, + "--policy", c.Policy, + } + + if c.InitRequired { + args = append(args, "--init-required") + } + + if c.CollectionsConfig != "" { + args = append(args, "--collections-config", c.CollectionsConfig) + } + + for _, p := range c.PeerAddresses { + args = append(args, "--peerAddresses", p) + } + + if c.WaitForEvent { + args = append(args, "--waitForEvent") + } + + return args +} + type ChaincodeInstantiate struct { ChannelID string Orderer string diff --git a/integration/nwo/configblock.go b/integration/nwo/configblock.go index 2be7750e889..c068c26df5d 100644 --- a/integration/nwo/configblock.go +++ b/integration/nwo/configblock.go @@ -71,7 +71,7 @@ func GetConfig(n *Network, peer *Peer, orderer *Orderer, channel string) *common // UpdateConfig computes, signs, and submits a configuration update and waits // for the update to complete. -func UpdateConfig(n *Network, orderer *Orderer, channel string, current, updated *common.Config, submitter *Peer, additionalSigners ...*Peer) { +func UpdateConfig(n *Network, orderer *Orderer, channel string, current, updated *common.Config, getConfigBlockFromOrderer bool, submitter *Peer, additionalSigners ...*Peer) { tempDir, err := ioutil.TempDir("", "updateConfig") Expect(err).NotTo(HaveOccurred()) defer os.RemoveAll(tempDir) @@ -102,8 +102,13 @@ func UpdateConfig(n *Network, orderer *Orderer, channel string, current, updated Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) } + var currentBlockNumber uint64 // get current configuration block number - currentBlockNumber := CurrentConfigBlockNumber(n, submitter, orderer, channel) + if getConfigBlockFromOrderer { + currentBlockNumber = CurrentConfigBlockNumber(n, submitter, orderer, channel) + } else { + currentBlockNumber = CurrentConfigBlockNumber(n, submitter, nil, channel) + } sess, err := n.PeerAdminSession(submitter, commands.ChannelUpdate{ ChannelID: channel, @@ -114,10 +119,15 @@ func UpdateConfig(n *Network, orderer *Orderer, channel string, current, updated Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) Expect(sess.Err).To(gbytes.Say("Successfully submitted channel update")) + if getConfigBlockFromOrderer { + ccb := func() uint64 { return CurrentConfigBlockNumber(n, submitter, orderer, channel) } + Eventually(ccb, n.EventuallyTimeout).Should(BeNumerically(">", currentBlockNumber)) + return + } // wait for the block to be committed to all peers that // have joined the channel for _, peer := range n.PeersWithChannel(channel) { - ccb := func() uint64 { return CurrentConfigBlockNumber(n, peer, orderer, channel) } + ccb := func() uint64 { return CurrentConfigBlockNumber(n, peer, nil, channel) } Eventually(ccb, n.EventuallyTimeout).Should(BeNumerically(">", currentBlockNumber)) } } @@ -160,7 +170,8 @@ func UpdateOrdererConfig(n *Network, orderer *Orderer, channel string, current, // CurrentConfigBlockNumber retrieves the block number from the header of the // current config block. This can be used to detect when configuration change -// has completed. +// has completed. If an orderer is not provided, the current config block will +// be fetched from the peer. func CurrentConfigBlockNumber(n *Network, peer *Peer, orderer *Orderer, channel string) uint64 { tempDir, err := ioutil.TempDir("", "currentConfigBlock") Expect(err).NotTo(HaveOccurred()) @@ -168,6 +179,11 @@ func CurrentConfigBlockNumber(n *Network, peer *Peer, orderer *Orderer, channel // fetch the config block output := filepath.Join(tempDir, "config_block.pb") + + if orderer == nil { + return CurrentConfigBlockNumberFromPeer(n, peer, channel, output) + } + sess, err := n.OrdererAdminSession(orderer, peer, commands.ChannelFetch{ ChannelID: channel, Block: "config", @@ -180,6 +196,24 @@ func CurrentConfigBlockNumber(n *Network, peer *Peer, orderer *Orderer, channel // unmarshal the config block bytes configBlock := UnmarshalBlockFromFile(output) + + return configBlock.Header.Number +} + +// CurrentConfigBlockNumberFromPeer retrieves the block number from the header +// of the peer's current config block. +func CurrentConfigBlockNumberFromPeer(n *Network, peer *Peer, channel, output string) uint64 { + sess, err := n.PeerAdminSession(peer, commands.ChannelFetch{ + ChannelID: channel, + Block: "config", + OutputFile: output, + }) + Expect(err).NotTo(HaveOccurred()) + Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) + Expect(sess.Err).To(gbytes.Say("Received block: ")) + + configBlock := UnmarshalBlockFromFile(output) + return configBlock.Header.Number } diff --git a/integration/nwo/core_template.go b/integration/nwo/core_template.go index c4267ef440b..e5d9e5ceb7a 100644 --- a/integration/nwo/core_template.go +++ b/integration/nwo/core_template.go @@ -59,7 +59,7 @@ peer: leaderAliveThreshold: 10s leaderElectionDuration: 5s pvtData: - pullRetryThreshold: 60s + pullRetryThreshold: 15s transientstoreMaxBlockRetention: 1000 pushAckTimeout: 3s reconcileBatchSize: 10 diff --git a/integration/nwo/deploy.go b/integration/nwo/deploy.go index 243c0e0883a..69d6bfe8576 100644 --- a/integration/nwo/deploy.go +++ b/integration/nwo/deploy.go @@ -7,11 +7,16 @@ SPDX-License-Identifier: Apache-2.0 package nwo import ( + "encoding/hex" "fmt" "io/ioutil" "os" + "github.com/golang/protobuf/proto" + "github.com/hyperledger/fabric/common/util" "github.com/hyperledger/fabric/integration/nwo/commands" + "github.com/hyperledger/fabric/protos/common" + "github.com/hyperledger/fabric/protoutil" . "github.com/onsi/gomega" "github.com/onsi/gomega/gbytes" "github.com/onsi/gomega/gexec" @@ -26,15 +31,22 @@ type Chaincode struct { Lang string CollectionsConfig string // optional PackageFile string + Hash string + Sequence string + EndorsementPlugin string + ValidationPlugin string + InitRequired bool } -// DeployChaincodePlusLifecycle is a helper that will install chaincode to all -// peers that are connected to the specified channel, instantiate the chaincode -// on one of the peers, and wait for the instantiation to complete on all of -// the peers. It uses the _lifecycle implementation. -// TODO: add _lifecycle DefineChaincode functionality once it has been -// be implemented server-side -func DeployChaincodePlusLifecycle(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peers ...*Peer) { +// DeployChaincodeNewLifecycle is a helper that will install chaincode to all +// peers that are connected to the specified channel, approve the chaincode +// on one of the peers of each organization in the network, commit the chaincode +// definition on the channel using one of the peers, and wait for the chaincode +// commit to complete on all of the peers. It uses the _lifecycle implementation. +// NOTE V2_0 capabilities must be enabled for this functionality to work. +// TODO add _lifecycle CommitChaincode functionality once it has been +// implemented server-side +func DeployChaincodeNewLifecycle(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peers ...*Peer) { if len(peers) == 0 { peers = n.PeersWithChannel(channel) } @@ -52,13 +64,18 @@ func DeployChaincodePlusLifecycle(n *Network, channel string, orderer *Orderer, } // package using the first peer - PackageChaincodePlusLifecycle(n, chaincode, peers[0]) + PackageChaincodeNewLifecycle(n, chaincode, peers[0]) // install on all peers - InstallChaincodePlusLifecycle(n, chaincode, peers...) + InstallChaincodeNewLifecycle(n, chaincode, peers...) - // define using the first peer - // DefineChaincode(n, channel, orderer, chaincode, peers[0], peers...) + // approve for each org + for _, org := range n.PeerOrgs() { + ApproveChaincodeForMyOrgNewLifecycle(n, channel, orderer, chaincode, n.PeersInOrg(org.Name)...) + } + + // commit using the first peer + // CommitChaincode(n, channel, orderer, chaincode, peers[0], peers...) } // DeployChaincode is a helper that will install chaincode to all peers @@ -96,7 +113,7 @@ func DeployChaincode(n *Network, channel string, orderer *Orderer, chaincode Cha InstantiateChaincode(n, channel, orderer, chaincode, peers[0], peers...) } -func PackageChaincodePlusLifecycle(n *Network, chaincode Chaincode, peer *Peer) { +func PackageChaincodeNewLifecycle(n *Network, chaincode Chaincode, peer *Peer) { sess, err := n.PeerAdminSession(peer, commands.ChaincodePackagePlusLifecycle{ Path: chaincode.Path, Lang: chaincode.Lang, @@ -118,7 +135,7 @@ func PackageChaincode(n *Network, chaincode Chaincode, peer *Peer) { Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) } -func InstallChaincodePlusLifecycle(n *Network, chaincode Chaincode, peers ...*Peer) { +func InstallChaincodeNewLifecycle(n *Network, chaincode Chaincode, peers ...*Peer) { for _, p := range peers { sess, err := n.PeerAdminSession(p, commands.ChaincodeInstallPlusLifecycle{ Name: chaincode.Name, @@ -131,7 +148,7 @@ func InstallChaincodePlusLifecycle(n *Network, chaincode Chaincode, peers ...*Pe sess, err = n.PeerAdminSession(p, commands.ChaincodeListInstalledPlusLifecycle{}) Expect(err).NotTo(HaveOccurred()) Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) - Expect(sess).To(gbytes.Say(fmt.Sprintf("Name: %s, Version: %s,", chaincode.Name, chaincode.Version))) + Expect(sess).To(gbytes.Say(fmt.Sprintf("Name: %s, Version: %s, Hash:", chaincode.Name, chaincode.Version))) } } @@ -154,6 +171,41 @@ func InstallChaincode(n *Network, chaincode Chaincode, peers ...*Peer) { } } +func ApproveChaincodeForMyOrgNewLifecycle(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peers ...*Peer) { + if chaincode.Hash == "" { + pkgBytes, err := ioutil.ReadFile(chaincode.PackageFile) + Expect(err).NotTo(HaveOccurred()) + hash := util.ComputeSHA256(pkgBytes) + chaincode.Hash = hex.EncodeToString(hash) + } + + // TODO Figure out why private data is unable to be fetched by the + // other peers for the org. For now, we will have each peer in the + // org endorse the proposal. + peerAddresses := []string{} + for _, p := range peers { + peerAddresses = append(peerAddresses, n.PeerAddress(p, ListenPort)) + } + + sess, err := n.PeerAdminSession(peers[0], commands.ChaincodeApproveForMyOrg{ + ChannelID: channel, + Orderer: n.OrdererAddress(orderer, ListenPort), + Name: chaincode.Name, + Version: chaincode.Version, + Hash: chaincode.Hash, + Sequence: chaincode.Sequence, + EndorsementPlugin: chaincode.EndorsementPlugin, + ValidationPlugin: chaincode.ValidationPlugin, + Policy: chaincode.Policy, + InitRequired: chaincode.InitRequired, + PeerAddresses: peerAddresses, + WaitForEvent: true, + }) + Expect(err).NotTo(HaveOccurred()) + Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) + Eventually(sess.Err, n.EventuallyTimeout).Should(gbytes.Say(`\Qcommitted with status (VALID)\E`)) +} + func InstantiateChaincode(n *Network, channel string, orderer *Orderer, chaincode Chaincode, peer *Peer, checkPeers ...*Peer) { sess, err := n.PeerAdminSession(peer, commands.ChaincodeInstantiate{ ChannelID: channel, @@ -216,3 +268,30 @@ func listInstantiated(n *Network, peer *Peer, channel string) func() *gbytes.Buf return sess.Buffer() } } + +// EnableV2_0Capabilities enables the V2_0 capabilities for a running network. +// It generates the config update using the first peer, signs the configuration +// with the subsequent peers, and then submits the config update using the +// first peer. +func EnableV2_0Capabilities(network *Network, channel string, orderer *Orderer, peers ...*Peer) { + if len(peers) == 0 { + return + } + + config := GetConfig(network, peers[0], orderer, channel) + updatedConfig := proto.Clone(config).(*common.Config) + + // include the V2_0 capability in the config + updatedConfig.ChannelGroup.Groups["Application"].Values["Capabilities"] = &common.ConfigValue{ + ModPolicy: "Admins", + Value: protoutil.MarshalOrPanic( + &common.Capabilities{ + Capabilities: map[string]*common.Capability{ + "V2_0": {}, + }, + }, + ), + } + + UpdateConfig(network, orderer, channel, config, updatedConfig, false, peers[0], peers...) +} diff --git a/integration/nwo/network_test.go b/integration/nwo/network_test.go index 4c73243c80c..11c5466643a 100644 --- a/integration/nwo/network_test.go +++ b/integration/nwo/network_test.go @@ -97,18 +97,23 @@ var _ = Describe("Network", func() { // peer := network.Peer("org1", "peer2") chaincode := nwo.Chaincode{ - Name: "mycc", - Version: "0.0", - Path: "github.com/hyperledger/fabric/integration/chaincode/simple/cmd", - Lang: "golang", - PackageFile: filepath.Join(tempDir, "simplecc.tar.gz"), - Ctor: `{"Args":["init","a","100","b","200"]}`, - Policy: `AND ('Org1ExampleCom.member','Org2ExampleCom.member')`, + Name: "mycc", + Version: "0.0", + Path: "github.com/hyperledger/fabric/integration/chaincode/simple/cmd", + Lang: "golang", + PackageFile: filepath.Join(tempDir, "simplecc.tar.gz"), + Ctor: `{"Args":["init","a","100","b","200"]}`, + EndorsementPlugin: "escc", + ValidationPlugin: "vscc", + Policy: `AND ('Org1ExampleCom.member','Org2ExampleCom.member')`, + Sequence: "1", + InitRequired: true, } network.CreateAndJoinChannels(orderer) - nwo.DeployChaincodePlusLifecycle(network, "testchannel", orderer, chaincode) - // TODO: uncomment once _lifecycle Define functionality is available + nwo.EnableV2_0Capabilities(network, "testchannel", orderer, network.Peer("org1", "peer1"), network.Peer("org2", "peer1")) + nwo.DeployChaincodeNewLifecycle(network, "testchannel", orderer, chaincode) + // TODO uncomment once _lifecycle Commit functionality is fully functional // server-side and has been added to nwo // RunQueryInvokeQuery(network, orderer, peer) }) @@ -240,16 +245,26 @@ var _ = Describe("Network", func() { testPeers := network.PeersWithChannel("testchannel") network.CreateChannel("testchannel", orderer, testPeers[0]) network.JoinChannel("testchannel", orderer, testPeers...) + nwo.EnableV2_0Capabilities(network, "testchannel", orderer, network.Peer("org1", "peer1"), network.Peer("org2", "peer1")) chaincode := nwo.Chaincode{ - Name: "mycc", - Version: "0.0", - Path: "github.com/hyperledger/fabric/integration/chaincode/simple/cmd", - Lang: "golang", - PackageFile: filepath.Join(tempDir, "simplecc.tar.gz"), + Name: "mycc", + Version: "0.0", + Path: "github.com/hyperledger/fabric/integration/chaincode/simple/cmd", + Lang: "golang", + PackageFile: filepath.Join(tempDir, "simplecc.tar.gz"), + Ctor: `{"Args":["init","a","100","b","200"]}`, + EndorsementPlugin: "escc", + ValidationPlugin: "vscc", + Policy: `AND ('Org1ExampleCom.member','Org2ExampleCom.member')`, + Sequence: "1", + InitRequired: true, + } + nwo.PackageChaincodeNewLifecycle(network, chaincode, testPeers[0]) + nwo.InstallChaincodeNewLifecycle(network, chaincode, testPeers...) + for _, org := range network.PeerOrgs() { + nwo.ApproveChaincodeForMyOrgNewLifecycle(network, "testchannel", orderer, chaincode, network.PeersInOrg(org.Name)...) } - nwo.PackageChaincodePlusLifecycle(network, chaincode, testPeers[0]) - nwo.InstallChaincodePlusLifecycle(network, chaincode, testPeers...) }) }) })