@cheny56/node-client 1.0.10 → 1.0.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cheny56/node-client",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "Client library for Quorum blockchain with Post-Quantum Cryptography (PQC) and Zero-Knowledge Proof (ZK) support",
5
5
  "main": "./src/index.js",
6
6
  "types": "src/index.d.ts",
package/src/contract.js CHANGED
@@ -160,6 +160,8 @@ class ERC20Token extends Contract {
160
160
  */
161
161
  async transferPQC(wallet, provider, to, amount, txOptions = {}) {
162
162
  const data = this.encodeFunctionData('transfer', [to, amount]);
163
+ // Ensure wallet is initialized
164
+ await wallet._initPromise;
163
165
  const nonce = await provider.getTransactionCount(wallet.address, 'pending');
164
166
 
165
167
  const tx = new PQCLegacyTransaction({
@@ -172,7 +174,7 @@ class ERC20Token extends Contract {
172
174
  data: data,
173
175
  });
174
176
 
175
- tx.sign(wallet);
177
+ await tx.sign(wallet);
176
178
  const serialized = tx.getHex();
177
179
  return await provider.sendRawTransaction(serialized);
178
180
  }
@@ -188,7 +190,9 @@ class ERC20Token extends Contract {
188
190
  */
189
191
  async transferHybrid(wallet, provider, to, amount, txOptions = {}) {
190
192
  const data = this.encodeFunctionData('transfer', [to, amount]);
191
- const nonce = await provider.getTransactionCount(wallet.address, 'pending');
193
+ // Ensure wallet is initialized
194
+ const address = await wallet.getAddress();
195
+ const nonce = await provider.getTransactionCount(address, 'pending');
192
196
 
193
197
  const tx = new HybridLegacyTransaction({
194
198
  chainId: txOptions.chainId || 1337,
@@ -200,7 +204,7 @@ class ERC20Token extends Contract {
200
204
  data: data,
201
205
  });
202
206
 
203
- tx.sign(wallet.ecdsaWallet, wallet.pqcWallet);
207
+ await tx.sign(wallet.ecdsaWallet, wallet.pqcWallet);
204
208
  const serialized = tx.getHex();
205
209
  return await provider.sendRawTransaction(serialized);
206
210
  }
@@ -118,30 +118,26 @@ class PQCLegacyTransaction extends BaseTransaction {
118
118
  /**
119
119
  * Sign with PQC wallet
120
120
  * @param {PQCWallet} wallet - PQC wallet
121
- * @returns {PQCLegacyTransaction} This transaction
121
+ * @returns {Promise<PQCLegacyTransaction>} This transaction
122
122
  */
123
- sign(wallet) {
123
+ async sign(wallet) {
124
+ // Ensure wallet is initialized
125
+ await wallet._initPromise;
126
+
124
127
  const hash = ethers.getBytes(this.getSigningHash());
125
128
  this.pqcPubKey = wallet.publicKey;
126
- // dilithium-crystals API: sign(secretKey, message) or signWithContext(secretKey, message, ctx)
127
- if (dilithium.sign) {
128
- this.pqcSig = dilithium.sign(wallet.secretKey, hash);
129
- } else if (dilithium.signWithContext) {
130
- const ctx = new TextEncoder().encode('ML-DSA-65');
131
- this.pqcSig = dilithium.signWithContext(wallet.secretKey, hash, ctx);
132
- } else {
133
- throw new Error('dilithium-crystals does not expose sign method');
134
- }
129
+ // Use wallet's sign method which handles the async properly
130
+ this.pqcSig = await wallet.sign(hash);
135
131
  this.v = Number(this.chainId) * 2 + 35;
136
132
  this.r = '0x0';
137
133
  this.s = '0x0';
138
134
  return this;
139
135
  }
140
136
 
141
- verify() {
137
+ async verify() {
142
138
  if (!this.pqcSig || !this.pqcPubKey) return false;
143
139
  const hash = ethers.getBytes(this.getSigningHash());
144
- // dilithium-crystals API: verify(publicKey, message, signature) or verifyWithContext
140
+ // Use dilithium verify directly
145
141
  if (dilithium.verify) {
146
142
  return dilithium.verify(this.pqcPubKey, hash, this.pqcSig);
147
143
  } else if (dilithium.verifyWithContext) {
@@ -235,9 +231,12 @@ class HybridLegacyTransaction extends BaseTransaction {
235
231
  * Sign with both ECDSA and PQC wallets
236
232
  * @param {ECDSAWallet} ecdsaWallet - ECDSA wallet
237
233
  * @param {PQCWallet} pqcWallet - PQC wallet
238
- * @returns {HybridLegacyTransaction} This transaction
234
+ * @returns {Promise<HybridLegacyTransaction>} This transaction
239
235
  */
240
- sign(ecdsaWallet, pqcWallet) {
236
+ async sign(ecdsaWallet, pqcWallet) {
237
+ // Ensure PQC wallet is initialized
238
+ await pqcWallet._initPromise;
239
+
241
240
  const hash = this.getSigningHash();
242
241
  const hashBytes = ethers.getBytes(hash);
243
242
 
@@ -248,16 +247,9 @@ class HybridLegacyTransaction extends BaseTransaction {
248
247
  this.r = ecdsaSig.r;
249
248
  this.s = ecdsaSig.s;
250
249
 
251
- // PQC signature
250
+ // PQC signature - use wallet's sign method
252
251
  this.pqcPubKey = pqcWallet.publicKey;
253
- if (dilithium.sign) {
254
- this.pqcSig = dilithium.sign(pqcWallet.secretKey, hashBytes);
255
- } else if (dilithium.signWithContext) {
256
- const ctx = new TextEncoder().encode('ML-DSA-65');
257
- this.pqcSig = dilithium.signWithContext(pqcWallet.secretKey, hashBytes, ctx);
258
- } else {
259
- throw new Error('dilithium-crystals does not expose sign method');
260
- }
252
+ this.pqcSig = await pqcWallet.sign(hashBytes);
261
253
 
262
254
  return this;
263
255
  }
@@ -391,21 +383,17 @@ class PQCAccessListTransaction extends BaseTransaction {
391
383
  return ethers.keccak256(ethers.hexlify(prefixed));
392
384
  }
393
385
 
394
- sign(wallet) {
386
+ async sign(wallet) {
387
+ // Ensure wallet is initialized
388
+ await wallet._initPromise;
389
+
395
390
  const hash = ethers.getBytes(this.getSigningHash());
396
391
  this.pqcPubKey = wallet.publicKey;
397
- if (dilithium.sign) {
398
- this.pqcSig = dilithium.sign(wallet.secretKey, hash);
399
- } else if (dilithium.signWithContext) {
400
- const ctx = new TextEncoder().encode('ML-DSA-65');
401
- this.pqcSig = dilithium.signWithContext(wallet.secretKey, hash, ctx);
402
- } else {
403
- throw new Error('dilithium-crystals does not expose sign method');
404
- }
392
+ this.pqcSig = await wallet.sign(hash);
405
393
  return this;
406
394
  }
407
395
 
408
- verify() {
396
+ async verify() {
409
397
  if (!this.pqcSig || !this.pqcPubKey) return false;
410
398
  const hash = ethers.getBytes(this.getSigningHash());
411
399
  if (dilithium.verify) {
@@ -550,21 +538,17 @@ class PQCDynamicFeeTransaction extends BaseTransaction {
550
538
  return ethers.keccak256(ethers.hexlify(prefixed));
551
539
  }
552
540
 
553
- sign(wallet) {
541
+ async sign(wallet) {
542
+ // Ensure wallet is initialized
543
+ await wallet._initPromise;
544
+
554
545
  const hash = ethers.getBytes(this.getSigningHash());
555
546
  this.pqcPubKey = wallet.publicKey;
556
- if (dilithium.sign) {
557
- this.pqcSig = dilithium.sign(wallet.secretKey, hash);
558
- } else if (dilithium.signWithContext) {
559
- const ctx = new TextEncoder().encode('ML-DSA-65');
560
- this.pqcSig = dilithium.signWithContext(wallet.secretKey, hash, ctx);
561
- } else {
562
- throw new Error('dilithium-crystals does not expose sign method');
563
- }
547
+ this.pqcSig = await wallet.sign(hash);
564
548
  return this;
565
549
  }
566
550
 
567
- verify() {
551
+ async verify() {
568
552
  if (!this.pqcSig || !this.pqcPubKey) return false;
569
553
  const hash = ethers.getBytes(this.getSigningHash());
570
554
  if (dilithium.verify) {
package/src/wallet.js CHANGED
@@ -66,17 +66,58 @@ class PQCWallet {
66
66
  if (secretKey && publicKey) {
67
67
  this.secretKey = secretKey instanceof Uint8Array ? secretKey : new Uint8Array(secretKey);
68
68
  this.publicKey = publicKey instanceof Uint8Array ? publicKey : new Uint8Array(publicKey);
69
+ this.address = derivePQCAddress(this.publicKey);
70
+ this._initialized = true;
71
+ this._initPromise = Promise.resolve();
69
72
  } else {
70
73
  // Generate new key pair using dilithium-crystals
71
- // dilithium-crystals uses keygen() which returns { publicKey, secretKey }
72
- if (!dilithium.keygen) {
73
- throw new Error('dilithium-crystals does not expose keygen method. Please check the package API.');
74
- }
75
- const keyPair = dilithium.keygen();
76
- this.secretKey = keyPair.secretKey;
77
- this.publicKey = keyPair.publicKey;
74
+ // dilithium-crystals uses keyPair() which is async and returns { publicKey, secretKey }
75
+ this.secretKey = null;
76
+ this.publicKey = null;
77
+ this.address = null;
78
+ this._initialized = false;
79
+ // Start initialization immediately
80
+ this._initPromise = this._ensureKeyPair();
78
81
  }
82
+ }
83
+
84
+ /**
85
+ * Create a new PQC wallet with initialized keys (static factory method)
86
+ * @returns {Promise<PQCWallet>} Initialized wallet
87
+ */
88
+ static async create() {
89
+ const wallet = new PQCWallet();
90
+ await wallet._initPromise;
91
+ return wallet;
92
+ }
93
+
94
+ /**
95
+ * Initialize key pair if not already done (async)
96
+ * @private
97
+ */
98
+ async _ensureKeyPair() {
99
+ if (this._initialized) {
100
+ return;
101
+ }
102
+
103
+ if (!dilithium.keyPair) {
104
+ throw new Error('dilithium-crystals does not expose keyPair method. Please check the package API.');
105
+ }
106
+
107
+ const keyPair = await dilithium.keyPair();
108
+ this.secretKey = keyPair.secretKey;
109
+ this.publicKey = keyPair.publicKey;
79
110
  this.address = derivePQCAddress(this.publicKey);
111
+ this._initialized = true;
112
+ }
113
+
114
+ /**
115
+ * Get address (will initialize keys if needed)
116
+ * @returns {Promise<string>} Wallet address
117
+ */
118
+ async getAddress() {
119
+ await this._initPromise;
120
+ return this.address;
80
121
  }
81
122
 
82
123
  /**
@@ -99,9 +140,12 @@ class PQCWallet {
99
140
  /**
100
141
  * Sign a message hash
101
142
  * @param {Uint8Array} hash - Message hash (32 bytes)
102
- * @returns {Uint8Array} Signature
143
+ * @returns {Promise<Uint8Array>} Signature
103
144
  */
104
- sign(hash) {
145
+ async sign(hash) {
146
+ // Ensure key pair is initialized
147
+ await this._initPromise;
148
+
105
149
  // dilithium-crystals API: sign(secretKey, message)
106
150
  // Some implementations may require context, but we'll try without first
107
151
  if (dilithium.sign) {
@@ -118,9 +162,12 @@ class PQCWallet {
118
162
  * Verify a signature
119
163
  * @param {Uint8Array} hash - Message hash
120
164
  * @param {Uint8Array} signature - Signature to verify
121
- * @returns {boolean} True if signature is valid
165
+ * @returns {Promise<boolean>} True if signature is valid
122
166
  */
123
- verify(hash, signature) {
167
+ async verify(hash, signature) {
168
+ // Ensure key pair is initialized
169
+ await this._initPromise;
170
+
124
171
  // dilithium-crystals API: verify(publicKey, message, signature)
125
172
  if (dilithium.verify) {
126
173
  return dilithium.verify(this.publicKey, hash, signature);
@@ -134,17 +181,19 @@ class PQCWallet {
134
181
 
135
182
  /**
136
183
  * Get public key as hex string
137
- * @returns {string} Public key (hex)
184
+ * @returns {Promise<string>} Public key (hex)
138
185
  */
139
- getPublicKeyHex() {
186
+ async getPublicKeyHex() {
187
+ await this._initPromise;
140
188
  return ethers.hexlify(this.publicKey);
141
189
  }
142
190
 
143
191
  /**
144
192
  * Get secret key as hex string (use with caution!)
145
- * @returns {string} Secret key (hex)
193
+ * @returns {Promise<string>} Secret key (hex)
146
194
  */
147
- getSecretKeyHex() {
195
+ async getSecretKeyHex() {
196
+ await this._initPromise;
148
197
  return ethers.hexlify(this.secretKey);
149
198
  }
150
199
  }
@@ -159,10 +208,46 @@ class HybridWallet {
159
208
  }
160
209
  this.ecdsaWallet = ecdsaWallet instanceof ECDSAWallet ? ecdsaWallet : new ECDSAWallet(ecdsaWallet);
161
210
  this.pqcWallet = pqcWallet instanceof PQCWallet ? pqcWallet : new PQCWallet();
211
+ this.address = null; // Will be computed when PQC wallet is initialized
212
+ this._initialized = false;
213
+ }
214
+
215
+ /**
216
+ * Create a new Hybrid wallet with initialized keys (static factory method)
217
+ * @param {ECDSAWallet|string} ecdsaWallet - ECDSA wallet or private key
218
+ * @returns {Promise<HybridWallet>} Initialized hybrid wallet
219
+ */
220
+ static async create(ecdsaWallet) {
221
+ const ecdsa = ecdsaWallet instanceof ECDSAWallet ? ecdsaWallet : new ECDSAWallet(ecdsaWallet);
222
+ const pqc = new PQCWallet();
223
+ await pqc._initPromise; // Wait for PQC keys to be generated
224
+ const wallet = new HybridWallet(ecdsa, pqc);
225
+ wallet.address = deriveHybridAddress(
226
+ ethers.getBytes(ecdsa.getPublicKey()),
227
+ pqc.publicKey
228
+ );
229
+ wallet._initialized = true;
230
+ return wallet;
231
+ }
232
+
233
+ /**
234
+ * Get the hybrid address (computed from ECDSA + PQC public keys)
235
+ * @returns {Promise<string>} Hybrid address
236
+ */
237
+ async getAddress() {
238
+ if (this._initialized && this.address) {
239
+ return this.address;
240
+ }
241
+
242
+ // Ensure PQC wallet has keys
243
+ await this.pqcWallet._initPromise;
244
+
162
245
  this.address = deriveHybridAddress(
163
246
  ethers.getBytes(this.ecdsaWallet.getPublicKey()),
164
247
  this.pqcWallet.publicKey
165
248
  );
249
+ this._initialized = true;
250
+ return this.address;
166
251
  }
167
252
 
168
253
  /**
@@ -175,7 +260,13 @@ class HybridWallet {
175
260
  static fromKeys(ecdsaPrivateKey, pqcSecretKey, pqcPublicKey) {
176
261
  const ecdsaWallet = new ECDSAWallet(ecdsaPrivateKey);
177
262
  const pqcWallet = new PQCWallet(pqcSecretKey, pqcPublicKey);
178
- return new HybridWallet(ecdsaWallet, pqcWallet);
263
+ const wallet = new HybridWallet(ecdsaWallet, pqcWallet);
264
+ wallet.address = deriveHybridAddress(
265
+ ethers.getBytes(ecdsaWallet.getPublicKey()),
266
+ pqcWallet.publicKey
267
+ );
268
+ wallet._initialized = true;
269
+ return wallet;
179
270
  }
180
271
 
181
272
  /**