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  }