184 lines
6.0 KiB
Go
184 lines
6.0 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package multichannel
|
|
|
|
import (
|
|
cb "github.com/hyperledger/fabric-protos-go/common"
|
|
"github.com/hyperledger/fabric/bccsp"
|
|
"github.com/hyperledger/fabric/common/ledger/blockledger"
|
|
"github.com/hyperledger/fabric/internal/pkg/identity"
|
|
"github.com/hyperledger/fabric/orderer/common/blockcutter"
|
|
"github.com/hyperledger/fabric/orderer/common/msgprocessor"
|
|
"github.com/hyperledger/fabric/orderer/common/types"
|
|
"github.com/hyperledger/fabric/orderer/consensus"
|
|
"github.com/hyperledger/fabric/protoutil"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// ChainSupport holds the resources for a particular channel.
|
|
type ChainSupport struct {
|
|
*ledgerResources
|
|
msgprocessor.Processor
|
|
*BlockWriter
|
|
consensus.Chain
|
|
cutter blockcutter.Receiver
|
|
identity.SignerSerializer
|
|
BCCSP bccsp.BCCSP
|
|
|
|
// NOTE: It makes sense to add this to the ChainSupport since the design of Registrar does not assume
|
|
// that there is a single consensus type at this orderer node and therefore the resolution of
|
|
// the consensus type too happens only at the ChainSupport level.
|
|
consensus.MetadataValidator
|
|
|
|
// The registrar is not aware of the exact type that the Chain is, e.g. etcdraft, inactive, or follower.
|
|
// Therefore, we let each chain report its cluster relation and status through this interface. Non cluster
|
|
// type chains (solo) are assigned a static reporter.
|
|
consensus.StatusReporter
|
|
}
|
|
|
|
func newChainSupport(
|
|
registrar *Registrar,
|
|
ledgerResources *ledgerResources,
|
|
consenters map[string]consensus.Consenter,
|
|
signer identity.SignerSerializer,
|
|
blockcutterMetrics *blockcutter.Metrics,
|
|
bccsp bccsp.BCCSP,
|
|
) (*ChainSupport, error) {
|
|
// Read in the last block and metadata for the channel
|
|
lastBlock := blockledger.GetBlock(ledgerResources, ledgerResources.Height()-1)
|
|
metadata, err := protoutil.GetConsenterMetadataFromBlock(lastBlock)
|
|
// Assuming a block created with cb.NewBlock(), this should not
|
|
// error even if the orderer metadata is an empty byte slice
|
|
if err != nil {
|
|
return nil, errors.WithMessagef(err, "error extracting orderer metadata for channel: %s", ledgerResources.ConfigtxValidator().ChannelID())
|
|
}
|
|
|
|
// Construct limited support needed as a parameter for additional support
|
|
cs := &ChainSupport{
|
|
ledgerResources: ledgerResources,
|
|
SignerSerializer: signer,
|
|
cutter: blockcutter.NewReceiverImpl(
|
|
ledgerResources.ConfigtxValidator().ChannelID(),
|
|
ledgerResources,
|
|
blockcutterMetrics,
|
|
),
|
|
BCCSP: bccsp,
|
|
}
|
|
|
|
// Set up the msgprocessor
|
|
cs.Processor = msgprocessor.NewStandardChannel(cs, msgprocessor.CreateStandardChannelFilters(cs, registrar.config), bccsp)
|
|
|
|
// Set up the block writer
|
|
cs.BlockWriter = newBlockWriter(lastBlock, cs)
|
|
|
|
// Set up the consenter
|
|
consenterType := ledgerResources.SharedConfig().ConsensusType()
|
|
consenter, ok := consenters[consenterType]
|
|
if !ok {
|
|
return nil, errors.Errorf("error retrieving consenter of type: %s", consenterType)
|
|
}
|
|
|
|
cs.Chain, err = consenter.HandleChain(cs, metadata)
|
|
if err != nil {
|
|
return nil, errors.WithMessagef(err, "error creating consenter for channel: %s", cs.ChannelID())
|
|
}
|
|
|
|
cs.MetadataValidator, ok = cs.Chain.(consensus.MetadataValidator)
|
|
if !ok {
|
|
cs.MetadataValidator = consensus.NoOpMetadataValidator{}
|
|
}
|
|
|
|
cs.StatusReporter, ok = cs.Chain.(consensus.StatusReporter)
|
|
if !ok { // Non-cluster types: solo
|
|
cs.StatusReporter = consensus.StaticStatusReporter{ConsensusRelation: types.ConsensusRelationOther, Status: types.StatusActive}
|
|
}
|
|
|
|
clusterRelation, status := cs.StatusReporter.StatusReport()
|
|
registrar.ReportConsensusRelationAndStatusMetrics(cs.ChannelID(), clusterRelation, status)
|
|
|
|
logger.Debugf("[channel: %s] Done creating channel support resources", cs.ChannelID())
|
|
|
|
return cs, nil
|
|
}
|
|
|
|
func (cs *ChainSupport) Reader() blockledger.Reader {
|
|
return cs
|
|
}
|
|
|
|
// Signer returns the SignerSerializer for this channel.
|
|
func (cs *ChainSupport) Signer() identity.SignerSerializer {
|
|
return cs
|
|
}
|
|
|
|
func (cs *ChainSupport) start() {
|
|
cs.Chain.Start()
|
|
}
|
|
|
|
// BlockCutter returns the blockcutter.Receiver instance for this channel.
|
|
func (cs *ChainSupport) BlockCutter() blockcutter.Receiver {
|
|
return cs.cutter
|
|
}
|
|
|
|
// Validate passes through to the underlying configtx.Validator
|
|
func (cs *ChainSupport) Validate(configEnv *cb.ConfigEnvelope) error {
|
|
return cs.ConfigtxValidator().Validate(configEnv)
|
|
}
|
|
|
|
// ProposeConfigUpdate validates a config update using the underlying configtx.Validator
|
|
// and the consensus.MetadataValidator.
|
|
func (cs *ChainSupport) ProposeConfigUpdate(configtx *cb.Envelope) (*cb.ConfigEnvelope, error) {
|
|
env, err := cs.ConfigtxValidator().ProposeConfigUpdate(configtx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
bundle, err := cs.CreateBundle(cs.ChannelID(), env.Config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = checkResources(bundle); err != nil {
|
|
return nil, errors.WithMessage(err, "config update is not compatible")
|
|
}
|
|
|
|
if err = cs.ValidateNew(bundle); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
oldOrdererConfig, ok := cs.OrdererConfig()
|
|
if !ok {
|
|
logger.Panic("old config is missing orderer group")
|
|
}
|
|
|
|
// we can remove this check since this is being validated in checkResources earlier
|
|
newOrdererConfig, ok := bundle.OrdererConfig()
|
|
if !ok {
|
|
return nil, errors.New("new config is missing orderer group")
|
|
}
|
|
|
|
if err = cs.ValidateConsensusMetadata(oldOrdererConfig, newOrdererConfig, false); err != nil {
|
|
return nil, errors.WithMessage(err, "consensus metadata update for channel config update is invalid")
|
|
}
|
|
return env, nil
|
|
}
|
|
|
|
// ConfigProto passes through to the underlying configtx.Validator
|
|
func (cs *ChainSupport) ConfigProto() *cb.Config {
|
|
return cs.ConfigtxValidator().ConfigProto()
|
|
}
|
|
|
|
// Sequence passes through to the underlying configtx.Validator
|
|
func (cs *ChainSupport) Sequence() uint64 {
|
|
return cs.ConfigtxValidator().Sequence()
|
|
}
|
|
|
|
// Append appends a new block to the ledger in its raw form,
|
|
// unlike WriteBlock that also mutates its metadata.
|
|
func (cs *ChainSupport) Append(block *cb.Block) error {
|
|
return cs.ledgerResources.ReadWriter.Append(block)
|
|
}
|