neozipkit 0.1.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/README.md +400 -0
- package/contracts/Lock.sol +34 -0
- package/contracts/bytecode.txt +1 -0
- package/contracts/compile-contract.js +168 -0
- package/contracts/deploy/deploy-both-contracts.js +286 -0
- package/contracts/deploy/deploy-enhanced-contract.js +197 -0
- package/contracts/deploy/deploy-private-contract.js +190 -0
- package/contracts/deploy/deploy-public-clean.js +1063 -0
- package/contracts/deploy/deploy-public-contract.js +1107 -0
- package/contracts/deployment.config.example.js +25 -0
- package/contracts/remix-compile.js +222 -0
- package/contracts/setup-deployment.js +51 -0
- package/contracts/src/NZIP-NFT-Private.sol +350 -0
- package/contracts/src/NZIP-NFT.sol +265 -0
- package/contracts/src/ZipFileNFT-DEPRECATED-6field.sol +260 -0
- package/contracts/src/ZipFileNFT-DEPRECATED-Enhanced.sol +289 -0
- package/dist/browser/CertificateGeneratorBrowser.d.ts +75 -0
- package/dist/browser/CertificateGeneratorBrowser.js +433 -0
- package/dist/browser/TokenVerifierBrowser.d.ts +76 -0
- package/dist/browser/TokenVerifierBrowser.js +299 -0
- package/dist/browser/ZipKitBrowser.d.ts +18 -0
- package/dist/browser/ZipKitBrowser.js +250 -0
- package/dist/browser/ZipKitStampBrowser.d.ts +2 -0
- package/dist/browser/ZipKitStampBrowser.js +12 -0
- package/dist/browser-index.d.ts +21 -0
- package/dist/browser-index.js +58 -0
- package/dist/browser.d.ts +21 -0
- package/dist/browser.js +58 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +76 -0
- package/dist/server/blockchain/CertificateToken.d.ts +48 -0
- package/dist/server/blockchain/CertificateToken.js +386 -0
- package/dist/server/blockchain/NFTMinter.d.ts +41 -0
- package/dist/server/blockchain/NFTMinter.js +323 -0
- package/dist/server/blockchain/TokenQuerier.d.ts +31 -0
- package/dist/server/blockchain/TokenQuerier.js +259 -0
- package/dist/server/blockchain/TokenVerifier.d.ts +20 -0
- package/dist/server/blockchain/TokenVerifier.js +395 -0
- package/dist/server/blockchain/WalletAnalyzer.d.ts +81 -0
- package/dist/server/blockchain/WalletAnalyzer.js +356 -0
- package/dist/server/blockchain/WalletFormatter.d.ts +39 -0
- package/dist/server/blockchain/WalletFormatter.js +196 -0
- package/dist/server/blockchain/WalletManager.d.ts +46 -0
- package/dist/server/blockchain/WalletManager.js +294 -0
- package/dist/server/blockchain/ZipMetadata.d.ts +27 -0
- package/dist/server/blockchain/ZipMetadata.js +131 -0
- package/dist/server/blockchain/ZipTokenProcessor.d.ts +44 -0
- package/dist/server/blockchain/ZipTokenProcessor.js +204 -0
- package/dist/server/blockchain/constants.d.ts +102 -0
- package/dist/server/blockchain/constants.js +968 -0
- package/dist/server/blockchain/index.d.ts +9 -0
- package/dist/server/blockchain/index.js +44 -0
- package/dist/server/blockchain/test-certificate.d.ts +8 -0
- package/dist/server/blockchain/test-certificate.js +100 -0
- package/dist/server/blockchain/types.d.ts +69 -0
- package/dist/server/blockchain/types.js +51 -0
- package/dist/server/platform/server/ZipKitServer.d.ts +105 -0
- package/dist/server/platform/server/ZipKitServer.js +437 -0
- package/dist/server/platform/servercomponents/ZipKitOTS.d.ts +91 -0
- package/dist/server/platform/servercomponents/ZipKitOTS.js +271 -0
- package/dist/shared/ZipEntry.d.ts +131 -0
- package/dist/shared/ZipEntry.js +686 -0
- package/dist/shared/ZipKit.d.ts +249 -0
- package/dist/shared/ZipKit.js +789 -0
- package/dist/shared/components/CertificateOTS.d.ts +15 -0
- package/dist/shared/components/CertificateOTS.js +116 -0
- package/dist/shared/components/FileSys.d.ts +2 -0
- package/dist/shared/components/FileSys.js +113 -0
- package/dist/shared/components/HashAccumulator.d.ts +55 -0
- package/dist/shared/components/HashAccumulator.js +100 -0
- package/dist/shared/components/Support.d.ts +63 -0
- package/dist/shared/components/Support.js +70 -0
- package/dist/shared/components/Util.d.ts +21 -0
- package/dist/shared/components/Util.js +115 -0
- package/dist/shared/components/ZipCrypto.d.ts +3 -0
- package/dist/shared/components/ZipCrypto.js +78 -0
- package/dist/shared/components/merkleTree.d.ts +66 -0
- package/dist/shared/components/merkleTree.js +136 -0
- package/dist/shared/constants/Errors.d.ts +53 -0
- package/dist/shared/constants/Errors.js +69 -0
- package/dist/shared/constants/Headers.d.ts +167 -0
- package/dist/shared/constants/Headers.js +191 -0
- package/dist/shared/types/index.d.ts +21 -0
- package/dist/shared/types/index.js +3 -0
- package/dist/shared/types.d.ts +33 -0
- package/dist/shared/types.js +8 -0
- package/dist/shared/version.d.ts +4 -0
- package/dist/shared/version.js +7 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/index.js +3 -0
- package/examples/unzip-token.ts +488 -0
- package/examples/zip-token.ts +604 -0
- package/package.json +117 -0
- package/src/browser/CertificateGeneratorBrowser.ts +498 -0
- package/src/browser/TokenVerifierBrowser.ts +344 -0
- package/src/browser/ZipKitBrowser.ts +242 -0
- package/src/browser/ZipKitStampBrowser.ts +9 -0
- package/src/browser-index.ts +36 -0
- package/src/index.ts +48 -0
- package/src/server/blockchain/CertificateToken.ts +444 -0
- package/src/server/blockchain/NFTMinter.ts +357 -0
- package/src/server/blockchain/TokenQuerier.ts +287 -0
- package/src/server/blockchain/TokenVerifier.ts +453 -0
- package/src/server/blockchain/WalletAnalyzer.ts +397 -0
- package/src/server/blockchain/WalletFormatter.ts +206 -0
- package/src/server/blockchain/WalletManager.ts +294 -0
- package/src/server/blockchain/ZipMetadata.ts +117 -0
- package/src/server/blockchain/ZipTokenProcessor.ts +234 -0
- package/src/server/blockchain/constants.ts +969 -0
- package/src/server/blockchain/index.ts +42 -0
- package/src/server/blockchain/test-certificate.ts +137 -0
- package/src/server/blockchain/types.ts +134 -0
- package/src/server/platform/server/ZipKitServer.ts +459 -0
- package/src/server/platform/servercomponents/ZipKitOTS.ts +313 -0
- package/src/server/platform/servercomponents/opentimestamps.d.ts +23 -0
- package/src/shared/ZipEntry.ts +767 -0
- package/src/shared/ZipKit.ts +899 -0
- package/src/shared/components/CertificateOTS.ts +132 -0
- package/src/shared/components/FileSys.ts +87 -0
- package/src/shared/components/HashAccumulator.ts +113 -0
- package/src/shared/components/Support.ts +77 -0
- package/src/shared/components/Util.ts +113 -0
- package/src/shared/components/ZipCrypto.ts +91 -0
- package/src/shared/components/merkleTree.ts +162 -0
- package/src/shared/constants/Errors.ts +82 -0
- package/src/shared/constants/Headers.ts +203 -0
- package/src/shared/types/index.ts +25 -0
- package/src/shared/types/modules.d.ts +22 -0
- package/src/shared/types/opentimestamps.d.ts +23 -0
- package/src/shared/types.d.ts +8 -0
- package/src/shared/types.ts +43 -0
- package/src/shared/version.ts +4 -0
- package/src/types/index.ts +25 -0
- package/src/types/modules.d.ts +22 -0
- package/src/types/opentimestamps.d.ts +23 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
// ======================================
|
|
2
|
+
// TokenVerifierBrowser.ts
|
|
3
|
+
// Browser-compatible blockchain token verification utilities for ZIP archives
|
|
4
|
+
// Copyright (c) 2025 NeoWare, Inc. All rights reserved.
|
|
5
|
+
// ======================================
|
|
6
|
+
|
|
7
|
+
import ZipKitBrowser from './ZipKitBrowser';
|
|
8
|
+
import { ZipEntry } from '../shared/ZipKit';
|
|
9
|
+
import { TOKENIZED_METADATA } from '../shared/constants/Headers';
|
|
10
|
+
import { NZIP_CONTRACT_ABI, CURRENT_DEPLOYMENT } from '../server/blockchain/constants';
|
|
11
|
+
|
|
12
|
+
// Token types (adapted for browser)
|
|
13
|
+
export interface NZipTokenInfo {
|
|
14
|
+
version: string;
|
|
15
|
+
tokenId: string;
|
|
16
|
+
contractAddress: string;
|
|
17
|
+
network: string;
|
|
18
|
+
networkChainId: number;
|
|
19
|
+
transactionHash: string;
|
|
20
|
+
merkleRoot: string;
|
|
21
|
+
fileName: string;
|
|
22
|
+
mintedAt: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface TokenVerificationOptions {
|
|
26
|
+
skipHash?: boolean;
|
|
27
|
+
skipToken?: boolean;
|
|
28
|
+
generateCertificate?: boolean;
|
|
29
|
+
zipFileName?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface TokenVerificationResult {
|
|
33
|
+
isTokenized: boolean;
|
|
34
|
+
tokenMetadata: NZipTokenInfo | null;
|
|
35
|
+
calculatedMerkleRoot: string | null;
|
|
36
|
+
blockchainMerkleRoot: string | null;
|
|
37
|
+
blockchainTimestamp?: number; // Actual blockchain timestamp from tokenizationTime
|
|
38
|
+
verificationPassed: boolean;
|
|
39
|
+
certificatePath?: string;
|
|
40
|
+
errorMessage?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface BlockchainVerification {
|
|
44
|
+
merkleRootMatches: boolean;
|
|
45
|
+
blockchainMerkleRoot: string;
|
|
46
|
+
calculatedMerkleRoot: string;
|
|
47
|
+
verificationStatus: 'PASSED' | 'FAILED' | 'UNAVAILABLE';
|
|
48
|
+
verificationTime?: number;
|
|
49
|
+
blockNumber?: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Network configuration for blockchain queries
|
|
53
|
+
const NETWORK_CONFIG: Record<string, any> = {
|
|
54
|
+
'Base Sepolia': {
|
|
55
|
+
chainId: 84532,
|
|
56
|
+
rpcUrls: [
|
|
57
|
+
'https://sepolia.base.org',
|
|
58
|
+
'https://base-sepolia.g.alchemy.com/v2/demo',
|
|
59
|
+
'https://base-sepolia-rpc.publicnode.com',
|
|
60
|
+
'https://base-sepolia.blockpi.network/v1/rpc/public'
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
'Base Mainnet': {
|
|
64
|
+
chainId: 8453,
|
|
65
|
+
rpcUrls: [
|
|
66
|
+
'https://mainnet.base.org',
|
|
67
|
+
'https://base-mainnet.g.alchemy.com/v2/demo',
|
|
68
|
+
'https://base-rpc.publicnode.com',
|
|
69
|
+
'https://base.blockpi.network/v1/rpc/public'
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Use the complete contract ABI from constants
|
|
75
|
+
const CONTRACT_ABI = NZIP_CONTRACT_ABI;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Browser-compatible token verification class
|
|
79
|
+
*/
|
|
80
|
+
export class TokenVerifierBrowser {
|
|
81
|
+
private zipKit: ZipKitBrowser;
|
|
82
|
+
|
|
83
|
+
constructor(zipKit: ZipKitBrowser) {
|
|
84
|
+
this.zipKit = zipKit;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Checks if the ZIP file contains NFT token metadata and verifies the merkle root
|
|
89
|
+
*/
|
|
90
|
+
async checkForTokenization(
|
|
91
|
+
zipEntries: ZipEntry[],
|
|
92
|
+
options: TokenVerificationOptions = {}
|
|
93
|
+
): Promise<TokenVerificationResult> {
|
|
94
|
+
// Look for the tokenized metadata file
|
|
95
|
+
const tokenEntry = zipEntries.find(entry => entry.filename === TOKENIZED_METADATA);
|
|
96
|
+
|
|
97
|
+
if (!tokenEntry) {
|
|
98
|
+
return {
|
|
99
|
+
isTokenized: false,
|
|
100
|
+
tokenMetadata: null,
|
|
101
|
+
calculatedMerkleRoot: null,
|
|
102
|
+
blockchainMerkleRoot: null,
|
|
103
|
+
verificationPassed: false
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
// Extract the token metadata
|
|
109
|
+
const tokenData = await this.zipKit.extractBlob(tokenEntry);
|
|
110
|
+
|
|
111
|
+
if (!tokenData) {
|
|
112
|
+
return {
|
|
113
|
+
isTokenized: false,
|
|
114
|
+
tokenMetadata: null,
|
|
115
|
+
calculatedMerkleRoot: null,
|
|
116
|
+
blockchainMerkleRoot: null,
|
|
117
|
+
verificationPassed: false,
|
|
118
|
+
errorMessage: 'Could not read token metadata'
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const tokenContent = await this.blobToString(tokenData);
|
|
123
|
+
const tokenMetadata = JSON.parse(tokenContent);
|
|
124
|
+
|
|
125
|
+
// Validate token metadata structure
|
|
126
|
+
if (!this.validateTokenInfo(tokenMetadata)) {
|
|
127
|
+
return {
|
|
128
|
+
isTokenized: false,
|
|
129
|
+
tokenMetadata: null,
|
|
130
|
+
calculatedMerkleRoot: null,
|
|
131
|
+
blockchainMerkleRoot: null,
|
|
132
|
+
verificationPassed: false,
|
|
133
|
+
errorMessage: 'Invalid token metadata structure'
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Skip verification if requested
|
|
138
|
+
if (options.skipHash || options.skipToken) {
|
|
139
|
+
return {
|
|
140
|
+
isTokenized: true,
|
|
141
|
+
tokenMetadata,
|
|
142
|
+
calculatedMerkleRoot: null,
|
|
143
|
+
blockchainMerkleRoot: null,
|
|
144
|
+
verificationPassed: false,
|
|
145
|
+
errorMessage: options.skipHash ? 'Hash verification skipped' : 'Token verification skipped'
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Calculate merkle root excluding token file
|
|
150
|
+
const calculatedMerkleRoot = await this.calculateMerkleRootExcludingToken(zipEntries);
|
|
151
|
+
|
|
152
|
+
if (!calculatedMerkleRoot) {
|
|
153
|
+
return {
|
|
154
|
+
isTokenized: true,
|
|
155
|
+
tokenMetadata,
|
|
156
|
+
calculatedMerkleRoot: null,
|
|
157
|
+
blockchainMerkleRoot: null,
|
|
158
|
+
verificationPassed: false,
|
|
159
|
+
errorMessage: 'Could not calculate merkle root'
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Query blockchain for the actual merkle root and timestamp
|
|
164
|
+
const blockchainData = await this.queryBlockchainData(tokenMetadata);
|
|
165
|
+
|
|
166
|
+
if (!blockchainData.merkleRoot) {
|
|
167
|
+
return {
|
|
168
|
+
isTokenized: true,
|
|
169
|
+
tokenMetadata,
|
|
170
|
+
calculatedMerkleRoot,
|
|
171
|
+
blockchainMerkleRoot: null,
|
|
172
|
+
blockchainTimestamp: blockchainData.timestamp ?? undefined,
|
|
173
|
+
verificationPassed: false,
|
|
174
|
+
errorMessage: 'Could not retrieve merkle root from blockchain'
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const verificationPassed = blockchainData.merkleRoot === calculatedMerkleRoot;
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
isTokenized: true,
|
|
182
|
+
tokenMetadata,
|
|
183
|
+
calculatedMerkleRoot,
|
|
184
|
+
blockchainMerkleRoot: blockchainData.merkleRoot,
|
|
185
|
+
blockchainTimestamp: blockchainData.timestamp ?? undefined,
|
|
186
|
+
verificationPassed,
|
|
187
|
+
errorMessage: verificationPassed ? undefined : 'Merkle root mismatch - archive may be tampered'
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
} catch (error) {
|
|
191
|
+
return {
|
|
192
|
+
isTokenized: false,
|
|
193
|
+
tokenMetadata: null,
|
|
194
|
+
calculatedMerkleRoot: null,
|
|
195
|
+
blockchainMerkleRoot: null,
|
|
196
|
+
verificationPassed: false,
|
|
197
|
+
errorMessage: `Error processing token: ${error instanceof Error ? error.message : String(error)}`
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Validates token metadata structure
|
|
204
|
+
*/
|
|
205
|
+
private validateTokenInfo(tokenMetadata: any): tokenMetadata is NZipTokenInfo {
|
|
206
|
+
return tokenMetadata &&
|
|
207
|
+
typeof tokenMetadata.version === 'string' &&
|
|
208
|
+
typeof tokenMetadata.tokenId === 'string' &&
|
|
209
|
+
typeof tokenMetadata.contractAddress === 'string' &&
|
|
210
|
+
typeof tokenMetadata.network === 'string' &&
|
|
211
|
+
typeof tokenMetadata.networkChainId === 'number' &&
|
|
212
|
+
typeof tokenMetadata.transactionHash === 'string' &&
|
|
213
|
+
typeof tokenMetadata.merkleRoot === 'string' &&
|
|
214
|
+
typeof tokenMetadata.fileName === 'string' &&
|
|
215
|
+
typeof tokenMetadata.mintedAt === 'string';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Calculates the merkle root excluding the token metadata file
|
|
220
|
+
*/
|
|
221
|
+
private async calculateMerkleRootExcludingToken(zipEntries: ZipEntry[]): Promise<string | null> {
|
|
222
|
+
try {
|
|
223
|
+
// Get all entries except the token file
|
|
224
|
+
const contentEntries = zipEntries.filter(entry => entry.filename !== TOKENIZED_METADATA);
|
|
225
|
+
|
|
226
|
+
if (contentEntries.length === 0) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// For each content entry, ensure we have SHA-256 hash calculated
|
|
231
|
+
for (const entry of contentEntries) {
|
|
232
|
+
if (!entry.sha256) {
|
|
233
|
+
// Extract the file to calculate its SHA-256 hash
|
|
234
|
+
await this.zipKit.extractBlob(entry);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Calculate merkle root from content files
|
|
239
|
+
// This would need to be implemented based on the ZipKit's merkle tree functionality
|
|
240
|
+
// For now, we'll use a placeholder that would need to be implemented
|
|
241
|
+
return await this.zipKit.getMerkleRoot?.() || null;
|
|
242
|
+
} catch (error) {
|
|
243
|
+
console.error('Error calculating merkle root:', error);
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Queries the blockchain to get the actual merkle root and timestamp stored in the smart contract
|
|
250
|
+
*/
|
|
251
|
+
private async queryBlockchainData(tokenMetadata: NZipTokenInfo): Promise<{merkleRoot: string | null, timestamp: number | null}> {
|
|
252
|
+
try {
|
|
253
|
+
// Dynamic import of ethers to avoid SSR issues
|
|
254
|
+
const { ethers } = await import('ethers');
|
|
255
|
+
|
|
256
|
+
const networkConfig = NETWORK_CONFIG[tokenMetadata.network];
|
|
257
|
+
if (!networkConfig) {
|
|
258
|
+
console.warn(`Unsupported network: ${tokenMetadata.network}`);
|
|
259
|
+
return { merkleRoot: null, timestamp: null };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Try multiple RPC endpoints
|
|
263
|
+
for (const rpcUrl of networkConfig.rpcUrls) {
|
|
264
|
+
try {
|
|
265
|
+
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
|
266
|
+
|
|
267
|
+
// Test connection with a timeout
|
|
268
|
+
const blockNumber = await Promise.race([
|
|
269
|
+
provider.getBlockNumber(),
|
|
270
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 5000))
|
|
271
|
+
]);
|
|
272
|
+
|
|
273
|
+
// Create contract instance
|
|
274
|
+
const contract = new ethers.Contract(tokenMetadata.contractAddress, CONTRACT_ABI, provider);
|
|
275
|
+
|
|
276
|
+
// Call getZipFileInfo to get the blockchain-stored data
|
|
277
|
+
const zipInfo = await contract.getZipFileInfo(tokenMetadata.tokenId);
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
merkleRoot: zipInfo.merkleRootHash,
|
|
281
|
+
timestamp: Number(zipInfo.tokenizationTime)
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
} catch (error) {
|
|
285
|
+
console.warn(`RPC ${rpcUrl} failed:`, error);
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
console.warn(`All RPC endpoints failed for network: ${tokenMetadata.network}`);
|
|
291
|
+
return { merkleRoot: null, timestamp: null };
|
|
292
|
+
|
|
293
|
+
} catch (error) {
|
|
294
|
+
console.error('Blockchain query error:', error);
|
|
295
|
+
return { merkleRoot: null, timestamp: null };
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Helper to convert Blob to string
|
|
301
|
+
*/
|
|
302
|
+
private async blobToString(blob: Blob): Promise<string> {
|
|
303
|
+
return new Promise((resolve, reject) => {
|
|
304
|
+
const reader = new FileReader();
|
|
305
|
+
reader.onload = () => resolve(reader.result as string);
|
|
306
|
+
reader.onerror = reject;
|
|
307
|
+
reader.readAsText(blob);
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Creates a formatted summary of token information
|
|
313
|
+
*/
|
|
314
|
+
createTokenSummary(tokenMetadata: NZipTokenInfo): string {
|
|
315
|
+
return `
|
|
316
|
+
🎫 NFT Token Information:
|
|
317
|
+
Token ID: ${tokenMetadata.tokenId}
|
|
318
|
+
Network: ${tokenMetadata.network}
|
|
319
|
+
Contract: ${tokenMetadata.contractAddress}
|
|
320
|
+
Transaction: ${tokenMetadata.transactionHash}
|
|
321
|
+
File: ${tokenMetadata.fileName}
|
|
322
|
+
Minted: ${new Date(tokenMetadata.mintedAt).toLocaleString()}
|
|
323
|
+
`;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Creates a new TokenVerifierBrowser instance
|
|
329
|
+
*/
|
|
330
|
+
export function createTokenVerifier(zipKit: ZipKitBrowser): TokenVerifierBrowser {
|
|
331
|
+
return new TokenVerifierBrowser(zipKit);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Convenience function to check for tokenization
|
|
336
|
+
*/
|
|
337
|
+
export async function checkForTokenization(
|
|
338
|
+
zipKit: ZipKitBrowser,
|
|
339
|
+
zipEntries: ZipEntry[],
|
|
340
|
+
options: TokenVerificationOptions = {}
|
|
341
|
+
): Promise<TokenVerificationResult> {
|
|
342
|
+
const verifier = new TokenVerifierBrowser(zipKit);
|
|
343
|
+
return verifier.checkForTokenization(zipEntries, options);
|
|
344
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
// ======================================
|
|
2
|
+
// ZipKitBrowser.ts
|
|
3
|
+
// Copyright (c) 2025 NeoWare, Inc. All rights reserved.
|
|
4
|
+
// ======================================
|
|
5
|
+
|
|
6
|
+
import ZipKit, { CompressOptions, CreateZipOptions } from '../shared/ZipKit';
|
|
7
|
+
import ZipEntry from '../shared/ZipEntry';
|
|
8
|
+
import Errors from '../shared/constants/Errors';
|
|
9
|
+
import * as Headers from '../shared/constants/Headers';
|
|
10
|
+
import { TOKENIZED_METADATA, TIMESTAMP_SUBMITTED, TIMESTAMP_METADATA } from '../shared/constants/Headers';
|
|
11
|
+
import { sha256 } from '../shared/components/ZipCrypto';
|
|
12
|
+
import { crc32 } from '../shared/components/Util';
|
|
13
|
+
import { DATATYPE, getTypeOf } from '../shared/components/Util';
|
|
14
|
+
import type { FileData } from '../shared/types';
|
|
15
|
+
|
|
16
|
+
// Re-export everything from ZipKit
|
|
17
|
+
export * from '../shared/ZipKit';
|
|
18
|
+
export { ZipEntry, Errors, Headers, sha256, getTypeOf };
|
|
19
|
+
|
|
20
|
+
// ======================================
|
|
21
|
+
// ZipKitBrowser
|
|
22
|
+
// ======================================
|
|
23
|
+
|
|
24
|
+
export default class ZipKitBrowser extends ZipKit {
|
|
25
|
+
constructor() {
|
|
26
|
+
super(); // Call the parent constructor
|
|
27
|
+
|
|
28
|
+
// console.log('ZipKit.support.isNode: ', ZipKit.support.isNode);
|
|
29
|
+
// console.log('ZipKit.support.buffer: ', ZipKit.support.buffer);
|
|
30
|
+
// console.log('ZipKit.support.blob: ', ZipKit.support.blob);
|
|
31
|
+
// console.log('ZipKit.support.streams: ', ZipKit.support.streams);
|
|
32
|
+
// console.log('ZipKit.support.fileReader: ', ZipKit.support.fileReader);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// --------------------------------------
|
|
36
|
+
// Load a ZIP file from a Blob and return the ZipEntry array
|
|
37
|
+
|
|
38
|
+
async loadZipBlob(data: Blob): Promise<ZipEntry[] | null> {
|
|
39
|
+
if (getTypeOf(data) !== DATATYPE.BLOB) {
|
|
40
|
+
throw new Error(Errors.DATATYPE_UNSUPPORTED);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
const _readEntries = (data: Buffer): ZipEntry[] | null => {
|
|
45
|
+
try {
|
|
46
|
+
super.loadZip(data);
|
|
47
|
+
return super.getDirectory();
|
|
48
|
+
} catch (error) {
|
|
49
|
+
reject(error);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const reader = new window.FileReader();
|
|
55
|
+
reader.readAsArrayBuffer(data);
|
|
56
|
+
|
|
57
|
+
reader.onload = function(this: FileReader, e: ProgressEvent<FileReader>) {
|
|
58
|
+
if (!this.result) {
|
|
59
|
+
reject(new Error('Failed to read file'));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const _inBuf = Buffer.from(this.result as ArrayBuffer);
|
|
63
|
+
resolve(_readEntries(_inBuf));
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
reader.onerror = function(this: FileReader, e: ProgressEvent<FileReader>) {
|
|
67
|
+
reject(this.error || new Error('Failed to read file'));
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// --------------------------------------
|
|
73
|
+
// Create a ZipEntry for a File object
|
|
74
|
+
|
|
75
|
+
createZipFileEntry(file: FileData): ZipEntry {
|
|
76
|
+
const entry = super.createZipEntry(file.name);
|
|
77
|
+
entry.fileData = file;
|
|
78
|
+
entry.uncompressedSize = file.size;
|
|
79
|
+
|
|
80
|
+
const fileDate = new Date(file.lastModified);
|
|
81
|
+
entry.timeDateDOS = entry.setDateTime(fileDate);
|
|
82
|
+
entry.cmpMethod = Headers.CMP_METHOD.DEFLATED;
|
|
83
|
+
|
|
84
|
+
return entry;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// --------------------------------------
|
|
88
|
+
// Calculate the SHA256 hash of a File object
|
|
89
|
+
|
|
90
|
+
async hashFile(file: FileData): Promise<string> {
|
|
91
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
92
|
+
const buffer = Buffer.from(arrayBuffer);
|
|
93
|
+
return sha256(buffer);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// --------------------------------------
|
|
97
|
+
// Compress the data and return the compressed data buffer
|
|
98
|
+
// Set the ZipEntry compressSize and CRC32 values
|
|
99
|
+
|
|
100
|
+
async compressDataBlob(entry: ZipEntry, data: Blob, options?: CompressOptions): Promise<Buffer> {
|
|
101
|
+
if (getTypeOf(data) !== DATATYPE.BLOB) {
|
|
102
|
+
throw new Error(Errors.DATATYPE_UNSUPPORTED);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return new Promise(async (resolve, reject) => {
|
|
106
|
+
const _compressData = async (entry: ZipEntry, data: Buffer, options?: CompressOptions): Promise<Buffer> => {
|
|
107
|
+
return await super.compressData(entry, data, options);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const reader = new window.FileReader();
|
|
111
|
+
reader.readAsArrayBuffer(data);
|
|
112
|
+
|
|
113
|
+
reader.onload = async function(this: FileReader, e: ProgressEvent<FileReader>) {
|
|
114
|
+
if (!this.result) {
|
|
115
|
+
reject(new Error('Failed to read file'));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
const _inBuf = Buffer.from(this.result as ArrayBuffer);
|
|
120
|
+
const result = await _compressData(entry, _inBuf, options);
|
|
121
|
+
resolve(result);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
reject(error);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
reader.onerror = function(this: FileReader, e: ProgressEvent<FileReader>) {
|
|
128
|
+
reject(this.error || new Error('Failed to read file'));
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// --------------------------------------
|
|
134
|
+
// Extract a Blob from a ZipEntry
|
|
135
|
+
|
|
136
|
+
async extractBlob(entry: ZipEntry, skipHashCheck?: boolean): Promise<Blob | null> {
|
|
137
|
+
try {
|
|
138
|
+
const _outBuf = await super.extract(entry, skipHashCheck);
|
|
139
|
+
if (!_outBuf) return null;
|
|
140
|
+
|
|
141
|
+
// Convert Buffer to Uint8Array for Blob compatibility
|
|
142
|
+
const uint8Array = new Uint8Array(_outBuf);
|
|
143
|
+
return new Blob([uint8Array], { type: 'application/octet-stream' });
|
|
144
|
+
} catch (error) {
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// --------------------------------------
|
|
150
|
+
// Create a ZIP file in memory and return a Blob
|
|
151
|
+
|
|
152
|
+
async createZipBlob(cmpOptions?: CompressOptions, options?: CreateZipOptions): Promise<Blob | null> {
|
|
153
|
+
const _onError = options?.onError || (() => {});
|
|
154
|
+
const _onEntryDone = options?.onEntryDone || (() => {});
|
|
155
|
+
|
|
156
|
+
const zipEntries = super.getDirectory();
|
|
157
|
+
if (!zipEntries) {
|
|
158
|
+
_onError(new Error(Errors.NO_FILES));
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let offset = 0;
|
|
163
|
+
const buffers: Buffer[] = [];
|
|
164
|
+
|
|
165
|
+
for (const entry of zipEntries) {
|
|
166
|
+
if (!entry.isUpdated) {
|
|
167
|
+
// CRITICAL FIX: Check for token files even in copy mode
|
|
168
|
+
if (entry.filename === TOKENIZED_METADATA || entry.filename === TIMESTAMP_SUBMITTED || entry.filename === TIMESTAMP_METADATA) {
|
|
169
|
+
// Force this entry to be processed with special handling instead of copied
|
|
170
|
+
entry.isUpdated = true;
|
|
171
|
+
// Don't continue here - let it fall through to special handling below
|
|
172
|
+
} else {
|
|
173
|
+
const inData = super.copyEntry(entry);
|
|
174
|
+
entry.localHdrOffset = offset;
|
|
175
|
+
offset += inData.length;
|
|
176
|
+
buffers.push(inData);
|
|
177
|
+
_onEntryDone(entry, 'Copied');
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (!entry.fileData) {
|
|
183
|
+
_onError(new Error(Errors.FILE_NOT_FOUND));
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const fileBuffer = await entry.fileData.arrayBuffer();
|
|
188
|
+
|
|
189
|
+
// Special handling for token metadata files - store uncompressed with CRC-32
|
|
190
|
+
if (entry.filename === TOKENIZED_METADATA || entry.filename === TIMESTAMP_SUBMITTED || entry.filename === TIMESTAMP_METADATA) {
|
|
191
|
+
// For token metadata, use STORED compression (no compression)
|
|
192
|
+
const buffer = Buffer.from(fileBuffer);
|
|
193
|
+
|
|
194
|
+
// Calculate CRC-32 for the token file (not SHA-256 to avoid Merkle Root interference)
|
|
195
|
+
entry.crc = crc32(buffer);
|
|
196
|
+
|
|
197
|
+
// Set up entry for STORED compression
|
|
198
|
+
entry.cmpMethod = 0; // STORED
|
|
199
|
+
entry.compressedSize = buffer.length;
|
|
200
|
+
entry.uncompressedSize = buffer.length;
|
|
201
|
+
|
|
202
|
+
// Do NOT set SHA-256 hash for token metadata to avoid Merkle Root calculation
|
|
203
|
+
// entry.sha256 should remain undefined for metadata files
|
|
204
|
+
|
|
205
|
+
const localHdr = entry.createLocalHdr();
|
|
206
|
+
entry.localHdrOffset = offset;
|
|
207
|
+
offset += localHdr.length + buffer.length;
|
|
208
|
+
|
|
209
|
+
buffers.push(localHdr);
|
|
210
|
+
buffers.push(buffer);
|
|
211
|
+
_onEntryDone(entry, 'Stored (metadata)');
|
|
212
|
+
} else {
|
|
213
|
+
// Normal file processing with compression
|
|
214
|
+
const cmpData = await super.compressData(entry, Buffer.from(fileBuffer), cmpOptions);
|
|
215
|
+
const localHdr = entry.createLocalHdr();
|
|
216
|
+
|
|
217
|
+
entry.localHdrOffset = offset;
|
|
218
|
+
offset += localHdr.length + cmpData.length;
|
|
219
|
+
|
|
220
|
+
buffers.push(localHdr);
|
|
221
|
+
buffers.push(cmpData);
|
|
222
|
+
_onEntryDone(entry, 'Written');
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const centralDirOffset = offset;
|
|
227
|
+
for (const entry of zipEntries) {
|
|
228
|
+
const centralDirEntry = entry.centralDirEntry();
|
|
229
|
+
offset += centralDirEntry.length;
|
|
230
|
+
buffers.push(centralDirEntry);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const centralDirSize = offset - centralDirOffset;
|
|
234
|
+
const ceBuf = super.centralEndHdr(centralDirSize, centralDirOffset);
|
|
235
|
+
buffers.push(ceBuf);
|
|
236
|
+
|
|
237
|
+
const combinedBuffer = Buffer.concat(buffers);
|
|
238
|
+
// Convert Buffer to Uint8Array for Blob compatibility
|
|
239
|
+
const uint8Array = new Uint8Array(combinedBuffer);
|
|
240
|
+
return new Blob([uint8Array], { type: 'application/zip' });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// A browser-safe version of ZipKitStamp
|
|
2
|
+
|
|
3
|
+
export const createTimestamp = async (hash: string, options: any = {}): Promise<any> => {
|
|
4
|
+
throw new Error('Blockchain timestamp creation is not supported in browsers');
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const verifyTimestamp = async (timestamp: any, options: any = {}): Promise<any> => {
|
|
8
|
+
throw new Error('Blockchain timestamp verification is not supported in browsers');
|
|
9
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// NeoZipKit - Browser-Only Package Exports
|
|
3
|
+
// =============================================================================
|
|
4
|
+
|
|
5
|
+
// Core shared exports
|
|
6
|
+
import ZipKit from './shared/ZipKit';
|
|
7
|
+
export { ZipEntry, Errors, CMP_METHOD, TIMESTAMP_METADATA, TIMESTAMP_SUBMITTED } from './shared/ZipKit';
|
|
8
|
+
export type { CompressOptions } from './shared/ZipKit';
|
|
9
|
+
export * from './shared/ZipKit';
|
|
10
|
+
export * from './shared/ZipEntry';
|
|
11
|
+
export default ZipKit;
|
|
12
|
+
|
|
13
|
+
// Browser-only exports
|
|
14
|
+
export { default as ZipKitBrowser } from './browser/ZipKitBrowser';
|
|
15
|
+
export { CertificateGeneratorBrowser } from './browser/CertificateGeneratorBrowser';
|
|
16
|
+
export { TokenVerificationResult, checkForTokenization as checkForTokenizationBrowser, NZipTokenInfo } from './browser/TokenVerifierBrowser';
|
|
17
|
+
export type { TokenVerificationOptions } from './browser/TokenVerifierBrowser';
|
|
18
|
+
|
|
19
|
+
// Shared component exports
|
|
20
|
+
export { default as HashAccumulator } from './shared/components/HashAccumulator';
|
|
21
|
+
export * from './shared/components/Util';
|
|
22
|
+
export * from './shared/components/Support';
|
|
23
|
+
|
|
24
|
+
// Type and constant exports
|
|
25
|
+
export * from './shared/types';
|
|
26
|
+
export * from './shared/constants/Headers';
|
|
27
|
+
export * from './shared/constants/Errors';
|
|
28
|
+
export * from './shared/version';
|
|
29
|
+
|
|
30
|
+
// Platform-specific conditional exports
|
|
31
|
+
export const PlatformUtils = {
|
|
32
|
+
// Browser detection
|
|
33
|
+
isBrowser: typeof window !== 'undefined' && typeof document !== 'undefined',
|
|
34
|
+
// Node.js detection
|
|
35
|
+
isNode: typeof process !== 'undefined' && process.versions && process.versions.node,
|
|
36
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// NeoZipKit - Unified Package Exports
|
|
3
|
+
// =============================================================================
|
|
4
|
+
|
|
5
|
+
// Core shared exports
|
|
6
|
+
import ZipKit from './shared/ZipKit';
|
|
7
|
+
export { ZipEntry, Errors, CMP_METHOD, TIMESTAMP_METADATA, TIMESTAMP_SUBMITTED } from './shared/ZipKit';
|
|
8
|
+
export type { CompressOptions } from './shared/ZipKit';
|
|
9
|
+
export * from './shared/ZipKit';
|
|
10
|
+
export * from './shared/ZipEntry';
|
|
11
|
+
export default ZipKit;
|
|
12
|
+
|
|
13
|
+
// Browser-only exports
|
|
14
|
+
export { default as ZipKitBrowser } from './browser/ZipKitBrowser';
|
|
15
|
+
export { CertificateGeneratorBrowser } from './browser/CertificateGeneratorBrowser';
|
|
16
|
+
export { TokenVerificationResult, checkForTokenization as checkForTokenizationBrowser, NZipTokenInfo } from './browser/TokenVerifierBrowser';
|
|
17
|
+
export type { TokenVerificationOptions } from './browser/TokenVerifierBrowser';
|
|
18
|
+
|
|
19
|
+
// Server-only exports (now included in main build)
|
|
20
|
+
export { default as ZipKitServer } from './server/platform/server/ZipKitServer';
|
|
21
|
+
export { verifyTimestamp, createTimestamp } from './server/platform/servercomponents/ZipKitOTS';
|
|
22
|
+
|
|
23
|
+
// Blockchain exports
|
|
24
|
+
export * from './server/blockchain';
|
|
25
|
+
export { checkForTokenization as checkForTokenizationServer } from './server/blockchain/TokenVerifier';
|
|
26
|
+
export { NFTMinter } from './server/blockchain/NFTMinter';
|
|
27
|
+
export { WalletAnalyzer } from './server/blockchain/WalletAnalyzer';
|
|
28
|
+
export { WalletManager } from './server/blockchain/WalletManager';
|
|
29
|
+
export { ZipTokenProcessor } from './server/blockchain/ZipTokenProcessor';
|
|
30
|
+
|
|
31
|
+
// Shared component exports
|
|
32
|
+
export { default as HashAccumulator } from './shared/components/HashAccumulator';
|
|
33
|
+
export * from './shared/components/Util';
|
|
34
|
+
export * from './shared/components/Support';
|
|
35
|
+
|
|
36
|
+
// Type and constant exports
|
|
37
|
+
export * from './shared/types';
|
|
38
|
+
export * from './shared/constants/Headers';
|
|
39
|
+
export * from './shared/constants/Errors';
|
|
40
|
+
export * from './shared/version';
|
|
41
|
+
|
|
42
|
+
// Platform-specific conditional exports
|
|
43
|
+
export const PlatformUtils = {
|
|
44
|
+
// Browser detection
|
|
45
|
+
isBrowser: typeof window !== 'undefined' && typeof document !== 'undefined',
|
|
46
|
+
// Node.js detection
|
|
47
|
+
isNode: typeof process !== 'undefined' && process.versions && process.versions.node,
|
|
48
|
+
};
|