diff --git a/configtx/constants.go b/configtx/constants.go index 4addb35..ca585c6 100644 --- a/configtx/constants.go +++ b/configtx/constants.go @@ -68,6 +68,9 @@ const ( // OrdererGroupKey is the group name for the orderer config. OrdererGroupKey = "Orderer" + // OrderersGroupKey is the group name for the orderer config. + OrderersGroupKey = "Orderers" + // ApplicationGroupKey is the group name for the Application config. ApplicationGroupKey = "Application" diff --git a/configtx/orderer.go b/configtx/orderer.go index 650e91e..8e29999 100644 --- a/configtx/orderer.go +++ b/configtx/orderer.go @@ -16,10 +16,13 @@ import ( "time" "github.com/golang/protobuf/proto" + "github.com/hyperledger/fabric-config/configtx/internal/policydsl" "github.com/hyperledger/fabric-config/configtx/orderer" 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 +41,9 @@ type Orderer struct { Kafka orderer.Kafka EtcdRaft orderer.EtcdRaft Organizations []Organization + + SmartBFT *sb.Options + ConsenterMapping []cb.Consenter // MaxChannels is the maximum count of channels an orderer supports. MaxChannels uint64 // Capabilities is a map of the capabilities the orderer supports. @@ -101,6 +107,8 @@ func (o *OrdererGroup) Organization(name string) *OrdererOrg { func (o *OrdererGroup) Configuration() (Orderer, error) { // CONSENSUS TYPE, STATE, AND METADATA var etcdRaft orderer.EtcdRaft + var smartBFT *sb.Options + var consenterMapping []cb.Consenter kafkaBrokers := orderer.Kafka{} consensusTypeProto := &ob.ConsensusType{} @@ -132,6 +140,32 @@ func (o *OrdererGroup) Configuration() (Orderer, error) { if err != nil { return Orderer{}, fmt.Errorf("unmarshaling etcd raft metadata: %v", err) } + case orderer.ConsensusTypeBFT: + smartBFT, err = unmarshalSmartBFTOptions(consensusTypeProto.Metadata) + if err != nil { + return Orderer{}, fmt.Errorf("unmarshaling smart BFT options: %v", err) + } + + orderersConfigValue, ok := o.ordererGroup.Values[OrderersGroupKey] + if !ok { + return Orderer{}, errors.New("unable to find orderers for orderer org") + } + orderers := cb.Orderers{} + err = proto.Unmarshal(orderersConfigValue.Value, &orderers) + if err != nil { + return Orderer{}, fmt.Errorf("unmarshaling orderers: %v", err) + } + for _, consenterItem := range orderers.ConsenterMapping { + consenterMapping = append(consenterMapping, cb.Consenter{ + Id: consenterItem.Id, + Host: consenterItem.Host, + Port: consenterItem.Port, + MspId: consenterItem.MspId, + Identity: consenterItem.Identity, + ClientTlsCert: consenterItem.ClientTlsCert, + ServerTlsCert: consenterItem.ServerTlsCert, + }) + } default: return Orderer{}, fmt.Errorf("config contains unknown consensus type '%s'", consensusTypeProto.Type) } @@ -192,14 +226,16 @@ func (o *OrdererGroup) Configuration() (Orderer, error) { AbsoluteMaxBytes: batchSize.AbsoluteMaxBytes, PreferredMaxBytes: batchSize.PreferredMaxBytes, }, - Kafka: kafkaBrokers, - EtcdRaft: etcdRaft, - Organizations: ordererOrgs, - MaxChannels: channelRestrictions.MaxCount, - Capabilities: capabilities, - Policies: policies, - State: state, - ModPolicy: o.ordererGroup.GetModPolicy(), + Kafka: kafkaBrokers, + EtcdRaft: etcdRaft, + ConsenterMapping: consenterMapping, + SmartBFT: smartBFT, + Organizations: ordererOrgs, + MaxChannels: channelRestrictions.MaxCount, + Capabilities: capabilities, + Policies: policies, + State: state, + ModPolicy: o.ordererGroup.GetModPolicy(), }, nil } @@ -433,6 +469,20 @@ func (o *OrdererGroup) SetConfiguration(ord Orderer) error { return nil } +func (o *OrdererGroup) SetConsenterMapping(consenterMapping []*cb.Consenter) error { + val, err := proto.Marshal(&cb.Orderers{ + ConsenterMapping: consenterMapping, + }) + if err != nil { + return err + } + o.ordererGroup.Values[OrderersGroupKey] = &cb.ConfigValue{ + Value: val, + ModPolicy: "Admins", + } + return nil +} + // AddConsenter adds a consenter to an etcdraft configuration. func (o *OrdererGroup) AddConsenter(consenter orderer.Consenter) error { cfg, err := o.Configuration() @@ -821,6 +871,39 @@ 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[OrderersGroupKey] = &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 + err = encodeBFTBlockVerificationPolicy(o.ConsenterMapping, ordererGroup) + if err != nil { + return fmt.Errorf("failed to encode BFT block verification policy: %v", err) + } default: return fmt.Errorf("unknown orderer type '%s'", o.OrdererType) } @@ -838,6 +921,11 @@ func addOrdererValues(ordererGroup *cb.ConfigGroup, o Orderer) error { return nil } +// marshalBFTOptions serializes smartbft options. +func marshalBFTOptions(op *sb.Options) ([]byte, error) { + return proto.Marshal(op) +} + // 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 { @@ -1033,6 +1121,16 @@ func unmarshalEtcdRaftMetadata(mdBytes []byte) (orderer.EtcdRaft, error) { }, nil } +// unmarshalSmartBFTOptions deserializes . +func unmarshalSmartBFTOptions(optsBytes []byte) (*sb.Options, error) { + smartBFTOptions := &sb.Options{} + err := proto.Unmarshal(optsBytes, smartBFTOptions) + if err != nil { + return nil, err + } + return smartBFTOptions, nil +} + // getOrdererOrg returns the organization config group for an orderer org in the // provided config. It returns nil if the org doesn't exist in the config. func getOrdererOrg(config *cb.Config, orgName string) *cb.ConfigGroup { @@ -1060,3 +1158,49 @@ func blockDataHashingStructureValue() *standardConfigValue { }, } } + +func encodeBFTBlockVerificationPolicy(consenterProtos []cb.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"