Skip to content

Commit db9c574

Browse files
committed
[FAB-12015] Add Raft confing update validation
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>
1 parent fddc27c commit db9c574

File tree

3 files changed

+127
-7
lines changed

3 files changed

+127
-7
lines changed

integration/e2e/e2e_test.go

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,15 @@ import (
1414
"syscall"
1515
"time"
1616

17-
docker "github.com/fsouza/go-dockerclient"
17+
"github.com/fsouza/go-dockerclient"
18+
"github.com/golang/protobuf/proto"
1819
"github.com/hyperledger/fabric/core/aclmgmt/resources"
1920
"github.com/hyperledger/fabric/integration/nwo"
2021
"github.com/hyperledger/fabric/integration/nwo/commands"
22+
"github.com/hyperledger/fabric/protos/common"
23+
orderer2 "github.com/hyperledger/fabric/protos/orderer"
24+
"github.com/hyperledger/fabric/protos/orderer/etcdraft"
25+
"github.com/hyperledger/fabric/protos/utils"
2126
. "github.com/onsi/ginkgo"
2227
. "github.com/onsi/gomega"
2328
"github.com/onsi/gomega/gbytes"
@@ -209,6 +214,56 @@ var _ = Describe("EndToEnd", func() {
209214
Eventually(sess.Err, network.EventuallyTimeout).Should(gbytes.Say(`\Qdeliver completed with status (FORBIDDEN)\E`))
210215
})
211216
})
217+
218+
Describe("etcd raft, checking valid configuration update of type B", func() {
219+
BeforeEach(func() {
220+
network = nwo.New(nwo.BasicEtcdRaft(), testDir, client, 32000, components)
221+
network.GenerateConfigTree()
222+
network.Bootstrap()
223+
224+
networkRunner := network.NetworkGroupRunner()
225+
process = ifrit.Invoke(networkRunner)
226+
Eventually(process.Ready()).Should(BeClosed())
227+
})
228+
229+
It("executes a basic etcdraft network with a single Raft node", func() {
230+
orderer := network.Orderer("orderer")
231+
peer := network.Peer("Org1", "peer1")
232+
233+
channel := "testchannel"
234+
network.CreateAndJoinChannel(orderer, channel)
235+
nwo.DeployChaincode(network, "testchannel", orderer, chaincode)
236+
RunQueryInvokeQuery(network, orderer, peer)
237+
238+
config := nwo.GetConfigBlock(network, peer, orderer, channel)
239+
updatedConfig := proto.Clone(config).(*common.Config)
240+
241+
consensusTypeConfigValue := updatedConfig.ChannelGroup.Groups["Orderer"].Values["ConsensusType"]
242+
consensusTypeValue := &orderer2.ConsensusType{}
243+
err := proto.Unmarshal(consensusTypeConfigValue.Value, consensusTypeValue)
244+
Expect(err).NotTo(HaveOccurred())
245+
246+
metadata := &etcdraft.Metadata{}
247+
err = proto.Unmarshal(consensusTypeValue.Metadata, metadata)
248+
Expect(err).NotTo(HaveOccurred())
249+
250+
// update max in flight messages
251+
metadata.Options.MaxInflightMsgs = 1000
252+
metadata.Options.MaxSizePerMsg = 512
253+
254+
// write metadata back
255+
consensusTypeValue.Metadata, err = proto.Marshal(metadata)
256+
Expect(err).NotTo(HaveOccurred())
257+
258+
updatedConfig.ChannelGroup.Groups["Orderer"].Values["ConsensusType"] = &common.ConfigValue{
259+
ModPolicy: "Admins",
260+
Value: utils.MarshalOrPanic(consensusTypeValue),
261+
}
262+
263+
nwo.UpdateOrdererConfig(network, orderer, channel, config, updatedConfig, peer, orderer)
264+
})
265+
})
266+
212267
})
213268

214269
func RunQueryInvokeQuery(n *nwo.Network, orderer *nwo.Orderer, peer *nwo.Peer) {

orderer/consensus/etcdraft/chain.go

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"context"
1111
"encoding/pem"
1212
"fmt"
13+
"reflect"
1314
"sync/atomic"
1415
"time"
1516

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

199-
// TODO Consider the read-set when processing type B configuration transactions
200200
// Check that only the ConsensusType is updated in the write-set
201201
if ordererConfigGroup, ok := configUpdate.WriteSet.Groups["Orderer"]; ok {
202-
if _, ok := ordererConfigGroup.Values["ConsensusType"]; ok {
203-
return errors.Errorf("updates to ConsensusType not supported currently")
202+
if val, ok := ordererConfigGroup.Values["ConsensusType"]; ok {
203+
return c.checkConsentersSet(val)
204204
}
205205
}
206206
return nil
@@ -603,3 +603,45 @@ func (c *Chain) pemToDER(pemBytes []byte, id uint64, certType string) ([]byte, e
603603
}
604604
return bl.Bytes, nil
605605
}
606+
607+
func (c *Chain) checkConsentersSet(configValue *common.ConfigValue) error {
608+
consensusTypeValue := &orderer.ConsensusType{}
609+
if err := proto.Unmarshal(configValue.Value, consensusTypeValue); err != nil {
610+
return errors.Wrap(err, "failed to unmarshal consensusType config update")
611+
}
612+
613+
updatedMetadata := &etcdraft.Metadata{}
614+
if err := proto.Unmarshal(consensusTypeValue.Metadata, updatedMetadata); err != nil {
615+
return errors.Wrap(err, "failed to unmarshal updated (new) etcdraft metadata configuration")
616+
}
617+
618+
currentMetadata := &etcdraft.Metadata{}
619+
if err := proto.Unmarshal(c.support.SharedConfig().ConsensusMetadata(), currentMetadata); err != nil {
620+
return errors.Wrap(err, "failed to unmarshal current etcdraft metadata configuration")
621+
}
622+
623+
if !c.consentersSetEqual(currentMetadata.Consenters, updatedMetadata.Consenters) {
624+
return errors.New("update of consenters set is not supported yet")
625+
}
626+
627+
return nil
628+
}
629+
630+
func (c *Chain) consentersSetEqual(c1 []*etcdraft.Consenter, c2 []*etcdraft.Consenter) bool {
631+
if len(c1) != len(c2) {
632+
return false
633+
}
634+
635+
consentersSet1 := c.consentersToMap(c1)
636+
consentersSet2 := c.consentersToMap(c2)
637+
638+
return reflect.DeepEqual(consentersSet1, consentersSet2)
639+
}
640+
641+
func (c *Chain) consentersToMap(c1 []*etcdraft.Consenter) map[string]*etcdraft.Consenter {
642+
set := map[string]*etcdraft.Consenter{}
643+
for _, c := range c1 {
644+
set[string(c.ClientTlsCert)] = c
645+
}
646+
return set
647+
}

orderer/consensus/etcdraft/chain_test.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ var _ = Describe("Chain", func() {
494494
"ConsensusType": {
495495
Version: 1,
496496
Value: marshalOrPanic(&orderer.ConsensusType{
497-
Metadata: []byte("new consenter"),
497+
Metadata: marshalOrPanic(consenterMetadata),
498498
}),
499499
},
500500
}
@@ -504,9 +504,32 @@ var _ = Describe("Chain", func() {
504504
configSeq = 0
505505
}) // BeforeEach block
506506

507-
It("should throw an error", func() {
507+
It("should be able to process config update of type B", func() {
508508
err := chain.Configure(configEnv, configSeq)
509-
Expect(err).To(MatchError("updates to ConsensusType not supported currently"))
509+
Expect(err).NotTo(HaveOccurred())
510+
})
511+
})
512+
513+
Context("updating consenters set", func() {
514+
// use to prepare the Orderer Values
515+
BeforeEach(func() {
516+
values := map[string]*common.ConfigValue{
517+
"ConsensusType": {
518+
Version: 1,
519+
Value: marshalOrPanic(&orderer.ConsensusType{
520+
Metadata: marshalOrPanic(createMetadata(3, tlsCA)),
521+
}),
522+
},
523+
}
524+
configEnv = newConfigEnv(channelID,
525+
common.HeaderType_CONFIG,
526+
newConfigUpdateEnv(channelID, values))
527+
configSeq = 0
528+
}) // BeforeEach block
529+
530+
It("should fail, since consenters set change is not supported yet", func() {
531+
err := chain.Configure(configEnv, configSeq)
532+
Expect(err).To(MatchError("update of consenters set is not supported yet"))
510533
})
511534
})
512535
})

0 commit comments

Comments
 (0)