@hardkas/accounts 0.11.1-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,6 +1,44 @@
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
+ import { TxPlan } from '@hardkas/tx-builder';
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
+ }
4
42
 
5
43
  type HardkasAccountKind = "simulated" | "kaspa-private-key" | "external-wallet" | "evm-private-key";
6
44
  interface KeystorePayload {
@@ -72,9 +110,11 @@ interface HardkasEvmPrivateKeyAccount extends HardkasBaseAccount {
72
110
  }
73
111
  type HardkasAccount = HardkasSimulatedAccount | HardkasKaspaPrivateKeyAccount | HardkasExternalWalletAccount | HardkasEvmPrivateKeyAccount;
74
112
  type HardkasSignerKind = "simulated" | "kaspa-private-key" | "external-wallet" | "unsupported";
113
+
75
114
  interface SignTxPlanInput {
76
115
  planArtifact: any;
77
- accountName: string;
116
+ accountName?: string;
117
+ authorizers?: Readonly<Record<number, TxInputAuthorizer>>;
78
118
  }
79
119
  interface SignTxPlanResult {
80
120
  signatureKind: HardkasSignerKind;
@@ -132,14 +172,6 @@ declare function prepareEvmAccountExport(account: HardkasAccount, networkId: str
132
172
 
133
173
  declare function getRequiredEnv(name: string): string;
134
174
 
135
- /**
136
- * Simulated signer for simnet development.
137
- * Produces deterministic signatures without real private keys.
138
- */
139
- declare class SimulatedTxPlanSigner implements HardkasTxPlanSigner {
140
- kind: HardkasSignerKind;
141
- signTxPlan(input: SignTxPlanInput): Promise<SignTxPlanResult>;
142
- }
143
175
  /**
144
176
  * Placeholder for real Kaspa signing.
145
177
  */
@@ -147,12 +179,14 @@ declare class UnsupportedRealKaspaSigner implements HardkasTxPlanSigner {
147
179
  kind: HardkasSignerKind;
148
180
  signTxPlan(_input: SignTxPlanInput): Promise<SignTxPlanResult>;
149
181
  }
182
+
150
183
  /**
151
184
  * Main entry point for signing transaction plan artifacts.
152
185
  */
153
186
  declare function signTxPlanArtifact(input: {
154
187
  planArtifact: TxPlanArtifact;
155
- account: HardkasAccount;
188
+ account?: HardkasAccount;
189
+ authorizers?: Readonly<Record<number, TxInputAuthorizer>>;
156
190
  config?: HardkasConfig;
157
191
  allowMainnet?: boolean;
158
192
  }): Promise<SignedTxArtifact>;
@@ -162,16 +196,29 @@ interface KaspaSigningBackendStatus {
162
196
  name: string;
163
197
  version?: string;
164
198
  error?: string;
199
+ capabilities: {
200
+ transactionV1Signing: boolean;
201
+ };
202
+ }
203
+ interface WasmProviderConfig {
204
+ provider: "npm" | "local" | "release-asset";
205
+ path?: string;
165
206
  }
166
207
  /**
167
208
  * Loads the official Kaspa WASM SDK dynamically.
168
209
  * This ensures the toolkit remains usable even if the SDK is not installed.
169
210
  */
170
- declare function loadKaspaWasm(): Promise<any>;
211
+ declare function loadKaspaWasm(config?: WasmProviderConfig): Promise<any>;
212
+ /**
213
+ * Detects the specific capabilities supported by the installed kaspa-wasm SDK.
214
+ */
215
+ declare function detectCapabilities(sdk: any): {
216
+ transactionV1Signing: boolean;
217
+ };
171
218
  /**
172
219
  * Checks if the Kaspa WASM SDK is available without throwing.
173
220
  */
174
- declare function getKaspaSigningBackendStatus(): Promise<KaspaSigningBackendStatus>;
221
+ declare function getKaspaSigningBackendStatus(config?: WasmProviderConfig): Promise<KaspaSigningBackendStatus>;
175
222
 
176
223
  interface GeneratedKaspaDevAccount {
177
224
  readonly address: string;
@@ -209,8 +256,9 @@ declare class KaspaWasmPrivateKeySigner implements HardkasTxPlanSigner {
209
256
  private options;
210
257
  kind: HardkasSignerKind;
211
258
  constructor(options: {
212
- account: HardkasKaspaPrivateKeyAccount;
259
+ account?: HardkasKaspaPrivateKeyAccount;
213
260
  allowMainnet?: boolean | undefined;
261
+ wasmConfig?: WasmProviderConfig;
214
262
  });
215
263
  signTxPlan(input: SignTxPlanInput): Promise<SignTxPlanResult>;
216
264
  }
@@ -223,19 +271,6 @@ declare function assertSigningNetworkAllowed(input: {
223
271
  allowMainnet?: boolean | undefined;
224
272
  }): void;
225
273
 
226
- /**
227
- * Deterministic fixture signer for Docker testing on simnet.
228
- * Never to be used with real funds or mainnet.
229
- */
230
- declare class HardkasFixtureSigner implements ExternalHardkasSigner {
231
- private networkId;
232
- private readonly FIXTURE_PK;
233
- constructor(networkId?: string);
234
- private loadKaspa;
235
- getAddress(): Promise<string>;
236
- signTransaction(plan: TxPlanArtifact): Promise<SignedTxArtifact>;
237
- }
238
-
239
274
  interface RealAccountStore extends HardkasArtifactBase {
240
275
  readonly schema: "hardkas.realAccountStore.v1";
241
276
  readonly networkId: NetworkId;
@@ -297,7 +332,7 @@ declare function resolveRealAccountOrAddress(store: RealAccountStore | null, nam
297
332
  };
298
333
 
299
334
  interface RealTxSigningInput {
300
- readonly plan: TxPlanArtifact;
335
+ readonly plan: TxPlan;
301
336
  readonly account: RealDevAccount;
302
337
  }
303
338
  interface RealTxSigningResult {
@@ -395,6 +430,7 @@ declare function listDevAccountsSync(workspaceDir: string): {
395
430
  name: string;
396
431
  address: string;
397
432
  }[];
433
+ declare function createDevSigner(workspaceDir: string, accountNameOrAddress: string): Promise<HardkasTxPlanSigner>;
398
434
 
399
435
  type NetworkType = "simnet" | "testnet" | "mainnet" | "local-docker-simnet";
400
436
  type ChainType = "receive" | "change" | 0 | 1;
@@ -522,4 +558,4 @@ declare class WalletStateStoreJson {
522
558
  nextChangeIndex(walletId: string): number;
523
559
  }
524
560
 
525
- 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, 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 };