github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/orderer/consensus/etcdraft/chain.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 "context" 11 "encoding/pem" 12 "fmt" 13 "github.com/osdi23p228/fabric/common/channelconfig" 14 "sync" 15 "sync/atomic" 16 "time" 17 18 "github.com/osdi23p228/fabric/orderer/common/types" 19 20 "code.cloudfoundry.org/clock" 21 "github.com/golang/protobuf/proto" 22 "github.com/hyperledger/fabric-protos-go/common" 23 "github.com/hyperledger/fabric-protos-go/orderer" 24 "github.com/hyperledger/fabric-protos-go/orderer/etcdraft" 25 "github.com/osdi23p228/fabric/bccsp" 26 "github.com/osdi23p228/fabric/common/flogging" 27 "github.com/osdi23p228/fabric/orderer/common/cluster" 28 "github.com/osdi23p228/fabric/orderer/consensus" 29 "github.com/osdi23p228/fabric/protoutil" 30 "github.com/pkg/errors" 31 "go.etcd.io/etcd/raft" 32 "go.etcd.io/etcd/raft/raftpb" 33 "go.etcd.io/etcd/wal" 34 ) 35 36 const ( 37 BYTE = 1 << (10 * iota) 38 KILOBYTE 39 MEGABYTE 40 GIGABYTE 41 TERABYTE 42 ) 43 44 const ( 45 // DefaultSnapshotCatchUpEntries is the default number of entries 46 // to preserve in memory when a snapshot is taken. This is for 47 // slow followers to catch up. 48 DefaultSnapshotCatchUpEntries = uint64(4) 49 50 // DefaultSnapshotIntervalSize is the default snapshot interval. It is 51 // used if SnapshotIntervalSize is not provided in channel config options. 52 // It is needed to enforce snapshot being set. 53 DefaultSnapshotIntervalSize = 16 * MEGABYTE 54 55 // DefaultEvictionSuspicion is the threshold that a node will start 56 // suspecting its own eviction if it has been leaderless for this 57 // period of time. 58 DefaultEvictionSuspicion = time.Minute * 10 59 60 // DefaultLeaderlessCheckInterval is the interval that a chain checks 61 // its own leadership status. 62 DefaultLeaderlessCheckInterval = time.Second * 10 63 ) 64 65 //go:generate counterfeiter -o mocks/configurator.go . Configurator 66 67 // Configurator is used to configure the communication layer 68 // when the chain starts. 69 type Configurator interface { 70 Configure(channel string, newNodes []cluster.RemoteNode) 71 } 72 73 //go:generate counterfeiter -o mocks/mock_rpc.go . RPC 74 75 // RPC is used to mock the transport layer in tests. 76 type RPC interface { 77 SendConsensus(dest uint64, msg *orderer.ConsensusRequest) error 78 SendSubmit(dest uint64, request *orderer.SubmitRequest) error 79 } 80 81 //go:generate counterfeiter -o mocks/mock_blockpuller.go . BlockPuller 82 83 // BlockPuller is used to pull blocks from other OSN 84 type BlockPuller interface { 85 PullBlock(seq uint64) *common.Block 86 HeightsByEndpoints() (map[string]uint64, error) 87 Close() 88 } 89 90 // CreateBlockPuller is a function to create BlockPuller on demand. 91 // It is passed into chain initializer so that tests could mock this. 92 type CreateBlockPuller func() (BlockPuller, error) 93 94 // Options contains all the configurations relevant to the chain. 95 type Options struct { 96 RaftID uint64 97 98 Clock clock.Clock 99 100 WALDir string 101 SnapDir string 102 SnapshotIntervalSize uint32 103 104 // This is configurable mainly for testing purpose. Users are not 105 // expected to alter this. Instead, DefaultSnapshotCatchUpEntries is used. 106 SnapshotCatchUpEntries uint64 107 108 MemoryStorage MemoryStorage 109 Logger *flogging.FabricLogger 110 111 TickInterval time.Duration 112 ElectionTick int 113 HeartbeatTick int 114 MaxSizePerMsg uint64 115 MaxInflightBlocks int 116 117 // BlockMetdata and Consenters should only be modified while under lock 118 // of raftMetadataLock 119 BlockMetadata *etcdraft.BlockMetadata 120 Consenters map[uint64]*etcdraft.Consenter 121 122 // MigrationInit is set when the node starts right after consensus-type migration 123 MigrationInit bool 124 125 Metrics *Metrics 126 Cert []byte 127 128 EvictionSuspicion time.Duration 129 LeaderCheckInterval time.Duration 130 } 131 132 type submit struct { 133 req *orderer.SubmitRequest 134 leader chan uint64 135 } 136 137 type gc struct { 138 index uint64 139 state raftpb.ConfState 140 data []byte 141 } 142 143 // Chain implements consensus.Chain interface. 144 type Chain struct { 145 configurator Configurator 146 147 rpc RPC 148 149 raftID uint64 150 channelID string 151 152 lastKnownLeader uint64 153 ActiveNodes atomic.Value 154 155 submitC chan *submit 156 applyC chan apply 157 observeC chan<- raft.SoftState // Notifies external observer on leader change (passed in optionally as an argument for tests) 158 haltC chan struct{} // Signals to goroutines that the chain is halting 159 doneC chan struct{} // Closes when the chain halts 160 startC chan struct{} // Closes when the node is started 161 snapC chan *raftpb.Snapshot // Signal to catch up with snapshot 162 gcC chan *gc // Signal to take snapshot 163 164 errorCLock sync.RWMutex 165 errorC chan struct{} // returned by Errored() 166 167 raftMetadataLock sync.RWMutex 168 confChangeInProgress *raftpb.ConfChange 169 justElected bool // this is true when node has just been elected 170 configInflight bool // this is true when there is config block or ConfChange in flight 171 blockInflight int // number of in flight blocks 172 173 clock clock.Clock // Tests can inject a fake clock 174 175 support consensus.ConsenterSupport 176 177 lastBlock *common.Block 178 appliedIndex uint64 179 180 // needed by snapshotting 181 sizeLimit uint32 // SnapshotIntervalSize in bytes 182 accDataSize uint32 // accumulative data size since last snapshot 183 lastSnapBlockNum uint64 184 confState raftpb.ConfState // Etcdraft requires ConfState to be persisted within snapshot 185 186 createPuller CreateBlockPuller // func used to create BlockPuller on demand 187 188 fresh bool // indicate if this is a fresh raft node 189 190 // this is exported so that test can use `Node.Status()` to get raft node status. 191 Node *node 192 opts Options 193 194 Metrics *Metrics 195 logger *flogging.FabricLogger 196 197 periodicChecker *PeriodicCheck 198 199 haltCallback func() 200 // BCCSP instane 201 CryptoProvider bccsp.BCCSP 202 } 203 204 // NewChain constructs a chain object. 205 func NewChain( 206 support consensus.ConsenterSupport, 207 opts Options, 208 conf Configurator, 209 rpc RPC, 210 cryptoProvider bccsp.BCCSP, 211 f CreateBlockPuller, 212 haltCallback func(), 213 observeC chan<- raft.SoftState, 214 ) (*Chain, error) { 215 216 lg := opts.Logger.With("channel", support.ChannelID(), "node", opts.RaftID) 217 218 fresh := !wal.Exist(opts.WALDir) 219 storage, err := CreateStorage(lg, opts.WALDir, opts.SnapDir, opts.MemoryStorage) 220 if err != nil { 221 return nil, errors.Errorf("failed to restore persisted raft data: %s", err) 222 } 223 224 if opts.SnapshotCatchUpEntries == 0 { 225 storage.SnapshotCatchUpEntries = DefaultSnapshotCatchUpEntries 226 } else { 227 storage.SnapshotCatchUpEntries = opts.SnapshotCatchUpEntries 228 } 229 230 sizeLimit := opts.SnapshotIntervalSize 231 if sizeLimit == 0 { 232 sizeLimit = DefaultSnapshotIntervalSize 233 } 234 235 // get block number in last snapshot, if exists 236 var snapBlkNum uint64 237 var cc raftpb.ConfState 238 if s := storage.Snapshot(); !raft.IsEmptySnap(s) { 239 b := protoutil.UnmarshalBlockOrPanic(s.Data) 240 snapBlkNum = b.Header.Number 241 cc = s.Metadata.ConfState 242 } 243 244 b := support.Block(support.Height() - 1) 245 if b == nil { 246 return nil, errors.Errorf("failed to get last block") 247 } 248 249 c := &Chain{ 250 configurator: conf, 251 rpc: rpc, 252 channelID: support.ChannelID(), 253 raftID: opts.RaftID, 254 submitC: make(chan *submit), 255 applyC: make(chan apply), 256 haltC: make(chan struct{}), 257 doneC: make(chan struct{}), 258 startC: make(chan struct{}), 259 snapC: make(chan *raftpb.Snapshot), 260 errorC: make(chan struct{}), 261 gcC: make(chan *gc), 262 observeC: observeC, 263 support: support, 264 fresh: fresh, 265 appliedIndex: opts.BlockMetadata.RaftIndex, 266 lastBlock: b, 267 sizeLimit: sizeLimit, 268 lastSnapBlockNum: snapBlkNum, 269 confState: cc, 270 createPuller: f, 271 clock: opts.Clock, 272 haltCallback: haltCallback, 273 Metrics: &Metrics{ 274 ClusterSize: opts.Metrics.ClusterSize.With("channel", support.ChannelID()), 275 IsLeader: opts.Metrics.IsLeader.With("channel", support.ChannelID()), 276 ActiveNodes: opts.Metrics.ActiveNodes.With("channel", support.ChannelID()), 277 CommittedBlockNumber: opts.Metrics.CommittedBlockNumber.With("channel", support.ChannelID()), 278 SnapshotBlockNumber: opts.Metrics.SnapshotBlockNumber.With("channel", support.ChannelID()), 279 LeaderChanges: opts.Metrics.LeaderChanges.With("channel", support.ChannelID()), 280 ProposalFailures: opts.Metrics.ProposalFailures.With("channel", support.ChannelID()), 281 DataPersistDuration: opts.Metrics.DataPersistDuration.With("channel", support.ChannelID()), 282 NormalProposalsReceived: opts.Metrics.NormalProposalsReceived.With("channel", support.ChannelID()), 283 ConfigProposalsReceived: opts.Metrics.ConfigProposalsReceived.With("channel", support.ChannelID()), 284 }, 285 logger: lg, 286 opts: opts, 287 CryptoProvider: cryptoProvider, 288 } 289 290 // Sets initial values for metrics 291 c.Metrics.ClusterSize.Set(float64(len(c.opts.BlockMetadata.ConsenterIds))) 292 c.Metrics.IsLeader.Set(float64(0)) // all nodes start out as followers 293 c.Metrics.ActiveNodes.Set(float64(0)) 294 c.Metrics.CommittedBlockNumber.Set(float64(c.lastBlock.Header.Number)) 295 c.Metrics.SnapshotBlockNumber.Set(float64(c.lastSnapBlockNum)) 296 297 // DO NOT use Applied option in config, see https://github.com/etcd-io/etcd/issues/10217 298 // We guard against replay of written blocks with `appliedIndex` instead. 299 config := &raft.Config{ 300 ID: c.raftID, 301 ElectionTick: c.opts.ElectionTick, 302 HeartbeatTick: c.opts.HeartbeatTick, 303 MaxSizePerMsg: c.opts.MaxSizePerMsg, 304 MaxInflightMsgs: c.opts.MaxInflightBlocks, 305 Logger: c.logger, 306 Storage: c.opts.MemoryStorage, 307 // PreVote prevents reconnected node from disturbing network. 308 // See etcd/raft doc for more details. 309 PreVote: true, 310 CheckQuorum: true, 311 DisableProposalForwarding: true, // This prevents blocks from being accidentally proposed by followers 312 } 313 314 disseminator := &Disseminator{RPC: c.rpc} 315 disseminator.UpdateMetadata(nil) // initialize 316 c.ActiveNodes.Store([]uint64{}) 317 318 c.Node = &node{ 319 chainID: c.channelID, 320 chain: c, 321 logger: c.logger, 322 metrics: c.Metrics, 323 storage: storage, 324 rpc: disseminator, 325 config: config, 326 tickInterval: c.opts.TickInterval, 327 clock: c.clock, 328 metadata: c.opts.BlockMetadata, 329 tracker: &Tracker{ 330 id: c.raftID, 331 sender: disseminator, 332 gauge: c.Metrics.ActiveNodes, 333 active: &c.ActiveNodes, 334 logger: c.logger, 335 }, 336 } 337 338 return c, nil 339 } 340 341 // Start instructs the orderer to begin serving the chain and keep it current. 342 func (c *Chain) Start() { 343 c.logger.Infof("Starting Raft node") 344 345 if err := c.configureComm(); err != nil { 346 c.logger.Errorf("Failed to start chain, aborting: +%v", err) 347 close(c.doneC) 348 return 349 } 350 351 isJoin := c.support.Height() > 1 352 if isJoin && c.opts.MigrationInit { 353 isJoin = false 354 c.logger.Infof("Consensus-type migration detected, starting new raft node on an existing channel; height=%d", c.support.Height()) 355 } 356 c.Node.start(c.fresh, isJoin) 357 358 close(c.startC) 359 close(c.errorC) 360 361 go c.gc() 362 go c.run() 363 364 es := c.newEvictionSuspector() 365 366 interval := DefaultLeaderlessCheckInterval 367 if c.opts.LeaderCheckInterval != 0 { 368 interval = c.opts.LeaderCheckInterval 369 } 370 371 c.periodicChecker = &PeriodicCheck{ 372 Logger: c.logger, 373 Report: es.confirmSuspicion, 374 ReportCleared: es.clearSuspicion, 375 CheckInterval: interval, 376 Condition: c.suspectEviction, 377 } 378 c.periodicChecker.Run() 379 } 380 381 // Order submits normal type transactions for ordering. 382 func (c *Chain) Order(env *common.Envelope, configSeq uint64) error { 383 c.Metrics.NormalProposalsReceived.Add(1) 384 return c.Submit(&orderer.SubmitRequest{LastValidationSeq: configSeq, Payload: env, Channel: c.channelID}, 0) 385 } 386 387 // Configure submits config type transactions for ordering. 388 func (c *Chain) Configure(env *common.Envelope, configSeq uint64) error { 389 c.Metrics.ConfigProposalsReceived.Add(1) 390 return c.Submit(&orderer.SubmitRequest{LastValidationSeq: configSeq, Payload: env, Channel: c.channelID}, 0) 391 } 392 393 // WaitReady blocks when the chain: 394 // - is catching up with other nodes using snapshot 395 // 396 // In any other case, it returns right away. 397 func (c *Chain) WaitReady() error { 398 if err := c.isRunning(); err != nil { 399 return err 400 } 401 402 select { 403 case c.submitC <- nil: 404 case <-c.doneC: 405 return errors.Errorf("chain is stopped") 406 } 407 408 return nil 409 } 410 411 // Errored returns a channel that closes when the chain stops. 412 func (c *Chain) Errored() <-chan struct{} { 413 c.errorCLock.RLock() 414 defer c.errorCLock.RUnlock() 415 return c.errorC 416 } 417 418 // Halt stops the chain. 419 func (c *Chain) Halt() { 420 select { 421 case <-c.startC: 422 default: 423 c.logger.Warnf("Attempted to halt a chain that has not started") 424 return 425 } 426 427 select { 428 case c.haltC <- struct{}{}: 429 case <-c.doneC: 430 return 431 } 432 <-c.doneC 433 434 if c.haltCallback != nil { 435 c.haltCallback() 436 } 437 } 438 439 func (c *Chain) isRunning() error { 440 select { 441 case <-c.startC: 442 default: 443 return errors.Errorf("chain is not started") 444 } 445 446 select { 447 case <-c.doneC: 448 return errors.Errorf("chain is stopped") 449 default: 450 } 451 452 return nil 453 } 454 455 // Consensus passes the given ConsensusRequest message to the raft.Node instance 456 func (c *Chain) Consensus(req *orderer.ConsensusRequest, sender uint64) error { 457 if err := c.isRunning(); err != nil { 458 return err 459 } 460 461 stepMsg := &raftpb.Message{} 462 if err := proto.Unmarshal(req.Payload, stepMsg); err != nil { 463 return fmt.Errorf("failed to unmarshal StepRequest payload to Raft Message: %s", err) 464 } 465 466 if stepMsg.To != c.raftID { 467 c.logger.Warnf("Received msg to %d, my ID is probably wrong due to out of date, cowardly halting", stepMsg.To) 468 c.Halt() 469 return nil 470 } 471 472 if err := c.Node.Step(context.TODO(), *stepMsg); err != nil { 473 return fmt.Errorf("failed to process Raft Step message: %s", err) 474 } 475 476 if len(req.Metadata) == 0 || atomic.LoadUint64(&c.lastKnownLeader) != sender { // ignore metadata from non-leader 477 return nil 478 } 479 480 clusterMetadata := &etcdraft.ClusterMetadata{} 481 if err := proto.Unmarshal(req.Metadata, clusterMetadata); err != nil { 482 return errors.Errorf("failed to unmarshal ClusterMetadata: %s", err) 483 } 484 485 c.Metrics.ActiveNodes.Set(float64(len(clusterMetadata.ActiveNodes))) 486 c.ActiveNodes.Store(clusterMetadata.ActiveNodes) 487 488 return nil 489 } 490 491 // Submit forwards the incoming request to: 492 // - the local run goroutine if this is leader 493 // - the actual leader via the transport mechanism 494 // The call fails if there's no leader elected yet. 495 func (c *Chain) Submit(req *orderer.SubmitRequest, sender uint64) error { 496 if err := c.isRunning(); err != nil { 497 c.Metrics.ProposalFailures.Add(1) 498 return err 499 } 500 501 leadC := make(chan uint64, 1) 502 select { 503 case c.submitC <- &submit{req, leadC}: 504 lead := <-leadC 505 if lead == raft.None { 506 c.Metrics.ProposalFailures.Add(1) 507 return errors.Errorf("no Raft leader") 508 } 509 510 if lead != c.raftID { 511 if err := c.rpc.SendSubmit(lead, req); err != nil { 512 c.Metrics.ProposalFailures.Add(1) 513 return err 514 } 515 } 516 517 case <-c.doneC: 518 c.Metrics.ProposalFailures.Add(1) 519 return errors.Errorf("chain is stopped") 520 } 521 522 return nil 523 } 524 525 type apply struct { 526 entries []raftpb.Entry 527 soft *raft.SoftState 528 } 529 530 func isCandidate(state raft.StateType) bool { 531 return state == raft.StatePreCandidate || state == raft.StateCandidate 532 } 533 534 func (c *Chain) run() { 535 ticking := false 536 timer := c.clock.NewTimer(time.Second) 537 // we need a stopped timer rather than nil, 538 // because we will be select waiting on timer.C() 539 if !timer.Stop() { 540 <-timer.C() 541 } 542 543 // if timer is already started, this is a no-op 544 startTimer := func() { 545 if !ticking { 546 ticking = true 547 timer.Reset(c.support.SharedConfig().BatchTimeout()) 548 } 549 } 550 551 stopTimer := func() { 552 if !timer.Stop() && ticking { 553 // we only need to drain the channel if the timer expired (not explicitly stopped) 554 <-timer.C() 555 } 556 ticking = false 557 } 558 559 var soft raft.SoftState 560 submitC := c.submitC 561 var bc *blockCreator 562 563 var propC chan<- *common.Block 564 var cancelProp context.CancelFunc 565 cancelProp = func() {} // no-op as initial value 566 567 becomeLeader := func() (chan<- *common.Block, context.CancelFunc) { 568 c.Metrics.IsLeader.Set(1) 569 570 c.blockInflight = 0 571 c.justElected = true 572 submitC = nil 573 ch := make(chan *common.Block, c.opts.MaxInflightBlocks) 574 575 // if there is unfinished ConfChange, we should resume the effort to propose it as 576 // new leader, and wait for it to be committed before start serving new requests. 577 if cc := c.getInFlightConfChange(); cc != nil { 578 // The reason `ProposeConfChange` should be called in go routine is documented in `writeConfigBlock` method. 579 go func() { 580 if err := c.Node.ProposeConfChange(context.TODO(), *cc); err != nil { 581 c.logger.Warnf("Failed to propose configuration update to Raft node: %s", err) 582 } 583 }() 584 585 c.confChangeInProgress = cc 586 c.configInflight = true 587 } 588 589 // Leader should call Propose in go routine, because this method may be blocked 590 // if node is leaderless (this can happen when leader steps down in a heavily 591 // loaded network). We need to make sure applyC can still be consumed properly. 592 ctx, cancel := context.WithCancel(context.Background()) 593 go func(ctx context.Context, ch <-chan *common.Block) { 594 for { 595 select { 596 case b := <-ch: 597 data := protoutil.MarshalOrPanic(b) 598 if err := c.Node.Propose(ctx, data); err != nil { 599 c.logger.Errorf("Failed to propose block [%d] to raft and discard %d blocks in queue: %s", b.Header.Number, len(ch), err) 600 return 601 } 602 c.logger.Debugf("Proposed block [%d] to raft consensus", b.Header.Number) 603 604 case <-ctx.Done(): 605 c.logger.Debugf("Quit proposing blocks, discarded %d blocks in the queue", len(ch)) 606 return 607 } 608 } 609 }(ctx, ch) 610 611 return ch, cancel 612 } 613 614 becomeFollower := func() { 615 cancelProp() 616 c.blockInflight = 0 617 _ = c.support.BlockCutter().Cut() 618 stopTimer() 619 submitC = c.submitC 620 bc = nil 621 c.Metrics.IsLeader.Set(0) 622 } 623 624 for { 625 select { 626 case s := <-submitC: 627 if s == nil { 628 // polled by `WaitReady` 629 continue 630 } 631 632 if soft.RaftState == raft.StatePreCandidate || soft.RaftState == raft.StateCandidate { 633 s.leader <- raft.None 634 continue 635 } 636 637 s.leader <- soft.Lead 638 if soft.Lead != c.raftID { 639 continue 640 } 641 642 batches, pending, err := c.ordered(s.req) 643 if err != nil { 644 c.logger.Errorf("Failed to order message: %s", err) 645 continue 646 } 647 if pending { 648 startTimer() // no-op if timer is already started 649 } else { 650 stopTimer() 651 } 652 653 c.propose(propC, bc, batches...) 654 655 if c.configInflight { 656 c.logger.Info("Received config transaction, pause accepting transaction till it is committed") 657 submitC = nil 658 } else if c.blockInflight >= c.opts.MaxInflightBlocks { 659 c.logger.Debugf("Number of in-flight blocks (%d) reaches limit (%d), pause accepting transaction", 660 c.blockInflight, c.opts.MaxInflightBlocks) 661 submitC = nil 662 } 663 664 case app := <-c.applyC: 665 if app.soft != nil { 666 newLeader := atomic.LoadUint64(&app.soft.Lead) // etcdraft requires atomic access 667 if newLeader != soft.Lead { 668 c.logger.Infof("Raft leader changed: %d -> %d", soft.Lead, newLeader) 669 c.Metrics.LeaderChanges.Add(1) 670 671 atomic.StoreUint64(&c.lastKnownLeader, newLeader) 672 673 if newLeader == c.raftID { 674 propC, cancelProp = becomeLeader() 675 } 676 677 if soft.Lead == c.raftID { 678 becomeFollower() 679 } 680 } 681 682 foundLeader := soft.Lead == raft.None && newLeader != raft.None 683 quitCandidate := isCandidate(soft.RaftState) && !isCandidate(app.soft.RaftState) 684 685 if foundLeader || quitCandidate { 686 c.errorCLock.Lock() 687 c.errorC = make(chan struct{}) 688 c.errorCLock.Unlock() 689 } 690 691 if isCandidate(app.soft.RaftState) || newLeader == raft.None { 692 atomic.StoreUint64(&c.lastKnownLeader, raft.None) 693 select { 694 case <-c.errorC: 695 default: 696 nodeCount := len(c.opts.BlockMetadata.ConsenterIds) 697 // Only close the error channel (to signal the broadcast/deliver front-end a consensus backend error) 698 // If we are a cluster of size 3 or more, otherwise we can't expand a cluster of size 1 to 2 nodes. 699 if nodeCount > 2 { 700 close(c.errorC) 701 } else { 702 c.logger.Warningf("No leader is present, cluster size is %d", nodeCount) 703 } 704 } 705 } 706 707 soft = raft.SoftState{Lead: newLeader, RaftState: app.soft.RaftState} 708 709 // notify external observer 710 select { 711 case c.observeC <- soft: 712 default: 713 } 714 } 715 716 c.apply(app.entries) 717 718 if c.justElected { 719 msgInflight := c.Node.lastIndex() > c.appliedIndex 720 if msgInflight { 721 c.logger.Debugf("There are in flight blocks, new leader should not serve requests") 722 continue 723 } 724 725 if c.configInflight { 726 c.logger.Debugf("There is config block in flight, new leader should not serve requests") 727 continue 728 } 729 730 c.logger.Infof("Start accepting requests as Raft leader at block [%d]", c.lastBlock.Header.Number) 731 bc = &blockCreator{ 732 hash: protoutil.BlockHeaderHash(c.lastBlock.Header), 733 number: c.lastBlock.Header.Number, 734 logger: c.logger, 735 } 736 submitC = c.submitC 737 c.justElected = false 738 } else if c.configInflight { 739 c.logger.Info("Config block or ConfChange in flight, pause accepting transaction") 740 submitC = nil 741 } else if c.blockInflight < c.opts.MaxInflightBlocks { 742 submitC = c.submitC 743 } 744 745 case <-timer.C(): 746 ticking = false 747 748 batch := c.support.BlockCutter().Cut() 749 if len(batch) == 0 { 750 c.logger.Warningf("Batch timer expired with no pending requests, this might indicate a bug") 751 continue 752 } 753 754 c.logger.Debugf("Batch timer expired, creating block") 755 c.propose(propC, bc, batch) // we are certain this is normal block, no need to block 756 757 case sn := <-c.snapC: 758 if sn.Metadata.Index != 0 { 759 if sn.Metadata.Index <= c.appliedIndex { 760 c.logger.Debugf("Skip snapshot taken at index %d, because it is behind current applied index %d", sn.Metadata.Index, c.appliedIndex) 761 break 762 } 763 764 c.confState = sn.Metadata.ConfState 765 c.appliedIndex = sn.Metadata.Index 766 } else { 767 c.logger.Infof("Received artificial snapshot to trigger catchup") 768 } 769 770 if err := c.catchUp(sn); err != nil { 771 c.logger.Panicf("Failed to recover from snapshot taken at Term %d and Index %d: %s", 772 sn.Metadata.Term, sn.Metadata.Index, err) 773 } 774 775 case <-c.doneC: 776 stopTimer() 777 cancelProp() 778 779 select { 780 case <-c.errorC: // avoid closing closed channel 781 default: 782 close(c.errorC) 783 } 784 785 c.logger.Infof("Stop serving requests") 786 c.periodicChecker.Stop() 787 return 788 } 789 } 790 } 791 792 func (c *Chain) writeBlock(block *common.Block, index uint64) { 793 if block.Header.Number > c.lastBlock.Header.Number+1 { 794 c.logger.Panicf("Got block [%d], expect block [%d]", block.Header.Number, c.lastBlock.Header.Number+1) 795 } else if block.Header.Number < c.lastBlock.Header.Number+1 { 796 c.logger.Infof("Got block [%d], expect block [%d], this node was forced to catch up", block.Header.Number, c.lastBlock.Header.Number+1) 797 return 798 } 799 800 if c.blockInflight > 0 { 801 c.blockInflight-- // only reduce on leader 802 } 803 c.lastBlock = block 804 805 c.logger.Infof("Writing block [%d] (Raft index: %d) to ledger", block.Header.Number, index) 806 807 if protoutil.IsConfigBlock(block) { 808 c.writeConfigBlock(block, index) 809 return 810 } 811 812 c.raftMetadataLock.Lock() 813 c.opts.BlockMetadata.RaftIndex = index 814 m := protoutil.MarshalOrPanic(c.opts.BlockMetadata) 815 c.raftMetadataLock.Unlock() 816 817 c.support.WriteBlock(block, m) 818 } 819 820 // Orders the envelope in the `msg` content. SubmitRequest. 821 // Returns 822 // -- batches [][]*common.Envelope; the batches cut, 823 // -- pending bool; if there are envelopes pending to be ordered, 824 // -- err error; the error encountered, if any. 825 // It takes care of config messages as well as the revalidation of messages if the config sequence has advanced. 826 func (c *Chain) ordered(msg *orderer.SubmitRequest) (batches [][]*common.Envelope, pending bool, err error) { 827 seq := c.support.Sequence() 828 829 if c.isConfig(msg.Payload) { 830 // ConfigMsg 831 if msg.LastValidationSeq < seq { 832 c.logger.Warnf("Config message was validated against %d, although current config seq has advanced (%d)", msg.LastValidationSeq, seq) 833 msg.Payload, _, err = c.support.ProcessConfigMsg(msg.Payload) 834 if err != nil { 835 c.Metrics.ProposalFailures.Add(1) 836 return nil, true, errors.Errorf("bad config message: %s", err) 837 } 838 } 839 840 batch := c.support.BlockCutter().Cut() 841 batches = [][]*common.Envelope{} 842 if len(batch) != 0 { 843 batches = append(batches, batch) 844 } 845 batches = append(batches, []*common.Envelope{msg.Payload}) 846 return batches, false, nil 847 } 848 // it is a normal message 849 if msg.LastValidationSeq < seq { 850 c.logger.Warnf("Normal message was validated against %d, although current config seq has advanced (%d)", msg.LastValidationSeq, seq) 851 if _, err := c.support.ProcessNormalMsg(msg.Payload); err != nil { 852 c.Metrics.ProposalFailures.Add(1) 853 return nil, true, errors.Errorf("bad normal message: %s", err) 854 } 855 } 856 batches, pending = c.support.BlockCutter().Ordered(msg.Payload) 857 return batches, pending, nil 858 859 } 860 861 func (c *Chain) propose(ch chan<- *common.Block, bc *blockCreator, batches ...[]*common.Envelope) { 862 for _, batch := range batches { 863 b := bc.createNextBlock(batch) 864 c.logger.Infof("Created block [%d], there are %d blocks in flight", b.Header.Number, c.blockInflight) 865 866 select { 867 case ch <- b: 868 default: 869 c.logger.Panic("Programming error: limit of in-flight blocks does not properly take effect or block is proposed by follower") 870 } 871 872 // if it is config block, then we should wait for the commit of the block 873 if protoutil.IsConfigBlock(b) { 874 c.configInflight = true 875 } 876 877 c.blockInflight++ 878 } 879 } 880 881 func (c *Chain) catchUp(snap *raftpb.Snapshot) error { 882 b, err := protoutil.UnmarshalBlock(snap.Data) 883 if err != nil { 884 return errors.Errorf("failed to unmarshal snapshot data to block: %s", err) 885 } 886 887 if c.lastBlock.Header.Number >= b.Header.Number { 888 c.logger.Warnf("Snapshot is at block [%d], local block number is %d, no sync needed", b.Header.Number, c.lastBlock.Header.Number) 889 return nil 890 } else if b.Header.Number == c.lastBlock.Header.Number+1 { 891 c.logger.Infof("The only missing block [%d] is encapsulated in snapshot, committing it to shortcut catchup process", b.Header.Number) 892 c.commitBlock(b) 893 c.lastBlock = b 894 return nil 895 } 896 897 puller, err := c.createPuller() 898 if err != nil { 899 return errors.Errorf("failed to create block puller: %s", err) 900 } 901 defer puller.Close() 902 903 next := c.lastBlock.Header.Number + 1 904 905 c.logger.Infof("Catching up with snapshot taken at block [%d], starting from block [%d]", b.Header.Number, next) 906 907 for next <= b.Header.Number { 908 block := puller.PullBlock(next) 909 if block == nil { 910 return errors.Errorf("failed to fetch block [%d] from cluster", next) 911 } 912 c.commitBlock(block) 913 c.lastBlock = block 914 next++ 915 } 916 917 c.logger.Infof("Finished syncing with cluster up to and including block [%d]", b.Header.Number) 918 return nil 919 } 920 921 func (c *Chain) commitBlock(block *common.Block) { 922 if !protoutil.IsConfigBlock(block) { 923 c.support.WriteBlock(block, nil) 924 return 925 } 926 927 c.support.WriteConfigBlock(block, nil) 928 929 configMembership := c.detectConfChange(block) 930 931 if configMembership != nil && configMembership.Changed() { 932 c.logger.Infof("Config block [%d] changes consenter set, communication should be reconfigured", block.Header.Number) 933 934 c.raftMetadataLock.Lock() 935 c.opts.BlockMetadata = configMembership.NewBlockMetadata 936 c.opts.Consenters = configMembership.NewConsenters 937 c.raftMetadataLock.Unlock() 938 939 if err := c.configureComm(); err != nil { 940 c.logger.Panicf("Failed to configure communication: %s", err) 941 } 942 } 943 } 944 945 func (c *Chain) detectConfChange(block *common.Block) *MembershipChanges { 946 // If config is targeting THIS channel, inspect consenter set and 947 // propose raft ConfChange if it adds/removes node. 948 configMetadata := c.newConfigMetadata(block) 949 950 if configMetadata == nil { 951 return nil 952 } 953 954 if configMetadata.Options != nil && 955 configMetadata.Options.SnapshotIntervalSize != 0 && 956 configMetadata.Options.SnapshotIntervalSize != c.sizeLimit { 957 c.logger.Infof("Update snapshot interval size to %d bytes (was %d)", 958 configMetadata.Options.SnapshotIntervalSize, c.sizeLimit) 959 c.sizeLimit = configMetadata.Options.SnapshotIntervalSize 960 } 961 962 changes, err := ComputeMembershipChanges(c.opts.BlockMetadata, c.opts.Consenters, configMetadata.Consenters) 963 if err != nil { 964 c.logger.Panicf("illegal configuration change detected: %s", err) 965 } 966 967 if changes.Rotated() { 968 c.logger.Infof("Config block [%d] rotates TLS certificate of node %d", block.Header.Number, changes.RotatedNode) 969 } 970 971 return changes 972 } 973 974 func (c *Chain) apply(ents []raftpb.Entry) { 975 if len(ents) == 0 { 976 return 977 } 978 979 if ents[0].Index > c.appliedIndex+1 { 980 c.logger.Panicf("first index of committed entry[%d] should <= appliedIndex[%d]+1", ents[0].Index, c.appliedIndex) 981 } 982 983 var position int 984 for i := range ents { 985 switch ents[i].Type { 986 case raftpb.EntryNormal: 987 if len(ents[i].Data) == 0 { 988 break 989 } 990 991 position = i 992 c.accDataSize += uint32(len(ents[i].Data)) 993 994 // We need to strictly avoid re-applying normal entries, 995 // otherwise we are writing the same block twice. 996 if ents[i].Index <= c.appliedIndex { 997 c.logger.Debugf("Received block with raft index (%d) <= applied index (%d), skip", ents[i].Index, c.appliedIndex) 998 break 999 } 1000 1001 block := protoutil.UnmarshalBlockOrPanic(ents[i].Data) 1002 c.writeBlock(block, ents[i].Index) 1003 c.Metrics.CommittedBlockNumber.Set(float64(block.Header.Number)) 1004 1005 case raftpb.EntryConfChange: 1006 var cc raftpb.ConfChange 1007 if err := cc.Unmarshal(ents[i].Data); err != nil { 1008 c.logger.Warnf("Failed to unmarshal ConfChange data: %s", err) 1009 continue 1010 } 1011 1012 c.confState = *c.Node.ApplyConfChange(cc) 1013 1014 switch cc.Type { 1015 case raftpb.ConfChangeAddNode: 1016 c.logger.Infof("Applied config change to add node %d, current nodes in channel: %+v", cc.NodeID, c.confState.Nodes) 1017 case raftpb.ConfChangeRemoveNode: 1018 c.logger.Infof("Applied config change to remove node %d, current nodes in channel: %+v", cc.NodeID, c.confState.Nodes) 1019 default: 1020 c.logger.Panic("Programming error, encountered unsupported raft config change") 1021 } 1022 1023 // This ConfChange was introduced by a previously committed config block, 1024 // we can now unblock submitC to accept envelopes. 1025 var configureComm bool 1026 if c.confChangeInProgress != nil && 1027 c.confChangeInProgress.NodeID == cc.NodeID && 1028 c.confChangeInProgress.Type == cc.Type { 1029 1030 configureComm = true 1031 c.confChangeInProgress = nil 1032 c.configInflight = false 1033 // report the new cluster size 1034 c.Metrics.ClusterSize.Set(float64(len(c.opts.BlockMetadata.ConsenterIds))) 1035 } 1036 1037 lead := atomic.LoadUint64(&c.lastKnownLeader) 1038 removeLeader := cc.Type == raftpb.ConfChangeRemoveNode && cc.NodeID == lead 1039 shouldHalt := cc.Type == raftpb.ConfChangeRemoveNode && cc.NodeID == c.raftID 1040 1041 // unblock `run` go routine so it can still consume Raft messages 1042 go func() { 1043 if removeLeader { 1044 c.logger.Infof("Current leader is being removed from channel, attempt leadership transfer") 1045 c.Node.abdicateLeader(lead) 1046 } 1047 1048 if configureComm && !shouldHalt { // no need to configure comm if this node is going to halt 1049 if err := c.configureComm(); err != nil { 1050 c.logger.Panicf("Failed to configure communication: %s", err) 1051 } 1052 } 1053 1054 if shouldHalt { 1055 c.logger.Infof("This node is being removed from replica set") 1056 c.Halt() 1057 return 1058 } 1059 }() 1060 } 1061 1062 if ents[i].Index > c.appliedIndex { 1063 c.appliedIndex = ents[i].Index 1064 } 1065 } 1066 1067 if c.accDataSize >= c.sizeLimit { 1068 b := protoutil.UnmarshalBlockOrPanic(ents[position].Data) 1069 1070 select { 1071 case c.gcC <- &gc{index: c.appliedIndex, state: c.confState, data: ents[position].Data}: 1072 c.logger.Infof("Accumulated %d bytes since last snapshot, exceeding size limit (%d bytes), "+ 1073 "taking snapshot at block [%d] (index: %d), last snapshotted block number is %d, current nodes: %+v", 1074 c.accDataSize, c.sizeLimit, b.Header.Number, c.appliedIndex, c.lastSnapBlockNum, c.confState.Nodes) 1075 c.accDataSize = 0 1076 c.lastSnapBlockNum = b.Header.Number 1077 c.Metrics.SnapshotBlockNumber.Set(float64(b.Header.Number)) 1078 default: 1079 c.logger.Warnf("Snapshotting is in progress, it is very likely that SnapshotIntervalSize is too small") 1080 } 1081 } 1082 } 1083 1084 func (c *Chain) gc() { 1085 for { 1086 select { 1087 case g := <-c.gcC: 1088 c.Node.takeSnapshot(g.index, g.state, g.data) 1089 case <-c.doneC: 1090 c.logger.Infof("Stop garbage collecting") 1091 return 1092 } 1093 } 1094 } 1095 1096 func (c *Chain) isConfig(env *common.Envelope) bool { 1097 h, err := protoutil.ChannelHeader(env) 1098 if err != nil { 1099 c.logger.Panicf("failed to extract channel header from envelope") 1100 } 1101 1102 return h.Type == int32(common.HeaderType_CONFIG) || h.Type == int32(common.HeaderType_ORDERER_TRANSACTION) 1103 } 1104 1105 func (c *Chain) configureComm() error { 1106 // Reset unreachable map when communication is reconfigured 1107 c.Node.unreachableLock.Lock() 1108 c.Node.unreachable = make(map[uint64]struct{}) 1109 c.Node.unreachableLock.Unlock() 1110 1111 nodes, err := c.remotePeers() 1112 if err != nil { 1113 return err 1114 } 1115 1116 c.configurator.Configure(c.channelID, nodes) 1117 return nil 1118 } 1119 1120 func (c *Chain) remotePeers() ([]cluster.RemoteNode, error) { 1121 c.raftMetadataLock.RLock() 1122 defer c.raftMetadataLock.RUnlock() 1123 1124 var nodes []cluster.RemoteNode 1125 for raftID, consenter := range c.opts.Consenters { 1126 // No need to know yourself 1127 if raftID == c.raftID { 1128 continue 1129 } 1130 serverCertAsDER, err := pemToDER(consenter.ServerTlsCert, raftID, "server", c.logger) 1131 if err != nil { 1132 return nil, errors.WithStack(err) 1133 } 1134 clientCertAsDER, err := pemToDER(consenter.ClientTlsCert, raftID, "client", c.logger) 1135 if err != nil { 1136 return nil, errors.WithStack(err) 1137 } 1138 nodes = append(nodes, cluster.RemoteNode{ 1139 ID: raftID, 1140 Endpoint: fmt.Sprintf("%s:%d", consenter.Host, consenter.Port), 1141 ServerTLSCert: serverCertAsDER, 1142 ClientTLSCert: clientCertAsDER, 1143 }) 1144 } 1145 return nodes, nil 1146 } 1147 1148 func pemToDER(pemBytes []byte, id uint64, certType string, logger *flogging.FabricLogger) ([]byte, error) { 1149 bl, _ := pem.Decode(pemBytes) 1150 if bl == nil { 1151 logger.Errorf("Rejecting PEM block of %s TLS cert for node %d, offending PEM is: %s", certType, id, string(pemBytes)) 1152 return nil, errors.Errorf("invalid PEM block") 1153 } 1154 return bl.Bytes, nil 1155 } 1156 1157 // writeConfigBlock writes configuration blocks into the ledger in 1158 // addition extracts updates about raft replica set and if there 1159 // are changes updates cluster membership as well 1160 func (c *Chain) writeConfigBlock(block *common.Block, index uint64) { 1161 hdr, err := ConfigChannelHeader(block) 1162 if err != nil { 1163 c.logger.Panicf("Failed to get config header type from config block: %s", err) 1164 } 1165 1166 c.configInflight = false 1167 1168 switch common.HeaderType(hdr.Type) { 1169 case common.HeaderType_CONFIG: 1170 configMembership := c.detectConfChange(block) 1171 1172 c.raftMetadataLock.Lock() 1173 c.opts.BlockMetadata.RaftIndex = index 1174 if configMembership != nil { 1175 c.opts.BlockMetadata = configMembership.NewBlockMetadata 1176 c.opts.Consenters = configMembership.NewConsenters 1177 } 1178 c.raftMetadataLock.Unlock() 1179 1180 blockMetadataBytes := protoutil.MarshalOrPanic(c.opts.BlockMetadata) 1181 1182 // write block with metadata 1183 c.support.WriteConfigBlock(block, blockMetadataBytes) 1184 1185 if configMembership == nil { 1186 return 1187 } 1188 1189 // update membership 1190 if configMembership.ConfChange != nil { 1191 // We need to propose conf change in a go routine, because it may be blocked if raft node 1192 // becomes leaderless, and we should not block `run` so it can keep consuming applyC, 1193 // otherwise we have a deadlock. 1194 go func() { 1195 // ProposeConfChange returns error only if node being stopped. 1196 // This proposal is dropped by followers because DisableProposalForwarding is enabled. 1197 if err := c.Node.ProposeConfChange(context.TODO(), *configMembership.ConfChange); err != nil { 1198 c.logger.Warnf("Failed to propose configuration update to Raft node: %s", err) 1199 } 1200 }() 1201 1202 c.confChangeInProgress = configMembership.ConfChange 1203 1204 switch configMembership.ConfChange.Type { 1205 case raftpb.ConfChangeAddNode: 1206 c.logger.Infof("Config block just committed adds node %d, pause accepting transactions till config change is applied", configMembership.ConfChange.NodeID) 1207 case raftpb.ConfChangeRemoveNode: 1208 c.logger.Infof("Config block just committed removes node %d, pause accepting transactions till config change is applied", configMembership.ConfChange.NodeID) 1209 default: 1210 c.logger.Panic("Programming error, encountered unsupported raft config change") 1211 } 1212 1213 c.configInflight = true 1214 } else if configMembership.Rotated() { 1215 lead := atomic.LoadUint64(&c.lastKnownLeader) 1216 if configMembership.RotatedNode == lead { 1217 c.logger.Infof("Certificate of Raft leader is being rotated, attempt leader transfer before reconfiguring communication") 1218 go func() { 1219 c.Node.abdicateLeader(lead) 1220 if err := c.configureComm(); err != nil { 1221 c.logger.Panicf("Failed to configure communication: %s", err) 1222 } 1223 }() 1224 } else { 1225 if err := c.configureComm(); err != nil { 1226 c.logger.Panicf("Failed to configure communication: %s", err) 1227 } 1228 } 1229 } 1230 1231 case common.HeaderType_ORDERER_TRANSACTION: 1232 // If this config is channel creation, no extra inspection is needed 1233 c.raftMetadataLock.Lock() 1234 c.opts.BlockMetadata.RaftIndex = index 1235 m := protoutil.MarshalOrPanic(c.opts.BlockMetadata) 1236 c.raftMetadataLock.Unlock() 1237 1238 c.support.WriteConfigBlock(block, m) 1239 1240 default: 1241 c.logger.Panicf("Programming error: unexpected config type: %s", common.HeaderType(hdr.Type)) 1242 } 1243 } 1244 1245 // getInFlightConfChange returns ConfChange in-flight if any. 1246 // It returns confChangeInProgress if it is not nil. Otherwise 1247 // it returns ConfChange from the last committed block (might be nil). 1248 func (c *Chain) getInFlightConfChange() *raftpb.ConfChange { 1249 if c.confChangeInProgress != nil { 1250 return c.confChangeInProgress 1251 } 1252 1253 if c.lastBlock.Header.Number == 0 { 1254 return nil // nothing to failover just started the chain 1255 } 1256 1257 if !protoutil.IsConfigBlock(c.lastBlock) { 1258 return nil 1259 } 1260 1261 // extracting current Raft configuration state 1262 confState := c.Node.ApplyConfChange(raftpb.ConfChange{}) 1263 1264 if len(confState.Nodes) == len(c.opts.BlockMetadata.ConsenterIds) { 1265 // Raft configuration change could only add one node or 1266 // remove one node at a time, if raft conf state size is 1267 // equal to membership stored in block metadata field, 1268 // that means everything is in sync and no need to propose 1269 // config update. 1270 return nil 1271 } 1272 1273 return ConfChange(c.opts.BlockMetadata, confState) 1274 } 1275 1276 // newMetadata extract config metadata from the configuration block 1277 func (c *Chain) newConfigMetadata(block *common.Block) *etcdraft.ConfigMetadata { 1278 metadata, err := ConsensusMetadataFromConfigBlock(block) 1279 if err != nil { 1280 c.logger.Panicf("error reading consensus metadata: %s", err) 1281 } 1282 return metadata 1283 } 1284 1285 // ValidateConsensusMetadata determines the validity of a 1286 // ConsensusMetadata update during config updates on the channel. 1287 func (c *Chain) ValidateConsensusMetadata(oldOrdererConfig, newOrdererConfig channelconfig.Orderer, newChannel bool) error { 1288 if newOrdererConfig == nil { 1289 c.logger.Panic("Programming Error: ValidateConsensusMetadata called with nil new channel config") 1290 return nil 1291 } 1292 1293 // metadata was not updated 1294 if newOrdererConfig.ConsensusMetadata() == nil { 1295 return nil 1296 } 1297 1298 if oldOrdererConfig == nil { 1299 c.logger.Panic("Programming Error: ValidateConsensusMetadata called with nil old channel config") 1300 return nil 1301 } 1302 1303 if oldOrdererConfig.ConsensusMetadata() == nil { 1304 c.logger.Panic("Programming Error: ValidateConsensusMetadata called with nil old metadata") 1305 return nil 1306 } 1307 1308 oldMetadata := &etcdraft.ConfigMetadata{} 1309 if err := proto.Unmarshal(oldOrdererConfig.ConsensusMetadata(), oldMetadata); err != nil { 1310 c.logger.Panicf("Programming Error: Failed to unmarshal old etcdraft consensus metadata: %v", err) 1311 } 1312 1313 newMetadata := &etcdraft.ConfigMetadata{} 1314 if err := proto.Unmarshal(newOrdererConfig.ConsensusMetadata(), newMetadata); err != nil { 1315 return errors.Wrap(err, "failed to unmarshal new etcdraft metadata configuration") 1316 } 1317 1318 verifyOpts, err := createX509VerifyOptions(newOrdererConfig) 1319 if err != nil { 1320 return errors.Wrapf(err, "failed to create x509 verify options from old and new orderer config") 1321 } 1322 1323 if err := VerifyConfigMetadata(newMetadata, verifyOpts); err != nil { 1324 return errors.Wrap(err, "invalid new config metadata") 1325 } 1326 1327 if newChannel { 1328 // check if the consenters are a subset of the existing consenters (system channel consenters) 1329 set := ConsentersToMap(oldMetadata.Consenters) 1330 for _, c := range newMetadata.Consenters { 1331 if !set.Exists(c) { 1332 return errors.New("new channel has consenter that is not part of system consenter set") 1333 } 1334 } 1335 return nil 1336 } 1337 1338 // create the dummy parameters for ComputeMembershipChanges 1339 c.raftMetadataLock.RLock() 1340 dummyOldBlockMetadata := proto.Clone(c.opts.BlockMetadata).(*etcdraft.BlockMetadata) 1341 c.raftMetadataLock.RUnlock() 1342 1343 dummyOldConsentersMap := CreateConsentersMap(dummyOldBlockMetadata, oldMetadata) 1344 changes, err := ComputeMembershipChanges(dummyOldBlockMetadata, dummyOldConsentersMap, newMetadata.Consenters) 1345 if err != nil { 1346 return err 1347 } 1348 1349 //new config metadata was verified above. Additionally need to check new consenters for certificates expiration 1350 for _, c := range changes.AddedNodes { 1351 if err := validateConsenterTLSCerts(c, verifyOpts, false); err != nil { 1352 return errors.Wrapf(err, "consenter %s:%d has invalid certificates", c.Host, c.Port) 1353 } 1354 } 1355 1356 active := c.ActiveNodes.Load().([]uint64) 1357 if changes.UnacceptableQuorumLoss(active) { 1358 return errors.Errorf("%d out of %d nodes are alive, configuration will result in quorum loss", len(active), len(dummyOldConsentersMap)) 1359 } 1360 1361 return nil 1362 } 1363 1364 // StatusReport returns the ClusterRelation & Status 1365 func (c *Chain) StatusReport() (types.ClusterRelation, types.Status) { 1366 return types.ClusterRelationMember, types.StatusActive 1367 } 1368 1369 func (c *Chain) suspectEviction() bool { 1370 if c.isRunning() != nil { 1371 return false 1372 } 1373 1374 return atomic.LoadUint64(&c.lastKnownLeader) == uint64(0) 1375 } 1376 1377 func (c *Chain) newEvictionSuspector() *evictionSuspector { 1378 consenterCertificate := &ConsenterCertificate{ 1379 Logger: c.logger, 1380 ConsenterCertificate: c.opts.Cert, 1381 CryptoProvider: c.CryptoProvider, 1382 } 1383 1384 return &evictionSuspector{ 1385 amIInChannel: consenterCertificate.IsConsenterOfChannel, 1386 evictionSuspicionThreshold: c.opts.EvictionSuspicion, 1387 writeBlock: c.support.Append, 1388 createPuller: c.createPuller, 1389 height: c.support.Height, 1390 triggerCatchUp: c.triggerCatchup, 1391 logger: c.logger, 1392 halt: func() { 1393 c.Halt() 1394 }, 1395 } 1396 } 1397 1398 func (c *Chain) triggerCatchup(sn *raftpb.Snapshot) { 1399 select { 1400 case c.snapC <- sn: 1401 case <-c.doneC: 1402 } 1403 }