Skip to content

Commit 1a9119b

Browse files
authored
Merge pull request #1935 from bitcoinjs/fix/p2tr-sigs-pt2
Fix taproot sig validation again.
2 parents da0b34d + 8d2d7db commit 1a9119b

File tree

7 files changed

+67
-15
lines changed

7 files changed

+67
-15
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 6.1.3
2+
__fixed__
3+
- validateSignaturesOfInput for taproot inputs returned false for valid signatures in specific cases. (#1934)
4+
15
# 6.1.2
26
__fixed__
37
- validateSignaturesOfInput for taproot inputs returned true for invalid signatures in specific cases. (#1932)

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bitcoinjs-lib",
3-
"version": "6.1.2",
3+
"version": "6.1.3",
44
"description": "Client-side Bitcoin JavaScript library",
55
"main": "./src/index.js",
66
"types": "./src/index.d.ts",
@@ -33,7 +33,7 @@
3333
"mocha:ts": "mocha --recursive --require test/ts-node-register",
3434
"nobuild:coverage-report": "nyc report --reporter=lcov",
3535
"nobuild:coverage-html": "nyc report --reporter=html",
36-
"nobuild:coverage": "npm run build:tests && nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha && npm run clean:jstests",
36+
"nobuild:coverage": "npm run build:tests && nyc --check-coverage --branches 85 --functions 90 --lines 90 mocha && npm run clean:jstests",
3737
"nobuild:integration": "npm run mocha:ts -- --timeout 50000 'test/integration/*.ts'",
3838
"nobuild:unit": "npm run mocha:ts -- 'test/*.ts'",
3939
"prettier": "prettier \"ts_src/**/*.ts\" \"test/**/*.ts\" --ignore-path ./.prettierignore",

src/psbt.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,8 +1283,10 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
12831283
function getAllTaprootHashesForSig(inputIndex, input, inputs, cache) {
12841284
const allPublicKeys = [];
12851285
if (input.tapInternalKey) {
1286-
const outputKey = (0, bip371_1.tweakInternalPubKey)(inputIndex, input);
1287-
allPublicKeys.push(outputKey);
1286+
const key = getPrevoutTaprootKey(inputIndex, input, cache);
1287+
if (key) {
1288+
allPublicKeys.push(key);
1289+
}
12881290
}
12891291
if (input.tapScriptSig) {
12901292
const tapScriptPubkeys = input.tapScriptSig.map(tss => tss.pubkey);
@@ -1295,8 +1297,12 @@ function getAllTaprootHashesForSig(inputIndex, input, inputs, cache) {
12951297
);
12961298
return allHashes.flat();
12971299
}
1300+
function getPrevoutTaprootKey(inputIndex, input, cache) {
1301+
const { script } = getScriptAndAmountFromUtxo(inputIndex, input, cache);
1302+
return (0, psbtutils_1.isP2TR)(script) ? script.subarray(2, 34) : null;
1303+
}
12981304
function trimTaprootSig(signature) {
1299-
return signature.length === 64 ? signature : signature.subarray(1);
1305+
return signature.length === 64 ? signature : signature.subarray(0, 64);
13001306
}
13011307
function getTaprootHashesForSig(
13021308
inputIndex,
@@ -1318,7 +1324,8 @@ function getTaprootHashesForSig(
13181324
const values = prevOuts.map(o => o.value);
13191325
const hashes = [];
13201326
if (input.tapInternalKey && !tapLeafHashToSign) {
1321-
const outputKey = (0, bip371_1.tweakInternalPubKey)(inputIndex, input);
1327+
const outputKey =
1328+
getPrevoutTaprootKey(inputIndex, input, cache) || Buffer.from([]);
13221329
if ((0, bip371_1.toXOnly)(pubkey).equals(outputKey)) {
13231330
const tapKeyHash = unsignedTx.hashForWitnessV1(
13241331
inputIndex,

test/fixtures/psbt.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@
310310
},
311311
{
312312
"description": "Sign PSBT with 3 inputs [P2PKH, P2TR (key-path), P2WPKH] and two outputs [P2TR, P2WPKH]",
313-
"psbt": "cHNidP8BAM8CAAAAAwPzd9k+uLSN1rgF01xY1TIA/8N+YytNZ4VP9gKFP4MyAAAAAAD/////ZtAAqL2E1fKcmGo+7xuqS+nSQeKFVKGRYaHfIvLXn4sAAAAAAP////9+h+SlCwIx1MUDT7Bek0NrWXS7xnSPi5LbYbDc9sxYIgAAAAAA/////wIgKRsAAAAAACJRIEb2SXyy8Z1Qw+npgqlQ3MhiFLAfzOQ3pCBhx72xIw0zuAUBAAAAAAAWABTJijE0v48z5ZmmfEAADXdCBcG0FAAAAAAAAQDiAgAAAAABAUfY2D1t0dyMeEH39C1yOdIxigpqm7XJNqHVT3Lc+FkiAAAAAAD+////AhIsGwAAAAAAGXapFJ5+8XZ3ZP80oFldvEwrcNsBftBmiKyYdK6xAAAAABepFLDBn59UffGbX7u/olyFDG0eG1UJhwJHMEQCIDAd3s05C61flXVFqOtov0NoHRGr8KFcOpH6R/81F46EAiBt+j9hHyvT2hYEyf8fdYsM9IgbnybtPV+kRTHDa6Rj0AEhAmmZfwmoHsmCkEOn9AfRTh+863mURelmE8hSqL4MG1EydJwgAAABASu4BQEAAAAAACJRIJQh5zSw+dLEZ+p90ZfGGstEZ83LyfTLDFcfi2OlxAyuARcglCHnNLD50sRn6n3Rl8Yay0RnzcvJ9MsMVx+LY6XEDK4AAQEfECcAAAAAAAAWABRPoqyhKLb53uUwnE9wBR5Jxl/XMgAAAA==",
313+
"psbt": "cHNidP8BAM8CAAAAAwPzd9k+uLSN1rgF01xY1TIA/8N+YytNZ4VP9gKFP4MyAAAAAAD/////ZtAAqL2E1fKcmGo+7xuqS+nSQeKFVKGRYaHfIvLXn4sAAAAAAP////9+h+SlCwIx1MUDT7Bek0NrWXS7xnSPi5LbYbDc9sxYIgAAAAAA/////wIgKRsAAAAAACJRIEb2SXyy8Z1Qw+npgqlQ3MhiFLAfzOQ3pCBhx72xIw0zuAUBAAAAAAAWABTJijE0v48z5ZmmfEAADXdCBcG0FAAAAAAAAQDiAgAAAAABAUfY2D1t0dyMeEH39C1yOdIxigpqm7XJNqHVT3Lc+FkiAAAAAAD+////AhIsGwAAAAAAGXapFJ5+8XZ3ZP80oFldvEwrcNsBftBmiKyYdK6xAAAAABepFLDBn59UffGbX7u/olyFDG0eG1UJhwJHMEQCIDAd3s05C61flXVFqOtov0NoHRGr8KFcOpH6R/81F46EAiBt+j9hHyvT2hYEyf8fdYsM9IgbnybtPV+kRTHDa6Rj0AEhAmmZfwmoHsmCkEOn9AfRTh+863mURelmE8hSqL4MG1EydJwgAAABASu4BQEAAAAAACJRIE/vXFFjvqaak+dKWWcrvrCBg3B3y5TPpuSBpc8A2KsYARcglCHnNLD50sRn6n3Rl8Yay0RnzcvJ9MsMVx+LY6XEDK4AAQEfECcAAAAAAAAWABRPoqyhKLb53uUwnE9wBR5Jxl/XMgAAAA==",
314314
"isTaproot": true,
315315
"keys": [
316316
{
@@ -326,7 +326,7 @@
326326
"WIF": "cPPRdCmAMZMjPdHfRmTCmzYVruZHJ8GbM1FqN2W6DnmEPWDg29aL"
327327
}
328328
],
329-
"result": "cHNidP8BAM8CAAAAAwPzd9k+uLSN1rgF01xY1TIA/8N+YytNZ4VP9gKFP4MyAAAAAAD/////ZtAAqL2E1fKcmGo+7xuqS+nSQeKFVKGRYaHfIvLXn4sAAAAAAP////9+h+SlCwIx1MUDT7Bek0NrWXS7xnSPi5LbYbDc9sxYIgAAAAAA/////wIgKRsAAAAAACJRIEb2SXyy8Z1Qw+npgqlQ3MhiFLAfzOQ3pCBhx72xIw0zuAUBAAAAAAAWABTJijE0v48z5ZmmfEAADXdCBcG0FAAAAAAAAQDiAgAAAAABAUfY2D1t0dyMeEH39C1yOdIxigpqm7XJNqHVT3Lc+FkiAAAAAAD+////AhIsGwAAAAAAGXapFJ5+8XZ3ZP80oFldvEwrcNsBftBmiKyYdK6xAAAAABepFLDBn59UffGbX7u/olyFDG0eG1UJhwJHMEQCIDAd3s05C61flXVFqOtov0NoHRGr8KFcOpH6R/81F46EAiBt+j9hHyvT2hYEyf8fdYsM9IgbnybtPV+kRTHDa6Rj0AEhAmmZfwmoHsmCkEOn9AfRTh+863mURelmE8hSqL4MG1EydJwgACICAi5ovBH1xLoGxPqtFh48wUEpnM+St1SbPzRwO7kBNKOQRzBEAiBpWClBybtHveXkhAgTiE8QSczMJs8MGuH4LOSNRA6s/AIgWlbB3xJOtJIsszj1qZ/whA5jK9wnTzeZzDlVs/ivq2cBAAEBK7gFAQAAAAAAIlEglCHnNLD50sRn6n3Rl8Yay0RnzcvJ9MsMVx+LY6XEDK4BE0Cd7/ny+QreV7urBWKNroQWCvnZczwkU0kLZiKsJQjtftKHWXMknftjt1d4K6aPYH7cBXzhlrUF+2GovjYLccZeARcglCHnNLD50sRn6n3Rl8Yay0RnzcvJ9MsMVx+LY6XEDK4AAQEfECcAAAAAAAAWABRPoqyhKLb53uUwnE9wBR5Jxl/XMiICA6VOpEBbPJM/xYsqO2euttYFpgec9vcxggyTyoklK660SDBFAiEAoCIktghL55iuMAmkzwYJzb+h+qmNewZXxAx/06ObxIQCIELCsBz/wd2wPlnJb27OluxMkTPnCyHA2C+SxHiX/FvPAQAAAA=="
329+
"result": "cHNidP8BAM8CAAAAAwPzd9k+uLSN1rgF01xY1TIA/8N+YytNZ4VP9gKFP4MyAAAAAAD/////ZtAAqL2E1fKcmGo+7xuqS+nSQeKFVKGRYaHfIvLXn4sAAAAAAP////9+h+SlCwIx1MUDT7Bek0NrWXS7xnSPi5LbYbDc9sxYIgAAAAAA/////wIgKRsAAAAAACJRIEb2SXyy8Z1Qw+npgqlQ3MhiFLAfzOQ3pCBhx72xIw0zuAUBAAAAAAAWABTJijE0v48z5ZmmfEAADXdCBcG0FAAAAAAAAQDiAgAAAAABAUfY2D1t0dyMeEH39C1yOdIxigpqm7XJNqHVT3Lc+FkiAAAAAAD+////AhIsGwAAAAAAGXapFJ5+8XZ3ZP80oFldvEwrcNsBftBmiKyYdK6xAAAAABepFLDBn59UffGbX7u/olyFDG0eG1UJhwJHMEQCIDAd3s05C61flXVFqOtov0NoHRGr8KFcOpH6R/81F46EAiBt+j9hHyvT2hYEyf8fdYsM9IgbnybtPV+kRTHDa6Rj0AEhAmmZfwmoHsmCkEOn9AfRTh+863mURelmE8hSqL4MG1EydJwgACICAi5ovBH1xLoGxPqtFh48wUEpnM+St1SbPzRwO7kBNKOQRzBEAiBpWClBybtHveXkhAgTiE8QSczMJs8MGuH4LOSNRA6s/AIgWlbB3xJOtJIsszj1qZ/whA5jK9wnTzeZzDlVs/ivq2cBAAEBK7gFAQAAAAAAIlEgT+9cUWO+ppqT50pZZyu+sIGDcHfLlM+m5IGlzwDYqxgBE0B0eYK4chVhtLT9WMi14T8ZknZSdTe1pMdIvaq6tIfwqY2xQ9YlcTTy0jWU9utItw/rHQ2c1FplbF9bRvZ6RLQSARcglCHnNLD50sRn6n3Rl8Yay0RnzcvJ9MsMVx+LY6XEDK4AAQEfECcAAAAAAAAWABRPoqyhKLb53uUwnE9wBR5Jxl/XMiICA6VOpEBbPJM/xYsqO2euttYFpgec9vcxggyTyoklK660SDBFAiEAoCIktghL55iuMAmkzwYJzb+h+qmNewZXxAx/06ObxIQCIELCsBz/wd2wPlnJb27OluxMkTPnCyHA2C+SxHiX/FvPAQAAAA=="
330330
},
331331
{
332332
"description": "Sign PSBT with 1 input [P2TR] (script-path, 3-of-3) and one output [P2TR]",
@@ -896,7 +896,7 @@
896896
"nonExistantIndex": 42
897897
},
898898
"validateSignaturesOfTapKeyInput": {
899-
"psbt": "cHNidP8BAM8CAAAAAwPzd9k+uLSN1rgF01xY1TIA/8N+YytNZ4VP9gKFP4MyAAAAAAD/////ZtAAqL2E1fKcmGo+7xuqS+nSQeKFVKGRYaHfIvLXn4sAAAAAAP////9+h+SlCwIx1MUDT7Bek0NrWXS7xnSPi5LbYbDc9sxYIgAAAAAA/////wIgKRsAAAAAACJRIEb2SXyy8Z1Qw+npgqlQ3MhiFLAfzOQ3pCBhx72xIw0zuAUBAAAAAAAWABTJijE0v48z5ZmmfEAADXdCBcG0FAAAAAAAAQDiAgAAAAABAUfY2D1t0dyMeEH39C1yOdIxigpqm7XJNqHVT3Lc+FkiAAAAAAD+////AhIsGwAAAAAAGXapFJ5+8XZ3ZP80oFldvEwrcNsBftBmiKyYdK6xAAAAABepFLDBn59UffGbX7u/olyFDG0eG1UJhwJHMEQCIDAd3s05C61flXVFqOtov0NoHRGr8KFcOpH6R/81F46EAiBt+j9hHyvT2hYEyf8fdYsM9IgbnybtPV+kRTHDa6Rj0AEhAmmZfwmoHsmCkEOn9AfRTh+863mURelmE8hSqL4MG1EydJwgACICAi5ovBH1xLoGxPqtFh48wUEpnM+St1SbPzRwO7kBNKOQRzBEAiBpWClBybtHveXkhAgTiE8QSczMJs8MGuH4LOSNRA6s/AIgWlbB3xJOtJIsszj1qZ/whA5jK9wnTzeZzDlVs/ivq2cBAAEBK7gFAQAAAAAAIlEglCHnNLD50sRn6n3Rl8Yay0RnzcvJ9MsMVx+LY6XEDK4BE0Cd7/ny+QreV7urBWKNroQWCvnZczwkU0kLZiKsJQjtftKHWXMknftjt1d4K6aPYH7cBXzhlrUF+2GovjYLccZeARcglCHnNLD50sRn6n3Rl8Yay0RnzcvJ9MsMVx+LY6XEDK4AAQEfECcAAAAAAAAWABRPoqyhKLb53uUwnE9wBR5Jxl/XMiICA6VOpEBbPJM/xYsqO2euttYFpgec9vcxggyTyoklK660SDBFAiEAoCIktghL55iuMAmkzwYJzb+h+qmNewZXxAx/06ObxIQCIELCsBz/wd2wPlnJb27OluxMkTPnCyHA2C+SxHiX/FvPAQAAAA==",
899+
"psbt": "cHNidP8BAM8CAAAAAwPzd9k+uLSN1rgF01xY1TIA/8N+YytNZ4VP9gKFP4MyAAAAAAD/////ZtAAqL2E1fKcmGo+7xuqS+nSQeKFVKGRYaHfIvLXn4sAAAAAAP////9+h+SlCwIx1MUDT7Bek0NrWXS7xnSPi5LbYbDc9sxYIgAAAAAA/////wIgKRsAAAAAACJRIEb2SXyy8Z1Qw+npgqlQ3MhiFLAfzOQ3pCBhx72xIw0zuAUBAAAAAAAWABTJijE0v48z5ZmmfEAADXdCBcG0FAAAAAAAAQDiAgAAAAABAUfY2D1t0dyMeEH39C1yOdIxigpqm7XJNqHVT3Lc+FkiAAAAAAD+////AhIsGwAAAAAAGXapFJ5+8XZ3ZP80oFldvEwrcNsBftBmiKyYdK6xAAAAABepFLDBn59UffGbX7u/olyFDG0eG1UJhwJHMEQCIDAd3s05C61flXVFqOtov0NoHRGr8KFcOpH6R/81F46EAiBt+j9hHyvT2hYEyf8fdYsM9IgbnybtPV+kRTHDa6Rj0AEhAmmZfwmoHsmCkEOn9AfRTh+863mURelmE8hSqL4MG1EydJwgACICAi5ovBH1xLoGxPqtFh48wUEpnM+St1SbPzRwO7kBNKOQRzBEAiBpWClBybtHveXkhAgTiE8QSczMJs8MGuH4LOSNRA6s/AIgWlbB3xJOtJIsszj1qZ/whA5jK9wnTzeZzDlVs/ivq2cBAAEBK7gFAQAAAAAAIlEgT+9cUWO+ppqT50pZZyu+sIGDcHfLlM+m5IGlzwDYqxgBE0B0eYK4chVhtLT9WMi14T8ZknZSdTe1pMdIvaq6tIfwqY2xQ9YlcTTy0jWU9utItw/rHQ2c1FplbF9bRvZ6RLQSARcglCHnNLD50sRn6n3Rl8Yay0RnzcvJ9MsMVx+LY6XEDK4AAQEfECcAAAAAAAAWABRPoqyhKLb53uUwnE9wBR5Jxl/XMiICA6VOpEBbPJM/xYsqO2euttYFpgec9vcxggyTyoklK660SDBFAiEAoCIktghL55iuMAmkzwYJzb+h+qmNewZXxAx/06ObxIQCIELCsBz/wd2wPlnJb27OluxMkTPnCyHA2C+SxHiX/FvPAQAAAA==",
900900
"index": 1,
901901
"pubkey": "Buffer.from('024fef5c5163bea69a93e74a59672bbeb081837077cb94cfa6e481a5cf00d8ab18', 'hex')",
902902
"incorrectPubkey": "Buffer.from('029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e02a', 'hex')"

test/integration/taproot.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,35 @@ describe('bitcoinjs-lib (transaction with taproot)', () => {
629629
'Should fail validation',
630630
);
631631
});
632+
633+
it('should succeed validating valid signatures for taproot (See issue #1934)', () => {
634+
const schnorrValidator = (
635+
pubkey: Buffer,
636+
msghash: Buffer,
637+
signature: Buffer,
638+
) => {
639+
return ecc.verifySchnorr(msghash, pubkey, signature);
640+
};
641+
642+
const psbtBase64 =
643+
`cHNidP8BAF4CAAAAAU6UzYPa7tES0HoS+obnRJuXX41Ob64Zs59qDEyKsu1ZAAAAAAD/////AYA
644+
zAjsAAAAAIlEgIlIzfR+flIWYTyewD9v+1N84IubZ/7qg6oHlYLzv1aYAAAAAAAEAXgEAAAAB8f+
645+
afEJBun7sRQLFE1Olc/gK9LBaduUpz3vB4fjXVF0AAAAAAP3///8BECcAAAAAAAAiUSAiUjN9H5+
646+
UhZhPJ7AP2/7U3zgi5tn/uqDqgeVgvO/VpgAAAAABASsQJwAAAAAAACJRICJSM30fn5SFmE8nsA/
647+
b/tTfOCLm2f+6oOqB5WC879WmAQMEgwAAAAETQWQwNOao3RMOBWPuAQ9Iph7Qzk47MvroTHbJR49
648+
MxKJmQ6hfhZa5wVVrdKYea5BW/loqa7al2pYYZMlGvdS06wODARcgjuYXxIpyOMVTYEvl35gDidC
649+
m/vUICZyuNNZKaPz9dxAAAQUgjuYXxIpyOMVTYEvl35gDidCm/vUICZyuNNZKaPz9dxAA`.replace(
650+
/\s+/g,
651+
'',
652+
);
653+
654+
const psbt = bitcoin.Psbt.fromBase64(psbtBase64);
655+
656+
assert(
657+
psbt.validateSignaturesOfAllInputs(schnorrValidator),
658+
'Should succeed validation',
659+
);
660+
});
632661
});
633662

634663
function buildLeafIndexFinalizer(

ts_src/psbt.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import {
2929
isTaprootInput,
3030
checkTaprootInputFields,
3131
checkTaprootOutputFields,
32-
tweakInternalPubKey,
3332
checkTaprootInputForSigs,
3433
} from './psbt/bip371';
3534
import {
@@ -42,6 +41,7 @@ import {
4241
isP2WPKH,
4342
isP2WSHScript,
4443
isP2SHScript,
44+
isP2TR,
4545
} from './psbt/psbtutils';
4646

4747
export interface TransactionInput {
@@ -1701,8 +1701,10 @@ function getAllTaprootHashesForSig(
17011701
): { pubkey: Buffer; hash: Buffer; leafHash?: Buffer }[] {
17021702
const allPublicKeys = [];
17031703
if (input.tapInternalKey) {
1704-
const outputKey = tweakInternalPubKey(inputIndex, input);
1705-
allPublicKeys.push(outputKey);
1704+
const key = getPrevoutTaprootKey(inputIndex, input, cache);
1705+
if (key) {
1706+
allPublicKeys.push(key);
1707+
}
17061708
}
17071709

17081710
if (input.tapScriptSig) {
@@ -1717,8 +1719,17 @@ function getAllTaprootHashesForSig(
17171719
return allHashes.flat();
17181720
}
17191721

1722+
function getPrevoutTaprootKey(
1723+
inputIndex: number,
1724+
input: PsbtInput,
1725+
cache: PsbtCache,
1726+
): Buffer | null {
1727+
const { script } = getScriptAndAmountFromUtxo(inputIndex, input, cache);
1728+
return isP2TR(script) ? script.subarray(2, 34) : null;
1729+
}
1730+
17201731
function trimTaprootSig(signature: Buffer): Buffer {
1721-
return signature.length === 64 ? signature : signature.subarray(1);
1732+
return signature.length === 64 ? signature : signature.subarray(0, 64);
17221733
}
17231734

17241735
function getTaprootHashesForSig(
@@ -1743,7 +1754,8 @@ function getTaprootHashesForSig(
17431754

17441755
const hashes = [];
17451756
if (input.tapInternalKey && !tapLeafHashToSign) {
1746-
const outputKey = tweakInternalPubKey(inputIndex, input);
1757+
const outputKey =
1758+
getPrevoutTaprootKey(inputIndex, input, cache) || Buffer.from([]);
17471759
if (toXOnly(pubkey).equals(outputKey)) {
17481760
const tapKeyHash = unsignedTx.hashForWitnessV1(
17491761
inputIndex,

0 commit comments

Comments
 (0)