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