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 }