gun-eth 1.4.23 → 1.4.24

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