Skip to content

Commit

Permalink
[FAB-12015] Add Raft confing update validation
Browse files Browse the repository at this point in the history
This commit allows Raft transactions updates which changes algorithm
parameters, while no update or change of consenters set is supported
yet.

Change-Id: Iefefa130302c78a8e00de005ff3421d2a686eb38
Signed-off-by: Artem Barger <bartem@il.ibm.com>
  • Loading branch information
C0rWin committed Oct 18, 2018
1 parent fddc27c commit db9c574
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 7 deletions.
57 changes: 56 additions & 1 deletion integration/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ import (
"syscall"
"time"

docker "github.com/fsouza/go-dockerclient"
"github.com/fsouza/go-dockerclient"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/core/aclmgmt/resources"
"github.com/hyperledger/fabric/integration/nwo"
"github.com/hyperledger/fabric/integration/nwo/commands"
"github.com/hyperledger/fabric/protos/common"
orderer2 "github.com/hyperledger/fabric/protos/orderer"
"github.com/hyperledger/fabric/protos/orderer/etcdraft"
"github.com/hyperledger/fabric/protos/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gbytes"
Expand Down Expand Up @@ -209,6 +214,56 @@ var _ = Describe("EndToEnd", func() {
Eventually(sess.Err, network.EventuallyTimeout).Should(gbytes.Say(`\Qdeliver completed with status (FORBIDDEN)\E`))
})
})

Describe("etcd raft, checking valid configuration update of type B", func() {
BeforeEach(func() {
network = nwo.New(nwo.BasicEtcdRaft(), testDir, client, 32000, components)
network.GenerateConfigTree()
network.Bootstrap()

networkRunner := network.NetworkGroupRunner()
process = ifrit.Invoke(networkRunner)
Eventually(process.Ready()).Should(BeClosed())
})

It("executes a basic etcdraft network with a single Raft node", func() {
orderer := network.Orderer("orderer")
peer := network.Peer("Org1", "peer1")

channel := "testchannel"
network.CreateAndJoinChannel(orderer, channel)
nwo.DeployChaincode(network, "testchannel", orderer, chaincode)
RunQueryInvokeQuery(network, orderer, peer)

config := nwo.GetConfigBlock(network, peer, orderer, channel)
updatedConfig := proto.Clone(config).(*common.Config)

consensusTypeConfigValue := updatedConfig.ChannelGroup.Groups["Orderer"].Values["ConsensusType"]
consensusTypeValue := &orderer2.ConsensusType{}
err := proto.Unmarshal(consensusTypeConfigValue.Value, consensusTypeValue)
Expect(err).NotTo(HaveOccurred())

metadata := &etcdraft.Metadata{}
err = proto.Unmarshal(consensusTypeValue.Metadata, metadata)
Expect(err).NotTo(HaveOccurred())

// update max in flight messages
metadata.Options.MaxInflightMsgs = 1000
metadata.Options.MaxSizePerMsg = 512

// write metadata back
consensusTypeValue.Metadata, err = proto.Marshal(metadata)
Expect(err).NotTo(HaveOccurred())

updatedConfig.ChannelGroup.Groups["Orderer"].Values["ConsensusType"] = &common.ConfigValue{
ModPolicy: "Admins",
Value: utils.MarshalOrPanic(consensusTypeValue),
}

nwo.UpdateOrdererConfig(network, orderer, channel, config, updatedConfig, peer, orderer)
})
})

})

func RunQueryInvokeQuery(n *nwo.Network, orderer *nwo.Orderer, peer *nwo.Peer) {
Expand Down
48 changes: 45 additions & 3 deletions orderer/consensus/etcdraft/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"context"
"encoding/pem"
"fmt"
"reflect"
"sync/atomic"
"time"

Expand Down Expand Up @@ -196,11 +197,10 @@ func (c *Chain) checkConfigUpdateValidity(ctx *common.Envelope) error {
return err
}

// TODO Consider the read-set when processing type B configuration transactions
// Check that only the ConsensusType is updated in the write-set
if ordererConfigGroup, ok := configUpdate.WriteSet.Groups["Orderer"]; ok {
if _, ok := ordererConfigGroup.Values["ConsensusType"]; ok {
return errors.Errorf("updates to ConsensusType not supported currently")
if val, ok := ordererConfigGroup.Values["ConsensusType"]; ok {
return c.checkConsentersSet(val)
}
}
return nil
Expand Down Expand Up @@ -603,3 +603,45 @@ func (c *Chain) pemToDER(pemBytes []byte, id uint64, certType string) ([]byte, e
}
return bl.Bytes, nil
}

func (c *Chain) checkConsentersSet(configValue *common.ConfigValue) error {
consensusTypeValue := &orderer.ConsensusType{}
if err := proto.Unmarshal(configValue.Value, consensusTypeValue); err != nil {
return errors.Wrap(err, "failed to unmarshal consensusType config update")
}

updatedMetadata := &etcdraft.Metadata{}
if err := proto.Unmarshal(consensusTypeValue.Metadata, updatedMetadata); err != nil {
return errors.Wrap(err, "failed to unmarshal updated (new) etcdraft metadata configuration")
}

currentMetadata := &etcdraft.Metadata{}
if err := proto.Unmarshal(c.support.SharedConfig().ConsensusMetadata(), currentMetadata); err != nil {
return errors.Wrap(err, "failed to unmarshal current etcdraft metadata configuration")
}

if !c.consentersSetEqual(currentMetadata.Consenters, updatedMetadata.Consenters) {
return errors.New("update of consenters set is not supported yet")
}

return nil
}

func (c *Chain) consentersSetEqual(c1 []*etcdraft.Consenter, c2 []*etcdraft.Consenter) bool {
if len(c1) != len(c2) {
return false
}

consentersSet1 := c.consentersToMap(c1)
consentersSet2 := c.consentersToMap(c2)

return reflect.DeepEqual(consentersSet1, consentersSet2)
}

func (c *Chain) consentersToMap(c1 []*etcdraft.Consenter) map[string]*etcdraft.Consenter {
set := map[string]*etcdraft.Consenter{}
for _, c := range c1 {
set[string(c.ClientTlsCert)] = c
}
return set
}
29 changes: 26 additions & 3 deletions orderer/consensus/etcdraft/chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ var _ = Describe("Chain", func() {
"ConsensusType": {
Version: 1,
Value: marshalOrPanic(&orderer.ConsensusType{
Metadata: []byte("new consenter"),
Metadata: marshalOrPanic(consenterMetadata),
}),
},
}
Expand All @@ -504,9 +504,32 @@ var _ = Describe("Chain", func() {
configSeq = 0
}) // BeforeEach block

It("should throw an error", func() {
It("should be able to process config update of type B", func() {
err := chain.Configure(configEnv, configSeq)
Expect(err).To(MatchError("updates to ConsensusType not supported currently"))
Expect(err).NotTo(HaveOccurred())
})
})

Context("updating consenters set", func() {
// use to prepare the Orderer Values
BeforeEach(func() {
values := map[string]*common.ConfigValue{
"ConsensusType": {
Version: 1,
Value: marshalOrPanic(&orderer.ConsensusType{
Metadata: marshalOrPanic(createMetadata(3, tlsCA)),
}),
},
}
configEnv = newConfigEnv(channelID,
common.HeaderType_CONFIG,
newConfigUpdateEnv(channelID, values))
configSeq = 0
}) // BeforeEach block

It("should fail, since consenters set change is not supported yet", func() {
err := chain.Configure(configEnv, configSeq)
Expect(err).To(MatchError("update of consenters set is not supported yet"))
})
})
})
Expand Down

0 comments on commit db9c574

Please sign in to comment.