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 }