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
146
147
148
149
150
151
152
153
154
155
156
157
|
package downloader
import (
"math"
"math/big"
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"gopkg.in/fatih/set.v0"
)
// queue represents hashes that are either need fetching or are being fetched
type queue struct {
hashPool *set.Set
fetchPool *set.Set
blockHashes *set.Set
mu sync.Mutex
fetching map[string]*chunk
blocks []*types.Block
}
func newqueue() *queue {
return &queue{
hashPool: set.New(),
fetchPool: set.New(),
blockHashes: set.New(),
fetching: make(map[string]*chunk),
}
}
func (c *queue) reset() {
c.mu.Lock()
defer c.mu.Unlock()
c.hashPool.Clear()
c.fetchPool.Clear()
c.blockHashes.Clear()
c.blocks = nil
c.fetching = make(map[string]*chunk)
}
// reserve a `max` set of hashes for `p` peer.
func (c *queue) get(p *peer, max int) *chunk {
c.mu.Lock()
defer c.mu.Unlock()
// return nothing if the pool has been depleted
if c.hashPool.Size() == 0 {
return nil
}
limit := int(math.Min(float64(max), float64(c.hashPool.Size())))
// Create a new set of hashes
hashes, i := set.New(), 0
c.hashPool.Each(func(v interface{}) bool {
// break on limit
if i == limit {
return false
}
// skip any hashes that have previously been requested from the peer
if p.ignored.Has(v) {
return true
}
hashes.Add(v)
i++
return true
})
// if no hashes can be requested return a nil chunk
if hashes.Size() == 0 {
return nil
}
// remove the fetchable hashes from hash pool
c.hashPool.Separate(hashes)
c.fetchPool.Merge(hashes)
// Create a new chunk for the seperated hashes. The time is being used
// to reset the chunk (timeout)
chunk := &chunk{p, hashes, time.Now()}
// register as 'fetching' state
c.fetching[p.id] = chunk
// create new chunk for peer
return chunk
}
func (c *queue) has(hash common.Hash) bool {
return c.hashPool.Has(hash) || c.fetchPool.Has(hash)
}
func (c *queue) addBlock(id string, block *types.Block, td *big.Int) {
c.mu.Lock()
defer c.mu.Unlock()
// when adding a block make sure it doesn't already exist
if !c.blockHashes.Has(block.Hash()) {
c.hashPool.Remove(block.Hash())
c.blocks = append(c.blocks, block)
}
}
// deliver delivers a chunk to the queue that was requested of the peer
func (c *queue) deliver(id string, blocks []*types.Block) {
c.mu.Lock()
defer c.mu.Unlock()
chunk := c.fetching[id]
// If the chunk was never requested simply ignore it
if chunk != nil {
delete(c.fetching, id)
// check the length of the returned blocks. If the length of blocks is 0
// we'll assume the peer doesn't know about the chain.
if len(blocks) == 0 {
// So we can ignore the blocks we didn't know about
chunk.peer.ignored.Merge(chunk.hashes)
}
// seperate the blocks and the hashes
blockHashes := chunk.fetchedHashes(blocks)
// merge block hashes
c.blockHashes.Merge(blockHashes)
// Add the blocks
c.blocks = append(c.blocks, blocks...)
// Add back whatever couldn't be delivered
c.hashPool.Merge(chunk.hashes)
c.fetchPool.Separate(chunk.hashes)
}
}
// puts puts sets of hashes on to the queue for fetching
func (c *queue) put(hashes *set.Set) {
c.mu.Lock()
defer c.mu.Unlock()
c.hashPool.Merge(hashes)
}
type chunk struct {
peer *peer
hashes *set.Set
itime time.Time
}
func (ch *chunk) fetchedHashes(blocks []*types.Block) *set.Set {
fhashes := set.New()
for _, block := range blocks {
fhashes.Add(block.Hash())
}
ch.hashes.Separate(fhashes)
return fhashes
}
|