neozip-cli 0.75.2-beta โ 0.90.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +78 -0
- package/DOCUMENTATION.md +20 -9
- package/README.md +55 -31
- package/dist/src/commands/mintTimestampProof.js +335 -0
- package/dist/src/commands/verifyEmail.js +146 -0
- package/dist/src/config/ConfigSetup.js +50 -20
- package/dist/src/config/ConfigStore.js +36 -3
- package/dist/src/index.js +1 -1
- package/dist/src/neolist.js +25 -11
- package/dist/src/neounzip.js +324 -66
- package/dist/src/neozip/blockchain.js +5 -5
- package/dist/src/neozip/createZip.js +211 -44
- package/dist/src/neozip/upgradeZip.js +182 -0
- package/dist/src/neozip.js +160 -24
- package/env.example +10 -0
- package/package.json +97 -82
- package/dist/neozipkit-bundles/blockchain.js +0 -13725
- package/dist/neozipkit-bundles/browser.js +0 -6186
- package/dist/neozipkit-bundles/core.js +0 -3839
- package/dist/neozipkit-bundles/node.js +0 -17730
- package/dist/neozipkit-wrappers/blockchain/core/contracts.js +0 -16
- package/dist/neozipkit-wrappers/blockchain/index.js +0 -2
- package/dist/neozipkit-wrappers/core/ZipDecompress.js +0 -2
- package/dist/neozipkit-wrappers/core/components/HashCalculator.js +0 -2
- package/dist/neozipkit-wrappers/core/components/Logger.js +0 -2
- package/dist/neozipkit-wrappers/core/constants/Errors.js +0 -2
- package/dist/neozipkit-wrappers/core/constants/Headers.js +0 -2
- package/dist/neozipkit-wrappers/core/encryption/ZipCrypto.js +0 -7
- package/dist/neozipkit-wrappers/core/index.js +0 -3
- package/dist/neozipkit-wrappers/index.js +0 -13
- package/dist/neozipkit-wrappers/node/index.js +0 -2
|
@@ -46,11 +46,14 @@ const fs = __importStar(require("fs"));
|
|
|
46
46
|
const path = __importStar(require("path"));
|
|
47
47
|
const neozipkit_1 = require("neozipkit");
|
|
48
48
|
const node_1 = __importDefault(require("neozipkit/node"));
|
|
49
|
-
const
|
|
49
|
+
const neozip_blockchain_1 = require("neozip-blockchain");
|
|
50
|
+
const ots_1 = require("neozip-blockchain/ots");
|
|
51
|
+
const zipstamp_server_1 = require("neozip-blockchain/zipstamp-server");
|
|
50
52
|
const CommentManager_1 = require("./CommentManager");
|
|
51
53
|
const utils_1 = require("./utils");
|
|
52
54
|
const file_operations_1 = require("./file-operations");
|
|
53
|
-
const
|
|
55
|
+
const blockchain_1 = require("./blockchain");
|
|
56
|
+
const ConfigStore_1 = require("../config/ConfigStore");
|
|
54
57
|
const user_interaction_1 = require("./user-interaction");
|
|
55
58
|
const exit_codes_1 = require("../exit-codes");
|
|
56
59
|
const version_1 = require("../version");
|
|
@@ -254,7 +257,7 @@ class ZipCreator {
|
|
|
254
257
|
this.centralDirOffset = 0;
|
|
255
258
|
this.zip = new node_1.default(); // Use ZipkitNode for ZIP operations
|
|
256
259
|
// Initialize HashAccumulator if blockchain features are enabled
|
|
257
|
-
if (this.options.blockchain || this.options.blockchainOts) {
|
|
260
|
+
if (this.options.blockchain || this.options.blockchainOts || this.options.blockchainZipstamp) {
|
|
258
261
|
this.hashAccumulator = new neozipkit_1.HashCalculator({ enableAccumulation: true });
|
|
259
262
|
}
|
|
260
263
|
}
|
|
@@ -284,7 +287,7 @@ class ZipCreator {
|
|
|
284
287
|
*/
|
|
285
288
|
async writeChunk(data) {
|
|
286
289
|
// For in-memory non-blockchain mode, write to buffer
|
|
287
|
-
if (this.options.inMemory && !this.options.blockchain && !this.options.blockchainOts) {
|
|
290
|
+
if (this.options.inMemory && !this.options.blockchain && !this.options.blockchainOts && !this.options.blockchainZipstamp) {
|
|
288
291
|
const bufferWriter = this.zip.bufferWriter;
|
|
289
292
|
if (bufferWriter) {
|
|
290
293
|
// Determine if this is a header or data by checking signature
|
|
@@ -333,7 +336,7 @@ class ZipCreator {
|
|
|
333
336
|
// Initialize output stream for file-based mode OR in-memory blockchain mode
|
|
334
337
|
// For in-memory blockchain mode, we need to write directly to file during compression
|
|
335
338
|
// For in-memory non-blockchain mode, output is written directly during compression
|
|
336
|
-
if (!this.options.inMemory || (this.options.blockchain || this.options.blockchainOts)) {
|
|
339
|
+
if (!this.options.inMemory || (this.options.blockchain || this.options.blockchainOts || this.options.blockchainZipstamp)) {
|
|
337
340
|
await this.initializeOutput();
|
|
338
341
|
}
|
|
339
342
|
await this.processFiles();
|
|
@@ -401,9 +404,12 @@ class ZipCreator {
|
|
|
401
404
|
const compressionDesc = this.options.level === 0 ? 'store (no compression)' : `${this.options.compression} (level ${this.options.level})`;
|
|
402
405
|
(0, utils_1.log)(`๐ง Compression: ${compressionDesc}`, this.options);
|
|
403
406
|
(0, utils_1.log)(`๐ Block size: ${(0, utils_1.formatBytes)(this.options.blockSize || 64 * 1024)}`, this.options);
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
+
if (!this.options.blockchainZipstamp && !this.options.blockchainOts) {
|
|
408
|
+
(0, utils_1.log)(`๐ Tokenization: ${this.options.blockchain ? 'enabled' : 'disabled'}`, this.options);
|
|
409
|
+
}
|
|
410
|
+
const encryptionMethod = this.options.encryptionMethod || 'aes256';
|
|
411
|
+
const encryptionLabel = encryptionMethod === 'aes256' ? 'AES-256' : 'PKZIP';
|
|
412
|
+
(0, utils_1.log)(`๐ Encryption: ${this.options.encrypt ? `${encryptionLabel} enabled` : 'disabled'}`, this.options);
|
|
407
413
|
(0, utils_1.log)('', this.options);
|
|
408
414
|
if (this.options.debug) {
|
|
409
415
|
(0, utils_1.logDebug)('Debug mode enabled', this.options);
|
|
@@ -433,6 +439,10 @@ class ZipCreator {
|
|
|
433
439
|
(0, utils_1.log)(`Blockchain: enabled (OpenTimestamp)`, this.options);
|
|
434
440
|
(0, utils_1.log)(`Bitcoin blockchain: enabled`, this.options);
|
|
435
441
|
}
|
|
442
|
+
else if (this.options.blockchainZipstamp) {
|
|
443
|
+
(0, utils_1.log)(`Blockchain: enabled (Zipstamp)`, this.options);
|
|
444
|
+
(0, utils_1.log)(`Zipstamp: enabled`, this.options);
|
|
445
|
+
}
|
|
436
446
|
else {
|
|
437
447
|
(0, utils_1.log)(`Blockchain: disabled`, this.options);
|
|
438
448
|
(0, utils_1.log)(`Tokenization: disabled`, this.options);
|
|
@@ -811,9 +821,9 @@ class ZipCreator {
|
|
|
811
821
|
const compressionOptions = {
|
|
812
822
|
level: level,
|
|
813
823
|
useZstd: level === 0 ? false : (this.options.compression || 'zstd') === 'zstd',
|
|
814
|
-
useSHA256: this.options.blockchain || this.options.blockchainOts || false,
|
|
824
|
+
useSHA256: this.options.blockchain || this.options.blockchainOts || this.options.blockchainZipstamp || false,
|
|
815
825
|
password: this.options.encrypt ? this.options.password : null,
|
|
816
|
-
encryptionMethod: this.options.encryptionMethod
|
|
826
|
+
encryptionMethod: (this.options.encryptionMethod === 'pkzip' ? 'zipcrypto' : 'aes256')
|
|
817
827
|
};
|
|
818
828
|
// Get entries from zip.getDirectory() (populated by processFiles())
|
|
819
829
|
// This is the single source of truth for entry order
|
|
@@ -829,7 +839,7 @@ class ZipCreator {
|
|
|
829
839
|
// Create ZipkitNode instance for buffer-based compression
|
|
830
840
|
const zipkit = new node_1.default();
|
|
831
841
|
// Determine if we need to write to file (for blockchain mode)
|
|
832
|
-
const writeToFile = this.options.blockchain || this.options.blockchainOts;
|
|
842
|
+
const writeToFile = this.options.blockchain || this.options.blockchainOts || this.options.blockchainZipstamp;
|
|
833
843
|
// Create appropriate output writer
|
|
834
844
|
let writer;
|
|
835
845
|
const positionRef = { current: this.currentPosition };
|
|
@@ -911,9 +921,11 @@ class ZipCreator {
|
|
|
911
921
|
if (this.hashAccumulator && entry.sha256) {
|
|
912
922
|
const filename = entry.filename || '';
|
|
913
923
|
// Skip blockchain metadata files
|
|
914
|
-
if (filename !==
|
|
915
|
-
filename !==
|
|
916
|
-
filename !==
|
|
924
|
+
if (filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
|
|
925
|
+
filename !== ots_1.TIMESTAMP_METADATA &&
|
|
926
|
+
filename !== ots_1.TIMESTAMP_SUBMITTED &&
|
|
927
|
+
filename !== zipstamp_server_1.SUBMIT_METADATA &&
|
|
928
|
+
filename !== zipstamp_server_1.TIMESTAMP_METADATA) {
|
|
917
929
|
const hashBuffer = Buffer.from(entry.sha256, 'hex');
|
|
918
930
|
this.hashAccumulator.addHash(hashBuffer);
|
|
919
931
|
if (this.options.verbose) {
|
|
@@ -978,9 +990,11 @@ class ZipCreator {
|
|
|
978
990
|
for (let index = 0; index < entryCnt; index++) {
|
|
979
991
|
const entry = zipEntries[index];
|
|
980
992
|
// Filter out metadata entries (handled separately in handleTokenization)
|
|
981
|
-
if (entry.filename ===
|
|
982
|
-
entry.filename ===
|
|
983
|
-
entry.filename ===
|
|
993
|
+
if (entry.filename === neozip_blockchain_1.TOKENIZED_METADATA ||
|
|
994
|
+
entry.filename === ots_1.TIMESTAMP_METADATA ||
|
|
995
|
+
entry.filename === ots_1.TIMESTAMP_SUBMITTED ||
|
|
996
|
+
entry.filename === zipstamp_server_1.SUBMIT_METADATA ||
|
|
997
|
+
entry.filename === zipstamp_server_1.TIMESTAMP_METADATA) {
|
|
984
998
|
zipEntries.splice(index, 1);
|
|
985
999
|
entryCnt--;
|
|
986
1000
|
index--;
|
|
@@ -1059,9 +1073,11 @@ class ZipCreator {
|
|
|
1059
1073
|
if (this.hashAccumulator) {
|
|
1060
1074
|
const filename = entry.filename || '';
|
|
1061
1075
|
// Skip blockchain metadata files
|
|
1062
|
-
if (filename !==
|
|
1063
|
-
filename !==
|
|
1064
|
-
filename !==
|
|
1076
|
+
if (filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
|
|
1077
|
+
filename !== ots_1.TIMESTAMP_METADATA &&
|
|
1078
|
+
filename !== ots_1.TIMESTAMP_SUBMITTED &&
|
|
1079
|
+
filename !== zipstamp_server_1.SUBMIT_METADATA &&
|
|
1080
|
+
filename !== zipstamp_server_1.TIMESTAMP_METADATA) {
|
|
1065
1081
|
this.hashAccumulator.addHash(hash);
|
|
1066
1082
|
if (this.options.verbose) {
|
|
1067
1083
|
(0, utils_1.logDebug)(`Added SHA-256 hash to accumulator for ${filename}`, this.options);
|
|
@@ -1139,9 +1155,11 @@ class ZipCreator {
|
|
|
1139
1155
|
// Filter out blockchain metadata files to ensure consistent Merkle Root calculation
|
|
1140
1156
|
const contentEntries = entries.filter(entry => {
|
|
1141
1157
|
const filename = entry.filename || '';
|
|
1142
|
-
return filename !==
|
|
1143
|
-
filename !==
|
|
1144
|
-
filename !==
|
|
1158
|
+
return filename !== neozip_blockchain_1.TOKENIZED_METADATA &&
|
|
1159
|
+
filename !== ots_1.TIMESTAMP_METADATA &&
|
|
1160
|
+
filename !== ots_1.TIMESTAMP_SUBMITTED &&
|
|
1161
|
+
filename !== zipstamp_server_1.SUBMIT_METADATA &&
|
|
1162
|
+
filename !== zipstamp_server_1.TIMESTAMP_METADATA;
|
|
1145
1163
|
});
|
|
1146
1164
|
// Add SHA-256 hashes to accumulator
|
|
1147
1165
|
for (const entry of contentEntries) {
|
|
@@ -1158,12 +1176,13 @@ class ZipCreator {
|
|
|
1158
1176
|
let tokenMeta = null;
|
|
1159
1177
|
let tokenInfoEntry = null;
|
|
1160
1178
|
let otsMetaEntry = null;
|
|
1179
|
+
let zipstampMetaEntry = null;
|
|
1161
1180
|
let merkleRoot = null;
|
|
1162
1181
|
if (this.options.blockchain) {
|
|
1163
1182
|
// Check for wallet passkey AFTER compression is complete
|
|
1164
1183
|
// Priority: -w option > wallet.json > environment variable
|
|
1165
1184
|
// Don't show error yet - we'll prompt user if key is missing
|
|
1166
|
-
const walletPasskey = (0,
|
|
1185
|
+
const walletPasskey = (0, blockchain_1.getWalletPasskey)(this.options.walletKey, false);
|
|
1167
1186
|
if (!walletPasskey) {
|
|
1168
1187
|
// Show error message and prompt user
|
|
1169
1188
|
console.error('\nโ Error: Wallet private key is required for blockchain operations');
|
|
@@ -1186,6 +1205,7 @@ class ZipCreator {
|
|
|
1186
1205
|
(0, utils_1.log)('โ ๏ธ Continuing without blockchain tokenization (ZIP file is already created)...', this.options);
|
|
1187
1206
|
this.options.blockchain = false;
|
|
1188
1207
|
this.options.blockchainOts = false;
|
|
1208
|
+
this.options.blockchainZipstamp = false;
|
|
1189
1209
|
return; // Skip tokenization, ZIP is already complete
|
|
1190
1210
|
}
|
|
1191
1211
|
else {
|
|
@@ -1276,7 +1296,7 @@ class ZipCreator {
|
|
|
1276
1296
|
// Always display network when tokenization is enabled
|
|
1277
1297
|
console.log(`๐ Network: ${network}`);
|
|
1278
1298
|
// Get network config for contract address and version info
|
|
1279
|
-
const networkConfig = (0,
|
|
1299
|
+
const networkConfig = (0, neozip_blockchain_1.getNetworkByName)(network);
|
|
1280
1300
|
let contractAddress;
|
|
1281
1301
|
let networkChainId;
|
|
1282
1302
|
let contractVersion;
|
|
@@ -1290,7 +1310,7 @@ class ZipCreator {
|
|
|
1290
1310
|
}
|
|
1291
1311
|
else {
|
|
1292
1312
|
// Fallback to default (Base Sepolia)
|
|
1293
|
-
const defaultConfig = (0,
|
|
1313
|
+
const defaultConfig = (0, neozip_blockchain_1.getContractConfig)(84532);
|
|
1294
1314
|
contractAddress = defaultConfig.address;
|
|
1295
1315
|
networkChainId = defaultConfig.chainId;
|
|
1296
1316
|
const versionStr = defaultConfig.version || '0';
|
|
@@ -1300,8 +1320,8 @@ class ZipCreator {
|
|
|
1300
1320
|
// Display contract address and version right after network
|
|
1301
1321
|
console.log(`๐ Contract: ${contractAddress}`);
|
|
1302
1322
|
console.log(`๐ข Version: ${contractVersion}`);
|
|
1303
|
-
if (!(0,
|
|
1304
|
-
const supportedNetworks = (0,
|
|
1323
|
+
if (!(0, blockchain_1.isSupportedNetwork)(network)) {
|
|
1324
|
+
const supportedNetworks = (0, neozip_blockchain_1.getSupportedNetworkNames)();
|
|
1305
1325
|
console.error(`โ Error: Unsupported network: ${network}`);
|
|
1306
1326
|
console.error(` Currently supported networks: ${supportedNetworks.join(', ')}`);
|
|
1307
1327
|
(0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.BLOCKCHAIN_CONFIG_ERROR);
|
|
@@ -1309,7 +1329,7 @@ class ZipCreator {
|
|
|
1309
1329
|
let minter = null;
|
|
1310
1330
|
try {
|
|
1311
1331
|
// Initialize blockchain minter
|
|
1312
|
-
minter = new
|
|
1332
|
+
minter = new neozip_blockchain_1.ZipkitMinter(merkleRoot, {
|
|
1313
1333
|
network: network,
|
|
1314
1334
|
walletPrivateKey: this.options.walletPasskey,
|
|
1315
1335
|
verbose: this.options.verbose,
|
|
@@ -1380,7 +1400,7 @@ class ZipCreator {
|
|
|
1380
1400
|
}
|
|
1381
1401
|
else if (userChoice.action === 'mint-new') {
|
|
1382
1402
|
// Mint new token
|
|
1383
|
-
const mintResult = await (0,
|
|
1403
|
+
const mintResult = await (0, blockchain_1.handleTokenMinting)(minter, this.zip, this.options.nonInteractive || false, 0);
|
|
1384
1404
|
if (mintResult.success && mintResult.tokenInfo) {
|
|
1385
1405
|
tokenMeta = mintResult.tokenInfo;
|
|
1386
1406
|
// Add token metadata directly to ZIP (after compression, at the end)
|
|
@@ -1417,7 +1437,7 @@ class ZipCreator {
|
|
|
1417
1437
|
}
|
|
1418
1438
|
else {
|
|
1419
1439
|
// No existing tokens, mint new one
|
|
1420
|
-
const mintResult = await (0,
|
|
1440
|
+
const mintResult = await (0, blockchain_1.handleTokenMinting)(minter, this.zip, this.options.nonInteractive || false, 0);
|
|
1421
1441
|
if (mintResult.success && mintResult.tokenInfo) {
|
|
1422
1442
|
tokenMeta = mintResult.tokenInfo;
|
|
1423
1443
|
// Add token metadata directly to ZIP (after compression, at the end)
|
|
@@ -1537,16 +1557,16 @@ class ZipCreator {
|
|
|
1537
1557
|
console.log(`๐ Merkle Root: ${merkleRoot}`);
|
|
1538
1558
|
// Create actual OpenTimestamp proof
|
|
1539
1559
|
try {
|
|
1540
|
-
const otsProof = await (0,
|
|
1560
|
+
const otsProof = await (0, ots_1.createTimestamp)(merkleRoot, { debug: this.options.verbose });
|
|
1541
1561
|
if (!otsProof) {
|
|
1542
1562
|
throw new Error('Failed to create OpenTimestamp proof');
|
|
1543
1563
|
}
|
|
1544
1564
|
console.log('๐ OpenTimestamp metadata prepared');
|
|
1545
1565
|
// Use the createOtsMetadataEntry function like in the example
|
|
1546
|
-
const metaEntry = (0,
|
|
1566
|
+
const metaEntry = (0, ots_1.createOtsMetadataEntry)(this.zip, otsProof);
|
|
1547
1567
|
if (metaEntry) {
|
|
1548
1568
|
// Ensure the filename is set correctly
|
|
1549
|
-
metaEntry.filename =
|
|
1569
|
+
metaEntry.filename = ots_1.TIMESTAMP_SUBMITTED;
|
|
1550
1570
|
// Ensure OTS entry uses STORED compression (no compression for small metadata file)
|
|
1551
1571
|
metaEntry.cmpMethod = 0; // STORED
|
|
1552
1572
|
metaEntry.compressedSize = metaEntry.fileBuffer?.length || metaEntry.uncompressedSize || 0;
|
|
@@ -1586,10 +1606,143 @@ class ZipCreator {
|
|
|
1586
1606
|
}
|
|
1587
1607
|
}
|
|
1588
1608
|
}
|
|
1609
|
+
else if (this.options.blockchainZipstamp) {
|
|
1610
|
+
// Handle Zipstamp timestamp for blockchain
|
|
1611
|
+
if (this.options.debug) {
|
|
1612
|
+
(0, utils_1.logDebug)('Zipstamp process starting...', this.options);
|
|
1613
|
+
}
|
|
1614
|
+
console.log('๐ Zipstamp: Creating timestamp proof on blockchain...');
|
|
1615
|
+
// Calculate merkle root (reuse same logic as OTS)
|
|
1616
|
+
let merkleRoot = null;
|
|
1617
|
+
if (this.hashAccumulator && this.hashAccumulator.leafCount() > 0) {
|
|
1618
|
+
merkleRoot = this.hashAccumulator.merkleRoot();
|
|
1619
|
+
if (this.options.verbose) {
|
|
1620
|
+
console.log(`โ
Calculated merkle root from ${this.hashAccumulator.leafCount()} accumulated SHA-256 hashes`);
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
else {
|
|
1624
|
+
let entriesForMerkle = [];
|
|
1625
|
+
const zipEntries = this.zip.getDirectory();
|
|
1626
|
+
if (zipEntries.length > 0) {
|
|
1627
|
+
entriesForMerkle = zipEntries;
|
|
1628
|
+
}
|
|
1629
|
+
else {
|
|
1630
|
+
try {
|
|
1631
|
+
if (fs.existsSync(this.archiveName)) {
|
|
1632
|
+
const zipkit = new node_1.default();
|
|
1633
|
+
if (this.options.inMemory) {
|
|
1634
|
+
const zipBuffer = fs.readFileSync(this.archiveName);
|
|
1635
|
+
zipkit.loadZip(zipBuffer);
|
|
1636
|
+
}
|
|
1637
|
+
else {
|
|
1638
|
+
await zipkit.loadZipFile(this.archiveName);
|
|
1639
|
+
}
|
|
1640
|
+
entriesForMerkle = zipkit.getDirectory() || [];
|
|
1641
|
+
}
|
|
1642
|
+
else {
|
|
1643
|
+
entriesForMerkle = await this.zip.getDirectory() || [];
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
catch (error) {
|
|
1647
|
+
console.error('โ Error loading ZIP file for merkle root calculation:', error instanceof Error ? error.message : String(error));
|
|
1648
|
+
entriesForMerkle = await this.zip.getDirectory() || [];
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
merkleRoot = this.calculateMerkleRootFromEntries(entriesForMerkle);
|
|
1652
|
+
}
|
|
1653
|
+
if (!merkleRoot) {
|
|
1654
|
+
console.error('โ Error calculating merkle root for Zipstamp');
|
|
1655
|
+
console.error(' Make sure files have been compressed with SHA-256 hashes.');
|
|
1656
|
+
if (this.options.verbose) {
|
|
1657
|
+
const hashCount = this.hashAccumulator ? this.hashAccumulator.leafCount() : 0;
|
|
1658
|
+
console.error(` Found ${hashCount} accumulated SHA-256 hashes`);
|
|
1659
|
+
}
|
|
1660
|
+
(0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.BAD_ARCHIVE_FORMAT);
|
|
1661
|
+
}
|
|
1662
|
+
if (this.options.verbose && this.hashAccumulator) {
|
|
1663
|
+
console.log(`โ
Calculated merkle root from ${this.hashAccumulator.leafCount()} accumulated SHA-256 hashes`);
|
|
1664
|
+
}
|
|
1665
|
+
if (merkleRoot) {
|
|
1666
|
+
console.log(`๐ Merkle Root: ${merkleRoot}`);
|
|
1667
|
+
const recipientEmail = this.options.timestampEmail ||
|
|
1668
|
+
process.env.NEOZIP_TIMESTAMP_EMAIL ||
|
|
1669
|
+
ConfigStore_1.ConfigStore.getConfig().timestampEmail;
|
|
1670
|
+
console.log('๐ Submitting hash digest to ZipStamp server...');
|
|
1671
|
+
console.log(` Zipstamp Server: ${(0, zipstamp_server_1.getZipStampServerUrl)()}`);
|
|
1672
|
+
console.log(`๐ง Email: ${recipientEmail || '(none set)'}`);
|
|
1673
|
+
try {
|
|
1674
|
+
const zipstampMetadata = await (0, zipstamp_server_1.createTimestamp)(merkleRoot, {
|
|
1675
|
+
recipientEmail: recipientEmail || undefined,
|
|
1676
|
+
debug: this.options.verbose
|
|
1677
|
+
});
|
|
1678
|
+
if (!zipstampMetadata) {
|
|
1679
|
+
throw new Error('Failed to create Zipstamp timestamp');
|
|
1680
|
+
}
|
|
1681
|
+
console.log('๐ Zipstamp metadata prepared');
|
|
1682
|
+
const metaEntry = (0, zipstamp_server_1.createTimestampMetadataEntry)(this.zip, zipstampMetadata);
|
|
1683
|
+
if (metaEntry) {
|
|
1684
|
+
// createTimestampMetadataEntry sets filename based on status (TS-SUBMIT.NZIP or TIMESTAMP.NZIP)
|
|
1685
|
+
metaEntry.cmpMethod = 0; // STORED
|
|
1686
|
+
metaEntry.compressedSize = metaEntry.fileBuffer?.length || metaEntry.uncompressedSize || 0;
|
|
1687
|
+
if (this.options.verbose) {
|
|
1688
|
+
console.log(`๐ Final timestamp entry: ${metaEntry.filename}`);
|
|
1689
|
+
console.log(`๐ Final timestamp entry size: ${metaEntry.fileBuffer?.length || metaEntry.uncompressedSize || 0} bytes`);
|
|
1690
|
+
}
|
|
1691
|
+
console.log('โ
Zipstamp proof created and queued for inclusion');
|
|
1692
|
+
const isConfirmed = (metaEntry.filename || '').includes('TIMESTAMP') && !(metaEntry.filename || '').includes('TS-SUBMIT');
|
|
1693
|
+
console.log(isConfirmed
|
|
1694
|
+
? ' Timestamp proof is recorded on the blockchain.'
|
|
1695
|
+
: ' Timestamp proof will be recorded on the blockchain once confirmed.');
|
|
1696
|
+
metaEntry.localHdrOffset = this.currentPosition;
|
|
1697
|
+
if (this.zipWriter) {
|
|
1698
|
+
this.zipWriter.entryPositions.set(metaEntry.filename, this.currentPosition);
|
|
1699
|
+
}
|
|
1700
|
+
const localHdr = metaEntry.createLocalHdr();
|
|
1701
|
+
if (this.options.verbose) {
|
|
1702
|
+
(0, utils_1.log)(`๐ Writing Zipstamp entry: ${metaEntry.filename} at offset ${this.currentPosition}`, this.options);
|
|
1703
|
+
(0, utils_1.log)(`๐ Zipstamp entry size: ${metaEntry.fileBuffer?.length || 0} bytes`, this.options);
|
|
1704
|
+
}
|
|
1705
|
+
await this.writeChunk(localHdr);
|
|
1706
|
+
if (metaEntry.fileBuffer) {
|
|
1707
|
+
await this.writeChunk(metaEntry.fileBuffer);
|
|
1708
|
+
}
|
|
1709
|
+
zipstampMetaEntry = metaEntry;
|
|
1710
|
+
}
|
|
1711
|
+
else {
|
|
1712
|
+
console.warn('โ ๏ธ Warning: Failed to create Zipstamp metadata entry');
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
catch (error) {
|
|
1716
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1717
|
+
console.error('โ Failed to create Zipstamp proof:', errorMessage);
|
|
1718
|
+
const isValidationOrEmailError = errorMessage.toLowerCase().includes('validation') ||
|
|
1719
|
+
errorMessage.toLowerCase().includes('email') ||
|
|
1720
|
+
errorMessage.toLowerCase().includes('verify');
|
|
1721
|
+
if (isValidationOrEmailError) {
|
|
1722
|
+
console.error('');
|
|
1723
|
+
if (recipientEmail) {
|
|
1724
|
+
console.error(` Email used: ${recipientEmail}`);
|
|
1725
|
+
console.error('');
|
|
1726
|
+
}
|
|
1727
|
+
else {
|
|
1728
|
+
console.error(' No email was set (use --timestamp-email, NEOZIP_TIMESTAMP_EMAIL, or neozip verify-email).');
|
|
1729
|
+
console.error('');
|
|
1730
|
+
}
|
|
1731
|
+
console.error('๐ก Zipstamp requires a verified email. Run:');
|
|
1732
|
+
console.error(' neozip verify-email');
|
|
1733
|
+
console.error('');
|
|
1734
|
+
console.error(' Or use: --timestamp-email <your-verified-email>');
|
|
1735
|
+
console.error(' Or set: NEOZIP_TIMESTAMP_EMAIL environment variable');
|
|
1736
|
+
console.error('');
|
|
1737
|
+
}
|
|
1738
|
+
throw error;
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1589
1742
|
}
|
|
1590
1743
|
async finalize() {
|
|
1591
1744
|
// Check if this is in-memory non-blockchain mode (needs special handling)
|
|
1592
|
-
if (this.options.inMemory && !this.options.blockchain && !this.options.blockchainOts) {
|
|
1745
|
+
if (this.options.inMemory && !this.options.blockchain && !this.options.blockchainOts && !this.options.blockchainZipstamp) {
|
|
1593
1746
|
// For in-memory non-blockchain mode, write complete ZIP file from buffer
|
|
1594
1747
|
// Metadata should already be in the buffer (added via writeChunk() in handleTokenization())
|
|
1595
1748
|
const bufferWriter = this.zip.bufferWriter;
|
|
@@ -1752,6 +1905,9 @@ async function testArchiveIntegrity(archiveName, options) {
|
|
|
1752
1905
|
try {
|
|
1753
1906
|
// Always show when integrity testing starts
|
|
1754
1907
|
(0, utils_1.log)(`\n๐งช Testing archive integrity: ${archiveName}`, options);
|
|
1908
|
+
if (options.blockchainZipstamp) {
|
|
1909
|
+
(0, utils_1.log)(` (Validating file integrity only; timestamp not yet confirmed on blockchain)`, options);
|
|
1910
|
+
}
|
|
1755
1911
|
// Import child_process for shelling out
|
|
1756
1912
|
const { spawn } = require('child_process');
|
|
1757
1913
|
// Use the installed neounzip command directly (works in both dev and installed)
|
|
@@ -1760,6 +1916,11 @@ async function testArchiveIntegrity(archiveName, options) {
|
|
|
1760
1916
|
return new Promise((resolve) => {
|
|
1761
1917
|
// Build neounzip command arguments
|
|
1762
1918
|
const args = ['-t'];
|
|
1919
|
+
// For Zipstamp timestamped files: skip blockchain verification during integrity test.
|
|
1920
|
+
// We only validate file integrity (SHA-256); timestamp is pending and not yet minted.
|
|
1921
|
+
if (options.blockchainZipstamp) {
|
|
1922
|
+
args.push('--skip-blockchain');
|
|
1923
|
+
}
|
|
1763
1924
|
// Add password if encryption is enabled
|
|
1764
1925
|
if (options.encrypt && options.password) {
|
|
1765
1926
|
args.push('-P', options.password);
|
|
@@ -1806,6 +1967,10 @@ async function testArchiveIntegrity(archiveName, options) {
|
|
|
1806
1967
|
else {
|
|
1807
1968
|
(0, utils_1.log)(`โ
Archive integrity test passed`, options);
|
|
1808
1969
|
}
|
|
1970
|
+
// For Zipstamp: clarify that timestamp is not validated and not minted
|
|
1971
|
+
if (options.blockchainZipstamp) {
|
|
1972
|
+
(0, utils_1.log)(` Note: File integrity validated. Timestamp is pending (not yet confirmed on blockchain).`, options);
|
|
1973
|
+
}
|
|
1809
1974
|
resolve(true);
|
|
1810
1975
|
}
|
|
1811
1976
|
else {
|
|
@@ -1910,7 +2075,7 @@ async function upgradeZipForTokenization(inputZipPath, options) {
|
|
|
1910
2075
|
}
|
|
1911
2076
|
(0, utils_1.log)(` Found ${entries.length} entries in ZIP`, options);
|
|
1912
2077
|
// Check for existing token metadata (ignore per requirement 3a)
|
|
1913
|
-
const hasTokenMetadata = entries.some((e) => e.filename ===
|
|
2078
|
+
const hasTokenMetadata = entries.some((e) => e.filename === neozip_blockchain_1.TOKENIZED_METADATA);
|
|
1914
2079
|
if (hasTokenMetadata && options.verbose) {
|
|
1915
2080
|
(0, utils_1.log)(` Note: ZIP already contains token metadata (will create new token)`, options);
|
|
1916
2081
|
}
|
|
@@ -1922,9 +2087,11 @@ async function upgradeZipForTokenization(inputZipPath, options) {
|
|
|
1922
2087
|
for (const entry of entries) {
|
|
1923
2088
|
const filename = entry.filename || '';
|
|
1924
2089
|
// Skip metadata entries and directories
|
|
1925
|
-
if (filename ===
|
|
1926
|
-
filename ===
|
|
1927
|
-
filename ===
|
|
2090
|
+
if (filename === neozip_blockchain_1.TOKENIZED_METADATA ||
|
|
2091
|
+
filename === ots_1.TIMESTAMP_METADATA ||
|
|
2092
|
+
filename === ots_1.TIMESTAMP_SUBMITTED ||
|
|
2093
|
+
filename === zipstamp_server_1.SUBMIT_METADATA ||
|
|
2094
|
+
filename === zipstamp_server_1.TIMESTAMP_METADATA ||
|
|
1928
2095
|
entry.isDirectory) {
|
|
1929
2096
|
continue;
|
|
1930
2097
|
}
|
|
@@ -2080,7 +2247,7 @@ async function upgradeZipForTokenization(inputZipPath, options) {
|
|
|
2080
2247
|
}
|
|
2081
2248
|
// Mint new token before finalizing ZIP (so we can add metadata)
|
|
2082
2249
|
// Check for wallet passkey - don't show error yet
|
|
2083
|
-
const walletPasskey = (0,
|
|
2250
|
+
const walletPasskey = (0, blockchain_1.getWalletPasskey)(options.walletKey, false);
|
|
2084
2251
|
if (!walletPasskey) {
|
|
2085
2252
|
// Show error message and prompt user
|
|
2086
2253
|
console.error('โ Error: Wallet private key is required for blockchain operations');
|
|
@@ -2118,10 +2285,10 @@ async function upgradeZipForTokenization(inputZipPath, options) {
|
|
|
2118
2285
|
if (walletPasskey) {
|
|
2119
2286
|
(0, utils_1.log)(` Minting new token on blockchain...`, options);
|
|
2120
2287
|
const network = options.network || 'base-sepolia';
|
|
2121
|
-
if (!(0,
|
|
2288
|
+
if (!(0, blockchain_1.isSupportedNetwork)(network)) {
|
|
2122
2289
|
(0, exit_codes_1.exitZip)(exit_codes_1.ZIP_EXIT_CODES.BLOCKCHAIN_CONFIG_ERROR, `Error: Unsupported network: ${network}`);
|
|
2123
2290
|
}
|
|
2124
|
-
const minter = new
|
|
2291
|
+
const minter = new neozip_blockchain_1.ZipkitMinter(merkleRoot, {
|
|
2125
2292
|
network,
|
|
2126
2293
|
walletPrivateKey: walletPasskey,
|
|
2127
2294
|
verbose: options.verbose,
|
|
@@ -2131,12 +2298,12 @@ async function upgradeZipForTokenization(inputZipPath, options) {
|
|
|
2131
2298
|
// Create a temporary ZipkitNode instance for handleTokenMinting
|
|
2132
2299
|
// (it needs a zip instance, but we'll add metadata manually)
|
|
2133
2300
|
const tempZip = new node_1.default();
|
|
2134
|
-
const mintResult = await (0,
|
|
2301
|
+
const mintResult = await (0, blockchain_1.handleTokenMinting)(minter, tempZip, options.nonInteractive || false, 0);
|
|
2135
2302
|
if (mintResult.success && mintResult.tokenInfo) {
|
|
2136
2303
|
// Add token metadata entry before central directory
|
|
2137
2304
|
const tokenContent = JSON.stringify(mintResult.tokenInfo, null, 2);
|
|
2138
2305
|
const tokenBuffer = Buffer.from(tokenContent, 'utf8');
|
|
2139
|
-
const tokenEntry = new neozipkit_1.ZipEntry(
|
|
2306
|
+
const tokenEntry = new neozipkit_1.ZipEntry(neozip_blockchain_1.TOKENIZED_METADATA, null, false);
|
|
2140
2307
|
tokenEntry.timeDateDOS = tokenEntry.setDateTime(new Date());
|
|
2141
2308
|
tokenEntry.compressedSize = tokenBuffer.length;
|
|
2142
2309
|
tokenEntry.uncompressedSize = tokenBuffer.length;
|