OSDN Git Service

Process block integra test (#1740)
[bytom/bytom.git] / protocol / orphan_manage.go
1 package protocol
2
3 import (
4         "sync"
5         "time"
6
7         log "github.com/sirupsen/logrus"
8
9         "github.com/bytom/protocol/bc"
10         "github.com/bytom/protocol/bc/types"
11         "github.com/bytom/testutil"
12 )
13
14 var (
15         orphanBlockTTL           = 60 * time.Minute
16         orphanExpireWorkInterval = 3 * time.Minute
17         numOrphanBlockLimit      = 256
18 )
19
20 type orphanBlock struct {
21         *types.Block
22         expiration time.Time
23 }
24
25 // OrphanManage is use to handle all the orphan block
26 type OrphanManage struct {
27         orphan      map[bc.Hash]*orphanBlock
28         prevOrphans map[bc.Hash][]*bc.Hash
29         mtx         sync.RWMutex
30 }
31
32 // NewOrphanManage return a new orphan block
33 func NewOrphanManage() *OrphanManage {
34         o := &OrphanManage{
35                 orphan:      make(map[bc.Hash]*orphanBlock),
36                 prevOrphans: make(map[bc.Hash][]*bc.Hash),
37         }
38
39         go o.orphanExpireWorker()
40         return o
41 }
42
43 // Add will add the block to OrphanManage
44 func (o *OrphanManage) Add(block *types.Block) {
45         blockHash := block.Hash()
46         o.mtx.Lock()
47         defer o.mtx.Unlock()
48
49         if _, ok := o.orphan[blockHash]; ok {
50                 return
51         }
52
53         if len(o.orphan) >= numOrphanBlockLimit {
54                 log.WithFields(log.Fields{"module": logModule, "hash": blockHash.String(), "height": block.Height}).Info("the number of orphan blocks exceeds the limit")
55                 return
56         }
57
58         o.orphan[blockHash] = &orphanBlock{block, time.Now().Add(orphanBlockTTL)}
59         o.prevOrphans[block.PreviousBlockHash] = append(o.prevOrphans[block.PreviousBlockHash], &blockHash)
60
61         log.WithFields(log.Fields{"module": logModule, "hash": blockHash.String(), "height": block.Height}).Info("add block to orphan")
62 }
63
64 // BlockExist check is the block in OrphanManage
65 func (o *OrphanManage) BlockExist(hash *bc.Hash) bool {
66         o.mtx.RLock()
67         _, ok := o.orphan[*hash]
68         o.mtx.RUnlock()
69         return ok
70 }
71
72 // Delete will delete the block from OrphanManage
73 func (o *OrphanManage) Delete(hash *bc.Hash) {
74         o.mtx.Lock()
75         defer o.mtx.Unlock()
76         o.delete(hash)
77 }
78
79 func (o *OrphanManage) Equals(o1 *OrphanManage) bool {
80         if o1 == nil {
81                 return false
82         }
83         return testutil.DeepEqual(o.orphan, o1.orphan) && testutil.DeepEqual(o.prevOrphans, o1.prevOrphans)
84 }
85
86 // Get return the orphan block by hash
87 func (o *OrphanManage) Get(hash *bc.Hash) (*types.Block, bool) {
88         o.mtx.RLock()
89         block, ok := o.orphan[*hash]
90         o.mtx.RUnlock()
91         return block.Block, ok
92 }
93
94 // GetPrevOrphans return the list of child orphans
95 func (o *OrphanManage) GetPrevOrphans(hash *bc.Hash) ([]*bc.Hash, bool) {
96         o.mtx.RLock()
97         prevOrphans, ok := o.prevOrphans[*hash]
98         o.mtx.RUnlock()
99         return prevOrphans, ok
100 }
101
102 func (o *OrphanManage) delete(hash *bc.Hash) {
103         block, ok := o.orphan[*hash]
104         if !ok {
105                 return
106         }
107         delete(o.orphan, *hash)
108
109         prevOrphans, ok := o.prevOrphans[block.Block.PreviousBlockHash]
110         if !ok || len(prevOrphans) == 1 {
111                 delete(o.prevOrphans, block.Block.PreviousBlockHash)
112                 return
113         }
114
115         for i, preOrphan := range prevOrphans {
116                 if preOrphan == hash {
117                         o.prevOrphans[block.Block.PreviousBlockHash] = append(prevOrphans[:i], prevOrphans[i+1:]...)
118                         return
119                 }
120         }
121 }
122
123 func (o *OrphanManage) orphanExpireWorker() {
124         ticker := time.NewTicker(orphanExpireWorkInterval)
125         for now := range ticker.C {
126                 o.orphanExpire(now)
127         }
128         ticker.Stop()
129 }
130
131 func (o *OrphanManage) orphanExpire(now time.Time) {
132         o.mtx.Lock()
133         defer o.mtx.Unlock()
134         for hash, orphan := range o.orphan {
135                 if orphan.expiration.Before(now) {
136                         o.delete(&hash)
137                 }
138         }
139 }