github.com/bigzoro/my_simplechain@v0.0.0-20240315012955-8ad0a2a29bb9/consensus/hotstuff/hotsptcl/handler.go (about) 1 package hotstuffprotocol 2 3 import ( 4 "context" 5 "crypto/rand" 6 "errors" 7 "fmt" 8 "sync" 9 "time" 10 11 "github.com/bigzoro/my_simplechain/common" 12 "github.com/bigzoro/my_simplechain/consensus" 13 bls "github.com/bigzoro/my_simplechain/consensus/hotstuff/bls12-381" 14 hots "github.com/bigzoro/my_simplechain/consensus/hotstuff/common" 15 "github.com/bigzoro/my_simplechain/core/types" 16 "github.com/bigzoro/my_simplechain/log" 17 "github.com/bigzoro/my_simplechain/p2p" 18 "github.com/bigzoro/my_simplechain/p2p/enode" 19 ) 20 21 var ( 22 errNotLegalPeer = errors.New("not a legal hotstuff peer") 23 errAlreadyRegistered = errors.New("replica is already registered") 24 errClosed = errors.New("relica hub is closed") 25 26 handshakeTimeoutRTT = 5 * time.Second 27 28 // handshakeValidatorLen is the fixed length of the authentication random number. 29 // used during handshake 30 handshakeValidatorLen = 256 31 ) 32 33 // handler handles Hotstuff protocol request messages. 34 type handler interface { 35 Timeout(id hots.ID, view uint64) error 36 37 Vote(id hots.ID, view uint64, block []byte, sig *bls.PartialSignature) error 38 } 39 40 type replica struct { 41 id hots.ID 42 handler handler 43 44 peer *p2p.Peer 45 rw p2p.MsgReadWriter 46 47 queued chan *timeoutMsg 48 quit chan struct{} 49 } 50 51 func newReplica(id hots.ID, peer *p2p.Peer, rw p2p.MsgReadWriter) *replica { 52 return &replica{ 53 peer: peer, 54 rw: rw, 55 id: id, 56 queued: make(chan *timeoutMsg), 57 quit: make(chan struct{}), 58 } 59 } 60 61 func (rep *replica) handle() error { 62 msg, err := rep.rw.ReadMsg() 63 if err != nil { 64 return err 65 } 66 if msg.Size > protocolMaxMsgSize { 67 return fmt.Errorf("too large message,%d - %d", msg.Size, protocolMaxMsgSize) 68 } 69 defer msg.Discard() 70 71 switch msg.Code { 72 case vote: 73 var vote voteMsg 74 if err := msg.Decode(&vote); err != nil { 75 return fmt.Errorf("msg %v: %v", msg, err) 76 } 77 sig := new(bls.PartialSignature) 78 if err := sig.FromBytes(vote.Signature); err != nil { 79 return fmt.Errorf("signature deserialization:%v", err) 80 } 81 if err := rep.handler.Vote(rep.id, vote.View, vote.Hash.Bytes(), sig); err != nil { 82 rep.peer.Log().Error("handle vote request", "error", err) 83 } 84 85 case timeout: 86 var timeout timeoutMsg 87 if err := msg.Decode(&timeout); err != nil { 88 return fmt.Errorf("msg %v: %v", msg, err) 89 } 90 if err := rep.handler.Timeout(rep.id, timeout.View); err != nil { 91 rep.peer.Log().Error("handle timeout request", "error", err) 92 } 93 94 default: 95 return fmt.Errorf("invalid message code %d", msg.Code) 96 } 97 return nil 98 } 99 100 func (rep *replica) run(ctx context.Context) { 101 for { 102 select { 103 case msg := <-rep.queued: 104 p2p.Send(rep.rw, timeout, msg) 105 106 case <-ctx.Done(): 107 return 108 case <-rep.quit: 109 return 110 } 111 } 112 } 113 114 func (rep *replica) close() error { 115 close(rep.quit) 116 return nil 117 } 118 119 type identifier interface { 120 Sign([]byte) (*bls.PartialSignature, error) 121 122 VerifyAt(*types.Header, *bls.PartialSignature, []byte) error 123 } 124 125 type engine interface { 126 identifier 127 handler 128 } 129 130 // Hub manages the lifecycle of all connected Hotstuff peers. 131 type Hub struct { 132 closed bool 133 134 ctx context.Context 135 cancel context.CancelFunc 136 137 mux sync.RWMutex 138 peers map[hots.ID]*replica 139 enodes map[hots.ID]enode.ID 140 141 engine engine 142 143 wg sync.WaitGroup 144 } 145 146 func NewHub(engine engine) *Hub { 147 ctx, cancel := context.WithCancel(context.Background()) 148 return &Hub{ 149 ctx: ctx, 150 cancel: cancel, 151 engine: engine, 152 peers: make(map[hots.ID]*replica), 153 enodes: make(map[hots.ID]enode.ID), 154 } 155 } 156 157 func (hub *Hub) Vote(remote hots.ID, view uint64, block common.Hash, sig *bls.PartialSignature) error { 158 hub.mux.RLock() 159 defer hub.mux.RUnlock() 160 161 if hub.closed { 162 return errClosed 163 } 164 165 peer, ok := hub.peers[remote] 166 if !ok { 167 return fmt.Errorf("unknown peer %v", remote) 168 } 169 sigbytes, _ := sig.ToBytes() 170 return p2p.Send(peer.rw, vote, &voteMsg{ 171 View: view, 172 Hash: block, 173 Signature: sigbytes, 174 }) 175 } 176 177 func (hub *Hub) GetEnode(ids []hots.ID) []enode.ID { 178 hub.mux.RLock() 179 defer hub.mux.RUnlock() 180 181 enodes := make([]enode.ID, 0, len(ids)) 182 for i := range ids { 183 if enodeId, ok := hub.enodes[ids[i]]; ok { 184 enodes = append(enodes, enodeId) 185 } 186 } 187 return enodes 188 } 189 190 func (hub *Hub) Timeout(view uint64) error { 191 hub.mux.RLock() 192 defer hub.mux.RUnlock() 193 194 if hub.closed { 195 return errClosed 196 } 197 198 for _, peer := range hub.peers { 199 select { 200 case peer.queued <- &timeoutMsg{View: view}: 201 case <-peer.quit: 202 } 203 } 204 205 return nil 206 } 207 208 func (hub *Hub) Close() error { 209 hub.cancel() 210 211 hub.mux.Lock() 212 for _, replica := range hub.peers { 213 replica.peer.Disconnect(p2p.DiscQuitting) 214 } 215 hub.closed = true 216 hub.mux.Unlock() 217 218 hub.wg.Wait() 219 log.Info("Hotstuff protocol handler stop") 220 221 return nil 222 } 223 224 func (hub *Hub) Handle(peer *p2p.Peer, rw p2p.MsgReadWriter, chain consensus.ChainReader) error { 225 id, err := hub.handshake(peer, rw, chain) 226 if err != nil { 227 peer.Log().Debug("peer handshake failed", "error", err) 228 return err 229 } 230 replica := newReplica(id, peer, rw) 231 if err := hub.register(replica); err != nil { 232 return err 233 } 234 defer hub.unregister(replica.id) 235 236 hub.wg.Add(1) 237 defer hub.wg.Done() 238 239 peer.Log().Info("hotstuff peer connected", "peer", peer.Name()) 240 241 // Handle incoming messages until the connection is torn down 242 for { 243 if err := replica.handle(); err != nil { 244 peer.Log().Debug("hotstuff message handling failed", "err", err) 245 return err 246 } 247 } 248 } 249 250 // Handshake executes the hotstuff protocol handshake 251 func (hub *Hub) handshake(peer *p2p.Peer, rw p2p.MsgReadWriter, chain consensus.ChainReader) (id hots.ID, err error) { 252 253 // 1.exchange random seed 254 // 2.exchange authentication signature 255 256 errc := make(chan error) 257 idch := make(chan hots.ID) 258 259 go func() { 260 if id, err := hub.checkHandshake(rw, chain); err != nil { 261 errc <- err 262 } else { 263 idch <- id 264 } 265 }() 266 267 timeout := time.NewTimer(2 * handshakeTimeoutRTT) 268 defer timeout.Stop() 269 270 select { 271 case id := <-idch: 272 return id, nil 273 case err := <-errc: 274 return hots.ID{}, err 275 case <-timeout.C: 276 return hots.ID{}, p2p.DiscReadTimeout 277 } 278 } 279 280 func (hub *Hub) checkHandshake(rw p2p.MsgReadWriter, chain consensus.ChainReader) (hots.ID, error) { 281 seed := make([]byte, handshakeValidatorLen) 282 rand.Read(seed) 283 if err := p2p.Send(rw, status, &handshakeData{Random: seed}); err != nil { 284 return hots.ID{}, err 285 } 286 287 validator, err := readLegacy(rw) 288 if err != nil { 289 return hots.ID{}, err 290 } 291 292 if len(validator.Random) != handshakeValidatorLen { 293 return hots.ID{}, errors.New("invalid handshake params") 294 } 295 296 sig, _ := hub.engine.Sign(validator.Random) 297 sigbytes, _ := sig.ToBytes() 298 if err := p2p.Send(rw, status, &handshakeData{Signature: sigbytes}); err != nil { 299 return hots.ID{}, err 300 } 301 302 if validator, err = readLegacy(rw); err != nil { 303 return hots.ID{}, err 304 } 305 if err = sig.FromBytes(validator.Signature); err != nil { 306 return hots.ID{}, err 307 } 308 309 if err = hub.engine.VerifyAt(chain.CurrentHeader(), sig, seed); err != nil { 310 return hots.ID{}, err 311 } 312 313 log.Debug("Hotstuff handshake", "id", sig.ID()) 314 return sig.ID(), nil 315 } 316 317 func (hub *Hub) register(rep *replica) error { 318 hub.mux.Lock() 319 defer hub.mux.Unlock() 320 321 if hub.closed { 322 return errClosed 323 } 324 if _, ok := hub.peers[rep.id]; ok { 325 return errAlreadyRegistered 326 } 327 328 hub.peers[rep.id] = rep 329 hub.enodes[rep.id] = rep.peer.ID() 330 rep.handler = hub.engine 331 go rep.run(hub.ctx) 332 333 return nil 334 } 335 336 func (hub *Hub) unregister(id hots.ID) error { 337 hub.mux.Lock() 338 defer hub.mux.Unlock() 339 340 if rep, ok := hub.peers[id]; ok { 341 delete(hub.peers, id) 342 delete(hub.enodes, id) 343 rep.close() 344 } 345 346 return nil 347 } 348 349 func readLegacy(rw p2p.MsgReadWriter) (*handshakeData, error) { 350 msg, err := rw.ReadMsg() 351 if err != nil { 352 return nil, err 353 } 354 355 if msg.Code != status { 356 return nil, fmt.Errorf("first msg has code %x (!= %x)", msg.Code, status) 357 } 358 if msg.Size > protocolMaxMsgSize { 359 return nil, fmt.Errorf("too large message,%d - %d", msg.Size, protocolMaxMsgSize) 360 } 361 var status handshakeData 362 return &status, msg.Decode(&status) 363 }