wowok 2.1.16 → 2.1.18
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/cjs/w/call/base.js +1 -0
- package/dist/cjs/w/call/base.js.map +1 -1
- package/dist/cjs/w/call/entity.js +1 -0
- package/dist/cjs/w/call/entity.js.map +1 -1
- package/dist/cjs/w/call/passport.js +1 -0
- package/dist/cjs/w/call/passport.js.map +1 -1
- package/dist/cjs/w/call/permission.js.map +1 -1
- package/dist/cjs/w/call/proof.js +1 -0
- package/dist/cjs/w/call/proof.js.map +1 -1
- package/dist/cjs/w/call/util.js +1 -0
- package/dist/cjs/w/call/util.js.map +1 -1
- package/dist/cjs/w/local/account.d.ts +2 -2
- package/dist/cjs/w/local/account.js +101 -28
- package/dist/cjs/w/local/account.js.map +1 -1
- package/dist/cjs/w/local/index.d.ts +1 -1
- package/dist/cjs/w/local/index.js +17 -8
- package/dist/cjs/w/local/index.js.map +1 -1
- package/dist/cjs/w/local/local.js +10 -5
- package/dist/cjs/w/local/local.js.map +1 -1
- package/dist/cjs/w/local/wip.js +1 -0
- package/dist/cjs/w/local/wip.js.map +1 -1
- package/dist/cjs/w/messenger/crypto.js +4 -4
- package/dist/cjs/w/messenger/crypto.js.map +1 -1
- package/dist/cjs/w/messenger/messenger-api.d.ts +1 -3
- package/dist/cjs/w/messenger/messenger-api.js +75 -56
- package/dist/cjs/w/messenger/messenger-api.js.map +1 -1
- package/dist/cjs/w/messenger/messenger-manager.d.ts +1 -6
- package/dist/cjs/w/messenger/messenger-manager.js +99 -150
- package/dist/cjs/w/messenger/messenger-manager.js.map +1 -1
- package/dist/cjs/w/messenger/messenger.d.ts +88 -31
- package/dist/cjs/w/messenger/messenger.js +448 -441
- package/dist/cjs/w/messenger/messenger.js.map +1 -1
- package/dist/cjs/w/messenger/server.d.ts +9 -1
- package/dist/cjs/w/messenger/server.js +56 -5
- package/dist/cjs/w/messenger/server.js.map +1 -1
- package/dist/cjs/w/messenger/session.d.ts +13 -10
- package/dist/cjs/w/messenger/session.js +290 -176
- package/dist/cjs/w/messenger/session.js.map +1 -1
- package/dist/cjs/w/messenger/storage.d.ts +30 -0
- package/dist/cjs/w/messenger/storage.js +138 -5
- package/dist/cjs/w/messenger/storage.js.map +1 -1
- package/dist/cjs/w/messenger/types.d.ts +37 -2
- package/dist/cjs/w/messenger/types.js +14 -3
- package/dist/cjs/w/messenger/types.js.map +1 -1
- package/dist/cjs/w/query/object.js +1 -0
- package/dist/cjs/w/query/object.js.map +1 -1
- package/dist/cjs/w/util.js +1 -0
- package/dist/cjs/w/util.js.map +1 -1
- package/dist/esm/w/call/base.js +1 -0
- package/dist/esm/w/call/base.js.map +1 -1
- package/dist/esm/w/call/entity.js +1 -0
- package/dist/esm/w/call/entity.js.map +1 -1
- package/dist/esm/w/call/passport.js +1 -0
- package/dist/esm/w/call/passport.js.map +1 -1
- package/dist/esm/w/call/permission.js.map +1 -1
- package/dist/esm/w/call/proof.js +1 -0
- package/dist/esm/w/call/proof.js.map +1 -1
- package/dist/esm/w/call/util.js +1 -0
- package/dist/esm/w/call/util.js.map +1 -1
- package/dist/esm/w/local/account.d.ts +2 -2
- package/dist/esm/w/local/account.js +101 -28
- package/dist/esm/w/local/account.js.map +1 -1
- package/dist/esm/w/local/index.d.ts +1 -1
- package/dist/esm/w/local/index.js +17 -8
- package/dist/esm/w/local/index.js.map +1 -1
- package/dist/esm/w/local/local.js +10 -5
- package/dist/esm/w/local/local.js.map +1 -1
- package/dist/esm/w/local/wip.js +1 -0
- package/dist/esm/w/local/wip.js.map +1 -1
- package/dist/esm/w/messenger/crypto.js +4 -4
- package/dist/esm/w/messenger/crypto.js.map +1 -1
- package/dist/esm/w/messenger/messenger-api.d.ts +1 -3
- package/dist/esm/w/messenger/messenger-api.js +75 -56
- package/dist/esm/w/messenger/messenger-api.js.map +1 -1
- package/dist/esm/w/messenger/messenger-manager.d.ts +1 -6
- package/dist/esm/w/messenger/messenger-manager.js +99 -150
- package/dist/esm/w/messenger/messenger-manager.js.map +1 -1
- package/dist/esm/w/messenger/messenger.d.ts +88 -31
- package/dist/esm/w/messenger/messenger.js +448 -441
- package/dist/esm/w/messenger/messenger.js.map +1 -1
- package/dist/esm/w/messenger/server.d.ts +9 -1
- package/dist/esm/w/messenger/server.js +56 -5
- package/dist/esm/w/messenger/server.js.map +1 -1
- package/dist/esm/w/messenger/session.d.ts +13 -10
- package/dist/esm/w/messenger/session.js +290 -176
- package/dist/esm/w/messenger/session.js.map +1 -1
- package/dist/esm/w/messenger/storage.d.ts +30 -0
- package/dist/esm/w/messenger/storage.js +138 -5
- package/dist/esm/w/messenger/storage.js.map +1 -1
- package/dist/esm/w/messenger/types.d.ts +37 -2
- package/dist/esm/w/messenger/types.js +14 -3
- package/dist/esm/w/messenger/types.js.map +1 -1
- package/dist/esm/w/query/object.js +1 -0
- package/dist/esm/w/query/object.js.map +1 -1
- package/dist/esm/w/util.js +1 -0
- package/dist/esm/w/util.js.map +1 -1
- package/package.json +3 -3
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { createHash } from "crypto";
|
|
18
18
|
import * as fs from "fs";
|
|
19
19
|
import * as path from "path";
|
|
20
|
-
|
|
20
|
+
// eslint-disable-next-line import/no-cycle
|
|
21
21
|
import { MessengerSession } from "./session.js";
|
|
22
22
|
import { MessengerServerClient } from "./server.js";
|
|
23
23
|
import { MessageStorage, SessionStateStorage } from "./storage.js";
|
|
@@ -25,25 +25,28 @@ import { hashPlaintext, verifyMessage, bytesToBase64 } from "./crypto.js";
|
|
|
25
25
|
import { canonicalizeJson } from "./utils.js";
|
|
26
26
|
import { DEFAULT_MESSENGER_CONFIG, MessengerError, MessengerErrorCode, MessageDirection, MessageStatus, WTS_FILE_BYTES_LIMIT, NORMAL_MESSAGE_BYTES_LIMIT, CHAIN_PROOF_TYPE, } from "./types.js";
|
|
27
27
|
import { isValidWowAddress } from "../../utils/sui-types.js";
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
typeof globalThis.EventSource === "undefined") {
|
|
33
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
|
-
globalThis.EventSource = EventSourcePolyfill;
|
|
35
|
-
console.log("[EventSource] Successfully polyfilled EventSource");
|
|
28
|
+
// 动态获取 Account 实例的辅助函数
|
|
29
|
+
async function getAccount() {
|
|
30
|
+
const { Account } = await import("../local/account.js");
|
|
31
|
+
return Account.Instance();
|
|
36
32
|
}
|
|
37
33
|
export class Messenger {
|
|
38
34
|
session;
|
|
39
35
|
serverClient;
|
|
40
36
|
config;
|
|
41
37
|
userAddress = null;
|
|
42
|
-
eventSource = null;
|
|
43
38
|
onMessageCallback = null;
|
|
44
|
-
|
|
39
|
+
pollingTimer = null;
|
|
40
|
+
messageConsecutiveEmptyPulls = 0;
|
|
41
|
+
prekeyConsecutiveOkCount = 0;
|
|
42
|
+
currentMessageInterval;
|
|
43
|
+
isPollingRunning = false;
|
|
45
44
|
messageStorage;
|
|
46
45
|
sessionStateStorage;
|
|
46
|
+
// 【新增】等待解密的消息池(用于 Skip Keys 机制)
|
|
47
|
+
waitingMessages = new Map();
|
|
48
|
+
// 【新增】解密失败的消息(用于用户决策重发)
|
|
49
|
+
failedMessages = new Map();
|
|
47
50
|
constructor(userAddress, config) {
|
|
48
51
|
this.userAddress = userAddress;
|
|
49
52
|
this.config = { ...DEFAULT_MESSENGER_CONFIG, ...config };
|
|
@@ -51,6 +54,8 @@ export class Messenger {
|
|
|
51
54
|
this.serverClient = new MessengerServerClient(this.config);
|
|
52
55
|
this.messageStorage = new MessageStorage(userAddress);
|
|
53
56
|
this.sessionStateStorage = new SessionStateStorage(userAddress);
|
|
57
|
+
this.currentMessageInterval =
|
|
58
|
+
this.config.message_poll_default_interval_ms ?? 6 * 60 * 1000;
|
|
54
59
|
}
|
|
55
60
|
/**
|
|
56
61
|
* 获取用户地址
|
|
@@ -70,7 +75,7 @@ export class Messenger {
|
|
|
70
75
|
if (!this.userAddress) {
|
|
71
76
|
throw new MessengerError(MessengerErrorCode.IDENTITY_NOT_FOUND, "User address not set");
|
|
72
77
|
}
|
|
73
|
-
const account = await
|
|
78
|
+
const account = await (await getAccount()).get(this.userAddress, false);
|
|
74
79
|
return account?.pubkey || "";
|
|
75
80
|
}
|
|
76
81
|
/**
|
|
@@ -93,7 +98,7 @@ export class Messenger {
|
|
|
93
98
|
if (!this.userAddress) {
|
|
94
99
|
throw new MessengerError(MessengerErrorCode.IDENTITY_NOT_FOUND, "User address not set");
|
|
95
100
|
}
|
|
96
|
-
const account = await
|
|
101
|
+
const account = await (await getAccount()).get(this.userAddress, false);
|
|
97
102
|
if (!account?.secret) {
|
|
98
103
|
throw new MessengerError(MessengerErrorCode.IDENTITY_NOT_FOUND, `Account not found or no secret key for ${this.userAddress}`);
|
|
99
104
|
}
|
|
@@ -126,20 +131,25 @@ export class Messenger {
|
|
|
126
131
|
* 2. 注册设备到服务器
|
|
127
132
|
* 3. 上传预密钥
|
|
128
133
|
* 4. 检查预密钥状态(如低于阈值则补充)
|
|
129
|
-
* 5.
|
|
134
|
+
* 5. 启动合并的polling timer
|
|
130
135
|
*/
|
|
131
136
|
async initialize() {
|
|
132
137
|
if (!this.userAddress) {
|
|
133
138
|
throw new MessengerError(MessengerErrorCode.IDENTITY_NOT_FOUND, "User address not set");
|
|
134
139
|
}
|
|
135
|
-
//
|
|
140
|
+
// 确保身份
|
|
136
141
|
await this.session.ensureIdentity(this.userAddress);
|
|
142
|
+
// 【更新】每次都注册/更新设备!
|
|
143
|
+
// Signal Protocol 最佳实践:每次初始化都可以更新预键
|
|
144
|
+
// - 已有会话不受影响(使用双棘轮动态密钥)
|
|
145
|
+
// - 新会话使用新的预键(确保本地和服务器一致)
|
|
146
|
+
//console.log(`[Messenger] Registering/updating device...`);
|
|
137
147
|
await this.session.registerDevice(this.userAddress);
|
|
138
148
|
await this.session.ensurePreKeys(this.userAddress);
|
|
139
149
|
// 初始化时检查预密钥状态
|
|
140
150
|
await this.checkAndRefillPrekeys();
|
|
141
|
-
//
|
|
142
|
-
this.
|
|
151
|
+
// 启动合并的polling timer
|
|
152
|
+
this.startPollingTimer();
|
|
143
153
|
}
|
|
144
154
|
/**
|
|
145
155
|
* 检查并补充预密钥
|
|
@@ -153,26 +163,112 @@ export class Messenger {
|
|
|
153
163
|
await this.session.ensurePreKeys(this.userAddress, false);
|
|
154
164
|
}
|
|
155
165
|
/**
|
|
156
|
-
*
|
|
166
|
+
* 启动合并的polling timer
|
|
157
167
|
*/
|
|
158
|
-
|
|
159
|
-
if (this.
|
|
160
|
-
|
|
168
|
+
startPollingTimer() {
|
|
169
|
+
if (this.pollingTimer) {
|
|
170
|
+
clearTimeout(this.pollingTimer);
|
|
161
171
|
}
|
|
162
|
-
|
|
163
|
-
this.
|
|
164
|
-
|
|
172
|
+
this.isPollingRunning = true;
|
|
173
|
+
this.scheduleNextPoll();
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* 停止polling timer
|
|
177
|
+
*/
|
|
178
|
+
stopPollingTimer() {
|
|
179
|
+
this.isPollingRunning = false;
|
|
180
|
+
if (this.pollingTimer) {
|
|
181
|
+
clearTimeout(this.pollingTimer);
|
|
182
|
+
this.pollingTimer = null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* 安排下一次poll
|
|
187
|
+
*/
|
|
188
|
+
scheduleNextPoll() {
|
|
189
|
+
if (!this.isPollingRunning)
|
|
190
|
+
return;
|
|
191
|
+
const interval = this.getNextPollInterval();
|
|
192
|
+
this.pollingTimer = setTimeout(() => {
|
|
193
|
+
this.poll()
|
|
194
|
+
.catch((error) => {
|
|
195
|
+
console.error("[Polling] Error during poll:", error);
|
|
196
|
+
})
|
|
197
|
+
.finally(() => {
|
|
198
|
+
this.scheduleNextPoll();
|
|
199
|
+
});
|
|
165
200
|
}, interval);
|
|
166
|
-
//console.log(`Prekey check timer started (interval: ${interval}ms)`);
|
|
167
201
|
}
|
|
168
202
|
/**
|
|
169
|
-
*
|
|
203
|
+
* 获取下次poll的间隔时间
|
|
204
|
+
* 【优化】增加3秒快速模式(有待解密消息时)
|
|
170
205
|
*/
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
this.
|
|
175
|
-
|
|
206
|
+
getNextPollInterval() {
|
|
207
|
+
// 如果有等待解密的消息,使用3秒快速轮询
|
|
208
|
+
if (this.waitingMessages.size > 0) {
|
|
209
|
+
return this.config.message_poll_waiting_interval_ms ?? 3 * 1000;
|
|
210
|
+
}
|
|
211
|
+
return this.currentMessageInterval;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* 执行一次poll(拉取消息和检查prekey状态)
|
|
215
|
+
*/
|
|
216
|
+
async poll() {
|
|
217
|
+
if (!this.userAddress)
|
|
218
|
+
return;
|
|
219
|
+
try {
|
|
220
|
+
const response = await this.pullMessages();
|
|
221
|
+
// 处理消息
|
|
222
|
+
if (response.messages.length > 0) {
|
|
223
|
+
this.messageConsecutiveEmptyPulls = 0;
|
|
224
|
+
this.currentMessageInterval =
|
|
225
|
+
this.config.message_poll_fast_interval_ms ?? 6 * 1000;
|
|
226
|
+
if (this.onMessageCallback) {
|
|
227
|
+
this.onMessageCallback(response.messages);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
this.messageConsecutiveEmptyPulls++;
|
|
232
|
+
if (this.messageConsecutiveEmptyPulls >=
|
|
233
|
+
(this.config.message_poll_consecutive_empty_limit ?? 3)) {
|
|
234
|
+
this.currentMessageInterval =
|
|
235
|
+
this.config.message_poll_default_interval_ms ??
|
|
236
|
+
6 * 60 * 1000;
|
|
237
|
+
this.messageConsecutiveEmptyPulls = 0;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// 处理prekey状态
|
|
241
|
+
if (response.prekey_status) {
|
|
242
|
+
const prekeyStatus = response.prekey_status;
|
|
243
|
+
if (prekeyStatus.shouldRefill) {
|
|
244
|
+
this.prekeyConsecutiveOkCount = 0;
|
|
245
|
+
await this.checkAndRefillPrekeys();
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
this.prekeyConsecutiveOkCount++;
|
|
249
|
+
if (this.prekeyConsecutiveOkCount >=
|
|
250
|
+
(this.config.prekey_poll_consecutive_ok_limit ?? 3)) {
|
|
251
|
+
this.prekeyConsecutiveOkCount = 0;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
console.error("[Polling] Poll error:", error);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* 触发快速poll(发送消息后调用)
|
|
262
|
+
*/
|
|
263
|
+
triggerFastPoll() {
|
|
264
|
+
this.currentMessageInterval =
|
|
265
|
+
this.config.message_poll_fast_interval_ms ?? 6 * 1000;
|
|
266
|
+
this.messageConsecutiveEmptyPulls = 0;
|
|
267
|
+
if (this.isPollingRunning) {
|
|
268
|
+
if (this.pollingTimer) {
|
|
269
|
+
clearTimeout(this.pollingTimer);
|
|
270
|
+
}
|
|
271
|
+
this.scheduleNextPoll();
|
|
176
272
|
}
|
|
177
273
|
}
|
|
178
274
|
/**
|
|
@@ -206,7 +302,7 @@ export class Messenger {
|
|
|
206
302
|
const plaintextHash = hashPlaintext(plaintext, createdAt, options?.guardAddress, options?.passportAddress, lastReceivedLeafIndex);
|
|
207
303
|
// 3. 签名消息
|
|
208
304
|
// 注意: guard_address 和 passport_address 始终包含在签名中(可能为空字符串),防止参数篡改
|
|
209
|
-
const account = await
|
|
305
|
+
const account = await (await getAccount()).get(this.userAddress, false);
|
|
210
306
|
if (!account) {
|
|
211
307
|
throw new MessengerError(MessengerErrorCode.IDENTITY_NOT_FOUND, "User account not found");
|
|
212
308
|
}
|
|
@@ -218,7 +314,7 @@ export class Messenger {
|
|
|
218
314
|
const lastReceivedStr = lastReceivedLeafIndex >= 0 ? lastReceivedLeafIndex.toString() : "";
|
|
219
315
|
const forceStr = options?.force ? "true" : "false";
|
|
220
316
|
const signMessage = `send_message:${this.userAddress}:${recipientAddress}:${guardAddr}:${passportAddr}:${plaintextHash}:${lastReceivedStr}:${publicKey}:${timestamp}:${nonce}:${forceStr}`;
|
|
221
|
-
const signResult = await
|
|
317
|
+
const signResult = await (await getAccount()).signData(this.userAddress, signMessage);
|
|
222
318
|
const signatureBuffer = Buffer.from(signResult.signature.slice(2), "hex");
|
|
223
319
|
const signatureBase64 = bytesToBase64(new Uint8Array(signatureBuffer));
|
|
224
320
|
// 使用统一的 sendMessage API
|
|
@@ -296,6 +392,7 @@ export class Messenger {
|
|
|
296
392
|
}
|
|
297
393
|
}
|
|
298
394
|
}
|
|
395
|
+
this.triggerFastPoll();
|
|
299
396
|
return {
|
|
300
397
|
messageId: response.messageId,
|
|
301
398
|
status: message.status,
|
|
@@ -321,9 +418,9 @@ export class Messenger {
|
|
|
321
418
|
const timestamp = Date.now(); //@ 使用ms时间戳
|
|
322
419
|
const nonce = this.generateNonce();
|
|
323
420
|
const message = `fetch_messages:${this.userAddress}:${limit || 100}:${timestamp}:${nonce}`;
|
|
324
|
-
const account = await
|
|
421
|
+
const account = await (await getAccount()).get(this.userAddress, false);
|
|
325
422
|
const publicKey = account?.pubkey || "";
|
|
326
|
-
const signResult = await
|
|
423
|
+
const signResult = await (await getAccount()).signData(this.userAddress, message);
|
|
327
424
|
const signature = Buffer.from(signResult.signature.slice(2), "hex");
|
|
328
425
|
const signatureParams = {
|
|
329
426
|
signatureScheme: "ED25519",
|
|
@@ -338,149 +435,77 @@ export class Messenger {
|
|
|
338
435
|
console.log(`[Messenger] 消息 IDs: ${response.messages.map(m => m.id).join(', ')}`);
|
|
339
436
|
}*/
|
|
340
437
|
if (response.messages.length === 0) {
|
|
341
|
-
return
|
|
438
|
+
return {
|
|
439
|
+
messages: [],
|
|
440
|
+
prekey_status: response.prekey_status,
|
|
441
|
+
};
|
|
342
442
|
}
|
|
343
443
|
const decryptedMessages = [];
|
|
344
444
|
const acknowledgedIds = [];
|
|
445
|
+
// 【优化】首先尝试处理 waitingMessages 池中的消息
|
|
446
|
+
await this.processWaitingMessages(decryptedMessages, acknowledgedIds);
|
|
447
|
+
// 处理新拉取的消息
|
|
345
448
|
for (const serverMsg of response.messages) {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
/*console.warn(
|
|
372
|
-
`[Messenger] ${serverMsg.from} 可能丢失了消息,` +
|
|
373
|
-
`对方确认: ${senderLastReceivedIndex}, ` +
|
|
374
|
-
`我已发送: ${myLastSent}`,
|
|
375
|
-
);*/
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
// 3. 使用通用验证函数验证消息
|
|
379
|
-
// 检查必需字段
|
|
380
|
-
if (!serverMsg.clientTimestamp) {
|
|
381
|
-
throw new Error(`Missing clientTimestamp for message ${serverMsg.id}`);
|
|
382
|
-
}
|
|
383
|
-
if (!serverMsg.plaintextHash) {
|
|
384
|
-
throw new Error(`Missing plaintextHash for message ${serverMsg.id}`);
|
|
385
|
-
}
|
|
386
|
-
// 验证 Merkle 元数据完整性
|
|
387
|
-
if (serverMsg.merkleMetadata) {
|
|
388
|
-
if (!serverMsg.merkleMetadata.prevRoot) {
|
|
389
|
-
throw new Error(`Message ${serverMsg.id} missing prevRoot in merkleMetadata`);
|
|
390
|
-
}
|
|
391
|
-
if (!serverMsg.merkleMetadata.newRoot) {
|
|
392
|
-
throw new Error(`Message ${serverMsg.id} missing newRoot in merkleMetadata`);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
const verificationResult = verifyMessage({
|
|
396
|
-
messageId: serverMsg.id,
|
|
397
|
-
plaintext,
|
|
398
|
-
plaintextHash: serverMsg.plaintextHash,
|
|
399
|
-
createdAt: serverMsg.clientTimestamp,
|
|
400
|
-
guardAddress: serverMsg.guardAddress,
|
|
401
|
-
passportAddress: serverMsg.passportAddress,
|
|
402
|
-
lastReceivedLeafIndex: serverMsg.lastReceivedLeafIndex, // 【新增】传入消息确认字段
|
|
403
|
-
serverSignature: serverMsg.merkleMetadata?.serverSignature,
|
|
404
|
-
serverPublicKey: serverMsg.merkleMetadata?.serverPublicKey,
|
|
405
|
-
merkleMetadata: serverMsg.merkleMetadata
|
|
406
|
-
? {
|
|
407
|
-
prevRoot: serverMsg.merkleMetadata.prevRoot,
|
|
408
|
-
newRoot: serverMsg.merkleMetadata.newRoot,
|
|
409
|
-
serverTimestamp: serverMsg.merkleMetadata.serverTimestamp,
|
|
410
|
-
leafIndex: serverMsg.merkleMetadata.leafIndex,
|
|
411
|
-
proofSiblings: serverMsg.merkleMetadata.proofSiblings,
|
|
412
|
-
proofIndices: serverMsg.merkleMetadata.proofIndices,
|
|
413
|
-
}
|
|
414
|
-
: undefined,
|
|
415
|
-
});
|
|
416
|
-
if (!verificationResult.valid) {
|
|
417
|
-
throw new Error(verificationResult.error);
|
|
418
|
-
}
|
|
419
|
-
// 5. 保存到本地存储
|
|
420
|
-
// 经过 verifyMessage 验证,这些字段一定存在
|
|
421
|
-
const message = {
|
|
422
|
-
messageId: serverMsg.id,
|
|
423
|
-
fromAddress: serverMsg.from,
|
|
424
|
-
toAddress: this.userAddress,
|
|
425
|
-
plaintextHash: serverMsg.plaintextHash,
|
|
426
|
-
plaintext,
|
|
427
|
-
guardAddress: serverMsg.guardAddress,
|
|
428
|
-
passportAddress: serverMsg.passportAddress,
|
|
429
|
-
lastReceivedLeafIndex: serverMsg.lastReceivedLeafIndex, // 【新增】保存消息确认字段
|
|
430
|
-
direction: MessageDirection.RECEIVED,
|
|
431
|
-
status: MessageStatus.CONFIRMED,
|
|
432
|
-
msgType: serverMsg.msgType,
|
|
433
|
-
leafIndex: serverMsg.merkleMetadata?.leafIndex,
|
|
434
|
-
prevRoot: serverMsg.merkleMetadata?.prevRoot,
|
|
435
|
-
newRoot: serverMsg.merkleMetadata?.newRoot,
|
|
436
|
-
serverSignature: serverMsg.merkleMetadata?.serverSignature,
|
|
437
|
-
serverTimestamp: serverMsg.merkleMetadata?.serverTimestamp,
|
|
438
|
-
serverPublicKey: serverMsg.merkleMetadata?.serverPublicKey,
|
|
439
|
-
createdAt: serverMsg.clientTimestamp,
|
|
440
|
-
// 保存 ZIP 元数据(如果是 ZIP 消息)
|
|
441
|
-
zipMetadata: serverMsg.zipMetadata,
|
|
442
|
-
};
|
|
443
|
-
this.messageStorage.saveMessage(message);
|
|
444
|
-
// 更新会话状态
|
|
445
|
-
if (serverMsg.merkleMetadata) {
|
|
446
|
-
this.sessionStateStorage.updateSessionState(this.userAddress, serverMsg.from, {
|
|
447
|
-
currentRoot: serverMsg.merkleMetadata.newRoot,
|
|
448
|
-
prevRoot: serverMsg.merkleMetadata.prevRoot,
|
|
449
|
-
lastLeafIndex: serverMsg.merkleMetadata.leafIndex,
|
|
450
|
-
lastSyncAt: Date.now(),
|
|
451
|
-
});
|
|
452
|
-
}
|
|
453
|
-
const timestamp = serverMsg.merkleMetadata?.serverTimestamp ||
|
|
454
|
-
serverMsg.clientTimestamp ||
|
|
455
|
-
Date.now();
|
|
456
|
-
decryptedMessages.push({
|
|
457
|
-
id: serverMsg.id,
|
|
458
|
-
from: serverMsg.from,
|
|
459
|
-
plaintext, // 【更新】明文直接是消息内容
|
|
460
|
-
timestamp,
|
|
461
|
-
merkleVerified: !!serverMsg.merkleMetadata,
|
|
462
|
-
merkleData: serverMsg.merkleMetadata
|
|
463
|
-
? {
|
|
464
|
-
leafIndex: serverMsg.merkleMetadata.leafIndex,
|
|
465
|
-
rootHash: serverMsg.merkleMetadata.newRoot,
|
|
466
|
-
}
|
|
467
|
-
: undefined,
|
|
468
|
-
});
|
|
449
|
+
// 检查是否已在 waitingMessages 池中
|
|
450
|
+
if (this.waitingMessages.has(serverMsg.id)) {
|
|
451
|
+
acknowledgedIds.push(serverMsg.id);
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
// 检查是否已在 failedMessages 中
|
|
455
|
+
if (this.failedMessages.has(serverMsg.id)) {
|
|
456
|
+
acknowledgedIds.push(serverMsg.id);
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
// 检查消息是否已经在本地存储
|
|
460
|
+
const existingMessage = this.messageStorage.getMessage(serverMsg.id);
|
|
461
|
+
if (existingMessage) {
|
|
462
|
+
// 消息已存在,直接确认
|
|
463
|
+
acknowledgedIds.push(serverMsg.id);
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
// 尝试解密消息
|
|
467
|
+
const decryptResult = await this.tryDecryptMessage(serverMsg);
|
|
468
|
+
if (decryptResult.success &&
|
|
469
|
+
decryptResult.message &&
|
|
470
|
+
decryptResult.decryptedData) {
|
|
471
|
+
// 解密成功,保存并添加到结果
|
|
472
|
+
this.messageStorage.saveMessage(decryptResult.message);
|
|
473
|
+
decryptedMessages.push(decryptResult.decryptedData);
|
|
469
474
|
acknowledgedIds.push(serverMsg.id);
|
|
475
|
+
// 从 waitingMessages 中移除(如果存在)
|
|
476
|
+
this.waitingMessages.delete(serverMsg.id);
|
|
470
477
|
}
|
|
471
|
-
|
|
472
|
-
|
|
478
|
+
else if (decryptResult.shouldRetry) {
|
|
479
|
+
// 需要重试(Skip Keys 机制),加入等待池
|
|
480
|
+
console.warn(`[Messenger] 消息 ${serverMsg.id} 解密失败,加入等待池: ${decryptResult.error}`);
|
|
481
|
+
this.waitingMessages.set(serverMsg.id, serverMsg);
|
|
482
|
+
// 不解密成功,不加入 acknowledgedIds,下次继续尝试
|
|
473
483
|
}
|
|
484
|
+
else {
|
|
485
|
+
// 永久失败,保存失败状态
|
|
486
|
+
console.error(`[Messenger] 消息 ${serverMsg.id} 解密永久失败: ${decryptResult.error}`);
|
|
487
|
+
this.failedMessages.set(serverMsg.id, {
|
|
488
|
+
message: serverMsg,
|
|
489
|
+
error: decryptResult.error || "Unknown error",
|
|
490
|
+
});
|
|
491
|
+
// 保存失败状态到存储
|
|
492
|
+
await this.saveFailedMessage(serverMsg, decryptResult.error || "Unknown error");
|
|
493
|
+
acknowledgedIds.push(serverMsg.id);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
// 【优化】重新调度轮询(如果有等待消息,会切换到3秒快速模式)
|
|
497
|
+
if (this.waitingMessages.size > 0 && this.isPollingRunning) {
|
|
498
|
+
this.reschedulePolling();
|
|
474
499
|
}
|
|
475
500
|
// 5. 确认已处理的消息(带签名)
|
|
476
501
|
if (acknowledgedIds.length > 0 && this.userAddress) {
|
|
477
502
|
const ackTimestamp = Date.now(); //@ 使用ms时间戳
|
|
478
503
|
const ackNonce = this.generateNonce();
|
|
479
504
|
// 签名消息格式: ack_messages:{user}:{message_ids}:{public_key}:{timestamp}:{nonce}
|
|
480
|
-
const account = await
|
|
505
|
+
const account = await (await getAccount()).get(this.userAddress, false);
|
|
481
506
|
if (account?.pubkey) {
|
|
482
507
|
const ackMessage = `ack_messages:${this.userAddress}:${acknowledgedIds.join(",")}:${account.pubkey}:${ackTimestamp}:${ackNonce}`;
|
|
483
|
-
const signResult = await
|
|
508
|
+
const signResult = await (await getAccount()).signData(this.userAddress, ackMessage);
|
|
484
509
|
const signatureBuffer = Buffer.from(signResult.signature.slice(2), "hex");
|
|
485
510
|
const signatureBase64 = bytesToBase64(new Uint8Array(signatureBuffer));
|
|
486
511
|
await this.serverClient.acknowledgeMessages(acknowledgedIds, signResult.publicKey, {
|
|
@@ -492,9 +517,11 @@ export class Messenger {
|
|
|
492
517
|
});
|
|
493
518
|
}
|
|
494
519
|
}
|
|
495
|
-
// 6.
|
|
496
|
-
|
|
497
|
-
|
|
520
|
+
// 6. 补充预密钥(poll方法会根据prekey_status来决定是否补充)
|
|
521
|
+
return {
|
|
522
|
+
messages: decryptedMessages,
|
|
523
|
+
prekey_status: response.prekey_status,
|
|
524
|
+
};
|
|
498
525
|
}
|
|
499
526
|
/**
|
|
500
527
|
* 获取最后发送的消息序号
|
|
@@ -565,6 +592,7 @@ export class Messenger {
|
|
|
565
592
|
timestamp: Date.now(),
|
|
566
593
|
};
|
|
567
594
|
// 使用 CallProof 提交到链上
|
|
595
|
+
const { CallProof } = await import("../call/proof.js");
|
|
568
596
|
const callProof = new CallProof({
|
|
569
597
|
proof: JSON.stringify(proofData),
|
|
570
598
|
server_pubkey: serverPubKey,
|
|
@@ -585,281 +613,12 @@ export class Messenger {
|
|
|
585
613
|
txHash: result?.digest || "",
|
|
586
614
|
};
|
|
587
615
|
}
|
|
588
|
-
/**
|
|
589
|
-
* 建立 SSE 连接并监听服务器事件
|
|
590
|
-
*
|
|
591
|
-
* 安全要求:必须提供签名验证,防止未授权监听他人事件
|
|
592
|
-
* @param onMessage 新消息回调
|
|
593
|
-
* @param signatureParams 签名参数(可选,不提供时自动生成)
|
|
594
|
-
*/
|
|
595
|
-
async connectEventSource(onMessage, signatureParams) {
|
|
596
|
-
if (!this.userAddress) {
|
|
597
|
-
throw new MessengerError(MessengerErrorCode.IDENTITY_NOT_FOUND, "User address not set");
|
|
598
|
-
}
|
|
599
|
-
// 关闭已有连接
|
|
600
|
-
this.disconnectEventSource();
|
|
601
|
-
if (onMessage) {
|
|
602
|
-
this.onMessageCallback = onMessage;
|
|
603
|
-
}
|
|
604
|
-
// 如果没有提供签名参数,自动生成
|
|
605
|
-
// 签名格式: event_stream:{address}:{timestamp}:{nonce}
|
|
606
|
-
const account = await Account.Instance().get(this.userAddress, false);
|
|
607
|
-
const publicKey = account?.pubkey || "";
|
|
608
|
-
let params = signatureParams;
|
|
609
|
-
if (!params) {
|
|
610
|
-
const timestamp = Date.now(); //@ 使用ms时间戳
|
|
611
|
-
const nonce = this.generateNonce();
|
|
612
|
-
const message = `event_stream:${this.userAddress}:${timestamp}:${nonce}`;
|
|
613
|
-
const signResult = await Account.Instance().signData(this.userAddress, message);
|
|
614
|
-
const signatureBuffer = Buffer.from(signResult.signature.slice(2), "hex");
|
|
615
|
-
const signatureBase64 = bytesToBase64(new Uint8Array(signatureBuffer));
|
|
616
|
-
params = {
|
|
617
|
-
signatureScheme: "ED25519",
|
|
618
|
-
signature: signatureBase64,
|
|
619
|
-
timestamp,
|
|
620
|
-
nonce,
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
// 构建带签名的 URL
|
|
624
|
-
const queryParams = new URLSearchParams({
|
|
625
|
-
publicKey: publicKey,
|
|
626
|
-
signatureScheme: params.signatureScheme,
|
|
627
|
-
signature: params.signature,
|
|
628
|
-
timestamp: params.timestamp.toString(),
|
|
629
|
-
nonce: params.nonce,
|
|
630
|
-
});
|
|
631
|
-
const url = `${this.config.serverUrl}/v1/events/${this.userAddress}?${queryParams}`;
|
|
632
|
-
//console.log(`Connecting to SSE: ${url}`);
|
|
633
|
-
// 检查是否有 EventSource 可用
|
|
634
|
-
if (typeof EventSource === "undefined") {
|
|
635
|
-
console.warn("EventSource is not available in this environment. SSE functionality disabled, falling back to polling.");
|
|
636
|
-
return;
|
|
637
|
-
}
|
|
638
|
-
try {
|
|
639
|
-
this.eventSource = new EventSource(url);
|
|
640
|
-
}
|
|
641
|
-
catch (error) {
|
|
642
|
-
console.warn("Failed to create EventSource connection, falling back to polling.", error);
|
|
643
|
-
return;
|
|
644
|
-
}
|
|
645
|
-
// 监听消息事件
|
|
646
|
-
this.eventSource.onmessage = async (event) => {
|
|
647
|
-
try {
|
|
648
|
-
const sseEvent = JSON.parse(event.data);
|
|
649
|
-
//console.log("SSE event received:", sseEvent.type);
|
|
650
|
-
switch (sseEvent.type) {
|
|
651
|
-
case "NewMessage":
|
|
652
|
-
// 收到新消息通知,拉取并解密消息
|
|
653
|
-
if (this.onMessageCallback) {
|
|
654
|
-
const messages = await this.pullMessages();
|
|
655
|
-
if (messages.length > 0) {
|
|
656
|
-
this.onMessageCallback(messages);
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
break;
|
|
660
|
-
case "ark_confirmed": {
|
|
661
|
-
// 发送方收到 ARK 确认,验证接收方签名后更新本地状态
|
|
662
|
-
const { message_ids, recipient, recipient_public_key, signature, timestamp, } = sseEvent.data;
|
|
663
|
-
/*console.log(
|
|
664
|
-
`SSE ark_confirmed: messages ${message_ids} confirmed by ${recipient}`,
|
|
665
|
-
);*/
|
|
666
|
-
// 验证接收方签名(防止服务器作恶)
|
|
667
|
-
// 签名消息格式: ack_messages:{recipient}:{message_ids}:{public_key}:{timestamp}:{nonce}
|
|
668
|
-
// 注意: SSE事件中不包含nonce,因为nonce是在ARK请求时由接收方生成的,服务器只是转发
|
|
669
|
-
const verifyMessage = `ack_messages:${recipient}:${message_ids.join(",")}:${recipient_public_key}:${timestamp}`;
|
|
670
|
-
try {
|
|
671
|
-
// 使用事件中携带的接收方公钥验证签名
|
|
672
|
-
const { Ed25519PublicKey } = await import("../../keypairs/ed25519/publickey.js");
|
|
673
|
-
const publicKey = new Ed25519PublicKey(recipient_public_key);
|
|
674
|
-
const signatureBytes = Uint8Array.from(Buffer.from(signature, "base64"));
|
|
675
|
-
const isValid = await publicKey.verify(new TextEncoder().encode(verifyMessage), signatureBytes);
|
|
676
|
-
if (isValid) {
|
|
677
|
-
// 签名验证成功,保存 ARK 确认(不可更改字段)
|
|
678
|
-
for (const msgId of message_ids) {
|
|
679
|
-
// 获取现有消息并更新 ARK 确认
|
|
680
|
-
const existingMsg = this.messageStorage.getMessage(msgId);
|
|
681
|
-
if (existingMsg) {
|
|
682
|
-
existingMsg.arkConfirmed = {
|
|
683
|
-
recipient,
|
|
684
|
-
recipientPublicKey: recipient_public_key,
|
|
685
|
-
signature,
|
|
686
|
-
timestamp,
|
|
687
|
-
};
|
|
688
|
-
this.messageStorage.saveMessage(existingMsg);
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
/*console.log(
|
|
692
|
-
`ARK verified, stored ${message_ids.length} ARK confirmations`,
|
|
693
|
-
);*/
|
|
694
|
-
}
|
|
695
|
-
else {
|
|
696
|
-
console.error("ARK signature verification failed - possible server tampering");
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
catch (error) {
|
|
700
|
-
console.error("Failed to verify ARK signature:", error);
|
|
701
|
-
}
|
|
702
|
-
break;
|
|
703
|
-
}
|
|
704
|
-
case "PreKeyLow":
|
|
705
|
-
// 收到预密钥低库存通知,立即补充
|
|
706
|
-
/*console.log(
|
|
707
|
-
`SSE PreKeyLow event: ${sseEvent.data.current}/${sseEvent.data.max_allowed}, auto-refilling...`,
|
|
708
|
-
);*/
|
|
709
|
-
await this.checkAndRefillPrekeys();
|
|
710
|
-
break;
|
|
711
|
-
case "Custom":
|
|
712
|
-
// 自定义事件(如 Guard 确认/拒绝),暂不处理
|
|
713
|
-
//console.log("SSE Custom event received");
|
|
714
|
-
break;
|
|
715
|
-
case "guard_confirmed": {
|
|
716
|
-
// Guard 消息验证通过,更新消息的 merkleData
|
|
717
|
-
const { message_id, merkle_data, timestamp: _timestamp, } = sseEvent.data;
|
|
718
|
-
/*console.log(
|
|
719
|
-
`SSE guard_confirmed: message ${message_id} confirmed`,
|
|
720
|
-
);*/
|
|
721
|
-
// 更新本地消息状态
|
|
722
|
-
// 获取原始消息数据用于验证
|
|
723
|
-
const existingMsg = this.messageStorage.getMessage(message_id);
|
|
724
|
-
if (!existingMsg) {
|
|
725
|
-
console.warn(`Guard confirmed event received but message ${message_id} not found in local storage`);
|
|
726
|
-
break;
|
|
727
|
-
}
|
|
728
|
-
// 验证 Merkle 数据完整性
|
|
729
|
-
if (!merkle_data.prevRoot) {
|
|
730
|
-
console.error(`Guard message ${message_id} missing prevRoot in merkle_data`);
|
|
731
|
-
break;
|
|
732
|
-
}
|
|
733
|
-
if (!merkle_data.newRoot) {
|
|
734
|
-
console.error(`Guard message ${message_id} missing newRoot in merkle_data`);
|
|
735
|
-
break;
|
|
736
|
-
}
|
|
737
|
-
if (!merkle_data.serverTimestamp) {
|
|
738
|
-
console.error(`Guard message ${message_id} missing serverTimestamp in merkle_data`);
|
|
739
|
-
break;
|
|
740
|
-
}
|
|
741
|
-
if (!merkle_data.serverSignature) {
|
|
742
|
-
console.error(`Guard message ${message_id} missing serverSignature in merkle_data`);
|
|
743
|
-
break;
|
|
744
|
-
}
|
|
745
|
-
if (!merkle_data.serverPublicKey) {
|
|
746
|
-
console.error(`Guard message ${message_id} missing serverPublicKey in merkle_data`);
|
|
747
|
-
break;
|
|
748
|
-
}
|
|
749
|
-
// 验证消息的 Merkle Root 计算
|
|
750
|
-
const { verifySingleMerkleRoot } = await import("./crypto.js");
|
|
751
|
-
const rootValid = verifySingleMerkleRoot(merkle_data.prevRoot, merkle_data.newRoot, existingMsg.plaintextHash, merkle_data.serverTimestamp, merkle_data.leafIndex);
|
|
752
|
-
if (!rootValid.valid) {
|
|
753
|
-
console.error(`Guard message ${message_id} Merkle root verification failed: ${rootValid.error}`);
|
|
754
|
-
break;
|
|
755
|
-
}
|
|
756
|
-
// 验证服务器签名
|
|
757
|
-
const { verifyEd25519Signature } = await import("./crypto.js");
|
|
758
|
-
const signData = `${merkle_data.prevRoot}:${merkle_data.newRoot}:${merkle_data.serverTimestamp}:${merkle_data.serverPublicKey}`;
|
|
759
|
-
const signatureValid = verifyEd25519Signature(merkle_data.serverPublicKey, signData, merkle_data.serverSignature);
|
|
760
|
-
if (!signatureValid) {
|
|
761
|
-
console.error(`Guard message ${message_id} server signature verification failed`);
|
|
762
|
-
break;
|
|
763
|
-
}
|
|
764
|
-
/*console.log(
|
|
765
|
-
`Guard message ${message_id} verification passed, updating...`,
|
|
766
|
-
);*/
|
|
767
|
-
// 直接使用 updateMessageStatus 更新消息的 Merkle 数据
|
|
768
|
-
// 因为 message_id 已经包含 nonce,不需要再更新消息 ID
|
|
769
|
-
this.messageStorage.updateMessageStatus(message_id, MessageStatus.CONFIRMED, {
|
|
770
|
-
leafIndex: merkle_data.leafIndex,
|
|
771
|
-
newRoot: merkle_data.newRoot,
|
|
772
|
-
serverSignature: merkle_data.serverSignature,
|
|
773
|
-
serverTimestamp: merkle_data.serverTimestamp,
|
|
774
|
-
serverPublicKey: merkle_data.serverPublicKey,
|
|
775
|
-
});
|
|
776
|
-
const updated = true;
|
|
777
|
-
if (updated) {
|
|
778
|
-
/*console.log(
|
|
779
|
-
`Guard message ${message_id} updated with merkle data`,
|
|
780
|
-
);*/
|
|
781
|
-
// 更新会话状态
|
|
782
|
-
if (existingMsg.toAddress === null) {
|
|
783
|
-
throw new Error(`Guard message ${message_id} has null toAddress`);
|
|
784
|
-
}
|
|
785
|
-
const toAddr = existingMsg.toAddress;
|
|
786
|
-
if (toAddr === null) {
|
|
787
|
-
throw new Error(`Guard message ${message_id} has null toAddress`);
|
|
788
|
-
}
|
|
789
|
-
this.sessionStateStorage.updateSessionState(this.userAddress, toAddr, {
|
|
790
|
-
currentRoot: merkle_data.newRoot,
|
|
791
|
-
prevRoot: merkle_data.prevRoot,
|
|
792
|
-
lastLeafIndex: merkle_data.leafIndex,
|
|
793
|
-
lastSyncAt: Date.now(),
|
|
794
|
-
});
|
|
795
|
-
}
|
|
796
|
-
else {
|
|
797
|
-
console.warn(`Failed to update Guard message ${message_id}`);
|
|
798
|
-
}
|
|
799
|
-
break;
|
|
800
|
-
}
|
|
801
|
-
case "guard_rejected": {
|
|
802
|
-
// Guard 消息验证被拒绝
|
|
803
|
-
const { message_id, reason: _reason, timestamp: _timestamp, } = sseEvent.data;
|
|
804
|
-
/*console.log(
|
|
805
|
-
`SSE guard_rejected: message ${message_id} rejected: ${reason}`,
|
|
806
|
-
);*/
|
|
807
|
-
// 更新本地消息状态
|
|
808
|
-
// 首先尝试通过 message_id 查找消息
|
|
809
|
-
const existingMsg = this.messageStorage.getMessage(message_id);
|
|
810
|
-
if (existingMsg) {
|
|
811
|
-
existingMsg.status = MessageStatus.REJECTED;
|
|
812
|
-
this.messageStorage.saveMessage(existingMsg);
|
|
813
|
-
/*console.log(
|
|
814
|
-
`Guard message ${message_id} marked as rejected`,
|
|
815
|
-
);*/
|
|
816
|
-
}
|
|
817
|
-
else {
|
|
818
|
-
/*console.warn(
|
|
819
|
-
`Guard rejected event received but message ${message_id} not found in local storage`,
|
|
820
|
-
);*/
|
|
821
|
-
}
|
|
822
|
-
break;
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
catch (error) {
|
|
827
|
-
console.error("Failed to handle SSE event:", error);
|
|
828
|
-
}
|
|
829
|
-
};
|
|
830
|
-
// 连接打开
|
|
831
|
-
this.eventSource.onopen = () => {
|
|
832
|
-
//console.log("SSE connection opened");
|
|
833
|
-
};
|
|
834
|
-
// 连接错误
|
|
835
|
-
this.eventSource.onerror = (error) => {
|
|
836
|
-
console.error("SSE connection error:", error);
|
|
837
|
-
this.disconnectEventSource();
|
|
838
|
-
};
|
|
839
|
-
}
|
|
840
|
-
/**
|
|
841
|
-
* 检查 SSE 是否已连接
|
|
842
|
-
*/
|
|
843
|
-
isEventSourceConnected() {
|
|
844
|
-
return this.eventSource !== null;
|
|
845
|
-
}
|
|
846
|
-
/**
|
|
847
|
-
* 断开 SSE 连接
|
|
848
|
-
*/
|
|
849
|
-
disconnectEventSource() {
|
|
850
|
-
if (this.eventSource) {
|
|
851
|
-
this.eventSource.close();
|
|
852
|
-
this.eventSource = null;
|
|
853
|
-
//console.log("SSE connection closed");
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
616
|
/**
|
|
857
617
|
* 销毁 Messenger 实例
|
|
858
|
-
*
|
|
618
|
+
* 清理所有资源(定时器等)
|
|
859
619
|
*/
|
|
860
620
|
destroy() {
|
|
861
|
-
this.
|
|
862
|
-
this.disconnectEventSource();
|
|
621
|
+
this.stopPollingTimer();
|
|
863
622
|
//console.log("Messenger instance destroyed");
|
|
864
623
|
}
|
|
865
624
|
/**
|
|
@@ -869,6 +628,13 @@ export class Messenger {
|
|
|
869
628
|
disconnect() {
|
|
870
629
|
this.destroy();
|
|
871
630
|
}
|
|
631
|
+
/**
|
|
632
|
+
* 设置新消息回调
|
|
633
|
+
* @param onMessage 新消息回调函数
|
|
634
|
+
*/
|
|
635
|
+
setOnMessageCallback(onMessage) {
|
|
636
|
+
this.onMessageCallback = onMessage;
|
|
637
|
+
}
|
|
872
638
|
/**
|
|
873
639
|
* 发送 ZIP 压缩文件
|
|
874
640
|
*
|
|
@@ -922,7 +688,7 @@ export class Messenger {
|
|
|
922
688
|
// 8. 发送到服务器(带 ZIP 元数据)
|
|
923
689
|
const timestamp = Date.now();
|
|
924
690
|
const nonce = this.generateNonce();
|
|
925
|
-
const account = await
|
|
691
|
+
const account = await (await getAccount()).get(this.userAddress, false);
|
|
926
692
|
if (!account) {
|
|
927
693
|
throw new MessengerError(MessengerErrorCode.IDENTITY_NOT_FOUND, "User account not found");
|
|
928
694
|
}
|
|
@@ -932,7 +698,7 @@ export class Messenger {
|
|
|
932
698
|
const lastReceivedStr = lastReceivedLeafIndex >= 0 ? lastReceivedLeafIndex.toString() : "";
|
|
933
699
|
const forceStr = options?.force ? "true" : "false";
|
|
934
700
|
const signMessage = `send_message:${this.userAddress}:${recipientAddress}:${guardAddr}:${passportAddr}:${plaintextHash}:${lastReceivedStr}:${publicKey}:${timestamp}:${nonce}:${forceStr}`;
|
|
935
|
-
const signResult = await
|
|
701
|
+
const signResult = await (await getAccount()).signData(this.userAddress, signMessage);
|
|
936
702
|
const signatureBuffer = Buffer.from(signResult.signature.slice(2), "hex");
|
|
937
703
|
const signatureBase64 = bytesToBase64(new Uint8Array(signatureBuffer));
|
|
938
704
|
// 使用统一的 sendMessage API
|
|
@@ -1013,6 +779,7 @@ export class Messenger {
|
|
|
1013
779
|
}
|
|
1014
780
|
}
|
|
1015
781
|
}
|
|
782
|
+
this.triggerFastPoll();
|
|
1016
783
|
return {
|
|
1017
784
|
messageId: response.messageId,
|
|
1018
785
|
status: message.status,
|
|
@@ -1067,11 +834,11 @@ export class Messenger {
|
|
|
1067
834
|
const nonce = this.generateNonce();
|
|
1068
835
|
const dataJson = canonicalizeJson(data);
|
|
1069
836
|
const signMessage = `${timestamp}:${nonce}:${dataJson}`;
|
|
1070
|
-
const signResult = await
|
|
837
|
+
const signResult = await (await getAccount()).signData(this.userAddress, signMessage);
|
|
1071
838
|
const signatureBuffer = Buffer.from(signResult.signature.slice(2), "hex");
|
|
1072
839
|
const signatureBase64 = bytesToBase64(new Uint8Array(signatureBuffer));
|
|
1073
840
|
// 获取公钥(带标志位)用于服务器验证
|
|
1074
|
-
const account = await
|
|
841
|
+
const account = await (await getAccount()).get(this.userAddress, false);
|
|
1075
842
|
if (!account || !account.pubkey) {
|
|
1076
843
|
throw new MessengerError(MessengerErrorCode.IDENTITY_NOT_FOUND, "Account public key not found");
|
|
1077
844
|
}
|
|
@@ -1325,5 +1092,245 @@ export class Messenger {
|
|
|
1325
1092
|
const signedRequest = await this._createSignedRequest({});
|
|
1326
1093
|
return this.serverClient.getGuardList(this.userAddress, signedRequest);
|
|
1327
1094
|
}
|
|
1095
|
+
// ========== 【新增】Skip Keys 和消息池相关方法 ==========
|
|
1096
|
+
/**
|
|
1097
|
+
* 尝试解密单条消息
|
|
1098
|
+
* @param serverMsg 服务器消息
|
|
1099
|
+
* @returns 解密结果
|
|
1100
|
+
*/
|
|
1101
|
+
async tryDecryptMessage(serverMsg) {
|
|
1102
|
+
try {
|
|
1103
|
+
// 1. 解密消息
|
|
1104
|
+
const ciphertext = Uint8Array.from(Buffer.from(serverMsg.bodyB64, "base64"));
|
|
1105
|
+
const decryptedData = await this.session.decryptMessage(this.userAddress, serverMsg.from, ciphertext.buffer, serverMsg.msgType);
|
|
1106
|
+
const plaintext = decryptedData;
|
|
1107
|
+
// 2. 验证消息
|
|
1108
|
+
if (!serverMsg.clientTimestamp) {
|
|
1109
|
+
throw new Error(`Missing clientTimestamp for message ${serverMsg.id}`);
|
|
1110
|
+
}
|
|
1111
|
+
if (!serverMsg.plaintextHash) {
|
|
1112
|
+
throw new Error(`Missing plaintextHash for message ${serverMsg.id}`);
|
|
1113
|
+
}
|
|
1114
|
+
const verificationResult = verifyMessage({
|
|
1115
|
+
messageId: serverMsg.id,
|
|
1116
|
+
plaintext,
|
|
1117
|
+
plaintextHash: serverMsg.plaintextHash,
|
|
1118
|
+
createdAt: serverMsg.clientTimestamp,
|
|
1119
|
+
guardAddress: serverMsg.guardAddress,
|
|
1120
|
+
passportAddress: serverMsg.passportAddress,
|
|
1121
|
+
lastReceivedLeafIndex: serverMsg.lastReceivedLeafIndex,
|
|
1122
|
+
serverSignature: serverMsg.merkleMetadata?.serverSignature,
|
|
1123
|
+
serverPublicKey: serverMsg.merkleMetadata?.serverPublicKey,
|
|
1124
|
+
merkleMetadata: serverMsg.merkleMetadata
|
|
1125
|
+
? {
|
|
1126
|
+
prevRoot: serverMsg.merkleMetadata.prevRoot,
|
|
1127
|
+
newRoot: serverMsg.merkleMetadata.newRoot,
|
|
1128
|
+
serverTimestamp: serverMsg.merkleMetadata.serverTimestamp,
|
|
1129
|
+
leafIndex: serverMsg.merkleMetadata.leafIndex,
|
|
1130
|
+
proofSiblings: serverMsg.merkleMetadata.proofSiblings,
|
|
1131
|
+
proofIndices: serverMsg.merkleMetadata.proofIndices,
|
|
1132
|
+
}
|
|
1133
|
+
: undefined,
|
|
1134
|
+
});
|
|
1135
|
+
if (!verificationResult.valid) {
|
|
1136
|
+
throw new Error(verificationResult.error);
|
|
1137
|
+
}
|
|
1138
|
+
// 3. 构造消息对象
|
|
1139
|
+
const message = {
|
|
1140
|
+
messageId: serverMsg.id,
|
|
1141
|
+
fromAddress: serverMsg.from,
|
|
1142
|
+
toAddress: this.userAddress,
|
|
1143
|
+
plaintextHash: serverMsg.plaintextHash,
|
|
1144
|
+
plaintext,
|
|
1145
|
+
guardAddress: serverMsg.guardAddress,
|
|
1146
|
+
passportAddress: serverMsg.passportAddress,
|
|
1147
|
+
lastReceivedLeafIndex: serverMsg.lastReceivedLeafIndex,
|
|
1148
|
+
direction: MessageDirection.RECEIVED,
|
|
1149
|
+
status: MessageStatus.DECRYPTED, // 【更新】使用 DECRYPTED 状态
|
|
1150
|
+
msgType: serverMsg.msgType,
|
|
1151
|
+
leafIndex: serverMsg.merkleMetadata?.leafIndex,
|
|
1152
|
+
prevRoot: serverMsg.merkleMetadata?.prevRoot,
|
|
1153
|
+
newRoot: serverMsg.merkleMetadata?.newRoot,
|
|
1154
|
+
serverSignature: serverMsg.merkleMetadata?.serverSignature,
|
|
1155
|
+
serverTimestamp: serverMsg.merkleMetadata?.serverTimestamp,
|
|
1156
|
+
serverPublicKey: serverMsg.merkleMetadata?.serverPublicKey,
|
|
1157
|
+
createdAt: serverMsg.clientTimestamp,
|
|
1158
|
+
receivedAt: Date.now(),
|
|
1159
|
+
zipMetadata: serverMsg.zipMetadata,
|
|
1160
|
+
};
|
|
1161
|
+
// 4. 更新会话状态
|
|
1162
|
+
if (serverMsg.merkleMetadata) {
|
|
1163
|
+
this.sessionStateStorage.updateSessionState(this.userAddress, serverMsg.from, {
|
|
1164
|
+
currentRoot: serverMsg.merkleMetadata.newRoot,
|
|
1165
|
+
prevRoot: serverMsg.merkleMetadata.prevRoot,
|
|
1166
|
+
lastLeafIndex: serverMsg.merkleMetadata.leafIndex,
|
|
1167
|
+
lastSyncAt: Date.now(),
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
const timestamp = serverMsg.merkleMetadata?.serverTimestamp ||
|
|
1171
|
+
serverMsg.clientTimestamp ||
|
|
1172
|
+
Date.now();
|
|
1173
|
+
return {
|
|
1174
|
+
success: true,
|
|
1175
|
+
shouldRetry: false,
|
|
1176
|
+
message,
|
|
1177
|
+
decryptedData: {
|
|
1178
|
+
id: serverMsg.id,
|
|
1179
|
+
from: serverMsg.from,
|
|
1180
|
+
plaintext,
|
|
1181
|
+
timestamp,
|
|
1182
|
+
merkleVerified: !!serverMsg.merkleMetadata,
|
|
1183
|
+
merkleData: serverMsg.merkleMetadata
|
|
1184
|
+
? {
|
|
1185
|
+
leafIndex: serverMsg.merkleMetadata.leafIndex,
|
|
1186
|
+
rootHash: serverMsg.merkleMetadata.newRoot,
|
|
1187
|
+
}
|
|
1188
|
+
: undefined,
|
|
1189
|
+
},
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
1192
|
+
catch (error) {
|
|
1193
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1194
|
+
// 判断是否应该重试(Skip Keys 机制)
|
|
1195
|
+
const shouldRetry = this.isRetryableError(errorMessage);
|
|
1196
|
+
return {
|
|
1197
|
+
success: false,
|
|
1198
|
+
shouldRetry,
|
|
1199
|
+
error: errorMessage,
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* 判断错误是否可重试(Skip Keys 机制)
|
|
1205
|
+
* @param errorMessage 错误信息
|
|
1206
|
+
* @returns 是否可重试
|
|
1207
|
+
*/
|
|
1208
|
+
isRetryableError(errorMessage) {
|
|
1209
|
+
// Signal Protocol 内部已经处理了 Skip Keys
|
|
1210
|
+
// 这里的错误通常是更严重的错误
|
|
1211
|
+
const retryablePatterns = [
|
|
1212
|
+
/message number/i,
|
|
1213
|
+
/chain key/i,
|
|
1214
|
+
/session not found/i,
|
|
1215
|
+
/prekey not found/i,
|
|
1216
|
+
];
|
|
1217
|
+
return retryablePatterns.some((pattern) => pattern.test(errorMessage));
|
|
1218
|
+
}
|
|
1219
|
+
/**
|
|
1220
|
+
* 处理 waitingMessages 池中的消息
|
|
1221
|
+
* @param decryptedMessages 解密成功的消息列表
|
|
1222
|
+
* @param acknowledgedIds 已确认的消息ID列表
|
|
1223
|
+
*/
|
|
1224
|
+
async processWaitingMessages(decryptedMessages, acknowledgedIds) {
|
|
1225
|
+
if (this.waitingMessages.size === 0)
|
|
1226
|
+
return;
|
|
1227
|
+
console.log(`[Messenger] 尝试处理 ${this.waitingMessages.size} 条等待消息`);
|
|
1228
|
+
const processedIds = [];
|
|
1229
|
+
for (const [id, serverMsg] of this.waitingMessages) {
|
|
1230
|
+
const decryptResult = await this.tryDecryptMessage(serverMsg);
|
|
1231
|
+
if (decryptResult.success &&
|
|
1232
|
+
decryptResult.message &&
|
|
1233
|
+
decryptResult.decryptedData) {
|
|
1234
|
+
// 解密成功
|
|
1235
|
+
this.messageStorage.saveMessage(decryptResult.message);
|
|
1236
|
+
decryptedMessages.push(decryptResult.decryptedData);
|
|
1237
|
+
acknowledgedIds.push(id);
|
|
1238
|
+
processedIds.push(id);
|
|
1239
|
+
}
|
|
1240
|
+
else if (!decryptResult.shouldRetry) {
|
|
1241
|
+
// 永久失败
|
|
1242
|
+
console.error(`[Messenger] 等待消息 ${id} 解密永久失败: ${decryptResult.error}`);
|
|
1243
|
+
this.failedMessages.set(id, {
|
|
1244
|
+
message: serverMsg,
|
|
1245
|
+
error: decryptResult.error || "Unknown error",
|
|
1246
|
+
});
|
|
1247
|
+
await this.saveFailedMessage(serverMsg, decryptResult.error || "Unknown error");
|
|
1248
|
+
acknowledgedIds.push(id);
|
|
1249
|
+
processedIds.push(id);
|
|
1250
|
+
}
|
|
1251
|
+
// 如果需要重试,保留在 waitingMessages 中
|
|
1252
|
+
}
|
|
1253
|
+
// 从 waitingMessages 中移除已处理的消息
|
|
1254
|
+
for (const id of processedIds) {
|
|
1255
|
+
this.waitingMessages.delete(id);
|
|
1256
|
+
}
|
|
1257
|
+
if (processedIds.length > 0) {
|
|
1258
|
+
console.log(`[Messenger] 成功处理 ${processedIds.length} 条等待消息`);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* 保存解密失败的消息
|
|
1263
|
+
* @param serverMsg 服务器消息
|
|
1264
|
+
* @param error 错误信息
|
|
1265
|
+
*/
|
|
1266
|
+
async saveFailedMessage(serverMsg, error) {
|
|
1267
|
+
const failedMessage = {
|
|
1268
|
+
messageId: serverMsg.id,
|
|
1269
|
+
fromAddress: serverMsg.from,
|
|
1270
|
+
toAddress: this.userAddress,
|
|
1271
|
+
plaintextHash: serverMsg.plaintextHash || "",
|
|
1272
|
+
guardAddress: serverMsg.guardAddress,
|
|
1273
|
+
passportAddress: serverMsg.passportAddress,
|
|
1274
|
+
lastReceivedLeafIndex: serverMsg.lastReceivedLeafIndex,
|
|
1275
|
+
direction: MessageDirection.RECEIVED,
|
|
1276
|
+
status: MessageStatus.DECRYPT_FAILED,
|
|
1277
|
+
msgType: serverMsg.msgType,
|
|
1278
|
+
leafIndex: serverMsg.merkleMetadata?.leafIndex,
|
|
1279
|
+
prevRoot: serverMsg.merkleMetadata?.prevRoot,
|
|
1280
|
+
newRoot: serverMsg.merkleMetadata?.newRoot,
|
|
1281
|
+
serverSignature: serverMsg.merkleMetadata?.serverSignature,
|
|
1282
|
+
serverTimestamp: serverMsg.merkleMetadata?.serverTimestamp,
|
|
1283
|
+
serverPublicKey: serverMsg.merkleMetadata?.serverPublicKey,
|
|
1284
|
+
createdAt: serverMsg.clientTimestamp || Date.now(),
|
|
1285
|
+
receivedAt: Date.now(),
|
|
1286
|
+
zipMetadata: serverMsg.zipMetadata,
|
|
1287
|
+
decryptError: error,
|
|
1288
|
+
decryptAttempts: 1,
|
|
1289
|
+
lastDecryptAttemptAt: Date.now(),
|
|
1290
|
+
};
|
|
1291
|
+
this.messageStorage.saveMessage(failedMessage);
|
|
1292
|
+
}
|
|
1293
|
+
/**
|
|
1294
|
+
* 重新调度轮询(用于切换到快速模式)
|
|
1295
|
+
*/
|
|
1296
|
+
reschedulePolling() {
|
|
1297
|
+
if (!this.isPollingRunning || !this.pollingTimer)
|
|
1298
|
+
return;
|
|
1299
|
+
console.log(`[Messenger] 重新调度轮询,切换到快速模式(等待消息: ${this.waitingMessages.size})`);
|
|
1300
|
+
clearTimeout(this.pollingTimer);
|
|
1301
|
+
this.pollingTimer = null;
|
|
1302
|
+
this.scheduleNextPoll();
|
|
1303
|
+
}
|
|
1304
|
+
/**
|
|
1305
|
+
* 获取解密失败的消息(供用户决策重发)
|
|
1306
|
+
* @returns 失败消息列表
|
|
1307
|
+
*/
|
|
1308
|
+
getFailedMessages() {
|
|
1309
|
+
return Array.from(this.failedMessages.entries()).map(([id, data]) => ({
|
|
1310
|
+
messageId: id,
|
|
1311
|
+
from: data.message.from,
|
|
1312
|
+
error: data.error,
|
|
1313
|
+
leafIndex: data.message.merkleMetadata?.leafIndex,
|
|
1314
|
+
}));
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* 清除解密失败的消息记录
|
|
1318
|
+
* @param messageId 消息ID(可选,不提供则清除所有)
|
|
1319
|
+
*/
|
|
1320
|
+
clearFailedMessages(messageId) {
|
|
1321
|
+
if (messageId) {
|
|
1322
|
+
this.failedMessages.delete(messageId);
|
|
1323
|
+
}
|
|
1324
|
+
else {
|
|
1325
|
+
this.failedMessages.clear();
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* 获取等待解密的消息数量
|
|
1330
|
+
* @returns 等待消息数量
|
|
1331
|
+
*/
|
|
1332
|
+
getWaitingMessageCount() {
|
|
1333
|
+
return this.waitingMessages.size;
|
|
1334
|
+
}
|
|
1328
1335
|
}
|
|
1329
1336
|
//# sourceMappingURL=messenger.js.map
|