@f2a/network 0.1.2

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.
Files changed (234) hide show
  1. package/.github/workflows/ci.yml +113 -0
  2. package/.github/workflows/publish.yml +60 -0
  3. package/LICENSE +21 -0
  4. package/MONOREPO.md +58 -0
  5. package/README.md +280 -0
  6. package/SKILL.md +137 -0
  7. package/dist/adapters/openclaw.d.ts +103 -0
  8. package/dist/adapters/openclaw.d.ts.map +1 -0
  9. package/dist/adapters/openclaw.js +297 -0
  10. package/dist/adapters/openclaw.js.map +1 -0
  11. package/dist/cli/commands.d.ts +17 -0
  12. package/dist/cli/commands.d.ts.map +1 -0
  13. package/dist/cli/commands.js +107 -0
  14. package/dist/cli/commands.js.map +1 -0
  15. package/dist/cli/index.d.ts +6 -0
  16. package/dist/cli/index.d.ts.map +1 -0
  17. package/dist/cli/index.js +203 -0
  18. package/dist/cli/index.js.map +1 -0
  19. package/dist/core/autonomous-economy.d.ts +136 -0
  20. package/dist/core/autonomous-economy.d.ts.map +1 -0
  21. package/dist/core/autonomous-economy.js +255 -0
  22. package/dist/core/autonomous-economy.js.map +1 -0
  23. package/dist/core/connection-manager.d.ts +80 -0
  24. package/dist/core/connection-manager.d.ts.map +1 -0
  25. package/dist/core/connection-manager.js +235 -0
  26. package/dist/core/connection-manager.js.map +1 -0
  27. package/dist/core/connection-manager.test.d.ts +2 -0
  28. package/dist/core/connection-manager.test.d.ts.map +1 -0
  29. package/dist/core/connection-manager.test.js +52 -0
  30. package/dist/core/connection-manager.test.js.map +1 -0
  31. package/dist/core/e2ee-crypto.d.ts +90 -0
  32. package/dist/core/e2ee-crypto.d.ts.map +1 -0
  33. package/dist/core/e2ee-crypto.js +190 -0
  34. package/dist/core/e2ee-crypto.js.map +1 -0
  35. package/dist/core/f2a.d.ts +126 -0
  36. package/dist/core/f2a.d.ts.map +1 -0
  37. package/dist/core/f2a.js +425 -0
  38. package/dist/core/f2a.js.map +1 -0
  39. package/dist/core/identity.d.ts +47 -0
  40. package/dist/core/identity.d.ts.map +1 -0
  41. package/dist/core/identity.js +130 -0
  42. package/dist/core/identity.js.map +1 -0
  43. package/dist/core/identity.test.d.ts +2 -0
  44. package/dist/core/identity.test.d.ts.map +1 -0
  45. package/dist/core/identity.test.js +43 -0
  46. package/dist/core/identity.test.js.map +1 -0
  47. package/dist/core/p2p-network.d.ts +242 -0
  48. package/dist/core/p2p-network.d.ts.map +1 -0
  49. package/dist/core/p2p-network.js +1182 -0
  50. package/dist/core/p2p-network.js.map +1 -0
  51. package/dist/core/reputation-security.d.ts +168 -0
  52. package/dist/core/reputation-security.d.ts.map +1 -0
  53. package/dist/core/reputation-security.js +369 -0
  54. package/dist/core/reputation-security.js.map +1 -0
  55. package/dist/core/reputation.d.ts +179 -0
  56. package/dist/core/reputation.d.ts.map +1 -0
  57. package/dist/core/reputation.js +472 -0
  58. package/dist/core/reputation.js.map +1 -0
  59. package/dist/core/review-committee.d.ts +130 -0
  60. package/dist/core/review-committee.d.ts.map +1 -0
  61. package/dist/core/review-committee.js +251 -0
  62. package/dist/core/review-committee.js.map +1 -0
  63. package/dist/core/serverless.d.ts +155 -0
  64. package/dist/core/serverless.d.ts.map +1 -0
  65. package/dist/core/serverless.js +615 -0
  66. package/dist/core/serverless.js.map +1 -0
  67. package/dist/core/token-manager.d.ts +42 -0
  68. package/dist/core/token-manager.d.ts.map +1 -0
  69. package/dist/core/token-manager.js +122 -0
  70. package/dist/core/token-manager.js.map +1 -0
  71. package/dist/daemon/control-server.d.ts +55 -0
  72. package/dist/daemon/control-server.d.ts.map +1 -0
  73. package/dist/daemon/control-server.js +262 -0
  74. package/dist/daemon/control-server.js.map +1 -0
  75. package/dist/daemon/index.d.ts +35 -0
  76. package/dist/daemon/index.d.ts.map +1 -0
  77. package/dist/daemon/index.js +69 -0
  78. package/dist/daemon/index.js.map +1 -0
  79. package/dist/daemon/main.d.ts +6 -0
  80. package/dist/daemon/main.d.ts.map +1 -0
  81. package/dist/daemon/main.js +38 -0
  82. package/dist/daemon/main.js.map +1 -0
  83. package/dist/daemon/start.d.ts +6 -0
  84. package/dist/daemon/start.d.ts.map +1 -0
  85. package/dist/daemon/start.js +25 -0
  86. package/dist/daemon/start.js.map +1 -0
  87. package/dist/daemon/webhook.d.ts +30 -0
  88. package/dist/daemon/webhook.d.ts.map +1 -0
  89. package/dist/daemon/webhook.js +86 -0
  90. package/dist/daemon/webhook.js.map +1 -0
  91. package/dist/daemon/webhook.test.d.ts +2 -0
  92. package/dist/daemon/webhook.test.d.ts.map +1 -0
  93. package/dist/daemon/webhook.test.js +24 -0
  94. package/dist/daemon/webhook.test.js.map +1 -0
  95. package/dist/index.d.ts +24 -0
  96. package/dist/index.d.ts.map +1 -0
  97. package/dist/index.js +25 -0
  98. package/dist/index.js.map +1 -0
  99. package/dist/protocol/messages.d.ts +739 -0
  100. package/dist/protocol/messages.d.ts.map +1 -0
  101. package/dist/protocol/messages.js +188 -0
  102. package/dist/protocol/messages.js.map +1 -0
  103. package/dist/protocol/messages.test.d.ts +2 -0
  104. package/dist/protocol/messages.test.d.ts.map +1 -0
  105. package/dist/protocol/messages.test.js +55 -0
  106. package/dist/protocol/messages.test.js.map +1 -0
  107. package/dist/types/index.d.ts +247 -0
  108. package/dist/types/index.d.ts.map +1 -0
  109. package/dist/types/index.js +10 -0
  110. package/dist/types/index.js.map +1 -0
  111. package/dist/types/result.d.ts +28 -0
  112. package/dist/types/result.d.ts.map +1 -0
  113. package/dist/types/result.js +16 -0
  114. package/dist/types/result.js.map +1 -0
  115. package/dist/utils/benchmark.d.ts +67 -0
  116. package/dist/utils/benchmark.d.ts.map +1 -0
  117. package/dist/utils/benchmark.js +179 -0
  118. package/dist/utils/benchmark.js.map +1 -0
  119. package/dist/utils/logger.d.ts +105 -0
  120. package/dist/utils/logger.d.ts.map +1 -0
  121. package/dist/utils/logger.js +275 -0
  122. package/dist/utils/logger.js.map +1 -0
  123. package/dist/utils/middleware.d.ts +85 -0
  124. package/dist/utils/middleware.d.ts.map +1 -0
  125. package/dist/utils/middleware.js +173 -0
  126. package/dist/utils/middleware.js.map +1 -0
  127. package/dist/utils/rate-limiter.d.ts +71 -0
  128. package/dist/utils/rate-limiter.d.ts.map +1 -0
  129. package/dist/utils/rate-limiter.js +160 -0
  130. package/dist/utils/rate-limiter.js.map +1 -0
  131. package/dist/utils/signature.d.ts +57 -0
  132. package/dist/utils/signature.d.ts.map +1 -0
  133. package/dist/utils/signature.js +102 -0
  134. package/dist/utils/signature.js.map +1 -0
  135. package/dist/utils/validation.d.ts +504 -0
  136. package/dist/utils/validation.d.ts.map +1 -0
  137. package/dist/utils/validation.js +159 -0
  138. package/dist/utils/validation.js.map +1 -0
  139. package/docs/F2A-PROTOCOL.md +61 -0
  140. package/docs/MOBILE_BOOTSTRAP_DESIGN.md +126 -0
  141. package/docs/a2a-lessons.md +316 -0
  142. package/docs/middleware-guide.md +448 -0
  143. package/docs/readme-update-checklist.md +90 -0
  144. package/docs/reputation-guide.md +396 -0
  145. package/docs/rfcs/001-reputation-system.md +712 -0
  146. package/docs/security-design.md +247 -0
  147. package/install.sh +231 -0
  148. package/package.json +64 -0
  149. package/packages/openclaw-adapter/README.md +510 -0
  150. package/packages/openclaw-adapter/openclaw.plugin.json +106 -0
  151. package/packages/openclaw-adapter/package.json +40 -0
  152. package/packages/openclaw-adapter/src/announcement-queue.test.ts +449 -0
  153. package/packages/openclaw-adapter/src/announcement-queue.ts +403 -0
  154. package/packages/openclaw-adapter/src/capability-detector.test.ts +99 -0
  155. package/packages/openclaw-adapter/src/capability-detector.ts +183 -0
  156. package/packages/openclaw-adapter/src/claim-handlers.test.ts +974 -0
  157. package/packages/openclaw-adapter/src/claim-handlers.ts +482 -0
  158. package/packages/openclaw-adapter/src/connector.business.test.ts +583 -0
  159. package/packages/openclaw-adapter/src/connector.ts +795 -0
  160. package/packages/openclaw-adapter/src/index.test.ts +82 -0
  161. package/packages/openclaw-adapter/src/index.ts +18 -0
  162. package/packages/openclaw-adapter/src/integration.e2e.test.ts +829 -0
  163. package/packages/openclaw-adapter/src/logger.ts +51 -0
  164. package/packages/openclaw-adapter/src/network-client.test.ts +266 -0
  165. package/packages/openclaw-adapter/src/network-client.ts +251 -0
  166. package/packages/openclaw-adapter/src/network-recovery.test.ts +465 -0
  167. package/packages/openclaw-adapter/src/node-manager.test.ts +136 -0
  168. package/packages/openclaw-adapter/src/node-manager.ts +429 -0
  169. package/packages/openclaw-adapter/src/plugin.test.ts +439 -0
  170. package/packages/openclaw-adapter/src/plugin.ts +104 -0
  171. package/packages/openclaw-adapter/src/reputation.test.ts +221 -0
  172. package/packages/openclaw-adapter/src/reputation.ts +368 -0
  173. package/packages/openclaw-adapter/src/task-guard.test.ts +502 -0
  174. package/packages/openclaw-adapter/src/task-guard.ts +860 -0
  175. package/packages/openclaw-adapter/src/task-queue.concurrency.test.ts +462 -0
  176. package/packages/openclaw-adapter/src/task-queue.edge-cases.test.ts +284 -0
  177. package/packages/openclaw-adapter/src/task-queue.persistence.test.ts +408 -0
  178. package/packages/openclaw-adapter/src/task-queue.ts +668 -0
  179. package/packages/openclaw-adapter/src/tool-handlers.test.ts +906 -0
  180. package/packages/openclaw-adapter/src/tool-handlers.ts +574 -0
  181. package/packages/openclaw-adapter/src/types.ts +361 -0
  182. package/packages/openclaw-adapter/src/webhook-pusher.test.ts +188 -0
  183. package/packages/openclaw-adapter/src/webhook-pusher.ts +220 -0
  184. package/packages/openclaw-adapter/src/webhook-server.test.ts +580 -0
  185. package/packages/openclaw-adapter/src/webhook-server.ts +202 -0
  186. package/packages/openclaw-adapter/tsconfig.json +20 -0
  187. package/src/cli/commands.test.ts +157 -0
  188. package/src/cli/commands.ts +129 -0
  189. package/src/cli/index.test.ts +77 -0
  190. package/src/cli/index.ts +234 -0
  191. package/src/core/autonomous-economy.test.ts +291 -0
  192. package/src/core/autonomous-economy.ts +428 -0
  193. package/src/core/e2ee-crypto.test.ts +125 -0
  194. package/src/core/e2ee-crypto.ts +246 -0
  195. package/src/core/f2a.test.ts +269 -0
  196. package/src/core/f2a.ts +618 -0
  197. package/src/core/p2p-network.test.ts +199 -0
  198. package/src/core/p2p-network.ts +1432 -0
  199. package/src/core/reputation-security.test.ts +403 -0
  200. package/src/core/reputation-security.ts +562 -0
  201. package/src/core/reputation.test.ts +260 -0
  202. package/src/core/reputation.ts +576 -0
  203. package/src/core/review-committee.test.ts +380 -0
  204. package/src/core/review-committee.ts +401 -0
  205. package/src/core/token-manager.test.ts +133 -0
  206. package/src/core/token-manager.ts +140 -0
  207. package/src/daemon/control-server.test.ts +216 -0
  208. package/src/daemon/control-server.ts +292 -0
  209. package/src/daemon/index.test.ts +85 -0
  210. package/src/daemon/index.ts +89 -0
  211. package/src/daemon/main.ts +44 -0
  212. package/src/daemon/start.ts +29 -0
  213. package/src/daemon/webhook.test.ts +68 -0
  214. package/src/daemon/webhook.ts +105 -0
  215. package/src/index.test.ts +436 -0
  216. package/src/index.ts +72 -0
  217. package/src/types/index.test.ts +87 -0
  218. package/src/types/index.ts +341 -0
  219. package/src/types/result.ts +68 -0
  220. package/src/utils/benchmark.ts +237 -0
  221. package/src/utils/logger.ts +331 -0
  222. package/src/utils/middleware.ts +229 -0
  223. package/src/utils/rate-limiter.ts +207 -0
  224. package/src/utils/signature.ts +136 -0
  225. package/src/utils/validation.ts +186 -0
  226. package/tests/docker/Dockerfile.node +23 -0
  227. package/tests/docker/Dockerfile.runner +18 -0
  228. package/tests/docker/docker-compose.test.yml +73 -0
  229. package/tests/integration/message-passing.test.ts +109 -0
  230. package/tests/integration/multi-node.test.ts +92 -0
  231. package/tests/integration/p2p-connection.test.ts +83 -0
  232. package/tests/integration/test-config.ts +32 -0
  233. package/tsconfig.json +21 -0
  234. package/vitest.config.ts +26 -0
@@ -0,0 +1,403 @@
1
+ /**
2
+ * 信誉安全机制测试
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach } from 'vitest';
6
+ import {
7
+ ChainSignatureManager,
8
+ InvitationManager,
9
+ ChallengeManager,
10
+ SignedReputationEvent,
11
+ } from './reputation-security.js';
12
+ import { ReputationManager } from './reputation.js';
13
+
14
+ describe('ChainSignatureManager', () => {
15
+ let chainManager: ChainSignatureManager;
16
+
17
+ beforeEach(() => {
18
+ chainManager = new ChainSignatureManager();
19
+ });
20
+
21
+ describe('事件链管理', () => {
22
+ it('should create genesis event', () => {
23
+ const event: SignedReputationEvent = {
24
+ peerId: 'peer-1',
25
+ delta: 10,
26
+ prevHash: 'genesis',
27
+ timestamp: Date.now(),
28
+ signatures: [],
29
+ };
30
+
31
+ const result = chainManager.addSignedEvent(event);
32
+ expect(result).toBe(true);
33
+ });
34
+
35
+ it('should chain events correctly', () => {
36
+ // 第一个事件
37
+ const event1: SignedReputationEvent = {
38
+ peerId: 'peer-1',
39
+ delta: 10,
40
+ prevHash: 'genesis',
41
+ timestamp: Date.now(),
42
+ signatures: [],
43
+ };
44
+ chainManager.addSignedEvent(event1);
45
+
46
+ // 第二个事件
47
+ const prevHash = chainManager.hashEvent(event1);
48
+ const event2: SignedReputationEvent = {
49
+ peerId: 'peer-1',
50
+ delta: -5,
51
+ prevHash,
52
+ timestamp: Date.now(),
53
+ signatures: [],
54
+ };
55
+
56
+ const result = chainManager.addSignedEvent(event2);
57
+ expect(result).toBe(true);
58
+ });
59
+
60
+ it('should reject event with wrong prevHash', () => {
61
+ // 第一个事件
62
+ const event1: SignedReputationEvent = {
63
+ peerId: 'peer-1',
64
+ delta: 10,
65
+ prevHash: 'genesis',
66
+ timestamp: Date.now(),
67
+ signatures: [],
68
+ };
69
+ chainManager.addSignedEvent(event1);
70
+
71
+ // 第二个事件,错误的 prevHash
72
+ const event2: SignedReputationEvent = {
73
+ peerId: 'peer-1',
74
+ delta: -5,
75
+ prevHash: 'wrong-hash',
76
+ timestamp: Date.now(),
77
+ signatures: [],
78
+ };
79
+
80
+ const result = chainManager.addSignedEvent(event2);
81
+ expect(result).toBe(false);
82
+ });
83
+ });
84
+
85
+ describe('链验证', () => {
86
+ it('should verify valid chain', () => {
87
+ const event1: SignedReputationEvent = {
88
+ peerId: 'peer-1',
89
+ delta: 10,
90
+ prevHash: 'genesis',
91
+ timestamp: Date.now(),
92
+ signatures: [],
93
+ };
94
+ chainManager.addSignedEvent(event1);
95
+
96
+ const event2: SignedReputationEvent = {
97
+ peerId: 'peer-1',
98
+ delta: -5,
99
+ prevHash: chainManager.hashEvent(event1),
100
+ timestamp: Date.now(),
101
+ signatures: [],
102
+ };
103
+ chainManager.addSignedEvent(event2);
104
+
105
+ expect(chainManager.verifyChain('peer-1')).toBe(true);
106
+ });
107
+
108
+ it('should detect broken chain', () => {
109
+ // 先添加一个有效事件
110
+ const validEvent: SignedReputationEvent = {
111
+ peerId: 'peer-1',
112
+ delta: 10,
113
+ prevHash: 'genesis',
114
+ timestamp: Date.now(),
115
+ signatures: [],
116
+ };
117
+ chainManager.addSignedEvent(validEvent);
118
+
119
+ // 现在手动添加一个破坏的事件到链中
120
+ const chain = chainManager.getEventChain('peer-1');
121
+ const brokenEvent: SignedReputationEvent = {
122
+ peerId: 'peer-1',
123
+ delta: 100,
124
+ prevHash: 'wrong-hash', // 这个应该等于 validEvent 的 hash
125
+ timestamp: Date.now(),
126
+ signatures: [],
127
+ };
128
+ chain.push(brokenEvent);
129
+
130
+ expect(chainManager.verifyChain('peer-1')).toBe(false);
131
+ });
132
+ });
133
+
134
+ describe('分数计算', () => {
135
+ it('should calculate score from chain', () => {
136
+ const event1: SignedReputationEvent = {
137
+ peerId: 'peer-1',
138
+ delta: 10,
139
+ prevHash: 'genesis',
140
+ timestamp: Date.now(),
141
+ signatures: [],
142
+ };
143
+ chainManager.addSignedEvent(event1);
144
+
145
+ const event2: SignedReputationEvent = {
146
+ peerId: 'peer-1',
147
+ delta: 20,
148
+ prevHash: chainManager.hashEvent(event1),
149
+ timestamp: Date.now(),
150
+ signatures: [],
151
+ };
152
+ chainManager.addSignedEvent(event2);
153
+
154
+ const score = chainManager.calculateScoreFromChain('peer-1', 70);
155
+ expect(score).toBe(100); // 70 + 10 + 20
156
+ });
157
+ });
158
+ });
159
+
160
+ describe('InvitationManager', () => {
161
+ let reputationManager: ReputationManager;
162
+ let invitationManager: InvitationManager;
163
+
164
+ beforeEach(() => {
165
+ reputationManager = new ReputationManager();
166
+ invitationManager = new InvitationManager(reputationManager);
167
+ });
168
+
169
+ describe('创建邀请', () => {
170
+ it('should create invitation for high reputation inviter', () => {
171
+ // 提升邀请者信誉
172
+ for (let i = 0; i < 5; i++) {
173
+ reputationManager.recordSuccess('inviter-1', `task-${i}`);
174
+ }
175
+
176
+ const result = invitationManager.createInvitation('inviter-1', 'invitee-1');
177
+ expect(result.success).toBe(true);
178
+ expect(result.invitation).toBeDefined();
179
+ });
180
+
181
+ it('should accept invitation from default reputation inviter (70 > 60)', () => {
182
+ // 邀请者默认信誉是 70,而要求是 60
183
+ const result = invitationManager.createInvitation('inviter-1', 'invitee-1');
184
+ expect(result.success).toBe(true);
185
+ });
186
+
187
+ it('should reject invitation from very low reputation inviter', () => {
188
+ // 降低邀请者信誉到低于 60
189
+ for (let i = 0; i < 5; i++) {
190
+ reputationManager.recordFailure('inviter-1', `task-${i}`, 'test');
191
+ }
192
+
193
+ const result = invitationManager.createInvitation('inviter-1', 'invitee-1');
194
+ // 检查信誉是否足够
195
+ const score = reputationManager.getReputation('inviter-1').score;
196
+ if (score < 60) {
197
+ expect(result.success).toBe(false);
198
+ }
199
+ });
200
+
201
+ it('should reject when invitation quota exhausted', () => {
202
+ // 提升邀请者信誉
203
+ for (let i = 0; i < 5; i++) {
204
+ reputationManager.recordSuccess('inviter-1', `task-${i}`);
205
+ }
206
+
207
+ // 创建 5 个邀请(达到上限)
208
+ for (let i = 0; i < 5; i++) {
209
+ invitationManager.createInvitation('inviter-1', `invitee-${i}`);
210
+ }
211
+
212
+ // 第 6 个应该失败
213
+ const result = invitationManager.createInvitation('inviter-1', 'invitee-6');
214
+ expect(result.success).toBe(false);
215
+ expect(result.error).toContain('quota');
216
+ });
217
+
218
+ it('should reject duplicate invitation', () => {
219
+ for (let i = 0; i < 5; i++) {
220
+ reputationManager.recordSuccess('inviter-1', `task-${i}`);
221
+ }
222
+
223
+ invitationManager.createInvitation('inviter-1', 'invitee-1');
224
+
225
+ // 另一个邀请者邀请同一人
226
+ for (let i = 0; i < 5; i++) {
227
+ reputationManager.recordSuccess('inviter-2', `task-${i}`);
228
+ }
229
+
230
+ const result = invitationManager.createInvitation('inviter-2', 'invitee-1');
231
+ expect(result.success).toBe(false);
232
+ });
233
+ });
234
+
235
+ describe('初始信誉计算', () => {
236
+ it('should set initial score based on inviter reputation', () => {
237
+ // 提升邀请者信誉到高分
238
+ for (let i = 0; i < 10; i++) {
239
+ reputationManager.recordSuccess('inviter-1', `task-${i}`);
240
+ }
241
+
242
+ const inviterScore = reputationManager.getReputation('inviter-1').score;
243
+
244
+ invitationManager.createInvitation('inviter-1', 'invitee-1');
245
+
246
+ const inviteeScore = reputationManager.getReputation('invitee-1').score;
247
+ // 初始分数 = inviter分数 * 0.5
248
+ expect(inviteeScore).toBeGreaterThanOrEqual(30);
249
+ });
250
+ });
251
+
252
+ describe('连带责任', () => {
253
+ it('should apply joint liability penalty', () => {
254
+ for (let i = 0; i < 5; i++) {
255
+ reputationManager.recordSuccess('inviter-1', `task-${i}`);
256
+ }
257
+
258
+ invitationManager.createInvitation('inviter-1', 'invitee-1');
259
+
260
+ const beforeScore = reputationManager.getReputation('inviter-1').score;
261
+
262
+ // 被邀请者作恶
263
+ invitationManager.applyJointLiability('invitee-1', 20);
264
+
265
+ const afterScore = reputationManager.getReputation('inviter-1').score;
266
+ expect(afterScore).toBeLessThan(beforeScore);
267
+ });
268
+
269
+ it('should not apply penalty when joint liability disabled', () => {
270
+ const noLiabilityManager = new InvitationManager(reputationManager, {
271
+ jointLiability: false,
272
+ });
273
+
274
+ for (let i = 0; i < 5; i++) {
275
+ reputationManager.recordSuccess('inviter-1', `task-${i}`);
276
+ }
277
+
278
+ noLiabilityManager.createInvitation('inviter-1', 'invitee-1');
279
+
280
+ const beforeScore = reputationManager.getReputation('inviter-1').score;
281
+
282
+ noLiabilityManager.applyJointLiability('invitee-1', 20);
283
+
284
+ const afterScore = reputationManager.getReputation('inviter-1').score;
285
+ expect(afterScore).toBe(beforeScore);
286
+ });
287
+ });
288
+ });
289
+
290
+ describe('ChallengeManager', () => {
291
+ let reputationManager: ReputationManager;
292
+ let chainManager: ChainSignatureManager;
293
+ let challengeManager: ChallengeManager;
294
+
295
+ beforeEach(() => {
296
+ reputationManager = new ReputationManager();
297
+ chainManager = new ChainSignatureManager();
298
+ challengeManager = new ChallengeManager(reputationManager, chainManager);
299
+ });
300
+
301
+ describe('提交挑战', () => {
302
+ it('should submit challenge', () => {
303
+ const challenge = challengeManager.submitChallenge(
304
+ 'challenger-1',
305
+ 'target-1',
306
+ 'invalid_history',
307
+ 'Evidence text'
308
+ );
309
+
310
+ expect(challenge.challengerId).toBe('challenger-1');
311
+ expect(challenge.targetId).toBe('target-1');
312
+ expect(challenge.status).toBe('pending');
313
+ });
314
+
315
+ it('should track challenges by target', () => {
316
+ challengeManager.submitChallenge('challenger-1', 'target-1', 'invalid_history', 'e1');
317
+ challengeManager.submitChallenge('challenger-2', 'target-1', 'collusion', 'e2');
318
+
319
+ const challenges = challengeManager.getChallenges('target-1');
320
+ expect(challenges.length).toBe(2);
321
+ });
322
+ });
323
+
324
+ describe('处理挑战', () => {
325
+ it('should detect invalid chain when prevHash is wrong', () => {
326
+ // 直接验证链,不依赖 processChallenge
327
+ const event: SignedReputationEvent = {
328
+ peerId: 'target-1',
329
+ delta: 10,
330
+ prevHash: 'wrong-hash', // 错误的 prevHash
331
+ timestamp: Date.now(),
332
+ signatures: [],
333
+ };
334
+
335
+ // 尝试添加无效事件
336
+ const result = chainManager.addSignedEvent(event);
337
+
338
+ // 由于没有 genesis 事件,prevHash 应该是 'genesis'
339
+ expect(result).toBe(false);
340
+
341
+ // 验证链应该是空的
342
+ expect(chainManager.verifyChain('target-1')).toBe(true);
343
+ });
344
+
345
+ it('should fail when chain is valid', () => {
346
+ // 创建有效的链
347
+ const event1: SignedReputationEvent = {
348
+ peerId: 'target-1',
349
+ delta: 10,
350
+ prevHash: 'genesis',
351
+ timestamp: Date.now(),
352
+ signatures: [],
353
+ };
354
+ chainManager.addSignedEvent(event1);
355
+
356
+ const challenge = challengeManager.submitChallenge(
357
+ 'challenger-1',
358
+ 'target-1',
359
+ 'invalid_history',
360
+ 'Chain looks suspicious'
361
+ );
362
+
363
+ const beforeScore = reputationManager.getReputation('challenger-1').score;
364
+ const result = challengeManager.processChallenge(challenge);
365
+ const afterScore = reputationManager.getReputation('challenger-1').score;
366
+
367
+ expect(result.success).toBe(false);
368
+ expect(afterScore).toBeLessThan(beforeScore); // 挑战失败,惩罚挑战者
369
+ });
370
+ });
371
+
372
+ describe('合谋检测', () => {
373
+ it('should detect high variance as potential collusion', () => {
374
+ // 创建分数波动大的历史
375
+ for (let i = 0; i < 10; i++) {
376
+ reputationManager.recordSuccess('suspicious-1', `task-${i}`, i % 2 === 0 ? 50 : -30);
377
+ }
378
+
379
+ const score = challengeManager.detectCollusion('suspicious-1');
380
+ expect(score).toBeGreaterThan(0);
381
+ });
382
+
383
+ it('should return low score for normal behavior', () => {
384
+ // 正常行为
385
+ for (let i = 0; i < 10; i++) {
386
+ reputationManager.recordSuccess('normal-1', `task-${i}`);
387
+ }
388
+
389
+ const score = challengeManager.detectCollusion('normal-1');
390
+ expect(score).toBeLessThan(0.5);
391
+ });
392
+ });
393
+
394
+ describe('待处理挑战', () => {
395
+ it('should list pending challenges', () => {
396
+ challengeManager.submitChallenge('c1', 't1', 'invalid_history', 'e1');
397
+ challengeManager.submitChallenge('c2', 't2', 'collusion', 'e2');
398
+
399
+ const pending = challengeManager.getPendingChallenges();
400
+ expect(pending.length).toBe(2);
401
+ });
402
+ });
403
+ });