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