github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/orderer/consensus/etcdraft/consenter.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package etcdraft
     8  
     9  import (
    10  	"bytes"
    11  	"path"
    12  	"reflect"
    13  	"time"
    14  
    15  	"code.cloudfoundry.org/clock"
    16  	"github.com/golang/protobuf/proto"
    17  	"github.com/hechain20/hechain/bccsp"
    18  	"github.com/hechain20/hechain/common/channelconfig"
    19  	"github.com/hechain20/hechain/common/crypto"
    20  	"github.com/hechain20/hechain/common/flogging"
    21  	"github.com/hechain20/hechain/common/metrics"
    22  	"github.com/hechain20/hechain/internal/pkg/comm"
    23  	"github.com/hechain20/hechain/orderer/common/cluster"
    24  	"github.com/hechain20/hechain/orderer/common/localconfig"
    25  	"github.com/hechain20/hechain/orderer/common/types"
    26  	"github.com/hechain20/hechain/orderer/consensus"
    27  	"github.com/hechain20/hechain/orderer/consensus/inactive"
    28  	"github.com/hechain20/hechain/protoutil"
    29  	"github.com/hyperledger/fabric-protos-go/common"
    30  	"github.com/hyperledger/fabric-protos-go/orderer"
    31  	"github.com/hyperledger/fabric-protos-go/orderer/etcdraft"
    32  	"github.com/mitchellh/mapstructure"
    33  	"github.com/pkg/errors"
    34  	"go.etcd.io/etcd/raft"
    35  )
    36  
    37  //go:generate counterfeiter -o mocks/inactive_chain_registry.go --fake-name InactiveChainRegistry . InactiveChainRegistry
    38  
    39  // InactiveChainRegistry registers chains that are inactive
    40  type InactiveChainRegistry interface {
    41  	// TrackChain tracks a chain with the given name, and calls the given callback
    42  	// when this chain should be created.
    43  	TrackChain(chainName string, genesisBlock *common.Block, createChain func())
    44  	// Stop stops the InactiveChainRegistry. This is used when removing the
    45  	// system channel.
    46  	Stop()
    47  }
    48  
    49  //go:generate counterfeiter -o mocks/chain_manager.go --fake-name ChainManager . ChainManager
    50  
    51  // ChainManager defines the methods from multichannel.Registrar needed by the Consenter.
    52  type ChainManager interface {
    53  	GetConsensusChain(channelID string) consensus.Chain
    54  	CreateChain(channelID string)
    55  	SwitchChainToFollower(channelID string)
    56  	ReportConsensusRelationAndStatusMetrics(channelID string, relation types.ConsensusRelation, status types.Status)
    57  }
    58  
    59  // Config contains etcdraft configurations
    60  type Config struct {
    61  	WALDir               string // WAL data of <my-channel> is stored in WALDir/<my-channel>
    62  	SnapDir              string // Snapshots of <my-channel> are stored in SnapDir/<my-channel>
    63  	EvictionSuspicion    string // Duration threshold that the node samples in order to suspect its eviction from the channel.
    64  	TickIntervalOverride string // Duration to use for tick interval instead of what is specified in the channel config.
    65  }
    66  
    67  // Consenter implements etcdraft consenter
    68  type Consenter struct {
    69  	ChainManager          ChainManager
    70  	InactiveChainRegistry InactiveChainRegistry
    71  	Dialer                *cluster.PredicateDialer
    72  	Communication         cluster.Communicator
    73  	*Dispatcher
    74  	Logger         *flogging.FabricLogger
    75  	EtcdRaftConfig Config
    76  	OrdererConfig  localconfig.TopLevel
    77  	Cert           []byte
    78  	Metrics        *Metrics
    79  	BCCSP          bccsp.BCCSP
    80  }
    81  
    82  // TargetChannel extracts the channel from the given proto.Message.
    83  // Returns an empty string on failure.
    84  func (c *Consenter) TargetChannel(message proto.Message) string {
    85  	switch req := message.(type) {
    86  	case *orderer.ConsensusRequest:
    87  		return req.Channel
    88  	case *orderer.SubmitRequest:
    89  		return req.Channel
    90  	default:
    91  		return ""
    92  	}
    93  }
    94  
    95  // ReceiverByChain returns the MessageReceiver for the given channelID or nil
    96  // if not found.
    97  func (c *Consenter) ReceiverByChain(channelID string) MessageReceiver {
    98  	chain := c.ChainManager.GetConsensusChain(channelID)
    99  	if chain == nil {
   100  		return nil
   101  	}
   102  	if etcdRaftChain, isEtcdRaftChain := chain.(*Chain); isEtcdRaftChain {
   103  		return etcdRaftChain
   104  	}
   105  	c.Logger.Warningf("Chain %s is of type %v and not etcdraft.Chain", channelID, reflect.TypeOf(chain))
   106  	return nil
   107  }
   108  
   109  func (c *Consenter) detectSelfID(consenters map[uint64]*etcdraft.Consenter) (uint64, error) {
   110  	thisNodeCertAsDER, err := pemToDER(c.Cert, 0, "server", c.Logger)
   111  	if err != nil {
   112  		return 0, err
   113  	}
   114  
   115  	var serverCertificates []string
   116  	for nodeID, cst := range consenters {
   117  		serverCertificates = append(serverCertificates, string(cst.ServerTlsCert))
   118  
   119  		certAsDER, err := pemToDER(cst.ServerTlsCert, nodeID, "server", c.Logger)
   120  		if err != nil {
   121  			return 0, err
   122  		}
   123  
   124  		if crypto.CertificatesWithSamePublicKey(thisNodeCertAsDER, certAsDER) == nil {
   125  			return nodeID, nil
   126  		}
   127  	}
   128  
   129  	c.Logger.Warning("Could not find", string(c.Cert), "among", serverCertificates)
   130  	return 0, cluster.ErrNotInChannel
   131  }
   132  
   133  // HandleChain returns a new Chain instance or an error upon failure
   134  func (c *Consenter) HandleChain(support consensus.ConsenterSupport, metadata *common.Metadata) (consensus.Chain, error) {
   135  	m := &etcdraft.ConfigMetadata{}
   136  	if err := proto.Unmarshal(support.SharedConfig().ConsensusMetadata(), m); err != nil {
   137  		return nil, errors.Wrap(err, "failed to unmarshal consensus metadata")
   138  	}
   139  
   140  	if m.Options == nil {
   141  		return nil, errors.New("etcdraft options have not been provided")
   142  	}
   143  
   144  	isMigration := (metadata == nil || len(metadata.Value) == 0) && (support.Height() > 1)
   145  	if isMigration {
   146  		c.Logger.Debugf("Block metadata is nil at block height=%d, it is consensus-type migration", support.Height())
   147  	}
   148  
   149  	// determine raft replica set mapping for each node to its id
   150  	// for newly started chain we need to read and initialize raft
   151  	// metadata by creating mapping between conseter and its id.
   152  	// In case chain has been restarted we restore raft metadata
   153  	// information from the recently committed block meta data
   154  	// field.
   155  	blockMetadata, err := ReadBlockMetadata(metadata, m)
   156  	if err != nil {
   157  		return nil, errors.Wrapf(err, "failed to read Raft metadata")
   158  	}
   159  
   160  	consenters := CreateConsentersMap(blockMetadata, m)
   161  
   162  	id, err := c.detectSelfID(consenters)
   163  	if err != nil {
   164  		if c.InactiveChainRegistry != nil {
   165  			// There is a system channel, use the InactiveChainRegistry to track the
   166  			// future config updates of application channel.
   167  			c.InactiveChainRegistry.TrackChain(support.ChannelID(), support.Block(0), func() {
   168  				c.ChainManager.CreateChain(support.ChannelID())
   169  			})
   170  			c.ChainManager.ReportConsensusRelationAndStatusMetrics(support.ChannelID(), types.ConsensusRelationConfigTracker, types.StatusInactive)
   171  			return &inactive.Chain{Err: errors.Errorf("channel %s is not serviced by me", support.ChannelID())}, nil
   172  		}
   173  
   174  		return nil, errors.Wrap(err, "without a system channel, a follower should have been created")
   175  	}
   176  
   177  	var evictionSuspicion time.Duration
   178  	if c.EtcdRaftConfig.EvictionSuspicion == "" {
   179  		c.Logger.Infof("EvictionSuspicion not set, defaulting to %v", DefaultEvictionSuspicion)
   180  		evictionSuspicion = DefaultEvictionSuspicion
   181  	} else {
   182  		evictionSuspicion, err = time.ParseDuration(c.EtcdRaftConfig.EvictionSuspicion)
   183  		if err != nil {
   184  			c.Logger.Panicf("Failed parsing Consensus.EvictionSuspicion: %s: %v", c.EtcdRaftConfig.EvictionSuspicion, err)
   185  		}
   186  	}
   187  
   188  	var tickInterval time.Duration
   189  	if c.EtcdRaftConfig.TickIntervalOverride == "" {
   190  		tickInterval, err = time.ParseDuration(m.Options.TickInterval)
   191  		if err != nil {
   192  			return nil, errors.Errorf("failed to parse TickInterval (%s) to time duration", m.Options.TickInterval)
   193  		}
   194  	} else {
   195  		tickInterval, err = time.ParseDuration(c.EtcdRaftConfig.TickIntervalOverride)
   196  		if err != nil {
   197  			return nil, errors.WithMessage(err, "failed parsing Consensus.TickIntervalOverride")
   198  		}
   199  		c.Logger.Infof("TickIntervalOverride is set, overriding channel configuration tick interval to %v", tickInterval)
   200  	}
   201  
   202  	opts := Options{
   203  		RPCTimeout:    c.OrdererConfig.General.Cluster.RPCTimeout,
   204  		RaftID:        id,
   205  		Clock:         clock.NewClock(),
   206  		MemoryStorage: raft.NewMemoryStorage(),
   207  		Logger:        c.Logger,
   208  
   209  		TickInterval:         tickInterval,
   210  		ElectionTick:         int(m.Options.ElectionTick),
   211  		HeartbeatTick:        int(m.Options.HeartbeatTick),
   212  		MaxInflightBlocks:    int(m.Options.MaxInflightBlocks),
   213  		MaxSizePerMsg:        uint64(support.SharedConfig().BatchSize().PreferredMaxBytes),
   214  		SnapshotIntervalSize: m.Options.SnapshotIntervalSize,
   215  
   216  		BlockMetadata: blockMetadata,
   217  		Consenters:    consenters,
   218  
   219  		MigrationInit: isMigration,
   220  
   221  		WALDir:            path.Join(c.EtcdRaftConfig.WALDir, support.ChannelID()),
   222  		SnapDir:           path.Join(c.EtcdRaftConfig.SnapDir, support.ChannelID()),
   223  		EvictionSuspicion: evictionSuspicion,
   224  		Cert:              c.Cert,
   225  		Metrics:           c.Metrics,
   226  	}
   227  
   228  	rpc := &cluster.RPC{
   229  		Timeout:       c.OrdererConfig.General.Cluster.RPCTimeout,
   230  		Logger:        c.Logger,
   231  		Channel:       support.ChannelID(),
   232  		Comm:          c.Communication,
   233  		StreamsByType: cluster.NewStreamsByType(),
   234  	}
   235  
   236  	var haltCallback func() // called after the etcdraft.Chain halts when it detects eviction form the cluster.
   237  	if c.InactiveChainRegistry != nil {
   238  		// when we have a system channel, we use the InactiveChainRegistry to track membership upon eviction.
   239  		c.Logger.Info("With system channel: after eviction InactiveChainRegistry.TrackChain will be called")
   240  		haltCallback = func() {
   241  			c.InactiveChainRegistry.TrackChain(support.ChannelID(), nil, func() { c.ChainManager.CreateChain(support.ChannelID()) })
   242  			c.ChainManager.ReportConsensusRelationAndStatusMetrics(support.ChannelID(), types.ConsensusRelationConfigTracker, types.StatusInactive)
   243  		}
   244  	} else {
   245  		// when we do NOT have a system channel, we switch to a follower.Chain upon eviction.
   246  		c.Logger.Info("Without system channel: after eviction Registrar.SwitchToFollower will be called")
   247  		haltCallback = func() { c.ChainManager.SwitchChainToFollower(support.ChannelID()) }
   248  	}
   249  
   250  	return NewChain(
   251  		support,
   252  		opts,
   253  		c.Communication,
   254  		rpc,
   255  		c.BCCSP,
   256  		func() (BlockPuller, error) {
   257  			return NewBlockPuller(support, c.Dialer, c.OrdererConfig.General.Cluster, c.BCCSP)
   258  		},
   259  		haltCallback,
   260  		nil,
   261  	)
   262  }
   263  
   264  func (c *Consenter) IsChannelMember(joinBlock *common.Block) (bool, error) {
   265  	if joinBlock == nil {
   266  		return false, errors.New("nil block")
   267  	}
   268  	envelopeConfig, err := protoutil.ExtractEnvelope(joinBlock, 0)
   269  	if err != nil {
   270  		return false, err
   271  	}
   272  	bundle, err := channelconfig.NewBundleFromEnvelope(envelopeConfig, c.BCCSP)
   273  	if err != nil {
   274  		return false, err
   275  	}
   276  	oc, exists := bundle.OrdererConfig()
   277  	if !exists {
   278  		return false, errors.New("no orderer config in bundle")
   279  	}
   280  	configMetadata := &etcdraft.ConfigMetadata{}
   281  	if err := proto.Unmarshal(oc.ConsensusMetadata(), configMetadata); err != nil {
   282  		return false, err
   283  	}
   284  
   285  	verifyOpts, err := createX509VerifyOptions(oc)
   286  	if err != nil {
   287  		return false, errors.Wrapf(err, "failed to create x509 verify options from orderer config")
   288  	}
   289  
   290  	if err := VerifyConfigMetadata(configMetadata, verifyOpts); err != nil {
   291  		return false, errors.Wrapf(err, "failed to validate config metadata of ordering config")
   292  	}
   293  
   294  	member := false
   295  	for _, consenter := range configMetadata.Consenters {
   296  		if bytes.Equal(c.Cert, consenter.ServerTlsCert) || bytes.Equal(c.Cert, consenter.ClientTlsCert) {
   297  			member = true
   298  			break
   299  		}
   300  	}
   301  
   302  	return member, nil
   303  }
   304  
   305  // RemoveInactiveChainRegistry stops and removes the inactive chain registry.
   306  // This is used when removing the system channel.
   307  func (c *Consenter) RemoveInactiveChainRegistry() {
   308  	if c.InactiveChainRegistry == nil {
   309  		return
   310  	}
   311  	c.InactiveChainRegistry.Stop()
   312  	c.InactiveChainRegistry = nil
   313  }
   314  
   315  // ReadBlockMetadata attempts to read raft metadata from block metadata, if available.
   316  // otherwise, it reads raft metadata from config metadata supplied.
   317  func ReadBlockMetadata(blockMetadata *common.Metadata, configMetadata *etcdraft.ConfigMetadata) (*etcdraft.BlockMetadata, error) {
   318  	if blockMetadata != nil && len(blockMetadata.Value) != 0 { // we have consenters mapping from block
   319  		m := &etcdraft.BlockMetadata{}
   320  		if err := proto.Unmarshal(blockMetadata.Value, m); err != nil {
   321  			return nil, errors.Wrap(err, "failed to unmarshal block's metadata")
   322  		}
   323  		return m, nil
   324  	}
   325  
   326  	m := &etcdraft.BlockMetadata{
   327  		NextConsenterId: 1,
   328  		ConsenterIds:    make([]uint64, len(configMetadata.Consenters)),
   329  	}
   330  	// need to read consenters from the configuration
   331  	for i := range m.ConsenterIds {
   332  		m.ConsenterIds[i] = m.NextConsenterId
   333  		m.NextConsenterId++
   334  	}
   335  
   336  	return m, nil
   337  }
   338  
   339  // New creates a etcdraft Consenter
   340  func New(
   341  	clusterDialer *cluster.PredicateDialer,
   342  	conf *localconfig.TopLevel,
   343  	srvConf comm.ServerConfig,
   344  	srv *comm.GRPCServer,
   345  	registrar ChainManager,
   346  	icr InactiveChainRegistry,
   347  	metricsProvider metrics.Provider,
   348  	bccsp bccsp.BCCSP,
   349  ) *Consenter {
   350  	logger := flogging.MustGetLogger("orderer.consensus.etcdraft")
   351  
   352  	var cfg Config
   353  	err := mapstructure.Decode(conf.Consensus, &cfg)
   354  	if err != nil {
   355  		logger.Panicf("Failed to decode etcdraft configuration: %s", err)
   356  	}
   357  
   358  	consenter := &Consenter{
   359  		ChainManager:          registrar,
   360  		Cert:                  srvConf.SecOpts.Certificate,
   361  		Logger:                logger,
   362  		EtcdRaftConfig:        cfg,
   363  		OrdererConfig:         *conf,
   364  		Dialer:                clusterDialer,
   365  		Metrics:               NewMetrics(metricsProvider),
   366  		InactiveChainRegistry: icr,
   367  		BCCSP:                 bccsp,
   368  	}
   369  	consenter.Dispatcher = &Dispatcher{
   370  		Logger:        logger,
   371  		ChainSelector: consenter,
   372  	}
   373  
   374  	comm := createComm(clusterDialer, consenter, conf.General.Cluster, metricsProvider)
   375  	consenter.Communication = comm
   376  	svc := &cluster.Service{
   377  		CertExpWarningThreshold:          conf.General.Cluster.CertExpirationWarningThreshold,
   378  		MinimumExpirationWarningInterval: cluster.MinimumExpirationWarningInterval,
   379  		StreamCountReporter: &cluster.StreamCountReporter{
   380  			Metrics: comm.Metrics,
   381  		},
   382  		StepLogger: flogging.MustGetLogger("orderer.common.cluster.step"),
   383  		Logger:     flogging.MustGetLogger("orderer.common.cluster"),
   384  		Dispatcher: comm,
   385  	}
   386  	orderer.RegisterClusterServer(srv.Server(), svc)
   387  
   388  	if icr == nil {
   389  		logger.Debug("Created an etcdraft consenter without a system channel, InactiveChainRegistry is nil")
   390  	}
   391  
   392  	return consenter
   393  }
   394  
   395  func createComm(clusterDialer *cluster.PredicateDialer, c *Consenter, config localconfig.Cluster, p metrics.Provider) *cluster.Comm {
   396  	metrics := cluster.NewMetrics(p)
   397  	logger := flogging.MustGetLogger("orderer.common.cluster")
   398  
   399  	compareCert := cluster.CachePublicKeyComparisons(func(a, b []byte) bool {
   400  		err := crypto.CertificatesWithSamePublicKey(a, b)
   401  		if err != nil && err != crypto.ErrPubKeyMismatch {
   402  			crypto.LogNonPubKeyMismatchErr(logger.Errorf, err, a, b)
   403  		}
   404  		return err == nil
   405  	})
   406  
   407  	comm := &cluster.Comm{
   408  		MinimumExpirationWarningInterval: cluster.MinimumExpirationWarningInterval,
   409  		CertExpWarningThreshold:          config.CertExpirationWarningThreshold,
   410  		SendBufferSize:                   config.SendBufferSize,
   411  		Logger:                           logger,
   412  		Chan2Members:                     make(map[string]cluster.MemberMapping),
   413  		Connections:                      cluster.NewConnectionStore(clusterDialer, metrics.EgressTLSConnectionCount),
   414  		Metrics:                          metrics,
   415  		ChanExt:                          c,
   416  		H:                                c,
   417  		CompareCertificate:               compareCert,
   418  	}
   419  	c.Communication = comm
   420  	return comm
   421  }