@@ -8,7 +8,6 @@ const types_1 = require('../types');
8
8
const taprootutils_1 = require('./taprootutils');
9
9
const lazy = require('./lazy');
10
10
const bech32_1 = require('bech32');
11
- const verifyecc_1 = require('./verifyecc');
12
11
const OPS = bscript.OPS;
13
12
const TAPROOT_WITNESS_VERSION = 0x01;
14
13
const ANNEX_PREFIX = 0x50;
@@ -22,10 +21,10 @@ function p2tr(a, opts) {
22
21
)
23
22
throw new TypeError('Not enough data');
24
23
opts = Object.assign({ validate: true }, opts || {});
25
- const _ecc = lazy.value(() => {
26
- if (!opts.eccLib ) throw new Error('ECC Library is missing for p2tr.');
27
- (0, verifyecc_1.verifyEcc)( opts.eccLib );
28
- return opts.eccLib ;
24
+ const _tweakFn = lazy.value(() => {
25
+ if (!opts.tweakFn ) throw new Error('Tweak function is missing for p2tr.');
26
+ verifyTweakFn( opts.tweakFn );
27
+ return opts.tweakFn ;
29
28
});
30
29
(0, types_1.typeforce)(
31
30
{
@@ -132,7 +131,7 @@ function p2tr(a, opts) {
132
131
if (a.output) return a.output.slice(2);
133
132
if (a.address) return _address().data;
134
133
if (o.internalPubkey) {
135
- const tweakedKey = tweakKey(o.internalPubkey, o.hash, _ecc ());
134
+ const tweakedKey = tweakKey(o.internalPubkey, o.hash, _tweakFn ());
136
135
if (tweakedKey) return tweakedKey.x;
137
136
}
138
137
});
@@ -157,7 +156,7 @@ function p2tr(a, opts) {
157
156
});
158
157
const path = (0, taprootutils_1.findScriptPath)(hashTree, leafHash);
159
158
if (!path) return;
160
- const outputKey = tweakKey(a.internalPubkey, hashTree.hash, _ecc ());
159
+ const outputKey = tweakKey(a.internalPubkey, hashTree.hash, _tweakFn ());
161
160
if (!outputKey) return;
162
161
const controlBock = buffer_1.Buffer.concat(
163
162
[
@@ -198,13 +197,13 @@ function p2tr(a, opts) {
198
197
else pubkey = a.output.slice(2);
199
198
}
200
199
if (a.internalPubkey) {
201
- const tweakedKey = tweakKey(a.internalPubkey, o.hash, _ecc ());
200
+ const tweakedKey = tweakKey(a.internalPubkey, o.hash, _tweakFn ());
202
201
if (pubkey.length > 0 && !pubkey.equals(tweakedKey.x))
203
202
throw new TypeError('Pubkey mismatch');
204
203
else pubkey = tweakedKey.x;
205
204
}
206
205
if (pubkey && pubkey.length) {
207
- if (!_ecc() .isXOnlyPoint(pubkey))
206
+ if (!(0, types_1 .isXOnlyPoint) (pubkey))
208
207
throw new TypeError('Invalid pubkey for p2tr');
209
208
}
210
209
const hashTree = _hashTree();
@@ -267,7 +266,7 @@ function p2tr(a, opts) {
267
266
const internalPubkey = controlBlock.slice(1, 33);
268
267
if (a.internalPubkey && !a.internalPubkey.equals(internalPubkey))
269
268
throw new TypeError('Internal pubkey mismatch');
270
- if (!_ecc() .isXOnlyPoint(internalPubkey))
269
+ if (!(0, types_1 .isXOnlyPoint) (internalPubkey))
271
270
throw new TypeError('Invalid internalPubkey for p2tr witness');
272
271
const leafVersion = controlBlock[0] & types_1.TAPLEAF_VERSION_MASK;
273
272
const script = witness[witness.length - 2];
@@ -279,7 +278,7 @@ function p2tr(a, opts) {
279
278
controlBlock,
280
279
leafHash,
281
280
);
282
- const outputKey = tweakKey(internalPubkey, hash, _ecc ());
281
+ const outputKey = tweakKey(internalPubkey, hash, _tweakFn ());
283
282
if (!outputKey)
284
283
// todo: needs test data
285
284
throw new TypeError('Invalid outputKey for p2tr witness');
@@ -293,12 +292,12 @@ function p2tr(a, opts) {
293
292
return Object.assign(o, a);
294
293
}
295
294
exports.p2tr = p2tr;
296
- function tweakKey(pubKey, h, eccLib ) {
295
+ function tweakKey(pubKey, h, tweakFn ) {
297
296
if (!buffer_1.Buffer.isBuffer(pubKey)) return null;
298
297
if (pubKey.length !== 32) return null;
299
298
if (h && h.length !== 32) return null;
300
299
const tweakHash = (0, taprootutils_1.tapTweakHash)(pubKey, h);
301
- const res = eccLib.xOnlyPointAddTweak (pubKey, tweakHash);
300
+ const res = tweakFn (pubKey, tweakHash);
302
301
if (!res || res.xOnlyPubkey === null) return null;
303
302
return {
304
303
parity: res.parity,
@@ -311,3 +310,43 @@ function stacksEqual(a, b) {
311
310
return x.equals(b[i]);
312
311
});
313
312
}
313
+ function verifyTweakFn(tweakFn) {
314
+ [
315
+ {
316
+ pubkey:
317
+ '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798',
318
+ tweak: 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140',
319
+ parity: -1,
320
+ result: null,
321
+ },
322
+ {
323
+ pubkey:
324
+ '1617d38ed8d8657da4d4761e8057bc396ea9e4b9d29776d4be096016dbd2509b',
325
+ tweak: 'a8397a935f0dfceba6ba9618f6451ef4d80637abf4e6af2669fbc9de6a8fd2ac',
326
+ parity: 1,
327
+ result:
328
+ 'e478f99dab91052ab39a33ea35fd5e6e4933f4d28023cd597c9a1f6760346adf',
329
+ },
330
+ {
331
+ pubkey:
332
+ '2c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991',
333
+ tweak: '823c3cd2142744b075a87eade7e1b8678ba308d566226a0056ca2b7a76f86b47',
334
+ parity: 0,
335
+ result:
336
+ '9534f8dc8c6deda2dc007655981c78b49c5d96c778fbf363462a11ec9dfd948c',
337
+ },
338
+ ].forEach(t => {
339
+ const r = tweakFn(
340
+ Buffer.from(t.pubkey, 'hex'),
341
+ Buffer.from(t.tweak, 'hex'),
342
+ );
343
+ if (t.result === null) {
344
+ if (r !== null) throw new Error('Expected failed tweak');
345
+ } else {
346
+ if (r === null) throw new Error('Expected successful tweak');
347
+ if (r.parity !== t.parity) throw new Error('Tweaked key parity mismatch');
348
+ if (!Buffer.from(r.xOnlyPubkey).equals(Buffer.from(t.result, 'hex')))
349
+ throw new Error('Tweaked key mismatch');
350
+ }
351
+ });
352
+ }
0 commit comments