@btc-vision/transaction 1.6.19 → 1.7.1
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/browser/index.js +1 -1
- package/browser/index.js.LICENSE.txt +2 -0
- package/browser/src/_version.d.ts +1 -0
- package/browser/{epoch → src/epoch}/interfaces/IChallengeSolution.d.ts +2 -0
- package/browser/{keypair → src/keypair}/Address.d.ts +7 -4
- package/browser/{keypair → src/keypair}/AddressVerificator.d.ts +3 -0
- package/browser/{keypair → src/keypair}/EcKeyPair.d.ts +3 -2
- package/browser/{keypair → src/keypair}/MessageSigner.d.ts +9 -0
- package/browser/src/keypair/Wallet.d.ts +47 -0
- package/browser/{keypair → src/keypair}/interfaces/IWallet.d.ts +2 -0
- package/browser/src/mnemonic/BIPStandard.d.ts +8 -0
- package/browser/src/mnemonic/Mnemonic.d.ts +34 -0
- package/browser/src/mnemonic/MnemonicStrength.d.ts +7 -0
- package/browser/{opnet.d.ts → src/opnet.d.ts} +5 -0
- package/browser/src/transaction/browser/types/OPWallet.d.ts +14 -0
- package/browser/test/address.test.d.ts +1 -0
- package/browser/test/addressverificator-mldsa.test.d.ts +1 -0
- package/browser/test/derivePath.test.d.ts +1 -0
- package/browser/test/messagesigner-mldsa.test.d.ts +1 -0
- package/browser/test/messagesigner-schnorr.test.d.ts +1 -0
- package/browser/test/network-awareness.test.d.ts +1 -0
- package/build/_version.d.ts +1 -1
- package/build/_version.js +1 -1
- package/build/crypto/crypto-browser.d.ts +11 -0
- package/build/crypto/crypto-browser.js +56 -0
- package/build/epoch/ChallengeSolution.js +3 -2
- package/build/epoch/interfaces/IChallengeSolution.d.ts +2 -0
- package/build/keypair/Address.d.ts +7 -4
- package/build/keypair/Address.js +88 -37
- package/build/keypair/AddressVerificator.d.ts +3 -0
- package/build/keypair/AddressVerificator.js +49 -1
- package/build/keypair/EcKeyPair.d.ts +3 -2
- package/build/keypair/EcKeyPair.js +17 -3
- package/build/keypair/MessageSigner.d.ts +9 -0
- package/build/keypair/MessageSigner.js +23 -0
- package/build/keypair/Wallet.d.ts +20 -3
- package/build/keypair/Wallet.js +108 -9
- package/build/keypair/interfaces/IWallet.d.ts +2 -0
- package/build/mnemonic/BIPStandard.d.ts +8 -0
- package/build/mnemonic/BIPStandard.js +24 -0
- package/build/mnemonic/Mnemonic.d.ts +34 -0
- package/build/mnemonic/Mnemonic.js +140 -0
- package/build/mnemonic/MnemonicStrength.d.ts +7 -0
- package/build/mnemonic/MnemonicStrength.js +8 -0
- package/build/opnet.d.ts +5 -0
- package/build/opnet.js +5 -0
- package/build/transaction/browser/types/OPWallet.d.ts +14 -0
- package/build/transaction/browser/types/OPWallet.js +6 -0
- package/documentation/README.md +32 -0
- package/documentation/quantum-support/01-introduction.md +88 -0
- package/documentation/quantum-support/02-mnemonic-and-wallet.md +445 -0
- package/documentation/quantum-support/03-address-generation.md +329 -0
- package/documentation/quantum-support/04-message-signing.md +623 -0
- package/documentation/quantum-support/05-address-verification.md +307 -0
- package/documentation/quantum-support/README.md +65 -0
- package/gulpfile.js +2 -2
- package/package.json +25 -17
- package/src/_version.ts +1 -1
- package/src/epoch/ChallengeSolution.ts +3 -2
- package/src/epoch/interfaces/IChallengeSolution.ts +2 -0
- package/src/keypair/Address.ts +145 -43
- package/src/keypair/AddressVerificator.ts +87 -2
- package/src/keypair/EcKeyPair.ts +58 -6
- package/src/keypair/MessageSigner.ts +58 -0
- package/src/keypair/Wallet.ts +339 -57
- package/src/keypair/interfaces/IWallet.ts +13 -3
- package/src/mnemonic/BIPStandard.ts +92 -0
- package/src/mnemonic/Mnemonic.ts +465 -0
- package/src/mnemonic/MnemonicStrength.ts +12 -0
- package/src/network/ChainId.ts +1 -4
- package/src/opnet.ts +17 -0
- package/src/transaction/browser/types/OPWallet.ts +73 -0
- package/test/address.test.ts +1068 -0
- package/test/addressverificator-mldsa.test.ts +473 -0
- package/test/derivePath.test.ts +513 -0
- package/test/messagesigner-mldsa.test.ts +1060 -0
- package/test/messagesigner-schnorr.test.ts +1011 -0
- package/test/network-awareness.test.ts +163 -0
- package/tsconfig.json +1 -1
- package/vitest.config.ts +21 -0
- package/browser/_version.d.ts +0 -1
- package/browser/keypair/Wallet.d.ts +0 -30
- package/doc/README.md +0 -0
- /package/browser/{abi → src/abi}/ABICoder.d.ts +0 -0
- /package/browser/{buffer → src/buffer}/BinaryReader.d.ts +0 -0
- /package/browser/{buffer → src/buffer}/BinaryWriter.d.ts +0 -0
- /package/browser/{bytecode → src/bytecode}/Compressor.d.ts +0 -0
- /package/browser/{consensus → src/consensus}/Consensus.d.ts +0 -0
- /package/browser/{consensus → src/consensus}/ConsensusConfig.d.ts +0 -0
- /package/browser/{consensus → src/consensus}/metadata/RoswellConsensus.d.ts +0 -0
- /package/browser/{crypto → src/crypto}/crypto-browser.d.ts +0 -0
- /package/browser/{crypto → src/crypto}/crypto.d.ts +0 -0
- /package/browser/{deterministic → src/deterministic}/AddressMap.d.ts +0 -0
- /package/browser/{deterministic → src/deterministic}/AddressSet.d.ts +0 -0
- /package/browser/{deterministic → src/deterministic}/DeterministicMap.d.ts +0 -0
- /package/browser/{deterministic → src/deterministic}/DeterministicSet.d.ts +0 -0
- /package/browser/{deterministic → src/deterministic}/Map.d.ts +0 -0
- /package/browser/{epoch → src/epoch}/ChallengeSolution.d.ts +0 -0
- /package/browser/{epoch → src/epoch}/validator/EpochValidator.d.ts +0 -0
- /package/browser/{event → src/event}/NetEvent.d.ts +0 -0
- /package/browser/{generators → src/generators}/AddressGenerator.d.ts +0 -0
- /package/browser/{generators → src/generators}/Features.d.ts +0 -0
- /package/browser/{generators → src/generators}/Generator.d.ts +0 -0
- /package/browser/{generators → src/generators}/builders/CalldataGenerator.d.ts +0 -0
- /package/browser/{generators → src/generators}/builders/CustomGenerator.d.ts +0 -0
- /package/browser/{generators → src/generators}/builders/DeploymentGenerator.d.ts +0 -0
- /package/browser/{generators → src/generators}/builders/LegacyCalldataGenerator.d.ts +0 -0
- /package/browser/{generators → src/generators}/builders/MultiSignGenerator.d.ts +0 -0
- /package/browser/{generators → src/generators}/builders/P2WDAGenerator.d.ts +0 -0
- /package/browser/{index.d.ts → src/index.d.ts} +0 -0
- /package/browser/{keypair → src/keypair}/Secp256k1PointDeriver.d.ts +0 -0
- /package/browser/{metadata → src/metadata}/ContractBaseMetadata.d.ts +0 -0
- /package/browser/{metadata → src/metadata}/tokens.d.ts +0 -0
- /package/browser/{network → src/network}/ChainId.d.ts +0 -0
- /package/browser/{p2wda → src/p2wda}/P2WDADetector.d.ts +0 -0
- /package/browser/{signer → src/signer}/SignerUtils.d.ts +0 -0
- /package/browser/{signer → src/signer}/TweakedSigner.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/ContractAddress.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/TransactionFactory.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/browser/BrowserSignerBase.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/browser/Web3Provider.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/browser/extensions/UnisatSigner.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/browser/extensions/XverseSigner.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/browser/types/Unisat.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/browser/types/Xverse.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/builders/CancelTransaction.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/builders/ChallengeSolutionTransaction.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/builders/CustomScriptTransaction.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/builders/DeploymentTransaction.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/builders/FundingTransaction.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/builders/InteractionTransaction.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/builders/InteractionTransactionP2WDA.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/builders/MultiSignTransaction.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/builders/SharedInteractionTransaction.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/builders/TransactionBuilder.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/enums/TransactionType.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/interfaces/ITransactionParameters.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/interfaces/Tap.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/mineable/IP2WSHAddress.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/mineable/TimelockGenerator.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/processor/PsbtTransaction.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/psbt/PSBTTypes.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/shared/P2TR_MS.d.ts +0 -0
- /package/browser/{transaction → src/transaction}/shared/TweakedTransaction.d.ts +0 -0
- /package/browser/{utils → src/utils}/BitcoinUtils.d.ts +0 -0
- /package/browser/{utils → src/utils}/BufferHelper.d.ts +0 -0
- /package/browser/{utils → src/utils}/StringToBuffer.d.ts +0 -0
- /package/browser/{utils → src/utils}/lengths.d.ts +0 -0
- /package/browser/{utils → src/utils}/types.d.ts +0 -0
- /package/browser/{utxo → src/utxo}/OPNetLimitedProvider.d.ts +0 -0
- /package/browser/{utxo → src/utxo}/interfaces/BroadcastResponse.d.ts +0 -0
- /package/browser/{utxo → src/utxo}/interfaces/IUTXO.d.ts +0 -0
- /package/browser/{verification → src/verification}/TapscriptVerificator.d.ts +0 -0
- /package/{doc → documentation}/addresses/P2OP.md +0 -0
- /package/{doc → documentation}/addresses/P2WDA.md +0 -0
package/src/keypair/Address.ts
CHANGED
|
@@ -8,9 +8,16 @@ import { BitcoinUtils } from '../utils/BitcoinUtils.js';
|
|
|
8
8
|
import { TimeLockGenerator } from '../transaction/mineable/TimelockGenerator.js';
|
|
9
9
|
import { IP2WSHAddress } from '../transaction/mineable/IP2WSHAddress.js';
|
|
10
10
|
import { P2WDADetector } from '../p2wda/P2WDADetector.js';
|
|
11
|
+
import { sha256 } from '@noble/hashes/sha2';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
|
-
* Objects of type "Address"
|
|
14
|
+
* Objects of type "Address" represent hashed ML-DSA (quantum) public keys (using SHA256 of quantum keys) and maintain classical public keys separately.
|
|
15
|
+
* This class supports a hybrid quantum-classical architecture, allowing conversion to different address formats and management of both key types.
|
|
16
|
+
*
|
|
17
|
+
* The Address internally stores the SHA256 hash of the ML-DSA public key as its primary content, while maintaining
|
|
18
|
+
* the classical public key in a separate field. This enables quantum-resistant addressing while preserving
|
|
19
|
+
* compatibility with traditional Bitcoin cryptography.
|
|
20
|
+
*
|
|
14
21
|
* @category KeyPair
|
|
15
22
|
*/
|
|
16
23
|
export class Address extends Uint8Array {
|
|
@@ -22,15 +29,23 @@ export class Address extends Uint8Array {
|
|
|
22
29
|
#uncompressed: UncompressedPublicKey | undefined;
|
|
23
30
|
#tweakedUncompressed: Buffer | undefined;
|
|
24
31
|
#p2wda: IP2WSHAddress | undefined;
|
|
32
|
+
#mldsaPublicKey: Uint8Array | undefined;
|
|
25
33
|
|
|
26
|
-
|
|
34
|
+
private classicPublicKey: Uint8Array | undefined;
|
|
35
|
+
|
|
36
|
+
public constructor(mldsaPublicKey?: ArrayLike<number>, publicKeyOrTweak?: ArrayLike<number>) {
|
|
27
37
|
super(ADDRESS_BYTE_LENGTH);
|
|
28
38
|
|
|
29
|
-
if (!
|
|
39
|
+
if (!mldsaPublicKey) {
|
|
30
40
|
return;
|
|
31
41
|
}
|
|
32
42
|
|
|
33
|
-
|
|
43
|
+
if (publicKeyOrTweak) {
|
|
44
|
+
this.classicPublicKey = new Uint8Array(publicKeyOrTweak.length);
|
|
45
|
+
this.classicPublicKey.set(publicKeyOrTweak);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this.set(mldsaPublicKey);
|
|
34
49
|
}
|
|
35
50
|
|
|
36
51
|
/**
|
|
@@ -41,13 +56,17 @@ export class Address extends Uint8Array {
|
|
|
41
56
|
return this.#originalPublicKey;
|
|
42
57
|
}
|
|
43
58
|
|
|
59
|
+
public get mldsaPublicKey(): Uint8Array | undefined {
|
|
60
|
+
return this.#mldsaPublicKey;
|
|
61
|
+
}
|
|
62
|
+
|
|
44
63
|
/**
|
|
45
64
|
* Get the key pair for the address
|
|
46
65
|
* @description This is only for internal use. Please use address.tweakedBytes instead.
|
|
47
66
|
*/
|
|
48
67
|
private get keyPair(): ECPairInterface {
|
|
49
68
|
if (!this.#keyPair) {
|
|
50
|
-
throw new Error('
|
|
69
|
+
throw new Error('Classical public key not set for address');
|
|
51
70
|
}
|
|
52
71
|
|
|
53
72
|
return this.#keyPair;
|
|
@@ -55,35 +74,48 @@ export class Address extends Uint8Array {
|
|
|
55
74
|
|
|
56
75
|
public static dead(): Address {
|
|
57
76
|
return Address.fromString(
|
|
77
|
+
'0x0000000000000000000000000000000000000000000000000000000000000000', // DEAD ADDRESS
|
|
58
78
|
'0x04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f',
|
|
59
79
|
);
|
|
60
80
|
}
|
|
61
81
|
|
|
62
|
-
public static zero(): Address {
|
|
63
|
-
return new Address();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
82
|
/**
|
|
67
83
|
* Create an address from a hex string
|
|
68
|
-
* @param {string}
|
|
84
|
+
* @param {string} mldsaPublicKey The ml-dsa public key in hex format
|
|
85
|
+
* @param {string} classicPublicKey The classical public key in hex format
|
|
69
86
|
* @returns {Address} The address
|
|
70
87
|
*/
|
|
71
|
-
public static fromString(
|
|
72
|
-
if (!
|
|
88
|
+
public static fromString(mldsaPublicKey: string, classicPublicKey?: string): Address {
|
|
89
|
+
if (!mldsaPublicKey) {
|
|
73
90
|
throw new Error('Invalid public key');
|
|
74
91
|
}
|
|
75
92
|
|
|
76
|
-
if (
|
|
77
|
-
|
|
93
|
+
if (mldsaPublicKey.startsWith('0x')) {
|
|
94
|
+
mldsaPublicKey = mldsaPublicKey.slice(2);
|
|
78
95
|
}
|
|
79
96
|
|
|
80
|
-
if (!BitcoinUtils.isValidHex(
|
|
97
|
+
if (!BitcoinUtils.isValidHex(mldsaPublicKey)) {
|
|
81
98
|
throw new Error(
|
|
82
99
|
'You must only pass public keys in hexadecimal format. If you have an address such as bc1q... you must convert it to a public key first. Please refer to await provider.getPublicKeyInfo("bc1q..."). If the public key associated with the address is not found, you must force the user to enter the destination public key. It looks like: 0x020373626d317ae8788ce3280b491068610d840c23ecb64c14075bbb9f670af52c.',
|
|
83
100
|
);
|
|
84
101
|
}
|
|
85
102
|
|
|
86
|
-
|
|
103
|
+
let classicBuffer: Buffer | undefined;
|
|
104
|
+
if (classicPublicKey) {
|
|
105
|
+
if (classicPublicKey.startsWith('0x')) {
|
|
106
|
+
classicPublicKey = classicPublicKey.slice(2);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!BitcoinUtils.isValidHex(classicPublicKey)) {
|
|
110
|
+
throw new Error(
|
|
111
|
+
'You must only pass classical public keys in hexadecimal format. If you have an address such as bc1q... you must convert it to a public key first. Please refer to await provider.getPublicKeyInfo("bc1q..."). If the public key associated with the address is not found, you must force the user to enter the destination public key. It looks like: 0x020373626d317ae8788ce3280b491068610d840c23ecb64c14075bbb9f670af52c.',
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
classicBuffer = Buffer.from(classicPublicKey, 'hex');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return new Address(Buffer.from(mldsaPublicKey, 'hex'), classicBuffer);
|
|
87
119
|
}
|
|
88
120
|
|
|
89
121
|
/**
|
|
@@ -117,16 +149,40 @@ export class Address extends Uint8Array {
|
|
|
117
149
|
}
|
|
118
150
|
|
|
119
151
|
/**
|
|
120
|
-
* Converts the
|
|
121
|
-
* @returns {
|
|
152
|
+
* Converts the classical public key to a hex string
|
|
153
|
+
* @returns {string} The hex string
|
|
154
|
+
*/
|
|
155
|
+
public tweakedToHex(): string {
|
|
156
|
+
if (!this.classicPublicKey) {
|
|
157
|
+
throw new Error('Classical public key not set');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return '0x' + Buffer.from(this.classicPublicKey).toString('hex');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Converts the address content (SHA256 hash of ML-DSA public key) to a buffer
|
|
165
|
+
* @returns {Buffer} The buffer containing the hashed ML-DSA public key
|
|
122
166
|
*/
|
|
123
167
|
public toBuffer(): Buffer {
|
|
124
168
|
return Buffer.from(this);
|
|
125
169
|
}
|
|
126
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Converts the classical public key to a buffer
|
|
173
|
+
* @returns {Buffer} The buffer
|
|
174
|
+
*/
|
|
175
|
+
public tweakedPublicKeyToBuffer(): Buffer {
|
|
176
|
+
if (!this.classicPublicKey) {
|
|
177
|
+
throw new Error('Classical public key not set');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return Buffer.from(this.classicPublicKey);
|
|
181
|
+
}
|
|
182
|
+
|
|
127
183
|
public toUncompressedHex(): string {
|
|
128
184
|
if (!this.#uncompressed) {
|
|
129
|
-
throw new Error('
|
|
185
|
+
throw new Error('Classical public key not set');
|
|
130
186
|
}
|
|
131
187
|
|
|
132
188
|
return '0x' + this.#uncompressed.uncompressed.toString('hex');
|
|
@@ -134,7 +190,7 @@ export class Address extends Uint8Array {
|
|
|
134
190
|
|
|
135
191
|
public toUncompressedBuffer(): Buffer {
|
|
136
192
|
if (!this.#uncompressed) {
|
|
137
|
-
throw new Error('
|
|
193
|
+
throw new Error('Classical public key not set');
|
|
138
194
|
}
|
|
139
195
|
|
|
140
196
|
return this.#uncompressed.uncompressed;
|
|
@@ -142,7 +198,7 @@ export class Address extends Uint8Array {
|
|
|
142
198
|
|
|
143
199
|
public toHybridPublicKeyHex(): string {
|
|
144
200
|
if (!this.#uncompressed) {
|
|
145
|
-
throw new Error('
|
|
201
|
+
throw new Error('Classical public key not set');
|
|
146
202
|
}
|
|
147
203
|
|
|
148
204
|
return '0x' + this.#uncompressed.hybrid.toString('hex');
|
|
@@ -150,7 +206,7 @@ export class Address extends Uint8Array {
|
|
|
150
206
|
|
|
151
207
|
public toHybridPublicKeyBuffer(): Buffer {
|
|
152
208
|
if (!this.#uncompressed) {
|
|
153
|
-
throw new Error('
|
|
209
|
+
throw new Error('Classical public key not set');
|
|
154
210
|
}
|
|
155
211
|
|
|
156
212
|
return this.#uncompressed.hybrid;
|
|
@@ -158,7 +214,7 @@ export class Address extends Uint8Array {
|
|
|
158
214
|
|
|
159
215
|
public originalPublicKeyBuffer(): Buffer {
|
|
160
216
|
if (!this.#originalPublicKey) {
|
|
161
|
-
throw new Error('
|
|
217
|
+
throw new Error('Classical public key not set');
|
|
162
218
|
}
|
|
163
219
|
|
|
164
220
|
return Buffer.from(this.#originalPublicKey);
|
|
@@ -225,24 +281,56 @@ export class Address extends Uint8Array {
|
|
|
225
281
|
|
|
226
282
|
/**
|
|
227
283
|
* Set the public key
|
|
228
|
-
* @param {ArrayLike<number>}
|
|
284
|
+
* @param {ArrayLike<number>} mldsaPublicKey ML-DSA public key
|
|
229
285
|
* @returns {void}
|
|
230
286
|
*/
|
|
231
|
-
public override set(
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
287
|
+
public override set(mldsaPublicKey: ArrayLike<number>): void {
|
|
288
|
+
if (this.classicPublicKey) {
|
|
289
|
+
const validLengths = [ADDRESS_BYTE_LENGTH, 33, 65];
|
|
290
|
+
if (!validLengths.includes(this.classicPublicKey.length)) {
|
|
291
|
+
throw new Error(`Invalid public key length ${this.classicPublicKey.length}`);
|
|
292
|
+
}
|
|
236
293
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
294
|
+
if (this.classicPublicKey.length === ADDRESS_BYTE_LENGTH) {
|
|
295
|
+
const buf = Buffer.alloc(ADDRESS_BYTE_LENGTH);
|
|
296
|
+
buf.set(this.classicPublicKey);
|
|
240
297
|
|
|
241
|
-
|
|
298
|
+
this.#tweakedUncompressed = ContractAddress.generateHybridKeyFromHash(buf);
|
|
299
|
+
} else {
|
|
300
|
+
this.autoFormat(this.classicPublicKey);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
242
303
|
|
|
243
|
-
|
|
304
|
+
// THIS is the SHA256(ORIGINAL_ML_DSA_PUBLIC_KEY)
|
|
305
|
+
if (mldsaPublicKey.length === ADDRESS_BYTE_LENGTH) {
|
|
306
|
+
const buf = new Uint8Array(ADDRESS_BYTE_LENGTH);
|
|
307
|
+
buf.set(mldsaPublicKey);
|
|
308
|
+
|
|
309
|
+
super.set(buf);
|
|
244
310
|
} else {
|
|
245
|
-
|
|
311
|
+
// Validate ML-DSA public key lengths according to BIP360 and FIPS 204
|
|
312
|
+
// ML-DSA-44 (Level 2): 1312 bytes public key
|
|
313
|
+
// ML-DSA-65 (Level 3): 1952 bytes public key
|
|
314
|
+
// ML-DSA-87 (Level 5): 2592 bytes public key
|
|
315
|
+
const validMLDSALengths = [1312, 1952, 2592];
|
|
316
|
+
|
|
317
|
+
if (!validMLDSALengths.includes(mldsaPublicKey.length)) {
|
|
318
|
+
throw new Error(
|
|
319
|
+
`Invalid ML-DSA public key length: ${mldsaPublicKey.length}. ` +
|
|
320
|
+
`Expected 1312 (ML-DSA-44/LEVEL2), 1952 (ML-DSA-65/LEVEL3), or 2592 (ML-DSA-87/LEVEL5) bytes.`,
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Store the original ML-DSA public key
|
|
325
|
+
this.#mldsaPublicKey = new Uint8Array(mldsaPublicKey.length);
|
|
326
|
+
this.#mldsaPublicKey.set(mldsaPublicKey);
|
|
327
|
+
|
|
328
|
+
// Hash the ML-DSA public key to get the 32-byte address
|
|
329
|
+
const hashedPublicKey = sha256(new Uint8Array(mldsaPublicKey));
|
|
330
|
+
const buf = new Uint8Array(ADDRESS_BYTE_LENGTH);
|
|
331
|
+
buf.set(hashedPublicKey);
|
|
332
|
+
|
|
333
|
+
super.set(buf);
|
|
246
334
|
}
|
|
247
335
|
}
|
|
248
336
|
|
|
@@ -305,11 +393,18 @@ export class Address extends Uint8Array {
|
|
|
305
393
|
* @param {Network} network The network
|
|
306
394
|
*/
|
|
307
395
|
public p2tr(network: Network): string {
|
|
396
|
+
if (!this.classicPublicKey) {
|
|
397
|
+
throw new Error('Classical public key not set');
|
|
398
|
+
}
|
|
399
|
+
|
|
308
400
|
if (this.#p2tr && this.#network === network) {
|
|
309
401
|
return this.#p2tr;
|
|
310
402
|
}
|
|
311
403
|
|
|
312
|
-
const p2trAddy: string | undefined = EcKeyPair.tweakedPubKeyBufferToAddress(
|
|
404
|
+
const p2trAddy: string | undefined = EcKeyPair.tweakedPubKeyBufferToAddress(
|
|
405
|
+
this.classicPublicKey,
|
|
406
|
+
network,
|
|
407
|
+
);
|
|
313
408
|
|
|
314
409
|
if (p2trAddy) {
|
|
315
410
|
this.#network = network;
|
|
@@ -318,7 +413,7 @@ export class Address extends Uint8Array {
|
|
|
318
413
|
return p2trAddy;
|
|
319
414
|
}
|
|
320
415
|
|
|
321
|
-
throw new Error('
|
|
416
|
+
throw new Error('Classical public key not set');
|
|
322
417
|
}
|
|
323
418
|
|
|
324
419
|
/**
|
|
@@ -407,8 +502,14 @@ export class Address extends Uint8Array {
|
|
|
407
502
|
}
|
|
408
503
|
|
|
409
504
|
/**
|
|
410
|
-
*
|
|
411
|
-
*
|
|
505
|
+
* Returns the OPNet address encoded in bech32m format, derived from the SHA256 hash of the ML-DSA public key
|
|
506
|
+
* (which is what the Address internally stores).
|
|
507
|
+
*
|
|
508
|
+
* This method generates a P2OP (Pay-to-OPNet) address using witness version 16, suitable for
|
|
509
|
+
* quantum-resistant transactions on the OPNet protocol.
|
|
510
|
+
*
|
|
511
|
+
* @param network - The Bitcoin network to use (mainnet, testnet, regtest)
|
|
512
|
+
* @returns The P2OP address in bech32m format
|
|
412
513
|
*/
|
|
413
514
|
public p2op(network: Network): string {
|
|
414
515
|
if (this.#p2op && this.#network === network) {
|
|
@@ -423,12 +524,12 @@ export class Address extends Uint8Array {
|
|
|
423
524
|
return p2opAddy;
|
|
424
525
|
}
|
|
425
526
|
|
|
426
|
-
throw new Error('
|
|
527
|
+
throw new Error('ML-DSA public key not set');
|
|
427
528
|
}
|
|
428
529
|
|
|
429
530
|
public toTweakedHybridPublicKeyHex(): string {
|
|
430
531
|
if (!this.#tweakedUncompressed) {
|
|
431
|
-
throw new Error('
|
|
532
|
+
throw new Error('Classical public key not set');
|
|
432
533
|
}
|
|
433
534
|
|
|
434
535
|
return '0x' + this.#tweakedUncompressed.toString('hex');
|
|
@@ -436,7 +537,7 @@ export class Address extends Uint8Array {
|
|
|
436
537
|
|
|
437
538
|
public toTweakedHybridPublicKeyBuffer(): Buffer {
|
|
438
539
|
if (!this.#tweakedUncompressed) {
|
|
439
|
-
throw new Error('
|
|
540
|
+
throw new Error('Classical public key not set');
|
|
440
541
|
}
|
|
441
542
|
|
|
442
543
|
return this.#tweakedUncompressed;
|
|
@@ -462,6 +563,7 @@ export class Address extends Uint8Array {
|
|
|
462
563
|
|
|
463
564
|
this.#tweakedUncompressed = ContractAddress.generateHybridKeyFromHash(tweakedBytes);
|
|
464
565
|
|
|
465
|
-
|
|
566
|
+
this.classicPublicKey = new Uint8Array(ADDRESS_BYTE_LENGTH);
|
|
567
|
+
this.classicPublicKey.set(tweakedBytes);
|
|
466
568
|
}
|
|
467
569
|
}
|
|
@@ -3,6 +3,7 @@ import * as ecc from '@bitcoinerlab/secp256k1';
|
|
|
3
3
|
import { EcKeyPair } from './EcKeyPair.js';
|
|
4
4
|
import { BitcoinUtils } from '../utils/BitcoinUtils.js';
|
|
5
5
|
import { P2WDADetector } from '../p2wda/P2WDADetector.js';
|
|
6
|
+
import { MLDSASecurityLevel } from '@btc-vision/bip32';
|
|
6
7
|
|
|
7
8
|
initEccLib(ecc);
|
|
8
9
|
|
|
@@ -155,6 +156,85 @@ export class AddressVerificator {
|
|
|
155
156
|
return false; // Not a valid public key
|
|
156
157
|
}
|
|
157
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Checks if the input is a valid ML-DSA public key.
|
|
161
|
+
* ML-DSA public keys have specific lengths depending on the security level:
|
|
162
|
+
* - ML-DSA-44 (Level 2): 1312 bytes (2624 hex characters)
|
|
163
|
+
* - ML-DSA-65 (Level 3): 1952 bytes (3904 hex characters)
|
|
164
|
+
* - ML-DSA-87 (Level 5): 2592 bytes (5184 hex characters)
|
|
165
|
+
*
|
|
166
|
+
* @param input - The input string (hex format) or Buffer to check.
|
|
167
|
+
* @returns - The security level if valid, null otherwise.
|
|
168
|
+
*/
|
|
169
|
+
public static isValidMLDSAPublicKey(
|
|
170
|
+
input: string | Buffer | Uint8Array,
|
|
171
|
+
): MLDSASecurityLevel | null {
|
|
172
|
+
try {
|
|
173
|
+
let byteLength: number;
|
|
174
|
+
|
|
175
|
+
if (Buffer.isBuffer(input) || input instanceof Uint8Array) {
|
|
176
|
+
byteLength = input.length;
|
|
177
|
+
} else {
|
|
178
|
+
// Handle string input
|
|
179
|
+
if (input.startsWith('0x')) {
|
|
180
|
+
input = input.slice(2);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (!BitcoinUtils.isValidHex(input)) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
byteLength = input.length / 2;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Check against valid ML-DSA public key lengths
|
|
191
|
+
switch (byteLength) {
|
|
192
|
+
case 1312:
|
|
193
|
+
return MLDSASecurityLevel.LEVEL2; // ML-DSA-44
|
|
194
|
+
case 1952:
|
|
195
|
+
return MLDSASecurityLevel.LEVEL3; // ML-DSA-65
|
|
196
|
+
case 2592:
|
|
197
|
+
return MLDSASecurityLevel.LEVEL5; // ML-DSA-87
|
|
198
|
+
default:
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
} catch (e) {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Checks if the given address is a valid P2OP (OPNet) address.
|
|
208
|
+
* P2OP addresses use witness version 16 and are encoded in Bech32m format.
|
|
209
|
+
*
|
|
210
|
+
* @param inAddress - The address to check.
|
|
211
|
+
* @param network - The network to validate against.
|
|
212
|
+
* @returns - True if the address is a valid P2OP address, false otherwise.
|
|
213
|
+
*/
|
|
214
|
+
public static isValidP2OPAddress(inAddress: string, network: Network): boolean {
|
|
215
|
+
if (!inAddress || inAddress.length < 20) return false;
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
// Decode the Bech32/Bech32m address
|
|
219
|
+
const decodedAddress = address.fromBech32(inAddress);
|
|
220
|
+
|
|
221
|
+
// Check if it matches the network's bech32 or bech32Opnet prefix
|
|
222
|
+
const validPrefix =
|
|
223
|
+
decodedAddress.prefix === network.bech32 ||
|
|
224
|
+
decodedAddress.prefix === network.bech32Opnet;
|
|
225
|
+
|
|
226
|
+
if (!validPrefix) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// P2OP uses witness version 16 (OP_16)
|
|
231
|
+
// The data length should be 21 bytes (1 byte deployment version + 20 byte hash160)
|
|
232
|
+
return decodedAddress.version === 16 && decodedAddress.data.length === 21;
|
|
233
|
+
} catch {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
158
238
|
/**
|
|
159
239
|
* Checks if the address requires a redeem script to spend funds.
|
|
160
240
|
* @param {string} addy - The address to check.
|
|
@@ -183,7 +263,9 @@ export class AddressVerificator {
|
|
|
183
263
|
* - P2SH-P2WPKH (Wrapped SegWit)
|
|
184
264
|
* - P2PK (Pay to PubKey, technically treated similarly to P2PKH)
|
|
185
265
|
* - P2WPKH (SegWit address starting with 'bc1q' for mainnet or 'tb1q' for testnet)
|
|
266
|
+
* - P2WSH (SegWit script hash address)
|
|
186
267
|
* - P2TR (Taproot address starting with 'bc1p' for mainnet or 'tb1p' for testnet)
|
|
268
|
+
* - P2OP (OPNet contract address with witness version 16)
|
|
187
269
|
*
|
|
188
270
|
* @param addy - The Bitcoin address to validate.
|
|
189
271
|
* @param network - The Bitcoin network to validate against (mainnet, testnet, etc.).
|
|
@@ -209,12 +291,15 @@ export class AddressVerificator {
|
|
|
209
291
|
} catch {}
|
|
210
292
|
|
|
211
293
|
try {
|
|
212
|
-
// Try to decode as a Bech32 or Bech32m address (P2WPKH or
|
|
294
|
+
// Try to decode as a Bech32 or Bech32m address (P2WPKH, P2WSH, P2TR, or P2OP)
|
|
213
295
|
const decodedBech32 = address.fromBech32(addy);
|
|
296
|
+
|
|
297
|
+
// P2OP: OPNet contract addresses (version 16, 21 bytes data)
|
|
214
298
|
if (
|
|
215
299
|
(decodedBech32.prefix === network.bech32Opnet ||
|
|
216
300
|
decodedBech32.prefix === network.bech32) &&
|
|
217
|
-
decodedBech32.version === 16
|
|
301
|
+
decodedBech32.version === 16 &&
|
|
302
|
+
decodedBech32.data.length === 21
|
|
218
303
|
) {
|
|
219
304
|
return AddressTypes.P2OP;
|
|
220
305
|
}
|
package/src/keypair/EcKeyPair.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import * as ecc from '@bitcoinerlab/secp256k1';
|
|
2
|
-
import bip32, {
|
|
2
|
+
import bip32, {
|
|
3
|
+
BIP32API,
|
|
4
|
+
BIP32Factory,
|
|
5
|
+
BIP32Interface,
|
|
6
|
+
MLDSAKeyPair,
|
|
7
|
+
MLDSASecurityLevel,
|
|
8
|
+
QuantumBIP32Factory,
|
|
9
|
+
} from '@btc-vision/bip32';
|
|
3
10
|
import bitcoin, {
|
|
4
11
|
address,
|
|
5
12
|
fromOutputScript,
|
|
@@ -17,7 +24,7 @@ import { IWallet } from './interfaces/IWallet.js';
|
|
|
17
24
|
import { secp256k1 } from '@noble/curves/secp256k1';
|
|
18
25
|
import { mod } from '@noble/curves/abstract/modular';
|
|
19
26
|
import { sha256 } from '@noble/hashes/sha2';
|
|
20
|
-
import { bytesToNumberBE, concatBytes, utf8ToBytes } from '@noble/curves/utils.js';
|
|
27
|
+
import { bytesToNumberBE, concatBytes, randomBytes, utf8ToBytes } from '@noble/curves/utils.js';
|
|
21
28
|
|
|
22
29
|
initEccLib(ecc);
|
|
23
30
|
|
|
@@ -306,11 +313,17 @@ export class EcKeyPair {
|
|
|
306
313
|
}
|
|
307
314
|
|
|
308
315
|
/**
|
|
309
|
-
* Generate a random wallet
|
|
310
|
-
*
|
|
311
|
-
* @
|
|
316
|
+
* Generate a random wallet with both classical and quantum keys
|
|
317
|
+
*
|
|
318
|
+
* @param network - The network to use
|
|
319
|
+
* @param securityLevel - The ML-DSA security level for quantum keys (default: LEVEL2/44)
|
|
320
|
+
* @returns An object containing both classical and quantum key information
|
|
312
321
|
*/
|
|
313
|
-
public static generateWallet(
|
|
322
|
+
public static generateWallet(
|
|
323
|
+
network: Network = networks.bitcoin,
|
|
324
|
+
securityLevel: MLDSASecurityLevel = MLDSASecurityLevel.LEVEL2,
|
|
325
|
+
): IWallet {
|
|
326
|
+
// Generate classical keypair
|
|
314
327
|
const keyPair = this.ECPair.makeRandom({
|
|
315
328
|
network: network,
|
|
316
329
|
});
|
|
@@ -321,10 +334,49 @@ export class EcKeyPair {
|
|
|
321
334
|
throw new Error('Failed to generate wallet');
|
|
322
335
|
}
|
|
323
336
|
|
|
337
|
+
// Generate random quantum keypair with network
|
|
338
|
+
const quantumKeyPair = this.generateQuantumKeyPair(securityLevel, network);
|
|
339
|
+
|
|
324
340
|
return {
|
|
325
341
|
address: wallet,
|
|
326
342
|
privateKey: keyPair.toWIF(),
|
|
327
343
|
publicKey: Buffer.from(keyPair.publicKey).toString('hex'),
|
|
344
|
+
quantumPrivateKey: Buffer.from(quantumKeyPair.privateKey).toString('hex'),
|
|
345
|
+
quantumPublicKey: Buffer.from(quantumKeyPair.publicKey).toString('hex'),
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Generate a random quantum ML-DSA keypair
|
|
351
|
+
*
|
|
352
|
+
* This creates a standalone quantum-resistant keypair without using BIP32 derivation.
|
|
353
|
+
* The keys are generated using cryptographically secure random bytes.
|
|
354
|
+
*
|
|
355
|
+
* @param securityLevel - The ML-DSA security level (default: LEVEL2/44)
|
|
356
|
+
* @param network - The Bitcoin network (default: bitcoin mainnet)
|
|
357
|
+
* @returns A random ML-DSA keypair
|
|
358
|
+
*/
|
|
359
|
+
public static generateQuantumKeyPair(
|
|
360
|
+
securityLevel: MLDSASecurityLevel = MLDSASecurityLevel.LEVEL2,
|
|
361
|
+
network: Network = networks.bitcoin,
|
|
362
|
+
): MLDSAKeyPair {
|
|
363
|
+
// Generate random seed for quantum key generation
|
|
364
|
+
const randomSeed = randomBytes(64);
|
|
365
|
+
|
|
366
|
+
// Create a quantum root from the random seed with network parameter
|
|
367
|
+
const quantumRoot = QuantumBIP32Factory.fromSeed(
|
|
368
|
+
Buffer.from(randomSeed),
|
|
369
|
+
network,
|
|
370
|
+
securityLevel,
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
if (!quantumRoot.privateKey || !quantumRoot.publicKey) {
|
|
374
|
+
throw new Error('Failed to generate quantum keypair');
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
privateKey: Buffer.from(quantumRoot.privateKey),
|
|
379
|
+
publicKey: Buffer.from(quantumRoot.publicKey),
|
|
328
380
|
};
|
|
329
381
|
}
|
|
330
382
|
|
|
@@ -3,12 +3,20 @@ import * as ecc from '@bitcoinerlab/secp256k1';
|
|
|
3
3
|
import { crypto, Network, toXOnly } from '@btc-vision/bitcoin';
|
|
4
4
|
import { TweakedSigner } from '../signer/TweakedSigner.js';
|
|
5
5
|
import { EcKeyPair } from './EcKeyPair.js';
|
|
6
|
+
import { MLDSASecurityLevel, QuantumBIP32Interface } from '@btc-vision/bip32';
|
|
6
7
|
|
|
7
8
|
export interface SignedMessage {
|
|
8
9
|
readonly signature: Uint8Array;
|
|
9
10
|
readonly message: Uint8Array;
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
export interface MLDSASignedMessage {
|
|
14
|
+
readonly signature: Uint8Array;
|
|
15
|
+
readonly message: Uint8Array;
|
|
16
|
+
readonly publicKey: Uint8Array;
|
|
17
|
+
readonly securityLevel: MLDSASecurityLevel;
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
class MessageSignerBase {
|
|
13
21
|
public sha256(message: Buffer | Uint8Array): Buffer {
|
|
14
22
|
return crypto.sha256(Buffer.from(message));
|
|
@@ -101,6 +109,56 @@ class MessageSignerBase {
|
|
|
101
109
|
|
|
102
110
|
return this.verifySignature(tweakedPublicKey, message, signature);
|
|
103
111
|
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Signs a message using ML-DSA signature scheme.
|
|
115
|
+
* @param {QuantumBIP32Interface} mldsaKeypair - The ML-DSA keypair to sign with. Must contain a private key.
|
|
116
|
+
* @param {Uint8Array | Buffer | string} message - The message to sign.
|
|
117
|
+
* @returns The ML-DSA signature with metadata.
|
|
118
|
+
* @throws Error if the private key is missing.
|
|
119
|
+
*/
|
|
120
|
+
public signMLDSAMessage(
|
|
121
|
+
mldsaKeypair: QuantumBIP32Interface,
|
|
122
|
+
message: Uint8Array | Buffer | string,
|
|
123
|
+
): MLDSASignedMessage {
|
|
124
|
+
if (typeof message === 'string') {
|
|
125
|
+
message = Buffer.from(message, 'utf-8');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!mldsaKeypair.privateKey) {
|
|
129
|
+
throw new Error('ML-DSA private key not found in keypair.');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const hashedMessage = this.sha256(message);
|
|
133
|
+
const signature = mldsaKeypair.sign(hashedMessage);
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
signature: Buffer.from(signature),
|
|
137
|
+
message: hashedMessage,
|
|
138
|
+
publicKey: Buffer.from(mldsaKeypair.publicKey),
|
|
139
|
+
securityLevel: mldsaKeypair.securityLevel,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Verifies an ML-DSA signature using the provided keypair.
|
|
145
|
+
* @param {QuantumBIP32Interface} mldsaKeypair - The ML-DSA keypair with the public key.
|
|
146
|
+
* @param {Uint8Array | Buffer | string} message - The message to verify.
|
|
147
|
+
* @param {Uint8Array | Buffer} signature - The ML-DSA signature to verify.
|
|
148
|
+
* @returns True if the signature is valid, false otherwise.
|
|
149
|
+
*/
|
|
150
|
+
public verifyMLDSASignature(
|
|
151
|
+
mldsaKeypair: QuantumBIP32Interface,
|
|
152
|
+
message: Uint8Array | Buffer | string,
|
|
153
|
+
signature: Uint8Array | Buffer,
|
|
154
|
+
): boolean {
|
|
155
|
+
if (typeof message === 'string') {
|
|
156
|
+
message = Buffer.from(message, 'utf-8');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const hashedMessage = this.sha256(message);
|
|
160
|
+
return mldsaKeypair.verify(hashedMessage, signature);
|
|
161
|
+
}
|
|
104
162
|
}
|
|
105
163
|
|
|
106
164
|
export const MessageSigner = new MessageSignerBase();
|