-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
heads.go
113 lines (94 loc) · 2.5 KB
/
heads.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
package headtracker
import (
"sort"
"sync"
"github.com/ethereum/go-ethereum/common"
evmtypes "github.com/smartcontractkit/chainlink/core/chains/evm/types"
)
// Heads is a collection of heads. All methods are thread-safe.
type Heads interface {
// LatestHead returns the block header with the highest number that has been seen, or nil.
LatestHead() *evmtypes.Head
// HeadByHash returns a head for the specified hash, or nil.
HeadByHash(hash common.Hash) *evmtypes.Head
// AddHeads adds newHeads to the collection, eliminates duplicates,
// sorts by head number, fixes parents and cuts off old heads (historyDepth).
AddHeads(historyDepth uint, newHeads ...*evmtypes.Head)
// Count returns number of heads in the collection.
Count() int
}
type heads struct {
heads []*evmtypes.Head
mu sync.RWMutex
}
func NewHeads() Heads {
return &heads{}
}
func (h *heads) LatestHead() *evmtypes.Head {
h.mu.RLock()
defer h.mu.RUnlock()
if len(h.heads) == 0 {
return nil
}
return h.heads[0]
}
func (h *heads) HeadByHash(hash common.Hash) *evmtypes.Head {
h.mu.RLock()
defer h.mu.RUnlock()
for _, head := range h.heads {
if head.Hash == hash {
return head
}
}
return nil
}
func (h *heads) Count() int {
h.mu.RLock()
defer h.mu.RUnlock()
return len(h.heads)
}
func (h *heads) AddHeads(historyDepth uint, newHeads ...*evmtypes.Head) {
h.mu.Lock()
defer h.mu.Unlock()
headsMap := make(map[common.Hash]*evmtypes.Head, len(h.heads)+len(newHeads))
for _, head := range append(h.heads, newHeads...) {
if head.Hash == head.ParentHash {
// shouldn't happen but it is untrusted input
continue
}
// copy all head objects to avoid races when a previous head chain is used
// elsewhere (since we mutate Parent here)
headCopy := *head
headCopy.Parent = nil // always build it from scratch in case it points to a head too old to be included
// map eliminates duplicates
headsMap[head.Hash] = &headCopy
}
heads := make([]*evmtypes.Head, len(headsMap))
// unsorted unique heads
{
var i int
for _, head := range headsMap {
heads[i] = head
i++
}
}
// sort the heads
sort.SliceStable(heads, func(i, j int) bool {
// sorting from the highest number to lowest
return heads[i].Number > heads[j].Number
})
// cut off the oldest
if uint(len(heads)) > historyDepth {
heads = heads[:historyDepth]
}
// assign parents
for i := 0; i < len(heads)-1; i++ {
head := heads[i]
parent, exists := headsMap[head.ParentHash]
if exists {
head.Parent = parent
}
}
// set
h.heads = heads
}