github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/orderer/multichain/manager.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 package multichain 7 8 import ( 9 "fmt" 10 11 "github.com/hyperledger/fabric/common/config" 12 "github.com/hyperledger/fabric/common/configtx" 13 configtxapi "github.com/hyperledger/fabric/common/configtx/api" 14 "github.com/hyperledger/fabric/common/policies" 15 "github.com/hyperledger/fabric/orderer/ledger" 16 cb "github.com/hyperledger/fabric/protos/common" 17 "github.com/hyperledger/fabric/protos/utils" 18 "github.com/op/go-logging" 19 20 "github.com/golang/protobuf/proto" 21 "github.com/hyperledger/fabric/common/crypto" 22 ) 23 24 var logger = logging.MustGetLogger("orderer/multichain") 25 26 const ( 27 msgVersion = int32(0) 28 epoch = 0 29 ) 30 31 // Manager coordinates the creation and access of chains 32 type Manager interface { 33 // GetChain retrieves the chain support for a chain (and whether it exists) 34 GetChain(chainID string) (ChainSupport, bool) 35 36 // SystemChannelID returns the channel ID for the system channel 37 SystemChannelID() string 38 39 // NewChannelConfig returns a bare bones configuration ready for channel 40 // creation request to be applied on top of it 41 NewChannelConfig(envConfigUpdate *cb.Envelope) (configtxapi.Manager, error) 42 } 43 44 type configResources struct { 45 configtxapi.Manager 46 } 47 48 func (cr *configResources) SharedConfig() config.Orderer { 49 oc, ok := cr.OrdererConfig() 50 if !ok { 51 logger.Panicf("[channel %s] has no orderer configuration", cr.ChainID()) 52 } 53 return oc 54 } 55 56 type ledgerResources struct { 57 *configResources 58 ledger ledger.ReadWriter 59 } 60 61 type multiLedger struct { 62 chains map[string]*chainSupport 63 consenters map[string]Consenter 64 ledgerFactory ledger.Factory 65 signer crypto.LocalSigner 66 systemChannelID string 67 systemChannel *chainSupport 68 } 69 70 func getConfigTx(reader ledger.Reader) *cb.Envelope { 71 lastBlock := ledger.GetBlock(reader, reader.Height()-1) 72 index, err := utils.GetLastConfigIndexFromBlock(lastBlock) 73 if err != nil { 74 logger.Panicf("Chain did not have appropriately encoded last config in its latest block: %s", err) 75 } 76 configBlock := ledger.GetBlock(reader, index) 77 if configBlock == nil { 78 logger.Panicf("Config block does not exist") 79 } 80 81 return utils.ExtractEnvelopeOrPanic(configBlock, 0) 82 } 83 84 // NewManagerImpl produces an instance of a Manager 85 func NewManagerImpl(ledgerFactory ledger.Factory, consenters map[string]Consenter, signer crypto.LocalSigner) Manager { 86 ml := &multiLedger{ 87 chains: make(map[string]*chainSupport), 88 ledgerFactory: ledgerFactory, 89 consenters: consenters, 90 signer: signer, 91 } 92 93 existingChains := ledgerFactory.ChainIDs() 94 for _, chainID := range existingChains { 95 rl, err := ledgerFactory.GetOrCreate(chainID) 96 if err != nil { 97 logger.Panicf("Ledger factory reported chainID %s but could not retrieve it: %s", chainID, err) 98 } 99 configTx := getConfigTx(rl) 100 if configTx == nil { 101 logger.Panic("Programming error, configTx should never be nil here") 102 } 103 ledgerResources := ml.newLedgerResources(configTx) 104 chainID := ledgerResources.ChainID() 105 106 if _, ok := ledgerResources.ConsortiumsConfig(); ok { 107 if ml.systemChannelID != "" { 108 logger.Panicf("There appear to be two system chains %s and %s", ml.systemChannelID, chainID) 109 } 110 chain := newChainSupport(createSystemChainFilters(ml, ledgerResources), 111 ledgerResources, 112 consenters, 113 signer) 114 logger.Infof("Starting with system channel %s and orderer type %s", chainID, chain.SharedConfig().ConsensusType()) 115 ml.chains[chainID] = chain 116 ml.systemChannelID = chainID 117 ml.systemChannel = chain 118 // We delay starting this chain, as it might try to copy and replace the chains map via newChain before the map is fully built 119 defer chain.start() 120 } else { 121 logger.Debugf("Starting chain: %s", chainID) 122 chain := newChainSupport(createStandardFilters(ledgerResources), 123 ledgerResources, 124 consenters, 125 signer) 126 ml.chains[chainID] = chain 127 chain.start() 128 } 129 130 } 131 132 if ml.systemChannelID == "" { 133 logger.Panicf("No system chain found. If bootstrapping, does your system channel contain a consortiums group definition?") 134 } 135 136 return ml 137 } 138 139 func (ml *multiLedger) SystemChannelID() string { 140 return ml.systemChannelID 141 } 142 143 // GetChain retrieves the chain support for a chain (and whether it exists) 144 func (ml *multiLedger) GetChain(chainID string) (ChainSupport, bool) { 145 cs, ok := ml.chains[chainID] 146 return cs, ok 147 } 148 149 func (ml *multiLedger) newLedgerResources(configTx *cb.Envelope) *ledgerResources { 150 initializer := configtx.NewInitializer() 151 configManager, err := configtx.NewManagerImpl(configTx, initializer, nil) 152 if err != nil { 153 logger.Panicf("Error creating configtx manager and handlers: %s", err) 154 } 155 156 chainID := configManager.ChainID() 157 158 ledger, err := ml.ledgerFactory.GetOrCreate(chainID) 159 if err != nil { 160 logger.Panicf("Error getting ledger for %s", chainID) 161 } 162 163 return &ledgerResources{ 164 configResources: &configResources{Manager: configManager}, 165 ledger: ledger, 166 } 167 } 168 169 func (ml *multiLedger) newChain(configtx *cb.Envelope) { 170 ledgerResources := ml.newLedgerResources(configtx) 171 ledgerResources.ledger.Append(ledger.CreateNextBlock(ledgerResources.ledger, []*cb.Envelope{configtx})) 172 173 // Copy the map to allow concurrent reads from broadcast/deliver while the new chainSupport is 174 newChains := make(map[string]*chainSupport) 175 for key, value := range ml.chains { 176 newChains[key] = value 177 } 178 179 cs := newChainSupport(createStandardFilters(ledgerResources), ledgerResources, ml.consenters, ml.signer) 180 chainID := ledgerResources.ChainID() 181 182 logger.Infof("Created and starting new chain %s", chainID) 183 184 newChains[string(chainID)] = cs 185 cs.start() 186 187 ml.chains = newChains 188 } 189 190 func (ml *multiLedger) channelsCount() int { 191 return len(ml.chains) 192 } 193 194 func (ml *multiLedger) NewChannelConfig(envConfigUpdate *cb.Envelope) (configtxapi.Manager, error) { 195 configUpdatePayload, err := utils.UnmarshalPayload(envConfigUpdate.Payload) 196 if err != nil { 197 return nil, fmt.Errorf("Failing initial channel config creation because of payload unmarshaling error: %s", err) 198 } 199 200 configUpdateEnv, err := configtx.UnmarshalConfigUpdateEnvelope(configUpdatePayload.Data) 201 if err != nil { 202 return nil, fmt.Errorf("Failing initial channel config creation because of config update envelope unmarshaling error: %s", err) 203 } 204 205 if configUpdatePayload.Header == nil { 206 return nil, fmt.Errorf("Failed initial channel config creation because config update header was missing") 207 } 208 channelHeader, err := utils.UnmarshalChannelHeader(configUpdatePayload.Header.ChannelHeader) 209 210 configUpdate, err := configtx.UnmarshalConfigUpdate(configUpdateEnv.ConfigUpdate) 211 if err != nil { 212 return nil, fmt.Errorf("Failing initial channel config creation because of config update unmarshaling error: %s", err) 213 } 214 215 if configUpdate.ChannelId != channelHeader.ChannelId { 216 return nil, fmt.Errorf("Failing initial channel config creation: mismatched channel IDs: '%s' != '%s'", configUpdate.ChannelId, channelHeader.ChannelId) 217 } 218 219 if configUpdate.WriteSet == nil { 220 return nil, fmt.Errorf("Config update has an empty writeset") 221 } 222 223 if configUpdate.WriteSet.Groups == nil || configUpdate.WriteSet.Groups[config.ApplicationGroupKey] == nil { 224 return nil, fmt.Errorf("Config update has missing application group") 225 } 226 227 if uv := configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Version; uv != 1 { 228 return nil, fmt.Errorf("Config update for channel creation does not set application group version to 1, was %d", uv) 229 } 230 231 consortiumConfigValue, ok := configUpdate.WriteSet.Values[config.ConsortiumKey] 232 if !ok { 233 return nil, fmt.Errorf("Consortium config value missing") 234 } 235 236 consortium := &cb.Consortium{} 237 err = proto.Unmarshal(consortiumConfigValue.Value, consortium) 238 if err != nil { 239 return nil, fmt.Errorf("Error reading unmarshaling consortium name: %s", err) 240 } 241 242 applicationGroup := cb.NewConfigGroup() 243 consortiumsConfig, ok := ml.systemChannel.ConsortiumsConfig() 244 if !ok { 245 return nil, fmt.Errorf("The ordering system channel does not appear to support creating channels") 246 } 247 248 consortiumConf, ok := consortiumsConfig.Consortiums()[consortium.Name] 249 if !ok { 250 return nil, fmt.Errorf("Unknown consortium name: %s", consortium.Name) 251 } 252 253 applicationGroup.Policies[config.ChannelCreationPolicyKey] = &cb.ConfigPolicy{ 254 Policy: consortiumConf.ChannelCreationPolicy(), 255 } 256 applicationGroup.ModPolicy = config.ChannelCreationPolicyKey 257 258 // Get the current system channel config 259 systemChannelGroup := ml.systemChannel.ConfigEnvelope().Config.ChannelGroup 260 261 // If the consortium group has no members, allow the source request to have no members. However, 262 // if the consortium group has any members, there must be at least one member in the source request 263 if len(systemChannelGroup.Groups[config.ConsortiumsGroupKey].Groups[consortium.Name].Groups) > 0 && 264 len(configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups) == 0 { 265 return nil, fmt.Errorf("Proposed configuration has no application group members, but consortium contains members") 266 } 267 268 // If the consortium has no members, allow the source request to contain arbitrary members 269 // Otherwise, require that the supplied members are a subset of the consortium members 270 if len(systemChannelGroup.Groups[config.ConsortiumsGroupKey].Groups[consortium.Name].Groups) > 0 { 271 for orgName := range configUpdate.WriteSet.Groups[config.ApplicationGroupKey].Groups { 272 consortiumGroup, ok := systemChannelGroup.Groups[config.ConsortiumsGroupKey].Groups[consortium.Name].Groups[orgName] 273 if !ok { 274 return nil, fmt.Errorf("Attempted to include a member which is not in the consortium") 275 } 276 applicationGroup.Groups[orgName] = consortiumGroup 277 } 278 } 279 280 channelGroup := cb.NewConfigGroup() 281 282 // Copy the system channel Channel level config to the new config 283 for key, value := range systemChannelGroup.Values { 284 channelGroup.Values[key] = value 285 if key == config.ConsortiumKey { 286 // Do not set the consortium name, we do this later 287 continue 288 } 289 } 290 291 for key, policy := range systemChannelGroup.Policies { 292 channelGroup.Policies[key] = policy 293 } 294 295 // Set the new config orderer group to the system channel orderer group and the application group to the new application group 296 channelGroup.Groups[config.OrdererGroupKey] = systemChannelGroup.Groups[config.OrdererGroupKey] 297 channelGroup.Groups[config.ApplicationGroupKey] = applicationGroup 298 channelGroup.Values[config.ConsortiumKey] = config.TemplateConsortium(consortium.Name).Values[config.ConsortiumKey] 299 300 templateConfig, _ := utils.CreateSignedEnvelope(cb.HeaderType_CONFIG, configUpdate.ChannelId, ml.signer, &cb.ConfigEnvelope{ 301 Config: &cb.Config{ 302 ChannelGroup: channelGroup, 303 }, 304 }, msgVersion, epoch) 305 306 initializer := configtx.NewInitializer() 307 308 // This is a very hacky way to disable the sanity check logging in the policy manager 309 // for the template configuration, but it is the least invasive near a release 310 pm, ok := initializer.PolicyManager().(*policies.ManagerImpl) 311 if ok { 312 pm.SuppressSanityLogMessages = true 313 defer func() { 314 pm.SuppressSanityLogMessages = false 315 }() 316 } 317 318 return configtx.NewManagerImpl(templateConfig, initializer, nil) 319 }