gun-eth 1.4.32 → 1.4.34

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,1369 +0,0 @@
1
- import Gun from 'gun';
2
- export { default } from 'gun';
3
- import SEA from 'gun/sea.js';
4
- import { ethers } from 'ethers';
5
- import { fileURLToPath } from 'url';
6
- import { dirname } from 'path';
7
- import { readFileSync } from 'fs';
8
-
9
- let contractAddresses$1 = {
10
- PROOF_OF_INTEGRITY_ADDRESS: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
11
- STEALTH_ANNOUNCER_ADDRESS: "0x5FbDB2315678afecb367f032d93F642f64180aa3"
12
- };
13
-
14
- if (typeof window === 'undefined') {
15
- const { fileURLToPath } = require('url');
16
- const { dirname } = require('path');
17
- const { readFileSync } = require('fs');
18
- const { join } = require('path');
19
-
20
- try {
21
- const __filename = fileURLToPath(import.meta.url);
22
- const __dirname = dirname(__filename);
23
- const rawdata = readFileSync(join(__dirname, 'contract-address.json'), 'utf8');
24
- contractAddresses$1 = JSON.parse(rawdata);
25
- console.log("Loaded contract addresses:", contractAddresses$1);
26
- } catch (error) {
27
- console.warn("Warning: contract-address.json not found or invalid");
28
- }
29
- }
30
-
31
- const LOCAL_CONFIG = {
32
- CHAIN_ID: 1337,
33
- PROOF_OF_INTEGRITY_ADDRESS: contractAddresses$1.PROOF_OF_INTEGRITY_ADDRESS,
34
- STEALTH_ANNOUNCER_ADDRESS: contractAddresses$1.STEALTH_ANNOUNCER_ADDRESS,
35
- RPC_URL: "http://127.0.0.1:8545",
36
- GUN_PEER: "http://localhost:8765/gun"
37
- };
38
-
39
- // Indirizzi di produzione per diverse chain
40
- const CHAIN_CONFIG = {
41
- optimismSepolia: {
42
- STEALTH_ANNOUNCER_ADDRESS: "0xD0F2e9DA59d2DFECFdE67CcF17300BB6093A72f8",
43
- PROOF_OF_INTEGRITY_ADDRESS: "0x...",
44
- RPC_URL: "https://sepolia.optimism.io",
45
- CHAIN_ID: 11155420
46
- },
47
- arbitrumSepolia: {
48
- STEALTH_ANNOUNCER_ADDRESS: "0x...",
49
- PROOF_OF_INTEGRITY_ADDRESS: "0x...",
50
- RPC_URL: "https://sepolia-rollup.arbitrum.io/rpc",
51
- CHAIN_ID: 421614
52
- },
53
- localhost: {
54
- RPC_URL: "http://127.0.0.1:8545",
55
- CHAIN_ID: 1337
56
- }
57
- };
58
-
59
- // Esporta gli indirizzi di default (Optimism Sepolia)
60
- CHAIN_CONFIG.optimismSepolia.STEALTH_ANNOUNCER_ADDRESS;
61
- CHAIN_CONFIG.optimismSepolia.PROOF_OF_INTEGRITY_ADDRESS;
62
-
63
- // Funzione per ottenere gli indirizzi corretti
64
- function getAddressesForChain(chainName) {
65
- let config;
66
-
67
- // Se è localhost, prova a caricare gli indirizzi locali
68
- if (chainName === 'localhost') {
69
- try {
70
- // Carica gli indirizzi dal file generato dal deploy locale
71
- const localAddresses = require('../config/contract-address.json');
72
- config = {
73
- ...CHAIN_CONFIG.localhost,
74
- ...localAddresses
75
- };
76
- console.log("Using local addresses:", config);
77
- return config;
78
- } catch (err) {
79
- console.warn('No local addresses found');
80
- throw new Error('No local addresses found. Did you run local deployment?');
81
- }
82
- }
83
-
84
- // Altrimenti usa gli indirizzi di produzione
85
- config = CHAIN_CONFIG[chainName];
86
- if (!config) {
87
- throw new Error(`Chain ${chainName} not supported. Supported chains: ${Object.keys(CHAIN_CONFIG).join(', ')}`);
88
- }
89
-
90
- return config;
91
- }
92
-
93
- // Funzione helper per verificare se siamo in ambiente locale
94
- function isLocalEnvironment() {
95
- return process.env.NODE_ENV === 'development' &&
96
- typeof window !== 'undefined' &&
97
- window.location.hostname === 'localhost';
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 PROOF_CONTRACT_ADDRESS;
438
- let rpcUrl = "";
439
- let privateKey = "";
440
-
441
- const MESSAGE_TO_SIGN = "Access GunDB with Ethereum";
442
-
443
- let contractAddresses = {
444
- PROOF_OF_INTEGRITY_ADDRESS: CHAIN_CONFIG.optimismSepolia.PROOF_OF_INTEGRITY_ADDRESS,
445
- STEALTH_ANNOUNCER_ADDRESS: CHAIN_CONFIG.optimismSepolia.STEALTH_ANNOUNCER_ADDRESS
446
- };
447
-
448
- // Solo per Node.js
449
- if (typeof window === 'undefined') {
450
- try {
451
- const __filename = fileURLToPath(import.meta.url);
452
- const __dirname = dirname(__filename);
453
-
454
- try {
455
- const rawdata = readFileSync(path.join(__dirname, 'contract-address.json'), 'utf8');
456
- contractAddresses = JSON.parse(rawdata);
457
- console.log('Loaded contract addresses:', contractAddresses);
458
- } catch (err) {
459
- console.warn('Warning: contract-address.json not found or invalid');
460
- }
461
- } catch (error) {
462
- console.error('Error loading Node.js modules:', error);
463
- }
464
- }
465
-
466
- // =============================================
467
- // UTILITY FUNCTIONS
468
- // =============================================
469
- /**
470
- * Generates a random node ID for GunDB
471
- * @returns {string} A random hexadecimal string
472
- */
473
- function generateRandomId() {
474
- const randomBytes = ethers.randomBytes(32);
475
- return ethers.hexlify(randomBytes).slice(2);
476
- }
477
-
478
- /**
479
- * Generates a password from a signature.
480
- * @param {string} signature - The signature to derive the password from.
481
- * @returns {string|null} The generated password or null if generation fails.
482
- */
483
- function generatePassword(signature) {
484
- try {
485
- const signatureBytes = ethers.toUtf8Bytes(signature);
486
- const hash = ethers.keccak256(signatureBytes);
487
- console.log("Generated password:", hash);
488
- return hash;
489
- } catch (error) {
490
- console.error("Error generating password:", error);
491
- return null;
492
- }
493
- }
494
-
495
- /**
496
- * Converts a Gun private key to an Ethereum account.
497
- * @param {string} gunPrivateKey - The Gun private key in base64url format.
498
- * @returns {Object} An object containing the Ethereum account and public key.
499
- */
500
- function gunToEthAccount(gunPrivateKey) {
501
- // Function to convert base64url to hex
502
- const base64UrlToHex = (base64url) => {
503
- const padding = "=".repeat((4 - (base64url.length % 4)) % 4);
504
- const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/") + padding;
505
- const binary = atob(base64);
506
- return Array.from(binary, (char) =>
507
- char.charCodeAt(0).toString(16).padStart(2, "0")
508
- ).join("");
509
- };
510
-
511
- // Convert Gun private key to hex format
512
- const hexPrivateKey = "0x" + base64UrlToHex(gunPrivateKey);
513
-
514
- // Create an Ethereum wallet from the private key
515
- const wallet = new ethers.Wallet(hexPrivateKey);
516
-
517
- // Get the public address (public key)
518
- const publicKey = wallet.address;
519
-
520
- return {
521
- account: wallet,
522
- publicKey: publicKey,
523
- privateKey: hexPrivateKey,
524
- };
525
- }
526
-
527
- /**
528
- * Gets an Ethereum signer based on current configuration
529
- * @returns {Promise<ethers.Signer>} The configured signer
530
- * @throws {Error} If no valid provider is found
531
- */
532
- const getSigner = async () => {
533
- if (rpcUrl && privateKey) {
534
- const provider = new ethers.JsonRpcProvider(rpcUrl, {
535
- chainId: LOCAL_CONFIG.CHAIN_ID,
536
- name: "localhost"
537
- });
538
- return new Wallet(privateKey, provider);
539
- } else if (
540
- typeof window !== "undefined" &&
541
- typeof window.ethereum !== "undefined"
542
- ) {
543
- await window.ethereum.request({ method: "eth_requestAccounts" });
544
- const provider = new ethers.BrowserProvider(window.ethereum);
545
- return provider.getSigner();
546
- } else {
547
- throw new Error("No valid Ethereum provider found");
548
- }
549
- };
550
-
551
- /**
552
- * Utility function to generate stealth address
553
- * @param {string} sharedSecret - The shared secret
554
- * @param {string} spendingPublicKey - The spending public key
555
- * @returns {Object} The stealth address and private key
556
- */
557
- function deriveStealthAddress(sharedSecret, spendingPublicKey) {
558
- try {
559
- const sharedSecretBytes = ethers.toUtf8Bytes(sharedSecret);
560
- const spendingPublicKeyBytes = ethers.toUtf8Bytes(spendingPublicKey);
561
-
562
- const stealthPrivateKey = ethers.keccak256(
563
- ethers.concat([
564
- sharedSecretBytes,
565
- spendingPublicKeyBytes
566
- ])
567
- );
568
-
569
- const stealthWallet = new Wallet(stealthPrivateKey);
570
-
571
- console.log("Debug deriveStealthAddress:", {
572
- sharedSecretHex: stealthPrivateKey,
573
- spendingPublicKey,
574
- stealthPrivateKey,
575
- stealthAddress: stealthWallet.address
576
- });
577
-
578
- return {
579
- stealthPrivateKey,
580
- stealthAddress: stealthWallet.address,
581
- wallet: stealthWallet
582
- };
583
- } catch (error) {
584
- console.error("Error in deriveStealthAddress:", error);
585
- throw error;
586
- }
587
- }
588
-
589
- // =============================================
590
- // BASIC GUN-ETH CHAIN METHODS
591
- // =============================================
592
-
593
- // Set the message to sign
594
- Gun.chain.MESSAGE_TO_SIGN = MESSAGE_TO_SIGN;
595
-
596
- /**
597
- * Sets standalone configuration for Gun.
598
- * @param {string} newRpcUrl - The new RPC URL.
599
- * @param {string} newPrivateKey - The new private key.
600
- * @returns {Gun} The Gun instance for chaining.
601
- */
602
- Gun.chain.setSigner = function (newRpcUrl, newPrivateKey) {
603
- rpcUrl = newRpcUrl;
604
- privateKey = newPrivateKey;
605
- console.log("Standalone configuration set");
606
- return this;
607
- };
608
-
609
- Gun.chain.getSigner = getSigner();
610
-
611
- /**
612
- * Verifies an Ethereum signature.
613
- * @param {string} message - The original message that was signed.
614
- * @param {string} signature - The signature to verify.
615
- * @returns {Promise<string|null>} The recovered address or null if verification fails.
616
- */
617
- Gun.chain.verifySignature = async function (message, signature) {
618
- try {
619
- const recoveredAddress = ethers.verifyMessage(message, signature);
620
- return recoveredAddress;
621
- } catch (error) {
622
- console.error("Error verifying signature:", error);
623
- return null;
624
- }
625
- };
626
-
627
- /**
628
- * Generates a password from a signature.
629
- * @param {string} signature - The signature to derive the password from.
630
- * @returns {string|null} The generated password or null if generation fails.
631
- */
632
- Gun.chain.generatePassword = function (signature) {
633
- return generatePassword(signature);
634
- };
635
-
636
- /**
637
- * Creates an Ethereum signature for a given message.
638
- * @param {string} message - The message to sign.
639
- * @returns {Promise<string|null>} The signature or null if signing fails.
640
- */
641
- Gun.chain.createSignature = async function (message) {
642
- try {
643
- // Check if message matches MESSAGE_TO_SIGN
644
- if (message !== MESSAGE_TO_SIGN) {
645
- throw new Error("Invalid message, valid message is: " + MESSAGE_TO_SIGN);
646
- }
647
- const signer = await getSigner();
648
- const signature = await signer.signMessage(message);
649
- console.log("Signature created:", signature);
650
- return signature;
651
- } catch (error) {
652
- console.error("Error creating signature:", error);
653
- return null;
654
- }
655
- };
656
-
657
- // =============================================
658
- // KEY PAIR MANAGEMENT
659
- // =============================================
660
- /**
661
- * Creates and stores an encrypted key pair for a given address.
662
- * @param {string} address - The Ethereum address to associate with the key pair.
663
- * @param {string} signature - The signature to use for encryption.
664
- * @returns {Promise<void>}
665
- */
666
- Gun.chain.createAndStoreEncryptedPair = async function (address, signature) {
667
- try {
668
- const gun = this;
669
- const pair = await SEA.pair();
670
- const v_pair = await SEA.pair();
671
- const s_pair = await SEA.pair();
672
- const password = generatePassword(signature);
673
-
674
- // Save original SEA pairs
675
- const encryptedPair = await SEA.encrypt(JSON.stringify(pair), password);
676
- const encryptedV_pair = await SEA.encrypt(JSON.stringify(v_pair), password);
677
- const encryptedS_pair = await SEA.encrypt(JSON.stringify(s_pair), password);
678
-
679
- // Convert only to get Ethereum addresses
680
- const viewingAccount = gunToEthAccount(v_pair.priv);
681
- const spendingAccount = gunToEthAccount(s_pair.priv);
682
-
683
- gun.get("gun-eth").get("users").get(address).put({
684
- pair: encryptedPair,
685
- v_pair: encryptedV_pair,
686
- s_pair: encryptedS_pair,
687
- publicKeys: {
688
- viewingPublicKey: v_pair.epub, // Use SEA encryption public key
689
- viewingPublicKey: v_pair.epub, // Use SEA encryption public key
690
- spendingPublicKey: spendingAccount.publicKey, // Use Ethereum address
691
- ethViewingAddress: viewingAccount.publicKey // Also save Ethereum address
692
- }
693
- });
694
-
695
- console.log("Encrypted pairs and public keys stored for:", address);
696
- } catch (error) {
697
- console.error("Error creating and storing encrypted pair:", error);
698
- throw error;
699
- }
700
- };
701
-
702
- /**
703
- * Retrieves and decrypts a stored key pair for a given address.
704
- * @param {string} address - The Ethereum address associated with the key pair.
705
- * @param {string} signature - The signature to use for decryption.
706
- * @returns {Promise<Object|null>} The decrypted key pair or null if retrieval fails.
707
- */
708
- Gun.chain.getAndDecryptPair = async function (address, signature) {
709
- try {
710
- const gun = this;
711
- const encryptedData = await gun
712
- .get("gun-eth")
713
- .get("users")
714
- .get(address)
715
- .get("pair")
716
- .then();
717
- if (!encryptedData) {
718
- throw new Error("No encrypted data found for this address");
719
- }
720
- const password = generatePassword(signature);
721
- const decryptedPair = await SEA.decrypt(encryptedData, password);
722
- console.log(decryptedPair);
723
- return decryptedPair;
724
- } catch (error) {
725
- console.error("Error retrieving and decrypting pair:", error);
726
- return null;
727
- }
728
- };
729
-
730
- // =============================================
731
- // PROOF OF INTEGRITY
732
- // =============================================
733
- /**
734
- * Proof of Integrity
735
- * @param {string} chain - The blockchain to use (e.g., "optimismSepolia").
736
- * @param {string} nodeId - The ID of the node to verify or write.
737
- * @param {Object} data - The data to write (if writing).
738
- * @param {Function} callback - Callback function to handle the result.
739
- * @returns {Gun} The Gun instance for chaining.
740
- */
741
- Gun.chain.proof = function (chain, nodeId, data, callback) {
742
- console.log("Proof plugin called with:", { chain, nodeId, data });
743
-
744
- if (typeof callback !== "function") {
745
- console.error("Callback must be a function");
746
- return this;
747
- }
748
-
749
- try {
750
- // Se siamo in localhost e in development, usa automaticamente la chain locale
751
- const targetChain = isLocalEnvironment() ? 'localhost' : chain;
752
- const chainConfig = getAddressesForChain(targetChain);
753
-
754
- console.log(`Using ${targetChain} configuration:`, chainConfig);
755
-
756
- // Usa gli indirizzi dalla configurazione
757
- const contract = new ethers.Contract(
758
- chainConfig.PROOF_OF_INTEGRITY_ADDRESS,
759
- PROOF_OF_INTEGRITY_ABI,
760
- signer
761
- );
762
-
763
- // Funzione per verificare on-chain
764
- const verifyOnChain = async (nodeId, contentHash) => {
765
- console.log("Verifying on chain:", { nodeId, contentHash });
766
- const signer = await getSigner();
767
- const contract = new Contract(
768
- PROOF_CONTRACT_ADDRESS,
769
- PROOF_OF_INTEGRITY_ABI,
770
- signer
771
- );
772
- const [isValid, timestamp, updater] = await contract.verifyData(
773
- ethers.toUtf8Bytes(nodeId),
774
- contentHash
775
- );
776
- console.log("Verification result:", { isValid, timestamp, updater });
777
- return { isValid, timestamp, updater };
778
- };
779
-
780
- // Funzione per scrivere on-chain
781
- const writeOnChain = async (nodeId, contentHash) => {
782
- console.log("Writing on chain:", { nodeId, contentHash });
783
- const signer = await getSigner();
784
- const contract = new Contract(
785
- PROOF_CONTRACT_ADDRESS,
786
- PROOF_OF_INTEGRITY_ABI,
787
- signer
788
- );
789
- const tx = await contract.updateData(
790
- toUtf8Bytes(nodeId),
791
- contentHash
792
- );
793
- console.log("Transaction sent:", tx.hash);
794
- const receipt = await tx.wait();
795
- console.log("Transaction confirmed:", receipt);
796
- return tx;
797
- };
798
-
799
- // Funzione per ottenere l'ultimo record
800
- const getLatestRecord = async (nodeId) => {
801
- const signer = await getSigner();
802
- const contract = new Contract(
803
- PROOF_CONTRACT_ADDRESS,
804
- PROOF_OF_INTEGRITY_ABI,
805
- signer
806
- );
807
- const [contentHash, timestamp, updater] = await contract.getLatestRecord(
808
- toUtf8Bytes(nodeId)
809
- );
810
- console.log("Latest record from blockchain:", {
811
- nodeId,
812
- contentHash,
813
- timestamp,
814
- updater,
815
- });
816
- return { contentHash, timestamp, updater };
817
- };
818
-
819
-
820
- if (nodeId && !data) {
821
- // Case 1: User passes only node
822
- gun.get(nodeId).once(async (existingData) => {
823
- if (!existingData) {
824
- if (callback) callback({ err: "Node not found in GunDB" });
825
- return;
826
- }
827
-
828
- console.log("existingData", existingData);
829
-
830
- // Use stored contentHash instead of recalculating
831
- const contentHash = existingData._contentHash;
832
- console.log("contentHash", contentHash);
833
-
834
- if (!contentHash) {
835
- if (callback) callback({ err: "No content hash found for this node" });
836
- return;
837
- }
838
-
839
- try {
840
- const { isValid, timestamp, updater } = await verifyOnChain(
841
- nodeId,
842
- contentHash
843
- );
844
- const latestRecord = await getLatestRecord(nodeId);
845
-
846
- if (isValid) {
847
- if (callback)
848
- callback({
849
- ok: true,
850
- message: "Data verified on blockchain",
851
- timestamp,
852
- updater,
853
- latestRecord,
854
- });
855
- } else {
856
- if (callback)
857
- callback({
858
- ok: false,
859
- message: "Data not verified on blockchain",
860
- latestRecord,
861
- });
862
- }
863
- } catch (error) {
864
- if (callback) callback({ err: error.message });
865
- }
866
- });
867
- } else if (data && !nodeId) {
868
- // Case 2: User passes only text (data)
869
- const newNodeId = generateRandomId();
870
- const dataString = JSON.stringify(data);
871
- const contentHash = ethers.keccak256(ethers.toUtf8Bytes(dataString));
872
-
873
- gun
874
- .get(newNodeId)
875
- .put({ ...data, _contentHash: contentHash }, async (ack) => {
876
- console.log("ack", ack);
877
- if (ack.err) {
878
- if (callback) callback({ err: "Error saving data to GunDB" });
879
- return;
880
- }
881
-
882
- try {
883
- const tx = await writeOnChain(newNodeId, contentHash);
884
- if (callback)
885
- callback({
886
- ok: true,
887
- message: "Data written to GunDB and blockchain",
888
- nodeId: newNodeId,
889
- txHash: tx.hash,
890
- });
891
- } catch (error) {
892
- if (callback) callback({ err: error.message });
893
- }
894
- });
895
- } else {
896
- if (callback)
897
- callback({
898
- err: "Invalid input. Provide either nodeId or data, not both.",
899
- });
900
- }
901
-
902
- return gun;
903
- } catch (error) {
904
- callback({ err: error.message });
905
- return this;
906
- }
907
- };
908
-
909
- // =============================================
910
- // STEALTH ADDRESS CORE FUNCTIONS
911
- // =============================================
912
- /**
913
- * Converts a Gun private key to an Ethereum account.
914
- * @param {string} gunPrivateKey - The Gun private key in base64url format.
915
- * @returns {Object} An object containing the Ethereum account and public key.
916
- */
917
- Gun.chain.gunToEthAccount = function (gunPrivateKey) {
918
- return gunToEthAccount(gunPrivateKey);
919
- };
920
-
921
- /**
922
- * Generate a stealth key and related key pairs
923
- * @param {string} recipientAddress - The recipient's Ethereum address
924
- * @param {string} signature - The sender's signature to access their keys
925
- * @returns {Promise<Object>} Object containing stealth addresses and keys
926
- */
927
- Gun.chain.generateStealthAddress = async function (recipientAddress, signature) {
928
- try {
929
- const gun = this;
930
-
931
- // Get recipient's public keys
932
- const recipientData = await gun
933
- .get("gun-eth")
934
- .get("users")
935
- .get(recipientAddress)
936
- .get("publicKeys")
937
- .then();
938
-
939
- if (!recipientData || !recipientData.viewingPublicKey || !recipientData.spendingPublicKey) {
940
- throw new Error("Recipient's public keys not found");
941
- }
942
-
943
- // Get sender's keys
944
- const senderAddress = await this.verifySignature(MESSAGE_TO_SIGN, signature);
945
- const password = generatePassword(signature);
946
-
947
- const senderData = await gun
948
- .get("gun-eth")
949
- .get("users")
950
- .get(senderAddress)
951
- .then();
952
-
953
- if (!senderData || !senderData.s_pair) {
954
- throw new Error("Sender's keys not found");
955
- }
956
-
957
- // Decrypt sender's spending pair
958
- let spendingKeyPair;
959
- try {
960
- const decryptedData = await SEA.decrypt(senderData.s_pair, password);
961
- spendingKeyPair = typeof decryptedData === 'string' ?
962
- JSON.parse(decryptedData) :
963
- decryptedData;
964
- } catch (error) {
965
- console.error("Error decrypting spending pair:", error);
966
- throw new Error("Unable to decrypt spending pair");
967
- }
968
-
969
- // Generate shared secret using SEA ECDH with encryption public key
970
- const sharedSecret = await SEA.secret(recipientData.viewingPublicKey, spendingKeyPair);
971
-
972
- if (!sharedSecret) {
973
- throw new Error("Unable to generate shared secret");
974
- }
975
-
976
- console.log("Generate shared secret:", sharedSecret);
977
-
978
- const { stealthAddress } = deriveStealthAddress(
979
- sharedSecret,
980
- recipientData.spendingPublicKey
981
- );
982
-
983
- return {
984
- stealthAddress,
985
- senderPublicKey: spendingKeyPair.epub, // Use encryption public key
986
- spendingPublicKey: recipientData.spendingPublicKey
987
- };
988
-
989
- } catch (error) {
990
- console.error("Error generating stealth address:", error);
991
- throw error;
992
- }
993
- };
994
-
995
- /**
996
- * Publish public keys needed to receive stealth payments
997
- * @param {string} signature - The signature to authenticate the user
998
- * @returns {Promise<void>}
999
- */
1000
- Gun.chain.publishStealthKeys = async function (signature) {
1001
- try {
1002
- const gun = this;
1003
- const address = await this.verifySignature(MESSAGE_TO_SIGN, signature);
1004
- const password = generatePassword(signature);
1005
-
1006
- // Get encrypted key pairs
1007
- const encryptedData = await gun
1008
- .get("gun-eth")
1009
- .get("users")
1010
- .get(address)
1011
- .then();
1012
-
1013
- if (!encryptedData || !encryptedData.v_pair || !encryptedData.s_pair) {
1014
- throw new Error("Keys not found");
1015
- }
1016
-
1017
- // Decrypt viewing and spending pairs
1018
- const viewingKeyPair = JSON.parse(
1019
- await SEA.decrypt(encryptedData.v_pair, password)
1020
- );
1021
- const spendingKeyPair = JSON.parse(
1022
- await SEA.decrypt(encryptedData.s_pair, password)
1023
- );
1024
-
1025
- const viewingAccount = gunToEthAccount(viewingKeyPair.priv);
1026
- const spendingAccount = gunToEthAccount(spendingKeyPair.priv);
1027
-
1028
- // Publish only public keys
1029
- gun.get("gun-eth").get("users").get(address).get("publicKeys").put({
1030
- viewingPublicKey: viewingAccount.publicKey,
1031
- spendingPublicKey: spendingAccount.publicKey,
1032
- });
1033
-
1034
- console.log("Stealth public keys published successfully");
1035
- } catch (error) {
1036
- console.error("Error publishing stealth keys:", error);
1037
- throw error;
1038
- }
1039
- };
1040
-
1041
- // =============================================
1042
- // STEALTH PAYMENT FUNCTIONS
1043
- // =============================================
1044
- /**
1045
- * Recover funds from a stealth address
1046
- * @param {string} stealthAddress - The stealth address to recover funds from
1047
- * @param {string} senderPublicKey - The sender's public key used to generate the address
1048
- * @param {string} signature - The signature to decrypt private keys
1049
- * @returns {Promise<Object>} Object containing wallet to access funds
1050
- */
1051
- Gun.chain.recoverStealthFunds = async function (
1052
- stealthAddress,
1053
- senderPublicKey,
1054
- signature,
1055
- spendingPublicKey
1056
- ) {
1057
- try {
1058
- const gun = this;
1059
- const password = generatePassword(signature);
1060
-
1061
- // Get own key pairs
1062
- const myAddress = await this.verifySignature(MESSAGE_TO_SIGN, signature);
1063
- const encryptedData = await gun
1064
- .get("gun-eth")
1065
- .get("users")
1066
- .get(myAddress)
1067
- .then();
1068
-
1069
- if (!encryptedData || !encryptedData.v_pair || !encryptedData.s_pair) {
1070
- throw new Error("Keys not found");
1071
- }
1072
-
1073
- // Decrypt viewing and spending pairs
1074
- let viewingKeyPair;
1075
- try {
1076
- const decryptedViewingData = await SEA.decrypt(encryptedData.v_pair, password);
1077
- viewingKeyPair = typeof decryptedViewingData === 'string' ?
1078
- JSON.parse(decryptedViewingData) :
1079
- decryptedViewingData;
1080
- } catch (error) {
1081
- console.error("Error decrypting keys:", error);
1082
- throw new Error("Unable to decrypt keys");
1083
- }
1084
-
1085
- // Generate shared secret using SEA ECDH
1086
- const sharedSecret = await SEA.secret(senderPublicKey, viewingKeyPair);
1087
-
1088
- if (!sharedSecret) {
1089
- throw new Error("Unable to generate shared secret");
1090
- }
1091
-
1092
- console.log("Recover shared secret:", sharedSecret);
1093
-
1094
- const { wallet, stealthAddress: recoveredAddress } = deriveStealthAddress(
1095
- sharedSecret,
1096
- spendingPublicKey
1097
- );
1098
-
1099
- // Verify address matches
1100
- if (recoveredAddress.toLowerCase() !== stealthAddress.toLowerCase()) {
1101
- console.error("Mismatch:", {
1102
- recovered: recoveredAddress,
1103
- expected: stealthAddress,
1104
- sharedSecret
1105
- });
1106
- throw new Error("Recovered stealth address does not match");
1107
- }
1108
-
1109
- return {
1110
- wallet,
1111
- address: recoveredAddress,
1112
- };
1113
- } catch (error) {
1114
- console.error("Error recovering stealth funds:", error);
1115
- throw error;
1116
- }
1117
- };
1118
-
1119
- /**
1120
- * Announce a stealth payment
1121
- * @param {string} stealthAddress - The generated stealth address
1122
- * @param {string} senderPublicKey - The sender's public key
1123
- * @param {string} spendingPublicKey - The spending public key
1124
- * @param {string} signature - The sender's signature
1125
- * @returns {Promise<void>}
1126
- */
1127
- Gun.chain.announceStealthPayment = async function (
1128
- stealthAddress,
1129
- senderPublicKey,
1130
- spendingPublicKey,
1131
- signature,
1132
- options = { onChain: false, chain: 'optimismSepolia' }
1133
- ) {
1134
- try {
1135
- const gun = this;
1136
- const senderAddress = await this.verifySignature(MESSAGE_TO_SIGN, signature);
1137
-
1138
- if (options.onChain) {
1139
- // On-chain announcement
1140
- const signer = await getSigner();
1141
- const chainConfig = getAddressesForChain(options.chain);
1142
- const contractAddress = chainConfig.STEALTH_ANNOUNCER_ADDRESS;
1143
-
1144
- console.log("Using contract address:", contractAddress);
1145
-
1146
- const contract = new Contract(
1147
- contractAddress,
1148
- STEALTH_ANNOUNCER_ABI,
1149
- signer
1150
- );
1151
-
1152
- // Get dev fee from contract
1153
- const devFee = await contract.devFee();
1154
- console.log("Dev fee:", devFee.toString());
1155
-
1156
- // Call contract
1157
- const tx = await contract.announcePayment(
1158
- senderPublicKey,
1159
- spendingPublicKey,
1160
- stealthAddress,
1161
- { value: devFee }
1162
- );
1163
-
1164
- console.log("Transaction sent:", tx.hash);
1165
- const receipt = await tx.wait();
1166
- console.log("Transaction confirmed:", receipt.hash);
1167
-
1168
- console.log("Stealth payment announced on-chain (dev fee paid)");
1169
- } else {
1170
- // Off-chain announcement (GunDB)
1171
- gun
1172
- .get("gun-eth")
1173
- .get("stealth-payments")
1174
- .set({
1175
- stealthAddress,
1176
- senderAddress,
1177
- senderPublicKey,
1178
- spendingPublicKey,
1179
- timestamp: Date.now(),
1180
- });
1181
- console.log("Stealth payment announced off-chain");
1182
- }
1183
- } catch (error) {
1184
- console.error("Error announcing stealth payment:", error);
1185
- console.error("Error details:", error.stack);
1186
- throw error;
1187
- }
1188
- };
1189
-
1190
- /**
1191
- * Get all stealth payments for an address
1192
- * @param {string} signature - The signature to authenticate the user
1193
- * @returns {Promise<Array>} List of stealth payments
1194
- */
1195
- Gun.chain.getStealthPayments = async function (signature, options = { source: 'both' }) {
1196
- try {
1197
- const payments = [];
1198
-
1199
- if (options.source === 'onChain' || options.source === 'both') {
1200
- const signer = await getSigner();
1201
- const chainConfig = getAddressesForChain(options.chain || 'optimismSepolia');
1202
- const contractAddress = chainConfig.STEALTH_ANNOUNCER_ADDRESS;
1203
-
1204
- const contract = new Contract(
1205
- contractAddress,
1206
- STEALTH_ANNOUNCER_ABI,
1207
- signer
1208
- );
1209
-
1210
- try {
1211
- // Get total number of announcements
1212
- const totalAnnouncements = await contract.getAnnouncementsCount();
1213
- const totalCount = Number(totalAnnouncements.toString());
1214
- console.log("Total on-chain announcements:", totalCount);
1215
-
1216
- if (totalCount > 0) {
1217
- // Get announcements in batches of 100
1218
- const batchSize = 100;
1219
- const lastIndex = totalCount - 1;
1220
-
1221
- for(let i = 0; i <= lastIndex; i += batchSize) {
1222
- const toIndex = Math.min(i + batchSize - 1, lastIndex);
1223
- const batch = await contract.getAnnouncementsInRange(i, toIndex);
1224
-
1225
- // For each announcement, try to decrypt
1226
- for(const announcement of batch) {
1227
- try {
1228
- // Verify announcement is valid
1229
- if (!announcement || !announcement.stealthAddress ||
1230
- !announcement.senderPublicKey || !announcement.spendingPublicKey) {
1231
- console.log("Invalid announcement:", announcement);
1232
- continue;
1233
- }
1234
-
1235
- // Try to recover funds to verify if announcement is for us
1236
- const recoveredWallet = await this.recoverStealthFunds(
1237
- announcement.stealthAddress,
1238
- announcement.senderPublicKey,
1239
- signature,
1240
- announcement.spendingPublicKey
1241
- );
1242
-
1243
- // If no errors thrown, announcement is for us
1244
- payments.push({
1245
- stealthAddress: announcement.stealthAddress,
1246
- senderPublicKey: announcement.senderPublicKey,
1247
- spendingPublicKey: announcement.spendingPublicKey,
1248
- timestamp: Number(announcement.timestamp),
1249
- source: 'onChain',
1250
- wallet: recoveredWallet
1251
- });
1252
-
1253
- } catch (e) {
1254
- // Not for us, continue
1255
- console.log(`Announcement not for us: ${announcement.stealthAddress}`);
1256
- continue;
1257
- }
1258
- }
1259
- }
1260
- }
1261
- } catch (error) {
1262
- console.error("Error retrieving on-chain announcements:", error);
1263
- }
1264
- }
1265
-
1266
- if (options.source === 'offChain' || options.source === 'both') {
1267
- // Get off-chain payments
1268
- const gun = this;
1269
- const offChainPayments = await new Promise((resolve) => {
1270
- const p = [];
1271
- gun
1272
- .get("gun-eth")
1273
- .get("stealth-payments")
1274
- .get(recipientAddress)
1275
- .map()
1276
- .once((payment, id) => {
1277
- if (payment?.stealthAddress) {
1278
- p.push({ ...payment, id, source: 'offChain' });
1279
- }
1280
- });
1281
- setTimeout(() => resolve(p), 2000);
1282
- });
1283
-
1284
- payments.push(...offChainPayments);
1285
- }
1286
-
1287
- console.log(`Found ${payments.length} stealth payments`);
1288
- return payments;
1289
- } catch (error) {
1290
- console.error("Error retrieving stealth payments:", error);
1291
- throw error;
1292
- }
1293
- };
1294
-
1295
- /**
1296
- * Clean up old stealth payments
1297
- * @param {string} recipientAddress - The recipient's address
1298
- * @returns {Promise<void>}
1299
- */
1300
- Gun.chain.cleanStealthPayments = async function(recipientAddress) {
1301
- try {
1302
- const gun = this;
1303
- const payments = await gun
1304
- .get("gun-eth")
1305
- .get("stealth-payments")
1306
- .get(recipientAddress)
1307
- .map()
1308
- .once()
1309
- .then();
1310
-
1311
- // Remove empty or invalid nodes
1312
- if (payments) {
1313
- Object.keys(payments).forEach(async (key) => {
1314
- const payment = payments[key];
1315
- if (!payment || !payment.stealthAddress || !payment.senderPublicKey || !payment.spendingPublicKey) {
1316
- await gun
1317
- .get("gun-eth")
1318
- .get("stealth-payments")
1319
- .get(recipientAddress)
1320
- .get(key)
1321
- .put(null);
1322
- }
1323
- });
1324
- }
1325
- } catch (error) {
1326
- console.error("Error cleaning stealth payments:", error);
1327
- }
1328
- };
1329
-
1330
- // =============================================
1331
- // EXPORTS
1332
- // =============================================
1333
-
1334
- // Crea una classe GunEth che contiene tutti i metodi e le utility
1335
- class GunEth {
1336
- // Static utility methods
1337
- static generateRandomId = generateRandomId;
1338
- static generatePassword = generatePassword;
1339
- static gunToEthAccount = gunToEthAccount;
1340
- static getSigner = getSigner;
1341
- static deriveStealthAddress = deriveStealthAddress;
1342
-
1343
- // Chain methods
1344
- static chainMethods = {
1345
- setSigner: Gun.chain.setSigner,
1346
- getSigner: Gun.chain.getSigner,
1347
- verifySignature: Gun.chain.verifySignature,
1348
- generatePassword: Gun.chain.generatePassword,
1349
- createSignature: Gun.chain.createSignature,
1350
- createAndStoreEncryptedPair: Gun.chain.createAndStoreEncryptedPair,
1351
- getAndDecryptPair: Gun.chain.getAndDecryptPair,
1352
- proof: Gun.chain.proof,
1353
- gunToEthAccount: Gun.chain.gunToEthAccount,
1354
- generateStealthAddress: Gun.chain.generateStealthAddress,
1355
- publishStealthKeys: Gun.chain.publishStealthKeys,
1356
- recoverStealthFunds: Gun.chain.recoverStealthFunds,
1357
- announceStealthPayment: Gun.chain.announceStealthPayment,
1358
- getStealthPayments: Gun.chain.getStealthPayments,
1359
- cleanStealthPayments: Gun.chain.cleanStealthPayments
1360
- };
1361
-
1362
- // Constants
1363
- static MESSAGE_TO_SIGN = MESSAGE_TO_SIGN;
1364
- static PROOF_CONTRACT_ADDRESS = PROOF_CONTRACT_ADDRESS;
1365
- static LOCAL_CONFIG = LOCAL_CONFIG;
1366
- }
1367
-
1368
- export { GunEth, MESSAGE_TO_SIGN, deriveStealthAddress, generatePassword, generateRandomId, getSigner, gunToEthAccount };
1369
- //# sourceMappingURL=gun-eth.esm.js.map