gun-eth 1.4.24 → 1.4.25

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