github.com/n1ghtfa1l/go-vnt@v0.6.4-alpha.6/consensus/dpos/msg_pool.go (about) 1 // Copyright 2019 The go-vnt Authors 2 // This file is part of the go-vnt library. 3 // 4 // The go-vnt 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-vnt 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-vnt library. If not, see <http://www.gnu.org/licenses/>. 16 17 package dpos 18 19 import ( 20 "fmt" 21 "github.com/pkg/errors" 22 "github.com/vntchain/go-vnt/common" 23 "github.com/vntchain/go-vnt/core/types" 24 "github.com/vntchain/go-vnt/log" 25 "math/big" 26 "sync" 27 ) 28 29 const ( 30 bftMsgBufSize = 30 31 msgCleanInterval = 100 32 ) 33 34 // msgPool store all bft consensus message of each height, and these message grouped by height. 35 type msgPool struct { 36 name string 37 pool map[uint64]*heightMsgPool 38 quorum int // 2f+1 39 lock sync.RWMutex 40 msgHashSet map[common.Hash]uint64 //value为高度方便按高度进行删除 41 } 42 43 func newMsgPool(q int, n string) *msgPool { 44 mp := &msgPool{ 45 name: n, 46 pool: make(map[uint64]*heightMsgPool), 47 quorum: q, 48 msgHashSet: make(map[common.Hash]uint64), 49 } 50 return mp 51 } 52 53 func (mp *msgPool) addMsg(msg types.ConsensusMsg) error { 54 msgHash := msg.Hash() 55 h := msg.GetBlockNum() 56 if h == nil { 57 return fmt.Errorf("addMsg msg's height is nil, msg: %s", msgHash.Hex()) 58 } 59 r := msg.GetRound() 60 61 mp.lock.Lock() 62 defer mp.lock.Unlock() 63 64 if _, exists := mp.msgHashSet[msgHash]; exists { 65 return fmt.Errorf("addMsg msg already exists, msg: %s", msgHash.Hex()) 66 } 67 68 rmp := mp.getOrNewRoundMsgPool(h, r) 69 70 if err := rmp.addMsg(msg); err != nil { 71 log.Warn("Msg pool add msg failed", "pool name", mp.name, "msg type", msg.Type().String(), "error", err) 72 return err 73 } 74 75 mp.msgHashSet[msgHash] = msg.GetBlockNum().Uint64() 76 return nil 77 } 78 79 func (mp *msgPool) getPrePrepareMsg(h *big.Int, r uint32) (*types.PreprepareMsg, error) { 80 mp.lock.RLock() 81 defer mp.lock.RUnlock() 82 83 rmp, err := mp.getRoundMsgPool(h, r) 84 if err != nil { 85 return nil, err 86 } 87 88 if rmp.prePreMsg == nil { 89 return nil, fmt.Errorf("round (%d,%d) has no pre-prepare msg", h.Uint64(), r) 90 } 91 return rmp.prePreMsg, nil 92 } 93 94 func (mp *msgPool) getAllMsgOf(h *big.Int, r uint32) []types.ConsensusMsg { 95 msg := make([]types.ConsensusMsg, 0, bftMsgBufSize*2+1) 96 97 mp.lock.RLock() 98 defer mp.lock.RUnlock() 99 100 rmp, _ := mp.getRoundMsgPool(h, r) 101 if rmp == nil { 102 return msg 103 } 104 105 if rmp.prePreMsg != nil { 106 msg = append(msg, rmp.prePreMsg) 107 } 108 for _, m := range rmp.preMsgs { 109 msg = append(msg, m) 110 } 111 for _, m := range rmp.commitMsgs { 112 msg = append(msg, m) 113 } 114 return msg 115 } 116 117 // getTwoThirdMajorityPrepareMsg get the majority prepare message, and the count of these 118 // message must is bigger than 2f. otherwise, return nil, nil 119 func (mp *msgPool) getTwoThirdMajorityPrepareMsg(h *big.Int, r uint32) ([]*types.PrepareMsg, error) { 120 mp.lock.RLock() 121 defer mp.lock.RUnlock() 122 123 rmp, _ := mp.getRoundMsgPool(h, r) 124 if rmp == nil { 125 return nil, nil 126 } 127 msgs := rmp.preMsgs 128 129 // too less commit message 130 if len(msgs) < mp.quorum { 131 return nil, errors.New("too less prepare message") 132 } 133 134 // count 135 cnt := make(map[common.Hash]int) 136 var maxCntHash common.Hash 137 maxCnt := 0 138 for _, msg := range msgs { 139 bh := msg.BlockHash 140 if _, ok := cnt[bh]; !ok { 141 cnt[bh] = 1 142 } else { 143 cnt[bh] += 1 144 } 145 146 if cnt[bh] > maxCnt { 147 maxCnt = cnt[bh] 148 maxCntHash = bh 149 } 150 } 151 152 // not enough 153 if maxCnt < mp.quorum { 154 return nil, errors.New("majority prepare message is too less") 155 } 156 157 // get prepare massage 158 matchedMsgs := make([]*types.PrepareMsg, 0, maxCnt) 159 for _, msg := range msgs { 160 if msg.BlockHash == maxCntHash { 161 matchedMsgs = append(matchedMsgs, msg) 162 } 163 } 164 return matchedMsgs, nil 165 } 166 167 // getTwoThirdMajorityCommitMsg get the majority commit message, and the count of these 168 // // message must is bigger than 2f. otherwise, return nil, nil 169 func (mp *msgPool) getTwoThirdMajorityCommitMsg(h *big.Int, r uint32) ([]*types.CommitMsg, error) { 170 mp.lock.RLock() 171 defer mp.lock.RUnlock() 172 173 rmp, _ := mp.getRoundMsgPool(h, r) 174 if rmp == nil { 175 return nil, nil 176 } 177 msgs := rmp.commitMsgs 178 179 // too less commit message 180 if len(msgs) < mp.quorum { 181 return nil, errors.New("too less commit message") 182 } 183 184 // count 185 cnt := make(map[common.Hash]int) 186 var maxCntHash common.Hash 187 maxCnt := 0 188 for _, msg := range msgs { 189 bh := msg.BlockHash 190 if _, ok := cnt[bh]; !ok { 191 cnt[bh] = 1 192 } else { 193 cnt[bh] += 1 194 } 195 196 if cnt[bh] > maxCnt { 197 maxCnt = cnt[bh] 198 maxCntHash = bh 199 } 200 } 201 202 // not enough 203 if maxCnt < mp.quorum { 204 return nil, errors.New("majority commit message is too less") 205 } 206 207 // get prepare massage 208 matchedMsgs := make([]*types.CommitMsg, 0, maxCnt) 209 for _, msg := range msgs { 210 if msg.BlockHash == maxCntHash { 211 matchedMsgs = append(matchedMsgs, msg) 212 } 213 } 214 return matchedMsgs, nil 215 } 216 217 // getOrNewRoundMsgPool if round msg pool not exist, it will create. 218 // WARN: caller should lock the msg pool 219 func (mp *msgPool) getOrNewRoundMsgPool(h *big.Int, r uint32) *roundMsgPool { 220 uh := h.Uint64() 221 if _, ok := mp.pool[uh]; !ok { 222 mp.pool[uh] = newHeightMsgPool() 223 } 224 hmp := mp.pool[uh] 225 if _, ok := hmp.pool[r]; !ok { 226 hmp.pool[r] = newRoundMsgPool() 227 } 228 return hmp.pool[r] 229 } 230 231 // getRoundMsgPool just to get round message pool. If not exist, it will return error 232 // WARN: caller should lock the msg pool 233 func (mp *msgPool) getRoundMsgPool(h *big.Int, r uint32) (*roundMsgPool, error) { 234 uh := h.Uint64() 235 if _, ok := mp.pool[uh]; !ok { 236 return nil, fmt.Errorf("hight manager is nil, h: %d", uh) 237 } 238 hmp := mp.pool[uh] 239 if _, ok := hmp.pool[r]; !ok { 240 return nil, fmt.Errorf("round manager is nil, (h,r): (%d, %d)", uh, r) 241 } 242 return hmp.pool[r], nil 243 } 244 245 func (mp *msgPool) cleanMsgOfHeight(h *big.Int) error { 246 mp.lock.Lock() 247 defer mp.lock.Unlock() 248 249 delete(mp.pool, h.Uint64()) 250 for k, height := range mp.msgHashSet { 251 if h.Uint64() == height { 252 delete(mp.msgHashSet, k) 253 } 254 } 255 256 return nil 257 } 258 259 func (mp *msgPool) cleanAllMessage() { 260 mp.lock.Lock() 261 defer mp.lock.Unlock() 262 263 mp.pool = make(map[uint64]*heightMsgPool) 264 mp.msgHashSet = make(map[common.Hash]uint64) 265 266 } 267 268 func (mp *msgPool) cleanOldMessage(h *big.Int) { 269 uh := h.Uint64() 270 271 if uh%msgCleanInterval == 0 { 272 log.Debug("Message pool clean old message") 273 mp.lock.Lock() 274 defer mp.lock.Unlock() 275 276 oldPool := mp.pool 277 oldHashSet := mp.msgHashSet 278 mp.pool = make(map[uint64]*heightMsgPool) 279 mp.msgHashSet = make(map[common.Hash]uint64) 280 for mh, hp := range oldPool { 281 if mh > uh { 282 mp.pool[mh] = hp 283 } 284 } 285 for k, h := range oldHashSet { 286 if h > uh { 287 mp.msgHashSet[k] = h 288 } 289 } 290 log.Debug("Message pool clean old message done", "num. of height cleaned", len(oldPool)-len(mp.pool)) 291 } 292 } 293 294 // heightMsgPool store all bft message of each height, and these message grouped by round index. 295 // WARN: heightMsgPool do not support lock, but MsgPool support lock 296 type heightMsgPool struct { 297 pool map[uint32]*roundMsgPool 298 } 299 300 func newHeightMsgPool() *heightMsgPool { 301 return &heightMsgPool{ 302 pool: make(map[uint32]*roundMsgPool), 303 } 304 } 305 306 func (hmp *heightMsgPool) addMsg(msg types.ConsensusMsg) error { 307 r := msg.GetRound() 308 if _, ok := hmp.pool[r]; !ok { 309 hmp.pool[r] = newRoundMsgPool() 310 } 311 return hmp.pool[r].addMsg(msg) 312 } 313 314 // roundMsgPool store all bft message of each round, and these message grouped by message type. 315 // WARN: heightMsgPool do not support lock, but MsgPool support lock 316 type roundMsgPool struct { 317 prePreMsg *types.PreprepareMsg 318 preMsgs []*types.PrepareMsg 319 commitMsgs []*types.CommitMsg 320 } 321 322 func newRoundMsgPool() *roundMsgPool { 323 return &roundMsgPool{ 324 prePreMsg: nil, 325 preMsgs: make([]*types.PrepareMsg, 0, bftMsgBufSize), 326 commitMsgs: make([]*types.CommitMsg, 0, bftMsgBufSize), 327 } 328 } 329 330 func (rmp *roundMsgPool) addMsg(msg types.ConsensusMsg) error { 331 switch msg.Type() { 332 case types.BftPreprepareMessage: 333 if rmp.prePreMsg == nil { 334 // fmt.Println("not save prepre") 335 rmp.prePreMsg = msg.(*types.PreprepareMsg) 336 } else { 337 return fmt.Errorf("already save a pre-prepare msg at round: (%d,%d), added: %s, adding: %s", 338 msg.GetBlockNum().Uint64(), msg.GetRound(), rmp.prePreMsg.Hash().Hex(), msg.Hash().Hex()) 339 } 340 341 case types.BftPrepareMessage: 342 rmp.preMsgs = append(rmp.preMsgs, msg.(*types.PrepareMsg)) 343 344 case types.BftCommitMessage: 345 rmp.commitMsgs = append(rmp.commitMsgs, msg.(*types.CommitMsg)) 346 347 default: 348 return fmt.Errorf("unknow bft message type: %d, hash: %s", msg.Type(), msg.Hash().Hex()) 349 } 350 return nil 351 } 352 353 func (rmp *roundMsgPool) clean() { 354 rmp.prePreMsg = nil 355 rmp.preMsgs = make([]*types.PrepareMsg, 0, bftMsgBufSize) 356 rmp.commitMsgs = make([]*types.CommitMsg, 0, bftMsgBufSize) 357 }