diff --git a/.github/workflows/main_ci.yml b/.github/workflows/main_ci.yml index 9c5879b9a..cbe825744 100644 --- a/.github/workflows/main_ci.yml +++ b/.github/workflows/main_ci.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ @@ -48,7 +48,7 @@ jobs: steps: - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ @@ -65,7 +65,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2 - - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3 + - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 with: node-version: 'lts/*' registry-url: https://registry.npmjs.org/ @@ -76,7 +76,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2 - - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3 + - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 with: node-version: 'lts/*' registry-url: https://registry.npmjs.org/ @@ -87,7 +87,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2 - - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3 + - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 with: node-version: 'lts/*' registry-url: https://registry.npmjs.org/ @@ -98,7 +98,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2 - - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3 + - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 with: node-version: 'lts/*' registry-url: https://registry.npmjs.org/ @@ -109,7 +109,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2 - - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3 + - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 with: node-version: 'lts/*' registry-url: https://registry.npmjs.org/ @@ -121,7 +121,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2 - - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd # v3 + - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6 with: node-version: 'lts/*' registry-url: https://registry.npmjs.org/ diff --git a/src/cjs/psbt.cjs b/src/cjs/psbt.cjs index c6ccbf07d..6e3a5a82d 100644 --- a/src/cjs/psbt.cjs +++ b/src/cjs/psbt.cjs @@ -516,14 +516,14 @@ class Psbt { throw new Error('Need validator function to validate signatures'); pubkey = pubkey && (0, bip371_js_1.toXOnly)(pubkey); const allHashses = pubkey - ? getTaprootHashesForSig( + ? getTaprootHashesForSigValidation( inputIndex, input, this.data.inputs, pubkey, this.__CACHE, ) - : getAllTaprootHashesForSig( + : getAllTaprootHashesForSigValidation( inputIndex, input, this.data.inputs, @@ -900,7 +900,7 @@ class Psbt { throw new Error( `Need Schnorr Signer to sign taproot input #${inputIndex}.`, ); - const hashesForSig = getTaprootHashesForSig( + const hashesForSig = getTaprootHashesForSigning( inputIndex, input, this.data.inputs, @@ -1348,7 +1348,7 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) { hash, }; } -function getAllTaprootHashesForSig(inputIndex, input, inputs, cache) { +function getAllTaprootHashesForSigValidation(inputIndex, input, inputs, cache) { const allPublicKeys = []; if (input.tapInternalKey) { const key = getPrevoutTaprootKey(inputIndex, input, cache); @@ -1361,7 +1361,13 @@ function getAllTaprootHashesForSig(inputIndex, input, inputs, cache) { allPublicKeys.push(...tapScriptPubkeys); } const allHashes = allPublicKeys.map(publicKey => - getTaprootHashesForSig(inputIndex, input, inputs, publicKey, cache), + getTaprootHashesForSigValidation( + inputIndex, + input, + inputs, + publicKey, + cache, + ), ); return allHashes.flat(); } @@ -1372,7 +1378,7 @@ function getPrevoutTaprootKey(inputIndex, input, cache) { function trimTaprootSig(signature) { return signature.length === 64 ? signature : signature.subarray(0, 64); } -function getTaprootHashesForSig( +function getTaprootHashesForSigning( inputIndex, input, inputs, @@ -1381,17 +1387,64 @@ function getTaprootHashesForSig( tapLeafHashToSign, allowedSighashTypes, ) { - const unsignedTx = cache.__TX; const sighashType = input.sighashType || transaction_js_1.Transaction.SIGHASH_DEFAULT; checkSighashTypeAllowed(sighashType, allowedSighashTypes); + const keySpend = Boolean(input.tapInternalKey && !tapLeafHashToSign); + return getTaprootHashesForSig( + inputIndex, + input, + inputs, + pubkey, + cache, + keySpend, + sighashType, + tapLeafHashToSign, + ); +} +function getTaprootHashesForSigValidation( + inputIndex, + input, + inputs, + pubkey, + cache, +) { + const sighashType = + input.sighashType || transaction_js_1.Transaction.SIGHASH_DEFAULT; + const keySpend = Boolean(input.tapKeySig); + return getTaprootHashesForSig( + inputIndex, + input, + inputs, + pubkey, + cache, + keySpend, + sighashType, + ); +} +/* + * This helper method is used for both generating a hash for signing as well for validating; + * thus depending on context key spend can be detected either with tapInternalKey with no leaves + * or tapKeySig (note tapKeySig is a signature to the prevout pk, so tapInternalKey is not needed). + */ +function getTaprootHashesForSig( + inputIndex, + input, + inputs, + pubkey, + cache, + keySpend, + sighashType, + tapLeafHashToSign, +) { + const unsignedTx = cache.__TX; const prevOuts = inputs.map((i, index) => getScriptAndAmountFromUtxo(index, i, cache), ); const signingScripts = prevOuts.map(o => o.script); const values = prevOuts.map(o => o.value); const hashes = []; - if (input.tapInternalKey && !tapLeafHashToSign) { + if (keySpend) { const outputKey = getPrevoutTaprootKey(inputIndex, input, cache) || Uint8Array.from([]); if (tools.compare((0, bip371_js_1.toXOnly)(pubkey), outputKey) === 0) { diff --git a/src/esm/psbt.js b/src/esm/psbt.js index 5e2e39b93..15c5e016d 100644 --- a/src/esm/psbt.js +++ b/src/esm/psbt.js @@ -475,14 +475,14 @@ export class Psbt { throw new Error('Need validator function to validate signatures'); pubkey = pubkey && toXOnly(pubkey); const allHashses = pubkey - ? getTaprootHashesForSig( + ? getTaprootHashesForSigValidation( inputIndex, input, this.data.inputs, pubkey, this.__CACHE, ) - : getAllTaprootHashesForSig( + : getAllTaprootHashesForSigValidation( inputIndex, input, this.data.inputs, @@ -840,7 +840,7 @@ export class Psbt { throw new Error( `Need Schnorr Signer to sign taproot input #${inputIndex}.`, ); - const hashesForSig = getTaprootHashesForSig( + const hashesForSig = getTaprootHashesForSigning( inputIndex, input, this.data.inputs, @@ -1274,7 +1274,7 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) { hash, }; } -function getAllTaprootHashesForSig(inputIndex, input, inputs, cache) { +function getAllTaprootHashesForSigValidation(inputIndex, input, inputs, cache) { const allPublicKeys = []; if (input.tapInternalKey) { const key = getPrevoutTaprootKey(inputIndex, input, cache); @@ -1287,7 +1287,13 @@ function getAllTaprootHashesForSig(inputIndex, input, inputs, cache) { allPublicKeys.push(...tapScriptPubkeys); } const allHashes = allPublicKeys.map(publicKey => - getTaprootHashesForSig(inputIndex, input, inputs, publicKey, cache), + getTaprootHashesForSigValidation( + inputIndex, + input, + inputs, + publicKey, + cache, + ), ); return allHashes.flat(); } @@ -1298,7 +1304,7 @@ function getPrevoutTaprootKey(inputIndex, input, cache) { function trimTaprootSig(signature) { return signature.length === 64 ? signature : signature.subarray(0, 64); } -function getTaprootHashesForSig( +function getTaprootHashesForSigning( inputIndex, input, inputs, @@ -1307,16 +1313,62 @@ function getTaprootHashesForSig( tapLeafHashToSign, allowedSighashTypes, ) { - const unsignedTx = cache.__TX; const sighashType = input.sighashType || Transaction.SIGHASH_DEFAULT; checkSighashTypeAllowed(sighashType, allowedSighashTypes); + const keySpend = Boolean(input.tapInternalKey && !tapLeafHashToSign); + return getTaprootHashesForSig( + inputIndex, + input, + inputs, + pubkey, + cache, + keySpend, + sighashType, + tapLeafHashToSign, + ); +} +function getTaprootHashesForSigValidation( + inputIndex, + input, + inputs, + pubkey, + cache, +) { + const sighashType = input.sighashType || Transaction.SIGHASH_DEFAULT; + const keySpend = Boolean(input.tapKeySig); + return getTaprootHashesForSig( + inputIndex, + input, + inputs, + pubkey, + cache, + keySpend, + sighashType, + ); +} +/* + * This helper method is used for both generating a hash for signing as well for validating; + * thus depending on context key spend can be detected either with tapInternalKey with no leaves + * or tapKeySig (note tapKeySig is a signature to the prevout pk, so tapInternalKey is not needed). + */ +function getTaprootHashesForSig( + inputIndex, + input, + inputs, + pubkey, + cache, + keySpend, + sighashType, + tapLeafHashToSign, +) { + const unsignedTx = cache.__TX; const prevOuts = inputs.map((i, index) => getScriptAndAmountFromUtxo(index, i, cache), ); const signingScripts = prevOuts.map(o => o.script); const values = prevOuts.map(o => o.value); const hashes = []; - if (input.tapInternalKey && !tapLeafHashToSign) { + if (keySpend) { const outputKey = getPrevoutTaprootKey(inputIndex, input, cache) || Uint8Array.from([]); if (tools.compare(toXOnly(pubkey), outputKey) === 0) { diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 357a059cd..f072f1d48 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -604,14 +604,14 @@ export class Psbt { pubkey = pubkey && toXOnly(pubkey); const allHashses = pubkey - ? getTaprootHashesForSig( + ? getTaprootHashesForSigValidation( inputIndex, input, this.data.inputs, pubkey, this.__CACHE, ) - : getAllTaprootHashesForSig( + : getAllTaprootHashesForSigValidation( inputIndex, input, this.data.inputs, @@ -1056,7 +1056,7 @@ export class Psbt { `Need Schnorr Signer to sign taproot input #${inputIndex}.`, ); - const hashesForSig = getTaprootHashesForSig( + const hashesForSig = getTaprootHashesForSigning( inputIndex, input, this.data.inputs, @@ -1720,7 +1720,7 @@ function getHashForSig( }; } -function getAllTaprootHashesForSig( +function getAllTaprootHashesForSigValidation( inputIndex: number, input: PsbtInput, inputs: PsbtInput[], @@ -1742,7 +1742,13 @@ function getAllTaprootHashesForSig( } const allHashes = allPublicKeys.map(publicKey => - getTaprootHashesForSig(inputIndex, input, inputs, publicKey, cache), + getTaprootHashesForSigValidation( + inputIndex, + input, + inputs, + publicKey, + cache, + ), ); return allHashes.flat(); @@ -1761,7 +1767,7 @@ function trimTaprootSig(signature: Uint8Array): Uint8Array { return signature.length === 64 ? signature : signature.subarray(0, 64); } -function getTaprootHashesForSig( +function getTaprootHashesForSigning( inputIndex: number, input: PsbtInput, inputs: PsbtInput[], @@ -1770,11 +1776,60 @@ function getTaprootHashesForSig( tapLeafHashToSign?: Uint8Array, allowedSighashTypes?: number[], ): { pubkey: Uint8Array; hash: Uint8Array; leafHash?: Uint8Array }[] { - const unsignedTx = cache.__TX; - const sighashType = input.sighashType || Transaction.SIGHASH_DEFAULT; checkSighashTypeAllowed(sighashType, allowedSighashTypes); + const keySpend = Boolean(input.tapInternalKey && !tapLeafHashToSign); + + return getTaprootHashesForSig( + inputIndex, + input, + inputs, + pubkey, + cache, + keySpend, + sighashType, + tapLeafHashToSign, + ); +} + +function getTaprootHashesForSigValidation( + inputIndex: number, + input: PsbtInput, + inputs: PsbtInput[], + pubkey: Uint8Array, + cache: PsbtCache, +): { pubkey: Uint8Array; hash: Uint8Array; leafHash?: Uint8Array }[] { + const sighashType = input.sighashType || Transaction.SIGHASH_DEFAULT; + const keySpend = Boolean(input.tapKeySig); + return getTaprootHashesForSig( + inputIndex, + input, + inputs, + pubkey, + cache, + keySpend, + sighashType, + ); +} + +/* + * This helper method is used for both generating a hash for signing as well for validating; + * thus depending on context key spend can be detected either with tapInternalKey with no leaves + * or tapKeySig (note tapKeySig is a signature to the prevout pk, so tapInternalKey is not needed). + */ +function getTaprootHashesForSig( + inputIndex: number, + input: PsbtInput, + inputs: PsbtInput[], + pubkey: Uint8Array, + cache: PsbtCache, + keySpend: boolean, + sighashType: number, + tapLeafHashToSign?: Uint8Array, +): { pubkey: Uint8Array; hash: Uint8Array; leafHash?: Uint8Array }[] { + const unsignedTx = cache.__TX; + const prevOuts: Output[] = inputs.map((i, index) => getScriptAndAmountFromUtxo(index, i, cache), ); @@ -1782,7 +1837,7 @@ function getTaprootHashesForSig( const values = prevOuts.map(o => o.value); const hashes = []; - if (input.tapInternalKey && !tapLeafHashToSign) { + if (keySpend) { const outputKey = getPrevoutTaprootKey(inputIndex, input, cache) || Uint8Array.from([]); if (tools.compare(toXOnly(pubkey), outputKey) === 0) {