@ledgerhq/hw-app-btc
Version:
Ledger Hardware Wallet Bitcoin Application API
92 lines • 4.14 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.arePathsEqual = arePathsEqual;
exports.validateAccountPathConsistency = validateAccountPathConsistency;
exports.validateScriptTypeConsistency = validateScriptTypeConsistency;
exports.resolveAccountPathFromOptions = resolveAccountPathFromOptions;
exports.determineInputScriptType = determineInputScriptType;
exports.analyzeInput = analyzeInput;
exports.analyzeAllInputs = analyzeAllInputs;
const psbtv2_1 = require("@ledgerhq/psbtv2");
const bip32_1 = require("../bip32");
const derivationAccessors_1 = require("./derivationAccessors");
function arePathsEqual(path1, path2) {
if (path1.length !== path2.length)
return false;
return path1.every((elem, idx) => elem === path2[idx]);
}
function validateAccountPathConsistency(accountPath, newAccountPath, inputIndex) {
if (accountPath.length > 0 && !arePathsEqual(accountPath, newAccountPath)) {
throw new Error(`Mixed accounts detected in PSBT. Input ${inputIndex} uses account path ` +
`${(0, bip32_1.pathArrayToString)(newAccountPath)} but expected ` +
`${(0, bip32_1.pathArrayToString)(accountPath)}. All internal inputs must belong to the same account.`);
}
}
function validateScriptTypeConsistency(detectedScriptType, newScriptType, inputIndex) {
if (detectedScriptType && newScriptType && detectedScriptType !== newScriptType) {
throw new Error(`Mixed input types detected in PSBT. Input ${inputIndex} uses ${newScriptType} ` +
`but expected ${detectedScriptType}. All internal inputs must use the same script type.`);
}
}
function resolveAccountPathFromOptions(accountPathOption) {
if (!accountPathOption) {
throw new Error("No internal inputs found in PSBT (no BIP32 derivation matching device fingerprint) " +
"and no account path provided in options. Please provide accountPath in options " +
"(e.g., \"m/84'/0'/0'\" for native segwit)");
}
return (0, bip32_1.pathStringToArray)(accountPathOption);
}
/**
* Determines the script type for a single input from witness UTXO or redeem script.
*/
function determineInputScriptType(psbt, inputIndex) {
const witnessUtxo = psbt.getInputWitnessUtxo(inputIndex);
if (witnessUtxo) {
return (0, psbtv2_1.detectScriptType)(witnessUtxo.scriptPubKey);
}
const redeemScript = psbt.getInputRedeemScript(inputIndex);
if (redeemScript) {
return "p2sh-p2wpkh";
}
return undefined;
}
/**
* Analyzes a single input to determine if it belongs to the connected signer and extracts account path and script type.
*/
function analyzeInput(psbt, inputIndex, masterFp) {
const derivationResult = (0, derivationAccessors_1.checkBip32Derivation)(psbt, inputIndex, masterFp);
const scriptType = determineInputScriptType(psbt, inputIndex);
return {
belongsToSigner: derivationResult.belongsToSigner,
accountPath: derivationResult.accountPath,
scriptType,
};
}
/**
* Analyzes all inputs and returns resolved account path, detected script type, and internal input indices.
*/
function analyzeAllInputs(psbt, inputCount, masterFp, accountPathOption) {
const internalInputIndices = [];
let accountPath = [];
let detectedScriptType;
for (let i = 0; i < inputCount; i++) {
const inputInfo = analyzeInput(psbt, i, masterFp);
if (!inputInfo.belongsToSigner) {
continue;
}
internalInputIndices.push(i);
validateAccountPathConsistency(accountPath, inputInfo.accountPath, i);
if (accountPath.length === 0) {
accountPath = inputInfo.accountPath;
}
validateScriptTypeConsistency(detectedScriptType, inputInfo.scriptType, i);
if (!detectedScriptType) {
detectedScriptType = inputInfo.scriptType;
}
}
if (internalInputIndices.length === 0) {
accountPath = resolveAccountPathFromOptions(accountPathOption);
}
return { accountPath, detectedScriptType, internalInputIndices };
}
//# sourceMappingURL=inputAnalysis.js.map