@hardkas/accounts 0.11.2-alpha → 0.11.4-alpha

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.
@@ -0,0 +1,166 @@
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
+ };
@@ -0,0 +1,18 @@
1
+ import {
2
+ DEV_ACCOUNTS_PASSWORD,
3
+ createDevSigner,
4
+ ensureDevAccounts,
5
+ getOrCreateDevAccount,
6
+ listDevAccountsSync
7
+ } from "./chunk-TY5CXPHB.js";
8
+ import "./chunk-WRRHW2WO.js";
9
+ import "./chunk-ILASLDZU.js";
10
+ import "./chunk-6DPO5V3N.js";
11
+ import "./chunk-YLG2NIAU.js";
12
+ export {
13
+ DEV_ACCOUNTS_PASSWORD,
14
+ createDevSigner,
15
+ ensureDevAccounts,
16
+ getOrCreateDevAccount,
17
+ listDevAccountsSync
18
+ };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,45 @@
1
+ import { TxPlanArtifact, SignedTxArtifact, HardkasArtifactBase } from '@hardkas/artifacts';
1
2
  import { HardkasConfig } from '@hardkas/config';
2
- import { TxPlanArtifact, SignedTxArtifact, ExternalHardkasSigner, HardkasArtifactBase } from '@hardkas/artifacts';
3
3
  import { NetworkId } from '@hardkas/core';
4
4
  import { TxPlan } from '@hardkas/tx-builder';
5
5
 
6
+ interface TxInputAuthorizationContext {
7
+ readonly inputIndex: number;
8
+ readonly plan: TxPlanArtifact;
9
+ readonly wasmTransaction: unknown;
10
+ readonly wasm: any;
11
+ }
12
+ type InputAuthorization = {
13
+ readonly kind: "signature-script";
14
+ readonly signatureScript: string;
15
+ } | {
16
+ readonly kind: "wasm-signer";
17
+ readonly signer: WasmInputSigner;
18
+ };
19
+ interface WasmInputSigner {
20
+ signInput(context: TxInputAuthorizationContext): Promise<string> | string;
21
+ }
22
+ interface TxInputAuthorizer {
23
+ authorize(context: TxInputAuthorizationContext): Promise<InputAuthorization> | InputAuthorization;
24
+ }
25
+ declare class StaticSignatureScriptAuthorizer implements TxInputAuthorizer {
26
+ private readonly signatureScript;
27
+ constructor(signatureScript: string);
28
+ authorize(): InputAuthorization;
29
+ }
30
+ declare class PrivateKeyAuthorizer implements TxInputAuthorizer {
31
+ readonly accountName: string;
32
+ readonly privateKeyHex: string;
33
+ constructor(accountName: string, privateKeyHex: string);
34
+ authorize(context: TxInputAuthorizationContext): InputAuthorization;
35
+ }
36
+ declare class LazyAccountAuthorizer implements TxInputAuthorizer {
37
+ readonly accountName: string;
38
+ private readonly workspaceRoot;
39
+ constructor(accountName: string, workspaceRoot: string);
40
+ authorize(context: TxInputAuthorizationContext): Promise<InputAuthorization>;
41
+ }
42
+
6
43
  type HardkasAccountKind = "simulated" | "kaspa-private-key" | "external-wallet" | "evm-private-key";
7
44
  interface KeystorePayload {
8
45
  address: string;
@@ -73,9 +110,11 @@ interface HardkasEvmPrivateKeyAccount extends HardkasBaseAccount {
73
110
  }
74
111
  type HardkasAccount = HardkasSimulatedAccount | HardkasKaspaPrivateKeyAccount | HardkasExternalWalletAccount | HardkasEvmPrivateKeyAccount;
75
112
  type HardkasSignerKind = "simulated" | "kaspa-private-key" | "external-wallet" | "unsupported";
113
+
76
114
  interface SignTxPlanInput {
77
115
  planArtifact: any;
78
- accountName: string;
116
+ accountName?: string;
117
+ authorizers?: Readonly<Record<number, TxInputAuthorizer>>;
79
118
  }
80
119
  interface SignTxPlanResult {
81
120
  signatureKind: HardkasSignerKind;
@@ -133,14 +172,6 @@ declare function prepareEvmAccountExport(account: HardkasAccount, networkId: str
133
172
 
134
173
  declare function getRequiredEnv(name: string): string;
135
174
 
136
- /**
137
- * Simulated signer for simnet development.
138
- * Produces deterministic signatures without real private keys.
139
- */
140
- declare class SimulatedTxPlanSigner implements HardkasTxPlanSigner {
141
- kind: HardkasSignerKind;
142
- signTxPlan(input: SignTxPlanInput): Promise<SignTxPlanResult>;
143
- }
144
175
  /**
145
176
  * Placeholder for real Kaspa signing.
146
177
  */
@@ -148,12 +179,14 @@ declare class UnsupportedRealKaspaSigner implements HardkasTxPlanSigner {
148
179
  kind: HardkasSignerKind;
149
180
  signTxPlan(_input: SignTxPlanInput): Promise<SignTxPlanResult>;
150
181
  }
182
+
151
183
  /**
152
184
  * Main entry point for signing transaction plan artifacts.
153
185
  */
154
186
  declare function signTxPlanArtifact(input: {
155
187
  planArtifact: TxPlanArtifact;
156
- account: HardkasAccount;
188
+ account?: HardkasAccount;
189
+ authorizers?: Readonly<Record<number, TxInputAuthorizer>>;
157
190
  config?: HardkasConfig;
158
191
  allowMainnet?: boolean;
159
192
  }): Promise<SignedTxArtifact>;
@@ -167,11 +200,15 @@ interface KaspaSigningBackendStatus {
167
200
  transactionV1Signing: boolean;
168
201
  };
169
202
  }
203
+ interface WasmProviderConfig {
204
+ provider: "npm" | "local" | "release-asset";
205
+ path?: string;
206
+ }
170
207
  /**
171
208
  * Loads the official Kaspa WASM SDK dynamically.
172
209
  * This ensures the toolkit remains usable even if the SDK is not installed.
173
210
  */
174
- declare function loadKaspaWasm(): Promise<any>;
211
+ declare function loadKaspaWasm(config?: WasmProviderConfig): Promise<any>;
175
212
  /**
176
213
  * Detects the specific capabilities supported by the installed kaspa-wasm SDK.
177
214
  */
@@ -181,7 +218,7 @@ declare function detectCapabilities(sdk: any): {
181
218
  /**
182
219
  * Checks if the Kaspa WASM SDK is available without throwing.
183
220
  */
184
- declare function getKaspaSigningBackendStatus(): Promise<KaspaSigningBackendStatus>;
221
+ declare function getKaspaSigningBackendStatus(config?: WasmProviderConfig): Promise<KaspaSigningBackendStatus>;
185
222
 
186
223
  interface GeneratedKaspaDevAccount {
187
224
  readonly address: string;
@@ -219,8 +256,9 @@ declare class KaspaWasmPrivateKeySigner implements HardkasTxPlanSigner {
219
256
  private options;
220
257
  kind: HardkasSignerKind;
221
258
  constructor(options: {
222
- account: HardkasKaspaPrivateKeyAccount;
259
+ account?: HardkasKaspaPrivateKeyAccount;
223
260
  allowMainnet?: boolean | undefined;
261
+ wasmConfig?: WasmProviderConfig;
224
262
  });
225
263
  signTxPlan(input: SignTxPlanInput): Promise<SignTxPlanResult>;
226
264
  }
@@ -233,19 +271,6 @@ declare function assertSigningNetworkAllowed(input: {
233
271
  allowMainnet?: boolean | undefined;
234
272
  }): void;
235
273
 
236
- /**
237
- * Deterministic fixture signer for Docker testing on simnet.
238
- * Never to be used with real funds or mainnet.
239
- */
240
- declare class HardkasFixtureSigner implements ExternalHardkasSigner {
241
- private networkId;
242
- private readonly FIXTURE_PK;
243
- constructor(networkId?: string);
244
- private loadKaspa;
245
- getAddress(): Promise<string>;
246
- signTransaction(plan: TxPlanArtifact): Promise<SignedTxArtifact>;
247
- }
248
-
249
274
  interface RealAccountStore extends HardkasArtifactBase {
250
275
  readonly schema: "hardkas.realAccountStore.v1";
251
276
  readonly networkId: NetworkId;
@@ -405,6 +430,7 @@ declare function listDevAccountsSync(workspaceDir: string): {
405
430
  name: string;
406
431
  address: string;
407
432
  }[];
433
+ declare function createDevSigner(workspaceDir: string, accountNameOrAddress: string): Promise<HardkasTxPlanSigner>;
408
434
 
409
435
  type NetworkType = "simnet" | "testnet" | "mainnet" | "local-docker-simnet";
410
436
  type ChainType = "receive" | "change" | 0 | 1;
@@ -532,4 +558,4 @@ declare class WalletStateStoreJson {
532
558
  nextChangeIndex(walletId: string): number;
533
559
  }
534
560
 
535
- export { AddressManager, type ChainType, 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, HardkasFixtureSigner, type HardkasKaspaPrivateKeyAccount, type HardkasSigner, type HardkasSignerKind, type HardkasSimulatedAccount, type HardkasTxPlanSigner, type HelperDeriveRequest, type KaspaKeyGenerator, KaspaSdkKeyGenerator, type KaspaSdkKeyGeneratorOptions, KaspaSdkRealTxSigner, type KaspaSdkRealTxSignerOptions, type KaspaSigningBackendStatus, KaspaWasmPrivateKeySigner, type KeystoreCipherParams, type KeystoreKdfParams, KeystoreManager, type KeystorePayload, type KeystoreUnlockResult, type NetworkType, type PathRequest, type RealAccountStore, type RealDevAccount, type RealTxSigner, type RealTxSigningInput, type RealTxSigningResult, type ResolveAccountOptions, type SignTxPlanInput, type SignTxPlanResult, SimulatedSigner, SimulatedTxPlanSigner, UnsupportedKaspaKeyGenerator, UnsupportedRealKaspaSigner, UnsupportedRealTxSigner, type WalletArtifact, type WalletClaims, type WalletCreateRequest, type WalletImportRequest, WalletManager, WalletManagerImpl, type WalletMetadata, type WalletState, WalletStateStoreJson, type WalletStateStoreOptions, appendToKeystoreJson, assertSigningNetworkAllowed, createEmptyRealAccountStore, createLocalKaspaWallet, describeAccount, detectCapabilities, ensureDevAccounts, getDefaultRealAccountsPath, getKaspaSigningBackendStatus, getOrCreateDevAccount, getRealDevAccount, getRequiredEnv, importRealDevAccount, listDevAccountsSync, listHardkasAccounts, listRealDevAccounts, loadKaspaWasm, loadOrCreateRealAccountStore, loadRealAccountStore, loadRealAccountStoreSync, prepareEvmAccountExport, removeRealDevAccount, resolveHardkasAccount, resolveHardkasAccountAddress, resolveRealAccountOrAddress, saveRealAccountStore, signTxPlanArtifact, validateAccountName, validateAddressNetwork, validateAddressPrefix, withKeystoreLock };
561
+ export { AddressManager, type ChainType, 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 HardkasKaspaPrivateKeyAccount, type HardkasSigner, type HardkasSignerKind, type HardkasSimulatedAccount, type HardkasTxPlanSigner, type HelperDeriveRequest, type InputAuthorization, type KaspaKeyGenerator, KaspaSdkKeyGenerator, type KaspaSdkKeyGeneratorOptions, KaspaSdkRealTxSigner, type KaspaSdkRealTxSignerOptions, type KaspaSigningBackendStatus, KaspaWasmPrivateKeySigner, 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, type ResolveAccountOptions, type SignTxPlanInput, type SignTxPlanResult, 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, appendToKeystoreJson, assertSigningNetworkAllowed, createDevSigner, createEmptyRealAccountStore, createLocalKaspaWallet, describeAccount, detectCapabilities, ensureDevAccounts, getDefaultRealAccountsPath, getKaspaSigningBackendStatus, getOrCreateDevAccount, getRealDevAccount, getRequiredEnv, importRealDevAccount, listDevAccountsSync, listHardkasAccounts, listRealDevAccounts, loadKaspaWasm, loadOrCreateRealAccountStore, loadRealAccountStore, loadRealAccountStoreSync, prepareEvmAccountExport, removeRealDevAccount, resolveHardkasAccount, resolveHardkasAccountAddress, resolveRealAccountOrAddress, saveRealAccountStore, signTxPlanArtifact, validateAccountName, validateAddressNetwork, validateAddressPrefix, withKeystoreLock };