/
highest_attestation.go
126 lines (111 loc) · 3.71 KB
/
highest_attestation.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
package kv
import (
"context"
"encoding/json"
"github.com/pkg/errors"
slashpb "github.com/prysmaticlabs/prysm/proto/slashing"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
bolt "go.etcd.io/bbolt"
"go.opencensus.io/trace"
)
func persistHighestAttestationCacheOnEviction(db *Store) func(key interface{}, value interface{}) {
// We use a closure here so we can access the database itself
// on the eviction of a span map from the cache. The function has the signature
// required by the ristretto cache OnEvict method.
// See https://godoc.org/github.com/dgraph-io/ristretto#Config.
return func(key interface{}, value interface{}) {
log.Tracef("Evicting highest attestation for validator: %d", key.(uint64))
err := db.update(func(tx *bolt.Tx) error {
enc, err := json.Marshal(value.(map[uint64]*slashpb.HighestAttestation))
if err != nil {
return errors.Wrap(err, "failed to marshal")
}
dbKey := highestAttSetkeyBytes(key.(uint64))
bucket := tx.Bucket(highestAttestationBucket)
if err := bucket.Put(dbKey, enc); err != nil {
return errors.Wrap(err, "failed to add highest attestation to slasher db.")
}
return nil
})
if err != nil {
log.Errorf("Failed to save highest attestation to db on cache eviction: %v", err)
}
}
}
// EnableHighestAttestationCache used to enable or disable highest attestation cache in tests.
func (s *Store) EnableHighestAttestationCache(enable bool) {
s.highestAttCacheEnabled = enable
}
// HighestAttestation returns the highest calculated attestation for a validatorID
func (s *Store) HighestAttestation(ctx context.Context, validatorID uint64) (*slashpb.HighestAttestation, error) {
ctx, span := trace.StartSpan(ctx, "SlasherDB.HighestAttestation")
defer span.End()
if s.highestAttCacheEnabled {
h, ok := s.highestAttestationCache.Get(highestAttSetkey(validatorID))
if ok && h[validatorID] != nil {
return h[validatorID], nil
}
}
key := highestAttSetkeyBytes(validatorID)
var highestAtt *slashpb.HighestAttestation
err := s.view(func(tx *bolt.Tx) error {
b := tx.Bucket(highestAttestationBucket)
if enc := b.Get(key); enc != nil {
set := map[uint64]*slashpb.HighestAttestation{}
err := json.Unmarshal(enc, &set)
if err != nil {
return err
}
highestAtt = set[validatorID]
}
return nil
})
return highestAtt, err
}
// SaveHighestAttestation saves highest attestation for a validatorID.
func (s *Store) SaveHighestAttestation(ctx context.Context, highest *slashpb.HighestAttestation) error {
ctx, span := trace.StartSpan(ctx, "SlasherDB.SaveHighestAttestation")
defer span.End()
if s.highestAttCacheEnabled {
s.highestAttestationCache.Set(highestAttSetkey(highest.ValidatorId), highest)
return nil
}
key := highestAttSetkeyBytes(highest.ValidatorId)
set := map[uint64]*slashpb.HighestAttestation{}
err := s.view(func(tx *bolt.Tx) error {
b := tx.Bucket(highestAttestationBucket)
if enc := b.Get(key); enc != nil {
err := json.Unmarshal(enc, &set)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return err
}
set[highest.ValidatorId] = highest
enc, err := json.Marshal(set)
if err != nil {
return errors.Wrap(err, "failed to marshal")
}
err = s.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(highestAttestationBucket)
if err := bucket.Put(key, enc); err != nil {
return errors.Wrap(err, "failed to add highest attestation to slasher s.")
}
return nil
})
if err != nil {
return err
}
return nil
}
func highestAttSetkeyBytes(validatorID uint64) []byte {
return bytesutil.Uint64ToBytesBigEndian(highestAttSetkey(validatorID))
}
// divide validators by id into 1k-ish buckets (0-1000,1001-1999, etc).
func highestAttSetkey(validatorID uint64) uint64 {
return validatorID / 1000
}