web-dc-api 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/cjs/babel-tools-BoLJtxtW.js +1 -0
  2. package/dist/cjs/cache-CRG9ugF3.js +1 -0
  3. package/dist/cjs/helia-core-B1Xqha7a.js +1 -0
  4. package/dist/cjs/helia-core-D8Uv1KjQ.js +1 -0
  5. package/dist/cjs/{index-B9UlHr3P.js → index-DtuGISOe.js} +1 -1
  6. package/dist/cjs/index.js +1 -1
  7. package/dist/cjs/{jwt-CcLVpKo_.js → jwt-DW2WimlW.js} +1 -1
  8. package/dist/cjs/polkadot-api-7PhQf3ws.js +1 -0
  9. package/dist/cjs/polkadot-api-CtrJVWuZ.js +1 -0
  10. package/dist/cjs/protobuf-BYUoud7y.js +1 -0
  11. package/dist/cjs/{validation-DfsHeOHP.js → validation-DPN2BqQd.js} +1 -1
  12. package/dist/dc.min.js +1 -1
  13. package/dist/esm/chunks/babel-tools-sSecqeD4.js +1 -0
  14. package/dist/esm/chunks/cache-C0pUsIbC.js +1 -0
  15. package/dist/esm/chunks/helia-core-BxMqyK2Y.js +1 -0
  16. package/dist/esm/chunks/helia-core-DMXRpcO-.js +1 -0
  17. package/dist/esm/chunks/{index-BUlGxni-.js → index-CsnoYfJo.js} +1 -1
  18. package/dist/esm/chunks/{jwt-DLILrtNW.js → jwt-DfD7pIy4.js} +1 -1
  19. package/dist/esm/chunks/polkadot-api-5Y9Bw8VT.js +1 -0
  20. package/dist/esm/chunks/polkadot-api-D69Ioun_.js +1 -0
  21. package/dist/esm/chunks/protobuf-25futi__.js +1 -0
  22. package/dist/esm/chunks/{validation-D9YLFwug.js → validation-C90ZcLFM.js} +1 -1
  23. package/dist/esm/index.js +1 -1
  24. package/dist/index.d.ts +3 -2
  25. package/lib/common/define.ts +1 -1
  26. package/lib/implements/threaddb/common/logstore.ts +1 -1
  27. package/lib/implements/threaddb/core/logstore.ts +2 -2
  28. package/lib/implements/threaddb/dbclient.ts +34 -11
  29. package/lib/implements/threaddb/lsstoreds/addr_book.ts +134 -48
  30. package/lib/implements/threaddb/lsstoreds/headbook.ts +48 -24
  31. package/lib/implements/threaddb/net/net.ts +18 -3
  32. package/lib/implements/wallet/manager.ts +4 -2
  33. package/lib/interfaces/components/news-component.ts +0 -0
  34. package/lib/modules/auth-module.ts +2 -2
  35. package/lib/modules/components/news-components.ts +390 -0
  36. package/package.json +1 -1
  37. package/dist/cjs/babel-tools-BWeUL20Q.js +0 -1
  38. package/dist/cjs/cache-DB5vOYUa.js +0 -1
  39. package/dist/cjs/helia-core-CGrygIE7.js +0 -1
  40. package/dist/cjs/polkadot-api-9SXYLDre.js +0 -1
  41. package/dist/cjs/protobuf-89JdugaK.js +0 -1
  42. package/dist/esm/chunks/babel-tools-CmeA0jUH.js +0 -1
  43. package/dist/esm/chunks/cache-NFMLvSTk.js +0 -1
  44. package/dist/esm/chunks/helia-core-BkEzHB9G.js +0 -1
  45. package/dist/esm/chunks/polkadot-api-BIp1_8eq.js +0 -1
  46. package/dist/esm/chunks/protobuf-De5gI78A.js +0 -1
package/dist/index.d.ts CHANGED
@@ -2904,7 +2904,7 @@ interface AddrBook {
2904
2904
  clearAddrs(threadId: ThreadID, peerId: PeerId): Promise<void>;
2905
2905
  logsWithAddrs(threadId: ThreadID): Promise<PeerId[]>;
2906
2906
  threadsFromAddrs(): Promise<ThreadID[]>;
2907
- addrsEdge(threadId: ThreadID): Promise<number>;
2907
+ addrsEdge(threadId: ThreadID, exceptPeerId?: string): Promise<bigint>;
2908
2908
  dumpAddrs(): Promise<DumpAddrBook>;
2909
2909
  restoreAddrs(book: DumpAddrBook): Promise<void>;
2910
2910
  }
@@ -2915,7 +2915,7 @@ interface HeadBook {
2915
2915
  setHeads(threadId: ThreadID, peerId: PeerId, heads: Head[]): Promise<void>;
2916
2916
  heads(threadId: ThreadID, peerId: PeerId): Promise<Head[]>;
2917
2917
  clearHeads(threadId: ThreadID, peerId: PeerId): Promise<void>;
2918
- headsEdge(threadId: ThreadID): Promise<number>;
2918
+ headsEdge(threadId: ThreadID): Promise<bigint>;
2919
2919
  dumpHeads(): Promise<DumpHeadBook>;
2920
2920
  restoreHeads(book: DumpHeadBook): Promise<void>;
2921
2921
  }
@@ -2995,6 +2995,7 @@ declare class DBClient {
2995
2995
  * @throws 错误,包括特定的"No address edge"和"No heads edge"错误
2996
2996
  */
2997
2997
  private localEdges;
2998
+ private edgeToBigInt;
2998
2999
  /**
2999
3000
  * 调度日志更新
3000
3001
  * @param tid threaddb ID
@@ -23,7 +23,7 @@ const walletOpenVersion =
23
23
  let _baseUrl = "";
24
24
  let _walletOrigin = "";
25
25
  if (true) {
26
- _baseUrl = "/v0_0_18";
26
+ _baseUrl = "/v0_0_19";
27
27
  _walletOrigin = "https://wallet.dcnetio.com";
28
28
 
29
29
  if (walletOpenOrgin) {
@@ -11,7 +11,7 @@ import {Key as ThreadKey} from './key';
11
11
  import {ThreadInfo} from '../core/core';
12
12
  import {Errors} from '../core/db';
13
13
 
14
- export const PermanentAddrTTL = 2^53-1; // 使用 bigint 精确表示 64 位整数
14
+ export const PermanentAddrTTL = 2**53-1;
15
15
 
16
16
 
17
17
  const managedSuffix = "/managed";
@@ -63,7 +63,7 @@ export interface AddrBook {
63
63
  clearAddrs(threadId: ThreadID, peerId: PeerId): Promise<void>;
64
64
  logsWithAddrs(threadId: ThreadID): Promise<PeerId[]>;
65
65
  threadsFromAddrs(): Promise<ThreadID[]>;
66
- addrsEdge(threadId: ThreadID): Promise<number>;
66
+ addrsEdge(threadId: ThreadID,exceptPeerId?:string): Promise<bigint>;
67
67
  dumpAddrs(): Promise<DumpAddrBook>;
68
68
  restoreAddrs(book: DumpAddrBook): Promise<void>;
69
69
  }
@@ -75,7 +75,7 @@ export interface HeadBook {
75
75
  setHeads(threadId: ThreadID, peerId: PeerId, heads: Head[]): Promise<void>;
76
76
  heads(threadId: ThreadID, peerId: PeerId): Promise<Head[]>;
77
77
  clearHeads(threadId: ThreadID, peerId: PeerId): Promise<void>;
78
- headsEdge(threadId: ThreadID): Promise<number>;
78
+ headsEdge(threadId: ThreadID): Promise<bigint>;
79
79
  dumpHeads(): Promise<DumpHeadBook>;
80
80
  restoreHeads(book: DumpHeadBook): Promise<void>;
81
81
  }
@@ -1,5 +1,6 @@
1
1
  import type { Client } from "../../common/dcapi";
2
2
  import type { Multiaddr as TMultiaddr } from "@multiformats/multiaddr";
3
+ import Long from "long";
3
4
  import { extractPublicKeyFromPeerId } from "../../common/dc-key/keyManager";
4
5
  import { SymmetricKey} from './common/key';
5
6
  import { NewThreadOptions } from './core/options';
@@ -188,8 +189,8 @@ async exchangeEdges(threadIds: ThreadID[]): Promise<void> {
188
189
  // 添加到请求中
189
190
  body.threads.push({
190
191
  threadID: tid.toBytes(),
191
- headsEdge: headsEdge,
192
- addressEdge: addrEdge
192
+ headsEdge: Long.fromString(headsEdge.toString(), true),
193
+ addressEdge: Long.fromString(addrEdge.toString(), true)
193
194
  });
194
195
  } catch (err:any) {
195
196
  if (err.message !== "No address edge" &&
@@ -238,8 +239,8 @@ async exchangeEdges(threadIds: ThreadID[]): Promise<void> {
238
239
  let addrEdgeLocal = 0, headsEdgeLocal = 0;
239
240
  try {
240
241
  const localEdges = await this.localEdges(tid);
241
- addrEdgeLocal = localEdges.addrEdge;
242
- headsEdgeLocal = localEdges.headsEdge;
242
+ addrEdgeLocal = Number(localEdges.addrEdge)||0;
243
+ headsEdgeLocal = Number(localEdges.headsEdge)||0;
243
244
  } catch (err:any) {
244
245
  // 允许本地边缘为空
245
246
  if (err.message !== "No address edge" &&
@@ -250,7 +251,7 @@ async exchangeEdges(threadIds: ThreadID[]): Promise<void> {
250
251
  }
251
252
  let needPush = false;
252
253
  // 检查地址边缘是否有更新
253
- const responseAddrEdge = edge.addressEdge || 0;
254
+ const responseAddrEdge = Number(edge.addressEdge ) || 0;
254
255
  if (responseAddrEdge !== 0 && responseAddrEdge !== addrEdgeLocal) {
255
256
  // 调度日志更新
256
257
  await this.scheduleUpdateLogs(tid);
@@ -258,7 +259,7 @@ async exchangeEdges(threadIds: ThreadID[]): Promise<void> {
258
259
  console.debug(`Log information update for thread ${tid} scheduled`);
259
260
  }
260
261
  // 检查头部边缘是否有更新
261
- const responseHeadEdge = edge.headsEdge || 0;
262
+ const responseHeadEdge = Number(edge.headsEdge) || 0;
262
263
  if (responseHeadEdge !== 0 && responseHeadEdge !== headsEdgeLocal) {
263
264
  // 调度记录更新
264
265
  await this.scheduleUpdateRecords(tid);
@@ -331,14 +332,23 @@ private async pushLogsHeadToPeer(tid: ThreadID): Promise<void> {
331
332
  * @returns 地址边缘值和头部边缘值的对象
332
333
  * @throws 错误,包括特定的"No address edge"和"No heads edge"错误
333
334
  */
334
- private async localEdges(tid: ThreadID): Promise<{ addrEdge: number, headsEdge: number }> {
335
+ private async localEdges(tid: ThreadID): Promise<{ addrEdge: bigint, headsEdge: bigint }> {
335
336
  // 使用默认值初始化头部边缘值
336
- let headsEdge = 0; // EmptyEdgeValue TS 中对应于 0
337
- let addrEdge = 0;
337
+ let headsEdge = 0n; // EmptyEdgeValue 对应 0n
338
+ let addrEdge = 0n;
338
339
 
339
340
  try {
340
- // 尝试获取地址边缘值
341
- addrEdge = await this.logstore.addrBook.addrsEdge(tid);
341
+ const exceptLogId = await this.logstore.metadata.getString(
342
+ tid,
343
+ "local_log_no_record_flag"
344
+ );
345
+ // 尝试获取地址边缘值
346
+ if (exceptLogId && exceptLogId?.length > 0) {
347
+ addrEdge = await this.logstore.addrBook.addrsEdge(tid,exceptLogId);
348
+ }else{
349
+ addrEdge = await this.logstore.addrBook.addrsEdge(tid);
350
+ }
351
+
342
352
  } catch (err:any) {
343
353
  // // 处理threaddb 未找到错误
344
354
  // if (err.message.includes("Thread not found")) {
@@ -363,6 +373,19 @@ private async localEdges(tid: ThreadID): Promise<{ addrEdge: number, headsEdge:
363
373
  // 返回边缘值对象
364
374
  return { addrEdge, headsEdge };
365
375
  }
376
+
377
+ private edgeToBigInt(value: number | Long | undefined | null): bigint {
378
+ if (value == null) {
379
+ return 0n;
380
+ }
381
+ if (typeof value === "number") {
382
+ return BigInt(value >>> 0);
383
+ }
384
+ if (Long.isLong(value)) {
385
+ return BigInt(value.toString());
386
+ }
387
+ return 0n;
388
+ }
366
389
  /**
367
390
  * 调度日志更新
368
391
  * @param tid threaddb ID
@@ -8,7 +8,11 @@ import * as pb from '../pb/lstore'
8
8
  import {uniqueLogIds,uniqueThreadIds,DSOptions,AllowEmptyRestore,dsThreadKey} from './global'
9
9
  import {LRUCache} from 'lru-cache'
10
10
  import { ThreadID } from '@textile/threads-id';
11
- import {DefaultOpts} from './global'
11
+ import {DefaultOpts} from './global';
12
+ import { TxnDatastoreExtended, Transaction } from '../core/db';
13
+ import * as buffer from "buffer/";
14
+ import { bases } from 'multiformats/basics';
15
+ const { Buffer } = buffer;
12
16
 
13
17
  const ADDR_FNV_OFFSET_BASIS = 0xcbf29ce484222325n;
14
18
  const ADDR_FNV_PRIME = 0x100000001b3n;
@@ -61,12 +65,12 @@ export class DsAddrBook implements AddrBook {
61
65
 
62
66
 
63
67
  private opts: DSOptions
64
- private ds: Datastore
68
+ private ds: TxnDatastoreExtended
65
69
  cache: LRUCache<CacheKey, AddrsRecord>
66
70
 
67
71
  constructor( ds: Datastore, opts: DSOptions) {
68
72
  this.opts = opts
69
- this.ds = ds
73
+ this.ds = ds as TxnDatastoreExtended
70
74
  this.cache = new LRUCache({ max: opts.CacheSize || 0 })
71
75
  }
72
76
 
@@ -90,7 +94,7 @@ export class DsAddrBook implements AddrBook {
90
94
  if (!item) return acc
91
95
  const addr = typeof item === 'string'
92
96
  ? multiaddr(item)
93
- : (item instanceof multiaddr ? item : null)
97
+ : (isMultiaddr(item) ? item : null)
94
98
  return addr ? [...acc, addr] : acc
95
99
  } catch {
96
100
  return acc
@@ -116,7 +120,7 @@ export class DsAddrBook implements AddrBook {
116
120
  // 更新现有地址
117
121
  outer: for (const [i, addr] of addrs.entries()) {
118
122
  for (const entry of record.data.addrs) {
119
- if (addr.bytes == entry.addr) {
123
+ if (bytes.compare(addr.bytes, entry.addr) === 0) {
120
124
  existed[i] = true
121
125
  entry.ttl = ttl
122
126
  entry.expiry = newExp
@@ -138,6 +142,7 @@ export class DsAddrBook implements AddrBook {
138
142
  record.dirty = true
139
143
  await this.cleanRecord(record)
140
144
  await this.flushRecord(record)
145
+ await this.invalidateEdge(t)
141
146
  } finally {
142
147
  release()
143
148
  }
@@ -158,6 +163,7 @@ export class DsAddrBook implements AddrBook {
158
163
  }
159
164
  await this.cleanRecord(record)
160
165
  await this.flushRecord(record)
166
+ await this.invalidateEdge(t)
161
167
  } finally {
162
168
  release()
163
169
  }
@@ -217,43 +223,108 @@ async threadsFromAddrs(): Promise<ThreadID[]> {
217
223
  }
218
224
  }
219
225
 
220
- async addrsEdge(t: ThreadID): Promise<number> {
221
- const key = this.genDSKey(t, DsAddrBook.logBookEdge.toString());
222
- try {
223
- const value = await this.ds.get(key);
224
- return Number(new DataView(value.buffer).getBigUint64(0));
225
- } catch (err: any) {
226
- if (err.code !== 'ERR_NOT_FOUND') {
227
- throw err;
226
+
227
+
228
+
229
+ async addrsEdge(t: ThreadID, exceptPeerId?: string): Promise<bigint> {
230
+ const key = dsThreadKey(t, DsAddrBook.logBookEdge);
231
+ const retries = 3
232
+ for (let attempt = 1; attempt <= retries; attempt++) {
233
+ try {
234
+ return await this.calculateEdge(t, key, exceptPeerId);
235
+ } catch (err: any) {
236
+ if (err.code !== 'TX_CONFLICT') throw err;
237
+ await this.randomDelay(attempt);
238
+ }
228
239
  }
240
+ throw new Error('Edge computation failed');
229
241
  }
230
242
 
231
- const result = this.ds.query({ prefix: this.genDSKey(t, DsAddrBook.logBookBase.toString()).toString()});
232
- const now = Date.now();
233
- const as: { logId: PeerId, addr: Uint8Array }[] = [];
243
+ private async calculateEdge(t: ThreadID, key: Key, exceptPeerId?: string): Promise<bigint> {
244
+ return this.withTransaction(async txn => {
245
+ try {
246
+ const data = await txn.get(key);
247
+ if (data) {
248
+ return this.decodeStoredEdge(data);
249
+ }
250
+ } catch (err: any) {
251
+ if (err.code !== 'ERR_NOT_FOUND') throw err;
252
+ }
234
253
 
235
- for await (const entry of result) {
236
- const { tid,pid, record } = this.decodeAddrEntry(entry, true);
237
- if (!record) {
238
- continue;
239
- }
240
- for (const addr of record.addrs) {
241
- if (addr.expiry > now) {
242
- as.push({ logId:pid, addr: addr.addr });
254
+ const addrs = await this.collectAddrs(txn, t, exceptPeerId);
255
+ if (addrs.length === 0) throw new Error('Thread not found');
256
+
257
+ const edge = this.computeAddrsEdge(addrs);
258
+ const buffer = Buffer.alloc(8);
259
+ this.writeEdgeValue(buffer, edge);
260
+
261
+ await txn.put(key, buffer);
262
+ return edge;
263
+ });
264
+ }
265
+
266
+ private async collectAddrs(txn: Transaction, t: ThreadID, exceptPeerId?: string): Promise<{ peerID: PeerId, addr: Uint8Array }[]> {
267
+ const query = { prefix: dsThreadKey(t, DsAddrBook.logBookBase).toString() };
268
+ const result = txn.query(query);
269
+ const now = Date.now();
270
+ const as: { peerID: PeerId, addr: Uint8Array }[] = [];
271
+
272
+ for await (const entry of result) {
273
+ const rawKey = entry.key as any;
274
+ const key = rawKey instanceof Key ? rawKey : new Key(rawKey.toString());
275
+ const pair: Pair = { key, value: entry.value };
276
+ const { tid,pid, record } = this.decodeAddrEntry(pair, true);
277
+ if (!record || (exceptPeerId && pid.toString() == exceptPeerId)) {
278
+ continue;
279
+ }
280
+ for (const addr of record.addrs) {
281
+ if (addr.expiry > now) {
282
+ as.push({ peerID:pid, addr: addr.addr });
283
+ }
243
284
  }
244
285
  }
286
+ return as;
287
+ }
288
+
289
+ private async withTransaction<T>(fn: (txn: Transaction) => Promise<T>): Promise<T> {
290
+ const txn = await this.ds.newTransactionExtended(false);
291
+ try {
292
+ const result = await fn(txn);
293
+ await txn.commit();
294
+ return result;
295
+ } catch (err: any) {
296
+ txn.discard();
297
+ throw err;
298
+ }
245
299
  }
246
300
 
247
- if (as.length === 0) {
248
- throw new Error('Thread not found');
301
+ private decodeStoredEdge(data: Uint8Array): bigint {
302
+ const buf: any = Buffer.from(data);
303
+ const reader = buf as unknown as { readBigUInt64BE?: (offset?: number) => bigint };
304
+ if (buf.length >= 8 && typeof reader.readBigUInt64BE === 'function') {
305
+ return reader.readBigUInt64BE(0);
306
+ }
307
+ if (buf.length >= 4) {
308
+ return BigInt(buf.readUInt32BE(0));
309
+ }
310
+ throw new Error('Corrupted head edge value');
249
311
  }
250
312
 
251
- const edge = this.computeAddrsEdge(as);
252
- const buff = new ArrayBuffer(8);
253
- new BigUint64Array(buff)[0] = BigInt(edge);
254
- await this.ds.put(key, new Uint8Array(buff));
255
- return edge;
256
- }
313
+ private writeEdgeValue(buf: any, value: bigint): void {
314
+ const writer = buf as unknown as { writeBigUInt64BE?: (val: bigint, offset?: number) => number };
315
+ if (typeof writer.writeBigUInt64BE === 'function') {
316
+ writer.writeBigUInt64BE(value, 0);
317
+ return;
318
+ }
319
+ buf.writeUInt32BE(Number(value & 0xffffffffn), 0);
320
+ }
321
+
322
+ private randomDelay(attempt: number): Promise<void> {
323
+ const delay = 50 * attempt + Math.random() * 30;
324
+ return new Promise(resolve =>
325
+ setTimeout(resolve, delay)
326
+ );
327
+ }
257
328
 
258
329
 
259
330
 
@@ -343,10 +414,11 @@ private async traverse(withAddrs: boolean): Promise<{ [key: string]: { [key: str
343
414
  }
344
415
 
345
416
 
346
- private decodeAddrEntry(entry: Pair, withAddrs: boolean): { tid: ThreadID, pid: PeerId, record?: pb.AddrBookRecord } {
347
- const kns = entry.key.toString().split('/');
417
+ private decodeAddrEntry(entry: any, withAddrs: boolean): { tid: ThreadID, pid: PeerId, record?: pb.AddrBookRecord } {
418
+ const keyStr = typeof entry.key === 'string' ? entry.key : entry.key.toString();
419
+ const kns = keyStr.split('/');
348
420
  if (kns.length < 3) {
349
- throw new Error(`bad addressbook key detected: ${entry.key}`);
421
+ throw new Error(`bad addressbook key detected: ${keyStr}`);
350
422
  }
351
423
 
352
424
  // get thread and log IDs from the key components
@@ -366,25 +438,39 @@ private async traverse(withAddrs: boolean): Promise<{ [key: string]: { [key: str
366
438
 
367
439
 
368
440
 
369
- private computeAddrsEdge(as: { logId: PeerId, addr: Uint8Array }[]): number {
370
- const sorted = [...as].sort((a, b) => {
371
- const left = a.logId.toString();
372
- const right = b.logId.toString();
373
- if (left === right) {
374
- return bytes.compare(a.addr, b.addr);
441
+ private computeAddrsEdge(as: { peerID: PeerId, addr: Uint8Array }[]): bigint {
442
+ const entries = as.map(item => ({
443
+ peerIDBytes: this.decodePeerId(item.peerID.toString(), new TextEncoder()),
444
+ addr: item.addr,
445
+ }));
446
+
447
+ entries.sort((a, b) => {
448
+ const peerIDCompare = bytes.compare(a.peerIDBytes, b.peerIDBytes);
449
+ if (peerIDCompare !== 0) {
450
+ return peerIDCompare;
375
451
  }
376
- return left < right ? -1 : 1;
452
+ return bytes.compare(a.addr, b.addr);
377
453
  });
378
454
 
379
- const encoder = new TextEncoder();
380
455
  let hash = ADDR_FNV_OFFSET_BASIS;
381
456
 
382
- for (const item of sorted) {
383
- hash = this.fnv1a64(encoder.encode(item.logId.toString()), hash);
384
- hash = this.fnv1a64(item.addr, hash);
457
+ for (const entry of entries) {
458
+ hash = this.fnv1a64(entry.peerIDBytes, hash);
459
+ hash = this.fnv1a64(entry.addr, hash);
385
460
  }
386
461
 
387
- return Number(hash);
462
+ return hash;
463
+ }
464
+
465
+ private decodePeerId(peerID: string, encoder: TextEncoder): Uint8Array {
466
+ if (!peerID) {
467
+ return new Uint8Array();
468
+ }
469
+ try {
470
+ return bases.base58btc.baseDecode(peerID);
471
+ } catch (err: any) {
472
+ return encoder.encode(peerID);
473
+ }
388
474
  }
389
475
 
390
476
  private fnv1a64(data: Uint8Array, initial: bigint): bigint {
@@ -460,4 +546,4 @@ class AsyncMutex {
460
546
  next?.()
461
547
  }
462
548
  }
463
- }
549
+ }
@@ -1,5 +1,7 @@
1
1
  import { CID } from 'multiformats/cid';
2
2
  import { Key,Pair } from 'interface-datastore';
3
+ import { bases } from 'multiformats/basics';
4
+
3
5
  import { Head, HeadBookRecord, serializeHeadBookRecord, deserializeHeadBookRecord } from '../core/head';
4
6
  import {
5
7
  TxnDatastoreExtended,
@@ -151,7 +153,7 @@ export class DsHeadBook implements HeadBook {
151
153
  await txn.put(key, recordBytes);
152
154
  }
153
155
 
154
- async headsEdge(tid: ThreadID, retries = 3): Promise<number> {
156
+ async headsEdge(tid: ThreadID, retries = 3): Promise<bigint> {
155
157
  const key = dsThreadKey(tid, hbEdge);
156
158
 
157
159
  for (let attempt = 1; attempt <= retries; attempt++) {
@@ -165,7 +167,7 @@ export class DsHeadBook implements HeadBook {
165
167
  throw new Error('Edge computation failed');
166
168
  }
167
169
 
168
- private async calculateEdge(tid: ThreadID, key: Key): Promise<number> {
170
+ private async calculateEdge(tid: ThreadID, key: Key): Promise<bigint> {
169
171
  return this.withTransaction(async txn => {
170
172
  try {
171
173
  const data = await txn.get(key);
@@ -184,7 +186,7 @@ export class DsHeadBook implements HeadBook {
184
186
  this.writeEdgeValue(buffer, edge);
185
187
 
186
188
  await txn.put(key, buffer);
187
- return Number(edge);
189
+ return edge;
188
190
  });
189
191
  }
190
192
 
@@ -217,27 +219,49 @@ export class DsHeadBook implements HeadBook {
217
219
  return heads;
218
220
  }
219
221
 
220
- private computeHeadsEdge(hs: LogHead[]): bigint {
221
- const sorted = [...hs].sort((a, b) => {
222
- if (a.logId === b.logId) {
223
- const left = a.head.id?.toString() ?? '';
224
- const right = b.head.id?.toString() ?? '';
225
- return left.localeCompare(right);
226
- }
227
- return a.logId.localeCompare(b.logId);
228
- });
222
+ private computeHeadsEdge(hs: LogHead[]): bigint {
223
+ const sorted = [...hs].sort((a, b) => {
224
+ if (a.logId === b.logId) {
225
+ return this.compareCidBytes(a.head.id?.bytes, b.head.id?.bytes);
226
+ }
227
+ return a.logId.localeCompare(b.logId);
228
+ });
229
229
 
230
- const encoder = new TextEncoder();
231
- let hash = FNV_OFFSET_BASIS;
230
+ const encoder = new TextEncoder();
231
+ let hash = FNV_OFFSET_BASIS;
232
232
 
233
- for (const item of sorted) {
234
- hash = this.fnv1a64(encoder.encode(item.logId), hash);
235
- const headBytes = item.head.id?.bytes ?? new Uint8Array();
236
- hash = this.fnv1a64(headBytes, hash);
237
- }
233
+ for (const item of sorted) {
234
+ const logIdBytes = this.decodeLogId(item.logId, encoder);
235
+ hash = this.fnv1a64(logIdBytes, hash);
236
+ const headBytes = item.head.id?.bytes ?? new Uint8Array();
237
+ hash = this.fnv1a64(headBytes, hash);
238
+ }
238
239
 
239
- return hash;
240
- }
240
+ return hash;
241
+ }
242
+
243
+ private compareCidBytes(left?: Uint8Array, right?: Uint8Array): number {
244
+ const a = left ?? new Uint8Array();
245
+ const b = right ?? new Uint8Array();
246
+ const min = Math.min(a.length, b.length);
247
+ for (let i = 0; i < min; i++) {
248
+ if (a[i] !== b[i]) {
249
+ return a[i] - b[i];
250
+ }
251
+ }
252
+ return a.length - b.length;
253
+ }
254
+
255
+ private decodeLogId(logId: string, encoder: TextEncoder): Uint8Array {
256
+ if (!logId) {
257
+ return new Uint8Array();
258
+ }
259
+ try {
260
+ return bases.base58btc.baseDecode(logId);
261
+ } catch (err: any) {
262
+ return encoder.encode(logId);
263
+ }
264
+ }
241
265
 
242
266
  private fnv1a64(data: Uint8Array, initial: bigint): bigint {
243
267
  let hash = initial;
@@ -253,14 +277,14 @@ private fnv1a64(data: Uint8Array, initial: bigint): bigint {
253
277
  await txn.delete(key);
254
278
  }
255
279
 
256
- private decodeStoredEdge(data: Uint8Array): number {
280
+ private decodeStoredEdge(data: Uint8Array): bigint {
257
281
  const buf: any = Buffer.from(data);
258
282
  const reader = buf as unknown as { readBigUInt64BE?: (offset?: number) => bigint };
259
283
  if (buf.length >= 8 && typeof reader.readBigUInt64BE === 'function') {
260
- return Number(reader.readBigUInt64BE(0));
284
+ return reader.readBigUInt64BE(0);
261
285
  }
262
286
  if (buf.length >= 4) {
263
- return buf.readUInt32BE(0);
287
+ return BigInt(buf.readUInt32BE(0));
264
288
  }
265
289
  throw new Error('Corrupted head edge value');
266
290
  }
@@ -82,6 +82,7 @@ import {
82
82
  import * as buffer from "buffer/";
83
83
  import { AsyncMutex } from "../common/AsyncMutex";
84
84
  import { DCContext } from "../../../interfaces";
85
+ import { time } from "console";
85
86
  const { Buffer } = buffer;
86
87
 
87
88
  /**
@@ -811,7 +812,12 @@ export class Network implements Net {
811
812
  managed: true,
812
813
  head: head,
813
814
  } as IThreadLogInfo;
814
-
815
+ //标记本地log没有生成任何记录
816
+ await this.logstore.metadata.putString(
817
+ id,
818
+ "local_log_no_record_flag",
819
+ `${peerId.toString()}`
820
+ );
815
821
  // 将日志添加到threaddb存储
816
822
  await this.logstore.addLog(id, logInfo);
817
823
  const logIDBytes = new TextEncoder().encode(peerId.toString());
@@ -1759,7 +1765,7 @@ export class Network implements Net {
1759
1765
 
1760
1766
  const sleep = (ms: number) =>
1761
1767
  new Promise<void>((resolve) => setTimeout(resolve, ms));
1762
-
1768
+ let haveRecordFlag = false;
1763
1769
  const loop = async () => {
1764
1770
  while (true) {
1765
1771
  try {
@@ -1770,6 +1776,15 @@ export class Network implements Net {
1770
1776
  }
1771
1777
  try {
1772
1778
  await this._doPushRecord(item.tid, item.lid, item.rec, item.counter);
1779
+ if (!haveRecordFlag) {
1780
+ await this.logstore.metadata.putString(
1781
+ item.tid,
1782
+ "local_log_no_record_flag",
1783
+ ""
1784
+ );
1785
+ haveRecordFlag = true;
1786
+ }
1787
+
1773
1788
  } catch (err) {
1774
1789
  console.error("pushRecord failed:", err);
1775
1790
  await sleep(200); // 简单退避,避免持续报错阻塞后续任务
@@ -1815,7 +1830,7 @@ export class Network implements Net {
1815
1830
  dbClient = new DBClient(client, this.dc, this, this.logstore);
1816
1831
  await dbClient.pushRecordToPeer(tid, lid, rec, counter);
1817
1832
  } catch (err) {
1818
- if (dbClient && err.message == Errors.ErrLogNotFound.message) { //log文件没绑定,进行绑定
1833
+ if (err instanceof Error && dbClient && err.message === Errors.ErrLogNotFound.message) { //log文件没绑定,进行绑定
1819
1834
  try {
1820
1835
  await this.context.dbManager?.addLogToThreadStart(null,tid, lid);
1821
1836
  const err = await dbClient.pushLogToPeer(tid, lid,rec); //推送本地log文件到对等节点,rec表示最新的记录
@@ -139,6 +139,7 @@ export class WalletManager {
139
139
  //非钱包插件
140
140
  return;
141
141
  }
142
+ console.log("debug================listenForWalletLoaded", new Date());
142
143
  if (data.type === "walletLoaded") {
143
144
  //钱包加载完成
144
145
  walletLoadedFlag = true;
@@ -158,7 +159,7 @@ export class WalletManager {
158
159
  clearInterval(interval);
159
160
  messageChannel.port1.close();
160
161
  } else {
161
- if (waitTimeCount >= 0) {
162
+ if (waitTimeCount > 0) {
162
163
  waitTimeCount--;
163
164
  } else {
164
165
  try {
@@ -218,7 +219,6 @@ export class WalletManager {
218
219
  (iframe as any).credentialless = true; // 去掉可以加快加载
219
220
  iframe.onload = async () => {
220
221
  console.log("debug================onload", new Date());
221
- resolve(true);
222
222
  };
223
223
  iframe.onerror = (error) => {
224
224
  console.error("openWallet error", error);
@@ -250,6 +250,7 @@ export class WalletManager {
250
250
  iframe.allowFullscreen = true;
251
251
  document.body.appendChild(iframe);
252
252
  console.log("debug================appendChild", new Date());
253
+ resolve(true);
253
254
  });
254
255
  }
255
256
 
@@ -549,6 +550,7 @@ export class WalletManager {
549
550
  return;
550
551
  }
551
552
  if (message.type === "walletLoaded") {
553
+ console.log("debug================walletLoaded", new Date());
552
554
  console.log("walletLoaded", message);
553
555
  console.log("event.origin", event.origin);
554
556
  console.log("walletOrigin", walletOrigin);
File without changes
@@ -488,8 +488,8 @@ export class AuthModule implements DCModule, IAuthOperations {
488
488
  }
489
489
  this.tokenTask = true;
490
490
 
491
- // 300秒一次心跳维持连接
492
- const period = 1000;
491
+ // 60秒一次心跳维持连接
492
+ const period = 60*1000;
493
493
  let count = 0;
494
494
  let waitCount = 0;
495
495