trust.go raw
1 package directory_client
2
3 import (
4 "sync"
5 "time"
6
7 "next.orly.dev/pkg/nostr/encoders/event"
8 "next.orly.dev/pkg/protocol/directory"
9 )
10
11 // TrustCalculator computes aggregate trust scores from multiple trust acts.
12 //
13 // It maintains a collection of trust acts and provides methods to calculate
14 // weighted trust scores for relay public keys.
15 type TrustCalculator struct {
16 mu sync.RWMutex
17 acts map[string][]*directory.TrustAct
18 }
19
20 // NewTrustCalculator creates a new trust calculator instance.
21 func NewTrustCalculator() *TrustCalculator {
22 return &TrustCalculator{
23 acts: make(map[string][]*directory.TrustAct),
24 }
25 }
26
27 // AddAct adds a trust act to the calculator.
28 func (tc *TrustCalculator) AddAct(act *directory.TrustAct) {
29 if act == nil {
30 return
31 }
32
33 tc.mu.Lock()
34 defer tc.mu.Unlock()
35
36 targetPubkey := act.TargetPubkey
37 tc.acts[targetPubkey] = append(tc.acts[targetPubkey], act)
38 }
39
40 // CalculateTrust calculates an aggregate trust score for a public key.
41 //
42 // The score is computed as a weighted average where:
43 // - high trust = 100
44 // - medium trust = 50
45 // - low trust = 25
46 //
47 // Expired trust acts are excluded from the calculation.
48 // Returns a score between 0 and 100.
49 func (tc *TrustCalculator) CalculateTrust(pubkey string) float64 {
50 tc.mu.RLock()
51 defer tc.mu.RUnlock()
52
53 acts := tc.acts[pubkey]
54 if len(acts) == 0 {
55 return 0
56 }
57
58 now := time.Now()
59 var total float64
60 var count int
61
62 // Weight mapping
63 weights := map[directory.TrustLevel]float64{
64 directory.TrustLevelHigh: 100,
65 directory.TrustLevelMedium: 50,
66 directory.TrustLevelLow: 25,
67 }
68
69 for _, act := range acts {
70 // Skip expired acts
71 if act.Expiry != nil && act.Expiry.Before(now) {
72 continue
73 }
74
75 weight, ok := weights[act.TrustLevel]
76 if !ok {
77 continue
78 }
79
80 total += weight
81 count++
82 }
83
84 if count == 0 {
85 return 0
86 }
87
88 return total / float64(count)
89 }
90
91 // GetActs returns all trust acts for a specific public key.
92 func (tc *TrustCalculator) GetActs(pubkey string) []*directory.TrustAct {
93 tc.mu.RLock()
94 defer tc.mu.RUnlock()
95
96 acts := tc.acts[pubkey]
97 result := make([]*directory.TrustAct, len(acts))
98 copy(result, acts)
99 return result
100 }
101
102 // GetActiveTrustActs returns only non-expired trust acts for a public key.
103 func (tc *TrustCalculator) GetActiveTrustActs(pubkey string) []*directory.TrustAct {
104 tc.mu.RLock()
105 defer tc.mu.RUnlock()
106
107 acts := tc.acts[pubkey]
108 now := time.Now()
109 result := make([]*directory.TrustAct, 0)
110
111 for _, act := range acts {
112 if act.Expiry == nil || act.Expiry.After(now) {
113 result = append(result, act)
114 }
115 }
116
117 return result
118 }
119
120 // Clear removes all trust acts from the calculator.
121 func (tc *TrustCalculator) Clear() {
122 tc.mu.Lock()
123 defer tc.mu.Unlock()
124
125 tc.acts = make(map[string][]*directory.TrustAct)
126 }
127
128 // GetAllPubkeys returns all public keys that have trust acts.
129 func (tc *TrustCalculator) GetAllPubkeys() []string {
130 tc.mu.RLock()
131 defer tc.mu.RUnlock()
132
133 pubkeys := make([]string, 0, len(tc.acts))
134 for pubkey := range tc.acts {
135 pubkeys = append(pubkeys, pubkey)
136 }
137 return pubkeys
138 }
139
140 // ReplicationFilter manages replication decisions based on trust scores.
141 //
142 // It uses a TrustCalculator to compute trust scores and determines which
143 // relays are trusted enough for replication based on a minimum threshold.
144 type ReplicationFilter struct {
145 mu sync.RWMutex
146 trustCalc *TrustCalculator
147 minTrustScore float64
148 trustedRelays map[string]bool
149 }
150
151 // NewReplicationFilter creates a new replication filter with a minimum trust score threshold.
152 func NewReplicationFilter(minTrustScore float64) *ReplicationFilter {
153 return &ReplicationFilter{
154 trustCalc: NewTrustCalculator(),
155 minTrustScore: minTrustScore,
156 trustedRelays: make(map[string]bool),
157 }
158 }
159
160 // AddTrustAct adds a trust act and updates the trusted relays set.
161 func (rf *ReplicationFilter) AddTrustAct(act *directory.TrustAct) {
162 if act == nil {
163 return
164 }
165
166 rf.trustCalc.AddAct(act)
167
168 // Update trusted relays based on new trust score
169 score := rf.trustCalc.CalculateTrust(act.TargetPubkey)
170
171 rf.mu.Lock()
172 defer rf.mu.Unlock()
173
174 if score >= rf.minTrustScore {
175 rf.trustedRelays[act.TargetPubkey] = true
176 } else {
177 delete(rf.trustedRelays, act.TargetPubkey)
178 }
179 }
180
181 // ShouldReplicate checks if a relay is trusted enough for replication.
182 func (rf *ReplicationFilter) ShouldReplicate(pubkey string) bool {
183 rf.mu.RLock()
184 defer rf.mu.RUnlock()
185
186 return rf.trustedRelays[pubkey]
187 }
188
189 // GetTrustedRelays returns all trusted relay public keys.
190 func (rf *ReplicationFilter) GetTrustedRelays() []string {
191 rf.mu.RLock()
192 defer rf.mu.RUnlock()
193
194 relays := make([]string, 0, len(rf.trustedRelays))
195 for pubkey := range rf.trustedRelays {
196 relays = append(relays, pubkey)
197 }
198 return relays
199 }
200
201 // GetTrustScore returns the trust score for a relay.
202 func (rf *ReplicationFilter) GetTrustScore(pubkey string) float64 {
203 return rf.trustCalc.CalculateTrust(pubkey)
204 }
205
206 // SetMinTrustScore updates the minimum trust score threshold and recalculates trusted relays.
207 func (rf *ReplicationFilter) SetMinTrustScore(minScore float64) {
208 rf.mu.Lock()
209 defer rf.mu.Unlock()
210
211 rf.minTrustScore = minScore
212
213 // Recalculate trusted relays with new threshold
214 rf.trustedRelays = make(map[string]bool)
215 for _, pubkey := range rf.trustCalc.GetAllPubkeys() {
216 score := rf.trustCalc.CalculateTrust(pubkey)
217 if score >= rf.minTrustScore {
218 rf.trustedRelays[pubkey] = true
219 }
220 }
221 }
222
223 // GetMinTrustScore returns the current minimum trust score threshold.
224 func (rf *ReplicationFilter) GetMinTrustScore() float64 {
225 rf.mu.RLock()
226 defer rf.mu.RUnlock()
227
228 return rf.minTrustScore
229 }
230
231 // FilterEvents filters events to only those from trusted relays.
232 func (rf *ReplicationFilter) FilterEvents(events []*event.E) []*event.E {
233 rf.mu.RLock()
234 defer rf.mu.RUnlock()
235
236 filtered := make([]*event.E, 0)
237 for _, ev := range events {
238 if rf.trustedRelays[string(ev.Pubkey)] {
239 filtered = append(filtered, ev)
240 }
241 }
242 return filtered
243 }
244