neozip-blockchain 0.5.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.
- package/LICENSE +22 -0
- package/README.md +189 -0
- package/dist/browser/TokenVerifierBrowser.d.ts +63 -0
- package/dist/browser/TokenVerifierBrowser.d.ts.map +1 -0
- package/dist/browser/TokenVerifierBrowser.js +358 -0
- package/dist/browser/TokenVerifierBrowser.js.map +1 -0
- package/dist/browser/WalletManagerBrowser.d.ts +99 -0
- package/dist/browser/WalletManagerBrowser.d.ts.map +1 -0
- package/dist/browser/WalletManagerBrowser.js +434 -0
- package/dist/browser/WalletManagerBrowser.js.map +1 -0
- package/dist/browser/ZipkitMinterBrowser.d.ts +59 -0
- package/dist/browser/ZipkitMinterBrowser.d.ts.map +1 -0
- package/dist/browser/ZipkitMinterBrowser.js +337 -0
- package/dist/browser/ZipkitMinterBrowser.js.map +1 -0
- package/dist/browser/index.d.ts +12 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +19 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/constants/index.d.ts +5 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +21 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/constants/metadata.d.ts +18 -0
- package/dist/constants/metadata.d.ts.map +1 -0
- package/dist/constants/metadata.js +33 -0
- package/dist/constants/metadata.js.map +1 -0
- package/dist/constants/servers.d.ts +38 -0
- package/dist/constants/servers.d.ts.map +1 -0
- package/dist/constants/servers.js +54 -0
- package/dist/constants/servers.js.map +1 -0
- package/dist/core/ContractVersionRegistry.d.ts +63 -0
- package/dist/core/ContractVersionRegistry.d.ts.map +1 -0
- package/dist/core/ContractVersionRegistry.js +128 -0
- package/dist/core/ContractVersionRegistry.js.map +1 -0
- package/dist/core/WalletManager.d.ts +123 -0
- package/dist/core/WalletManager.d.ts.map +1 -0
- package/dist/core/WalletManager.js +360 -0
- package/dist/core/WalletManager.js.map +1 -0
- package/dist/core/ZipkitMinter.d.ts +155 -0
- package/dist/core/ZipkitMinter.d.ts.map +1 -0
- package/dist/core/ZipkitMinter.js +625 -0
- package/dist/core/ZipkitMinter.js.map +1 -0
- package/dist/core/ZipkitVerifier.d.ts +252 -0
- package/dist/core/ZipkitVerifier.d.ts.map +1 -0
- package/dist/core/ZipkitVerifier.js +1370 -0
- package/dist/core/ZipkitVerifier.js.map +1 -0
- package/dist/core/adapters/AdapterFactory.d.ts +22 -0
- package/dist/core/adapters/AdapterFactory.d.ts.map +1 -0
- package/dist/core/adapters/AdapterFactory.js +62 -0
- package/dist/core/adapters/AdapterFactory.js.map +1 -0
- package/dist/core/adapters/ContractVersionAdapter.d.ts +96 -0
- package/dist/core/adapters/ContractVersionAdapter.d.ts.map +1 -0
- package/dist/core/adapters/ContractVersionAdapter.js +9 -0
- package/dist/core/adapters/ContractVersionAdapter.js.map +1 -0
- package/dist/core/adapters/V2_10Adapter.d.ts +24 -0
- package/dist/core/adapters/V2_10Adapter.d.ts.map +1 -0
- package/dist/core/adapters/V2_10Adapter.js +87 -0
- package/dist/core/adapters/V2_10Adapter.js.map +1 -0
- package/dist/core/adapters/V2_11Adapter.d.ts +24 -0
- package/dist/core/adapters/V2_11Adapter.d.ts.map +1 -0
- package/dist/core/adapters/V2_11Adapter.js +87 -0
- package/dist/core/adapters/V2_11Adapter.js.map +1 -0
- package/dist/core/adapters/V2_50Adapter.d.ts +105 -0
- package/dist/core/adapters/V2_50Adapter.d.ts.map +1 -0
- package/dist/core/adapters/V2_50Adapter.js +263 -0
- package/dist/core/adapters/V2_50Adapter.js.map +1 -0
- package/dist/core/contracts.d.ts +169 -0
- package/dist/core/contracts.d.ts.map +1 -0
- package/dist/core/contracts.js +731 -0
- package/dist/core/contracts.js.map +1 -0
- package/dist/core/index.d.ts +24 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +82 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/node/WalletManagerNode.d.ts +47 -0
- package/dist/node/WalletManagerNode.d.ts.map +1 -0
- package/dist/node/WalletManagerNode.js +203 -0
- package/dist/node/WalletManagerNode.js.map +1 -0
- package/dist/node/index.d.ts +12 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +21 -0
- package/dist/node/index.js.map +1 -0
- package/dist/ots/ZipkitOTS.d.ts +152 -0
- package/dist/ots/ZipkitOTS.d.ts.map +1 -0
- package/dist/ots/ZipkitOTS.js +544 -0
- package/dist/ots/ZipkitOTS.js.map +1 -0
- package/dist/ots/index.d.ts +9 -0
- package/dist/ots/index.d.ts.map +1 -0
- package/dist/ots/index.js +25 -0
- package/dist/ots/index.js.map +1 -0
- package/dist/types/index.d.ts +84 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +37 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/Logger.d.ts +73 -0
- package/dist/utils/Logger.d.ts.map +1 -0
- package/dist/utils/Logger.js +156 -0
- package/dist/utils/Logger.js.map +1 -0
- package/dist/utils/crc32.d.ts +6 -0
- package/dist/utils/crc32.d.ts.map +1 -0
- package/dist/utils/crc32.js +23 -0
- package/dist/utils/crc32.js.map +1 -0
- package/dist/zipstamp-server/CalendarManager.d.ts +238 -0
- package/dist/zipstamp-server/CalendarManager.d.ts.map +1 -0
- package/dist/zipstamp-server/CalendarManager.js +364 -0
- package/dist/zipstamp-server/CalendarManager.js.map +1 -0
- package/dist/zipstamp-server/ZipstampServerAPI.d.ts +427 -0
- package/dist/zipstamp-server/ZipstampServerAPI.d.ts.map +1 -0
- package/dist/zipstamp-server/ZipstampServerAPI.js +696 -0
- package/dist/zipstamp-server/ZipstampServerAPI.js.map +1 -0
- package/dist/zipstamp-server/ZipstampServerClient.d.ts +590 -0
- package/dist/zipstamp-server/ZipstampServerClient.d.ts.map +1 -0
- package/dist/zipstamp-server/ZipstampServerClient.js +433 -0
- package/dist/zipstamp-server/ZipstampServerClient.js.map +1 -0
- package/dist/zipstamp-server/ZipstampServerHelpers.d.ts +430 -0
- package/dist/zipstamp-server/ZipstampServerHelpers.d.ts.map +1 -0
- package/dist/zipstamp-server/ZipstampServerHelpers.js +503 -0
- package/dist/zipstamp-server/ZipstampServerHelpers.js.map +1 -0
- package/dist/zipstamp-server/index.d.ts +24 -0
- package/dist/zipstamp-server/index.d.ts.map +1 -0
- package/dist/zipstamp-server/index.js +67 -0
- package/dist/zipstamp-server/index.js.map +1 -0
- package/package.json +117 -0
|
@@ -0,0 +1,696 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ======================================
|
|
4
|
+
* ZipstampServerAPI.ts - Zipstamp Server API Integration
|
|
5
|
+
* ======================================
|
|
6
|
+
*
|
|
7
|
+
* This module provides timestamping functionality using the Zipstamp server API,
|
|
8
|
+
* similar to OpenTimestamps but using Ethereum blockchain.
|
|
9
|
+
*
|
|
10
|
+
* Key Features:
|
|
11
|
+
* - Ethereum blockchain timestamping via Zipstamp server
|
|
12
|
+
* - Merkle root calculation and verification
|
|
13
|
+
* - ZIP file integration for timestamp metadata
|
|
14
|
+
* - Support for immediate, batched, and transferable modes
|
|
15
|
+
*
|
|
16
|
+
* @fileoverview Zipstamp server API utilities for blockchain timestamping
|
|
17
|
+
* @author NeoWare Inc.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.CMP_METHOD = void 0;
|
|
21
|
+
exports.getEthTimestampEntry = getEthTimestampEntry;
|
|
22
|
+
exports.extractTimestampData = extractTimestampData;
|
|
23
|
+
exports.createTimestamp = createTimestamp;
|
|
24
|
+
exports.verifyTimestamp = verifyTimestamp;
|
|
25
|
+
exports.verifyTimestampedZip = verifyTimestampedZip;
|
|
26
|
+
exports.createTimestampMetadataEntry = createTimestampMetadataEntry;
|
|
27
|
+
exports.createTimestampedZip = createTimestampedZip;
|
|
28
|
+
exports.findMetadataEntry = findMetadataEntry;
|
|
29
|
+
exports.getMetadataType = getMetadataType;
|
|
30
|
+
exports.shouldUpgrade = shouldUpgrade;
|
|
31
|
+
exports.getMetadataFileNames = getMetadataFileNames;
|
|
32
|
+
exports.verifyMerkleProofLocal = verifyMerkleProofLocal;
|
|
33
|
+
const contracts_1 = require("../core/contracts");
|
|
34
|
+
const servers_1 = require("../constants/servers");
|
|
35
|
+
const metadata_1 = require("../constants/metadata");
|
|
36
|
+
const crc32_1 = require("../utils/crc32");
|
|
37
|
+
const ethers_1 = require("ethers");
|
|
38
|
+
const ZipstampServerHelpers_1 = require("./ZipstampServerHelpers");
|
|
39
|
+
exports.CMP_METHOD = { STORED: 0, DEFLATED: 8 };
|
|
40
|
+
/**
|
|
41
|
+
* Safely extracts merkle root from a ZIP file.
|
|
42
|
+
*/
|
|
43
|
+
function getMerkleRootSafe(zip) {
|
|
44
|
+
try {
|
|
45
|
+
const mr = zip.getMerkleRoot?.();
|
|
46
|
+
return (typeof mr === 'string' && mr.length > 0) ? mr : null;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Finds the timestamp metadata entry in a ZIP file.
|
|
54
|
+
*
|
|
55
|
+
* Searches for timestamp metadata entries in the ZIP directory, preferring confirmed
|
|
56
|
+
* timestamps (TIMESTAMP.NZIP) over pending submissions (TS-SUBMIT.NZIP).
|
|
57
|
+
*
|
|
58
|
+
* @param zip - A ZipkitLike instance representing the ZIP file to search
|
|
59
|
+
* @returns The ZIP entry containing timestamp metadata, or `null` if not found
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* const entry = getEthTimestampEntry(zipkit);
|
|
64
|
+
* if (entry) {
|
|
65
|
+
* const metadata = await extractTimestampData(zipkit, entry);
|
|
66
|
+
* console.log('Timestamp status:', metadata?.status);
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
function getEthTimestampEntry(zip) {
|
|
71
|
+
try {
|
|
72
|
+
const entries = zip.getDirectory?.(true) || [];
|
|
73
|
+
// Prefer confirmed metadata entry first
|
|
74
|
+
let entry = entries.find((e) => e && e.filename === metadata_1.TIMESTAMP_NZIP);
|
|
75
|
+
if (entry)
|
|
76
|
+
return entry;
|
|
77
|
+
// Fallback to submitted proof entry
|
|
78
|
+
entry = entries.find((e) => e && e.filename === metadata_1.TS_SUBMIT_NZIP);
|
|
79
|
+
if (entry)
|
|
80
|
+
return entry;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Extracts timestamp metadata from a ZIP entry.
|
|
89
|
+
*
|
|
90
|
+
* Reads and parses the JSON metadata from a timestamp entry (either TIMESTAMP.NZIP
|
|
91
|
+
* or TS-SUBMIT.NZIP) in the ZIP file.
|
|
92
|
+
*
|
|
93
|
+
* @param zip - A ZipkitLike instance representing the ZIP file
|
|
94
|
+
* @param entry - The ZIP entry containing the timestamp metadata (from `getEthTimestampEntry`)
|
|
95
|
+
* @returns Parsed timestamp metadata object, or `null` if extraction fails
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```typescript
|
|
99
|
+
* const entry = getEthTimestampEntry(zipkit);
|
|
100
|
+
* if (entry) {
|
|
101
|
+
* const metadata = await extractTimestampData(zipkit, entry);
|
|
102
|
+
* if (metadata) {
|
|
103
|
+
* console.log('Digest:', metadata.digest);
|
|
104
|
+
* console.log('Batch ID:', metadata.batchId);
|
|
105
|
+
* }
|
|
106
|
+
* }
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
async function extractTimestampData(zip, entry) {
|
|
110
|
+
try {
|
|
111
|
+
const data = await zip.extract?.(entry, true);
|
|
112
|
+
if (!data)
|
|
113
|
+
return null;
|
|
114
|
+
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
115
|
+
const jsonString = buffer.toString('utf-8');
|
|
116
|
+
const metadata = JSON.parse(jsonString);
|
|
117
|
+
return metadata;
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Creates a timestamp by submitting a merkle root to the Zipstamp server.
|
|
125
|
+
*
|
|
126
|
+
* Submits a SHA-256 digest (merkle root) to the Zipstamp server API for blockchain
|
|
127
|
+
* timestamping. The digest is typically the merkle root of a ZIP file's contents.
|
|
128
|
+
*
|
|
129
|
+
* The function uses `submitDigest` internally and converts the response to a
|
|
130
|
+
* `TimestampMetadata` format suitable for storage in ZIP files.
|
|
131
|
+
*
|
|
132
|
+
* @param merkleRoot - A 64-character hexadecimal string representing the SHA-256 hash
|
|
133
|
+
* @param options - Configuration options for timestamp creation
|
|
134
|
+
* @param options.recipientEmail - Optional email address for notifications
|
|
135
|
+
* @param options.serverUrl - Optional Zipstamp server URL (defaults to `ZIPSTAMP_SERVER_URL` env var or `https://zipstamp-dev.neozip.io`)
|
|
136
|
+
* @param options.debug - Enable debug logging (defaults to `false`)
|
|
137
|
+
* @param options.mode - **Deprecated**: For backward compatibility only, not sent to API
|
|
138
|
+
* @param options.recipientAddress - **Deprecated**: For backward compatibility only, not sent to API
|
|
139
|
+
* @param options.metadata - **Deprecated**: For backward compatibility only, not sent to API
|
|
140
|
+
* @returns Promise resolving to timestamp metadata, or `null` if creation fails
|
|
141
|
+
* @throws {Error} If merkle root format is invalid or API request fails
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```typescript
|
|
145
|
+
* const merkleRoot = 'a1b2c3d4e5f6...'; // 64-char hex string
|
|
146
|
+
* const metadata = await createTimestamp(merkleRoot, {
|
|
147
|
+
* recipientEmail: 'user@example.com',
|
|
148
|
+
* debug: true
|
|
149
|
+
* });
|
|
150
|
+
*
|
|
151
|
+
* if (metadata) {
|
|
152
|
+
* console.log('Timestamp created:', metadata.batchId);
|
|
153
|
+
* console.log('Status:', metadata.status); // 'pending' or 'confirmed'
|
|
154
|
+
* }
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
async function createTimestamp(merkleRoot, options = {}) {
|
|
158
|
+
if (!merkleRoot || !/^[a-f0-9]{64}$/i.test(merkleRoot)) {
|
|
159
|
+
throw new Error('Invalid merkle root format. Must be a 64-character hex string (SHA-256)');
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
const serverUrl = (0, servers_1.getZipStampServerUrl)(options);
|
|
163
|
+
if (options.debug) {
|
|
164
|
+
console.log('[Zipstamp Server API] ========================================');
|
|
165
|
+
console.log('[Zipstamp Server API] Creating Timestamp');
|
|
166
|
+
console.log('[Zipstamp Server API] ========================================');
|
|
167
|
+
console.log('[Zipstamp Server API] Server URL:', serverUrl);
|
|
168
|
+
console.log('[Zipstamp Server API] Merkle Root:', merkleRoot);
|
|
169
|
+
console.log('[Zipstamp Server API] ----------------------------------------');
|
|
170
|
+
}
|
|
171
|
+
// Use submitDigest helper (only digest, email, and chainId are sent to API)
|
|
172
|
+
// Note: mode, recipientAddress, and metadata options are for backward compatibility
|
|
173
|
+
// but are not actually sent to the Zipstamp server API
|
|
174
|
+
const response = await (0, ZipstampServerHelpers_1.submitDigest)(merkleRoot, options.recipientEmail, undefined, // chainId - let server determine
|
|
175
|
+
{ serverUrl, debug: options.debug });
|
|
176
|
+
if (!response.success) {
|
|
177
|
+
const errorMsg = response.error || 'Failed to create timestamp';
|
|
178
|
+
if (options.debug) {
|
|
179
|
+
console.error('[Zipstamp Server API] Error in response:', errorMsg);
|
|
180
|
+
}
|
|
181
|
+
throw new Error(errorMsg);
|
|
182
|
+
}
|
|
183
|
+
// Get network chain ID from network name
|
|
184
|
+
const networkChainId = response.network
|
|
185
|
+
? ((0, contracts_1.getChainIdByName)(response.network) || 0)
|
|
186
|
+
: 0;
|
|
187
|
+
// Get contract address from network config if available
|
|
188
|
+
let contractAddress = '';
|
|
189
|
+
if (networkChainId) {
|
|
190
|
+
const config = (0, contracts_1.getContractConfig)(networkChainId);
|
|
191
|
+
contractAddress = config?.address || '';
|
|
192
|
+
}
|
|
193
|
+
const timestampMetadata = {
|
|
194
|
+
digest: response.digest,
|
|
195
|
+
status: response.status === 'confirmed' ? 'confirmed' : 'pending',
|
|
196
|
+
tokenId: undefined, // Not in SubmitDigestResponse
|
|
197
|
+
transactionHash: undefined, // Not in SubmitDigestResponse
|
|
198
|
+
contractAddress,
|
|
199
|
+
network: response.network || 'unknown',
|
|
200
|
+
chainId: networkChainId,
|
|
201
|
+
batchId: response.batchId,
|
|
202
|
+
batchNumber: response.batchNumber,
|
|
203
|
+
serverUrl,
|
|
204
|
+
submittedAt: new Date().toISOString(),
|
|
205
|
+
};
|
|
206
|
+
if (options.debug) {
|
|
207
|
+
console.log('[Zipstamp Server API] Processed Timestamp Metadata:');
|
|
208
|
+
console.log(JSON.stringify(timestampMetadata, null, 2));
|
|
209
|
+
console.log('[Zipstamp Server API] ========================================\n');
|
|
210
|
+
}
|
|
211
|
+
return timestampMetadata;
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
if (options.debug) {
|
|
215
|
+
console.error('[Zipstamp Server API] ========================================');
|
|
216
|
+
console.error('[Zipstamp Server API] Error creating timestamp:');
|
|
217
|
+
console.error('[Zipstamp Server API]', error instanceof Error ? error.message : String(error));
|
|
218
|
+
if (error instanceof Error && error.stack) {
|
|
219
|
+
console.error('[Zipstamp Server API] Stack trace:');
|
|
220
|
+
console.error(error.stack);
|
|
221
|
+
}
|
|
222
|
+
console.error('[Zipstamp Server API] ========================================\n');
|
|
223
|
+
}
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Verifies a timestamp against the Zipstamp server.
|
|
229
|
+
*
|
|
230
|
+
* Verifies that a timestamp has been confirmed on the blockchain by checking
|
|
231
|
+
* with the Zipstamp server API. Uses `verifyDigest` internally and converts the
|
|
232
|
+
* response to an `EthTimestampVerifyResult` format.
|
|
233
|
+
*
|
|
234
|
+
* @param merkleRoot - The SHA-256 digest (merkle root) to verify
|
|
235
|
+
* @param timestampData - The timestamp metadata from the ZIP file (contains batchId, chainId, etc.)
|
|
236
|
+
* @param options - Configuration options for verification
|
|
237
|
+
* @param options.serverUrl - Optional Zipstamp server URL (defaults to `ZIPSTAMP_SERVER_URL` env var or `https://zipstamp-dev.neozip.io`)
|
|
238
|
+
* @param options.debug - Enable debug logging (defaults to `false`)
|
|
239
|
+
* @returns Promise resolving to verification result with status and blockchain details
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```typescript
|
|
243
|
+
* const result = await verifyTimestamp(merkleRoot, timestampMetadata, {
|
|
244
|
+
* debug: true
|
|
245
|
+
* });
|
|
246
|
+
*
|
|
247
|
+
* if (result.status === 'valid') {
|
|
248
|
+
* console.log('Verified on blockchain!');
|
|
249
|
+
* console.log('Transaction:', result.transactionHash);
|
|
250
|
+
* console.log('Block:', result.blockNumber);
|
|
251
|
+
* } else if (result.status === 'pending') {
|
|
252
|
+
* console.log('Timestamp is pending confirmation');
|
|
253
|
+
* }
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
async function verifyTimestamp(merkleRoot, timestampData, options = {}) {
|
|
257
|
+
try {
|
|
258
|
+
const serverUrl = (0, servers_1.getZipStampServerUrl)(options);
|
|
259
|
+
if (options.debug) {
|
|
260
|
+
console.log('[Zipstamp Server API] ========================================');
|
|
261
|
+
console.log('[Zipstamp Server API] Verifying Timestamp');
|
|
262
|
+
console.log('[Zipstamp Server API] ========================================');
|
|
263
|
+
console.log('[Zipstamp Server API] Server URL:', serverUrl);
|
|
264
|
+
console.log('[Zipstamp Server API] Merkle Root:', merkleRoot);
|
|
265
|
+
console.log('[Zipstamp Server API] Timestamp Data:');
|
|
266
|
+
console.log(JSON.stringify(timestampData, null, 2));
|
|
267
|
+
console.log('[Zipstamp Server API] ----------------------------------------');
|
|
268
|
+
}
|
|
269
|
+
// Use verifyDigest helper
|
|
270
|
+
const response = await (0, ZipstampServerHelpers_1.verifyDigest)(merkleRoot, timestampData.chainId, timestampData.batchId || undefined, undefined, // client - use default
|
|
271
|
+
{ serverUrl, debug: options.debug });
|
|
272
|
+
if (!response.success) {
|
|
273
|
+
const errorMsg = response.error || 'Verification failed';
|
|
274
|
+
if (options.debug) {
|
|
275
|
+
console.error('[Zipstamp Server API] Error in response:', errorMsg);
|
|
276
|
+
console.log('[Zipstamp Server API] ========================================\n');
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
status: 'error',
|
|
280
|
+
message: errorMsg,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
if (response.verified) {
|
|
284
|
+
const result = {
|
|
285
|
+
status: 'valid',
|
|
286
|
+
verified: true,
|
|
287
|
+
digest: response.digest,
|
|
288
|
+
tokenId: response.tokenId,
|
|
289
|
+
transactionHash: response.transactionHash,
|
|
290
|
+
contractAddress: response.contractAddress,
|
|
291
|
+
network: response.network,
|
|
292
|
+
blockNumber: response.blockNumber,
|
|
293
|
+
timestamp: response.timestamp,
|
|
294
|
+
};
|
|
295
|
+
if (options.debug) {
|
|
296
|
+
console.log('[Zipstamp Server API] Verification Result (VALID):');
|
|
297
|
+
console.log(JSON.stringify(result, null, 2));
|
|
298
|
+
console.log('[Zipstamp Server API] ========================================\n');
|
|
299
|
+
}
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
const pendingResult = {
|
|
303
|
+
status: 'pending',
|
|
304
|
+
verified: false,
|
|
305
|
+
digest: response.digest,
|
|
306
|
+
message: 'Timestamp is pending confirmation',
|
|
307
|
+
};
|
|
308
|
+
if (options.debug) {
|
|
309
|
+
console.log('[Zipstamp Server API] Verification Result (PENDING):');
|
|
310
|
+
console.log(JSON.stringify(pendingResult, null, 2));
|
|
311
|
+
console.log('[Zipstamp Server API] ========================================\n');
|
|
312
|
+
}
|
|
313
|
+
return pendingResult;
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
const errorResult = {
|
|
317
|
+
status: 'error',
|
|
318
|
+
message: error instanceof Error ? error.message : 'Verification error',
|
|
319
|
+
};
|
|
320
|
+
if (options.debug) {
|
|
321
|
+
console.error('[Zipstamp Server API] ========================================');
|
|
322
|
+
console.error('[Zipstamp Server API] Error during verification:');
|
|
323
|
+
console.error('[Zipstamp Server API]', error instanceof Error ? error.message : String(error));
|
|
324
|
+
if (error instanceof Error && error.stack) {
|
|
325
|
+
console.error('[Zipstamp Server API] Stack trace:');
|
|
326
|
+
console.error(error.stack);
|
|
327
|
+
}
|
|
328
|
+
console.error('[Zipstamp Server API] ========================================\n');
|
|
329
|
+
}
|
|
330
|
+
return errorResult;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Verifies a timestamp within a ZIP file.
|
|
335
|
+
*
|
|
336
|
+
* Extracts the merkle root and timestamp metadata from a ZIP file, then verifies
|
|
337
|
+
* the timestamp against the Zipstamp server API. This is a convenience function that
|
|
338
|
+
* combines `getEthTimestampEntry`, `extractTimestampData`, and `verifyTimestamp`.
|
|
339
|
+
*
|
|
340
|
+
* @param zip - A ZipkitLike instance representing the timestamped ZIP file
|
|
341
|
+
* @param options - Configuration options for verification
|
|
342
|
+
* @param options.serverUrl - Optional Zipstamp server URL (defaults to `ZIPSTAMP_SERVER_URL` env var or `https://zipstamp-dev.neozip.io`)
|
|
343
|
+
* @param options.debug - Enable debug logging (defaults to `false`)
|
|
344
|
+
* @returns Promise resolving to verification result
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* ```typescript
|
|
348
|
+
* const result = await verifyTimestampedZip(zipkit, { debug: true });
|
|
349
|
+
*
|
|
350
|
+
* switch (result.status) {
|
|
351
|
+
* case 'valid':
|
|
352
|
+
* console.log('✅ Timestamp verified on blockchain');
|
|
353
|
+
* break;
|
|
354
|
+
* case 'pending':
|
|
355
|
+
* console.log('⏳ Timestamp pending confirmation');
|
|
356
|
+
* break;
|
|
357
|
+
* case 'none':
|
|
358
|
+
* console.log('❌ No timestamp found in ZIP file');
|
|
359
|
+
* break;
|
|
360
|
+
* case 'error':
|
|
361
|
+
* console.log('❌ Verification error:', result.message);
|
|
362
|
+
* break;
|
|
363
|
+
* }
|
|
364
|
+
* ```
|
|
365
|
+
*/
|
|
366
|
+
async function verifyTimestampedZip(zip, options = {}) {
|
|
367
|
+
const merkleRoot = getMerkleRootSafe(zip);
|
|
368
|
+
if (!merkleRoot) {
|
|
369
|
+
return { status: 'error', message: 'Merkle root not found in ZIP file' };
|
|
370
|
+
}
|
|
371
|
+
const entry = getEthTimestampEntry(zip);
|
|
372
|
+
if (!entry) {
|
|
373
|
+
return { status: 'none', message: 'No timestamp entry found in ZIP file' };
|
|
374
|
+
}
|
|
375
|
+
const timestampData = await extractTimestampData(zip, entry);
|
|
376
|
+
if (!timestampData) {
|
|
377
|
+
return { status: 'error', message: 'Failed to extract timestamp metadata' };
|
|
378
|
+
}
|
|
379
|
+
// Verify that the digest matches the merkle root
|
|
380
|
+
if (timestampData.digest.toLowerCase() !== merkleRoot.toLowerCase()) {
|
|
381
|
+
return {
|
|
382
|
+
status: 'error',
|
|
383
|
+
message: 'Timestamp digest does not match ZIP merkle root',
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
return await verifyTimestamp(merkleRoot, timestampData, options);
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Creates a ZIP entry containing timestamp metadata.
|
|
390
|
+
*
|
|
391
|
+
* Creates a ZIP entry object that can be added to a ZIP file. The entry contains
|
|
392
|
+
* the timestamp metadata as JSON. The filename is automatically chosen based on
|
|
393
|
+
* the timestamp status:
|
|
394
|
+
* - `TIMESTAMP.NZIP` for confirmed timestamps
|
|
395
|
+
* - `TS-SUBMIT.NZIP` for pending timestamps
|
|
396
|
+
*
|
|
397
|
+
* @param zipkit - A ZipkitLike instance for creating ZIP entries
|
|
398
|
+
* @param timestampMetadata - The timestamp metadata to embed in the ZIP entry
|
|
399
|
+
* @returns A ZIP entry object ready to be added to a ZIP file, or `null` if creation fails
|
|
400
|
+
*
|
|
401
|
+
* @example
|
|
402
|
+
* ```typescript
|
|
403
|
+
* // Create timestamp
|
|
404
|
+
* const metadata = await createTimestamp(merkleRoot);
|
|
405
|
+
*
|
|
406
|
+
* // Create ZIP entry
|
|
407
|
+
* const entry = createTimestampMetadataEntry(zipkit, metadata);
|
|
408
|
+
* if (entry) {
|
|
409
|
+
* // Add entry to ZIP file using zipkit methods
|
|
410
|
+
* zipkit.addEntry(entry);
|
|
411
|
+
* }
|
|
412
|
+
* ```
|
|
413
|
+
*/
|
|
414
|
+
function createTimestampMetadataEntry(zipkit, timestampMetadata) {
|
|
415
|
+
if (!zipkit)
|
|
416
|
+
return null;
|
|
417
|
+
try {
|
|
418
|
+
const jsonString = JSON.stringify(timestampMetadata, null, 2);
|
|
419
|
+
const buffer = Buffer.from(jsonString, 'utf-8');
|
|
420
|
+
// Determine which filename to use based on status
|
|
421
|
+
const filename = timestampMetadata.status === 'confirmed'
|
|
422
|
+
? metadata_1.TIMESTAMP_NZIP
|
|
423
|
+
: metadata_1.TS_SUBMIT_NZIP;
|
|
424
|
+
const entry = zipkit.createZipEntry?.(filename);
|
|
425
|
+
if (!entry)
|
|
426
|
+
return null;
|
|
427
|
+
entry.crc = (0, crc32_1.crc32)(buffer);
|
|
428
|
+
entry.uncompressedSize = buffer.length;
|
|
429
|
+
entry.compressedSize = buffer.length;
|
|
430
|
+
entry.cmpMethod = exports.CMP_METHOD.STORED;
|
|
431
|
+
entry.fileBuffer = buffer;
|
|
432
|
+
entry.isMetaData = true;
|
|
433
|
+
return entry;
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Creates timestamp metadata for a ZIP file.
|
|
441
|
+
*
|
|
442
|
+
* Extracts the merkle root from a ZIP file and submits it to the Zipstamp server
|
|
443
|
+
* for timestamping. Returns the timestamp metadata that can then be added to
|
|
444
|
+
* the ZIP file using `createTimestampMetadataEntry`.
|
|
445
|
+
*
|
|
446
|
+
* **Note**: This function does NOT modify the ZIP file. It only creates the
|
|
447
|
+
* timestamp metadata. You must use `createTimestampMetadataEntry` to create
|
|
448
|
+
* a ZIP entry and add it to your ZIP file.
|
|
449
|
+
*
|
|
450
|
+
* @param zipkit - A ZipkitLike instance representing the ZIP file to timestamp
|
|
451
|
+
* @param options - Configuration options for timestamp creation
|
|
452
|
+
* @param options.recipientEmail - Optional email address for notifications
|
|
453
|
+
* @param options.serverUrl - Optional Zipstamp server URL (defaults to `ZIPSTAMP_SERVER_URL` env var or `https://zipstamp-dev.neozip.io`)
|
|
454
|
+
* @param options.debug - Enable debug logging (defaults to `false`)
|
|
455
|
+
* @returns Promise resolving to timestamp metadata, or `null` if creation fails
|
|
456
|
+
* @throws {Error} If merkle root is not found in ZIP or timestamp creation fails
|
|
457
|
+
*
|
|
458
|
+
* @example
|
|
459
|
+
* ```typescript
|
|
460
|
+
* // Create timestamp metadata
|
|
461
|
+
* const metadata = await createTimestampedZip(zipkit, {
|
|
462
|
+
* recipientEmail: 'user@example.com'
|
|
463
|
+
* });
|
|
464
|
+
*
|
|
465
|
+
* if (metadata) {
|
|
466
|
+
* // Create ZIP entry from metadata
|
|
467
|
+
* const entry = createTimestampMetadataEntry(zipkit, metadata);
|
|
468
|
+
* if (entry) {
|
|
469
|
+
* // Add entry to ZIP file
|
|
470
|
+
* zipkit.addEntry(entry);
|
|
471
|
+
* await zipkit.save('timestamped-file.nzip');
|
|
472
|
+
* }
|
|
473
|
+
* }
|
|
474
|
+
* ```
|
|
475
|
+
*/
|
|
476
|
+
async function createTimestampedZip(zipkit, options = {}) {
|
|
477
|
+
const merkleRoot = getMerkleRootSafe(zipkit);
|
|
478
|
+
if (!merkleRoot) {
|
|
479
|
+
throw new Error('Merkle root not found in ZIP file');
|
|
480
|
+
}
|
|
481
|
+
// Create timestamp
|
|
482
|
+
const timestampMetadata = await createTimestamp(merkleRoot, options);
|
|
483
|
+
if (!timestampMetadata) {
|
|
484
|
+
throw new Error('Failed to create timestamp');
|
|
485
|
+
}
|
|
486
|
+
// Return metadata - caller should use createTimestampMetadataEntry to add to ZIP
|
|
487
|
+
return timestampMetadata;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Finds timestamp metadata entry in an array of ZIP directory entries.
|
|
491
|
+
*
|
|
492
|
+
* Searches through ZIP directory entries to find timestamp metadata files.
|
|
493
|
+
* Prefers confirmed timestamps (TIMESTAMP.NZIP) over pending submissions (TS-SUBMIT.NZIP).
|
|
494
|
+
* Returns both the entry and its type for easier handling.
|
|
495
|
+
*
|
|
496
|
+
* @param entries - Array of ZIP directory entries to search
|
|
497
|
+
* @returns Object containing the metadata entry and its type ('pending' | 'confirmed'), or `null` if not found
|
|
498
|
+
*
|
|
499
|
+
* @example
|
|
500
|
+
* ```typescript
|
|
501
|
+
* const entries = zipkit.getDirectory(true);
|
|
502
|
+
* const result = findMetadataEntry(entries);
|
|
503
|
+
*
|
|
504
|
+
* if (result) {
|
|
505
|
+
* console.log('Found', result.type, 'timestamp');
|
|
506
|
+
* const metadata = await extractTimestampData(zipkit, result.entry);
|
|
507
|
+
* }
|
|
508
|
+
* ```
|
|
509
|
+
*/
|
|
510
|
+
function findMetadataEntry(entries) {
|
|
511
|
+
// Check for confirmed timestamp first
|
|
512
|
+
const timestampEntry = entries.find((e) => e.filename === metadata_1.TIMESTAMP_NZIP);
|
|
513
|
+
if (timestampEntry) {
|
|
514
|
+
return {
|
|
515
|
+
entry: timestampEntry,
|
|
516
|
+
type: 'confirmed'
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
// Fall back to submission metadata
|
|
520
|
+
const submitEntry = entries.find((e) => e.filename === metadata_1.TS_SUBMIT_NZIP);
|
|
521
|
+
if (submitEntry) {
|
|
522
|
+
return {
|
|
523
|
+
entry: submitEntry,
|
|
524
|
+
type: 'pending'
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Determines the metadata type from a ZIP entry's filename.
|
|
531
|
+
*
|
|
532
|
+
* Examines the filename of a ZIP entry to determine if it's a timestamp metadata
|
|
533
|
+
* file and whether it represents a pending or confirmed timestamp.
|
|
534
|
+
*
|
|
535
|
+
* @param entry - The ZIP entry to examine (must have a `filename` property)
|
|
536
|
+
* @returns The metadata type: `'pending'` for TS-SUBMIT.NZIP, `'confirmed'` for TIMESTAMP.NZIP, or `null` if not a timestamp metadata file
|
|
537
|
+
*
|
|
538
|
+
* @example
|
|
539
|
+
* ```typescript
|
|
540
|
+
* const entry = getEthTimestampEntry(zipkit);
|
|
541
|
+
* const type = getMetadataType(entry);
|
|
542
|
+
*
|
|
543
|
+
* if (type === 'pending') {
|
|
544
|
+
* console.log('Timestamp is pending confirmation');
|
|
545
|
+
* } else if (type === 'confirmed') {
|
|
546
|
+
* console.log('Timestamp is confirmed on blockchain');
|
|
547
|
+
* }
|
|
548
|
+
* ```
|
|
549
|
+
*/
|
|
550
|
+
function getMetadataType(entry) {
|
|
551
|
+
if (!entry)
|
|
552
|
+
return null;
|
|
553
|
+
if (entry.filename === metadata_1.TIMESTAMP_NZIP) {
|
|
554
|
+
return 'confirmed';
|
|
555
|
+
}
|
|
556
|
+
if (entry.filename === metadata_1.TS_SUBMIT_NZIP) {
|
|
557
|
+
return 'pending';
|
|
558
|
+
}
|
|
559
|
+
return null;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Determines if a timestamp metadata needs to be upgraded.
|
|
563
|
+
*
|
|
564
|
+
* Checks whether a timestamp should be upgraded from pending (TS-SUBMIT.NZIP) to
|
|
565
|
+
* confirmed (TIMESTAMP.NZIP) status. An upgrade is needed if:
|
|
566
|
+
* - The metadata file type is 'pending' (TS-SUBMIT.NZIP)
|
|
567
|
+
* - OR the metadata status is 'pending' (for backward compatibility)
|
|
568
|
+
* - OR the metadata lacks a transactionHash (not yet confirmed on blockchain)
|
|
569
|
+
*
|
|
570
|
+
* @param metadata - The timestamp metadata object to check
|
|
571
|
+
* @param metadataType - The type of metadata file ('pending' | 'confirmed' | null)
|
|
572
|
+
* @returns `true` if the timestamp should be upgraded, `false` otherwise
|
|
573
|
+
*
|
|
574
|
+
* @example
|
|
575
|
+
* ```typescript
|
|
576
|
+
* const result = findMetadataEntry(entries);
|
|
577
|
+
* if (result && shouldUpgrade(metadata, result.type)) {
|
|
578
|
+
* console.log('Timestamp needs upgrade - batch may be confirmed now');
|
|
579
|
+
* // Call upgrade-zip script or pollForConfirmation
|
|
580
|
+
* }
|
|
581
|
+
* ```
|
|
582
|
+
*/
|
|
583
|
+
function shouldUpgrade(metadata, metadataType) {
|
|
584
|
+
// If we have confirmed metadata file, no upgrade needed
|
|
585
|
+
if (metadataType === 'confirmed') {
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
// If we have pending metadata file, upgrade is needed
|
|
589
|
+
if (metadataType === 'pending') {
|
|
590
|
+
return true;
|
|
591
|
+
}
|
|
592
|
+
// Fallback: check metadata content
|
|
593
|
+
// If no transactionHash, it's not confirmed
|
|
594
|
+
if (!metadata.transactionHash) {
|
|
595
|
+
return true;
|
|
596
|
+
}
|
|
597
|
+
// If status is explicitly pending, upgrade needed
|
|
598
|
+
if (metadata.status === 'pending') {
|
|
599
|
+
return true;
|
|
600
|
+
}
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Returns all timestamp metadata file names.
|
|
605
|
+
*
|
|
606
|
+
* Returns an array of all possible timestamp metadata filenames. Useful for
|
|
607
|
+
* error messages, validation, or filtering operations.
|
|
608
|
+
*
|
|
609
|
+
* @returns Array containing `['META-INF/TIMESTAMP.NZIP', 'META-INF/TS-SUBMIT.NZIP']`
|
|
610
|
+
*
|
|
611
|
+
* @example
|
|
612
|
+
* ```typescript
|
|
613
|
+
* const metadataFiles = getMetadataFileNames();
|
|
614
|
+
* console.log('Looking for:', metadataFiles.join(', '));
|
|
615
|
+
*
|
|
616
|
+
* // Filter entries to exclude metadata files
|
|
617
|
+
* const dataEntries = entries.filter(
|
|
618
|
+
* entry => !metadataFiles.includes(entry.filename)
|
|
619
|
+
* );
|
|
620
|
+
* ```
|
|
621
|
+
*/
|
|
622
|
+
function getMetadataFileNames() {
|
|
623
|
+
return [metadata_1.TIMESTAMP_NZIP, metadata_1.TS_SUBMIT_NZIP];
|
|
624
|
+
}
|
|
625
|
+
// ============================================================================
|
|
626
|
+
// Proof Verification Utilities
|
|
627
|
+
// ============================================================================
|
|
628
|
+
/**
|
|
629
|
+
* Proof verification utilities for Zipstamp server timestamps.
|
|
630
|
+
*
|
|
631
|
+
* The Zipstamp server provides merkle proofs as an array of bytes32 hex strings.
|
|
632
|
+
* This module provides local proof verification in the same style as
|
|
633
|
+
* OpenZeppelin's MerkleProof (sorted pair hashing).
|
|
634
|
+
*/
|
|
635
|
+
function normalizeBytes32(hex) {
|
|
636
|
+
const with0x = hex.startsWith('0x') ? hex : `0x${hex}`;
|
|
637
|
+
const bytes = ethers_1.ethers.getBytes(with0x);
|
|
638
|
+
if (bytes.length !== 32) {
|
|
639
|
+
throw new Error(`Expected bytes32, got ${bytes.length} bytes`);
|
|
640
|
+
}
|
|
641
|
+
return ethers_1.ethers.hexlify(bytes);
|
|
642
|
+
}
|
|
643
|
+
function hashPair(a, b) {
|
|
644
|
+
// OpenZeppelin MerkleProof uses a sorted pair hash
|
|
645
|
+
const aBytes = ethers_1.ethers.getBytes(a);
|
|
646
|
+
const bBytes = ethers_1.ethers.getBytes(b);
|
|
647
|
+
const [left, right] = Buffer.compare(Buffer.from(aBytes), Buffer.from(bBytes)) <= 0 ? [aBytes, bBytes] : [bBytes, aBytes];
|
|
648
|
+
return ethers_1.ethers.keccak256(ethers_1.ethers.concat([left, right]));
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Verifies a Merkle proof locally using sorted pair hashing (keccak256).
|
|
652
|
+
*
|
|
653
|
+
* Performs local verification of a Merkle proof without requiring access to
|
|
654
|
+
* the Zipstamp server or blockchain. Uses the same sorted pair hashing algorithm
|
|
655
|
+
* as OpenZeppelin's MerkleProof library, ensuring compatibility with on-chain
|
|
656
|
+
* verification.
|
|
657
|
+
*
|
|
658
|
+
* This is useful for:
|
|
659
|
+
* - Verifying timestamps offline
|
|
660
|
+
* - Validating proofs before submitting to blockchain
|
|
661
|
+
* - Confirming proof integrity without API calls
|
|
662
|
+
*
|
|
663
|
+
* @param digest - The leaf digest (64-character hex string, bytes32) - typically the ZIP file's merkle root
|
|
664
|
+
* @param batchMerkleRoot - The root of the batch Merkle tree (64-character hex string, bytes32) stored on-chain
|
|
665
|
+
* @param merkleProof - Array of sibling hashes (each 64-character hex string, bytes32) forming the proof path from leaf to root
|
|
666
|
+
* @returns `true` if the proof is valid (digest is proven to be in the batch tree), `false` otherwise
|
|
667
|
+
*
|
|
668
|
+
* @example
|
|
669
|
+
* ```typescript
|
|
670
|
+
* const isValid = verifyMerkleProofLocal(
|
|
671
|
+
* zipMerkleRoot, // Your ZIP file's merkle root
|
|
672
|
+
* batchMerkleRoot, // From TIMESTAMP.NZIP metadata
|
|
673
|
+
* merkleProof // From TIMESTAMP.NZIP metadata
|
|
674
|
+
* );
|
|
675
|
+
*
|
|
676
|
+
* if (isValid) {
|
|
677
|
+
* console.log('✅ Proof is valid - ZIP is in the confirmed batch');
|
|
678
|
+
* } else {
|
|
679
|
+
* console.log('❌ Proof is invalid');
|
|
680
|
+
* }
|
|
681
|
+
* ```
|
|
682
|
+
*/
|
|
683
|
+
function verifyMerkleProofLocal(digest, batchMerkleRoot, merkleProof) {
|
|
684
|
+
try {
|
|
685
|
+
let computed = normalizeBytes32(digest);
|
|
686
|
+
for (const p of merkleProof || []) {
|
|
687
|
+
computed = hashPair(computed, normalizeBytes32(p));
|
|
688
|
+
}
|
|
689
|
+
const root = normalizeBytes32(batchMerkleRoot);
|
|
690
|
+
return computed.toLowerCase() === root.toLowerCase();
|
|
691
|
+
}
|
|
692
|
+
catch {
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
//# sourceMappingURL=ZipstampServerAPI.js.map
|