github.com/elastos/Elastos.ELA.SideChain.ETH@v0.2.2/dpos/blockpool.go (about)

     1  // Copyright (c) 2017-2019 The Elastos Foundation
     2  // Use of this source code is governed by an MIT
     3  // license that can be found in the LICENSE file.
     4  //
     5  
     6  package dpos
     7  
     8  import (
     9  	"errors"
    10  	"sync"
    11  
    12  	"github.com/elastos/Elastos.ELA.SideChain.ESC/log"
    13  
    14  	"github.com/elastos/Elastos.ELA/common"
    15  	"github.com/elastos/Elastos.ELA/core/types/payload"
    16  )
    17  
    18  const cachedCount = 6
    19  
    20  type DBlock interface {
    21  	GetHash() common.Uint256
    22  	GetHeight() uint64
    23  	Nonce() uint64
    24  }
    25  
    26  type ConfirmInfo struct {
    27  	Confirm *payload.Confirm
    28  	Height  uint64
    29  }
    30  
    31  type BlockPool struct {
    32  	sync.RWMutex
    33  	blocks         map[common.Uint256]DBlock
    34  	confirms       map[common.Uint256]*payload.Confirm
    35  	heightConfirms map[uint64]*payload.Confirm
    36  	badBlocks      map[common.Uint256]DBlock
    37  
    38  	VerifyConfirm func(confirm *payload.Confirm, elaHeight uint64) error
    39  	VerifyBlock   func(block DBlock) error
    40  	SealHash      func(block DBlock) (common.Uint256, error)
    41  
    42  	futureBlocks map[common.Uint256]DBlock
    43  }
    44  
    45  func NewBlockPool(verifyConfirm func(confirm *payload.Confirm, elaHeight uint64) error,
    46  	verifyBlock func(block DBlock) error,
    47  	sealHash func(block DBlock) (common.Uint256, error)) *BlockPool {
    48  	return &BlockPool{
    49  		blocks:         make(map[common.Uint256]DBlock),
    50  		confirms:       make(map[common.Uint256]*payload.Confirm),
    51  		heightConfirms: make(map[uint64]*payload.Confirm),
    52  		badBlocks:      make(map[common.Uint256]DBlock),
    53  		futureBlocks:   make(map[common.Uint256]DBlock),
    54  		VerifyConfirm:  verifyConfirm,
    55  		VerifyBlock:    verifyBlock,
    56  		SealHash:       sealHash,
    57  	}
    58  }
    59  
    60  func (bm *BlockPool) HandleParentBlock(parent DBlock) bool {
    61  	bm.Lock()
    62  	var handledBlock DBlock
    63  	for _, block := range bm.futureBlocks {
    64  		if block.GetHeight()-1 == parent.GetHeight() {
    65  			handledBlock = block
    66  			break
    67  		}
    68  	}
    69  	bm.Unlock()
    70  	if handledBlock != nil {
    71  		bm.AppendDposBlock(handledBlock)
    72  		return true
    73  	}
    74  	return false
    75  }
    76  
    77  func (bm *BlockPool) IsFutureBlock(hash common.Uint256) bool {
    78  	bm.Lock()
    79  	defer bm.Unlock()
    80  
    81  	for _, block := range bm.futureBlocks {
    82  		sealHash, err := bm.SealHash(block)
    83  		if err != nil {
    84  			return false
    85  		}
    86  		if sealHash.IsEqual(hash) {
    87  			return true
    88  		}
    89  	}
    90  	return false
    91  }
    92  
    93  func (bm *BlockPool) AddBadBlock(block DBlock) error {
    94  	bm.Lock()
    95  	defer bm.Unlock()
    96  
    97  	hash, err := bm.SealHash(block)
    98  	if err != nil {
    99  		return err
   100  	}
   101  	if _, ok := bm.badBlocks[hash]; ok {
   102  		return errors.New("duplicate badBlock in pool")
   103  	}
   104  	bm.badBlocks[hash] = block
   105  	return nil
   106  }
   107  
   108  func (bm *BlockPool) IsBadBlockProposal(proposal *payload.DPOSProposal) bool {
   109  	bm.Lock()
   110  	defer bm.Unlock()
   111  	hash := proposal.BlockHash
   112  	if b, ok := bm.badBlocks[hash]; ok {
   113  		log.Info("bad block propsoal", "height", b.GetHeight())
   114  		return true
   115  	}
   116  	return false
   117  }
   118  
   119  func (bm *BlockPool) AppendFutureBlock(dposBlock DBlock) error {
   120  	bm.Lock()
   121  	defer bm.Unlock()
   122  
   123  	return bm.appendFutureBlock(dposBlock)
   124  }
   125  
   126  func (bm *BlockPool) appendFutureBlock(block DBlock) error {
   127  	hash, err := bm.SealHash(block)
   128  	if err != nil {
   129  		return err
   130  	}
   131  	if _, ok := bm.futureBlocks[hash]; ok {
   132  		return errors.New("duplicate futureBlocks in pool")
   133  	}
   134  	bm.futureBlocks[hash] = block
   135  	return nil
   136  }
   137  
   138  func (bm *BlockPool) AppendConfirm(confirm *payload.Confirm) error {
   139  	bm.Lock()
   140  	_, ok := bm.confirms[confirm.Proposal.BlockHash]
   141  	bm.Unlock()
   142  	if ok {
   143  		return errors.New("conformation is all ready in block pool")
   144  	}
   145  	return bm.appendConfirm(confirm)
   146  }
   147  
   148  func (bm *BlockPool) AppendDposBlock(dposBlock DBlock) error {
   149  	Info("[--AppendDposBlock--], height", dposBlock.GetHeight())
   150  	return bm.appendBlock(dposBlock)
   151  }
   152  
   153  func (bm *BlockPool) appendBlock(block DBlock) error {
   154  	// add block
   155  	hash, err := bm.SealHash(block)
   156  	if err != nil {
   157  		return err
   158  	}
   159  	if bm.HasBlock(hash) {
   160  		return errors.New("duplicate block in pool")
   161  	}
   162  	// verify block
   163  	if err := bm.VerifyBlock(block); err != nil {
   164  		Info("[AppendBlock] check block sanity failed, ", err)
   165  		return err
   166  	}
   167  	bm.Lock()
   168  	bm.blocks[hash] = block
   169  	if _, ok := bm.futureBlocks[hash]; ok {
   170  		delete(bm.futureBlocks, hash)
   171  	}
   172  	bm.Unlock()
   173  	return nil
   174  }
   175  
   176  func (bm *BlockPool) appendConfirm(confirm *payload.Confirm) error {
   177  	log.Info("[appendConfirm] start")
   178  	defer Info("[appendConfirm] end")
   179  	// verify confirmation
   180  	dblock, ok := bm.GetBlock(confirm.Proposal.BlockHash)
   181  	if !ok {
   182  		return errors.New("appennd confirm error, not have DBlock")
   183  	}
   184  	if err := bm.VerifyConfirm(confirm, dblock.Nonce()); err != nil {
   185  		return err
   186  	}
   187  	bm.Lock()
   188  	bm.confirms[confirm.Proposal.BlockHash] = confirm
   189  	bm.Unlock()
   190  	err := bm.confirmBlock(confirm.Proposal.BlockHash)
   191  	if err != nil {
   192  		return err
   193  	}
   194  
   195  	return nil
   196  }
   197  
   198  func (bm *BlockPool) ConfirmBlock(hash common.Uint256) error {
   199  	bm.Lock()
   200  	err := bm.confirmBlock(hash)
   201  	bm.Unlock()
   202  	return err
   203  }
   204  
   205  func (bm *BlockPool) confirmBlock(hash common.Uint256) error {
   206  	Info("[ConfirmBlock] block hash:", hash)
   207  	bm.Lock()
   208  
   209  	block, ok := bm.blocks[hash]
   210  	if !ok {
   211  		bm.Unlock()
   212  		return errors.New("there is no block in pool when confirming block")
   213  	}
   214  
   215  	confirm, ok := bm.confirms[hash]
   216  	if !ok {
   217  		bm.Unlock()
   218  		return errors.New("there is no block confirmation in pool when confirming block")
   219  	}
   220  
   221  	bm.heightConfirms[block.GetHeight()] = confirm
   222  	bm.Unlock()
   223  
   224  	return nil
   225  }
   226  
   227  func (bm *BlockPool) AddToBlockMap(block DBlock) {
   228  	bm.Lock()
   229  	defer bm.Unlock()
   230  
   231  	hash, _ := bm.SealHash(block)
   232  
   233  	bm.blocks[hash] = block
   234  }
   235  
   236  func (bm *BlockPool) HasBlock(hash common.Uint256) bool {
   237  	_, ok := bm.GetBlock(hash)
   238  	return ok
   239  }
   240  
   241  func (bm *BlockPool) HashConfirmed(number uint64) bool {
   242  	bm.Lock()
   243  	var temp DBlock = nil
   244  	for _, block := range bm.blocks {
   245  		if block.GetHeight() == number {
   246  			temp = block
   247  			break
   248  		}
   249  	}
   250  	bm.Unlock()
   251  	if temp != nil {
   252  		hash, _ := bm.SealHash(temp)
   253  		_, has := bm.GetConfirm(hash)
   254  		return has
   255  	}
   256  
   257  	return false
   258  }
   259  
   260  func (bm *BlockPool) GetBlock(hash common.Uint256) (DBlock, bool) {
   261  	bm.Lock()
   262  	block, ok := bm.blocks[hash]
   263  	bm.Unlock()
   264  	return block, ok
   265  }
   266  
   267  func (bm *BlockPool) AddToConfirmMap(confirm *payload.Confirm) {
   268  	bm.Lock()
   269  	defer bm.Unlock()
   270  
   271  	bm.confirms[confirm.Proposal.BlockHash] = confirm
   272  }
   273  
   274  func (bm *BlockPool) CleanFinalConfirmedBlock(height uint64) {
   275  	bm.Lock()
   276  	defer bm.Unlock()
   277  
   278  	for _, block := range bm.blocks {
   279  		hash, _ := bm.SealHash(block)
   280  		if height > cachedCount && block.GetHeight() < height-cachedCount {
   281  			delete(bm.blocks, hash)
   282  			delete(bm.confirms, hash)
   283  		}
   284  	}
   285  
   286  	for _, block := range bm.badBlocks {
   287  		hash, _ := bm.SealHash(block)
   288  		if height > cachedCount && block.GetHeight() < height-cachedCount {
   289  			delete(bm.badBlocks, hash)
   290  		}
   291  	}
   292  
   293  	for cheight, _ := range bm.heightConfirms {
   294  		if height > cachedCount && cheight < height-cachedCount {
   295  			delete(bm.heightConfirms, cheight)
   296  		}
   297  	}
   298  }
   299  
   300  func (bm *BlockPool) GetConfirmByHeight(height uint64) (*payload.Confirm, bool) {
   301  	bm.Lock()
   302  	defer bm.Unlock()
   303  
   304  	confirm, ok := bm.heightConfirms[height]
   305  	return confirm, ok
   306  }
   307  
   308  func (bm *BlockPool) GetConfirm(hash common.Uint256) (*payload.Confirm, bool) {
   309  	bm.Lock()
   310  	defer bm.Unlock()
   311  
   312  	confirm, ok := bm.confirms[hash]
   313  	return confirm, ok
   314  }
   315  
   316  func (bm *BlockPool) RemoveConfirm(hash common.Uint256) {
   317  	bm.Lock()
   318  	defer bm.Unlock()
   319  
   320  	if _, ok := bm.confirms[hash]; ok {
   321  		delete(bm.confirms, hash)
   322  	}
   323  }