@cheny56/node-client 1.0.9 → 1.0.11
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 +9 -3
- package/src/transaction.js +36 -44
- package/src/wallet.js +77 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cheny56/node-client",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
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,10 @@ 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
|
+
if (!wallet._initialized) {
|
|
165
|
+
await wallet._ensureKeyPair();
|
|
166
|
+
}
|
|
163
167
|
const nonce = await provider.getTransactionCount(wallet.address, 'pending');
|
|
164
168
|
|
|
165
169
|
const tx = new PQCLegacyTransaction({
|
|
@@ -172,7 +176,7 @@ class ERC20Token extends Contract {
|
|
|
172
176
|
data: data,
|
|
173
177
|
});
|
|
174
178
|
|
|
175
|
-
tx.sign(wallet);
|
|
179
|
+
await tx.sign(wallet);
|
|
176
180
|
const serialized = tx.getHex();
|
|
177
181
|
return await provider.sendRawTransaction(serialized);
|
|
178
182
|
}
|
|
@@ -188,7 +192,9 @@ class ERC20Token extends Contract {
|
|
|
188
192
|
*/
|
|
189
193
|
async transferHybrid(wallet, provider, to, amount, txOptions = {}) {
|
|
190
194
|
const data = this.encodeFunctionData('transfer', [to, amount]);
|
|
191
|
-
|
|
195
|
+
// Ensure wallet is initialized
|
|
196
|
+
const address = await wallet.getAddress();
|
|
197
|
+
const nonce = await provider.getTransactionCount(address, 'pending');
|
|
192
198
|
|
|
193
199
|
const tx = new HybridLegacyTransaction({
|
|
194
200
|
chainId: txOptions.chainId || 1337,
|
|
@@ -200,7 +206,7 @@ class ERC20Token extends Contract {
|
|
|
200
206
|
data: data,
|
|
201
207
|
});
|
|
202
208
|
|
|
203
|
-
tx.sign(wallet.ecdsaWallet, wallet.pqcWallet);
|
|
209
|
+
await tx.sign(wallet.ecdsaWallet, wallet.pqcWallet);
|
|
204
210
|
const serialized = tx.getHex();
|
|
205
211
|
return await provider.sendRawTransaction(serialized);
|
|
206
212
|
}
|
package/src/transaction.js
CHANGED
|
@@ -118,30 +118,28 @@ 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
|
+
if (!wallet._initialized) {
|
|
126
|
+
await wallet._ensureKeyPair();
|
|
127
|
+
}
|
|
128
|
+
|
|
124
129
|
const hash = ethers.getBytes(this.getSigningHash());
|
|
125
130
|
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
|
-
}
|
|
131
|
+
// Use wallet's sign method which handles the async properly
|
|
132
|
+
this.pqcSig = await wallet.sign(hash);
|
|
135
133
|
this.v = Number(this.chainId) * 2 + 35;
|
|
136
134
|
this.r = '0x0';
|
|
137
135
|
this.s = '0x0';
|
|
138
136
|
return this;
|
|
139
137
|
}
|
|
140
138
|
|
|
141
|
-
verify() {
|
|
139
|
+
async verify() {
|
|
142
140
|
if (!this.pqcSig || !this.pqcPubKey) return false;
|
|
143
141
|
const hash = ethers.getBytes(this.getSigningHash());
|
|
144
|
-
// dilithium
|
|
142
|
+
// Use dilithium verify directly
|
|
145
143
|
if (dilithium.verify) {
|
|
146
144
|
return dilithium.verify(this.pqcPubKey, hash, this.pqcSig);
|
|
147
145
|
} else if (dilithium.verifyWithContext) {
|
|
@@ -235,9 +233,14 @@ class HybridLegacyTransaction extends BaseTransaction {
|
|
|
235
233
|
* Sign with both ECDSA and PQC wallets
|
|
236
234
|
* @param {ECDSAWallet} ecdsaWallet - ECDSA wallet
|
|
237
235
|
* @param {PQCWallet} pqcWallet - PQC wallet
|
|
238
|
-
* @returns {HybridLegacyTransaction} This transaction
|
|
236
|
+
* @returns {Promise<HybridLegacyTransaction>} This transaction
|
|
239
237
|
*/
|
|
240
|
-
sign(ecdsaWallet, pqcWallet) {
|
|
238
|
+
async sign(ecdsaWallet, pqcWallet) {
|
|
239
|
+
// Ensure PQC wallet is initialized
|
|
240
|
+
if (!pqcWallet._initialized) {
|
|
241
|
+
await pqcWallet._ensureKeyPair();
|
|
242
|
+
}
|
|
243
|
+
|
|
241
244
|
const hash = this.getSigningHash();
|
|
242
245
|
const hashBytes = ethers.getBytes(hash);
|
|
243
246
|
|
|
@@ -248,16 +251,9 @@ class HybridLegacyTransaction extends BaseTransaction {
|
|
|
248
251
|
this.r = ecdsaSig.r;
|
|
249
252
|
this.s = ecdsaSig.s;
|
|
250
253
|
|
|
251
|
-
// PQC signature
|
|
254
|
+
// PQC signature - use wallet's sign method
|
|
252
255
|
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
|
-
}
|
|
256
|
+
this.pqcSig = await pqcWallet.sign(hashBytes);
|
|
261
257
|
|
|
262
258
|
return this;
|
|
263
259
|
}
|
|
@@ -391,21 +387,19 @@ class PQCAccessListTransaction extends BaseTransaction {
|
|
|
391
387
|
return ethers.keccak256(ethers.hexlify(prefixed));
|
|
392
388
|
}
|
|
393
389
|
|
|
394
|
-
sign(wallet) {
|
|
390
|
+
async sign(wallet) {
|
|
391
|
+
// Ensure wallet is initialized
|
|
392
|
+
if (!wallet._initialized) {
|
|
393
|
+
await wallet._ensureKeyPair();
|
|
394
|
+
}
|
|
395
|
+
|
|
395
396
|
const hash = ethers.getBytes(this.getSigningHash());
|
|
396
397
|
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
|
-
}
|
|
398
|
+
this.pqcSig = await wallet.sign(hash);
|
|
405
399
|
return this;
|
|
406
400
|
}
|
|
407
401
|
|
|
408
|
-
verify() {
|
|
402
|
+
async verify() {
|
|
409
403
|
if (!this.pqcSig || !this.pqcPubKey) return false;
|
|
410
404
|
const hash = ethers.getBytes(this.getSigningHash());
|
|
411
405
|
if (dilithium.verify) {
|
|
@@ -550,21 +544,19 @@ class PQCDynamicFeeTransaction extends BaseTransaction {
|
|
|
550
544
|
return ethers.keccak256(ethers.hexlify(prefixed));
|
|
551
545
|
}
|
|
552
546
|
|
|
553
|
-
sign(wallet) {
|
|
547
|
+
async sign(wallet) {
|
|
548
|
+
// Ensure wallet is initialized
|
|
549
|
+
if (!wallet._initialized) {
|
|
550
|
+
await wallet._ensureKeyPair();
|
|
551
|
+
}
|
|
552
|
+
|
|
554
553
|
const hash = ethers.getBytes(this.getSigningHash());
|
|
555
554
|
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
|
-
}
|
|
555
|
+
this.pqcSig = await wallet.sign(hash);
|
|
564
556
|
return this;
|
|
565
557
|
}
|
|
566
558
|
|
|
567
|
-
verify() {
|
|
559
|
+
async verify() {
|
|
568
560
|
if (!this.pqcSig || !this.pqcPubKey) return false;
|
|
569
561
|
const hash = ethers.getBytes(this.getSigningHash());
|
|
570
562
|
if (dilithium.verify) {
|
package/src/wallet.js
CHANGED
|
@@ -66,14 +66,36 @@ 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;
|
|
69
71
|
} else {
|
|
70
72
|
// Generate new key pair using dilithium-crystals
|
|
71
|
-
// dilithium-crystals
|
|
72
|
-
|
|
73
|
-
this.
|
|
74
|
-
this.
|
|
73
|
+
// dilithium-crystals uses keyPair() which is async and returns { publicKey, secretKey }
|
|
74
|
+
this.secretKey = null;
|
|
75
|
+
this.publicKey = null;
|
|
76
|
+
this.address = null;
|
|
77
|
+
this._initialized = false;
|
|
75
78
|
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Initialize key pair if not already done (async)
|
|
83
|
+
* @private
|
|
84
|
+
*/
|
|
85
|
+
async _ensureKeyPair() {
|
|
86
|
+
if (this._initialized) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!dilithium.keyPair) {
|
|
91
|
+
throw new Error('dilithium-crystals does not expose keyPair method. Please check the package API.');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const keyPair = await dilithium.keyPair();
|
|
95
|
+
this.secretKey = keyPair.secretKey;
|
|
96
|
+
this.publicKey = keyPair.publicKey;
|
|
76
97
|
this.address = derivePQCAddress(this.publicKey);
|
|
98
|
+
this._initialized = true;
|
|
77
99
|
}
|
|
78
100
|
|
|
79
101
|
/**
|
|
@@ -96,9 +118,14 @@ class PQCWallet {
|
|
|
96
118
|
/**
|
|
97
119
|
* Sign a message hash
|
|
98
120
|
* @param {Uint8Array} hash - Message hash (32 bytes)
|
|
99
|
-
* @returns {Uint8Array} Signature
|
|
121
|
+
* @returns {Promise<Uint8Array>} Signature
|
|
100
122
|
*/
|
|
101
|
-
sign(hash) {
|
|
123
|
+
async sign(hash) {
|
|
124
|
+
// Ensure key pair is initialized
|
|
125
|
+
if (!this._initialized) {
|
|
126
|
+
await this._ensureKeyPair();
|
|
127
|
+
}
|
|
128
|
+
|
|
102
129
|
// dilithium-crystals API: sign(secretKey, message)
|
|
103
130
|
// Some implementations may require context, but we'll try without first
|
|
104
131
|
if (dilithium.sign) {
|
|
@@ -115,9 +142,14 @@ class PQCWallet {
|
|
|
115
142
|
* Verify a signature
|
|
116
143
|
* @param {Uint8Array} hash - Message hash
|
|
117
144
|
* @param {Uint8Array} signature - Signature to verify
|
|
118
|
-
* @returns {boolean} True if signature is valid
|
|
145
|
+
* @returns {Promise<boolean>} True if signature is valid
|
|
119
146
|
*/
|
|
120
|
-
verify(hash, signature) {
|
|
147
|
+
async verify(hash, signature) {
|
|
148
|
+
// Ensure key pair is initialized
|
|
149
|
+
if (!this._initialized) {
|
|
150
|
+
await this._ensureKeyPair();
|
|
151
|
+
}
|
|
152
|
+
|
|
121
153
|
// dilithium-crystals API: verify(publicKey, message, signature)
|
|
122
154
|
if (dilithium.verify) {
|
|
123
155
|
return dilithium.verify(this.publicKey, hash, signature);
|
|
@@ -131,17 +163,23 @@ class PQCWallet {
|
|
|
131
163
|
|
|
132
164
|
/**
|
|
133
165
|
* Get public key as hex string
|
|
134
|
-
* @returns {string} Public key (hex)
|
|
166
|
+
* @returns {Promise<string>} Public key (hex)
|
|
135
167
|
*/
|
|
136
|
-
getPublicKeyHex() {
|
|
168
|
+
async getPublicKeyHex() {
|
|
169
|
+
if (!this._initialized) {
|
|
170
|
+
await this._ensureKeyPair();
|
|
171
|
+
}
|
|
137
172
|
return ethers.hexlify(this.publicKey);
|
|
138
173
|
}
|
|
139
174
|
|
|
140
175
|
/**
|
|
141
176
|
* Get secret key as hex string (use with caution!)
|
|
142
|
-
* @returns {string} Secret key (hex)
|
|
177
|
+
* @returns {Promise<string>} Secret key (hex)
|
|
143
178
|
*/
|
|
144
|
-
getSecretKeyHex() {
|
|
179
|
+
async getSecretKeyHex() {
|
|
180
|
+
if (!this._initialized) {
|
|
181
|
+
await this._ensureKeyPair();
|
|
182
|
+
}
|
|
145
183
|
return ethers.hexlify(this.secretKey);
|
|
146
184
|
}
|
|
147
185
|
}
|
|
@@ -156,10 +194,30 @@ class HybridWallet {
|
|
|
156
194
|
}
|
|
157
195
|
this.ecdsaWallet = ecdsaWallet instanceof ECDSAWallet ? ecdsaWallet : new ECDSAWallet(ecdsaWallet);
|
|
158
196
|
this.pqcWallet = pqcWallet instanceof PQCWallet ? pqcWallet : new PQCWallet();
|
|
197
|
+
this.address = null; // Will be computed when PQC wallet is initialized
|
|
198
|
+
this._initialized = false;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Get the hybrid address (computed from ECDSA + PQC public keys)
|
|
203
|
+
* @returns {Promise<string>} Hybrid address
|
|
204
|
+
*/
|
|
205
|
+
async getAddress() {
|
|
206
|
+
if (this._initialized && this.address) {
|
|
207
|
+
return this.address;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Ensure PQC wallet has keys
|
|
211
|
+
if (!this.pqcWallet._initialized) {
|
|
212
|
+
await this.pqcWallet._ensureKeyPair();
|
|
213
|
+
}
|
|
214
|
+
|
|
159
215
|
this.address = deriveHybridAddress(
|
|
160
216
|
ethers.getBytes(this.ecdsaWallet.getPublicKey()),
|
|
161
217
|
this.pqcWallet.publicKey
|
|
162
218
|
);
|
|
219
|
+
this._initialized = true;
|
|
220
|
+
return this.address;
|
|
163
221
|
}
|
|
164
222
|
|
|
165
223
|
/**
|
|
@@ -172,7 +230,13 @@ class HybridWallet {
|
|
|
172
230
|
static fromKeys(ecdsaPrivateKey, pqcSecretKey, pqcPublicKey) {
|
|
173
231
|
const ecdsaWallet = new ECDSAWallet(ecdsaPrivateKey);
|
|
174
232
|
const pqcWallet = new PQCWallet(pqcSecretKey, pqcPublicKey);
|
|
175
|
-
|
|
233
|
+
const wallet = new HybridWallet(ecdsaWallet, pqcWallet);
|
|
234
|
+
wallet.address = deriveHybridAddress(
|
|
235
|
+
ethers.getBytes(ecdsaWallet.getPublicKey()),
|
|
236
|
+
pqcWallet.publicKey
|
|
237
|
+
);
|
|
238
|
+
wallet._initialized = true;
|
|
239
|
+
return wallet;
|
|
176
240
|
}
|
|
177
241
|
|
|
178
242
|
/**
|