Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 40 additions & 92 deletions src/cjs/psbt.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ var __importStar =
Object.defineProperty(exports, '__esModule', { value: true });
exports.Psbt = exports.toXOnly = void 0;
const bip174_1 = require('bip174');
const varuint = __importStar(require('varuint-bitcoin'));
const bip174_2 = require('bip174');
const address_js_1 = require('./address.cjs');
const bufferutils_js_1 = require('./bufferutils.cjs');
Expand All @@ -63,6 +62,8 @@ Object.defineProperty(exports, 'toXOnly', {
},
});
const psbtutils_js_1 = require('./psbt/psbtutils.cjs');
const witness_js_1 = require('./psbt/internal/witness.cjs');
const scriptType_js_1 = require('./psbt/internal/scriptType.cjs');
const tools = __importStar(require('uint8array-tools'));
/**
* These are the default arguments for a Psbt instance.
Expand Down Expand Up @@ -261,7 +262,8 @@ class Psbt {
}
(0, bip371_js_1.checkTaprootInputFields)(inputData, inputData, 'addInput');
checkInputsForPartialSig(this.data.inputs, 'addInput');
if (inputData.witnessScript) checkInvalidP2WSH(inputData.witnessScript);
if (inputData.witnessScript)
checkInvalidP2WSHScript(inputData.witnessScript);
const c = this.__CACHE;
this.data.addInput(inputData);
const txIn = c.__TX.ins[c.__TX.ins.length - 1];
Expand Down Expand Up @@ -421,16 +423,20 @@ class Psbt {
getInputType(inputIndex) {
const input = (0, bip174_2.checkForInput)(this.data.inputs, inputIndex);
const script = getScriptFromUtxo(inputIndex, input, this.__CACHE);
const result = getMeaningfulScript(
const result = (0, scriptType_js_1.getMeaningfulScript)(
script,
inputIndex,
'input',
input.redeemScript || redeemFromFinalScriptSig(input.finalScriptSig),
input.witnessScript ||
redeemFromFinalWitnessScript(input.finalScriptWitness),
SCRIPT_TYPE_DEPS,
);
const type = result.type === 'raw' ? '' : result.type + '-';
const mainType = classifyScript(result.meaningfulScript);
const mainType = (0, scriptType_js_1.classifyScript)(
result.meaningfulScript,
SCRIPT_TYPE_DEPS,
);
return type + mainType;
}
inputHasPubkey(inputIndex, pubkey) {
Expand Down Expand Up @@ -932,7 +938,8 @@ class Psbt {
return this;
}
updateInput(inputIndex, updateData) {
if (updateData.witnessScript) checkInvalidP2WSH(updateData.witnessScript);
if (updateData.witnessScript)
checkInvalidP2WSHScript(updateData.witnessScript);
(0, bip371_js_1.checkTaprootInputFields)(
this.data.inputs[inputIndex],
updateData,
Expand Down Expand Up @@ -1173,6 +1180,22 @@ const checkWitnessScript = scriptCheckerFactory(
payments.p2wsh,
'Witness script',
);
const SCRIPT_TYPE_DEPS = {
isP2WPKH: psbtutils_js_1.isP2WPKH,
isP2PKH: psbtutils_js_1.isP2PKH,
isP2MS: psbtutils_js_1.isP2MS,
isP2PK: psbtutils_js_1.isP2PK,
isP2SHScript: psbtutils_js_1.isP2SHScript,
isP2WSHScript: psbtutils_js_1.isP2WSHScript,
checkRedeemScript,
checkWitnessScript,
};
const checkInvalidP2WSHScript = script =>
(0, scriptType_js_1.checkInvalidP2WSH)(
script,
psbtutils_js_1.isP2WPKH,
psbtutils_js_1.isP2SHScript,
);
function getTxCacheValue(key, name, inputs, c) {
if (!inputs.every(isFinalized))
throw new Error(`PSBT must be finalized to calculate ${name}`);
Expand All @@ -1191,7 +1214,10 @@ function getTxCacheValue(key, name, inputs, c) {
else if (key === '__FEE') return c.__FEE;
}
function getFinalScripts(inputIndex, input, script, isSegwit, isP2SH, isP2WSH) {
const scriptType = classifyScript(script);
const scriptType = (0, scriptType_js_1.classifyScript)(
script,
SCRIPT_TYPE_DEPS,
);
if (!canFinalize(input, script, scriptType))
throw new Error(`Can not finalize input #${inputIndex}`);
return prepareFinalScripts(
Expand Down Expand Up @@ -1291,12 +1317,13 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
} else {
throw new Error('Need a Utxo input item for signing');
}
const { meaningfulScript, type } = getMeaningfulScript(
const { meaningfulScript, type } = (0, scriptType_js_1.getMeaningfulScript)(
prevout.script,
inputIndex,
'input',
input.redeemScript,
input.witnessScript,
SCRIPT_TYPE_DEPS,
);
if (['p2sh-p2wsh', 'p2wsh'].indexOf(type) >= 0) {
hash = unsignedTx.hashForWitnessV0(
Expand Down Expand Up @@ -1554,28 +1581,6 @@ function getSortedSigs(script, partialSig) {
})
.filter(v => !!v);
}
function scriptWitnessToWitnessStack(buffer) {
let offset = 0;
function readSlice(n) {
offset += n;
return buffer.slice(offset - n, offset);
}
function readVarInt() {
const vi = varuint.decode(buffer, offset);
offset += varuint.encodingLength(vi.bigintValue);
return vi.numberValue;
}
function readVarSlice() {
return readSlice(readVarInt());
}
function readVector() {
const count = readVarInt();
const vector = [];
for (let i = 0; i < count; i++) vector.push(readVarSlice());
return vector;
}
return readVector();
}
function sighashTypeToString(sighashType) {
let text =
sighashType & transaction_js_1.Transaction.SIGHASH_ANYONECANPAY
Expand Down Expand Up @@ -1626,7 +1631,7 @@ function inputFinalizeGetAmts(inputs, tx, cache, mustFinalize) {
if (mustFinalize && input.finalScriptSig)
tx.ins[idx].script = input.finalScriptSig;
if (mustFinalize && input.finalScriptWitness) {
tx.ins[idx].witness = scriptWitnessToWitnessStack(
tx.ins[idx].witness = (0, witness_js_1.scriptWitnessToWitnessStack)(
input.finalScriptWitness,
);
}
Expand Down Expand Up @@ -1680,23 +1685,25 @@ function getScriptAndAmountFromUtxo(inputIndex, input, cache) {
}
function pubkeyInInput(pubkey, input, inputIndex, cache) {
const script = getScriptFromUtxo(inputIndex, input, cache);
const { meaningfulScript } = getMeaningfulScript(
const { meaningfulScript } = (0, scriptType_js_1.getMeaningfulScript)(
script,
inputIndex,
'input',
input.redeemScript,
input.witnessScript,
SCRIPT_TYPE_DEPS,
);
return (0, psbtutils_js_1.pubkeyInScript)(pubkey, meaningfulScript);
}
function pubkeyInOutput(pubkey, output, outputIndex, cache) {
const script = cache.__TX.outs[outputIndex].script;
const { meaningfulScript } = getMeaningfulScript(
const { meaningfulScript } = (0, scriptType_js_1.getMeaningfulScript)(
script,
outputIndex,
'output',
output.redeemScript,
output.witnessScript,
SCRIPT_TYPE_DEPS,
);
return (0, psbtutils_js_1.pubkeyInScript)(pubkey, meaningfulScript);
}
Expand All @@ -1717,7 +1724,7 @@ function redeemFromFinalScriptSig(finalScript) {
}
function redeemFromFinalWitnessScript(finalScript) {
if (!finalScript) return;
const decomp = scriptWitnessToWitnessStack(finalScript);
const decomp = (0, witness_js_1.scriptWitnessToWitnessStack)(finalScript);
const lastItem = decomp[decomp.length - 1];
if (isPubkeyLike(lastItem)) return;
const sDecomp = bscript.decompile(lastItem);
Expand All @@ -1739,65 +1746,6 @@ function isPubkeyLike(buf) {
function isSigLike(buf) {
return bscript.isCanonicalScriptSignature(buf);
}
function getMeaningfulScript(
script,
index,
ioType,
redeemScript,
witnessScript,
) {
const isP2SH = (0, psbtutils_js_1.isP2SHScript)(script);
const isP2SHP2WSH =
isP2SH && redeemScript && (0, psbtutils_js_1.isP2WSHScript)(redeemScript);
const isP2WSH = (0, psbtutils_js_1.isP2WSHScript)(script);
if (isP2SH && redeemScript === undefined)
throw new Error('scriptPubkey is P2SH but redeemScript missing');
if ((isP2WSH || isP2SHP2WSH) && witnessScript === undefined)
throw new Error(
'scriptPubkey or redeemScript is P2WSH but witnessScript missing',
);
let meaningfulScript;
if (isP2SHP2WSH) {
meaningfulScript = witnessScript;
checkRedeemScript(index, script, redeemScript, ioType);
checkWitnessScript(index, redeemScript, witnessScript, ioType);
checkInvalidP2WSH(meaningfulScript);
} else if (isP2WSH) {
meaningfulScript = witnessScript;
checkWitnessScript(index, script, witnessScript, ioType);
checkInvalidP2WSH(meaningfulScript);
} else if (isP2SH) {
meaningfulScript = redeemScript;
checkRedeemScript(index, script, redeemScript, ioType);
} else {
meaningfulScript = script;
}
return {
meaningfulScript,
type: isP2SHP2WSH
? 'p2sh-p2wsh'
: isP2SH
? 'p2sh'
: isP2WSH
? 'p2wsh'
: 'raw',
};
}
function checkInvalidP2WSH(script) {
if (
(0, psbtutils_js_1.isP2WPKH)(script) ||
(0, psbtutils_js_1.isP2SHScript)(script)
) {
throw new Error('P2WPKH or P2SH can not be contained within P2WSH');
}
}
function classifyScript(script) {
if ((0, psbtutils_js_1.isP2WPKH)(script)) return 'witnesspubkeyhash';
if ((0, psbtutils_js_1.isP2PKH)(script)) return 'pubkeyhash';
if ((0, psbtutils_js_1.isP2MS)(script)) return 'multisig';
if ((0, psbtutils_js_1.isP2PK)(script)) return 'pubkey';
return 'nonstandard';
}
function range(n) {
return [...Array(n).keys()];
}
62 changes: 62 additions & 0 deletions src/cjs/psbt/internal/scriptType.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
exports.classifyScript = classifyScript;
exports.getMeaningfulScript = getMeaningfulScript;
exports.checkInvalidP2WSH = checkInvalidP2WSH;
function classifyScript(script, deps) {
if (deps.isP2WPKH(script)) return 'witnesspubkeyhash';
if (deps.isP2PKH(script)) return 'pubkeyhash';
if (deps.isP2MS(script)) return 'multisig';
if (deps.isP2PK(script)) return 'pubkey';
return 'nonstandard';
}
function getMeaningfulScript(
script,
index,
ioType,
redeemScript,
witnessScript,
deps,
) {
const isP2SH = deps.isP2SHScript(script);
const isP2SHP2WSH =
isP2SH && redeemScript && deps.isP2WSHScript(redeemScript);
const isP2WSH = deps.isP2WSHScript(script);
if (isP2SH && redeemScript === undefined)
throw new Error('scriptPubkey is P2SH but redeemScript missing');
if ((isP2WSH || isP2SHP2WSH) && witnessScript === undefined)
throw new Error(
'scriptPubkey or redeemScript is P2WSH but witnessScript missing',
);
let meaningfulScript;
if (isP2SHP2WSH) {
meaningfulScript = witnessScript;
deps.checkRedeemScript(index, script, redeemScript, ioType);
deps.checkWitnessScript(index, redeemScript, witnessScript, ioType);
checkInvalidP2WSH(meaningfulScript, deps.isP2WPKH, deps.isP2SHScript);
} else if (isP2WSH) {
meaningfulScript = witnessScript;
deps.checkWitnessScript(index, script, witnessScript, ioType);
checkInvalidP2WSH(meaningfulScript, deps.isP2WPKH, deps.isP2SHScript);
} else if (isP2SH) {
meaningfulScript = redeemScript;
deps.checkRedeemScript(index, script, redeemScript, ioType);
} else {
meaningfulScript = script;
}
return {
meaningfulScript,
type: isP2SHP2WSH
? 'p2sh-p2wsh'
: isP2SH
? 'p2sh'
: isP2WSH
? 'p2wsh'
: 'raw',
};
}
function checkInvalidP2WSH(script, isP2WPKH, isP2SHScript) {
if (isP2WPKH(script) || isP2SHScript(script)) {
throw new Error('P2WPKH or P2SH can not be contained within P2WSH');
}
}
17 changes: 17 additions & 0 deletions src/cjs/psbt/internal/scriptType.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export type ScriptType = 'witnesspubkeyhash' | 'pubkeyhash' | 'multisig' | 'pubkey' | 'nonstandard';
export interface ScriptTypeDeps {
isP2WPKH(script: Uint8Array): boolean;
isP2PKH(script: Uint8Array): boolean;
isP2MS(script: Uint8Array): boolean;
isP2PK(script: Uint8Array): boolean;
isP2SHScript(script: Uint8Array): boolean;
isP2WSHScript(script: Uint8Array): boolean;
checkRedeemScript(index: number, script: Uint8Array, redeemScript: Uint8Array, ioType: 'input' | 'output'): void;
checkWitnessScript(index: number, script: Uint8Array, witnessScript: Uint8Array, ioType: 'input' | 'output'): void;
}
export declare function classifyScript(script: Uint8Array, deps: Pick<ScriptTypeDeps, 'isP2WPKH' | 'isP2PKH' | 'isP2MS' | 'isP2PK'>): ScriptType;
export declare function getMeaningfulScript(script: Uint8Array, index: number, ioType: 'input' | 'output', redeemScript: Uint8Array | undefined, witnessScript: Uint8Array | undefined, deps: Pick<ScriptTypeDeps, 'isP2SHScript' | 'isP2WSHScript' | 'isP2WPKH' | 'checkRedeemScript' | 'checkWitnessScript'>): {
meaningfulScript: Uint8Array;
type: 'p2sh' | 'p2wsh' | 'p2sh-p2wsh' | 'raw';
};
export declare function checkInvalidP2WSH(script: Uint8Array, isP2WPKH: (script: Uint8Array) => boolean, isP2SHScript: (script: Uint8Array) => boolean): void;
Loading
Loading