import type { PeerScoreParams } from './peer-score-params.js' import type { PeerStats } from './peer-stats.js' export function computeScore ( peer: string, pstats: PeerStats, params: PeerScoreParams, peerIPs: Map> ): number { let score = 0 // topic stores Object.entries(pstats.topics).forEach(([topic, tstats]) => { // the topic parameters const topicParams = params.topics[topic] if (topicParams === undefined) { // we are not scoring this topic return } let topicScore = 0 // P1: time in Mesh if (tstats.inMesh) { let p1 = tstats.meshTime / topicParams.timeInMeshQuantum if (p1 > topicParams.timeInMeshCap) { p1 = topicParams.timeInMeshCap } topicScore += p1 * topicParams.timeInMeshWeight } // P2: first message deliveries let p2 = tstats.firstMessageDeliveries if (p2 > topicParams.firstMessageDeliveriesCap) { p2 = topicParams.firstMessageDeliveriesCap } topicScore += p2 * topicParams.firstMessageDeliveriesWeight // P3: mesh message deliveries if ( tstats.meshMessageDeliveriesActive && tstats.meshMessageDeliveries < topicParams.meshMessageDeliveriesThreshold ) { const deficit = topicParams.meshMessageDeliveriesThreshold - tstats.meshMessageDeliveries const p3 = deficit * deficit topicScore += p3 * topicParams.meshMessageDeliveriesWeight } // P3b: // NOTE: the weight of P3b is negative (validated in validateTopicScoreParams) so this detracts const p3b = tstats.meshFailurePenalty topicScore += p3b * topicParams.meshFailurePenaltyWeight // P4: invalid messages // NOTE: the weight of P4 is negative (validated in validateTopicScoreParams) so this detracts const p4 = tstats.invalidMessageDeliveries * tstats.invalidMessageDeliveries topicScore += p4 * topicParams.invalidMessageDeliveriesWeight // update score, mixing with topic weight score += topicScore * topicParams.topicWeight }) // apply the topic score cap, if any if (params.topicScoreCap > 0 && score > params.topicScoreCap) { score = params.topicScoreCap } // P5: application-specific score const p5 = params.appSpecificScore(peer) score += p5 * params.appSpecificWeight // P6: IP colocation factor pstats.knownIPs.forEach((ip) => { if (params.IPColocationFactorWhitelist.has(ip)) { return } // P6 has a cliff (IPColocationFactorThreshold) // It's only applied if at least that many peers are connected to us from that source IP addr. // It is quadratic, and the weight is negative (validated in validatePeerScoreParams) const peersInIP = peerIPs.get(ip) const numPeersInIP = (peersInIP != null) ? peersInIP.size : 0 if (numPeersInIP > params.IPColocationFactorThreshold) { const surplus = numPeersInIP - params.IPColocationFactorThreshold const p6 = surplus * surplus score += p6 * params.IPColocationFactorWeight } }) // P7: behavioural pattern penalty if (pstats.behaviourPenalty > params.behaviourPenaltyThreshold) { const excess = pstats.behaviourPenalty - params.behaviourPenaltyThreshold const p7 = excess * excess score += p7 * params.behaviourPenaltyWeight } return score }