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