@hardkas/accounts 0.12.0-rc.24 → 0.12.0-rc.26
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/dist/{authorizers-YESBCCTJ.js → authorizers-OAGU7NYR.js} +1 -1
- package/dist/{chunk-Q22JFUWC.js → chunk-G6PYXAIU.js} +304 -136
- package/dist/{chunk-2UQTGGML.js → chunk-IRNHRG4N.js} +11 -9
- package/dist/{chunk-PKDTLCCQ.js → chunk-YBCXJBA5.js} +6 -11
- package/dist/index.d.ts +22 -1
- package/dist/index.js +17 -12
- package/dist/kaspa-wasm-signer-RBV2EPB7.js +13 -0
- package/dist/{resolve-FO4P46YC.js → resolve-FPK6KS5J.js} +1 -1
- package/package.json +6 -6
- package/dist/chunk-YLG2NIAU.js +0 -166
- package/dist/dev-accounts-JTJKQAPV.js +0 -18
- package/dist/keystore-CMWEKGBF.js +0 -6
|
@@ -6,20 +6,288 @@ import {
|
|
|
6
6
|
} from "./chunk-BNF566PS.js";
|
|
7
7
|
import {
|
|
8
8
|
resolveHardkasAccount
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import {
|
|
11
|
-
KeystoreManager
|
|
12
|
-
} from "./chunk-YLG2NIAU.js";
|
|
9
|
+
} from "./chunk-IRNHRG4N.js";
|
|
13
10
|
|
|
14
|
-
// src/
|
|
11
|
+
// src/kaspa-wasm-signer.ts
|
|
12
|
+
import { recomputeDeclaredContentHash } from "@hardkas/artifacts";
|
|
13
|
+
import { getNetworkPrefix as getNetworkPrefix2 } from "@hardkas/core";
|
|
14
|
+
|
|
15
|
+
// src/keystore.ts
|
|
15
16
|
import fs from "fs";
|
|
16
17
|
import path from "path";
|
|
17
18
|
import crypto from "crypto";
|
|
18
|
-
import {
|
|
19
|
+
import { argon2id } from "hash-wasm";
|
|
20
|
+
import { writeFileAtomic, stripBom } from "@hardkas/core";
|
|
21
|
+
var KeystoreManager = class {
|
|
22
|
+
/**
|
|
23
|
+
* Keystore container format version. Separate from ARTIFACT_VERSION.
|
|
24
|
+
* This versions the encrypted keystore envelope, not HardKAS artifacts.
|
|
25
|
+
*/
|
|
26
|
+
static KEYSTORE_FORMAT_VERSION = "2.0.0";
|
|
27
|
+
static KEYSTORE_FORMAT_TYPE = "hardkas.encryptedKeystore.v2";
|
|
28
|
+
/**
|
|
29
|
+
* Creates an encrypted keystore from a payload and password.
|
|
30
|
+
*/
|
|
31
|
+
static async createEncryptedKeystore(payload, password, options) {
|
|
32
|
+
if (!password) throw new Error("Password cannot be empty.");
|
|
33
|
+
if (password.length < 8)
|
|
34
|
+
throw new Error("Password must be at least 8 characters long.");
|
|
35
|
+
const salt = crypto.randomBytes(16);
|
|
36
|
+
const nonce = crypto.randomBytes(12);
|
|
37
|
+
const iterations = options.iterations || 3;
|
|
38
|
+
const memory = options.memory || 65536;
|
|
39
|
+
const parallelism = options.parallelism || 1;
|
|
40
|
+
const derivedKeyHex = await argon2id({
|
|
41
|
+
password,
|
|
42
|
+
salt,
|
|
43
|
+
parallelism,
|
|
44
|
+
iterations,
|
|
45
|
+
memorySize: memory,
|
|
46
|
+
hashLength: 32,
|
|
47
|
+
// 256 bits for AES-256
|
|
48
|
+
outputType: "hex"
|
|
49
|
+
});
|
|
50
|
+
const derivedKey = Buffer.from(derivedKeyHex, "hex");
|
|
51
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", derivedKey, nonce);
|
|
52
|
+
const encryptedPayload = Buffer.concat([
|
|
53
|
+
cipher.update(JSON.stringify(payload), "utf8"),
|
|
54
|
+
cipher.final()
|
|
55
|
+
]);
|
|
56
|
+
const tag = cipher.getAuthTag();
|
|
57
|
+
derivedKey.fill(0);
|
|
58
|
+
return {
|
|
59
|
+
version: this.KEYSTORE_FORMAT_VERSION,
|
|
60
|
+
type: this.KEYSTORE_FORMAT_TYPE,
|
|
61
|
+
kdf: {
|
|
62
|
+
algorithm: "argon2id",
|
|
63
|
+
memory,
|
|
64
|
+
iterations,
|
|
65
|
+
parallelism,
|
|
66
|
+
salt: salt.toString("base64")
|
|
67
|
+
},
|
|
68
|
+
cipher: {
|
|
69
|
+
algorithm: "aes-256-gcm",
|
|
70
|
+
nonce: nonce.toString("base64"),
|
|
71
|
+
tag: tag.toString("base64")
|
|
72
|
+
},
|
|
73
|
+
encryptedPayload: encryptedPayload.toString("base64"),
|
|
74
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
75
|
+
metadata: {
|
|
76
|
+
label: options.label,
|
|
77
|
+
network: options.network,
|
|
78
|
+
address: payload.address
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Decrypts an encrypted keystore using a password.
|
|
84
|
+
*/
|
|
85
|
+
static async decryptEncryptedKeystore(keystore, password) {
|
|
86
|
+
if (keystore.version !== this.KEYSTORE_FORMAT_VERSION) {
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
error: `Unsupported keystore version: ${keystore.version}`
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const salt = Buffer.from(keystore.kdf.salt, "base64");
|
|
94
|
+
const nonce = Buffer.from(keystore.cipher.nonce, "base64");
|
|
95
|
+
const tag = Buffer.from(keystore.cipher.tag, "base64");
|
|
96
|
+
const encryptedData = Buffer.from(keystore.encryptedPayload, "base64");
|
|
97
|
+
const derivedKeyHex = await argon2id({
|
|
98
|
+
password,
|
|
99
|
+
salt,
|
|
100
|
+
parallelism: keystore.kdf.parallelism,
|
|
101
|
+
iterations: keystore.kdf.iterations,
|
|
102
|
+
memorySize: keystore.kdf.memory,
|
|
103
|
+
hashLength: 32,
|
|
104
|
+
outputType: "hex"
|
|
105
|
+
});
|
|
106
|
+
const derivedKey = Buffer.from(derivedKeyHex, "hex");
|
|
107
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", derivedKey, nonce);
|
|
108
|
+
decipher.setAuthTag(tag);
|
|
109
|
+
const decrypted = Buffer.concat([decipher.update(encryptedData), decipher.final()]);
|
|
110
|
+
derivedKey.fill(0);
|
|
111
|
+
const payload = JSON.parse(decrypted.toString("utf8"));
|
|
112
|
+
return { success: true, payload };
|
|
113
|
+
} catch (e) {
|
|
114
|
+
return { success: false, error: "Invalid password or corrupted keystore." };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Verifies if the password is correct for the keystore.
|
|
119
|
+
*/
|
|
120
|
+
static async verifyKeystorePassword(keystore, password) {
|
|
121
|
+
const result = await this.decryptEncryptedKeystore(keystore, password);
|
|
122
|
+
return result.success;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Changes the password of an encrypted keystore.
|
|
126
|
+
*/
|
|
127
|
+
static async changeKeystorePassword(keystore, oldPassword, newPassword) {
|
|
128
|
+
const unlock = await this.decryptEncryptedKeystore(keystore, oldPassword);
|
|
129
|
+
if (!unlock.success || !unlock.payload) {
|
|
130
|
+
throw new Error("Invalid current password.");
|
|
131
|
+
}
|
|
132
|
+
return this.createEncryptedKeystore(unlock.payload, newPassword, {
|
|
133
|
+
label: keystore.metadata.label,
|
|
134
|
+
network: keystore.metadata.network,
|
|
135
|
+
iterations: keystore.kdf.iterations,
|
|
136
|
+
memory: keystore.kdf.memory,
|
|
137
|
+
parallelism: keystore.kdf.parallelism
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Loads an encrypted keystore from the filesystem.
|
|
142
|
+
*/
|
|
143
|
+
static async loadEncryptedKeystore(filePath) {
|
|
144
|
+
try {
|
|
145
|
+
const data = await fs.promises.readFile(filePath, "utf-8");
|
|
146
|
+
const keystore = JSON.parse(stripBom(data));
|
|
147
|
+
if (keystore.type !== this.KEYSTORE_FORMAT_TYPE) {
|
|
148
|
+
throw new Error(`Invalid keystore type: ${keystore.type}`);
|
|
149
|
+
}
|
|
150
|
+
return keystore;
|
|
151
|
+
} catch (e) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
`Failed to load keystore at ${filePath}: ${e instanceof Error ? e instanceof Error ? e instanceof Error ? e.message : String(e) : String(e) : String(e)}`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Saves an encrypted keystore to the filesystem.
|
|
159
|
+
*/
|
|
160
|
+
static async saveEncryptedKeystore(filePath, keystore) {
|
|
161
|
+
try {
|
|
162
|
+
const dir = path.dirname(filePath);
|
|
163
|
+
if (!fs.existsSync(dir)) {
|
|
164
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
165
|
+
}
|
|
166
|
+
await writeFileAtomic(filePath, JSON.stringify(keystore, null, 2), {
|
|
167
|
+
encoding: "utf-8",
|
|
168
|
+
mode: 384
|
|
169
|
+
});
|
|
170
|
+
} catch (e) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
`Failed to save keystore at ${filePath}: ${e instanceof Error ? e instanceof Error ? e instanceof Error ? e.message : String(e) : String(e) : String(e)}`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
};
|
|
19
177
|
|
|
20
|
-
// src/
|
|
21
|
-
import
|
|
22
|
-
import
|
|
178
|
+
// src/dev-accounts.ts
|
|
179
|
+
import fs2 from "fs";
|
|
180
|
+
import path2 from "path";
|
|
181
|
+
import crypto2 from "crypto";
|
|
182
|
+
import { deterministicCompare, getNetworkPrefix, stripBom as stripBom2 } from "@hardkas/core";
|
|
183
|
+
var DEV_ACCOUNTS_PASSWORD = "hardkas-local-dev";
|
|
184
|
+
var SIMNET_DETERMINISTIC_SEED = "hardkas-deterministic-simnet-seed-v1";
|
|
185
|
+
async function ensureDevAccounts(workspaceDir) {
|
|
186
|
+
const devAccountsDir = path2.join(workspaceDir, ".hardkas", "dev-accounts");
|
|
187
|
+
if (!fs2.existsSync(devAccountsDir)) {
|
|
188
|
+
await fs2.promises.mkdir(devAccountsDir, { recursive: true });
|
|
189
|
+
}
|
|
190
|
+
await getOrCreateDevAccount(workspaceDir, 0, "alice");
|
|
191
|
+
await getOrCreateDevAccount(workspaceDir, 1, "bob");
|
|
192
|
+
await getOrCreateDevAccount(workspaceDir, 2, "carol");
|
|
193
|
+
await getOrCreateDevAccount(workspaceDir, 3, "dave");
|
|
194
|
+
await getOrCreateDevAccount(workspaceDir, 4, "erin");
|
|
195
|
+
}
|
|
196
|
+
async function getOrCreateDevAccount(workspaceDir, index, alias) {
|
|
197
|
+
const devAccountsDir = path2.join(workspaceDir, ".hardkas", "dev-accounts");
|
|
198
|
+
const filePath = path2.join(devAccountsDir, `${alias}.json`);
|
|
199
|
+
if (fs2.existsSync(filePath)) {
|
|
200
|
+
const keystore2 = await KeystoreManager.loadEncryptedKeystore(filePath);
|
|
201
|
+
const unlock = await KeystoreManager.decryptEncryptedKeystore(
|
|
202
|
+
keystore2,
|
|
203
|
+
DEV_ACCOUNTS_PASSWORD
|
|
204
|
+
);
|
|
205
|
+
if (!unlock.success || !unlock.payload) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
`Failed to decrypt dev account ${alias}. Expected password: ${DEV_ACCOUNTS_PASSWORD}`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
address: unlock.payload.address,
|
|
212
|
+
privateKey: unlock.payload.privateKey,
|
|
213
|
+
publicKey: unlock.payload.publicKey
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
const seedString = `${SIMNET_DETERMINISTIC_SEED}-${index}`;
|
|
217
|
+
const privateKeyHex = crypto2.createHash("sha256").update(seedString).digest("hex");
|
|
218
|
+
const network = "simnet";
|
|
219
|
+
const { loadKaspaWasm: loadKaspaWasm2 } = await import("./signer-backend-LHWY7ULI.js");
|
|
220
|
+
const kaspaWasm = await loadKaspaWasm2();
|
|
221
|
+
const kp = new kaspaWasm.PrivateKey(privateKeyHex).toKeypair();
|
|
222
|
+
const address = kp.toAddress(getNetworkPrefix(network)).toString();
|
|
223
|
+
const publicKey = kp.publicKey;
|
|
224
|
+
const privateKey = privateKeyHex;
|
|
225
|
+
const accountData = {
|
|
226
|
+
address,
|
|
227
|
+
privateKey,
|
|
228
|
+
publicKey
|
|
229
|
+
};
|
|
230
|
+
if (!fs2.existsSync(devAccountsDir)) {
|
|
231
|
+
await fs2.promises.mkdir(devAccountsDir, { recursive: true });
|
|
232
|
+
}
|
|
233
|
+
const payload = {
|
|
234
|
+
address: accountData.address,
|
|
235
|
+
privateKey: accountData.privateKey,
|
|
236
|
+
network: "simnet"
|
|
237
|
+
};
|
|
238
|
+
if (accountData.publicKey) {
|
|
239
|
+
payload.publicKey = accountData.publicKey;
|
|
240
|
+
}
|
|
241
|
+
const keystore = await KeystoreManager.createEncryptedKeystore(
|
|
242
|
+
payload,
|
|
243
|
+
DEV_ACCOUNTS_PASSWORD,
|
|
244
|
+
{
|
|
245
|
+
label: alias,
|
|
246
|
+
network: "simnet"
|
|
247
|
+
}
|
|
248
|
+
);
|
|
249
|
+
await KeystoreManager.saveEncryptedKeystore(filePath, keystore);
|
|
250
|
+
return accountData;
|
|
251
|
+
}
|
|
252
|
+
function listDevAccountsSync(workspaceDir) {
|
|
253
|
+
const devAccountsDir = path2.join(workspaceDir, ".hardkas", "dev-accounts");
|
|
254
|
+
if (!fs2.existsSync(devAccountsDir)) {
|
|
255
|
+
return [];
|
|
256
|
+
}
|
|
257
|
+
const accounts = [];
|
|
258
|
+
const files = fs2.readdirSync(devAccountsDir);
|
|
259
|
+
for (const file of files) {
|
|
260
|
+
if (file.endsWith(".json")) {
|
|
261
|
+
const name = path2.basename(file, ".json");
|
|
262
|
+
try {
|
|
263
|
+
const data = fs2.readFileSync(path2.join(devAccountsDir, file), "utf-8");
|
|
264
|
+
const keystore = JSON.parse(stripBom2(data));
|
|
265
|
+
if (keystore.type === "hardkas.encryptedKeystore.v2") {
|
|
266
|
+
accounts.push({
|
|
267
|
+
name,
|
|
268
|
+
address: keystore.metadata?.address || ""
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
} catch (e) {
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
accounts.sort((a, b) => deterministicCompare(a.name, b.name));
|
|
276
|
+
return accounts;
|
|
277
|
+
}
|
|
278
|
+
async function createDevSigner(workspaceDir, accountNameOrAddress) {
|
|
279
|
+
const account = resolveHardkasAccount({
|
|
280
|
+
nameOrAddress: accountNameOrAddress,
|
|
281
|
+
config: { cwd: workspaceDir }
|
|
282
|
+
});
|
|
283
|
+
if (account.kind !== "kaspa") {
|
|
284
|
+
throw new Error(`Account '${accountNameOrAddress}' is not a private key account, cannot create local dev signer.`);
|
|
285
|
+
}
|
|
286
|
+
return new KaspaWasmPrivateKeySigner({
|
|
287
|
+
account,
|
|
288
|
+
allowMainnet: false
|
|
289
|
+
});
|
|
290
|
+
}
|
|
23
291
|
|
|
24
292
|
// src/wasm-transaction.ts
|
|
25
293
|
function assertValueConservation(input) {
|
|
@@ -96,6 +364,20 @@ function planOutputsWithChange(plan) {
|
|
|
96
364
|
}
|
|
97
365
|
|
|
98
366
|
// src/kaspa-wasm-signer.ts
|
|
367
|
+
var keystoreError = (code, message) => Object.assign(new Error(`${code}: ${message}`), { code });
|
|
368
|
+
async function unlockAccountKeystore(account, password) {
|
|
369
|
+
const devAccount = account.keystoreKind === "dev-account";
|
|
370
|
+
const secret = devAccount ? DEV_ACCOUNTS_PASSWORD : password;
|
|
371
|
+
if (!secret) {
|
|
372
|
+
throw keystoreError("KEYSTORE_PASSWORD_REQUIRED", `account '${account.name}' is an encrypted keystore and no password was given to open it.`);
|
|
373
|
+
}
|
|
374
|
+
const keystore = await KeystoreManager.loadEncryptedKeystore(account.keystorePath);
|
|
375
|
+
const unlock = await KeystoreManager.decryptEncryptedKeystore(keystore, secret);
|
|
376
|
+
if (!unlock.success || !unlock.payload?.privateKey) {
|
|
377
|
+
throw devAccount ? keystoreError("DEV_ACCOUNT_KEY_UNAVAILABLE", `the development keystore of account '${account.name}' does not open.`) : keystoreError("KEYSTORE_PASSWORD_INVALID", `the password does not open the keystore of account '${account.name}' (wrong password, or a damaged keystore).`);
|
|
378
|
+
}
|
|
379
|
+
return unlock.payload.privateKey;
|
|
380
|
+
}
|
|
99
381
|
var KaspaWasmPrivateKeySigner = class {
|
|
100
382
|
constructor(options) {
|
|
101
383
|
this.options = options;
|
|
@@ -131,16 +413,10 @@ var KaspaWasmPrivateKeySigner = class {
|
|
|
131
413
|
pkValue = account.privateKey;
|
|
132
414
|
}
|
|
133
415
|
if (!pkValue && account.keystorePath) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const unlock = await KeystoreManager2.decryptEncryptedKeystore(keystore, DEV_ACCOUNTS_PASSWORD2);
|
|
139
|
-
if (unlock.success && unlock.payload) {
|
|
140
|
-
pkValue = unlock.payload.privateKey;
|
|
141
|
-
}
|
|
142
|
-
} catch (e) {
|
|
143
|
-
}
|
|
416
|
+
pkValue = await unlockAccountKeystore(
|
|
417
|
+
{ name: account.name, keystorePath: account.keystorePath, keystoreKind: account.keystoreKind },
|
|
418
|
+
this.options.keystorePassword
|
|
419
|
+
);
|
|
144
420
|
}
|
|
145
421
|
if (!pkValue) {
|
|
146
422
|
const err = new Error(`DEV_ACCOUNT_KEY_UNAVAILABLE: Missing required private key for account '${account.name}'.`);
|
|
@@ -152,8 +428,8 @@ var KaspaWasmPrivateKeySigner = class {
|
|
|
152
428
|
err.code = "INVALID_PRIVATE_KEY_MATERIAL";
|
|
153
429
|
throw err;
|
|
154
430
|
}
|
|
155
|
-
const expectedAddress = new sdk.PrivateKey(pkValue).toKeypair().toAddress(
|
|
156
|
-
const { PrivateKeyAuthorizer } = await import("./authorizers-
|
|
431
|
+
const expectedAddress = new sdk.PrivateKey(pkValue).toKeypair().toAddress(getNetworkPrefix2(plan.networkId || "simnet")).toString();
|
|
432
|
+
const { PrivateKeyAuthorizer } = await import("./authorizers-OAGU7NYR.js");
|
|
157
433
|
const sourceInputs = plan.inputs || plan.selectedUtxos || [];
|
|
158
434
|
for (let i = 0; i < numInputs; i++) {
|
|
159
435
|
if (!authorizers[i]) {
|
|
@@ -332,127 +608,19 @@ function assertSigningNetworkAllowed(input) {
|
|
|
332
608
|
}
|
|
333
609
|
}
|
|
334
610
|
|
|
335
|
-
// src/dev-accounts.ts
|
|
336
|
-
var DEV_ACCOUNTS_PASSWORD = "hardkas-local-dev";
|
|
337
|
-
var SIMNET_DETERMINISTIC_SEED = "hardkas-deterministic-simnet-seed-v1";
|
|
338
|
-
async function ensureDevAccounts(workspaceDir) {
|
|
339
|
-
const devAccountsDir = path.join(workspaceDir, ".hardkas", "dev-accounts");
|
|
340
|
-
if (!fs.existsSync(devAccountsDir)) {
|
|
341
|
-
await fs.promises.mkdir(devAccountsDir, { recursive: true });
|
|
342
|
-
}
|
|
343
|
-
await getOrCreateDevAccount(workspaceDir, 0, "alice");
|
|
344
|
-
await getOrCreateDevAccount(workspaceDir, 1, "bob");
|
|
345
|
-
await getOrCreateDevAccount(workspaceDir, 2, "carol");
|
|
346
|
-
await getOrCreateDevAccount(workspaceDir, 3, "dave");
|
|
347
|
-
await getOrCreateDevAccount(workspaceDir, 4, "erin");
|
|
348
|
-
}
|
|
349
|
-
async function getOrCreateDevAccount(workspaceDir, index, alias) {
|
|
350
|
-
const devAccountsDir = path.join(workspaceDir, ".hardkas", "dev-accounts");
|
|
351
|
-
const filePath = path.join(devAccountsDir, `${alias}.json`);
|
|
352
|
-
if (fs.existsSync(filePath)) {
|
|
353
|
-
const keystore2 = await KeystoreManager.loadEncryptedKeystore(filePath);
|
|
354
|
-
const unlock = await KeystoreManager.decryptEncryptedKeystore(
|
|
355
|
-
keystore2,
|
|
356
|
-
DEV_ACCOUNTS_PASSWORD
|
|
357
|
-
);
|
|
358
|
-
if (!unlock.success || !unlock.payload) {
|
|
359
|
-
throw new Error(
|
|
360
|
-
`Failed to decrypt dev account ${alias}. Expected password: ${DEV_ACCOUNTS_PASSWORD}`
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
return {
|
|
364
|
-
address: unlock.payload.address,
|
|
365
|
-
privateKey: unlock.payload.privateKey,
|
|
366
|
-
publicKey: unlock.payload.publicKey
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
const seedString = `${SIMNET_DETERMINISTIC_SEED}-${index}`;
|
|
370
|
-
const privateKeyHex = crypto.createHash("sha256").update(seedString).digest("hex");
|
|
371
|
-
const network = "simnet";
|
|
372
|
-
const { loadKaspaWasm: loadKaspaWasm2 } = await import("./signer-backend-LHWY7ULI.js");
|
|
373
|
-
const kaspaWasm = await loadKaspaWasm2();
|
|
374
|
-
const kp = new kaspaWasm.PrivateKey(privateKeyHex).toKeypair();
|
|
375
|
-
const address = kp.toAddress(getNetworkPrefix2(network)).toString();
|
|
376
|
-
const publicKey = kp.publicKey;
|
|
377
|
-
const privateKey = privateKeyHex;
|
|
378
|
-
const accountData = {
|
|
379
|
-
address,
|
|
380
|
-
privateKey,
|
|
381
|
-
publicKey
|
|
382
|
-
};
|
|
383
|
-
if (!fs.existsSync(devAccountsDir)) {
|
|
384
|
-
await fs.promises.mkdir(devAccountsDir, { recursive: true });
|
|
385
|
-
}
|
|
386
|
-
const payload = {
|
|
387
|
-
address: accountData.address,
|
|
388
|
-
privateKey: accountData.privateKey,
|
|
389
|
-
network: "simnet"
|
|
390
|
-
};
|
|
391
|
-
if (accountData.publicKey) {
|
|
392
|
-
payload.publicKey = accountData.publicKey;
|
|
393
|
-
}
|
|
394
|
-
const keystore = await KeystoreManager.createEncryptedKeystore(
|
|
395
|
-
payload,
|
|
396
|
-
DEV_ACCOUNTS_PASSWORD,
|
|
397
|
-
{
|
|
398
|
-
label: alias,
|
|
399
|
-
network: "simnet"
|
|
400
|
-
}
|
|
401
|
-
);
|
|
402
|
-
await KeystoreManager.saveEncryptedKeystore(filePath, keystore);
|
|
403
|
-
return accountData;
|
|
404
|
-
}
|
|
405
|
-
function listDevAccountsSync(workspaceDir) {
|
|
406
|
-
const devAccountsDir = path.join(workspaceDir, ".hardkas", "dev-accounts");
|
|
407
|
-
if (!fs.existsSync(devAccountsDir)) {
|
|
408
|
-
return [];
|
|
409
|
-
}
|
|
410
|
-
const accounts = [];
|
|
411
|
-
const files = fs.readdirSync(devAccountsDir);
|
|
412
|
-
for (const file of files) {
|
|
413
|
-
if (file.endsWith(".json")) {
|
|
414
|
-
const name = path.basename(file, ".json");
|
|
415
|
-
try {
|
|
416
|
-
const data = fs.readFileSync(path.join(devAccountsDir, file), "utf-8");
|
|
417
|
-
const keystore = JSON.parse(data);
|
|
418
|
-
if (keystore.type === "hardkas.encryptedKeystore.v2") {
|
|
419
|
-
accounts.push({
|
|
420
|
-
name,
|
|
421
|
-
address: keystore.metadata?.address || ""
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
} catch (e) {
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
accounts.sort((a, b) => deterministicCompare(a.name, b.name));
|
|
429
|
-
return accounts;
|
|
430
|
-
}
|
|
431
|
-
async function createDevSigner(workspaceDir, accountNameOrAddress) {
|
|
432
|
-
const account = resolveHardkasAccount({
|
|
433
|
-
nameOrAddress: accountNameOrAddress,
|
|
434
|
-
config: { cwd: workspaceDir }
|
|
435
|
-
});
|
|
436
|
-
if (account.kind !== "kaspa") {
|
|
437
|
-
throw new Error(`Account '${accountNameOrAddress}' is not a private key account, cannot create local dev signer.`);
|
|
438
|
-
}
|
|
439
|
-
return new KaspaWasmPrivateKeySigner({
|
|
440
|
-
account,
|
|
441
|
-
allowMainnet: false
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
|
|
445
611
|
export {
|
|
446
|
-
|
|
447
|
-
createBalancedTransaction,
|
|
448
|
-
createScriptTransaction,
|
|
449
|
-
toWasmScriptPublicKey,
|
|
450
|
-
planOutputsWithChange,
|
|
612
|
+
KeystoreManager,
|
|
451
613
|
DEV_ACCOUNTS_PASSWORD,
|
|
452
614
|
ensureDevAccounts,
|
|
453
615
|
getOrCreateDevAccount,
|
|
454
616
|
listDevAccountsSync,
|
|
455
617
|
createDevSigner,
|
|
618
|
+
assertValueConservation,
|
|
619
|
+
createBalancedTransaction,
|
|
620
|
+
createScriptTransaction,
|
|
621
|
+
toWasmScriptPublicKey,
|
|
622
|
+
planOutputsWithChange,
|
|
623
|
+
unlockAccountKeystore,
|
|
456
624
|
KaspaWasmPrivateKeySigner,
|
|
457
625
|
assertSigningNetworkAllowed
|
|
458
626
|
};
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import fs2 from "fs";
|
|
3
3
|
import path2 from "path";
|
|
4
4
|
import { createDeterministicAccounts } from "@hardkas/localnet";
|
|
5
|
-
import { CrossWorldAccountCollisionError, AccountNetworkMismatchError } from "@hardkas/core";
|
|
5
|
+
import { CrossWorldAccountCollisionError, AccountNetworkMismatchError, stripBom as stripBom2 } from "@hardkas/core";
|
|
6
6
|
|
|
7
7
|
// src/real-accounts.ts
|
|
8
8
|
import fs from "fs";
|
|
9
9
|
import path from "path";
|
|
10
10
|
import util from "util";
|
|
11
|
-
import { writeFileAtomicSync } from "@hardkas/core";
|
|
11
|
+
import { writeFileAtomicSync, stripBom } from "@hardkas/core";
|
|
12
12
|
import {
|
|
13
13
|
HARDKAS_VERSION,
|
|
14
14
|
ARTIFACT_SCHEMAS,
|
|
@@ -64,7 +64,7 @@ function loadRealAccountStoreSync(options) {
|
|
|
64
64
|
}
|
|
65
65
|
try {
|
|
66
66
|
const data = fs.readFileSync(filePath, "utf-8");
|
|
67
|
-
const store = JSON.parse(data);
|
|
67
|
+
const store = JSON.parse(stripBom(data));
|
|
68
68
|
for (const a of store.accounts) {
|
|
69
69
|
if ("privateKey" in a && a.privateKey !== void 0) {
|
|
70
70
|
if (a.privateKey === null || a.privateKey === "" || a.privateKey === "[object Object]" || typeof a.privateKey === "object" || typeof a.privateKey === "string" && a.privateKey.includes("__wbg_ptr") || typeof a.privateKey === "object" && "__wbg_ptr" in a.privateKey) {
|
|
@@ -326,7 +326,7 @@ function listHardkasAccounts(config, executionTarget) {
|
|
|
326
326
|
try {
|
|
327
327
|
const name = path2.basename(file, ".json");
|
|
328
328
|
const data = fs2.readFileSync(path2.join(devAccountsDir, file), "utf-8");
|
|
329
|
-
const keystore = JSON.parse(data);
|
|
329
|
+
const keystore = JSON.parse(stripBom2(data));
|
|
330
330
|
if (keystore.type === "hardkas.encryptedKeystore.v2") {
|
|
331
331
|
if (!keystore.metadata?.network) {
|
|
332
332
|
throw new AccountNetworkMismatchError({ expected: "known network", actual: "undefined", detail: `at ${path2.join(devAccountsDir, file)}` });
|
|
@@ -337,7 +337,8 @@ function listHardkasAccounts(config, executionTarget) {
|
|
|
337
337
|
kind: "synthetic",
|
|
338
338
|
executionMode: "simulator",
|
|
339
339
|
address: `kaspa:sim_${name}`,
|
|
340
|
-
keystorePath: path2.join(devAccountsDir, file)
|
|
340
|
+
keystorePath: path2.join(devAccountsDir, file),
|
|
341
|
+
keystoreKind: "dev-account"
|
|
341
342
|
});
|
|
342
343
|
} else {
|
|
343
344
|
accounts.set(name, {
|
|
@@ -345,7 +346,8 @@ function listHardkasAccounts(config, executionTarget) {
|
|
|
345
346
|
kind: "kaspa",
|
|
346
347
|
network: keystore.metadata.network,
|
|
347
348
|
address: keystore.payload?.address || keystore.metadata?.address,
|
|
348
|
-
keystorePath: path2.join(devAccountsDir, file)
|
|
349
|
+
keystorePath: path2.join(devAccountsDir, file),
|
|
350
|
+
keystoreKind: "dev-account"
|
|
349
351
|
});
|
|
350
352
|
}
|
|
351
353
|
}
|
|
@@ -359,7 +361,7 @@ function listHardkasAccounts(config, executionTarget) {
|
|
|
359
361
|
if (fs2.existsSync(keystoreJsonPath)) {
|
|
360
362
|
try {
|
|
361
363
|
const data = fs2.readFileSync(keystoreJsonPath, "utf-8");
|
|
362
|
-
const ks = JSON.parse(data);
|
|
364
|
+
const ks = JSON.parse(stripBom2(data));
|
|
363
365
|
for (const [name, acc] of Object.entries(ks)) {
|
|
364
366
|
if (acc.type === "simulated") {
|
|
365
367
|
if (targetMode === "simulator") {
|
|
@@ -400,7 +402,7 @@ function listHardkasAccounts(config, executionTarget) {
|
|
|
400
402
|
});
|
|
401
403
|
}
|
|
402
404
|
}
|
|
403
|
-
const keystoreDir = path2.join(
|
|
405
|
+
const keystoreDir = path2.join(workspaceRoot, ".hardkas", "keystore");
|
|
404
406
|
if (fs2.existsSync(keystoreDir)) {
|
|
405
407
|
const files = fs2.readdirSync(keystoreDir);
|
|
406
408
|
for (const file of files) {
|
|
@@ -408,7 +410,7 @@ function listHardkasAccounts(config, executionTarget) {
|
|
|
408
410
|
try {
|
|
409
411
|
const name = path2.basename(file, ".json");
|
|
410
412
|
const data = fs2.readFileSync(path2.join(keystoreDir, file), "utf-8");
|
|
411
|
-
const keystore = JSON.parse(data);
|
|
413
|
+
const keystore = JSON.parse(stripBom2(data));
|
|
412
414
|
if (keystore.type === "hardkas.encryptedKeystore.v2") {
|
|
413
415
|
if (!keystore.metadata?.network) {
|
|
414
416
|
throw new AccountNetworkMismatchError({ expected: "known network", actual: "undefined", detail: `at ${path2.join(keystoreDir, file)}` });
|
|
@@ -62,7 +62,7 @@ var LazyAccountAuthorizer = class {
|
|
|
62
62
|
accountName;
|
|
63
63
|
workspaceRoot;
|
|
64
64
|
async authorize(context) {
|
|
65
|
-
const { resolveHardkasAccount } = await import("./resolve-
|
|
65
|
+
const { resolveHardkasAccount } = await import("./resolve-FPK6KS5J.js");
|
|
66
66
|
const account = await resolveHardkasAccount({
|
|
67
67
|
nameOrAddress: this.accountName,
|
|
68
68
|
config: { cwd: this.workspaceRoot }
|
|
@@ -72,16 +72,11 @@ var LazyAccountAuthorizer = class {
|
|
|
72
72
|
pkValue = account.privateKey;
|
|
73
73
|
}
|
|
74
74
|
if (!pkValue && account.keystorePath) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (unlock.success && unlock.payload) {
|
|
81
|
-
pkValue = unlock.payload.privateKey;
|
|
82
|
-
}
|
|
83
|
-
} catch (e) {
|
|
84
|
-
}
|
|
75
|
+
const { unlockAccountKeystore } = await import("./kaspa-wasm-signer-RBV2EPB7.js");
|
|
76
|
+
pkValue = await unlockAccountKeystore(
|
|
77
|
+
{ name: this.accountName, keystorePath: account.keystorePath, keystoreKind: account.keystoreKind },
|
|
78
|
+
void 0
|
|
79
|
+
);
|
|
85
80
|
}
|
|
86
81
|
if (!pkValue) {
|
|
87
82
|
throw new Error(`DEV_ACCOUNT_KEY_UNAVAILABLE: Missing required private key for account '${this.accountName}'.`);
|
package/dist/index.d.ts
CHANGED
|
@@ -101,6 +101,12 @@ interface HardkasKaspaAccount extends HardkasBaseAccount {
|
|
|
101
101
|
privateKeyEnv?: string;
|
|
102
102
|
privateKey?: string;
|
|
103
103
|
keystorePath?: string;
|
|
104
|
+
/**
|
|
105
|
+
* Set by the resolver for the workspace's development accounts (`.hardkas/dev-accounts/`), whose
|
|
106
|
+
* keystores use the published development password. Any other keystore opens only with its owner's
|
|
107
|
+
* password, given explicitly.
|
|
108
|
+
*/
|
|
109
|
+
keystoreKind?: "dev-account";
|
|
104
110
|
address: string;
|
|
105
111
|
}
|
|
106
112
|
interface HardkasExternalWalletAccount extends HardkasBaseAccount {
|
|
@@ -198,6 +204,8 @@ declare function signTxPlanArtifact(input: {
|
|
|
198
204
|
authorizers?: Readonly<Record<number, TxInputAuthorizer>>;
|
|
199
205
|
config?: HardkasConfig;
|
|
200
206
|
allowMainnet?: boolean;
|
|
207
|
+
/** The password of the account's keystore, in memory only: never stored, logged or echoed. */
|
|
208
|
+
keystorePassword?: string;
|
|
201
209
|
}): Promise<SignedTxArtifact>;
|
|
202
210
|
|
|
203
211
|
interface KaspaSigningBackendStatus {
|
|
@@ -607,7 +615,20 @@ interface KaspaWasmSignerOptions {
|
|
|
607
615
|
account?: HardkasKaspaAccount;
|
|
608
616
|
allowMainnet?: boolean;
|
|
609
617
|
wasmConfig?: WasmProviderConfig;
|
|
618
|
+
/** The password of the account's keystore, in memory only: never stored, logged or echoed. */
|
|
619
|
+
keystorePassword?: string;
|
|
610
620
|
}
|
|
621
|
+
/**
|
|
622
|
+
* Opens the keystore of `account` and returns its private key. A development account (marked by the
|
|
623
|
+
* resolver for `.hardkas/dev-accounts/`) opens with the published development password; any other
|
|
624
|
+
* keystore only with `password`, its owner's, given explicitly. Nothing is tried silently: a missing
|
|
625
|
+
* password is KEYSTORE_PASSWORD_REQUIRED, one that does not open the keystore KEYSTORE_PASSWORD_INVALID.
|
|
626
|
+
*/
|
|
627
|
+
declare function unlockAccountKeystore(account: {
|
|
628
|
+
name: string;
|
|
629
|
+
keystorePath: string;
|
|
630
|
+
keystoreKind?: "dev-account" | undefined;
|
|
631
|
+
}, password: string | undefined): Promise<string>;
|
|
611
632
|
/**
|
|
612
633
|
* Real Kaspa signer using the official WASM SDK (the pinned managed toolchain).
|
|
613
634
|
*/
|
|
@@ -925,4 +946,4 @@ declare class WalletStateStoreJson {
|
|
|
925
946
|
nextChangeIndex(walletId: string): number;
|
|
926
947
|
}
|
|
927
948
|
|
|
928
|
-
export { AddressManager, type BalancedOutput, type ChainType, type CovenantFundingInput, type CovenantGenesisRequest, type CovenantTransactionResult, type CovenantTransitionRequest, type CovenantUtxo, type CreateKaspaWalletOptions, DEV_ACCOUNTS_PASSWORD, type DeriveRequest, type DerivedAddress, type EncryptedKeystoreV2, type EvmExportResult, type GeneratedKaspaDevAccount, type HardkasAccount, type HardkasAccountKind, type HardkasBaseAccount, type HardkasEvmPrivateKeyAccount, type HardkasExternalWalletAccount, type HardkasKaspaAccount, type HardkasSigner, type HardkasSignerKind, type HardkasSyntheticAccount, type HardkasTxPlanSigner, type HelperDeriveRequest, type InputAuthorization, type KaspaKeyGenerator, KaspaSdkKeyGenerator, type KaspaSdkKeyGeneratorOptions, KaspaSdkRealTxSigner, type KaspaSdkRealTxSignerOptions, type KaspaSigningBackendStatus, KaspaWasmPrivateKeySigner, type KaspaWasmSignerOptions, type KeystoreCipherParams, type KeystoreKdfParams, KeystoreManager, type KeystorePayload, type KeystoreUnlockResult, LazyAccountAuthorizer, type NetworkType, type PathRequest, PrivateKeyAuthorizer, type RealAccountStore, type RealDevAccount, type RealTxSigner, type RealTxSigningInput, type RealTxSigningResult, SILVER_SPEND_DRAFT_SCHEMA, type ScriptFundingRequest, type ScriptFundingResult, type ScriptOutput, type SignTxPlanInput, type SignTxPlanResult, type SilverContractUtxo, type SilverDraftArg, type SilverSignatureArg, type SilverSignerSlot, type SilverSpendArg, type SilverSpendDraft, type SilverSpendDraftRequest, type SilverSpendResult, type SilverSweepRequest, SimulatedSigner, StaticSignatureScriptAuthorizer, type TxInputAuthorizationContext, type TxInputAuthorizer, UnsupportedKaspaKeyGenerator, UnsupportedRealKaspaSigner, UnsupportedRealTxSigner, type WalletArtifact, type WalletClaims, type WalletCreateRequest, type WalletImportRequest, WalletManager, WalletManagerImpl, type WalletMetadata, type WalletState, WalletStateStoreJson, type WalletStateStoreOptions, type WasmInputSigner, type WasmProviderConfig, type WasmUtxoEntrySource, type WasmUtxoInput, appendToKeystoreJson, assertAccountCompatible, assertSigningNetworkAllowed, assertValueConservation, attachInspectRedactor, buildCovenantGenesis, buildCovenantTransition, buildScriptFunding, buildSilverSweep, createBalancedTransaction, createDevSigner, createEmptyRealAccountStore, createLocalKaspaWallet, createScriptTransaction, describeAccount, detectCapabilities, ensureDevAccounts, finalizeSilverSpend, getDefaultRealAccountsPath, getKaspaSigningBackendStatus, getOrCreateDevAccount, getRealDevAccount, getRequiredEnv, importRealDevAccount, listDevAccountsSync, listHardkasAccounts, listRealDevAccounts, loadKaspaWasm, loadKaspaWasmSync, loadOrCreateRealAccountStore, loadRealAccountStore, loadRealAccountStoreSync, planOutputsWithChange, prepareEvmAccountExport, prepareSilverSpend, redactAccountForLog, removeRealDevAccount, resolveHardkasAccount, resolveHardkasAccountAddress, resolveRealAccountOrAddress, saveRealAccountStore, signSilverSpend, signTxPlanArtifact, toWasmScriptPublicKey, validateAccountName, validateAddressNetwork, validateAddressPrefix, withKeystoreLock };
|
|
949
|
+
export { AddressManager, type BalancedOutput, type ChainType, type CovenantFundingInput, type CovenantGenesisRequest, type CovenantTransactionResult, type CovenantTransitionRequest, type CovenantUtxo, type CreateKaspaWalletOptions, DEV_ACCOUNTS_PASSWORD, type DeriveRequest, type DerivedAddress, type EncryptedKeystoreV2, type EvmExportResult, type GeneratedKaspaDevAccount, type HardkasAccount, type HardkasAccountKind, type HardkasBaseAccount, type HardkasEvmPrivateKeyAccount, type HardkasExternalWalletAccount, type HardkasKaspaAccount, type HardkasSigner, type HardkasSignerKind, type HardkasSyntheticAccount, type HardkasTxPlanSigner, type HelperDeriveRequest, type InputAuthorization, type KaspaKeyGenerator, KaspaSdkKeyGenerator, type KaspaSdkKeyGeneratorOptions, KaspaSdkRealTxSigner, type KaspaSdkRealTxSignerOptions, type KaspaSigningBackendStatus, KaspaWasmPrivateKeySigner, type KaspaWasmSignerOptions, type KeystoreCipherParams, type KeystoreKdfParams, KeystoreManager, type KeystorePayload, type KeystoreUnlockResult, LazyAccountAuthorizer, type NetworkType, type PathRequest, PrivateKeyAuthorizer, type RealAccountStore, type RealDevAccount, type RealTxSigner, type RealTxSigningInput, type RealTxSigningResult, SILVER_SPEND_DRAFT_SCHEMA, type ScriptFundingRequest, type ScriptFundingResult, type ScriptOutput, type SignTxPlanInput, type SignTxPlanResult, type SilverContractUtxo, type SilverDraftArg, type SilverSignatureArg, type SilverSignerSlot, type SilverSpendArg, type SilverSpendDraft, type SilverSpendDraftRequest, type SilverSpendResult, type SilverSweepRequest, SimulatedSigner, StaticSignatureScriptAuthorizer, type TxInputAuthorizationContext, type TxInputAuthorizer, UnsupportedKaspaKeyGenerator, UnsupportedRealKaspaSigner, UnsupportedRealTxSigner, type WalletArtifact, type WalletClaims, type WalletCreateRequest, type WalletImportRequest, WalletManager, WalletManagerImpl, type WalletMetadata, type WalletState, WalletStateStoreJson, type WalletStateStoreOptions, type WasmInputSigner, type WasmProviderConfig, type WasmUtxoEntrySource, type WasmUtxoInput, appendToKeystoreJson, assertAccountCompatible, assertSigningNetworkAllowed, assertValueConservation, attachInspectRedactor, buildCovenantGenesis, buildCovenantTransition, buildScriptFunding, buildSilverSweep, createBalancedTransaction, createDevSigner, createEmptyRealAccountStore, createLocalKaspaWallet, createScriptTransaction, describeAccount, detectCapabilities, ensureDevAccounts, finalizeSilverSpend, getDefaultRealAccountsPath, getKaspaSigningBackendStatus, getOrCreateDevAccount, getRealDevAccount, getRequiredEnv, importRealDevAccount, listDevAccountsSync, listHardkasAccounts, listRealDevAccounts, loadKaspaWasm, loadKaspaWasmSync, loadOrCreateRealAccountStore, loadRealAccountStore, loadRealAccountStoreSync, planOutputsWithChange, prepareEvmAccountExport, prepareSilverSpend, redactAccountForLog, removeRealDevAccount, resolveHardkasAccount, resolveHardkasAccountAddress, resolveRealAccountOrAddress, saveRealAccountStore, signSilverSpend, signTxPlanArtifact, toWasmScriptPublicKey, unlockAccountKeystore, validateAccountName, validateAddressNetwork, validateAddressPrefix, withKeystoreLock };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LazyAccountAuthorizer,
|
|
3
|
+
PrivateKeyAuthorizer,
|
|
4
|
+
StaticSignatureScriptAuthorizer
|
|
5
|
+
} from "./chunk-YBCXJBA5.js";
|
|
1
6
|
import {
|
|
2
7
|
DEV_ACCOUNTS_PASSWORD,
|
|
3
8
|
KaspaWasmPrivateKeySigner,
|
|
9
|
+
KeystoreManager,
|
|
4
10
|
assertSigningNetworkAllowed,
|
|
5
11
|
assertValueConservation,
|
|
6
12
|
createBalancedTransaction,
|
|
@@ -10,8 +16,9 @@ import {
|
|
|
10
16
|
getOrCreateDevAccount,
|
|
11
17
|
listDevAccountsSync,
|
|
12
18
|
planOutputsWithChange,
|
|
13
|
-
toWasmScriptPublicKey
|
|
14
|
-
|
|
19
|
+
toWasmScriptPublicKey,
|
|
20
|
+
unlockAccountKeystore
|
|
21
|
+
} from "./chunk-G6PYXAIU.js";
|
|
15
22
|
import {
|
|
16
23
|
parseWasmTxToRpc
|
|
17
24
|
} from "./chunk-GM2BY4VS.js";
|
|
@@ -43,15 +50,7 @@ import {
|
|
|
43
50
|
validateAccountName,
|
|
44
51
|
validateAddressNetwork,
|
|
45
52
|
validateAddressPrefix
|
|
46
|
-
} from "./chunk-
|
|
47
|
-
import {
|
|
48
|
-
KeystoreManager
|
|
49
|
-
} from "./chunk-YLG2NIAU.js";
|
|
50
|
-
import {
|
|
51
|
-
LazyAccountAuthorizer,
|
|
52
|
-
PrivateKeyAuthorizer,
|
|
53
|
-
StaticSignatureScriptAuthorizer
|
|
54
|
-
} from "./chunk-PKDTLCCQ.js";
|
|
53
|
+
} from "./chunk-IRNHRG4N.js";
|
|
55
54
|
|
|
56
55
|
// src/simulated.ts
|
|
57
56
|
var SimulatedSigner = class {
|
|
@@ -196,6 +195,9 @@ async function signTxPlanArtifact(input) {
|
|
|
196
195
|
if (input.config?.wasm) {
|
|
197
196
|
signerOptions.wasmConfig = input.config.wasm;
|
|
198
197
|
}
|
|
198
|
+
if (input.keystorePassword !== void 0) {
|
|
199
|
+
signerOptions.keystorePassword = input.keystorePassword;
|
|
200
|
+
}
|
|
199
201
|
const signer = new KaspaWasmPrivateKeySigner(signerOptions);
|
|
200
202
|
const result = await signer.signTxPlan({
|
|
201
203
|
planArtifact,
|
|
@@ -784,8 +786,10 @@ var KaspaSdkKeyGenerator = class {
|
|
|
784
786
|
try {
|
|
785
787
|
return await rawLoader();
|
|
786
788
|
} catch (e) {
|
|
789
|
+
if (e?.code === "WASM_TOOLCHAIN_NOT_INSTALLED" || e?.code === "WASM_TOOLCHAIN_INTEGRITY_FAILED") throw e;
|
|
787
790
|
const err = new Error(
|
|
788
|
-
|
|
791
|
+
`WALLET_BACKEND_UNAVAILABLE: the Kaspa WASM SDK could not be loaded, so real accounts cannot be generated (${e?.message ?? String(e)}).
|
|
792
|
+
Check it with: hardkas toolchain status`
|
|
789
793
|
);
|
|
790
794
|
err.code = "WALLET_BACKEND_UNAVAILABLE";
|
|
791
795
|
throw err;
|
|
@@ -1289,6 +1293,7 @@ export {
|
|
|
1289
1293
|
signSilverSpend,
|
|
1290
1294
|
signTxPlanArtifact,
|
|
1291
1295
|
toWasmScriptPublicKey,
|
|
1296
|
+
unlockAccountKeystore,
|
|
1292
1297
|
validateAccountName,
|
|
1293
1298
|
validateAddressNetwork,
|
|
1294
1299
|
validateAddressPrefix,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
KaspaWasmPrivateKeySigner,
|
|
3
|
+
assertSigningNetworkAllowed,
|
|
4
|
+
unlockAccountKeystore
|
|
5
|
+
} from "./chunk-G6PYXAIU.js";
|
|
6
|
+
import "./chunk-GM2BY4VS.js";
|
|
7
|
+
import "./chunk-BNF566PS.js";
|
|
8
|
+
import "./chunk-IRNHRG4N.js";
|
|
9
|
+
export {
|
|
10
|
+
KaspaWasmPrivateKeySigner,
|
|
11
|
+
assertSigningNetworkAllowed,
|
|
12
|
+
unlockAccountKeystore
|
|
13
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hardkas/accounts",
|
|
3
|
-
"version": "0.12.0-rc.
|
|
3
|
+
"version": "0.12.0-rc.26",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"hash-wasm": "^4.12.0",
|
|
26
|
-
"@hardkas/
|
|
27
|
-
"@hardkas/
|
|
28
|
-
"@hardkas/
|
|
29
|
-
"@hardkas/
|
|
30
|
-
"@hardkas/tx-builder": "0.12.0-rc.
|
|
26
|
+
"@hardkas/core": "0.12.0-rc.26",
|
|
27
|
+
"@hardkas/artifacts": "0.12.0-rc.26",
|
|
28
|
+
"@hardkas/localnet": "0.12.0-rc.26",
|
|
29
|
+
"@hardkas/config": "0.12.0-rc.26",
|
|
30
|
+
"@hardkas/tx-builder": "0.12.0-rc.26"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"tsup": "^8.3.5",
|
package/dist/chunk-YLG2NIAU.js
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
// src/keystore.ts
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import crypto from "crypto";
|
|
5
|
-
import { argon2id } from "hash-wasm";
|
|
6
|
-
import { writeFileAtomic } from "@hardkas/core";
|
|
7
|
-
var KeystoreManager = class {
|
|
8
|
-
/**
|
|
9
|
-
* Keystore container format version. Separate from ARTIFACT_VERSION.
|
|
10
|
-
* This versions the encrypted keystore envelope, not HardKAS artifacts.
|
|
11
|
-
*/
|
|
12
|
-
static KEYSTORE_FORMAT_VERSION = "2.0.0";
|
|
13
|
-
static KEYSTORE_FORMAT_TYPE = "hardkas.encryptedKeystore.v2";
|
|
14
|
-
/**
|
|
15
|
-
* Creates an encrypted keystore from a payload and password.
|
|
16
|
-
*/
|
|
17
|
-
static async createEncryptedKeystore(payload, password, options) {
|
|
18
|
-
if (!password) throw new Error("Password cannot be empty.");
|
|
19
|
-
if (password.length < 8)
|
|
20
|
-
throw new Error("Password must be at least 8 characters long.");
|
|
21
|
-
const salt = crypto.randomBytes(16);
|
|
22
|
-
const nonce = crypto.randomBytes(12);
|
|
23
|
-
const iterations = options.iterations || 3;
|
|
24
|
-
const memory = options.memory || 65536;
|
|
25
|
-
const parallelism = options.parallelism || 1;
|
|
26
|
-
const derivedKeyHex = await argon2id({
|
|
27
|
-
password,
|
|
28
|
-
salt,
|
|
29
|
-
parallelism,
|
|
30
|
-
iterations,
|
|
31
|
-
memorySize: memory,
|
|
32
|
-
hashLength: 32,
|
|
33
|
-
// 256 bits for AES-256
|
|
34
|
-
outputType: "hex"
|
|
35
|
-
});
|
|
36
|
-
const derivedKey = Buffer.from(derivedKeyHex, "hex");
|
|
37
|
-
const cipher = crypto.createCipheriv("aes-256-gcm", derivedKey, nonce);
|
|
38
|
-
const encryptedPayload = Buffer.concat([
|
|
39
|
-
cipher.update(JSON.stringify(payload), "utf8"),
|
|
40
|
-
cipher.final()
|
|
41
|
-
]);
|
|
42
|
-
const tag = cipher.getAuthTag();
|
|
43
|
-
derivedKey.fill(0);
|
|
44
|
-
return {
|
|
45
|
-
version: this.KEYSTORE_FORMAT_VERSION,
|
|
46
|
-
type: this.KEYSTORE_FORMAT_TYPE,
|
|
47
|
-
kdf: {
|
|
48
|
-
algorithm: "argon2id",
|
|
49
|
-
memory,
|
|
50
|
-
iterations,
|
|
51
|
-
parallelism,
|
|
52
|
-
salt: salt.toString("base64")
|
|
53
|
-
},
|
|
54
|
-
cipher: {
|
|
55
|
-
algorithm: "aes-256-gcm",
|
|
56
|
-
nonce: nonce.toString("base64"),
|
|
57
|
-
tag: tag.toString("base64")
|
|
58
|
-
},
|
|
59
|
-
encryptedPayload: encryptedPayload.toString("base64"),
|
|
60
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
61
|
-
metadata: {
|
|
62
|
-
label: options.label,
|
|
63
|
-
network: options.network,
|
|
64
|
-
address: payload.address
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Decrypts an encrypted keystore using a password.
|
|
70
|
-
*/
|
|
71
|
-
static async decryptEncryptedKeystore(keystore, password) {
|
|
72
|
-
if (keystore.version !== this.KEYSTORE_FORMAT_VERSION) {
|
|
73
|
-
return {
|
|
74
|
-
success: false,
|
|
75
|
-
error: `Unsupported keystore version: ${keystore.version}`
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
try {
|
|
79
|
-
const salt = Buffer.from(keystore.kdf.salt, "base64");
|
|
80
|
-
const nonce = Buffer.from(keystore.cipher.nonce, "base64");
|
|
81
|
-
const tag = Buffer.from(keystore.cipher.tag, "base64");
|
|
82
|
-
const encryptedData = Buffer.from(keystore.encryptedPayload, "base64");
|
|
83
|
-
const derivedKeyHex = await argon2id({
|
|
84
|
-
password,
|
|
85
|
-
salt,
|
|
86
|
-
parallelism: keystore.kdf.parallelism,
|
|
87
|
-
iterations: keystore.kdf.iterations,
|
|
88
|
-
memorySize: keystore.kdf.memory,
|
|
89
|
-
hashLength: 32,
|
|
90
|
-
outputType: "hex"
|
|
91
|
-
});
|
|
92
|
-
const derivedKey = Buffer.from(derivedKeyHex, "hex");
|
|
93
|
-
const decipher = crypto.createDecipheriv("aes-256-gcm", derivedKey, nonce);
|
|
94
|
-
decipher.setAuthTag(tag);
|
|
95
|
-
const decrypted = Buffer.concat([decipher.update(encryptedData), decipher.final()]);
|
|
96
|
-
derivedKey.fill(0);
|
|
97
|
-
const payload = JSON.parse(decrypted.toString("utf8"));
|
|
98
|
-
return { success: true, payload };
|
|
99
|
-
} catch (e) {
|
|
100
|
-
return { success: false, error: "Invalid password or corrupted keystore." };
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Verifies if the password is correct for the keystore.
|
|
105
|
-
*/
|
|
106
|
-
static async verifyKeystorePassword(keystore, password) {
|
|
107
|
-
const result = await this.decryptEncryptedKeystore(keystore, password);
|
|
108
|
-
return result.success;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Changes the password of an encrypted keystore.
|
|
112
|
-
*/
|
|
113
|
-
static async changeKeystorePassword(keystore, oldPassword, newPassword) {
|
|
114
|
-
const unlock = await this.decryptEncryptedKeystore(keystore, oldPassword);
|
|
115
|
-
if (!unlock.success || !unlock.payload) {
|
|
116
|
-
throw new Error("Invalid current password.");
|
|
117
|
-
}
|
|
118
|
-
return this.createEncryptedKeystore(unlock.payload, newPassword, {
|
|
119
|
-
label: keystore.metadata.label,
|
|
120
|
-
network: keystore.metadata.network,
|
|
121
|
-
iterations: keystore.kdf.iterations,
|
|
122
|
-
memory: keystore.kdf.memory,
|
|
123
|
-
parallelism: keystore.kdf.parallelism
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Loads an encrypted keystore from the filesystem.
|
|
128
|
-
*/
|
|
129
|
-
static async loadEncryptedKeystore(filePath) {
|
|
130
|
-
try {
|
|
131
|
-
const data = await fs.promises.readFile(filePath, "utf-8");
|
|
132
|
-
const keystore = JSON.parse(data);
|
|
133
|
-
if (keystore.type !== this.KEYSTORE_FORMAT_TYPE) {
|
|
134
|
-
throw new Error(`Invalid keystore type: ${keystore.type}`);
|
|
135
|
-
}
|
|
136
|
-
return keystore;
|
|
137
|
-
} catch (e) {
|
|
138
|
-
throw new Error(
|
|
139
|
-
`Failed to load keystore at ${filePath}: ${e instanceof Error ? e instanceof Error ? e instanceof Error ? e.message : String(e) : String(e) : String(e)}`
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Saves an encrypted keystore to the filesystem.
|
|
145
|
-
*/
|
|
146
|
-
static async saveEncryptedKeystore(filePath, keystore) {
|
|
147
|
-
try {
|
|
148
|
-
const dir = path.dirname(filePath);
|
|
149
|
-
if (!fs.existsSync(dir)) {
|
|
150
|
-
await fs.promises.mkdir(dir, { recursive: true });
|
|
151
|
-
}
|
|
152
|
-
await writeFileAtomic(filePath, JSON.stringify(keystore, null, 2), {
|
|
153
|
-
encoding: "utf-8",
|
|
154
|
-
mode: 384
|
|
155
|
-
});
|
|
156
|
-
} catch (e) {
|
|
157
|
-
throw new Error(
|
|
158
|
-
`Failed to save keystore at ${filePath}: ${e instanceof Error ? e instanceof Error ? e instanceof Error ? e.message : String(e) : String(e) : String(e)}`
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
export {
|
|
165
|
-
KeystoreManager
|
|
166
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DEV_ACCOUNTS_PASSWORD,
|
|
3
|
-
createDevSigner,
|
|
4
|
-
ensureDevAccounts,
|
|
5
|
-
getOrCreateDevAccount,
|
|
6
|
-
listDevAccountsSync
|
|
7
|
-
} from "./chunk-Q22JFUWC.js";
|
|
8
|
-
import "./chunk-GM2BY4VS.js";
|
|
9
|
-
import "./chunk-BNF566PS.js";
|
|
10
|
-
import "./chunk-2UQTGGML.js";
|
|
11
|
-
import "./chunk-YLG2NIAU.js";
|
|
12
|
-
export {
|
|
13
|
-
DEV_ACCOUNTS_PASSWORD,
|
|
14
|
-
createDevSigner,
|
|
15
|
-
ensureDevAccounts,
|
|
16
|
-
getOrCreateDevAccount,
|
|
17
|
-
listDevAccountsSync
|
|
18
|
-
};
|