1089 lines
42 KiB
Go
1089 lines
42 KiB
Go
/*
|
|
Copyright IBM Corp All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package raft
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/x509"
|
|
"encoding/json"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
"time"
|
|
|
|
docker "github.com/fsouza/go-dockerclient"
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/hyperledger/fabric-config/configtx"
|
|
"github.com/hyperledger/fabric-config/configtx/orderer"
|
|
"github.com/hyperledger/fabric-protos-go/common"
|
|
"github.com/hyperledger/fabric/bccsp"
|
|
"github.com/hyperledger/fabric/common/ledger/blockledger/fileledger"
|
|
"github.com/hyperledger/fabric/common/metrics/disabled"
|
|
"github.com/hyperledger/fabric/integration/channelparticipation"
|
|
conftx "github.com/hyperledger/fabric/integration/configtx"
|
|
"github.com/hyperledger/fabric/integration/nwo"
|
|
"github.com/hyperledger/fabric/integration/ordererclient"
|
|
"github.com/hyperledger/fabric/protoutil"
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
"github.com/onsi/gomega/gbytes"
|
|
"github.com/tedsuo/ifrit"
|
|
ginkgomon "github.com/tedsuo/ifrit/ginkgomon_v2"
|
|
)
|
|
|
|
var _ = Describe("ChannelParticipation", func() {
|
|
var (
|
|
testDir string
|
|
client *docker.Client
|
|
network *nwo.Network
|
|
ordererProcesses []ifrit.Process
|
|
ordererRunners []*ginkgomon.Runner
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
var err error
|
|
testDir, err = ioutil.TempDir("", "channel-participation")
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
client, err = docker.NewClientFromEnv()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
ordererProcesses = []ifrit.Process{}
|
|
ordererRunners = []*ginkgomon.Runner{}
|
|
})
|
|
|
|
AfterEach(func() {
|
|
for _, ordererProcess := range ordererProcesses {
|
|
ordererProcess.Signal(syscall.SIGTERM)
|
|
Eventually(ordererProcess.Wait(), network.EventuallyTimeout).Should(Receive())
|
|
}
|
|
if network != nil {
|
|
network.Cleanup()
|
|
}
|
|
os.RemoveAll(testDir)
|
|
})
|
|
|
|
Describe("three node etcdraft network without a system channel", func() {
|
|
startOrderer := func(o *nwo.Orderer) {
|
|
ordererRunner := network.OrdererRunner(o)
|
|
ordererProcess := ifrit.Invoke(ordererRunner)
|
|
Eventually(ordererProcess.Ready(), network.EventuallyTimeout).Should(BeClosed())
|
|
Eventually(ordererRunner.Err(), network.EventuallyTimeout).Should(gbytes.Say("Registrar initializing without a system channel"))
|
|
ordererProcesses = append(ordererProcesses, ordererProcess)
|
|
ordererRunners = append(ordererRunners, ordererRunner)
|
|
}
|
|
|
|
BeforeEach(func() {
|
|
network = nwo.New(multiNodeEtcdRaftTwoChannels(), testDir, client, StartPort(), components)
|
|
network.GenerateConfigTree()
|
|
network.Bootstrap()
|
|
})
|
|
|
|
It("joins application channels from genesis block and removes a channel using the channel participation API", func() {
|
|
orderer1 := network.Orderer("orderer1")
|
|
orderer2 := network.Orderer("orderer2")
|
|
orderer3 := network.Orderer("orderer3")
|
|
orderers := []*nwo.Orderer{orderer1, orderer2, orderer3}
|
|
consenters := []*nwo.Orderer{orderer1, orderer2}
|
|
peer := network.Peer("Org1", "peer0")
|
|
|
|
By("starting all three orderers")
|
|
for _, o := range orderers {
|
|
startOrderer(o)
|
|
cl := channelparticipation.List(network, o)
|
|
Expect(cl).To(Equal(channelparticipation.ChannelList{}))
|
|
}
|
|
|
|
genesisBlock := applicationChannelGenesisBlock(network, consenters, []*nwo.Peer{peer}, "participation-trophy")
|
|
expectedChannelInfoPT := channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 1,
|
|
}
|
|
|
|
for _, o := range consenters {
|
|
By("joining " + o.Name + " to channel as a consenter")
|
|
channelparticipation.Join(network, o, "participation-trophy", genesisBlock, expectedChannelInfoPT)
|
|
channelInfo := channelparticipation.ListOne(network, o, "participation-trophy")
|
|
Expect(channelInfo).To(Equal(expectedChannelInfoPT))
|
|
}
|
|
|
|
submitPeerTxn(orderer1, peer, network, channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 2,
|
|
})
|
|
|
|
submitPeerTxn(orderer2, peer, network, channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 3,
|
|
})
|
|
|
|
By("joining orderer3 to the channel as a follower")
|
|
// make sure we can join using a config block from one of the other orderers
|
|
configBlockPT := nwo.GetConfigBlock(network, peer, orderer2, "participation-trophy")
|
|
expectedChannelInfoPTFollower := channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "onboarding",
|
|
ConsensusRelation: "follower",
|
|
Height: 0,
|
|
}
|
|
channelparticipation.Join(network, orderer3, "participation-trophy", configBlockPT, expectedChannelInfoPTFollower)
|
|
|
|
By("ensuring orderer3 completes onboarding successfully")
|
|
expectedChannelInfoPTFollower.Status = "active"
|
|
expectedChannelInfoPTFollower.Height = 3
|
|
Eventually(func() channelparticipation.ChannelInfo {
|
|
return channelparticipation.ListOne(network, orderer3, "participation-trophy")
|
|
}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPTFollower))
|
|
|
|
By("adding orderer3 to the consenters set")
|
|
channelConfig := nwo.GetConfig(network, peer, orderer1, "participation-trophy")
|
|
c := configtx.New(channelConfig)
|
|
err := c.Orderer().AddConsenter(consenterChannelConfig(network, orderer3))
|
|
Expect(err).NotTo(HaveOccurred())
|
|
computeSignSubmitConfigUpdate(network, orderer1, peer, c, "participation-trophy")
|
|
|
|
By("ensuring orderer3 transitions from follower to consenter")
|
|
// config update above added a block
|
|
expectedChannelInfoPT.Height = 4
|
|
Eventually(func() channelparticipation.ChannelInfo {
|
|
return channelparticipation.ListOne(network, orderer3, "participation-trophy")
|
|
}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPT))
|
|
|
|
By("submitting transaction to orderer3 to ensure it is active")
|
|
submitPeerTxn(orderer3, peer, network, channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 5,
|
|
})
|
|
|
|
By("joining orderer1 to another channel as a consenter")
|
|
genesisBlockAPT := applicationChannelGenesisBlock(network, []*nwo.Orderer{orderer1}, []*nwo.Peer{peer}, "another-participation-trophy")
|
|
expectedChannelInfoAPT := channelparticipation.ChannelInfo{
|
|
Name: "another-participation-trophy",
|
|
URL: "/participation/v1/channels/another-participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 1,
|
|
}
|
|
channelparticipation.Join(network, orderer1, "another-participation-trophy", genesisBlockAPT, expectedChannelInfoAPT)
|
|
channelInfo := channelparticipation.ListOne(network, orderer1, "another-participation-trophy")
|
|
Expect(channelInfo).To(Equal(expectedChannelInfoAPT))
|
|
|
|
By("listing all channels for orderer1")
|
|
cl := channelparticipation.List(network, orderer1)
|
|
channelparticipation.ChannelListMatcher(cl, []string{"participation-trophy", "another-participation-trophy"})
|
|
|
|
By("removing orderer1 from the consenter set")
|
|
channelConfig = nwo.GetConfig(network, peer, orderer2, "participation-trophy")
|
|
c = configtx.New(channelConfig)
|
|
err = c.Orderer().RemoveConsenter(consenterChannelConfig(network, orderer1))
|
|
Expect(err).NotTo(HaveOccurred())
|
|
computeSignSubmitConfigUpdate(network, orderer2, peer, c, "participation-trophy")
|
|
|
|
By("ensuring orderer1 transitions to a follower")
|
|
Eventually(func() channelparticipation.ChannelInfo {
|
|
return channelparticipation.ListOne(network, orderer1, "participation-trophy")
|
|
}, network.EventuallyTimeout).Should(Equal(channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "follower",
|
|
Height: 6,
|
|
}))
|
|
|
|
submitPeerTxn(orderer2, peer, network, channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 7,
|
|
})
|
|
|
|
By("ensuring orderer1 pulls the latest block as a follower")
|
|
Eventually(func() channelparticipation.ChannelInfo {
|
|
return channelparticipation.ListOne(network, orderer1, "participation-trophy")
|
|
}, network.EventuallyTimeout).Should(Equal(channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "follower",
|
|
Height: 7,
|
|
}))
|
|
|
|
By("removing orderer1 from a channel")
|
|
channelparticipation.Remove(network, orderer1, "participation-trophy")
|
|
Eventually(func() channelparticipation.ChannelList {
|
|
return channelparticipation.List(network, orderer1)
|
|
}, network.EventuallyTimeout).Should(Equal(channelparticipation.ChannelList{
|
|
Channels: []channelparticipation.ChannelInfoShort{
|
|
{
|
|
Name: "another-participation-trophy",
|
|
URL: "/participation/v1/channels/another-participation-trophy",
|
|
},
|
|
},
|
|
}))
|
|
|
|
By("submitting transaction to orderer1")
|
|
env := CreateBroadcastEnvelope(network, peer, "participation-trophy", []byte("hello"))
|
|
resp, err := ordererclient.Broadcast(network, orderer1, env)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(resp.Status).To(Equal(common.Status_BAD_REQUEST))
|
|
|
|
By("listing all channels for orderer1")
|
|
cl = channelparticipation.List(network, orderer1)
|
|
channelparticipation.ChannelListMatcher(cl, []string{"another-participation-trophy"})
|
|
|
|
By("joining orderer1 to channel it was previously removed from as consenter")
|
|
configBlockPT = nwo.GetConfigBlock(network, peer, orderer2, "participation-trophy")
|
|
expectedChannelInfoPTFollower = channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "onboarding",
|
|
ConsensusRelation: "follower",
|
|
Height: 0,
|
|
}
|
|
channelparticipation.Join(network, orderer1, "participation-trophy", configBlockPT, expectedChannelInfoPTFollower)
|
|
|
|
By("ensuring orderer1 completes onboarding successfully")
|
|
expectedChannelInfoPTFollower.Status = "active"
|
|
expectedChannelInfoPTFollower.Height = 7
|
|
Eventually(func() channelparticipation.ChannelInfo {
|
|
return channelparticipation.ListOne(network, orderer1, "participation-trophy")
|
|
}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPTFollower))
|
|
|
|
By("adding orderer1 to the consenters set")
|
|
channelConfig = nwo.GetConfig(network, peer, orderer3, "participation-trophy")
|
|
c = configtx.New(channelConfig)
|
|
err = c.Orderer().AddConsenter(consenterChannelConfig(network, orderer1))
|
|
Expect(err).NotTo(HaveOccurred())
|
|
computeSignSubmitConfigUpdate(network, orderer3, peer, c, "participation-trophy")
|
|
|
|
By("ensuring orderer1 transitions from follower to consenter")
|
|
expectedChannelInfoPT = channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 8,
|
|
}
|
|
Eventually(func() channelparticipation.ChannelInfo {
|
|
return channelparticipation.ListOne(network, orderer1, "participation-trophy")
|
|
}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPT))
|
|
|
|
submitPeerTxn(orderer1, peer, network, channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 9,
|
|
})
|
|
|
|
By("ensuring the channel is still usable by submitting a transaction to each remaining consenter for the channel")
|
|
submitPeerTxn(orderer2, peer, network, channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 10,
|
|
})
|
|
|
|
submitPeerTxn(orderer3, peer, network, channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 11,
|
|
})
|
|
|
|
By("attempting to join with an invalid block")
|
|
channelparticipationJoinFailure(network, orderer3, "nice-try", &common.Block{}, http.StatusBadRequest, "invalid join block: block is not a config block")
|
|
|
|
By("attempting to join a channel that already exists")
|
|
channelparticipationJoinFailure(network, orderer3, "participation-trophy", genesisBlock, http.StatusMethodNotAllowed, "cannot join: channel already exists")
|
|
|
|
By("attempting to join a channel that defines a system channel")
|
|
channelparticipationJoinFailure(network, orderer3, "nice-try", createJoinBlockDefineSystemChannel("nice-try"), http.StatusBadRequest, "invalid join block: invalid config: contains consortiums: system channel not supported")
|
|
})
|
|
|
|
It("joins application channels with join-block as consenter via channel participation api", func() {
|
|
orderer1 := network.Orderer("orderer1")
|
|
orderer2 := network.Orderer("orderer2")
|
|
orderer3 := network.Orderer("orderer3")
|
|
orderers := []*nwo.Orderer{orderer1, orderer2}
|
|
peer := network.Peer("Org1", "peer0")
|
|
|
|
By("starting two orderers")
|
|
for _, o := range orderers {
|
|
startOrderer(o)
|
|
cl := channelparticipation.List(network, o)
|
|
Expect(cl).To(Equal(channelparticipation.ChannelList{}))
|
|
}
|
|
|
|
genesisBlock := applicationChannelGenesisBlock(network, orderers, []*nwo.Peer{peer}, "participation-trophy")
|
|
expectedChannelInfoPT := channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 1,
|
|
}
|
|
|
|
for _, o := range orderers {
|
|
By("joining " + o.Name + " to channel as a consenter")
|
|
channelparticipation.Join(network, o, "participation-trophy", genesisBlock, expectedChannelInfoPT)
|
|
channelInfo := channelparticipation.ListOne(network, o, "participation-trophy")
|
|
Expect(channelInfo).To(Equal(expectedChannelInfoPT))
|
|
}
|
|
|
|
submitPeerTxn(orderer1, peer, network, channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 2,
|
|
})
|
|
|
|
submitPeerTxn(orderer2, peer, network, channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 3,
|
|
})
|
|
|
|
By("submitting a channel config update")
|
|
channelConfig := nwo.GetConfig(network, peer, orderer1, "participation-trophy")
|
|
c := configtx.New(channelConfig)
|
|
err := c.Orderer().AddCapability("V1_1")
|
|
Expect(err).NotTo(HaveOccurred())
|
|
computeSignSubmitConfigUpdate(network, orderer1, peer, c, "participation-trophy")
|
|
|
|
currentBlockNumber := nwo.CurrentConfigBlockNumber(network, peer, orderer1, "participation-trophy")
|
|
Expect(currentBlockNumber).To(BeNumerically(">", 1))
|
|
|
|
By("starting third orderer")
|
|
startOrderer(orderer3)
|
|
cl := channelparticipation.List(network, orderer3)
|
|
Expect(cl).To(Equal(channelparticipation.ChannelList{}))
|
|
|
|
By("adding orderer3 to the consenters set")
|
|
channelConfig = nwo.GetConfig(network, peer, orderer2, "participation-trophy")
|
|
c = configtx.New(channelConfig)
|
|
err = c.Orderer().AddConsenter(consenterChannelConfig(network, orderer3))
|
|
Expect(err).NotTo(HaveOccurred())
|
|
computeSignSubmitConfigUpdate(network, orderer2, peer, c, "participation-trophy")
|
|
|
|
By("joining orderer3 to the channel as a consenter")
|
|
// make sure we can join using a config block from one of the other orderers
|
|
configBlockPT := nwo.GetConfigBlock(network, peer, orderer2, "participation-trophy")
|
|
expectedChannelInfoConsenter := channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "onboarding",
|
|
ConsensusRelation: "consenter",
|
|
Height: 0,
|
|
}
|
|
channelparticipation.Join(network, orderer3, "participation-trophy", configBlockPT, expectedChannelInfoConsenter)
|
|
|
|
By("ensuring orderer3 completes onboarding successfully")
|
|
expectedChannelInfoConsenter.Status = "active"
|
|
expectedChannelInfoConsenter.Height = 5
|
|
Eventually(func() channelparticipation.ChannelInfo {
|
|
return channelparticipation.ListOne(network, orderer3, "participation-trophy")
|
|
}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoConsenter))
|
|
|
|
submitPeerTxn(orderer3, peer, network, channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 6,
|
|
})
|
|
})
|
|
|
|
Context("joining application channels with join-block as follower via channel participation api", func() {
|
|
var (
|
|
orderer1, orderer2, orderer3 *nwo.Orderer
|
|
orderers []*nwo.Orderer
|
|
peer *nwo.Peer
|
|
genesisBlock, configBlock *common.Block
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
orderer1 = network.Orderer("orderer1")
|
|
orderer2 = network.Orderer("orderer2")
|
|
orderer3 = network.Orderer("orderer3")
|
|
orderers = []*nwo.Orderer{orderer1, orderer2}
|
|
peer = network.Peer("Org1", "peer0")
|
|
|
|
By("starting two orderers")
|
|
for _, o := range orderers {
|
|
startOrderer(o)
|
|
cl := channelparticipation.List(network, o)
|
|
Expect(cl).To(Equal(channelparticipation.ChannelList{}))
|
|
}
|
|
|
|
genesisBlock = applicationChannelGenesisBlock(network, orderers, []*nwo.Peer{peer}, "participation-trophy")
|
|
expectedChannelInfoPT := channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 1,
|
|
}
|
|
|
|
By("joining orderer1 and orderer2 to the channel with a genesis block")
|
|
for _, o := range orderers {
|
|
By("joining " + o.Name + " to channel as a consenter")
|
|
channelparticipation.Join(network, o, "participation-trophy", genesisBlock, expectedChannelInfoPT)
|
|
channelInfo := channelparticipation.ListOne(network, o, "participation-trophy")
|
|
Expect(channelInfo).To(Equal(expectedChannelInfoPT))
|
|
}
|
|
|
|
submitPeerTxn(orderer1, peer, network, channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 2,
|
|
})
|
|
|
|
submitPeerTxn(orderer2, peer, network, channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 3,
|
|
})
|
|
|
|
By("submitting a channel config update")
|
|
channelConfig := nwo.GetConfig(network, peer, orderer1, "participation-trophy")
|
|
c := configtx.New(channelConfig)
|
|
err := c.Orderer().AddCapability("V1_1")
|
|
Expect(err).NotTo(HaveOccurred())
|
|
computeSignSubmitConfigUpdate(network, orderer1, peer, c, "participation-trophy")
|
|
|
|
currentBlockNumber := nwo.CurrentConfigBlockNumber(network, peer, orderer1, "participation-trophy")
|
|
Expect(currentBlockNumber).To(BeNumerically(">", 1))
|
|
|
|
By("getting the updated config block")
|
|
configBlock = nwo.GetConfigBlock(network, peer, orderer2, "participation-trophy")
|
|
})
|
|
|
|
It("joins the channel as a follower using a config block", func() {
|
|
By("starting third orderer")
|
|
startOrderer(orderer3)
|
|
cl := channelparticipation.List(network, orderer3)
|
|
Expect(cl).To(Equal(channelparticipation.ChannelList{}))
|
|
|
|
By("joining orderer3 to the channel as a follower")
|
|
// make sure we can join using a config block from one of the other orderers
|
|
expectedChannelInfoPTFollower := channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "onboarding",
|
|
ConsensusRelation: "follower",
|
|
Height: 0,
|
|
}
|
|
channelparticipation.Join(network, orderer3, "participation-trophy", configBlock, expectedChannelInfoPTFollower)
|
|
|
|
By("ensuring orderer3 completes onboarding successfully")
|
|
expectedChannelInfoPTFollower.Status = "active"
|
|
expectedChannelInfoPTFollower.Height = 4
|
|
Eventually(func() channelparticipation.ChannelInfo {
|
|
return channelparticipation.ListOne(network, orderer3, "participation-trophy")
|
|
}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPTFollower))
|
|
|
|
By("adding orderer3 to the consenters set")
|
|
channelConfig := nwo.GetConfig(network, peer, orderer1, "participation-trophy")
|
|
c := configtx.New(channelConfig)
|
|
err := c.Orderer().AddConsenter(consenterChannelConfig(network, orderer3))
|
|
Expect(err).NotTo(HaveOccurred())
|
|
computeSignSubmitConfigUpdate(network, orderer1, peer, c, "participation-trophy")
|
|
|
|
By("ensuring orderer3 transitions from follower to consenter")
|
|
// config update above added a block
|
|
expectedChannelInfoPT := channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 5,
|
|
}
|
|
Eventually(func() channelparticipation.ChannelInfo {
|
|
return channelparticipation.ListOne(network, orderer3, "participation-trophy")
|
|
}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPT))
|
|
|
|
submitPeerTxn(orderer3, peer, network, channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 6,
|
|
})
|
|
})
|
|
|
|
It("recovers from a crash after the join block is written to the pendingops file repo", func() {
|
|
By("simulating the filesystem state at crash")
|
|
joinBlockFileRepoPath := filepath.Join(network.OrdererDir(orderer3), "system", "pendingops", "join")
|
|
err := os.MkdirAll(joinBlockFileRepoPath, 0o755)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
blockPath := filepath.Join(joinBlockFileRepoPath, "participation-trophy.join")
|
|
configBlockBytes, err := proto.Marshal(configBlock)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
err = ioutil.WriteFile(blockPath, configBlockBytes, 0o600)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
By("starting third orderer")
|
|
startOrderer(orderer3)
|
|
|
|
By("ensuring orderer3 completes onboarding successfully")
|
|
expectedChannelInfoPTFollower := channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "follower",
|
|
Height: 4,
|
|
}
|
|
Eventually(func() channelparticipation.ChannelInfo {
|
|
return channelparticipation.ListOne(network, orderer3, "participation-trophy")
|
|
}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPTFollower))
|
|
})
|
|
|
|
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() {
|
|
By("simulating the filesystem state at crash")
|
|
joinBlockFileRepoPath := filepath.Join(network.OrdererDir(orderer3), "system", "pendingops", "join")
|
|
err := os.MkdirAll(joinBlockFileRepoPath, 0o755)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
blockPath := filepath.Join(joinBlockFileRepoPath, "participation-trophy.join")
|
|
configBlockBytes, err := proto.Marshal(configBlock)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
err = ioutil.WriteFile(blockPath, configBlockBytes, 0o600)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
// create the ledger directory
|
|
ledgerPath := filepath.Join(network.OrdererDir(orderer3), "system", "chains", "participation-trophy")
|
|
err = os.MkdirAll(ledgerPath, 0o755)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
By("starting third orderer")
|
|
startOrderer(orderer3)
|
|
|
|
By("ensuring orderer3 completes onboarding successfully")
|
|
expectedChannelInfoPTFollower := channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "follower",
|
|
Height: 4,
|
|
}
|
|
Eventually(func() channelparticipation.ChannelInfo {
|
|
return channelparticipation.ListOne(network, orderer3, "participation-trophy")
|
|
}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPTFollower))
|
|
})
|
|
|
|
It("recovers from a crash after the join block is written to the pendingops file repo and the ledger has been created", func() {
|
|
By("simulating the filesystem state at crash")
|
|
joinBlockFileRepoPath := filepath.Join(network.OrdererDir(orderer3), "system", "pendingops", "join")
|
|
err := os.MkdirAll(joinBlockFileRepoPath, 0o755)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
blockPath := filepath.Join(joinBlockFileRepoPath, "participation-trophy.join")
|
|
configBlockBytes, err := proto.Marshal(configBlock)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
err = ioutil.WriteFile(blockPath, configBlockBytes, 0o600)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
// create the ledger and add the genesis block
|
|
ledgerDir := filepath.Join(network.OrdererDir(orderer3), "system")
|
|
lf, err := fileledger.New(ledgerDir, &disabled.Provider{})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
ledger, err := lf.GetOrCreate("participation-trophy")
|
|
Expect(err).NotTo(HaveOccurred())
|
|
err = ledger.Append(genesisBlock)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
lf.Close()
|
|
|
|
By("starting third orderer")
|
|
startOrderer(orderer3)
|
|
|
|
By("ensuring orderer3 completes onboarding successfully")
|
|
expectedChannelInfoPTFollower := channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "follower",
|
|
Height: 4,
|
|
}
|
|
Eventually(func() channelparticipation.ChannelInfo {
|
|
return channelparticipation.ListOne(network, orderer3, "participation-trophy")
|
|
}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPTFollower))
|
|
|
|
By("killing orderer3")
|
|
ordererProcesses[2].Signal(syscall.SIGKILL)
|
|
Eventually(ordererProcesses[2].Wait(), network.EventuallyTimeout).Should(Receive(MatchError("exit status 137")))
|
|
|
|
By("submitting transactions while orderer3 is down")
|
|
submitPeerTxn(orderer1, peer, network, channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 5,
|
|
})
|
|
|
|
submitPeerTxn(orderer2, peer, network, channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "consenter",
|
|
Height: 6,
|
|
})
|
|
|
|
By("restarting orderer3 (follower) and ensuring it catches up to the blocks it missed")
|
|
ordererRunner := network.OrdererRunner(orderer3)
|
|
ordererProcess := ifrit.Invoke(ordererRunner)
|
|
Eventually(ordererProcess.Ready(), network.EventuallyTimeout).Should(BeClosed())
|
|
ordererProcesses[2] = ordererProcess
|
|
ordererRunners[2] = ordererRunner
|
|
expectedChannelInfoPTFollower = channelparticipation.ChannelInfo{
|
|
Name: "participation-trophy",
|
|
URL: "/participation/v1/channels/participation-trophy",
|
|
Status: "active",
|
|
ConsensusRelation: "follower",
|
|
Height: 6,
|
|
}
|
|
Eventually(func() channelparticipation.ChannelInfo {
|
|
return channelparticipation.ListOne(network, orderer3, "participation-trophy")
|
|
}, network.EventuallyTimeout).Should(Equal(expectedChannelInfoPTFollower))
|
|
})
|
|
})
|
|
|
|
PIt("removing a channel clears the consensus data and the ledger", func() {
|
|
By("start 3 nodes")
|
|
By("join 3 nodes to a 3-node channel")
|
|
By("remove channel from those 3 nodes")
|
|
By("join node1 to a 1-node channel with the same name")
|
|
// TODO See: https://github.com/hyperledger/fabric/issues/4016
|
|
})
|
|
|
|
It("requires a client certificate to connect when TLS is enabled", func() {
|
|
orderer := network.Orderer("orderer1")
|
|
_, unauthClient := nwo.OrdererOperationalClients(network, orderer)
|
|
ordererAddress := fmt.Sprintf("127.0.0.1:%d", network.OrdererPort(orderer, nwo.AdminPort))
|
|
listChannelsURL := fmt.Sprintf("https://%s/participation/v1/channels", ordererAddress)
|
|
|
|
_, err := unauthClient.Get(listChannelsURL)
|
|
Expect(err).To(MatchError(fmt.Sprintf("Get \"%s\": dial tcp %s: connect: connection refused", listChannelsURL, ordererAddress)))
|
|
})
|
|
})
|
|
})
|
|
|
|
// submit a transaction signed by the peer and ensure it was
|
|
// committed to the ledger
|
|
func submitPeerTxn(o *nwo.Orderer, peer *nwo.Peer, n *nwo.Network, expectedChannelInfo channelparticipation.ChannelInfo) {
|
|
env := CreateBroadcastEnvelope(n, peer, expectedChannelInfo.Name, []byte("hello"))
|
|
submitTxn(o, env, n, expectedChannelInfo)
|
|
}
|
|
|
|
// submit a transaction signed by the orderer and ensure it is
|
|
// committed to the ledger
|
|
func submitOrdererTxn(o *nwo.Orderer, n *nwo.Network, expectedChannelInfo channelparticipation.ChannelInfo) {
|
|
env := CreateBroadcastEnvelope(n, o, expectedChannelInfo.Name, []byte("hello"))
|
|
submitTxn(o, env, n, expectedChannelInfo)
|
|
}
|
|
|
|
// submit the envelope to the orderer and ensure it is committed
|
|
// to the ledger
|
|
func submitTxn(o *nwo.Orderer, env *common.Envelope, n *nwo.Network, expectedChannelInfo channelparticipation.ChannelInfo) {
|
|
By("submitting a transaction to " + o.Name)
|
|
Eventually(broadcastTransactionFunc(n, o, env), n.EventuallyTimeout, time.Second).Should(Equal(common.Status_SUCCESS))
|
|
|
|
By("checking the channel info on " + o.Name)
|
|
Eventually(func() channelparticipation.ChannelInfo {
|
|
return channelparticipation.ListOne(n, o, expectedChannelInfo.Name)
|
|
}, n.EventuallyTimeout).Should(Equal(expectedChannelInfo))
|
|
}
|
|
|
|
func applicationChannelGenesisBlock(n *nwo.Network, orderers []*nwo.Orderer, peers []*nwo.Peer, channel string) *common.Block {
|
|
ordererOrgs, consenters := ordererOrganizationsAndConsenters(n, orderers)
|
|
peerOrgs := peerOrganizations(n, peers)
|
|
|
|
channelConfig := configtx.Channel{
|
|
Orderer: configtx.Orderer{
|
|
OrdererType: "etcdraft",
|
|
Organizations: ordererOrgs,
|
|
EtcdRaft: orderer.EtcdRaft{
|
|
Consenters: consenters,
|
|
Options: orderer.EtcdRaftOptions{
|
|
TickInterval: "500ms",
|
|
ElectionTick: 10,
|
|
HeartbeatTick: 1,
|
|
MaxInflightBlocks: 5,
|
|
SnapshotIntervalSize: 16 * 1024 * 1024, // 16 MB
|
|
},
|
|
},
|
|
Policies: map[string]configtx.Policy{
|
|
"Readers": {
|
|
Type: "ImplicitMeta",
|
|
Rule: "ANY Readers",
|
|
},
|
|
"Writers": {
|
|
Type: "ImplicitMeta",
|
|
Rule: "ANY Writers",
|
|
},
|
|
"Admins": {
|
|
Type: "ImplicitMeta",
|
|
Rule: "MAJORITY Admins",
|
|
},
|
|
"BlockValidation": {
|
|
Type: "ImplicitMeta",
|
|
Rule: "ANY Writers",
|
|
},
|
|
},
|
|
Capabilities: []string{"V2_0"},
|
|
BatchSize: orderer.BatchSize{
|
|
MaxMessageCount: 100,
|
|
AbsoluteMaxBytes: 1024 * 1024,
|
|
PreferredMaxBytes: 512 * 1024,
|
|
},
|
|
BatchTimeout: 2 * time.Second,
|
|
State: "STATE_NORMAL",
|
|
},
|
|
Application: configtx.Application{
|
|
Organizations: peerOrgs,
|
|
Capabilities: []string{"V2_0"},
|
|
Policies: map[string]configtx.Policy{
|
|
"Readers": {
|
|
Type: "ImplicitMeta",
|
|
Rule: "ANY Readers",
|
|
},
|
|
"Writers": {
|
|
Type: "ImplicitMeta",
|
|
Rule: "ANY Writers",
|
|
},
|
|
"Admins": {
|
|
Type: "ImplicitMeta",
|
|
Rule: "MAJORITY Admins",
|
|
},
|
|
"Endorsement": {
|
|
Type: "ImplicitMeta",
|
|
Rule: "MAJORITY Endorsement",
|
|
},
|
|
"LifecycleEndorsement": {
|
|
Type: "ImplicitMeta",
|
|
Rule: "MAJORITY Endorsement",
|
|
},
|
|
},
|
|
},
|
|
Capabilities: []string{"V2_0"},
|
|
Policies: map[string]configtx.Policy{
|
|
"Readers": {
|
|
Type: "ImplicitMeta",
|
|
Rule: "ANY Readers",
|
|
},
|
|
"Writers": {
|
|
Type: "ImplicitMeta",
|
|
Rule: "ANY Writers",
|
|
},
|
|
"Admins": {
|
|
Type: "ImplicitMeta",
|
|
Rule: "MAJORITY Admins",
|
|
},
|
|
},
|
|
}
|
|
|
|
genesisBlock, err := configtx.NewApplicationChannelGenesisBlock(channelConfig, channel)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
return genesisBlock
|
|
}
|
|
|
|
// parseCertificate loads the PEM-encoded x509 certificate at the specified
|
|
// path.
|
|
func parseCertificate(path string) *x509.Certificate {
|
|
certBytes, err := ioutil.ReadFile(path)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
pemBlock, _ := pem.Decode(certBytes)
|
|
cert, err := x509.ParseCertificate(pemBlock.Bytes)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
return cert
|
|
}
|
|
|
|
// parsePrivateKey loads the PEM-encoded private key at the specified path.
|
|
func parsePrivateKey(path string) crypto.PrivateKey {
|
|
pkBytes, err := ioutil.ReadFile(path)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
pemBlock, _ := pem.Decode(pkBytes)
|
|
privateKey, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
return privateKey
|
|
}
|
|
|
|
func ordererOrganizationsAndConsenters(n *nwo.Network, orderers []*nwo.Orderer) ([]configtx.Organization, []orderer.Consenter) {
|
|
ordererOrgsMap := map[string]*configtx.Organization{}
|
|
consenters := make([]orderer.Consenter, len(orderers))
|
|
|
|
for i, o := range orderers {
|
|
rootCert := parseCertificate(n.OrdererCACert(o))
|
|
adminCert := parseCertificate(n.OrdererUserCert(o, "Admin"))
|
|
tlsRootCert := parseCertificate(filepath.Join(n.OrdererLocalTLSDir(o), "ca.crt"))
|
|
|
|
orgConfig, ok := ordererOrgsMap[o.Organization]
|
|
if !ok {
|
|
orgConfig := configtxOrganization(n.Organization(o.Organization), rootCert, adminCert, tlsRootCert)
|
|
orgConfig.OrdererEndpoints = []string{
|
|
n.OrdererAddress(o, nwo.ListenPort),
|
|
}
|
|
ordererOrgsMap[o.Organization] = &orgConfig
|
|
} else {
|
|
orgConfig.OrdererEndpoints = append(orgConfig.OrdererEndpoints, n.OrdererAddress(o, nwo.ListenPort))
|
|
orgConfig.MSP.RootCerts = append(orgConfig.MSP.RootCerts, rootCert)
|
|
orgConfig.MSP.Admins = append(orgConfig.MSP.Admins, adminCert)
|
|
orgConfig.MSP.TLSRootCerts = append(orgConfig.MSP.TLSRootCerts, tlsRootCert)
|
|
}
|
|
|
|
consenters[i] = consenterChannelConfig(n, o)
|
|
}
|
|
|
|
ordererOrgs := []configtx.Organization{}
|
|
for _, o := range ordererOrgsMap {
|
|
ordererOrgs = append(ordererOrgs, *o)
|
|
}
|
|
|
|
return ordererOrgs, consenters
|
|
}
|
|
|
|
// constructs the peer organizations for a config block. It should be passed
|
|
// only one peer per organization.
|
|
func peerOrganizations(n *nwo.Network, peers []*nwo.Peer) []configtx.Organization {
|
|
peerOrgs := make([]configtx.Organization, len(peers))
|
|
for i, p := range peers {
|
|
rootCert := parseCertificate(n.PeerCACert(p))
|
|
adminCert := parseCertificate(n.PeerUserCert(p, "Admin"))
|
|
tlsRootCert := parseCertificate(filepath.Join(n.PeerLocalTLSDir(p), "ca.crt"))
|
|
|
|
peerOrgs[i] = configtxOrganization(n.Organization(p.Organization), rootCert, adminCert, tlsRootCert)
|
|
}
|
|
|
|
return peerOrgs
|
|
}
|
|
|
|
func configtxOrganization(org *nwo.Organization, rootCert, adminCert, tlsRootCert *x509.Certificate) configtx.Organization {
|
|
return configtx.Organization{
|
|
Name: org.Name,
|
|
Policies: map[string]configtx.Policy{
|
|
"Readers": {
|
|
Type: "Signature",
|
|
Rule: fmt.Sprintf("OR('%s.member')", org.MSPID),
|
|
},
|
|
"Writers": {
|
|
Type: "Signature",
|
|
Rule: fmt.Sprintf("OR('%s.member')", org.MSPID),
|
|
},
|
|
"Admins": {
|
|
Type: "Signature",
|
|
Rule: fmt.Sprintf("OR('%s.admin')", org.MSPID),
|
|
},
|
|
},
|
|
MSP: configtx.MSP{
|
|
Name: org.MSPID,
|
|
RootCerts: []*x509.Certificate{rootCert},
|
|
Admins: []*x509.Certificate{adminCert},
|
|
TLSRootCerts: []*x509.Certificate{tlsRootCert},
|
|
},
|
|
}
|
|
}
|
|
|
|
func computeSignSubmitConfigUpdate(n *nwo.Network, o *nwo.Orderer, p *nwo.Peer, c configtx.ConfigTx, channel string) {
|
|
configUpdate, err := c.ComputeMarshaledUpdate(channel)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
signingIdentity := configtx.SigningIdentity{
|
|
Certificate: parseCertificate(n.OrdererUserCert(o, "Admin")),
|
|
PrivateKey: parsePrivateKey(n.OrdererUserKey(o, "Admin")),
|
|
MSPID: n.Organization(o.Organization).MSPID,
|
|
}
|
|
signature, err := signingIdentity.CreateConfigSignature(configUpdate)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
configUpdateEnvelope, err := configtx.NewEnvelope(configUpdate, signature)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
err = signingIdentity.SignEnvelope(configUpdateEnvelope)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
currentBlockNumber := nwo.CurrentConfigBlockNumber(n, p, o, channel)
|
|
|
|
Eventually(broadcastTransactionFunc(n, o, configUpdateEnvelope), n.EventuallyTimeout).Should(Equal(common.Status_SUCCESS))
|
|
|
|
ccb := func() uint64 { return nwo.CurrentConfigBlockNumber(n, p, o, channel) }
|
|
Eventually(ccb, n.EventuallyTimeout).Should(BeNumerically(">", currentBlockNumber))
|
|
}
|
|
|
|
func broadcastTransactionFunc(n *nwo.Network, o *nwo.Orderer, env *common.Envelope) func() common.Status {
|
|
return func() common.Status {
|
|
resp, err := ordererclient.Broadcast(n, o, env)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
return resp.Status
|
|
}
|
|
}
|
|
|
|
func consenterChannelConfig(n *nwo.Network, o *nwo.Orderer) orderer.Consenter {
|
|
host, port := conftx.OrdererClusterHostPort(n, o)
|
|
tlsCert := parseCertificate(filepath.Join(n.OrdererLocalTLSDir(o), "server.crt"))
|
|
return orderer.Consenter{
|
|
Address: orderer.EtcdAddress{
|
|
Host: host,
|
|
Port: port,
|
|
},
|
|
ClientTLSCert: tlsCert,
|
|
ServerTLSCert: tlsCert,
|
|
}
|
|
}
|
|
|
|
type errorResponse struct {
|
|
Error string `json:"error"`
|
|
}
|
|
|
|
func channelparticipationJoinFailure(n *nwo.Network, o *nwo.Orderer, channel string, block *common.Block, expectedStatus int, expectedError string) {
|
|
blockBytes, err := proto.Marshal(block)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
protocol := "http"
|
|
if n.TLSEnabled {
|
|
protocol = "https"
|
|
}
|
|
url := fmt.Sprintf("%s://127.0.0.1:%d/participation/v1/channels", protocol, n.OrdererPort(o, nwo.AdminPort))
|
|
req := channelparticipation.GenerateJoinRequest(url, channel, blockBytes)
|
|
authClient, _ := nwo.OrdererOperationalClients(n, o)
|
|
|
|
doBodyFailure(authClient, req, expectedStatus, expectedError)
|
|
}
|
|
|
|
func channelparticipationJoinConnectFailure(n *nwo.Network, o *nwo.Orderer, channel string, block *common.Block, expectedError string) {
|
|
blockBytes, err := proto.Marshal(block)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
protocol := "http"
|
|
if n.TLSEnabled {
|
|
protocol = "https"
|
|
}
|
|
url := fmt.Sprintf("%s://127.0.0.1:%d/participation/v1/channels", protocol, n.OrdererPort(o, nwo.AdminPort))
|
|
|
|
req := channelparticipation.GenerateJoinRequest(url, channel, blockBytes)
|
|
authClient, _ := nwo.OrdererOperationalClients(n, o)
|
|
|
|
_, err = authClient.Do(req)
|
|
Expect(err).To(HaveOccurred())
|
|
Expect(err.Error()).To(ContainSubstring(expectedError))
|
|
}
|
|
|
|
func doBodyFailure(client *http.Client, req *http.Request, expectedStatus int, expectedError string) {
|
|
resp, err := client.Do(req)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(resp.StatusCode).To(Equal(expectedStatus))
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
resp.Body.Close()
|
|
|
|
errorResponse := &errorResponse{}
|
|
err = json.Unmarshal(body, errorResponse)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(errorResponse.Error).To(Equal(expectedError))
|
|
}
|
|
|
|
func channelparticipationRemoveFailure(n *nwo.Network, o *nwo.Orderer, channel string, expectedStatus int, expectedError string) {
|
|
authClient, _ := nwo.OrdererOperationalClients(n, o)
|
|
url := fmt.Sprintf("https://127.0.0.1:%d/participation/v1/channels/%s", n.OrdererPort(o, nwo.AdminPort), channel)
|
|
|
|
req, err := http.NewRequest(http.MethodDelete, url, nil)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
doBodyFailure(authClient, req, expectedStatus, expectedError)
|
|
}
|
|
|
|
func multiNodeEtcdRaftTwoChannels() *nwo.Config {
|
|
config := nwo.MultiNodeEtcdRaft()
|
|
config.Channels = []*nwo.Channel{
|
|
{Name: "testchannel", Profile: "TwoOrgsAppChannelEtcdRaft"},
|
|
{Name: "testchannel2", Profile: "TwoOrgsAppChannelEtcdRaft"},
|
|
}
|
|
|
|
for _, peer := range config.Peers {
|
|
peer.Channels = []*nwo.PeerChannel{
|
|
{Name: "testchannel", Anchor: true},
|
|
{Name: "testchannel2", Anchor: true},
|
|
}
|
|
}
|
|
|
|
return config
|
|
}
|
|
|
|
func createJoinBlockDefineSystemChannel(channelID string) *common.Block {
|
|
block := protoutil.NewBlock(0, []byte{})
|
|
block.Data = &common.BlockData{
|
|
Data: [][]byte{
|
|
protoutil.MarshalOrPanic(&common.Envelope{
|
|
Payload: protoutil.MarshalOrPanic(&common.Payload{
|
|
Data: protoutil.MarshalOrPanic(&common.ConfigEnvelope{
|
|
Config: &common.Config{
|
|
ChannelGroup: &common.ConfigGroup{
|
|
Groups: map[string]*common.ConfigGroup{
|
|
"Consortiums": {},
|
|
},
|
|
Values: map[string]*common.ConfigValue{
|
|
"HashingAlgorithm": {
|
|
Value: protoutil.MarshalOrPanic(&common.HashingAlgorithm{
|
|
Name: bccsp.SHA256,
|
|
}),
|
|
},
|
|
"BlockDataHashingStructure": {
|
|
Value: protoutil.MarshalOrPanic(&common.BlockDataHashingStructure{
|
|
Width: math.MaxUint32,
|
|
}),
|
|
},
|
|
"OrdererAddresses": {
|
|
Value: protoutil.MarshalOrPanic(&common.OrdererAddresses{
|
|
Addresses: []string{"localhost"},
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
Header: &common.Header{
|
|
ChannelHeader: protoutil.MarshalOrPanic(&common.ChannelHeader{
|
|
Type: int32(common.HeaderType_CONFIG),
|
|
ChannelId: channelID,
|
|
}),
|
|
},
|
|
}),
|
|
}),
|
|
},
|
|
}
|
|
block.Header.DataHash = protoutil.BlockDataHash(block.Data)
|
|
protoutil.InitBlockMetadata(block)
|
|
|
|
return block
|
|
}
|