github.com/true-sqn/fabric@v2.1.1+incompatible/orderer/consensus/etcdraft/util.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package etcdraft
     8  
     9  import (
    10  	"bytes"
    11  	"crypto/x509"
    12  	"encoding/pem"
    13  	"time"
    14  
    15  	"github.com/golang/protobuf/proto"
    16  	"github.com/hyperledger/fabric-protos-go/common"
    17  	"github.com/hyperledger/fabric-protos-go/orderer"
    18  	"github.com/hyperledger/fabric-protos-go/orderer/etcdraft"
    19  	"github.com/hyperledger/fabric/bccsp"
    20  	"github.com/hyperledger/fabric/common/channelconfig"
    21  	"github.com/hyperledger/fabric/common/configtx"
    22  	"github.com/hyperledger/fabric/orderer/common/cluster"
    23  	"github.com/hyperledger/fabric/protoutil"
    24  	"github.com/pkg/errors"
    25  	"go.etcd.io/etcd/raft"
    26  	"go.etcd.io/etcd/raft/raftpb"
    27  )
    28  
    29  // RaftPeers maps consenters to slice of raft.Peer
    30  func RaftPeers(consenterIDs []uint64) []raft.Peer {
    31  	var peers []raft.Peer
    32  
    33  	for _, raftID := range consenterIDs {
    34  		peers = append(peers, raft.Peer{ID: raftID})
    35  	}
    36  	return peers
    37  }
    38  
    39  // ConsentersToMap maps consenters into set where key is client TLS certificate
    40  func ConsentersToMap(consenters []*etcdraft.Consenter) map[string]struct{} {
    41  	set := map[string]struct{}{}
    42  	for _, c := range consenters {
    43  		set[string(c.ClientTlsCert)] = struct{}{}
    44  	}
    45  	return set
    46  }
    47  
    48  // MetadataHasDuplication returns an error if the metadata has duplication of consenters.
    49  // A duplication is defined by having a server or a client TLS certificate that is found
    50  // in two different consenters, regardless of the type of certificate (client/server).
    51  func MetadataHasDuplication(md *etcdraft.ConfigMetadata) error {
    52  	if md == nil {
    53  		return errors.New("nil metadata")
    54  	}
    55  
    56  	for _, consenter := range md.Consenters {
    57  		if consenter == nil {
    58  			return errors.New("nil consenter in metadata")
    59  		}
    60  	}
    61  
    62  	seen := make(map[string]struct{})
    63  	for _, consenter := range md.Consenters {
    64  		serverKey := string(consenter.ServerTlsCert)
    65  		clientKey := string(consenter.ClientTlsCert)
    66  		_, duplicateServerCert := seen[serverKey]
    67  		_, duplicateClientCert := seen[clientKey]
    68  		if duplicateServerCert || duplicateClientCert {
    69  			return errors.Errorf("duplicate consenter: server cert: %s, client cert: %s", serverKey, clientKey)
    70  		}
    71  
    72  		seen[serverKey] = struct{}{}
    73  		seen[clientKey] = struct{}{}
    74  	}
    75  	return nil
    76  }
    77  
    78  // MetadataFromConfigValue reads and translates configuration updates from config value into raft metadata
    79  func MetadataFromConfigValue(configValue *common.ConfigValue) (*etcdraft.ConfigMetadata, error) {
    80  	consensusTypeValue := &orderer.ConsensusType{}
    81  	if err := proto.Unmarshal(configValue.Value, consensusTypeValue); err != nil {
    82  		return nil, errors.Wrap(err, "failed to unmarshal consensusType config update")
    83  	}
    84  
    85  	updatedMetadata := &etcdraft.ConfigMetadata{}
    86  	if err := proto.Unmarshal(consensusTypeValue.Metadata, updatedMetadata); err != nil {
    87  		return nil, errors.Wrap(err, "failed to unmarshal updated (new) etcdraft metadata configuration")
    88  	}
    89  
    90  	return updatedMetadata, nil
    91  }
    92  
    93  // MetadataFromConfigUpdate extracts consensus metadata from config update
    94  func MetadataFromConfigUpdate(update *common.ConfigUpdate) (*etcdraft.ConfigMetadata, error) {
    95  	var baseVersion uint64
    96  	if update.ReadSet != nil && update.ReadSet.Groups != nil {
    97  		if ordererConfigGroup, ok := update.ReadSet.Groups["Orderer"]; ok {
    98  			if val, ok := ordererConfigGroup.Values["ConsensusType"]; ok {
    99  				baseVersion = val.Version
   100  			}
   101  		}
   102  	}
   103  
   104  	if update.WriteSet != nil && update.WriteSet.Groups != nil {
   105  		if ordererConfigGroup, ok := update.WriteSet.Groups["Orderer"]; ok {
   106  			if val, ok := ordererConfigGroup.Values["ConsensusType"]; ok {
   107  				if baseVersion == val.Version {
   108  					// Only if the version in the write set differs from the read-set
   109  					// should we consider this to be an update to the consensus type
   110  					return nil, nil
   111  				}
   112  				return MetadataFromConfigValue(val)
   113  			}
   114  		}
   115  	}
   116  	return nil, nil
   117  }
   118  
   119  // ConfigChannelHeader expects a config block and returns the header type
   120  // of the config envelope wrapped in it, e.g. HeaderType_ORDERER_TRANSACTION
   121  func ConfigChannelHeader(block *common.Block) (hdr *common.ChannelHeader, err error) {
   122  	envelope, err := protoutil.ExtractEnvelope(block, 0)
   123  	if err != nil {
   124  		return nil, errors.Wrap(err, "failed to extract envelope from the block")
   125  	}
   126  
   127  	channelHeader, err := protoutil.ChannelHeader(envelope)
   128  	if err != nil {
   129  		return nil, errors.Wrap(err, "cannot extract channel header")
   130  	}
   131  
   132  	return channelHeader, nil
   133  }
   134  
   135  // ConfigEnvelopeFromBlock extracts configuration envelope from the block based on the
   136  // config type, i.e. HeaderType_ORDERER_TRANSACTION or HeaderType_CONFIG
   137  func ConfigEnvelopeFromBlock(block *common.Block) (*common.Envelope, error) {
   138  	if block == nil {
   139  		return nil, errors.New("nil block")
   140  	}
   141  
   142  	envelope, err := protoutil.ExtractEnvelope(block, 0)
   143  	if err != nil {
   144  		return nil, errors.Wrapf(err, "failed to extract envelope from the block")
   145  	}
   146  
   147  	channelHeader, err := protoutil.ChannelHeader(envelope)
   148  	if err != nil {
   149  		return nil, errors.Wrap(err, "cannot extract channel header")
   150  	}
   151  
   152  	switch channelHeader.Type {
   153  	case int32(common.HeaderType_ORDERER_TRANSACTION):
   154  		payload, err := protoutil.UnmarshalPayload(envelope.Payload)
   155  		if err != nil {
   156  			return nil, errors.Wrap(err, "failed to unmarshal envelope to extract config payload for orderer transaction")
   157  		}
   158  		configEnvelop, err := protoutil.UnmarshalEnvelope(payload.Data)
   159  		if err != nil {
   160  			return nil, errors.Wrap(err, "failed to unmarshal config envelope for orderer type transaction")
   161  		}
   162  
   163  		return configEnvelop, nil
   164  	case int32(common.HeaderType_CONFIG):
   165  		return envelope, nil
   166  	default:
   167  		return nil, errors.Errorf("unexpected header type: %v", channelHeader.Type)
   168  	}
   169  }
   170  
   171  // ConsensusMetadataFromConfigBlock reads consensus metadata updates from the configuration block
   172  func ConsensusMetadataFromConfigBlock(block *common.Block) (*etcdraft.ConfigMetadata, error) {
   173  	if block == nil {
   174  		return nil, errors.New("nil block")
   175  	}
   176  
   177  	if !protoutil.IsConfigBlock(block) {
   178  		return nil, errors.New("not a config block")
   179  	}
   180  
   181  	configEnvelope, err := ConfigEnvelopeFromBlock(block)
   182  	if err != nil {
   183  		return nil, errors.Wrap(err, "cannot read config update")
   184  	}
   185  
   186  	payload, err := protoutil.UnmarshalPayload(configEnvelope.Payload)
   187  	if err != nil {
   188  		return nil, errors.Wrap(err, "failed to extract payload from config envelope")
   189  	}
   190  	// get config update
   191  	configUpdate, err := configtx.UnmarshalConfigUpdateFromPayload(payload)
   192  	if err != nil {
   193  		return nil, errors.Wrap(err, "could not read config update")
   194  	}
   195  
   196  	return MetadataFromConfigUpdate(configUpdate)
   197  }
   198  
   199  // CheckConfigMetadata validates Raft config metadata
   200  func CheckConfigMetadata(metadata *etcdraft.ConfigMetadata) error {
   201  	if metadata == nil {
   202  		// defensive check. this should not happen as CheckConfigMetadata
   203  		// should always be called with non-nil config metadata
   204  		return errors.Errorf("nil Raft config metadata")
   205  	}
   206  
   207  	if metadata.Options == nil {
   208  		return errors.Errorf("nil Raft config metadata options")
   209  	}
   210  
   211  	if metadata.Options.HeartbeatTick == 0 ||
   212  		metadata.Options.ElectionTick == 0 ||
   213  		metadata.Options.MaxInflightBlocks == 0 {
   214  		// if SnapshotIntervalSize is zero, DefaultSnapshotIntervalSize is used
   215  		return errors.Errorf("none of HeartbeatTick (%d), ElectionTick (%d) and MaxInflightBlocks (%d) can be zero",
   216  			metadata.Options.HeartbeatTick, metadata.Options.ElectionTick, metadata.Options.MaxInflightBlocks)
   217  	}
   218  
   219  	// check Raft options
   220  	if metadata.Options.ElectionTick <= metadata.Options.HeartbeatTick {
   221  		return errors.Errorf("ElectionTick (%d) must be greater than HeartbeatTick (%d)",
   222  			metadata.Options.ElectionTick, metadata.Options.HeartbeatTick)
   223  	}
   224  
   225  	if d, err := time.ParseDuration(metadata.Options.TickInterval); err != nil {
   226  		return errors.Errorf("failed to parse TickInterval (%s) to time duration: %s", metadata.Options.TickInterval, err)
   227  	} else if d == 0 {
   228  		return errors.Errorf("TickInterval cannot be zero")
   229  	}
   230  
   231  	if len(metadata.Consenters) == 0 {
   232  		return errors.Errorf("empty consenter set")
   233  	}
   234  
   235  	// sanity check of certificates
   236  	for _, consenter := range metadata.Consenters {
   237  		if consenter == nil {
   238  			return errors.Errorf("metadata has nil consenter")
   239  		}
   240  		if err := validateCert(consenter.ServerTlsCert, "server"); err != nil {
   241  			return err
   242  		}
   243  		if err := validateCert(consenter.ClientTlsCert, "client"); err != nil {
   244  			return err
   245  		}
   246  	}
   247  
   248  	if err := MetadataHasDuplication(metadata); err != nil {
   249  		return err
   250  	}
   251  
   252  	return nil
   253  }
   254  
   255  func validateCert(pemData []byte, certRole string) error {
   256  	bl, _ := pem.Decode(pemData)
   257  
   258  	if bl == nil {
   259  		return errors.Errorf("%s TLS certificate is not PEM encoded: %s", certRole, string(pemData))
   260  	}
   261  
   262  	if _, err := x509.ParseCertificate(bl.Bytes); err != nil {
   263  		return errors.Errorf("%s TLS certificate has invalid ASN1 structure, %v: %s", certRole, err, string(pemData))
   264  	}
   265  	return nil
   266  }
   267  
   268  // ConsenterCertificate denotes a TLS certificate of a consenter
   269  type ConsenterCertificate struct {
   270  	ConsenterCertificate []byte
   271  	CryptoProvider       bccsp.BCCSP
   272  }
   273  
   274  // type ConsenterCertificate []byte
   275  
   276  // IsConsenterOfChannel returns whether the caller is a consenter of a channel
   277  // by inspecting the given configuration block.
   278  // It returns nil if true, else returns an error.
   279  func (conCert ConsenterCertificate) IsConsenterOfChannel(configBlock *common.Block) error {
   280  	if configBlock == nil {
   281  		return errors.New("nil block")
   282  	}
   283  	envelopeConfig, err := protoutil.ExtractEnvelope(configBlock, 0)
   284  	if err != nil {
   285  		return err
   286  	}
   287  	bundle, err := channelconfig.NewBundleFromEnvelope(envelopeConfig, conCert.CryptoProvider)
   288  	if err != nil {
   289  		return err
   290  	}
   291  	oc, exists := bundle.OrdererConfig()
   292  	if !exists {
   293  		return errors.New("no orderer config in bundle")
   294  	}
   295  	m := &etcdraft.ConfigMetadata{}
   296  	if err := proto.Unmarshal(oc.ConsensusMetadata(), m); err != nil {
   297  		return err
   298  	}
   299  
   300  	for _, consenter := range m.Consenters {
   301  		if bytes.Equal(conCert.ConsenterCertificate, consenter.ServerTlsCert) || bytes.Equal(conCert.ConsenterCertificate, consenter.ClientTlsCert) {
   302  			return nil
   303  		}
   304  	}
   305  	return cluster.ErrNotInChannel
   306  }
   307  
   308  // NodeExists returns trues if node id exists in the slice
   309  // and false otherwise
   310  func NodeExists(id uint64, nodes []uint64) bool {
   311  	for _, nodeID := range nodes {
   312  		if nodeID == id {
   313  			return true
   314  		}
   315  	}
   316  	return false
   317  }
   318  
   319  // ConfChange computes Raft configuration changes based on current Raft
   320  // configuration state and consenters IDs stored in RaftMetadata.
   321  func ConfChange(blockMetadata *etcdraft.BlockMetadata, confState *raftpb.ConfState) *raftpb.ConfChange {
   322  	raftConfChange := &raftpb.ConfChange{}
   323  
   324  	// need to compute conf changes to propose
   325  	if len(confState.Nodes) < len(blockMetadata.ConsenterIds) {
   326  		// adding new node
   327  		raftConfChange.Type = raftpb.ConfChangeAddNode
   328  		for _, consenterID := range blockMetadata.ConsenterIds {
   329  			if NodeExists(consenterID, confState.Nodes) {
   330  				continue
   331  			}
   332  			raftConfChange.NodeID = consenterID
   333  		}
   334  	} else {
   335  		// removing node
   336  		raftConfChange.Type = raftpb.ConfChangeRemoveNode
   337  		for _, nodeID := range confState.Nodes {
   338  			if NodeExists(nodeID, blockMetadata.ConsenterIds) {
   339  				continue
   340  			}
   341  			raftConfChange.NodeID = nodeID
   342  		}
   343  	}
   344  
   345  	return raftConfChange
   346  }
   347  
   348  // CreateConsentersMap creates a map of Raft Node IDs to Consenter given the block metadata and the config metadata.
   349  func CreateConsentersMap(blockMetadata *etcdraft.BlockMetadata, configMetadata *etcdraft.ConfigMetadata) map[uint64]*etcdraft.Consenter {
   350  	consenters := map[uint64]*etcdraft.Consenter{}
   351  	for i, consenter := range configMetadata.Consenters {
   352  		consenters[blockMetadata.ConsenterIds[i]] = consenter
   353  	}
   354  	return consenters
   355  }