@ledgerhq/hw-app-btc
Version:
Ledger Hardware Wallet Bitcoin Application API
83 lines • 3.66 kB
JavaScript
import { detectScriptType } from "@ledgerhq/psbtv2";
import { pathArrayToString, pathStringToArray } from "../bip32";
import { checkBip32Derivation } from "./derivationAccessors";
export function arePathsEqual(path1, path2) {
if (path1.length !== path2.length)
return false;
return path1.every((elem, idx) => elem === path2[idx]);
}
export 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 ` +
`${pathArrayToString(newAccountPath)} but expected ` +
`${pathArrayToString(accountPath)}. All internal inputs must belong to the same account.`);
}
}
export 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.`);
}
}
export 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 pathStringToArray(accountPathOption);
}
/**
* Determines the script type for a single input from witness UTXO or redeem script.
*/
export function determineInputScriptType(psbt, inputIndex) {
const witnessUtxo = psbt.getInputWitnessUtxo(inputIndex);
if (witnessUtxo) {
return 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.
*/
export function analyzeInput(psbt, inputIndex, masterFp) {
const derivationResult = 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.
*/
export 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