@claw-network/protocol 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/contracts/events.d.ts +238 -0
- package/dist/contracts/events.d.ts.map +1 -0
- package/dist/contracts/events.js +499 -0
- package/dist/contracts/events.js.map +1 -0
- package/dist/contracts/index.d.ts +5 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +5 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/state.d.ts +16 -0
- package/dist/contracts/state.d.ts.map +1 -0
- package/dist/contracts/state.js +399 -0
- package/dist/contracts/state.js.map +1 -0
- package/dist/contracts/store.d.ts +19 -0
- package/dist/contracts/store.d.ts.map +1 -0
- package/dist/contracts/store.js +22 -0
- package/dist/contracts/store.js.map +1 -0
- package/dist/contracts/types.d.ts +81 -0
- package/dist/contracts/types.d.ts.map +1 -0
- package/dist/contracts/types.js +2 -0
- package/dist/contracts/types.js.map +1 -0
- package/dist/dao/events.d.ts +191 -0
- package/dist/dao/events.d.ts.map +1 -0
- package/dist/dao/events.js +294 -0
- package/dist/dao/events.js.map +1 -0
- package/dist/dao/index.d.ts +6 -0
- package/dist/dao/index.d.ts.map +1 -0
- package/dist/dao/index.js +6 -0
- package/dist/dao/index.js.map +1 -0
- package/dist/dao/state.d.ts +44 -0
- package/dist/dao/state.d.ts.map +1 -0
- package/dist/dao/state.js +373 -0
- package/dist/dao/state.js.map +1 -0
- package/dist/dao/store.d.ts +47 -0
- package/dist/dao/store.d.ts.map +1 -0
- package/dist/dao/store.js +52 -0
- package/dist/dao/store.js.map +1 -0
- package/dist/dao/types.d.ts +149 -0
- package/dist/dao/types.d.ts.map +1 -0
- package/dist/dao/types.js +95 -0
- package/dist/dao/types.js.map +1 -0
- package/dist/dao/voting.d.ts +56 -0
- package/dist/dao/voting.d.ts.map +1 -0
- package/dist/dao/voting.js +129 -0
- package/dist/dao/voting.js.map +1 -0
- package/dist/identity/document.d.ts +35 -0
- package/dist/identity/document.d.ts.map +1 -0
- package/dist/identity/document.js +84 -0
- package/dist/identity/document.js.map +1 -0
- package/dist/identity/events.d.ts +96 -0
- package/dist/identity/events.d.ts.map +1 -0
- package/dist/identity/events.js +165 -0
- package/dist/identity/events.js.map +1 -0
- package/dist/identity/index.d.ts +4 -0
- package/dist/identity/index.d.ts.map +1 -0
- package/dist/identity/index.js +4 -0
- package/dist/identity/index.js.map +1 -0
- package/dist/identity/resolver.d.ts +10 -0
- package/dist/identity/resolver.d.ts.map +1 -0
- package/dist/identity/resolver.js +10 -0
- package/dist/identity/resolver.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/markets/broadcast.d.ts +5 -0
- package/dist/markets/broadcast.d.ts.map +1 -0
- package/dist/markets/broadcast.js +9 -0
- package/dist/markets/broadcast.js.map +1 -0
- package/dist/markets/capability.d.ts +129 -0
- package/dist/markets/capability.d.ts.map +1 -0
- package/dist/markets/capability.js +262 -0
- package/dist/markets/capability.js.map +1 -0
- package/dist/markets/events.d.ts +448 -0
- package/dist/markets/events.d.ts.map +1 -0
- package/dist/markets/events.js +1074 -0
- package/dist/markets/events.js.map +1 -0
- package/dist/markets/index.d.ts +12 -0
- package/dist/markets/index.d.ts.map +1 -0
- package/dist/markets/index.js +12 -0
- package/dist/markets/index.js.map +1 -0
- package/dist/markets/info-flow.d.ts +144 -0
- package/dist/markets/info-flow.d.ts.map +1 -0
- package/dist/markets/info-flow.js +257 -0
- package/dist/markets/info-flow.js.map +1 -0
- package/dist/markets/info-store.d.ts +80 -0
- package/dist/markets/info-store.d.ts.map +1 -0
- package/dist/markets/info-store.js +384 -0
- package/dist/markets/info-store.js.map +1 -0
- package/dist/markets/info.d.ts +118 -0
- package/dist/markets/info.d.ts.map +1 -0
- package/dist/markets/info.js +123 -0
- package/dist/markets/info.js.map +1 -0
- package/dist/markets/search.d.ts +70 -0
- package/dist/markets/search.d.ts.map +1 -0
- package/dist/markets/search.js +659 -0
- package/dist/markets/search.js.map +1 -0
- package/dist/markets/state.d.ts +30 -0
- package/dist/markets/state.d.ts.map +1 -0
- package/dist/markets/state.js +610 -0
- package/dist/markets/state.js.map +1 -0
- package/dist/markets/store.d.ts +43 -0
- package/dist/markets/store.d.ts.map +1 -0
- package/dist/markets/store.js +460 -0
- package/dist/markets/store.js.map +1 -0
- package/dist/markets/task.d.ts +96 -0
- package/dist/markets/task.d.ts.map +1 -0
- package/dist/markets/task.js +286 -0
- package/dist/markets/task.js.map +1 -0
- package/dist/markets/types.d.ts +340 -0
- package/dist/markets/types.d.ts.map +1 -0
- package/dist/markets/types.js +88 -0
- package/dist/markets/types.js.map +1 -0
- package/dist/p2p/codec.d.ts +42 -0
- package/dist/p2p/codec.d.ts.map +1 -0
- package/dist/p2p/codec.js +187 -0
- package/dist/p2p/codec.js.map +1 -0
- package/dist/p2p/flatbuffers.d.ts +57 -0
- package/dist/p2p/flatbuffers.d.ts.map +1 -0
- package/dist/p2p/flatbuffers.js +418 -0
- package/dist/p2p/flatbuffers.js.map +1 -0
- package/dist/p2p/index.d.ts +3 -0
- package/dist/p2p/index.d.ts.map +1 -0
- package/dist/p2p/index.js +3 -0
- package/dist/p2p/index.js.map +1 -0
- package/dist/p2p/types.d.ts +79 -0
- package/dist/p2p/types.d.ts.map +1 -0
- package/dist/p2p/types.js +15 -0
- package/dist/p2p/types.js.map +1 -0
- package/dist/reputation/anti-cheat.d.ts +24 -0
- package/dist/reputation/anti-cheat.d.ts.map +1 -0
- package/dist/reputation/anti-cheat.js +109 -0
- package/dist/reputation/anti-cheat.js.map +1 -0
- package/dist/reputation/events.d.ts +29 -0
- package/dist/reputation/events.d.ts.map +1 -0
- package/dist/reputation/events.js +122 -0
- package/dist/reputation/events.js.map +1 -0
- package/dist/reputation/index.d.ts +6 -0
- package/dist/reputation/index.d.ts.map +1 -0
- package/dist/reputation/index.js +6 -0
- package/dist/reputation/index.js.map +1 -0
- package/dist/reputation/scoring.d.ts +40 -0
- package/dist/reputation/scoring.d.ts.map +1 -0
- package/dist/reputation/scoring.js +114 -0
- package/dist/reputation/scoring.js.map +1 -0
- package/dist/reputation/state.d.ts +48 -0
- package/dist/reputation/state.d.ts.map +1 -0
- package/dist/reputation/state.js +135 -0
- package/dist/reputation/state.js.map +1 -0
- package/dist/reputation/store.d.ts +23 -0
- package/dist/reputation/store.d.ts.map +1 -0
- package/dist/reputation/store.js +33 -0
- package/dist/reputation/store.js.map +1 -0
- package/dist/wallet/events.d.ts +121 -0
- package/dist/wallet/events.d.ts.map +1 -0
- package/dist/wallet/events.js +175 -0
- package/dist/wallet/events.js.map +1 -0
- package/dist/wallet/finality.d.ts +4 -0
- package/dist/wallet/finality.d.ts.map +1 -0
- package/dist/wallet/finality.js +32 -0
- package/dist/wallet/finality.js.map +1 -0
- package/dist/wallet/index.d.ts +4 -0
- package/dist/wallet/index.d.ts.map +1 -0
- package/dist/wallet/index.js +4 -0
- package/dist/wallet/index.js.map +1 -0
- package/dist/wallet/state.d.ts +32 -0
- package/dist/wallet/state.d.ts.map +1 -0
- package/dist/wallet/state.js +170 -0
- package/dist/wallet/state.js.map +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export const DEFAULT_FRAUD_OPTIONS = {
|
|
2
|
+
windowMs: 60 * 60 * 1000,
|
|
3
|
+
maxRecordsPerWindow: 10,
|
|
4
|
+
maxSameIssuerRatio: 0.6,
|
|
5
|
+
outlierThreshold: 400,
|
|
6
|
+
minRecordsForDominance: 5,
|
|
7
|
+
};
|
|
8
|
+
function median(values) {
|
|
9
|
+
if (values.length === 0) {
|
|
10
|
+
return 0;
|
|
11
|
+
}
|
|
12
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
13
|
+
const mid = Math.floor(sorted.length / 2);
|
|
14
|
+
if (sorted.length % 2 === 0) {
|
|
15
|
+
return (sorted[mid - 1] + sorted[mid]) / 2;
|
|
16
|
+
}
|
|
17
|
+
return sorted[mid];
|
|
18
|
+
}
|
|
19
|
+
export function detectReputationFraud(records, options = {}, now = Date.now()) {
|
|
20
|
+
const config = { ...DEFAULT_FRAUD_OPTIONS, ...options };
|
|
21
|
+
const target = records[0]?.target ?? '';
|
|
22
|
+
const signals = [];
|
|
23
|
+
const hasSelfReview = records.some((record) => record.issuer === record.target);
|
|
24
|
+
if (hasSelfReview) {
|
|
25
|
+
signals.push({
|
|
26
|
+
type: 'self_review',
|
|
27
|
+
severity: 'high',
|
|
28
|
+
details: 'issuer matches target',
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const recentCount = records.filter((record) => now - record.ts <= config.windowMs).length;
|
|
32
|
+
if (recentCount > config.maxRecordsPerWindow) {
|
|
33
|
+
signals.push({
|
|
34
|
+
type: 'burst',
|
|
35
|
+
severity: 'medium',
|
|
36
|
+
details: `records in window: ${recentCount}`,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if (records.length >= config.minRecordsForDominance) {
|
|
40
|
+
const counts = new Map();
|
|
41
|
+
for (const record of records) {
|
|
42
|
+
counts.set(record.issuer, (counts.get(record.issuer) ?? 0) + 1);
|
|
43
|
+
}
|
|
44
|
+
let max = 0;
|
|
45
|
+
for (const count of counts.values()) {
|
|
46
|
+
if (count > max) {
|
|
47
|
+
max = count;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const ratio = records.length ? max / records.length : 0;
|
|
51
|
+
if (ratio >= config.maxSameIssuerRatio) {
|
|
52
|
+
signals.push({
|
|
53
|
+
type: 'issuer_dominance',
|
|
54
|
+
severity: 'medium',
|
|
55
|
+
details: `top issuer ratio: ${ratio.toFixed(2)}`,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (records.length > 0) {
|
|
60
|
+
const scores = records.map((record) => record.score);
|
|
61
|
+
const mid = median(scores);
|
|
62
|
+
const outlierCount = scores.filter((score) => Math.abs(score - mid) >= config.outlierThreshold)
|
|
63
|
+
.length;
|
|
64
|
+
if (outlierCount > 0) {
|
|
65
|
+
signals.push({
|
|
66
|
+
type: 'outlier_score',
|
|
67
|
+
severity: outlierCount > 1 ? 'medium' : 'low',
|
|
68
|
+
details: `outliers: ${outlierCount}`,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
let riskScore = 0;
|
|
73
|
+
for (const signal of signals) {
|
|
74
|
+
switch (signal.type) {
|
|
75
|
+
case 'self_review':
|
|
76
|
+
riskScore += 0.4;
|
|
77
|
+
break;
|
|
78
|
+
case 'burst':
|
|
79
|
+
riskScore += 0.25;
|
|
80
|
+
break;
|
|
81
|
+
case 'issuer_dominance':
|
|
82
|
+
riskScore += 0.2;
|
|
83
|
+
break;
|
|
84
|
+
case 'outlier_score':
|
|
85
|
+
riskScore += 0.15;
|
|
86
|
+
break;
|
|
87
|
+
default:
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
riskScore = Math.min(1, riskScore);
|
|
92
|
+
let riskLevel = 'low';
|
|
93
|
+
if (riskScore >= 0.9) {
|
|
94
|
+
riskLevel = 'critical';
|
|
95
|
+
}
|
|
96
|
+
else if (riskScore >= 0.7) {
|
|
97
|
+
riskLevel = 'high';
|
|
98
|
+
}
|
|
99
|
+
else if (riskScore >= 0.4) {
|
|
100
|
+
riskLevel = 'medium';
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
target,
|
|
104
|
+
riskScore,
|
|
105
|
+
riskLevel,
|
|
106
|
+
signals,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=anti-cheat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anti-cheat.js","sourceRoot":"","sources":["../../src/reputation/anti-cheat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { EventEnvelope } from '@claw-network/core/protocol';
|
|
2
|
+
import { ReputationDimension } from './scoring.js';
|
|
3
|
+
export declare const REPUTATION_ASPECT_KEYS: readonly ["communication", "quality", "timeliness", "professionalism"];
|
|
4
|
+
export type ReputationAspectKey = (typeof REPUTATION_ASPECT_KEYS)[number];
|
|
5
|
+
export declare function isReputationAspectKey(value: string): value is ReputationAspectKey;
|
|
6
|
+
export interface ReputationRecordPayload extends Record<string, unknown> {
|
|
7
|
+
target: string;
|
|
8
|
+
dimension: ReputationDimension;
|
|
9
|
+
score: number;
|
|
10
|
+
ref: string;
|
|
11
|
+
comment?: string;
|
|
12
|
+
aspects?: Record<ReputationAspectKey, number>;
|
|
13
|
+
}
|
|
14
|
+
export interface ReputationRecordEventParams {
|
|
15
|
+
issuer: string;
|
|
16
|
+
privateKey: Uint8Array;
|
|
17
|
+
target: string;
|
|
18
|
+
dimension: ReputationDimension;
|
|
19
|
+
score: number;
|
|
20
|
+
ref: string;
|
|
21
|
+
comment?: string;
|
|
22
|
+
aspects?: Record<ReputationAspectKey, number>;
|
|
23
|
+
ts: number;
|
|
24
|
+
nonce: number;
|
|
25
|
+
prev?: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function parseReputationRecordPayload(payload: Record<string, unknown>): ReputationRecordPayload;
|
|
28
|
+
export declare function createReputationRecordEnvelope(params: ReputationRecordEventParams): Promise<EventEnvelope>;
|
|
29
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/reputation/events.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAA2B,MAAM,6BAA6B,CAAC;AACrF,OAAO,EAAyC,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAE1F,eAAO,MAAM,sBAAsB,wEAKzB,CAAC;AAEX,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,mBAAmB,CAEjF;AAED,MAAM,WAAW,uBAAwB,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,mBAAmB,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,2BAA2B;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,mBAAmB,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AA2BD,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,uBAAuB,CA6DzB;AAED,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,aAAa,CAAC,CA4BxB"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { multibaseEncode } from '@claw-network/core/encoding';
|
|
2
|
+
import { publicKeyFromDid } from '@claw-network/core/identity';
|
|
3
|
+
import { eventHashHex, signEvent } from '@claw-network/core/protocol';
|
|
4
|
+
import { isReputationDimension, normalizeScore } from './scoring.js';
|
|
5
|
+
export const REPUTATION_ASPECT_KEYS = [
|
|
6
|
+
'communication',
|
|
7
|
+
'quality',
|
|
8
|
+
'timeliness',
|
|
9
|
+
'professionalism',
|
|
10
|
+
];
|
|
11
|
+
export function isReputationAspectKey(value) {
|
|
12
|
+
return REPUTATION_ASPECT_KEYS.includes(value);
|
|
13
|
+
}
|
|
14
|
+
function requireNonEmpty(value, field) {
|
|
15
|
+
if (!value || value.trim().length === 0) {
|
|
16
|
+
throw new Error(`${field} is required`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function assertValidDid(value, field) {
|
|
20
|
+
requireNonEmpty(value, field);
|
|
21
|
+
try {
|
|
22
|
+
publicKeyFromDid(value);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw new Error(`${field} must be a valid did:claw identifier`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function parseScore(value) {
|
|
29
|
+
if (typeof value === 'number') {
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
if (typeof value === 'string' && value.trim().length > 0) {
|
|
33
|
+
return Number(value);
|
|
34
|
+
}
|
|
35
|
+
return Number.NaN;
|
|
36
|
+
}
|
|
37
|
+
export function parseReputationRecordPayload(payload) {
|
|
38
|
+
const target = String(payload.target ?? '');
|
|
39
|
+
const dimensionValue = String(payload.dimension ?? '');
|
|
40
|
+
const ref = String(payload.ref ?? '');
|
|
41
|
+
const rawScore = parseScore(payload.score);
|
|
42
|
+
const rawComment = payload.comment;
|
|
43
|
+
const rawAspects = payload.aspects;
|
|
44
|
+
assertValidDid(target, 'target');
|
|
45
|
+
if (!isReputationDimension(dimensionValue)) {
|
|
46
|
+
throw new Error('dimension must be a supported reputation dimension');
|
|
47
|
+
}
|
|
48
|
+
if (!Number.isFinite(rawScore)) {
|
|
49
|
+
throw new Error('score must be a number');
|
|
50
|
+
}
|
|
51
|
+
const score = normalizeScore(rawScore, 'score');
|
|
52
|
+
requireNonEmpty(ref, 'ref');
|
|
53
|
+
let comment;
|
|
54
|
+
if (rawComment !== undefined && rawComment !== null) {
|
|
55
|
+
if (typeof rawComment !== 'string') {
|
|
56
|
+
throw new Error('comment must be a string');
|
|
57
|
+
}
|
|
58
|
+
const trimmed = rawComment.trim();
|
|
59
|
+
if (trimmed.length) {
|
|
60
|
+
comment = trimmed;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
let aspects;
|
|
64
|
+
if (rawAspects !== undefined && rawAspects !== null) {
|
|
65
|
+
if (typeof rawAspects !== 'object' || Array.isArray(rawAspects)) {
|
|
66
|
+
throw new Error('aspects must be an object');
|
|
67
|
+
}
|
|
68
|
+
const entries = Object.entries(rawAspects);
|
|
69
|
+
if (entries.length) {
|
|
70
|
+
aspects = {};
|
|
71
|
+
for (const [key, value] of entries) {
|
|
72
|
+
if (!isReputationAspectKey(key)) {
|
|
73
|
+
throw new Error(`aspect ${key} is not supported`);
|
|
74
|
+
}
|
|
75
|
+
const numeric = typeof value === 'number' ? value : Number(value);
|
|
76
|
+
if (!Number.isFinite(numeric) || !Number.isInteger(numeric)) {
|
|
77
|
+
throw new Error(`aspect ${key} must be an integer`);
|
|
78
|
+
}
|
|
79
|
+
if (numeric < 1 || numeric > 5) {
|
|
80
|
+
throw new Error(`aspect ${key} must be between 1 and 5`);
|
|
81
|
+
}
|
|
82
|
+
aspects[key] = numeric;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
target,
|
|
88
|
+
dimension: dimensionValue,
|
|
89
|
+
score,
|
|
90
|
+
ref,
|
|
91
|
+
comment,
|
|
92
|
+
aspects,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export async function createReputationRecordEnvelope(params) {
|
|
96
|
+
assertValidDid(params.issuer, 'issuer');
|
|
97
|
+
const publicKey = publicKeyFromDid(params.issuer);
|
|
98
|
+
const payload = parseReputationRecordPayload({
|
|
99
|
+
target: params.target,
|
|
100
|
+
dimension: params.dimension,
|
|
101
|
+
score: params.score,
|
|
102
|
+
ref: params.ref,
|
|
103
|
+
comment: params.comment,
|
|
104
|
+
aspects: params.aspects,
|
|
105
|
+
});
|
|
106
|
+
const baseEnvelope = {
|
|
107
|
+
v: 1,
|
|
108
|
+
type: 'reputation.record',
|
|
109
|
+
issuer: params.issuer,
|
|
110
|
+
ts: params.ts,
|
|
111
|
+
nonce: params.nonce,
|
|
112
|
+
payload,
|
|
113
|
+
prev: params.prev,
|
|
114
|
+
sig: '',
|
|
115
|
+
pub: multibaseEncode(publicKey),
|
|
116
|
+
hash: '',
|
|
117
|
+
};
|
|
118
|
+
const hash = eventHashHex(baseEnvelope);
|
|
119
|
+
const sig = await signEvent(baseEnvelope, params.privateKey);
|
|
120
|
+
return { ...baseEnvelope, hash, sig };
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/reputation/events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reputation/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/reputation/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export declare const REPUTATION_DIMENSIONS: readonly ["transaction", "fulfillment", "quality", "social", "behavior"];
|
|
2
|
+
export type ReputationDimension = (typeof REPUTATION_DIMENSIONS)[number];
|
|
3
|
+
export declare function isReputationDimension(value: string): value is ReputationDimension;
|
|
4
|
+
export type ReputationLevel = 'legend' | 'elite' | 'expert' | 'trusted' | 'newcomer' | 'observed' | 'risky';
|
|
5
|
+
export interface ReputationDimensionWeights {
|
|
6
|
+
transaction: number;
|
|
7
|
+
fulfillment: number;
|
|
8
|
+
quality: number;
|
|
9
|
+
social: number;
|
|
10
|
+
behavior: number;
|
|
11
|
+
}
|
|
12
|
+
export declare const DEFAULT_DIMENSION_WEIGHTS: ReputationDimensionWeights;
|
|
13
|
+
export interface DecayConfig {
|
|
14
|
+
halfLifeMs: number;
|
|
15
|
+
minWeight: number;
|
|
16
|
+
maxAgeMs: number;
|
|
17
|
+
}
|
|
18
|
+
export declare const DEFAULT_DECAY_CONFIG: DecayConfig;
|
|
19
|
+
export declare const DEFAULT_BASELINE_SCORE = 500;
|
|
20
|
+
export declare function normalizeScore(value: number, field?: string): number;
|
|
21
|
+
export declare function clampScore(value: number, fallback?: number): number;
|
|
22
|
+
export declare function decayWeight(timestamp: number, now?: number, config?: DecayConfig): number;
|
|
23
|
+
export interface ScoredRecord {
|
|
24
|
+
score: number;
|
|
25
|
+
ts: number;
|
|
26
|
+
}
|
|
27
|
+
export declare function calculateDecayedAverage(records: ScoredRecord[], options?: {
|
|
28
|
+
now?: number;
|
|
29
|
+
decay?: DecayConfig;
|
|
30
|
+
baseline?: number;
|
|
31
|
+
}): number;
|
|
32
|
+
export type ReputationDimensionScores = Record<ReputationDimension, number>;
|
|
33
|
+
export declare function calculateDimensionScore(records: ScoredRecord[], options?: {
|
|
34
|
+
now?: number;
|
|
35
|
+
decay?: DecayConfig;
|
|
36
|
+
baseline?: number;
|
|
37
|
+
}): number;
|
|
38
|
+
export declare function calculateOverallScore(scores: Partial<ReputationDimensionScores>, weights?: ReputationDimensionWeights, baseline?: number): number;
|
|
39
|
+
export declare function reputationLevelForScore(score: number): ReputationLevel;
|
|
40
|
+
//# sourceMappingURL=scoring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../../src/reputation/scoring.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,0EAMxB,CAAC;AAEX,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,mBAAmB,CAEjF;AAED,MAAM,MAAM,eAAe,GACvB,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,SAAS,GACT,UAAU,GACV,UAAU,GACV,OAAO,CAAC;AAEZ,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,yBAAyB,EAAE,0BAMvC,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,oBAAoB,EAAE,WAIlC,CAAC;AAEF,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,SAAU,GAAG,MAAM,CAQrE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAyB,GAAG,MAAM,CAKnF;AAED,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,GAAG,GAAE,MAAmB,EACxB,MAAM,GAAE,WAAkC,GACzC,MAAM,CAaR;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GACrE,MAAM,CAqBR;AAED,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;AAE5E,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GACrE,MAAM,CAER;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,OAAO,CAAC,yBAAyB,CAAC,EAC1C,OAAO,GAAE,0BAAsD,EAC/D,QAAQ,GAAE,MAA+B,GACxC,MAAM,CAaR;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAqBtE"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export const REPUTATION_DIMENSIONS = [
|
|
2
|
+
'transaction',
|
|
3
|
+
'fulfillment',
|
|
4
|
+
'quality',
|
|
5
|
+
'social',
|
|
6
|
+
'behavior',
|
|
7
|
+
];
|
|
8
|
+
export function isReputationDimension(value) {
|
|
9
|
+
return REPUTATION_DIMENSIONS.includes(value);
|
|
10
|
+
}
|
|
11
|
+
export const DEFAULT_DIMENSION_WEIGHTS = {
|
|
12
|
+
transaction: 0.25,
|
|
13
|
+
fulfillment: 0.3,
|
|
14
|
+
quality: 0.2,
|
|
15
|
+
social: 0.15,
|
|
16
|
+
behavior: 0.1,
|
|
17
|
+
};
|
|
18
|
+
export const DEFAULT_DECAY_CONFIG = {
|
|
19
|
+
halfLifeMs: 180 * 24 * 60 * 60 * 1000,
|
|
20
|
+
minWeight: 0.1,
|
|
21
|
+
maxAgeMs: 2 * 365 * 24 * 60 * 60 * 1000,
|
|
22
|
+
};
|
|
23
|
+
export const DEFAULT_BASELINE_SCORE = 500;
|
|
24
|
+
export function normalizeScore(value, field = 'score') {
|
|
25
|
+
if (!Number.isFinite(value) || !Number.isInteger(value)) {
|
|
26
|
+
throw new Error(`${field} must be an integer`);
|
|
27
|
+
}
|
|
28
|
+
if (value < 0 || value > 1000) {
|
|
29
|
+
throw new Error(`${field} must be between 0 and 1000`);
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
export function clampScore(value, fallback = DEFAULT_BASELINE_SCORE) {
|
|
34
|
+
if (!Number.isFinite(value)) {
|
|
35
|
+
return fallback;
|
|
36
|
+
}
|
|
37
|
+
return Math.max(0, Math.min(1000, Math.round(value)));
|
|
38
|
+
}
|
|
39
|
+
export function decayWeight(timestamp, now = Date.now(), config = DEFAULT_DECAY_CONFIG) {
|
|
40
|
+
const age = Math.max(0, now - timestamp);
|
|
41
|
+
if (!Number.isFinite(age)) {
|
|
42
|
+
return config.minWeight;
|
|
43
|
+
}
|
|
44
|
+
if (age > config.maxAgeMs) {
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
if (config.halfLifeMs <= 0) {
|
|
48
|
+
return 1;
|
|
49
|
+
}
|
|
50
|
+
const raw = Math.pow(0.5, age / config.halfLifeMs);
|
|
51
|
+
return Math.max(config.minWeight, raw);
|
|
52
|
+
}
|
|
53
|
+
export function calculateDecayedAverage(records, options = {}) {
|
|
54
|
+
const baseline = options.baseline ?? DEFAULT_BASELINE_SCORE;
|
|
55
|
+
if (!records.length) {
|
|
56
|
+
return clampScore(baseline, baseline);
|
|
57
|
+
}
|
|
58
|
+
const now = options.now ?? Date.now();
|
|
59
|
+
const decay = options.decay ?? DEFAULT_DECAY_CONFIG;
|
|
60
|
+
let weightedSum = 0;
|
|
61
|
+
let totalWeight = 0;
|
|
62
|
+
for (const record of records) {
|
|
63
|
+
const weight = decayWeight(record.ts, now, decay);
|
|
64
|
+
if (weight <= 0) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
weightedSum += record.score * weight;
|
|
68
|
+
totalWeight += weight;
|
|
69
|
+
}
|
|
70
|
+
if (totalWeight === 0) {
|
|
71
|
+
return clampScore(baseline, baseline);
|
|
72
|
+
}
|
|
73
|
+
return clampScore(weightedSum / totalWeight, baseline);
|
|
74
|
+
}
|
|
75
|
+
export function calculateDimensionScore(records, options = {}) {
|
|
76
|
+
return calculateDecayedAverage(records, options);
|
|
77
|
+
}
|
|
78
|
+
export function calculateOverallScore(scores, weights = DEFAULT_DIMENSION_WEIGHTS, baseline = DEFAULT_BASELINE_SCORE) {
|
|
79
|
+
let totalWeight = 0;
|
|
80
|
+
let weightedSum = 0;
|
|
81
|
+
for (const dimension of REPUTATION_DIMENSIONS) {
|
|
82
|
+
const weight = weights[dimension] ?? 0;
|
|
83
|
+
const score = scores[dimension] ?? baseline;
|
|
84
|
+
weightedSum += score * weight;
|
|
85
|
+
totalWeight += weight;
|
|
86
|
+
}
|
|
87
|
+
if (totalWeight <= 0) {
|
|
88
|
+
return clampScore(baseline, baseline);
|
|
89
|
+
}
|
|
90
|
+
return clampScore(weightedSum / totalWeight, baseline);
|
|
91
|
+
}
|
|
92
|
+
export function reputationLevelForScore(score) {
|
|
93
|
+
const normalized = clampScore(score, DEFAULT_BASELINE_SCORE);
|
|
94
|
+
if (normalized >= 900) {
|
|
95
|
+
return 'legend';
|
|
96
|
+
}
|
|
97
|
+
if (normalized >= 800) {
|
|
98
|
+
return 'elite';
|
|
99
|
+
}
|
|
100
|
+
if (normalized >= 700) {
|
|
101
|
+
return 'expert';
|
|
102
|
+
}
|
|
103
|
+
if (normalized >= 500) {
|
|
104
|
+
return 'trusted';
|
|
105
|
+
}
|
|
106
|
+
if (normalized >= 300) {
|
|
107
|
+
return 'newcomer';
|
|
108
|
+
}
|
|
109
|
+
if (normalized >= 100) {
|
|
110
|
+
return 'observed';
|
|
111
|
+
}
|
|
112
|
+
return 'risky';
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=scoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoring.js","sourceRoot":"","sources":["../../src/reputation/scoring.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { EventEnvelope } from '@claw-network/core/protocol';
|
|
2
|
+
import { ReputationAspectKey, ReputationRecordPayload } from './events.js';
|
|
3
|
+
import { DecayConfig, ReputationDimension, ReputationDimensionWeights, ReputationLevel } from './scoring.js';
|
|
4
|
+
export interface ReputationRecord {
|
|
5
|
+
hash: string;
|
|
6
|
+
issuer: string;
|
|
7
|
+
target: string;
|
|
8
|
+
dimension: ReputationDimension;
|
|
9
|
+
score: number;
|
|
10
|
+
ref: string;
|
|
11
|
+
comment?: string;
|
|
12
|
+
aspects?: Record<ReputationAspectKey, number>;
|
|
13
|
+
ts: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ReputationState {
|
|
16
|
+
records: ReputationRecord[];
|
|
17
|
+
recordsByTarget: Record<string, ReputationRecord[]>;
|
|
18
|
+
recordIndex: Set<string>;
|
|
19
|
+
}
|
|
20
|
+
export interface ReputationDimensionSummary {
|
|
21
|
+
score: number;
|
|
22
|
+
recordCount: number;
|
|
23
|
+
lastUpdatedAt: number | null;
|
|
24
|
+
}
|
|
25
|
+
export interface ReputationProfile {
|
|
26
|
+
did: string;
|
|
27
|
+
dimensions: Record<ReputationDimension, ReputationDimensionSummary>;
|
|
28
|
+
overallScore: number;
|
|
29
|
+
level: ReputationLevel;
|
|
30
|
+
updatedAt: number | null;
|
|
31
|
+
}
|
|
32
|
+
export interface ReputationProfileOptions {
|
|
33
|
+
now?: number;
|
|
34
|
+
weights?: ReputationDimensionWeights;
|
|
35
|
+
decay?: DecayConfig;
|
|
36
|
+
baseline?: number;
|
|
37
|
+
}
|
|
38
|
+
export declare function createReputationState(): ReputationState;
|
|
39
|
+
export declare function addReputationRecord(state: ReputationState, record: ReputationRecord): ReputationState;
|
|
40
|
+
export declare function mergeReputationRecords(state: ReputationState, records: ReputationRecord[]): {
|
|
41
|
+
state: ReputationState;
|
|
42
|
+
added: number;
|
|
43
|
+
};
|
|
44
|
+
export declare function applyReputationEvent(state: ReputationState, envelope: EventEnvelope): ReputationState;
|
|
45
|
+
export declare function getReputationRecords(state: ReputationState, target: string, dimension?: ReputationDimension): ReputationRecord[];
|
|
46
|
+
export declare function buildReputationProfile(state: ReputationState, target: string, options?: ReputationProfileOptions): ReputationProfile;
|
|
47
|
+
export declare function recordFromPayload(payload: ReputationRecordPayload, issuer: string, ts: number, hash: string): ReputationRecord;
|
|
48
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/reputation/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAgB,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAEL,mBAAmB,EACnB,uBAAuB,EACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EAML,WAAW,EACX,mBAAmB,EAEnB,0BAA0B,EAC1B,eAAe,EAGhB,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,mBAAmB,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;IAC9C,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;IACpD,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC1B;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC,mBAAmB,EAAE,0BAA0B,CAAC,CAAC;IACpE,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,eAAe,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,wBAAwB;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,0BAA0B,CAAC;IACrC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,qBAAqB,IAAI,eAAe,CAMvD;AAUD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,eAAe,EACtB,MAAM,EAAE,gBAAgB,GACvB,eAAe,CAUjB;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,gBAAgB,EAAE,GAC1B;IAAE,KAAK,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAW3C;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,eAAe,EACtB,QAAQ,EAAE,aAAa,GACtB,eAAe,CA4BjB;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,eAAe,EACtB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,mBAAmB,GAC9B,gBAAgB,EAAE,CAMpB;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,eAAe,EACtB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,wBAA6B,GACrC,iBAAiB,CAoDnB;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,uBAAuB,EAChC,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,GACX,gBAAgB,CAYlB"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { eventHashHex } from '@claw-network/core/protocol';
|
|
2
|
+
import { parseReputationRecordPayload, } from './events.js';
|
|
3
|
+
import { calculateDimensionScore, calculateOverallScore, DEFAULT_BASELINE_SCORE, DEFAULT_DECAY_CONFIG, DEFAULT_DIMENSION_WEIGHTS, reputationLevelForScore, REPUTATION_DIMENSIONS, } from './scoring.js';
|
|
4
|
+
export function createReputationState() {
|
|
5
|
+
return {
|
|
6
|
+
records: [],
|
|
7
|
+
recordsByTarget: {},
|
|
8
|
+
recordIndex: new Set(),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function cloneState(state) {
|
|
12
|
+
return {
|
|
13
|
+
records: [...state.records],
|
|
14
|
+
recordsByTarget: { ...state.recordsByTarget },
|
|
15
|
+
recordIndex: new Set(state.recordIndex),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function addReputationRecord(state, record) {
|
|
19
|
+
if (state.recordIndex.has(record.hash)) {
|
|
20
|
+
return state;
|
|
21
|
+
}
|
|
22
|
+
const next = cloneState(state);
|
|
23
|
+
next.recordIndex.add(record.hash);
|
|
24
|
+
next.records.push(record);
|
|
25
|
+
const current = state.recordsByTarget[record.target] ?? [];
|
|
26
|
+
next.recordsByTarget[record.target] = [...current, record];
|
|
27
|
+
return next;
|
|
28
|
+
}
|
|
29
|
+
export function mergeReputationRecords(state, records) {
|
|
30
|
+
let next = state;
|
|
31
|
+
let added = 0;
|
|
32
|
+
for (const record of records) {
|
|
33
|
+
const updated = addReputationRecord(next, record);
|
|
34
|
+
if (updated !== next) {
|
|
35
|
+
next = updated;
|
|
36
|
+
added += 1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { state: next, added };
|
|
40
|
+
}
|
|
41
|
+
export function applyReputationEvent(state, envelope) {
|
|
42
|
+
if (String(envelope.type ?? '') !== 'reputation.record') {
|
|
43
|
+
return state;
|
|
44
|
+
}
|
|
45
|
+
const issuer = typeof envelope.issuer === 'string' ? envelope.issuer : '';
|
|
46
|
+
if (!issuer) {
|
|
47
|
+
throw new Error('reputation record missing issuer');
|
|
48
|
+
}
|
|
49
|
+
const payload = parseReputationRecordPayload((envelope.payload ?? {}));
|
|
50
|
+
const ts = typeof envelope.ts === 'number' ? envelope.ts : Date.now();
|
|
51
|
+
const hash = typeof envelope.hash === 'string' && envelope.hash.length
|
|
52
|
+
? envelope.hash
|
|
53
|
+
: eventHashHex(envelope);
|
|
54
|
+
const record = {
|
|
55
|
+
hash,
|
|
56
|
+
issuer,
|
|
57
|
+
target: payload.target,
|
|
58
|
+
dimension: payload.dimension,
|
|
59
|
+
score: payload.score,
|
|
60
|
+
ref: payload.ref,
|
|
61
|
+
comment: payload.comment,
|
|
62
|
+
aspects: payload.aspects,
|
|
63
|
+
ts,
|
|
64
|
+
};
|
|
65
|
+
return addReputationRecord(state, record);
|
|
66
|
+
}
|
|
67
|
+
export function getReputationRecords(state, target, dimension) {
|
|
68
|
+
const list = state.recordsByTarget[target] ?? [];
|
|
69
|
+
if (!dimension) {
|
|
70
|
+
return [...list];
|
|
71
|
+
}
|
|
72
|
+
return list.filter((record) => record.dimension === dimension);
|
|
73
|
+
}
|
|
74
|
+
export function buildReputationProfile(state, target, options = {}) {
|
|
75
|
+
const records = getReputationRecords(state, target);
|
|
76
|
+
const now = options.now ?? Date.now();
|
|
77
|
+
const decay = options.decay ?? DEFAULT_DECAY_CONFIG;
|
|
78
|
+
const baseline = options.baseline ?? DEFAULT_BASELINE_SCORE;
|
|
79
|
+
const weights = options.weights ?? DEFAULT_DIMENSION_WEIGHTS;
|
|
80
|
+
const byDimension = new Map();
|
|
81
|
+
for (const dimension of REPUTATION_DIMENSIONS) {
|
|
82
|
+
byDimension.set(dimension, []);
|
|
83
|
+
}
|
|
84
|
+
for (const record of records) {
|
|
85
|
+
const list = byDimension.get(record.dimension);
|
|
86
|
+
if (list) {
|
|
87
|
+
list.push(record);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const dimensions = {};
|
|
91
|
+
let updatedAt = null;
|
|
92
|
+
const scoreMap = {};
|
|
93
|
+
for (const dimension of REPUTATION_DIMENSIONS) {
|
|
94
|
+
const list = byDimension.get(dimension) ?? [];
|
|
95
|
+
const score = calculateDimensionScore(list, { now, decay, baseline });
|
|
96
|
+
let lastUpdatedAt = null;
|
|
97
|
+
for (const record of list) {
|
|
98
|
+
if (lastUpdatedAt === null || record.ts > lastUpdatedAt) {
|
|
99
|
+
lastUpdatedAt = record.ts;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (lastUpdatedAt !== null && (updatedAt === null || lastUpdatedAt > updatedAt)) {
|
|
103
|
+
updatedAt = lastUpdatedAt;
|
|
104
|
+
}
|
|
105
|
+
dimensions[dimension] = {
|
|
106
|
+
score,
|
|
107
|
+
recordCount: list.length,
|
|
108
|
+
lastUpdatedAt,
|
|
109
|
+
};
|
|
110
|
+
scoreMap[dimension] = score;
|
|
111
|
+
}
|
|
112
|
+
const overallScore = calculateOverallScore(scoreMap, weights, baseline);
|
|
113
|
+
const level = reputationLevelForScore(overallScore);
|
|
114
|
+
return {
|
|
115
|
+
did: target,
|
|
116
|
+
dimensions,
|
|
117
|
+
overallScore,
|
|
118
|
+
level,
|
|
119
|
+
updatedAt,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export function recordFromPayload(payload, issuer, ts, hash) {
|
|
123
|
+
return {
|
|
124
|
+
hash,
|
|
125
|
+
issuer,
|
|
126
|
+
target: payload.target,
|
|
127
|
+
dimension: payload.dimension,
|
|
128
|
+
score: payload.score,
|
|
129
|
+
ref: payload.ref,
|
|
130
|
+
comment: payload.comment,
|
|
131
|
+
aspects: payload.aspects,
|
|
132
|
+
ts,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=state.js.map
|