github.com/aergoio/aergo@v1.3.1/chain/orphanpool.go (about)

     1  /**
     2   *  @file
     3   *  @copyright defined in aergo/LICENSE.txt
     4   */
     5  
     6  package chain
     7  
     8  import (
     9  	"errors"
    10  	"sync"
    11  
    12  	"github.com/aergoio/aergo/internal/enc"
    13  	"github.com/aergoio/aergo/types"
    14  	"github.com/hashicorp/golang-lru/simplelru"
    15  )
    16  
    17  var (
    18  	DfltOrphanPoolSize = 100
    19  
    20  	ErrRemoveOldestOrphan = errors.New("failed to remove oldest orphan block")
    21  	ErrNotExistOrphanLRU  = errors.New("given orphan doesn't exist in lru")
    22  )
    23  
    24  type OrphanBlock struct {
    25  	*types.Block
    26  }
    27  
    28  type OrphanPool struct {
    29  	sync.RWMutex
    30  	cache map[types.BlockID]*OrphanBlock
    31  	lru   *simplelru.LRU
    32  
    33  	maxCnt int
    34  	curCnt int
    35  }
    36  
    37  func NewOrphanPool(size int) *OrphanPool {
    38  	lru, err := simplelru.NewLRU(DfltOrphanPoolSize, nil)
    39  	if err != nil {
    40  		logger.Fatal().Err(err).Msg("failed to init lru")
    41  		return nil
    42  	}
    43  
    44  	return &OrphanPool{
    45  		cache:  map[types.BlockID]*OrphanBlock{},
    46  		lru:    lru,
    47  		maxCnt: size,
    48  		curCnt: 0,
    49  	}
    50  }
    51  
    52  // add Orphan into the orphan cache pool
    53  func (op *OrphanPool) addOrphan(block *types.Block) error {
    54  	logger.Warn().Str("prev", enc.ToString(block.GetHeader().GetPrevBlockHash())).Msg("add orphan Block")
    55  
    56  	id := types.ToBlockID(block.Header.PrevBlockHash)
    57  	cachedblock, exists := op.cache[id]
    58  	if exists {
    59  		logger.Debug().Str("hash", block.ID()).
    60  			Str("cached", cachedblock.ID()).Msg("already exist")
    61  		return nil
    62  	}
    63  
    64  	if op.isFull() {
    65  		logger.Debug().Msg("orphan block pool is full")
    66  		// replace one
    67  		if err := op.removeOldest(); err != nil {
    68  			return err
    69  		}
    70  	}
    71  
    72  	orpEntry := &OrphanBlock{Block: block}
    73  
    74  	op.cache[id] = orpEntry
    75  	op.lru.Add(id, orpEntry)
    76  	op.curCnt++
    77  
    78  	return nil
    79  }
    80  
    81  // get the BlockID of Root Block of Orphan branch
    82  func (op *OrphanPool) getRoot(block *types.Block) types.BlockID {
    83  	orphanRoot := types.ToBlockID(block.Header.PrevBlockHash)
    84  	prevID := orphanRoot
    85  	for {
    86  		orphan, exists := op.cache[prevID]
    87  		if !exists {
    88  			break
    89  		}
    90  		orphanRoot = prevID
    91  		prevID = types.ToBlockID(orphan.Header.PrevBlockHash)
    92  	}
    93  
    94  	return orphanRoot
    95  }
    96  
    97  func (op *OrphanPool) isFull() bool {
    98  	return op.maxCnt == op.curCnt
    99  }
   100  
   101  // remove oldest block, but also remove expired
   102  func (op *OrphanPool) removeOldest() error {
   103  	var (
   104  		id types.BlockID
   105  	)
   106  
   107  	if !op.isFull() {
   108  		return nil
   109  	}
   110  
   111  	key, _, ok := op.lru.GetOldest()
   112  	if !ok {
   113  		return ErrRemoveOldestOrphan
   114  	}
   115  
   116  	id = key.(types.BlockID)
   117  	if err := op.removeOrphan(id); err != nil {
   118  		return err
   119  	}
   120  
   121  	logger.Debug().Str("hash", id.String()).Msg("orphan block removed(oldest)")
   122  
   123  	return nil
   124  }
   125  
   126  // remove one single element by id (must succeed)
   127  func (op *OrphanPool) removeOrphan(id types.BlockID) error {
   128  	op.curCnt--
   129  	delete(op.cache, id)
   130  	if exist := op.lru.Remove(id); !exist {
   131  		return ErrNotExistOrphanLRU
   132  	}
   133  	return nil
   134  }
   135  
   136  func (op *OrphanPool) getOrphan(hash []byte) *types.Block {
   137  	prevID := types.ToBlockID(hash)
   138  
   139  	orphan, exists := op.cache[prevID]
   140  	if !exists {
   141  		return nil
   142  	} else {
   143  		return orphan.Block
   144  	}
   145  }