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  }