UNPKG

@ledgerhq/hw-app-btc

Version:
167 lines • 7.57 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getScriptPubKeyFromNonWitnessUtxo = getScriptPubKeyFromNonWitnessUtxo; exports.getInputScriptPubKey = getInputScriptPubKey; exports.fixDerivationFingerprints = fixDerivationFingerprints; exports.fixExistingDerivationFingerprints = fixExistingDerivationFingerprints; exports.setElementBip32DerivationData = setElementBip32DerivationData; exports.populateMissingBip32Derivations = populateMissingBip32Derivations; const psbtv2_1 = require("@ledgerhq/psbtv2"); const bip32_1 = require("../bip32"); const accounttype_1 = require("../newops/accounttype"); const derivationAccessors_1 = require("./derivationAccessors"); const accountTypeResolver_1 = require("./accountTypeResolver"); /** * Extracts the scriptPubKey from a non-witness UTXO by parsing the previous transaction. */ function getScriptPubKeyFromNonWitnessUtxo(psbt, inputIndex) { const nonWitnessUtxo = psbt.getInputNonWitnessUtxo(inputIndex); if (!nonWitnessUtxo) { return undefined; } try { const outputIndex = psbt.getInputOutputIndex(inputIndex); const reader = new psbtv2_1.BufferReader(nonWitnessUtxo); reader.readSlice(4); const marker = reader.readUInt8(); if (marker === 0) { const flag = reader.readUInt8(); if (flag === 0) { return undefined; } } else { reader.offset -= 1; } const inputCount = reader.readVarInt(); for (let i = 0; i < inputCount; i++) { reader.readSlice(32); reader.readSlice(4); const scriptLen = reader.readVarInt(); reader.readSlice(scriptLen); reader.readSlice(4); } const outputCount = reader.readVarInt(); for (let i = 0; i < outputCount; i++) { reader.readSlice(8); const scriptLen = reader.readVarInt(); const scriptPubKey = reader.readSlice(scriptLen); if (i === outputIndex) { return scriptPubKey; } } return undefined; } catch { return undefined; } } /** * Extracts the scriptPubKey from an input's UTXO data. */ function getInputScriptPubKey(psbt, inputIndex) { const witnessUtxo = psbt.getInputWitnessUtxo(inputIndex); if (witnessUtxo) { return witnessUtxo.scriptPubKey; } return getScriptPubKeyFromNonWitnessUtxo(psbt, inputIndex); } /** * Generic method to fix BIP32 derivations with wrong master fingerprint. */ async function fixDerivationFingerprints(client, accessors, elementIndex, masterFp) { const keyDatas = accessors.getKeyDatas(elementIndex, accessors.bip32KeyType); for (const existingPubkey of keyDatas) { const derivationInfo = accessors.getBip32Derivation(elementIndex, existingPubkey); if (!derivationInfo) continue; if (derivationInfo.masterFingerprint.equals(masterFp)) continue; const path = derivationInfo.path; const xpub = await client.getExtendedPubkey(false, path); const derivedPubkey = (0, bip32_1.pubkeyFromXpub)(xpub); if (existingPubkey.equals(derivedPubkey)) { accessors.setBip32Derivation(elementIndex, existingPubkey, masterFp, path); return true; } } const tapKeyDatas = accessors.getKeyDatas(elementIndex, accessors.tapBip32KeyType); for (const existingPubkey of tapKeyDatas) { const derivationInfo = accessors.getTapBip32Derivation(elementIndex, existingPubkey); if (!derivationInfo || derivationInfo.masterFingerprint.equals(masterFp)) continue; const path = derivationInfo.path; const xpub = await client.getExtendedPubkey(false, path); const derivedPubkey = (0, bip32_1.pubkeyFromXpub)(xpub); const xonlyDerived = derivedPubkey.subarray(1); if (existingPubkey.equals(xonlyDerived)) { accessors.setTapBip32Derivation(elementIndex, existingPubkey, derivationInfo.hashes, masterFp, path); return true; } } return false; } async function fixExistingDerivationFingerprints(client, psbt, elementIndex, masterFp, elementType) { const accessors = (0, derivationAccessors_1.getDerivationAccessors)(psbt, elementType); return fixDerivationFingerprints(client, accessors, elementIndex, masterFp); } /** * Generic method to set BIP32 derivation data on an input or output. */ function setElementBip32DerivationData(accessors, elementIndex, pubkey, masterFp, path, accountType) { if (accountType instanceof accounttype_1.p2tr) { const xonlyPubkey = pubkey.subarray(1); accessors.setTapBip32Derivation(elementIndex, xonlyPubkey, [], masterFp, path); } else { accessors.setBip32Derivation(elementIndex, pubkey, masterFp, path); } } async function collectElementsNeedingLocalScan(client, psbt, count, masterFp, elementType, hasDerivation) { const indices = []; for (let i = 0; i < count; i++) { if (hasDerivation(i)) continue; const fixed = await fixExistingDerivationFingerprints(client, psbt, i, masterFp, elementType); if (!fixed) indices.push(i); } return indices; } function applyLookupToElements(psbt, indices, lookupMap, masterFp, elementType, getScriptPubKey) { const accessors = (0, derivationAccessors_1.getDerivationAccessors)(psbt, elementType); for (const elementIndex of indices) { const scriptPubKey = getScriptPubKey(elementIndex); if (!scriptPubKey) continue; const extracted = (0, psbtv2_1.extractHashFromScriptPubKey)(scriptPubKey); if (!extracted) continue; const result = lookupMap.get(extracted.hashHex); if (result) { const accountType = (0, accountTypeResolver_1.createAccountTypeFromScriptType)(extracted.scriptType, psbt, masterFp); setElementBip32DerivationData(accessors, elementIndex, result.pubkey, masterFp, result.path, accountType); } } } /** * Populates missing BIP32 derivations using the provided known-address map. * The map must be built by the caller from the wallet's known addresses (e.g. receive/change). */ async function populateMissingBip32Derivations(client, psbt, inputCount, masterFp, accountPath, knownAddressDerivations) { const accountType = (0, accountTypeResolver_1.determineAccountTypeFromPurpose)(accountPath, psbt, masterFp); if (!accountType) return; const outputCount = psbt.getGlobalOutputCount(); const [inputsNeedingLocalScan, outputsNeedingLocalScan] = await Promise.all([ collectElementsNeedingLocalScan(client, psbt, inputCount, masterFp, "input", i => (0, derivationAccessors_1.checkBip32Derivation)(psbt, i, masterFp).belongsToSigner), collectElementsNeedingLocalScan(client, psbt, outputCount, masterFp, "output", i => (0, derivationAccessors_1.checkOutputBip32Derivation)(psbt, i, masterFp)), ]); if (inputsNeedingLocalScan.length === 0 && outputsNeedingLocalScan.length === 0) return; if (knownAddressDerivations.size === 0) return; applyLookupToElements(psbt, inputsNeedingLocalScan, knownAddressDerivations, masterFp, "input", i => getInputScriptPubKey(psbt, i)); applyLookupToElements(psbt, outputsNeedingLocalScan, knownAddressDerivations, masterFp, "output", i => psbt.getOutputScript(i)); } //# sourceMappingURL=derivationPopulation.js.map