web-dc-api 0.0.50 → 0.0.51

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.
@@ -1,63 +1,90 @@
1
- import crypto from 'crypto';
2
-
3
- import { peerIdFromPublicKey,peerIdFromPrivateKey, peerIdFromMultihash, peerIdFromString } from "@libp2p/peer-id";
1
+ import crypto from "crypto";
2
+
3
+ import {
4
+ peerIdFromPublicKey,
5
+ peerIdFromPrivateKey,
6
+ peerIdFromMultihash,
7
+ peerIdFromString,
8
+ } from "@libp2p/peer-id";
4
9
  import { keys } from "@libp2p/crypto";
5
- import { Multiaddr as TMultiaddr, multiaddr,protocols } from '@multiformats/multiaddr'; // 多地址库
6
- import { Head } from '../core/head';
7
- import { ThreadID } from '@textile/threads-id';
8
- import { Ed25519PrivKey,Ed25519PubKey } from "../../../common/dc-key/ed25519";
9
- import type { PeerId,PublicKey,PrivateKey, Ed25519PublicKey } from "@libp2p/interface";
10
- import { SymmetricKey, Key as ThreadKey } from '../common/key';
11
- import {validateIDData} from '../lsstoreds/global';
12
- import { ThreadInfo, IThreadLogInfo, SymKey, IThreadInfo, ThreadMuliaddr} from '../core/core';
13
- import {ThreadToken} from '../core/identity';
14
- import {ILogstore} from '../core/logstore';
15
- import {Datastore} from 'interface-datastore';
16
- import {Blocks} from 'helia'
17
- import { DAGCBOR } from '@helia/dag-cbor'; // DAGService
18
- import * as dagCBOR from '@ipld/dag-cbor';
19
- import { IRecord,IThreadRecord } from '../core/record';
20
- import {DCGrpcServer} from './grpcserver';
10
+ import {
11
+ Multiaddr as TMultiaddr,
12
+ multiaddr,
13
+ protocols,
14
+ } from "@multiformats/multiaddr"; // 多地址库
15
+ import { Head } from "../core/head";
16
+ import { ThreadID } from "@textile/threads-id";
17
+ import { Ed25519PrivKey, Ed25519PubKey } from "../../../common/dc-key/ed25519";
18
+ import type {
19
+ PeerId,
20
+ PublicKey,
21
+ PrivateKey,
22
+ Ed25519PublicKey,
23
+ } from "@libp2p/interface";
24
+ import { SymmetricKey, Key as ThreadKey } from "../common/key";
25
+ import { validateIDData } from "../lsstoreds/global";
26
+ import {
27
+ ThreadInfo,
28
+ IThreadLogInfo,
29
+ SymKey,
30
+ IThreadInfo,
31
+ ThreadMuliaddr,
32
+ } from "../core/core";
33
+ import { ThreadToken } from "../core/identity";
34
+ import { ILogstore } from "../core/logstore";
35
+ import { Datastore } from "interface-datastore";
36
+ import { Blocks } from "helia";
37
+ import { DAGCBOR } from "@helia/dag-cbor"; // DAGService
38
+ import * as dagCBOR from "@ipld/dag-cbor";
39
+ import { IRecord, IThreadRecord } from "../core/record";
40
+ import { DCGrpcServer } from "./grpcserver";
21
41
  import { Libp2p } from "@libp2p/interface";
22
- import {dc_protocol} from '../../../common/define';
23
- import {EventFromNode,Event} from '../cbor/event';
24
- import {Node} from '../cbor/node';
25
- import {IPLDNode} from '../core/core';
26
- import {ThreadRecord,TimestampedRecord,PeerRecords,netPullingLimit} from './define';
27
- import {CID} from 'multiformats/cid';
28
- import {getHeadUndef} from '../core/head';
29
- import {GetRecord,CreateRecord, logToProto, logFromProto, CreateRecordConfig} from '../cbor/record';
30
- import {IThreadEvent} from '../core/event';
31
- import {DBGrpcClient} from './grpcClient';
32
- import { DBClient } from '../dbclient';
42
+ import { dc_protocol } from "../../../common/define";
43
+ import { EventFromNode, Event } from "../cbor/event";
44
+ import { Node } from "../cbor/node";
45
+ import { IPLDNode } from "../core/core";
46
+ import {
47
+ ThreadRecord,
48
+ TimestampedRecord,
49
+ PeerRecords,
50
+ netPullingLimit,
51
+ } from "./define";
52
+ import { CID } from "multiformats/cid";
53
+ import { getHeadUndef } from "../core/head";
54
+ import {
55
+ GetRecord,
56
+ CreateRecord,
57
+ logToProto,
58
+ logFromProto,
59
+ CreateRecordConfig,
60
+ } from "../cbor/record";
61
+ import { IThreadEvent } from "../core/event";
62
+ import { DBGrpcClient } from "./grpcClient";
63
+ import { DBClient } from "../dbclient";
33
64
  import { Client } from "../../../common/dcapi";
34
- import {App,Connector,Net,PubKey,Token} from "../core/app";
35
- import {ChainUtil} from '../../../common/chain';
36
- import { DcUtil } from '../../../common/dcutil';
37
- import {CreateEvent} from '../cbor/event';
38
- import {Errors} from '../core/db';
39
- import {net as net_pb} from "../pb/net_pb";
40
- import {PermanentAddrTTL} from "../common/logstore";
41
- import {PeerStatus} from '../../../common/types/types';
42
- import {
43
- PeerIDConverter,
44
- MultiaddrConverter,
45
- CidConverter,
46
- ThreadIDConverter,
47
- KeyConverter,
65
+ import { App, Connector, Net, PubKey, Token } from "../core/app";
66
+ import { ChainUtil } from "../../../common/chain";
67
+ import { DcUtil } from "../../../common/dcutil";
68
+ import { CreateEvent } from "../cbor/event";
69
+ import { Errors } from "../core/db";
70
+ import { net as net_pb } from "../pb/net_pb";
71
+ import { PermanentAddrTTL } from "../common/logstore";
72
+ import { PeerStatus } from "../../../common/types/types";
73
+ import {
74
+ PeerIDConverter,
75
+ MultiaddrConverter,
76
+ CidConverter,
77
+ ThreadIDConverter,
78
+ KeyConverter,
48
79
  ProtoKeyConverter,
49
- json
50
- } from '../pb/proto-custom-types'
80
+ json,
81
+ } from "../pb/proto-custom-types";
51
82
  import * as buffer from "buffer/";
52
- import { AsyncMutex } from '../common/AsyncMutex';
53
- import { DCContext } from '../../../interfaces';
54
- import { p } from 'lib/common/blowfish/const';
83
+ import { AsyncMutex } from "../common/AsyncMutex";
84
+ import { DCContext } from "../../../interfaces";
85
+ import { p } from "lib/common/blowfish/const";
55
86
  const { Buffer } = buffer;
56
87
 
57
-
58
-
59
-
60
-
61
88
  /**
62
89
  * Creates a new ThreadRecord
63
90
  */
@@ -65,26 +92,34 @@ function newRecord(r: IRecord, id: ThreadID, lid: PeerId): IThreadRecord {
65
92
  return new ThreadRecord(r, id, lid);
66
93
  }
67
94
 
68
-
69
- // 定义 Network 类
95
+ // 定义 Network 类
70
96
  export class Network implements Net {
71
97
  bstore: Blocks;
72
- private logstore: ILogstore;
98
+ private logstore: ILogstore;
73
99
  private dcChain: ChainUtil;
74
100
  private dc: DcUtil;
75
- private dagService: DAGCBOR;
76
- private hostID: string;
77
- private context: DCContext;
78
- private server:DCGrpcServer;
101
+ private dagService: DAGCBOR;
102
+ private hostID: string;
103
+ private context: DCContext;
104
+ private server: DCGrpcServer;
79
105
  private libp2p: Libp2p;
80
106
  private connectors: Record<string, Connector>;
81
107
  private cachePeers: Record<string, TMultiaddr> = {};
82
108
  private threadMutexes: Record<string, AsyncMutex> = {};
83
109
 
84
- constructor(dcUtil : DcUtil,dcChain: ChainUtil,libp2p:Libp2p,grpcServer:DCGrpcServer,logstore: ILogstore,bstore: Blocks,dagService: DAGCBOR, context: DCContext) {
85
- this.logstore = logstore;
86
- this.hostID = libp2p.peerId.toString();
87
- this.context = context;
110
+ constructor(
111
+ dcUtil: DcUtil,
112
+ dcChain: ChainUtil,
113
+ libp2p: Libp2p,
114
+ grpcServer: DCGrpcServer,
115
+ logstore: ILogstore,
116
+ bstore: Blocks,
117
+ dagService: DAGCBOR,
118
+ context: DCContext
119
+ ) {
120
+ this.logstore = logstore;
121
+ this.hostID = libp2p.peerId.toString();
122
+ this.context = context;
88
123
  this.bstore = bstore;
89
124
  this.dagService = dagService;
90
125
  this.libp2p = libp2p;
@@ -92,74 +127,88 @@ export class Network implements Net {
92
127
  this.connectors = {};
93
128
  this.dcChain = dcChain;
94
129
  this.dc = dcUtil;
95
- }
130
+ }
96
131
 
97
- // 签名,后续应该改成发送到钱包iframe中签名,发送数据包含payload和用户公钥
98
- sign = async (payload: Uint8Array): Promise<Uint8Array> => {
132
+ // 签名,后续应该改成发送到钱包iframe中签名,发送数据包含payload和用户公钥
133
+ sign = async (payload: Uint8Array): Promise<Uint8Array> => {
99
134
  if (!this.context) {
100
135
  throw new Error("privKey is null");
101
136
  }
102
137
  const signature = this.context.sign(payload);
103
138
  return signature;
104
- }
139
+ };
105
140
 
106
- async getClient(peerId: PeerId):Promise<[Client | null, Error | null]>{
107
- try{
141
+ async getClient(peerId: PeerId): Promise<[Client | null, Error | null]> {
142
+ try {
108
143
  let cachedFlag = true;
109
- const cacheAddr = this.cachePeers[peerId.toString()];
144
+ const cacheAddr = this.cachePeers[peerId.toString()];
110
145
  let peerAddr: TMultiaddr | null = cacheAddr || null;
111
- let peerStatus: PeerStatus | null = null;
146
+ let peerStatus: PeerStatus | null = PeerStatus.PeerStatusOnline;
112
147
  if (!cacheAddr) {
113
148
  cachedFlag = false;
114
- [peerAddr, peerStatus] = await this.dcChain.getDcNodeWebrtcDirectAddr(peerId.toString());
149
+ [peerAddr, peerStatus] = await this.dcChain.getDcNodeWebrtcDirectAddr(
150
+ peerId.toString()
151
+ );
115
152
  }
116
-
153
+
117
154
  if (!peerAddr) {
118
155
  throw new Error("peerAddr is null");
119
- }
156
+ }
120
157
  if (peerStatus !== PeerStatus.PeerStatusOnline) {
121
158
  throw new Error("peerStatus is not online");
122
159
  }
123
- if (!this.context.publicKey){
160
+ if (!this.context.publicKey) {
124
161
  throw new Error("publicKey is null");
125
162
  }
126
163
 
127
164
  const addr = multiaddr(peerAddr);
128
- const client = new Client(this.libp2p,this.bstore,addr, dc_protocol);
165
+ const client = new Client(this.libp2p, this.bstore, addr, dc_protocol);
129
166
  //获取token
130
- const token = await client.GetToken(this.context.appInfo.appId||"", this.context.publicKey.string(),(payload: Uint8Array):Promise<Uint8Array> => {
131
- return this.sign(payload);
132
- });
167
+ const token = await client.GetToken(
168
+ this.context.appInfo.appId || "",
169
+ this.context.publicKey.string(),
170
+ (payload: Uint8Array): Promise<Uint8Array> => {
171
+ return this.sign(payload);
172
+ }
173
+ );
133
174
  if (!token) {
134
175
  if (cachedFlag) {
135
- let _:PeerStatus| null = null;
136
- [peerAddr,_] = await this.dcChain.getDcNodeWebrtcDirectAddr(peerId.toString());
176
+ let _: PeerStatus | null = null;
177
+ [peerAddr, _] = await this.dcChain.getDcNodeWebrtcDirectAddr(
178
+ peerId.toString()
179
+ );
137
180
  delete this.cachePeers[peerId.toString()];
138
181
  if (!peerAddr) {
139
182
  throw new Error("peerAddr is null");
140
- }
183
+ }
141
184
  const addr = multiaddr(peerAddr);
142
- const client = new Client(this.libp2p,this.bstore,addr, dc_protocol);
143
- const token = await client.GetToken(this.context.appInfo.appId||"",this.context.publicKey.string(),(payload: Uint8Array) => {
144
- return this.sign(payload);
145
- });
185
+ const client = new Client(
186
+ this.libp2p,
187
+ this.bstore,
188
+ addr,
189
+ dc_protocol
190
+ );
191
+ const token = await client.GetToken(
192
+ this.context.appInfo.appId || "",
193
+ this.context.publicKey.string(),
194
+ (payload: Uint8Array) => {
195
+ return this.sign(payload);
196
+ }
197
+ );
146
198
  if (token) {
147
199
  this.cachePeers[peerId.toString()] = peerAddr;
148
200
  return [client, null];
149
201
  }
150
202
  }
151
203
  throw new Error("get token is null");
152
-
153
204
  }
154
205
  this.cachePeers[peerId.toString()] = peerAddr;
155
206
  return [client, null];
156
- }catch(err){
157
- return [null,err as Error];
207
+ } catch (err) {
208
+ return [null, err as Error];
158
209
  }
159
210
  }
160
211
 
161
-
162
-
163
212
  getMutexForThread(threadId: string): AsyncMutex {
164
213
  if (!this.threadMutexes[threadId]) {
165
214
  this.threadMutexes[threadId] = new AsyncMutex();
@@ -167,328 +216,358 @@ export class Network implements Net {
167
216
  return this.threadMutexes[threadId];
168
217
  }
169
218
 
170
- async getPeers(id: ThreadID):Promise<PeerId[]| undefined>{
171
- const peers = await this.dcChain.getObjNodes(id.toString());
172
- if (!peers) {
173
- return undefined;
174
- }
175
- const peerIds: PeerId[] = [];
176
- for (const peer of peers) {
177
- const peerStr = Buffer.from(peer.slice(2), "hex").toString(
178
- "utf8"
179
- );
180
- const peerId = peerIdFromString(peerStr);
181
- if (!peerId) {
182
- continue
219
+ async getPeers(id: ThreadID): Promise<PeerId[] | undefined> {
220
+ const peers = await this.dcChain.getObjNodes(id.toString());
221
+ if (!peers) {
222
+ return undefined;
223
+ }
224
+ const peerIds: PeerId[] = [];
225
+ for (const peer of peers) {
226
+ const peerStr = Buffer.from(peer.slice(2), "hex").toString("utf8");
227
+ const peerId = peerIdFromString(peerStr);
228
+ if (!peerId) {
229
+ continue;
183
230
  }
184
- peerIds.push(peerId);
185
- }
186
- return peerIds;
231
+ peerIds.push(peerId);
232
+ }
233
+ return peerIds;
187
234
  }
188
235
 
189
- /**
190
- * 创建threaddb
191
- */
192
- async createThread(id: ThreadID, options: { token: ThreadToken; logKey?: Ed25519PrivKey|Ed25519PubKey, threadKey?: ThreadKey }): Promise<ThreadInfo> {
193
- if (!this.context.publicKey) {
236
+ /**
237
+ * 创建threaddb
238
+ */
239
+ async createThread(
240
+ id: ThreadID,
241
+ options: {
242
+ token: ThreadToken;
243
+ logKey?: Ed25519PrivKey | Ed25519PubKey;
244
+ threadKey?: ThreadKey;
245
+ }
246
+ ): Promise<ThreadInfo> {
247
+ if (!this.context.publicKey) {
194
248
  throw new Error("Identity creation failed.");
195
- }
196
- const identity = this.context.publicKey;
197
-
249
+ }
250
+ const identity = this.context.publicKey;
198
251
 
199
- await this.ensureUniqueLog(id, options.logKey, identity);
200
- const threadKey = options.threadKey || this.generateRandomKey();
252
+ await this.ensureUniqueLog(id, options.logKey, identity);
253
+ const threadKey = options.threadKey || this.generateRandomKey();
201
254
 
202
- const threadInfo = new ThreadInfo(id, [], [], threadKey );
255
+ const threadInfo = new ThreadInfo(id, [], [], threadKey);
203
256
 
204
- await this.logstore.addThread(threadInfo);
205
- const logInfo = await this.createLog(id, options.logKey, identity);
257
+ await this.logstore.addThread(threadInfo);
258
+ const logInfo = await this.createLog(id, options.logKey, identity);
206
259
  threadInfo.logs.push(logInfo);
207
- return threadInfo;
208
- }
260
+ return threadInfo;
261
+ }
209
262
 
263
+ /**
264
+ * 从多地址添加threaddb
265
+ *
266
+ * @param addr 包含threaddb ID的多地址
267
+ * @param options threaddb 选项
268
+ * @returns 带有地址的threaddb 信息
269
+ */
270
+ async addThread(
271
+ addr: ThreadMuliaddr,
272
+ options: {
273
+ token?: ThreadToken | undefined;
274
+ logKey?: Ed25519PrivKey | Ed25519PubKey | undefined;
275
+ threadKey?: ThreadKey | undefined;
276
+ } = {}
277
+ ): Promise<ThreadInfo> {
278
+ try {
279
+ // 从多地址提取threaddb ID
280
+ const idStr = addr.id.toString();
281
+ if (!idStr) {
282
+ throw new Error("Invalid thread address");
283
+ }
284
+ const id = ThreadID.fromString(idStr);
285
+ // 验证身份
286
+ let identity = await this.validate(id, options.token);
287
+ if (identity) {
288
+ console.debug(`Adding thread with identity: ${identity.toString()}`);
289
+ } else {
290
+ identity = this.context.publicKey;
291
+ }
210
292
 
211
- /**
212
- * 从多地址添加threaddb
213
- *
214
- * @param addr 包含threaddb ID的多地址
215
- * @param options threaddb 选项
216
- * @returns 带有地址的threaddb 信息
217
- */
218
- async addThread(
219
- addr: ThreadMuliaddr,
220
- options: { token?: ThreadToken | undefined; logKey?: Ed25519PrivKey | Ed25519PubKey| undefined; threadKey?: ThreadKey| undefined } = {}
221
- ): Promise<ThreadInfo> {
222
- try {
223
- // 从多地址提取threaddb ID
224
- const idStr = addr.id.toString();
225
- if (!idStr) {
226
- throw new Error("Invalid thread address");
227
- }
228
- const id = ThreadID.fromString(idStr);
229
- // 验证身份
230
- let identity = await this.validate(id, options.token);
231
- if (identity) {
232
- console.debug(`Adding thread with identity: ${identity.toString()}`);
233
- } else {
234
- identity = this.context.publicKey;
235
- }
293
+ // 确保日志唯一性
294
+ await this.ensureUniqueLog(id, options.logKey, identity);
236
295
 
237
- // 确保日志唯一性
238
- await this.ensureUniqueLog(id, options.logKey, identity);
296
+ // 分离threaddb 组件以获取对等点地址
297
+ // const threadComp = `/thread/${id.toString()}`;
298
+ // const peerAddr = multiaddr(addr.toString().split(threadComp)[0]);
299
+ const peerAddr = addr.addr;
300
+ // 获取对等点信息
301
+ const peerId = peerAddr.getPeerId();
302
+ if (!peerId) {
303
+ throw new Error("Invalid peer address");
304
+ }
239
305
 
240
- // 分离threaddb 组件以获取对等点地址
241
- // const threadComp = `/thread/${id.toString()}`;
242
- // const peerAddr = multiaddr(addr.toString().split(threadComp)[0]);
243
- const peerAddr = addr.addr
244
- // 获取对等点信息
245
- const peerId = peerAddr.getPeerId();
246
- if (!peerId) {
247
- throw new Error("Invalid peer address");
248
- }
249
-
250
- const pid = peerIdFromString(peerId);
251
- const addFromSelf = pid.toString() === this.hostID;
306
+ const pid = peerIdFromString(peerId);
307
+ const addFromSelf = pid.toString() === this.hostID;
252
308
 
253
- // 如果我们从自己添加,检查threaddb 是否存在
254
- if (addFromSelf) {
255
- try {
256
- await this.logstore.getThread(id);
257
- } catch (err: any) {
258
- if (err.message === "Thread not found") {
259
- throw new Error(`Cannot retrieve thread from self: ${err.message}`);
309
+ // 如果我们从自己添加,检查threaddb 是否存在
310
+ if (addFromSelf) {
311
+ try {
312
+ await this.logstore.getThread(id);
313
+ } catch (err: any) {
314
+ if (err.message === "Thread not found") {
315
+ throw new Error(`Cannot retrieve thread from self: ${err.message}`);
316
+ }
317
+ throw err;
260
318
  }
261
- throw err;
262
319
  }
263
- }
264
-
265
- // 添加threaddb 到存储
266
- const threadInfo = new ThreadInfo(id, [], [], options.threadKey);
267
- await this.logstore.addThread(threadInfo);
268
320
 
269
- // 如果可以读取或有日志密钥,则创建日志
270
- if ((options.threadKey && options.threadKey.canRead()) || options.logKey) {
271
- const logInfo = await this.createLog(id, options.logKey, identity);
272
- threadInfo.logs.push(logInfo);
273
- }
274
- // 如果不是从自己添加,则连接并获取日志
275
- if (!addFromSelf) {
276
- // 从对等点更新日志
277
- await this.updateLogsFromPeer(id, pid);
278
- }
321
+ // 添加threaddb 到存储
322
+ const threadInfo = new ThreadInfo(id, [], [], options.threadKey);
323
+ await this.logstore.addThread(threadInfo);
324
+
325
+ // 如果可以读取或有日志密钥,则创建日志
326
+ if (
327
+ (options.threadKey && options.threadKey.canRead()) ||
328
+ options.logKey
329
+ ) {
330
+ const logInfo = await this.createLog(id, options.logKey, identity);
331
+ threadInfo.logs.push(logInfo);
332
+ }
333
+ // 如果不是从自己添加,则连接并获取日志
334
+ if (!addFromSelf) {
335
+ // 从对等点更新日志
336
+ await this.updateLogsFromPeer(id, pid);
337
+ }
279
338
 
280
- // 返回带有地址的threaddb 信息
281
- return this.getThreadWithAddrs(id);
282
- } catch (err) {
283
- throw new Error(`Failed to add thread: ${err instanceof Error ? err.message : String(err)}`);
339
+ // 返回带有地址的threaddb 信息
340
+ return this.getThreadWithAddrs(id);
341
+ } catch (err) {
342
+ throw new Error(
343
+ `Failed to add thread: ${
344
+ err instanceof Error ? err.message : String(err)
345
+ }`
346
+ );
347
+ }
284
348
  }
285
- }
286
-
287
349
 
350
+ /**
351
+ * 获取threaddb 信息
352
+ * 返回包含地址的threaddb 信息对象
353
+ *
354
+ * @param id threaddb ID
355
+ * @param options 选项,可以包含令牌等
356
+ * @returns 包含地址的threaddb 信息
357
+ * @throws 如果threaddb 验证失败或threaddb 不存在
358
+ */
359
+ async getThread(
360
+ id: ThreadID,
361
+ options: { token?: ThreadToken } = {}
362
+ ): Promise<ThreadInfo> {
363
+ try {
364
+ // 验证threaddb ID和令牌
365
+ await this.validate(id, options.token);
288
366
 
289
- /**
290
- * 获取threaddb 信息
291
- * 返回包含地址的threaddb 信息对象
292
- *
293
- * @param id threaddb ID
294
- * @param options 选项,可以包含令牌等
295
- * @returns 包含地址的threaddb 信息
296
- * @throws 如果threaddb 验证失败或threaddb 不存在
297
- */
298
- async getThread(
299
- id: ThreadID,
300
- options: { token?: ThreadToken } = {}
301
- ): Promise<ThreadInfo> {
302
- try {
303
- // 验证threaddb ID和令牌
304
- await this.validate(id, options.token);
305
-
306
- // 获取带有地址的threaddb 信息
307
- return this.getThreadWithAddrs(id);
308
- } catch (err) {
309
- throw new Error(`Error getting thread ${id.toString()}: ${err instanceof Error ? err.message : String(err)}`);
367
+ // 获取带有地址的threaddb 信息
368
+ return this.getThreadWithAddrs(id);
369
+ } catch (err) {
370
+ throw new Error(
371
+ `Error getting thread ${id.toString()}: ${
372
+ err instanceof Error ? err.message : String(err)
373
+ }`
374
+ );
375
+ }
310
376
  }
311
- }
312
-
313
377
 
378
+ /**
379
+ * 从连接的远程对等点获取线程信息
380
+ *
381
+ * @param id 线程ID
382
+ * @param options 线程选项
383
+ * @returns 线程信息对象
384
+ * @throws 如果连接刷新失败或获取线程信息失败
385
+ */
386
+ async getThreadFromPeer(
387
+ id: ThreadID,
388
+ peerId: PeerId,
389
+ options: { token?: ThreadToken } = {}
390
+ ): Promise<ThreadInfo> {
391
+ try {
392
+ const [client, _] = await this.getClient(peerId);
393
+ if (!client) {
394
+ throw new Error("Failed to get client");
395
+ }
396
+ const dbClient = new DBClient(client, this.dc, this, this.logstore);
314
397
 
315
- /**
316
- * 从连接的远程对等点获取线程信息
317
- *
318
- * @param id 线程ID
319
- * @param options 线程选项
320
- * @returns 线程信息对象
321
- * @throws 如果连接刷新失败或获取线程信息失败
322
- */
323
- async getThreadFromPeer(
324
- id: ThreadID,
325
- peerId: PeerId,
326
- options: { token?: ThreadToken } = {}
327
- ): Promise<ThreadInfo> {
328
- try {
329
- const [client, _] = await this.getClient(peerId);
330
- if (!client) {
331
- throw new Error("Failed to get client");
398
+ // 发送请求
399
+ try {
400
+ const threadInfo = dbClient.getThreadFromPeer(id, peerId, options);
401
+ return threadInfo;
402
+ } catch (err) {
403
+ throw new Error(
404
+ `Error getting thread from peer: ${
405
+ err instanceof Error ? err.message : String(err)
406
+ }`
407
+ );
408
+ }
409
+ } catch (err) {
410
+ throw new Error(
411
+ `Error getting thread from peer: ${
412
+ err instanceof Error ? err.message : String(err)
413
+ }`
414
+ );
332
415
  }
333
- const dbClient = new DBClient(client,this.dc,this,this.logstore);
334
-
335
- // 发送请求
416
+ }
417
+
418
+ /**
419
+ * 删除线程
420
+ *
421
+ * @param id 线程ID
422
+ * @param options 线程选项
423
+ * @returns 无返回值
424
+ * @throws 如果验证失败或线程正在被使用
425
+ */
426
+ async deleteThread(
427
+ id: ThreadID,
428
+ options: { token?: ThreadToken; apiToken?: Token } = {}
429
+ ): Promise<void> {
336
430
  try {
337
- const threadInfo = dbClient.getThreadFromPeer(id,peerId,options);
338
- return threadInfo;
431
+ // 验证线程ID和令牌
432
+ await this.validate(id, options.token);
433
+
434
+ // 检查线程是否被应用使用
435
+ const [_, ok] = this.getConnectorProtected(id, options.apiToken);
436
+ if (!ok) {
437
+ throw new Error("Cannot delete thread: thread in use");
438
+ }
439
+ console.debug(`Deleting thread ${id.toString()}...`);
440
+ const mutex = this.getMutexForThread(id.toString());
441
+ await mutex.acquire();
442
+ try {
443
+ // 执行删除操作
444
+ await this.logstore.deleteThread(id);
445
+ delete this.connectors[id.toString()];
446
+ } finally {
447
+ mutex.release();
448
+ }
339
449
  } catch (err) {
340
- throw new Error(`Error getting thread from peer: ${err instanceof Error ? err.message : String(err)}`);
450
+ throw new Error(
451
+ `Failed to delete thread: ${
452
+ err instanceof Error ? err.message : String(err)
453
+ }`
454
+ );
341
455
  }
342
- } catch (err) {
343
- throw new Error(`Error getting thread from peer: ${err instanceof Error ? err.message : String(err)}`);
344
456
  }
345
- }
346
457
 
347
-
348
- /**
349
- * 删除线程
350
- *
351
- * @param id 线程ID
352
- * @param options 线程选项
353
- * @returns 无返回值
354
- * @throws 如果验证失败或线程正在被使用
355
- */
356
- async deleteThread(
357
- id: ThreadID,
358
- options: { token?: ThreadToken, apiToken?: Token } = {}
359
- ): Promise<void> {
360
- try {
361
- // 验证线程ID和令牌
362
- await this.validate(id, options.token)
363
-
364
- // 检查线程是否被应用使用
365
- const [_, ok] = this.getConnectorProtected(id, options.apiToken);
366
- if (!ok) {
367
- throw new Error("Cannot delete thread: thread in use");
458
+ /**
459
+ * 验证threaddb ID 和 Token
460
+ */
461
+ async validate(
462
+ id: ThreadID,
463
+ token?: ThreadToken
464
+ ): Promise<Ed25519PubKey | undefined> {
465
+ if (!validateIDData(id.toBytes())) {
466
+ throw new Error("Invalid thread ID.");
368
467
  }
369
- console.debug(`Deleting thread ${id.toString()}...`);
370
- const mutex = this.getMutexForThread(id.toString());
371
- await mutex.acquire();
372
- try {
373
- // 执行删除操作
374
- await this.logstore.deleteThread(id);
375
- delete this.connectors[id.toString()];
376
- } finally {
377
- mutex.release();
468
+ if (!token) {
469
+ return;
378
470
  }
379
- } catch (err) {
380
- throw new Error(`Failed to delete thread: ${err instanceof Error ? err.message : String(err)}`);
471
+ return token.pubKey();
381
472
  }
382
- }
383
473
 
384
- /**
385
- * 验证threaddb ID 和 Token
386
- */
387
- async validate(
388
- id: ThreadID,
389
- token?: ThreadToken
390
- ): Promise<Ed25519PubKey |undefined> {
391
- if (!validateIDData(id.toBytes())) {
392
- throw new Error("Invalid thread ID.");
393
- }
394
- if (!token) {
395
- return
396
- }
397
- return token.pubKey()
398
- }
474
+ /**
475
+ * 确保日志唯一性
476
+ * 检查给定threaddb 是否已存在具有相同密钥或身份的日志
477
+ */
478
+ async ensureUniqueLog(
479
+ id: ThreadID,
480
+ key?: Ed25519PrivKey | Ed25519PubKey,
481
+ identity?: PublicKey
482
+ ): Promise<void> {
483
+ try {
484
+ const thrd = await this.logstore.getThread(id);
399
485
 
400
-
401
- /**
402
- * 确保日志唯一性
403
- * 检查给定threaddb 是否已存在具有相同密钥或身份的日志
404
- */
405
- async ensureUniqueLog(id: ThreadID, key?: Ed25519PrivKey | Ed25519PubKey, identity?: PublicKey): Promise<void> {
406
- try {
407
- const thrd = await this.logstore.getThread(id);
408
-
409
- // threaddb 存在,继续检查
410
- let lid: PeerId;
411
-
412
- if (key) {
413
- // 根据密钥类型处理
414
- if (key instanceof Ed25519PubKey) {
415
- lid = peerIdFromPublicKey(key);
416
- } else if (key instanceof Ed25519PrivKey) {
417
- lid = peerIdFromPrivateKey(key);
486
+ // threaddb 存在,继续检查
487
+ let lid: PeerId;
488
+
489
+ if (key) {
490
+ // 根据密钥类型处理
491
+ if (key instanceof Ed25519PubKey) {
492
+ lid = peerIdFromPublicKey(key);
493
+ } else if (key instanceof Ed25519PrivKey) {
494
+ lid = peerIdFromPrivateKey(key);
495
+ } else {
496
+ throw new Error("Invalid log key");
497
+ }
418
498
  } else {
419
- throw new Error("Invalid log key");
420
- }
421
- } else {
422
- // 没有提供密钥,检查是否有此身份的日志
423
- if (!identity) {
424
- throw new Error("Either key or identity must be provided");
425
- }
426
-
427
- try {
428
- const lidb = await this.logstore.metadata.getBytes(id, identity.toString());
429
- if (!lidb || lidb.length === 0) {
430
- // 检查是否有旧式"own"(未索引)日志
431
- if (identity.equals(this.context.publicKey)) {
432
- const firstPrivKeyLog = thrd.getFirstPrivKeyLog();
433
- if (firstPrivKeyLog && firstPrivKeyLog.privKey) {
434
- throw new Error("Thread exists");
499
+ // 没有提供密钥,检查是否有此身份的日志
500
+ if (!identity) {
501
+ throw new Error("Either key or identity must be provided");
502
+ }
503
+
504
+ try {
505
+ const lidb = await this.logstore.metadata.getBytes(
506
+ id,
507
+ identity.toString()
508
+ );
509
+ if (!lidb || lidb.length === 0) {
510
+ // 检查是否有旧式"own"(未索引)日志
511
+ if (identity.equals(this.context.publicKey)) {
512
+ const firstPrivKeyLog = thrd.getFirstPrivKeyLog();
513
+ if (firstPrivKeyLog && firstPrivKeyLog.privKey) {
514
+ throw new Error("Thread exists");
515
+ }
435
516
  }
517
+ return;
436
518
  }
519
+ const lidbstr = new TextDecoder().decode(lidb);
520
+ // 从字节转换为PeerId
521
+ lid = peerIdFromString(lidbstr);
522
+ } catch (error) {
523
+ throw error;
524
+ }
525
+ }
526
+
527
+ try {
528
+ const lginfo = await this.logstore.getLog(id, lid);
529
+ if (lginfo) {
530
+ // 如果到达这里,说明日志存在
531
+ throw new Error("Log exists");
532
+ }
533
+ } catch (error: any) {
534
+ if (error.message === Errors.ErrLogNotFound.message) {
437
535
  return;
438
536
  }
439
- const lidbstr = new TextDecoder().decode(lidb);
440
- // 从字节转换为PeerId
441
- lid = peerIdFromString(lidbstr);
442
- } catch (error) {
443
537
  throw error;
444
538
  }
445
- }
446
-
447
- try {
448
- const lginfo = await this.logstore.getLog(id, lid);
449
- if (lginfo) {
450
- // 如果到达这里,说明日志存在
451
- throw new Error("Log exists");
452
- }
453
539
  } catch (error: any) {
454
- if (error.message === Errors.ErrLogNotFound.message) {
540
+ if (error.message === Errors.ErrThreadNotFound.message) {
455
541
  return;
456
542
  }
457
543
  throw error;
458
544
  }
459
- } catch (error: any) {
460
- if (error.message === Errors.ErrThreadNotFound.message) {
461
- return;
462
- }
463
- throw error;
464
545
  }
465
- }
466
-
467
-
468
546
 
469
- /**
470
- * Get thread information with addresses
471
- */
472
- async getThreadWithAddrs(id: ThreadID): Promise<ThreadInfo> {
473
- try {
474
- const tinfo = await this.logstore.getThread(id);
475
- // Get host addresses
476
- const hostAddrs = this.libp2p.getMultiaddrs();
477
- const resultAddrs: ThreadMuliaddr[] = [];
478
-
479
- // Encapsulate each address with peer and thread components
480
- for (const addr of hostAddrs) {
481
- const withPeerId = addr.encapsulate(`/p2p/${this.libp2p.peerId.toString()}`);
482
- const threadMultiaddr = new ThreadMuliaddr(withPeerId, tinfo.id);
483
- resultAddrs.push(threadMultiaddr);
484
- }
485
- tinfo.addrs = resultAddrs;
486
- return tinfo;
487
- } catch (err) {
488
- throw err;
547
+ /**
548
+ * Get thread information with addresses
549
+ */
550
+ async getThreadWithAddrs(id: ThreadID): Promise<ThreadInfo> {
551
+ try {
552
+ const tinfo = await this.logstore.getThread(id);
553
+ // Get host addresses
554
+ const hostAddrs = this.libp2p.getMultiaddrs();
555
+ const resultAddrs: ThreadMuliaddr[] = [];
556
+
557
+ // Encapsulate each address with peer and thread components
558
+ for (const addr of hostAddrs) {
559
+ const withPeerId = addr.encapsulate(
560
+ `/p2p/${this.libp2p.peerId.toString()}`
561
+ );
562
+ const threadMultiaddr = new ThreadMuliaddr(withPeerId, tinfo.id);
563
+ resultAddrs.push(threadMultiaddr);
489
564
  }
565
+ tinfo.addrs = resultAddrs;
566
+ return tinfo;
567
+ } catch (err) {
568
+ throw err;
490
569
  }
491
-
570
+ }
492
571
 
493
572
  /**
494
573
  * Pull thread updates from peers
@@ -503,40 +582,94 @@ async ensureUniqueLog(id: ThreadID, key?: Ed25519PrivKey | Ed25519PubKey, identi
503
582
  } finally {
504
583
  mutex.release();
505
584
  }
506
-
507
- const [connector, appConnected] = this.getConnector(id);
508
-
585
+
586
+ const [connector, appConnected] = this.getConnector(id);
587
+
509
588
  // Handle records
510
589
  const tRecords: TimestampedRecord[] = [];
511
-
512
- for (const [lidStr, rec ] of Object.entries(recs)) {
590
+
591
+ for (const [lidStr, rec] of Object.entries(recs)) {
513
592
  const lid = peerIdFromString(lidStr);
514
593
  const rs = rec as PeerRecords;
515
594
  if (appConnected) {
516
- let indexCounter = rs.counter - rs.records.length + 1;
517
-
595
+ // 使用并发控制,但不分批 - 避免慢任务拖累整批
596
+ const maxConcurrency = 10;
597
+ let activePromises: Promise<void>[] = [];
598
+ let processedCount = 0;
599
+
600
+ const processRecord = async (r: any, index: number): Promise<void> => {
601
+ try {
602
+ const block = await r.getBlock(this.bstore);
603
+ const event =
604
+ block instanceof Event
605
+ ? block
606
+ : await EventFromNode(block as Node);
607
+
608
+ const header = await event.getHeader(this.bstore);
609
+ const body = await event.getBody(this.bstore);
610
+
611
+
612
+ // Store internal blocks locally
613
+ await this.addMany([event, header, body]);
614
+
615
+ } catch (err) {
616
+ console.error(`预加载记录 ${index + 1} 失败:`, err);
617
+ // 继续处理其他记录
618
+ }
619
+ };
620
+
621
+ // 处理所有记录,但控制并发数
518
622
  for (let i = 0; i < rs.records.length; i++) {
519
623
  const r = rs.records[i]!;
624
+ // 创建处理Promise
625
+ const promise = processRecord(r, i).finally(() => {
626
+ processedCount++;
627
+ // 从活跃Promise列表中移除
628
+ const index = activePromises.indexOf(promise);
629
+ if (index > -1) {
630
+ activePromises.splice(index, 1);
631
+ }
632
+ });
520
633
 
521
- // Get blocks for validation
522
- const block = await r.getBlock( this.bstore);
523
- const event = block instanceof Event ? block : await EventFromNode(block as Node);
634
+ activePromises.push(promise);
524
635
 
636
+ // 当达到最大并发数时,等待最快完成的一个
637
+ if (activePromises.length >= maxConcurrency) {
638
+ await Promise.race(activePromises);
639
+ }
640
+ }
641
+ // 等待所有剩余的Promise完成
642
+ await Promise.all(activePromises);
643
+
644
+ console.log(`所有 ${rs.records.length} 条记录预加载完成`);
645
+ //开始正式处理,前面数据已经拉取到本地,这时处理速度很快
646
+ let indexCounter = rs.counter - rs.records.length + 1;
647
+
648
+ for (let i = 0; i < rs.records.length; i++) {
649
+ const r = rs.records[i]!;
650
+
651
+ // Get blocks for validation
652
+ const block = await r.getBlock(this.bstore);
653
+ const event =
654
+ block instanceof Event
655
+ ? block
656
+ : await EventFromNode(block as Node);
657
+
525
658
  const header = await event.getHeader(this.bstore);
526
659
  const body = await event.getBody(this.bstore);
527
-
660
+
528
661
  // Store internal blocks locally
529
662
  await this.addMany([event, header, body]);
530
-
663
+
531
664
  const tRecord = newRecord(r, id, lid);
532
665
  const counter = indexCounter + i;
533
- const createtime = await connector!.getNetRecordCreateTime( tRecord);
534
-
666
+ const createtime = await connector!.getNetRecordCreateTime(tRecord);
667
+
535
668
  tRecords.push({
536
669
  record: r,
537
670
  counter: counter,
538
671
  createtime: createtime,
539
- logid: lid
672
+ logid: lid,
540
673
  });
541
674
  }
542
675
  } else {
@@ -544,26 +677,27 @@ async ensureUniqueLog(id: ThreadID, key?: Ed25519PrivKey | Ed25519PubKey, identi
544
677
  }
545
678
  }
546
679
 
547
- tRecords.sort((a, b) => {
680
+ tRecords.sort((a, b) => {
548
681
  if (a.createtime < b.createtime) return -1;
549
682
  if (a.createtime > b.createtime) return 1;
550
683
  return 0;
551
- });
684
+ });
552
685
  let i = 0;
686
+
553
687
  // Process each record in order
554
688
  for (const r of tRecords) {
555
- await this.putRecords( id, r.logid, [r.record], r.counter);
689
+ await this.putRecords(id, r.logid, [r.record], r.counter);
556
690
  }
691
+
557
692
  } catch (err) {
558
693
  throw err;
559
- }
694
+ }
560
695
  }
561
696
 
562
-
563
- async addMany( nodes: IPLDNode[]): Promise<void> {
697
+ async addMany(nodes: IPLDNode[]): Promise<void> {
564
698
  for (const node of nodes) {
565
699
  await this.bstore.put(node.cid(), node.data());
566
- }
700
+ }
567
701
  }
568
702
  /**
569
703
  * Pull thread records from peers
@@ -572,110 +706,119 @@ async ensureUniqueLog(id: ThreadID, key?: Ed25519PrivKey | Ed25519PubKey, identi
572
706
  try {
573
707
  let [offsets, peers] = await this.threadOffsets(tid);
574
708
  try {
575
- const extPeers = await this.getPeers( tid);
709
+ const extPeers = await this.getPeers(tid);
576
710
  if (extPeers && extPeers.length > 0) {
577
711
  peers = extPeers;
578
712
  }
579
713
  } catch (err) {
580
- console.error(`Error getting peers for thread ${tid}: ${err instanceof Error ? err.message : String(err)}`);
714
+ console.error(
715
+ `Error getting peers for thread ${tid}: ${
716
+ err instanceof Error ? err.message : String(err)
717
+ }`
718
+ );
581
719
  // Ignore getPeers errors
582
720
  }
583
721
  const pulledRecs: Record<string, PeerRecords> = {};
584
-
722
+
585
723
  // Continue pulling until no more records
586
724
  while (true) {
587
- const recs = await this.getRecords(peers, tid, offsets, netPullingLimit);
725
+ const recs = await this.getRecords(
726
+ peers,
727
+ tid,
728
+ offsets,
729
+ netPullingLimit
730
+ );
588
731
  let continueFlag = false;
589
-
590
732
  for (const [lidStr, rs] of Object.entries(recs)) {
591
733
  if (rs.records.length > 0) {
592
734
  const existing = pulledRecs[lidStr] || { records: [], counter: 0 };
593
735
  const records = [...existing.records, ...rs.records];
594
-
736
+
595
737
  pulledRecs[lidStr] = {
596
738
  records: records,
597
- counter: rs.counter
739
+ counter: rs.counter,
598
740
  };
599
-
741
+
600
742
  const lastRecord = rs.records[rs.records.length - 1]!;
601
743
  offsets[lidStr] = {
602
744
  id: lastRecord.cid(),
603
- counter: rs.counter
745
+ counter: rs.counter,
604
746
  };
605
-
747
+
606
748
  if (rs.records.length >= netPullingLimit) {
607
749
  continueFlag = true;
608
750
  }
609
751
  }
610
752
  }
611
-
753
+
612
754
  if (!continueFlag) {
613
755
  break;
614
756
  }
615
757
  }
616
-
617
758
  return pulledRecs;
618
759
  } catch (err) {
619
760
  throw err;
620
761
  }
621
762
  }
622
763
 
623
-
624
- /**
625
- * 创建日志,分配 privKey 和 pubKey
626
- */
627
- async createLog(
628
- id: ThreadID,
629
- key?: Ed25519PrivKey|Ed25519PubKey,
630
- identity?: PublicKey
631
- ): Promise<IThreadLogInfo> {
632
- let privKey: PrivateKey | undefined;
633
- let pubKey: PublicKey;
634
- let peerId: PeerId;
635
- if (!key) {
636
- const keyPair = await keys.generateKeyPair('Ed25519');
637
- privKey = new Ed25519PrivKey(keyPair.raw)
638
- pubKey = keyPair.publicKey;
639
- peerId = peerIdFromPrivateKey(privKey);
640
- } else {
641
- if (key instanceof Ed25519PrivKey) {
764
+ /**
765
+ * 创建日志,分配 privKey 和 pubKey
766
+ */
767
+ async createLog(
768
+ id: ThreadID,
769
+ key?: Ed25519PrivKey | Ed25519PubKey,
770
+ identity?: PublicKey
771
+ ): Promise<IThreadLogInfo> {
772
+ let privKey: PrivateKey | undefined;
773
+ let pubKey: PublicKey;
774
+ let peerId: PeerId;
775
+ if (!key) {
776
+ const keyPair = await keys.generateKeyPair("Ed25519");
777
+ privKey = new Ed25519PrivKey(keyPair.raw);
778
+ pubKey = keyPair.publicKey;
779
+ peerId = peerIdFromPrivateKey(privKey);
780
+ } else {
781
+ if (key instanceof Ed25519PrivKey) {
642
782
  privKey = key;
643
- pubKey = key.publicKey;
644
- peerId = peerIdFromPrivateKey(key);
645
- }else if(key instanceof Ed25519PubKey){
783
+ pubKey = key.publicKey;
784
+ peerId = peerIdFromPrivateKey(key);
785
+ } else if (key instanceof Ed25519PubKey) {
646
786
  pubKey = key;
647
787
  peerId = peerIdFromPublicKey(key);
648
- }else{
649
- throw new Error("Invalid key type.");
650
- }
651
- }
652
- const addr = multiaddr(`/p2p/${this.hostID}`); // 基于 hostID 生成地址
653
- const head : Head = {
654
- counter: 0,
788
+ } else {
789
+ throw new Error("Invalid key type.");
790
+ }
655
791
  }
656
- const logInfo: IThreadLogInfo = {
657
- privKey,
658
- pubKey,
659
- id: peerId,
660
- addrs: [addr],
661
- managed: true,
662
- head: head
663
- } as IThreadLogInfo;
664
-
665
- // 将日志添加到threaddb存储
666
- await this.logstore.addLog(id, logInfo);
667
- const logIDBytes = new TextEncoder().encode(peerId.toString());
668
- await this.logstore.metadata.putBytes(id, identity?.toString() || "", logIDBytes);
669
- return logInfo;
670
- }
671
-
672
- /**
673
- * 生成随机threaddb 密钥
674
- */
675
- generateRandomKey(): ThreadKey {
676
- return new ThreadKey(SymmetricKey.new(),SymmetricKey.new());
677
- }
792
+ const addr = multiaddr(`/p2p/${this.hostID}`); // 基于 hostID 生成地址
793
+ const head: Head = {
794
+ counter: 0,
795
+ };
796
+ const logInfo: IThreadLogInfo = {
797
+ privKey,
798
+ pubKey,
799
+ id: peerId,
800
+ addrs: [addr],
801
+ managed: true,
802
+ head: head,
803
+ } as IThreadLogInfo;
804
+
805
+ // 将日志添加到threaddb存储
806
+ await this.logstore.addLog(id, logInfo);
807
+ const logIDBytes = new TextEncoder().encode(peerId.toString());
808
+ await this.logstore.metadata.putBytes(
809
+ id,
810
+ identity?.toString() || "",
811
+ logIDBytes
812
+ );
813
+ return logInfo;
814
+ }
678
815
 
816
+ /**
817
+ * 生成随机threaddb 密钥
818
+ */
819
+ generateRandomKey(): ThreadKey {
820
+ return new ThreadKey(SymmetricKey.new(), SymmetricKey.new());
821
+ }
679
822
 
680
823
  /**
681
824
  * Add connector for thread
@@ -690,169 +833,190 @@ async ensureUniqueLog(id: ThreadID, key?: Ed25519PrivKey | Ed25519PubKey, identi
690
833
  getConnector(id: ThreadID): [Connector | null, boolean] {
691
834
  const conn = this.connectors[id.toString()];
692
835
  return [conn || null, !!conn];
693
-
694
836
  }
695
837
 
696
-
697
838
  /**
698
839
  * Get thread offsets and peers
699
840
  */
700
- async threadOffsets(tid: ThreadID): Promise<[Record<string, Head>, PeerId[]]> {
841
+ async threadOffsets(
842
+ tid: ThreadID
843
+ ): Promise<[Record<string, Head>, PeerId[]]> {
701
844
  const info = await this.logstore.getThread(tid);
702
-
845
+
703
846
  const offsets: Record<string, Head> = {};
704
847
  const addrs: TMultiaddr[] = [];
705
-
848
+
706
849
  // Process all logs in thread
707
850
  for (const lg of info.logs) {
708
851
  // Check if head is known
709
852
  let has = false;
710
-
853
+
711
854
  if (lg.head?.id) {
712
- has = await this.isKnown( lg.head.id);
855
+ has = await this.isKnown(lg.head.id);
713
856
  }
714
-
857
+
715
858
  if (has && lg.head) {
716
859
  offsets[lg.id.toString()] = lg.head;
717
- // Collect addresses
718
-
719
- }else{
860
+ // Collect addresses
861
+ } else {
720
862
  offsets[lg.id.toString()] = await getHeadUndef();
721
863
  }
722
864
  if (lg.addrs && lg.addrs.length > 0) {
723
865
  addrs.push(...lg.addrs);
724
866
  }
725
-
726
-
727
867
  }
728
-
868
+
729
869
  // Get unique peer IDs
730
870
  const peers = await this.uniquePeers(addrs);
731
-
871
+
732
872
  return [offsets, peers];
733
873
  }
734
874
 
735
- async updateLogsFromPeer(tid: ThreadID,peerId: PeerId): Promise<void> {
736
- try {
737
- const [client,_] = await this.getClient(peerId);
738
- if (!client) {
739
- return ;
740
- }
741
- const dbClient = new DBClient(client,this.dc,this,this.logstore);
742
- await dbClient.scheduleUpdateLogs(tid);
743
- } catch (err) {
744
- throw new Error(`Getting records for thread ${tid} failed: ${err instanceof Error ? err.message : String(err)}`);
745
- }
746
- }
747
-
748
-
749
- /**
750
- * 从对等点获取新的日志和记录并添加到本地存储
751
- *
752
- * @param tid threaddb ID
753
- * @param pid 对等点ID
754
- * @returns 无返回值,但会抛出错误
755
- */
756
- async updateRecordsFromPeer(tid: ThreadID,peerId: PeerId|null,client?:DBClient): Promise<void> {
757
- try {
758
- // 获取threaddb 偏移量
759
- const [offsets, peers] = await this.threadOffsets(tid);
760
-
761
- // 构建获取记录的请求
762
- const { req, serviceKey } = await this.buildGetRecordsRequest(tid, offsets, netPullingLimit);
763
- let recs : Record<string, PeerRecords> = {};
764
- if (client) {
765
- recs = await this.getRecordsWithDbClient(client, req, serviceKey);
766
- }else{
767
- if (!peerId) {
768
- throw new Error("A peer-id is required to request records");
875
+ async updateLogsFromPeer(tid: ThreadID, peerId: PeerId): Promise<void> {
876
+ try {
877
+ const [client, _] = await this.getClient(peerId);
878
+ if (!client) {
879
+ return;
769
880
  }
770
- // 从对等点获取记录
771
- recs = await this.getRecordsFromPeer(
772
- peerId,
773
- req,
774
- serviceKey
881
+ const dbClient = new DBClient(client, this.dc, this, this.logstore);
882
+ await dbClient.scheduleUpdateLogs(tid);
883
+ } catch (err) {
884
+ throw new Error(
885
+ `Getting records for thread ${tid} failed: ${
886
+ err instanceof Error ? err.message : String(err)
887
+ }`
775
888
  );
776
889
  }
777
- // 处理接收到的记录
778
- for (const [lidStr, rs] of Object.entries(recs)) {
779
- try {
780
- // 将字符串ID转换为PeerId对象
781
- const lid = peerIdFromString(lidStr);
782
-
783
- // 将记录添加到本地存储
784
- await this.putRecords( tid, lid, rs.records, rs.counter);
785
- } catch (err) {
786
- throw new Error(`Putting records from log ${lidStr} (thread ${tid}) failed: ${err instanceof Error ? err.message : String(err)}`);
787
- }
788
- }
789
- // 检查是否可能有更多记录需要获取
790
- for (const [lidStr, rs] of Object.entries(recs)) {
791
- try {
792
- const lid = peerIdFromString(lidStr);
793
- const head = await this.currentHead(tid, lid);
794
-
795
- // 如果我们收到了最大数量的记录,并且可能还有更多
796
- if (head.counter <= rs.counter && rs.records.length === netPullingLimit) {
797
- // 递归调用继续获取更多记录
798
- return this.updateRecordsFromPeer(tid, peerId,client);
799
- }
800
- } catch (err) {
801
- // 忽略获取头部的错误,继续检查其他日志
802
- console.warn(`Error checking head for log ${lidStr}: ${err instanceof Error ? err.message : String(err)}`);
803
- }
804
- }
805
- } catch (err) {
806
- throw new Error(`Getting records for thread ${tid} failed: ${err instanceof Error ? err.message : String(err)}`);
807
890
  }
808
- }
809
891
 
810
-
811
- /**
812
- * 从特定对等点获取记录
813
- * 注意: 这是一个假设的实现,需要根据你的实际gRPC客户端实现来调整
814
- */
815
- async getRecordsFromPeer(
816
- peerId: PeerId,
817
- req: any,
818
- serviceKey: SymKey
819
- ): Promise<Record<string, PeerRecords>> {
820
- try {
821
-
822
- const [client,_] = await this.getClient(peerId);
823
- if (!client) {
824
- return {};
892
+ /**
893
+ * 从对等点获取新的日志和记录并添加到本地存储
894
+ *
895
+ * @param tid threaddb ID
896
+ * @param pid 对等点ID
897
+ * @returns 无返回值,但会抛出错误
898
+ */
899
+ async updateRecordsFromPeer(
900
+ tid: ThreadID,
901
+ peerId: PeerId | null,
902
+ client?: DBClient
903
+ ): Promise<void> {
904
+ try {
905
+ // 获取threaddb 偏移量
906
+ const [offsets, peers] = await this.threadOffsets(tid);
907
+
908
+ // 构建获取记录的请求
909
+ const { req, serviceKey } = await this.buildGetRecordsRequest(
910
+ tid,
911
+ offsets,
912
+ netPullingLimit
913
+ );
914
+ let recs: Record<string, PeerRecords> = {};
915
+ if (client) {
916
+ recs = await this.getRecordsWithDbClient(client, req, serviceKey);
917
+ } else {
918
+ if (!peerId) {
919
+ throw new Error("A peer-id is required to request records");
920
+ }
921
+ // 从对等点获取记录
922
+ recs = await this.getRecordsFromPeer(peerId, req, serviceKey);
923
+ }
924
+ // 处理接收到的记录
925
+ for (const [lidStr, rs] of Object.entries(recs)) {
926
+ try {
927
+ // 将字符串ID转换为PeerId对象
928
+ const lid = peerIdFromString(lidStr);
929
+
930
+ // 将记录添加到本地存储
931
+ await this.putRecords(tid, lid, rs.records, rs.counter);
932
+ } catch (err) {
933
+ throw new Error(
934
+ `Putting records from log ${lidStr} (thread ${tid}) failed: ${
935
+ err instanceof Error ? err.message : String(err)
936
+ }`
937
+ );
938
+ }
939
+ }
940
+ // 检查是否可能有更多记录需要获取
941
+ for (const [lidStr, rs] of Object.entries(recs)) {
942
+ try {
943
+ const lid = peerIdFromString(lidStr);
944
+ const head = await this.currentHead(tid, lid);
945
+
946
+ // 如果我们收到了最大数量的记录,并且可能还有更多
947
+ if (
948
+ head.counter <= rs.counter &&
949
+ rs.records.length === netPullingLimit
950
+ ) {
951
+ // 递归调用继续获取更多记录
952
+ return this.updateRecordsFromPeer(tid, peerId, client);
953
+ }
954
+ } catch (err) {
955
+ // 忽略获取头部的错误,继续检查其他日志
956
+ console.warn(
957
+ `Error checking head for log ${lidStr}: ${
958
+ err instanceof Error ? err.message : String(err)
959
+ }`
960
+ );
961
+ }
962
+ }
963
+ } catch (err) {
964
+ throw new Error(
965
+ `Getting records for thread ${tid} failed: ${
966
+ err instanceof Error ? err.message : String(err)
967
+ }`
968
+ );
825
969
  }
826
- const dbClient = new DBClient(client,this.dc,this,this.logstore);
827
- const recs = await dbClient.getRecordsFromPeer( req, serviceKey);
828
- return recs;
829
- } catch (err) {
830
- console.error("getRecordsFromPeer error:", err);
831
- throw err;
832
970
  }
833
- }
834
971
 
835
- async getRecordsWithDbClient(
836
- dbClient: DBClient,
837
- req: any,
838
- serviceKey: SymKey
839
- ): Promise<Record<string, PeerRecords>> {
840
- try {
841
- const recs = await dbClient.getRecordsFromPeer( req, serviceKey);
842
- return recs;
843
- } catch (err) {
844
- console.error("getRecordsFromPeer error:", err);
845
- throw err;
972
+ /**
973
+ * 从特定对等点获取记录
974
+ * 注意: 这是一个假设的实现,需要根据你的实际gRPC客户端实现来调整
975
+ */
976
+ async getRecordsFromPeer(
977
+ peerId: PeerId,
978
+ req: any,
979
+ serviceKey: SymKey
980
+ ): Promise<Record<string, PeerRecords>> {
981
+ try {
982
+ const [client, _] = await this.getClient(peerId);
983
+ if (!client) {
984
+ return {};
985
+ }
986
+ const dbClient = new DBClient(client, this.dc, this, this.logstore);
987
+ const recs = await dbClient.getRecordsFromPeer(req, serviceKey);
988
+ return recs;
989
+ } catch (err) {
990
+ console.error("getRecordsFromPeer error:", err);
991
+ throw err;
992
+ }
846
993
  }
847
- }
848
994
 
995
+ async getRecordsWithDbClient(
996
+ dbClient: DBClient,
997
+ req: any,
998
+ serviceKey: SymKey
999
+ ): Promise<Record<string, PeerRecords>> {
1000
+ try {
1001
+ const recs = await dbClient.getRecordsFromPeer(req, serviceKey);
1002
+ return recs;
1003
+ } catch (err) {
1004
+ console.error("getRecordsFromPeer error:", err);
1005
+ throw err;
1006
+ }
1007
+ }
849
1008
 
850
1009
  /**
851
1010
  * Add records to a thread
852
1011
  */
853
- async putRecords(tid: ThreadID, lid: PeerId, recs: IRecord[], counter: number): Promise<void> {
854
- const [chain, head] = await this.loadRecords( tid, lid, recs, counter);
855
-
1012
+ async putRecords(
1013
+ tid: ThreadID,
1014
+ lid: PeerId,
1015
+ recs: IRecord[],
1016
+ counter: number
1017
+ ): Promise<void> {
1018
+ const [chain, head] = await this.loadRecords(tid, lid, recs, counter);
1019
+
856
1020
  if (chain.length === 0) {
857
1021
  return;
858
1022
  }
@@ -863,11 +1027,11 @@ async getRecordsWithDbClient(
863
1027
  const current = await this.currentHead(tid, lid);
864
1028
  let headReached = true;
865
1029
  let updatedHead = head;
866
- if (current?.id?.toString() != head?.id?.toString() ) {
1030
+ if (current?.id?.toString() != head?.id?.toString()) {
867
1031
  // Fast-forward the chain up to the updated head
868
1032
  headReached = false;
869
1033
  updatedHead = current;
870
-
1034
+
871
1035
  for (let i = 0; i < chain.length; i++) {
872
1036
  if (chain[i]!.value().cid().equals(current.id)) {
873
1037
  chain.splice(0, i + 1);
@@ -875,7 +1039,7 @@ async getRecordsWithDbClient(
875
1039
  break;
876
1040
  }
877
1041
  }
878
-
1042
+
879
1043
  if (!headReached) {
880
1044
  // Entire chain already processed
881
1045
  return;
@@ -883,17 +1047,17 @@ async getRecordsWithDbClient(
883
1047
  }
884
1048
 
885
1049
  const [connector, appConnected] = this.getConnector(tid);
886
- let identity:Ed25519PubKey;
1050
+ let identity: Ed25519PubKey;
887
1051
  let readKey: SymmetricKey | null = null;
888
1052
  let validate = false;
889
1053
  let updatedCounter = updatedHead.counter;
890
-
1054
+
891
1055
  if (appConnected) {
892
- const symKey = await this.logstore.keyBook.readKey(tid)
893
- if (symKey){
894
- readKey = SymmetricKey.fromSymKey( symKey);
895
- }
896
- if (readKey) {
1056
+ const symKey = await this.logstore.keyBook.readKey(tid);
1057
+ if (symKey) {
1058
+ readKey = SymmetricKey.fromSymKey(symKey);
1059
+ }
1060
+ if (readKey) {
897
1061
  validate = true;
898
1062
  }
899
1063
  }
@@ -901,23 +1065,28 @@ async getRecordsWithDbClient(
901
1065
  if (validate) {
902
1066
  // Validate the record
903
1067
  const block = await record.value().getBlock(this.bstore);
904
-
1068
+
905
1069
  let event: Event;
906
1070
  if (block instanceof Event) {
907
1071
  event = block;
908
1072
  } else {
909
- event = await EventFromNode(block as Node) as Event;
1073
+ event = (await EventFromNode(block as Node)) as Event;
910
1074
  }
911
-
912
- const dbody = await event.getBody( this.bstore, readKey?readKey: undefined);
913
-
914
- identity = await KeyConverter.publicFromBytes<Ed25519PubKey>(record.value().pubKey());
915
-
1075
+
1076
+ const dbody = await event.getBody(
1077
+ this.bstore,
1078
+ readKey ? readKey : undefined
1079
+ );
1080
+
1081
+ identity = await KeyConverter.publicFromBytes<Ed25519PubKey>(
1082
+ record.value().pubKey()
1083
+ );
1084
+
916
1085
  try {
917
1086
  await connector!.validateNetRecordBody(dbody, identity);
918
1087
  } catch (err) {
919
1088
  // If validation fails, clean up blocks
920
- const header = await event.getHeader( this.bstore );
1089
+ const header = await event.getHeader(this.bstore);
921
1090
  const body = await event.getBody(this.bstore);
922
1091
  this.bstore.deleteMany([event.cid(), header.cid(), body.cid()]);
923
1092
  throw err;
@@ -926,20 +1095,21 @@ async getRecordsWithDbClient(
926
1095
 
927
1096
  // Update head counter
928
1097
  updatedCounter++;
929
-
1098
+
930
1099
  // Set new head for the log
931
- await this.logstore.headBook.setHead(
1100
+ await this.logstore.headBook.setHead(tid, lid, {
1101
+ id: record.value().cid(),
1102
+ counter: updatedCounter,
1103
+ });
1104
+
1105
+ // Set checkpoint for log
1106
+ await this.setThreadLogPoint(
932
1107
  tid,
933
1108
  lid,
934
- {
935
- id: record.value().cid(),
936
- counter: updatedCounter
937
- }
1109
+ updatedCounter,
1110
+ record.value().cid()
938
1111
  );
939
-
940
- // Set checkpoint for log
941
- await this.setThreadLogPoint(tid, lid, updatedCounter, record.value().cid());
942
-
1112
+
943
1113
  // Handle record in app connector
944
1114
  if (appConnected) {
945
1115
  try {
@@ -952,7 +1122,6 @@ async getRecordsWithDbClient(
952
1122
 
953
1123
  // Add record to blockstore
954
1124
  await this.bstore.put(record.value().cid(), record.value().data());
955
-
956
1125
  }
957
1126
  } finally {
958
1127
  mutex.release();
@@ -971,12 +1140,12 @@ async getRecordsWithDbClient(
971
1140
  if (recs.length === 0) {
972
1141
  throw new Error("Cannot load empty record chain");
973
1142
  }
974
-
1143
+
975
1144
  const head = await this.currentHead(tid, lid);
976
-
1145
+
977
1146
  // Check if the last record was already loaded and processed
978
1147
  const last = recs[recs.length - 1]!;
979
-
1148
+
980
1149
  // If we don't have the counter, check if record exists
981
1150
  if (counter === undefined) {
982
1151
  const exist = await this.isKnown(last.cid());
@@ -986,28 +1155,27 @@ async getRecordsWithDbClient(
986
1155
  } else if (counter <= head.counter) {
987
1156
  return [[], head];
988
1157
  }
989
-
990
1158
  let chain: IRecord[] = [];
991
1159
  let complete = false;
992
1160
  // Check which records we already have
993
1161
  for (let i = recs.length - 1; i >= 0; i--) {
994
1162
  const next = recs[i]!;
995
- if ( next.cid.toString() == "" || next.cid().equals(head.id)) {
1163
+ if (next.cid().toString() == "" || next.cid().equals(head.id)) {
996
1164
  complete = true;
997
1165
  break;
998
1166
  }
999
1167
  chain.push(next);
1000
1168
  }
1001
-
1002
1169
 
1003
1170
  // Bridge the gap between the last provided record and current head
1004
1171
  if (!complete && chain.length > 0) {
1172
+
1005
1173
  let c = chain[chain.length - 1]!.prevID();
1006
1174
  while (c && !(last.cid().toString() == "")) {
1007
1175
  if (c.equals(head.id)) {
1008
1176
  break;
1009
1177
  }
1010
- const r = await this.getRecord( tid, c);
1178
+ const r = await this.getRecord(tid, c);
1011
1179
  chain.push(r);
1012
1180
  c = r.prevID();
1013
1181
  }
@@ -1019,131 +1187,131 @@ async getRecordsWithDbClient(
1019
1187
 
1020
1188
  // Prepare thread records in the right order
1021
1189
  const tRecords: ThreadRecord[] = [];
1022
-
1190
+
1023
1191
  for (let i = chain.length - 1; i >= 0; i--) {
1024
1192
  const r = chain[i]!;
1025
-
1193
+
1026
1194
  // Get and cache blocks
1027
1195
  const block = await r.getBlock(this.bstore);
1028
- let event: IThreadEvent;
1196
+ let event: IThreadEvent;
1029
1197
  if (block instanceof Event) {
1030
1198
  event = block;
1031
1199
  } else {
1032
- event = await EventFromNode(block as Node) ;
1200
+ event = await EventFromNode(block as Node);
1033
1201
  }
1034
1202
 
1035
1203
  const header = await event.getHeader(this.bstore);
1036
1204
  const body = await event.getBody(this.bstore);
1037
-
1205
+
1038
1206
  // Store internal blocks
1039
1207
  await this.addMany([event, header, body]);
1040
1208
  tRecords.push(newRecord(r, tid, lid) as ThreadRecord);
1041
- }
1209
+ }
1042
1210
  return [tRecords, head];
1043
1211
  }
1044
1212
 
1045
-
1046
-
1047
1213
  /**
1048
1214
  * Check if a record exists
1049
1215
  */
1050
- async isKnown( rec: CID): Promise<boolean> {
1051
- return await this.bstore.has( rec);
1216
+ async isKnown(rec: CID): Promise<boolean> {
1217
+ return await this.bstore.has(rec);
1052
1218
  }
1053
1219
 
1220
+ /**
1221
+ * 获取记录
1222
+ * 从给定的threaddb 和CID获取记录
1223
+ *
1224
+ * @param ctx 上下文
1225
+ * @param id threaddb ID
1226
+ * @param rid 记录CID
1227
+ * @returns 检索到的记录
1228
+ * @throws 如果无法获取服务密钥或记录
1229
+ */
1230
+ async getRecord(id: ThreadID, rid: CID): Promise<IRecord> {
1231
+ // 从存储中获取服务密钥
1232
+ const serviceKey = await this.logstore.keyBook.serviceKey(id);
1054
1233
 
1055
- /**
1056
- * 获取记录
1057
- * 从给定的threaddb 和CID获取记录
1058
- *
1059
- * @param ctx 上下文
1060
- * @param id threaddb ID
1061
- * @param rid 记录CID
1062
- * @returns 检索到的记录
1063
- * @throws 如果无法获取服务密钥或记录
1064
- */
1065
- async getRecord( id: ThreadID, rid: CID): Promise<IRecord> {
1066
- // 从存储中获取服务密钥
1067
- const serviceKey = await this.logstore.keyBook.serviceKey(id);
1068
-
1069
- // 检查服务密钥是否存在
1070
- if (!serviceKey) {
1071
- throw new Error("A service-key is required to get records");
1234
+ // 检查服务密钥是否存在
1235
+ if (!serviceKey) {
1236
+ throw new Error("A service-key is required to get records");
1237
+ }
1238
+
1239
+ const sk = SymmetricKey.fromSymKey(serviceKey);
1240
+ // 使用CBOR获取记录
1241
+ return await GetRecord(this.dagService, rid, sk);
1072
1242
  }
1073
-
1074
- const sk = SymmetricKey.fromSymKey(serviceKey)
1075
- // 使用CBOR获取记录
1076
- return await GetRecord(this.dagService, rid, sk);
1077
- }
1078
- /**
1243
+ /**
1079
1244
  * Set a thread log checkpoint
1080
1245
  */
1081
- async setThreadLogPoint(tid: ThreadID, lid: PeerId, counter: number, rcid: CID): Promise<void> {
1082
- // Only store checkpoints at multiples of 10000
1083
- if (counter % 10000 !== 0) {
1084
- return;
1085
- }
1086
- // Create key for checkpoint
1087
- const key = `${lid.toString()}/${counter}`;
1088
-
1089
- // Store checkpoint
1090
- await this.logstore.metadata.putBytes(tid, key, rcid.bytes);
1246
+ async setThreadLogPoint(
1247
+ tid: ThreadID,
1248
+ lid: PeerId,
1249
+ counter: number,
1250
+ rcid: CID
1251
+ ): Promise<void> {
1252
+ // Only store checkpoints at multiples of 10000
1253
+ if (counter % 10000 !== 0) {
1254
+ return;
1091
1255
  }
1256
+ // Create key for checkpoint
1257
+ const key = `${lid.toString()}/${counter}`;
1258
+
1259
+ // Store checkpoint
1260
+ await this.logstore.metadata.putBytes(tid, key, rcid.bytes);
1261
+ }
1092
1262
 
1093
1263
  /**
1094
1264
  * Get the current head of a log
1095
1265
  */
1096
1266
  async currentHead(tid: ThreadID, lid: PeerId): Promise<Head> {
1097
1267
  const heads = await this.logstore.headBook.heads(tid, lid);
1098
-
1268
+
1099
1269
  if (heads.length > 0) {
1100
1270
  return heads[0]!;
1101
1271
  } else {
1102
1272
  return await getHeadUndef();
1103
1273
  }
1104
1274
  }
1105
- /**
1106
- * Extract unique peer IDs from addresses
1107
- */
1108
- async uniquePeers(addrs: TMultiaddr[]): Promise<PeerId[]> {
1109
- const peerMap = new Map<string, PeerId>();
1110
-
1111
- for (const addr of addrs) {
1112
- try {
1113
- const [pid, callable] = await this.callablePeer(addr);
1114
-
1115
- if (!callable) {
1116
- continue;
1117
- }
1118
-
1119
- peerMap.set(pid.toString(), pid);
1120
- } catch (err) {
1121
- // Skip addresses that can't be parsed
1275
+ /**
1276
+ * Extract unique peer IDs from addresses
1277
+ */
1278
+ async uniquePeers(addrs: TMultiaddr[]): Promise<PeerId[]> {
1279
+ const peerMap = new Map<string, PeerId>();
1280
+
1281
+ for (const addr of addrs) {
1282
+ try {
1283
+ const [pid, callable] = await this.callablePeer(addr);
1284
+
1285
+ if (!callable) {
1286
+ continue;
1122
1287
  }
1288
+
1289
+ peerMap.set(pid.toString(), pid);
1290
+ } catch (err) {
1291
+ // Skip addresses that can't be parsed
1123
1292
  }
1124
-
1125
- return Array.from(peerMap.values());
1126
1293
  }
1127
-
1128
1294
 
1295
+ return Array.from(peerMap.values());
1296
+ }
1129
1297
 
1130
- /**
1131
- * Extract peer ID from multiaddress and check if callable
1132
- */
1133
- async callablePeer(addr: TMultiaddr): Promise<[PeerId, boolean]> {
1134
- const p = addr.getPeerId();
1135
- if (!p) {
1136
- throw new Error("Address does not contain peer ID");
1137
- }
1138
-
1139
- const pid = peerIdFromString(p);
1140
-
1141
- // Don't call ourselves
1142
- if (pid.toString() === this.hostID) {
1143
- return [pid, false];
1144
- }
1145
- return [pid, true];
1146
- }
1298
+ /**
1299
+ * Extract peer ID from multiaddress and check if callable
1300
+ */
1301
+ async callablePeer(addr: TMultiaddr): Promise<[PeerId, boolean]> {
1302
+ const p = addr.getPeerId();
1303
+ if (!p) {
1304
+ throw new Error("Address does not contain peer ID");
1305
+ }
1306
+
1307
+ const pid = peerIdFromString(p);
1308
+
1309
+ // Don't call ourselves
1310
+ if (pid.toString() === this.hostID) {
1311
+ return [pid, false];
1312
+ }
1313
+ return [pid, true];
1314
+ }
1147
1315
 
1148
1316
  /**
1149
1317
  * 从指定对等点获取记录
@@ -1162,156 +1330,113 @@ async getRecord( id: ThreadID, rid: CID): Promise<IRecord> {
1162
1330
  const recordCollector = new RecordCollector();
1163
1331
 
1164
1332
  // 确定需要获取的对等点数量
1165
- const needFetched = Math.min(1, peers.length);
1166
- let resolved = false; // 防止重复调用 resolve
1167
- // 使用对象来保证引用一致性
1168
- const fetchState = {
1169
- fetchedPeers: 0
1170
- };
1171
- let mainTimeoutId: NodeJS.Timeout | undefined;
1172
- // 设置主超时
1173
- const timeoutPromise = new Promise<void>((resolve) => {
1174
- mainTimeoutId = setTimeout(() => {
1175
- resolve(); // 超时时总是resolve,在外部判断是否有足够的数据
1176
- }, 30000);
1177
- });
1178
- let dealedCount = 0;
1333
+ const needFetched = Math.min(2, peers.length);
1334
+ let fetchedPeers = 0;
1335
+
1179
1336
  // 创建一个 Promise 在足够的对等点响应时解析
1180
1337
  const fetchPromise = new Promise<void>((resolve) => {
1181
- // 立即检查是否满足条件
1338
+ // 当所有查询完成时,解析 Promise
1182
1339
  const checkComplete = () => {
1183
- if (!resolved && (dealedCount >= peers.length || fetchState.fetchedPeers >= needFetched)) {
1184
- resolved = true;
1340
+ if (fetchedPeers >= needFetched) {
1185
1341
  resolve();
1186
1342
  }
1187
1343
  };
1188
1344
 
1189
1345
  // 从每个对等点查询记录
1190
1346
  const fetchPromises = peers.map(async (peerId) => {
1191
-
1192
- let timeoutId: NodeJS.Timeout | undefined;
1193
- // 为每个peer设置独立的超时
1194
- const peerTimeout = new Promise<never>((_, reject) => {
1195
- timeoutId = setTimeout(() => reject(new Error(`Peer ${peerId} timeout after 30s`)), 30000);
1196
- });
1197
-
1198
1347
  try {
1199
- // 使用 Promise.race 为每个peer设置超时
1200
- await Promise.race([
1201
- (async () => {
1202
- //连接到指定peerId,返回一个Client
1203
- const [client,_] = await this.getClient(peerId);
1204
- if (!client) {
1205
- return;
1206
- }
1207
- const dbClient = new DBClient(client,this.dc,this,this.logstore);
1348
+ //连接到指定peerId,返回一个Client
1349
+ const [client,err] = await this.getClient(peerId);
1350
+ if (!client) {
1351
+ console.error(`Error getting records from peer ${peerId},no client,errinfo:`, err);
1352
+ return;
1353
+ }
1354
+ const dbClient = new DBClient(client,this.dc,this,this.logstore);
1355
+
1356
+ // 这里使用一个队列来控制并发,类似于 Go 代码中的 queueGetRecords
1357
+ const records = await dbClient.getRecordsFromPeer( req, serviceKey);
1358
+
1359
+ // 更新收集器
1360
+ Object.entries(records).forEach(([logId, rs]) => {
1361
+ recordCollector.updateHeadCounter(logId, rs.counter);
1362
+ rs.records.forEach(record => {
1363
+ recordCollector.store(logId, record);
1364
+ });
1365
+ });
1208
1366
 
1209
- const records = await dbClient.getRecordsFromPeer( req, serviceKey);
1210
-
1211
- // 更新收集器
1212
- Object.entries(records).forEach(([logId, rs]) => {
1213
- recordCollector.updateHeadCounter(logId, rs.counter);
1214
- rs.records.forEach(record => {
1215
- recordCollector.store(logId, record);
1216
- });
1217
- });
1218
-
1219
- fetchState.fetchedPeers++;
1220
-
1221
- // 每次成功获取记录后立即检查是否满足条件
1222
- checkComplete();
1223
- })(),
1224
- peerTimeout
1225
- ]);
1367
+ fetchedPeers++;
1368
+
1369
+ // 如果获取了记录并且达到了所需的对等点数量,则解析 Promise
1370
+ if (Object.keys(records).length > 0 && fetchedPeers >= needFetched) {
1371
+ resolve();
1372
+ }
1226
1373
  } catch (err) {
1227
1374
  console.error(`Error getting records from peer ${peerId}:`, err);
1228
- // 错误不影响其他peer的执行
1229
- } finally {
1230
- // 清除定时器,防止内存泄漏
1231
- if (timeoutId) {
1232
- clearTimeout(timeoutId);
1233
- }
1234
- dealedCount ++;
1235
- }
1236
- });
1237
-
1238
- // 使用 Promise.allSettled 替代 Promise.all,避免被单个失败阻塞
1239
- // 同时设置额外的兜底检查机制
1240
- Promise.allSettled(fetchPromises).then(() => {
1241
- if (!resolved) {
1242
- checkComplete();
1243
1375
  }
1244
1376
  });
1245
1377
 
1246
- // 添加定期检查机制,防止被某些未知问题阻塞
1247
- const checkInterval = setInterval(() => {
1248
- if (resolved) {
1249
- clearInterval(checkInterval);
1250
- } else {
1251
- checkComplete();
1252
- }
1253
- }, 5000); // 每5秒检查一次
1254
-
1255
- // 确保interval最终被清理
1256
- setTimeout(() => clearInterval(checkInterval), 35000);
1378
+ // 当所有查询完成或失败时,检查是否需要解析 Promise
1379
+ Promise.all(fetchPromises.map(p => p.catch(e => e)))
1380
+ .then(() => checkComplete());
1257
1381
  });
1258
1382
 
1259
-
1383
+ // 设置超时
1384
+ const timeoutPromise = new Promise<void>((resolve) => {
1385
+ setTimeout(() => {
1386
+ resolve(); // 超时时总是resolve,在外部判断是否有足够的数据
1387
+ }, 60000);
1388
+ });
1260
1389
 
1261
1390
  // 等待获取足够的记录或超时
1262
1391
  await Promise.race([fetchPromise, timeoutPromise]);
1263
- // 清除超时定时器
1264
- if (mainTimeoutId) {
1265
- clearTimeout(mainTimeoutId);
1266
- }
1267
- // 检查是否获取到足够的数据
1268
- if (fetchState.fetchedPeers === 0 && dealedCount < peers.length) {
1392
+ // 检查是否获取到足够的数据
1393
+ if (fetchedPeers === 0) {
1269
1394
  throw new Error("Fetch records timeout: no peers responded");
1270
1395
  }
1271
-
1272
- // 如果有数据就返回(不管是1个还是2个节点)
1273
- console.log(`获取记录完成: ${fetchState.fetchedPeers}/${needFetched} 个节点响应`);
1396
+ // 返回收集到的记录
1274
1397
  return recordCollector.list();
1275
1398
  } catch (err) {
1276
1399
  console.error("getRecords error:", err);
1277
1400
  throw err;
1278
1401
  }
1279
1402
  }
1280
-
1403
+
1281
1404
  /**
1282
1405
  * 构建获取记录的请求
1283
1406
  */
1284
1407
  async buildGetRecordsRequest(
1285
1408
  tid: ThreadID,
1286
- offsets: Record<string,Head>,
1409
+ offsets: Record<string, Head>,
1287
1410
  limit: number
1288
- ): Promise<{ req: net_pb.pb.IGetRecordsRequest, serviceKey: SymKey }> {
1411
+ ): Promise<{ req: net_pb.pb.IGetRecordsRequest; serviceKey: SymKey }> {
1289
1412
  try {
1290
1413
  // 从存储中获取服务密钥
1291
1414
  const serviceKey = await this.logstore.keyBook.serviceKey(tid);
1292
-
1415
+
1293
1416
  if (!serviceKey) {
1294
1417
  throw new Error("A service-key is required to request records");
1295
1418
  }
1296
-
1419
+
1297
1420
  // 创建日志条目
1298
1421
  const logs = Object.entries(offsets).map(([logId, offset]) => {
1299
1422
  const pbLog: net_pb.pb.GetRecordsRequest.Body.ILogEntry = {
1300
1423
  logID: PeerIDConverter.toBytes(logId),
1301
1424
  limit: limit,
1302
1425
  counter: offset.counter,
1303
- offset: offset.id?CidConverter.toBytes(offset.id):new Uint8Array()
1304
- }
1426
+ offset: offset.id
1427
+ ? CidConverter.toBytes(offset.id)
1428
+ : new Uint8Array(),
1429
+ };
1305
1430
  return pbLog;
1306
- });
1307
-
1431
+ });
1432
+
1308
1433
  // 构建请求体
1309
1434
  const body: net_pb.pb.GetRecordsRequest.IBody = {
1310
1435
  threadID: ThreadIDConverter.toBytes(tid.toString()),
1311
1436
  serviceKey: serviceKey.raw,
1312
- logs: logs
1437
+ logs: logs,
1313
1438
  };
1314
-
1439
+
1315
1440
  // 创建请求
1316
1441
  const req = new net_pb.pb.GetRecordsRequest();
1317
1442
  req.body = body;
@@ -1322,9 +1447,8 @@ async getRecord( id: ThreadID, rid: CID): Promise<IRecord> {
1322
1447
  }
1323
1448
  }
1324
1449
 
1325
-
1326
1450
  /**
1327
- * 连接应用到指定threaddb
1451
+ * 连接应用到指定threaddb
1328
1452
  * @param app 应用程序对象
1329
1453
  * @param id threaddb ID
1330
1454
  * @returns 返回一个应用连接器
@@ -1332,461 +1456,497 @@ async getRecord( id: ThreadID, rid: CID): Promise<IRecord> {
1332
1456
  */
1333
1457
  async connectApp(app: App, id: ThreadID): Promise<Connector> {
1334
1458
  // 验证threaddb ID
1335
- if (!id.isDefined()) {
1336
- throw new Error("Invalid thread ID");
1337
- }
1459
+ if (!id.isDefined()) {
1460
+ throw new Error("Invalid thread ID");
1461
+ }
1338
1462
  // 获取threaddb信息
1339
1463
  let info;
1340
1464
  try {
1341
1465
  info = await this.getThreadWithAddrs(id);
1342
1466
  } catch (err) {
1343
- throw new Error(`Error getting thread ${id.toString()}: ${err instanceof Error ? err.message : String(err)}`);
1467
+ throw new Error(
1468
+ `Error getting thread ${id.toString()}: ${
1469
+ err instanceof Error ? err.message : String(err)
1470
+ }`
1471
+ );
1344
1472
  }
1345
-
1473
+
1346
1474
  // 创建应用连接器
1347
1475
  let con;
1348
1476
  try {
1349
- con = new Connector(this, app, info);
1477
+ con = new Connector(this, app, info);
1350
1478
  } catch (err) {
1351
- throw new Error(`Error making connector ${id.toString()}: ${err instanceof Error ? err.message : String(err)}`);
1479
+ throw new Error(
1480
+ `Error making connector ${id.toString()}: ${
1481
+ err instanceof Error ? err.message : String(err)
1482
+ }`
1483
+ );
1352
1484
  }
1353
-
1485
+
1354
1486
  // 添加连接器到网络
1355
1487
  this.addConnector(id, con);
1356
-
1488
+
1357
1489
  return con;
1358
1490
  }
1359
1491
 
1492
+ /**
1493
+ * 获取线程的所有日志
1494
+ * 返回线程的日志列表和线程信息
1495
+ *
1496
+ * @param tid 线程ID
1497
+ * @returns 日志数组和线程信息
1498
+ */
1499
+ async getPbLogs(tid: ThreadID): Promise<[net_pb.pb.ILog[], IThreadInfo]> {
1500
+ try {
1501
+ // 从存储中获取线程信息
1502
+ const info = await this.logstore.getThread(tid);
1360
1503
 
1504
+ // 创建日志数组
1505
+ const logs: net_pb.pb.ILog[] = [];
1361
1506
 
1362
-
1363
- /**
1364
- * 获取线程的所有日志
1365
- * 返回线程的日志列表和线程信息
1366
- *
1367
- * @param tid 线程ID
1368
- * @returns 日志数组和线程信息
1369
- */
1370
- async getPbLogs(tid: ThreadID): Promise<[net_pb.pb.ILog[], IThreadInfo]> {
1371
- try {
1372
- // 从存储中获取线程信息
1373
- const info = await this.logstore.getThread(tid);
1374
-
1375
- // 创建日志数组
1376
- const logs: net_pb.pb.ILog[] = [];
1377
-
1378
- // 将每个日志信息转换为protobuf格式
1379
- for (const logInfo of info.logs) {
1380
- logs.push(await logToProto(logInfo));
1507
+ // 将每个日志信息转换为protobuf格式
1508
+ for (const logInfo of info.logs) {
1509
+ logs.push(await logToProto(logInfo));
1510
+ }
1511
+ return [logs, info];
1512
+ } catch (err) {
1513
+ throw new Error(
1514
+ `Failed to get logs for thread ${tid}: ${
1515
+ err instanceof Error ? err.message : String(err)
1516
+ }`
1517
+ );
1381
1518
  }
1382
- return [logs, info];
1383
- } catch (err) {
1384
- throw new Error(`Failed to get logs for thread ${tid}: ${err instanceof Error ? err.message : String(err)}`);
1385
1519
  }
1386
- }
1387
1520
 
1388
- /**
1389
- * 预加载日志到线程
1390
- * 浏览器兼容版本
1391
- *
1392
- * @param tid 线程ID
1393
- * @param logs 协议缓冲区日志数组
1394
- */
1395
- async preLoadLogs(tid: ThreadID, logs: net_pb.pb.Log[]): Promise<void> {
1396
- try {
1397
- // 创建一个与日志数组相同长度的ThreadLogInfo数组
1398
- const lgs: IThreadLogInfo[] = [];
1399
-
1400
- // 遍历并转换每个日志
1401
- for (let i = 0; i < logs.length; i++) {
1402
- lgs.push(await logFromProto(logs[i]!));
1521
+ /**
1522
+ * 预加载日志到线程
1523
+ * 浏览器兼容版本
1524
+ *
1525
+ * @param tid 线程ID
1526
+ * @param logs 协议缓冲区日志数组
1527
+ */
1528
+ async preLoadLogs(tid: ThreadID, logs: net_pb.pb.Log[]): Promise<void> {
1529
+ try {
1530
+ // 创建一个与日志数组相同长度的ThreadLogInfo数组
1531
+ const lgs: IThreadLogInfo[] = [];
1532
+
1533
+ // 遍历并转换每个日志
1534
+ for (let i = 0; i < logs.length; i++) {
1535
+ lgs.push(await logFromProto(logs[i]!));
1536
+ }
1537
+ // 调用创建外部日志的方法
1538
+ await this.createExternalLogsIfNotExistForPreload(tid, lgs);
1539
+ } catch (err) {
1540
+ throw new Error(
1541
+ `Failed to preload logs: ${
1542
+ err instanceof Error ? err.message : String(err)
1543
+ }`
1544
+ );
1403
1545
  }
1404
- // 调用创建外部日志的方法
1405
- await this.createExternalLogsIfNotExistForPreload(tid, lgs);
1406
- } catch (err) {
1407
- throw new Error(`Failed to preload logs: ${err instanceof Error ? err.message : String(err)}`);
1408
1546
  }
1409
- }
1410
1547
 
1548
+ /**
1549
+ * 创建外部日志(如果不存在)
1550
+ * 创建的日志将使用未定义CID作为当前头部
1551
+ * 此方法是线程安全的
1552
+ *
1553
+ * @param tid 线程ID
1554
+ * @param logs 日志信息数组
1555
+ */
1556
+ async createExternalLogsIfNotExist(
1557
+ tid: ThreadID,
1558
+ logs: IThreadLogInfo[]
1559
+ ): Promise<void> {
1560
+ const mutex = this.getMutexForThread(tid.toString());
1561
+ await mutex.acquire();
1562
+ try {
1563
+ for (const log of logs) {
1564
+ try {
1565
+ const heads = await this.logstore.headBook.heads(tid, log.id);
1411
1566
 
1412
- /**
1413
- * 创建外部日志(如果不存在)
1414
- * 创建的日志将使用未定义CID作为当前头部
1415
- * 此方法是线程安全的
1416
- *
1417
- * @param tid 线程ID
1418
- * @param logs 日志信息数组
1419
- */
1420
- async createExternalLogsIfNotExist(tid: ThreadID, logs: IThreadLogInfo[]): Promise<void> {
1421
- const mutex = this.getMutexForThread(tid.toString());
1422
- await mutex.acquire();
1423
- try {
1424
- for (const log of logs) {
1425
- try {
1426
- const heads = await this.logstore.headBook.heads(tid, log.id);
1427
-
1428
- if (heads.length === 0) {
1429
- log.head = await getHeadUndef();
1430
- await this.logstore.addLog(tid, log);
1431
- } else {
1432
- await this.logstore.addrBook.addAddrs(
1433
- tid,
1434
- log.id,
1435
- log.addrs,
1436
- PermanentAddrTTL
1437
- );
1567
+ if (heads.length === 0) {
1568
+ log.head = await getHeadUndef();
1569
+ await this.logstore.addLog(tid, log);
1570
+ } else {
1571
+ await this.logstore.addrBook.addAddrs(
1572
+ tid,
1573
+ log.id,
1574
+ log.addrs,
1575
+ PermanentAddrTTL
1576
+ );
1577
+ }
1578
+ } catch (err) {
1579
+ continue;
1438
1580
  }
1439
- } catch (err) {
1440
- continue
1441
1581
  }
1582
+ } finally {
1583
+ mutex.release();
1442
1584
  }
1443
- } finally {
1444
- mutex.release();
1445
1585
  }
1446
- }
1447
1586
 
1448
- /**
1449
- * 预加载时创建外部日志(如果不存在)
1450
- * 浏览器兼容版本
1451
- *
1452
- * @param tid 线程ID
1453
- * @param logs 日志信息数组
1454
- */
1455
- async createExternalLogsIfNotExistForPreload(tid: ThreadID, logs: IThreadLogInfo[]): Promise<void> {
1456
- const mutex = this.getMutexForThread(tid.toString());
1457
- await mutex.acquire();
1458
- try {
1459
- for (const log of logs) {
1460
- try {
1461
- const heads = await this.logstore.headBook.heads(tid, log.id);
1462
- if (heads.length === 0) {
1463
- await this.logstore.addLog(tid, log);
1464
- } else {
1465
- await this.logstore.addrBook.addAddrs(
1466
- tid,
1467
- log.id,
1468
- log.addrs,
1469
- PermanentAddrTTL
1470
- );
1587
+ /**
1588
+ * 预加载时创建外部日志(如果不存在)
1589
+ * 浏览器兼容版本
1590
+ *
1591
+ * @param tid 线程ID
1592
+ * @param logs 日志信息数组
1593
+ */
1594
+ async createExternalLogsIfNotExistForPreload(
1595
+ tid: ThreadID,
1596
+ logs: IThreadLogInfo[]
1597
+ ): Promise<void> {
1598
+ const mutex = this.getMutexForThread(tid.toString());
1599
+ await mutex.acquire();
1600
+ try {
1601
+ for (const log of logs) {
1602
+ try {
1603
+ const heads = await this.logstore.headBook.heads(tid, log.id);
1604
+ if (heads.length === 0) {
1605
+ await this.logstore.addLog(tid, log);
1606
+ } else {
1607
+ await this.logstore.addrBook.addAddrs(
1608
+ tid,
1609
+ log.id,
1610
+ log.addrs,
1611
+ PermanentAddrTTL
1612
+ );
1613
+ }
1614
+ } catch (err) {
1615
+ continue;
1471
1616
  }
1472
- } catch (err) {
1473
- continue;
1474
1617
  }
1618
+ } finally {
1619
+ mutex.release();
1475
1620
  }
1476
- } finally {
1477
- mutex.release();
1478
1621
  }
1479
- }
1480
1622
 
1481
- /**
1482
- * 创建新的记录
1483
- *
1484
- * @param id threadID
1485
- * @param body 节点内容
1486
- * @param options 选项
1487
- * @returns 创建的threaddb记录
1488
- */
1489
- async createRecord(
1490
- id: ThreadID,
1491
- body: IPLDNode,
1492
- options: { token?: ThreadToken, apiToken?: Token } = {}
1493
- ): Promise<IThreadRecord> {
1494
- try {
1495
- if (this.context.publicKey === undefined) {
1496
- throw new Error("No identity provided for creating record");
1497
- }
1498
- // 验证身份,用节点的pubkey
1499
- const identity = this.context.publicKey;
1500
- // 获取并验证连接器
1501
- const [con, ok] = this.getConnectorProtected(id, options.apiToken);
1502
-
1503
- if (!ok) {
1504
- throw new Error("Cannot create record: thread in use");
1505
- } else if (con) {
1506
- await con.validateNetRecordBody(body, identity as Ed25519PubKey);
1507
- }
1508
-
1509
- // 获取或创建日志
1510
- const lg = await this.getOrCreateLog(id, identity);
1511
-
1512
- // 创建新记录
1513
- const r = await this.newRecord(id, lg, body, identity);
1514
-
1515
- // 创建threaddb 记录
1516
- const tr = newRecord(r, id, lg.id);
1517
- if (!lg.head){
1518
- lg.head = {
1519
- counter: 0
1623
+ /**
1624
+ * 创建新的记录
1625
+ *
1626
+ * @param id threadID
1627
+ * @param body 节点内容
1628
+ * @param options 选项
1629
+ * @returns 创建的threaddb记录
1630
+ */
1631
+ async createRecord(
1632
+ id: ThreadID,
1633
+ body: IPLDNode,
1634
+ options: { token?: ThreadToken; apiToken?: Token } = {}
1635
+ ): Promise<IThreadRecord> {
1636
+ try {
1637
+ if (this.context.publicKey === undefined) {
1638
+ throw new Error("No identity provided for creating record");
1639
+ }
1640
+ // 验证身份,用节点的pubkey
1641
+ const identity = this.context.publicKey;
1642
+ // 获取并验证连接器
1643
+ const [con, ok] = this.getConnectorProtected(id, options.apiToken);
1644
+
1645
+ if (!ok) {
1646
+ throw new Error("Cannot create record: thread in use");
1647
+ } else if (con) {
1648
+ await con.validateNetRecordBody(body, identity as Ed25519PubKey);
1520
1649
  }
1521
- }
1522
-
1523
- // 更新头部信息
1524
- const head: Head = {
1525
- id: tr.value().cid(),
1526
- counter: lg.head.counter + 1
1527
- };
1528
-
1529
- await this.logstore.headBook.setHead(id, lg.id, head);
1530
-
1531
- // 设置记录点(每10000个记录设置一个检查点)
1532
- await this.setThreadLogPoint(id, lg.id, lg.head?.counter + 1, tr.value().cid());
1533
-
1534
- console.debug(`Created record ${tr.value().cid()} (thread=${id}, log=${lg.id})`);
1535
-
1536
- // 推送记录到节点
1537
- if (this.server) {
1538
- this.pushRecord(id, lg.id, tr.value(), lg.head.counter + 1);
1539
- }
1540
-
1541
- return tr;
1542
- } catch (error) {
1543
- throw error;
1544
- }
1545
- }
1546
1650
 
1547
- /**
1548
- * 获取或创建当前身份的日志
1549
- */
1550
- async getOrCreateLog(id: ThreadID, identity: PublicKey): Promise<IThreadLogInfo> {
1551
- // 默认使用当前主机身份,如果未提供
1552
- if (!identity) {
1553
- throw new Error("No identity provided");
1554
- }
1555
-
1556
- // 尝试获取此身份的现有日志ID
1557
- const lidb = await this.logstore.metadata.getBytes(id, identity.toString());
1558
-
1559
- // 检查旧式"自有"日志
1560
- if (!lidb && identity.equals(this.context.publicKey)) {
1561
- const thrd = await this.logstore.getThread(id);
1562
- const ownLog = thrd.getFirstPrivKeyLog();
1563
- if (ownLog) {
1564
- return ownLog;
1565
- }
1566
- } else if (lidb) {
1567
- // 获取现有日志
1568
- const lidbstr = new TextDecoder().decode(lidb);
1569
- const lid = peerIdFromString(lidbstr);
1570
- return this.logstore.getLog(id, lid);
1571
- }
1572
-
1573
- // 创建新日志,如果不存在
1574
- return this.createLog(id, undefined, identity);
1575
- }
1651
+ // 获取或创建日志
1652
+ const lg = await this.getOrCreateLog(id, identity);
1576
1653
 
1577
- /**
1578
- * 创建新记录
1579
- */
1580
- async newRecord(id: ThreadID, lg: IThreadLogInfo, body: IPLDNode, identity: PublicKey): Promise<IRecord> {
1581
- // 获取threaddb 服务密钥
1582
- const serviceKey = await this.logstore.keyBook.serviceKey(id);
1583
- if (!serviceKey) {
1584
- throw new Error("No service key for thread");
1585
- }
1586
- if (!this.context.publicKey) {
1587
- throw new Error("No identity provided for creating record");
1588
- }
1589
- const heads = await this.logstore.headBook.heads(id, lg.id)
1590
- // 创建事件
1591
- const sk = SymmetricKey.fromSymKey(serviceKey);
1592
- const readKey = await this.logstore.keyBook.readKey(id);
1593
- const rk = readKey ? SymmetricKey.fromSymKey(readKey) : null;
1594
- if (!rk) {
1595
- throw new Error("No read key for thread");
1596
- }
1597
- const event = await CreateEvent(
1598
- this.bstore,
1599
- body as Node,
1600
- rk
1601
- );
1602
- // 将事件保存到存储
1603
- await this.bstore.put(event.cid(), event.data());
1604
- let prev:CID|undefined = undefined;
1605
- if (heads && heads.length > 0 ){
1606
- prev = heads[0]!.id;
1607
- }
1608
- const rec = await CreateRecord(
1609
- this.bstore,
1610
- {
1611
- block: event,
1612
- prev: prev,
1613
- key: lg.privKey as Ed25519PrivKey,
1614
- pubKey: this.context.publicKey,
1615
- serviceKey: sk
1616
- } as CreateRecordConfig)
1617
-
1618
-
1619
- return rec
1620
- }
1654
+ // 创建新记录
1655
+ const r = await this.newRecord(id, lg, body, identity);
1656
+
1657
+ // 创建threaddb 记录
1658
+ const tr = newRecord(r, id, lg.id);
1659
+ if (!lg.head) {
1660
+ lg.head = {
1661
+ counter: 0,
1662
+ };
1663
+ }
1621
1664
 
1665
+ // 更新头部信息
1666
+ const head: Head = {
1667
+ id: tr.value().cid(),
1668
+ counter: lg.head.counter + 1,
1669
+ };
1622
1670
 
1671
+ await this.logstore.headBook.setHead(id, lg.id, head);
1623
1672
 
1624
- /**
1625
- * 推送记录到日志地址和threaddb 主题
1626
- * @param tid threaddb ID
1627
- * @param lid 对等点ID
1628
- * @param rec 记录对象
1629
- * @param counter 计数器
1630
- */
1631
- async pushRecord(tid: ThreadID, lid: PeerId, rec: IRecord, counter: number): Promise<void> {
1632
- try {
1633
- // 收集已知的写入器地址
1634
- const addrs: TMultiaddr[] = [];
1635
- const info = await this.logstore.getThread(tid);
1636
-
1637
- // 收集所有日志的地址
1638
- for (const l of info.logs) {
1639
- if (l.addrs && l.addrs.length > 0) {
1640
- addrs.push(...l.addrs);
1673
+ // 设置记录点(每10000个记录设置一个检查点)
1674
+ await this.setThreadLogPoint(
1675
+ id,
1676
+ lg.id,
1677
+ lg.head?.counter + 1,
1678
+ tr.value().cid()
1679
+ );
1680
+
1681
+ console.debug(
1682
+ `Created record ${tr.value().cid()} (thread=${id}, log=${lg.id})`
1683
+ );
1684
+
1685
+ // 推送记录到节点
1686
+ if (this.server) {
1687
+ this.pushRecord(id, lg.id, tr.value(), lg.head.counter + 1);
1641
1688
  }
1689
+
1690
+ return tr;
1691
+ } catch (error) {
1692
+ throw error;
1642
1693
  }
1643
- const peers = await this.getPeers(tid);
1644
- if (!peers) {
1645
- throw new Error(`No peers for thread ${tid}`);
1694
+ }
1695
+
1696
+ /**
1697
+ * 获取或创建当前身份的日志
1698
+ */
1699
+ async getOrCreateLog(
1700
+ id: ThreadID,
1701
+ identity: PublicKey
1702
+ ): Promise<IThreadLogInfo> {
1703
+ // 默认使用当前主机身份,如果未提供
1704
+ if (!identity) {
1705
+ throw new Error("No identity provided");
1646
1706
  }
1647
- // 向每个对等点推送
1648
- for (const p of peers) {
1649
- // 跳过无效对等点
1650
- if (!p || p.toString() === "") {
1651
- continue;
1652
- }
1653
- try {
1654
- const [client,_] = await this.getClient(p);
1655
- if (!client) {
1656
- continue
1657
- }
1658
- const dbClient = new DBClient(client, this.dc,this,this.logstore);
1659
- // 启动异步推送(不等待完成)
1660
- dbClient.pushRecordToPeer(tid, lid, rec, counter);
1661
- }catch (err) {
1662
- continue; // 继续处理其他对等点
1707
+
1708
+ // 尝试获取此身份的现有日志ID
1709
+ const lidb = await this.logstore.metadata.getBytes(id, identity.toString());
1710
+
1711
+ // 检查旧式"自有"日志
1712
+ if (!lidb && identity.equals(this.context.publicKey)) {
1713
+ const thrd = await this.logstore.getThread(id);
1714
+ const ownLog = thrd.getFirstPrivKeyLog();
1715
+ if (ownLog) {
1716
+ return ownLog;
1663
1717
  }
1664
-
1718
+ } else if (lidb) {
1719
+ // 获取现有日志
1720
+ const lidbstr = new TextDecoder().decode(lidb);
1721
+ const lid = peerIdFromString(lidbstr);
1722
+ return this.logstore.getLog(id, lid);
1665
1723
  }
1666
- } catch (err) {
1667
- throw new Error(`Failed to push record: ${err instanceof Error ? err.message : String(err)}`);
1724
+
1725
+ // 创建新日志,如果不存在
1726
+ return this.createLog(id, undefined, identity);
1668
1727
  }
1669
- }
1670
1728
 
1671
- /**
1672
- * 与拥有该threaddb 的对等节点交换threaddb 记录
1673
- * @param tid threaddb ID
1674
- */
1675
- async exchange(tid: ThreadID): Promise<void> {
1676
- try {
1677
- // 获取threaddb 信息
1678
- const info = await this.logstore.getThread(tid);
1679
-
1680
- // 收集所有日志的地址
1681
- const addrs: TMultiaddr[] = [];
1682
- for (const lg of info.logs) {
1683
- addrs.push(...lg.addrs);
1729
+ /**
1730
+ * 创建新记录
1731
+ */
1732
+ async newRecord(
1733
+ id: ThreadID,
1734
+ lg: IThreadLogInfo,
1735
+ body: IPLDNode,
1736
+ identity: PublicKey
1737
+ ): Promise<IRecord> {
1738
+ // 获取threaddb 服务密钥
1739
+ const serviceKey = await this.logstore.keyBook.serviceKey(id);
1740
+ if (!serviceKey) {
1741
+ throw new Error("No service key for thread");
1684
1742
  }
1685
-
1686
- // 获取唯一对等点
1687
- let peers = await this.uniquePeers(addrs);
1688
-
1689
- // 尝试从外部对象获取对等点
1690
- const extPeers = await this.getPeers(tid);
1691
- if (extPeers && extPeers.length > 0) {
1692
- peers = extPeers;
1743
+ if (!this.context.publicKey) {
1744
+ throw new Error("No identity provided for creating record");
1693
1745
  }
1694
-
1695
- // 与每个对等点交换
1696
- for (const pid of peers) {
1697
- // 使用异步方式处理交换,不等待完成
1698
- this.exchangeWithPeer(pid, tid).catch(err => {
1699
- console.error(`Error exchanging with peer ${pid}: ${err instanceof Error ? err.message : String(err)}`);
1700
- });
1746
+ const heads = await this.logstore.headBook.heads(id, lg.id);
1747
+ // 创建事件
1748
+ const sk = SymmetricKey.fromSymKey(serviceKey);
1749
+ const readKey = await this.logstore.keyBook.readKey(id);
1750
+ const rk = readKey ? SymmetricKey.fromSymKey(readKey) : null;
1751
+ if (!rk) {
1752
+ throw new Error("No read key for thread");
1701
1753
  }
1702
- } catch (err) {
1703
- throw new Error(`Exchange failed: ${err instanceof Error ? err.message : String(err)}`);
1754
+ const event = await CreateEvent(this.bstore, body as Node, rk);
1755
+ // 将事件保存到存储
1756
+ await this.bstore.put(event.cid(), event.data());
1757
+ let prev: CID | undefined = undefined;
1758
+ if (heads && heads.length > 0) {
1759
+ prev = heads[0]!.id;
1760
+ }
1761
+ const rec = await CreateRecord(this.bstore, {
1762
+ block: event,
1763
+ prev: prev,
1764
+ key: lg.privKey as Ed25519PrivKey,
1765
+ pubKey: this.context.publicKey,
1766
+ serviceKey: sk,
1767
+ } as CreateRecordConfig);
1768
+
1769
+ return rec;
1704
1770
  }
1705
- }
1706
1771
 
1707
- /**
1708
- * 与单个对等点交换threaddb 边缘
1709
- * @param pid 对等点ID
1710
- * @param tid threaddb ID
1711
- */
1712
- private async exchangeWithPeer(pid: PeerId, tid: ThreadID): Promise<void> {
1713
- try {
1714
- // 获取客户端连接
1715
- const [client,_] = await this.getClient(pid);
1716
- if (!client) {
1717
- return;
1772
+ /**
1773
+ * 推送记录到日志地址和threaddb 主题
1774
+ * @param tid threaddb ID
1775
+ * @param lid 对等点ID
1776
+ * @param rec 记录对象
1777
+ * @param counter 计数器
1778
+ */
1779
+ async pushRecord(
1780
+ tid: ThreadID,
1781
+ lid: PeerId,
1782
+ rec: IRecord,
1783
+ counter: number
1784
+ ): Promise<void> {
1785
+ try {
1786
+ // 收集已知的写入器地址
1787
+ const addrs: TMultiaddr[] = [];
1788
+ const info = await this.logstore.getThread(tid);
1789
+
1790
+ // 收集所有日志的地址
1791
+ for (const l of info.logs) {
1792
+ if (l.addrs && l.addrs.length > 0) {
1793
+ addrs.push(...l.addrs);
1794
+ }
1795
+ }
1796
+ const peers = await this.getPeers(tid);
1797
+ if (!peers) {
1798
+ throw new Error(`No peers for thread ${tid}`);
1799
+ }
1800
+ // 向每个对等点推送
1801
+ for (const p of peers) {
1802
+ // 跳过无效对等点
1803
+ if (!p || p.toString() === "") {
1804
+ continue;
1805
+ }
1806
+ try {
1807
+ const [client, _] = await this.getClient(p);
1808
+ if (!client) {
1809
+ continue;
1810
+ }
1811
+ const dbClient = new DBClient(client, this.dc, this, this.logstore);
1812
+ // 启动异步推送(不等待完成)
1813
+ dbClient.pushRecordToPeer(tid, lid, rec, counter);
1814
+ } catch (err) {
1815
+ continue; // 继续处理其他对等点
1816
+ }
1817
+ }
1818
+ } catch (err) {
1819
+ throw new Error(
1820
+ `Failed to push record: ${
1821
+ err instanceof Error ? err.message : String(err)
1822
+ }`
1823
+ );
1718
1824
  }
1719
-
1720
- // 创建数据库客户端
1721
- const dbClient = new DBClient(client, this.dc,this,this.logstore);
1722
-
1723
- // 交换边缘
1724
- await dbClient.exchangeEdges([tid]);
1725
- } catch (err) {
1726
- throw err;
1727
1825
  }
1728
- }
1729
1826
 
1827
+ /**
1828
+ * 与拥有该threaddb 的对等节点交换threaddb 记录
1829
+ * @param tid threaddb ID
1830
+ */
1831
+ async exchange(tid: ThreadID): Promise<void> {
1832
+ try {
1833
+ // 获取threaddb 信息
1834
+ const info = await this.logstore.getThread(tid);
1730
1835
 
1836
+ // 收集所有日志的地址
1837
+ const addrs: TMultiaddr[] = [];
1838
+ for (const lg of info.logs) {
1839
+ addrs.push(...lg.addrs);
1840
+ }
1731
1841
 
1732
- /**
1733
- * 获取受保护的threaddb 连接器
1734
- */
1735
- getConnectorProtected(id: ThreadID, token?: Token): [Connector | undefined, boolean] {
1736
- const [conn, exists] = this.getConnector(id);
1737
-
1738
- if (!exists) {
1739
- return [undefined, true]; // threaddb 未被连接器使用
1740
- }
1741
-
1742
- if (!token || !conn?.token || !this.bytesEqual(token, conn.token)) {
1743
- return [undefined, false]; // 无效令牌
1842
+ // 获取唯一对等点
1843
+ let peers = await this.uniquePeers(addrs);
1844
+
1845
+ // 尝试从外部对象获取对等点
1846
+ const extPeers = await this.getPeers(tid);
1847
+ if (extPeers && extPeers.length > 0) {
1848
+ peers = extPeers;
1849
+ }
1850
+
1851
+ // 与每个对等点交换
1852
+ for (const pid of peers) {
1853
+ // 使用异步方式处理交换,不等待完成
1854
+ this.exchangeWithPeer(pid, tid).catch((err) => {
1855
+ console.error(
1856
+ `Error exchanging with peer ${pid}: ${
1857
+ err instanceof Error ? err.message : String(err)
1858
+ }`
1859
+ );
1860
+ });
1861
+ }
1862
+ } catch (err) {
1863
+ throw new Error(
1864
+ `Exchange failed: ${err instanceof Error ? err.message : String(err)}`
1865
+ );
1866
+ }
1744
1867
  }
1745
-
1746
- return [conn, true];
1747
- }
1748
1868
 
1869
+ /**
1870
+ * 与单个对等点交换threaddb 边缘
1871
+ * @param pid 对等点ID
1872
+ * @param tid threaddb ID
1873
+ */
1874
+ private async exchangeWithPeer(pid: PeerId, tid: ThreadID): Promise<void> {
1875
+ try {
1876
+ // 获取客户端连接
1877
+ const [client, _] = await this.getClient(pid);
1878
+ if (!client) {
1879
+ return;
1880
+ }
1749
1881
 
1882
+ // 创建数据库客户端
1883
+ const dbClient = new DBClient(client, this.dc, this, this.logstore);
1750
1884
 
1885
+ // 交换边缘
1886
+ await dbClient.exchangeEdges([tid]);
1887
+ } catch (err) {
1888
+ throw err;
1889
+ }
1890
+ }
1751
1891
 
1892
+ /**
1893
+ * 获取受保护的threaddb 连接器
1894
+ */
1895
+ getConnectorProtected(
1896
+ id: ThreadID,
1897
+ token?: Token
1898
+ ): [Connector | undefined, boolean] {
1899
+ const [conn, exists] = this.getConnector(id);
1900
+
1901
+ if (!exists) {
1902
+ return [undefined, true]; // threaddb 未被连接器使用
1903
+ }
1752
1904
 
1905
+ if (!token || !conn?.token || !this.bytesEqual(token, conn.token)) {
1906
+ return [undefined, false]; // 无效令牌
1907
+ }
1908
+
1909
+ return [conn, true];
1910
+ }
1753
1911
 
1754
-
1755
1912
  /**
1756
1913
  * 检查两个字节数组是否相等
1757
1914
  */
1758
- bytesEqual(a: Uint8Array, b: Uint8Array): boolean {
1915
+ bytesEqual(a: Uint8Array, b: Uint8Array): boolean {
1759
1916
  if (a.length !== b.length) return false;
1760
-
1917
+
1761
1918
  for (let i = 0; i < a.length; i++) {
1762
1919
  if (a[i] !== b[i]) return false;
1763
1920
  }
1764
-
1921
+
1765
1922
  return true;
1766
1923
  }
1767
-
1768
-
1769
-
1770
-
1771
1924
  }
1772
1925
 
1773
-
1774
-
1775
- /**
1926
+ /**
1776
1927
  * 记录收集器,用于合并多个对等点的记录
1928
+ * 线程安全版本
1777
1929
  */
1778
- class RecordCollector {
1779
- private records: Map<string, Map<string, any>> = new Map();
1780
- private counters: Map<string, number> = new Map();
1781
-
1782
- updateHeadCounter(logId: string, counter: number): void {
1930
+ class RecordCollector {
1931
+ private records: Map<string, Map<string, any>> = new Map();
1932
+ private counters: Map<string, number> = new Map();
1933
+ private mutex: AsyncMutex = new AsyncMutex();
1934
+
1935
+ async updateHeadCounter(logId: string, counter: number): Promise<void> {
1936
+ await this.mutex.acquire();
1937
+ try {
1783
1938
  const current = this.counters.get(logId) || 0;
1784
1939
  if (counter > current) {
1785
1940
  this.counters.set(logId, counter);
1786
1941
  }
1942
+ } finally {
1943
+ this.mutex.release();
1787
1944
  }
1788
-
1789
- store(logId: string, record: any): void {
1945
+ }
1946
+
1947
+ async store(logId: string, record: any): Promise<void> {
1948
+ await this.mutex.acquire();
1949
+ try {
1790
1950
  let logRecords = this.records.get(logId);
1791
1951
  if (!logRecords) {
1792
1952
  logRecords = new Map();
@@ -1795,23 +1955,47 @@ getConnectorProtected(id: ThreadID, token?: Token): [Connector | undefined, bool
1795
1955
  // 使用记录的CID作为键,避免重复
1796
1956
  const key = record.cid().toString();
1797
1957
  logRecords.set(key, record);
1958
+ } finally {
1959
+ this.mutex.release();
1798
1960
  }
1799
-
1800
- list(): Record<string, PeerRecords> {
1801
- const result: Record<string, PeerRecords> = {};
1802
-
1803
- this.records.forEach((logRecords, logId) => {
1804
- const records = Array.from(logRecords.values());
1805
- result[logId] = {
1806
- records,
1807
- counter: this.counters.get(logId) || 0
1808
- };
1961
+ }
1962
+
1963
+ async batchUpdate(logId: string, rs: PeerRecords): Promise<void> {
1964
+ await this.mutex.acquire();
1965
+ try {
1966
+ // 更新计数器
1967
+ const current = this.counters.get(logId) || 0;
1968
+ if (rs.counter > current) {
1969
+ this.counters.set(logId, rs.counter);
1970
+ }
1971
+
1972
+ // 存储记录
1973
+ let logRecords = this.records.get(logId);
1974
+ if (!logRecords) {
1975
+ logRecords = new Map();
1976
+ this.records.set(logId, logRecords);
1977
+ }
1978
+
1979
+ rs.records.forEach(record => {
1980
+ const key = record.cid().toString();
1981
+ logRecords!.set(key, record);
1809
1982
  });
1810
-
1811
- return result;
1983
+ } finally {
1984
+ this.mutex.release();
1812
1985
  }
1813
1986
  }
1814
1987
 
1988
+ list(): Record<string, PeerRecords> {
1989
+ const result: Record<string, PeerRecords> = {};
1815
1990
 
1991
+ this.records.forEach((logRecords, logId) => {
1992
+ const records = Array.from(logRecords.values());
1993
+ result[logId] = {
1994
+ records,
1995
+ counter: this.counters.get(logId) || 0,
1996
+ };
1997
+ });
1816
1998
 
1817
-
1999
+ return result;
2000
+ }
2001
+ }