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

     1  /*
     2  Copyright hechain All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package raft
     8  
     9  import (
    10  	"crypto"
    11  	"crypto/x509"
    12  	"encoding/json"
    13  	"encoding/pem"
    14  	"fmt"
    15  	"io/ioutil"
    16  	"net/http"
    17  	"os"
    18  	"path/filepath"
    19  	"syscall"
    20  	"time"
    21  
    22  	docker "github.com/fsouza/go-dockerclient"
    23  	"github.com/golang/protobuf/proto"
    24  	"github.com/hechain20/hechain/common/ledger/blockledger/fileledger"
    25  	"github.com/hechain20/hechain/common/metrics/disabled"
    26  	"github.com/hechain20/hechain/integration/channelparticipation"
    27  	conftx "github.com/hechain20/hechain/integration/configtx"
    28  	"github.com/hechain20/hechain/integration/nwo"
    29  	"github.com/hechain20/hechain/integration/nwo/commands"
    30  	"github.com/hechain20/hechain/integration/ordererclient"
    31  	"github.com/hyperledger/fabric-config/configtx"
    32  	"github.com/hyperledger/fabric-config/configtx/orderer"
    33  	"github.com/hyperledger/fabric-protos-go/common"
    34  	. "github.com/onsi/ginkgo"
    35  	. "github.com/onsi/gomega"
    36  	"github.com/onsi/gomega/gbytes"
    37  	"github.com/onsi/gomega/gexec"
    38  	"github.com/tedsuo/ifrit"
    39  	"github.com/tedsuo/ifrit/ginkgomon"
    40  )
    41  
    42  var _ = Describe("ChannelParticipation", func() {
    43  	var (
    44  		testDir          string
    45  		client           *docker.Client
    46  		network          *nwo.Network
    47  		ordererProcesses []ifrit.Process
    48  		ordererRunners   []*ginkgomon.Runner
    49  	)
    50  
    51  	BeforeEach(func() {
    52  		var err error
    53  		testDir, err = ioutil.TempDir("", "channel-participation")
    54  		Expect(err).NotTo(HaveOccurred())
    55  
    56  		client, err = docker.NewClientFromEnv()
    57  		Expect(err).NotTo(HaveOccurred())
    58  
    59  		ordererProcesses = []ifrit.Process{}
    60  		ordererRunners = []*ginkgomon.Runner{}
    61  	})
    62  
    63  	AfterEach(func() {
    64  		for _, ordererProcess := range ordererProcesses {
    65  			ordererProcess.Signal(syscall.SIGTERM)
    66  			Eventually(ordererProcess.Wait(), network.EventuallyTimeout).Should(Receive())
    67  		}
    68  		if network != nil {
    69  			network.Cleanup()
    70  		}
    71  		os.RemoveAll(testDir)
    72  	})
    73  
    74  	restartOrderer := func(o *nwo.Orderer, index int) {
    75  		ordererProcesses[index].Signal(syscall.SIGKILL)
    76  		Eventually(ordererProcesses[index].Wait(), network.EventuallyTimeout).Should(Receive(MatchError("exit status 137")))
    77  		ordererRunner := network.OrdererRunner(o)
    78  		ordererProcess := ifrit.Invoke(ordererRunner)
    79  		Eventually(ordererProcess.Ready(), network.EventuallyTimeout).Should(BeClosed())
    80  		ordererProcesses[index] = ordererProcess
    81  		ordererRunners[index] = ordererRunner
    82  	}
    83  
    84  	Describe("three node etcdraft network without a system channel", func() {
    85  		startOrderer := func(o *nwo.Orderer) {
    86  			ordererRunner := network.OrdererRunner(o)
    87  			ordererProcess := ifrit.Invoke(ordererRunner)
    88  			Eventually(ordererProcess.Ready(), network.EventuallyTimeout).Should(BeClosed())
    89  			Eventually(ordererRunner.Err(), network.EventuallyTimeout).Should(gbytes.Say("Registrar initializing without a system channel"))
    90  			ordererProcesses = append(ordererProcesses, ordererProcess)
    91  			ordererRunners = append(ordererRunners, ordererRunner)
    92  		}
    93  
    94  		BeforeEach(func() {
    95  			network = nwo.New(multiNodeEtcdRaftTwoChannels(), testDir, client, StartPort(), components)
    96  			network.Consensus.ChannelParticipationEnabled = true
    97  			network.Consensus.BootstrapMethod = "none"
    98  			network.GenerateConfigTree()
    99  			network.Bootstrap()
   100  		})
   101  
   102  		It("starts an orderer but rejects channel creation requests via the legacy channel creation", func() {
   103  			orderer1 := network.Orderer("orderer1")
   104  			startOrderer(orderer1)
   105  
   106  			cl := channelparticipation.List(network, orderer1)
   107  			Expect(cl).To(Equal(channelparticipation.ChannelList{}))
   108  
   109  			By("attempting to create a channel without a system channel defined")
   110  			sess, err := network.PeerAdminSession(network.Peer("Org1", "peer0"), commands.ChannelCreate{
   111  				ChannelID:   "testchannel",
   112  				Orderer:     network.OrdererAddress(orderer1, nwo.ListenPort),
   113  				File:        network.CreateChannelTxPath("testchannel"),
   114  				OutputBlock: "/dev/null",
   115  				ClientAuth:  network.ClientAuthRequired,
   116  			})
   117  			Expect(err).NotTo(HaveOccurred())
   118  			Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(1))
   119  			Eventually(sess.Err, network.EventuallyTimeout).Should(gbytes.Say("channel creation request not allowed because the orderer system channel is not defined"))
   120  		})
   121  
   122  		It("joins application channels from genesis block and removes a channel using the channel participation API", func() {
   123  			orderer1 := network.Orderer("orderer1")
   124  			orderer2 := network.Orderer("orderer2")
   125  			orderer3 := network.Orderer("orderer3")
   126  			orderers := []*nwo.Orderer{orderer1, orderer2, orderer3}
   127  			consenters := []*nwo.Orderer{orderer1, orderer2}
   128  			peer := network.Peer("Org1", "peer0")
   129  
   130  			By("starting all three orderers")
   131  			for _, o := range orderers {
   132  				startOrderer(o)
   133  				cl := channelparticipation.List(network, o)
   134  				Expect(cl).To(Equal(channelparticipation.ChannelList{}))
   135  			}
   136  
   137  			genesisBlock := applicationChannelGenesisBlock(network, consenters, []*nwo.Peer{peer}, "participation-trophy")
   138  			expectedChannelInfoPT := channelparticipation.ChannelInfo{
   139  				Name:              "participation-trophy",
   140  				URL:               "/participation/v1/channels/participation-trophy",
   141  				Status:            "active",
   142  				ConsensusRelation: "consenter",
   143  				Height:            1,
   144  			}
   145  
   146  			for _, o := range consenters {
   147  				By("joining " + o.Name + " to channel as a consenter")
   148  				channelparticipation.Join(network, o, "participation-trophy", genesisBlock, expectedChannelInfoPT)
   149  				channelInfo := channelparticipation.ListOne(network, o, "participation-trophy")
   150  				Expect(channelInfo).To(Equal(expectedChannelInfoPT))
   151  			}
   152  
   153  			submitPeerTxn(orderer1, peer, network, channelparticipation.ChannelInfo{
   154  				Name:              "participation-trophy",
   155  				URL:               "/participation/v1/channels/participation-trophy",
   156  				Status:            "active",
   157  				ConsensusRelation: "consenter",
   158  				Height:            2,
   159  			})
   160  
   161  			submitPeerTxn(orderer2, peer, network, channelparticipation.ChannelInfo{
   162  				Name:              "participation-trophy",
   163  				URL:               "/participation/v1/channels/participation-trophy",
   164  				Status:            "active",
   165  				ConsensusRelation: "consenter",
   166  				Height:            3,
   167  			})
   168  
   169  			By("joining orderer3 to the channel as a follower")
   170  			// make sure we can join using a config block from one of the other orderers
   171  			configBlockPT := nwo.GetConfigBlock(network, peer, orderer2, "participation-trophy")
   172  			expectedChannelInfoPTFollower := channelparticipation.ChannelInfo{
   173  				Name:              "participation-trophy",
   174  				URL:               "/participation/v1/channels/participation-trophy",
   175  				Status:            "onboarding",
   176  				ConsensusRelation: "follower",
   177  				Height:            0,
   178  			}
   179  			channelparticipation.Join(network, orderer3, "participation-trophy", configBlockPT, expectedChannelInfoPTFollower)
   180  
   181  			By("ensuring orderer3 completes onboarding successfully")
   182  			expectedChannelInfoPTFollower.Status = "active"
   183  			expectedChannelInfoPTFollower.Height = 3
   184  			Eventually(func() channelparticipation.ChannelInfo {
   185  				return channelparticipation.ListOne(network, orderer3, "participation-trophy")
   186  			}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPTFollower))
   187  
   188  			By("adding orderer3 to the consenters set")
   189  			channelConfig := nwo.GetConfig(network, peer, orderer1, "participation-trophy")
   190  			c := configtx.New(channelConfig)
   191  			err := c.Orderer().AddConsenter(consenterChannelConfig(network, orderer3))
   192  			Expect(err).NotTo(HaveOccurred())
   193  			computeSignSubmitConfigUpdate(network, orderer1, peer, c, "participation-trophy")
   194  
   195  			By("ensuring orderer3 transitions from follower to consenter")
   196  			// config update above added a block
   197  			expectedChannelInfoPT.Height = 4
   198  			Eventually(func() channelparticipation.ChannelInfo {
   199  				return channelparticipation.ListOne(network, orderer3, "participation-trophy")
   200  			}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPT))
   201  
   202  			By("submitting transaction to orderer3 to ensure it is active")
   203  			submitPeerTxn(orderer3, peer, network, channelparticipation.ChannelInfo{
   204  				Name:              "participation-trophy",
   205  				URL:               "/participation/v1/channels/participation-trophy",
   206  				Status:            "active",
   207  				ConsensusRelation: "consenter",
   208  				Height:            5,
   209  			})
   210  
   211  			By("joining orderer1 to another channel as a consenter")
   212  			genesisBlockAPT := applicationChannelGenesisBlock(network, []*nwo.Orderer{orderer1}, []*nwo.Peer{peer}, "another-participation-trophy")
   213  			expectedChannelInfoAPT := channelparticipation.ChannelInfo{
   214  				Name:              "another-participation-trophy",
   215  				URL:               "/participation/v1/channels/another-participation-trophy",
   216  				Status:            "active",
   217  				ConsensusRelation: "consenter",
   218  				Height:            1,
   219  			}
   220  			channelparticipation.Join(network, orderer1, "another-participation-trophy", genesisBlockAPT, expectedChannelInfoAPT)
   221  			channelInfo := channelparticipation.ListOne(network, orderer1, "another-participation-trophy")
   222  			Expect(channelInfo).To(Equal(expectedChannelInfoAPT))
   223  
   224  			By("listing all channels for orderer1")
   225  			cl := channelparticipation.List(network, orderer1)
   226  			channelparticipation.ChannelListMatcher(cl, []string{"participation-trophy", "another-participation-trophy"})
   227  
   228  			By("removing orderer1 from the consenter set")
   229  			channelConfig = nwo.GetConfig(network, peer, orderer2, "participation-trophy")
   230  			c = configtx.New(channelConfig)
   231  			err = c.Orderer().RemoveConsenter(consenterChannelConfig(network, orderer1))
   232  			Expect(err).NotTo(HaveOccurred())
   233  			computeSignSubmitConfigUpdate(network, orderer2, peer, c, "participation-trophy")
   234  
   235  			By("ensuring orderer1 transitions to a follower")
   236  			Eventually(func() channelparticipation.ChannelInfo {
   237  				return channelparticipation.ListOne(network, orderer1, "participation-trophy")
   238  			}, network.EventuallyTimeout).Should(Equal(channelparticipation.ChannelInfo{
   239  				Name:              "participation-trophy",
   240  				URL:               "/participation/v1/channels/participation-trophy",
   241  				Status:            "active",
   242  				ConsensusRelation: "follower",
   243  				Height:            6,
   244  			}))
   245  
   246  			submitPeerTxn(orderer2, peer, network, channelparticipation.ChannelInfo{
   247  				Name:              "participation-trophy",
   248  				URL:               "/participation/v1/channels/participation-trophy",
   249  				Status:            "active",
   250  				ConsensusRelation: "consenter",
   251  				Height:            7,
   252  			})
   253  
   254  			By("ensuring orderer1 pulls the latest block as a follower")
   255  			Eventually(func() channelparticipation.ChannelInfo {
   256  				return channelparticipation.ListOne(network, orderer1, "participation-trophy")
   257  			}, network.EventuallyTimeout).Should(Equal(channelparticipation.ChannelInfo{
   258  				Name:              "participation-trophy",
   259  				URL:               "/participation/v1/channels/participation-trophy",
   260  				Status:            "active",
   261  				ConsensusRelation: "follower",
   262  				Height:            7,
   263  			}))
   264  
   265  			By("removing orderer1 from a channel")
   266  			channelparticipation.Remove(network, orderer1, "participation-trophy")
   267  			Eventually(func() channelparticipation.ChannelList {
   268  				return channelparticipation.List(network, orderer1)
   269  			}, network.EventuallyTimeout).Should(Equal(channelparticipation.ChannelList{
   270  				SystemChannel: nil,
   271  				Channels: []channelparticipation.ChannelInfoShort{
   272  					{
   273  						Name: "another-participation-trophy",
   274  						URL:  "/participation/v1/channels/another-participation-trophy",
   275  					},
   276  				},
   277  			}))
   278  
   279  			By("submitting transaction to orderer1")
   280  			env := CreateBroadcastEnvelope(network, peer, "participation-trophy", []byte("hello"))
   281  			resp, err := ordererclient.Broadcast(network, orderer1, env)
   282  			Expect(err).NotTo(HaveOccurred())
   283  			Expect(resp.Status).To(Equal(common.Status_BAD_REQUEST))
   284  
   285  			By("listing all channels for orderer1")
   286  			cl = channelparticipation.List(network, orderer1)
   287  			channelparticipation.ChannelListMatcher(cl, []string{"another-participation-trophy"})
   288  
   289  			By("joining orderer1 to channel it was previously removed from as consenter")
   290  			configBlockPT = nwo.GetConfigBlock(network, peer, orderer2, "participation-trophy")
   291  			expectedChannelInfoPTFollower = channelparticipation.ChannelInfo{
   292  				Name:              "participation-trophy",
   293  				URL:               "/participation/v1/channels/participation-trophy",
   294  				Status:            "onboarding",
   295  				ConsensusRelation: "follower",
   296  				Height:            0,
   297  			}
   298  			channelparticipation.Join(network, orderer1, "participation-trophy", configBlockPT, expectedChannelInfoPTFollower)
   299  
   300  			By("ensuring orderer1 completes onboarding successfully")
   301  			expectedChannelInfoPTFollower.Status = "active"
   302  			expectedChannelInfoPTFollower.Height = 7
   303  			Eventually(func() channelparticipation.ChannelInfo {
   304  				return channelparticipation.ListOne(network, orderer1, "participation-trophy")
   305  			}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPTFollower))
   306  
   307  			By("adding orderer1 to the consenters set")
   308  			channelConfig = nwo.GetConfig(network, peer, orderer3, "participation-trophy")
   309  			c = configtx.New(channelConfig)
   310  			err = c.Orderer().AddConsenter(consenterChannelConfig(network, orderer1))
   311  			Expect(err).NotTo(HaveOccurred())
   312  			computeSignSubmitConfigUpdate(network, orderer3, peer, c, "participation-trophy")
   313  
   314  			By("ensuring orderer1 transitions from follower to consenter")
   315  			expectedChannelInfoPT = channelparticipation.ChannelInfo{
   316  				Name:              "participation-trophy",
   317  				URL:               "/participation/v1/channels/participation-trophy",
   318  				Status:            "active",
   319  				ConsensusRelation: "consenter",
   320  				Height:            8,
   321  			}
   322  			Eventually(func() channelparticipation.ChannelInfo {
   323  				return channelparticipation.ListOne(network, orderer1, "participation-trophy")
   324  			}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPT))
   325  
   326  			submitPeerTxn(orderer1, peer, network, channelparticipation.ChannelInfo{
   327  				Name:              "participation-trophy",
   328  				URL:               "/participation/v1/channels/participation-trophy",
   329  				Status:            "active",
   330  				ConsensusRelation: "consenter",
   331  				Height:            9,
   332  			})
   333  
   334  			By("ensuring the channel is still usable by submitting a transaction to each remaining consenter for the channel")
   335  			submitPeerTxn(orderer2, peer, network, channelparticipation.ChannelInfo{
   336  				Name:              "participation-trophy",
   337  				URL:               "/participation/v1/channels/participation-trophy",
   338  				Status:            "active",
   339  				ConsensusRelation: "consenter",
   340  				Height:            10,
   341  			})
   342  
   343  			submitPeerTxn(orderer3, peer, network, channelparticipation.ChannelInfo{
   344  				Name:              "participation-trophy",
   345  				URL:               "/participation/v1/channels/participation-trophy",
   346  				Status:            "active",
   347  				ConsensusRelation: "consenter",
   348  				Height:            11,
   349  			})
   350  
   351  			By("attempting to join with an invalid block")
   352  			channelparticipationJoinFailure(network, orderer3, "nice-try", &common.Block{}, http.StatusBadRequest, "invalid join block: block is not a config block")
   353  
   354  			By("attempting to join a channel that already exists")
   355  			channelparticipationJoinFailure(network, orderer3, "participation-trophy", genesisBlock, http.StatusMethodNotAllowed, "cannot join: channel already exists")
   356  
   357  			By("attempting to join system channel when app channels already exist")
   358  			systemChannelBlockBytes, err := ioutil.ReadFile(network.OutputBlockPath(network.SystemChannel.Name))
   359  			Expect(err).NotTo(HaveOccurred())
   360  			systemChannelBlock := &common.Block{}
   361  			err = proto.Unmarshal(systemChannelBlockBytes, systemChannelBlock)
   362  			Expect(err).NotTo(HaveOccurred())
   363  			channelparticipationJoinFailure(network, orderer3, "systemchannel", systemChannelBlock, http.StatusForbidden, "cannot join: application channels already exist")
   364  		})
   365  
   366  		It("joins application channels with join-block as consenter via channel participation api", func() {
   367  			orderer1 := network.Orderer("orderer1")
   368  			orderer2 := network.Orderer("orderer2")
   369  			orderer3 := network.Orderer("orderer3")
   370  			orderers := []*nwo.Orderer{orderer1, orderer2}
   371  			peer := network.Peer("Org1", "peer0")
   372  
   373  			By("starting two orderers")
   374  			for _, o := range orderers {
   375  				startOrderer(o)
   376  				cl := channelparticipation.List(network, o)
   377  				Expect(cl).To(Equal(channelparticipation.ChannelList{}))
   378  			}
   379  
   380  			genesisBlock := applicationChannelGenesisBlock(network, orderers, []*nwo.Peer{peer}, "participation-trophy")
   381  			expectedChannelInfoPT := channelparticipation.ChannelInfo{
   382  				Name:              "participation-trophy",
   383  				URL:               "/participation/v1/channels/participation-trophy",
   384  				Status:            "active",
   385  				ConsensusRelation: "consenter",
   386  				Height:            1,
   387  			}
   388  
   389  			for _, o := range orderers {
   390  				By("joining " + o.Name + " to channel as a consenter")
   391  				channelparticipation.Join(network, o, "participation-trophy", genesisBlock, expectedChannelInfoPT)
   392  				channelInfo := channelparticipation.ListOne(network, o, "participation-trophy")
   393  				Expect(channelInfo).To(Equal(expectedChannelInfoPT))
   394  			}
   395  
   396  			submitPeerTxn(orderer1, peer, network, channelparticipation.ChannelInfo{
   397  				Name:              "participation-trophy",
   398  				URL:               "/participation/v1/channels/participation-trophy",
   399  				Status:            "active",
   400  				ConsensusRelation: "consenter",
   401  				Height:            2,
   402  			})
   403  
   404  			submitPeerTxn(orderer2, peer, network, channelparticipation.ChannelInfo{
   405  				Name:              "participation-trophy",
   406  				URL:               "/participation/v1/channels/participation-trophy",
   407  				Status:            "active",
   408  				ConsensusRelation: "consenter",
   409  				Height:            3,
   410  			})
   411  
   412  			By("submitting a channel config update")
   413  			channelConfig := nwo.GetConfig(network, peer, orderer1, "participation-trophy")
   414  			c := configtx.New(channelConfig)
   415  			err := c.Orderer().AddCapability("V1_1")
   416  			Expect(err).NotTo(HaveOccurred())
   417  			computeSignSubmitConfigUpdate(network, orderer1, peer, c, "participation-trophy")
   418  
   419  			currentBlockNumber := nwo.CurrentConfigBlockNumber(network, peer, orderer1, "participation-trophy")
   420  			Expect(currentBlockNumber).To(BeNumerically(">", 1))
   421  
   422  			By("starting third orderer")
   423  			startOrderer(orderer3)
   424  			cl := channelparticipation.List(network, orderer3)
   425  			Expect(cl).To(Equal(channelparticipation.ChannelList{}))
   426  
   427  			By("adding orderer3 to the consenters set")
   428  			channelConfig = nwo.GetConfig(network, peer, orderer2, "participation-trophy")
   429  			c = configtx.New(channelConfig)
   430  			err = c.Orderer().AddConsenter(consenterChannelConfig(network, orderer3))
   431  			Expect(err).NotTo(HaveOccurred())
   432  			computeSignSubmitConfigUpdate(network, orderer2, peer, c, "participation-trophy")
   433  
   434  			By("joining orderer3 to the channel as a consenter")
   435  			// make sure we can join using a config block from one of the other orderers
   436  			configBlockPT := nwo.GetConfigBlock(network, peer, orderer2, "participation-trophy")
   437  			expectedChannelInfoConsenter := channelparticipation.ChannelInfo{
   438  				Name:              "participation-trophy",
   439  				URL:               "/participation/v1/channels/participation-trophy",
   440  				Status:            "onboarding",
   441  				ConsensusRelation: "consenter",
   442  				Height:            0,
   443  			}
   444  			channelparticipation.Join(network, orderer3, "participation-trophy", configBlockPT, expectedChannelInfoConsenter)
   445  
   446  			By("ensuring orderer3 completes onboarding successfully")
   447  			expectedChannelInfoConsenter.Status = "active"
   448  			expectedChannelInfoConsenter.Height = 5
   449  			Eventually(func() channelparticipation.ChannelInfo {
   450  				return channelparticipation.ListOne(network, orderer3, "participation-trophy")
   451  			}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoConsenter))
   452  
   453  			submitPeerTxn(orderer3, peer, network, channelparticipation.ChannelInfo{
   454  				Name:              "participation-trophy",
   455  				URL:               "/participation/v1/channels/participation-trophy",
   456  				Status:            "active",
   457  				ConsensusRelation: "consenter",
   458  				Height:            6,
   459  			})
   460  		})
   461  
   462  		Context("joining application channels with join-block as follower via channel participation api", func() {
   463  			var (
   464  				orderer1, orderer2, orderer3 *nwo.Orderer
   465  				orderers                     []*nwo.Orderer
   466  				peer                         *nwo.Peer
   467  				genesisBlock, configBlock    *common.Block
   468  			)
   469  
   470  			BeforeEach(func() {
   471  				orderer1 = network.Orderer("orderer1")
   472  				orderer2 = network.Orderer("orderer2")
   473  				orderer3 = network.Orderer("orderer3")
   474  				orderers = []*nwo.Orderer{orderer1, orderer2}
   475  				peer = network.Peer("Org1", "peer0")
   476  
   477  				By("starting two orderers")
   478  				for _, o := range orderers {
   479  					startOrderer(o)
   480  					cl := channelparticipation.List(network, o)
   481  					Expect(cl).To(Equal(channelparticipation.ChannelList{}))
   482  				}
   483  
   484  				genesisBlock = applicationChannelGenesisBlock(network, orderers, []*nwo.Peer{peer}, "participation-trophy")
   485  				expectedChannelInfoPT := channelparticipation.ChannelInfo{
   486  					Name:              "participation-trophy",
   487  					URL:               "/participation/v1/channels/participation-trophy",
   488  					Status:            "active",
   489  					ConsensusRelation: "consenter",
   490  					Height:            1,
   491  				}
   492  
   493  				By("joining orderer1 and orderer2 to the channel with a genesis block")
   494  				for _, o := range orderers {
   495  					By("joining " + o.Name + " to channel as a consenter")
   496  					channelparticipation.Join(network, o, "participation-trophy", genesisBlock, expectedChannelInfoPT)
   497  					channelInfo := channelparticipation.ListOne(network, o, "participation-trophy")
   498  					Expect(channelInfo).To(Equal(expectedChannelInfoPT))
   499  				}
   500  
   501  				submitPeerTxn(orderer1, peer, network, channelparticipation.ChannelInfo{
   502  					Name:              "participation-trophy",
   503  					URL:               "/participation/v1/channels/participation-trophy",
   504  					Status:            "active",
   505  					ConsensusRelation: "consenter",
   506  					Height:            2,
   507  				})
   508  
   509  				submitPeerTxn(orderer2, peer, network, channelparticipation.ChannelInfo{
   510  					Name:              "participation-trophy",
   511  					URL:               "/participation/v1/channels/participation-trophy",
   512  					Status:            "active",
   513  					ConsensusRelation: "consenter",
   514  					Height:            3,
   515  				})
   516  
   517  				By("submitting a channel config update")
   518  				channelConfig := nwo.GetConfig(network, peer, orderer1, "participation-trophy")
   519  				c := configtx.New(channelConfig)
   520  				err := c.Orderer().AddCapability("V1_1")
   521  				Expect(err).NotTo(HaveOccurred())
   522  				computeSignSubmitConfigUpdate(network, orderer1, peer, c, "participation-trophy")
   523  
   524  				currentBlockNumber := nwo.CurrentConfigBlockNumber(network, peer, orderer1, "participation-trophy")
   525  				Expect(currentBlockNumber).To(BeNumerically(">", 1))
   526  
   527  				By("getting the updated config block")
   528  				configBlock = nwo.GetConfigBlock(network, peer, orderer2, "participation-trophy")
   529  			})
   530  
   531  			It("joins the channel as a follower using a config block", func() {
   532  				By("starting third orderer")
   533  				startOrderer(orderer3)
   534  				cl := channelparticipation.List(network, orderer3)
   535  				Expect(cl).To(Equal(channelparticipation.ChannelList{}))
   536  
   537  				By("joining orderer3 to the channel as a follower")
   538  				// make sure we can join using a config block from one of the other orderers
   539  				expectedChannelInfoPTFollower := channelparticipation.ChannelInfo{
   540  					Name:              "participation-trophy",
   541  					URL:               "/participation/v1/channels/participation-trophy",
   542  					Status:            "onboarding",
   543  					ConsensusRelation: "follower",
   544  					Height:            0,
   545  				}
   546  				channelparticipation.Join(network, orderer3, "participation-trophy", configBlock, expectedChannelInfoPTFollower)
   547  
   548  				By("ensuring orderer3 completes onboarding successfully")
   549  				expectedChannelInfoPTFollower.Status = "active"
   550  				expectedChannelInfoPTFollower.Height = 4
   551  				Eventually(func() channelparticipation.ChannelInfo {
   552  					return channelparticipation.ListOne(network, orderer3, "participation-trophy")
   553  				}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPTFollower))
   554  
   555  				By("adding orderer3 to the consenters set")
   556  				channelConfig := nwo.GetConfig(network, peer, orderer1, "participation-trophy")
   557  				c := configtx.New(channelConfig)
   558  				err := c.Orderer().AddConsenter(consenterChannelConfig(network, orderer3))
   559  				Expect(err).NotTo(HaveOccurred())
   560  				computeSignSubmitConfigUpdate(network, orderer1, peer, c, "participation-trophy")
   561  
   562  				By("ensuring orderer3 transitions from follower to consenter")
   563  				// config update above added a block
   564  				expectedChannelInfoPT := channelparticipation.ChannelInfo{
   565  					Name:              "participation-trophy",
   566  					URL:               "/participation/v1/channels/participation-trophy",
   567  					Status:            "active",
   568  					ConsensusRelation: "consenter",
   569  					Height:            5,
   570  				}
   571  				Eventually(func() channelparticipation.ChannelInfo {
   572  					return channelparticipation.ListOne(network, orderer3, "participation-trophy")
   573  				}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPT))
   574  
   575  				submitPeerTxn(orderer3, peer, network, channelparticipation.ChannelInfo{
   576  					Name:              "participation-trophy",
   577  					URL:               "/participation/v1/channels/participation-trophy",
   578  					Status:            "active",
   579  					ConsensusRelation: "consenter",
   580  					Height:            6,
   581  				})
   582  			})
   583  
   584  			It("recovers from a crash after the join block is written to the pendingops file repo", func() {
   585  				By("simulating the filesystem state at crash")
   586  				joinBlockFileRepoPath := filepath.Join(network.OrdererDir(orderer3), "system", "pendingops", "join")
   587  				err := os.MkdirAll(joinBlockFileRepoPath, 0o755)
   588  				Expect(err).NotTo(HaveOccurred())
   589  				blockPath := filepath.Join(joinBlockFileRepoPath, "participation-trophy.join")
   590  				configBlockBytes, err := proto.Marshal(configBlock)
   591  				Expect(err).NotTo(HaveOccurred())
   592  				err = ioutil.WriteFile(blockPath, configBlockBytes, 0o600)
   593  				Expect(err).NotTo(HaveOccurred())
   594  
   595  				By("starting third orderer")
   596  				startOrderer(orderer3)
   597  
   598  				By("ensuring orderer3 completes onboarding successfully")
   599  				expectedChannelInfoPTFollower := channelparticipation.ChannelInfo{
   600  					Name:              "participation-trophy",
   601  					URL:               "/participation/v1/channels/participation-trophy",
   602  					Status:            "active",
   603  					ConsensusRelation: "follower",
   604  					Height:            4,
   605  				}
   606  				Eventually(func() channelparticipation.ChannelInfo {
   607  					return channelparticipation.ListOne(network, orderer3, "participation-trophy")
   608  				}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPTFollower))
   609  			})
   610  
   611  			It("recovers from a crash after the join block is written to the pendingops file repo and the ledger directory (but not the ledger) has been created", func() {
   612  				By("simulating the filesystem state at crash")
   613  				joinBlockFileRepoPath := filepath.Join(network.OrdererDir(orderer3), "system", "pendingops", "join")
   614  				err := os.MkdirAll(joinBlockFileRepoPath, 0o755)
   615  				Expect(err).NotTo(HaveOccurred())
   616  				blockPath := filepath.Join(joinBlockFileRepoPath, "participation-trophy.join")
   617  				configBlockBytes, err := proto.Marshal(configBlock)
   618  				Expect(err).NotTo(HaveOccurred())
   619  				err = ioutil.WriteFile(blockPath, configBlockBytes, 0o600)
   620  				Expect(err).NotTo(HaveOccurred())
   621  
   622  				// create the ledger directory
   623  				ledgerPath := filepath.Join(network.OrdererDir(orderer3), "system", "chains", "participation-trophy")
   624  				err = os.MkdirAll(ledgerPath, 0o755)
   625  				Expect(err).NotTo(HaveOccurred())
   626  
   627  				By("starting third orderer")
   628  				startOrderer(orderer3)
   629  
   630  				By("ensuring orderer3 completes onboarding successfully")
   631  				expectedChannelInfoPTFollower := channelparticipation.ChannelInfo{
   632  					Name:              "participation-trophy",
   633  					URL:               "/participation/v1/channels/participation-trophy",
   634  					Status:            "active",
   635  					ConsensusRelation: "follower",
   636  					Height:            4,
   637  				}
   638  				Eventually(func() channelparticipation.ChannelInfo {
   639  					return channelparticipation.ListOne(network, orderer3, "participation-trophy")
   640  				}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPTFollower))
   641  			})
   642  
   643  			It("recovers from a crash after the join block is written to the pendingops file repo and the ledger has been created", func() {
   644  				By("simulating the filesystem state at crash")
   645  				joinBlockFileRepoPath := filepath.Join(network.OrdererDir(orderer3), "system", "pendingops", "join")
   646  				err := os.MkdirAll(joinBlockFileRepoPath, 0o755)
   647  				Expect(err).NotTo(HaveOccurred())
   648  				blockPath := filepath.Join(joinBlockFileRepoPath, "participation-trophy.join")
   649  				configBlockBytes, err := proto.Marshal(configBlock)
   650  				Expect(err).NotTo(HaveOccurred())
   651  				err = ioutil.WriteFile(blockPath, configBlockBytes, 0o600)
   652  				Expect(err).NotTo(HaveOccurred())
   653  
   654  				// create the ledger and add the genesis block
   655  				ledgerDir := filepath.Join(network.OrdererDir(orderer3), "system")
   656  				lf, err := fileledger.New(ledgerDir, &disabled.Provider{})
   657  				Expect(err).NotTo(HaveOccurred())
   658  				ledger, err := lf.GetOrCreate("participation-trophy")
   659  				Expect(err).NotTo(HaveOccurred())
   660  				err = ledger.Append(genesisBlock)
   661  				Expect(err).NotTo(HaveOccurred())
   662  				lf.Close()
   663  
   664  				By("starting third orderer")
   665  				startOrderer(orderer3)
   666  
   667  				By("ensuring orderer3 completes onboarding successfully")
   668  				expectedChannelInfoPTFollower := channelparticipation.ChannelInfo{
   669  					Name:              "participation-trophy",
   670  					URL:               "/participation/v1/channels/participation-trophy",
   671  					Status:            "active",
   672  					ConsensusRelation: "follower",
   673  					Height:            4,
   674  				}
   675  				Eventually(func() channelparticipation.ChannelInfo {
   676  					return channelparticipation.ListOne(network, orderer3, "participation-trophy")
   677  				}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPTFollower))
   678  
   679  				By("killing orderer3")
   680  				ordererProcesses[2].Signal(syscall.SIGKILL)
   681  				Eventually(ordererProcesses[2].Wait(), network.EventuallyTimeout).Should(Receive(MatchError("exit status 137")))
   682  
   683  				By("submitting transactions while orderer3 is down")
   684  				submitPeerTxn(orderer1, peer, network, channelparticipation.ChannelInfo{
   685  					Name:              "participation-trophy",
   686  					URL:               "/participation/v1/channels/participation-trophy",
   687  					Status:            "active",
   688  					ConsensusRelation: "consenter",
   689  					Height:            5,
   690  				})
   691  
   692  				submitPeerTxn(orderer2, peer, network, channelparticipation.ChannelInfo{
   693  					Name:              "participation-trophy",
   694  					URL:               "/participation/v1/channels/participation-trophy",
   695  					Status:            "active",
   696  					ConsensusRelation: "consenter",
   697  					Height:            6,
   698  				})
   699  
   700  				By("restarting orderer3 (follower) and ensuring it catches up to the blocks it missed")
   701  				ordererRunner := network.OrdererRunner(orderer3)
   702  				ordererProcess := ifrit.Invoke(ordererRunner)
   703  				Eventually(ordererProcess.Ready(), network.EventuallyTimeout).Should(BeClosed())
   704  				ordererProcesses[2] = ordererProcess
   705  				ordererRunners[2] = ordererRunner
   706  				expectedChannelInfoPTFollower = channelparticipation.ChannelInfo{
   707  					Name:              "participation-trophy",
   708  					URL:               "/participation/v1/channels/participation-trophy",
   709  					Status:            "active",
   710  					ConsensusRelation: "follower",
   711  					Height:            6,
   712  				}
   713  				Eventually(func() channelparticipation.ChannelInfo {
   714  					return channelparticipation.ListOne(network, orderer3, "participation-trophy")
   715  				}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPTFollower))
   716  			})
   717  		})
   718  
   719  		It("creates the system channel on two orderers with a genesis block and joins a third using a config block", func() {
   720  			orderer1 := network.Orderer("orderer1")
   721  			orderer2 := network.Orderer("orderer2")
   722  			orderer3 := network.Orderer("orderer3")
   723  			orderers1and2 := []*nwo.Orderer{orderer1, orderer2}
   724  			orderers := []*nwo.Orderer{orderer1, orderer2, orderer3}
   725  			org1peer0 := network.Peer("Org1", "peer0")
   726  			org2peer0 := network.Peer("Org2", "peer0")
   727  			peers := []*nwo.Peer{org1peer0, org2peer0}
   728  
   729  			for _, o := range orderers {
   730  				startOrderer(o)
   731  			}
   732  
   733  			systemChannelGenesisBlock := systemChannelGenesisBlock(network, orderers1and2, peers, network.SystemChannel.Name)
   734  
   735  			expectedChannelInfo := channelparticipation.ChannelInfo{
   736  				Name:              "systemchannel",
   737  				URL:               "/participation/v1/channels/systemchannel",
   738  				Status:            "inactive",
   739  				ConsensusRelation: "consenter",
   740  				Height:            1,
   741  			}
   742  
   743  			By("joining orderers to systemchannel")
   744  			for _, o := range orderers1and2 {
   745  				channelparticipation.Join(network, o, "systemchannel", systemChannelGenesisBlock, expectedChannelInfo)
   746  			}
   747  
   748  			By("attempting to join a channel when system channel is present")
   749  			channelparticipationJoinFailure(network, orderer1, "systemchannel", systemChannelGenesisBlock, http.StatusMethodNotAllowed, "cannot join: system channel exists")
   750  
   751  			By("ensuring the system channel is unusable before restarting by attempting to submit a transaction")
   752  			for _, o := range orderers1and2 {
   753  				By("submitting transaction to " + o.Name)
   754  				env := CreateBroadcastEnvelope(network, org1peer0, "systemchannel", []byte("hello"))
   755  				Expect(broadcastTransactionFunc(network, o, env)()).To(Equal(common.Status_FORBIDDEN))
   756  			}
   757  
   758  			By("restarting all orderers in the system channel")
   759  			for i, o := range orderers1and2 {
   760  				restartOrderer(o, i)
   761  			}
   762  
   763  			By("creating a channel that will have only two consenters")
   764  			network.CreateChannel("testchannel", orderer1, org1peer0)
   765  
   766  			expectedChannelInfo = channelparticipation.ChannelInfo{
   767  				Name:              "testchannel",
   768  				URL:               "/participation/v1/channels/testchannel",
   769  				Status:            "active",
   770  				ConsensusRelation: "consenter",
   771  				Height:            1,
   772  			}
   773  			for _, o := range orderers1and2 {
   774  				By("listing single channel for " + o.Name)
   775  				Eventually(func() channelparticipation.ChannelInfo {
   776  					return channelparticipation.ListOne(network, o, "testchannel")
   777  				}, network.EventuallyTimeout).Should(Equal(expectedChannelInfo))
   778  			}
   779  
   780  			for _, o := range orderers1and2 {
   781  				By("listing the channels for " + o.Name)
   782  				cl := channelparticipation.List(network, o)
   783  				channelparticipation.ChannelListMatcher(cl, []string{"testchannel"}, "systemchannel")
   784  			}
   785  
   786  			expectedChannelInfo = channelparticipation.ChannelInfo{
   787  				Name:              "systemchannel",
   788  				URL:               "/participation/v1/channels/systemchannel",
   789  				Status:            "active",
   790  				ConsensusRelation: "consenter",
   791  				Height:            2,
   792  			}
   793  			for _, o := range orderers1and2 {
   794  				By("listing single channel for " + o.Name)
   795  				Eventually(func() channelparticipation.ChannelInfo {
   796  					return channelparticipation.ListOne(network, o, "systemchannel")
   797  				}, network.EventuallyTimeout).Should(Equal(expectedChannelInfo))
   798  			}
   799  
   800  			By("submitting transaction to each active orderer to confirm channel is usable")
   801  			submitPeerTxn(orderer1, org1peer0, network, channelparticipation.ChannelInfo{
   802  				Name:              "testchannel",
   803  				URL:               "/participation/v1/channels/testchannel",
   804  				Status:            "active",
   805  				ConsensusRelation: "consenter",
   806  				Height:            2,
   807  			})
   808  
   809  			submitPeerTxn(orderer2, org1peer0, network, channelparticipation.ChannelInfo{
   810  				Name:              "testchannel",
   811  				URL:               "/participation/v1/channels/testchannel",
   812  				Status:            "active",
   813  				ConsensusRelation: "consenter",
   814  				Height:            3,
   815  			})
   816  
   817  			By("creating a second channel that will have three consenters")
   818  			network.CreateChannel("testchannel2", orderer1, org1peer0)
   819  
   820  			expectedChannelInfo = channelparticipation.ChannelInfo{
   821  				Name:              "testchannel2",
   822  				URL:               "/participation/v1/channels/testchannel2",
   823  				Status:            "active",
   824  				ConsensusRelation: "consenter",
   825  				Height:            1,
   826  			}
   827  			for _, o := range orderers1and2 {
   828  				By("listing single channel for " + o.Name)
   829  				Eventually(func() channelparticipation.ChannelInfo {
   830  					return channelparticipation.ListOne(network, o, "testchannel2")
   831  				}, network.EventuallyTimeout).Should(Equal(expectedChannelInfo))
   832  			}
   833  
   834  			for _, o := range orderers1and2 {
   835  				By("listing the channels for " + o.Name)
   836  				cl := channelparticipation.List(network, o)
   837  				channelparticipation.ChannelListMatcher(cl, []string{"testchannel", "testchannel2"}, "systemchannel")
   838  			}
   839  
   840  			expectedChannelInfo = channelparticipation.ChannelInfo{
   841  				Name:              "systemchannel",
   842  				URL:               "/participation/v1/channels/systemchannel",
   843  				Status:            "active",
   844  				ConsensusRelation: "consenter",
   845  				Height:            3,
   846  			}
   847  			for _, o := range orderers1and2 {
   848  				By("listing single channel for " + o.Name)
   849  				Eventually(func() channelparticipation.ChannelInfo {
   850  					return channelparticipation.ListOne(network, o, "systemchannel")
   851  				}, network.EventuallyTimeout).Should(Equal(expectedChannelInfo))
   852  			}
   853  
   854  			By("submitting transaction to each active orderer to confirm channel is usable")
   855  			submitPeerTxn(orderer1, org1peer0, network, channelparticipation.ChannelInfo{
   856  				Name:              "testchannel2",
   857  				URL:               "/participation/v1/channels/testchannel2",
   858  				Status:            "active",
   859  				ConsensusRelation: "consenter",
   860  				Height:            2,
   861  			})
   862  
   863  			submitPeerTxn(orderer2, org1peer0, network, channelparticipation.ChannelInfo{
   864  				Name:              "testchannel2",
   865  				URL:               "/participation/v1/channels/testchannel2",
   866  				Status:            "active",
   867  				ConsensusRelation: "consenter",
   868  				Height:            3,
   869  			})
   870  
   871  			By("submitting a channel config update for the system channel, adding orderer3 to consenters set")
   872  			channelConfig := nwo.GetConfig(network, org1peer0, orderer1, "systemchannel")
   873  			c := configtx.New(channelConfig)
   874  			err := c.Orderer().AddConsenter(consenterChannelConfig(network, orderer3))
   875  			Expect(err).NotTo(HaveOccurred())
   876  			computeSignSubmitConfigUpdate(network, orderer1, org1peer0, c, "systemchannel")
   877  			currentBlockNumber := nwo.CurrentConfigBlockNumber(network, org1peer0, orderer1, "systemchannel")
   878  			Expect(currentBlockNumber).To(BeNumerically(">", 1))
   879  
   880  			expectedChannelInfo = channelparticipation.ChannelInfo{
   881  				Name:              "systemchannel",
   882  				URL:               "/participation/v1/channels/systemchannel",
   883  				Status:            "active",
   884  				ConsensusRelation: "consenter",
   885  				Height:            4,
   886  			}
   887  			for _, o := range orderers1and2 {
   888  				By("listing single channel for " + o.Name)
   889  				Eventually(func() channelparticipation.ChannelInfo {
   890  					return channelparticipation.ListOne(network, o, "systemchannel")
   891  				}, network.EventuallyTimeout).Should(Equal(expectedChannelInfo))
   892  			}
   893  
   894  			By("submitting a channel config update for testchannel2, adding orderer3 to consenters set")
   895  			channelConfig = nwo.GetConfig(network, org1peer0, orderer1, "testchannel2")
   896  			c = configtx.New(channelConfig)
   897  			err = c.Orderer().AddConsenter(consenterChannelConfig(network, orderer3))
   898  			Expect(err).NotTo(HaveOccurred())
   899  			computeSignSubmitConfigUpdate(network, orderer1, org1peer0, c, "testchannel2")
   900  			currentBlockNumber = nwo.CurrentConfigBlockNumber(network, org1peer0, orderer1, "testchannel2")
   901  			Expect(currentBlockNumber).To(BeNumerically(">", 2))
   902  
   903  			expectedChannelInfo = channelparticipation.ChannelInfo{
   904  				Name:              "testchannel2",
   905  				URL:               "/participation/v1/channels/testchannel2",
   906  				Status:            "active",
   907  				ConsensusRelation: "consenter",
   908  				Height:            4,
   909  			}
   910  			for _, o := range orderers1and2 {
   911  				By("listing single channel for " + o.Name)
   912  				Eventually(func() channelparticipation.ChannelInfo {
   913  					return channelparticipation.ListOne(network, o, "testchannel2")
   914  				}, network.EventuallyTimeout).Should(Equal(expectedChannelInfo))
   915  			}
   916  
   917  			By("joining orderer3 to the system channel")
   918  			// make sure we can join using a config block from one of the other orderers
   919  
   920  			configBlockSC := nwo.GetConfigBlock(network, org1peer0, orderer2, "systemchannel")
   921  			Expect(configBlockSC.Header.Number).To(Equal(uint64(3)))
   922  
   923  			expectedChannelInfo = channelparticipation.ChannelInfo{
   924  				Name:              "systemchannel",
   925  				URL:               "/participation/v1/channels/systemchannel",
   926  				Status:            "inactive",
   927  				ConsensusRelation: "consenter",
   928  				Height:            0,
   929  			}
   930  			channelparticipation.Join(network, orderer3, "systemchannel", configBlockSC, expectedChannelInfo)
   931  
   932  			By("restarting orderer3")
   933  			restartOrderer(orderer3, 2)
   934  
   935  			By("listing the channels for orderer3")
   936  			cl := channelparticipation.List(network, orderer3)
   937  			channelparticipation.ChannelListMatcher(cl, []string{"testchannel", "testchannel2"}, "systemchannel")
   938  
   939  			By("ensuring orderer3 catches up to the latest height as an active consenter")
   940  			expectedChannelInfo.Status = "active"
   941  			expectedChannelInfo.Height = 4
   942  			Eventually(func() channelparticipation.ChannelInfo {
   943  				return channelparticipation.ListOne(network, orderer3, "systemchannel")
   944  			}, network.EventuallyTimeout).Should(Equal(expectedChannelInfo))
   945  
   946  			By("submitting a channel config update to add orderer3 to the endpoints")
   947  			channelConfig = nwo.GetConfig(network, org1peer0, orderer1, "systemchannel")
   948  			c = configtx.New(channelConfig)
   949  			host, port := conftx.OrdererHostPort(network, orderer3)
   950  			err = c.Orderer().Organization(orderer3.Organization).SetEndpoint(
   951  				configtx.Address{
   952  					Host: host,
   953  					Port: port,
   954  				},
   955  			)
   956  			Expect(err).NotTo(HaveOccurred())
   957  			computeSignSubmitConfigUpdate(network, orderer2, org1peer0, c, "systemchannel")
   958  
   959  			By("ensuring all orderers are active consenters for the system channel")
   960  			expectedChannelInfo = channelparticipation.ChannelInfo{
   961  				Name:              "systemchannel",
   962  				URL:               "/participation/v1/channels/systemchannel",
   963  				Status:            "active",
   964  				ConsensusRelation: "consenter",
   965  				Height:            5,
   966  			}
   967  			for _, o := range orderers {
   968  				By("listing single channel for " + o.Name)
   969  				Eventually(func() channelparticipation.ChannelInfo {
   970  					return channelparticipation.ListOne(network, o, "systemchannel")
   971  				}, network.EventuallyTimeout).Should(Equal(expectedChannelInfo))
   972  			}
   973  
   974  			By("ensuring orderer3 becomes an active consenter for the testchannel2 application channel")
   975  			expectedChannelInfo = channelparticipation.ChannelInfo{
   976  				Name:              "testchannel2",
   977  				URL:               "/participation/v1/channels/testchannel2",
   978  				Status:            "active",
   979  				ConsensusRelation: "consenter",
   980  				Height:            4,
   981  			}
   982  			Eventually(func() channelparticipation.ChannelInfo {
   983  				return channelparticipation.ListOne(network, orderer3, "testchannel2")
   984  			}, network.EventuallyTimeout).Should(Equal(expectedChannelInfo))
   985  
   986  			By("submitting transactions to ensure the testchannel2 application channel is usable")
   987  			submitPeerTxn(orderer3, org1peer0, network, channelparticipation.ChannelInfo{
   988  				Name:              "testchannel2",
   989  				URL:               "/participation/v1/channels/testchannel2",
   990  				Status:            "active",
   991  				ConsensusRelation: "consenter",
   992  				Height:            5,
   993  			})
   994  
   995  			submitPeerTxn(orderer2, org1peer0, network, channelparticipation.ChannelInfo{
   996  				Name:              "testchannel2",
   997  				URL:               "/participation/v1/channels/testchannel2",
   998  				Status:            "active",
   999  				ConsensusRelation: "consenter",
  1000  				Height:            6,
  1001  			})
  1002  
  1003  			submitPeerTxn(orderer1, org1peer0, network, channelparticipation.ChannelInfo{
  1004  				Name:              "testchannel2",
  1005  				URL:               "/participation/v1/channels/testchannel2",
  1006  				Status:            "active",
  1007  				ConsensusRelation: "consenter",
  1008  				Height:            7,
  1009  			})
  1010  
  1011  			By("ensuring orderer3 becomes an inactive config-tracker for the testchannel application channel")
  1012  			expectedChannelInfo = channelparticipation.ChannelInfo{
  1013  				Name:              "testchannel",
  1014  				URL:               "/participation/v1/channels/testchannel",
  1015  				Status:            "inactive",
  1016  				ConsensusRelation: "config-tracker",
  1017  				Height:            1,
  1018  			}
  1019  			Eventually(func() channelparticipation.ChannelInfo {
  1020  				return channelparticipation.ListOne(network, orderer3, "testchannel")
  1021  			}, network.EventuallyTimeout).Should(Equal(expectedChannelInfo))
  1022  		})
  1023  
  1024  		It("requires a client certificate to connect when TLS is enabled", func() {
  1025  			orderer := network.Orderer("orderer1")
  1026  			_, unauthClient := nwo.OrdererOperationalClients(network, orderer)
  1027  			ordererAddress := fmt.Sprintf("127.0.0.1:%d", network.OrdererPort(orderer, nwo.AdminPort))
  1028  			listChannelsURL := fmt.Sprintf("https://%s/participation/v1/channels", ordererAddress)
  1029  
  1030  			_, err := unauthClient.Get(listChannelsURL)
  1031  			Expect(err).To(MatchError(fmt.Sprintf("Get \"%s\": dial tcp %s: connect: connection refused", listChannelsURL, ordererAddress)))
  1032  		})
  1033  	})
  1034  
  1035  	Describe("three node etcdraft network with a system channel", func() {
  1036  		startOrderer := func(o *nwo.Orderer) {
  1037  			ordererRunner := network.OrdererRunner(o)
  1038  			ordererProcess := ifrit.Invoke(ordererRunner)
  1039  			Eventually(ordererProcess.Ready(), network.EventuallyTimeout).Should(BeClosed())
  1040  			ordererProcesses = append(ordererProcesses, ordererProcess)
  1041  			ordererRunners = append(ordererRunners, ordererRunner)
  1042  		}
  1043  
  1044  		restartOrderer := func(o *nwo.Orderer, index int) {
  1045  			ordererProcesses[index].Signal(syscall.SIGKILL)
  1046  			Eventually(ordererProcesses[index].Wait(), network.EventuallyTimeout).Should(Receive(MatchError("exit status 137")))
  1047  			ordererRunner := network.OrdererRunner(o)
  1048  			ordererProcess := ifrit.Invoke(ordererRunner)
  1049  			Eventually(ordererProcess.Ready(), network.EventuallyTimeout).Should(BeClosed())
  1050  			ordererProcesses[index] = ordererProcess
  1051  			ordererRunners[index] = ordererRunner
  1052  		}
  1053  
  1054  		BeforeEach(func() {
  1055  			network = nwo.New(nwo.MultiNodeEtcdRaft(), testDir, client, StartPort(), components)
  1056  			network.GenerateConfigTree()
  1057  			network.Bootstrap()
  1058  		})
  1059  
  1060  		It("joins channels using the legacy channel creation mechanism and then removes the system channel to transition to the channel participation API", func() {
  1061  			orderer1 := network.Orderer("orderer1")
  1062  			orderer2 := network.Orderer("orderer2")
  1063  			orderer3 := network.Orderer("orderer3")
  1064  			orderers := []*nwo.Orderer{orderer1, orderer2, orderer3}
  1065  			peer := network.Peer("Org1", "peer0")
  1066  			for _, o := range orderers {
  1067  				startOrderer(o)
  1068  			}
  1069  
  1070  			By("creating an application channel using system channel")
  1071  			network.CreateChannel("testchannel", orderer1, peer)
  1072  
  1073  			By("broadcasting envelopes to each orderer")
  1074  			for _, o := range orderers {
  1075  				env := CreateBroadcastEnvelope(network, peer, "testchannel", []byte("hello"))
  1076  				Eventually(broadcastTransactionFunc(network, o, env), network.EventuallyTimeout).Should(Equal(common.Status_SUCCESS))
  1077  			}
  1078  
  1079  			By("enabling the channel participation API on each orderer")
  1080  			network.Consensus.ChannelParticipationEnabled = true
  1081  			network.Consensus.BootstrapMethod = "none"
  1082  			for i, o := range orderers {
  1083  				network.GenerateOrdererConfig(o)
  1084  				restartOrderer(o, i)
  1085  			}
  1086  
  1087  			By("listing the channels")
  1088  			expectedChannelInfo := channelparticipation.ChannelInfo{
  1089  				Name:              "testchannel",
  1090  				URL:               "/participation/v1/channels/testchannel",
  1091  				Status:            "active",
  1092  				ConsensusRelation: "consenter",
  1093  				Height:            4,
  1094  			}
  1095  			for _, o := range orderers {
  1096  				By("listing single channel")
  1097  				Eventually(func() channelparticipation.ChannelInfo {
  1098  					return channelparticipation.ListOne(network, o, "testchannel")
  1099  				}, network.EventuallyTimeout).Should(Equal(expectedChannelInfo))
  1100  				By("listing all channels")
  1101  				cl := channelparticipation.List(network, o)
  1102  				channelparticipation.ChannelListMatcher(cl, []string{"testchannel"}, "systemchannel")
  1103  			}
  1104  
  1105  			By("submitting a transaction to ensure the system channel is active after restart")
  1106  			submitOrdererTxn(orderer2, network, channelparticipation.ChannelInfo{
  1107  				Name:              "systemchannel",
  1108  				URL:               "/participation/v1/channels/systemchannel",
  1109  				Status:            "active",
  1110  				ConsensusRelation: "consenter",
  1111  				Height:            3,
  1112  			})
  1113  
  1114  			By("submitting a transaction to ensure the application channel is active after restart")
  1115  			submitPeerTxn(orderer2, peer, network, channelparticipation.ChannelInfo{
  1116  				Name:              "testchannel",
  1117  				URL:               "/participation/v1/channels/testchannel",
  1118  				Status:            "active",
  1119  				ConsensusRelation: "consenter",
  1120  				Height:            5,
  1121  			})
  1122  
  1123  			By("removing orderer3 from the consenters set")
  1124  			channelConfig := nwo.GetConfig(network, peer, orderer2, "testchannel")
  1125  			c := configtx.New(channelConfig)
  1126  			err := c.Orderer().RemoveConsenter(consenterChannelConfig(network, orderer3))
  1127  			Expect(err).NotTo(HaveOccurred())
  1128  			computeSignSubmitConfigUpdate(network, orderer2, peer, c, "testchannel")
  1129  
  1130  			By("ensuring orderer3 transitions to inactive/config-tracker")
  1131  			Eventually(func() channelparticipation.ChannelInfo {
  1132  				return channelparticipation.ListOne(network, orderer3, "testchannel")
  1133  			}, network.EventuallyTimeout).Should(Equal(channelparticipation.ChannelInfo{
  1134  				Name:              "testchannel",
  1135  				URL:               "/participation/v1/channels/testchannel",
  1136  				Status:            "inactive",
  1137  				ConsensusRelation: "config-tracker",
  1138  				Height:            6,
  1139  			}))
  1140  
  1141  			By("ensuring orderers 1 and 2 receive the block")
  1142  			orderers1and2 := []*nwo.Orderer{orderer1, orderer2}
  1143  			for _, o := range orderers1and2 {
  1144  				Eventually(func() channelparticipation.ChannelInfo {
  1145  					return channelparticipation.ListOne(network, o, "testchannel")
  1146  				}, network.EventuallyTimeout).Should(Equal(channelparticipation.ChannelInfo{
  1147  					Name:              "testchannel",
  1148  					URL:               "/participation/v1/channels/testchannel",
  1149  					Status:            "active",
  1150  					ConsensusRelation: "consenter",
  1151  					Height:            6,
  1152  				}))
  1153  			}
  1154  
  1155  			By("submitting a transaction to each active orderer")
  1156  			submitPeerTxn(orderer1, peer, network, channelparticipation.ChannelInfo{
  1157  				Name:              "testchannel",
  1158  				URL:               "/participation/v1/channels/testchannel",
  1159  				Status:            "active",
  1160  				ConsensusRelation: "consenter",
  1161  				Height:            7,
  1162  			})
  1163  
  1164  			submitPeerTxn(orderer2, peer, network, channelparticipation.ChannelInfo{
  1165  				Name:              "testchannel",
  1166  				URL:               "/participation/v1/channels/testchannel",
  1167  				Status:            "active",
  1168  				ConsensusRelation: "consenter",
  1169  				Height:            8,
  1170  			})
  1171  
  1172  			By("restarting orderer3 to ensure it still reports inactive/config-tracker")
  1173  			restartOrderer(orderer3, 2)
  1174  			Eventually(func() channelparticipation.ChannelInfo {
  1175  				return channelparticipation.ListOne(network, orderer3, "testchannel")
  1176  			}, network.EventuallyTimeout).Should(Equal(channelparticipation.ChannelInfo{
  1177  				Name:              "testchannel",
  1178  				URL:               "/participation/v1/channels/testchannel",
  1179  				Status:            "inactive",
  1180  				ConsensusRelation: "config-tracker",
  1181  				Height:            6,
  1182  			}))
  1183  
  1184  			By("attempting to join a channel when the system channel is present")
  1185  			genesisBlock := applicationChannelGenesisBlock(network, orderers, []*nwo.Peer{peer}, "participation-trophy")
  1186  			channelparticipationJoinFailure(network, orderers[0], "participation-trophy", genesisBlock, http.StatusMethodNotAllowed, "cannot join: system channel exists")
  1187  
  1188  			By("attempting to remove a channel when the system channel is present")
  1189  			channelparticipationRemoveFailure(network, orderers[0], "testchannel", http.StatusMethodNotAllowed, "cannot remove: system channel exists")
  1190  
  1191  			By("submitting a transaction to ensure the system channel is active after restart")
  1192  			submitOrdererTxn(orderer3, network, channelparticipation.ChannelInfo{
  1193  				Name:              "systemchannel",
  1194  				URL:               "/participation/v1/channels/systemchannel",
  1195  				Status:            "active",
  1196  				ConsensusRelation: "consenter",
  1197  				Height:            4,
  1198  			})
  1199  
  1200  			By("putting the system channel into maintenance mode")
  1201  			channelConfig = nwo.GetConfig(network, peer, orderer2, "systemchannel")
  1202  			c = configtx.New(channelConfig)
  1203  			err = c.Orderer().SetConsensusState(orderer.ConsensusStateMaintenance)
  1204  			Expect(err).NotTo(HaveOccurred())
  1205  			computeSignSubmitConfigUpdate(network, orderer2, peer, c, "systemchannel")
  1206  
  1207  			By("removing the system channel with the channel participation API")
  1208  			for _, o := range orderers {
  1209  				channelparticipation.Remove(network, o, "systemchannel")
  1210  			}
  1211  
  1212  			By("listing the channels after removing the system channel")
  1213  			for _, o := range orderers1and2 {
  1214  				cl := channelparticipation.List(network, o)
  1215  				channelparticipation.ChannelListMatcher(cl, []string{"testchannel"})
  1216  			}
  1217  			cl := channelparticipation.List(network, orderer3)
  1218  			channelparticipation.ChannelListMatcher(cl, nil)
  1219  
  1220  			By("fetching a block from each orderer to ensure a leader has been elected for the existing application channel")
  1221  			for _, o := range orderers1and2 {
  1222  				FetchBlock(network, o, 0, "testchannel")
  1223  			}
  1224  
  1225  			By("submitting a transaction to each active orderer on testchannel")
  1226  			submitPeerTxn(orderer1, peer, network, channelparticipation.ChannelInfo{
  1227  				Name:              "testchannel",
  1228  				URL:               "/participation/v1/channels/testchannel",
  1229  				Status:            "active",
  1230  				ConsensusRelation: "consenter",
  1231  				Height:            9,
  1232  			})
  1233  
  1234  			submitPeerTxn(orderer2, peer, network, channelparticipation.ChannelInfo{
  1235  				Name:              "testchannel",
  1236  				URL:               "/participation/v1/channels/testchannel",
  1237  				Status:            "active",
  1238  				ConsensusRelation: "consenter",
  1239  				Height:            10,
  1240  			})
  1241  
  1242  			By("using the channel participation API to join a new channel")
  1243  			expectedChannelInfoPT := channelparticipation.ChannelInfo{
  1244  				Name:              "participation-trophy",
  1245  				URL:               "/participation/v1/channels/participation-trophy",
  1246  				Status:            "active",
  1247  				ConsensusRelation: "consenter",
  1248  				Height:            1,
  1249  			}
  1250  
  1251  			for _, o := range orderers {
  1252  				By("joining " + o.Name + " to channel as a consenter")
  1253  				channelparticipation.Join(network, o, "participation-trophy", genesisBlock, expectedChannelInfoPT)
  1254  				channelInfo := channelparticipation.ListOne(network, o, "participation-trophy")
  1255  				Expect(channelInfo).To(Equal(expectedChannelInfoPT))
  1256  			}
  1257  
  1258  			submitPeerTxn(orderer1, peer, network, channelparticipation.ChannelInfo{
  1259  				Name:              "participation-trophy",
  1260  				URL:               "/participation/v1/channels/participation-trophy",
  1261  				Status:            "active",
  1262  				ConsensusRelation: "consenter",
  1263  				Height:            2,
  1264  			})
  1265  
  1266  			submitPeerTxn(orderer2, peer, network, channelparticipation.ChannelInfo{
  1267  				Name:              "participation-trophy",
  1268  				URL:               "/participation/v1/channels/participation-trophy",
  1269  				Status:            "active",
  1270  				ConsensusRelation: "consenter",
  1271  				Height:            3,
  1272  			})
  1273  
  1274  			submitPeerTxn(orderer3, peer, network, channelparticipation.ChannelInfo{
  1275  				Name:              "participation-trophy",
  1276  				URL:               "/participation/v1/channels/participation-trophy",
  1277  				Status:            "active",
  1278  				ConsensusRelation: "consenter",
  1279  				Height:            4,
  1280  			})
  1281  		})
  1282  	})
  1283  })
  1284  
  1285  // submit a transaction signed by the peer and ensure it was
  1286  // committed to the ledger
  1287  func submitPeerTxn(o *nwo.Orderer, peer *nwo.Peer, n *nwo.Network, expectedChannelInfo channelparticipation.ChannelInfo) {
  1288  	env := CreateBroadcastEnvelope(n, peer, expectedChannelInfo.Name, []byte("hello"))
  1289  	submitTxn(o, env, n, expectedChannelInfo)
  1290  }
  1291  
  1292  // submit a transaction signed by the orderer and ensure it is
  1293  // committed to the ledger
  1294  func submitOrdererTxn(o *nwo.Orderer, n *nwo.Network, expectedChannelInfo channelparticipation.ChannelInfo) {
  1295  	env := CreateBroadcastEnvelope(n, o, expectedChannelInfo.Name, []byte("hello"))
  1296  	submitTxn(o, env, n, expectedChannelInfo)
  1297  }
  1298  
  1299  // submit the envelope to the orderer and ensure it is committed
  1300  // to the ledger
  1301  func submitTxn(o *nwo.Orderer, env *common.Envelope, n *nwo.Network, expectedChannelInfo channelparticipation.ChannelInfo) {
  1302  	By("submitting a transaction to " + o.Name)
  1303  	Eventually(broadcastTransactionFunc(n, o, env), n.EventuallyTimeout, time.Second).Should(Equal(common.Status_SUCCESS))
  1304  
  1305  	By("checking the channel info on " + o.Name)
  1306  	Eventually(func() channelparticipation.ChannelInfo {
  1307  		return channelparticipation.ListOne(n, o, expectedChannelInfo.Name)
  1308  	}, n.EventuallyTimeout).Should(Equal(expectedChannelInfo))
  1309  }
  1310  
  1311  func applicationChannelGenesisBlock(n *nwo.Network, orderers []*nwo.Orderer, peers []*nwo.Peer, channel string) *common.Block {
  1312  	ordererOrgs, consenters := ordererOrganizationsAndConsenters(n, orderers)
  1313  	peerOrgs := peerOrganizations(n, peers)
  1314  
  1315  	channelConfig := configtx.Channel{
  1316  		Orderer: configtx.Orderer{
  1317  			OrdererType:   "etcdraft",
  1318  			Organizations: ordererOrgs,
  1319  			EtcdRaft: orderer.EtcdRaft{
  1320  				Consenters: consenters,
  1321  				Options: orderer.EtcdRaftOptions{
  1322  					TickInterval:         "500ms",
  1323  					ElectionTick:         10,
  1324  					HeartbeatTick:        1,
  1325  					MaxInflightBlocks:    5,
  1326  					SnapshotIntervalSize: 16 * 1024 * 1024, // 16 MB
  1327  				},
  1328  			},
  1329  			Policies: map[string]configtx.Policy{
  1330  				"Readers": {
  1331  					Type: "ImplicitMeta",
  1332  					Rule: "ANY Readers",
  1333  				},
  1334  				"Writers": {
  1335  					Type: "ImplicitMeta",
  1336  					Rule: "ANY Writers",
  1337  				},
  1338  				"Admins": {
  1339  					Type: "ImplicitMeta",
  1340  					Rule: "MAJORITY Admins",
  1341  				},
  1342  				"BlockValidation": {
  1343  					Type: "ImplicitMeta",
  1344  					Rule: "ANY Writers",
  1345  				},
  1346  			},
  1347  			Capabilities: []string{"V2_0"},
  1348  			BatchSize: orderer.BatchSize{
  1349  				MaxMessageCount:   100,
  1350  				AbsoluteMaxBytes:  1024 * 1024,
  1351  				PreferredMaxBytes: 512 * 1024,
  1352  			},
  1353  			BatchTimeout: 2 * time.Second,
  1354  			State:        "STATE_NORMAL",
  1355  		},
  1356  		Application: configtx.Application{
  1357  			Organizations: peerOrgs,
  1358  			Capabilities:  []string{"V2_0"},
  1359  			Policies: map[string]configtx.Policy{
  1360  				"Readers": {
  1361  					Type: "ImplicitMeta",
  1362  					Rule: "ANY Readers",
  1363  				},
  1364  				"Writers": {
  1365  					Type: "ImplicitMeta",
  1366  					Rule: "ANY Writers",
  1367  				},
  1368  				"Admins": {
  1369  					Type: "ImplicitMeta",
  1370  					Rule: "MAJORITY Admins",
  1371  				},
  1372  				"Endorsement": {
  1373  					Type: "ImplicitMeta",
  1374  					Rule: "MAJORITY Endorsement",
  1375  				},
  1376  				"LifecycleEndorsement": {
  1377  					Type: "ImplicitMeta",
  1378  					Rule: "MAJORITY Endorsement",
  1379  				},
  1380  			},
  1381  		},
  1382  		Capabilities: []string{"V2_0"},
  1383  		Policies: map[string]configtx.Policy{
  1384  			"Readers": {
  1385  				Type: "ImplicitMeta",
  1386  				Rule: "ANY Readers",
  1387  			},
  1388  			"Writers": {
  1389  				Type: "ImplicitMeta",
  1390  				Rule: "ANY Writers",
  1391  			},
  1392  			"Admins": {
  1393  				Type: "ImplicitMeta",
  1394  				Rule: "MAJORITY Admins",
  1395  			},
  1396  		},
  1397  	}
  1398  
  1399  	genesisBlock, err := configtx.NewApplicationChannelGenesisBlock(channelConfig, channel)
  1400  	Expect(err).NotTo(HaveOccurred())
  1401  
  1402  	return genesisBlock
  1403  }
  1404  
  1405  func systemChannelGenesisBlock(n *nwo.Network, orderers []*nwo.Orderer, peers []*nwo.Peer, channel string) *common.Block {
  1406  	ordererOrgs, consenters := ordererOrganizationsAndConsenters(n, orderers)
  1407  	peerOrgs := peerOrganizations(n, peers)
  1408  
  1409  	channelConfig := configtx.Channel{
  1410  		Orderer: configtx.Orderer{
  1411  			OrdererType:   "etcdraft",
  1412  			Organizations: ordererOrgs,
  1413  			EtcdRaft: orderer.EtcdRaft{
  1414  				Consenters: consenters,
  1415  				Options: orderer.EtcdRaftOptions{
  1416  					TickInterval:         "500ms",
  1417  					ElectionTick:         10,
  1418  					HeartbeatTick:        1,
  1419  					MaxInflightBlocks:    5,
  1420  					SnapshotIntervalSize: 16 * 1024 * 1024, // 16 MB
  1421  				},
  1422  			},
  1423  			Policies: map[string]configtx.Policy{
  1424  				"Readers": {
  1425  					Type: "ImplicitMeta",
  1426  					Rule: "ANY Readers",
  1427  				},
  1428  				"Writers": {
  1429  					Type: "ImplicitMeta",
  1430  					Rule: "ANY Writers",
  1431  				},
  1432  				"Admins": {
  1433  					Type: "ImplicitMeta",
  1434  					Rule: "MAJORITY Admins",
  1435  				},
  1436  				"BlockValidation": {
  1437  					Type: "ImplicitMeta",
  1438  					Rule: "ANY Writers",
  1439  				},
  1440  			},
  1441  			Capabilities: []string{"V2_0"},
  1442  			BatchSize: orderer.BatchSize{
  1443  				MaxMessageCount:   100,
  1444  				AbsoluteMaxBytes:  1024 * 1024,
  1445  				PreferredMaxBytes: 512 * 1024,
  1446  			},
  1447  			BatchTimeout: 2 * time.Second,
  1448  			State:        "STATE_NORMAL",
  1449  		},
  1450  		Consortiums: []configtx.Consortium{
  1451  			{
  1452  				Name:          n.Consortiums[0].Name,
  1453  				Organizations: peerOrgs,
  1454  			},
  1455  		},
  1456  		Capabilities: []string{"V2_0"},
  1457  		Policies: map[string]configtx.Policy{
  1458  			"Readers": {
  1459  				Type: "ImplicitMeta",
  1460  				Rule: "ANY Readers",
  1461  			},
  1462  			"Writers": {
  1463  				Type: "ImplicitMeta",
  1464  				Rule: "ANY Writers",
  1465  			},
  1466  			"Admins": {
  1467  				Type: "ImplicitMeta",
  1468  				Rule: "MAJORITY Admins",
  1469  			},
  1470  		},
  1471  	}
  1472  
  1473  	genesisBlock, err := configtx.NewSystemChannelGenesisBlock(channelConfig, channel)
  1474  	Expect(err).NotTo(HaveOccurred())
  1475  
  1476  	return genesisBlock
  1477  }
  1478  
  1479  // parseCertificate loads the PEM-encoded x509 certificate at the specified
  1480  // path.
  1481  func parseCertificate(path string) *x509.Certificate {
  1482  	certBytes, err := ioutil.ReadFile(path)
  1483  	Expect(err).NotTo(HaveOccurred())
  1484  	pemBlock, _ := pem.Decode(certBytes)
  1485  	cert, err := x509.ParseCertificate(pemBlock.Bytes)
  1486  	Expect(err).NotTo(HaveOccurred())
  1487  	return cert
  1488  }
  1489  
  1490  // parsePrivateKey loads the PEM-encoded private key at the specified path.
  1491  func parsePrivateKey(path string) crypto.PrivateKey {
  1492  	pkBytes, err := ioutil.ReadFile(path)
  1493  	Expect(err).NotTo(HaveOccurred())
  1494  	pemBlock, _ := pem.Decode(pkBytes)
  1495  	privateKey, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes)
  1496  	Expect(err).NotTo(HaveOccurred())
  1497  	return privateKey
  1498  }
  1499  
  1500  func ordererOrganizationsAndConsenters(n *nwo.Network, orderers []*nwo.Orderer) ([]configtx.Organization, []orderer.Consenter) {
  1501  	ordererOrgsMap := map[string]*configtx.Organization{}
  1502  	consenters := make([]orderer.Consenter, len(orderers))
  1503  
  1504  	for i, o := range orderers {
  1505  		rootCert := parseCertificate(n.OrdererCACert(o))
  1506  		adminCert := parseCertificate(n.OrdererUserCert(o, "Admin"))
  1507  		tlsRootCert := parseCertificate(filepath.Join(n.OrdererLocalTLSDir(o), "ca.crt"))
  1508  
  1509  		orgConfig, ok := ordererOrgsMap[o.Organization]
  1510  		if !ok {
  1511  			orgConfig := configtxOrganization(n.Organization(o.Organization), rootCert, adminCert, tlsRootCert)
  1512  			orgConfig.OrdererEndpoints = []string{
  1513  				n.OrdererAddress(o, nwo.ListenPort),
  1514  			}
  1515  			ordererOrgsMap[o.Organization] = &orgConfig
  1516  		} else {
  1517  			orgConfig.OrdererEndpoints = append(orgConfig.OrdererEndpoints, n.OrdererAddress(o, nwo.ListenPort))
  1518  			orgConfig.MSP.RootCerts = append(orgConfig.MSP.RootCerts, rootCert)
  1519  			orgConfig.MSP.Admins = append(orgConfig.MSP.Admins, adminCert)
  1520  			orgConfig.MSP.TLSRootCerts = append(orgConfig.MSP.TLSRootCerts, tlsRootCert)
  1521  		}
  1522  
  1523  		consenters[i] = consenterChannelConfig(n, o)
  1524  	}
  1525  
  1526  	ordererOrgs := []configtx.Organization{}
  1527  	for _, o := range ordererOrgsMap {
  1528  		ordererOrgs = append(ordererOrgs, *o)
  1529  	}
  1530  
  1531  	return ordererOrgs, consenters
  1532  }
  1533  
  1534  // constructs the peer organizations for a config block. It should be passed
  1535  // only one peer per organization.
  1536  func peerOrganizations(n *nwo.Network, peers []*nwo.Peer) []configtx.Organization {
  1537  	peerOrgs := make([]configtx.Organization, len(peers))
  1538  	for i, p := range peers {
  1539  		rootCert := parseCertificate(n.PeerCACert(p))
  1540  		adminCert := parseCertificate(n.PeerUserCert(p, "Admin"))
  1541  		tlsRootCert := parseCertificate(filepath.Join(n.PeerLocalTLSDir(p), "ca.crt"))
  1542  
  1543  		peerOrgs[i] = configtxOrganization(n.Organization(p.Organization), rootCert, adminCert, tlsRootCert)
  1544  	}
  1545  
  1546  	return peerOrgs
  1547  }
  1548  
  1549  func configtxOrganization(org *nwo.Organization, rootCert, adminCert, tlsRootCert *x509.Certificate) configtx.Organization {
  1550  	return configtx.Organization{
  1551  		Name: org.Name,
  1552  		Policies: map[string]configtx.Policy{
  1553  			"Readers": {
  1554  				Type: "Signature",
  1555  				Rule: fmt.Sprintf("OR('%s.member')", org.MSPID),
  1556  			},
  1557  			"Writers": {
  1558  				Type: "Signature",
  1559  				Rule: fmt.Sprintf("OR('%s.member')", org.MSPID),
  1560  			},
  1561  			"Admins": {
  1562  				Type: "Signature",
  1563  				Rule: fmt.Sprintf("OR('%s.admin')", org.MSPID),
  1564  			},
  1565  		},
  1566  		MSP: configtx.MSP{
  1567  			Name:         org.MSPID,
  1568  			RootCerts:    []*x509.Certificate{rootCert},
  1569  			Admins:       []*x509.Certificate{adminCert},
  1570  			TLSRootCerts: []*x509.Certificate{tlsRootCert},
  1571  		},
  1572  	}
  1573  }
  1574  
  1575  func computeSignSubmitConfigUpdate(n *nwo.Network, o *nwo.Orderer, p *nwo.Peer, c configtx.ConfigTx, channel string) {
  1576  	configUpdate, err := c.ComputeMarshaledUpdate(channel)
  1577  	Expect(err).NotTo(HaveOccurred())
  1578  
  1579  	signingIdentity := configtx.SigningIdentity{
  1580  		Certificate: parseCertificate(n.OrdererUserCert(o, "Admin")),
  1581  		PrivateKey:  parsePrivateKey(n.OrdererUserKey(o, "Admin")),
  1582  		MSPID:       n.Organization(o.Organization).MSPID,
  1583  	}
  1584  	signature, err := signingIdentity.CreateConfigSignature(configUpdate)
  1585  	Expect(err).NotTo(HaveOccurred())
  1586  
  1587  	configUpdateEnvelope, err := configtx.NewEnvelope(configUpdate, signature)
  1588  	Expect(err).NotTo(HaveOccurred())
  1589  	err = signingIdentity.SignEnvelope(configUpdateEnvelope)
  1590  	Expect(err).NotTo(HaveOccurred())
  1591  
  1592  	currentBlockNumber := nwo.CurrentConfigBlockNumber(n, p, o, channel)
  1593  
  1594  	Eventually(broadcastTransactionFunc(n, o, configUpdateEnvelope), n.EventuallyTimeout).Should(Equal(common.Status_SUCCESS))
  1595  
  1596  	ccb := func() uint64 { return nwo.CurrentConfigBlockNumber(n, p, o, channel) }
  1597  	Eventually(ccb, n.EventuallyTimeout).Should(BeNumerically(">", currentBlockNumber))
  1598  }
  1599  
  1600  func broadcastTransactionFunc(n *nwo.Network, o *nwo.Orderer, env *common.Envelope) func() common.Status {
  1601  	return func() common.Status {
  1602  		resp, err := ordererclient.Broadcast(n, o, env)
  1603  		Expect(err).NotTo(HaveOccurred())
  1604  		return resp.Status
  1605  	}
  1606  }
  1607  
  1608  func consenterChannelConfig(n *nwo.Network, o *nwo.Orderer) orderer.Consenter {
  1609  	host, port := conftx.OrdererClusterHostPort(n, o)
  1610  	tlsCert := parseCertificate(filepath.Join(n.OrdererLocalTLSDir(o), "server.crt"))
  1611  	return orderer.Consenter{
  1612  		Address: orderer.EtcdAddress{
  1613  			Host: host,
  1614  			Port: port,
  1615  		},
  1616  		ClientTLSCert: tlsCert,
  1617  		ServerTLSCert: tlsCert,
  1618  	}
  1619  }
  1620  
  1621  type errorResponse struct {
  1622  	Error string `json:"error"`
  1623  }
  1624  
  1625  func channelparticipationJoinFailure(n *nwo.Network, o *nwo.Orderer, channel string, block *common.Block, expectedStatus int, expectedError string) {
  1626  	blockBytes, err := proto.Marshal(block)
  1627  	Expect(err).NotTo(HaveOccurred())
  1628  	url := fmt.Sprintf("https://127.0.0.1:%d/participation/v1/channels", n.OrdererPort(o, nwo.AdminPort))
  1629  	req := channelparticipation.GenerateJoinRequest(url, channel, blockBytes)
  1630  	authClient, _ := nwo.OrdererOperationalClients(n, o)
  1631  
  1632  	doBodyFailure(authClient, req, expectedStatus, expectedError)
  1633  }
  1634  
  1635  func doBodyFailure(client *http.Client, req *http.Request, expectedStatus int, expectedError string) {
  1636  	resp, err := client.Do(req)
  1637  	Expect(err).NotTo(HaveOccurred())
  1638  	Expect(resp.StatusCode).To(Equal(expectedStatus))
  1639  	body, err := ioutil.ReadAll(resp.Body)
  1640  	Expect(err).NotTo(HaveOccurred())
  1641  	resp.Body.Close()
  1642  
  1643  	errorResponse := &errorResponse{}
  1644  	err = json.Unmarshal(body, errorResponse)
  1645  	Expect(err).NotTo(HaveOccurred())
  1646  	Expect(errorResponse.Error).To(Equal(expectedError))
  1647  }
  1648  
  1649  func channelparticipationRemoveFailure(n *nwo.Network, o *nwo.Orderer, channel string, expectedStatus int, expectedError string) {
  1650  	authClient, _ := nwo.OrdererOperationalClients(n, o)
  1651  	url := fmt.Sprintf("https://127.0.0.1:%d/participation/v1/channels/%s", n.OrdererPort(o, nwo.AdminPort), channel)
  1652  
  1653  	req, err := http.NewRequest(http.MethodDelete, url, nil)
  1654  	Expect(err).NotTo(HaveOccurred())
  1655  
  1656  	doBodyFailure(authClient, req, expectedStatus, expectedError)
  1657  }
  1658  
  1659  func multiNodeEtcdRaftTwoChannels() *nwo.Config {
  1660  	config := nwo.MultiNodeEtcdRaft()
  1661  	config.Channels = []*nwo.Channel{
  1662  		{Name: "testchannel", Profile: "TwoOrgsChannel"},
  1663  		{Name: "testchannel2", Profile: "TwoOrgsChannel"},
  1664  	}
  1665  
  1666  	for _, peer := range config.Peers {
  1667  		peer.Channels = []*nwo.PeerChannel{
  1668  			{Name: "testchannel", Anchor: true},
  1669  			{Name: "testchannel2", Anchor: true},
  1670  		}
  1671  	}
  1672  
  1673  	return config
  1674  }