-
Notifications
You must be signed in to change notification settings - Fork 42
/
nullifier_log.go
120 lines (97 loc) · 2.93 KB
/
nullifier_log.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
package rln
import (
"bytes"
"context"
"errors"
"sync"
"time"
"github.com/waku-org/go-zerokit-rln/rln"
"go.uber.org/zap"
)
// NullifierLog is the log of nullifiers and Shamir shares of the past messages grouped per epoch
type NullifierLog struct {
sync.RWMutex
log *zap.Logger
nullifierLog map[rln.Nullifier][]rln.ProofMetadata // Might make sense to replace this map by a shrinkable map due to https://github.com/golang/go/issues/20135.
nullifierQueue []rln.Nullifier
}
// NewNullifierLog creates an instance of NullifierLog
func NewNullifierLog(ctx context.Context, log *zap.Logger) *NullifierLog {
result := &NullifierLog{
nullifierLog: make(map[rln.Nullifier][]rln.ProofMetadata),
log: log,
}
go result.cleanup(ctx)
return result
}
var errAlreadyExists = errors.New("proof already exists")
// Insert stores a proof in the nullifier log only if it doesnt exist already
func (n *NullifierLog) Insert(proofMD rln.ProofMetadata) error {
n.Lock()
defer n.Unlock()
proofs, ok := n.nullifierLog[proofMD.ExternalNullifier]
if ok {
// check if an identical record exists
for _, p := range proofs {
if p.Equals(proofMD) {
// TODO: slashing logic
return errAlreadyExists
}
}
}
n.nullifierLog[proofMD.ExternalNullifier] = append(proofs, proofMD)
n.nullifierQueue = append(n.nullifierQueue, proofMD.ExternalNullifier)
return nil
}
// HasDuplicate returns true if there is another message in the `nullifierLog` with the same
// epoch and nullifier as `msg`'s epoch and nullifier but different Shamir secret shares
// otherwise, returns false
func (n *NullifierLog) HasDuplicate(proofMD rln.ProofMetadata) (bool, error) {
n.RLock()
defer n.RUnlock()
proofs, ok := n.nullifierLog[proofMD.ExternalNullifier]
if !ok {
// epoch does not exist
return false, nil
}
for _, p := range proofs {
if p.Equals(proofMD) {
// there is an identical record, ignore the msg
return true, nil
}
}
// check for a message with the same nullifier but different secret shares
matched := false
for _, it := range proofs {
if bytes.Equal(it.Nullifier[:], proofMD.Nullifier[:]) && (!bytes.Equal(it.ShareX[:], proofMD.ShareX[:]) || !bytes.Equal(it.ShareY[:], proofMD.ShareY[:])) {
matched = true
break
}
}
return matched, nil
}
// cleanup cleans up the log every time there are more than MaxEpochGap epochs stored in it
func (n *NullifierLog) cleanup(ctx context.Context) {
t := time.NewTicker(1 * time.Minute) // TODO: tune this
defer t.Stop()
for {
select {
case <-ctx.Done():
return
case <-t.C:
func() {
n.Lock()
defer n.Unlock()
if int64(len(n.nullifierQueue)) < maxEpochGap {
return
}
n.log.Debug("clearing epochs from the nullifier log", zap.Int64("count", maxEpochGap))
toDelete := n.nullifierQueue[0:maxEpochGap]
for _, l := range toDelete {
delete(n.nullifierLog, l)
}
n.nullifierQueue = n.nullifierQueue[maxEpochGap:]
}()
}
}
}