@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.
Files changed (99) hide show
  1. package/CHANGELOG.md +31 -3
  2. package/README.md +51 -1
  3. package/out/src/CWIStyleWalletManager.d.ts +37 -27
  4. package/out/src/CWIStyleWalletManager.d.ts.map +1 -1
  5. package/out/src/CWIStyleWalletManager.js +157 -151
  6. package/out/src/CWIStyleWalletManager.js.map +1 -1
  7. package/out/src/WalletAuthenticationManager.d.ts +20 -0
  8. package/out/src/WalletAuthenticationManager.d.ts.map +1 -1
  9. package/out/src/WalletAuthenticationManager.js +180 -47
  10. package/out/src/WalletAuthenticationManager.js.map +1 -1
  11. package/out/src/services/chaintracker/chaintracks/Api/BulkFileDataCacheApi.d.ts +22 -0
  12. package/out/src/services/chaintracker/chaintracks/Api/BulkFileDataCacheApi.d.ts.map +1 -1
  13. package/out/src/services/chaintracker/chaintracks/Api/BulkFileDataValidatorApi.d.ts +70 -0
  14. package/out/src/services/chaintracker/chaintracks/Api/BulkFileDataValidatorApi.d.ts.map +1 -0
  15. package/out/src/services/chaintracker/chaintracks/Api/BulkFileDataValidatorApi.js +21 -0
  16. package/out/src/services/chaintracker/chaintracks/Api/BulkFileDataValidatorApi.js.map +1 -0
  17. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksClientApi.d.ts +33 -0
  18. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksClientApi.d.ts.map +1 -1
  19. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksFetchApi.d.ts +6 -1
  20. package/out/src/services/chaintracker/chaintracks/Api/ChaintracksFetchApi.d.ts.map +1 -1
  21. package/out/src/services/chaintracker/chaintracks/Chaintracks.d.ts +9 -3
  22. package/out/src/services/chaintracker/chaintracks/Chaintracks.d.ts.map +1 -1
  23. package/out/src/services/chaintracker/chaintracks/Chaintracks.js +45 -5
  24. package/out/src/services/chaintracker/chaintracks/Chaintracks.js.map +1 -1
  25. package/out/src/services/chaintracker/chaintracks/configureChaintracksIngestors.d.ts +3 -0
  26. package/out/src/services/chaintracker/chaintracks/configureChaintracksIngestors.d.ts.map +1 -1
  27. package/out/src/services/chaintracker/chaintracks/configureChaintracksIngestors.js +2 -1
  28. package/out/src/services/chaintracker/chaintracks/configureChaintracksIngestors.js.map +1 -1
  29. package/out/src/services/chaintracker/chaintracks/index.all.d.ts +2 -0
  30. package/out/src/services/chaintracker/chaintracks/index.all.d.ts.map +1 -1
  31. package/out/src/services/chaintracker/chaintracks/index.all.js +2 -0
  32. package/out/src/services/chaintracker/chaintracks/index.all.js.map +1 -1
  33. package/out/src/services/chaintracker/chaintracks/index.mobile.d.ts +2 -0
  34. package/out/src/services/chaintracker/chaintracks/index.mobile.d.ts.map +1 -1
  35. package/out/src/services/chaintracker/chaintracks/index.mobile.js +2 -0
  36. package/out/src/services/chaintracker/chaintracks/index.mobile.js.map +1 -1
  37. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataCacheFs.d.ts +15 -2
  38. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataCacheFs.d.ts.map +1 -1
  39. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataCacheFs.js +115 -13
  40. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataCacheFs.js.map +1 -1
  41. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataManager.d.ts +17 -1
  42. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataManager.d.ts.map +1 -1
  43. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataManager.js +174 -87
  44. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataManager.js.map +1 -1
  45. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataValidator.worker.d.ts +32 -0
  46. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataValidator.worker.d.ts.map +1 -0
  47. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataValidator.worker.js +30 -0
  48. package/out/src/services/chaintracker/chaintracks/util/BulkFileDataValidator.worker.js.map +1 -0
  49. package/out/src/services/chaintracker/chaintracks/util/ChaintracksFetch.d.ts +2 -2
  50. package/out/src/services/chaintracker/chaintracks/util/ChaintracksFetch.d.ts.map +1 -1
  51. package/out/src/services/chaintracker/chaintracks/util/ChaintracksFetch.js +5 -3
  52. package/out/src/services/chaintracker/chaintracks/util/ChaintracksFetch.js.map +1 -1
  53. package/out/src/services/chaintracker/chaintracks/util/DurableFileBulkFileDownloadBudget.d.ts +39 -0
  54. package/out/src/services/chaintracker/chaintracks/util/DurableFileBulkFileDownloadBudget.d.ts.map +1 -0
  55. package/out/src/services/chaintracker/chaintracks/util/DurableFileBulkFileDownloadBudget.js +194 -0
  56. package/out/src/services/chaintracker/chaintracks/util/DurableFileBulkFileDownloadBudget.js.map +1 -0
  57. package/out/src/services/chaintracker/chaintracks/util/FixedWindowBulkFileDownloadBudget.d.ts +1 -0
  58. package/out/src/services/chaintracker/chaintracks/util/FixedWindowBulkFileDownloadBudget.d.ts.map +1 -1
  59. package/out/src/services/chaintracker/chaintracks/util/FixedWindowBulkFileDownloadBudget.js +1 -0
  60. package/out/src/services/chaintracker/chaintracks/util/FixedWindowBulkFileDownloadBudget.js.map +1 -1
  61. package/out/src/services/chaintracker/chaintracks/util/InlineBulkFileDataValidator.d.ts +12 -0
  62. package/out/src/services/chaintracker/chaintracks/util/InlineBulkFileDataValidator.d.ts.map +1 -0
  63. package/out/src/services/chaintracker/chaintracks/util/InlineBulkFileDataValidator.js +52 -0
  64. package/out/src/services/chaintracker/chaintracks/util/InlineBulkFileDataValidator.js.map +1 -0
  65. package/out/src/services/chaintracker/chaintracks/util/NodeBulkFileDataValidator.d.ts +40 -0
  66. package/out/src/services/chaintracker/chaintracks/util/NodeBulkFileDataValidator.d.ts.map +1 -0
  67. package/out/src/services/chaintracker/chaintracks/util/NodeBulkFileDataValidator.js +207 -0
  68. package/out/src/services/chaintracker/chaintracks/util/NodeBulkFileDataValidator.js.map +1 -0
  69. package/out/src/storage/StorageIdb.d.ts.map +1 -1
  70. package/out/src/storage/StorageIdb.js +5 -2
  71. package/out/src/storage/StorageIdb.js.map +1 -1
  72. package/out/src/storage/StorageProvider.d.ts.map +1 -1
  73. package/out/src/storage/StorageProvider.js +15 -9
  74. package/out/src/storage/StorageProvider.js.map +1 -1
  75. package/out/src/storage/methods/createAction.d.ts.map +1 -1
  76. package/out/src/storage/methods/createAction.js +6 -1
  77. package/out/src/storage/methods/createAction.js.map +1 -1
  78. package/out/src/storage/remoting/StorageClientBase.d.ts.map +1 -1
  79. package/out/src/storage/remoting/StorageClientBase.js +29 -0
  80. package/out/src/storage/remoting/StorageClientBase.js.map +1 -1
  81. package/out/src/storage/schema/entities/EntitySyncState.d.ts +1 -1
  82. package/out/src/storage/schema/entities/EntitySyncState.d.ts.map +1 -1
  83. package/out/src/storage/schema/entities/EntitySyncState.js +5 -5
  84. package/out/src/storage/schema/entities/EntitySyncState.js.map +1 -1
  85. package/out/src/utility/beefForTxids.d.ts +12 -3
  86. package/out/src/utility/beefForTxids.d.ts.map +1 -1
  87. package/out/src/utility/beefForTxids.js +69 -21
  88. package/out/src/utility/beefForTxids.js.map +1 -1
  89. package/out/src/wab-client/WABClient.d.ts.map +1 -1
  90. package/out/src/wab-client/WABClient.js +13 -13
  91. package/out/src/wab-client/WABClient.js.map +1 -1
  92. package/out/src/wab-client/WABTransport.d.ts +17 -23
  93. package/out/src/wab-client/WABTransport.d.ts.map +1 -1
  94. package/out/src/wab-client/WABTransport.js +129 -141
  95. package/out/src/wab-client/WABTransport.js.map +1 -1
  96. package/out/src/wab-client/auth-method-interactors/AuthMethodInteractor.d.ts +6 -0
  97. package/out/src/wab-client/auth-method-interactors/AuthMethodInteractor.d.ts.map +1 -1
  98. package/out/src/wab-client/auth-method-interactors/AuthMethodInteractor.js.map +1 -1
  99. 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
- constructor(rootFolder) {
52
- this.rootFolder = rootFolder;
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
- filePath(file) {
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
- return path.join(this.rootFolder, file.fileName);
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.filePath(file)));
85
+ return new Uint8Array(await node_fs_1.promises.readFile(this.objectPath(file)));
63
86
  }
64
87
  catch (error) {
65
- if (error.code === 'ENOENT')
66
- return undefined;
67
- throw error;
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.filePath(file);
72
- await node_fs_1.promises.mkdir(this.rootFolder, { recursive: true });
73
- const temporary = path.join(this.rootFolder, `.${file.fileName}.${process.pid}.${(0, node_crypto_1.randomUUID)()}.tmp`);
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 delete(file) {
133
+ async promoteValidated(file, data) {
92
134
  try {
93
- await node_fs_1.promises.unlink(this.filePath(file));
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;AAIjC;;;;;;;;GAQG;AACH;IAC+B,UAAU;IAAvC,YAA6B,UAAkB;0BAAlB,UAAU;IAAW,CAAC;IAE3C,QAAQ,CAAC,IAAkC;QACjD,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,GAAG,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtG,MAAM,IAAI,KAAK,CAAC,wCAAwC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC1E,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;IAClD,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,IAAkC;QAC1C,IAAI,CAAC;YACH,OAAO,IAAI,UAAU,CAAC,MAAM,kBAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAC/D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAO,SAAS,CAAA;YACxE,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,IAAkC,EAAE,IAAgB;QAC5D,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QACvC,MAAM,kBAAE,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACpD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,IAAI,IAAA,wBAAU,GAAE,MAAM,CAAC,CAAA;QACpG,IAAI,MAAiC,CAAA;QACrC,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,kBAAE,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;YAC9C,MAAM,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;YAC5B,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;YACnB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;YACpB,MAAM,GAAG,SAAS,CAAA;YAClB,MAAM,kBAAE,CAAC,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,CAAA;QACzC,CAAC;gBAAS,CAAC;YACT,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;YAC5C,MAAM,kBAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;gBACvC,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;oBAAE,MAAM,KAAK,CAAA;YACrE,CAAC,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAkC;QAC7C,IAAI,CAAC;YACH,MAAM,kBAAE,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAA;QACtC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAA;QACrE,CAAC;IACH,CAAC;CACF"}
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;AAWjC;;;;;;;;GAQG;AACH;IACmB,UAAU,CAAQ;IAClB,WAAW,CAAU;IAEtC,YAAY,mBAAwD;QAClE,MAAM,OAAO,GAAG,OAAO,mBAAmB,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,mBAAmB,EAAE,CAAC,CAAC,CAAC,mBAAmB,CAAA;QACnH,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAA;QACpC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,EAAE,CAAA;IAC9C,CAAC;IAEO,cAAc,CAAC,IAAY,EAAE,IAAkC;QACrE,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAA;QAC3B,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;IACvC,CAAC;IAEO,gBAAgB,CAAC,IAAkC;QACzD,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,GAAG,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtG,MAAM,IAAI,KAAK,CAAC,wCAAwC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC1E,CAAC;IACH,CAAC;IAEO,SAAS,CAAC,IAAkC;QAClD,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC7F,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;QACnD,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;YACxE,MAAM,IAAI,KAAK,CAAC,kCAAkC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;QACpE,CAAC;QACD,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC/B,CAAC;IAEO,UAAU,CAAC,IAAkC;QACnD,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAA;QAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;QACnC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,MAAM,UAAU,CAAC,CAAA;IACvF,CAAC;IAEO,qBAAqB,CAAC,IAAkC;QAC9D,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAA;IACjG,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,IAAkC;QAC1C,IAAI,CAAC;YACH,OAAO,IAAI,UAAU,CAAC,MAAM,kBAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAA;QACrE,CAAC;QACD,IAAI,CAAC;YACH,MAAM,kBAAE,CAAC,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAA;YACjD,OAAO,SAAS,CAAA;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAA;QACrE,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACpC,IAAI,CAAC;gBACH,OAAO,IAAI,UAAU,CAAC,MAAM,kBAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;YAC3E,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;oBAAE,MAAM,KAAK,CAAA;YACrE,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,IAAkC,EAAE,IAAgB;QAC5D,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;QACxC,MAAM,kBAAE,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,GAAG,IAAI,IAAA,wBAAU,GAAE,MAAM,CAAC,CAAA;QACxG,IAAI,MAAiC,CAAA;QACrC,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,kBAAE,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;YAC9C,MAAM,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;YAC5B,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;YACnB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;YACpB,MAAM,GAAG,SAAS,CAAA;YAClB,MAAM,kBAAE,CAAC,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,CAAA;YACvC,MAAM,aAAa,CAAC,MAAM,CAAC,CAAA;QAC7B,CAAC;gBAAS,CAAC;YACT,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;YAC5C,MAAM,kBAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;gBACvC,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;oBAAE,MAAM,KAAK,CAAA;YACrE,CAAC,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,IAAkC,EAAE,IAAgB;QACzE,IAAI,CAAC;YACH,MAAM,kBAAE,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAA;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAA;YACnE,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAC5B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAkC,EAAE,MAAc,EAAE,YAAyB;QAC5F,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;QACpC,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAA;QACjE,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,IAAA,wBAAU,GAAE,UAAU,CAAC,CAAA;QAChH,MAAM,kBAAE,CAAC,KAAK,CAAC,gBAAgB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACrD,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;YAC3C,IAAI,YAAY,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBAAE,OAAM;YAChF,MAAM,kBAAE,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;YACpC,MAAM,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;YACzC,MAAM,aAAa,CAAC,gBAAgB,CAAC,CAAA;QACvC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAA;YACnE,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAM;YACzC,yEAAyE;YACzE,uEAAuE;YACvE,wCAAwC;YACxC,MAAM,eAAe,CACnB,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAChC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAC1F,CAAA;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAkC;QAC7C,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,gDAAgD,CAAC,CAAA;IAC/E,CAAC;CACF;;AAED,KAAK,UAAU,eAAe,CAAC,WAAmB,EAAE,IAAgB;IAClE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;IACxC,MAAM,kBAAE,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,GAAG,IAAI,IAAA,wBAAU,GAAE,MAAM,CAAC,CAAA;IACxG,IAAI,MAAiC,CAAA;IACrC,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,kBAAE,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;QAC9C,MAAM,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;QAC5B,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;QACnB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QACpB,MAAM,GAAG,SAAS,CAAA;QAClB,MAAM,kBAAE,CAAC,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,CAAA;QACvC,MAAM,aAAa,CAAC,MAAM,CAAC,CAAA;IAC7B,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QAC5C,MAAM,kBAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;YACvC,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAA;QACrE,CAAC,CAAC,CAAA;IACJ,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,MAAc;IACzC,IAAI,MAAiC,CAAA;IACrC,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,kBAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QACnC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;IACrB,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,EAAE,KAAK,EAAE,CAAA;IACvB,CAAC;AACH,CAAC"}
@@ -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":"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"}
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 { ...this.stats };
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 bfd = this.getBfdForHeight(file.firstHeight);
314
- if (bfd == null || bfd.count < file.count) {
315
- throw new sdk_1.WERR_INVALID_PARAMETER('file', `a match for ${file.firstHeight}, ${file.count} in the BulkFileDataManager.`);
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 await this.lock.withReadLock(async () => await this.getDataFromFileNoLock(bfd, offset, length));
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
- return await this.lock.withReadLock(async () => {
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
- const offset = (height - file.firstHeight) * 80;
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
- if (bfd.data?.length !== bfd.count * 80) {
429
- throw new sdk_1.WERR_INVALID_PARAMETER('file.data', `bulk file ${bfd.fileName} data length ${bfd.data?.length} does not match expected count ${bfd.count}`);
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 { lastHeaderHash, lastChainWork } = (0, blockHeaderUtilities_1.validateBufferOfHeaders)(bfd.data, prevHash, 0, undefined, prevChainWork);
442
- if (bfd.lastHash && bfd.lastHash !== lastHeaderHash) {
443
- throw new sdk_1.WERR_INVALID_PARAMETER('file.lastHash', `expected ${bfd.lastHash} but got ${lastHeaderHash}`);
444
- }
445
- if (bfd.lastChainWork && bfd.lastChainWork !== lastChainWork) {
446
- throw new sdk_1.WERR_INVALID_PARAMETER('file.lastChainWork', `expected ${bfd.lastChainWork} but got ${lastChainWork}`);
447
- }
448
- bfd.lastHash = lastHeaderHash;
449
- bfd.lastChainWork = lastChainWork;
450
- if (bfd.firstHeight === 0)
451
- (0, blockHeaderUtilities_1.validateGenesisHeader)(bfd.data, bfd.chain);
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
- if (this.storage != null && bfd.fileId) {
710
- const stored = await this.storage.getBulkFileData(bfd.fileId);
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 (this.cache != null) {
719
- const cached = await this.cache.get(bfd);
720
- if (cached != null) {
721
- try {
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
- validateRetrievedData(bfd, data) {
753
- if (data.length !== bfd.count * 80) {
754
- throw new sdk_1.WERR_INVALID_PARAMETER('file.data', `bulk file ${bfd.fileName} data length ${data.length} does not match expected count ${bfd.count}`);
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 fileHash = (0, utilityHelpers_noBuffer_1.asString)(sdk_2.Hash.sha256((0, utilityHelpers_noBuffer_1.asArray)(data)), 'base64');
757
- if (fileHash !== bfd.fileHash) {
758
- throw new sdk_1.WERR_INVALID_PARAMETER('fileHash', `a match for retrieved data for ${bfd.fileName}`);
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 last = (0, blockHeaderUtilities_1.validateBufferOfHeaders)(data, lastHeaderHash, 0, undefined, lastChainWork);
804
- await toFs.writeFile(toPath(i), data);
805
- const fileHash = (0, utilityHelpers_noBuffer_1.asString)(sdk_2.Hash.sha256((0, utilityHelpers_noBuffer_1.asArray)(data)), 'base64');
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: last.lastChainWork,
813
- lastHash: last.lastHeaderHash,
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;