@bsv/wallet-toolbox 2.8.0 → 2.10.0
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/CHANGELOG.md +31 -3
- package/README.md +51 -1
- package/out/src/CWIStyleWalletManager.d.ts +37 -27
- package/out/src/CWIStyleWalletManager.d.ts.map +1 -1
- package/out/src/CWIStyleWalletManager.js +157 -151
- package/out/src/CWIStyleWalletManager.js.map +1 -1
- package/out/src/WalletAuthenticationManager.d.ts +20 -0
- package/out/src/WalletAuthenticationManager.d.ts.map +1 -1
- package/out/src/WalletAuthenticationManager.js +180 -47
- package/out/src/WalletAuthenticationManager.js.map +1 -1
- package/out/src/services/chaintracker/chaintracks/Api/BulkFileDataCacheApi.d.ts +22 -0
- package/out/src/services/chaintracker/chaintracks/Api/BulkFileDataCacheApi.d.ts.map +1 -1
- package/out/src/services/chaintracker/chaintracks/Api/BulkFileDataValidatorApi.d.ts +70 -0
- package/out/src/services/chaintracker/chaintracks/Api/BulkFileDataValidatorApi.d.ts.map +1 -0
- package/out/src/services/chaintracker/chaintracks/Api/BulkFileDataValidatorApi.js +21 -0
- package/out/src/services/chaintracker/chaintracks/Api/BulkFileDataValidatorApi.js.map +1 -0
- package/out/src/services/chaintracker/chaintracks/Api/ChaintracksClientApi.d.ts +33 -0
- package/out/src/services/chaintracker/chaintracks/Api/ChaintracksClientApi.d.ts.map +1 -1
- package/out/src/services/chaintracker/chaintracks/Api/ChaintracksFetchApi.d.ts +6 -1
- package/out/src/services/chaintracker/chaintracks/Api/ChaintracksFetchApi.d.ts.map +1 -1
- package/out/src/services/chaintracker/chaintracks/Chaintracks.d.ts +9 -3
- package/out/src/services/chaintracker/chaintracks/Chaintracks.d.ts.map +1 -1
- package/out/src/services/chaintracker/chaintracks/Chaintracks.js +45 -5
- package/out/src/services/chaintracker/chaintracks/Chaintracks.js.map +1 -1
- package/out/src/services/chaintracker/chaintracks/configureChaintracksIngestors.d.ts +3 -0
- package/out/src/services/chaintracker/chaintracks/configureChaintracksIngestors.d.ts.map +1 -1
- package/out/src/services/chaintracker/chaintracks/configureChaintracksIngestors.js +2 -1
- package/out/src/services/chaintracker/chaintracks/configureChaintracksIngestors.js.map +1 -1
- package/out/src/services/chaintracker/chaintracks/index.all.d.ts +2 -0
- package/out/src/services/chaintracker/chaintracks/index.all.d.ts.map +1 -1
- package/out/src/services/chaintracker/chaintracks/index.all.js +2 -0
- package/out/src/services/chaintracker/chaintracks/index.all.js.map +1 -1
- package/out/src/services/chaintracker/chaintracks/index.mobile.d.ts +2 -0
- package/out/src/services/chaintracker/chaintracks/index.mobile.d.ts.map +1 -1
- package/out/src/services/chaintracker/chaintracks/index.mobile.js +2 -0
- package/out/src/services/chaintracker/chaintracks/index.mobile.js.map +1 -1
- package/out/src/services/chaintracker/chaintracks/util/BulkFileDataCacheFs.d.ts +15 -2
- package/out/src/services/chaintracker/chaintracks/util/BulkFileDataCacheFs.d.ts.map +1 -1
- package/out/src/services/chaintracker/chaintracks/util/BulkFileDataCacheFs.js +115 -13
- package/out/src/services/chaintracker/chaintracks/util/BulkFileDataCacheFs.js.map +1 -1
- package/out/src/services/chaintracker/chaintracks/util/BulkFileDataManager.d.ts +17 -1
- package/out/src/services/chaintracker/chaintracks/util/BulkFileDataManager.d.ts.map +1 -1
- package/out/src/services/chaintracker/chaintracks/util/BulkFileDataManager.js +174 -87
- package/out/src/services/chaintracker/chaintracks/util/BulkFileDataManager.js.map +1 -1
- package/out/src/services/chaintracker/chaintracks/util/BulkFileDataValidator.worker.d.ts +32 -0
- package/out/src/services/chaintracker/chaintracks/util/BulkFileDataValidator.worker.d.ts.map +1 -0
- package/out/src/services/chaintracker/chaintracks/util/BulkFileDataValidator.worker.js +30 -0
- package/out/src/services/chaintracker/chaintracks/util/BulkFileDataValidator.worker.js.map +1 -0
- package/out/src/services/chaintracker/chaintracks/util/ChaintracksFetch.d.ts +2 -2
- package/out/src/services/chaintracker/chaintracks/util/ChaintracksFetch.d.ts.map +1 -1
- package/out/src/services/chaintracker/chaintracks/util/ChaintracksFetch.js +5 -3
- package/out/src/services/chaintracker/chaintracks/util/ChaintracksFetch.js.map +1 -1
- package/out/src/services/chaintracker/chaintracks/util/DurableFileBulkFileDownloadBudget.d.ts +39 -0
- package/out/src/services/chaintracker/chaintracks/util/DurableFileBulkFileDownloadBudget.d.ts.map +1 -0
- package/out/src/services/chaintracker/chaintracks/util/DurableFileBulkFileDownloadBudget.js +194 -0
- package/out/src/services/chaintracker/chaintracks/util/DurableFileBulkFileDownloadBudget.js.map +1 -0
- package/out/src/services/chaintracker/chaintracks/util/FixedWindowBulkFileDownloadBudget.d.ts +1 -0
- package/out/src/services/chaintracker/chaintracks/util/FixedWindowBulkFileDownloadBudget.d.ts.map +1 -1
- package/out/src/services/chaintracker/chaintracks/util/FixedWindowBulkFileDownloadBudget.js +1 -0
- package/out/src/services/chaintracker/chaintracks/util/FixedWindowBulkFileDownloadBudget.js.map +1 -1
- package/out/src/services/chaintracker/chaintracks/util/InlineBulkFileDataValidator.d.ts +12 -0
- package/out/src/services/chaintracker/chaintracks/util/InlineBulkFileDataValidator.d.ts.map +1 -0
- package/out/src/services/chaintracker/chaintracks/util/InlineBulkFileDataValidator.js +52 -0
- package/out/src/services/chaintracker/chaintracks/util/InlineBulkFileDataValidator.js.map +1 -0
- package/out/src/services/chaintracker/chaintracks/util/NodeBulkFileDataValidator.d.ts +40 -0
- package/out/src/services/chaintracker/chaintracks/util/NodeBulkFileDataValidator.d.ts.map +1 -0
- package/out/src/services/chaintracker/chaintracks/util/NodeBulkFileDataValidator.js +207 -0
- package/out/src/services/chaintracker/chaintracks/util/NodeBulkFileDataValidator.js.map +1 -0
- package/out/src/storage/StorageIdb.d.ts.map +1 -1
- package/out/src/storage/StorageIdb.js +5 -2
- package/out/src/storage/StorageIdb.js.map +1 -1
- package/out/src/storage/StorageProvider.d.ts.map +1 -1
- package/out/src/storage/StorageProvider.js +15 -9
- package/out/src/storage/StorageProvider.js.map +1 -1
- package/out/src/storage/methods/createAction.d.ts.map +1 -1
- package/out/src/storage/methods/createAction.js +6 -1
- package/out/src/storage/methods/createAction.js.map +1 -1
- package/out/src/storage/remoting/StorageClientBase.d.ts.map +1 -1
- package/out/src/storage/remoting/StorageClientBase.js +29 -0
- package/out/src/storage/remoting/StorageClientBase.js.map +1 -1
- package/out/src/storage/schema/entities/EntitySyncState.d.ts +1 -1
- package/out/src/storage/schema/entities/EntitySyncState.d.ts.map +1 -1
- package/out/src/storage/schema/entities/EntitySyncState.js +5 -5
- package/out/src/storage/schema/entities/EntitySyncState.js.map +1 -1
- package/out/src/utility/beefForTxids.d.ts +12 -3
- package/out/src/utility/beefForTxids.d.ts.map +1 -1
- package/out/src/utility/beefForTxids.js +69 -21
- package/out/src/utility/beefForTxids.js.map +1 -1
- package/out/src/wab-client/WABClient.d.ts.map +1 -1
- package/out/src/wab-client/WABClient.js +13 -13
- package/out/src/wab-client/WABClient.js.map +1 -1
- package/out/src/wab-client/WABTransport.d.ts +17 -23
- package/out/src/wab-client/WABTransport.d.ts.map +1 -1
- package/out/src/wab-client/WABTransport.js +129 -141
- package/out/src/wab-client/WABTransport.js.map +1 -1
- package/out/src/wab-client/auth-method-interactors/AuthMethodInteractor.d.ts +6 -0
- package/out/src/wab-client/auth-method-interactors/AuthMethodInteractor.d.ts.map +1 -1
- package/out/src/wab-client/auth-method-interactors/AuthMethodInteractor.js.map +1 -1
- package/package.json +2 -2
|
@@ -48,29 +48,70 @@ const path = __importStar(require("node:path"));
|
|
|
48
48
|
*/
|
|
49
49
|
class BulkFileDataCacheFs {
|
|
50
50
|
rootFolder;
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
legacyRoots;
|
|
52
|
+
constructor(rootFolderOrOptions) {
|
|
53
|
+
const options = typeof rootFolderOrOptions === 'string' ? { rootFolder: rootFolderOrOptions } : rootFolderOrOptions;
|
|
54
|
+
this.rootFolder = options.rootFolder;
|
|
55
|
+
this.legacyRoots = options.legacyRoots ?? [];
|
|
53
56
|
}
|
|
54
|
-
|
|
57
|
+
legacyFilePath(root, file) {
|
|
58
|
+
this.validateFileName(file);
|
|
59
|
+
return path.join(root, file.fileName);
|
|
60
|
+
}
|
|
61
|
+
validateFileName(file) {
|
|
55
62
|
if (path.basename(file.fileName) !== file.fileName || file.fileName === '.' || file.fileName === '..') {
|
|
56
63
|
throw new Error(`Invalid bulk-header cache file name: ${file.fileName}`);
|
|
57
64
|
}
|
|
58
|
-
|
|
65
|
+
}
|
|
66
|
+
digestHex(file) {
|
|
67
|
+
if (file.fileHash == null)
|
|
68
|
+
throw new Error(`Missing bulk-header digest for ${file.fileName}`);
|
|
69
|
+
const digest = Buffer.from(file.fileHash, 'base64');
|
|
70
|
+
if (digest.length !== 32 || digest.toString('base64') !== file.fileHash) {
|
|
71
|
+
throw new Error(`Invalid bulk-header digest for ${file.fileName}`);
|
|
72
|
+
}
|
|
73
|
+
return digest.toString('hex');
|
|
74
|
+
}
|
|
75
|
+
objectPath(file) {
|
|
76
|
+
this.validateFileName(file);
|
|
77
|
+
const digest = this.digestHex(file);
|
|
78
|
+
return path.join(this.rootFolder, 'objects', digest.slice(0, 2), `${digest}.headers`);
|
|
79
|
+
}
|
|
80
|
+
legacyRejectionMarker(file) {
|
|
81
|
+
return path.join(this.rootFolder, 'quarantine', `${this.digestHex(file)}.legacy-rejected.json`);
|
|
59
82
|
}
|
|
60
83
|
async get(file) {
|
|
61
84
|
try {
|
|
62
|
-
return new Uint8Array(await node_fs_1.promises.readFile(this.
|
|
85
|
+
return new Uint8Array(await node_fs_1.promises.readFile(this.objectPath(file)));
|
|
63
86
|
}
|
|
64
87
|
catch (error) {
|
|
65
|
-
if (error.code
|
|
66
|
-
|
|
67
|
-
|
|
88
|
+
if (error.code !== 'ENOENT')
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
await node_fs_1.promises.access(this.legacyRejectionMarker(file));
|
|
93
|
+
return undefined;
|
|
68
94
|
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
if (error.code !== 'ENOENT')
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
for (const root of this.legacyRoots) {
|
|
100
|
+
try {
|
|
101
|
+
return new Uint8Array(await node_fs_1.promises.readFile(this.legacyFilePath(root, file)));
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
if (error.code !== 'ENOENT')
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return undefined;
|
|
69
109
|
}
|
|
70
110
|
async set(file, data) {
|
|
71
|
-
const destination = this.
|
|
72
|
-
|
|
73
|
-
|
|
111
|
+
const destination = this.objectPath(file);
|
|
112
|
+
const folder = path.dirname(destination);
|
|
113
|
+
await node_fs_1.promises.mkdir(folder, { recursive: true });
|
|
114
|
+
const temporary = path.join(folder, `.${path.basename(destination)}.${process.pid}.${(0, node_crypto_1.randomUUID)()}.tmp`);
|
|
74
115
|
let handle;
|
|
75
116
|
try {
|
|
76
117
|
handle = await node_fs_1.promises.open(temporary, 'wx', 0o600);
|
|
@@ -79,6 +120,7 @@ class BulkFileDataCacheFs {
|
|
|
79
120
|
await handle.close();
|
|
80
121
|
handle = undefined;
|
|
81
122
|
await node_fs_1.promises.rename(temporary, destination);
|
|
123
|
+
await syncDirectory(folder);
|
|
82
124
|
}
|
|
83
125
|
finally {
|
|
84
126
|
await handle?.close().catch(() => undefined);
|
|
@@ -88,15 +130,75 @@ class BulkFileDataCacheFs {
|
|
|
88
130
|
});
|
|
89
131
|
}
|
|
90
132
|
}
|
|
91
|
-
async
|
|
133
|
+
async promoteValidated(file, data) {
|
|
92
134
|
try {
|
|
93
|
-
await node_fs_1.promises.
|
|
135
|
+
await node_fs_1.promises.access(this.objectPath(file));
|
|
94
136
|
}
|
|
95
137
|
catch (error) {
|
|
96
138
|
if (error.code !== 'ENOENT')
|
|
97
139
|
throw error;
|
|
140
|
+
await this.set(file, data);
|
|
98
141
|
}
|
|
99
142
|
}
|
|
143
|
+
async quarantine(file, reason, rejectedData) {
|
|
144
|
+
const source = this.objectPath(file);
|
|
145
|
+
const quarantineFolder = path.join(this.rootFolder, 'quarantine');
|
|
146
|
+
const destination = path.join(quarantineFolder, `${this.digestHex(file)}.${Date.now()}.${(0, node_crypto_1.randomUUID)()}.invalid`);
|
|
147
|
+
await node_fs_1.promises.mkdir(quarantineFolder, { recursive: true });
|
|
148
|
+
try {
|
|
149
|
+
const candidate = await node_fs_1.promises.readFile(source);
|
|
150
|
+
if (rejectedData != null && !candidate.equals(Buffer.from(rejectedData)))
|
|
151
|
+
return;
|
|
152
|
+
await node_fs_1.promises.rename(source, destination);
|
|
153
|
+
await syncDirectory(path.dirname(source));
|
|
154
|
+
await syncDirectory(quarantineFolder);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
if (error.code !== 'ENOENT')
|
|
158
|
+
throw error;
|
|
159
|
+
if (this.legacyRoots.length === 0)
|
|
160
|
+
return;
|
|
161
|
+
// Legacy entries are intentionally read-only. Once replacement succeeds,
|
|
162
|
+
// the content-addressed object takes precedence without destroying the
|
|
163
|
+
// old deployment's only retained bytes.
|
|
164
|
+
await writeAtomicFile(this.legacyRejectionMarker(file), Buffer.from(JSON.stringify({ fileName: file.fileName, fileHash: file.fileHash, reason })));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async delete(file) {
|
|
168
|
+
await this.quarantine(file, 'delete requested through legacy cache contract');
|
|
169
|
+
}
|
|
100
170
|
}
|
|
101
171
|
exports.BulkFileDataCacheFs = BulkFileDataCacheFs;
|
|
172
|
+
async function writeAtomicFile(destination, data) {
|
|
173
|
+
const folder = path.dirname(destination);
|
|
174
|
+
await node_fs_1.promises.mkdir(folder, { recursive: true });
|
|
175
|
+
const temporary = path.join(folder, `.${path.basename(destination)}.${process.pid}.${(0, node_crypto_1.randomUUID)()}.tmp`);
|
|
176
|
+
let handle;
|
|
177
|
+
try {
|
|
178
|
+
handle = await node_fs_1.promises.open(temporary, 'wx', 0o600);
|
|
179
|
+
await handle.writeFile(data);
|
|
180
|
+
await handle.sync();
|
|
181
|
+
await handle.close();
|
|
182
|
+
handle = undefined;
|
|
183
|
+
await node_fs_1.promises.rename(temporary, destination);
|
|
184
|
+
await syncDirectory(folder);
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
await handle?.close().catch(() => undefined);
|
|
188
|
+
await node_fs_1.promises.unlink(temporary).catch(error => {
|
|
189
|
+
if (error.code !== 'ENOENT')
|
|
190
|
+
throw error;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async function syncDirectory(folder) {
|
|
195
|
+
let handle;
|
|
196
|
+
try {
|
|
197
|
+
handle = await node_fs_1.promises.open(folder, 'r');
|
|
198
|
+
await handle.sync();
|
|
199
|
+
}
|
|
200
|
+
finally {
|
|
201
|
+
await handle?.close();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
102
204
|
//# sourceMappingURL=BulkFileDataCacheFs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BulkFileDataCacheFs.js","sourceRoot":"","sources":["../../../../../../src/services/chaintracker/chaintracks/util/BulkFileDataCacheFs.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qCAAwC;AACxC,6CAAwC;AACxC,MAAY,IAAI,sCAAiB;
|
|
1
|
+
{"version":3,"file":"BulkFileDataCacheFs.js","sourceRoot":"","sources":["../../../../../../src/services/chaintracker/chaintracks/util/BulkFileDataCacheFs.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,8 @@ import { BulkHeaderFileInfo } from './BulkHeaderFile';
|
|
|
5
5
|
import { HeightRange } from './HeightRange';
|
|
6
6
|
import { ChaintracksStorageBulkFileApi } from '../Api/ChaintracksStorageApi';
|
|
7
7
|
import { ChaintracksFsApi } from '../Api/ChaintracksFsApi';
|
|
8
|
-
import type { BulkFileDataCacheApi, BulkFileDownloadBudgetApi } from '../Api/BulkFileDataCacheApi';
|
|
8
|
+
import type { BulkFileDataCacheApi, BulkFileDownloadBudgetApi, BulkFileDownloadBudgetSnapshot } from '../Api/BulkFileDataCacheApi';
|
|
9
|
+
import { type BulkFileDataValidatorApi, type BulkFileDataValidatorStats } from '../Api/BulkFileDataValidatorApi';
|
|
9
10
|
export interface BulkFileDataManagerOptions {
|
|
10
11
|
chain: Chain;
|
|
11
12
|
maxPerFile: number;
|
|
@@ -16,6 +17,10 @@ export interface BulkFileDataManagerOptions {
|
|
|
16
17
|
cache?: BulkFileDataCacheApi;
|
|
17
18
|
/** Optional process-local or shared reservation budget for remote bytes. */
|
|
18
19
|
downloadBudget?: BulkFileDownloadBudgetApi;
|
|
20
|
+
/** Complete-object validator. Node services should inject a worker-backed implementation. */
|
|
21
|
+
validator?: BulkFileDataValidatorApi;
|
|
22
|
+
/** Cooldown after a failed load before the same immutable object can retry. */
|
|
23
|
+
failedLoadRetryMsecs?: number;
|
|
19
24
|
}
|
|
20
25
|
/** @public */
|
|
21
26
|
export interface BulkFileDataManagerStats {
|
|
@@ -27,6 +32,9 @@ export interface BulkFileDataManagerStats {
|
|
|
27
32
|
coalescedLoads: number;
|
|
28
33
|
downloads: number;
|
|
29
34
|
downloadedBytes: number;
|
|
35
|
+
loadBackoffs: number;
|
|
36
|
+
validation?: BulkFileDataValidatorStats;
|
|
37
|
+
downloadBudget?: BulkFileDownloadBudgetSnapshot;
|
|
30
38
|
}
|
|
31
39
|
/**
|
|
32
40
|
* Manages bulk file data (typically 8MB chunks of 100,000 headers each).
|
|
@@ -44,6 +52,7 @@ export declare class BulkFileDataManager {
|
|
|
44
52
|
private fileHashToIndex;
|
|
45
53
|
private readonly lock;
|
|
46
54
|
private readonly inFlightLoads;
|
|
55
|
+
private readonly failedLoads;
|
|
47
56
|
private storage?;
|
|
48
57
|
private readonly stats;
|
|
49
58
|
readonly chain: Chain;
|
|
@@ -53,6 +62,8 @@ export declare class BulkFileDataManager {
|
|
|
53
62
|
readonly fromKnownSourceUrl?: string;
|
|
54
63
|
readonly cache?: BulkFileDataCacheApi;
|
|
55
64
|
readonly downloadBudget?: BulkFileDownloadBudgetApi;
|
|
65
|
+
readonly validator: BulkFileDataValidatorApi;
|
|
66
|
+
readonly failedLoadRetryMsecs: number;
|
|
56
67
|
constructor(options: BulkFileDataManagerOptions | Chain);
|
|
57
68
|
getStats(): BulkFileDataManagerStats;
|
|
58
69
|
deleteBulkFiles(): Promise<void>;
|
|
@@ -81,6 +92,7 @@ export declare class BulkFileDataManager {
|
|
|
81
92
|
getBulkFiles(keepData?: boolean): Promise<BulkHeaderFileInfo[]>;
|
|
82
93
|
getHeightRange(): Promise<HeightRange>;
|
|
83
94
|
getDataFromFile(file: BulkHeaderFileInfo, offset?: number, length?: number): Promise<Uint8Array | undefined>;
|
|
95
|
+
private getDataFromSnapshot;
|
|
84
96
|
private getDataFromFileNoLock;
|
|
85
97
|
findHeaderForHeightOrUndefined(height: number): Promise<BlockHeader | undefined>;
|
|
86
98
|
getFileForHeight(height: number): Promise<BulkHeaderFileInfo | undefined>;
|
|
@@ -148,9 +160,13 @@ export declare class BulkFileDataManager {
|
|
|
148
160
|
*/
|
|
149
161
|
private ensureData;
|
|
150
162
|
private loadAndValidateData;
|
|
163
|
+
private loadFromStorage;
|
|
164
|
+
private loadFromCache;
|
|
165
|
+
private loadFromRemote;
|
|
151
166
|
private validateRetrievedData;
|
|
152
167
|
private ensureMaxRetained;
|
|
153
168
|
exportHeadersToFs(toFs: ChaintracksFsApi, toHeadersPerFile: number, toFolder: string, sourceUrl?: string, maxHeight?: number): Promise<void>;
|
|
169
|
+
destroy(): Promise<void>;
|
|
154
170
|
}
|
|
155
171
|
interface BulkFileData extends BulkHeaderFileInfo {
|
|
156
172
|
mru: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BulkFileDataManager.d.ts","sourceRoot":"","sources":["../../../../../../src/services/chaintracker/chaintracks/util/BulkFileDataManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAiE,MAAM,iBAAiB,CAAA;AAGnH,OAAO,EAAE,kBAAkB,EAAuB,MAAM,kBAAkB,CAAA;AAE1E,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"BulkFileDataManager.d.ts","sourceRoot":"","sources":["../../../../../../src/services/chaintracker/chaintracks/util/BulkFileDataManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,6 +11,8 @@ const HeightRange_1 = require("./HeightRange");
|
|
|
11
11
|
const blockHeaderUtilities_1 = require("./blockHeaderUtilities");
|
|
12
12
|
const ChaintracksFetch_1 = require("./ChaintracksFetch");
|
|
13
13
|
const SingleWriterMultiReaderLock_1 = require("./SingleWriterMultiReaderLock");
|
|
14
|
+
const BulkFileDataValidatorApi_1 = require("../Api/BulkFileDataValidatorApi");
|
|
15
|
+
const InlineBulkFileDataValidator_1 = require("./InlineBulkFileDataValidator");
|
|
14
16
|
/**
|
|
15
17
|
* Manages bulk file data (typically 8MB chunks of 100,000 headers each).
|
|
16
18
|
*
|
|
@@ -35,6 +37,7 @@ class BulkFileDataManager {
|
|
|
35
37
|
fileHashToIndex = {};
|
|
36
38
|
lock = new SingleWriterMultiReaderLock_1.SingleWriterMultiReaderLock();
|
|
37
39
|
inFlightLoads = new Map();
|
|
40
|
+
failedLoads = new Map();
|
|
38
41
|
storage;
|
|
39
42
|
stats = {
|
|
40
43
|
memoryHits: 0,
|
|
@@ -44,7 +47,8 @@ class BulkFileDataManager {
|
|
|
44
47
|
persistentCacheRejects: 0,
|
|
45
48
|
coalescedLoads: 0,
|
|
46
49
|
downloads: 0,
|
|
47
|
-
downloadedBytes: 0
|
|
50
|
+
downloadedBytes: 0,
|
|
51
|
+
loadBackoffs: 0
|
|
48
52
|
};
|
|
49
53
|
chain;
|
|
50
54
|
maxPerFile;
|
|
@@ -53,6 +57,8 @@ class BulkFileDataManager {
|
|
|
53
57
|
fromKnownSourceUrl;
|
|
54
58
|
cache;
|
|
55
59
|
downloadBudget;
|
|
60
|
+
validator;
|
|
61
|
+
failedLoadRetryMsecs;
|
|
56
62
|
constructor(options) {
|
|
57
63
|
const resolvedOptions = typeof options === 'object' ? options : BulkFileDataManager.createDefaultOptions(options);
|
|
58
64
|
this.chain = resolvedOptions.chain;
|
|
@@ -62,10 +68,19 @@ class BulkFileDataManager {
|
|
|
62
68
|
this.fetch = resolvedOptions.fetch;
|
|
63
69
|
this.cache = resolvedOptions.cache;
|
|
64
70
|
this.downloadBudget = resolvedOptions.downloadBudget;
|
|
71
|
+
this.validator = resolvedOptions.validator ?? new InlineBulkFileDataValidator_1.InlineBulkFileDataValidator();
|
|
72
|
+
this.failedLoadRetryMsecs = resolvedOptions.failedLoadRetryMsecs ?? 30 * 1000;
|
|
73
|
+
if (!Number.isSafeInteger(this.failedLoadRetryMsecs) || this.failedLoadRetryMsecs < 0) {
|
|
74
|
+
throw new sdk_1.WERR_INVALID_PARAMETER('failedLoadRetryMsecs', 'a non-negative safe integer');
|
|
75
|
+
}
|
|
65
76
|
this.deleteBulkFilesNoLock();
|
|
66
77
|
}
|
|
67
78
|
getStats() {
|
|
68
|
-
return {
|
|
79
|
+
return {
|
|
80
|
+
...this.stats,
|
|
81
|
+
validation: this.validator.getStats?.(),
|
|
82
|
+
downloadBudget: this.downloadBudget?.snapshot?.()
|
|
83
|
+
};
|
|
69
84
|
}
|
|
70
85
|
async deleteBulkFiles() {
|
|
71
86
|
return await this.lock.withWriteLock(async () => this.deleteBulkFilesNoLock());
|
|
@@ -310,11 +325,36 @@ class BulkFileDataManager {
|
|
|
310
325
|
});
|
|
311
326
|
}
|
|
312
327
|
async getDataFromFile(file, offset, length) {
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
328
|
+
const resolved = await this.lock.withReadLock(async () => {
|
|
329
|
+
const resolved = this.getBfdForHeight(file.firstHeight);
|
|
330
|
+
if (resolved == null || resolved.count < file.count) {
|
|
331
|
+
throw new sdk_1.WERR_INVALID_PARAMETER('file', `a match for ${file.firstHeight}, ${file.count} in the BulkFileDataManager.`);
|
|
332
|
+
}
|
|
333
|
+
return { current: resolved, snapshot: snapshotBfd(resolved) };
|
|
334
|
+
});
|
|
335
|
+
// The immutable descriptor is snapshotted under the reader lock, but disk,
|
|
336
|
+
// network, and validation work happens after releasing it. A concurrent
|
|
337
|
+
// writer can publish a newer descriptor without invalidating this read.
|
|
338
|
+
return await this.getDataFromSnapshot(resolved.current, resolved.snapshot, offset, length);
|
|
339
|
+
}
|
|
340
|
+
async getDataFromSnapshot(original, snapshot, offset, length) {
|
|
341
|
+
const data = await this.getDataFromFileNoLock(snapshot, offset, length);
|
|
342
|
+
if (snapshot.data != null) {
|
|
343
|
+
await this.lock.withWriteLock(async () => {
|
|
344
|
+
if (this.bfds.includes(original) &&
|
|
345
|
+
original.fileHash === snapshot.fileHash &&
|
|
346
|
+
original.firstHeight === snapshot.firstHeight &&
|
|
347
|
+
original.count === snapshot.count) {
|
|
348
|
+
original.data = snapshot.data;
|
|
349
|
+
original.validated = true;
|
|
350
|
+
original.lastHash = snapshot.lastHash;
|
|
351
|
+
original.lastChainWork = snapshot.lastChainWork;
|
|
352
|
+
original.mru = Date.now();
|
|
353
|
+
this.ensureMaxRetained();
|
|
354
|
+
}
|
|
355
|
+
});
|
|
316
356
|
}
|
|
317
|
-
return
|
|
357
|
+
return data;
|
|
318
358
|
}
|
|
319
359
|
async getDataFromFileNoLock(bfd, offset, length) {
|
|
320
360
|
const fileLength = bfd.count * 80;
|
|
@@ -331,20 +371,19 @@ class BulkFileDataManager {
|
|
|
331
371
|
return data.slice(offset, offset + length);
|
|
332
372
|
}
|
|
333
373
|
async findHeaderForHeightOrUndefined(height) {
|
|
334
|
-
|
|
374
|
+
const resolved = await this.lock.withReadLock(async () => {
|
|
335
375
|
if (!Number.isInteger(height) || height < 0) {
|
|
336
376
|
throw new sdk_1.WERR_INVALID_PARAMETER('height', `a non-negative integer (${height}).`);
|
|
337
377
|
}
|
|
338
378
|
const file = this.bfds.find(f => f.firstHeight <= height && f.firstHeight + f.count > height);
|
|
339
379
|
if (file == null)
|
|
340
380
|
return undefined;
|
|
341
|
-
|
|
342
|
-
const data = await this.getDataFromFileNoLock(file, offset, 80);
|
|
343
|
-
if (data == null)
|
|
344
|
-
return undefined;
|
|
345
|
-
const header = (0, blockHeaderUtilities_1.deserializeBlockHeader)(data, height, 0);
|
|
346
|
-
return header;
|
|
381
|
+
return { current: file, snapshot: snapshotBfd(file), offset: (height - file.firstHeight) * 80 };
|
|
347
382
|
});
|
|
383
|
+
if (resolved == null)
|
|
384
|
+
return undefined;
|
|
385
|
+
const data = await this.getDataFromSnapshot(resolved.current, resolved.snapshot, resolved.offset, 80);
|
|
386
|
+
return data == null ? undefined : (0, blockHeaderUtilities_1.deserializeBlockHeader)(data, height, 0);
|
|
348
387
|
}
|
|
349
388
|
async getFileForHeight(height) {
|
|
350
389
|
return await this.lock.withReadLock(async () => {
|
|
@@ -424,31 +463,31 @@ class BulkFileDataManager {
|
|
|
424
463
|
return bfd;
|
|
425
464
|
}
|
|
426
465
|
async validateBfdData(bfd, expectedFileHash) {
|
|
427
|
-
await this.ensureData(bfd);
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
bfd.fileHash = (0, utilityHelpers_noBuffer_1.asString)(sdk_2.Hash.sha256((0, utilityHelpers_noBuffer_1.asArray)(bfd.data)), 'base64');
|
|
432
|
-
if (expectedFileHash && expectedFileHash !== bfd.fileHash) {
|
|
433
|
-
throw new sdk_1.WERR_INVALID_PARAMETER('file.fileHash', `expected ${expectedFileHash} but got ${bfd.fileHash}`);
|
|
434
|
-
}
|
|
435
|
-
this.validateBfdHeaders(bfd);
|
|
466
|
+
const data = await this.ensureData(bfd);
|
|
467
|
+
const validated = await this.validateRetrievedData(bfd, data, expectedFileHash);
|
|
468
|
+
bfd.data = validated;
|
|
436
469
|
}
|
|
437
|
-
validateBfdHeaders(bfd) {
|
|
470
|
+
async validateBfdHeaders(bfd, expectedFileHash = bfd.fileHash) {
|
|
438
471
|
const pbf = bfd.firstHeight > 0 ? this.getBfdForHeight(bfd.firstHeight - 1) : undefined;
|
|
439
472
|
const prevHash = pbf?.lastHash ?? '00'.repeat(32);
|
|
440
473
|
const prevChainWork = pbf?.lastChainWork ?? '00'.repeat(32);
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
474
|
+
const result = await this.validator.validate({
|
|
475
|
+
fileName: bfd.fileName,
|
|
476
|
+
data: bfd.data,
|
|
477
|
+
count: bfd.count,
|
|
478
|
+
fileHash: expectedFileHash,
|
|
479
|
+
firstHeight: bfd.firstHeight,
|
|
480
|
+
prevHash,
|
|
481
|
+
prevChainWork,
|
|
482
|
+
lastHash: bfd.lastHash,
|
|
483
|
+
lastChainWork: bfd.lastChainWork,
|
|
484
|
+
chain: bfd.chain
|
|
485
|
+
});
|
|
486
|
+
bfd.data = result.data;
|
|
487
|
+
bfd.fileHash = result.fileHash;
|
|
488
|
+
bfd.lastHash = result.lastHeaderHash;
|
|
489
|
+
bfd.lastChainWork = result.lastChainWork;
|
|
490
|
+
return result.data;
|
|
452
491
|
}
|
|
453
492
|
async ReValidate() {
|
|
454
493
|
return await this.lock.withReadLock(async () => await this.ReValidateNoLock());
|
|
@@ -690,75 +729,110 @@ class BulkFileDataManager {
|
|
|
690
729
|
this.ensureMaxRetained();
|
|
691
730
|
return data;
|
|
692
731
|
}
|
|
732
|
+
const failed = this.failedLoads.get(key);
|
|
733
|
+
if (failed != null) {
|
|
734
|
+
if (Date.now() < failed.retryAt) {
|
|
735
|
+
this.stats.loadBackoffs++;
|
|
736
|
+
throw failed.error;
|
|
737
|
+
}
|
|
738
|
+
this.failedLoads.delete(key);
|
|
739
|
+
}
|
|
693
740
|
const load = this.loadAndValidateData(bfd);
|
|
694
741
|
this.inFlightLoads.set(key, load);
|
|
695
742
|
try {
|
|
696
743
|
const data = await load;
|
|
697
744
|
bfd.data = data;
|
|
698
745
|
bfd.validated = true;
|
|
746
|
+
this.failedLoads.delete(key);
|
|
699
747
|
bfd.mru = Date.now();
|
|
700
748
|
this.ensureMaxRetained();
|
|
701
749
|
return data;
|
|
702
750
|
}
|
|
751
|
+
catch (error) {
|
|
752
|
+
const resolved = error instanceof Error ? error : new Error(String(error));
|
|
753
|
+
this.failedLoads.set(key, { retryAt: Date.now() + this.failedLoadRetryMsecs, error: resolved });
|
|
754
|
+
throw resolved;
|
|
755
|
+
}
|
|
703
756
|
finally {
|
|
704
757
|
if (this.inFlightLoads.get(key) === load)
|
|
705
758
|
this.inFlightLoads.delete(key);
|
|
706
759
|
}
|
|
707
760
|
}
|
|
708
761
|
async loadAndValidateData(bfd) {
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
if (stored == null) {
|
|
712
|
-
throw new sdk_1.WERR_INVALID_PARAMETER('fileId', `valid, data not found for fileId ${bfd.fileId}`);
|
|
713
|
-
}
|
|
714
|
-
this.validateRetrievedData(bfd, stored);
|
|
715
|
-
this.stats.storageHits++;
|
|
762
|
+
const stored = await this.loadFromStorage(bfd);
|
|
763
|
+
if (stored != null)
|
|
716
764
|
return stored;
|
|
717
|
-
|
|
718
|
-
if (
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
this.validateRetrievedData(bfd, cached);
|
|
723
|
-
this.stats.persistentCacheHits++;
|
|
724
|
-
return cached;
|
|
725
|
-
}
|
|
726
|
-
catch (error) {
|
|
727
|
-
this.stats.persistentCacheRejects++;
|
|
728
|
-
await this.cache.delete?.(bfd);
|
|
729
|
-
this.log(`Rejected corrupt bulk-header cache entry ${bfd.fileName}: ${String(error)}`);
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
else {
|
|
733
|
-
this.stats.persistentCacheMisses++;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
if (this.fetch != null && bfd.sourceUrl) {
|
|
737
|
-
const expectedBytes = bfd.count * 80;
|
|
738
|
-
await this.downloadBudget?.consume(expectedBytes);
|
|
739
|
-
const url = this.fetch.pathJoin(bfd.sourceUrl, bfd.fileName);
|
|
740
|
-
const downloaded = await this.fetch.download(url, expectedBytes);
|
|
741
|
-
if (downloaded == null) {
|
|
742
|
-
throw new sdk_1.WERR_INVALID_PARAMETER('sourceUrl', `data not found for sourceUrl ${url}`);
|
|
743
|
-
}
|
|
744
|
-
this.validateRetrievedData(bfd, downloaded);
|
|
745
|
-
this.stats.downloads++;
|
|
746
|
-
this.stats.downloadedBytes += downloaded.length;
|
|
747
|
-
await this.cache?.set(bfd, downloaded);
|
|
765
|
+
const cached = await this.loadFromCache(bfd);
|
|
766
|
+
if (cached != null)
|
|
767
|
+
return cached;
|
|
768
|
+
const downloaded = await this.loadFromRemote(bfd);
|
|
769
|
+
if (downloaded != null)
|
|
748
770
|
return downloaded;
|
|
749
|
-
}
|
|
750
771
|
throw new sdk_1.WERR_INVALID_PARAMETER('data', `defined. Unable to retrieve data for ${bfd.fileName}`);
|
|
751
772
|
}
|
|
752
|
-
|
|
753
|
-
if (
|
|
754
|
-
|
|
773
|
+
async loadFromStorage(bfd) {
|
|
774
|
+
if (this.storage == null || !bfd.fileId)
|
|
775
|
+
return undefined;
|
|
776
|
+
const stored = await this.storage.getBulkFileData(bfd.fileId);
|
|
777
|
+
if (stored == null) {
|
|
778
|
+
throw new sdk_1.WERR_INVALID_PARAMETER('fileId', `valid, data not found for fileId ${bfd.fileId}`);
|
|
755
779
|
}
|
|
756
|
-
const
|
|
757
|
-
|
|
758
|
-
|
|
780
|
+
const validated = await this.validateRetrievedData(bfd, stored);
|
|
781
|
+
this.stats.storageHits++;
|
|
782
|
+
return validated;
|
|
783
|
+
}
|
|
784
|
+
async loadFromCache(bfd) {
|
|
785
|
+
if (this.cache == null)
|
|
786
|
+
return undefined;
|
|
787
|
+
const cached = await this.cache.get(bfd);
|
|
788
|
+
if (cached == null) {
|
|
789
|
+
this.stats.persistentCacheMisses++;
|
|
790
|
+
return undefined;
|
|
759
791
|
}
|
|
792
|
+
try {
|
|
793
|
+
const validated = await this.validateRetrievedData(bfd, cached);
|
|
794
|
+
this.stats.persistentCacheHits++;
|
|
795
|
+
await this.cache.promoteValidated?.(bfd, validated);
|
|
796
|
+
return validated;
|
|
797
|
+
}
|
|
798
|
+
catch (error) {
|
|
799
|
+
// Only deterministic rejection of these exact bytes may quarantine an
|
|
800
|
+
// immutable cache object. Operational validation failures preserve it.
|
|
801
|
+
if (!(error instanceof BulkFileDataValidatorApi_1.BulkFileDataValidationError))
|
|
802
|
+
throw error;
|
|
803
|
+
this.stats.persistentCacheRejects++;
|
|
804
|
+
let rejectedData = error.data;
|
|
805
|
+
if (!(rejectedData instanceof Uint8Array) && cached.byteLength > 0)
|
|
806
|
+
rejectedData = cached;
|
|
807
|
+
await this.cache.quarantine?.(bfd, String(error), rejectedData);
|
|
808
|
+
this.log(`Rejected corrupt bulk-header cache entry ${bfd.fileName}: ${String(error)}`);
|
|
809
|
+
return undefined;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
async loadFromRemote(bfd) {
|
|
813
|
+
if (this.fetch == null || !bfd.sourceUrl)
|
|
814
|
+
return undefined;
|
|
815
|
+
const expectedBytes = bfd.count * 80;
|
|
816
|
+
await this.downloadBudget?.consume(expectedBytes);
|
|
817
|
+
const url = this.fetch.pathJoin(bfd.sourceUrl, bfd.fileName);
|
|
818
|
+
const downloaded = await this.fetch.download(url, expectedBytes, {
|
|
819
|
+
beforeRetry: async () => await this.downloadBudget?.consume(expectedBytes)
|
|
820
|
+
});
|
|
821
|
+
if (downloaded == null) {
|
|
822
|
+
throw new sdk_1.WERR_INVALID_PARAMETER('sourceUrl', `data not found for sourceUrl ${url}`);
|
|
823
|
+
}
|
|
824
|
+
const validated = await this.validateRetrievedData(bfd, downloaded);
|
|
825
|
+
this.stats.downloads++;
|
|
826
|
+
this.stats.downloadedBytes += validated.length;
|
|
827
|
+
await this.cache?.set(bfd, validated);
|
|
828
|
+
return validated;
|
|
829
|
+
}
|
|
830
|
+
async validateRetrievedData(bfd, data, expectedFileHash = bfd.fileHash) {
|
|
760
831
|
const candidate = { ...bfd, data };
|
|
761
|
-
this.validateBfdHeaders(candidate);
|
|
832
|
+
const validated = await this.validateBfdHeaders(candidate, expectedFileHash);
|
|
833
|
+
bfd.lastHash = candidate.lastHash;
|
|
834
|
+
bfd.lastChainWork = candidate.lastChainWork;
|
|
835
|
+
return validated;
|
|
762
836
|
}
|
|
763
837
|
ensureMaxRetained() {
|
|
764
838
|
if (this.maxRetained === undefined)
|
|
@@ -800,17 +874,24 @@ class BulkFileDataManager {
|
|
|
800
874
|
if (data == null || data.length === 0) {
|
|
801
875
|
break;
|
|
802
876
|
}
|
|
803
|
-
const
|
|
804
|
-
|
|
805
|
-
|
|
877
|
+
const validated = await this.validator.validate({
|
|
878
|
+
fileName: toFileName(i),
|
|
879
|
+
data,
|
|
880
|
+
count: data.length / 80,
|
|
881
|
+
firstHeight,
|
|
882
|
+
prevHash: lastHeaderHash,
|
|
883
|
+
prevChainWork: lastChainWork,
|
|
884
|
+
chain
|
|
885
|
+
});
|
|
886
|
+
await toFs.writeFile(toPath(i), validated.data);
|
|
806
887
|
const file = {
|
|
807
888
|
chain,
|
|
808
|
-
count: data.length / 80,
|
|
809
|
-
fileHash,
|
|
889
|
+
count: validated.data.length / 80,
|
|
890
|
+
fileHash: validated.fileHash,
|
|
810
891
|
fileName: toFileName(i),
|
|
811
892
|
firstHeight,
|
|
812
|
-
lastChainWork:
|
|
813
|
-
lastHash:
|
|
893
|
+
lastChainWork: validated.lastChainWork,
|
|
894
|
+
lastHash: validated.lastHeaderHash,
|
|
814
895
|
prevChainWork: lastChainWork,
|
|
815
896
|
prevHash: lastHeaderHash,
|
|
816
897
|
sourceUrl
|
|
@@ -822,8 +903,14 @@ class BulkFileDataManager {
|
|
|
822
903
|
}
|
|
823
904
|
await toFs.writeFile(toJsonPath(), (0, utilityHelpers_noBuffer_1.asUint8Array)(JSON.stringify(toBulkFiles), 'utf8'));
|
|
824
905
|
}
|
|
906
|
+
async destroy() {
|
|
907
|
+
await this.validator.destroy?.();
|
|
908
|
+
}
|
|
825
909
|
}
|
|
826
910
|
exports.BulkFileDataManager = BulkFileDataManager;
|
|
911
|
+
function snapshotBfd(file) {
|
|
912
|
+
return { ...file, data: file.data };
|
|
913
|
+
}
|
|
827
914
|
function selectBulkHeaderFiles(files, chain, maxPerFile) {
|
|
828
915
|
const r = [];
|
|
829
916
|
let height = 0;
|