gun-eth 1.4.35 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,1418 +0,0 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- var Gun = require('gun');
6
- var ethers = require('ethers');
7
- require('gun/sea.js');
8
-
9
- var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
10
- let contractAddresses$1 = {
11
- PROOF_OF_INTEGRITY_ADDRESS: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
12
- STEALTH_ANNOUNCER_ADDRESS: "0x5FbDB2315678afecb367f032d93F642f64180aa3"
13
- };
14
-
15
- if (typeof window === 'undefined') {
16
- const { fileURLToPath } = require('url');
17
- const { dirname } = require('path');
18
- const { readFileSync } = require('fs');
19
- const { join } = require('path');
20
-
21
- try {
22
- const __filename = fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('gun-eth.cjs.js', document.baseURI).href)));
23
- const __dirname = dirname(__filename);
24
- const rawdata = readFileSync(join(__dirname, 'contract-address.json'), 'utf8');
25
- contractAddresses$1 = JSON.parse(rawdata);
26
- console.log("Loaded contract addresses:", contractAddresses$1);
27
- } catch (error) {
28
- console.warn("Warning: contract-address.json not found or invalid");
29
- }
30
- }
31
-
32
- const LOCAL_CONFIG = {
33
- CHAIN_ID: 1337,
34
- PROOF_OF_INTEGRITY_ADDRESS: contractAddresses$1.PROOF_OF_INTEGRITY_ADDRESS,
35
- STEALTH_ANNOUNCER_ADDRESS: contractAddresses$1.STEALTH_ANNOUNCER_ADDRESS,
36
- RPC_URL: "http://127.0.0.1:8545",
37
- GUN_PEER: "http://localhost:8765/gun"
38
- };
39
-
40
- // Indirizzi di produzione per diverse chain
41
- const CHAIN_CONFIG = {
42
- optimismSepolia: {
43
- STEALTH_ANNOUNCER_ADDRESS: "0xD0F2e9DA59d2DFECFdE67CcF17300BB6093A72f8",
44
- PROOF_OF_INTEGRITY_ADDRESS: "0x...",
45
- RPC_URL: "https://sepolia.optimism.io",
46
- CHAIN_ID: 11155420
47
- },
48
- arbitrumSepolia: {
49
- STEALTH_ANNOUNCER_ADDRESS: "0x...",
50
- PROOF_OF_INTEGRITY_ADDRESS: "0x...",
51
- RPC_URL: "https://sepolia-rollup.arbitrum.io/rpc",
52
- CHAIN_ID: 421614
53
- },
54
- localhost: {
55
- RPC_URL: "http://127.0.0.1:8545",
56
- CHAIN_ID: 1337
57
- }
58
- };
59
-
60
- // Esporta gli indirizzi di default (Optimism Sepolia)
61
- CHAIN_CONFIG.optimismSepolia.STEALTH_ANNOUNCER_ADDRESS;
62
- CHAIN_CONFIG.optimismSepolia.PROOF_OF_INTEGRITY_ADDRESS;
63
-
64
- // Funzione per ottenere gli indirizzi corretti
65
- function getAddressesForChain(chainName) {
66
- let config;
67
-
68
- // Se è localhost, prova a caricare gli indirizzi locali
69
- if (chainName === 'localhost') {
70
- try {
71
- // Carica gli indirizzi dal file generato dal deploy locale
72
- const localAddresses = require('../config/contract-address.json');
73
- config = {
74
- ...CHAIN_CONFIG.localhost,
75
- ...localAddresses
76
- };
77
- console.log("Using local addresses:", config);
78
- return config;
79
- } catch (err) {
80
- console.warn('No local addresses found');
81
- throw new Error('No local addresses found. Did you run local deployment?');
82
- }
83
- }
84
-
85
- // Altrimenti usa gli indirizzi di produzione
86
- config = CHAIN_CONFIG[chainName];
87
- if (!config) {
88
- throw new Error(`Chain ${chainName} not supported. Supported chains: ${Object.keys(CHAIN_CONFIG).join(', ')}`);
89
- }
90
-
91
- return config;
92
- }
93
-
94
- // Funzione helper per verificare se siamo in ambiente locale
95
- function isLocalEnvironment() {
96
- return process.env.NODE_ENV === 'development' &&
97
- typeof window !== 'undefined' &&
98
- window.location.hostname === 'localhost';
99
- }
100
-
101
- const STEALTH_ANNOUNCER_ABI = [
102
- {
103
- "inputs": [
104
- {
105
- "internalType": "address",
106
- "name": "_devAddress",
107
- "type": "address"
108
- }
109
- ],
110
- "stateMutability": "nonpayable",
111
- "type": "constructor"
112
- },
113
- {
114
- "anonymous": false,
115
- "inputs": [
116
- {
117
- "internalType": "string",
118
- "name": "senderPublicKey",
119
- "type": "string"
120
- },
121
- {
122
- "internalType": "string",
123
- "name": "spendingPublicKey",
124
- "type": "string"
125
- },
126
- {
127
- "internalType": "address",
128
- "name": "stealthAddress",
129
- "type": "address"
130
- },
131
- {
132
- "internalType": "uint256",
133
- "name": "timestamp",
134
- "type": "uint256"
135
- }
136
- ],
137
- "name": "StealthPaymentAnnounced",
138
- "type": "event"
139
- },
140
- {
141
- "anonymous": false,
142
- "inputs": [
143
- {
144
- "internalType": "address",
145
- "name": "newAddress",
146
- "type": "address"
147
- }
148
- ],
149
- "name": "DevAddressUpdated",
150
- "type": "event"
151
- },
152
- {
153
- "anonymous": false,
154
- "inputs": [
155
- {
156
- "internalType": "uint256",
157
- "name": "newFee",
158
- "type": "uint256"
159
- }
160
- ],
161
- "name": "DevFeeUpdated",
162
- "type": "event"
163
- },
164
- {
165
- "inputs": [
166
- {
167
- "internalType": "string",
168
- "name": "senderPublicKey",
169
- "type": "string"
170
- },
171
- {
172
- "internalType": "string",
173
- "name": "spendingPublicKey",
174
- "type": "string"
175
- },
176
- {
177
- "internalType": "address",
178
- "name": "stealthAddress",
179
- "type": "address"
180
- }
181
- ],
182
- "name": "announcePayment",
183
- "outputs": [],
184
- "stateMutability": "payable",
185
- "type": "function"
186
- },
187
- {
188
- "inputs": [],
189
- "name": "devAddress",
190
- "outputs": [
191
- {
192
- "internalType": "address",
193
- "name": "",
194
- "type": "address"
195
- }
196
- ],
197
- "stateMutability": "view",
198
- "type": "function"
199
- },
200
- {
201
- "inputs": [],
202
- "name": "devFee",
203
- "outputs": [
204
- {
205
- "internalType": "uint256",
206
- "name": "",
207
- "type": "uint256"
208
- }
209
- ],
210
- "stateMutability": "view",
211
- "type": "function"
212
- },
213
- {
214
- "inputs": [],
215
- "name": "getAnnouncementsCount",
216
- "outputs": [
217
- {
218
- "internalType": "uint256",
219
- "name": "",
220
- "type": "uint256"
221
- }
222
- ],
223
- "stateMutability": "view",
224
- "type": "function"
225
- },
226
- {
227
- "inputs": [
228
- {
229
- "internalType": "uint256",
230
- "name": "fromIndex",
231
- "type": "uint256"
232
- },
233
- {
234
- "internalType": "uint256",
235
- "name": "toIndex",
236
- "type": "uint256"
237
- }
238
- ],
239
- "name": "getAnnouncementsInRange",
240
- "outputs": [
241
- {
242
- "components": [
243
- {
244
- "internalType": "string",
245
- "name": "senderPublicKey",
246
- "type": "string"
247
- },
248
- {
249
- "internalType": "string",
250
- "name": "spendingPublicKey",
251
- "type": "string"
252
- },
253
- {
254
- "internalType": "address",
255
- "name": "stealthAddress",
256
- "type": "address"
257
- },
258
- {
259
- "internalType": "uint256",
260
- "name": "timestamp",
261
- "type": "uint256"
262
- }
263
- ],
264
- "internalType": "struct StealthAnnouncer.StealthAnnouncement[]",
265
- "name": "",
266
- "type": "tuple[]"
267
- }
268
- ],
269
- "stateMutability": "view",
270
- "type": "function"
271
- },
272
- {
273
- "inputs": [
274
- {
275
- "internalType": "uint256",
276
- "name": "_newFee",
277
- "type": "uint256"
278
- }
279
- ],
280
- "name": "updateDevFee",
281
- "outputs": [],
282
- "stateMutability": "nonpayable",
283
- "type": "function"
284
- },
285
- {
286
- "inputs": [
287
- {
288
- "internalType": "address",
289
- "name": "_newAddress",
290
- "type": "address"
291
- }
292
- ],
293
- "name": "updateDevAddress",
294
- "outputs": [],
295
- "stateMutability": "nonpayable",
296
- "type": "function"
297
- },
298
- {
299
- "inputs": [],
300
- "name": "withdrawStuckETH",
301
- "outputs": [],
302
- "stateMutability": "nonpayable",
303
- "type": "function"
304
- }
305
- ];
306
-
307
- const PROOF_OF_INTEGRITY_ABI = [
308
- {
309
- "inputs": [
310
- {
311
- "internalType": "bytes[]",
312
- "name": "nodeIds",
313
- "type": "bytes[]"
314
- },
315
- {
316
- "internalType": "bytes32[]",
317
- "name": "contentHashes",
318
- "type": "bytes32[]"
319
- }
320
- ],
321
- "name": "batchUpdateData",
322
- "outputs": [],
323
- "stateMutability": "nonpayable",
324
- "type": "function"
325
- },
326
- {
327
- "anonymous": false,
328
- "inputs": [
329
- {
330
- "indexed": true,
331
- "internalType": "bytes",
332
- "name": "nodeId",
333
- "type": "bytes"
334
- },
335
- {
336
- "indexed": false,
337
- "internalType": "bytes32",
338
- "name": "contentHash",
339
- "type": "bytes32"
340
- },
341
- {
342
- "indexed": false,
343
- "internalType": "address",
344
- "name": "updater",
345
- "type": "address"
346
- }
347
- ],
348
- "name": "DataUpdated",
349
- "type": "event"
350
- },
351
- {
352
- "inputs": [
353
- {
354
- "internalType": "bytes",
355
- "name": "nodeId",
356
- "type": "bytes"
357
- }
358
- ],
359
- "name": "getLatestRecord",
360
- "outputs": [
361
- {
362
- "internalType": "bytes32",
363
- "name": "",
364
- "type": "bytes32"
365
- },
366
- {
367
- "internalType": "uint256",
368
- "name": "",
369
- "type": "uint256"
370
- },
371
- {
372
- "internalType": "address",
373
- "name": "",
374
- "type": "address"
375
- }
376
- ],
377
- "stateMutability": "view",
378
- "type": "function"
379
- },
380
- {
381
- "inputs": [
382
- {
383
- "internalType": "bytes",
384
- "name": "nodeId",
385
- "type": "bytes"
386
- },
387
- {
388
- "internalType": "bytes32",
389
- "name": "contentHash",
390
- "type": "bytes32"
391
- }
392
- ],
393
- "name": "updateData",
394
- "outputs": [],
395
- "stateMutability": "nonpayable",
396
- "type": "function"
397
- },
398
- {
399
- "inputs": [
400
- {
401
- "internalType": "bytes",
402
- "name": "nodeId",
403
- "type": "bytes"
404
- },
405
- {
406
- "internalType": "bytes32",
407
- "name": "contentHash",
408
- "type": "bytes32"
409
- }
410
- ],
411
- "name": "verifyData",
412
- "outputs": [
413
- {
414
- "internalType": "bool",
415
- "name": "",
416
- "type": "bool"
417
- },
418
- {
419
- "internalType": "uint256",
420
- "name": "",
421
- "type": "uint256"
422
- },
423
- {
424
- "internalType": "address",
425
- "name": "",
426
- "type": "address"
427
- }
428
- ],
429
- "stateMutability": "view",
430
- "type": "function"
431
- }
432
- ];
433
-
434
- const SEA = Gun.SEA;
435
-
436
- let PROOF_CONTRACT_ADDRESS;
437
- let contractAddresses = {
438
- PROOF_OF_INTEGRITY_ADDRESS:
439
- CHAIN_CONFIG.optimismSepolia.PROOF_OF_INTEGRITY_ADDRESS,
440
- STEALTH_ANNOUNCER_ADDRESS:
441
- CHAIN_CONFIG.optimismSepolia.STEALTH_ANNOUNCER_ADDRESS,
442
- };
443
-
444
- // Funzione di inizializzazione per Node.js
445
- const initNodeEnvironment = async () => {
446
- if (typeof window === "undefined") {
447
- try {
448
- // Importazioni dinamiche
449
- const { fileURLToPath } = await import('url');
450
- const { dirname } = await import('path');
451
- const { readFile } = await import('fs/promises');
452
- const { join } = await import('path');
453
-
454
- const __filename = fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('gun-eth.cjs.js', document.baseURI).href)));
455
- const __dirname = dirname(__filename);
456
-
457
- try {
458
- const rawdata = await readFile(
459
- join(__dirname, "contract-address.json"),
460
- "utf8"
461
- );
462
- contractAddresses = JSON.parse(rawdata);
463
- console.log("Loaded contract addresses:", contractAddresses);
464
- } catch (err) {
465
- console.warn("Warning: contract-address.json not found or invalid");
466
- }
467
- } catch (error) {
468
- console.error("Error loading Node.js modules:", error);
469
- }
470
- }
471
- };
472
-
473
- // Inizializza solo in ambiente Node.js
474
- if (typeof window === "undefined") {
475
- initNodeEnvironment();
476
- }
477
-
478
- // =============================================
479
- // UTILITY FUNCTIONS
480
- // =============================================
481
- /**
482
- * Generates a random node ID for GunDB
483
- * @returns {string} A random hexadecimal string
484
- */
485
- function generateRandomId() {
486
- const randomBytes = ethers.ethers.randomBytes(32);
487
- return ethers.ethers.hexlify(randomBytes).slice(2);
488
- }
489
-
490
- /**
491
- * Generates a password from a signature.
492
- * @param {string} signature - The signature to derive the password from.
493
- * @returns {string|null} The generated password or null if generation fails.
494
- */
495
- function generatePassword(signature) {
496
- try {
497
- const signatureBytes = ethers.ethers.toUtf8Bytes(signature);
498
- const hash = ethers.ethers.keccak256(signatureBytes);
499
- console.log("Generated password:", hash);
500
- return hash;
501
- } catch (error) {
502
- console.error("Error generating password:", error);
503
- return null;
504
- }
505
- }
506
-
507
- /**
508
- * Converts a Gun private key to an Ethereum account.
509
- * @param {string} gunPrivateKey - The Gun private key in base64url format.
510
- * @returns {Object} An object containing the Ethereum account and public key.
511
- */
512
- function gunToEthAccount(gunPrivateKey) {
513
- // Function to convert base64url to hex
514
- const base64UrlToHex = (base64url) => {
515
- const padding = "=".repeat((4 - (base64url.length % 4)) % 4);
516
- const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/") + padding;
517
- const binary = atob(base64);
518
- return Array.from(binary, (char) =>
519
- char.charCodeAt(0).toString(16).padStart(2, "0")
520
- ).join("");
521
- };
522
-
523
- // Convert Gun private key to hex format
524
- const hexPrivateKey = "0x" + base64UrlToHex(gunPrivateKey);
525
-
526
- // Create an Ethereum wallet from the private key
527
- const wallet = new ethers.ethers.Wallet(hexPrivateKey);
528
-
529
- // Get the public address (public key)
530
- const publicKey = wallet.address;
531
-
532
- return {
533
- account: wallet,
534
- publicKey: publicKey,
535
- privateKey: hexPrivateKey,
536
- };
537
- }
538
-
539
- /**
540
- * Gets an Ethereum signer based on current configuration
541
- * @returns {Promise<ethers.Signer>} The configured signer
542
- * @throws {Error} If no valid provider is found
543
- */
544
- const getSigner = async () => {
545
- if (rpcUrl && privateKey) {
546
- const provider = new ethers.ethers.JsonRpcProvider(rpcUrl, {
547
- chainId: LOCAL_CONFIG.CHAIN_ID,
548
- name: "localhost",
549
- });
550
- return new Wallet(privateKey, provider);
551
- } else if (
552
- typeof window !== "undefined" &&
553
- typeof window.ethereum !== "undefined"
554
- ) {
555
- await window.ethereum.request({ method: "eth_requestAccounts" });
556
- const provider = new ethers.ethers.BrowserProvider(window.ethereum);
557
- return provider.getSigner();
558
- } else {
559
- throw new Error("No valid Ethereum provider found");
560
- }
561
- };
562
-
563
- /**
564
- * Utility function to generate stealth address
565
- * @param {string} sharedSecret - The shared secret
566
- * @param {string} spendingPublicKey - The spending public key
567
- * @returns {Object} The stealth address and private key
568
- */
569
- function deriveStealthAddress(sharedSecret, spendingPublicKey) {
570
- try {
571
- const sharedSecretBytes = ethers.ethers.toUtf8Bytes(sharedSecret);
572
- const spendingPublicKeyBytes = ethers.ethers.toUtf8Bytes(spendingPublicKey);
573
-
574
- const stealthPrivateKey = ethers.ethers.keccak256(
575
- ethers.ethers.concat([sharedSecretBytes, spendingPublicKeyBytes])
576
- );
577
-
578
- const stealthWallet = new Wallet(stealthPrivateKey);
579
-
580
- console.log("Debug deriveStealthAddress:", {
581
- sharedSecretHex: stealthPrivateKey,
582
- spendingPublicKey,
583
- stealthPrivateKey,
584
- stealthAddress: stealthWallet.address,
585
- });
586
-
587
- return {
588
- stealthPrivateKey,
589
- stealthAddress: stealthWallet.address,
590
- wallet: stealthWallet,
591
- };
592
- } catch (error) {
593
- console.error("Error in deriveStealthAddress:", error);
594
- throw error;
595
- }
596
- }
597
-
598
- // =============================================
599
- // BASIC GUN-ETH CHAIN METHODS
600
- // =============================================
601
-
602
- // Set the message to sign
603
- Gun.chain.MESSAGE_TO_SIGN = MESSAGE_TO_SIGN;
604
-
605
- /**
606
- * Sets standalone configuration for Gun.
607
- * @param {string} newRpcUrl - The new RPC URL.
608
- * @param {string} newPrivateKey - The new private key.
609
- * @returns {Gun} The Gun instance for chaining.
610
- */
611
- Gun.chain.setSigner = function (newRpcUrl, newPrivateKey) {
612
- rpcUrl = newRpcUrl;
613
- privateKey = newPrivateKey;
614
- console.log("Standalone configuration set");
615
- return this;
616
- };
617
-
618
- Gun.chain.getSigner = getSigner();
619
-
620
- /**
621
- * Verifies an Ethereum signature.
622
- * @param {string} message - The original message that was signed.
623
- * @param {string} signature - The signature to verify.
624
- * @returns {Promise<string|null>} The recovered address or null if verification fails.
625
- */
626
- Gun.chain.verifySignature = async function (message, signature) {
627
- try {
628
- const recoveredAddress = ethers.ethers.verifyMessage(message, signature);
629
- return recoveredAddress;
630
- } catch (error) {
631
- console.error("Error verifying signature:", error);
632
- return null;
633
- }
634
- };
635
-
636
- /**
637
- * Generates a password from a signature.
638
- * @param {string} signature - The signature to derive the password from.
639
- * @returns {string|null} The generated password or null if generation fails.
640
- */
641
- Gun.chain.generatePassword = function (signature) {
642
- return generatePassword(signature);
643
- };
644
-
645
- /**
646
- * Creates an Ethereum signature for a given message.
647
- * @param {string} message - The message to sign.
648
- * @returns {Promise<string|null>} The signature or null if signing fails.
649
- */
650
- Gun.chain.createSignature = async function (message) {
651
- try {
652
- // Check if message matches MESSAGE_TO_SIGN
653
- if (message !== MESSAGE_TO_SIGN) {
654
- throw new Error("Invalid message, valid message is: " + MESSAGE_TO_SIGN);
655
- }
656
- const signer = await getSigner();
657
- const signature = await signer.signMessage(message);
658
- console.log("Signature created:", signature);
659
- return signature;
660
- } catch (error) {
661
- console.error("Error creating signature:", error);
662
- return null;
663
- }
664
- };
665
-
666
- // =============================================
667
- // KEY PAIR MANAGEMENT
668
- // =============================================
669
- /**
670
- * Creates and stores an encrypted key pair for a given address.
671
- * @param {string} address - The Ethereum address to associate with the key pair.
672
- * @param {string} signature - The signature to use for encryption.
673
- * @returns {Promise<void>}
674
- */
675
- Gun.chain.createAndStoreEncryptedPair = async function (address, signature) {
676
- try {
677
- const gun = this;
678
- const pair = await SEA.pair();
679
- const v_pair = await SEA.pair();
680
- const s_pair = await SEA.pair();
681
- const password = generatePassword(signature);
682
-
683
- // Save original SEA pairs
684
- const encryptedPair = await SEA.encrypt(JSON.stringify(pair), password);
685
- const encryptedV_pair = await SEA.encrypt(JSON.stringify(v_pair), password);
686
- const encryptedS_pair = await SEA.encrypt(JSON.stringify(s_pair), password);
687
-
688
- // Convert only to get Ethereum addresses
689
- const viewingAccount = gunToEthAccount(v_pair.priv);
690
- const spendingAccount = gunToEthAccount(s_pair.priv);
691
-
692
- gun
693
- .get("gun-eth")
694
- .get("users")
695
- .get(address)
696
- .put({
697
- pair: encryptedPair,
698
- v_pair: encryptedV_pair,
699
- s_pair: encryptedS_pair,
700
- publicKeys: {
701
- viewingPublicKey: v_pair.epub, // Use SEA encryption public key
702
- viewingPublicKey: v_pair.epub, // Use SEA encryption public key
703
- spendingPublicKey: spendingAccount.publicKey, // Use Ethereum address
704
- ethViewingAddress: viewingAccount.publicKey, // Also save Ethereum address
705
- },
706
- });
707
-
708
- console.log("Encrypted pairs and public keys stored for:", address);
709
- } catch (error) {
710
- console.error("Error creating and storing encrypted pair:", error);
711
- throw error;
712
- }
713
- };
714
-
715
- /**
716
- * Retrieves and decrypts a stored key pair for a given address.
717
- * @param {string} address - The Ethereum address associated with the key pair.
718
- * @param {string} signature - The signature to use for decryption.
719
- * @returns {Promise<Object|null>} The decrypted key pair or null if retrieval fails.
720
- */
721
- Gun.chain.getAndDecryptPair = async function (address, signature) {
722
- try {
723
- const gun = this;
724
- const encryptedData = await gun
725
- .get("gun-eth")
726
- .get("users")
727
- .get(address)
728
- .get("pair")
729
- .then();
730
- if (!encryptedData) {
731
- throw new Error("No encrypted data found for this address");
732
- }
733
- const password = generatePassword(signature);
734
- const decryptedPair = await SEA.decrypt(encryptedData, password);
735
- console.log(decryptedPair);
736
- return decryptedPair;
737
- } catch (error) {
738
- console.error("Error retrieving and decrypting pair:", error);
739
- return null;
740
- }
741
- };
742
-
743
- // =============================================
744
- // PROOF OF INTEGRITY
745
- // =============================================
746
- /**
747
- * Proof of Integrity
748
- * @param {string} chain - The blockchain to use (e.g., "optimismSepolia").
749
- * @param {string} nodeId - The ID of the node to verify or write.
750
- * @param {Object} data - The data to write (if writing).
751
- * @param {Function} callback - Callback function to handle the result.
752
- * @returns {Gun} The Gun instance for chaining.
753
- */
754
- Gun.chain.proof = function (chain, nodeId, data, callback) {
755
- console.log("Proof plugin called with:", { chain, nodeId, data });
756
-
757
- if (typeof callback !== "function") {
758
- console.error("Callback must be a function");
759
- return this;
760
- }
761
-
762
- try {
763
- // Se siamo in localhost e in development, usa automaticamente la chain locale
764
- const targetChain = isLocalEnvironment() ? "localhost" : chain;
765
- const chainConfig = getAddressesForChain(targetChain);
766
-
767
- console.log(`Using ${targetChain} configuration:`, chainConfig);
768
-
769
- // Usa gli indirizzi dalla configurazione
770
- const contract = new ethers.ethers.Contract(
771
- chainConfig.PROOF_OF_INTEGRITY_ADDRESS,
772
- PROOF_OF_INTEGRITY_ABI,
773
- signer
774
- );
775
-
776
- // Funzione per verificare on-chain
777
- const verifyOnChain = async (nodeId, contentHash) => {
778
- console.log("Verifying on chain:", { nodeId, contentHash });
779
- const signer = await getSigner();
780
- const contract = new Contract(
781
- PROOF_CONTRACT_ADDRESS,
782
- PROOF_OF_INTEGRITY_ABI,
783
- signer
784
- );
785
- const [isValid, timestamp, updater] = await contract.verifyData(
786
- ethers.ethers.toUtf8Bytes(nodeId),
787
- contentHash
788
- );
789
- console.log("Verification result:", { isValid, timestamp, updater });
790
- return { isValid, timestamp, updater };
791
- };
792
-
793
- // Funzione per scrivere on-chain
794
- const writeOnChain = async (nodeId, contentHash) => {
795
- console.log("Writing on chain:", { nodeId, contentHash });
796
- const signer = await getSigner();
797
- const contract = new Contract(
798
- PROOF_CONTRACT_ADDRESS,
799
- PROOF_OF_INTEGRITY_ABI,
800
- signer
801
- );
802
- const tx = await contract.updateData(toUtf8Bytes(nodeId), contentHash);
803
- console.log("Transaction sent:", tx.hash);
804
- const receipt = await tx.wait();
805
- console.log("Transaction confirmed:", receipt);
806
- return tx;
807
- };
808
-
809
- // Funzione per ottenere l'ultimo record
810
- const getLatestRecord = async (nodeId) => {
811
- const signer = await getSigner();
812
- const contract = new Contract(
813
- PROOF_CONTRACT_ADDRESS,
814
- PROOF_OF_INTEGRITY_ABI,
815
- signer
816
- );
817
- const [contentHash, timestamp, updater] = await contract.getLatestRecord(
818
- toUtf8Bytes(nodeId)
819
- );
820
- console.log("Latest record from blockchain:", {
821
- nodeId,
822
- contentHash,
823
- timestamp,
824
- updater,
825
- });
826
- return { contentHash, timestamp, updater };
827
- };
828
-
829
- if (nodeId && !data) {
830
- // Case 1: User passes only node
831
- gun.get(nodeId).once(async (existingData) => {
832
- if (!existingData) {
833
- if (callback) callback({ err: "Node not found in GunDB" });
834
- return;
835
- }
836
-
837
- console.log("existingData", existingData);
838
-
839
- // Use stored contentHash instead of recalculating
840
- const contentHash = existingData._contentHash;
841
- console.log("contentHash", contentHash);
842
-
843
- if (!contentHash) {
844
- if (callback)
845
- callback({ err: "No content hash found for this node" });
846
- return;
847
- }
848
-
849
- try {
850
- const { isValid, timestamp, updater } = await verifyOnChain(
851
- nodeId,
852
- contentHash
853
- );
854
- const latestRecord = await getLatestRecord(nodeId);
855
-
856
- if (isValid) {
857
- if (callback)
858
- callback({
859
- ok: true,
860
- message: "Data verified on blockchain",
861
- timestamp,
862
- updater,
863
- latestRecord,
864
- });
865
- } else {
866
- if (callback)
867
- callback({
868
- ok: false,
869
- message: "Data not verified on blockchain",
870
- latestRecord,
871
- });
872
- }
873
- } catch (error) {
874
- if (callback) callback({ err: error.message });
875
- }
876
- });
877
- } else if (data && !nodeId) {
878
- // Case 2: User passes only text (data)
879
- const newNodeId = generateRandomId();
880
- const dataString = JSON.stringify(data);
881
- const contentHash = ethers.ethers.keccak256(ethers.ethers.toUtf8Bytes(dataString));
882
-
883
- gun
884
- .get(newNodeId)
885
- .put({ ...data, _contentHash: contentHash }, async (ack) => {
886
- console.log("ack", ack);
887
- if (ack.err) {
888
- if (callback) callback({ err: "Error saving data to GunDB" });
889
- return;
890
- }
891
-
892
- try {
893
- const tx = await writeOnChain(newNodeId, contentHash);
894
- if (callback)
895
- callback({
896
- ok: true,
897
- message: "Data written to GunDB and blockchain",
898
- nodeId: newNodeId,
899
- txHash: tx.hash,
900
- });
901
- } catch (error) {
902
- if (callback) callback({ err: error.message });
903
- }
904
- });
905
- } else {
906
- if (callback)
907
- callback({
908
- err: "Invalid input. Provide either nodeId or data, not both.",
909
- });
910
- }
911
-
912
- return gun;
913
- } catch (error) {
914
- callback({ err: error.message });
915
- return this;
916
- }
917
- };
918
-
919
- // =============================================
920
- // STEALTH ADDRESS CORE FUNCTIONS
921
- // =============================================
922
- /**
923
- * Converts a Gun private key to an Ethereum account.
924
- * @param {string} gunPrivateKey - The Gun private key in base64url format.
925
- * @returns {Object} An object containing the Ethereum account and public key.
926
- */
927
- Gun.chain.gunToEthAccount = function (gunPrivateKey) {
928
- return gunToEthAccount(gunPrivateKey);
929
- };
930
-
931
- /**
932
- * Generate a stealth key and related key pairs
933
- * @param {string} recipientAddress - The recipient's Ethereum address
934
- * @param {string} signature - The sender's signature to access their keys
935
- * @returns {Promise<Object>} Object containing stealth addresses and keys
936
- */
937
- Gun.chain.generateStealthAddress = async function (
938
- recipientAddress,
939
- signature
940
- ) {
941
- try {
942
- const gun = this;
943
-
944
- // Get recipient's public keys
945
- const recipientData = await gun
946
- .get("gun-eth")
947
- .get("users")
948
- .get(recipientAddress)
949
- .get("publicKeys")
950
- .then();
951
-
952
- if (
953
- !recipientData ||
954
- !recipientData.viewingPublicKey ||
955
- !recipientData.spendingPublicKey
956
- ) {
957
- throw new Error("Recipient's public keys not found");
958
- }
959
-
960
- // Get sender's keys
961
- const senderAddress = await this.verifySignature(
962
- MESSAGE_TO_SIGN,
963
- signature
964
- );
965
- const password = generatePassword(signature);
966
-
967
- const senderData = await gun
968
- .get("gun-eth")
969
- .get("users")
970
- .get(senderAddress)
971
- .then();
972
-
973
- if (!senderData || !senderData.s_pair) {
974
- throw new Error("Sender's keys not found");
975
- }
976
-
977
- // Decrypt sender's spending pair
978
- let spendingKeyPair;
979
- try {
980
- const decryptedData = await SEA.decrypt(senderData.s_pair, password);
981
- spendingKeyPair =
982
- typeof decryptedData === "string"
983
- ? JSON.parse(decryptedData)
984
- : decryptedData;
985
- } catch (error) {
986
- console.error("Error decrypting spending pair:", error);
987
- throw new Error("Unable to decrypt spending pair");
988
- }
989
-
990
- // Generate shared secret using SEA ECDH with encryption public key
991
- const sharedSecret = await SEA.secret(
992
- recipientData.viewingPublicKey,
993
- spendingKeyPair
994
- );
995
-
996
- if (!sharedSecret) {
997
- throw new Error("Unable to generate shared secret");
998
- }
999
-
1000
- console.log("Generate shared secret:", sharedSecret);
1001
-
1002
- const { stealthAddress } = deriveStealthAddress(
1003
- sharedSecret,
1004
- recipientData.spendingPublicKey
1005
- );
1006
-
1007
- return {
1008
- stealthAddress,
1009
- senderPublicKey: spendingKeyPair.epub, // Use encryption public key
1010
- spendingPublicKey: recipientData.spendingPublicKey,
1011
- };
1012
- } catch (error) {
1013
- console.error("Error generating stealth address:", error);
1014
- throw error;
1015
- }
1016
- };
1017
-
1018
- /**
1019
- * Publish public keys needed to receive stealth payments
1020
- * @param {string} signature - The signature to authenticate the user
1021
- * @returns {Promise<void>}
1022
- */
1023
- Gun.chain.publishStealthKeys = async function (signature) {
1024
- try {
1025
- const gun = this;
1026
- const address = await this.verifySignature(MESSAGE_TO_SIGN, signature);
1027
- const password = generatePassword(signature);
1028
-
1029
- // Get encrypted key pairs
1030
- const encryptedData = await gun
1031
- .get("gun-eth")
1032
- .get("users")
1033
- .get(address)
1034
- .then();
1035
-
1036
- if (!encryptedData || !encryptedData.v_pair || !encryptedData.s_pair) {
1037
- throw new Error("Keys not found");
1038
- }
1039
-
1040
- // Decrypt viewing and spending pairs
1041
- const viewingKeyPair = JSON.parse(
1042
- await SEA.decrypt(encryptedData.v_pair, password)
1043
- );
1044
- const spendingKeyPair = JSON.parse(
1045
- await SEA.decrypt(encryptedData.s_pair, password)
1046
- );
1047
-
1048
- const viewingAccount = gunToEthAccount(viewingKeyPair.priv);
1049
- const spendingAccount = gunToEthAccount(spendingKeyPair.priv);
1050
-
1051
- // Publish only public keys
1052
- gun.get("gun-eth").get("users").get(address).get("publicKeys").put({
1053
- viewingPublicKey: viewingAccount.publicKey,
1054
- spendingPublicKey: spendingAccount.publicKey,
1055
- });
1056
-
1057
- console.log("Stealth public keys published successfully");
1058
- } catch (error) {
1059
- console.error("Error publishing stealth keys:", error);
1060
- throw error;
1061
- }
1062
- };
1063
-
1064
- // =============================================
1065
- // STEALTH PAYMENT FUNCTIONS
1066
- // =============================================
1067
- /**
1068
- * Recover funds from a stealth address
1069
- * @param {string} stealthAddress - The stealth address to recover funds from
1070
- * @param {string} senderPublicKey - The sender's public key used to generate the address
1071
- * @param {string} signature - The signature to decrypt private keys
1072
- * @returns {Promise<Object>} Object containing wallet to access funds
1073
- */
1074
- Gun.chain.recoverStealthFunds = async function (
1075
- stealthAddress,
1076
- senderPublicKey,
1077
- signature,
1078
- spendingPublicKey
1079
- ) {
1080
- try {
1081
- const gun = this;
1082
- const password = generatePassword(signature);
1083
-
1084
- // Get own key pairs
1085
- const myAddress = await this.verifySignature(MESSAGE_TO_SIGN, signature);
1086
- const encryptedData = await gun
1087
- .get("gun-eth")
1088
- .get("users")
1089
- .get(myAddress)
1090
- .then();
1091
-
1092
- if (!encryptedData || !encryptedData.v_pair || !encryptedData.s_pair) {
1093
- throw new Error("Keys not found");
1094
- }
1095
-
1096
- // Decrypt viewing and spending pairs
1097
- let viewingKeyPair;
1098
- try {
1099
- const decryptedViewingData = await SEA.decrypt(
1100
- encryptedData.v_pair,
1101
- password
1102
- );
1103
- viewingKeyPair =
1104
- typeof decryptedViewingData === "string"
1105
- ? JSON.parse(decryptedViewingData)
1106
- : decryptedViewingData;
1107
- } catch (error) {
1108
- console.error("Error decrypting keys:", error);
1109
- throw new Error("Unable to decrypt keys");
1110
- }
1111
-
1112
- // Generate shared secret using SEA ECDH
1113
- const sharedSecret = await SEA.secret(senderPublicKey, viewingKeyPair);
1114
-
1115
- if (!sharedSecret) {
1116
- throw new Error("Unable to generate shared secret");
1117
- }
1118
-
1119
- console.log("Recover shared secret:", sharedSecret);
1120
-
1121
- const { wallet, stealthAddress: recoveredAddress } = deriveStealthAddress(
1122
- sharedSecret,
1123
- spendingPublicKey
1124
- );
1125
-
1126
- // Verify address matches
1127
- if (recoveredAddress.toLowerCase() !== stealthAddress.toLowerCase()) {
1128
- console.error("Mismatch:", {
1129
- recovered: recoveredAddress,
1130
- expected: stealthAddress,
1131
- sharedSecret,
1132
- });
1133
- throw new Error("Recovered stealth address does not match");
1134
- }
1135
-
1136
- return {
1137
- wallet,
1138
- address: recoveredAddress,
1139
- };
1140
- } catch (error) {
1141
- console.error("Error recovering stealth funds:", error);
1142
- throw error;
1143
- }
1144
- };
1145
-
1146
- /**
1147
- * Announce a stealth payment
1148
- * @param {string} stealthAddress - The generated stealth address
1149
- * @param {string} senderPublicKey - The sender's public key
1150
- * @param {string} spendingPublicKey - The spending public key
1151
- * @param {string} signature - The sender's signature
1152
- * @returns {Promise<void>}
1153
- */
1154
- Gun.chain.announceStealthPayment = async function (
1155
- stealthAddress,
1156
- senderPublicKey,
1157
- spendingPublicKey,
1158
- signature,
1159
- options = { onChain: false, chain: "optimismSepolia" }
1160
- ) {
1161
- try {
1162
- const gun = this;
1163
- const senderAddress = await this.verifySignature(
1164
- MESSAGE_TO_SIGN,
1165
- signature
1166
- );
1167
-
1168
- if (options.onChain) {
1169
- // On-chain announcement
1170
- const signer = await getSigner();
1171
- const chainConfig = getAddressesForChain(options.chain);
1172
- const contractAddress = chainConfig.STEALTH_ANNOUNCER_ADDRESS;
1173
-
1174
- console.log("Using contract address:", contractAddress);
1175
-
1176
- const contract = new Contract(
1177
- contractAddress,
1178
- STEALTH_ANNOUNCER_ABI,
1179
- signer
1180
- );
1181
-
1182
- // Get dev fee from contract
1183
- const devFee = await contract.devFee();
1184
- console.log("Dev fee:", devFee.toString());
1185
-
1186
- // Call contract
1187
- const tx = await contract.announcePayment(
1188
- senderPublicKey,
1189
- spendingPublicKey,
1190
- stealthAddress,
1191
- { value: devFee }
1192
- );
1193
-
1194
- console.log("Transaction sent:", tx.hash);
1195
- const receipt = await tx.wait();
1196
- console.log("Transaction confirmed:", receipt.hash);
1197
-
1198
- console.log("Stealth payment announced on-chain (dev fee paid)");
1199
- } else {
1200
- // Off-chain announcement (GunDB)
1201
- gun.get("gun-eth").get("stealth-payments").set({
1202
- stealthAddress,
1203
- senderAddress,
1204
- senderPublicKey,
1205
- spendingPublicKey,
1206
- timestamp: Date.now(),
1207
- });
1208
- console.log("Stealth payment announced off-chain");
1209
- }
1210
- } catch (error) {
1211
- console.error("Error announcing stealth payment:", error);
1212
- console.error("Error details:", error.stack);
1213
- throw error;
1214
- }
1215
- };
1216
-
1217
- /**
1218
- * Get all stealth payments for an address
1219
- * @param {string} signature - The signature to authenticate the user
1220
- * @returns {Promise<Array>} List of stealth payments
1221
- */
1222
- Gun.chain.getStealthPayments = async function (
1223
- signature,
1224
- options = { source: "both" }
1225
- ) {
1226
- try {
1227
- const payments = [];
1228
-
1229
- if (options.source === "onChain" || options.source === "both") {
1230
- const signer = await getSigner();
1231
- const chainConfig = getAddressesForChain(
1232
- options.chain || "optimismSepolia"
1233
- );
1234
- const contractAddress = chainConfig.STEALTH_ANNOUNCER_ADDRESS;
1235
-
1236
- const contract = new Contract(
1237
- contractAddress,
1238
- STEALTH_ANNOUNCER_ABI,
1239
- signer
1240
- );
1241
-
1242
- try {
1243
- // Get total number of announcements
1244
- const totalAnnouncements = await contract.getAnnouncementsCount();
1245
- const totalCount = Number(totalAnnouncements.toString());
1246
- console.log("Total on-chain announcements:", totalCount);
1247
-
1248
- if (totalCount > 0) {
1249
- // Get announcements in batches of 100
1250
- const batchSize = 100;
1251
- const lastIndex = totalCount - 1;
1252
-
1253
- for (let i = 0; i <= lastIndex; i += batchSize) {
1254
- const toIndex = Math.min(i + batchSize - 1, lastIndex);
1255
- const batch = await contract.getAnnouncementsInRange(i, toIndex);
1256
-
1257
- // For each announcement, try to decrypt
1258
- for (const announcement of batch) {
1259
- try {
1260
- // Verify announcement is valid
1261
- if (
1262
- !announcement ||
1263
- !announcement.stealthAddress ||
1264
- !announcement.senderPublicKey ||
1265
- !announcement.spendingPublicKey
1266
- ) {
1267
- console.log("Invalid announcement:", announcement);
1268
- continue;
1269
- }
1270
-
1271
- // Try to recover funds to verify if announcement is for us
1272
- const recoveredWallet = await this.recoverStealthFunds(
1273
- announcement.stealthAddress,
1274
- announcement.senderPublicKey,
1275
- signature,
1276
- announcement.spendingPublicKey
1277
- );
1278
-
1279
- // If no errors thrown, announcement is for us
1280
- payments.push({
1281
- stealthAddress: announcement.stealthAddress,
1282
- senderPublicKey: announcement.senderPublicKey,
1283
- spendingPublicKey: announcement.spendingPublicKey,
1284
- timestamp: Number(announcement.timestamp),
1285
- source: "onChain",
1286
- wallet: recoveredWallet,
1287
- });
1288
- } catch (e) {
1289
- // Not for us, continue
1290
- console.log(
1291
- `Announcement not for us: ${announcement.stealthAddress}`
1292
- );
1293
- continue;
1294
- }
1295
- }
1296
- }
1297
- }
1298
- } catch (error) {
1299
- console.error("Error retrieving on-chain announcements:", error);
1300
- }
1301
- }
1302
-
1303
- if (options.source === "offChain" || options.source === "both") {
1304
- // Get off-chain payments
1305
- const gun = this;
1306
- const offChainPayments = await new Promise((resolve) => {
1307
- const p = [];
1308
- gun
1309
- .get("gun-eth")
1310
- .get("stealth-payments")
1311
- .get(recipientAddress)
1312
- .map()
1313
- .once((payment, id) => {
1314
- if (payment?.stealthAddress) {
1315
- p.push({ ...payment, id, source: "offChain" });
1316
- }
1317
- });
1318
- setTimeout(() => resolve(p), 2000);
1319
- });
1320
-
1321
- payments.push(...offChainPayments);
1322
- }
1323
-
1324
- console.log(`Found ${payments.length} stealth payments`);
1325
- return payments;
1326
- } catch (error) {
1327
- console.error("Error retrieving stealth payments:", error);
1328
- throw error;
1329
- }
1330
- };
1331
-
1332
- /**
1333
- * Clean up old stealth payments
1334
- * @param {string} recipientAddress - The recipient's address
1335
- * @returns {Promise<void>}
1336
- */
1337
- Gun.chain.cleanStealthPayments = async function (recipientAddress) {
1338
- try {
1339
- const gun = this;
1340
- const payments = await gun
1341
- .get("gun-eth")
1342
- .get("stealth-payments")
1343
- .get(recipientAddress)
1344
- .map()
1345
- .once()
1346
- .then();
1347
-
1348
- // Remove empty or invalid nodes
1349
- if (payments) {
1350
- Object.keys(payments).forEach(async (key) => {
1351
- const payment = payments[key];
1352
- if (
1353
- !payment ||
1354
- !payment.stealthAddress ||
1355
- !payment.senderPublicKey ||
1356
- !payment.spendingPublicKey
1357
- ) {
1358
- await gun
1359
- .get("gun-eth")
1360
- .get("stealth-payments")
1361
- .get(recipientAddress)
1362
- .get(key)
1363
- .put(null);
1364
- }
1365
- });
1366
- }
1367
- } catch (error) {
1368
- console.error("Error cleaning stealth payments:", error);
1369
- }
1370
- };
1371
-
1372
- // =============================================
1373
- // EXPORTS
1374
- // =============================================
1375
-
1376
- // Crea una classe GunEth che contiene tutti i metodi e le utility
1377
- class GunEth {
1378
- // Static utility methods
1379
- static generateRandomId = generateRandomId;
1380
- static generatePassword = generatePassword;
1381
- static gunToEthAccount = gunToEthAccount;
1382
- static getSigner = getSigner;
1383
- static deriveStealthAddress = deriveStealthAddress;
1384
-
1385
- // Chain methods
1386
- static chainMethods = {
1387
- setSigner: Gun.chain.setSigner,
1388
- getSigner: Gun.chain.getSigner,
1389
- verifySignature: Gun.chain.verifySignature,
1390
- generatePassword: Gun.chain.generatePassword,
1391
- createSignature: Gun.chain.createSignature,
1392
- createAndStoreEncryptedPair: Gun.chain.createAndStoreEncryptedPair,
1393
- getAndDecryptPair: Gun.chain.getAndDecryptPair,
1394
- proof: Gun.chain.proof,
1395
- gunToEthAccount: Gun.chain.gunToEthAccount,
1396
- generateStealthAddress: Gun.chain.generateStealthAddress,
1397
- publishStealthKeys: Gun.chain.publishStealthKeys,
1398
- recoverStealthFunds: Gun.chain.recoverStealthFunds,
1399
- announceStealthPayment: Gun.chain.announceStealthPayment,
1400
- getStealthPayments: Gun.chain.getStealthPayments,
1401
- cleanStealthPayments: Gun.chain.cleanStealthPayments,
1402
- };
1403
-
1404
- // Constants
1405
- static MESSAGE_TO_SIGN = MESSAGE_TO_SIGN;
1406
- static PROOF_CONTRACT_ADDRESS = PROOF_CONTRACT_ADDRESS;
1407
- static LOCAL_CONFIG = LOCAL_CONFIG;
1408
- }
1409
-
1410
- exports.Gun = Gun;
1411
- exports.default = Gun;
1412
- exports.GunEth = GunEth;
1413
- exports.deriveStealthAddress = deriveStealthAddress;
1414
- exports.generatePassword = generatePassword;
1415
- exports.generateRandomId = generateRandomId;
1416
- exports.getSigner = getSigner;
1417
- exports.gunToEthAccount = gunToEthAccount;
1418
- exports.initNodeEnvironment = initNodeEnvironment;