Skip to content

Commit f32d706

Browse files
committed
refactor: move out some utils
1 parent 057cdc5 commit f32d706

File tree

8 files changed

+216
-190
lines changed

8 files changed

+216
-190
lines changed

src/psbt.js

Lines changed: 2 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,79 +1037,12 @@ function checkFees(psbt, cache, opts) {
10371037
function checkInputsForPartialSig(inputs, action) {
10381038
inputs.forEach(input => {
10391039
const throws = (0, bip371_1.isTaprootInput)(input)
1040-
? checkTaprootInputForSigs(input, action)
1041-
: checkInputForSig(input, action);
1040+
? (0, bip371_1.checkTaprootInputForSigs)(input, action)
1041+
: (0, psbtutils_1.checkInputForSig)(input, action);
10421042
if (throws)
10431043
throw new Error('Can not modify transaction, signatures exist.');
10441044
});
10451045
}
1046-
function checkInputForSig(input, action) {
1047-
const pSigs = extractPartialSigs(input);
1048-
return pSigs.some(pSig =>
1049-
signatureBlocksAction(pSig, bscript.signature.decode, action),
1050-
);
1051-
}
1052-
function checkTaprootInputForSigs(input, action) {
1053-
const sigs = extractTaprootSigs(input);
1054-
return sigs.some(sig =>
1055-
signatureBlocksAction(sig, decodeSchnorSignature, action),
1056-
);
1057-
}
1058-
function decodeSchnorSignature(signature) {
1059-
return {
1060-
signature: signature.slice(0, 64),
1061-
hashType:
1062-
signature.slice(64)[0] || transaction_1.Transaction.SIGHASH_DEFAULT,
1063-
};
1064-
}
1065-
function extractTaprootSigs(input) {
1066-
const sigs = [];
1067-
if (input.tapKeySig) sigs.push(input.tapKeySig);
1068-
if (input.tapScriptSig)
1069-
sigs.push(...input.tapScriptSig.map(s => s.signature));
1070-
if (!sigs.length) {
1071-
const finalTapKeySig = getTapKeySigFromWithness(input.finalScriptWitness);
1072-
if (finalTapKeySig) sigs.push(finalTapKeySig);
1073-
}
1074-
return sigs;
1075-
}
1076-
function getTapKeySigFromWithness(finalScriptWitness) {
1077-
if (!finalScriptWitness) return;
1078-
const witness = finalScriptWitness.slice(2);
1079-
// todo: add schnor signature validation
1080-
if (witness.length === 64 || witness.length === 65) return witness;
1081-
}
1082-
function extractPartialSigs(input) {
1083-
let pSigs = [];
1084-
if ((input.partialSig || []).length === 0) {
1085-
if (!input.finalScriptSig && !input.finalScriptWitness) return [];
1086-
pSigs = getPsigsFromInputFinalScripts(input);
1087-
} else {
1088-
pSigs = input.partialSig;
1089-
}
1090-
return pSigs.map(p => p.signature);
1091-
}
1092-
function signatureBlocksAction(signature, signatureDecodeFn, action) {
1093-
const { hashType } = signatureDecodeFn(signature);
1094-
const whitelist = [];
1095-
const isAnyoneCanPay =
1096-
hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY;
1097-
if (isAnyoneCanPay) whitelist.push('addInput');
1098-
const hashMod = hashType & 0x1f;
1099-
switch (hashMod) {
1100-
case transaction_1.Transaction.SIGHASH_ALL:
1101-
break;
1102-
case transaction_1.Transaction.SIGHASH_SINGLE:
1103-
case transaction_1.Transaction.SIGHASH_NONE:
1104-
whitelist.push('addOutput');
1105-
whitelist.push('setInputSequence');
1106-
break;
1107-
}
1108-
if (whitelist.indexOf(action) === -1) {
1109-
return true;
1110-
}
1111-
return false;
1112-
}
11131046
function checkPartialSigSighashes(input) {
11141047
if (!input.sighashType || !input.partialSig) return;
11151048
const { partialSig, sighashType } = input;
@@ -1459,20 +1392,6 @@ function getPayment(script, scriptType, partialSig) {
14591392
}
14601393
return payment;
14611394
}
1462-
function getPsigsFromInputFinalScripts(input) {
1463-
const scriptItems = !input.finalScriptSig
1464-
? []
1465-
: bscript.decompile(input.finalScriptSig) || [];
1466-
const witnessItems = !input.finalScriptWitness
1467-
? []
1468-
: bscript.decompile(input.finalScriptWitness) || [];
1469-
return scriptItems
1470-
.concat(witnessItems)
1471-
.filter(item => {
1472-
return Buffer.isBuffer(item) && bscript.isCanonicalScriptSignature(item);
1473-
})
1474-
.map(sig => ({ signature: sig }));
1475-
}
14761395
function getScriptFromInput(inputIndex, input, cache) {
14771396
const unsignedTx = cache.__TX;
14781397
const res = {

src/psbt/bip371.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@ export declare function tapTreeToList(tree: Taptree): TapLeaf[];
3838
* @returns the corresponding taptree, or throws an exception if the tree cannot be reconstructed
3939
*/
4040
export declare function tapTreeFromList(leaves?: TapLeaf[]): Taptree;
41+
export declare function checkTaprootInputForSigs(input: PsbtInput, action: string): boolean;

src/psbt/bip371.js

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
'use strict';
22
Object.defineProperty(exports, '__esModule', { value: true });
3-
exports.tapTreeFromList = exports.tapTreeToList = exports.tweakInternalPubKey = exports.checkTaprootOutputFields = exports.checkTaprootInputFields = exports.isTaprootOutput = exports.isTaprootInput = exports.serializeTaprootSignature = exports.tapScriptFinalizer = exports.toXOnly = void 0;
3+
exports.checkTaprootInputForSigs = exports.tapTreeFromList = exports.tapTreeToList = exports.tweakInternalPubKey = exports.checkTaprootOutputFields = exports.checkTaprootInputFields = exports.isTaprootOutput = exports.isTaprootInput = exports.serializeTaprootSignature = exports.tapScriptFinalizer = exports.toXOnly = void 0;
44
const types_1 = require('../types');
5+
const transaction_1 = require('../transaction');
56
const psbtutils_1 = require('./psbtutils');
67
const taprootutils_1 = require('../payments/taprootutils');
78
const payments_1 = require('../payments');
9+
const psbtutils_2 = require('./psbtutils');
810
const toXOnly = pubKey => (pubKey.length === 32 ? pubKey : pubKey.slice(1, 33));
911
exports.toXOnly = toXOnly;
1012
/**
@@ -141,6 +143,37 @@ function tapTreeFromList(leaves = []) {
141143
return instertLeavesInTree(leaves);
142144
}
143145
exports.tapTreeFromList = tapTreeFromList;
146+
function checkTaprootInputForSigs(input, action) {
147+
const sigs = extractTaprootSigs(input);
148+
return sigs.some(sig =>
149+
(0, psbtutils_2.signatureBlocksAction)(sig, decodeSchnorSignature, action),
150+
);
151+
}
152+
exports.checkTaprootInputForSigs = checkTaprootInputForSigs;
153+
function decodeSchnorSignature(signature) {
154+
return {
155+
signature: signature.slice(0, 64),
156+
hashType:
157+
signature.slice(64)[0] || transaction_1.Transaction.SIGHASH_DEFAULT,
158+
};
159+
}
160+
function extractTaprootSigs(input) {
161+
const sigs = [];
162+
if (input.tapKeySig) sigs.push(input.tapKeySig);
163+
if (input.tapScriptSig)
164+
sigs.push(...input.tapScriptSig.map(s => s.signature));
165+
if (!sigs.length) {
166+
const finalTapKeySig = getTapKeySigFromWithness(input.finalScriptWitness);
167+
if (finalTapKeySig) sigs.push(finalTapKeySig);
168+
}
169+
return sigs;
170+
}
171+
function getTapKeySigFromWithness(finalScriptWitness) {
172+
if (!finalScriptWitness) return;
173+
const witness = finalScriptWitness.slice(2);
174+
// todo: add schnor signature validation
175+
if (witness.length === 64 || witness.length === 65) return witness;
176+
}
144177
function _tapTreeToList(tree, leaves = [], depth = 0) {
145178
if (depth > taprootutils_1.MAX_TAPTREE_DEPTH)
146179
throw new Error('Max taptree depth exceeded.');

src/psbt/psbtutils.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/// <reference types="node" />
2+
import { PsbtInput } from 'bip174/src/lib/interfaces';
23
export declare const isP2MS: (script: Buffer) => boolean;
34
export declare const isP2PK: (script: Buffer) => boolean;
45
export declare const isP2PKH: (script: Buffer) => boolean;
@@ -9,3 +10,10 @@ export declare const isP2TR: (script: Buffer) => boolean;
910
export declare function witnessStackToScriptWitness(witness: Buffer[]): Buffer;
1011
export declare function pubkeyPositionInScript(pubkey: Buffer, script: Buffer): number;
1112
export declare function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean;
13+
export declare function checkInputForSig(input: PsbtInput, action: string): boolean;
14+
declare type SignatureDecodeFunc = (buffer: Buffer) => {
15+
signature: Buffer;
16+
hashType: number;
17+
};
18+
export declare function signatureBlocksAction(signature: Buffer, signatureDecodeFn: SignatureDecodeFunc, action: string): boolean;
19+
export {};

src/psbt/psbtutils.js

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
'use strict';
22
Object.defineProperty(exports, '__esModule', { value: true });
3-
exports.pubkeyInScript = exports.pubkeyPositionInScript = exports.witnessStackToScriptWitness = exports.isP2TR = exports.isP2SHScript = exports.isP2WSHScript = exports.isP2WPKH = exports.isP2PKH = exports.isP2PK = exports.isP2MS = void 0;
3+
exports.signatureBlocksAction = exports.checkInputForSig = exports.pubkeyInScript = exports.pubkeyPositionInScript = exports.witnessStackToScriptWitness = exports.isP2TR = exports.isP2SHScript = exports.isP2WSHScript = exports.isP2WPKH = exports.isP2PKH = exports.isP2PK = exports.isP2MS = void 0;
44
const varuint = require('bip174/src/lib/converter/varint');
55
const bscript = require('../script');
6+
const transaction_1 = require('../transaction');
67
const crypto_1 = require('../crypto');
78
const payments = require('../payments');
89
function isPaymentFactory(payment) {
@@ -64,3 +65,56 @@ function pubkeyInScript(pubkey, script) {
6465
return pubkeyPositionInScript(pubkey, script) !== -1;
6566
}
6667
exports.pubkeyInScript = pubkeyInScript;
68+
function checkInputForSig(input, action) {
69+
const pSigs = extractPartialSigs(input);
70+
return pSigs.some(pSig =>
71+
signatureBlocksAction(pSig, bscript.signature.decode, action),
72+
);
73+
}
74+
exports.checkInputForSig = checkInputForSig;
75+
function signatureBlocksAction(signature, signatureDecodeFn, action) {
76+
const { hashType } = signatureDecodeFn(signature);
77+
const whitelist = [];
78+
const isAnyoneCanPay =
79+
hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY;
80+
if (isAnyoneCanPay) whitelist.push('addInput');
81+
const hashMod = hashType & 0x1f;
82+
switch (hashMod) {
83+
case transaction_1.Transaction.SIGHASH_ALL:
84+
break;
85+
case transaction_1.Transaction.SIGHASH_SINGLE:
86+
case transaction_1.Transaction.SIGHASH_NONE:
87+
whitelist.push('addOutput');
88+
whitelist.push('setInputSequence');
89+
break;
90+
}
91+
if (whitelist.indexOf(action) === -1) {
92+
return true;
93+
}
94+
return false;
95+
}
96+
exports.signatureBlocksAction = signatureBlocksAction;
97+
function extractPartialSigs(input) {
98+
let pSigs = [];
99+
if ((input.partialSig || []).length === 0) {
100+
if (!input.finalScriptSig && !input.finalScriptWitness) return [];
101+
pSigs = getPsigsFromInputFinalScripts(input);
102+
} else {
103+
pSigs = input.partialSig;
104+
}
105+
return pSigs.map(p => p.signature);
106+
}
107+
function getPsigsFromInputFinalScripts(input) {
108+
const scriptItems = !input.finalScriptSig
109+
? []
110+
: bscript.decompile(input.finalScriptSig) || [];
111+
const witnessItems = !input.finalScriptWitness
112+
? []
113+
: bscript.decompile(input.finalScriptWitness) || [];
114+
return scriptItems
115+
.concat(witnessItems)
116+
.filter(item => {
117+
return Buffer.isBuffer(item) && bscript.isCanonicalScriptSignature(item);
118+
})
119+
.map(sig => ({ signature: sig }));
120+
}

ts_src/psbt.ts

Lines changed: 2 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@ import {
3030
checkTaprootInputFields,
3131
checkTaprootOutputFields,
3232
tweakInternalPubKey,
33+
checkTaprootInputForSigs,
3334
} from './psbt/bip371';
3435
import {
3536
witnessStackToScriptWitness,
37+
checkInputForSig,
3638
pubkeyInScript,
3739
isP2MS,
3840
isP2PK,
@@ -1376,96 +1378,6 @@ function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void {
13761378
});
13771379
}
13781380

1379-
function checkInputForSig(input: PsbtInput, action: string): boolean {
1380-
const pSigs = extractPartialSigs(input);
1381-
return pSigs.some(pSig =>
1382-
signatureBlocksAction(pSig, bscript.signature.decode, action),
1383-
);
1384-
}
1385-
1386-
function checkTaprootInputForSigs(input: PsbtInput, action: string): boolean {
1387-
const sigs = extractTaprootSigs(input);
1388-
return sigs.some(sig =>
1389-
signatureBlocksAction(sig, decodeSchnorSignature, action),
1390-
);
1391-
}
1392-
1393-
function decodeSchnorSignature(
1394-
signature: Buffer,
1395-
): {
1396-
signature: Buffer;
1397-
hashType: number;
1398-
} {
1399-
return {
1400-
signature: signature.slice(0, 64),
1401-
hashType: signature.slice(64)[0] || Transaction.SIGHASH_DEFAULT,
1402-
};
1403-
}
1404-
1405-
function extractTaprootSigs(input: PsbtInput): Buffer[] {
1406-
const sigs: Buffer[] = [];
1407-
if (input.tapKeySig) sigs.push(input.tapKeySig);
1408-
if (input.tapScriptSig)
1409-
sigs.push(...input.tapScriptSig.map(s => s.signature));
1410-
if (!sigs.length) {
1411-
const finalTapKeySig = getTapKeySigFromWithness(input.finalScriptWitness);
1412-
if (finalTapKeySig) sigs.push(finalTapKeySig);
1413-
}
1414-
1415-
return sigs;
1416-
}
1417-
1418-
function getTapKeySigFromWithness(
1419-
finalScriptWitness?: Buffer,
1420-
): Buffer | undefined {
1421-
if (!finalScriptWitness) return;
1422-
const witness = finalScriptWitness.slice(2);
1423-
// todo: add schnor signature validation
1424-
if (witness.length === 64 || witness.length === 65) return witness;
1425-
}
1426-
1427-
function extractPartialSigs(input: PsbtInput): Buffer[] {
1428-
let pSigs: PartialSig[] = [];
1429-
if ((input.partialSig || []).length === 0) {
1430-
if (!input.finalScriptSig && !input.finalScriptWitness) return [];
1431-
pSigs = getPsigsFromInputFinalScripts(input);
1432-
} else {
1433-
pSigs = input.partialSig!;
1434-
}
1435-
return pSigs.map(p => p.signature);
1436-
}
1437-
1438-
type SignatureDecodeFunc = (
1439-
buffer: Buffer,
1440-
) => {
1441-
signature: Buffer;
1442-
hashType: number;
1443-
};
1444-
function signatureBlocksAction(
1445-
signature: Buffer,
1446-
signatureDecodeFn: SignatureDecodeFunc,
1447-
action: string,
1448-
): boolean {
1449-
const { hashType } = signatureDecodeFn(signature);
1450-
const whitelist: string[] = [];
1451-
const isAnyoneCanPay = hashType & Transaction.SIGHASH_ANYONECANPAY;
1452-
if (isAnyoneCanPay) whitelist.push('addInput');
1453-
const hashMod = hashType & 0x1f;
1454-
switch (hashMod) {
1455-
case Transaction.SIGHASH_ALL:
1456-
break;
1457-
case Transaction.SIGHASH_SINGLE:
1458-
case Transaction.SIGHASH_NONE:
1459-
whitelist.push('addOutput');
1460-
whitelist.push('setInputSequence');
1461-
break;
1462-
}
1463-
if (whitelist.indexOf(action) === -1) {
1464-
return true;
1465-
}
1466-
return false;
1467-
}
1468-
14691381
function checkPartialSigSighashes(input: PsbtInput): void {
14701382
if (!input.sighashType || !input.partialSig) return;
14711383
const { partialSig, sighashType } = input;
@@ -1926,21 +1838,6 @@ function getPayment(
19261838
return payment!;
19271839
}
19281840

1929-
function getPsigsFromInputFinalScripts(input: PsbtInput): PartialSig[] {
1930-
const scriptItems = !input.finalScriptSig
1931-
? []
1932-
: bscript.decompile(input.finalScriptSig) || [];
1933-
const witnessItems = !input.finalScriptWitness
1934-
? []
1935-
: bscript.decompile(input.finalScriptWitness) || [];
1936-
return scriptItems
1937-
.concat(witnessItems)
1938-
.filter(item => {
1939-
return Buffer.isBuffer(item) && bscript.isCanonicalScriptSignature(item);
1940-
})
1941-
.map(sig => ({ signature: sig })) as PartialSig[];
1942-
}
1943-
19441841
interface GetScriptReturn {
19451842
script: Buffer | null;
19461843
isSegwit: boolean;

0 commit comments

Comments
 (0)