neozipkit 0.1.1 → 0.2.1

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 (115) hide show
  1. package/README.md +52 -105
  2. package/contracts/INTEGRATION.md +338 -0
  3. package/contracts/NETWORKS.md +156 -0
  4. package/contracts/README.md +152 -0
  5. package/{src/server/blockchain → contracts}/constants.ts +5 -22
  6. package/contracts/deploy-bytecode.mjs +94 -0
  7. package/dist/blockchain/ZipKitMinter.d.ts +106 -0
  8. package/dist/blockchain/ZipKitMinter.js +347 -0
  9. package/dist/blockchain/ZipKitNFT.d.ts +3 -0
  10. package/dist/blockchain/ZipKitNFT.js +7 -0
  11. package/dist/blockchain/ZipKitOTS.d.ts +135 -0
  12. package/dist/blockchain/ZipKitOTS.js +564 -0
  13. package/dist/blockchain/ZipKitVerifier.d.ts +69 -0
  14. package/dist/blockchain/ZipKitVerifier.js +289 -0
  15. package/dist/{server/blockchain/WalletAnalyzer.d.ts → blockchain/ZipKitWallet.d.ts} +31 -43
  16. package/dist/{server/blockchain/WalletAnalyzer.js → blockchain/ZipKitWallet.js} +193 -161
  17. package/dist/blockchain/contracts.d.ts +14 -0
  18. package/dist/blockchain/contracts.js +77 -0
  19. package/dist/blockchain/index.d.ts +7 -0
  20. package/dist/blockchain/index.js +50 -0
  21. package/dist/{server/blockchain → blockchain}/types.d.ts +12 -34
  22. package/dist/{server/blockchain → blockchain}/types.js +1 -22
  23. package/dist/browser-index.d.ts +0 -3
  24. package/dist/browser-index.js +4 -5
  25. package/dist/browser.d.ts +0 -3
  26. package/dist/browser.js +4 -5
  27. package/dist/index.d.ts +8 -15
  28. package/dist/index.js +12 -24
  29. package/dist/server/{platform/server/ZipKitServer.d.ts → ZipKitServer.d.ts} +4 -35
  30. package/dist/server/{platform/server/ZipKitServer.js → ZipKitServer.js} +55 -182
  31. package/dist/shared/ZipEntry.js +1 -1
  32. package/dist/shared/ZipKit.d.ts +0 -6
  33. package/dist/shared/ZipKit.js +1 -18
  34. package/dist/shared/types.js +2 -2
  35. package/dist/shared/version.d.ts +2 -4
  36. package/dist/shared/version.js +4 -5
  37. package/package.json +13 -3
  38. package/src/blockchain/README.md +229 -0
  39. package/src/blockchain/ZipKitMinter.ts +470 -0
  40. package/src/blockchain/ZipKitNFT.ts +12 -0
  41. package/src/blockchain/ZipKitOTS.ts +622 -0
  42. package/src/blockchain/ZipKitVerifier.ts +382 -0
  43. package/src/blockchain/ZipKitWallet.ts +338 -0
  44. package/src/blockchain/contracts.ts +88 -0
  45. package/src/blockchain/index.ts +61 -0
  46. package/src/blockchain/types.ts +91 -0
  47. package/src/browser/{TokenVerifierBrowser.ts → TokenVerifierBrowser.ts-remove} +1 -1
  48. package/src/browser-index.ts +3 -3
  49. package/src/index.ts +10 -17
  50. package/src/server/README.md +25 -0
  51. package/src/server/ZipKitServer.ts +305 -0
  52. package/src/shared/ZipEntry.ts +1 -1
  53. package/src/shared/ZipKit.ts +1 -19
  54. package/src/shared/types.ts +3 -3
  55. package/src/shared/version.ts +3 -4
  56. package/src/types/modules.d.ts +11 -1
  57. package/contracts/Lock.sol +0 -34
  58. package/contracts/compile-contract.js +0 -168
  59. package/contracts/deploy/deploy-both-contracts.js +0 -286
  60. package/contracts/deploy/deploy-enhanced-contract.js +0 -197
  61. package/contracts/deploy/deploy-private-contract.js +0 -190
  62. package/contracts/deploy/deploy-public-clean.js +0 -1063
  63. package/contracts/deploy/deploy-public-contract.js +0 -1107
  64. package/contracts/deployment.config.example.js +0 -25
  65. package/contracts/remix-compile.js +0 -222
  66. package/contracts/setup-deployment.js +0 -51
  67. package/contracts/src/ZipFileNFT-DEPRECATED-6field.sol +0 -260
  68. package/contracts/src/ZipFileNFT-DEPRECATED-Enhanced.sol +0 -289
  69. package/dist/browser/CertificateGeneratorBrowser.d.ts +0 -75
  70. package/dist/browser/CertificateGeneratorBrowser.js +0 -433
  71. package/dist/browser/TokenVerifierBrowser.d.ts +0 -76
  72. package/dist/browser/TokenVerifierBrowser.js +0 -299
  73. package/dist/server/blockchain/NFTMinter.d.ts +0 -41
  74. package/dist/server/blockchain/NFTMinter.js +0 -323
  75. package/dist/server/blockchain/TokenQuerier.d.ts +0 -31
  76. package/dist/server/blockchain/TokenQuerier.js +0 -259
  77. package/dist/server/blockchain/TokenVerifier.d.ts +0 -20
  78. package/dist/server/blockchain/TokenVerifier.js +0 -395
  79. package/dist/server/blockchain/WalletFormatter.d.ts +0 -39
  80. package/dist/server/blockchain/WalletFormatter.js +0 -196
  81. package/dist/server/blockchain/WalletManager.d.ts +0 -46
  82. package/dist/server/blockchain/WalletManager.js +0 -294
  83. package/dist/server/blockchain/ZipMetadata.d.ts +0 -27
  84. package/dist/server/blockchain/ZipMetadata.js +0 -131
  85. package/dist/server/blockchain/ZipTokenProcessor.d.ts +0 -44
  86. package/dist/server/blockchain/ZipTokenProcessor.js +0 -204
  87. package/dist/server/blockchain/constants.d.ts +0 -102
  88. package/dist/server/blockchain/constants.js +0 -968
  89. package/dist/server/blockchain/index.d.ts +0 -9
  90. package/dist/server/blockchain/index.js +0 -44
  91. package/dist/server/blockchain/test-certificate.d.ts +0 -8
  92. package/dist/server/blockchain/test-certificate.js +0 -100
  93. package/dist/server/platform/servercomponents/ZipKitOTS.d.ts +0 -91
  94. package/dist/server/platform/servercomponents/ZipKitOTS.js +0 -271
  95. package/examples/unzip-token.ts +0 -488
  96. package/examples/zip-token.ts +0 -604
  97. package/src/server/blockchain/NFTMinter.ts +0 -357
  98. package/src/server/blockchain/TokenQuerier.ts +0 -287
  99. package/src/server/blockchain/TokenVerifier.ts +0 -453
  100. package/src/server/blockchain/WalletAnalyzer.ts +0 -397
  101. package/src/server/blockchain/WalletFormatter.ts +0 -206
  102. package/src/server/blockchain/WalletManager.ts +0 -294
  103. package/src/server/blockchain/ZipMetadata.ts +0 -117
  104. package/src/server/blockchain/ZipTokenProcessor.ts +0 -234
  105. package/src/server/blockchain/index.ts +0 -42
  106. package/src/server/blockchain/test-certificate.ts +0 -137
  107. package/src/server/blockchain/types.ts +0 -134
  108. package/src/server/platform/server/ZipKitServer.ts +0 -459
  109. package/src/server/platform/servercomponents/ZipKitOTS.ts +0 -313
  110. package/src/server/platform/servercomponents/opentimestamps.d.ts +0 -23
  111. /package/contracts/{bytecode.txt → NZIP-NFT-bytecode.txt} +0 -0
  112. /package/dist/{server/blockchain → blockchain}/CertificateToken.d.ts +0 -0
  113. /package/dist/{server/blockchain → blockchain}/CertificateToken.js +0 -0
  114. /package/src/{server/blockchain → blockchain}/CertificateToken.ts +0 -0
  115. /package/src/browser/{CertificateGeneratorBrowser.ts → CertificateGeneratorBrowser.ts-remove} +0 -0
@@ -0,0 +1,564 @@
1
+ /**
2
+ * ======================================
3
+ * ZipKitOTS.ts - OpenTimestamps Integration
4
+ * ======================================
5
+ *
6
+ * This module provides comprehensive OpenTimestamps (OTS) functionality for ZIP files,
7
+ * including timestamp creation, verification, certificate generation, and proof upgrades.
8
+ *
9
+ * Key Features:
10
+ * - Bitcoin blockchain timestamping via OpenTimestamps
11
+ * - Merkle root calculation and verification
12
+ * - OTS proof upgrade capabilities
13
+ * - Certificate generation for verified timestamps
14
+ * - Integration with ZIP file metadata
15
+ *
16
+ * @fileoverview OpenTimestamps utilities for blockchain-enabled ZIP files
17
+ * @version 0.1.2
18
+ * @author NeoWare Inc.
19
+ */
20
+ "use server";
21
+ "use strict";
22
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
23
+ if (k2 === undefined) k2 = k;
24
+ var desc = Object.getOwnPropertyDescriptor(m, k);
25
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
26
+ desc = { enumerable: true, get: function() { return m[k]; } };
27
+ }
28
+ Object.defineProperty(o, k2, desc);
29
+ }) : (function(o, m, k, k2) {
30
+ if (k2 === undefined) k2 = k;
31
+ o[k2] = m[k];
32
+ }));
33
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
34
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
35
+ }) : function(o, v) {
36
+ o["default"] = v;
37
+ });
38
+ var __importStar = (this && this.__importStar) || (function () {
39
+ var ownKeys = function(o) {
40
+ ownKeys = Object.getOwnPropertyNames || function (o) {
41
+ var ar = [];
42
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
43
+ return ar;
44
+ };
45
+ return ownKeys(o);
46
+ };
47
+ return function (mod) {
48
+ if (mod && mod.__esModule) return mod;
49
+ var result = {};
50
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
51
+ __setModuleDefault(result, mod);
52
+ return result;
53
+ };
54
+ })();
55
+ var __importDefault = (this && this.__importDefault) || function (mod) {
56
+ return (mod && mod.__esModule) ? mod : { "default": mod };
57
+ };
58
+ Object.defineProperty(exports, "__esModule", { value: true });
59
+ exports.verifyOts = verifyOts;
60
+ exports.bufferToArrayBuffer = bufferToArrayBuffer;
61
+ exports.getOtsEntry = getOtsEntry;
62
+ exports.getOtsBuffer = getOtsBuffer;
63
+ exports.getMerkleRootSafe = getMerkleRootSafe;
64
+ exports.verifyOtsZip = verifyOtsZip;
65
+ exports.createTimestamp = createTimestamp;
66
+ exports.deserializeOts = deserializeOts;
67
+ exports.parseVerifyResult = parseVerifyResult;
68
+ exports.createCertificateOts = createCertificateOts;
69
+ exports.upgradeOTS = upgradeOTS;
70
+ exports.createOtsMetadataEntry = createOtsMetadataEntry;
71
+ // ===== Imports =====
72
+ const moment_timezone_1 = __importDefault(require("moment-timezone"));
73
+ const fs = __importStar(require("fs"));
74
+ const Errors_1 = __importDefault(require("../shared/constants/Errors"));
75
+ const CertificateOTS_1 = __importDefault(require("../shared/components/CertificateOTS"));
76
+ const ZipKit_1 = __importDefault(require("../shared/ZipKit"));
77
+ const Headers_1 = require("../shared/constants/Headers");
78
+ const Util_1 = require("../shared/components/Util");
79
+ if (globalThis._bitcore)
80
+ delete globalThis._bitcore;
81
+ let OTSInstance = null;
82
+ /**
83
+ * Lazily loads the OpenTimestamps module to avoid import conflicts.
84
+ * @returns Promise resolving to OpenTimestamps module instance
85
+ */
86
+ async function getOpenTimestamps() {
87
+ if (!OTSInstance) {
88
+ OTSInstance = (await Promise.resolve().then(() => __importStar(require('opentimestamps')))).default;
89
+ }
90
+ return OTSInstance;
91
+ }
92
+ /**
93
+ * Executes a function with OpenTimestamps logging control.
94
+ * @param debug - If true, keeps logging enabled; if false, suppresses console output
95
+ * @param fn - Function to execute with controlled logging
96
+ * @returns Promise resolving to function result
97
+ */
98
+ async function withOtsLogging(debug, fn) {
99
+ if (debug)
100
+ return fn();
101
+ const originalLog = console.log;
102
+ const originalDebug = console.debug;
103
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
104
+ console.log = () => { };
105
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
106
+ console.debug = () => { };
107
+ try {
108
+ return await fn();
109
+ }
110
+ finally {
111
+ console.log = originalLog;
112
+ console.debug = originalDebug;
113
+ }
114
+ }
115
+ /**
116
+ * Verifies an OpenTimestamps proof against a hash digest.
117
+ * @param hashDigest - SHA256 hash as hex string to verify
118
+ * @param ots - OpenTimestamps proof data
119
+ * @param options - Configuration options
120
+ * @param options.debug - Enable debug logging
121
+ * @returns Promise resolving to verification result, error object, or null if inputs invalid
122
+ */
123
+ async function verifyOts(hashDigest, ots, options) {
124
+ if (!hashDigest || !ots)
125
+ return null;
126
+ const tsResult = {
127
+ merkleRoot: hashDigest,
128
+ ots,
129
+ verified: false,
130
+ results: null,
131
+ error: null,
132
+ attestations: null,
133
+ upgradedOts: null,
134
+ };
135
+ try {
136
+ return await withOtsLogging(options?.debug, async () => {
137
+ const OpenTimestamps = await getOpenTimestamps();
138
+ const _otsBuffer = Buffer.from(ots);
139
+ const detachedOts = OpenTimestamps.DetachedTimestampFile.deserialize(_otsBuffer);
140
+ const digest = Buffer.from(hashDigest, 'hex');
141
+ const hashOts = OpenTimestamps.DetachedTimestampFile.fromHash(new OpenTimestamps.Ops.OpSHA256(), digest);
142
+ const upgrade = await OpenTimestamps.upgrade(detachedOts);
143
+ if (upgrade) {
144
+ console.log('verifyOts === Timestamp should be upgraded!');
145
+ tsResult.upgradedOts = detachedOts.serializeToBytes();
146
+ }
147
+ tsResult.results = await OpenTimestamps.verify(detachedOts, hashOts, {
148
+ ignoreBitcoinNode: true,
149
+ timeout: 5000,
150
+ });
151
+ const _attest = [];
152
+ detachedOts.timestamp.allAttestations().forEach((attestation) => {
153
+ if (!(attestation instanceof OpenTimestamps.Notary.UnknownAttestation)) {
154
+ _attest.push(attestation);
155
+ }
156
+ });
157
+ tsResult.attestations = JSON.stringify(_attest);
158
+ tsResult.verified = !!tsResult.results && Object.keys(tsResult.results).length > 0;
159
+ if (!tsResult.verified)
160
+ tsResult.results = null;
161
+ console.log('verifyOts === tsResult: ', tsResult);
162
+ return tsResult;
163
+ });
164
+ }
165
+ catch (error) {
166
+ let errMsg = error?.message || 'Timestamp Verification Failed!';
167
+ if (errMsg.includes('ESOCKETTIMEDOUT'))
168
+ errMsg = 'Server timed out, Try again in a few minutes.';
169
+ else if (errMsg.includes('502'))
170
+ errMsg = 'Server failed to respond, Try again in a few minutes.';
171
+ else if (errMsg.includes('ECONNRESET'))
172
+ errMsg = 'Server connection reset, Try again in a few minutes.';
173
+ return { error: errMsg };
174
+ }
175
+ }
176
+ /**
177
+ * Converts a Node.js Buffer to an ArrayBuffer.
178
+ * @param buf - Buffer to convert
179
+ * @returns ArrayBuffer with same data
180
+ */
181
+ function bufferToArrayBuffer(buf) {
182
+ const ab = new ArrayBuffer(buf.byteLength);
183
+ const view = new Uint8Array(ab);
184
+ view.set(buf);
185
+ return ab;
186
+ }
187
+ /**
188
+ * Finds the OpenTimestamps entry in a ZIP file.
189
+ * Searches for TIMESTAMP_METADATA first, then TIMESTAMP_SUBMITTED.
190
+ * @param zip - ZipKit instance to search
191
+ * @returns ZIP entry containing OTS proof, or null if not found
192
+ */
193
+ function getOtsEntry(zip) {
194
+ try {
195
+ // Include metadata entries in the directory
196
+ const entries = zip.getDirectory?.(true) || [];
197
+ // Prefer upgraded metadata entry first
198
+ let entry = entries.find((e) => e && e.filename === Headers_1.TIMESTAMP_METADATA);
199
+ if (entry)
200
+ return entry;
201
+ // Fallback to submitted proof entry
202
+ entry = entries.find((e) => e && e.filename === Headers_1.TIMESTAMP_SUBMITTED);
203
+ if (entry)
204
+ return entry;
205
+ }
206
+ catch {
207
+ return null;
208
+ }
209
+ }
210
+ /**
211
+ * Extracts OTS proof data from a ZIP entry.
212
+ * @param zip - ZipKit instance
213
+ * @param otsEntry - ZIP entry containing OTS data
214
+ * @returns Promise resolving to OTS proof buffer, or null if extraction fails
215
+ */
216
+ async function getOtsBuffer(zip, otsEntry) {
217
+ try {
218
+ const data = await zip.extract(otsEntry, true);
219
+ return Buffer.isBuffer(data) ? data : null;
220
+ }
221
+ catch {
222
+ return null;
223
+ }
224
+ }
225
+ /**
226
+ * Safely extracts merkle root from a ZIP file.
227
+ * @param zip - ZipKit instance
228
+ * @returns Merkle root hash string, or null if not available
229
+ */
230
+ function getMerkleRootSafe(zip) {
231
+ try {
232
+ const mr = zip.getMerkleRoot?.();
233
+ return (typeof mr === 'string' && mr.length > 0) ? mr : null;
234
+ }
235
+ catch {
236
+ return null;
237
+ }
238
+ }
239
+ /**
240
+ * Converts various date formats to a Date object.
241
+ * @param val - Date value (number timestamp, string, or Date)
242
+ * @returns Date object or undefined if invalid
243
+ */
244
+ function coerceDate(val) {
245
+ if (!val)
246
+ return undefined;
247
+ if (typeof val === 'number')
248
+ return new Date(val * (val > 10000000000 ? 1 : 1000));
249
+ if (typeof val === 'string')
250
+ return new Date(val);
251
+ return undefined;
252
+ }
253
+ /**
254
+ * Verifies OpenTimestamps proof within a ZIP file.
255
+ * Extracts merkle root and OTS proof, then verifies against Bitcoin blockchain.
256
+ * @param zip - ZipKit instance containing files and OTS proof
257
+ * @returns Promise resolving to verification result with status: 'none', 'error', 'pending', or 'valid'
258
+ */
259
+ async function verifyOtsZip(zip) {
260
+ const mr = getMerkleRootSafe(zip);
261
+ const entry = getOtsEntry(zip);
262
+ if (!entry)
263
+ return { status: 'none' };
264
+ const otsBuf = await getOtsBuffer(zip, entry);
265
+ if (!otsBuf || !mr)
266
+ return { status: 'error' };
267
+ try {
268
+ const result = await verifyOts(mr, bufferToArrayBuffer(otsBuf));
269
+ if (result?.verified === true) {
270
+ const bitcoin = result?.results?.bitcoin || {};
271
+ const blockHeight = result.blockHeight ?? result.block ?? result.height ?? bitcoin.height;
272
+ const attestedAt = coerceDate(result.attestedAt ?? result.attested ?? result.time ?? bitcoin.timestamp);
273
+ const upgraded = Boolean(result.upgraded || result.upgradedOts);
274
+ const upgradedOts = result.upgradedOts ? Buffer.from(result.upgradedOts) : undefined;
275
+ return { status: 'valid', blockHeight, attestedAt, upgraded, upgradedOts };
276
+ }
277
+ return { status: 'pending' };
278
+ }
279
+ catch (e) {
280
+ return { status: 'pending', message: e?.message };
281
+ }
282
+ }
283
+ /**
284
+ * Creates an OpenTimestamps proof for a hash digest.
285
+ * Submits hash to OpenTimestamps servers for Bitcoin blockchain anchoring.
286
+ * @param hashDigest - SHA256 hash as hex string to timestamp
287
+ * @param options - Configuration options
288
+ * @param options.debug - Enable debug logging
289
+ * @returns Promise resolving to OTS proof buffer, or null if creation failed
290
+ */
291
+ async function createTimestamp(hashDigest, options) {
292
+ if (!hashDigest)
293
+ return null;
294
+ try {
295
+ return await withOtsLogging(options?.debug, async () => {
296
+ const OpenTimestamps = await getOpenTimestamps();
297
+ const hashBuffer = Buffer.from(hashDigest, 'hex');
298
+ const detached = OpenTimestamps.DetachedTimestampFile.fromHash(new OpenTimestamps.Ops.OpSHA256(), hashBuffer);
299
+ await OpenTimestamps.stamp(detached);
300
+ return Buffer.from(detached.serializeToBytes());
301
+ });
302
+ }
303
+ catch (error) {
304
+ console.error('Error creating timestamp:', error);
305
+ throw new Error('Error creating timestamp');
306
+ }
307
+ }
308
+ /**
309
+ * Deserializes OpenTimestamps verification result into JSON format.
310
+ * Extracts blockchain attestation data and transaction details.
311
+ * @param tsResult - Timestamp verification result to deserialize
312
+ * @param options - Configuration options
313
+ * @param options.debug - Enable debug logging
314
+ * @returns Promise resolving to JSON string with attestation data, or null if failed
315
+ */
316
+ async function deserializeOts(tsResult, options) {
317
+ let result = {
318
+ attestStr: null,
319
+ needsUpdateMsg: null,
320
+ attestationValues: [],
321
+ };
322
+ for (const chain in tsResult.results) {
323
+ const _results = tsResult.results[chain];
324
+ const _attestDate = (0, moment_timezone_1.default)(_results.timestamp * 1000).tz(moment_timezone_1.default.tz.guess()).format('MM-DD-YYYY hh:mma z');
325
+ const blockchain = chain.charAt(0).toUpperCase() + chain.slice(1);
326
+ result.attestStr = `<b>${blockchain} block ${_results.height}</b> attests existence as of <b>${_attestDate}</b>`;
327
+ }
328
+ let timestamp = tsResult.ots;
329
+ if (tsResult.upgradedOts != null) {
330
+ result.needsUpdateMsg = 'This Zip file requires updated Timestamp Metadata!';
331
+ timestamp = tsResult.upgradedOts;
332
+ }
333
+ if (!timestamp)
334
+ return null;
335
+ const infoResult = await withOtsLogging(options?.debug, async () => {
336
+ const OpenTimestamps = await getOpenTimestamps();
337
+ const detached = OpenTimestamps.DetachedTimestampFile.deserialize(Buffer.from(timestamp));
338
+ return OpenTimestamps.info(detached);
339
+ });
340
+ const transactionIdMatches = [...infoResult.matchAll(/transaction id ([a-f0-9]+)/gi)];
341
+ const transactionIds = transactionIdMatches.map((m) => m[1]);
342
+ const blockHeightMatches = [...infoResult.matchAll(/verify BitcoinBlockHeaderAttestation\((\d+)\)/g)];
343
+ const blockHeights = blockHeightMatches.map((m) => m[1]);
344
+ const merkleRootMatches = [...infoResult.matchAll(/block merkle root ([a-f0-9]+)/gi)];
345
+ const merkleRoots = merkleRootMatches.map((m) => m[1]);
346
+ result.attestationValues = [];
347
+ if (transactionIds.length === blockHeights.length && transactionIds.length === merkleRoots.length) {
348
+ for (let i = 0; i < transactionIds.length; i++) {
349
+ result.attestationValues.push({
350
+ blockHeight: blockHeights[i],
351
+ transactionId: transactionIds[i],
352
+ merkleRoot: merkleRoots[i],
353
+ });
354
+ }
355
+ result.attestationValues.sort((a, b) => a.blockHeight - b.blockHeight);
356
+ }
357
+ return JSON.stringify(result);
358
+ }
359
+ /**
360
+ * Parses timestamp verification result into readable information.
361
+ * Formats blockchain attestation data with human-readable messages and dates.
362
+ * @param verifyResult - Timestamp verification result to parse
363
+ * @returns Promise resolving to formatted timestamp information
364
+ */
365
+ async function parseVerifyResult(verifyResult) {
366
+ let tsInfo = {
367
+ message: '',
368
+ results: [],
369
+ attestDate: null,
370
+ attestHeight: null,
371
+ submittedUri: null,
372
+ otsUpgraded: false,
373
+ };
374
+ for (const chain in verifyResult?.results) {
375
+ const _results = verifyResult.results[chain];
376
+ const _date = (0, moment_timezone_1.default)(_results.timestamp * 1000).tz('UTC').format('MM-DD-YYYY hh:mma z');
377
+ const _blockchain = chain.charAt(0).toUpperCase() + chain.slice(1);
378
+ tsInfo.message = `Validated - ${_blockchain} block ${_results.height} attests existence as of ${_date}`;
379
+ if (tsInfo.attestHeight == null) {
380
+ tsInfo.attestDate = new Date(_results.timestamp * 1000);
381
+ tsInfo.attestHeight = _results.height;
382
+ }
383
+ tsInfo.results.push(tsInfo.message);
384
+ }
385
+ if (verifyResult?.upgradedOts) {
386
+ tsInfo.otsUpgraded = true;
387
+ tsInfo.results.push('Notice: Please select "Upgrade Timestamp" to save the upgraded timestamp.');
388
+ }
389
+ return tsInfo;
390
+ }
391
+ // =========================
392
+ // Server-side certificate generation (HTML)
393
+ // =========================
394
+ /**
395
+ * Creates an HTML certificate for OpenTimestamps verification.
396
+ * Generates a certificate file documenting the timestamp verification results.
397
+ * @param fileName - Base name for the ZIP file (used for certificate filename)
398
+ * @param zipEntries - Array of ZIP entries to include in certificate
399
+ * @param timestampResult - Verification result to document
400
+ * @returns Promise resolving to success message with certificate filename, or error message
401
+ */
402
+ async function createCertificateOts(fileName, zipEntries, timestampResult) {
403
+ if (!timestampResult || !timestampResult.merkleRoot || !fileName) {
404
+ return Errors_1.default.CERTIFICATE_OTS_BAD;
405
+ }
406
+ const rsp = await deserializeOts(timestampResult);
407
+ if (!rsp)
408
+ return Errors_1.default.CERTIFICATE_OTS_FAILED;
409
+ const result = JSON.parse(rsp);
410
+ const cert = (0, CertificateOTS_1.default)(result.attestStr, fileName, timestampResult.merkleRoot, result.needsUpdateMsg, result.attestationValues, zipEntries, false);
411
+ if (!cert)
412
+ return Errors_1.default.CERTIFICATE_OTS_FAILED;
413
+ const certFileName = fileName + '.certificate.html';
414
+ try {
415
+ fs.writeFileSync(certFileName, cert, { encoding: 'utf8' });
416
+ return `Certificate created as ${certFileName}`;
417
+ }
418
+ catch (error) {
419
+ console.error(`Failed to write certificate file: ${error}`);
420
+ return Errors_1.default.CERTIFICATE_OTS_FAILED;
421
+ }
422
+ }
423
+ /**
424
+ * Upgrades OpenTimestamps proof in an existing ZIP file.
425
+ * Replaces old timestamp metadata with upgraded OTS proof data.
426
+ * @param zipFilePath - Path to ZIP file to upgrade
427
+ * @param upgradedOts - Buffer containing upgraded OTS proof
428
+ * @returns Promise that resolves when upgrade is complete
429
+ * @throws Error if upgrade fails
430
+ */
431
+ async function upgradeOTS(zipFilePath, upgradedOts) {
432
+ if (!fs.existsSync(zipFilePath)) {
433
+ throw new Error(`Zip file not found: ${zipFilePath}`);
434
+ }
435
+ if (!upgradedOts || upgradedOts.length === 0) {
436
+ throw new Error('Invalid upgraded OTS data provided');
437
+ }
438
+ // Create a temporary file for the upgrade process
439
+ const tempPath = `${zipFilePath}.upgrading.${Date.now()}.${process.pid}`;
440
+ try {
441
+ // Load the existing zip file
442
+ const zipKit = new ZipKit_1.default();
443
+ const zipData = fs.readFileSync(zipFilePath);
444
+ zipKit.loadZip(zipData);
445
+ const zipEntries = zipKit.getDirectory(false) || [];
446
+ if (!zipEntries || zipEntries.length === 0) {
447
+ throw new Error('No entries found in zip file or file is corrupted');
448
+ }
449
+ // Prepare buffers for the new zip file
450
+ const buffers = [];
451
+ let offset = 0;
452
+ const keptEntries = [];
453
+ // Process existing entries, excluding old timestamp entries
454
+ for (const entry of zipEntries) {
455
+ if (entry.filename === Headers_1.TIMESTAMP_METADATA ||
456
+ entry.filename === Headers_1.TIMESTAMP_SUBMITTED) {
457
+ // Skip old timestamp entries - they will be replaced
458
+ continue;
459
+ }
460
+ // Copy the entry data
461
+ const entryData = zipKit.copyEntry(entry);
462
+ if (!entryData) {
463
+ throw new Error(`Failed to copy entry data for: ${entry.filename}`);
464
+ }
465
+ // Update the entry's local header offset
466
+ entry.localHdrOffset = offset;
467
+ offset += entryData.length;
468
+ buffers.push(entryData);
469
+ keptEntries.push(entry);
470
+ }
471
+ // Create the new upgraded timestamp entry with standard filename
472
+ const tsEntry = zipKit.createZipEntry(Headers_1.TIMESTAMP_METADATA);
473
+ if (!tsEntry) {
474
+ throw new Error('Failed to create timestamp metadata entry');
475
+ }
476
+ // Configure the timestamp entry
477
+ tsEntry.timeDateDOS = tsEntry.setDateTime(new Date());
478
+ tsEntry.compressedSize = upgradedOts.length;
479
+ tsEntry.uncompressedSize = upgradedOts.length;
480
+ tsEntry.cmpMethod = Headers_1.CMP_METHOD.STORED; // Store without compression
481
+ tsEntry.crc = (0, Util_1.crc32)(upgradedOts);
482
+ tsEntry.fileData = upgradedOts; // Cast to any to avoid type issues
483
+ // Create and add the local header for the timestamp entry
484
+ const localHdr = tsEntry.createLocalHdr();
485
+ if (!localHdr) {
486
+ throw new Error('Failed to create local header for timestamp entry');
487
+ }
488
+ tsEntry.localHdrOffset = offset;
489
+ offset += localHdr.length + tsEntry.compressedSize;
490
+ buffers.push(localHdr);
491
+ buffers.push(upgradedOts);
492
+ // Add the timestamp entry to our list of entries
493
+ keptEntries.push(tsEntry);
494
+ // Create central directory
495
+ const centralDirOffset = offset;
496
+ let centralDirSize = 0;
497
+ for (const entry of keptEntries) {
498
+ const centralDirEntry = entry.centralDirEntry();
499
+ if (!centralDirEntry) {
500
+ throw new Error(`Failed to create central directory entry for: ${entry.filename}`);
501
+ }
502
+ centralDirSize += centralDirEntry.length;
503
+ buffers.push(centralDirEntry);
504
+ }
505
+ // Create end of central directory record
506
+ const endOfCentralDir = Buffer.alloc(22);
507
+ endOfCentralDir.writeUInt32LE(0x06054b50, 0); // End of central dir signature
508
+ endOfCentralDir.writeUInt16LE(0, 4); // Number of this disk
509
+ endOfCentralDir.writeUInt16LE(0, 6); // Disk where central directory starts
510
+ endOfCentralDir.writeUInt16LE(keptEntries.length, 8); // Number of central directory records on this disk
511
+ endOfCentralDir.writeUInt16LE(keptEntries.length, 10); // Total number of central directory records
512
+ endOfCentralDir.writeUInt32LE(centralDirSize, 12); // Size of central directory
513
+ endOfCentralDir.writeUInt32LE(centralDirOffset, 16); // Offset of start of central directory
514
+ endOfCentralDir.writeUInt16LE(0, 20); // ZIP file comment length
515
+ buffers.push(endOfCentralDir);
516
+ // Write the complete new zip file to temporary location
517
+ const finalBuffer = Buffer.concat(buffers);
518
+ fs.writeFileSync(tempPath, finalBuffer);
519
+ // Validate the temporary file was created successfully
520
+ if (!fs.existsSync(tempPath)) {
521
+ throw new Error('Failed to create temporary upgraded file');
522
+ }
523
+ const tempStats = fs.statSync(tempPath);
524
+ if (tempStats.size === 0) {
525
+ throw new Error('Temporary upgraded file is empty');
526
+ }
527
+ // Replace the original file with the upgraded version
528
+ fs.unlinkSync(zipFilePath);
529
+ fs.renameSync(tempPath, zipFilePath);
530
+ }
531
+ catch (error) {
532
+ // Clean up temporary file if it exists
533
+ if (fs.existsSync(tempPath)) {
534
+ try {
535
+ fs.unlinkSync(tempPath);
536
+ }
537
+ catch (cleanupError) {
538
+ // Log cleanup error but don't throw - main error is more important
539
+ console.warn(`Warning: Could not clean up temporary file ${tempPath}: ${cleanupError}`);
540
+ }
541
+ }
542
+ // Re-throw the original error
543
+ throw error;
544
+ }
545
+ }
546
+ /**
547
+ * Creates a ZIP metadata entry containing OpenTimestamps proof.
548
+ * Creates a special ZIP entry marked as metadata that stores OTS proof data.
549
+ * @param zipKit - ZipKit instance to add metadata entry to
550
+ * @param ots - Buffer containing OpenTimestamps proof data
551
+ * @returns ZIP entry containing OTS metadata, or null if creation failed
552
+ */
553
+ function createOtsMetadataEntry(zipKit, ots) {
554
+ if (!ots || !zipKit)
555
+ return null;
556
+ const tsEntry = zipKit.createZipEntry(Headers_1.TIMESTAMP_SUBMITTED);
557
+ tsEntry.crc = (0, Util_1.crc32)(ots);
558
+ tsEntry.uncompressedSize = ots.length;
559
+ tsEntry.compressedSize = ots.length;
560
+ tsEntry.cmpMethod = Headers_1.CMP_METHOD.STORED;
561
+ tsEntry.fileBuffer = ots;
562
+ tsEntry.isMetaData = true;
563
+ return tsEntry;
564
+ }
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ZipKitVerifier - Handles NFT token verification for ZIP files
4
+ * Verifier API for NZIP-NFT smart contract.
5
+ */
6
+ import type { TokenMetadata } from './types';
7
+ export interface VerificationOptions {
8
+ debug?: boolean;
9
+ skipHash?: boolean;
10
+ }
11
+ export interface VerificationResult {
12
+ success: boolean;
13
+ message: string;
14
+ tokenMetadata?: TokenMetadata;
15
+ verificationDetails?: {
16
+ tokenId: string;
17
+ network: string;
18
+ contractAddress: string;
19
+ merkleRoot: string;
20
+ mintDate: string;
21
+ calculatedMerkleRoot?: string;
22
+ declaredMerkleRoot?: string;
23
+ merkleRootMatch?: boolean;
24
+ onChainValid?: boolean;
25
+ };
26
+ errorDetails?: {
27
+ errorType: 'METADATA_ERROR' | 'MERKLE_ERROR' | 'NETWORK_ERROR' | 'CONTRACT_ERROR';
28
+ networkName?: string;
29
+ rpcUrl?: string;
30
+ contractAddress?: string;
31
+ tokenId?: string;
32
+ merkleRoot?: string;
33
+ };
34
+ }
35
+ /**
36
+ * Blockchain Token Verifier for ZIP files
37
+ */
38
+ export declare class ZipKitVerifier {
39
+ private debug;
40
+ constructor(options?: VerificationOptions);
41
+ /**
42
+ * Extract and validate token metadata from ZIP
43
+ */
44
+ extractTokenMetadata(tokenBuffer: Buffer): Promise<{
45
+ success: boolean;
46
+ metadata?: TokenMetadata;
47
+ error?: string;
48
+ }>;
49
+ /**
50
+ * Map network name to RPC configuration
51
+ */
52
+ private getNetworkConfig;
53
+ /**
54
+ * Perform merkle root comparison
55
+ */
56
+ private compareMerkleRoots;
57
+ /**
58
+ * Verify token against blockchain
59
+ */
60
+ verifyOnChain(tokenId: string, contractAddress: string, networkConfig: any, merkleRoot: string): Promise<{
61
+ success: boolean;
62
+ isValid?: boolean;
63
+ error?: string;
64
+ }>;
65
+ /**
66
+ * Complete verification process
67
+ */
68
+ verifyToken(tokenMetadata: TokenMetadata, calculatedMerkleRoot: string | null, options?: VerificationOptions): Promise<VerificationResult>;
69
+ }