261 lines
7.9 KiB
Go
261 lines
7.9 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package channelconfig
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
cb "github.com/hyperledger/fabric-protos-go/common"
|
|
ab "github.com/hyperledger/fabric-protos-go/orderer"
|
|
"github.com/hyperledger/fabric/common/capabilities"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
const (
|
|
// OrdererGroupKey is the group name for the orderer config.
|
|
OrdererGroupKey = "Orderer"
|
|
)
|
|
|
|
const (
|
|
// ConsensusTypeKey is the cb.ConfigItem type key name for the ConsensusType message.
|
|
ConsensusTypeKey = "ConsensusType"
|
|
|
|
// BatchSizeKey is the cb.ConfigItem type key name for the BatchSize message.
|
|
BatchSizeKey = "BatchSize"
|
|
|
|
// BatchTimeoutKey is the cb.ConfigItem type key name for the BatchTimeout message.
|
|
BatchTimeoutKey = "BatchTimeout"
|
|
|
|
// ChannelRestrictionsKey is the key name for the ChannelRestrictions message.
|
|
ChannelRestrictionsKey = "ChannelRestrictions"
|
|
|
|
// EndpointsKey is the cb.COnfigValue key name for the Endpoints message in the OrdererOrgGroup.
|
|
EndpointsKey = "Endpoints"
|
|
)
|
|
|
|
// OrdererProtos is used as the source of the OrdererConfig.
|
|
type OrdererProtos struct {
|
|
ConsensusType *ab.ConsensusType
|
|
BatchSize *ab.BatchSize
|
|
BatchTimeout *ab.BatchTimeout
|
|
// We have to keep this field in order to be able to load legacy config blocks from a chain that started with kafka
|
|
// and was later migrated to etcdraft.
|
|
KafkaBrokers *ab.KafkaBrokers
|
|
ChannelRestrictions *ab.ChannelRestrictions
|
|
Orderers *cb.Orderers
|
|
Capabilities *cb.Capabilities
|
|
}
|
|
|
|
// OrdererConfig holds the orderer configuration information.
|
|
type OrdererConfig struct {
|
|
protos *OrdererProtos
|
|
orgs map[string]OrdererOrg
|
|
|
|
batchTimeout time.Duration
|
|
}
|
|
|
|
// OrdererOrgProtos are deserialized from the Orderer org config values
|
|
type OrdererOrgProtos struct {
|
|
Endpoints *cb.OrdererAddresses
|
|
}
|
|
|
|
// OrdererOrgConfig defines the configuration for an orderer org
|
|
type OrdererOrgConfig struct {
|
|
*OrganizationConfig
|
|
protos *OrdererOrgProtos
|
|
name string
|
|
}
|
|
|
|
// Endpoints returns the set of addresses this ordering org exposes as orderers
|
|
func (oc *OrdererOrgConfig) Endpoints() []string {
|
|
return oc.protos.Endpoints.Addresses
|
|
}
|
|
|
|
// NewOrdererOrgConfig returns an orderer org config built from the given ConfigGroup.
|
|
func NewOrdererOrgConfig(orgName string, orgGroup *cb.ConfigGroup, mspConfigHandler *MSPConfigHandler, channelCapabilities ChannelCapabilities) (*OrdererOrgConfig, error) {
|
|
if len(orgGroup.Groups) > 0 {
|
|
return nil, fmt.Errorf("OrdererOrg config does not allow sub-groups")
|
|
}
|
|
|
|
if !channelCapabilities.OrgSpecificOrdererEndpoints() {
|
|
if _, ok := orgGroup.Values[EndpointsKey]; ok {
|
|
return nil, errors.Errorf("Orderer Org %s cannot contain endpoints value until V1_4_2+ capabilities have been enabled", orgName)
|
|
}
|
|
}
|
|
|
|
protos := &OrdererOrgProtos{}
|
|
orgProtos := &OrganizationProtos{}
|
|
|
|
if err := DeserializeProtoValuesFromGroup(orgGroup, protos, orgProtos); err != nil {
|
|
return nil, errors.Wrap(err, "failed to deserialize values")
|
|
}
|
|
|
|
ooc := &OrdererOrgConfig{
|
|
name: orgName,
|
|
protos: protos,
|
|
OrganizationConfig: &OrganizationConfig{
|
|
name: orgName,
|
|
protos: orgProtos,
|
|
mspConfigHandler: mspConfigHandler,
|
|
},
|
|
}
|
|
|
|
if err := ooc.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return ooc, nil
|
|
}
|
|
|
|
func (ooc *OrdererOrgConfig) Validate() error {
|
|
return ooc.OrganizationConfig.Validate()
|
|
}
|
|
|
|
// NewOrdererConfig creates a new instance of the orderer config.
|
|
func NewOrdererConfig(ordererGroup *cb.ConfigGroup, mspConfig *MSPConfigHandler, channelCapabilities ChannelCapabilities) (*OrdererConfig, error) {
|
|
oc := &OrdererConfig{
|
|
protos: &OrdererProtos{},
|
|
orgs: make(map[string]OrdererOrg),
|
|
}
|
|
|
|
if err := DeserializeProtoValuesFromGroup(ordererGroup, oc.protos); err != nil {
|
|
return nil, errors.Wrap(err, "failed to deserialize values")
|
|
}
|
|
|
|
if err := oc.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for orgName, orgGroup := range ordererGroup.Groups {
|
|
var err error
|
|
if oc.orgs[orgName], err = NewOrdererOrgConfig(orgName, orgGroup, mspConfig, channelCapabilities); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return oc, nil
|
|
}
|
|
|
|
// ConsensusType returns the configured consensus type.
|
|
func (oc *OrdererConfig) ConsensusType() string {
|
|
return oc.protos.ConsensusType.Type
|
|
}
|
|
|
|
// ConsensusMetadata returns the metadata associated with the consensus type.
|
|
func (oc *OrdererConfig) ConsensusMetadata() []byte {
|
|
return oc.protos.ConsensusType.Metadata
|
|
}
|
|
|
|
// ConsensusState return the consensus type state.
|
|
func (oc *OrdererConfig) ConsensusState() ab.ConsensusType_State {
|
|
return oc.protos.ConsensusType.State
|
|
}
|
|
|
|
// BatchSize returns the maximum number of messages to include in a block.
|
|
func (oc *OrdererConfig) BatchSize() *ab.BatchSize {
|
|
return oc.protos.BatchSize
|
|
}
|
|
|
|
// BatchTimeout returns the amount of time to wait before creating a batch.
|
|
func (oc *OrdererConfig) BatchTimeout() time.Duration {
|
|
return oc.batchTimeout
|
|
}
|
|
|
|
// MaxChannelsCount returns the maximum count of channels this orderer supports.
|
|
func (oc *OrdererConfig) MaxChannelsCount() uint64 {
|
|
return oc.protos.ChannelRestrictions.MaxCount
|
|
}
|
|
|
|
// Organizations returns a map of the orgs in the channel.
|
|
func (oc *OrdererConfig) Organizations() map[string]OrdererOrg {
|
|
return oc.orgs
|
|
}
|
|
|
|
func (oc *OrdererConfig) Consenters() []*cb.Consenter {
|
|
return oc.protos.Orderers.ConsenterMapping
|
|
}
|
|
|
|
// Capabilities returns the capabilities the ordering network has for this channel.
|
|
func (oc *OrdererConfig) Capabilities() OrdererCapabilities {
|
|
return capabilities.NewOrdererProvider(oc.protos.Capabilities.Capabilities)
|
|
}
|
|
|
|
func (oc *OrdererConfig) Validate() error {
|
|
for _, validator := range []func() error{
|
|
oc.validateBatchSize,
|
|
oc.validateBatchTimeout,
|
|
} {
|
|
if err := validator(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (oc *OrdererConfig) validateBatchSize() error {
|
|
if oc.protos.BatchSize.MaxMessageCount == 0 {
|
|
return fmt.Errorf("Attempted to set the batch size max message count to an invalid value: 0")
|
|
}
|
|
if oc.protos.BatchSize.AbsoluteMaxBytes == 0 {
|
|
return fmt.Errorf("Attempted to set the batch size absolute max bytes to an invalid value: 0")
|
|
}
|
|
if oc.protos.BatchSize.PreferredMaxBytes == 0 {
|
|
return fmt.Errorf("Attempted to set the batch size preferred max bytes to an invalid value: 0")
|
|
}
|
|
if oc.protos.BatchSize.PreferredMaxBytes > oc.protos.BatchSize.AbsoluteMaxBytes {
|
|
return fmt.Errorf("Attempted to set the batch size preferred max bytes (%v) greater than the absolute max bytes (%v).", oc.protos.BatchSize.PreferredMaxBytes, oc.protos.BatchSize.AbsoluteMaxBytes)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (oc *OrdererConfig) validateBatchTimeout() error {
|
|
var err error
|
|
oc.batchTimeout, err = time.ParseDuration(oc.protos.BatchTimeout.Timeout)
|
|
if err != nil {
|
|
return fmt.Errorf("Attempted to set the batch timeout to a invalid value: %s", err)
|
|
}
|
|
if oc.batchTimeout <= 0 {
|
|
return fmt.Errorf("Attempted to set the batch timeout to a non-positive value: %s", oc.batchTimeout)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// This does just a barebones sanity check.
|
|
func brokerEntrySeemsValid(broker string) bool {
|
|
if !strings.Contains(broker, ":") {
|
|
return false
|
|
}
|
|
|
|
parts := strings.Split(broker, ":")
|
|
if len(parts) > 2 {
|
|
return false
|
|
}
|
|
|
|
host := parts[0]
|
|
port := parts[1]
|
|
|
|
if _, err := strconv.ParseUint(port, 10, 16); err != nil {
|
|
return false
|
|
}
|
|
|
|
// Valid hostnames may contain only the ASCII letters 'a' through 'z' (in a
|
|
// case-insensitive manner), the digits '0' through '9', and the hyphen. IP
|
|
// v4 addresses are represented in dot-decimal notation, which consists of
|
|
// four decimal numbers, each ranging from 0 to 255, separated by dots,
|
|
// e.g., 172.16.254.1
|
|
// The following regular expression:
|
|
// 1. allows just a-z (case-insensitive), 0-9, and the dot and hyphen characters
|
|
// 2. does not allow leading trailing dots or hyphens
|
|
re, _ := regexp.Compile("^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9])$")
|
|
matched := re.FindString(host)
|
|
return len(matched) == len(host)
|
|
}
|