@exodus/bip322-js 1.1.0-rc.0 → 1.1.0
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/README.md +13 -13
- package/dist/BIP322.d.ts +33 -2
- package/dist/BIP322.js +91 -14
- package/dist/Signer.d.ts +20 -1
- package/dist/Signer.js +97 -32
- package/dist/Verifier.d.ts +39 -0
- package/dist/Verifier.js +155 -34
- package/dist/bitcoinjs/DecodeScriptSignature.d.ts +1 -0
- package/dist/bitcoinjs/DecodeScriptSignature.js +7 -1
- package/dist/bitcoinjs/index.js +5 -2
- package/dist/helpers/Address.d.ts +47 -0
- package/dist/helpers/Address.js +97 -7
- package/dist/helpers/BIP137.d.ts +21 -0
- package/dist/helpers/BIP137.js +54 -4
- package/dist/helpers/VarInt.d.ts +17 -0
- package/dist/helpers/VarInt.js +28 -3
- package/dist/helpers/VarStr.d.ts +17 -0
- package/dist/helpers/VarStr.js +32 -5
- package/dist/helpers/Witness.d.ts +18 -0
- package/dist/helpers/Witness.js +46 -9
- package/dist/helpers/index.js +16 -6
- package/dist/index.js +17 -5
- package/package.json +44 -46
package/dist/Verifier.js
CHANGED
|
@@ -1,118 +1,239 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
// Import dependencies
|
|
30
|
+
const BIP322_1 = __importDefault(require("./BIP322"));
|
|
31
|
+
const helpers_1 = require("./helpers");
|
|
32
|
+
const bitcoin = __importStar(require("@exodus/bitcoinjs-lib"));
|
|
33
|
+
const bitcoinerlab_secp256k1_1 = __importDefault(require("@exodus/bitcoinerlab-secp256k1"));
|
|
34
|
+
const bitcoinMessage = __importStar(require("bitcoinjs-message"));
|
|
35
|
+
const bitcoinjs_1 = require("./bitcoinjs");
|
|
36
|
+
/**
|
|
37
|
+
* Class that handles BIP-322 signature verification.
|
|
38
|
+
* Reference: https://github.com/LegReq/bip0322-signatures/blob/master/BIP0322_verification.ipynb
|
|
39
|
+
*/
|
|
7
40
|
class Verifier {
|
|
41
|
+
/**
|
|
42
|
+
* Verify a BIP-322 signature from P2WPKH, P2SH-P2WPKH, and single-key-spend P2TR address.
|
|
43
|
+
* @param signerAddress Address of the signing address
|
|
44
|
+
* @param message message_challenge signed by the address
|
|
45
|
+
* @param signatureBase64 Signature produced by the signing address
|
|
46
|
+
* @returns True if the provided signature is a valid BIP-322 signature for the given message and address, false if otherwise
|
|
47
|
+
* @throws If the provided signature fails basic validation, or if unsupported address and signature are provided
|
|
48
|
+
*/
|
|
8
49
|
static verifySignature(signerAddress, message, signatureBase64) {
|
|
9
|
-
|
|
50
|
+
// Handle legacy BIP-137 signature
|
|
51
|
+
// For P2PKH address, assume the signature is also a legacy signature
|
|
52
|
+
if (helpers_1.Address.isP2PKH(signerAddress) || helpers_1.BIP137.isBIP137Signature(signatureBase64)) {
|
|
10
53
|
return this.verifyBIP137Signature(signerAddress, message, signatureBase64);
|
|
11
54
|
}
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
55
|
+
// Convert address into corresponding script pubkey
|
|
56
|
+
const scriptPubKey = helpers_1.Address.convertAdressToScriptPubkey(signerAddress);
|
|
57
|
+
// Draft corresponding toSpend and toSign transaction using the message and script pubkey
|
|
58
|
+
const toSpendTx = BIP322_1.default.buildToSpendTx(message, scriptPubKey);
|
|
59
|
+
const toSignTx = BIP322_1.default.buildToSignTx(toSpendTx.getId(), scriptPubKey);
|
|
60
|
+
// Add the witness stack into the toSignTx
|
|
15
61
|
toSignTx.updateInput(0, {
|
|
16
62
|
finalScriptWitness: Buffer.from(signatureBase64, 'base64')
|
|
17
63
|
});
|
|
64
|
+
// Obtain the signature within the witness components
|
|
18
65
|
const witness = toSignTx.extractTransaction().ins[0].witness;
|
|
19
66
|
const encodedSignature = witness[0];
|
|
20
|
-
|
|
67
|
+
// Branch depending on whether the signing address is a non-taproot or a taproot address
|
|
68
|
+
if (helpers_1.Address.isP2WPKHWitness(witness)) {
|
|
69
|
+
// For non-taproot segwit transaciton, public key is included as the second part of the witness data
|
|
21
70
|
const publicKey = witness[1];
|
|
22
|
-
const { signature } = decodeScriptSignature(encodedSignature);
|
|
71
|
+
const { signature } = (0, bitcoinjs_1.decodeScriptSignature)(encodedSignature);
|
|
72
|
+
// Compute OP_HASH160(publicKey)
|
|
23
73
|
const hashedPubkey = bitcoin.crypto.hash160(publicKey);
|
|
24
|
-
|
|
25
|
-
|
|
74
|
+
// Common path variable
|
|
75
|
+
let hashToSign; // Hash expected to be signed by the signing address
|
|
76
|
+
if (helpers_1.Address.isP2SH(signerAddress)) {
|
|
77
|
+
// P2SH-P2WPKH verification path
|
|
78
|
+
// Compute the hash that correspond to the toSignTx
|
|
26
79
|
hashToSign = this.getHashForSigP2SHInP2WPKH(toSignTx, hashedPubkey);
|
|
80
|
+
// The original locking script for P2SH-P2WPKH is OP_0 <PubKeyHash>
|
|
27
81
|
const lockingScript = Buffer.concat([Buffer.from([0x00, 0x14]), hashedPubkey]);
|
|
82
|
+
// Compute OP_HASH160(lockingScript)
|
|
28
83
|
const hashedLockingScript = bitcoin.crypto.hash160(lockingScript);
|
|
84
|
+
// For nested segwit (P2SH-P2WPKH) address, the hashed locking script is located from the 3rd byte to the last 2nd byte as OP_HASH160 <HASH> OP_EQUAL
|
|
29
85
|
const hashedLockingScriptInScriptPubKey = scriptPubKey.subarray(2, -1);
|
|
86
|
+
// Check if the P2SH locking script OP_HASH160 <HASH> OP_EQUAL is satisified
|
|
30
87
|
if (Buffer.compare(hashedLockingScript, hashedLockingScriptInScriptPubKey) !== 0) {
|
|
31
|
-
return false;
|
|
88
|
+
return false; // Reject signature if the hashed locking script is different from the hashed locking script in the scriptPubKey
|
|
32
89
|
}
|
|
33
90
|
}
|
|
34
91
|
else {
|
|
92
|
+
// P2WPKH verification path
|
|
93
|
+
// Compute the hash that correspond to the toSignTx
|
|
35
94
|
hashToSign = this.getHashForSigP2WPKH(toSignTx);
|
|
95
|
+
// For native segwit address, the hashed public key is located from the 3rd to the end as OP_0 <HASH>
|
|
36
96
|
const hashedPubkeyInScriptPubkey = scriptPubKey.subarray(2);
|
|
97
|
+
// Check if OP_HASH160(publicKey) === hashedPubkeyInScriptPubkey
|
|
37
98
|
if (Buffer.compare(hashedPubkey, hashedPubkeyInScriptPubkey) !== 0) {
|
|
38
|
-
return false;
|
|
99
|
+
return false; // Reject signature if the hashed public key did not match
|
|
39
100
|
}
|
|
40
101
|
}
|
|
41
|
-
|
|
102
|
+
// Computing OP_CHECKSIG in Javascript
|
|
103
|
+
return bitcoinerlab_secp256k1_1.default.verify(hashToSign, publicKey, signature);
|
|
42
104
|
}
|
|
43
|
-
else if (Address.isP2TR(signerAddress)) {
|
|
44
|
-
if
|
|
105
|
+
else if (helpers_1.Address.isP2TR(signerAddress)) {
|
|
106
|
+
// Check if the witness stack correspond to a single-key-spend P2TR address
|
|
107
|
+
if (!helpers_1.Address.isSingleKeyP2TRWitness(witness)) {
|
|
45
108
|
throw new Error('BIP-322 verification from script-spend P2TR is unsupported.');
|
|
46
109
|
}
|
|
110
|
+
// For taproot address, the public key is located starting from the 3rd byte of the script public key
|
|
47
111
|
const publicKey = scriptPubKey.subarray(2);
|
|
112
|
+
// Compute the hash to be signed by the signing address
|
|
113
|
+
// Reference: https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#user-content-Taproot_key_path_spending_signature_validation
|
|
48
114
|
let hashToSign;
|
|
49
115
|
let signature;
|
|
50
116
|
if (encodedSignature.byteLength === 64) {
|
|
117
|
+
// If a BIP-341 signature is 64 bytes, the signature is signed using SIGHASH_DEFAULT 0x00
|
|
51
118
|
hashToSign = this.getHashForSigP2TR(toSignTx, 0x00);
|
|
119
|
+
// And the entirety of the encoded signature is the actual signature
|
|
52
120
|
signature = encodedSignature;
|
|
53
121
|
}
|
|
54
122
|
else if (encodedSignature.byteLength === 65) {
|
|
123
|
+
// If a BIP-341 signature is 65 bytes, the signature is signed using SIGHASH included at the last byte of the signature
|
|
55
124
|
hashToSign = this.getHashForSigP2TR(toSignTx, encodedSignature[64]);
|
|
125
|
+
// And encodedSignature[0:64] holds the actual signature
|
|
56
126
|
signature = encodedSignature.subarray(0, -1);
|
|
57
127
|
}
|
|
58
128
|
else {
|
|
129
|
+
// Fail validation if the signature is not 64 or 65 bytes
|
|
59
130
|
throw new Error('Invalid Schnorr signature provided.');
|
|
60
131
|
}
|
|
61
|
-
|
|
132
|
+
// Computing OP_CHECKSIG in Javascript
|
|
133
|
+
return bitcoinerlab_secp256k1_1.default.verifySchnorr(hashToSign, publicKey, signature);
|
|
62
134
|
}
|
|
63
135
|
else {
|
|
64
136
|
throw new Error('Only P2WPKH, P2SH-P2WPKH, and single-key-spend P2TR BIP-322 verification is supported. Unsupported address is provided.');
|
|
65
137
|
}
|
|
66
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Verify a legacy BIP-137 signature.
|
|
141
|
+
* Note that a signature is considered valid for all types of addresses that can be derived from the recovered public key.
|
|
142
|
+
* @param signerAddress Address of the signing address
|
|
143
|
+
* @param message message_challenge signed by the address
|
|
144
|
+
* @param signatureBase64 Signature produced by the signing address
|
|
145
|
+
* @returns True if the provided signature is a valid BIP-137 signature for the given message and address, false if otherwise
|
|
146
|
+
* @throws If the provided signature fails basic validation, or if unsupported address and signature are provided
|
|
147
|
+
*/
|
|
67
148
|
static verifyBIP137Signature(signerAddress, message, signatureBase64) {
|
|
68
|
-
if (Address.isP2PKH(signerAddress)) {
|
|
149
|
+
if (helpers_1.Address.isP2PKH(signerAddress)) {
|
|
69
150
|
return bitcoinMessage.verify(message, signerAddress, signatureBase64);
|
|
70
151
|
}
|
|
71
152
|
else {
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
153
|
+
// Recover the public key associated with the signature
|
|
154
|
+
const publicKeySigned = helpers_1.BIP137.derivePubKey(message, signatureBase64);
|
|
155
|
+
// Set the equivalent legacy address to prepare for validation from bitcoinjs-message
|
|
156
|
+
const legacySigningAddress = helpers_1.Address.convertPubKeyIntoAddress(publicKeySigned, 'p2pkh').mainnet;
|
|
157
|
+
// Make sure that public key recovered corresponds to the claimed signing address
|
|
158
|
+
if (helpers_1.Address.isP2SH(signerAddress)) {
|
|
159
|
+
// Assume it is a P2SH-P2WPKH address, derive a P2SH-P2WPKH address based on the public key recovered
|
|
160
|
+
const p2shAddressDerived = helpers_1.Address.convertPubKeyIntoAddress(publicKeySigned, 'p2sh-p2wpkh');
|
|
161
|
+
// Assert that the derived address is identical to the claimed signing address
|
|
76
162
|
if (p2shAddressDerived.mainnet !== signerAddress && p2shAddressDerived.testnet !== signerAddress) {
|
|
77
|
-
return false;
|
|
163
|
+
return false; // Derived address did not match with the claimed signing address
|
|
78
164
|
}
|
|
79
165
|
}
|
|
80
|
-
else if (Address.isP2WPKH(signerAddress)) {
|
|
81
|
-
|
|
166
|
+
else if (helpers_1.Address.isP2WPKH(signerAddress)) {
|
|
167
|
+
// Assume it is a P2WPKH address, derive a P2WPKH address based on the public key recovered
|
|
168
|
+
const p2wpkhAddressDerived = helpers_1.Address.convertPubKeyIntoAddress(publicKeySigned, 'p2wpkh');
|
|
169
|
+
// Assert that the derived address is identical to the claimed signing address
|
|
82
170
|
if (p2wpkhAddressDerived.mainnet !== signerAddress && p2wpkhAddressDerived.testnet !== signerAddress) {
|
|
83
|
-
return false;
|
|
171
|
+
return false; // Derived address did not match with the claimed signing address
|
|
84
172
|
}
|
|
85
173
|
}
|
|
86
|
-
else if (Address.isP2TR(signerAddress)) {
|
|
87
|
-
|
|
174
|
+
else if (helpers_1.Address.isP2TR(signerAddress)) {
|
|
175
|
+
// Assume it is a P2TR address, derive a P2TR address based on the public key recovered
|
|
176
|
+
const p2trAddressDerived = helpers_1.Address.convertPubKeyIntoAddress(publicKeySigned, 'p2tr');
|
|
177
|
+
// Assert that the derived address is identical to the claimed signing address
|
|
88
178
|
if (p2trAddressDerived.mainnet !== signerAddress && p2trAddressDerived.testnet !== signerAddress) {
|
|
89
|
-
return false;
|
|
179
|
+
return false; // Derived address did not match with the claimed signing address
|
|
90
180
|
}
|
|
91
181
|
}
|
|
92
182
|
else {
|
|
93
|
-
return false;
|
|
183
|
+
return false; // Unsupported address type
|
|
94
184
|
}
|
|
185
|
+
// Validate the signature using bitcoinjs-message if address assertion succeeded
|
|
95
186
|
return bitcoinMessage.verify(message, legacySigningAddress, signatureBase64);
|
|
96
187
|
}
|
|
97
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Compute the hash to be signed for a given P2WPKH BIP-322 toSign transaction.
|
|
191
|
+
* @param toSignTx PSBT instance of the toSign transaction
|
|
192
|
+
* @returns Computed transaction hash that requires signing
|
|
193
|
+
*/
|
|
98
194
|
static getHashForSigP2WPKH(toSignTx) {
|
|
195
|
+
// Create a signing script to unlock the P2WPKH output based on the P2PKH template
|
|
196
|
+
// Reference: https://github.com/bitcoinjs/bitcoinjs-lib/blob/1a9119b53bcea4b83a6aa8b948f0e6370209b1b4/ts_src/psbt.ts#L1654
|
|
99
197
|
const signingScript = bitcoin.payments.p2pkh({
|
|
100
198
|
hash: toSignTx.data.inputs[0].witnessUtxo.script.subarray(2)
|
|
101
199
|
}).output;
|
|
200
|
+
// Return computed transaction hash to be signed
|
|
102
201
|
return toSignTx.extractTransaction().hashForWitnessV0(0, signingScript, 0, bitcoin.Transaction.SIGHASH_ALL);
|
|
103
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* Compute the hash to be signed for a given P2SH-P2WPKH BIP-322 toSign transaction.
|
|
205
|
+
* @param toSignTx PSBT instance of the toSign transaction
|
|
206
|
+
* @param hashedPubkey Hashed public key of the signing address
|
|
207
|
+
* @returns Computed transaction hash that requires signing
|
|
208
|
+
*/
|
|
104
209
|
static getHashForSigP2SHInP2WPKH(toSignTx, hashedPubkey) {
|
|
210
|
+
// Create a signing script to unlock the P2WPKH output based on the P2PKH template
|
|
211
|
+
// Reference: https://github.com/bitcoinjs/bitcoinjs-lib/blob/1a9119b53bcea4b83a6aa8b948f0e6370209b1b4/ts_src/psbt.ts#L1654
|
|
212
|
+
// Like P2WPKH, the hash for deriving the meaningfulScript for a P2SH-P2WPKH transaction is its public key hash
|
|
213
|
+
// It can be derived by hashing the provided public key in the witness stack
|
|
105
214
|
const signingScript = bitcoin.payments.p2pkh({
|
|
106
215
|
hash: hashedPubkey
|
|
107
216
|
}).output;
|
|
217
|
+
// Return computed transaction hash to be signed
|
|
108
218
|
return toSignTx.extractTransaction().hashForWitnessV0(0, signingScript, 0, bitcoin.Transaction.SIGHASH_ALL);
|
|
109
219
|
}
|
|
220
|
+
/**
|
|
221
|
+
* Compute the hash to be signed for a given P2TR BIP-322 toSign transaction.
|
|
222
|
+
* @param toSignTx PSBT instance of the toSign transaction
|
|
223
|
+
* @param hashType Hash type used to sign the toSign transaction, must be either 0x00 or 0x01
|
|
224
|
+
* @returns Computed transaction hash that requires signing
|
|
225
|
+
* @throws Error if hashType is anything other than 0x00 or 0x01
|
|
226
|
+
*/
|
|
110
227
|
static getHashForSigP2TR(toSignTx, hashType) {
|
|
228
|
+
// BIP-322 states that 'all signatures must use the SIGHASH_ALL flag'
|
|
229
|
+
// But, in BIP-341, SIGHASH_DEFAULT (0x00) is equivalent to SIGHASH_ALL (0x01) so both should be allowed
|
|
111
230
|
if (hashType !== bitcoin.Transaction.SIGHASH_DEFAULT && hashType !== bitcoin.Transaction.SIGHASH_ALL) {
|
|
231
|
+
// Throw error if hashType is neither SIGHASH_DEFAULT or SIGHASH_ALL
|
|
112
232
|
throw new Error('Invalid SIGHASH used in signature. Must be either SIGHASH_ALL or SIGHASH_DEFAULT.');
|
|
113
233
|
}
|
|
234
|
+
// Return computed transaction hash to be signed
|
|
114
235
|
return toSignTx.extractTransaction().hashForWitnessV1(0, [toSignTx.data.inputs[0].witnessUtxo.script], [0], hashType);
|
|
115
236
|
}
|
|
116
237
|
}
|
|
117
|
-
|
|
238
|
+
exports.default = Verifier;
|
|
118
239
|
//# sourceMappingURL=Verifier.js.map
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decodeScriptSignature = void 0;
|
|
4
|
+
// Taken from https://github.com/bitcoinjs/bitcoinjs-lib/blob/5d2ff1c61165932e2814d5f37630e6720168561c/ts_src/script_signature.ts#L29
|
|
5
|
+
function decodeScriptSignature(buffer) {
|
|
2
6
|
const hashType = buffer.readUInt8(buffer.length - 1);
|
|
3
7
|
const hashTypeMod = hashType & ~0x80;
|
|
4
8
|
if (hashTypeMod <= 0 || hashTypeMod >= 4)
|
|
@@ -9,6 +13,7 @@ export function decodeScriptSignature(buffer) {
|
|
|
9
13
|
const signature = Buffer.concat([r, s], 64);
|
|
10
14
|
return { signature, hashType };
|
|
11
15
|
}
|
|
16
|
+
exports.decodeScriptSignature = decodeScriptSignature;
|
|
12
17
|
function fromDER(x) {
|
|
13
18
|
if (x[0] === 0x00)
|
|
14
19
|
x = x.slice(1);
|
|
@@ -48,6 +53,7 @@ function decode2(buffer) {
|
|
|
48
53
|
throw new Error('S value is negative');
|
|
49
54
|
if (lenS > 1 && buffer[lenR + 6] === 0x00 && !(buffer[lenR + 7] & 0x80))
|
|
50
55
|
throw new Error('S value excessively padded');
|
|
56
|
+
// non-BIP66 - extract R, S values
|
|
51
57
|
return {
|
|
52
58
|
r: buffer.slice(4, 4 + lenR),
|
|
53
59
|
s: buffer.slice(6 + lenR),
|
package/dist/bitcoinjs/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decodeScriptSignature = void 0;
|
|
4
|
+
const DecodeScriptSignature_1 = require("./DecodeScriptSignature");
|
|
5
|
+
Object.defineProperty(exports, "decodeScriptSignature", { enumerable: true, get: function () { return DecodeScriptSignature_1.decodeScriptSignature; } });
|
|
3
6
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,11 +1,58 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/**
|
|
3
|
+
* Class that implement address-related utility functions.
|
|
4
|
+
*/
|
|
1
5
|
declare class Address {
|
|
6
|
+
/**
|
|
7
|
+
* Check if a given Bitcoin address is a pay-to-public-key-hash (p2pkh) address.
|
|
8
|
+
* @param address Bitcoin address to be checked
|
|
9
|
+
* @returns True if the provided address correspond to a valid P2PKH address, false if otherwise
|
|
10
|
+
*/
|
|
2
11
|
static isP2PKH(address: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Check if a given Bitcoin address is a pay-to-script-hash (P2SH) address.
|
|
14
|
+
* @param address Bitcoin address to be checked
|
|
15
|
+
* @returns True if the provided address correspond to a valid P2SH address, false if otherwise
|
|
16
|
+
*/
|
|
3
17
|
static isP2SH(address: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Check if a given Bitcoin address is a pay-to-witness-public-key-hash (P2WPKH) address.
|
|
20
|
+
* @param address Bitcoin address to be checked
|
|
21
|
+
* @returns True if the provided address correspond to a valid P2WPKH address, false if otherwise
|
|
22
|
+
*/
|
|
4
23
|
static isP2WPKH(address: string): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Check if a given Bitcoin address is a taproot address.
|
|
26
|
+
* @param address Bitcoin address to be checked
|
|
27
|
+
* @returns True if the provided address is a taproot address, false if otherwise
|
|
28
|
+
*/
|
|
5
29
|
static isP2TR(address: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Check if a given witness stack corresponds to a P2WPKH address.
|
|
32
|
+
* @param witness Witness data associated with the toSign BIP-322 transaction
|
|
33
|
+
* @returns True if the provided witness stack correspond to a valid P2WPKH address, false if otherwise
|
|
34
|
+
*/
|
|
6
35
|
static isP2WPKHWitness(witness: Buffer[]): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Check if a given witness stack corresponds to a single-key-spend P2TR address.
|
|
38
|
+
* @param witness Witness data associated with the toSign BIP-322 transaction
|
|
39
|
+
* @returns True if the provided address and witness stack correspond to a valid single-key-spend P2TR address, false if otherwise
|
|
40
|
+
*/
|
|
7
41
|
static isSingleKeyP2TRWitness(witness: Buffer[]): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Convert a given Bitcoin address into its corresponding script public key.
|
|
44
|
+
* Reference: https://github.com/buidl-bitcoin/buidl-python/blob/d79e9808e8ca60975d315be41293cb40d968626d/buidl/script.py#L607
|
|
45
|
+
* @param address Bitcoin address
|
|
46
|
+
* @returns Script public key of the given Bitcoin address
|
|
47
|
+
* @throws Error when the provided address is not a valid Bitcoin address
|
|
48
|
+
*/
|
|
8
49
|
static convertAdressToScriptPubkey(address: string): Buffer;
|
|
50
|
+
/**
|
|
51
|
+
* Convert a given public key into a corresponding Bitcoin address.
|
|
52
|
+
* @param publicKey Public key for deriving the address, or internal public key for deriving taproot address
|
|
53
|
+
* @param addressType Bitcoin address type to be derived, must be either 'p2pkh', 'p2sh-p2wpkh', 'p2wpkh', or 'p2tr'
|
|
54
|
+
* @returns Bitcoin address that correspond to the given public key in both mainnet and testnet
|
|
55
|
+
*/
|
|
9
56
|
static convertPubKeyIntoAddress(publicKey: Buffer, addressType: 'p2pkh' | 'p2sh-p2wpkh' | 'p2wpkh' | 'p2tr'): {
|
|
10
57
|
mainnet: string;
|
|
11
58
|
testnet: string;
|
package/dist/helpers/Address.js
CHANGED
|
@@ -1,44 +1,106 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
// Import dependency
|
|
27
|
+
const bitcoin = __importStar(require("@exodus/bitcoinjs-lib"));
|
|
28
|
+
/**
|
|
29
|
+
* Class that implement address-related utility functions.
|
|
30
|
+
*/
|
|
2
31
|
class Address {
|
|
32
|
+
/**
|
|
33
|
+
* Check if a given Bitcoin address is a pay-to-public-key-hash (p2pkh) address.
|
|
34
|
+
* @param address Bitcoin address to be checked
|
|
35
|
+
* @returns True if the provided address correspond to a valid P2PKH address, false if otherwise
|
|
36
|
+
*/
|
|
3
37
|
static isP2PKH(address) {
|
|
38
|
+
// Check if the provided address is a P2PKH address
|
|
4
39
|
if (address[0] === '1' || address[0] === 'm' || address[0] === 'n') {
|
|
5
|
-
return true;
|
|
40
|
+
return true; // P2PKH address
|
|
6
41
|
}
|
|
7
42
|
else {
|
|
8
43
|
return false;
|
|
9
44
|
}
|
|
10
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Check if a given Bitcoin address is a pay-to-script-hash (P2SH) address.
|
|
48
|
+
* @param address Bitcoin address to be checked
|
|
49
|
+
* @returns True if the provided address correspond to a valid P2SH address, false if otherwise
|
|
50
|
+
*/
|
|
11
51
|
static isP2SH(address) {
|
|
52
|
+
// Check if the provided address is a P2SH address
|
|
12
53
|
if (address[0] === '3' || address[0] === '2') {
|
|
13
|
-
return true;
|
|
54
|
+
return true; // P2SH address
|
|
14
55
|
}
|
|
15
56
|
else {
|
|
16
57
|
return false;
|
|
17
58
|
}
|
|
18
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Check if a given Bitcoin address is a pay-to-witness-public-key-hash (P2WPKH) address.
|
|
62
|
+
* @param address Bitcoin address to be checked
|
|
63
|
+
* @returns True if the provided address correspond to a valid P2WPKH address, false if otherwise
|
|
64
|
+
*/
|
|
19
65
|
static isP2WPKH(address) {
|
|
66
|
+
// Check if the provided address is a P2WPKH/P2WSH address
|
|
20
67
|
if (address.slice(0, 4) === 'bc1q' || address.slice(0, 4) === 'tb1q') {
|
|
68
|
+
// Either a P2WPKH / P2WSH address
|
|
69
|
+
// Convert the address into a scriptPubKey
|
|
21
70
|
const scriptPubKey = this.convertAdressToScriptPubkey(address);
|
|
71
|
+
// Check if the scriptPubKey is exactly 22 bytes since P2WPKH scriptPubKey should be 0014<20-BYTE-PUBKEY-HASH>
|
|
22
72
|
if (scriptPubKey.byteLength === 22) {
|
|
23
|
-
return true;
|
|
73
|
+
return true; // P2WPKH
|
|
24
74
|
}
|
|
25
75
|
else {
|
|
26
|
-
return false;
|
|
76
|
+
return false; // Not P2WPKH, probably P2WSH
|
|
27
77
|
}
|
|
28
78
|
}
|
|
29
79
|
else {
|
|
30
80
|
return false;
|
|
31
81
|
}
|
|
32
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Check if a given Bitcoin address is a taproot address.
|
|
85
|
+
* @param address Bitcoin address to be checked
|
|
86
|
+
* @returns True if the provided address is a taproot address, false if otherwise
|
|
87
|
+
*/
|
|
33
88
|
static isP2TR(address) {
|
|
34
89
|
if (address.slice(0, 4) === 'bc1p' || address.slice(0, 4) === 'tb1p') {
|
|
35
|
-
return true;
|
|
90
|
+
return true; // P2TR address
|
|
36
91
|
}
|
|
37
92
|
else {
|
|
38
93
|
return false;
|
|
39
94
|
}
|
|
40
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Check if a given witness stack corresponds to a P2WPKH address.
|
|
98
|
+
* @param witness Witness data associated with the toSign BIP-322 transaction
|
|
99
|
+
* @returns True if the provided witness stack correspond to a valid P2WPKH address, false if otherwise
|
|
100
|
+
*/
|
|
41
101
|
static isP2WPKHWitness(witness) {
|
|
102
|
+
// Check whether the witness stack is as expected for a P2WPKH address
|
|
103
|
+
// It should contain exactly two items, with the second item being a public key with 33 bytes, and the first byte must be either 0x02/0x03
|
|
42
104
|
if (witness.length === 2 && witness[1].byteLength === 33 && (witness[1][0] === 0x02 || witness[1][0] === 0x03)) {
|
|
43
105
|
return true;
|
|
44
106
|
}
|
|
@@ -46,7 +108,14 @@ class Address {
|
|
|
46
108
|
return false;
|
|
47
109
|
}
|
|
48
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Check if a given witness stack corresponds to a single-key-spend P2TR address.
|
|
113
|
+
* @param witness Witness data associated with the toSign BIP-322 transaction
|
|
114
|
+
* @returns True if the provided address and witness stack correspond to a valid single-key-spend P2TR address, false if otherwise
|
|
115
|
+
*/
|
|
49
116
|
static isSingleKeyP2TRWitness(witness) {
|
|
117
|
+
// Check whether the witness stack is as expected for a single-key-spend taproot address
|
|
118
|
+
// It should contain exactly one items which is the signature for the transaction
|
|
50
119
|
if (witness.length === 1) {
|
|
51
120
|
return true;
|
|
52
121
|
}
|
|
@@ -54,27 +123,39 @@ class Address {
|
|
|
54
123
|
return false;
|
|
55
124
|
}
|
|
56
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Convert a given Bitcoin address into its corresponding script public key.
|
|
128
|
+
* Reference: https://github.com/buidl-bitcoin/buidl-python/blob/d79e9808e8ca60975d315be41293cb40d968626d/buidl/script.py#L607
|
|
129
|
+
* @param address Bitcoin address
|
|
130
|
+
* @returns Script public key of the given Bitcoin address
|
|
131
|
+
* @throws Error when the provided address is not a valid Bitcoin address
|
|
132
|
+
*/
|
|
57
133
|
static convertAdressToScriptPubkey(address) {
|
|
58
134
|
if (address[0] === '1' || address[0] === 'm' || address[0] === 'n') {
|
|
135
|
+
// P2PKH address
|
|
59
136
|
return bitcoin.payments.p2pkh({
|
|
60
137
|
address: address,
|
|
61
138
|
network: (address[0] === '1') ? bitcoin.networks.bitcoin : bitcoin.networks.testnet
|
|
62
139
|
}).output;
|
|
63
140
|
}
|
|
64
141
|
else if (address[0] === '3' || address[0] === '2') {
|
|
142
|
+
// P2SH address
|
|
65
143
|
return bitcoin.payments.p2sh({
|
|
66
144
|
address: address,
|
|
67
145
|
network: (address[0] === '3') ? bitcoin.networks.bitcoin : bitcoin.networks.testnet
|
|
68
146
|
}).output;
|
|
69
147
|
}
|
|
70
148
|
else if (address.slice(0, 4) === 'bc1q' || address.slice(0, 4) === 'tb1q') {
|
|
149
|
+
// P2WPKH or P2WSH address
|
|
71
150
|
if (address.length === 42) {
|
|
151
|
+
// P2WPKH address
|
|
72
152
|
return bitcoin.payments.p2wpkh({
|
|
73
153
|
address: address,
|
|
74
154
|
network: (address.slice(0, 4) === 'bc1q') ? bitcoin.networks.bitcoin : bitcoin.networks.testnet
|
|
75
155
|
}).output;
|
|
76
156
|
}
|
|
77
157
|
else if (address.length === 62) {
|
|
158
|
+
// P2WSH address
|
|
78
159
|
return bitcoin.payments.p2wsh({
|
|
79
160
|
address: address,
|
|
80
161
|
network: (address.slice(0, 4) === 'bc1q') ? bitcoin.networks.bitcoin : bitcoin.networks.testnet
|
|
@@ -83,6 +164,7 @@ class Address {
|
|
|
83
164
|
}
|
|
84
165
|
else if (address.slice(0, 4) === 'bc1p' || address.slice(0, 4) === 'tb1p') {
|
|
85
166
|
if (address.length === 62) {
|
|
167
|
+
// P2TR address
|
|
86
168
|
return bitcoin.payments.p2tr({
|
|
87
169
|
address: address,
|
|
88
170
|
network: (address.slice(0, 4) === 'bc1p') ? bitcoin.networks.bitcoin : bitcoin.networks.testnet
|
|
@@ -91,6 +173,12 @@ class Address {
|
|
|
91
173
|
}
|
|
92
174
|
throw new Error("Unknown address type");
|
|
93
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Convert a given public key into a corresponding Bitcoin address.
|
|
178
|
+
* @param publicKey Public key for deriving the address, or internal public key for deriving taproot address
|
|
179
|
+
* @param addressType Bitcoin address type to be derived, must be either 'p2pkh', 'p2sh-p2wpkh', 'p2wpkh', or 'p2tr'
|
|
180
|
+
* @returns Bitcoin address that correspond to the given public key in both mainnet and testnet
|
|
181
|
+
*/
|
|
94
182
|
static convertPubKeyIntoAddress(publicKey, addressType) {
|
|
95
183
|
switch (addressType) {
|
|
96
184
|
case 'p2pkh':
|
|
@@ -99,6 +187,7 @@ class Address {
|
|
|
99
187
|
testnet: bitcoin.payments.p2pkh({ pubkey: publicKey, network: bitcoin.networks.testnet }).address
|
|
100
188
|
};
|
|
101
189
|
case 'p2sh-p2wpkh':
|
|
190
|
+
// Reference: https://github.com/bitcoinjs/bitcoinjs-lib/blob/1a9119b53bcea4b83a6aa8b948f0e6370209b1b4/test/integration/addresses.spec.ts#L70
|
|
102
191
|
return {
|
|
103
192
|
mainnet: bitcoin.payments.p2sh({
|
|
104
193
|
redeem: bitcoin.payments.p2wpkh({ pubkey: publicKey, network: bitcoin.networks.bitcoin }),
|
|
@@ -115,6 +204,7 @@ class Address {
|
|
|
115
204
|
testnet: bitcoin.payments.p2wpkh({ pubkey: publicKey, network: bitcoin.networks.testnet }).address
|
|
116
205
|
};
|
|
117
206
|
case 'p2tr':
|
|
207
|
+
// Convert full-length public key into internal public key if necessary
|
|
118
208
|
const internalPubkey = publicKey.byteLength === 33 ? publicKey.subarray(1, 33) : publicKey;
|
|
119
209
|
return {
|
|
120
210
|
mainnet: bitcoin.payments.p2tr({ internalPubkey: internalPubkey, network: bitcoin.networks.bitcoin }).address,
|
|
@@ -125,5 +215,5 @@ class Address {
|
|
|
125
215
|
}
|
|
126
216
|
}
|
|
127
217
|
}
|
|
128
|
-
|
|
218
|
+
exports.default = Address;
|
|
129
219
|
//# sourceMappingURL=Address.js.map
|
package/dist/helpers/BIP137.d.ts
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/**
|
|
3
|
+
* Class that implement BIP137-related utility functions.
|
|
4
|
+
*/
|
|
1
5
|
declare class BIP137 {
|
|
6
|
+
/**
|
|
7
|
+
* Check if a given signature satisified the format of a BIP-137 signature.
|
|
8
|
+
* @param signature Base64-encoded signature to be checked
|
|
9
|
+
* @returns True if the provided signature correspond to a valid BIP-137 signature, false if otherwise
|
|
10
|
+
*/
|
|
2
11
|
static isBIP137Signature(signature: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Derive the public key that signed a valid BIP-137 signature.
|
|
14
|
+
* @param message Message signed by the signature
|
|
15
|
+
* @param signature Base-64 encoded signature to be decoded
|
|
16
|
+
* @returns Public key that signs the provided signature
|
|
17
|
+
*/
|
|
3
18
|
static derivePubKey(message: string, signature: string): Buffer;
|
|
19
|
+
/**
|
|
20
|
+
* Decode a BIP-137 signature.
|
|
21
|
+
* Function copied from bitcoinjs-message library.
|
|
22
|
+
* @param signature BIP-137 signature to be decoded
|
|
23
|
+
* @returns Decoded BIP-137 signature
|
|
24
|
+
*/
|
|
4
25
|
private static decodeSignature;
|
|
5
26
|
}
|
|
6
27
|
export default BIP137;
|