@@ -8,7 +8,6 @@ const types_1 = require('../types');
88const taprootutils_1 = require('./taprootutils');
99const lazy = require('./lazy');
1010const bech32_1 = require('bech32');
11- const verifyecc_1 = require('./verifyecc');
1211const OPS = bscript.OPS;
1312const TAPROOT_WITNESS_VERSION = 0x01;
1413const ANNEX_PREFIX = 0x50;
@@ -22,10 +21,10 @@ function p2tr(a, opts) {
2221 )
2322 throw new TypeError('Not enough data');
2423 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 ;
2928 });
3029 (0, types_1.typeforce)(
3130 {
@@ -132,7 +131,7 @@ function p2tr(a, opts) {
132131 if (a.output) return a.output.slice(2);
133132 if (a.address) return _address().data;
134133 if (o.internalPubkey) {
135- const tweakedKey = tweakKey(o.internalPubkey, o.hash, _ecc ());
134+ const tweakedKey = tweakKey(o.internalPubkey, o.hash, _tweakFn ());
136135 if (tweakedKey) return tweakedKey.x;
137136 }
138137 });
@@ -157,7 +156,7 @@ function p2tr(a, opts) {
157156 });
158157 const path = (0, taprootutils_1.findScriptPath)(hashTree, leafHash);
159158 if (!path) return;
160- const outputKey = tweakKey(a.internalPubkey, hashTree.hash, _ecc ());
159+ const outputKey = tweakKey(a.internalPubkey, hashTree.hash, _tweakFn ());
161160 if (!outputKey) return;
162161 const controlBock = buffer_1.Buffer.concat(
163162 [
@@ -198,13 +197,13 @@ function p2tr(a, opts) {
198197 else pubkey = a.output.slice(2);
199198 }
200199 if (a.internalPubkey) {
201- const tweakedKey = tweakKey(a.internalPubkey, o.hash, _ecc ());
200+ const tweakedKey = tweakKey(a.internalPubkey, o.hash, _tweakFn ());
202201 if (pubkey.length > 0 && !pubkey.equals(tweakedKey.x))
203202 throw new TypeError('Pubkey mismatch');
204203 else pubkey = tweakedKey.x;
205204 }
206205 if (pubkey && pubkey.length) {
207- if (!_ecc() .isXOnlyPoint(pubkey))
206+ if (!(0, types_1 .isXOnlyPoint) (pubkey))
208207 throw new TypeError('Invalid pubkey for p2tr');
209208 }
210209 const hashTree = _hashTree();
@@ -267,7 +266,7 @@ function p2tr(a, opts) {
267266 const internalPubkey = controlBlock.slice(1, 33);
268267 if (a.internalPubkey && !a.internalPubkey.equals(internalPubkey))
269268 throw new TypeError('Internal pubkey mismatch');
270- if (!_ecc() .isXOnlyPoint(internalPubkey))
269+ if (!(0, types_1 .isXOnlyPoint) (internalPubkey))
271270 throw new TypeError('Invalid internalPubkey for p2tr witness');
272271 const leafVersion = controlBlock[0] & types_1.TAPLEAF_VERSION_MASK;
273272 const script = witness[witness.length - 2];
@@ -279,7 +278,7 @@ function p2tr(a, opts) {
279278 controlBlock,
280279 leafHash,
281280 );
282- const outputKey = tweakKey(internalPubkey, hash, _ecc ());
281+ const outputKey = tweakKey(internalPubkey, hash, _tweakFn ());
283282 if (!outputKey)
284283 // todo: needs test data
285284 throw new TypeError('Invalid outputKey for p2tr witness');
@@ -293,12 +292,12 @@ function p2tr(a, opts) {
293292 return Object.assign(o, a);
294293}
295294exports.p2tr = p2tr;
296- function tweakKey(pubKey, h, eccLib ) {
295+ function tweakKey(pubKey, h, tweakFn ) {
297296 if (!buffer_1.Buffer.isBuffer(pubKey)) return null;
298297 if (pubKey.length !== 32) return null;
299298 if (h && h.length !== 32) return null;
300299 const tweakHash = (0, taprootutils_1.tapTweakHash)(pubKey, h);
301- const res = eccLib.xOnlyPointAddTweak (pubKey, tweakHash);
300+ const res = tweakFn (pubKey, tweakHash);
302301 if (!res || res.xOnlyPubkey === null) return null;
303302 return {
304303 parity: res.parity,
@@ -311,3 +310,43 @@ function stacksEqual(a, b) {
311310 return x.equals(b[i]);
312311 });
313312}
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