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