github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/consensus/istanbul/ibft/core/handler.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package core 18 19 import ( 20 "github.com/kisexp/xdchain/common" 21 "github.com/kisexp/xdchain/consensus/istanbul" 22 istanbulcommon "github.com/kisexp/xdchain/consensus/istanbul/common" 23 ibfttypes "github.com/kisexp/xdchain/consensus/istanbul/ibft/types" 24 ) 25 26 // Start implements core.Engine.Start 27 func (c *core) Start() error { 28 // Tests will handle events itself, so we have to make subscribeEvents() 29 // be able to call in test. 30 c.subscribeEvents() 31 c.handlerWg.Add(1) 32 go c.handleEvents() 33 34 // Start a new round from last sequence + 1 35 c.startNewRound(common.Big0) 36 37 return nil 38 } 39 40 // Stop implements core.Engine.Stop 41 func (c *core) Stop() error { 42 c.stopTimer() 43 c.unsubscribeEvents() 44 45 c.handlerWg.Wait() 46 return nil 47 } 48 49 // ---------------------------------------------------------------------------- 50 51 // Subscribe both internal and external events 52 func (c *core) subscribeEvents() { 53 c.events = c.backend.EventMux().Subscribe( 54 // external events 55 istanbul.RequestEvent{}, 56 istanbul.MessageEvent{}, 57 // internal events 58 backlogEvent{}, 59 ) 60 c.timeoutSub = c.backend.EventMux().Subscribe( 61 timeoutEvent{}, 62 ) 63 c.finalCommittedSub = c.backend.EventMux().Subscribe( 64 istanbul.FinalCommittedEvent{}, 65 ) 66 } 67 68 // Unsubscribe all events 69 func (c *core) unsubscribeEvents() { 70 c.events.Unsubscribe() 71 c.timeoutSub.Unsubscribe() 72 c.finalCommittedSub.Unsubscribe() 73 } 74 75 func (c *core) handleEvents() { 76 // Clear state 77 defer func() { 78 c.current = nil 79 c.handlerWg.Done() 80 }() 81 82 for { 83 select { 84 case event, ok := <-c.events.Chan(): 85 if !ok { 86 return 87 } 88 89 // A real event arrived, process interesting content 90 switch ev := event.Data.(type) { 91 case istanbul.RequestEvent: 92 93 r := &istanbul.Request{ 94 Proposal: ev.Proposal, 95 } 96 err := c.handleRequest(r) 97 if err == istanbulcommon.ErrFutureMessage { 98 c.storeRequestMsg(r) 99 } 100 case istanbul.MessageEvent: 101 102 if err := c.handleMsg(ev.Payload); err == nil { 103 c.backend.Gossip(c.valSet, ev.Code, ev.Payload) 104 } 105 case backlogEvent: 106 // No need to check signature for internal messages 107 if err := c.handleCheckedMsg(ev.msg, ev.src); err == nil { 108 p, err := ev.msg.Payload() 109 if err != nil { 110 c.logger.Warn("Get message payload failed", "err", err) 111 continue 112 } 113 c.backend.Gossip(c.valSet, ev.msg.Code, p) 114 } 115 } 116 case _, ok := <-c.timeoutSub.Chan(): 117 if !ok { 118 return 119 } 120 c.handleTimeoutMsg() 121 case event, ok := <-c.finalCommittedSub.Chan(): 122 if !ok { 123 return 124 } 125 switch event.Data.(type) { 126 case istanbul.FinalCommittedEvent: 127 c.handleFinalCommitted() 128 } 129 } 130 } 131 } 132 133 // sendEvent sends events to mux 134 func (c *core) sendEvent(ev interface{}) { 135 c.backend.EventMux().Post(ev) 136 } 137 138 func (c *core) handleMsg(payload []byte) error { 139 logger := c.logger.New() 140 141 // Decode message and check its signature 142 msg := new(ibfttypes.Message) 143 if err := msg.FromPayload(payload, c.validateFn); err != nil { 144 logger.Error("Failed to decode message from payload", "err", err) 145 return err 146 } 147 148 // Only accept message if the address is valid 149 _, src := c.valSet.GetByAddress(msg.Address) 150 if src == nil { 151 logger.Error("Invalid address in message", "msg", msg) 152 return istanbul.ErrUnauthorizedAddress 153 } 154 155 return c.handleCheckedMsg(msg, src) 156 } 157 158 func (c *core) handleCheckedMsg(msg *ibfttypes.Message, src istanbul.Validator) error { 159 logger := c.logger.New("address", c.address, "from", src) 160 161 // Store the message if it's a future message 162 testBacklog := func(err error) error { 163 if err == istanbulcommon.ErrFutureMessage { 164 c.storeBacklog(msg, src) 165 } 166 167 return err 168 } 169 170 switch msg.Code { 171 case ibfttypes.MsgPreprepare: 172 err := c.handlePreprepare(msg, src) 173 return testBacklog(err) 174 case ibfttypes.MsgPrepare: 175 err := c.handlePrepare(msg, src) 176 return testBacklog(err) 177 case ibfttypes.MsgCommit: 178 err := c.handleCommit(msg, src) 179 return testBacklog(err) 180 case ibfttypes.MsgRoundChange: 181 err := c.handleRoundChange(msg, src) 182 return testBacklog(err) 183 default: 184 logger.Error("Invalid message", "msg", msg) 185 } 186 187 return istanbulcommon.ErrInvalidMessage 188 } 189 190 func (c *core) handleTimeoutMsg() { 191 // If we're not waiting for round change yet, we can try to catch up 192 // the max round with F+1 round change message. We only need to catch up 193 // if the max round is larger than current round. 194 if !c.waitingForRoundChange { 195 maxRound := c.roundChangeSet.MaxRound(c.valSet.F() + 1) 196 if maxRound != nil && maxRound.Cmp(c.current.Round()) > 0 { 197 c.sendRoundChange(maxRound) 198 return 199 } 200 } 201 202 lastProposal, _ := c.backend.LastProposal() 203 if lastProposal != nil && lastProposal.Number().Cmp(c.current.Sequence()) >= 0 { 204 c.logger.Trace("round change timeout, catch up latest sequence", "number", lastProposal.Number().Uint64()) 205 c.startNewRound(common.Big0) 206 } else { 207 c.sendNextRoundChange() 208 } 209 }