From 3f92a9f58af33a2cdd59eb47d58cf7b52e45c2c9 Mon Sep 17 00:00:00 2001 From: David VIEJO Date: Wed, 11 Oct 2023 17:30:32 +0200 Subject: [PATCH] Add support for ConsensusTypeBFT in the orderer.go Signed-off-by: David VIEJO --- configtx/orderer.go | 92 +++++++++++++++++++++++++++++++++++++ configtx/orderer/orderer.go | 3 ++ 2 files changed, 95 insertions(+) diff --git a/configtx/orderer.go b/configtx/orderer.go index 650e91e..885e907 100644 --- a/configtx/orderer.go +++ b/configtx/orderer.go @@ -16,10 +16,14 @@ import ( "time" "github.com/golang/protobuf/proto" + "github.com/hyperledger/fabric-config/configtx/internal/policydsl" "github.com/hyperledger/fabric-config/configtx/orderer" + "github.com/hyperledger/fabric-protos-go/common" cb "github.com/hyperledger/fabric-protos-go/common" + mspa "github.com/hyperledger/fabric-protos-go/msp" ob "github.com/hyperledger/fabric-protos-go/orderer" eb "github.com/hyperledger/fabric-protos-go/orderer/etcdraft" + sb "github.com/hyperledger/fabric-protos-go/orderer/smartbft" ) const ( @@ -38,6 +42,9 @@ type Orderer struct { Kafka orderer.Kafka EtcdRaft orderer.EtcdRaft Organizations []Organization + + SmartBFT *sb.Options + ConsenterMapping []common.Consenter // MaxChannels is the maximum count of channels an orderer supports. MaxChannels uint64 // Capabilities is a map of the capabilities the orderer supports. @@ -821,6 +828,36 @@ func addOrdererValues(ordererGroup *cb.ConfigGroup, o Orderer) error { if consensusMetadata, err = marshalEtcdRaftMetadata(o.EtcdRaft); err != nil { return fmt.Errorf("marshaling etcdraft metadata for orderer type '%s': %v", orderer.ConsensusTypeEtcdRaft, err) } + case orderer.ConsensusTypeBFT: + consenterMapping := []*cb.Consenter{} + for _, consenter := range o.ConsenterMapping { + consenterMapping = append(consenterMapping, &cb.Consenter{ + Id: consenter.Id, + Host: consenter.Host, + Port: consenter.Port, + MspId: consenter.MspId, + Identity: consenter.Identity, + ClientTlsCert: consenter.ClientTlsCert, + ServerTlsCert: consenter.ServerTlsCert, + }) + } + consentersProto, err := proto.Marshal(&cb.Orderers{ + ConsenterMapping: consenterMapping, + }) + if err != nil { + return fmt.Errorf("marshaling consenters for orderer type '%s': %v", orderer.ConsensusTypeBFT, err) + } + + ordererGroup.Values["Orderers"] = &cb.ConfigValue{ + Value: consentersProto, + ModPolicy: "Admins", + } + // addValue(ordererGroup, channelconfig.OrderersValue(consenterProtos), channelconfig.AdminsPolicyKey) + if consensusMetadata, err = MarshalBFTOptions(o.SmartBFT); err != nil { + return fmt.Errorf("consenter options read failed with error %s for orderer type %s", err, orderer.ConsensusTypeBFT) + } + // Overwrite policy manually by computing it from the consenters + EncodeBFTBlockVerificationPolicy(o.ConsenterMapping, ordererGroup) default: return fmt.Errorf("unknown orderer type '%s'", o.OrdererType) } @@ -838,6 +875,15 @@ func addOrdererValues(ordererGroup *cb.ConfigGroup, o Orderer) error { return nil } +// MarshalBFTOptions serializes smartbft options. +func MarshalBFTOptions(op *sb.Options) ([]byte, error) { + if copyMd, ok := proto.Clone(op).(*sb.Options); ok { + return proto.Marshal(copyMd) + } else { + return nil, errors.New("consenter options type mismatch") + } +} + // setOrdererPolicies adds *cb.ConfigPolicies to the passed Orderer *cb.ConfigGroup's Policies map. // It checks that the BlockValidation policy is defined alongside the standard policy checks. func setOrdererPolicies(cg *cb.ConfigGroup, policyMap map[string]Policy, modPolicy string) error { @@ -1060,3 +1106,49 @@ func blockDataHashingStructureValue() *standardConfigValue { }, } } + +func EncodeBFTBlockVerificationPolicy(consenterProtos []common.Consenter, ordererGroup *cb.ConfigGroup) error { + n := len(consenterProtos) + f := (n - 1) / 3 + + var identities []*mspa.MSPPrincipal + var pols []*cb.SignaturePolicy + for i, consenter := range consenterProtos { + pols = append(pols, &cb.SignaturePolicy{ + Type: &cb.SignaturePolicy_SignedBy{ + SignedBy: int32(i), + }, + }) + principalBytes, err := proto.Marshal(&mspa.SerializedIdentity{Mspid: consenter.MspId, IdBytes: consenter.Identity}) + if err != nil { + return err + } + identities = append(identities, &mspa.MSPPrincipal{ + PrincipalClassification: mspa.MSPPrincipal_IDENTITY, + Principal: principalBytes, + }) + } + + quorumSize := ComputeBFTQuorum(n, f) + sp := &cb.SignaturePolicyEnvelope{ + Rule: policydsl.NOutOf(int32(quorumSize), pols), + Identities: identities, + } + policyValue, err := proto.Marshal(sp) + if err != nil { + return err + } + ordererGroup.Policies[BlockValidationPolicyKey] = &cb.ConfigPolicy{ + // Inherit modification policy + ModPolicy: ordererGroup.Policies[BlockValidationPolicyKey].ModPolicy, + Policy: &cb.Policy{ + Type: int32(cb.Policy_SIGNATURE), + Value: policyValue, + }, + } + return nil +} + +func ComputeBFTQuorum(totalNodes, faultyNodes int) int { + return int(math.Ceil(float64(totalNodes+faultyNodes+1) / 2)) +} diff --git a/configtx/orderer/orderer.go b/configtx/orderer/orderer.go index 5ece74c..252c3db 100644 --- a/configtx/orderer/orderer.go +++ b/configtx/orderer/orderer.go @@ -28,6 +28,9 @@ const ( // ConsensusTypeEtcdRaft identifies the Raft-based consensus implementation. ConsensusTypeEtcdRaft = "etcdraft" + // ConsensusTypeBFT identifies the SmartBFT-based consensus implementation. + ConsensusTypeBFT = "BFT" + // KafkaBrokersKey is the common.ConfigValue type key name for the KafkaBrokers message. // Deprecated: the kafka consensus type is no longer supported KafkaBrokersKey = "KafkaBrokers"