gun-eth 1.2.2 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -21
- package/index.js +270 -1
- package/package.json +1 -1
- package/gun-eth.js +0 -270
package/README.md
CHANGED
|
@@ -4,43 +4,123 @@
|
|
|
4
4
|
|
|
5
5
|
gun-eth is a plugin for GunDB that integrates Ethereum and Web3 functionality. This plugin extends GunDB's capabilities by allowing interaction with the Ethereum blockchain and providing cryptographic and signature management features.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
SHINE Smart Contract deployed on Optimism Sepolia: [0x43D838b683F772F08f321E5FA265ad3e333BE9C2](https://sepolia-optimism.etherscan.io/address/0x43D838b683F772F08f321E5FA265ad3e333BE9C2)
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- Signature-based password generation
|
|
11
|
-
- Signature creation using Ethereum providers (e.g., MetaMask)
|
|
9
|
+
Currently, the contract is deployed only on Optimism Sepolia. In the future, it will be deployed on multiple chains.
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
### Key Features
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
- **Ethereum Signature Verification**: Verify Ethereum signatures for messages.
|
|
14
|
+
- **Password Generation**: Generate secure passwords from Ethereum signatures.
|
|
15
|
+
- **Signature Creation**: Create Ethereum signatures for messages.
|
|
16
|
+
- **Encrypted Key Pair Management**: Create, store, and retrieve encrypted key pairs.
|
|
17
|
+
- **SHINE Implementation**: Implement the SHINE for data verification on the blockchain.
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
### How to install
|
|
18
20
|
|
|
19
21
|
```bash
|
|
20
22
|
npm install gun-eth
|
|
21
23
|
```
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const Gun = require("gun");
|
|
27
|
-
require("gun-eth");
|
|
25
|
+
```bash
|
|
26
|
+
import gun from "gun";
|
|
27
|
+
import "gun-eth";
|
|
28
28
|
|
|
29
|
-
const gun =
|
|
29
|
+
const gun = gun();
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
await gun.generatePassword("YOUR_SIGNATURE");
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
### How to use
|
|
35
|
+
|
|
36
|
+
Learn more about Gun.js [here](https://gun.eco/docs/Getting-Started).
|
|
37
|
+
|
|
38
|
+
Learn more about plugin implementation [here](https://github.com/amark/gun/wiki/Adding-Methods-to-the-Gun-Chain#abstraction-layers).
|
|
39
|
+
|
|
40
|
+
### Core Functions
|
|
41
|
+
|
|
42
|
+
- `verifySignature(message, signature)`: Verifies an Ethereum signature for a given message.
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
const recoveredAddress = await gun.verifySignature(message, signature);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- `generatePassword(signature)`: Generates a password from an Ethereum signature.
|
|
49
|
+
|
|
50
|
+
```js
|
|
51
|
+
const password = gun.generatePassword(signature);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- `createSignature(message)`: Creates an Ethereum signature for a message.
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
const signature = await gun.createSignature(message);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- `createAndStoreEncryptedPair(address, signature)`: Creates and stores an encrypted key pair.
|
|
35
61
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
62
|
+
```js
|
|
63
|
+
await gun.createAndStoreEncryptedPair(address, signature);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- `getAndDecryptPair(address, signature)`: Retrieves and decrypts a stored key pair.
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
const decryptedPair = await gun.getAndDecryptPair(address, signature);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- `shine(chain, nodeId, data, callback)`: Implements SHINE for data verification and storage on the blockchain.
|
|
73
|
+
```js
|
|
74
|
+
gun.shine("optimismSepolia", nodeId, data, callback);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### SHINE
|
|
78
|
+
|
|
79
|
+
SHINE (Secure Hash Integrity Network Ethereum) provides a mechanism for verifying data integrity using Ethereum and Gun.js.
|
|
80
|
+
|
|
81
|
+
1. **Data Storage**: When saving data, a content hash is generated and stored in both Gun.js and on the Ethereum blockchain.
|
|
82
|
+
2. **Data Verification**: To verify data, the stored hash is compared with a hash generated from the data retrieved from Gun.js.
|
|
83
|
+
3. **Blockchain Interaction**: The plugin interacts with an Ethereum smart contract to store and verify data hashes.
|
|
84
|
+
|
|
85
|
+
### Usage Examples
|
|
86
|
+
|
|
87
|
+
#### Verifying Data by NodeId
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
const nodeId = "your-node-id-here";
|
|
91
|
+
gun.shine("optimismSepolia", nodeId, null, (ack) => {
|
|
92
|
+
if (ack.ok) {
|
|
93
|
+
console.log("Data verified on blockchain", ack);
|
|
94
|
+
console.log("Timestamp:", ack.timestamp);
|
|
95
|
+
console.log("Updater:", ack.updater);
|
|
96
|
+
console.log("Latest Record:", ack.latestRecord);
|
|
97
|
+
} else {
|
|
98
|
+
console.log("Data not verified or not found", ack);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### Storing New Data
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
const data = { message: "Hello, blockchain!" };
|
|
107
|
+
gun.shine("optimismSepolia", null, data, (ack) => {
|
|
108
|
+
if (ack.ok) {
|
|
109
|
+
console.log("Data stored on Gun.js and blockchain", ack);
|
|
110
|
+
console.log("New Node ID:", ack.nodeId);
|
|
111
|
+
console.log("Transaction Hash:", ack.txHash);
|
|
112
|
+
} else {
|
|
113
|
+
console.log("Error storing data", ack);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
```
|
|
39
117
|
|
|
40
|
-
|
|
118
|
+
### Security Considerations
|
|
41
119
|
|
|
42
|
-
-
|
|
43
|
-
-
|
|
120
|
+
- Use a secure Ethereum provider (e.g., MetaMask) when interacting with functions that require signatures.
|
|
121
|
+
- Generated passwords and key pairs are sensitive. Handle them carefully and avoid exposing them.
|
|
122
|
+
- Keep Gun.js and Ethereum dependencies up to date for security.
|
|
123
|
+
- Be aware of gas costs associated with blockchain interactions when using SHINE.
|
|
44
124
|
|
|
45
125
|
## Contributing
|
|
46
126
|
|
package/index.js
CHANGED
|
@@ -1 +1,270 @@
|
|
|
1
|
-
|
|
1
|
+
/* const Gun = require("gun/gun");
|
|
2
|
+
const SEA = require("gun/sea");
|
|
3
|
+
const ethers = require("ethers");
|
|
4
|
+
const SHINE = require("./SHINE.json"); */
|
|
5
|
+
|
|
6
|
+
import Gun from "gun";
|
|
7
|
+
import SEA from "gun/sea";
|
|
8
|
+
import { ethers } from "ethers";
|
|
9
|
+
|
|
10
|
+
import SHINE from "./SHINE.json";
|
|
11
|
+
|
|
12
|
+
const SHINE_ABI = SHINE.abi;
|
|
13
|
+
const SHINE_OPTIMISM_SEPOLIA = SHINE.address;
|
|
14
|
+
|
|
15
|
+
let SHINE_CONTRACT_ADDRESS;
|
|
16
|
+
|
|
17
|
+
// Aggiungi il metodo alla catena di Gun
|
|
18
|
+
Gun.chain.verifySignature = async function (message, signature) {
|
|
19
|
+
try {
|
|
20
|
+
const recoveredAddress = ethers.verifyMessage(message, signature);
|
|
21
|
+
return recoveredAddress;
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.error("Errore durante la verifica della firma:", error);
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
Gun.chain.generatePassword = function (signature) {
|
|
29
|
+
try {
|
|
30
|
+
// Usa SHA-256 per derivare una password dalla firma
|
|
31
|
+
const hexSignature = ethers.hexlify(signature);
|
|
32
|
+
const hash = ethers.keccak256(hexSignature);
|
|
33
|
+
|
|
34
|
+
console.log("Password generata:", hash);
|
|
35
|
+
return hash;
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error("Errore nella generazione della password:", error);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
Gun.chain.createSignature = async function (message) {
|
|
43
|
+
try {
|
|
44
|
+
// Controlla se window.ethereum è disponibile (metamask o altro provider)
|
|
45
|
+
if (typeof window.ethereum !== "undefined") {
|
|
46
|
+
await window.ethereum.request({ method: "eth_requestAccounts" });
|
|
47
|
+
const provider = new ethers.BrowserProvider(window.ethereum);
|
|
48
|
+
const signer = await provider.getSigner();
|
|
49
|
+
const signature = await signer.signMessage(message);
|
|
50
|
+
console.log("Firma creata:", signature);
|
|
51
|
+
return signature;
|
|
52
|
+
} else {
|
|
53
|
+
throw new Error("Provider Ethereum non trovato");
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error("Errore durante la creazione della firma:", error);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
Gun.chain.createAndStoreEncryptedPair = async function (address, signature) {
|
|
62
|
+
try {
|
|
63
|
+
const gun = this;
|
|
64
|
+
const pair = await SEA.pair();
|
|
65
|
+
const encryptedPair = await SEA.encrypt(JSON.stringify(pair), signature);
|
|
66
|
+
|
|
67
|
+
await gun.get("users").get(address).put({ encryptedPair });
|
|
68
|
+
console.log("Pair crittografato e archiviato per:", address);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error(
|
|
71
|
+
"Errore durante la creazione e l'archiviazione del pair crittografato:",
|
|
72
|
+
error
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
Gun.chain.getAndDecryptPair = async function (address, signature) {
|
|
78
|
+
try {
|
|
79
|
+
const gun = this;
|
|
80
|
+
const encryptedData = await gun
|
|
81
|
+
.get("users")
|
|
82
|
+
.get(address)
|
|
83
|
+
.get("encryptedPair")
|
|
84
|
+
.then();
|
|
85
|
+
if (!encryptedData) {
|
|
86
|
+
throw new Error("Nessun dato crittografato trovato per questo indirizzo");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const decryptedPair = await SEA.decrypt(encryptedData, signature);
|
|
90
|
+
|
|
91
|
+
console.log(decryptedPair);
|
|
92
|
+
return decryptedPair;
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error(
|
|
95
|
+
"Errore durante il recupero e la decrittazione del pair:",
|
|
96
|
+
error
|
|
97
|
+
);
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
Gun.chain.shine = function (chain, nodeId, data, callback) {
|
|
103
|
+
console.log("SHINE plugin called with:", { chain, nodeId, data });
|
|
104
|
+
|
|
105
|
+
if (typeof callback !== "function") {
|
|
106
|
+
console.error("Callback must be a function");
|
|
107
|
+
return this;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const gun = this;
|
|
111
|
+
|
|
112
|
+
// Seleziona l'indirizzo basato sulla catena
|
|
113
|
+
if (chain === "optimismSepolia") {
|
|
114
|
+
SHINE_CONTRACT_ADDRESS = SHINE_OPTIMISM_SEPOLIA;
|
|
115
|
+
} else {
|
|
116
|
+
throw new Error("Chain not supported");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Funzione per ottenere il signer
|
|
120
|
+
const getSigner = async () => {
|
|
121
|
+
if (typeof window.ethereum !== "undefined") {
|
|
122
|
+
await window.ethereum.request({ method: "eth_requestAccounts" });
|
|
123
|
+
const provider = new ethers.BrowserProvider(window.ethereum);
|
|
124
|
+
return provider.getSigner();
|
|
125
|
+
} else {
|
|
126
|
+
throw new Error("Ethereum provider not found");
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Funzione per verificare on-chain
|
|
131
|
+
const verifyOnChain = async (nodeId, contentHash) => {
|
|
132
|
+
console.log("Verifying on chain:", { nodeId, contentHash });
|
|
133
|
+
const signer = await getSigner();
|
|
134
|
+
const contract = new ethers.Contract(
|
|
135
|
+
SHINE_CONTRACT_ADDRESS,
|
|
136
|
+
SHINE_ABI,
|
|
137
|
+
signer
|
|
138
|
+
);
|
|
139
|
+
const [isValid, timestamp, updater] = await contract.verifyData(
|
|
140
|
+
ethers.toUtf8Bytes(nodeId),
|
|
141
|
+
contentHash
|
|
142
|
+
);
|
|
143
|
+
console.log("Verification result:", { isValid, timestamp, updater });
|
|
144
|
+
return { isValid, timestamp, updater };
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// Funzione per scrivere on-chain
|
|
148
|
+
const writeOnChain = async (nodeId, contentHash) => {
|
|
149
|
+
console.log("Writing on chain:", { nodeId, contentHash });
|
|
150
|
+
const signer = await getSigner();
|
|
151
|
+
const contract = new ethers.Contract(
|
|
152
|
+
SHINE_CONTRACT_ADDRESS,
|
|
153
|
+
SHINE_ABI,
|
|
154
|
+
signer
|
|
155
|
+
);
|
|
156
|
+
const tx = await contract.updateData(
|
|
157
|
+
ethers.toUtf8Bytes(nodeId),
|
|
158
|
+
contentHash
|
|
159
|
+
);
|
|
160
|
+
console.log("Transaction sent:", tx.hash);
|
|
161
|
+
const receipt = await tx.wait();
|
|
162
|
+
console.log("Transaction confirmed:", receipt);
|
|
163
|
+
return tx;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// Nuova funzione per ottenere l'ultimo record dalla blockchain
|
|
167
|
+
const getLatestRecord = async (nodeId) => {
|
|
168
|
+
const signer = await getSigner();
|
|
169
|
+
const contract = new ethers.Contract(
|
|
170
|
+
SHINE_CONTRACT_ADDRESS,
|
|
171
|
+
SHINE_ABI,
|
|
172
|
+
signer
|
|
173
|
+
);
|
|
174
|
+
const [contentHash, timestamp, updater] = await contract.getLatestRecord(
|
|
175
|
+
ethers.toUtf8Bytes(nodeId)
|
|
176
|
+
);
|
|
177
|
+
console.log("Latest record from blockchain:", {
|
|
178
|
+
nodeId,
|
|
179
|
+
contentHash,
|
|
180
|
+
timestamp,
|
|
181
|
+
updater,
|
|
182
|
+
});
|
|
183
|
+
return { contentHash, timestamp, updater };
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// Processo SHINE
|
|
187
|
+
if (nodeId && !data) {
|
|
188
|
+
// Caso 1: Utente passa solo il nodo
|
|
189
|
+
gun.get(nodeId).once(async (existingData) => {
|
|
190
|
+
if (!existingData) {
|
|
191
|
+
if (callback) callback({ err: "Node not found in GunDB" });
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
console.log("existingData", existingData);
|
|
196
|
+
|
|
197
|
+
// Usa il contentHash memorizzato invece di ricalcolarlo
|
|
198
|
+
const contentHash = existingData._contentHash;
|
|
199
|
+
console.log("contentHash", contentHash);
|
|
200
|
+
|
|
201
|
+
if (!contentHash) {
|
|
202
|
+
if (callback) callback({ err: "No content hash found for this node" });
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
const { isValid, timestamp, updater } = await verifyOnChain(
|
|
208
|
+
nodeId,
|
|
209
|
+
contentHash
|
|
210
|
+
);
|
|
211
|
+
const latestRecord = await getLatestRecord(nodeId);
|
|
212
|
+
|
|
213
|
+
if (isValid) {
|
|
214
|
+
if (callback)
|
|
215
|
+
callback({
|
|
216
|
+
ok: true,
|
|
217
|
+
message: "Data verified on blockchain",
|
|
218
|
+
timestamp,
|
|
219
|
+
updater,
|
|
220
|
+
latestRecord,
|
|
221
|
+
});
|
|
222
|
+
} else {
|
|
223
|
+
if (callback)
|
|
224
|
+
callback({
|
|
225
|
+
ok: false,
|
|
226
|
+
message: "Data not verified on blockchain",
|
|
227
|
+
latestRecord,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
} catch (error) {
|
|
231
|
+
if (callback) callback({ err: error.message });
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
} else if (data && !nodeId) {
|
|
235
|
+
// Caso 2: Utente passa solo il testo (data)
|
|
236
|
+
const newNodeId = Gun.text.random();
|
|
237
|
+
const dataString = JSON.stringify(data);
|
|
238
|
+
const contentHash = ethers.keccak256(ethers.toUtf8Bytes(dataString));
|
|
239
|
+
|
|
240
|
+
gun
|
|
241
|
+
.get(newNodeId)
|
|
242
|
+
.put({ ...data, _contentHash: contentHash }, async (ack) => {
|
|
243
|
+
console.log("ack", ack);
|
|
244
|
+
if (ack.err) {
|
|
245
|
+
if (callback) callback({ err: "Error saving data to GunDB" });
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
const tx = await writeOnChain(newNodeId, contentHash);
|
|
251
|
+
if (callback)
|
|
252
|
+
callback({
|
|
253
|
+
ok: true,
|
|
254
|
+
message: "Data written to GunDB and blockchain",
|
|
255
|
+
nodeId: newNodeId,
|
|
256
|
+
txHash: tx.hash,
|
|
257
|
+
});
|
|
258
|
+
} catch (error) {
|
|
259
|
+
if (callback) callback({ err: error.message });
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
} else {
|
|
263
|
+
if (callback)
|
|
264
|
+
callback({
|
|
265
|
+
err: "Invalid input. Provide either nodeId or data, not both.",
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return gun;
|
|
270
|
+
};
|
package/package.json
CHANGED
package/gun-eth.js
DELETED
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
/* const Gun = require("gun/gun");
|
|
2
|
-
const SEA = require("gun/sea");
|
|
3
|
-
const ethers = require("ethers");
|
|
4
|
-
const SHINE = require("./SHINE.json"); */
|
|
5
|
-
|
|
6
|
-
import Gun from "gun";
|
|
7
|
-
import SEA from "gun/sea";
|
|
8
|
-
import { ethers } from "ethers";
|
|
9
|
-
|
|
10
|
-
import SHINE from "./SHINE.json";
|
|
11
|
-
|
|
12
|
-
const SHINE_ABI = SHINE.abi;
|
|
13
|
-
const SHINE_OPTIMISM_SEPOLIA = SHINE.address;
|
|
14
|
-
|
|
15
|
-
let SHINE_CONTRACT_ADDRESS;
|
|
16
|
-
|
|
17
|
-
// Aggiungi il metodo alla catena di Gun
|
|
18
|
-
Gun.chain.verifySignature = async function (message, signature) {
|
|
19
|
-
try {
|
|
20
|
-
const recoveredAddress = ethers.verifyMessage(message, signature);
|
|
21
|
-
return recoveredAddress;
|
|
22
|
-
} catch (error) {
|
|
23
|
-
console.error("Errore durante la verifica della firma:", error);
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
Gun.chain.generatePassword = function (signature) {
|
|
29
|
-
try {
|
|
30
|
-
// Usa SHA-256 per derivare una password dalla firma
|
|
31
|
-
const hexSignature = ethers.hexlify(signature);
|
|
32
|
-
const hash = ethers.keccak256(hexSignature);
|
|
33
|
-
|
|
34
|
-
console.log("Password generata:", hash);
|
|
35
|
-
return hash;
|
|
36
|
-
} catch (error) {
|
|
37
|
-
console.error("Errore nella generazione della password:", error);
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
Gun.chain.createSignature = async function (message) {
|
|
43
|
-
try {
|
|
44
|
-
// Controlla se window.ethereum è disponibile (metamask o altro provider)
|
|
45
|
-
if (typeof window.ethereum !== "undefined") {
|
|
46
|
-
await window.ethereum.request({ method: "eth_requestAccounts" });
|
|
47
|
-
const provider = new ethers.BrowserProvider(window.ethereum);
|
|
48
|
-
const signer = await provider.getSigner();
|
|
49
|
-
const signature = await signer.signMessage(message);
|
|
50
|
-
console.log("Firma creata:", signature);
|
|
51
|
-
return signature;
|
|
52
|
-
} else {
|
|
53
|
-
throw new Error("Provider Ethereum non trovato");
|
|
54
|
-
}
|
|
55
|
-
} catch (error) {
|
|
56
|
-
console.error("Errore durante la creazione della firma:", error);
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
Gun.chain.createAndStoreEncryptedPair = async function (address, signature) {
|
|
62
|
-
try {
|
|
63
|
-
const gun = this;
|
|
64
|
-
const pair = await SEA.pair();
|
|
65
|
-
const encryptedPair = await SEA.encrypt(JSON.stringify(pair), signature);
|
|
66
|
-
|
|
67
|
-
await gun.get("users").get(address).put({ encryptedPair });
|
|
68
|
-
console.log("Pair crittografato e archiviato per:", address);
|
|
69
|
-
} catch (error) {
|
|
70
|
-
console.error(
|
|
71
|
-
"Errore durante la creazione e l'archiviazione del pair crittografato:",
|
|
72
|
-
error
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
Gun.chain.getAndDecryptPair = async function (address, signature) {
|
|
78
|
-
try {
|
|
79
|
-
const gun = this;
|
|
80
|
-
const encryptedData = await gun
|
|
81
|
-
.get("users")
|
|
82
|
-
.get(address)
|
|
83
|
-
.get("encryptedPair")
|
|
84
|
-
.then();
|
|
85
|
-
if (!encryptedData) {
|
|
86
|
-
throw new Error("Nessun dato crittografato trovato per questo indirizzo");
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const decryptedPair = await SEA.decrypt(encryptedData, signature);
|
|
90
|
-
|
|
91
|
-
console.log(decryptedPair);
|
|
92
|
-
return decryptedPair;
|
|
93
|
-
} catch (error) {
|
|
94
|
-
console.error(
|
|
95
|
-
"Errore durante il recupero e la decrittazione del pair:",
|
|
96
|
-
error
|
|
97
|
-
);
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
Gun.chain.shine = function (chain, nodeId, data, callback) {
|
|
103
|
-
console.log("SHINE plugin called with:", { chain, nodeId, data });
|
|
104
|
-
|
|
105
|
-
if (typeof callback !== "function") {
|
|
106
|
-
console.error("Callback must be a function");
|
|
107
|
-
return this;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const gun = this;
|
|
111
|
-
|
|
112
|
-
// Seleziona l'indirizzo basato sulla catena
|
|
113
|
-
if (chain === "optimismSepolia") {
|
|
114
|
-
SHINE_CONTRACT_ADDRESS = SHINE_OPTIMISM_SEPOLIA;
|
|
115
|
-
} else {
|
|
116
|
-
throw new Error("Chain not supported");
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Funzione per ottenere il signer
|
|
120
|
-
const getSigner = async () => {
|
|
121
|
-
if (typeof window.ethereum !== "undefined") {
|
|
122
|
-
await window.ethereum.request({ method: "eth_requestAccounts" });
|
|
123
|
-
const provider = new ethers.BrowserProvider(window.ethereum);
|
|
124
|
-
return provider.getSigner();
|
|
125
|
-
} else {
|
|
126
|
-
throw new Error("Ethereum provider not found");
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
// Funzione per verificare on-chain
|
|
131
|
-
const verifyOnChain = async (nodeId, contentHash) => {
|
|
132
|
-
console.log("Verifying on chain:", { nodeId, contentHash });
|
|
133
|
-
const signer = await getSigner();
|
|
134
|
-
const contract = new ethers.Contract(
|
|
135
|
-
SHINE_CONTRACT_ADDRESS,
|
|
136
|
-
SHINE_ABI,
|
|
137
|
-
signer
|
|
138
|
-
);
|
|
139
|
-
const [isValid, timestamp, updater] = await contract.verifyData(
|
|
140
|
-
ethers.toUtf8Bytes(nodeId),
|
|
141
|
-
contentHash
|
|
142
|
-
);
|
|
143
|
-
console.log("Verification result:", { isValid, timestamp, updater });
|
|
144
|
-
return { isValid, timestamp, updater };
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
// Funzione per scrivere on-chain
|
|
148
|
-
const writeOnChain = async (nodeId, contentHash) => {
|
|
149
|
-
console.log("Writing on chain:", { nodeId, contentHash });
|
|
150
|
-
const signer = await getSigner();
|
|
151
|
-
const contract = new ethers.Contract(
|
|
152
|
-
SHINE_CONTRACT_ADDRESS,
|
|
153
|
-
SHINE_ABI,
|
|
154
|
-
signer
|
|
155
|
-
);
|
|
156
|
-
const tx = await contract.updateData(
|
|
157
|
-
ethers.toUtf8Bytes(nodeId),
|
|
158
|
-
contentHash
|
|
159
|
-
);
|
|
160
|
-
console.log("Transaction sent:", tx.hash);
|
|
161
|
-
const receipt = await tx.wait();
|
|
162
|
-
console.log("Transaction confirmed:", receipt);
|
|
163
|
-
return tx;
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
// Nuova funzione per ottenere l'ultimo record dalla blockchain
|
|
167
|
-
const getLatestRecord = async (nodeId) => {
|
|
168
|
-
const signer = await getSigner();
|
|
169
|
-
const contract = new ethers.Contract(
|
|
170
|
-
SHINE_CONTRACT_ADDRESS,
|
|
171
|
-
SHINE_ABI,
|
|
172
|
-
signer
|
|
173
|
-
);
|
|
174
|
-
const [contentHash, timestamp, updater] = await contract.getLatestRecord(
|
|
175
|
-
ethers.toUtf8Bytes(nodeId)
|
|
176
|
-
);
|
|
177
|
-
console.log("Latest record from blockchain:", {
|
|
178
|
-
nodeId,
|
|
179
|
-
contentHash,
|
|
180
|
-
timestamp,
|
|
181
|
-
updater,
|
|
182
|
-
});
|
|
183
|
-
return { contentHash, timestamp, updater };
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
// Processo SHINE
|
|
187
|
-
if (nodeId && !data) {
|
|
188
|
-
// Caso 1: Utente passa solo il nodo
|
|
189
|
-
gun.get(nodeId).once(async (existingData) => {
|
|
190
|
-
if (!existingData) {
|
|
191
|
-
if (callback) callback({ err: "Node not found in GunDB" });
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
console.log("existingData", existingData);
|
|
196
|
-
|
|
197
|
-
// Usa il contentHash memorizzato invece di ricalcolarlo
|
|
198
|
-
const contentHash = existingData._contentHash;
|
|
199
|
-
console.log("contentHash", contentHash);
|
|
200
|
-
|
|
201
|
-
if (!contentHash) {
|
|
202
|
-
if (callback) callback({ err: "No content hash found for this node" });
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
try {
|
|
207
|
-
const { isValid, timestamp, updater } = await verifyOnChain(
|
|
208
|
-
nodeId,
|
|
209
|
-
contentHash
|
|
210
|
-
);
|
|
211
|
-
const latestRecord = await getLatestRecord(nodeId);
|
|
212
|
-
|
|
213
|
-
if (isValid) {
|
|
214
|
-
if (callback)
|
|
215
|
-
callback({
|
|
216
|
-
ok: true,
|
|
217
|
-
message: "Data verified on blockchain",
|
|
218
|
-
timestamp,
|
|
219
|
-
updater,
|
|
220
|
-
latestRecord,
|
|
221
|
-
});
|
|
222
|
-
} else {
|
|
223
|
-
if (callback)
|
|
224
|
-
callback({
|
|
225
|
-
ok: false,
|
|
226
|
-
message: "Data not verified on blockchain",
|
|
227
|
-
latestRecord,
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
} catch (error) {
|
|
231
|
-
if (callback) callback({ err: error.message });
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
} else if (data && !nodeId) {
|
|
235
|
-
// Caso 2: Utente passa solo il testo (data)
|
|
236
|
-
const newNodeId = Gun.text.random();
|
|
237
|
-
const dataString = JSON.stringify(data);
|
|
238
|
-
const contentHash = ethers.keccak256(ethers.toUtf8Bytes(dataString));
|
|
239
|
-
|
|
240
|
-
gun
|
|
241
|
-
.get(newNodeId)
|
|
242
|
-
.put({ ...data, _contentHash: contentHash }, async (ack) => {
|
|
243
|
-
console.log("ack", ack);
|
|
244
|
-
if (ack.err) {
|
|
245
|
-
if (callback) callback({ err: "Error saving data to GunDB" });
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
try {
|
|
250
|
-
const tx = await writeOnChain(newNodeId, contentHash);
|
|
251
|
-
if (callback)
|
|
252
|
-
callback({
|
|
253
|
-
ok: true,
|
|
254
|
-
message: "Data written to GunDB and blockchain",
|
|
255
|
-
nodeId: newNodeId,
|
|
256
|
-
txHash: tx.hash,
|
|
257
|
-
});
|
|
258
|
-
} catch (error) {
|
|
259
|
-
if (callback) callback({ err: error.message });
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
} else {
|
|
263
|
-
if (callback)
|
|
264
|
-
callback({
|
|
265
|
-
err: "Invalid input. Provide either nodeId or data, not both.",
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return gun;
|
|
270
|
-
};
|