github.com/kaituanwang/hyperledger@v2.0.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.HeartbeatTick == 0 || 208 metadata.Options.ElectionTick == 0 || 209 metadata.Options.MaxInflightBlocks == 0 { 210 // if SnapshotIntervalSize is zero, DefaultSnapshotIntervalSize is used 211 return errors.Errorf("none of HeartbeatTick (%d), ElectionTick (%d) and MaxInflightBlocks (%d) can be zero", 212 metadata.Options.HeartbeatTick, metadata.Options.ElectionTick, metadata.Options.MaxInflightBlocks) 213 } 214 215 // check Raft options 216 if metadata.Options.ElectionTick <= metadata.Options.HeartbeatTick { 217 return errors.Errorf("ElectionTick (%d) must be greater than HeartbeatTick (%d)", 218 metadata.Options.HeartbeatTick, metadata.Options.HeartbeatTick) 219 } 220 221 if d, err := time.ParseDuration(metadata.Options.TickInterval); err != nil { 222 return errors.Errorf("failed to parse TickInterval (%s) to time duration: %s", metadata.Options.TickInterval, err) 223 } else if d == 0 { 224 return errors.Errorf("TickInterval cannot be zero") 225 } 226 227 if len(metadata.Consenters) == 0 { 228 return errors.Errorf("empty consenter set") 229 } 230 231 // sanity check of certificates 232 for _, consenter := range metadata.Consenters { 233 if consenter == nil { 234 return errors.Errorf("metadata has nil consenter") 235 } 236 if err := validateCert(consenter.ServerTlsCert, "server"); err != nil { 237 return err 238 } 239 if err := validateCert(consenter.ClientTlsCert, "client"); err != nil { 240 return err 241 } 242 } 243 244 if err := MetadataHasDuplication(metadata); err != nil { 245 return err 246 } 247 248 return nil 249 } 250 251 func validateCert(pemData []byte, certRole string) error { 252 bl, _ := pem.Decode(pemData) 253 254 if bl == nil { 255 return errors.Errorf("%s TLS certificate is not PEM encoded: %s", certRole, string(pemData)) 256 } 257 258 if _, err := x509.ParseCertificate(bl.Bytes); err != nil { 259 return errors.Errorf("%s TLS certificate has invalid ASN1 structure, %v: %s", certRole, err, string(pemData)) 260 } 261 return nil 262 } 263 264 // ConsenterCertificate denotes a TLS certificate of a consenter 265 type ConsenterCertificate struct { 266 ConsenterCertificate []byte 267 CryptoProvider bccsp.BCCSP 268 } 269 270 // type ConsenterCertificate []byte 271 272 // IsConsenterOfChannel returns whether the caller is a consenter of a channel 273 // by inspecting the given configuration block. 274 // It returns nil if true, else returns an error. 275 func (conCert ConsenterCertificate) IsConsenterOfChannel(configBlock *common.Block) error { 276 if configBlock == nil { 277 return errors.New("nil block") 278 } 279 envelopeConfig, err := protoutil.ExtractEnvelope(configBlock, 0) 280 if err != nil { 281 return err 282 } 283 bundle, err := channelconfig.NewBundleFromEnvelope(envelopeConfig, conCert.CryptoProvider) 284 if err != nil { 285 return err 286 } 287 oc, exists := bundle.OrdererConfig() 288 if !exists { 289 return errors.New("no orderer config in bundle") 290 } 291 m := &etcdraft.ConfigMetadata{} 292 if err := proto.Unmarshal(oc.ConsensusMetadata(), m); err != nil { 293 return err 294 } 295 296 for _, consenter := range m.Consenters { 297 if bytes.Equal(conCert.ConsenterCertificate, consenter.ServerTlsCert) || bytes.Equal(conCert.ConsenterCertificate, consenter.ClientTlsCert) { 298 return nil 299 } 300 } 301 return cluster.ErrNotInChannel 302 } 303 304 // NodeExists returns trues if node id exists in the slice 305 // and false otherwise 306 func NodeExists(id uint64, nodes []uint64) bool { 307 for _, nodeID := range nodes { 308 if nodeID == id { 309 return true 310 } 311 } 312 return false 313 } 314 315 // ConfChange computes Raft configuration changes based on current Raft 316 // configuration state and consenters IDs stored in RaftMetadata. 317 func ConfChange(blockMetadata *etcdraft.BlockMetadata, confState *raftpb.ConfState) *raftpb.ConfChange { 318 raftConfChange := &raftpb.ConfChange{} 319 320 // need to compute conf changes to propose 321 if len(confState.Nodes) < len(blockMetadata.ConsenterIds) { 322 // adding new node 323 raftConfChange.Type = raftpb.ConfChangeAddNode 324 for _, consenterID := range blockMetadata.ConsenterIds { 325 if NodeExists(consenterID, confState.Nodes) { 326 continue 327 } 328 raftConfChange.NodeID = consenterID 329 } 330 } else { 331 // removing node 332 raftConfChange.Type = raftpb.ConfChangeRemoveNode 333 for _, nodeID := range confState.Nodes { 334 if NodeExists(nodeID, blockMetadata.ConsenterIds) { 335 continue 336 } 337 raftConfChange.NodeID = nodeID 338 } 339 } 340 341 return raftConfChange 342 } 343 344 // CreateConsentersMap creates a map of Raft Node IDs to Consenter given the block metadata and the config metadata. 345 func CreateConsentersMap(blockMetadata *etcdraft.BlockMetadata, configMetadata *etcdraft.ConfigMetadata) map[uint64]*etcdraft.Consenter { 346 consenters := map[uint64]*etcdraft.Consenter{} 347 for i, consenter := range configMetadata.Consenters { 348 consenters[blockMetadata.ConsenterIds[i]] = consenter 349 } 350 return consenters 351 }