OSDN Git Service

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