Skip to content

Commit 5a9affc

Browse files
committed
refactor(psbt): extract finalize helpers into internal module
1 parent 17b495f commit 5a9affc

7 files changed

Lines changed: 535 additions & 456 deletions

File tree

src/cjs/psbt.cjs

Lines changed: 6 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ Object.defineProperty(exports, 'toXOnly', {
6363
});
6464
const psbtutils_js_1 = require('./psbt/psbtutils.cjs');
6565
const witness_js_1 = require('./psbt/internal/witness.cjs');
66+
const finalize_js_1 = require('./psbt/internal/finalize.cjs');
6667
const scriptType_js_1 = require('./psbt/internal/scriptType.cjs');
6768
const tools = __importStar(require('uint8array-tools'));
6869
/**
@@ -366,7 +367,11 @@ class Psbt {
366367
);
367368
throw new Error(`Cannot finalize input #${inputIndex}. Not Taproot.`);
368369
}
369-
_finalizeInput(inputIndex, input, finalScriptsFunc = getFinalScripts) {
370+
_finalizeInput(
371+
inputIndex,
372+
input,
373+
finalScriptsFunc = finalize_js_1.getFinalScripts,
374+
) {
370375
const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
371376
inputIndex,
372377
input,
@@ -1039,42 +1044,11 @@ class PsbtTransaction {
10391044
return this.tx.toBuffer();
10401045
}
10411046
}
1042-
function canFinalize(input, script, scriptType) {
1043-
switch (scriptType) {
1044-
case 'pubkey':
1045-
case 'pubkeyhash':
1046-
case 'witnesspubkeyhash':
1047-
return hasSigs(1, input.partialSig);
1048-
case 'multisig':
1049-
const p2ms = payments.p2ms({ output: script });
1050-
return hasSigs(p2ms.m, input.partialSig, p2ms.pubkeys);
1051-
default:
1052-
return false;
1053-
}
1054-
}
10551047
function checkCache(cache) {
10561048
if (cache.__UNSAFE_SIGN_NONSEGWIT !== false) {
10571049
throw new Error('Not BIP174 compliant, can not export');
10581050
}
10591051
}
1060-
function hasSigs(neededSigs, partialSig, pubkeys) {
1061-
if (!partialSig) return false;
1062-
let sigs;
1063-
if (pubkeys) {
1064-
sigs = pubkeys
1065-
.map(pkey => {
1066-
const pubkey = compressPubkey(pkey);
1067-
return partialSig.find(
1068-
pSig => tools.compare(pSig.pubkey, pubkey) === 0,
1069-
);
1070-
})
1071-
.filter(v => !!v);
1072-
} else {
1073-
sigs = partialSig;
1074-
}
1075-
if (sigs.length > neededSigs) throw new Error('Too many signatures');
1076-
return sigs.length === neededSigs;
1077-
}
10781052
function isFinalized(input) {
10791053
return !!input.finalScriptSig || !!input.finalScriptWitness;
10801054
}
@@ -1213,61 +1187,6 @@ function getTxCacheValue(key, name, inputs, c) {
12131187
if (key === '__FEE_RATE') return c.__FEE_RATE;
12141188
else if (key === '__FEE') return c.__FEE;
12151189
}
1216-
function getFinalScripts(inputIndex, input, script, isSegwit, isP2SH, isP2WSH) {
1217-
const scriptType = (0, scriptType_js_1.classifyScript)(
1218-
script,
1219-
SCRIPT_TYPE_DEPS,
1220-
);
1221-
if (!canFinalize(input, script, scriptType))
1222-
throw new Error(`Can not finalize input #${inputIndex}`);
1223-
return prepareFinalScripts(
1224-
script,
1225-
scriptType,
1226-
input.partialSig,
1227-
isSegwit,
1228-
isP2SH,
1229-
isP2WSH,
1230-
);
1231-
}
1232-
function prepareFinalScripts(
1233-
script,
1234-
scriptType,
1235-
partialSig,
1236-
isSegwit,
1237-
isP2SH,
1238-
isP2WSH,
1239-
) {
1240-
let finalScriptSig;
1241-
let finalScriptWitness;
1242-
// Wow, the payments API is very handy
1243-
const payment = getPayment(script, scriptType, partialSig);
1244-
const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
1245-
const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
1246-
if (isSegwit) {
1247-
if (p2wsh) {
1248-
finalScriptWitness = (0, psbtutils_js_1.witnessStackToScriptWitness)(
1249-
p2wsh.witness,
1250-
);
1251-
} else {
1252-
finalScriptWitness = (0, psbtutils_js_1.witnessStackToScriptWitness)(
1253-
payment.witness,
1254-
);
1255-
}
1256-
if (p2sh) {
1257-
finalScriptSig = p2sh.input;
1258-
}
1259-
} else {
1260-
if (p2sh) {
1261-
finalScriptSig = p2sh.input;
1262-
} else {
1263-
finalScriptSig = payment.input;
1264-
}
1265-
}
1266-
return {
1267-
finalScriptSig,
1268-
finalScriptWitness,
1269-
};
1270-
}
12711190
function getHashAndSighashType(
12721191
inputs,
12731192
inputIndex,
@@ -1472,39 +1391,6 @@ function checkSighashTypeAllowed(sighashType, sighashTypes) {
14721391
);
14731392
}
14741393
}
1475-
function getPayment(script, scriptType, partialSig) {
1476-
let payment;
1477-
switch (scriptType) {
1478-
case 'multisig':
1479-
const sigs = getSortedSigs(script, partialSig);
1480-
payment = payments.p2ms({
1481-
output: script,
1482-
signatures: sigs,
1483-
});
1484-
break;
1485-
case 'pubkey':
1486-
payment = payments.p2pk({
1487-
output: script,
1488-
signature: partialSig[0].signature,
1489-
});
1490-
break;
1491-
case 'pubkeyhash':
1492-
payment = payments.p2pkh({
1493-
output: script,
1494-
pubkey: partialSig[0].pubkey,
1495-
signature: partialSig[0].signature,
1496-
});
1497-
break;
1498-
case 'witnesspubkeyhash':
1499-
payment = payments.p2wpkh({
1500-
output: script,
1501-
pubkey: partialSig[0].pubkey,
1502-
signature: partialSig[0].signature,
1503-
});
1504-
break;
1505-
}
1506-
return payment;
1507-
}
15081394
function getScriptFromInput(inputIndex, input, cache) {
15091395
const unsignedTx = cache.__TX;
15101396
const res = {
@@ -1565,22 +1451,6 @@ function getSignersFromHD(inputIndex, inputs, hdKeyPair) {
15651451
});
15661452
return signers;
15671453
}
1568-
function getSortedSigs(script, partialSig) {
1569-
const p2ms = payments.p2ms({ output: script });
1570-
// for each pubkey in order of p2ms script
1571-
return p2ms.pubkeys
1572-
.map(pk => {
1573-
// filter partialSig array by pubkey being equal
1574-
return (
1575-
partialSig.filter(ps => {
1576-
return tools.compare(ps.pubkey, pk) === 0;
1577-
})[0] || {}
1578-
).signature;
1579-
// Any pubkey without a match will return undefined
1580-
// this last filter removes all the undefined items in the array.
1581-
})
1582-
.filter(v => !!v);
1583-
}
15841454
function sighashTypeToString(sighashType) {
15851455
let text =
15861456
sighashType & transaction_js_1.Transaction.SIGHASH_ANYONECANPAY
@@ -1731,15 +1601,6 @@ function redeemFromFinalWitnessScript(finalScript) {
17311601
if (!sDecomp) return;
17321602
return lastItem;
17331603
}
1734-
function compressPubkey(pubkey) {
1735-
if (pubkey.length === 65) {
1736-
const parity = pubkey[64] & 1;
1737-
const newKey = pubkey.slice(0, 33);
1738-
newKey[0] = 2 | parity;
1739-
return newKey;
1740-
}
1741-
return pubkey.slice();
1742-
}
17431604
function isPubkeyLike(buf) {
17441605
return buf.length === 33 && bscript.isCanonicalPubKey(buf);
17451606
}

src/cjs/psbt/internal/finalize.cjs

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
'use strict';
2+
var __createBinding =
3+
(this && this.__createBinding) ||
4+
(Object.create
5+
? function (o, m, k, k2) {
6+
if (k2 === undefined) k2 = k;
7+
var desc = Object.getOwnPropertyDescriptor(m, k);
8+
if (
9+
!desc ||
10+
('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
11+
) {
12+
desc = {
13+
enumerable: true,
14+
get: function () {
15+
return m[k];
16+
},
17+
};
18+
}
19+
Object.defineProperty(o, k2, desc);
20+
}
21+
: function (o, m, k, k2) {
22+
if (k2 === undefined) k2 = k;
23+
o[k2] = m[k];
24+
});
25+
var __setModuleDefault =
26+
(this && this.__setModuleDefault) ||
27+
(Object.create
28+
? function (o, v) {
29+
Object.defineProperty(o, 'default', { enumerable: true, value: v });
30+
}
31+
: function (o, v) {
32+
o['default'] = v;
33+
});
34+
var __importStar =
35+
(this && this.__importStar) ||
36+
function (mod) {
37+
if (mod && mod.__esModule) return mod;
38+
var result = {};
39+
if (mod != null)
40+
for (var k in mod)
41+
if (k !== 'default' && Object.prototype.hasOwnProperty.call(mod, k))
42+
__createBinding(result, mod, k);
43+
__setModuleDefault(result, mod);
44+
return result;
45+
};
46+
Object.defineProperty(exports, '__esModule', { value: true });
47+
exports.getFinalScripts = getFinalScripts;
48+
const payments = __importStar(require('../../payments/index.cjs'));
49+
const psbtutils_js_1 = require('../psbtutils.cjs');
50+
const psbtutils_js_2 = require('../psbtutils.cjs');
51+
const scriptType_js_1 = require('./scriptType.cjs');
52+
const tools = __importStar(require('uint8array-tools'));
53+
function getFinalScripts(inputIndex, input, script, isSegwit, isP2SH, isP2WSH) {
54+
const scriptType = (0, scriptType_js_1.classifyScript)(script, {
55+
isP2WPKH: psbtutils_js_2.isP2WPKH,
56+
isP2PKH: psbtutils_js_2.isP2PKH,
57+
isP2MS: psbtutils_js_2.isP2MS,
58+
isP2PK: psbtutils_js_2.isP2PK,
59+
});
60+
if (!canFinalize(input, script, scriptType))
61+
throw new Error(`Can not finalize input #${inputIndex}`);
62+
return prepareFinalScripts(
63+
script,
64+
scriptType,
65+
input.partialSig,
66+
isSegwit,
67+
isP2SH,
68+
isP2WSH,
69+
);
70+
}
71+
function prepareFinalScripts(
72+
script,
73+
scriptType,
74+
partialSig,
75+
isSegwit,
76+
isP2SH,
77+
isP2WSH,
78+
) {
79+
let finalScriptSig;
80+
let finalScriptWitness;
81+
const payment = getPayment(script, scriptType, partialSig);
82+
const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
83+
const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
84+
if (isSegwit) {
85+
if (p2wsh) {
86+
finalScriptWitness = (0, psbtutils_js_1.witnessStackToScriptWitness)(
87+
p2wsh.witness,
88+
);
89+
} else {
90+
finalScriptWitness = (0, psbtutils_js_1.witnessStackToScriptWitness)(
91+
payment.witness,
92+
);
93+
}
94+
if (p2sh) {
95+
finalScriptSig = p2sh.input;
96+
}
97+
} else {
98+
if (p2sh) {
99+
finalScriptSig = p2sh.input;
100+
} else {
101+
finalScriptSig = payment.input;
102+
}
103+
}
104+
return {
105+
finalScriptSig,
106+
finalScriptWitness,
107+
};
108+
}
109+
function canFinalize(input, script, scriptType) {
110+
switch (scriptType) {
111+
case 'pubkey':
112+
case 'pubkeyhash':
113+
case 'witnesspubkeyhash':
114+
return hasSigs(1, input.partialSig);
115+
case 'multisig':
116+
const p2ms = payments.p2ms({ output: script });
117+
return hasSigs(p2ms.m, input.partialSig, p2ms.pubkeys);
118+
default:
119+
return false;
120+
}
121+
}
122+
function hasSigs(neededSigs, partialSig, pubkeys) {
123+
if (!partialSig) return false;
124+
let sigs;
125+
if (pubkeys) {
126+
sigs = pubkeys
127+
.map(pkey => {
128+
const pubkey = compressPubkey(pkey);
129+
return partialSig.find(
130+
pSig => tools.compare(pSig.pubkey, pubkey) === 0,
131+
);
132+
})
133+
.filter(v => !!v);
134+
} else {
135+
sigs = partialSig;
136+
}
137+
if (sigs.length > neededSigs) throw new Error('Too many signatures');
138+
return sigs.length === neededSigs;
139+
}
140+
function getSortedSigs(script, partialSig) {
141+
const p2ms = payments.p2ms({ output: script });
142+
return p2ms.pubkeys
143+
.map(pk => {
144+
return (
145+
partialSig.filter(ps => {
146+
return tools.compare(ps.pubkey, pk) === 0;
147+
})[0] || {}
148+
).signature;
149+
})
150+
.filter(v => !!v);
151+
}
152+
function getPayment(script, scriptType, partialSig) {
153+
let payment;
154+
switch (scriptType) {
155+
case 'multisig':
156+
const sigs = getSortedSigs(script, partialSig);
157+
payment = payments.p2ms({
158+
output: script,
159+
signatures: sigs,
160+
});
161+
break;
162+
case 'pubkey':
163+
payment = payments.p2pk({
164+
output: script,
165+
signature: partialSig[0].signature,
166+
});
167+
break;
168+
case 'pubkeyhash':
169+
payment = payments.p2pkh({
170+
output: script,
171+
pubkey: partialSig[0].pubkey,
172+
signature: partialSig[0].signature,
173+
});
174+
break;
175+
case 'witnesspubkeyhash':
176+
payment = payments.p2wpkh({
177+
output: script,
178+
pubkey: partialSig[0].pubkey,
179+
signature: partialSig[0].signature,
180+
});
181+
break;
182+
}
183+
return payment;
184+
}
185+
function compressPubkey(pubkey) {
186+
if (pubkey.length === 65) {
187+
const parity = pubkey[64] & 1;
188+
const newKey = pubkey.slice(0, 33);
189+
newKey[0] = 2 | parity;
190+
return newKey;
191+
}
192+
return pubkey.slice();
193+
}

0 commit comments

Comments
 (0)