github.com/defanghe/fabric@v2.1.1+incompatible/internal/configtxgen/genesisconfig/config.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package genesisconfig
     8  
     9  import (
    10  	"encoding/json"
    11  	"fmt"
    12  	"path/filepath"
    13  	"sync"
    14  	"time"
    15  
    16  	"github.com/hyperledger/fabric-protos-go/orderer/etcdraft"
    17  	"github.com/hyperledger/fabric/common/flogging"
    18  	"github.com/hyperledger/fabric/common/policies"
    19  	"github.com/hyperledger/fabric/common/viperutil"
    20  	cf "github.com/hyperledger/fabric/core/config"
    21  	"github.com/hyperledger/fabric/msp"
    22  	"github.com/spf13/viper"
    23  )
    24  
    25  const (
    26  	// The type key for etcd based RAFT consensus.
    27  	EtcdRaft = "etcdraft"
    28  )
    29  
    30  var logger = flogging.MustGetLogger("common.tools.configtxgen.localconfig")
    31  
    32  const (
    33  	// SampleInsecureSoloProfile references the sample profile which does not
    34  	// include any MSPs and uses solo for ordering.
    35  	SampleInsecureSoloProfile = "SampleInsecureSolo"
    36  	// SampleDevModeSoloProfile references the sample profile which requires
    37  	// only basic membership for admin privileges and uses solo for ordering.
    38  	SampleDevModeSoloProfile = "SampleDevModeSolo"
    39  	// SampleSingleMSPSoloProfile references the sample profile which includes
    40  	// only the sample MSP and uses solo for ordering.
    41  	SampleSingleMSPSoloProfile = "SampleSingleMSPSolo"
    42  
    43  	// SampleInsecureKafkaProfile references the sample profile which does not
    44  	// include any MSPs and uses Kafka for ordering.
    45  	SampleInsecureKafkaProfile = "SampleInsecureKafka"
    46  	// SampleDevModeKafkaProfile references the sample profile which requires only
    47  	// basic membership for admin privileges and uses Kafka for ordering.
    48  	SampleDevModeKafkaProfile = "SampleDevModeKafka"
    49  	// SampleSingleMSPKafkaProfile references the sample profile which includes
    50  	// only the sample MSP and uses Kafka for ordering.
    51  	SampleSingleMSPKafkaProfile = "SampleSingleMSPKafka"
    52  
    53  	// SampleDevModeEtcdRaftProfile references the sample profile used for testing
    54  	// the etcd/raft-based ordering service.
    55  	SampleDevModeEtcdRaftProfile = "SampleDevModeEtcdRaft"
    56  
    57  	// SampleSingleMSPChannelProfile references the sample profile which
    58  	// includes only the sample MSP and is used to create a channel
    59  	SampleSingleMSPChannelProfile = "SampleSingleMSPChannel"
    60  
    61  	// SampleConsortiumName is the sample consortium from the
    62  	// sample configtx.yaml
    63  	SampleConsortiumName = "SampleConsortium"
    64  	// SampleOrgName is the name of the sample org in the sample profiles
    65  	SampleOrgName = "SampleOrg"
    66  
    67  	// AdminRoleAdminPrincipal is set as AdminRole to cause the MSP role of
    68  	// type Admin to be used as the admin principal default
    69  	AdminRoleAdminPrincipal = "Role.ADMIN"
    70  )
    71  
    72  // TopLevel consists of the structs used by the configtxgen tool.
    73  type TopLevel struct {
    74  	Profiles      map[string]*Profile        `yaml:"Profiles"`
    75  	Organizations []*Organization            `yaml:"Organizations"`
    76  	Channel       *Profile                   `yaml:"Channel"`
    77  	Application   *Application               `yaml:"Application"`
    78  	Orderer       *Orderer                   `yaml:"Orderer"`
    79  	Capabilities  map[string]map[string]bool `yaml:"Capabilities"`
    80  	Resources     *Resources                 `yaml:"Resources"`
    81  }
    82  
    83  // Profile encodes orderer/application configuration combinations for the
    84  // configtxgen tool.
    85  type Profile struct {
    86  	Consortium   string                 `yaml:"Consortium"`
    87  	Application  *Application           `yaml:"Application"`
    88  	Orderer      *Orderer               `yaml:"Orderer"`
    89  	Consortiums  map[string]*Consortium `yaml:"Consortiums"`
    90  	Capabilities map[string]bool        `yaml:"Capabilities"`
    91  	Policies     map[string]*Policy     `yaml:"Policies"`
    92  }
    93  
    94  // Policy encodes a channel config policy
    95  type Policy struct {
    96  	Type string `yaml:"Type"`
    97  	Rule string `yaml:"Rule"`
    98  }
    99  
   100  // Consortium represents a group of organizations which may create channels
   101  // with each other
   102  type Consortium struct {
   103  	Organizations []*Organization `yaml:"Organizations"`
   104  }
   105  
   106  // Application encodes the application-level configuration needed in config
   107  // transactions.
   108  type Application struct {
   109  	Organizations []*Organization    `yaml:"Organizations"`
   110  	Capabilities  map[string]bool    `yaml:"Capabilities"`
   111  	Resources     *Resources         `yaml:"Resources"`
   112  	Policies      map[string]*Policy `yaml:"Policies"`
   113  	ACLs          map[string]string  `yaml:"ACLs"`
   114  }
   115  
   116  // Resources encodes the application-level resources configuration needed to
   117  // seed the resource tree
   118  type Resources struct {
   119  	DefaultModPolicy string
   120  }
   121  
   122  // Organization encodes the organization-level configuration needed in
   123  // config transactions.
   124  type Organization struct {
   125  	Name     string             `yaml:"Name"`
   126  	ID       string             `yaml:"ID"`
   127  	MSPDir   string             `yaml:"MSPDir"`
   128  	MSPType  string             `yaml:"MSPType"`
   129  	Policies map[string]*Policy `yaml:"Policies"`
   130  
   131  	// Note: Viper deserialization does not seem to care for
   132  	// embedding of types, so we use one organization struct
   133  	// for both orderers and applications.
   134  	AnchorPeers      []*AnchorPeer `yaml:"AnchorPeers"`
   135  	OrdererEndpoints []string      `yaml:"OrdererEndpoints"`
   136  
   137  	// AdminPrincipal is deprecated and may be removed in a future release
   138  	// it was used for modifying the default policy generation, but policies
   139  	// may now be specified explicitly so it is redundant and unnecessary
   140  	AdminPrincipal string `yaml:"AdminPrincipal"`
   141  
   142  	// SkipAsForeign indicates that this org definition is actually unknown to this
   143  	// instance of the tool, so, parsing of this org's parameters should be ignored.
   144  	SkipAsForeign bool
   145  }
   146  
   147  // AnchorPeer encodes the necessary fields to identify an anchor peer.
   148  type AnchorPeer struct {
   149  	Host string `yaml:"Host"`
   150  	Port int    `yaml:"Port"`
   151  }
   152  
   153  // Orderer contains configuration associated to a channel.
   154  type Orderer struct {
   155  	OrdererType   string                   `yaml:"OrdererType"`
   156  	Addresses     []string                 `yaml:"Addresses"`
   157  	BatchTimeout  time.Duration            `yaml:"BatchTimeout"`
   158  	BatchSize     BatchSize                `yaml:"BatchSize"`
   159  	Kafka         Kafka                    `yaml:"Kafka"`
   160  	EtcdRaft      *etcdraft.ConfigMetadata `yaml:"EtcdRaft"`
   161  	Organizations []*Organization          `yaml:"Organizations"`
   162  	MaxChannels   uint64                   `yaml:"MaxChannels"`
   163  	Capabilities  map[string]bool          `yaml:"Capabilities"`
   164  	Policies      map[string]*Policy       `yaml:"Policies"`
   165  }
   166  
   167  // BatchSize contains configuration affecting the size of batches.
   168  type BatchSize struct {
   169  	MaxMessageCount   uint32 `yaml:"MaxMessageCount"`
   170  	AbsoluteMaxBytes  uint32 `yaml:"AbsoluteMaxBytes"`
   171  	PreferredMaxBytes uint32 `yaml:"PreferredMaxBytes"`
   172  }
   173  
   174  // Kafka contains configuration for the Kafka-based orderer.
   175  type Kafka struct {
   176  	Brokers []string `yaml:"Brokers"`
   177  }
   178  
   179  var genesisDefaults = TopLevel{
   180  	Orderer: &Orderer{
   181  		OrdererType:  "solo",
   182  		Addresses:    []string{"127.0.0.1:7050"},
   183  		BatchTimeout: 2 * time.Second,
   184  		BatchSize: BatchSize{
   185  			MaxMessageCount:   500,
   186  			AbsoluteMaxBytes:  10 * 1024 * 1024,
   187  			PreferredMaxBytes: 2 * 1024 * 1024,
   188  		},
   189  		Kafka: Kafka{
   190  			Brokers: []string{"127.0.0.1:9092"},
   191  		},
   192  		EtcdRaft: &etcdraft.ConfigMetadata{
   193  			Options: &etcdraft.Options{
   194  				TickInterval:         "500ms",
   195  				ElectionTick:         10,
   196  				HeartbeatTick:        1,
   197  				MaxInflightBlocks:    5,
   198  				SnapshotIntervalSize: 16 * 1024 * 1024, // 16 MB
   199  			},
   200  		},
   201  	},
   202  }
   203  
   204  // LoadTopLevel simply loads the configtx.yaml file into the structs above and
   205  // completes their initialization. Config paths may optionally be provided and
   206  // will be used in place of the FABRIC_CFG_PATH env variable.
   207  //
   208  // Note, for environment overrides to work properly within a profile, Load
   209  // should be used instead.
   210  func LoadTopLevel(configPaths ...string) *TopLevel {
   211  	config := viper.New()
   212  	if len(configPaths) > 0 {
   213  		for _, p := range configPaths {
   214  			config.AddConfigPath(p)
   215  		}
   216  		config.SetConfigName("configtx")
   217  	} else {
   218  		cf.InitViper(config, "configtx")
   219  	}
   220  
   221  	err := config.ReadInConfig()
   222  	if err != nil {
   223  		logger.Panicf("Error reading configuration: %s", err)
   224  	}
   225  	logger.Debugf("Using config file: %s", config.ConfigFileUsed())
   226  
   227  	uconf, err := cache.load(config, config.ConfigFileUsed())
   228  	if err != nil {
   229  		logger.Panicf("failed to load configCache: %s", err)
   230  	}
   231  	uconf.completeInitialization(filepath.Dir(config.ConfigFileUsed()))
   232  	logger.Infof("Loaded configuration: %s", config.ConfigFileUsed())
   233  
   234  	return uconf
   235  }
   236  
   237  // Load returns the orderer/application config combination that corresponds to
   238  // a given profile. Config paths may optionally be provided and will be used
   239  // in place of the FABRIC_CFG_PATH env variable.
   240  func Load(profile string, configPaths ...string) *Profile {
   241  	config := viper.New()
   242  	if len(configPaths) > 0 {
   243  		for _, p := range configPaths {
   244  			config.AddConfigPath(p)
   245  		}
   246  		config.SetConfigName("configtx")
   247  	} else {
   248  		cf.InitViper(config, "configtx")
   249  	}
   250  
   251  	err := config.ReadInConfig()
   252  	if err != nil {
   253  		logger.Panicf("Error reading configuration: %s", err)
   254  	}
   255  	logger.Debugf("Using config file: %s", config.ConfigFileUsed())
   256  
   257  	uconf, err := cache.load(config, config.ConfigFileUsed())
   258  	if err != nil {
   259  		logger.Panicf("Error loading config from config cache: %s", err)
   260  	}
   261  
   262  	result, ok := uconf.Profiles[profile]
   263  	if !ok {
   264  		logger.Panicf("Could not find profile: %s", profile)
   265  	}
   266  
   267  	result.completeInitialization(filepath.Dir(config.ConfigFileUsed()))
   268  
   269  	logger.Infof("Loaded configuration: %s", config.ConfigFileUsed())
   270  
   271  	return result
   272  }
   273  
   274  func (t *TopLevel) completeInitialization(configDir string) {
   275  	for _, org := range t.Organizations {
   276  		org.completeInitialization(configDir)
   277  	}
   278  
   279  	if t.Orderer != nil {
   280  		t.Orderer.completeInitialization(configDir)
   281  	}
   282  }
   283  
   284  func (p *Profile) completeInitialization(configDir string) {
   285  	if p.Application != nil {
   286  		for _, org := range p.Application.Organizations {
   287  			org.completeInitialization(configDir)
   288  		}
   289  		if p.Application.Resources != nil {
   290  			p.Application.Resources.completeInitialization()
   291  		}
   292  	}
   293  
   294  	if p.Consortiums != nil {
   295  		for _, consortium := range p.Consortiums {
   296  			for _, org := range consortium.Organizations {
   297  				org.completeInitialization(configDir)
   298  			}
   299  		}
   300  	}
   301  
   302  	if p.Orderer != nil {
   303  		for _, org := range p.Orderer.Organizations {
   304  			org.completeInitialization(configDir)
   305  		}
   306  		// Some profiles will not define orderer parameters
   307  		p.Orderer.completeInitialization(configDir)
   308  	}
   309  }
   310  
   311  func (r *Resources) completeInitialization() {
   312  	for {
   313  		switch {
   314  		case r.DefaultModPolicy == "":
   315  			r.DefaultModPolicy = policies.ChannelApplicationAdmins
   316  		default:
   317  			return
   318  		}
   319  	}
   320  }
   321  
   322  func (org *Organization) completeInitialization(configDir string) {
   323  	// set the MSP type; if none is specified we assume BCCSP
   324  	if org.MSPType == "" {
   325  		org.MSPType = msp.ProviderTypeToString(msp.FABRIC)
   326  	}
   327  
   328  	if org.AdminPrincipal == "" {
   329  		org.AdminPrincipal = AdminRoleAdminPrincipal
   330  	}
   331  	translatePaths(configDir, org)
   332  }
   333  
   334  func (ord *Orderer) completeInitialization(configDir string) {
   335  loop:
   336  	for {
   337  		switch {
   338  		case ord.OrdererType == "":
   339  			logger.Infof("Orderer.OrdererType unset, setting to %v", genesisDefaults.Orderer.OrdererType)
   340  			ord.OrdererType = genesisDefaults.Orderer.OrdererType
   341  		case ord.Addresses == nil:
   342  			logger.Infof("Orderer.Addresses unset, setting to %s", genesisDefaults.Orderer.Addresses)
   343  			ord.Addresses = genesisDefaults.Orderer.Addresses
   344  		case ord.BatchTimeout == 0:
   345  			logger.Infof("Orderer.BatchTimeout unset, setting to %s", genesisDefaults.Orderer.BatchTimeout)
   346  			ord.BatchTimeout = genesisDefaults.Orderer.BatchTimeout
   347  		case ord.BatchSize.MaxMessageCount == 0:
   348  			logger.Infof("Orderer.BatchSize.MaxMessageCount unset, setting to %v", genesisDefaults.Orderer.BatchSize.MaxMessageCount)
   349  			ord.BatchSize.MaxMessageCount = genesisDefaults.Orderer.BatchSize.MaxMessageCount
   350  		case ord.BatchSize.AbsoluteMaxBytes == 0:
   351  			logger.Infof("Orderer.BatchSize.AbsoluteMaxBytes unset, setting to %v", genesisDefaults.Orderer.BatchSize.AbsoluteMaxBytes)
   352  			ord.BatchSize.AbsoluteMaxBytes = genesisDefaults.Orderer.BatchSize.AbsoluteMaxBytes
   353  		case ord.BatchSize.PreferredMaxBytes == 0:
   354  			logger.Infof("Orderer.BatchSize.PreferredMaxBytes unset, setting to %v", genesisDefaults.Orderer.BatchSize.PreferredMaxBytes)
   355  			ord.BatchSize.PreferredMaxBytes = genesisDefaults.Orderer.BatchSize.PreferredMaxBytes
   356  		default:
   357  			break loop
   358  		}
   359  	}
   360  
   361  	logger.Infof("orderer type: %s", ord.OrdererType)
   362  	// Additional, consensus type-dependent initialization goes here
   363  	// Also using this to panic on unknown orderer type.
   364  	switch ord.OrdererType {
   365  	case "solo":
   366  		// nothing to be done here
   367  	case "kafka":
   368  		if ord.Kafka.Brokers == nil {
   369  			logger.Infof("Orderer.Kafka unset, setting to %v", genesisDefaults.Orderer.Kafka.Brokers)
   370  			ord.Kafka.Brokers = genesisDefaults.Orderer.Kafka.Brokers
   371  		}
   372  	case EtcdRaft:
   373  		if ord.EtcdRaft == nil {
   374  			logger.Panicf("%s configuration missing", EtcdRaft)
   375  		}
   376  		if ord.EtcdRaft.Options == nil {
   377  			logger.Infof("Orderer.EtcdRaft.Options unset, setting to %v", genesisDefaults.Orderer.EtcdRaft.Options)
   378  			ord.EtcdRaft.Options = genesisDefaults.Orderer.EtcdRaft.Options
   379  		}
   380  	second_loop:
   381  		for {
   382  			switch {
   383  			case ord.EtcdRaft.Options.TickInterval == "":
   384  				logger.Infof("Orderer.EtcdRaft.Options.TickInterval unset, setting to %v", genesisDefaults.Orderer.EtcdRaft.Options.TickInterval)
   385  				ord.EtcdRaft.Options.TickInterval = genesisDefaults.Orderer.EtcdRaft.Options.TickInterval
   386  
   387  			case ord.EtcdRaft.Options.ElectionTick == 0:
   388  				logger.Infof("Orderer.EtcdRaft.Options.ElectionTick unset, setting to %v", genesisDefaults.Orderer.EtcdRaft.Options.ElectionTick)
   389  				ord.EtcdRaft.Options.ElectionTick = genesisDefaults.Orderer.EtcdRaft.Options.ElectionTick
   390  
   391  			case ord.EtcdRaft.Options.HeartbeatTick == 0:
   392  				logger.Infof("Orderer.EtcdRaft.Options.HeartbeatTick unset, setting to %v", genesisDefaults.Orderer.EtcdRaft.Options.HeartbeatTick)
   393  				ord.EtcdRaft.Options.HeartbeatTick = genesisDefaults.Orderer.EtcdRaft.Options.HeartbeatTick
   394  
   395  			case ord.EtcdRaft.Options.MaxInflightBlocks == 0:
   396  				logger.Infof("Orderer.EtcdRaft.Options.MaxInflightBlocks unset, setting to %v", genesisDefaults.Orderer.EtcdRaft.Options.MaxInflightBlocks)
   397  				ord.EtcdRaft.Options.MaxInflightBlocks = genesisDefaults.Orderer.EtcdRaft.Options.MaxInflightBlocks
   398  
   399  			case ord.EtcdRaft.Options.SnapshotIntervalSize == 0:
   400  				logger.Infof("Orderer.EtcdRaft.Options.SnapshotIntervalSize unset, setting to %v", genesisDefaults.Orderer.EtcdRaft.Options.SnapshotIntervalSize)
   401  				ord.EtcdRaft.Options.SnapshotIntervalSize = genesisDefaults.Orderer.EtcdRaft.Options.SnapshotIntervalSize
   402  
   403  			case len(ord.EtcdRaft.Consenters) == 0:
   404  				logger.Panicf("%s configuration did not specify any consenter", EtcdRaft)
   405  
   406  			default:
   407  				break second_loop
   408  			}
   409  		}
   410  
   411  		if _, err := time.ParseDuration(ord.EtcdRaft.Options.TickInterval); err != nil {
   412  			logger.Panicf("Etcdraft TickInterval (%s) must be in time duration format", ord.EtcdRaft.Options.TickInterval)
   413  		}
   414  
   415  		// validate the specified members for Options
   416  		if ord.EtcdRaft.Options.ElectionTick <= ord.EtcdRaft.Options.HeartbeatTick {
   417  			logger.Panicf("election tick must be greater than heartbeat tick")
   418  		}
   419  
   420  		for _, c := range ord.EtcdRaft.GetConsenters() {
   421  			if c.Host == "" {
   422  				logger.Panicf("consenter info in %s configuration did not specify host", EtcdRaft)
   423  			}
   424  			if c.Port == 0 {
   425  				logger.Panicf("consenter info in %s configuration did not specify port", EtcdRaft)
   426  			}
   427  			if c.ClientTlsCert == nil {
   428  				logger.Panicf("consenter info in %s configuration did not specify client TLS cert", EtcdRaft)
   429  			}
   430  			if c.ServerTlsCert == nil {
   431  				logger.Panicf("consenter info in %s configuration did not specify server TLS cert", EtcdRaft)
   432  			}
   433  			clientCertPath := string(c.GetClientTlsCert())
   434  			cf.TranslatePathInPlace(configDir, &clientCertPath)
   435  			c.ClientTlsCert = []byte(clientCertPath)
   436  			serverCertPath := string(c.GetServerTlsCert())
   437  			cf.TranslatePathInPlace(configDir, &serverCertPath)
   438  			c.ServerTlsCert = []byte(serverCertPath)
   439  		}
   440  	default:
   441  		logger.Panicf("unknown orderer type: %s", ord.OrdererType)
   442  	}
   443  }
   444  
   445  func translatePaths(configDir string, org *Organization) {
   446  	cf.TranslatePathInPlace(configDir, &org.MSPDir)
   447  }
   448  
   449  // configCache stores marshalled bytes of config structures that produced from
   450  // EnhancedExactUnmarshal. Cache key is the path of the configuration file that was used.
   451  type configCache struct {
   452  	mutex sync.Mutex
   453  	cache map[string][]byte
   454  }
   455  
   456  var cache = &configCache{
   457  	cache: make(map[string][]byte),
   458  }
   459  
   460  // load loads the TopLevel config structure from configCache.
   461  // if not successful, it unmarshal a config file, and populate configCache
   462  // with marshaled TopLevel struct.
   463  func (c *configCache) load(config *viper.Viper, configPath string) (*TopLevel, error) {
   464  	c.mutex.Lock()
   465  	defer c.mutex.Unlock()
   466  
   467  	conf := &TopLevel{}
   468  	serializedConf, ok := c.cache[configPath]
   469  	logger.Debug("Loading configuration from cache :%v", ok)
   470  	if !ok {
   471  		err := viperutil.EnhancedExactUnmarshal(config, conf)
   472  		if err != nil {
   473  			return nil, fmt.Errorf("Error unmarshaling config into struct: %s", err)
   474  		}
   475  
   476  		serializedConf, err = json.Marshal(conf)
   477  		if err != nil {
   478  			return nil, err
   479  		}
   480  		c.cache[configPath] = serializedConf
   481  	}
   482  
   483  	err := json.Unmarshal(serializedConf, conf)
   484  	if err != nil {
   485  		return nil, err
   486  	}
   487  	return conf, nil
   488  }