shogun-core 1.1.4 → 1.2.2
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 +61 -1327
- package/dist/browser/shogun-core.js +1 -1
- package/dist/browser/shogun-core.js.LICENSE.txt +0 -9
- package/dist/browser/shogun-core.light.js +1 -1
- package/dist/browser/shogun-core.vendors.light.js +1 -1
- package/dist/browser.js +27 -11
- package/dist/core.js +603 -0
- package/dist/{gun → gundb}/crypto.js +38 -8
- package/dist/gundb/gun.js +676 -0
- package/dist/{gun → gundb}/index.js +0 -5
- package/dist/{gun → gundb}/utils.js +6 -0
- package/dist/index.js +1 -807
- package/dist/plugins/index.js +15 -28
- package/dist/plugins/{stealth → nostr}/index.js +3 -4
- package/dist/plugins/nostr/nostrConnector.js +656 -0
- package/dist/plugins/nostr/nostrConnectorPlugin.js +259 -0
- package/dist/plugins/{metamask → web3}/index.js +2 -2
- package/dist/plugins/{metamask/metamask.js → web3/web3Connector.js} +8 -8
- package/dist/plugins/{metamask/metamaskPlugin.js → web3/web3ConnectorPlugin.js} +32 -42
- package/dist/plugins/webauthn/webauthnPlugin.js +4 -0
- package/dist/types/browser.d.ts +9 -4
- package/dist/types/core.d.ts +221 -0
- package/dist/types/{gun → gundb}/crypto.d.ts +20 -5
- package/dist/types/{gun → gundb}/gun.d.ts +56 -28
- package/dist/types/gundb/index.d.ts +1 -0
- package/dist/types/{gun → gundb}/utils.d.ts +1 -0
- package/dist/types/index.d.ts +1 -282
- package/dist/types/plugins/index.d.ts +7 -10
- package/dist/types/plugins/nostr/index.d.ts +3 -0
- package/dist/types/plugins/nostr/nostrConnector.d.ts +111 -0
- package/dist/types/plugins/nostr/nostrConnectorPlugin.d.ts +87 -0
- package/dist/types/plugins/nostr/types.d.ts +122 -0
- package/dist/types/plugins/web3/index.d.ts +3 -0
- package/dist/types/plugins/{metamask → web3}/types.d.ts +4 -4
- package/dist/types/plugins/{metamask/metamask.d.ts → web3/web3Connector.d.ts} +7 -7
- package/dist/types/plugins/{metamask/metamaskPlugin.d.ts → web3/web3ConnectorPlugin.d.ts} +4 -4
- package/dist/types/shogun.js +40 -15
- package/dist/types/types/shogun.d.ts +67 -67
- package/dist/types/utils/errorHandler.d.ts +39 -36
- package/dist/types/utils/utility.d.ts +0 -4
- package/dist/utils/errorHandler.js +43 -40
- package/dist/utils/utility.js +0 -8
- package/package.json +2 -2
- package/dist/config.js +0 -18
- package/dist/gun/gun.js +0 -542
- package/dist/plugins/stealth/stealth.js +0 -176
- package/dist/plugins/stealth/stealthPlugin.js +0 -113
- package/dist/plugins/stealth/types.js +0 -2
- package/dist/plugins/utils/stubs/didStub.js +0 -35
- package/dist/plugins/utils/stubs/stealthStub.js +0 -35
- package/dist/plugins/utils/stubs/webauthnStub.js +0 -29
- package/dist/plugins/wallet/index.js +0 -20
- package/dist/plugins/wallet/types.js +0 -15
- package/dist/plugins/wallet/walletManager.js +0 -1832
- package/dist/plugins/wallet/walletPlugin.js +0 -236
- package/dist/types/config.d.ts +0 -15
- package/dist/types/gun/index.d.ts +0 -6
- package/dist/types/gun/types.d.ts +0 -2
- package/dist/types/plugins/metamask/index.d.ts +0 -3
- package/dist/types/plugins/stealth/index.d.ts +0 -3
- package/dist/types/plugins/stealth/stealth.d.ts +0 -93
- package/dist/types/plugins/stealth/stealthPlugin.d.ts +0 -60
- package/dist/types/plugins/stealth/types.d.ts +0 -93
- package/dist/types/plugins/utils/stubs/didStub.d.ts +0 -15
- package/dist/types/plugins/utils/stubs/stealthStub.d.ts +0 -15
- package/dist/types/plugins/utils/stubs/webauthnStub.d.ts +0 -13
- package/dist/types/plugins/wallet/index.d.ts +0 -3
- package/dist/types/plugins/wallet/types.d.ts +0 -167
- package/dist/types/plugins/wallet/walletManager.d.ts +0 -306
- package/dist/types/plugins/wallet/walletPlugin.d.ts +0 -126
- package/dist/types/utils/stubs/didStub.d.ts +0 -15
- package/dist/types/utils/stubs/stealthStub.d.ts +0 -15
- package/dist/types/utils/stubs/webauthnStub.d.ts +0 -13
- package/dist/utils/stubs/didStub.js +0 -35
- package/dist/utils/stubs/stealthStub.js +0 -35
- package/dist/utils/stubs/webauthnStub.js +0 -29
- /package/dist/{gun → gundb}/errors.js +0 -0
- /package/dist/{gun → gundb}/rxjs-integration.js +0 -0
- /package/dist/{gun → plugins/nostr}/types.js +0 -0
- /package/dist/plugins/{metamask → web3}/types.js +0 -0
- /package/dist/types/{gun → gundb}/errors.d.ts +0 -0
- /package/dist/types/{gun → gundb}/rxjs-integration.d.ts +0 -0
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NostrConnector = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* The BitcoinWallet class provides functionality for connecting, signing up, and logging in using Bitcoin wallets.
|
|
6
|
+
* Supports Alby and Nostr extensions, as well as manual key management.
|
|
7
|
+
*/
|
|
8
|
+
const logger_1 = require("../../utils/logger");
|
|
9
|
+
const errorHandler_1 = require("../../utils/errorHandler");
|
|
10
|
+
const eventEmitter_1 = require("../../utils/eventEmitter");
|
|
11
|
+
/**
|
|
12
|
+
* Class for Bitcoin wallet connections and operations
|
|
13
|
+
*/
|
|
14
|
+
class NostrConnector extends eventEmitter_1.EventEmitter {
|
|
15
|
+
MESSAGE_TO_SIGN = "I Love Shogun!";
|
|
16
|
+
DEFAULT_CONFIG = {
|
|
17
|
+
cacheDuration: 24 * 60 * 60 * 1000, // 24 hours instead of 30 minutes for better UX
|
|
18
|
+
maxRetries: 3,
|
|
19
|
+
retryDelay: 1000,
|
|
20
|
+
timeout: 60000,
|
|
21
|
+
network: "mainnet",
|
|
22
|
+
useApi: false,
|
|
23
|
+
};
|
|
24
|
+
config;
|
|
25
|
+
signatureCache = new Map();
|
|
26
|
+
// Connection state
|
|
27
|
+
connectedAddress = null;
|
|
28
|
+
connectedType = null;
|
|
29
|
+
manualKeyPair = null;
|
|
30
|
+
constructor(config = {}) {
|
|
31
|
+
super();
|
|
32
|
+
this.config = { ...this.DEFAULT_CONFIG, ...config };
|
|
33
|
+
this.setupEventListeners();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Setup event listeners
|
|
37
|
+
*/
|
|
38
|
+
setupEventListeners() {
|
|
39
|
+
// Currently no global events to listen to
|
|
40
|
+
// This would be the place to add listeners for wallet connections/disconnections
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Cleanup event listeners
|
|
44
|
+
*/
|
|
45
|
+
cleanup() {
|
|
46
|
+
this.removeAllListeners();
|
|
47
|
+
this.connectedAddress = null;
|
|
48
|
+
this.connectedType = null;
|
|
49
|
+
this.manualKeyPair = null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get cached signature if valid
|
|
53
|
+
*/
|
|
54
|
+
getCachedSignature(address) {
|
|
55
|
+
// First check in-memory cache
|
|
56
|
+
const cached = this.signatureCache.get(address);
|
|
57
|
+
if (cached) {
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
if (now - cached.timestamp <= this.config.cacheDuration) {
|
|
60
|
+
return cached.signature;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
this.signatureCache.delete(address);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Then check localStorage for persistence across page reloads
|
|
67
|
+
try {
|
|
68
|
+
const localStorageKey = `shogun_bitcoin_sig_${address}`;
|
|
69
|
+
const localCached = localStorage.getItem(localStorageKey);
|
|
70
|
+
if (localCached) {
|
|
71
|
+
const parsedCache = JSON.parse(localCached);
|
|
72
|
+
const now = Date.now();
|
|
73
|
+
if (now - parsedCache.timestamp <= this.config.cacheDuration) {
|
|
74
|
+
// Restore to in-memory cache
|
|
75
|
+
this.signatureCache.set(address, parsedCache);
|
|
76
|
+
return parsedCache.signature;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
// Remove expired cache
|
|
80
|
+
localStorage.removeItem(localStorageKey);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
(0, logger_1.logError)("Error reading signature cache from localStorage:", error);
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Cache signature
|
|
91
|
+
*/
|
|
92
|
+
cacheSignature(address, signature) {
|
|
93
|
+
const cacheEntry = {
|
|
94
|
+
signature,
|
|
95
|
+
timestamp: Date.now(),
|
|
96
|
+
address,
|
|
97
|
+
};
|
|
98
|
+
// Store in memory
|
|
99
|
+
this.signatureCache.set(address, cacheEntry);
|
|
100
|
+
// Store in localStorage for persistence
|
|
101
|
+
try {
|
|
102
|
+
const localStorageKey = `shogun_bitcoin_sig_${address}`;
|
|
103
|
+
localStorage.setItem(localStorageKey, JSON.stringify(cacheEntry));
|
|
104
|
+
(0, logger_1.log)(`Cached signature for address: ${address.substring(0, 10)}...`);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
(0, logger_1.logError)("Error saving signature cache to localStorage:", error);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Clear signature cache for a specific address or all addresses
|
|
112
|
+
*/
|
|
113
|
+
clearSignatureCache(address) {
|
|
114
|
+
if (address) {
|
|
115
|
+
// Clear cache for specific address
|
|
116
|
+
this.signatureCache.delete(address);
|
|
117
|
+
try {
|
|
118
|
+
const localStorageKey = `shogun_bitcoin_sig_${address}`;
|
|
119
|
+
localStorage.removeItem(localStorageKey);
|
|
120
|
+
(0, logger_1.log)(`Cleared signature cache for address: ${address.substring(0, 10)}...`);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
(0, logger_1.logError)("Error clearing signature cache from localStorage:", error);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// Clear all signature caches
|
|
128
|
+
this.signatureCache.clear();
|
|
129
|
+
try {
|
|
130
|
+
// Find and remove all shogun_bitcoin_sig_ keys
|
|
131
|
+
const keysToRemove = [];
|
|
132
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
133
|
+
const key = localStorage.key(i);
|
|
134
|
+
if (key && key.startsWith("shogun_bitcoin_sig_")) {
|
|
135
|
+
keysToRemove.push(key);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
keysToRemove.forEach((key) => localStorage.removeItem(key));
|
|
139
|
+
(0, logger_1.log)(`Cleared all signature caches (${keysToRemove.length} entries)`);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
(0, logger_1.logError)("Error clearing all signature caches from localStorage:", error);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Validates that the address is valid
|
|
148
|
+
*/
|
|
149
|
+
validateAddress(address) {
|
|
150
|
+
if (!address) {
|
|
151
|
+
throw new Error("Address not provided");
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
const normalizedAddress = String(address).trim();
|
|
155
|
+
// Basic validation for Bitcoin addresses and Nostr pubkeys
|
|
156
|
+
if (this.connectedType === "nostr") {
|
|
157
|
+
// Nostr pubkeys are typically hex strings starting with 'npub' when encoded
|
|
158
|
+
if (!/^(npub1|[0-9a-f]{64})/.test(normalizedAddress)) {
|
|
159
|
+
throw new Error("Invalid Nostr public key format");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
// Simple format check for Bitcoin addresses
|
|
164
|
+
// More sophisticated validation would require a library
|
|
165
|
+
if (!/^(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,59}$/.test(normalizedAddress)) {
|
|
166
|
+
throw new Error("Invalid Bitcoin address format");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return normalizedAddress;
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
errorHandler_1.ErrorHandler.handle(errorHandler_1.ErrorType.VALIDATION, "INVALID_ADDRESS", "Invalid Bitcoin address provided", error);
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Check if Alby extension is available
|
|
178
|
+
* @deprecated Alby support is deprecated, use Nostr instead
|
|
179
|
+
*/
|
|
180
|
+
isAlbyAvailable() {
|
|
181
|
+
(0, logger_1.logWarn)("Alby support is deprecated, use Nostr instead");
|
|
182
|
+
// Return false to encourage using Nostr
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Check if Nostr extension is available
|
|
187
|
+
*/
|
|
188
|
+
isNostrExtensionAvailable() {
|
|
189
|
+
return typeof window !== "undefined" && !!window.nostr;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Check if any Bitcoin wallet is available
|
|
193
|
+
*/
|
|
194
|
+
isAvailable() {
|
|
195
|
+
return this.isNostrExtensionAvailable() || this.manualKeyPair !== null;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Connect to a wallet type
|
|
199
|
+
*/
|
|
200
|
+
async connectWallet(type = "nostr") {
|
|
201
|
+
(0, logger_1.log)(`Connecting to Bitcoin wallet via ${type}...`);
|
|
202
|
+
try {
|
|
203
|
+
let result;
|
|
204
|
+
// Attempt to connect to the specified wallet type
|
|
205
|
+
switch (type) {
|
|
206
|
+
case "alby":
|
|
207
|
+
(0, logger_1.log)("Alby is deprecated, redirecting to Nostr");
|
|
208
|
+
result = await this.connectNostr();
|
|
209
|
+
break;
|
|
210
|
+
case "nostr":
|
|
211
|
+
result = await this.connectNostr();
|
|
212
|
+
break;
|
|
213
|
+
case "manual":
|
|
214
|
+
result = await this.connectManual();
|
|
215
|
+
break;
|
|
216
|
+
default:
|
|
217
|
+
throw new Error(`Unsupported wallet type: ${type}`);
|
|
218
|
+
}
|
|
219
|
+
if (result.success && result.address) {
|
|
220
|
+
this.connectedAddress = result.address;
|
|
221
|
+
this.connectedType = type;
|
|
222
|
+
(0, logger_1.log)(`Successfully connected to ${type} wallet: ${result.address}`);
|
|
223
|
+
this.emit("wallet_connected", {
|
|
224
|
+
address: result.address,
|
|
225
|
+
type: this.connectedType,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
(0, logger_1.logError)(`Error connecting to ${type} wallet:`, error);
|
|
232
|
+
return {
|
|
233
|
+
success: false,
|
|
234
|
+
error: error.message || "Failed to connect to wallet",
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Connect to Nostr extension
|
|
240
|
+
*/
|
|
241
|
+
async connectNostr() {
|
|
242
|
+
if (!this.isNostrExtensionAvailable()) {
|
|
243
|
+
return {
|
|
244
|
+
success: false,
|
|
245
|
+
error: "Nostr extension is not available. Please install a Nostr compatible extension.",
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
try {
|
|
249
|
+
// Get public key from Nostr extension
|
|
250
|
+
const pubKey = await window.nostr.getPublicKey();
|
|
251
|
+
if (!pubKey) {
|
|
252
|
+
throw new Error("Could not get public key from Nostr extension");
|
|
253
|
+
}
|
|
254
|
+
this.connectedAddress = pubKey;
|
|
255
|
+
this.connectedType = "nostr";
|
|
256
|
+
// Emit connected event
|
|
257
|
+
this.emit("connected", { address: pubKey, type: "nostr" });
|
|
258
|
+
const username = `nostr_${pubKey.substring(0, 10)}`;
|
|
259
|
+
return {
|
|
260
|
+
success: true,
|
|
261
|
+
address: pubKey,
|
|
262
|
+
username,
|
|
263
|
+
extensionType: "nostr",
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
throw new Error(`Nostr connection error: ${error.message}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Set up manual key pair for connection
|
|
272
|
+
*/
|
|
273
|
+
async connectManual() {
|
|
274
|
+
// For manual connection, we'd need to have a keypair set
|
|
275
|
+
if (!this.manualKeyPair) {
|
|
276
|
+
return {
|
|
277
|
+
success: false,
|
|
278
|
+
error: "No manual key pair configured. Use setKeyPair() first.",
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
this.connectedAddress = this.manualKeyPair.address;
|
|
282
|
+
this.connectedType = "manual";
|
|
283
|
+
// Emit connected event
|
|
284
|
+
this.emit("connected", {
|
|
285
|
+
address: this.manualKeyPair.address,
|
|
286
|
+
type: "manual",
|
|
287
|
+
});
|
|
288
|
+
const username = `btc_${this.manualKeyPair.address.substring(0, 10)}`;
|
|
289
|
+
return {
|
|
290
|
+
success: true,
|
|
291
|
+
address: this.manualKeyPair.address,
|
|
292
|
+
username,
|
|
293
|
+
extensionType: "manual",
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Set a manual key pair for use
|
|
298
|
+
*/
|
|
299
|
+
setKeyPair(keyPair) {
|
|
300
|
+
this.manualKeyPair = keyPair;
|
|
301
|
+
if (keyPair.address) {
|
|
302
|
+
this.connectedAddress = keyPair.address;
|
|
303
|
+
this.connectedType = "manual";
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Generate credentials using the connected wallet
|
|
308
|
+
*/
|
|
309
|
+
async generateCredentials(address) {
|
|
310
|
+
(0, logger_1.logDebug)(`Generating credentials for address: ${address}`);
|
|
311
|
+
try {
|
|
312
|
+
// Validate the address
|
|
313
|
+
const validAddress = this.validateAddress(address);
|
|
314
|
+
// Check cache first
|
|
315
|
+
const cachedSignature = this.getCachedSignature(validAddress);
|
|
316
|
+
if (cachedSignature) {
|
|
317
|
+
(0, logger_1.logDebug)("Using cached signature");
|
|
318
|
+
return await this.generateCredentialsFromSignature(validAddress, cachedSignature);
|
|
319
|
+
}
|
|
320
|
+
// For consistent authentication, we need to use a deterministic approach
|
|
321
|
+
// that doesn't require a fresh signature each time
|
|
322
|
+
const message = this.MESSAGE_TO_SIGN;
|
|
323
|
+
let signature;
|
|
324
|
+
// NEW STRATEGY: Always try deterministic signature first for consistency
|
|
325
|
+
// This ensures that existing users get the same credentials even after localStorage.clear()
|
|
326
|
+
try {
|
|
327
|
+
// First, try to use a deterministic signature based on the address
|
|
328
|
+
// This ensures consistent credentials across sessions
|
|
329
|
+
signature = await this.generateDeterministicSignature(validAddress);
|
|
330
|
+
(0, logger_1.log)("Using deterministic signature for consistency");
|
|
331
|
+
// Cache this deterministic signature for future use
|
|
332
|
+
this.cacheSignature(validAddress, signature);
|
|
333
|
+
return await this.generateCredentialsFromSignature(validAddress, signature);
|
|
334
|
+
}
|
|
335
|
+
catch (deterministicError) {
|
|
336
|
+
(0, logger_1.logError)("Error generating deterministic signature:", deterministicError);
|
|
337
|
+
// Fallback to requesting a real signature only if deterministic fails
|
|
338
|
+
try {
|
|
339
|
+
signature = await this.requestSignatureWithTimeout(validAddress, message, this.config.timeout);
|
|
340
|
+
// Cache the signature for future use
|
|
341
|
+
this.cacheSignature(validAddress, signature);
|
|
342
|
+
(0, logger_1.log)("Using real Nostr signature as fallback");
|
|
343
|
+
}
|
|
344
|
+
catch (signError) {
|
|
345
|
+
(0, logger_1.logError)("Error requesting signature:", signError);
|
|
346
|
+
// Final fallback: use deterministic signature anyway
|
|
347
|
+
signature = await this.generateDeterministicSignature(validAddress);
|
|
348
|
+
this.cacheSignature(validAddress, signature);
|
|
349
|
+
(0, logger_1.log)("Using deterministic signature as final fallback");
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return await this.generateCredentialsFromSignature(validAddress, signature);
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
(0, logger_1.logError)("Error generating credentials:", error);
|
|
356
|
+
throw error;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Generate a deterministic signature for consistent authentication
|
|
361
|
+
*/
|
|
362
|
+
async generateDeterministicSignature(address) {
|
|
363
|
+
// Create a deterministic signature based on the address and a fixed message
|
|
364
|
+
// This ensures the same credentials are generated each time for the same address
|
|
365
|
+
const baseString = `${address}_${this.MESSAGE_TO_SIGN}_shogun_deterministic`;
|
|
366
|
+
// Simple hash function to create a deterministic signature
|
|
367
|
+
let hash = "";
|
|
368
|
+
let runningValue = 0;
|
|
369
|
+
for (let i = 0; i < baseString.length; i++) {
|
|
370
|
+
const charCode = baseString.charCodeAt(i);
|
|
371
|
+
runningValue = (runningValue * 31 + charCode) & 0xffffffff;
|
|
372
|
+
if (i % 4 === 3) {
|
|
373
|
+
hash += runningValue.toString(16).padStart(8, "0");
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
// Ensure we have exactly 128 characters (64 bytes in hex)
|
|
377
|
+
while (hash.length < 128) {
|
|
378
|
+
runningValue = (runningValue * 31 + hash.length) & 0xffffffff;
|
|
379
|
+
hash += runningValue.toString(16).padStart(8, "0");
|
|
380
|
+
}
|
|
381
|
+
// Ensure the result is exactly 128 characters and contains only valid hex characters
|
|
382
|
+
let deterministicSignature = hash.substring(0, 128);
|
|
383
|
+
// Double-check that it's a valid hex string
|
|
384
|
+
deterministicSignature = deterministicSignature
|
|
385
|
+
.toLowerCase()
|
|
386
|
+
.replace(/[^0-9a-f]/g, "0");
|
|
387
|
+
// Ensure it's exactly 128 characters
|
|
388
|
+
if (deterministicSignature.length < 128) {
|
|
389
|
+
deterministicSignature = deterministicSignature.padEnd(128, "0");
|
|
390
|
+
}
|
|
391
|
+
else if (deterministicSignature.length > 128) {
|
|
392
|
+
deterministicSignature = deterministicSignature.substring(0, 128);
|
|
393
|
+
}
|
|
394
|
+
(0, logger_1.log)(`Generated deterministic signature: ${deterministicSignature.substring(0, 16)}... (${deterministicSignature.length} chars)`);
|
|
395
|
+
return deterministicSignature;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Generate credentials from an existing signature
|
|
399
|
+
*/
|
|
400
|
+
async generateCredentialsFromSignature(address, signature) {
|
|
401
|
+
(0, logger_1.log)("Generating credentials from signature");
|
|
402
|
+
// Create deterministic username based on the address - similar to MetaMask's approach
|
|
403
|
+
const username = `${address.toLowerCase()}`;
|
|
404
|
+
// Create password from the signature
|
|
405
|
+
const password = await this.generatePassword(signature);
|
|
406
|
+
return {
|
|
407
|
+
username,
|
|
408
|
+
password,
|
|
409
|
+
message: this.MESSAGE_TO_SIGN,
|
|
410
|
+
signature,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Generate a password from a signature
|
|
415
|
+
*/
|
|
416
|
+
async generatePassword(signature) {
|
|
417
|
+
if (!signature) {
|
|
418
|
+
throw new Error("Invalid signature");
|
|
419
|
+
}
|
|
420
|
+
try {
|
|
421
|
+
// Create a deterministic hash from the signature
|
|
422
|
+
// Following a similar approach to the Ethereum connector for consistency
|
|
423
|
+
// Normalize the signature to ensure it's clean
|
|
424
|
+
const normalizedSig = signature.toLowerCase().replace(/[^a-f0-9]/g, "");
|
|
425
|
+
// Create a hash using a simple algorithm
|
|
426
|
+
// In a production environment, you would use a proper crypto library
|
|
427
|
+
// For example:
|
|
428
|
+
// const hashBuffer = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(normalizedSig));
|
|
429
|
+
// const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
430
|
+
// const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
431
|
+
// For now, implement a simple deterministic hash
|
|
432
|
+
let hash = "";
|
|
433
|
+
let runningValue = 0;
|
|
434
|
+
for (let i = 0; i < normalizedSig.length; i++) {
|
|
435
|
+
const charCode = normalizedSig.charCodeAt(i);
|
|
436
|
+
runningValue = (runningValue * 31 + charCode) & 0xffffffff;
|
|
437
|
+
if (i % 8 === 7) {
|
|
438
|
+
hash += runningValue.toString(16).padStart(8, "0");
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
// Ensure we have at least 64 characters
|
|
442
|
+
while (hash.length < 64) {
|
|
443
|
+
runningValue = (runningValue * 31 + hash.length) & 0xffffffff;
|
|
444
|
+
hash += runningValue.toString(16).padStart(8, "0");
|
|
445
|
+
}
|
|
446
|
+
// Trim to 64 characters (matching the Ethereum approach that returns 64 chars)
|
|
447
|
+
return hash.substring(0, 64);
|
|
448
|
+
}
|
|
449
|
+
catch (error) {
|
|
450
|
+
(0, logger_1.logError)("Error generating password:", error);
|
|
451
|
+
throw new Error("Failed to generate password from signature");
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Verify a signature
|
|
456
|
+
*/
|
|
457
|
+
async verifySignature(message, signature, address) {
|
|
458
|
+
try {
|
|
459
|
+
(0, logger_1.log)(`Verifying signature for address: ${address}`);
|
|
460
|
+
if (!signature || !message) {
|
|
461
|
+
(0, logger_1.logError)("Invalid message or signature for verification");
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
464
|
+
// Log signature details for debugging
|
|
465
|
+
(0, logger_1.log)(`Signature to verify: ${signature.substring(0, 20)}... (length: ${signature.length})`);
|
|
466
|
+
(0, logger_1.log)(`Message to verify: ${message}`);
|
|
467
|
+
// For Nostr wallet type, we need to use a proper verification approach
|
|
468
|
+
if (this.connectedType === "nostr" || this.connectedType === "alby") {
|
|
469
|
+
(0, logger_1.log)("Using Nostr-specific verification");
|
|
470
|
+
try {
|
|
471
|
+
// In a production implementation, we would use proper nostr verification
|
|
472
|
+
// using secp256k1 to verify the signature
|
|
473
|
+
//
|
|
474
|
+
// For example with nostr-tools:
|
|
475
|
+
// import * as nostr from 'nostr-tools';
|
|
476
|
+
// const event = {
|
|
477
|
+
// kind: 1,
|
|
478
|
+
// created_at: Math.floor(Date.now() / 1000),
|
|
479
|
+
// tags: [],
|
|
480
|
+
// content: message,
|
|
481
|
+
// pubkey: address,
|
|
482
|
+
// id: '', // would be computed
|
|
483
|
+
// sig: signature
|
|
484
|
+
// };
|
|
485
|
+
// const verified = nostr.verifySignature(event);
|
|
486
|
+
// return verified;
|
|
487
|
+
// Instead for now, we're checking if the address matches what we have connected
|
|
488
|
+
// This ensures at least some level of verification
|
|
489
|
+
if (address.toLowerCase() !== this.connectedAddress?.toLowerCase()) {
|
|
490
|
+
(0, logger_1.logError)("Address mismatch in signature verification");
|
|
491
|
+
(0, logger_1.logError)(`Expected: ${this.connectedAddress}, Got: ${address}`);
|
|
492
|
+
return false;
|
|
493
|
+
}
|
|
494
|
+
// Basic verification that signature exists and is a hex string
|
|
495
|
+
const isValidHexFormat = /^[0-9a-f]+$/i.test(signature);
|
|
496
|
+
const hasValidLength = signature.length >= 64;
|
|
497
|
+
(0, logger_1.log)(`Signature format check: hex=${isValidHexFormat}, length=${hasValidLength} (${signature.length} chars)`);
|
|
498
|
+
if (!isValidHexFormat) {
|
|
499
|
+
(0, logger_1.logError)("Invalid signature format - not a valid hex string");
|
|
500
|
+
(0, logger_1.logError)(`Signature contains invalid characters: ${signature}`);
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
if (!hasValidLength) {
|
|
504
|
+
(0, logger_1.logError)(`Invalid signature length: ${signature.length} (minimum 64 required)`);
|
|
505
|
+
return false;
|
|
506
|
+
}
|
|
507
|
+
(0, logger_1.log)("Nostr signature appears valid");
|
|
508
|
+
return true;
|
|
509
|
+
}
|
|
510
|
+
catch (verifyError) {
|
|
511
|
+
(0, logger_1.logError)("Error in signature verification:", verifyError);
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
else if (this.connectedType === "manual" && this.manualKeyPair) {
|
|
516
|
+
(0, logger_1.log)("Using manual verification for keypair");
|
|
517
|
+
// For manual keypairs, implement proper signature verification
|
|
518
|
+
// For now, we're just checking that the address matches our keypair
|
|
519
|
+
try {
|
|
520
|
+
const addressMatch = address.toLowerCase() === this.manualKeyPair.address.toLowerCase();
|
|
521
|
+
(0, logger_1.log)(`Manual verification - address match: ${addressMatch}`);
|
|
522
|
+
return addressMatch;
|
|
523
|
+
}
|
|
524
|
+
catch (manualVerifyError) {
|
|
525
|
+
(0, logger_1.logError)("Error in manual signature verification:", manualVerifyError);
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
// For other wallet types or if API verification is enabled
|
|
530
|
+
if (this.config.useApi && this.config.apiUrl) {
|
|
531
|
+
(0, logger_1.log)("Using API-based verification");
|
|
532
|
+
try {
|
|
533
|
+
// In a real implementation, this would make an API call to verify
|
|
534
|
+
// return await this.verifySignatureViaApi(message, signature, address);
|
|
535
|
+
// For now, return true as this is a placeholder
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
catch (apiError) {
|
|
539
|
+
(0, logger_1.logError)("API verification error:", apiError);
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
(0, logger_1.logWarn)("No specific verification method available, signature cannot be fully verified");
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
546
|
+
catch (error) {
|
|
547
|
+
(0, logger_1.logError)("Error verifying signature:", error);
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Get the currently connected address
|
|
553
|
+
*/
|
|
554
|
+
getConnectedAddress() {
|
|
555
|
+
return this.connectedAddress;
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Get the currently connected wallet type
|
|
559
|
+
*/
|
|
560
|
+
getConnectedType() {
|
|
561
|
+
return this.connectedType;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Request signature with timeout
|
|
565
|
+
*/
|
|
566
|
+
requestSignatureWithTimeout(address, message, timeout = 30000) {
|
|
567
|
+
return new Promise((resolve, reject) => {
|
|
568
|
+
const timeoutId = setTimeout(() => {
|
|
569
|
+
reject(new Error("Signature request timed out"));
|
|
570
|
+
}, timeout);
|
|
571
|
+
this.requestSignature(address, message)
|
|
572
|
+
.then((signature) => {
|
|
573
|
+
clearTimeout(timeoutId);
|
|
574
|
+
resolve(signature);
|
|
575
|
+
})
|
|
576
|
+
.catch((error) => {
|
|
577
|
+
clearTimeout(timeoutId);
|
|
578
|
+
reject(error);
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Request a signature from the connected wallet
|
|
584
|
+
*/
|
|
585
|
+
async requestSignature(address, message) {
|
|
586
|
+
if (!this.connectedType) {
|
|
587
|
+
throw new Error("No wallet connected");
|
|
588
|
+
}
|
|
589
|
+
try {
|
|
590
|
+
switch (this.connectedType) {
|
|
591
|
+
case "alby":
|
|
592
|
+
// Redirect Alby requests to Nostr
|
|
593
|
+
(0, logger_1.logWarn)("Alby is deprecated, redirecting signature request to Nostr");
|
|
594
|
+
if (!window.nostr) {
|
|
595
|
+
throw new Error("Nostr extension not available");
|
|
596
|
+
}
|
|
597
|
+
// For Nostr, we need to create an event to sign
|
|
598
|
+
const albyRedirectEvent = {
|
|
599
|
+
kind: 1,
|
|
600
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
601
|
+
tags: [],
|
|
602
|
+
content: message,
|
|
603
|
+
pubkey: address,
|
|
604
|
+
};
|
|
605
|
+
const albyRedirectResult = await window.nostr.signEvent(albyRedirectEvent);
|
|
606
|
+
return albyRedirectResult.sig;
|
|
607
|
+
case "nostr":
|
|
608
|
+
(0, logger_1.log)("Requesting Nostr signature for message:", message);
|
|
609
|
+
// For Nostr, we need to create an event to sign
|
|
610
|
+
const nostrEvent = {
|
|
611
|
+
kind: 1,
|
|
612
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
613
|
+
tags: [],
|
|
614
|
+
content: message,
|
|
615
|
+
pubkey: address,
|
|
616
|
+
};
|
|
617
|
+
const nostrSignedEvent = await window.nostr.signEvent(nostrEvent);
|
|
618
|
+
(0, logger_1.log)("Received Nostr signature:", nostrSignedEvent.sig.substring(0, 20) + "...");
|
|
619
|
+
// Normalize the signature to ensure compatibility with GunDB
|
|
620
|
+
let signature = nostrSignedEvent.sig;
|
|
621
|
+
// Ensure the signature is in the expected format (hex string)
|
|
622
|
+
if (signature && typeof signature === "string") {
|
|
623
|
+
// Remove any non-hex characters and ensure lowercase
|
|
624
|
+
signature = signature.replace(/[^a-f0-9]/gi, "").toLowerCase();
|
|
625
|
+
// Ensure it's a valid length hex string (64 bytes = 128 hex chars for secp256k1)
|
|
626
|
+
if (signature.length > 128) {
|
|
627
|
+
signature = signature.substring(0, 128);
|
|
628
|
+
}
|
|
629
|
+
else if (signature.length < 128) {
|
|
630
|
+
// Pad with zeros if needed (shouldn't happen with valid signatures)
|
|
631
|
+
signature = signature.padStart(128, "0");
|
|
632
|
+
}
|
|
633
|
+
(0, logger_1.log)(`Normalized Nostr signature: ${signature.substring(0, 10)}...`);
|
|
634
|
+
}
|
|
635
|
+
return signature;
|
|
636
|
+
case "manual":
|
|
637
|
+
(0, logger_1.log)("Using manual key pair for signature");
|
|
638
|
+
if (!this.manualKeyPair) {
|
|
639
|
+
throw new Error("No manual key pair available");
|
|
640
|
+
}
|
|
641
|
+
// In a real implementation, we would use a Bitcoin signing library here
|
|
642
|
+
// For now, create a deterministic signature from the private key and message
|
|
643
|
+
const manualSignature = `${this.manualKeyPair.privateKey.substring(0, 32)}_${message}_${Date.now()}`;
|
|
644
|
+
(0, logger_1.log)("Generated manual signature:", manualSignature.substring(0, 20) + "...");
|
|
645
|
+
return manualSignature;
|
|
646
|
+
default:
|
|
647
|
+
throw new Error(`Unsupported wallet type: ${this.connectedType}`);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
catch (error) {
|
|
651
|
+
(0, logger_1.logError)("Error requesting signature:", error);
|
|
652
|
+
throw new Error(`Failed to get signature: ${error.message}`);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
exports.NostrConnector = NostrConnector;
|