gun-eth 1.3.4 → 1.3.5

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gun-eth",
3
- "version": "1.3.4",
3
+ "version": "1.3.5",
4
4
  "description": "A GunDB plugin for Ethereum, and Web3",
5
5
  "main": "./src/index.js",
6
6
  "scripts": {
package/src/index.js CHANGED
@@ -1,5 +1,533 @@
1
- if (typeof window === 'undefined') {
2
- module.exports = require('./node/gun-eth-node.js');
3
- } else {
4
- module.exports = require('./browser/gun-eth-browser.js');
5
- }
1
+ (function (root, factory) {
2
+ if (typeof define === "function" && define.amd) {
3
+ define(["gun", "gun/sea", "ethers"], factory);
4
+ } else if (typeof module === "object" && module.exports) {
5
+ module.exports = factory(
6
+ require("gun/gun"),
7
+ require("gun/sea"),
8
+ require("ethers")
9
+ );
10
+ } else {
11
+ factory(root.Gun, root.SEA, root.ethers);
12
+ }
13
+ })(typeof self !== "undefined" ? self : this, function (Gun, SEA, ethers) {
14
+ console.log("Factory del plugin Gun-Eth chiamata");
15
+
16
+ const MESSAGE_TO_SIGN = "Accesso a GunDB con Ethereum";
17
+
18
+ // Funzione per verificare se ethers è disponibile
19
+ function checkEthers() {
20
+ if (typeof ethers === "undefined") {
21
+ console.error(
22
+ "Ethers.js non è disponibile. Assicurati che sia caricato prima di questo script."
23
+ );
24
+ return false;
25
+ }
26
+ console.log("Ethers version:", ethers.version);
27
+ return true;
28
+ }
29
+
30
+ // Global variables
31
+ let SHINE_ABI = [
32
+ {
33
+ anonymous: false,
34
+ inputs: [
35
+ {
36
+ indexed: true,
37
+ internalType: "bytes",
38
+ name: "nodeId",
39
+ type: "bytes",
40
+ },
41
+ {
42
+ indexed: false,
43
+ internalType: "bytes32",
44
+ name: "contentHash",
45
+ type: "bytes32",
46
+ },
47
+ {
48
+ indexed: false,
49
+ internalType: "address",
50
+ name: "updater",
51
+ type: "address",
52
+ },
53
+ ],
54
+ name: "DataUpdated",
55
+ type: "event",
56
+ },
57
+ {
58
+ inputs: [
59
+ {
60
+ internalType: "bytes[]",
61
+ name: "nodeIds",
62
+ type: "bytes[]",
63
+ },
64
+ {
65
+ internalType: "bytes32[]",
66
+ name: "contentHashes",
67
+ type: "bytes32[]",
68
+ },
69
+ ],
70
+ name: "batchUpdateData",
71
+ outputs: [],
72
+ stateMutability: "nonpayable",
73
+ type: "function",
74
+ },
75
+ {
76
+ inputs: [
77
+ {
78
+ internalType: "bytes",
79
+ name: "nodeId",
80
+ type: "bytes",
81
+ },
82
+ ],
83
+ name: "getLatestRecord",
84
+ outputs: [
85
+ {
86
+ internalType: "bytes32",
87
+ name: "",
88
+ type: "bytes32",
89
+ },
90
+ {
91
+ internalType: "uint256",
92
+ name: "",
93
+ type: "uint256",
94
+ },
95
+ {
96
+ internalType: "address",
97
+ name: "",
98
+ type: "address",
99
+ },
100
+ ],
101
+ stateMutability: "view",
102
+ type: "function",
103
+ },
104
+ {
105
+ inputs: [
106
+ {
107
+ internalType: "bytes",
108
+ name: "",
109
+ type: "bytes",
110
+ },
111
+ ],
112
+ name: "nodeData",
113
+ outputs: [
114
+ {
115
+ internalType: "bytes32",
116
+ name: "contentHash",
117
+ type: "bytes32",
118
+ },
119
+ {
120
+ internalType: "uint256",
121
+ name: "timestamp",
122
+ type: "uint256",
123
+ },
124
+ {
125
+ internalType: "address",
126
+ name: "updater",
127
+ type: "address",
128
+ },
129
+ ],
130
+ stateMutability: "view",
131
+ type: "function",
132
+ },
133
+ {
134
+ inputs: [
135
+ {
136
+ internalType: "bytes",
137
+ name: "nodeId",
138
+ type: "bytes",
139
+ },
140
+ {
141
+ internalType: "bytes32",
142
+ name: "contentHash",
143
+ type: "bytes32",
144
+ },
145
+ ],
146
+ name: "updateData",
147
+ outputs: [],
148
+ stateMutability: "nonpayable",
149
+ type: "function",
150
+ },
151
+ {
152
+ inputs: [
153
+ {
154
+ internalType: "bytes",
155
+ name: "nodeId",
156
+ type: "bytes",
157
+ },
158
+ {
159
+ internalType: "bytes32",
160
+ name: "contentHash",
161
+ type: "bytes32",
162
+ },
163
+ ],
164
+ name: "verifyData",
165
+ outputs: [
166
+ {
167
+ internalType: "bool",
168
+ name: "",
169
+ type: "bool",
170
+ },
171
+ {
172
+ internalType: "uint256",
173
+ name: "",
174
+ type: "uint256",
175
+ },
176
+ {
177
+ internalType: "address",
178
+ name: "",
179
+ type: "address",
180
+ },
181
+ ],
182
+ stateMutability: "view",
183
+ type: "function",
184
+ },
185
+ ];
186
+
187
+ let SHINE_OPTIMISM_SEPOLIA = "0x43D838b683F772F08f321E5FA265ad3e333BE9C2";
188
+ let SHINE_CONTRACT_ADDRESS;
189
+ let rpcUrl = "";
190
+ let privateKey = "";
191
+
192
+ /**
193
+ * Funzione per ottenere il signer
194
+ * @returns {Promise<ethers.Signer>} Il signer.
195
+ */
196
+ const getSigner = async () => {
197
+ if (rpcUrl && privateKey) {
198
+ // Modalità standalone
199
+ const provider = new ethers.JsonRpcProvider(rpcUrl);
200
+ return new ethers.Wallet(privateKey, provider);
201
+ } else if (
202
+ typeof window !== "undefined" &&
203
+ typeof window.ethereum !== "undefined"
204
+ ) {
205
+ // Modalità browser
206
+ await window.ethereum.request({ method: "eth_requestAccounts" });
207
+ const provider = new ethers.BrowserProvider(window.ethereum);
208
+ return provider.getSigner();
209
+ } else {
210
+ throw new Error("No valid Ethereum provider found");
211
+ }
212
+ };
213
+
214
+ /**
215
+ * Sets standalone configuration for Gun.
216
+ * @param {string} newRpcUrl - The new RPC URL.
217
+ * @param {string} newPrivateKey - The new private key.
218
+ * @returns {Gun} The Gun instance for chaining.
219
+ */
220
+ Gun.chain.setStandaloneConfig = function (newRpcUrl, newPrivateKey) {
221
+ rpcUrl = newRpcUrl;
222
+ privateKey = newPrivateKey;
223
+ console.log("Standalone configuration set");
224
+ return this;
225
+ };
226
+
227
+ /**
228
+ * Verifies an Ethereum signature.
229
+ * @param {string} message - The original message that was signed.
230
+ * @param {string} signature - The signature to verify.
231
+ * @returns {Promise<string|null>} The recovered address or null if verification fails.
232
+ */
233
+ Gun.chain.verifySignature = async function (message, signature) {
234
+ try {
235
+ const recoveredAddress = ethers.verifyMessage(message, signature);
236
+ return recoveredAddress;
237
+ } catch (error) {
238
+ console.error("Error verifying signature:", error);
239
+ return null;
240
+ }
241
+ };
242
+
243
+ /**
244
+ * Generates a password from a signature.
245
+ * @param {string} signature - The signature to derive the password from.
246
+ * @returns {string|null} The generated password or null if generation fails.
247
+ */
248
+ Gun.chain.generatePassword = function (signature) {
249
+ try {
250
+ const hexSignature = ethers.hexlify(signature);
251
+ const hash = ethers.keccak256(hexSignature);
252
+ console.log("Generated password:", hash);
253
+ return hash;
254
+ } catch (error) {
255
+ console.error("Error generating password:", error);
256
+ return null;
257
+ }
258
+ };
259
+
260
+ /**
261
+ * Creates an Ethereum signature for a given message.
262
+ * @param {string} message - The message to sign.
263
+ * @returns {Promise<string|null>} The signature or null if signing fails.
264
+ */
265
+ Gun.chain.createSignature = async function (message) {
266
+ try {
267
+ // Verifica se il messaggio è uguale a MESSAGE_TO_SIGN
268
+ if (message !== MESSAGE_TO_SIGN) {
269
+ throw new Error(
270
+ "Invalid message, valid message is: " + MESSAGE_TO_SIGN
271
+ );
272
+ }
273
+ const signer = await getSigner();
274
+ const signature = await signer.signMessage(message);
275
+ console.log("Signature created:", signature);
276
+ return signature;
277
+ } catch (error) {
278
+ console.error("Error creating signature:", error);
279
+ return null;
280
+ }
281
+ };
282
+
283
+ /**
284
+ * Creates and stores an encrypted key pair for a given address.
285
+ * @param {string} address - The Ethereum address to associate with the key pair.
286
+ * @param {string} signature - The signature to use for encryption.
287
+ * @returns {Promise<void>}
288
+ */
289
+ Gun.chain.createAndStoreEncryptedPair = async function (address, signature) {
290
+ try {
291
+ const gun = this;
292
+ const pair = await SEA.pair();
293
+ const encryptedPair = await SEA.encrypt(JSON.stringify(pair), signature);
294
+ await gun.get("gun-eth").get("users").get(address).put({ encryptedPair });
295
+ console.log("Encrypted pair stored for:", address);
296
+ } catch (error) {
297
+ console.error("Error creating and storing encrypted pair:", error);
298
+ }
299
+ };
300
+
301
+ /**
302
+ * Retrieves and decrypts a stored key pair for a given address.
303
+ * @param {string} address - The Ethereum address associated with the key pair.
304
+ * @param {string} signature - The signature to use for decryption.
305
+ * @returns {Promise<Object|null>} The decrypted key pair or null if retrieval fails.
306
+ */
307
+ Gun.chain.getAndDecryptPair = async function (address, signature) {
308
+ try {
309
+ const gun = this;
310
+ const encryptedData = await gun
311
+ .get("gun-eth")
312
+ .get("users")
313
+ .get(address)
314
+ .get("encryptedPair")
315
+ .then();
316
+ if (!encryptedData) {
317
+ throw new Error("No encrypted data found for this address");
318
+ }
319
+ const decryptedPair = await SEA.decrypt(encryptedData, signature);
320
+ console.log(decryptedPair);
321
+ return decryptedPair;
322
+ } catch (error) {
323
+ console.error("Error retrieving and decrypting pair:", error);
324
+ return null;
325
+ }
326
+ };
327
+
328
+ /**
329
+ * SHINE (Secure Hybrid Information and Network Environment) functionality.
330
+ * @param {string} chain - The blockchain to use (e.g., "optimismSepolia").
331
+ * @param {string} nodeId - The ID of the node to verify or write.
332
+ * @param {Object} data - The data to write (if writing).
333
+ * @param {Function} callback - Callback function to handle the result.
334
+ * @returns {Gun} The Gun instance for chaining.
335
+ */
336
+ Gun.chain.shine = function (chain, nodeId, data, callback) {
337
+ console.log("SHINE plugin called with:", { chain, nodeId, data });
338
+
339
+ if (!checkEthers()) {
340
+ if (callback) callback({ err: "Ethers.js non è disponibile" });
341
+ return this;
342
+ }
343
+
344
+ if (typeof callback !== "function") {
345
+ console.error("Callback must be a function");
346
+ return this;
347
+ }
348
+
349
+ const gun = this;
350
+
351
+ // Seleziona l'indirizzo basato sulla catena
352
+ if (chain === "optimismSepolia") {
353
+ SHINE_CONTRACT_ADDRESS = SHINE_OPTIMISM_SEPOLIA;
354
+ } else {
355
+ throw new Error("Chain not supported");
356
+ }
357
+
358
+ // Funzione per verificare on-chain
359
+ const verifyOnChain = async (nodeId, contentHash) => {
360
+ console.log("Verifying on chain:", { nodeId, contentHash });
361
+ const signer = await getSigner();
362
+ const contract = new ethers.Contract(
363
+ SHINE_CONTRACT_ADDRESS,
364
+ SHINE_ABI,
365
+ signer
366
+ );
367
+ const [isValid, timestamp, updater] = await contract.verifyData(
368
+ ethers.toUtf8Bytes(nodeId),
369
+ contentHash
370
+ );
371
+ console.log("Verification result:", { isValid, timestamp, updater });
372
+ return { isValid, timestamp, updater };
373
+ };
374
+
375
+ // Funzione per scrivere on-chain
376
+ const writeOnChain = async (nodeId, contentHash) => {
377
+ console.log("Writing on chain:", { nodeId, contentHash });
378
+ const signer = await getSigner();
379
+ const contract = new ethers.Contract(
380
+ SHINE_CONTRACT_ADDRESS,
381
+ SHINE_ABI,
382
+ signer
383
+ );
384
+ const tx = await contract.updateData(
385
+ ethers.toUtf8Bytes(nodeId),
386
+ contentHash
387
+ );
388
+ console.log("Transaction sent:", tx.hash);
389
+ const receipt = await tx.wait();
390
+ console.log("Transaction confirmed:", receipt);
391
+ return tx;
392
+ };
393
+
394
+ // Nuova funzione per ottenere l'ultimo record dalla blockchain
395
+ const getLatestRecord = async (nodeId) => {
396
+ const signer = await getSigner();
397
+ const contract = new ethers.Contract(
398
+ SHINE_CONTRACT_ADDRESS,
399
+ SHINE_ABI,
400
+ signer
401
+ );
402
+ const [contentHash, timestamp, updater] = await contract.getLatestRecord(
403
+ ethers.toUtf8Bytes(nodeId)
404
+ );
405
+ console.log("Latest record from blockchain:", {
406
+ nodeId,
407
+ contentHash,
408
+ timestamp,
409
+ updater,
410
+ });
411
+ return { contentHash, timestamp, updater };
412
+ };
413
+
414
+ // Processo SHINE
415
+ if (nodeId && !data) {
416
+ // Caso 1: Utente passa solo il nodo
417
+ gun.get(nodeId).once(async (existingData) => {
418
+ if (!existingData) {
419
+ if (callback) callback({ err: "Node not found in GunDB" });
420
+ return;
421
+ }
422
+
423
+ console.log("existingData", existingData);
424
+
425
+ // Usa il contentHash memorizzato invece di ricalcolarlo
426
+ const contentHash = existingData._contentHash;
427
+ console.log("contentHash", contentHash);
428
+
429
+ if (!contentHash) {
430
+ if (callback)
431
+ callback({ err: "No content hash found for this node" });
432
+ return;
433
+ }
434
+
435
+ try {
436
+ const { isValid, timestamp, updater } = await verifyOnChain(
437
+ nodeId,
438
+ contentHash
439
+ );
440
+ const latestRecord = await getLatestRecord(nodeId);
441
+
442
+ if (isValid) {
443
+ if (callback)
444
+ callback({
445
+ ok: true,
446
+ message: "Data verified on blockchain",
447
+ timestamp,
448
+ updater,
449
+ latestRecord,
450
+ });
451
+ } else {
452
+ if (callback)
453
+ callback({
454
+ ok: false,
455
+ message: "Data not verified on blockchain",
456
+ latestRecord,
457
+ });
458
+ }
459
+ } catch (error) {
460
+ if (callback) callback({ err: error.message });
461
+ }
462
+ });
463
+ } else if (data && !nodeId) {
464
+ // Caso 2: Utente passa solo il testo (data)
465
+ const newNodeId = Gun.text.random();
466
+ const dataString = JSON.stringify(data);
467
+ const contentHash = ethers.keccak256(ethers.toUtf8Bytes(dataString));
468
+
469
+ gun
470
+ .get(newNodeId)
471
+ .put({ ...data, _contentHash: contentHash }, async (ack) => {
472
+ console.log("ack", ack);
473
+ if (ack.err) {
474
+ if (callback) callback({ err: "Error saving data to GunDB" });
475
+ return;
476
+ }
477
+
478
+ try {
479
+ const tx = await writeOnChain(newNodeId, contentHash);
480
+ if (callback)
481
+ callback({
482
+ ok: true,
483
+ message: "Data written to GunDB and blockchain",
484
+ nodeId: newNodeId,
485
+ txHash: tx.hash,
486
+ });
487
+ } catch (error) {
488
+ if (callback) callback({ err: error.message });
489
+ }
490
+ });
491
+ } else {
492
+ if (callback)
493
+ callback({
494
+ err: "Invalid input. Provide either nodeId or data, not both.",
495
+ });
496
+ }
497
+
498
+ return gun;
499
+ };
500
+
501
+ /**
502
+ * Converts a Gun private key to an Ethereum account.
503
+ * @param {string} gunPrivateKey - The Gun private key in base64url format.
504
+ * @returns {Object} An object containing the Ethereum account and public key.
505
+ */
506
+ Gun.chain.gunToEthAccount = function (gunPrivateKey) {
507
+ // Function to convert base64url to hex
508
+ const base64UrlToHex = (base64url) => {
509
+ const padding = "=".repeat((4 - (base64url.length % 4)) % 4);
510
+ const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/") + padding;
511
+ const binary = atob(base64);
512
+ return Array.from(binary, (char) =>
513
+ char.charCodeAt(0).toString(16).padStart(2, "0")
514
+ ).join("");
515
+ };
516
+
517
+ // Convert Gun private key to hex format
518
+ const hexPrivateKey = "0x" + base64UrlToHex(gunPrivateKey);
519
+
520
+ // Create an Ethereum wallet from the private key
521
+ const wallet = new ethers.Wallet(hexPrivateKey);
522
+
523
+ // Get the public address (public key)
524
+ const publicKey = wallet.address;
525
+
526
+ return {
527
+ account: wallet,
528
+ publicKey: publicKey,
529
+ };
530
+ };
531
+
532
+ console.log("Plugin Gun-Eth successfully loaded");
533
+ });
@@ -1,533 +0,0 @@
1
- (function (root, factory) {
2
- if (typeof define === "function" && define.amd) {
3
- define(["gun", "gun/sea", "ethers"], factory);
4
- } else if (typeof module === "object" && module.exports) {
5
- module.exports = factory(
6
- require("gun/gun"),
7
- require("gun/sea"),
8
- require("ethers")
9
- );
10
- } else {
11
- factory(root.Gun, root.SEA, root.ethers);
12
- }
13
- })(typeof self !== "undefined" ? self : this, function (Gun, SEA, ethers) {
14
- console.log("Factory del plugin Gun-Eth chiamata");
15
-
16
- const MESSAGE_TO_SIGN = "Accesso a GunDB con Ethereum";
17
-
18
- // Funzione per verificare se ethers è disponibile
19
- function checkEthers() {
20
- if (typeof ethers === "undefined") {
21
- console.error(
22
- "Ethers.js non è disponibile. Assicurati che sia caricato prima di questo script."
23
- );
24
- return false;
25
- }
26
- console.log("Ethers version:", ethers.version);
27
- return true;
28
- }
29
-
30
- // Global variables
31
- let SHINE_ABI = [
32
- {
33
- anonymous: false,
34
- inputs: [
35
- {
36
- indexed: true,
37
- internalType: "bytes",
38
- name: "nodeId",
39
- type: "bytes",
40
- },
41
- {
42
- indexed: false,
43
- internalType: "bytes32",
44
- name: "contentHash",
45
- type: "bytes32",
46
- },
47
- {
48
- indexed: false,
49
- internalType: "address",
50
- name: "updater",
51
- type: "address",
52
- },
53
- ],
54
- name: "DataUpdated",
55
- type: "event",
56
- },
57
- {
58
- inputs: [
59
- {
60
- internalType: "bytes[]",
61
- name: "nodeIds",
62
- type: "bytes[]",
63
- },
64
- {
65
- internalType: "bytes32[]",
66
- name: "contentHashes",
67
- type: "bytes32[]",
68
- },
69
- ],
70
- name: "batchUpdateData",
71
- outputs: [],
72
- stateMutability: "nonpayable",
73
- type: "function",
74
- },
75
- {
76
- inputs: [
77
- {
78
- internalType: "bytes",
79
- name: "nodeId",
80
- type: "bytes",
81
- },
82
- ],
83
- name: "getLatestRecord",
84
- outputs: [
85
- {
86
- internalType: "bytes32",
87
- name: "",
88
- type: "bytes32",
89
- },
90
- {
91
- internalType: "uint256",
92
- name: "",
93
- type: "uint256",
94
- },
95
- {
96
- internalType: "address",
97
- name: "",
98
- type: "address",
99
- },
100
- ],
101
- stateMutability: "view",
102
- type: "function",
103
- },
104
- {
105
- inputs: [
106
- {
107
- internalType: "bytes",
108
- name: "",
109
- type: "bytes",
110
- },
111
- ],
112
- name: "nodeData",
113
- outputs: [
114
- {
115
- internalType: "bytes32",
116
- name: "contentHash",
117
- type: "bytes32",
118
- },
119
- {
120
- internalType: "uint256",
121
- name: "timestamp",
122
- type: "uint256",
123
- },
124
- {
125
- internalType: "address",
126
- name: "updater",
127
- type: "address",
128
- },
129
- ],
130
- stateMutability: "view",
131
- type: "function",
132
- },
133
- {
134
- inputs: [
135
- {
136
- internalType: "bytes",
137
- name: "nodeId",
138
- type: "bytes",
139
- },
140
- {
141
- internalType: "bytes32",
142
- name: "contentHash",
143
- type: "bytes32",
144
- },
145
- ],
146
- name: "updateData",
147
- outputs: [],
148
- stateMutability: "nonpayable",
149
- type: "function",
150
- },
151
- {
152
- inputs: [
153
- {
154
- internalType: "bytes",
155
- name: "nodeId",
156
- type: "bytes",
157
- },
158
- {
159
- internalType: "bytes32",
160
- name: "contentHash",
161
- type: "bytes32",
162
- },
163
- ],
164
- name: "verifyData",
165
- outputs: [
166
- {
167
- internalType: "bool",
168
- name: "",
169
- type: "bool",
170
- },
171
- {
172
- internalType: "uint256",
173
- name: "",
174
- type: "uint256",
175
- },
176
- {
177
- internalType: "address",
178
- name: "",
179
- type: "address",
180
- },
181
- ],
182
- stateMutability: "view",
183
- type: "function",
184
- },
185
- ];
186
-
187
- let SHINE_OPTIMISM_SEPOLIA = "0x43D838b683F772F08f321E5FA265ad3e333BE9C2";
188
- let SHINE_CONTRACT_ADDRESS;
189
- let rpcUrl = "";
190
- let privateKey = "";
191
-
192
- /**
193
- * Funzione per ottenere il signer
194
- * @returns {Promise<ethers.Signer>} Il signer.
195
- */
196
- const getSigner = async () => {
197
- if (rpcUrl && privateKey) {
198
- // Modalità standalone
199
- const provider = new ethers.JsonRpcProvider(rpcUrl);
200
- return new ethers.Wallet(privateKey, provider);
201
- } else if (
202
- typeof window !== "undefined" &&
203
- typeof window.ethereum !== "undefined"
204
- ) {
205
- // Modalità browser
206
- await window.ethereum.request({ method: "eth_requestAccounts" });
207
- const provider = new ethers.BrowserProvider(window.ethereum);
208
- return provider.getSigner();
209
- } else {
210
- throw new Error("No valid Ethereum provider found");
211
- }
212
- };
213
-
214
- /**
215
- * Sets standalone configuration for Gun.
216
- * @param {string} newRpcUrl - The new RPC URL.
217
- * @param {string} newPrivateKey - The new private key.
218
- * @returns {Gun} The Gun instance for chaining.
219
- */
220
- Gun.chain.setStandaloneConfig = function (newRpcUrl, newPrivateKey) {
221
- rpcUrl = newRpcUrl;
222
- privateKey = newPrivateKey;
223
- console.log("Standalone configuration set");
224
- return this;
225
- };
226
-
227
- /**
228
- * Verifies an Ethereum signature.
229
- * @param {string} message - The original message that was signed.
230
- * @param {string} signature - The signature to verify.
231
- * @returns {Promise<string|null>} The recovered address or null if verification fails.
232
- */
233
- Gun.chain.verifySignature = async function (message, signature) {
234
- try {
235
- const recoveredAddress = ethers.verifyMessage(message, signature);
236
- return recoveredAddress;
237
- } catch (error) {
238
- console.error("Error verifying signature:", error);
239
- return null;
240
- }
241
- };
242
-
243
- /**
244
- * Generates a password from a signature.
245
- * @param {string} signature - The signature to derive the password from.
246
- * @returns {string|null} The generated password or null if generation fails.
247
- */
248
- Gun.chain.generatePassword = function (signature) {
249
- try {
250
- const hexSignature = ethers.hexlify(signature);
251
- const hash = ethers.keccak256(hexSignature);
252
- console.log("Generated password:", hash);
253
- return hash;
254
- } catch (error) {
255
- console.error("Error generating password:", error);
256
- return null;
257
- }
258
- };
259
-
260
- /**
261
- * Creates an Ethereum signature for a given message.
262
- * @param {string} message - The message to sign.
263
- * @returns {Promise<string|null>} The signature or null if signing fails.
264
- */
265
- Gun.chain.createSignature = async function (message) {
266
- try {
267
- // Verifica se il messaggio è uguale a MESSAGE_TO_SIGN
268
- if (message !== MESSAGE_TO_SIGN) {
269
- throw new Error(
270
- "Invalid message, valid message is: " + MESSAGE_TO_SIGN
271
- );
272
- }
273
- const signer = await getSigner();
274
- const signature = await signer.signMessage(message);
275
- console.log("Signature created:", signature);
276
- return signature;
277
- } catch (error) {
278
- console.error("Error creating signature:", error);
279
- return null;
280
- }
281
- };
282
-
283
- /**
284
- * Creates and stores an encrypted key pair for a given address.
285
- * @param {string} address - The Ethereum address to associate with the key pair.
286
- * @param {string} signature - The signature to use for encryption.
287
- * @returns {Promise<void>}
288
- */
289
- Gun.chain.createAndStoreEncryptedPair = async function (address, signature) {
290
- try {
291
- const gun = this;
292
- const pair = await SEA.pair();
293
- const encryptedPair = await SEA.encrypt(JSON.stringify(pair), signature);
294
- await gun.get("gun-eth").get("users").get(address).put({ encryptedPair });
295
- console.log("Encrypted pair stored for:", address);
296
- } catch (error) {
297
- console.error("Error creating and storing encrypted pair:", error);
298
- }
299
- };
300
-
301
- /**
302
- * Retrieves and decrypts a stored key pair for a given address.
303
- * @param {string} address - The Ethereum address associated with the key pair.
304
- * @param {string} signature - The signature to use for decryption.
305
- * @returns {Promise<Object|null>} The decrypted key pair or null if retrieval fails.
306
- */
307
- Gun.chain.getAndDecryptPair = async function (address, signature) {
308
- try {
309
- const gun = this;
310
- const encryptedData = await gun
311
- .get("gun-eth")
312
- .get("users")
313
- .get(address)
314
- .get("encryptedPair")
315
- .then();
316
- if (!encryptedData) {
317
- throw new Error("No encrypted data found for this address");
318
- }
319
- const decryptedPair = await SEA.decrypt(encryptedData, signature);
320
- console.log(decryptedPair);
321
- return decryptedPair;
322
- } catch (error) {
323
- console.error("Error retrieving and decrypting pair:", error);
324
- return null;
325
- }
326
- };
327
-
328
- /**
329
- * SHINE (Secure Hybrid Information and Network Environment) functionality.
330
- * @param {string} chain - The blockchain to use (e.g., "optimismSepolia").
331
- * @param {string} nodeId - The ID of the node to verify or write.
332
- * @param {Object} data - The data to write (if writing).
333
- * @param {Function} callback - Callback function to handle the result.
334
- * @returns {Gun} The Gun instance for chaining.
335
- */
336
- Gun.chain.shine = function (chain, nodeId, data, callback) {
337
- console.log("SHINE plugin called with:", { chain, nodeId, data });
338
-
339
- if (!checkEthers()) {
340
- if (callback) callback({ err: "Ethers.js non è disponibile" });
341
- return this;
342
- }
343
-
344
- if (typeof callback !== "function") {
345
- console.error("Callback must be a function");
346
- return this;
347
- }
348
-
349
- const gun = this;
350
-
351
- // Seleziona l'indirizzo basato sulla catena
352
- if (chain === "optimismSepolia") {
353
- SHINE_CONTRACT_ADDRESS = SHINE_OPTIMISM_SEPOLIA;
354
- } else {
355
- throw new Error("Chain not supported");
356
- }
357
-
358
- // Funzione per verificare on-chain
359
- const verifyOnChain = async (nodeId, contentHash) => {
360
- console.log("Verifying on chain:", { nodeId, contentHash });
361
- const signer = await getSigner();
362
- const contract = new ethers.Contract(
363
- SHINE_CONTRACT_ADDRESS,
364
- SHINE_ABI,
365
- signer
366
- );
367
- const [isValid, timestamp, updater] = await contract.verifyData(
368
- ethers.toUtf8Bytes(nodeId),
369
- contentHash
370
- );
371
- console.log("Verification result:", { isValid, timestamp, updater });
372
- return { isValid, timestamp, updater };
373
- };
374
-
375
- // Funzione per scrivere on-chain
376
- const writeOnChain = async (nodeId, contentHash) => {
377
- console.log("Writing on chain:", { nodeId, contentHash });
378
- const signer = await getSigner();
379
- const contract = new ethers.Contract(
380
- SHINE_CONTRACT_ADDRESS,
381
- SHINE_ABI,
382
- signer
383
- );
384
- const tx = await contract.updateData(
385
- ethers.toUtf8Bytes(nodeId),
386
- contentHash
387
- );
388
- console.log("Transaction sent:", tx.hash);
389
- const receipt = await tx.wait();
390
- console.log("Transaction confirmed:", receipt);
391
- return tx;
392
- };
393
-
394
- // Nuova funzione per ottenere l'ultimo record dalla blockchain
395
- const getLatestRecord = async (nodeId) => {
396
- const signer = await getSigner();
397
- const contract = new ethers.Contract(
398
- SHINE_CONTRACT_ADDRESS,
399
- SHINE_ABI,
400
- signer
401
- );
402
- const [contentHash, timestamp, updater] = await contract.getLatestRecord(
403
- ethers.toUtf8Bytes(nodeId)
404
- );
405
- console.log("Latest record from blockchain:", {
406
- nodeId,
407
- contentHash,
408
- timestamp,
409
- updater,
410
- });
411
- return { contentHash, timestamp, updater };
412
- };
413
-
414
- // Processo SHINE
415
- if (nodeId && !data) {
416
- // Caso 1: Utente passa solo il nodo
417
- gun.get(nodeId).once(async (existingData) => {
418
- if (!existingData) {
419
- if (callback) callback({ err: "Node not found in GunDB" });
420
- return;
421
- }
422
-
423
- console.log("existingData", existingData);
424
-
425
- // Usa il contentHash memorizzato invece di ricalcolarlo
426
- const contentHash = existingData._contentHash;
427
- console.log("contentHash", contentHash);
428
-
429
- if (!contentHash) {
430
- if (callback)
431
- callback({ err: "No content hash found for this node" });
432
- return;
433
- }
434
-
435
- try {
436
- const { isValid, timestamp, updater } = await verifyOnChain(
437
- nodeId,
438
- contentHash
439
- );
440
- const latestRecord = await getLatestRecord(nodeId);
441
-
442
- if (isValid) {
443
- if (callback)
444
- callback({
445
- ok: true,
446
- message: "Data verified on blockchain",
447
- timestamp,
448
- updater,
449
- latestRecord,
450
- });
451
- } else {
452
- if (callback)
453
- callback({
454
- ok: false,
455
- message: "Data not verified on blockchain",
456
- latestRecord,
457
- });
458
- }
459
- } catch (error) {
460
- if (callback) callback({ err: error.message });
461
- }
462
- });
463
- } else if (data && !nodeId) {
464
- // Caso 2: Utente passa solo il testo (data)
465
- const newNodeId = Gun.text.random();
466
- const dataString = JSON.stringify(data);
467
- const contentHash = ethers.keccak256(ethers.toUtf8Bytes(dataString));
468
-
469
- gun
470
- .get(newNodeId)
471
- .put({ ...data, _contentHash: contentHash }, async (ack) => {
472
- console.log("ack", ack);
473
- if (ack.err) {
474
- if (callback) callback({ err: "Error saving data to GunDB" });
475
- return;
476
- }
477
-
478
- try {
479
- const tx = await writeOnChain(newNodeId, contentHash);
480
- if (callback)
481
- callback({
482
- ok: true,
483
- message: "Data written to GunDB and blockchain",
484
- nodeId: newNodeId,
485
- txHash: tx.hash,
486
- });
487
- } catch (error) {
488
- if (callback) callback({ err: error.message });
489
- }
490
- });
491
- } else {
492
- if (callback)
493
- callback({
494
- err: "Invalid input. Provide either nodeId or data, not both.",
495
- });
496
- }
497
-
498
- return gun;
499
- };
500
-
501
- /**
502
- * Converts a Gun private key to an Ethereum account.
503
- * @param {string} gunPrivateKey - The Gun private key in base64url format.
504
- * @returns {Object} An object containing the Ethereum account and public key.
505
- */
506
- Gun.chain.gunToEthAccount = function (gunPrivateKey) {
507
- // Function to convert base64url to hex
508
- const base64UrlToHex = (base64url) => {
509
- const padding = "=".repeat((4 - (base64url.length % 4)) % 4);
510
- const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/") + padding;
511
- const binary = atob(base64);
512
- return Array.from(binary, (char) =>
513
- char.charCodeAt(0).toString(16).padStart(2, "0")
514
- ).join("");
515
- };
516
-
517
- // Convert Gun private key to hex format
518
- const hexPrivateKey = "0x" + base64UrlToHex(gunPrivateKey);
519
-
520
- // Create an Ethereum wallet from the private key
521
- const wallet = new ethers.Wallet(hexPrivateKey);
522
-
523
- // Get the public address (public key)
524
- const publicKey = wallet.address;
525
-
526
- return {
527
- account: wallet,
528
- publicKey: publicKey,
529
- };
530
- };
531
-
532
- console.log("Plugin Gun-Eth successfully loaded");
533
- });
@@ -1,347 +0,0 @@
1
- const Gun = require("gun/gun");
2
- const SEA = require("gun/sea");
3
- const ethers = require("ethers");
4
- const SHINE = require("../abis/SHINE.json");
5
-
6
- const SHINE_ABI = SHINE.abi;
7
- const SHINE_OPTIMISM_SEPOLIA = SHINE.address;
8
-
9
- let SHINE_CONTRACT_ADDRESS;
10
-
11
- let rpcUrl = "";
12
- let privateKey = "";
13
-
14
- const MESSAGE_TO_SIGN = "Accesso a GunDB con Ethereum";
15
-
16
- /**
17
- * Funzione per ottenere il signer
18
- * @returns {Promise<ethers.Signer>} Il signer.
19
- */
20
- const getSigner = async () => {
21
- if (rpcUrl && privateKey) {
22
- // Modalità standalone
23
- const provider = new ethers.JsonRpcProvider(rpcUrl);
24
- return new ethers.Wallet(privateKey, provider);
25
- } else if (
26
- typeof window !== "undefined" &&
27
- typeof window.ethereum !== "undefined"
28
- ) {
29
- // Modalità browser
30
- await window.ethereum.request({ method: "eth_requestAccounts" });
31
- const provider = new ethers.BrowserProvider(window.ethereum);
32
- return provider.getSigner();
33
- } else {
34
- throw new Error("No valid Ethereum provider found");
35
- }
36
- };
37
-
38
- /**
39
- * Sets standalone configuration for Gun.
40
- * @param {string} newRpcUrl - The new RPC URL.
41
- * @param {string} newPrivateKey - The new private key.
42
- * @returns {Gun} The Gun instance for chaining.
43
- */
44
- Gun.chain.setStandaloneConfig = function (newRpcUrl, newPrivateKey) {
45
- rpcUrl = newRpcUrl;
46
- privateKey = newPrivateKey;
47
- console.log("Standalone configuration set");
48
- return this;
49
- };
50
-
51
- /**
52
- * Verifies an Ethereum signature.
53
- * @param {string} message - The original message that was signed.
54
- * @param {string} signature - The signature to verify.
55
- * @returns {Promise<string|null>} The recovered address or null if verification fails.
56
- */
57
- Gun.chain.verifySignature = async function (message, signature) {
58
- try {
59
- const recoveredAddress = ethers.verifyMessage(message, signature);
60
- return recoveredAddress;
61
- } catch (error) {
62
- console.error("Error verifying signature:", error);
63
- return null;
64
- }
65
- };
66
-
67
- /**
68
- * Generates a password from a signature.
69
- * @param {string} signature - The signature to derive the password from.
70
- * @returns {string|null} The generated password or null if generation fails.
71
- */
72
- Gun.chain.generatePassword = function (signature) {
73
- try {
74
- const hexSignature = ethers.hexlify(signature);
75
- const hash = ethers.keccak256(hexSignature);
76
- console.log("Generated password:", hash);
77
- return hash;
78
- } catch (error) {
79
- console.error("Error generating password:", error);
80
- return null;
81
- }
82
- };
83
-
84
- /**
85
- * Creates an Ethereum signature for a given message.
86
- * @param {string} message - The message to sign.
87
- * @returns {Promise<string|null>} The signature or null if signing fails.
88
- */
89
- Gun.chain.createSignature = async function (message) {
90
- try {
91
- // Verifica se il messaggio è uguale a MESSAGE_TO_SIGN
92
- if (message !== MESSAGE_TO_SIGN) {
93
- throw new Error("Invalid message, valid message is: " + MESSAGE_TO_SIGN);
94
- }
95
- const signer = await getSigner();
96
- const signature = await signer.signMessage(message);
97
- console.log("Signature created:", signature);
98
- return signature;
99
- } catch (error) {
100
- console.error("Error creating signature:", error);
101
- return null;
102
- }
103
- };
104
-
105
- /**
106
- * Creates and stores an encrypted key pair for a given address.
107
- * @param {string} address - The Ethereum address to associate with the key pair.
108
- * @param {string} signature - The signature to use for encryption.
109
- * @returns {Promise<void>}
110
- */
111
- Gun.chain.createAndStoreEncryptedPair = async function (address, signature) {
112
- try {
113
- const gun = this;
114
- const pair = await SEA.pair();
115
- const encryptedPair = await SEA.encrypt(JSON.stringify(pair), signature);
116
- await gun.get("gun-eth").get("users").get(address).put({ encryptedPair });
117
- console.log("Encrypted pair stored for:", address);
118
- } catch (error) {
119
- console.error("Error creating and storing encrypted pair:", error);
120
- }
121
- };
122
-
123
- /**
124
- * Retrieves and decrypts a stored key pair for a given address.
125
- * @param {string} address - The Ethereum address associated with the key pair.
126
- * @param {string} signature - The signature to use for decryption.
127
- * @returns {Promise<Object|null>} The decrypted key pair or null if retrieval fails.
128
- */
129
- Gun.chain.getAndDecryptPair = async function (address, signature) {
130
- try {
131
- const gun = this;
132
- const encryptedData = await gun
133
- .get("gun-eth")
134
- .get("users")
135
- .get(address)
136
- .get("encryptedPair")
137
- .then();
138
- if (!encryptedData) {
139
- throw new Error("No encrypted data found for this address");
140
- }
141
- const decryptedPair = await SEA.decrypt(encryptedData, signature);
142
- console.log(decryptedPair);
143
- return decryptedPair;
144
- } catch (error) {
145
- console.error("Error retrieving and decrypting pair:", error);
146
- return null;
147
- }
148
- };
149
-
150
- /**
151
- * SHINE (Secure Hybrid Information and Network Environment) functionality.
152
- * @param {string} chain - The blockchain to use (e.g., "optimismSepolia").
153
- * @param {string} nodeId - The ID of the node to verify or write.
154
- * @param {Object} data - The data to write (if writing).
155
- * @param {Function} callback - Callback function to handle the result.
156
- * @returns {Gun} The Gun instance for chaining.
157
- */
158
- Gun.chain.shine = function (chain, nodeId, data, callback) {
159
- console.log("SHINE plugin called with:", { chain, nodeId, data });
160
-
161
- if (typeof callback !== "function") {
162
- console.error("Callback must be a function");
163
- return this;
164
- }
165
-
166
- const gun = this;
167
-
168
- // Seleziona l'indirizzo basato sulla catena
169
- if (chain === "optimismSepolia") {
170
- SHINE_CONTRACT_ADDRESS = SHINE_OPTIMISM_SEPOLIA;
171
- } else {
172
- throw new Error("Chain not supported");
173
- }
174
- // Funzione per verificare on-chain
175
- const verifyOnChain = async (nodeId, contentHash) => {
176
- console.log("Verifying on chain:", { nodeId, contentHash });
177
- const signer = await getSigner();
178
- const contract = new ethers.Contract(
179
- SHINE_CONTRACT_ADDRESS,
180
- SHINE_ABI,
181
- signer
182
- );
183
- const [isValid, timestamp, updater] = await contract.verifyData(
184
- ethers.toUtf8Bytes(nodeId),
185
- contentHash
186
- );
187
- console.log("Verification result:", { isValid, timestamp, updater });
188
- return { isValid, timestamp, updater };
189
- };
190
-
191
- // Funzione per scrivere on-chain
192
- const writeOnChain = async (nodeId, contentHash) => {
193
- console.log("Writing on chain:", { nodeId, contentHash });
194
- const signer = await getSigner();
195
- const contract = new ethers.Contract(
196
- SHINE_CONTRACT_ADDRESS,
197
- SHINE_ABI,
198
- signer
199
- );
200
- const tx = await contract.updateData(
201
- ethers.toUtf8Bytes(nodeId),
202
- contentHash
203
- );
204
- console.log("Transaction sent:", tx.hash);
205
- const receipt = await tx.wait();
206
- console.log("Transaction confirmed:", receipt);
207
- return tx;
208
- };
209
-
210
- // Nuova funzione per ottenere l'ultimo record dalla blockchain
211
- const getLatestRecord = async (nodeId) => {
212
- const signer = await getSigner();
213
- const contract = new ethers.Contract(
214
- SHINE_CONTRACT_ADDRESS,
215
- SHINE_ABI,
216
- signer
217
- );
218
- const [contentHash, timestamp, updater] = await contract.getLatestRecord(
219
- ethers.toUtf8Bytes(nodeId)
220
- );
221
- console.log("Latest record from blockchain:", {
222
- nodeId,
223
- contentHash,
224
- timestamp,
225
- updater,
226
- });
227
- return { contentHash, timestamp, updater };
228
- };
229
-
230
- // Processo SHINE
231
- if (nodeId && !data) {
232
- // Caso 1: Utente passa solo il nodo
233
- gun.get(nodeId).once(async (existingData) => {
234
- if (!existingData) {
235
- if (callback) callback({ err: "Node not found in GunDB" });
236
- return;
237
- }
238
-
239
- console.log("existingData", existingData);
240
-
241
- // Usa il contentHash memorizzato invece di ricalcolarlo
242
- const contentHash = existingData._contentHash;
243
- console.log("contentHash", contentHash);
244
-
245
- if (!contentHash) {
246
- if (callback) callback({ err: "No content hash found for this node" });
247
- return;
248
- }
249
-
250
- try {
251
- const { isValid, timestamp, updater } = await verifyOnChain(
252
- nodeId,
253
- contentHash
254
- );
255
- const latestRecord = await getLatestRecord(nodeId);
256
-
257
- if (isValid) {
258
- if (callback)
259
- callback({
260
- ok: true,
261
- message: "Data verified on blockchain",
262
- timestamp,
263
- updater,
264
- latestRecord,
265
- });
266
- } else {
267
- if (callback)
268
- callback({
269
- ok: false,
270
- message: "Data not verified on blockchain",
271
- latestRecord,
272
- });
273
- }
274
- } catch (error) {
275
- if (callback) callback({ err: error.message });
276
- }
277
- });
278
- } else if (data && !nodeId) {
279
- // Caso 2: Utente passa solo il testo (data)
280
- const newNodeId = Gun.text.random();
281
- const dataString = JSON.stringify(data);
282
- const contentHash = ethers.keccak256(ethers.toUtf8Bytes(dataString));
283
-
284
- gun
285
- .get(newNodeId)
286
- .put({ ...data, _contentHash: contentHash }, async (ack) => {
287
- console.log("ack", ack);
288
- if (ack.err) {
289
- if (callback) callback({ err: "Error saving data to GunDB" });
290
- return;
291
- }
292
-
293
- try {
294
- const tx = await writeOnChain(newNodeId, contentHash);
295
- if (callback)
296
- callback({
297
- ok: true,
298
- message: "Data written to GunDB and blockchain",
299
- nodeId: newNodeId,
300
- txHash: tx.hash,
301
- });
302
- } catch (error) {
303
- if (callback) callback({ err: error.message });
304
- }
305
- });
306
- } else {
307
- if (callback)
308
- callback({
309
- err: "Invalid input. Provide either nodeId or data, not both.",
310
- });
311
- }
312
-
313
- return gun;
314
- };
315
-
316
- /**
317
- * Converts a Gun private key to an Ethereum account.
318
- * @param {string} gunPrivateKey - The Gun private key in base64url format.
319
- * @returns {Object} An object containing the Ethereum account and public key.
320
- */
321
- Gun.chain.gunToEthAccount = function (gunPrivateKey) {
322
- // Function to convert base64url to hex
323
- const base64UrlToHex = (base64url) => {
324
- const padding = "=".repeat((4 - (base64url.length % 4)) % 4);
325
- const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/") + padding;
326
- const binary = atob(base64);
327
- return Array.from(binary, (char) =>
328
- char.charCodeAt(0).toString(16).padStart(2, "0")
329
- ).join("");
330
- };
331
-
332
- // Convert Gun private key to hex format
333
- const hexPrivateKey = "0x" + base64UrlToHex(gunPrivateKey);
334
-
335
- // Create an Ethereum wallet from the private key
336
- const wallet = new ethers.Wallet(hexPrivateKey);
337
-
338
- // Get the public address (public key)
339
- const publicKey = wallet.address;
340
-
341
- return {
342
- account: wallet,
343
- publicKey: publicKey,
344
- };
345
- };
346
-
347
- module.exports = Gun;