/
cache.go
160 lines (143 loc) 路 3.99 KB
/
cache.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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package beacon
import (
"bytes"
"encoding/binary"
"github.com/drand/drand/chain"
"github.com/drand/drand/key"
"github.com/drand/drand/log"
"github.com/drand/drand/protobuf/drand"
)
// partialCache is a cache that stores (or not) all the partials the node
// receives.
// The partialCache contains some logic to prevent a DDOS attack on the partial
// signatures cache. Namely, it makes sure that there is a limited number of
// partial signatures from the same index stored at any given time.
type partialCache struct {
rounds map[string]*roundCache
rcvd map[int][]string
l log.Logger
}
func newPartialCache(l log.Logger) *partialCache {
return &partialCache{
rounds: make(map[string]*roundCache),
rcvd: make(map[int][]string),
l: l,
}
}
func roundID(round uint64, previous []byte) string {
var buff bytes.Buffer
_ = binary.Write(&buff, binary.BigEndian, round)
_, _ = buff.Write(previous)
return buff.String()
}
// Append adds a partial signature to the cache.
func (c *partialCache) Append(p *drand.PartialBeaconPacket) {
id := roundID(p.GetRound(), p.GetPreviousSig())
idx, _ := key.Scheme.IndexOf(p.GetPartialSig())
round := c.getCache(id, p)
if round == nil {
return
}
if round.append(p) {
// we increment the counter of that node index
c.rcvd[idx] = append(c.rcvd[idx], id)
}
}
// FlushRounds deletes all rounds cache that are inferior or equal to `round`.
func (c *partialCache) FlushRounds(round uint64) {
for id, cache := range c.rounds {
if cache.round > round {
continue
}
// delete the cache entry
delete(c.rounds, id)
// delete the counter of each nodes that participated in that round
for idx := range cache.sigs {
var idSlice = c.rcvd[idx][:0]
for _, idd := range c.rcvd[idx] {
if idd == id {
continue
}
idSlice = append(idSlice, idd)
}
if len(idSlice) > 0 {
c.rcvd[idx] = idSlice
} else {
delete(c.rcvd, idx)
}
}
}
}
func (c *partialCache) GetRoundCache(round uint64, previous []byte) *roundCache {
id := roundID(round, previous)
return c.rounds[id]
}
// newRoundCache creates a new round cache given p. If the signer of the partial
// already has more than `
func (c *partialCache) getCache(id string, p *drand.PartialBeaconPacket) *roundCache {
if round, ok := c.rounds[id]; ok {
return round
}
idx, _ := key.Scheme.IndexOf(p.GetPartialSig())
if len(c.rcvd[idx]) >= MaxPartialsPerNode {
// this node has submitted too many partials - we take the last one off
toEvict := c.rcvd[idx][0]
round, ok := c.rounds[toEvict]
if !ok {
c.l.Error("cache", "miss", "node", idx, "not_present_for", p.GetRound())
return nil
}
round.flushIndex(idx)
c.rcvd[idx] = append(c.rcvd[idx][1:], id)
// if the round is now empty, delete it
if round.Len() == 0 {
delete(c.rounds, toEvict)
}
}
round := newRoundCache(id, p)
c.rounds[id] = round
return round
}
type roundCache struct {
round uint64
prev []byte
id string
sigs map[int][]byte
}
func newRoundCache(id string, p *drand.PartialBeaconPacket) *roundCache {
return &roundCache{
round: p.GetRound(),
prev: p.GetPreviousSig(),
id: id,
sigs: make(map[int][]byte),
}
}
// append stores the partial and returns true if the partial is not stored . It
// returns false if the cache is already caching this partial signature.
func (r *roundCache) append(p *drand.PartialBeaconPacket) bool {
idx, _ := key.Scheme.IndexOf(p.GetPartialSig())
if _, seen := r.sigs[idx]; seen {
return false
}
r.sigs[idx] = p.GetPartialSig()
return true
}
// Len shows how many items are in the cache
func (r *roundCache) Len() int {
return len(r.sigs)
}
// Msg provides the chain for the current round
func (r *roundCache) Msg() []byte {
return chain.Message(r.round, r.prev)
}
// Partials provides all cached partial signatures
func (r *roundCache) Partials() [][]byte {
partials := make([][]byte, 0, len(r.sigs))
for _, sig := range r.sigs {
partials = append(partials, sig)
}
return partials
}
func (r *roundCache) flushIndex(idx int) {
delete(r.sigs, idx)
}