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.
- package/dist/cjs/babel-tools-BoLJtxtW.js +1 -0
- package/dist/cjs/cache-CRG9ugF3.js +1 -0
- package/dist/cjs/helia-core-B1Xqha7a.js +1 -0
- package/dist/cjs/helia-core-D8Uv1KjQ.js +1 -0
- package/dist/cjs/{index-B9UlHr3P.js → index-DtuGISOe.js} +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/{jwt-CcLVpKo_.js → jwt-DW2WimlW.js} +1 -1
- package/dist/cjs/polkadot-api-7PhQf3ws.js +1 -0
- package/dist/cjs/polkadot-api-CtrJVWuZ.js +1 -0
- package/dist/cjs/protobuf-BYUoud7y.js +1 -0
- package/dist/cjs/{validation-DfsHeOHP.js → validation-DPN2BqQd.js} +1 -1
- package/dist/dc.min.js +1 -1
- package/dist/esm/chunks/babel-tools-sSecqeD4.js +1 -0
- package/dist/esm/chunks/cache-C0pUsIbC.js +1 -0
- package/dist/esm/chunks/helia-core-BxMqyK2Y.js +1 -0
- package/dist/esm/chunks/helia-core-DMXRpcO-.js +1 -0
- package/dist/esm/chunks/{index-BUlGxni-.js → index-CsnoYfJo.js} +1 -1
- package/dist/esm/chunks/{jwt-DLILrtNW.js → jwt-DfD7pIy4.js} +1 -1
- package/dist/esm/chunks/polkadot-api-5Y9Bw8VT.js +1 -0
- package/dist/esm/chunks/polkadot-api-D69Ioun_.js +1 -0
- package/dist/esm/chunks/protobuf-25futi__.js +1 -0
- package/dist/esm/chunks/{validation-D9YLFwug.js → validation-C90ZcLFM.js} +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/index.d.ts +3 -2
- package/lib/common/define.ts +1 -1
- package/lib/implements/threaddb/common/logstore.ts +1 -1
- package/lib/implements/threaddb/core/logstore.ts +2 -2
- package/lib/implements/threaddb/dbclient.ts +34 -11
- package/lib/implements/threaddb/lsstoreds/addr_book.ts +134 -48
- package/lib/implements/threaddb/lsstoreds/headbook.ts +48 -24
- package/lib/implements/threaddb/net/net.ts +18 -3
- package/lib/implements/wallet/manager.ts +4 -2
- package/lib/interfaces/components/news-component.ts +0 -0
- package/lib/modules/auth-module.ts +2 -2
- package/lib/modules/components/news-components.ts +390 -0
- package/package.json +1 -1
- package/dist/cjs/babel-tools-BWeUL20Q.js +0 -1
- package/dist/cjs/cache-DB5vOYUa.js +0 -1
- package/dist/cjs/helia-core-CGrygIE7.js +0 -1
- package/dist/cjs/polkadot-api-9SXYLDre.js +0 -1
- package/dist/cjs/protobuf-89JdugaK.js +0 -1
- package/dist/esm/chunks/babel-tools-CmeA0jUH.js +0 -1
- package/dist/esm/chunks/cache-NFMLvSTk.js +0 -1
- package/dist/esm/chunks/helia-core-BkEzHB9G.js +0 -1
- package/dist/esm/chunks/polkadot-api-BIp1_8eq.js +0 -1
- 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<
|
|
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<
|
|
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
|
package/lib/common/define.ts
CHANGED
|
@@ -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
|
|
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<
|
|
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<
|
|
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:
|
|
335
|
+
private async localEdges(tid: ThreadID): Promise<{ addrEdge: bigint, headsEdge: bigint }> {
|
|
335
336
|
// 使用默认值初始化头部边缘值
|
|
336
|
-
let headsEdge =
|
|
337
|
-
let addrEdge =
|
|
337
|
+
let headsEdge = 0n; // EmptyEdgeValue 对应 0n
|
|
338
|
+
let addrEdge = 0n;
|
|
338
339
|
|
|
339
340
|
try {
|
|
340
|
-
|
|
341
|
-
|
|
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:
|
|
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
|
|
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
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
-
|
|
248
|
-
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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:
|
|
347
|
-
const
|
|
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: ${
|
|
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: {
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
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
|
|
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
|
|
383
|
-
hash = this.fnv1a64(
|
|
384
|
-
hash = this.fnv1a64(
|
|
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
|
|
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<
|
|
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<
|
|
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
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
231
|
-
|
|
230
|
+
const encoder = new TextEncoder();
|
|
231
|
+
let hash = FNV_OFFSET_BASIS;
|
|
232
232
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
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):
|
|
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
|
|
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
|
|
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
|
|
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
|