@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 +1 -1
- package/src/contract.js +7 -3
- package/src/transaction.js +28 -44
- package/src/wallet.js +107 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cheny56/node-client",
|
|
3
|
-
"version": "1.0.
|
|
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
|
-
|
|
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
|
}
|
package/src/transaction.js
CHANGED
|
@@ -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
|
-
//
|
|
127
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
this.
|
|
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
|
-
|
|
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
|
/**
|