You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
243 lines
5.8 KiB
243 lines
5.8 KiB
package directory_client |
|
|
|
import ( |
|
"sync" |
|
"time" |
|
|
|
"next.orly.dev/pkg/encoders/event" |
|
"next.orly.dev/pkg/protocol/directory" |
|
) |
|
|
|
// TrustCalculator computes aggregate trust scores from multiple trust acts. |
|
// |
|
// It maintains a collection of trust acts and provides methods to calculate |
|
// weighted trust scores for relay public keys. |
|
type TrustCalculator struct { |
|
mu sync.RWMutex |
|
acts map[string][]*directory.TrustAct |
|
} |
|
|
|
// NewTrustCalculator creates a new trust calculator instance. |
|
func NewTrustCalculator() *TrustCalculator { |
|
return &TrustCalculator{ |
|
acts: make(map[string][]*directory.TrustAct), |
|
} |
|
} |
|
|
|
// AddAct adds a trust act to the calculator. |
|
func (tc *TrustCalculator) AddAct(act *directory.TrustAct) { |
|
if act == nil { |
|
return |
|
} |
|
|
|
tc.mu.Lock() |
|
defer tc.mu.Unlock() |
|
|
|
targetPubkey := act.TargetPubkey |
|
tc.acts[targetPubkey] = append(tc.acts[targetPubkey], act) |
|
} |
|
|
|
// CalculateTrust calculates an aggregate trust score for a public key. |
|
// |
|
// The score is computed as a weighted average where: |
|
// - high trust = 100 |
|
// - medium trust = 50 |
|
// - low trust = 25 |
|
// |
|
// Expired trust acts are excluded from the calculation. |
|
// Returns a score between 0 and 100. |
|
func (tc *TrustCalculator) CalculateTrust(pubkey string) float64 { |
|
tc.mu.RLock() |
|
defer tc.mu.RUnlock() |
|
|
|
acts := tc.acts[pubkey] |
|
if len(acts) == 0 { |
|
return 0 |
|
} |
|
|
|
now := time.Now() |
|
var total float64 |
|
var count int |
|
|
|
// Weight mapping |
|
weights := map[directory.TrustLevel]float64{ |
|
directory.TrustLevelHigh: 100, |
|
directory.TrustLevelMedium: 50, |
|
directory.TrustLevelLow: 25, |
|
} |
|
|
|
for _, act := range acts { |
|
// Skip expired acts |
|
if act.Expiry != nil && act.Expiry.Before(now) { |
|
continue |
|
} |
|
|
|
weight, ok := weights[act.TrustLevel] |
|
if !ok { |
|
continue |
|
} |
|
|
|
total += weight |
|
count++ |
|
} |
|
|
|
if count == 0 { |
|
return 0 |
|
} |
|
|
|
return total / float64(count) |
|
} |
|
|
|
// GetActs returns all trust acts for a specific public key. |
|
func (tc *TrustCalculator) GetActs(pubkey string) []*directory.TrustAct { |
|
tc.mu.RLock() |
|
defer tc.mu.RUnlock() |
|
|
|
acts := tc.acts[pubkey] |
|
result := make([]*directory.TrustAct, len(acts)) |
|
copy(result, acts) |
|
return result |
|
} |
|
|
|
// GetActiveTrustActs returns only non-expired trust acts for a public key. |
|
func (tc *TrustCalculator) GetActiveTrustActs(pubkey string) []*directory.TrustAct { |
|
tc.mu.RLock() |
|
defer tc.mu.RUnlock() |
|
|
|
acts := tc.acts[pubkey] |
|
now := time.Now() |
|
result := make([]*directory.TrustAct, 0) |
|
|
|
for _, act := range acts { |
|
if act.Expiry == nil || act.Expiry.After(now) { |
|
result = append(result, act) |
|
} |
|
} |
|
|
|
return result |
|
} |
|
|
|
// Clear removes all trust acts from the calculator. |
|
func (tc *TrustCalculator) Clear() { |
|
tc.mu.Lock() |
|
defer tc.mu.Unlock() |
|
|
|
tc.acts = make(map[string][]*directory.TrustAct) |
|
} |
|
|
|
// GetAllPubkeys returns all public keys that have trust acts. |
|
func (tc *TrustCalculator) GetAllPubkeys() []string { |
|
tc.mu.RLock() |
|
defer tc.mu.RUnlock() |
|
|
|
pubkeys := make([]string, 0, len(tc.acts)) |
|
for pubkey := range tc.acts { |
|
pubkeys = append(pubkeys, pubkey) |
|
} |
|
return pubkeys |
|
} |
|
|
|
// ReplicationFilter manages replication decisions based on trust scores. |
|
// |
|
// It uses a TrustCalculator to compute trust scores and determines which |
|
// relays are trusted enough for replication based on a minimum threshold. |
|
type ReplicationFilter struct { |
|
mu sync.RWMutex |
|
trustCalc *TrustCalculator |
|
minTrustScore float64 |
|
trustedRelays map[string]bool |
|
} |
|
|
|
// NewReplicationFilter creates a new replication filter with a minimum trust score threshold. |
|
func NewReplicationFilter(minTrustScore float64) *ReplicationFilter { |
|
return &ReplicationFilter{ |
|
trustCalc: NewTrustCalculator(), |
|
minTrustScore: minTrustScore, |
|
trustedRelays: make(map[string]bool), |
|
} |
|
} |
|
|
|
// AddTrustAct adds a trust act and updates the trusted relays set. |
|
func (rf *ReplicationFilter) AddTrustAct(act *directory.TrustAct) { |
|
if act == nil { |
|
return |
|
} |
|
|
|
rf.trustCalc.AddAct(act) |
|
|
|
// Update trusted relays based on new trust score |
|
score := rf.trustCalc.CalculateTrust(act.TargetPubkey) |
|
|
|
rf.mu.Lock() |
|
defer rf.mu.Unlock() |
|
|
|
if score >= rf.minTrustScore { |
|
rf.trustedRelays[act.TargetPubkey] = true |
|
} else { |
|
delete(rf.trustedRelays, act.TargetPubkey) |
|
} |
|
} |
|
|
|
// ShouldReplicate checks if a relay is trusted enough for replication. |
|
func (rf *ReplicationFilter) ShouldReplicate(pubkey string) bool { |
|
rf.mu.RLock() |
|
defer rf.mu.RUnlock() |
|
|
|
return rf.trustedRelays[pubkey] |
|
} |
|
|
|
// GetTrustedRelays returns all trusted relay public keys. |
|
func (rf *ReplicationFilter) GetTrustedRelays() []string { |
|
rf.mu.RLock() |
|
defer rf.mu.RUnlock() |
|
|
|
relays := make([]string, 0, len(rf.trustedRelays)) |
|
for pubkey := range rf.trustedRelays { |
|
relays = append(relays, pubkey) |
|
} |
|
return relays |
|
} |
|
|
|
// GetTrustScore returns the trust score for a relay. |
|
func (rf *ReplicationFilter) GetTrustScore(pubkey string) float64 { |
|
return rf.trustCalc.CalculateTrust(pubkey) |
|
} |
|
|
|
// SetMinTrustScore updates the minimum trust score threshold and recalculates trusted relays. |
|
func (rf *ReplicationFilter) SetMinTrustScore(minScore float64) { |
|
rf.mu.Lock() |
|
defer rf.mu.Unlock() |
|
|
|
rf.minTrustScore = minScore |
|
|
|
// Recalculate trusted relays with new threshold |
|
rf.trustedRelays = make(map[string]bool) |
|
for _, pubkey := range rf.trustCalc.GetAllPubkeys() { |
|
score := rf.trustCalc.CalculateTrust(pubkey) |
|
if score >= rf.minTrustScore { |
|
rf.trustedRelays[pubkey] = true |
|
} |
|
} |
|
} |
|
|
|
// GetMinTrustScore returns the current minimum trust score threshold. |
|
func (rf *ReplicationFilter) GetMinTrustScore() float64 { |
|
rf.mu.RLock() |
|
defer rf.mu.RUnlock() |
|
|
|
return rf.minTrustScore |
|
} |
|
|
|
// FilterEvents filters events to only those from trusted relays. |
|
func (rf *ReplicationFilter) FilterEvents(events []*event.E) []*event.E { |
|
rf.mu.RLock() |
|
defer rf.mu.RUnlock() |
|
|
|
filtered := make([]*event.E, 0) |
|
for _, ev := range events { |
|
if rf.trustedRelays[string(ev.Pubkey)] { |
|
filtered = append(filtered, ev) |
|
} |
|
} |
|
return filtered |
|
}
|
|
|