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 }