web-dc-api 0.1.5 → 0.1.7
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/index.js +1 -1
- package/dist/dc.min.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/index.d.ts +934 -878
- package/package.json +4 -8
- package/dist/cjs/helia-core-B1Xqha7a.js +0 -1
- package/dist/cjs/helia-core-D8Uv1KjQ.js +0 -1
- package/dist/cjs/polkadot-api-7PhQf3ws.js +0 -1
- package/dist/cjs/polkadot-api-CtrJVWuZ.js +0 -1
- package/dist/esm/chunks/helia-core-BxMqyK2Y.js +0 -1
- package/dist/esm/chunks/helia-core-DMXRpcO-.js +0 -1
- package/dist/esm/chunks/polkadot-api-5Y9Bw8VT.js +0 -1
- package/dist/esm/chunks/polkadot-api-D69Ioun_.js +0 -1
- package/lib/common/blowfish/block.ts +0 -259
- package/lib/common/blowfish/cipher.ts +0 -144
- package/lib/common/blowfish/const.ts +0 -195
- package/lib/common/chain.ts +0 -469
- package/lib/common/commonclient.ts +0 -202
- package/lib/common/constants.ts +0 -55
- package/lib/common/dc-key/ed25519.ts +0 -343
- package/lib/common/dc-key/keyManager.ts +0 -424
- package/lib/common/dcapi.ts +0 -98
- package/lib/common/dcutil.ts +0 -627
- package/lib/common/define.ts +0 -70
- package/lib/common/error.ts +0 -67
- package/lib/common/grpc-dc.ts +0 -104
- package/lib/common/module-system.ts +0 -184
- package/lib/common/service-worker.ts +0 -234
- package/lib/common/types/types.ts +0 -344
- package/lib/dc.ts +0 -701
- package/lib/implements/account/client.ts +0 -185
- package/lib/implements/account/manager.ts +0 -683
- package/lib/implements/aiproxy/client.ts +0 -357
- package/lib/implements/aiproxy/manager.ts +0 -670
- package/lib/implements/cache/client.ts +0 -105
- package/lib/implements/cache/manager.ts +0 -127
- package/lib/implements/comment/client.ts +0 -982
- package/lib/implements/comment/manager.ts +0 -1151
- package/lib/implements/dc/client.ts +0 -51
- package/lib/implements/dc/manager.ts +0 -33
- package/lib/implements/file/client.ts +0 -253
- package/lib/implements/file/file-cache-manager.ts +0 -142
- package/lib/implements/file/manager.ts +0 -1240
- package/lib/implements/file/seekableFileStream.ts +0 -344
- package/lib/implements/file/streamwriter.ts +0 -322
- package/lib/implements/keyvalue/client.ts +0 -376
- package/lib/implements/keyvalue/manager.ts +0 -759
- package/lib/implements/message/client.ts +0 -250
- package/lib/implements/message/manager.ts +0 -215
- package/lib/implements/threaddb/cbor/coding.ts +0 -62
- package/lib/implements/threaddb/cbor/event.ts +0 -336
- package/lib/implements/threaddb/cbor/node.ts +0 -542
- package/lib/implements/threaddb/cbor/record.ts +0 -398
- package/lib/implements/threaddb/common/AsyncMutex.ts +0 -24
- package/lib/implements/threaddb/common/addrinfo.ts +0 -135
- package/lib/implements/threaddb/common/dispatcher.ts +0 -81
- package/lib/implements/threaddb/common/idbstore-adapter.ts +0 -260
- package/lib/implements/threaddb/common/json-patcher.ts +0 -204
- package/lib/implements/threaddb/common/key.ts +0 -290
- package/lib/implements/threaddb/common/level-adapter.ts +0 -235
- package/lib/implements/threaddb/common/lineReader.ts +0 -79
- package/lib/implements/threaddb/common/logstore.ts +0 -215
- package/lib/implements/threaddb/common/transformed-datastore.ts +0 -308
- package/lib/implements/threaddb/core/app.ts +0 -206
- package/lib/implements/threaddb/core/core.ts +0 -230
- package/lib/implements/threaddb/core/db.ts +0 -249
- package/lib/implements/threaddb/core/event.ts +0 -54
- package/lib/implements/threaddb/core/head.ts +0 -89
- package/lib/implements/threaddb/core/identity.ts +0 -171
- package/lib/implements/threaddb/core/logstore.ts +0 -137
- package/lib/implements/threaddb/core/options.ts +0 -14
- package/lib/implements/threaddb/core/record.ts +0 -54
- package/lib/implements/threaddb/db/collection.ts +0 -1910
- package/lib/implements/threaddb/db/db.ts +0 -698
- package/lib/implements/threaddb/db/json2Query.ts +0 -192
- package/lib/implements/threaddb/db/query.ts +0 -524
- package/lib/implements/threaddb/dbclient.ts +0 -543
- package/lib/implements/threaddb/dbmanager.ts +0 -1906
- package/lib/implements/threaddb/lsstoreds/addr_book.ts +0 -549
- package/lib/implements/threaddb/lsstoreds/cache.ts +0 -36
- package/lib/implements/threaddb/lsstoreds/cyclic_batch.ts +0 -87
- package/lib/implements/threaddb/lsstoreds/global.ts +0 -151
- package/lib/implements/threaddb/lsstoreds/headbook.ts +0 -373
- package/lib/implements/threaddb/lsstoreds/keybook.ts +0 -297
- package/lib/implements/threaddb/lsstoreds/logstore.ts +0 -29
- package/lib/implements/threaddb/lsstoreds/metadata.ts +0 -223
- package/lib/implements/threaddb/net/define.ts +0 -149
- package/lib/implements/threaddb/net/grpcClient.ts +0 -589
- package/lib/implements/threaddb/net/grpcserver.ts +0 -146
- package/lib/implements/threaddb/net/net.ts +0 -2047
- package/lib/implements/threaddb/pb/lstore.proto +0 -38
- package/lib/implements/threaddb/pb/lstore.ts +0 -393
- package/lib/implements/threaddb/pb/lstore_pb.d.ts +0 -433
- package/lib/implements/threaddb/pb/lstore_pb.js +0 -1085
- package/lib/implements/threaddb/pb/net.proto +0 -194
- package/lib/implements/threaddb/pb/net_pb.d.ts +0 -2349
- package/lib/implements/threaddb/pb/net_pb.js +0 -5525
- package/lib/implements/threaddb/pb/proto-custom-types.ts +0 -212
- package/lib/implements/util/client.ts +0 -72
- package/lib/implements/util/manager.ts +0 -146
- package/lib/implements/wallet/manager.ts +0 -671
- package/lib/index.ts +0 -57
- package/lib/interfaces/DCContext.ts +0 -51
- package/lib/interfaces/aiproxy-interface.ts +0 -145
- package/lib/interfaces/auth-interface.ts +0 -118
- package/lib/interfaces/cache-interface.ts +0 -22
- package/lib/interfaces/client-interface.ts +0 -11
- package/lib/interfaces/comment-interface.ts +0 -167
- package/lib/interfaces/components/news-component.ts +0 -0
- package/lib/interfaces/database-interface.ts +0 -169
- package/lib/interfaces/file-interface.ts +0 -120
- package/lib/interfaces/index.ts +0 -10
- package/lib/interfaces/keyvalue-interface.ts +0 -156
- package/lib/interfaces/message-interface.ts +0 -22
- package/lib/interfaces/util-interface.ts +0 -31
- package/lib/modules/aiproxy-module.ts +0 -246
- package/lib/modules/auth-module.ts +0 -753
- package/lib/modules/cache-module.ts +0 -99
- package/lib/modules/client-module.ts +0 -71
- package/lib/modules/comment-module.ts +0 -429
- package/lib/modules/components/news-components.ts +0 -390
- package/lib/modules/database-module.ts +0 -598
- package/lib/modules/file-module.ts +0 -291
- package/lib/modules/index.ts +0 -13
- package/lib/modules/keyvalue-module.ts +0 -379
- package/lib/modules/message-module.ts +0 -107
- package/lib/modules/util-module.ts +0 -148
- package/lib/polyfills/process-env-browser.ts +0 -1
- package/lib/proto/datasource.ts +0 -93
- package/lib/proto/dcnet.proto +0 -1601
- package/lib/proto/dcnet_proto.d.ts +0 -22857
- package/lib/proto/dcnet_proto.js +0 -55204
- package/lib/proto/dcnet_proto_sparse.js +0 -55166
- package/lib/proto/oidfetch.proto +0 -25
- package/lib/proto/oidfetch_proto.d.ts +0 -585
- package/lib/proto/oidfetch_proto.js +0 -1247
- package/lib/serverless/babel-browser.ts +0 -39
- package/lib/serverless/base_entity.ts +0 -78
- package/lib/serverless/base_repository.ts +0 -414
- package/lib/serverless/browser_schema_extractor.ts +0 -283
- package/lib/serverless/decorator_factory.ts +0 -322
- package/lib/util/BrowserLineReader.ts +0 -73
- package/lib/util/base64.ts +0 -105
- package/lib/util/bcrypt.ts +0 -206
- package/lib/util/curve25519Encryption.ts +0 -418
- package/lib/util/dccrypt.ts +0 -73
- package/lib/util/logger.ts +0 -104
- package/lib/util/utils.ts +0 -289
|
@@ -1,549 +0,0 @@
|
|
|
1
|
-
import { Datastore, Key, Pair } from 'interface-datastore'
|
|
2
|
-
import * as bytes from 'uint8arrays'
|
|
3
|
-
import { peerIdFromString } from '@libp2p/peer-id'
|
|
4
|
-
import type { PeerId,PrivateKey,PublicKey } from "@libp2p/interface";
|
|
5
|
-
import { Multiaddr, multiaddr, isMultiaddr } from '@multiformats/multiaddr'
|
|
6
|
-
import { AddrBook,DumpAddrBook} from '../core/logstore' // 核心接口定义
|
|
7
|
-
import * as pb from '../pb/lstore'
|
|
8
|
-
import {uniqueLogIds,uniqueThreadIds,DSOptions,AllowEmptyRestore,dsThreadKey} from './global'
|
|
9
|
-
import {LRUCache} from 'lru-cache'
|
|
10
|
-
import { ThreadID } from '@textile/threads-id';
|
|
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;
|
|
16
|
-
|
|
17
|
-
const ADDR_FNV_OFFSET_BASIS = 0xcbf29ce484222325n;
|
|
18
|
-
const ADDR_FNV_PRIME = 0x100000001b3n;
|
|
19
|
-
const ADDR_MOD64 = 2n ** 64n;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// 缓存键类型
|
|
23
|
-
type CacheKey = {
|
|
24
|
-
threadID: ThreadID
|
|
25
|
-
peerID: string
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// TTL写入模式
|
|
29
|
-
enum TTLWriteMode {
|
|
30
|
-
Override,
|
|
31
|
-
Extend
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// 地址记录增强类型
|
|
35
|
-
interface AddrsRecord {
|
|
36
|
-
lock: AsyncMutex
|
|
37
|
-
data: pb.AddrBookRecord
|
|
38
|
-
dirty: boolean
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// NewAddrBook initializes a new datastore-backed address book. It serves as a drop-in replacement for pstoremem
|
|
43
|
-
// (memory-backed peerstore), and works with any datastore implementing the ds.Batching interface.
|
|
44
|
-
//
|
|
45
|
-
// Threads and logs addresses are serialized into protobuf, storing one datastore entry per (thread, log), along with metadata
|
|
46
|
-
// to control address expiration. To alleviate disk access and serde overhead, we internally use a read/write-through
|
|
47
|
-
// ARC cache, the size of which is adjustable via Options.CacheSize.
|
|
48
|
-
export async function newAddrBook(ds: Datastore, opts?: DSOptions): Promise<DsAddrBook> {
|
|
49
|
-
if (!opts) {
|
|
50
|
-
opts = DefaultOpts();
|
|
51
|
-
}
|
|
52
|
-
const ab = new DsAddrBook(ds, opts);
|
|
53
|
-
if (opts.CacheSize > 0) {
|
|
54
|
-
ab.cache = new LRUCache({ max: opts.CacheSize });
|
|
55
|
-
} else {
|
|
56
|
-
ab.cache = new LRUCache({ max: 0 });
|
|
57
|
-
}
|
|
58
|
-
return ab;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
export class DsAddrBook implements AddrBook {
|
|
63
|
-
private static readonly logBookBase = new Key('/thread/addrs')
|
|
64
|
-
private static readonly logBookEdge = new Key('/thread/addrs:edge')
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
private opts: DSOptions
|
|
68
|
-
private ds: TxnDatastoreExtended
|
|
69
|
-
cache: LRUCache<CacheKey, AddrsRecord>
|
|
70
|
-
|
|
71
|
-
constructor( ds: Datastore, opts: DSOptions) {
|
|
72
|
-
this.opts = opts
|
|
73
|
-
this.ds = ds as TxnDatastoreExtended
|
|
74
|
-
this.cache = new LRUCache({ max: opts.CacheSize || 0 })
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
async addAddr(t: ThreadID, p: PeerId, addrs: Multiaddr, ttl: number): Promise<void> {
|
|
79
|
-
|
|
80
|
-
if (ttl <= 0) return
|
|
81
|
-
const cleaned = this.cleanAddrs([addrs])
|
|
82
|
-
await this.setAddrs(t, p, cleaned, ttl)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async addAddrs(t: ThreadID, p: PeerId, addrs: Multiaddr[], ttl: number): Promise<void> {
|
|
86
|
-
if (ttl <= 0) return
|
|
87
|
-
const cleaned = this.cleanAddrs(addrs)
|
|
88
|
-
await this.setAddrs(t, p, cleaned, ttl)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
cleanAddrs(addrs: (string | Multiaddr | null | undefined)[]): Multiaddr[] {
|
|
92
|
-
return addrs.reduce<Multiaddr[]>((acc, item) => {
|
|
93
|
-
try {
|
|
94
|
-
if (!item) return acc
|
|
95
|
-
const addr = typeof item === 'string'
|
|
96
|
-
? multiaddr(item)
|
|
97
|
-
: (isMultiaddr(item) ? item : null)
|
|
98
|
-
return addr ? [...acc, addr] : acc
|
|
99
|
-
} catch {
|
|
100
|
-
return acc
|
|
101
|
-
}
|
|
102
|
-
}, [])
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
async setAddr(t: ThreadID, p: PeerId, addr: Multiaddr, ttl: number): Promise<void> {
|
|
107
|
-
if (ttl <= 0) return
|
|
108
|
-
const cleaned = this.cleanAddrs([addr])
|
|
109
|
-
await this.setAddrs(t, p, cleaned, ttl)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async setAddrs(t: ThreadID, logId: PeerId, addrs: Multiaddr[], ttl: number): Promise<void> {
|
|
113
|
-
let release = () => {}
|
|
114
|
-
try {
|
|
115
|
-
const record = await this.loadRecord(t, logId.toString(), true,false)
|
|
116
|
-
release = await record.lock.acquire()
|
|
117
|
-
const newExp = Date.now() + ttl
|
|
118
|
-
const existed = new Array(addrs.length).fill(false)
|
|
119
|
-
|
|
120
|
-
// 更新现有地址
|
|
121
|
-
outer: for (const [i, addr] of addrs.entries()) {
|
|
122
|
-
for (const entry of record.data.addrs) {
|
|
123
|
-
if (bytes.compare(addr.bytes, entry.addr) === 0) {
|
|
124
|
-
existed[i] = true
|
|
125
|
-
entry.ttl = ttl
|
|
126
|
-
entry.expiry = newExp
|
|
127
|
-
continue outer
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// 添加新地址
|
|
133
|
-
const added = addrs
|
|
134
|
-
.filter((_, i) => !existed[i])
|
|
135
|
-
.map(addr => new pb.AddrBookRecord.AddrEntry({
|
|
136
|
-
addr: addr.bytes,
|
|
137
|
-
expiry: newExp,
|
|
138
|
-
ttl
|
|
139
|
-
}))
|
|
140
|
-
|
|
141
|
-
record.data.addrs.push(...added)
|
|
142
|
-
record.dirty = true
|
|
143
|
-
await this.cleanRecord(record)
|
|
144
|
-
await this.flushRecord(record)
|
|
145
|
-
await this.invalidateEdge(t)
|
|
146
|
-
} finally {
|
|
147
|
-
release()
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
async updateAddrs(t: ThreadID, logId: PeerId, oldTTL: number, newTTL: number): Promise<void> {
|
|
152
|
-
let release = () => {}
|
|
153
|
-
try {
|
|
154
|
-
const record = await this.loadRecord(t, logId.toString(), true,false)
|
|
155
|
-
release = await record.lock.acquire()
|
|
156
|
-
const newExp = Date.now() + newTTL
|
|
157
|
-
for (const entry of record.data.addrs) {
|
|
158
|
-
if (entry.ttl === oldTTL) {
|
|
159
|
-
entry.ttl = newTTL
|
|
160
|
-
entry.expiry = newExp
|
|
161
|
-
record.dirty = true
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
await this.cleanRecord(record)
|
|
165
|
-
await this.flushRecord(record)
|
|
166
|
-
await this.invalidateEdge(t)
|
|
167
|
-
} finally {
|
|
168
|
-
release()
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
async addrs(t: ThreadID, logId: PeerId): Promise<Multiaddr[]> {
|
|
175
|
-
let release = () => {}
|
|
176
|
-
try {
|
|
177
|
-
const record = await this.loadRecord(t, logId.toString(), true,true)
|
|
178
|
-
release = await record.lock.acquire()
|
|
179
|
-
const addrs: Multiaddr[] = []
|
|
180
|
-
for (const entry of record.data.addrs) {
|
|
181
|
-
addrs.push(multiaddr(entry.addr))
|
|
182
|
-
}
|
|
183
|
-
return addrs
|
|
184
|
-
} finally {
|
|
185
|
-
release()
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
async clearAddrs(t: ThreadID, logId: PeerId): Promise<void> {
|
|
191
|
-
this.cache.delete({ threadID: t, peerID: logId.toString() });
|
|
192
|
-
|
|
193
|
-
const key = this.genDSKey(t, logId.toString());
|
|
194
|
-
try {
|
|
195
|
-
await this.ds.delete(key);
|
|
196
|
-
await this.invalidateEdge(t);
|
|
197
|
-
} catch (err:any) {
|
|
198
|
-
throw new Error(`Failed to clear addresses for log ${logId.toString()}: ${err.message}`);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
async logsWithAddrs(t: ThreadID): Promise<PeerId[]> {
|
|
205
|
-
try {
|
|
206
|
-
const ids = await uniqueLogIds(this.ds, DsAddrBook.logBookBase.child(new Key(t.toString())), (result) => {
|
|
207
|
-
return result.name();
|
|
208
|
-
});
|
|
209
|
-
return ids;
|
|
210
|
-
} catch (err : any) {
|
|
211
|
-
throw new Error(`Error while retrieving logs with addresses: ${err.message}`);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
async threadsFromAddrs(): Promise<ThreadID[]> {
|
|
216
|
-
try {
|
|
217
|
-
const ids = await uniqueThreadIds(this.ds, DsAddrBook.logBookBase, (result) => {
|
|
218
|
-
return result.parent().name();
|
|
219
|
-
});
|
|
220
|
-
return ids;
|
|
221
|
-
} catch (err:any) {
|
|
222
|
-
throw new Error(`Error while retrieving thread from addresses: ${err.message}`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
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
|
-
}
|
|
239
|
-
}
|
|
240
|
-
throw new Error('Edge computation failed');
|
|
241
|
-
}
|
|
242
|
-
|
|
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
|
-
}
|
|
253
|
-
|
|
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
|
-
}
|
|
284
|
-
}
|
|
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
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
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');
|
|
311
|
-
}
|
|
312
|
-
|
|
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
|
-
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
private async loadRecord(t: ThreadID, p: string, cache: boolean,update:boolean): Promise<AddrsRecord> {
|
|
332
|
-
const cacheKey = { threadID: t, peerID: p }
|
|
333
|
-
if (this.cache.has(cacheKey)) {
|
|
334
|
-
const addrsRecord = this.cache.get(cacheKey)!
|
|
335
|
-
await this.cleanRecord(addrsRecord)
|
|
336
|
-
if (update) {
|
|
337
|
-
await this.flushRecord(addrsRecord)
|
|
338
|
-
}
|
|
339
|
-
return addrsRecord
|
|
340
|
-
}
|
|
341
|
-
const key = this.genDSKey(t, p)
|
|
342
|
-
try {
|
|
343
|
-
const data = await this.ds.get(key)
|
|
344
|
-
const record = pb.AddrBookRecord.deserialize(data)
|
|
345
|
-
const addrsRecord = { lock: new AsyncMutex(), data: record, dirty: false }
|
|
346
|
-
await this.cleanRecord(addrsRecord)
|
|
347
|
-
if (update) {
|
|
348
|
-
await this.flushRecord(addrsRecord)
|
|
349
|
-
}
|
|
350
|
-
if (cache) {
|
|
351
|
-
this.cache.set(cacheKey, addrsRecord)
|
|
352
|
-
return addrsRecord
|
|
353
|
-
}
|
|
354
|
-
return addrsRecord
|
|
355
|
-
} catch (err:any) {
|
|
356
|
-
if (err.code === 'ERR_NOT_FOUND') {
|
|
357
|
-
const peerIdBytes = new TextEncoder().encode(p)
|
|
358
|
-
return {
|
|
359
|
-
lock: new AsyncMutex(),
|
|
360
|
-
data: pb.AddrBookRecord.fromObject({threadID:t.toBytes(), peerID:peerIdBytes,addrs:[] } ),
|
|
361
|
-
dirty: false
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
throw err
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
private async cleanRecord(record: AddrsRecord): Promise<boolean> {
|
|
369
|
-
const now = Date.now()
|
|
370
|
-
const valid = record.data.addrs.filter(a => a.expiry > now)
|
|
371
|
-
const changed = valid.length !== record.data.addrs.length
|
|
372
|
-
record.data.addrs = valid.sort((a, b) => a.expiry - b.expiry)
|
|
373
|
-
return changed
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
private async flushRecord(record: AddrsRecord): Promise<void> {
|
|
377
|
-
const threadId = ThreadID.fromBytes(record.data.threadID)
|
|
378
|
-
const logId = new TextDecoder().decode(record.data.peerID)
|
|
379
|
-
if (record.data.addrs.length === 0) {
|
|
380
|
-
await this.ds.delete(this.genDSKey(threadId, logId))
|
|
381
|
-
} else {
|
|
382
|
-
const data = record.data.serialize()
|
|
383
|
-
await this.ds.put(this.genDSKey(threadId, logId), data)
|
|
384
|
-
}
|
|
385
|
-
record.dirty = false
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
private genDSKey(t: ThreadID, p: string): Key {
|
|
389
|
-
const tKey = new Key(t.toString())
|
|
390
|
-
const pKey = new Key(p)
|
|
391
|
-
return DsAddrBook.logBookBase.child(tKey).child(pKey)
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
private async invalidateEdge(tid: ThreadID): Promise<void> {
|
|
396
|
-
var key = dsThreadKey(tid, DsAddrBook.logBookEdge)
|
|
397
|
-
return this.ds.delete(key)
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
private async traverse(withAddrs: boolean): Promise<{ [key: string]: { [key: string]: pb.AddrBookRecord } }> {
|
|
403
|
-
const data: { [key: string]: { [key: string]: pb.AddrBookRecord } } = {};
|
|
404
|
-
let result = this.ds.query({ prefix: DsAddrBook.logBookBase.toString()});
|
|
405
|
-
for await (const entry of result) {
|
|
406
|
-
const { tid, pid, record } = this.decodeAddrEntry(entry, withAddrs);
|
|
407
|
-
const tidStr = tid.toString();
|
|
408
|
-
if (!data[tidStr]) {
|
|
409
|
-
data[tidStr] = {};
|
|
410
|
-
}
|
|
411
|
-
data[tidStr][pid.toString()] = record!;
|
|
412
|
-
}
|
|
413
|
-
return data;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
|
|
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('/');
|
|
420
|
-
if (kns.length < 3) {
|
|
421
|
-
throw new Error(`bad addressbook key detected: ${keyStr}`);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// get thread and log IDs from the key components
|
|
425
|
-
const ts = kns[kns.length - 2];
|
|
426
|
-
const ls = kns[kns.length - 1];
|
|
427
|
-
|
|
428
|
-
const tid = ThreadID.fromString(ts!);
|
|
429
|
-
const pid = peerIdFromString(ls!);
|
|
430
|
-
|
|
431
|
-
if (withAddrs) {
|
|
432
|
-
const record = pb.AddrBookRecord.deserialize(entry.value);
|
|
433
|
-
return { tid, pid, record };
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
return { tid, pid };
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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;
|
|
451
|
-
}
|
|
452
|
-
return bytes.compare(a.addr, b.addr);
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
let hash = ADDR_FNV_OFFSET_BASIS;
|
|
456
|
-
|
|
457
|
-
for (const entry of entries) {
|
|
458
|
-
hash = this.fnv1a64(entry.peerIDBytes, hash);
|
|
459
|
-
hash = this.fnv1a64(entry.addr, hash);
|
|
460
|
-
}
|
|
461
|
-
|
|
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
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
private fnv1a64(data: Uint8Array, initial: bigint): bigint {
|
|
477
|
-
let hash = initial;
|
|
478
|
-
for (const byte of data) {
|
|
479
|
-
hash ^= BigInt(byte);
|
|
480
|
-
hash = (hash * ADDR_FNV_PRIME) % ADDR_MOD64;
|
|
481
|
-
}
|
|
482
|
-
return hash;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
// dumpAddrs(): Promise<DumpAddrBook>;
|
|
487
|
-
async dumpAddrs(): Promise<DumpAddrBook> {
|
|
488
|
-
const dump: { [key: string]: { [key: string]: { addr: Multiaddr, expires: Date }[] } } = {};
|
|
489
|
-
const data = await this.traverse(true);
|
|
490
|
-
|
|
491
|
-
for (const [tid, logs] of Object.entries(data)) {
|
|
492
|
-
const lm: { [key: string]: { addr: Multiaddr, expires: Date }[] } = {};
|
|
493
|
-
for (const [lid, rec] of Object.entries(logs)) {
|
|
494
|
-
if (rec.addrs.length > 0) {
|
|
495
|
-
const addrs = rec.addrs.map(r => ({
|
|
496
|
-
addr: multiaddr(r.addr),
|
|
497
|
-
expires: new Date(r.expiry)
|
|
498
|
-
}));
|
|
499
|
-
lm[lid] = addrs.map(a => ({ addr: a.addr, expires: a.expires }));
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
dump[tid] = lm;
|
|
503
|
-
}
|
|
504
|
-
return { data: dump };
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
async restoreAddrs(dump:DumpAddrBook): Promise<void> {
|
|
508
|
-
if (!AllowEmptyRestore && Object.keys(dump.data).length === 0) {
|
|
509
|
-
throw new Error('Empty dump');
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
const stored = await this.traverse(false);
|
|
513
|
-
|
|
514
|
-
for (const [tid, logs] of Object.entries(stored)) {
|
|
515
|
-
for (const lid of Object.keys(logs)) {
|
|
516
|
-
await this.clearAddrs(ThreadID.fromString(tid), peerIdFromString(lid));
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
const current = new Date();
|
|
520
|
-
for (const [tid, logs] of Object.entries(dump.data)) {
|
|
521
|
-
for (const [lid, addrs] of Object.entries(logs)) {
|
|
522
|
-
for (const addr of addrs) {
|
|
523
|
-
const ttl = addr.expires.getTime() - current.getTime();
|
|
524
|
-
if (ttl > 0) {
|
|
525
|
-
await this.setAddrs(ThreadID.fromString(tid), peerIdFromString(lid), [multiaddr(addr.addr)], ttl);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// 辅助类实现
|
|
534
|
-
class AsyncMutex {
|
|
535
|
-
private queue: (() => void)[] = []
|
|
536
|
-
private locked = false
|
|
537
|
-
|
|
538
|
-
async acquire(): Promise<() => void> {
|
|
539
|
-
while (this.locked) {
|
|
540
|
-
await new Promise<void>(resolve => this.queue.push(resolve))
|
|
541
|
-
}
|
|
542
|
-
this.locked = true
|
|
543
|
-
return () => {
|
|
544
|
-
this.locked = false
|
|
545
|
-
const next = this.queue.shift()
|
|
546
|
-
next?.()
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// 修改后的缓存接口声明(保证方法签名严格匹配)
|
|
2
|
-
interface CacheStore<K, V> {
|
|
3
|
-
get(key: K): V | undefined;
|
|
4
|
-
add(key: K, value: V): void; // 明确声明两个参数
|
|
5
|
-
remove(key: K): void;
|
|
6
|
-
contains(key: K): boolean;
|
|
7
|
-
peek(key: K): V | undefined;
|
|
8
|
-
keys(): K[];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// 修正实现类的参数匹配
|
|
12
|
-
class NoopCache<K, V> implements CacheStore<K, V> {
|
|
13
|
-
get(_key: K): V | undefined {
|
|
14
|
-
return undefined;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
add(_key: K, _value: V): void {
|
|
18
|
-
// 空操作,保留两个参数
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
remove(_key: K): void {
|
|
22
|
-
// 空操作,保留单个参数
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
contains(_key: K): boolean {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
peek(_key: K): V | undefined {
|
|
30
|
-
return undefined;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
keys(): K[] {
|
|
34
|
-
return [];
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { Datastore,Batch ,Key} from 'interface-datastore';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
type CyclicBatchOptions = {
|
|
5
|
-
opsPerBatch?: number;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
export class CyclicBatch implements Batch {
|
|
9
|
-
private currentBatch: Batch | null = null;
|
|
10
|
-
private pendingOps = 0;
|
|
11
|
-
private readonly threshold: number;
|
|
12
|
-
private closed = false;
|
|
13
|
-
|
|
14
|
-
constructor(
|
|
15
|
-
private readonly ds: Datastore,
|
|
16
|
-
options: CyclicBatchOptions = {}
|
|
17
|
-
) {
|
|
18
|
-
this.threshold = options.opsPerBatch || 20;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
private async initializeBatch(): Promise<void> {
|
|
22
|
-
if (!this.currentBatch && !this.closed) {
|
|
23
|
-
this.currentBatch = this.ds.batch();
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
private async cycle(): Promise<void> {
|
|
28
|
-
if (this.closed) {
|
|
29
|
-
throw new Error('Cyclic batch is closed');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (this.pendingOps < this.threshold || !this.currentBatch) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
try {
|
|
37
|
-
await this.currentBatch.commit();
|
|
38
|
-
this.pendingOps = 0;
|
|
39
|
-
this.currentBatch = this.ds.batch();
|
|
40
|
-
} catch (err:any) {
|
|
41
|
-
throw new Error(`Batch operation failed: ${err.message}`);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async put(key: Key, value: Uint8Array): Promise<void> {
|
|
46
|
-
if (this.closed) {
|
|
47
|
-
throw new Error('Cannot write to closed batch');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
await this.initializeBatch();
|
|
51
|
-
await this.cycle();
|
|
52
|
-
|
|
53
|
-
if (this.currentBatch) {
|
|
54
|
-
this.pendingOps++;
|
|
55
|
-
await this.currentBatch.put(key, value);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async delete(key: Key): Promise<void> {
|
|
60
|
-
if (this.closed) {
|
|
61
|
-
throw new Error('Cannot write to closed batch');
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
await this.initializeBatch();
|
|
65
|
-
await this.cycle();
|
|
66
|
-
|
|
67
|
-
if (this.currentBatch) {
|
|
68
|
-
this.pendingOps++;
|
|
69
|
-
await this.currentBatch.delete(key);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async commit(): Promise<void> {
|
|
74
|
-
if (this.closed) {
|
|
75
|
-
throw new Error('Batch already committed');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (this.currentBatch) {
|
|
79
|
-
await this.currentBatch.commit();
|
|
80
|
-
this.currentBatch = null;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
this.closed = true;
|
|
84
|
-
this.pendingOps = 0;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|