New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@backpacker69/hw-app-btc

Package Overview
Dependencies
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@backpacker69/hw-app-btc - npm Package Compare versions

Comparing version 6.24.1 to 6.24.2

15

lib-es/Btc.d.ts
/// <reference types="node" />
import type Transport from "@ledgerhq/hw-transport";
import BtcNew from "./BtcNew";
import BtcOld from "./BtcOld";
import type { CreateTransactionArg } from "./createTransaction";

@@ -45,3 +43,3 @@ import type { AddressFormat } from "./getWalletPublicKey";

*
* - bech32 format with 84' paths
* - bech32 format with 173' paths
*

@@ -86,6 +84,6 @@ * - cashaddr in case of Bitcoin Cash

* @param changePath is an optional BIP 32 path pointing to the path to the public key used to compute the change address
* @param outputScriptHex is the hexadecimal serialized outputs of the transaction to sign, including leading vararg voutCount
* @param outputScriptHex is the hexadecimal serialized outputs of the transaction to sign
* @param lockTime is the optional lockTime of the transaction to sign, or default (0)
* @param sigHashType is the hash type of the transaction to sign, or default (all)
* @param segwit is an optional boolean indicating wether to use segwit or not. This includes wrapped segwit.
* @param segwit is an optional boolean indicating wether to use segwit or not
* @param initialTimestamp is an optional timestamp of the function call to use for coins that necessitate timestamps only, (not the one that the tx will include)

@@ -95,3 +93,3 @@ * @param additionals list of additionnal options

* - "bech32" for spending native segwit outputs
* - "bech32m" for spending segwit v1+ outputs
* - "bech32m" for spending native segwit outputs
* - "abc" for bch

@@ -102,3 +100,3 @@ * - "gold" for btg

* @param expiryHeight is an optional Buffer for zec overwinter / sapling Txs
* @param useTrustedInputForSegwit trust inputs for segwit transactions. If app version >= 1.4.0 this should be true.
* @param useTrustedInputForSegwit trust inputs for segwit transactions
* @return the signed transaction ready to be broadcast

@@ -150,5 +148,4 @@ * @example

private inferCorrectImpl;
protected old(): BtcOld;
protected new(): BtcNew;
private old;
}
//# sourceMappingURL=Btc.d.ts.map

81

lib-es/Btc.js

@@ -37,3 +37,2 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

};
import { pathStringToArray } from "./bip32";
import BtcNew, { canSupportApp } from "./BtcNew";

@@ -94,3 +93,3 @@ import BtcOld from "./BtcOld";

*
* - bech32 format with 84' paths
* - bech32 format with 173' paths
*

@@ -104,3 +103,2 @@ * - cashaddr in case of Bitcoin Cash

Btc.prototype.getWalletPublicKey = function (path, opts) {
var _this = this;
var options;

@@ -119,35 +117,3 @@ if (arguments.length > 2 || typeof opts === "boolean") {

return this.getCorrectImpl().then(function (impl) {
/**
* Definition: A "normal path" is a prefix of a standard path where all
* the hardened steps of the standard path are included. For example, the
* paths m/44'/1'/17' and m/44'/1'/17'/1 are normal paths, but m/44'/1'
* is not. m/'199/1'/17'/0/1 is not a normal path either.
*
* There's a compatiblity issue between old and new app: When exporting
* the key of a non-normal path with verify=false, the new app would
* return an error, whereas the old app would return the key.
*
* See
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md#get_extended_pubkey
*
* If format bech32m is used, we'll not use old, because it doesn't
* support it.
*
* When to use new (given the app supports it)
* * format is bech32m or
* * path is normal or
* * verify is true
*
* Otherwise use old.
*/
if (impl instanceof BtcNew &&
options.format != "bech32m" &&
(!options.verify || options.verify == false) &&
!isPathNormal(path)) {
console.warn("WARNING: Using deprecated device protocol to get the public key because\n \n * a non-standard path is requested, and\n * verify flag is false\n \n The new protocol only allows export of non-standard paths if the \n verify flag is true. Standard paths are (currently):\n\n M/44'/(1|0)'/X'\n M/49'/(1|0)'/X'\n M/84'/(1|0)'/X'\n M/86'/(1|0)'/X'\n M/48'/(1|0)'/X'/Y'\n\n followed by \"\", \"(0|1)\", or \"(0|1)/b\", where a and b are \n non-hardened. For example, the following paths are standard\n \n M/48'/1'/99'/7'\n M/86'/1'/99'/0\n M/48'/0'/99'/7'/1/17\n\n The following paths are non-standard\n\n M/48'/0'/99' // Not deepest hardened path\n M/48'/0'/99'/7'/1/17/2 // Too many non-hardened derivation steps\n M/199'/0'/1'/0/88 // Not a known purpose 199\n M/86'/1'/99'/2 // Change path item must be 0 or 1\n\n This compatibility safeguard will be removed in the future.\n Please consider calling Btc.getWalletXpub() instead.");
return _this.old().getWalletPublicKey(path, options);
}
else {
return impl.getWalletPublicKey(path, options);
}
return impl.getWalletPublicKey(path, options);
});

@@ -177,6 +143,6 @@ };

* @param changePath is an optional BIP 32 path pointing to the path to the public key used to compute the change address
* @param outputScriptHex is the hexadecimal serialized outputs of the transaction to sign, including leading vararg voutCount
* @param outputScriptHex is the hexadecimal serialized outputs of the transaction to sign
* @param lockTime is the optional lockTime of the transaction to sign, or default (0)
* @param sigHashType is the hash type of the transaction to sign, or default (all)
* @param segwit is an optional boolean indicating wether to use segwit or not. This includes wrapped segwit.
* @param segwit is an optional boolean indicating wether to use segwit or not
* @param initialTimestamp is an optional timestamp of the function call to use for coins that necessitate timestamps only, (not the one that the tx will include)

@@ -186,3 +152,3 @@ * @param additionals list of additionnal options

* - "bech32" for spending native segwit outputs
* - "bech32m" for spending segwit v1+ outputs
* - "bech32m" for spending native segwit outputs
* - "abc" for bch

@@ -193,3 +159,3 @@ * - "gold" for btg

* @param expiryHeight is an optional Buffer for zec overwinter / sapling Txs
* @param useTrustedInputForSegwit trust inputs for segwit transactions. If app version >= 1.4.0 this should be true.
* @param useTrustedInputForSegwit trust inputs for segwit transactions
* @return the signed transaction ready to be broadcast

@@ -289,6 +255,6 @@ * @example

if (!canUseNewImplementation) {
return [2 /*return*/, this.old()];
return [2 /*return*/, new BtcOld(this.transport)];
}
else {
return [2 /*return*/, this["new"]()];
return [2 /*return*/, new BtcNew(new AppClient(this.transport))];
}

@@ -303,36 +269,5 @@ return [2 /*return*/];

};
Btc.prototype["new"] = function () {
return new BtcNew(new AppClient(this.transport));
};
return Btc;
}());
export default Btc;
function isPathNormal(path) {
//path is not deepest hardened node of a standard path or deeper, use BtcOld
var h = 0x80000000;
var pathElems = pathStringToArray(path);
var hard = function (n) { return n >= h; };
var soft = function (n) { return !n || n < h; };
var change = function (n) { return !n || n == 0 || n == 1; };
if (pathElems.length >= 3 &&
pathElems.length <= 5 &&
[44 + h, 49 + h, 84 + h, 86 + h].some(function (v) { return v == pathElems[0]; }) &&
[0 + h, 1 + h].some(function (v) { return v == pathElems[1]; }) &&
hard(pathElems[2]) &&
change(pathElems[3]) &&
soft(pathElems[4])) {
return true;
}
if (pathElems.length >= 4 &&
pathElems.length <= 6 &&
48 + h == pathElems[0] &&
[0 + h, 1 + h].some(function (v) { return v == pathElems[1]; }) &&
hard(pathElems[2]) &&
hard(pathElems[3]) &&
change(pathElems[4]) &&
soft(pathElems[5])) {
return true;
}
return false;
}
//# sourceMappingURL=Btc.js.map
import type { CreateTransactionArg } from "./createTransaction";
import { AppAndVersion } from "./getAppAndVersion";
import type { AddressFormat } from "./getWalletPublicKey";
import { AppClient as Client } from "./newops/appClient";
import { AppAndVersion } from "./getAppAndVersion";
export declare function canSupportApp(appAndVersion: AppAndVersion): boolean;
/**
* This class implements the same interface as BtcOld (formerly
* named Btc), but interacts with Bitcoin hardware app version 2+
* which uses a totally new APDU protocol. This new
* protocol is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md
*
* Since the interface must remain compatible with BtcOld, the methods
* of this class are quite clunky, because it needs to adapt legacy
* input data into the PSBT process. In the future, a new interface should
* be developed that exposes PSBT to the outer world, which would render
* a much cleaner implementation.
*/
export default class BtcNew {
private client;
constructor(client: Client);
/**
* This is a new method that allow users to get an xpub at a standard path.
* Standard paths are described at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md#description
*
* This boils down to paths (N=0 for Bitcoin, N=1 for Testnet):
* M/44'/N'/x'/**
* M/48'/N'/x'/y'/**
* M/49'/N'/x'/**
* M/84'/N'/x'/**
* M/86'/N'/x'/**
*
* The method was added because of added security in the hardware app v2+. The
* new hardware app will allow export of any xpub up to and including the
* deepest hardened key of standard derivation paths, whereas the old app
* would allow export of any key.
*
* This caused an issue for callers of this class, who only had
* getWalletPublicKey() to call which means they have to constuct xpub
* themselves:
*
* Suppose a user of this class wants to create an account xpub on a standard
* path, M/44'/0'/Z'. The user must get the parent key fingerprint (see BIP32)
* by requesting the parent key M/44'/0'. The new app won't allow that, because
* it only allows exporting deepest level hardened path. So the options are to
* allow requesting M/44'/0' from the app, or to add a new function
* "getWalletXpub".
*
* We opted for adding a new function, which can greatly simplify client code.
*/
getWalletXpub({ path, xpubVersion, }: {

@@ -56,9 +13,2 @@ path: string;

}): Promise<string>;
/**
* This method returns a public key, a bitcoin address, and and a chaincode
* for a specific derivation path.
*
* Limitation: If the path is not a leaf node of a standard path, the address
* will be the empty string "", see this.getWalletAddress() for details.
*/
getWalletPublicKey(path: string, opts?: {

@@ -81,3 +31,3 @@ verify?: boolean;

* get it from the device to save development time. However, it shouldn't take
* too much time to implement local address generation.
* more than a few hours to implement local address generation.
*

@@ -90,36 +40,39 @@ * Moreover, if the path is not for a leaf, ie accountPath+/X/Y, there is no

/**
* Build and sign a transaction. See Btc.createPaymentTransactionNew for
* details on how to use this method.
* To sign a transaction involving standard (P2PKH) inputs, call createTransaction with the following parameters
* @param inputs is an array of [ transaction, output_index, optional redeem script, optional sequence ] where
*
* This method will convert the legacy arguments, CreateTransactionArg, into
* a psbt which is finally signed and finalized, and the extracted fully signed
* transaction is returned.
* * transaction is the previously computed transaction object for this UTXO
* * output_index is the output in the transaction used as input for this UTXO (counting from 0)
* * redeem script is the optional redeem script to use when consuming a Segregated Witness input
* * sequence is the sequence number to use for this input (when using RBF), or non present
* @param associatedKeysets is an array of BIP 32 paths pointing to the path to the private key used for each UTXO
* @param changePath is an optional BIP 32 path pointing to the path to the public key used to compute the change address
* @param outputScriptHex is the hexadecimal serialized outputs of the transaction to sign, including leading vararg voutCount
* @param lockTime is the optional lockTime of the transaction to sign, or default (0)
* @param sigHashType is the hash type of the transaction to sign, or default (all)
* @param segwit is an optional boolean indicating wether to use segwit or not. This includes wrapped segwit.
* @param initialTimestamp is an optional timestamp of the function call to use for coins that necessitate timestamps only, (not the one that the tx will include)
* @param additionals list of additionnal options
*
* - "bech32" for spending native segwit outputs
* - "bech32m" for spending segwit v1+ outptus
* - "abc" for bch
* - "gold" for btg
* - "bipxxx" for using BIPxxx
* - "sapling" to indicate a zec transaction is supporting sapling (to be set over block 419200)
* @param expiryHeight is an optional Buffer for zec overwinter / sapling Txs
* @param useTrustedInputForSegwit trust inputs for segwit transactions. If app version >= 1.4.0 this should be true.
* @return the signed transaction ready to be broadcast
* @example
btc.createTransaction({
inputs: [ [tx1, 1] ],
associatedKeysets: ["0'/0/0"],
outputScriptHex: "01905f0100000000001976a91472a5d75c8d2d0565b656a5232703b167d50d5a2b88ac"
}).then(res => ...);
*/
createPaymentTransactionNew(arg: CreateTransactionArg): Promise<string>;
/**
* Calculates an output script along with public key and possible redeemScript
* from a path and accountType. The accountPath must be a prefix of path.
*
* @returns an object with output script (property "script"), redeemScript (if
* wrapped p2wpkh), and pubkey at provided path. The values of these three
* properties depend on the accountType used.
*/
private outputScriptAt;
/**
* Adds relevant data about an input to the psbt. This includes sequence,
* previous txid, output index, spent UTXO, redeem script for wrapped p2wpkh,
* public key and its derivation path.
*/
private setInput;
/**
* This implements the "Signer" role of the BIP370 transaction signing
* process.
*
* It ssks the hardware device to sign the a psbt using the specified wallet
* policy. This method assumes BIP32 derived keys are used for all inputs, see
* comment in-line. The signatures returned from the hardware device is added
* to the appropriate input fields of the PSBT.
*/
private signPsbt;
}
//# sourceMappingURL=BtcNew.d.ts.map

@@ -0,1 +1,12 @@

var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

@@ -38,8 +49,8 @@ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }

import { crypto } from "bitcoinjs-lib";
import { pointCompress, pointAddScalar } from "tiny-secp256k1";
import semver from "semver";
import { pointCompress } from "tiny-secp256k1";
import { getXpubComponents, hardenedPathOf, pathArrayToString, pathStringToArray, pubkeyFromXpub, } from "./bip32";
import { BufferReader } from "./buffertools";
import { p2pkh, p2tr, p2wpkh, p2wpkhWrapped, } from "./newops/accounttype";
import { createKey, WalletPolicy, } from "./newops/policy";
import { BufferReader, BufferWriter } from "./buffertools";
import { hashPublicKey } from "./hashPublicKey";
import { createKey, WalletPolicy } from "./newops/policy";
import { extract } from "./newops/psbtExtractor";

@@ -49,2 +60,3 @@ import { finalize } from "./newops/psbtFinalizer";

import { serializeTransaction } from "./serializeTransaction";
import { HASH_SIZE, OP_CHECKSIG, OP_DUP, OP_EQUAL, OP_EQUALVERIFY, OP_HASH160, } from "./constants";
var newSupportedApps = ["Bitcoin", "Bitcoin Test"];

@@ -55,15 +67,2 @@ export function canSupportApp(appAndVersion) {

}
/**
* This class implements the same interface as BtcOld (formerly
* named Btc), but interacts with Bitcoin hardware app version 2+
* which uses a totally new APDU protocol. This new
* protocol is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md
*
* Since the interface must remain compatible with BtcOld, the methods
* of this class are quite clunky, because it needs to adapt legacy
* input data into the PSBT process. In the future, a new interface should
* be developed that exposes PSBT to the outer world, which would render
* a much cleaner implementation.
*/
var BtcNew = /** @class */ (function () {

@@ -73,32 +72,2 @@ function BtcNew(client) {

}
/**
* This is a new method that allow users to get an xpub at a standard path.
* Standard paths are described at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md#description
*
* This boils down to paths (N=0 for Bitcoin, N=1 for Testnet):
* M/44'/N'/x'/**
* M/48'/N'/x'/y'/**
* M/49'/N'/x'/**
* M/84'/N'/x'/**
* M/86'/N'/x'/**
*
* The method was added because of added security in the hardware app v2+. The
* new hardware app will allow export of any xpub up to and including the
* deepest hardened key of standard derivation paths, whereas the old app
* would allow export of any key.
*
* This caused an issue for callers of this class, who only had
* getWalletPublicKey() to call which means they have to constuct xpub
* themselves:
*
* Suppose a user of this class wants to create an account xpub on a standard
* path, M/44'/0'/Z'. The user must get the parent key fingerprint (see BIP32)
* by requesting the parent key M/44'/0'. The new app won't allow that, because
* it only allows exporting deepest level hardened path. So the options are to
* allow requesting M/44'/0' from the app, or to add a new function
* "getWalletXpub".
*
* We opted for adding a new function, which can greatly simplify client code.
*/
BtcNew.prototype.getWalletXpub = function (_a) {

@@ -112,3 +81,3 @@ var path = _a.path, xpubVersion = _a.xpubVersion;

pathElements = pathStringToArray(path);
return [4 /*yield*/, this.client.getExtendedPubkey(false, pathElements)];
return [4 /*yield*/, this.client.getPubkey(false, pathElements)];
case 1:

@@ -125,9 +94,2 @@ xpub = _b.sent();

};
/**
* This method returns a public key, a bitcoin address, and and a chaincode
* for a specific derivation path.
*
* Limitation: If the path is not a leaf node of a standard path, the address
* will be the empty string "", see this.getWalletAddress() for details.
*/
BtcNew.prototype.getWalletPublicKey = function (path, opts) {

@@ -141,7 +103,7 @@ var _a, _b;

pathElements = pathStringToArray(path);
return [4 /*yield*/, this.client.getExtendedPubkey(false, pathElements)];
return [4 /*yield*/, this.client.getPubkey(false, pathElements)];
case 1:
xpub = _c.sent();
display = (_a = opts === null || opts === void 0 ? void 0 : opts.verify) !== null && _a !== void 0 ? _a : false;
return [4 /*yield*/, this.getWalletAddress(pathElements, descrTemplFrom((_b = opts === null || opts === void 0 ? void 0 : opts.format) !== null && _b !== void 0 ? _b : "legacy"), display)];
return [4 /*yield*/, this.getWalletAddress(pathElements, accountTypeFrom((_b = opts === null || opts === void 0 ? void 0 : opts.format) !== null && _b !== void 0 ? _b : "legacy"), display)];
case 2:

@@ -169,3 +131,3 @@ address = _c.sent();

* get it from the device to save development time. However, it shouldn't take
* too much time to implement local address generation.
* more than a few hours to implement local address generation.
*

@@ -176,3 +138,3 @@ * Moreover, if the path is not for a leaf, ie accountPath+/X/Y, there is no

*/
BtcNew.prototype.getWalletAddress = function (pathElements, descrTempl, display) {
BtcNew.prototype.getWalletAddress = function (pathElements, accountType, display) {
return __awaiter(this, void 0, void 0, function () {

@@ -187,3 +149,3 @@ var accountPath, accountXpub, masterFingerprint, policy, changeAndIndex;

}
return [4 /*yield*/, this.client.getExtendedPubkey(false, accountPath)];
return [4 /*yield*/, this.client.getPubkey(false, accountPath)];
case 1:

@@ -194,3 +156,3 @@ accountXpub = _a.sent();

masterFingerprint = _a.sent();
policy = new WalletPolicy(descrTempl, createKey(masterFingerprint, accountPath, accountXpub));
policy = new WalletPolicy(accountType, createKey(masterFingerprint, accountPath, accountXpub));
changeAndIndex = pathElements.slice(-2, pathElements.length);

@@ -203,41 +165,55 @@ return [2 /*return*/, this.client.getWalletAddress(policy, Buffer.alloc(32, 0), changeAndIndex[0], changeAndIndex[1], display)];

/**
* Build and sign a transaction. See Btc.createPaymentTransactionNew for
* details on how to use this method.
* To sign a transaction involving standard (P2PKH) inputs, call createTransaction with the following parameters
* @param inputs is an array of [ transaction, output_index, optional redeem script, optional sequence ] where
*
* This method will convert the legacy arguments, CreateTransactionArg, into
* a psbt which is finally signed and finalized, and the extracted fully signed
* transaction is returned.
* * transaction is the previously computed transaction object for this UTXO
* * output_index is the output in the transaction used as input for this UTXO (counting from 0)
* * redeem script is the optional redeem script to use when consuming a Segregated Witness input
* * sequence is the sequence number to use for this input (when using RBF), or non present
* @param associatedKeysets is an array of BIP 32 paths pointing to the path to the private key used for each UTXO
* @param changePath is an optional BIP 32 path pointing to the path to the public key used to compute the change address
* @param outputScriptHex is the hexadecimal serialized outputs of the transaction to sign, including leading vararg voutCount
* @param lockTime is the optional lockTime of the transaction to sign, or default (0)
* @param sigHashType is the hash type of the transaction to sign, or default (all)
* @param segwit is an optional boolean indicating wether to use segwit or not. This includes wrapped segwit.
* @param initialTimestamp is an optional timestamp of the function call to use for coins that necessitate timestamps only, (not the one that the tx will include)
* @param additionals list of additionnal options
*
* - "bech32" for spending native segwit outputs
* - "bech32m" for spending segwit v1+ outptus
* - "abc" for bch
* - "gold" for btg
* - "bipxxx" for using BIPxxx
* - "sapling" to indicate a zec transaction is supporting sapling (to be set over block 419200)
* @param expiryHeight is an optional Buffer for zec overwinter / sapling Txs
* @param useTrustedInputForSegwit trust inputs for segwit transactions. If app version >= 1.4.0 this should be true.
* @return the signed transaction ready to be broadcast
* @example
btc.createTransaction({
inputs: [ [tx1, 1] ],
associatedKeysets: ["0'/0/0"],
outputScriptHex: "01905f0100000000001976a91472a5d75c8d2d0565b656a5232703b167d50d5a2b88ac"
}).then(res => ...);
*/
BtcNew.prototype.createPaymentTransactionNew = function (arg) {
return __awaiter(this, void 0, void 0, function () {
var inputCount, psbt, masterFp, accountType, notifyCount, progress, accountXpub, accountPath, i, pathElems, outputsConcat, outputsBufferReader, outputCount, changeData, changeFound, i, amount, outputScript, isChange, changePath, pubkey, key, p, firstSigned, progressCallback, serializedTx;
var psbt, accountType, masterFp, accountXpub, accountPath, i, pathElems, outputsConcat, outputsBufferReader, outputCount, changeData, changeFound, i, amount, outputScript, isChange, changePath, pubkey, key, p;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
inputCount = arg.inputs.length;
if (inputCount == 0) {
if (arg.inputs.length == 0) {
throw Error("No inputs");
}
psbt = new PsbtV2();
return [4 /*yield*/, this.client.getMasterFingerprint()];
case 1:
masterFp = _a.sent();
accountType = accountTypeFromArg(arg, psbt, masterFp);
if (arg.lockTime != undefined) {
// The signer will assume locktime 0 if unset
accountType = accountTypeFromArg(arg);
psbt.setGlobalTxVersion(2);
if (arg.lockTime) {
psbt.setGlobalFallbackLocktime(arg.lockTime);
}
psbt.setGlobalInputCount(inputCount);
psbt.setGlobalInputCount(arg.inputs.length);
psbt.setGlobalPsbtVersion(2);
psbt.setGlobalTxVersion(2);
notifyCount = 0;
progress = function () {
if (!arg.onDeviceStreaming)
return;
arg.onDeviceStreaming({
total: 2 * inputCount,
index: notifyCount,
progress: ++notifyCount / (2 * inputCount)
});
};
return [4 /*yield*/, this.client.getMasterFingerprint()];
case 1:
masterFp = _a.sent();
accountXpub = "";

@@ -248,4 +224,3 @@ accountPath = [];

case 2:
if (!(i < inputCount)) return [3 /*break*/, 7];
progress();
if (!(i < arg.inputs.length)) return [3 /*break*/, 7];
pathElems = pathStringToArray(arg.associatedKeysets[i]);

@@ -256,7 +231,7 @@ if (!(accountXpub == "")) return [3 /*break*/, 4];

accountPath = pathElems.slice(0, -2);
return [4 /*yield*/, this.client.getExtendedPubkey(false, accountPath)];
return [4 /*yield*/, this.client.getPubkey(false, accountPath)];
case 3:
accountXpub = _a.sent();
_a.label = 4;
case 4: return [4 /*yield*/, this.setInput(psbt, i, arg.inputs[i], pathElems, accountType, masterFp, arg.sigHashType)];
case 4: return [4 /*yield*/, this.setInput(psbt, i, arg.inputs[i], pathElems, accountType, masterFp)];
case 5:

@@ -272,6 +247,6 @@ _a.sent();

outputCount = outputsBufferReader.readVarInt();
psbt.setGlobalOutputCount(outputCount);
return [4 /*yield*/, this.outputScriptAt(accountPath, accountType, arg.changePath)];
case 8:
changeData = _a.sent();
psbt.setGlobalOutputCount(outputCount);
changeFound = !changeData;

@@ -281,5 +256,3 @@ for (i = 0; i < outputCount; i++) {

outputScript = outputsBufferReader.readVarSlice();
psbt.setOutputAmount(i, amount);
psbt.setOutputScript(i, outputScript);
isChange = changeData && outputScript.equals(changeData === null || changeData === void 0 ? void 0 : changeData.cond.scriptPubKey);
isChange = changeData && outputScript.equals(changeData === null || changeData === void 0 ? void 0 : changeData.script);
if (isChange) {

@@ -289,29 +262,28 @@ changeFound = true;

pubkey = changeData.pubkey;
accountType.setOwnOutput(i, changeData.cond, [pubkey], [changePath]);
if (accountType == AccountType.p2pkh) {
psbt.setOutputBip32Derivation(i, pubkey, masterFp, changePath);
}
else if (accountType == AccountType.p2wpkh) {
psbt.setOutputBip32Derivation(i, pubkey, masterFp, changePath);
}
else if (accountType == AccountType.p2wpkhWrapped) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
psbt.setOutputRedeemScript(i, changeData.redeemScript);
psbt.setOutputBip32Derivation(i, pubkey, masterFp, changePath);
}
else if (accountType == AccountType.p2tr) {
psbt.setOutputTapBip32Derivation(i, pubkey, [], masterFp, changePath);
}
}
psbt.setOutputAmount(i, amount);
psbt.setOutputScript(i, outputScript);
}
if (!changeFound) {
throw new Error("Change script not found among outputs! " +
(changeData === null || changeData === void 0 ? void 0 : changeData.cond.scriptPubKey.toString("hex")));
(changeData === null || changeData === void 0 ? void 0 : changeData.script.toString("hex")));
}
key = createKey(masterFp, accountPath, accountXpub);
p = new WalletPolicy(accountType.getDescriptorTemplate(), key);
// This is cheating, because it's not actually requested on the
// device yet, but it will be, soonish.
if (arg.onDeviceSignatureRequested)
arg.onDeviceSignatureRequested();
firstSigned = false;
progressCallback = function () {
if (!firstSigned) {
firstSigned = true;
arg.onDeviceSignatureGranted && arg.onDeviceSignatureGranted();
}
progress();
};
return [4 /*yield*/, this.signPsbt(psbt, p, progressCallback)];
case 9:
_a.sent();
finalize(psbt);
serializedTx = extract(psbt);
return [2 /*return*/, serializedTx.toString("hex")];
p = new WalletPolicy(accountType, key);
return [4 /*yield*/, this.signPsbt(psbt, p)];
case 9: return [2 /*return*/, _a.sent()];
}

@@ -321,13 +293,5 @@ });

};
/**
* Calculates an output script along with public key and possible redeemScript
* from a path and accountType. The accountPath must be a prefix of path.
*
* @returns an object with output script (property "script"), redeemScript (if
* wrapped p2wpkh), and pubkey at provided path. The values of these three
* properties depend on the accountType used.
*/
BtcNew.prototype.outputScriptAt = function (accountPath, accountType, path) {
return __awaiter(this, void 0, void 0, function () {
var pathElems, i, xpub, pubkey, cond;
var pathElems, i, xpub, pubkey, script;
return __generator(this, function (_a) {

@@ -346,8 +310,11 @@ switch (_a.label) {

}
return [4 /*yield*/, this.client.getExtendedPubkey(false, pathElems)];
return [4 /*yield*/, this.client.getPubkey(false, pathElems)];
case 1:
xpub = _a.sent();
pubkey = pubkeyFromXpub(xpub);
cond = accountType.spendingCondition([pubkey]);
return [2 /*return*/, { cond: cond, pubkey: pubkey }];
if (accountType == AccountType.p2tr) {
pubkey = pubkey.slice(1);
}
script = outputScriptOf(pubkey, accountType);
return [2 /*return*/, __assign(__assign({}, script), { pubkey: pubkey })];
}

@@ -357,10 +324,5 @@ });

};
/**
* Adds relevant data about an input to the psbt. This includes sequence,
* previous txid, output index, spent UTXO, redeem script for wrapped p2wpkh,
* public key and its derivation path.
*/
BtcNew.prototype.setInput = function (psbt, i, input, pathElements, accountType, masterFP, sigHashType) {
BtcNew.prototype.setInput = function (psbt, i, input, pathElements, accountType, masterFP) {
return __awaiter(this, void 0, void 0, function () {
var inputTx, spentOutputIndex, redeemScript, sequence, inputTxBuffer, inputTxid, xpubBase58, pubkey, spentTxOutput, spendCondition, spentOutput;
var inputTx, spentOutputIndex, redeemScript, sequence, inputTxBuffer, inputTxid, xpubBase58, pubkey, spentOutput, expectedRedeemScript, xonly;
return __generator(this, function (_a) {

@@ -371,13 +333,10 @@ switch (_a.label) {

spentOutputIndex = input[1];
redeemScript = input[2] ? Buffer.from(input[2], "hex") : undefined;
redeemScript = input[2];
sequence = input[3];
if (sequence != undefined) {
if (sequence) {
psbt.setInputSequence(i, sequence);
}
if (sigHashType != undefined) {
psbt.setInputSighashType(i, sigHashType);
}
inputTxBuffer = serializeTransaction(inputTx, true);
inputTxid = crypto.hash256(inputTxBuffer);
return [4 /*yield*/, this.client.getExtendedPubkey(false, pathElements)];
return [4 /*yield*/, this.client.getPubkey(false, pathElements)];
case 1:

@@ -388,9 +347,30 @@ xpubBase58 = _a.sent();

throw Error("Missing outputs array in transaction to sign");
spentTxOutput = inputTx.outputs[spentOutputIndex];
spendCondition = {
scriptPubKey: spentTxOutput.script,
redeemScript: redeemScript
};
spentOutput = { cond: spendCondition, amount: spentTxOutput.amount };
accountType.setInput(i, inputTxBuffer, spentOutput, [pubkey], [pathElements]);
spentOutput = inputTx.outputs[spentOutputIndex];
if (accountType == AccountType.p2pkh) {
psbt.setInputNonWitnessUtxo(i, inputTxBuffer);
psbt.setInputBip32Derivation(i, pubkey, masterFP, pathElements);
}
else if (accountType == AccountType.p2wpkh) {
psbt.setInputNonWitnessUtxo(i, inputTxBuffer);
psbt.setInputBip32Derivation(i, pubkey, masterFP, pathElements);
psbt.setInputWitnessUtxo(i, spentOutput.amount, spentOutput.script);
}
else if (accountType == AccountType.p2wpkhWrapped) {
psbt.setInputNonWitnessUtxo(i, inputTxBuffer);
psbt.setInputBip32Derivation(i, pubkey, masterFP, pathElements);
if (!redeemScript) {
throw new Error("Missing redeemScript for p2wpkhWrapped input");
}
expectedRedeemScript = createRedeemScript(pubkey);
if (redeemScript != expectedRedeemScript.toString("hex")) {
throw new Error("Unexpected redeemScript");
}
psbt.setInputRedeemScript(i, expectedRedeemScript);
psbt.setInputWitnessUtxo(i, spentOutput.amount, spentOutput.script);
}
else if (accountType == AccountType.p2tr) {
xonly = pubkey.slice(1);
psbt.setInputTapBip32Derivation(i, xonly, [], masterFP, pathElements);
psbt.setInputWitnessUtxo(i, spentOutput.amount, spentOutput.script);
}
psbt.setInputPreviousTxId(i, inputTxid);

@@ -403,22 +383,13 @@ psbt.setInputOutputIndex(i, spentOutputIndex);

};
/**
* This implements the "Signer" role of the BIP370 transaction signing
* process.
*
* It ssks the hardware device to sign the a psbt using the specified wallet
* policy. This method assumes BIP32 derived keys are used for all inputs, see
* comment in-line. The signatures returned from the hardware device is added
* to the appropriate input fields of the PSBT.
*/
BtcNew.prototype.signPsbt = function (psbt, walletPolicy, progressCallback) {
BtcNew.prototype.signPsbt = function (psbt, walletPolicy) {
return __awaiter(this, void 0, void 0, function () {
var sigs;
var sigs, serializedTx;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.client.signPsbt(psbt, walletPolicy, Buffer.alloc(32, 0), progressCallback)];
case 0: return [4 /*yield*/, this.client.signPsbt(psbt, walletPolicy, Buffer.alloc(32, 0))];
case 1:
sigs = _a.sent();
sigs.forEach(function (v, k) {
// Note: Looking at BIP32 derivation does not work in the generic case,
// since some inputs might not have a BIP32-derived pubkey.
// Note: Looking at BIP32 derivation does not work in the generic case.
// some inputs might not have a BIP32-derived pubkey.
var pubkeys = psbt.getInputKeyDatas(k, psbtIn.BIP32_DERIVATION);

@@ -439,3 +410,5 @@ var pubkey;

});
return [2 /*return*/];
finalize(psbt);
serializedTx = extract(psbt);
return [2 /*return*/, serializedTx.toString("hex")];
}

@@ -448,22 +421,88 @@ });

export default BtcNew;
function descrTemplFrom(addressFormat) {
var AccountType;
(function (AccountType) {
AccountType["p2pkh"] = "pkh(@0)";
AccountType["p2wpkh"] = "wpkh(@0)";
AccountType["p2wpkhWrapped"] = "sh(wpkh(@0))";
AccountType["p2tr"] = "tr(@0)";
})(AccountType || (AccountType = {}));
function createRedeemScript(pubkey) {
var pubkeyHash = hashPublicKey(pubkey);
return Buffer.concat([Buffer.from("0014", "hex"), pubkeyHash]);
}
function outputScriptOf(pubkey, accountType) {
var buf = new BufferWriter();
var pubkeyHash = hashPublicKey(pubkey);
var redeemScript;
if (accountType == AccountType.p2pkh) {
buf.writeSlice(Buffer.of(OP_DUP, OP_HASH160, HASH_SIZE));
buf.writeSlice(pubkeyHash);
buf.writeSlice(Buffer.of(OP_EQUALVERIFY, OP_CHECKSIG));
}
else if (accountType == AccountType.p2wpkhWrapped) {
redeemScript = createRedeemScript(pubkey);
var scriptHash = hashPublicKey(redeemScript);
buf.writeSlice(Buffer.of(OP_HASH160, HASH_SIZE));
buf.writeSlice(scriptHash);
buf.writeUInt8(OP_EQUAL);
}
else if (accountType == AccountType.p2wpkh) {
buf.writeSlice(Buffer.of(0, HASH_SIZE));
buf.writeSlice(pubkeyHash);
}
else if (accountType == AccountType.p2tr) {
console.log("Internal key: " + pubkey.toString("hex"));
var outputKey = getTaprootOutputKey(pubkey);
buf.writeSlice(Buffer.of(0x51, 32)); // push1, pubkeylen
buf.writeSlice(outputKey);
}
return { script: buf.buffer(), redeemScript: redeemScript };
}
function accountTypeFrom(addressFormat) {
if (addressFormat == "legacy")
return "pkh(@0)";
return AccountType.p2pkh;
if (addressFormat == "p2sh")
return "sh(wpkh(@0))";
return AccountType.p2wpkhWrapped;
if (addressFormat == "bech32")
return "wpkh(@0)";
return AccountType.p2wpkh;
if (addressFormat == "bech32m")
return "tr(@0)";
return AccountType.p2tr;
throw new Error("Unsupported address format " + addressFormat);
}
function accountTypeFromArg(arg, psbt, masterFp) {
function accountTypeFromArg(arg) {
if (arg.additionals.includes("bech32m"))
return new p2tr(psbt, masterFp);
return AccountType.p2tr;
if (arg.additionals.includes("bech32"))
return new p2wpkh(psbt, masterFp);
return AccountType.p2wpkh;
if (arg.segwit)
return new p2wpkhWrapped(psbt, masterFp);
return new p2pkh(psbt, masterFp);
return AccountType.p2wpkhWrapped;
return AccountType.p2pkh;
}
/*
The following two functions are copied from wallet-btc and adapte.
They should be moved to a library to avoid code reuse.
*/
function hashTapTweak(x) {
// hash_tag(x) = SHA256(SHA256(tag) || SHA256(tag) || x), see BIP340
// See https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#specification
var h = crypto.sha256(Buffer.from("TapTweak", "utf-8"));
return crypto.sha256(Buffer.concat([h, h, x]));
}
function getTaprootOutputKey(internalPubkey) {
if (internalPubkey.length != 32) {
throw new Error("Expected 32 byte pubkey. Got " + internalPubkey.length);
}
// A BIP32 derived key can be converted to a schnorr pubkey by dropping
// the first byte, which represent the oddness/evenness. In schnorr all
// pubkeys are even.
// https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#public-key-conversion
var evenEcdsaPubkey = Buffer.concat([Buffer.of(0x02), internalPubkey]);
var tweak = hashTapTweak(internalPubkey);
// Q = P + int(hash_TapTweak(bytes(P)))G
var outputEcdsaKey = Buffer.from(pointAddScalar(evenEcdsaPubkey, tweak));
// Convert to schnorr.
var outputSchnorrKey = outputEcdsaKey.slice(1);
// Create address
return outputSchnorrKey;
}
//# sourceMappingURL=BtcNew.js.map
/// <reference types="node" />
export declare function unsafeTo64bitLE(n: number): Buffer;
export declare function unsafeFrom64bitLE(byteArray: Buffer): number;
export declare class BufferWriter {

@@ -10,3 +8,3 @@ private bufs;

writeUInt32(i: number): void;
writeUInt64(i: number): void;
writeUInt64(i: bigint): void;
writeVarInt(i: number): void;

@@ -25,3 +23,3 @@ writeSlice(slice: Buffer): void;

readUInt32(): number;
readUInt64(): number;
readUInt64(): bigint;
readVarInt(): number;

@@ -28,0 +26,0 @@ readSlice(n: number): Buffer;

import varuint from "varuint-bitcoin";
export function unsafeTo64bitLE(n) {
// we want to represent the input as a 8-bytes array
if (n > Number.MAX_SAFE_INTEGER) {
throw new Error("Can't convert numbers > MAX_SAFE_INT");
}
var byteArray = Buffer.alloc(8, 0);
for (var index = 0; index < byteArray.length; index++) {
var byte = n & 0xff;
byteArray[index] = byte;
n = (n - byte) / 256;
}
return byteArray;
}
export function unsafeFrom64bitLE(byteArray) {
var value = 0;
if (byteArray.length != 8) {
throw new Error("Expected Bufffer of lenght 8");
}
if (byteArray[7] != 0) {
throw new Error("Can't encode numbers > MAX_SAFE_INT");
}
if (byteArray[6] > 0x1f) {
throw new Error("Can't encode numbers > MAX_SAFE_INT");
}
for (var i = byteArray.length - 1; i >= 0; i--) {
value = value * 256 + byteArray[i];
}
return value;
}
var BufferWriter = /** @class */ (function () {

@@ -50,4 +21,3 @@ function BufferWriter() {

BufferWriter.prototype.writeUInt64 = function (i) {
var bytes = unsafeTo64bitLE(i);
this.writeSlice(bytes);
this.write(8, function (b) { return b.writeBigUInt64LE(i, 0); });
};

@@ -95,5 +65,5 @@ BufferWriter.prototype.writeVarInt = function (i) {

BufferReader.prototype.readUInt64 = function () {
var buf = this.readSlice(8);
var n = unsafeFrom64bitLE(buf);
return n;
var result = this.buffer.readBigUInt64LE(this.offset);
this.offset += 8;
return result;
};

@@ -100,0 +70,0 @@ BufferReader.prototype.readVarInt = function () {

@@ -5,6 +5,2 @@ /// <reference types="node" />

import { WalletPolicy } from "./policy";
/**
* This class encapsulates the APDU protocol documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md
*/
export declare class AppClient {

@@ -14,7 +10,7 @@ transport: Transport;

private makeRequest;
getExtendedPubkey(display: boolean, pathElements: number[]): Promise<string>;
getPubkey(display: boolean, pathElements: number[]): Promise<string>;
getWalletAddress(walletPolicy: WalletPolicy, walletHMAC: Buffer | null, change: number, addressIndex: number, display: boolean): Promise<string>;
signPsbt(psbt: PsbtV2, walletPolicy: WalletPolicy, walletHMAC: Buffer | null, progressCallback: () => void): Promise<Map<number, Buffer>>;
signPsbt(psbt: PsbtV2, walletPolicy: WalletPolicy, walletHMAC: Buffer | null): Promise<Map<number, Buffer>>;
getMasterFingerprint(): Promise<Buffer>;
}
//# sourceMappingURL=appClient.d.ts.map

@@ -68,6 +68,2 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

})(FrameworkIns || (FrameworkIns = {}));
/**
* This class encapsulates the APDU protocol documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md
*/
var AppClient = /** @class */ (function () {

@@ -105,3 +101,3 @@ function AppClient(transport) {

};
AppClient.prototype.getExtendedPubkey = function (display, pathElements) {
AppClient.prototype.getPubkey = function (display, pathElements) {
return __awaiter(this, void 0, void 0, function () {

@@ -116,3 +112,3 @@ var response;

return [4 /*yield*/, this.makeRequest(BitcoinIns.GET_PUBKEY, Buffer.concat([
Buffer.from(display ? [1] : [0]),
Buffer.of(display ? 1 : 0),
pathElementsToBuffer(pathElements),

@@ -140,3 +136,3 @@ ]))];

}
clientInterpreter = new ClientCommandInterpreter(function () { });
clientInterpreter = new ClientCommandInterpreter();
clientInterpreter.addKnownList(walletPolicy.keys.map(function (k) { return Buffer.from(k, "ascii"); }));

@@ -147,6 +143,6 @@ clientInterpreter.addKnownPreimage(walletPolicy.serialize());

return [4 /*yield*/, this.makeRequest(BitcoinIns.GET_WALLET_ADDRESS, Buffer.concat([
Buffer.from(display ? [1] : [0]),
Buffer.of(display ? 1 : 0),
walletPolicy.getWalletId(),
walletHMAC || Buffer.alloc(32, 0),
Buffer.from([change]),
Buffer.of(change),
addressIndexBuffer,

@@ -161,3 +157,3 @@ ]), clientInterpreter)];

};
AppClient.prototype.signPsbt = function (psbt, walletPolicy, walletHMAC, progressCallback) {
AppClient.prototype.signPsbt = function (psbt, walletPolicy, walletHMAC) {
return __awaiter(this, void 0, void 0, function () {

@@ -173,3 +169,3 @@ var merkelizedPsbt, clientInterpreter, _a, _b, map, _c, _d, map, inputMapsRoot, outputMapsRoot, yielded, ret, yielded_1, yielded_1_1, inputAndSig;

}
clientInterpreter = new ClientCommandInterpreter(progressCallback);
clientInterpreter = new ClientCommandInterpreter();
// prepare ClientCommandInterpreter

@@ -243,3 +239,3 @@ clientInterpreter.addKnownList(walletPolicy.keys.map(function (k) { return Buffer.from(k, "ascii"); }));

return __generator(this, function (_a) {
return [2 /*return*/, this.makeRequest(BitcoinIns.GET_MASTER_FINGERPRINT, Buffer.from([]))];
return [2 /*return*/, this.makeRequest(BitcoinIns.GET_MASTER_FINGERPRINT, Buffer.of())];
});

@@ -246,0 +242,0 @@ });

@@ -16,6 +16,5 @@ /// <reference types="node" />

export declare class YieldCommand extends ClientCommand {
private progressCallback;
private results;
code: ClientCommandCode;
constructor(results: Buffer[], progressCallback: () => void);
constructor(results: Buffer[]);
execute(request: Buffer): Buffer;

@@ -49,17 +48,2 @@ }

}
/**
* This class will dispatch a client command coming from the hardware device to
* the appropriate client command implementation. Those client commands
* typically requests data from a merkle tree or merkelized maps.
*
* A ClientCommandInterpreter is prepared by adding the merkle trees and
* merkelized maps it should be able to serve to the hardware device. This class
* doesn't know anything about the semantics of the data it holds, it just
* serves merkle data. It doesn't even know in what context it is being
* executed, ie SignPsbt, getWalletAddress, etc.
*
* If the command yelds results to the client, as signPsbt does, the yielded
* data will be accessible after the command completed by calling getYielded(),
* which will return the yields in the same order as they came in.
*/
export declare class ClientCommandInterpreter {

@@ -71,3 +55,3 @@ private roots;

private commands;
constructor(progressCallback: () => void);
constructor();
getYielded(): Buffer[];

@@ -74,0 +58,0 @@ addKnownPreimage(preimage: Buffer): void;

@@ -53,3 +53,2 @@ var __extends = (this && this.__extends) || (function () {

import { crypto } from "bitcoinjs-lib";
import { BufferReader } from "../buffertools";
import { createVarint } from "../varint";

@@ -72,5 +71,4 @@ import { hashLeaf, Merkle } from "./merkle";

__extends(YieldCommand, _super);
function YieldCommand(results, progressCallback) {
function YieldCommand(results) {
var _this = _super.call(this) || this;
_this.progressCallback = progressCallback;
_this.code = ClientCommandCode.YIELD;

@@ -82,3 +80,2 @@ _this.results = results;

this.results.push(Buffer.from(request.subarray(1)));
this.progressCallback();
return Buffer.from("");

@@ -99,3 +96,3 @@ };

GetPreimageCommand.prototype.execute = function (request) {
var req = Buffer.from(request.subarray(1));
var req = request.subarray(1);
// we expect no more data to read

@@ -129,3 +126,3 @@ if (req.length != 1 + 32) {

Buffer.from([payload_size]),
Buffer.from(known_preimage.subarray(0, payload_size)),
known_preimage.subarray(0, payload_size),
]);

@@ -149,18 +146,14 @@ }

var _a;
var req = Buffer.from(request.subarray(1));
if (req.length < 32 + 1 + 1) {
throw new Error("Invalid request, expected at least 34 bytes");
var req = request.subarray(1);
if (req.length != 32 + 4 + 4) {
throw new Error("Invalid request, unexpected trailing data");
}
var reqBuf = new BufferReader(req);
var hash = reqBuf.readSlice(32);
// read the hash
var hash = Buffer.alloc(32);
for (var i = 0; i < 32; i++) {
hash[i] = req.readUInt8(i);
}
var hash_hex = hash.toString("hex");
var tree_size;
var leaf_index;
try {
tree_size = reqBuf.readVarInt();
leaf_index = reqBuf.readVarInt();
}
catch (e) {
throw new Error("Invalid request, couldn't parse tree_size or leaf_index");
}
var tree_size = req.readUInt32BE(32);
var leaf_index = req.readUInt32BE(32 + 4);
var mt = this.known_trees.get(hash_hex);

@@ -201,3 +194,3 @@ if (!mt) {

GetMerkleLeafIndexCommand.prototype.execute = function (request) {
var req = Buffer.from(request.subarray(1));
var req = request.subarray(1);
if (req.length != 32 + 32) {

@@ -267,19 +260,4 @@ throw new Error("Invalid request, unexpected trailing data");

export { GetMoreElementsCommand };
/**
* This class will dispatch a client command coming from the hardware device to
* the appropriate client command implementation. Those client commands
* typically requests data from a merkle tree or merkelized maps.
*
* A ClientCommandInterpreter is prepared by adding the merkle trees and
* merkelized maps it should be able to serve to the hardware device. This class
* doesn't know anything about the semantics of the data it holds, it just
* serves merkle data. It doesn't even know in what context it is being
* executed, ie SignPsbt, getWalletAddress, etc.
*
* If the command yelds results to the client, as signPsbt does, the yielded
* data will be accessible after the command completed by calling getYielded(),
* which will return the yields in the same order as they came in.
*/
var ClientCommandInterpreter = /** @class */ (function () {
function ClientCommandInterpreter(progressCallback) {
function ClientCommandInterpreter() {
var e_1, _a;

@@ -292,3 +270,3 @@ this.roots = new Map();

var commands = [
new YieldCommand(this.yielded, progressCallback),
new YieldCommand(this.yielded),
new GetPreimageCommand(this.preimages, this.queue),

@@ -295,0 +273,0 @@ new GetMerkleLeafIndexCommand(this.roots),

/// <reference types="node" />
import { MerkleMap } from "./merkleMap";
import { PsbtV2 } from "./psbtv2";
/**
* This class merkelizes a PSBTv2, by merkelizing the different
* maps of the psbt. This is used during the transaction signing process,
* where the hardware app can request specific parts of the psbt from the
* client code and be sure that the response data actually belong to the psbt.
* The reason for this is the limited amount of memory available to the app,
* so it can't always store the full psbt in memory.
*
* The signing process is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md#sign_psbt
*/
export declare class MerkelizedPsbt extends PsbtV2 {

@@ -16,0 +5,0 @@ globalMerkleMap: MerkleMap;

@@ -43,13 +43,2 @@ var __extends = (this && this.__extends) || (function () {

import { PsbtV2 } from "./psbtv2";
/**
* This class merkelizes a PSBTv2, by merkelizing the different
* maps of the psbt. This is used during the transaction signing process,
* where the hardware app can request specific parts of the psbt from the
* client code and be sure that the response data actually belong to the psbt.
* The reason for this is the limited amount of memory available to the app,
* so it can't always store the full psbt in memory.
*
* The signing process is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md#sign_psbt
*/
var MerkelizedPsbt = /** @class */ (function (_super) {

@@ -56,0 +45,0 @@ __extends(MerkelizedPsbt, _super);

/// <reference types="node" />
/**
* This class implements the merkle tree used by Ledger Bitcoin app v2+,
* which is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/merkle.md
*/
export declare class Merkle {

@@ -8,0 +3,0 @@ private leaves;

@@ -27,7 +27,2 @@ var __read = (this && this.__read) || function (o, n) {

import { crypto } from "bitcoinjs-lib";
/**
* This class implements the merkle tree used by Ledger Bitcoin app v2+,
* which is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/merkle.md
*/
var Merkle = /** @class */ (function () {

@@ -83,3 +78,3 @@ function Merkle(leaves, hasher) {

Merkle.prototype.hashNode = function (left, right) {
return this.h(Buffer.concat([Buffer.from([1]), left, right]));
return this.h(Buffer.concat([Buffer.of(1), left, right]));
};

@@ -91,3 +86,3 @@ return Merkle;

if (hashFunction === void 0) { hashFunction = crypto.sha256; }
return hashConcat(Buffer.from([0]), buf, hashFunction);
return hashConcat(Buffer.of(0), buf, hashFunction);
}

@@ -94,0 +89,0 @@ function hashConcat(bufA, bufB, hashFunction) {

/// <reference types="node" />
import { Merkle } from "./merkle";
/**
* This implements "Merkelized Maps", documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/merkle.md#merkleized-maps
*
* A merkelized map consist of two merkle trees, one for the keys of
* a map and one for the values of the same map, thus the two merkle
* trees have the same shape. The commitment is the number elements
* in the map followed by the keys' merkle root followed by the
* values' merkle root.
*/
export declare class MerkleMap {

@@ -14,0 +4,0 @@ keys: Buffer[];

import { createVarint } from "../varint";
import { hashLeaf, Merkle } from "./merkle";
/**
* This implements "Merkelized Maps", documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/merkle.md#merkleized-maps
*
* A merkelized map consist of two merkle trees, one for the keys of
* a map and one for the values of the same map, thus the two merkle
* trees have the same shape. The commitment is the number elements
* in the map followed by the keys' merkle root followed by the
* values' merkle root.
*/
var MerkleMap = /** @class */ (function () {

@@ -14,0 +4,0 @@ /**

/// <reference types="node" />
export declare type DefaultDescriptorTemplate = "pkh(@0)" | "sh(wpkh(@0))" | "wpkh(@0)" | "tr(@0)";
/**
* The Bitcon hardware app uses a descriptors-like thing to describe
* how to construct output scripts from keys. A "Wallet Policy" consists
* of a "Descriptor Template" and a list of "keys". A key is basically
* a serialized BIP32 extended public key with some added derivation path
* information. This is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/wallet.md
*/
export declare class WalletPolicy {

@@ -12,0 +4,0 @@ descriptorTemplate: string;

@@ -1,13 +0,5 @@

import { crypto } from "bitcoinjs-lib";
import { pathArrayToString } from "../bip32";
import { BufferWriter } from "../buffertools";
import { hashLeaf, Merkle } from "./merkle";
/**
* The Bitcon hardware app uses a descriptors-like thing to describe
* how to construct output scripts from keys. A "Wallet Policy" consists
* of a "Descriptor Template" and a list of "keys". A key is basically
* a serialized BIP32 extended public key with some added derivation path
* information. This is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/wallet.md
*/
import { crypto } from "bitcoinjs-lib";
import { Merkle, hashLeaf } from "./merkle";
var WalletPolicy = /** @class */ (function () {

@@ -14,0 +6,0 @@ /**

/// <reference types="node" />
import { PsbtV2 } from "./psbtv2";
/**
* This implements the "Transaction Extractor" role of BIP370 (PSBTv2
* https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#transaction-extractor). However
* the role is partially documented in BIP174 (PSBTv0
* https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#transaction-extractor).
*/
export declare function extract(psbt: PsbtV2): Buffer;
//# sourceMappingURL=psbtExtractor.d.ts.map
import { BufferWriter } from "../buffertools";
/**
* This implements the "Transaction Extractor" role of BIP370 (PSBTv2
* https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#transaction-extractor). However
* the role is partially documented in BIP174 (PSBTv0
* https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#transaction-extractor).
*/
export function extract(psbt) {

@@ -14,3 +8,3 @@ var _a, _b;

if (isSegwit) {
tx.writeSlice(Buffer.from([0, 1]));
tx.writeSlice(Buffer.of(0, 1));
}

@@ -23,3 +17,3 @@ var inputCount = psbt.getGlobalInputCount();

tx.writeUInt32(psbt.getInputOutputIndex(i));
tx.writeVarSlice((_a = psbt.getInputFinalScriptsig(i)) !== null && _a !== void 0 ? _a : Buffer.from([]));
tx.writeVarSlice((_a = psbt.getInputFinalScriptsig(i)) !== null && _a !== void 0 ? _a : Buffer.of());
tx.writeUInt32(psbt.getInputSequence(i));

@@ -33,3 +27,3 @@ if (isSegwit) {

for (var i = 0; i < outputCount; i++) {
tx.writeUInt64(psbt.getOutputAmount(i));
tx.writeUInt64(BigInt(psbt.getOutputAmount(i)));
tx.writeVarSlice(psbt.getOutputScript(i));

@@ -36,0 +30,0 @@ }

import { PsbtV2 } from "./psbtv2";
/**
* This roughly implements the "input finalizer" role of BIP370 (PSBTv2
* https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki). However
* the role is documented in BIP174 (PSBTv0
* https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki).
*
* Verify that all inputs have a signature, and set inputFinalScriptwitness
* and/or inputFinalScriptSig depending on the type of the spent outputs. Clean
* fields that aren't useful anymore, partial signatures, redeem script and
* derivation paths.
*
* @param psbt The psbt with all signatures added as partial sigs, either
* through PSBT_IN_PARTIAL_SIG or PSBT_IN_TAP_KEY_SIG
* @param psbt The psbt with all signatures added as partial sigs, either through PSBT_IN_PARTIAL_SIG or PSBT_IN_TAP_KEY_SIG
*/
export declare function finalize(psbt: PsbtV2): void;
//# sourceMappingURL=psbtFinalizer.d.ts.map
import { BufferWriter } from "../buffertools";
import { psbtIn } from "./psbtv2";
/**
* This roughly implements the "input finalizer" role of BIP370 (PSBTv2
* https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki). However
* the role is documented in BIP174 (PSBTv0
* https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki).
*
* Verify that all inputs have a signature, and set inputFinalScriptwitness
* and/or inputFinalScriptSig depending on the type of the spent outputs. Clean
* fields that aren't useful anymore, partial signatures, redeem script and
* derivation paths.
*
* @param psbt The psbt with all signatures added as partial sigs, either
* through PSBT_IN_PARTIAL_SIG or PSBT_IN_TAP_KEY_SIG
* @param psbt The psbt with all signatures added as partial sigs, either through PSBT_IN_PARTIAL_SIG or PSBT_IN_TAP_KEY_SIG
*/

@@ -83,9 +73,2 @@ export function finalize(psbt) {

}
/**
* Deletes fields that are no longer neccesary from the psbt.
*
* Note, the spec doesn't say anything about removing ouput fields
* like PSBT_OUT_BIP32_DERIVATION_PATH and others, so we keep them
* without actually knowing why. I think we should remove them too.
*/
function clearFinalizedInput(psbt, inputIndex) {

@@ -108,10 +91,2 @@ var keyTypes = [

}
/**
* Writes a script push operation to buf, which looks different
* depending on the size of the data. See
* https://en.bitcoin.it/wiki/Script#Constants
*
* @param buf the BufferWriter to write to
* @param data the Buffer to be pushed.
*/
function writePush(buf, data) {

@@ -118,0 +93,0 @@ if (data.length <= 75) {

@@ -14,3 +14,2 @@ /// <reference types="node" />

PARTIAL_SIG = 2,
SIGHASH_TYPE = 3,
REDEEM_SCRIPT = 4,

@@ -35,20 +34,2 @@ BIP32_DERIVATION = 6,

}
/**
* Implements Partially Signed Bitcoin Transaction version 2, BIP370, as
* documented at https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki
* and https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
*
* A psbt is a data structure that can carry all relevant information about a
* transaction through all stages of the signing process. From constructing an
* unsigned transaction to extracting the final serialized transaction ready for
* broadcast.
*
* This implementation is limited to what's needed in ledgerjs to carry out its
* duties, which means that support for features like multisig or taproot script
* path spending are not implemented. Specifically, it supports p2pkh,
* p2wpkhWrappedInP2sh, p2wpkh and p2tr key path spending.
*
* This class is made purposefully dumb, so it's easy to add support for
* complemantary fields as needed in the future.
*/
export declare class PsbtV2 {

@@ -79,4 +60,2 @@ protected globalMap: Map<string, Buffer>;

getInputPartialSig(inputIndex: number, pubkey: Buffer): Buffer | undefined;
setInputSighashType(inputIndex: number, sigHashtype: number): void;
getInputSighashType(inputIndex: number): number | undefined;
setInputRedeemScript(inputIndex: number, redeemScript: Buffer): void;

@@ -138,2 +117,3 @@ getInputRedeemScript(inputIndex: number): Buffer | undefined;

private setInput;
private getMap;
private getInput;

@@ -143,3 +123,3 @@ private getInputOptional;

private getOutput;
private getMap;
private getOutputOptional;
private encodeBip32Derivation;

@@ -146,0 +126,0 @@ private decodeBip32Derivation;

@@ -29,3 +29,3 @@ var __extends = (this && this.__extends) || (function () {

/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { BufferReader, BufferWriter, unsafeFrom64bitLE, unsafeTo64bitLE, } from "../buffertools";
import { BufferReader, BufferWriter } from "../buffertools";
export var psbtGlobal;

@@ -45,3 +45,2 @@ (function (psbtGlobal) {

psbtIn[psbtIn["PARTIAL_SIG"] = 2] = "PARTIAL_SIG";
psbtIn[psbtIn["SIGHASH_TYPE"] = 3] = "SIGHASH_TYPE";
psbtIn[psbtIn["REDEEM_SCRIPT"] = 4] = "REDEEM_SCRIPT";

@@ -65,3 +64,3 @@ psbtIn[psbtIn["BIP32_DERIVATION"] = 6] = "BIP32_DERIVATION";

})(psbtOut || (psbtOut = {}));
var PSBT_MAGIC_BYTES = Buffer.from([0x70, 0x73, 0x62, 0x74, 0xff]);
var PSBT_MAGIC_BYTES = Buffer.of(0x70, 0x73, 0x62, 0x74, 0xff);
var NoSuchEntry = /** @class */ (function (_super) {

@@ -75,20 +74,2 @@ __extends(NoSuchEntry, _super);

export { NoSuchEntry };
/**
* Implements Partially Signed Bitcoin Transaction version 2, BIP370, as
* documented at https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki
* and https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
*
* A psbt is a data structure that can carry all relevant information about a
* transaction through all stages of the signing process. From constructing an
* unsigned transaction to extracting the final serialized transaction ready for
* broadcast.
*
* This implementation is limited to what's needed in ledgerjs to carry out its
* duties, which means that support for features like multisig or taproot script
* path spending are not implemented. Specifically, it supports p2pkh,
* p2wpkhWrappedInP2sh, p2wpkh and p2tr key path spending.
*
* This class is made purposefully dumb, so it's easy to add support for
* complemantary fields as needed in the future.
*/
var PsbtV2 = /** @class */ (function () {

@@ -162,11 +143,2 @@ function PsbtV2() {

};
PsbtV2.prototype.setInputSighashType = function (inputIndex, sigHashtype) {
this.setInput(inputIndex, psbtIn.SIGHASH_TYPE, b(), uint32LE(sigHashtype));
};
PsbtV2.prototype.getInputSighashType = function (inputIndex) {
var result = this.getInputOptional(inputIndex, psbtIn.SIGHASH_TYPE, b());
if (!result)
return undefined;
return result.readUInt32LE(0);
};
PsbtV2.prototype.setInputRedeemScript = function (inputIndex, redeemScript) {

@@ -256,4 +228,3 @@ this.setInput(inputIndex, psbtIn.REDEEM_SCRIPT, b(), redeemScript);

PsbtV2.prototype.getOutputAmount = function (outputIndex) {
var buf = this.getOutput(outputIndex, psbtOut.AMOUNT, b());
return unsafeFrom64bitLE(buf);
return Number(this.getOutput(outputIndex, psbtOut.AMOUNT, b()).readBigUInt64LE(0));
};

@@ -301,3 +272,3 @@ PsbtV2.prototype.setOutputScript = function (outputIndex, scriptPubKey) {

var buf = new BufferWriter();
buf.writeSlice(Buffer.from([0x70, 0x73, 0x62, 0x74, 0xff]));
buf.writeSlice(Buffer.of(0x70, 0x73, 0x62, 0x74, 0xff));
serializeMap(buf, this.globalMap);

@@ -356,3 +327,3 @@ this.inputMaps.forEach(function (map) {

PsbtV2.prototype.setGlobal = function (keyType, value) {
var key = new Key(keyType, Buffer.from([]));
var key = new Key(keyType, Buffer.of());
this.globalMap.set(key.toString(), value);

@@ -369,2 +340,8 @@ };

};
PsbtV2.prototype.getMap = function (index, maps) {
if (maps[index]) {
return maps[index];
}
return (maps[index] = new Map());
};
PsbtV2.prototype.getInput = function (index, keyType, keyData) {

@@ -382,7 +359,4 @@ return get(this.inputMaps[index], keyType, keyData, false);

};
PsbtV2.prototype.getMap = function (index, maps) {
if (maps[index]) {
return maps[index];
}
return (maps[index] = new Map());
PsbtV2.prototype.getOutputOptional = function (index, keyType, keyData) {
return get(this.outputMaps[index], keyType, keyData, true);
};

@@ -491,3 +465,3 @@ PsbtV2.prototype.encodeBip32Derivation = function (masterFingerprint, path) {

function b() {
return Buffer.from([]);
return Buffer.of();
}

@@ -504,3 +478,5 @@ function set(map, keyType, keyData, value) {

function uint64LE(n) {
return unsafeTo64bitLE(n);
var b = Buffer.alloc(8);
b.writeBigUInt64LE(BigInt(n), 0);
return b;
}

@@ -507,0 +483,0 @@ function varint(n) {

/// <reference types="node" />
import type Transport from "@ledgerhq/hw-transport";
import BtcNew from "./BtcNew";
import BtcOld from "./BtcOld";
import type { CreateTransactionArg } from "./createTransaction";

@@ -45,3 +43,3 @@ import type { AddressFormat } from "./getWalletPublicKey";

*
* - bech32 format with 84' paths
* - bech32 format with 173' paths
*

@@ -86,6 +84,6 @@ * - cashaddr in case of Bitcoin Cash

* @param changePath is an optional BIP 32 path pointing to the path to the public key used to compute the change address
* @param outputScriptHex is the hexadecimal serialized outputs of the transaction to sign, including leading vararg voutCount
* @param outputScriptHex is the hexadecimal serialized outputs of the transaction to sign
* @param lockTime is the optional lockTime of the transaction to sign, or default (0)
* @param sigHashType is the hash type of the transaction to sign, or default (all)
* @param segwit is an optional boolean indicating wether to use segwit or not. This includes wrapped segwit.
* @param segwit is an optional boolean indicating wether to use segwit or not
* @param initialTimestamp is an optional timestamp of the function call to use for coins that necessitate timestamps only, (not the one that the tx will include)

@@ -95,3 +93,3 @@ * @param additionals list of additionnal options

* - "bech32" for spending native segwit outputs
* - "bech32m" for spending segwit v1+ outputs
* - "bech32m" for spending native segwit outputs
* - "abc" for bch

@@ -102,3 +100,3 @@ * - "gold" for btg

* @param expiryHeight is an optional Buffer for zec overwinter / sapling Txs
* @param useTrustedInputForSegwit trust inputs for segwit transactions. If app version >= 1.4.0 this should be true.
* @param useTrustedInputForSegwit trust inputs for segwit transactions
* @return the signed transaction ready to be broadcast

@@ -150,5 +148,4 @@ * @example

private inferCorrectImpl;
protected old(): BtcOld;
protected new(): BtcNew;
private old;
}
//# sourceMappingURL=Btc.d.ts.map

@@ -61,3 +61,2 @@ "use strict";

exports.__esModule = true;
var bip32_1 = require("./bip32");
var BtcNew_1 = __importStar(require("./BtcNew"));

@@ -118,3 +117,3 @@ var BtcOld_1 = __importDefault(require("./BtcOld"));

*
* - bech32 format with 84' paths
* - bech32 format with 173' paths
*

@@ -128,3 +127,2 @@ * - cashaddr in case of Bitcoin Cash

Btc.prototype.getWalletPublicKey = function (path, opts) {
var _this = this;
var options;

@@ -143,35 +141,3 @@ if (arguments.length > 2 || typeof opts === "boolean") {

return this.getCorrectImpl().then(function (impl) {
/**
* Definition: A "normal path" is a prefix of a standard path where all
* the hardened steps of the standard path are included. For example, the
* paths m/44'/1'/17' and m/44'/1'/17'/1 are normal paths, but m/44'/1'
* is not. m/'199/1'/17'/0/1 is not a normal path either.
*
* There's a compatiblity issue between old and new app: When exporting
* the key of a non-normal path with verify=false, the new app would
* return an error, whereas the old app would return the key.
*
* See
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md#get_extended_pubkey
*
* If format bech32m is used, we'll not use old, because it doesn't
* support it.
*
* When to use new (given the app supports it)
* * format is bech32m or
* * path is normal or
* * verify is true
*
* Otherwise use old.
*/
if (impl instanceof BtcNew_1["default"] &&
options.format != "bech32m" &&
(!options.verify || options.verify == false) &&
!isPathNormal(path)) {
console.warn("WARNING: Using deprecated device protocol to get the public key because\n \n * a non-standard path is requested, and\n * verify flag is false\n \n The new protocol only allows export of non-standard paths if the \n verify flag is true. Standard paths are (currently):\n\n M/44'/(1|0)'/X'\n M/49'/(1|0)'/X'\n M/84'/(1|0)'/X'\n M/86'/(1|0)'/X'\n M/48'/(1|0)'/X'/Y'\n\n followed by \"\", \"(0|1)\", or \"(0|1)/b\", where a and b are \n non-hardened. For example, the following paths are standard\n \n M/48'/1'/99'/7'\n M/86'/1'/99'/0\n M/48'/0'/99'/7'/1/17\n\n The following paths are non-standard\n\n M/48'/0'/99' // Not deepest hardened path\n M/48'/0'/99'/7'/1/17/2 // Too many non-hardened derivation steps\n M/199'/0'/1'/0/88 // Not a known purpose 199\n M/86'/1'/99'/2 // Change path item must be 0 or 1\n\n This compatibility safeguard will be removed in the future.\n Please consider calling Btc.getWalletXpub() instead.");
return _this.old().getWalletPublicKey(path, options);
}
else {
return impl.getWalletPublicKey(path, options);
}
return impl.getWalletPublicKey(path, options);
});

@@ -201,6 +167,6 @@ };

* @param changePath is an optional BIP 32 path pointing to the path to the public key used to compute the change address
* @param outputScriptHex is the hexadecimal serialized outputs of the transaction to sign, including leading vararg voutCount
* @param outputScriptHex is the hexadecimal serialized outputs of the transaction to sign
* @param lockTime is the optional lockTime of the transaction to sign, or default (0)
* @param sigHashType is the hash type of the transaction to sign, or default (all)
* @param segwit is an optional boolean indicating wether to use segwit or not. This includes wrapped segwit.
* @param segwit is an optional boolean indicating wether to use segwit or not
* @param initialTimestamp is an optional timestamp of the function call to use for coins that necessitate timestamps only, (not the one that the tx will include)

@@ -210,3 +176,3 @@ * @param additionals list of additionnal options

* - "bech32" for spending native segwit outputs
* - "bech32m" for spending segwit v1+ outputs
* - "bech32m" for spending native segwit outputs
* - "abc" for bch

@@ -217,3 +183,3 @@ * - "gold" for btg

* @param expiryHeight is an optional Buffer for zec overwinter / sapling Txs
* @param useTrustedInputForSegwit trust inputs for segwit transactions. If app version >= 1.4.0 this should be true.
* @param useTrustedInputForSegwit trust inputs for segwit transactions
* @return the signed transaction ready to be broadcast

@@ -313,6 +279,6 @@ * @example

if (!canUseNewImplementation) {
return [2 /*return*/, this.old()];
return [2 /*return*/, new BtcOld_1["default"](this.transport)];
}
else {
return [2 /*return*/, this["new"]()];
return [2 /*return*/, new BtcNew_1["default"](new appClient_1.AppClient(this.transport))];
}

@@ -327,36 +293,5 @@ return [2 /*return*/];

};
Btc.prototype["new"] = function () {
return new BtcNew_1["default"](new appClient_1.AppClient(this.transport));
};
return Btc;
}());
exports["default"] = Btc;
function isPathNormal(path) {
//path is not deepest hardened node of a standard path or deeper, use BtcOld
var h = 0x80000000;
var pathElems = (0, bip32_1.pathStringToArray)(path);
var hard = function (n) { return n >= h; };
var soft = function (n) { return !n || n < h; };
var change = function (n) { return !n || n == 0 || n == 1; };
if (pathElems.length >= 3 &&
pathElems.length <= 5 &&
[44 + h, 49 + h, 84 + h, 86 + h].some(function (v) { return v == pathElems[0]; }) &&
[0 + h, 1 + h].some(function (v) { return v == pathElems[1]; }) &&
hard(pathElems[2]) &&
change(pathElems[3]) &&
soft(pathElems[4])) {
return true;
}
if (pathElems.length >= 4 &&
pathElems.length <= 6 &&
48 + h == pathElems[0] &&
[0 + h, 1 + h].some(function (v) { return v == pathElems[1]; }) &&
hard(pathElems[2]) &&
hard(pathElems[3]) &&
change(pathElems[4]) &&
soft(pathElems[5])) {
return true;
}
return false;
}
//# sourceMappingURL=Btc.js.map
import type { CreateTransactionArg } from "./createTransaction";
import { AppAndVersion } from "./getAppAndVersion";
import type { AddressFormat } from "./getWalletPublicKey";
import { AppClient as Client } from "./newops/appClient";
import { AppAndVersion } from "./getAppAndVersion";
export declare function canSupportApp(appAndVersion: AppAndVersion): boolean;
/**
* This class implements the same interface as BtcOld (formerly
* named Btc), but interacts with Bitcoin hardware app version 2+
* which uses a totally new APDU protocol. This new
* protocol is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md
*
* Since the interface must remain compatible with BtcOld, the methods
* of this class are quite clunky, because it needs to adapt legacy
* input data into the PSBT process. In the future, a new interface should
* be developed that exposes PSBT to the outer world, which would render
* a much cleaner implementation.
*/
export default class BtcNew {
private client;
constructor(client: Client);
/**
* This is a new method that allow users to get an xpub at a standard path.
* Standard paths are described at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md#description
*
* This boils down to paths (N=0 for Bitcoin, N=1 for Testnet):
* M/44'/N'/x'/**
* M/48'/N'/x'/y'/**
* M/49'/N'/x'/**
* M/84'/N'/x'/**
* M/86'/N'/x'/**
*
* The method was added because of added security in the hardware app v2+. The
* new hardware app will allow export of any xpub up to and including the
* deepest hardened key of standard derivation paths, whereas the old app
* would allow export of any key.
*
* This caused an issue for callers of this class, who only had
* getWalletPublicKey() to call which means they have to constuct xpub
* themselves:
*
* Suppose a user of this class wants to create an account xpub on a standard
* path, M/44'/0'/Z'. The user must get the parent key fingerprint (see BIP32)
* by requesting the parent key M/44'/0'. The new app won't allow that, because
* it only allows exporting deepest level hardened path. So the options are to
* allow requesting M/44'/0' from the app, or to add a new function
* "getWalletXpub".
*
* We opted for adding a new function, which can greatly simplify client code.
*/
getWalletXpub({ path, xpubVersion, }: {

@@ -56,9 +13,2 @@ path: string;

}): Promise<string>;
/**
* This method returns a public key, a bitcoin address, and and a chaincode
* for a specific derivation path.
*
* Limitation: If the path is not a leaf node of a standard path, the address
* will be the empty string "", see this.getWalletAddress() for details.
*/
getWalletPublicKey(path: string, opts?: {

@@ -81,3 +31,3 @@ verify?: boolean;

* get it from the device to save development time. However, it shouldn't take
* too much time to implement local address generation.
* more than a few hours to implement local address generation.
*

@@ -90,36 +40,39 @@ * Moreover, if the path is not for a leaf, ie accountPath+/X/Y, there is no

/**
* Build and sign a transaction. See Btc.createPaymentTransactionNew for
* details on how to use this method.
* To sign a transaction involving standard (P2PKH) inputs, call createTransaction with the following parameters
* @param inputs is an array of [ transaction, output_index, optional redeem script, optional sequence ] where
*
* This method will convert the legacy arguments, CreateTransactionArg, into
* a psbt which is finally signed and finalized, and the extracted fully signed
* transaction is returned.
* * transaction is the previously computed transaction object for this UTXO
* * output_index is the output in the transaction used as input for this UTXO (counting from 0)
* * redeem script is the optional redeem script to use when consuming a Segregated Witness input
* * sequence is the sequence number to use for this input (when using RBF), or non present
* @param associatedKeysets is an array of BIP 32 paths pointing to the path to the private key used for each UTXO
* @param changePath is an optional BIP 32 path pointing to the path to the public key used to compute the change address
* @param outputScriptHex is the hexadecimal serialized outputs of the transaction to sign, including leading vararg voutCount
* @param lockTime is the optional lockTime of the transaction to sign, or default (0)
* @param sigHashType is the hash type of the transaction to sign, or default (all)
* @param segwit is an optional boolean indicating wether to use segwit or not. This includes wrapped segwit.
* @param initialTimestamp is an optional timestamp of the function call to use for coins that necessitate timestamps only, (not the one that the tx will include)
* @param additionals list of additionnal options
*
* - "bech32" for spending native segwit outputs
* - "bech32m" for spending segwit v1+ outptus
* - "abc" for bch
* - "gold" for btg
* - "bipxxx" for using BIPxxx
* - "sapling" to indicate a zec transaction is supporting sapling (to be set over block 419200)
* @param expiryHeight is an optional Buffer for zec overwinter / sapling Txs
* @param useTrustedInputForSegwit trust inputs for segwit transactions. If app version >= 1.4.0 this should be true.
* @return the signed transaction ready to be broadcast
* @example
btc.createTransaction({
inputs: [ [tx1, 1] ],
associatedKeysets: ["0'/0/0"],
outputScriptHex: "01905f0100000000001976a91472a5d75c8d2d0565b656a5232703b167d50d5a2b88ac"
}).then(res => ...);
*/
createPaymentTransactionNew(arg: CreateTransactionArg): Promise<string>;
/**
* Calculates an output script along with public key and possible redeemScript
* from a path and accountType. The accountPath must be a prefix of path.
*
* @returns an object with output script (property "script"), redeemScript (if
* wrapped p2wpkh), and pubkey at provided path. The values of these three
* properties depend on the accountType used.
*/
private outputScriptAt;
/**
* Adds relevant data about an input to the psbt. This includes sequence,
* previous txid, output index, spent UTXO, redeem script for wrapped p2wpkh,
* public key and its derivation path.
*/
private setInput;
/**
* This implements the "Signer" role of the BIP370 transaction signing
* process.
*
* It ssks the hardware device to sign the a psbt using the specified wallet
* policy. This method assumes BIP32 derived keys are used for all inputs, see
* comment in-line. The signatures returned from the hardware device is added
* to the appropriate input fields of the PSBT.
*/
private signPsbt;
}
//# sourceMappingURL=BtcNew.d.ts.map
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

@@ -44,7 +55,7 @@ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }

var bitcoinjs_lib_1 = require("bitcoinjs-lib");
var tiny_secp256k1_1 = require("tiny-secp256k1");
var semver_1 = __importDefault(require("semver"));
var tiny_secp256k1_1 = require("tiny-secp256k1");
var bip32_1 = require("./bip32");
var buffertools_1 = require("./buffertools");
var accounttype_1 = require("./newops/accounttype");
var hashPublicKey_1 = require("./hashPublicKey");
var policy_1 = require("./newops/policy");

@@ -55,2 +66,3 @@ var psbtExtractor_1 = require("./newops/psbtExtractor");

var serializeTransaction_1 = require("./serializeTransaction");
var constants_1 = require("./constants");
var newSupportedApps = ["Bitcoin", "Bitcoin Test"];

@@ -62,15 +74,2 @@ function canSupportApp(appAndVersion) {

exports.canSupportApp = canSupportApp;
/**
* This class implements the same interface as BtcOld (formerly
* named Btc), but interacts with Bitcoin hardware app version 2+
* which uses a totally new APDU protocol. This new
* protocol is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md
*
* Since the interface must remain compatible with BtcOld, the methods
* of this class are quite clunky, because it needs to adapt legacy
* input data into the PSBT process. In the future, a new interface should
* be developed that exposes PSBT to the outer world, which would render
* a much cleaner implementation.
*/
var BtcNew = /** @class */ (function () {

@@ -80,32 +79,2 @@ function BtcNew(client) {

}
/**
* This is a new method that allow users to get an xpub at a standard path.
* Standard paths are described at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md#description
*
* This boils down to paths (N=0 for Bitcoin, N=1 for Testnet):
* M/44'/N'/x'/**
* M/48'/N'/x'/y'/**
* M/49'/N'/x'/**
* M/84'/N'/x'/**
* M/86'/N'/x'/**
*
* The method was added because of added security in the hardware app v2+. The
* new hardware app will allow export of any xpub up to and including the
* deepest hardened key of standard derivation paths, whereas the old app
* would allow export of any key.
*
* This caused an issue for callers of this class, who only had
* getWalletPublicKey() to call which means they have to constuct xpub
* themselves:
*
* Suppose a user of this class wants to create an account xpub on a standard
* path, M/44'/0'/Z'. The user must get the parent key fingerprint (see BIP32)
* by requesting the parent key M/44'/0'. The new app won't allow that, because
* it only allows exporting deepest level hardened path. So the options are to
* allow requesting M/44'/0' from the app, or to add a new function
* "getWalletXpub".
*
* We opted for adding a new function, which can greatly simplify client code.
*/
BtcNew.prototype.getWalletXpub = function (_a) {

@@ -119,3 +88,3 @@ var path = _a.path, xpubVersion = _a.xpubVersion;

pathElements = (0, bip32_1.pathStringToArray)(path);
return [4 /*yield*/, this.client.getExtendedPubkey(false, pathElements)];
return [4 /*yield*/, this.client.getPubkey(false, pathElements)];
case 1:

@@ -132,9 +101,2 @@ xpub = _b.sent();

};
/**
* This method returns a public key, a bitcoin address, and and a chaincode
* for a specific derivation path.
*
* Limitation: If the path is not a leaf node of a standard path, the address
* will be the empty string "", see this.getWalletAddress() for details.
*/
BtcNew.prototype.getWalletPublicKey = function (path, opts) {

@@ -148,7 +110,7 @@ var _a, _b;

pathElements = (0, bip32_1.pathStringToArray)(path);
return [4 /*yield*/, this.client.getExtendedPubkey(false, pathElements)];
return [4 /*yield*/, this.client.getPubkey(false, pathElements)];
case 1:
xpub = _c.sent();
display = (_a = opts === null || opts === void 0 ? void 0 : opts.verify) !== null && _a !== void 0 ? _a : false;
return [4 /*yield*/, this.getWalletAddress(pathElements, descrTemplFrom((_b = opts === null || opts === void 0 ? void 0 : opts.format) !== null && _b !== void 0 ? _b : "legacy"), display)];
return [4 /*yield*/, this.getWalletAddress(pathElements, accountTypeFrom((_b = opts === null || opts === void 0 ? void 0 : opts.format) !== null && _b !== void 0 ? _b : "legacy"), display)];
case 2:

@@ -176,3 +138,3 @@ address = _c.sent();

* get it from the device to save development time. However, it shouldn't take
* too much time to implement local address generation.
* more than a few hours to implement local address generation.
*

@@ -183,3 +145,3 @@ * Moreover, if the path is not for a leaf, ie accountPath+/X/Y, there is no

*/
BtcNew.prototype.getWalletAddress = function (pathElements, descrTempl, display) {
BtcNew.prototype.getWalletAddress = function (pathElements, accountType, display) {
return __awaiter(this, void 0, void 0, function () {

@@ -194,3 +156,3 @@ var accountPath, accountXpub, masterFingerprint, policy, changeAndIndex;

}
return [4 /*yield*/, this.client.getExtendedPubkey(false, accountPath)];
return [4 /*yield*/, this.client.getPubkey(false, accountPath)];
case 1:

@@ -201,3 +163,3 @@ accountXpub = _a.sent();

masterFingerprint = _a.sent();
policy = new policy_1.WalletPolicy(descrTempl, (0, policy_1.createKey)(masterFingerprint, accountPath, accountXpub));
policy = new policy_1.WalletPolicy(accountType, (0, policy_1.createKey)(masterFingerprint, accountPath, accountXpub));
changeAndIndex = pathElements.slice(-2, pathElements.length);

@@ -210,41 +172,55 @@ return [2 /*return*/, this.client.getWalletAddress(policy, Buffer.alloc(32, 0), changeAndIndex[0], changeAndIndex[1], display)];

/**
* Build and sign a transaction. See Btc.createPaymentTransactionNew for
* details on how to use this method.
* To sign a transaction involving standard (P2PKH) inputs, call createTransaction with the following parameters
* @param inputs is an array of [ transaction, output_index, optional redeem script, optional sequence ] where
*
* This method will convert the legacy arguments, CreateTransactionArg, into
* a psbt which is finally signed and finalized, and the extracted fully signed
* transaction is returned.
* * transaction is the previously computed transaction object for this UTXO
* * output_index is the output in the transaction used as input for this UTXO (counting from 0)
* * redeem script is the optional redeem script to use when consuming a Segregated Witness input
* * sequence is the sequence number to use for this input (when using RBF), or non present
* @param associatedKeysets is an array of BIP 32 paths pointing to the path to the private key used for each UTXO
* @param changePath is an optional BIP 32 path pointing to the path to the public key used to compute the change address
* @param outputScriptHex is the hexadecimal serialized outputs of the transaction to sign, including leading vararg voutCount
* @param lockTime is the optional lockTime of the transaction to sign, or default (0)
* @param sigHashType is the hash type of the transaction to sign, or default (all)
* @param segwit is an optional boolean indicating wether to use segwit or not. This includes wrapped segwit.
* @param initialTimestamp is an optional timestamp of the function call to use for coins that necessitate timestamps only, (not the one that the tx will include)
* @param additionals list of additionnal options
*
* - "bech32" for spending native segwit outputs
* - "bech32m" for spending segwit v1+ outptus
* - "abc" for bch
* - "gold" for btg
* - "bipxxx" for using BIPxxx
* - "sapling" to indicate a zec transaction is supporting sapling (to be set over block 419200)
* @param expiryHeight is an optional Buffer for zec overwinter / sapling Txs
* @param useTrustedInputForSegwit trust inputs for segwit transactions. If app version >= 1.4.0 this should be true.
* @return the signed transaction ready to be broadcast
* @example
btc.createTransaction({
inputs: [ [tx1, 1] ],
associatedKeysets: ["0'/0/0"],
outputScriptHex: "01905f0100000000001976a91472a5d75c8d2d0565b656a5232703b167d50d5a2b88ac"
}).then(res => ...);
*/
BtcNew.prototype.createPaymentTransactionNew = function (arg) {
return __awaiter(this, void 0, void 0, function () {
var inputCount, psbt, masterFp, accountType, notifyCount, progress, accountXpub, accountPath, i, pathElems, outputsConcat, outputsBufferReader, outputCount, changeData, changeFound, i, amount, outputScript, isChange, changePath, pubkey, key, p, firstSigned, progressCallback, serializedTx;
var psbt, accountType, masterFp, accountXpub, accountPath, i, pathElems, outputsConcat, outputsBufferReader, outputCount, changeData, changeFound, i, amount, outputScript, isChange, changePath, pubkey, key, p;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
inputCount = arg.inputs.length;
if (inputCount == 0) {
if (arg.inputs.length == 0) {
throw Error("No inputs");
}
psbt = new psbtv2_1.PsbtV2();
return [4 /*yield*/, this.client.getMasterFingerprint()];
case 1:
masterFp = _a.sent();
accountType = accountTypeFromArg(arg, psbt, masterFp);
if (arg.lockTime != undefined) {
// The signer will assume locktime 0 if unset
accountType = accountTypeFromArg(arg);
psbt.setGlobalTxVersion(2);
if (arg.lockTime) {
psbt.setGlobalFallbackLocktime(arg.lockTime);
}
psbt.setGlobalInputCount(inputCount);
psbt.setGlobalInputCount(arg.inputs.length);
psbt.setGlobalPsbtVersion(2);
psbt.setGlobalTxVersion(2);
notifyCount = 0;
progress = function () {
if (!arg.onDeviceStreaming)
return;
arg.onDeviceStreaming({
total: 2 * inputCount,
index: notifyCount,
progress: ++notifyCount / (2 * inputCount)
});
};
return [4 /*yield*/, this.client.getMasterFingerprint()];
case 1:
masterFp = _a.sent();
accountXpub = "";

@@ -255,4 +231,3 @@ accountPath = [];

case 2:
if (!(i < inputCount)) return [3 /*break*/, 7];
progress();
if (!(i < arg.inputs.length)) return [3 /*break*/, 7];
pathElems = (0, bip32_1.pathStringToArray)(arg.associatedKeysets[i]);

@@ -263,7 +238,7 @@ if (!(accountXpub == "")) return [3 /*break*/, 4];

accountPath = pathElems.slice(0, -2);
return [4 /*yield*/, this.client.getExtendedPubkey(false, accountPath)];
return [4 /*yield*/, this.client.getPubkey(false, accountPath)];
case 3:
accountXpub = _a.sent();
_a.label = 4;
case 4: return [4 /*yield*/, this.setInput(psbt, i, arg.inputs[i], pathElems, accountType, masterFp, arg.sigHashType)];
case 4: return [4 /*yield*/, this.setInput(psbt, i, arg.inputs[i], pathElems, accountType, masterFp)];
case 5:

@@ -279,6 +254,6 @@ _a.sent();

outputCount = outputsBufferReader.readVarInt();
psbt.setGlobalOutputCount(outputCount);
return [4 /*yield*/, this.outputScriptAt(accountPath, accountType, arg.changePath)];
case 8:
changeData = _a.sent();
psbt.setGlobalOutputCount(outputCount);
changeFound = !changeData;

@@ -288,5 +263,3 @@ for (i = 0; i < outputCount; i++) {

outputScript = outputsBufferReader.readVarSlice();
psbt.setOutputAmount(i, amount);
psbt.setOutputScript(i, outputScript);
isChange = changeData && outputScript.equals(changeData === null || changeData === void 0 ? void 0 : changeData.cond.scriptPubKey);
isChange = changeData && outputScript.equals(changeData === null || changeData === void 0 ? void 0 : changeData.script);
if (isChange) {

@@ -296,29 +269,28 @@ changeFound = true;

pubkey = changeData.pubkey;
accountType.setOwnOutput(i, changeData.cond, [pubkey], [changePath]);
if (accountType == AccountType.p2pkh) {
psbt.setOutputBip32Derivation(i, pubkey, masterFp, changePath);
}
else if (accountType == AccountType.p2wpkh) {
psbt.setOutputBip32Derivation(i, pubkey, masterFp, changePath);
}
else if (accountType == AccountType.p2wpkhWrapped) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
psbt.setOutputRedeemScript(i, changeData.redeemScript);
psbt.setOutputBip32Derivation(i, pubkey, masterFp, changePath);
}
else if (accountType == AccountType.p2tr) {
psbt.setOutputTapBip32Derivation(i, pubkey, [], masterFp, changePath);
}
}
psbt.setOutputAmount(i, amount);
psbt.setOutputScript(i, outputScript);
}
if (!changeFound) {
throw new Error("Change script not found among outputs! " +
(changeData === null || changeData === void 0 ? void 0 : changeData.cond.scriptPubKey.toString("hex")));
(changeData === null || changeData === void 0 ? void 0 : changeData.script.toString("hex")));
}
key = (0, policy_1.createKey)(masterFp, accountPath, accountXpub);
p = new policy_1.WalletPolicy(accountType.getDescriptorTemplate(), key);
// This is cheating, because it's not actually requested on the
// device yet, but it will be, soonish.
if (arg.onDeviceSignatureRequested)
arg.onDeviceSignatureRequested();
firstSigned = false;
progressCallback = function () {
if (!firstSigned) {
firstSigned = true;
arg.onDeviceSignatureGranted && arg.onDeviceSignatureGranted();
}
progress();
};
return [4 /*yield*/, this.signPsbt(psbt, p, progressCallback)];
case 9:
_a.sent();
(0, psbtFinalizer_1.finalize)(psbt);
serializedTx = (0, psbtExtractor_1.extract)(psbt);
return [2 /*return*/, serializedTx.toString("hex")];
p = new policy_1.WalletPolicy(accountType, key);
return [4 /*yield*/, this.signPsbt(psbt, p)];
case 9: return [2 /*return*/, _a.sent()];
}

@@ -328,13 +300,5 @@ });

};
/**
* Calculates an output script along with public key and possible redeemScript
* from a path and accountType. The accountPath must be a prefix of path.
*
* @returns an object with output script (property "script"), redeemScript (if
* wrapped p2wpkh), and pubkey at provided path. The values of these three
* properties depend on the accountType used.
*/
BtcNew.prototype.outputScriptAt = function (accountPath, accountType, path) {
return __awaiter(this, void 0, void 0, function () {
var pathElems, i, xpub, pubkey, cond;
var pathElems, i, xpub, pubkey, script;
return __generator(this, function (_a) {

@@ -353,8 +317,11 @@ switch (_a.label) {

}
return [4 /*yield*/, this.client.getExtendedPubkey(false, pathElems)];
return [4 /*yield*/, this.client.getPubkey(false, pathElems)];
case 1:
xpub = _a.sent();
pubkey = (0, bip32_1.pubkeyFromXpub)(xpub);
cond = accountType.spendingCondition([pubkey]);
return [2 /*return*/, { cond: cond, pubkey: pubkey }];
if (accountType == AccountType.p2tr) {
pubkey = pubkey.slice(1);
}
script = outputScriptOf(pubkey, accountType);
return [2 /*return*/, __assign(__assign({}, script), { pubkey: pubkey })];
}

@@ -364,10 +331,5 @@ });

};
/**
* Adds relevant data about an input to the psbt. This includes sequence,
* previous txid, output index, spent UTXO, redeem script for wrapped p2wpkh,
* public key and its derivation path.
*/
BtcNew.prototype.setInput = function (psbt, i, input, pathElements, accountType, masterFP, sigHashType) {
BtcNew.prototype.setInput = function (psbt, i, input, pathElements, accountType, masterFP) {
return __awaiter(this, void 0, void 0, function () {
var inputTx, spentOutputIndex, redeemScript, sequence, inputTxBuffer, inputTxid, xpubBase58, pubkey, spentTxOutput, spendCondition, spentOutput;
var inputTx, spentOutputIndex, redeemScript, sequence, inputTxBuffer, inputTxid, xpubBase58, pubkey, spentOutput, expectedRedeemScript, xonly;
return __generator(this, function (_a) {

@@ -378,13 +340,10 @@ switch (_a.label) {

spentOutputIndex = input[1];
redeemScript = input[2] ? Buffer.from(input[2], "hex") : undefined;
redeemScript = input[2];
sequence = input[3];
if (sequence != undefined) {
if (sequence) {
psbt.setInputSequence(i, sequence);
}
if (sigHashType != undefined) {
psbt.setInputSighashType(i, sigHashType);
}
inputTxBuffer = (0, serializeTransaction_1.serializeTransaction)(inputTx, true);
inputTxid = bitcoinjs_lib_1.crypto.hash256(inputTxBuffer);
return [4 /*yield*/, this.client.getExtendedPubkey(false, pathElements)];
return [4 /*yield*/, this.client.getPubkey(false, pathElements)];
case 1:

@@ -395,9 +354,30 @@ xpubBase58 = _a.sent();

throw Error("Missing outputs array in transaction to sign");
spentTxOutput = inputTx.outputs[spentOutputIndex];
spendCondition = {
scriptPubKey: spentTxOutput.script,
redeemScript: redeemScript
};
spentOutput = { cond: spendCondition, amount: spentTxOutput.amount };
accountType.setInput(i, inputTxBuffer, spentOutput, [pubkey], [pathElements]);
spentOutput = inputTx.outputs[spentOutputIndex];
if (accountType == AccountType.p2pkh) {
psbt.setInputNonWitnessUtxo(i, inputTxBuffer);
psbt.setInputBip32Derivation(i, pubkey, masterFP, pathElements);
}
else if (accountType == AccountType.p2wpkh) {
psbt.setInputNonWitnessUtxo(i, inputTxBuffer);
psbt.setInputBip32Derivation(i, pubkey, masterFP, pathElements);
psbt.setInputWitnessUtxo(i, spentOutput.amount, spentOutput.script);
}
else if (accountType == AccountType.p2wpkhWrapped) {
psbt.setInputNonWitnessUtxo(i, inputTxBuffer);
psbt.setInputBip32Derivation(i, pubkey, masterFP, pathElements);
if (!redeemScript) {
throw new Error("Missing redeemScript for p2wpkhWrapped input");
}
expectedRedeemScript = createRedeemScript(pubkey);
if (redeemScript != expectedRedeemScript.toString("hex")) {
throw new Error("Unexpected redeemScript");
}
psbt.setInputRedeemScript(i, expectedRedeemScript);
psbt.setInputWitnessUtxo(i, spentOutput.amount, spentOutput.script);
}
else if (accountType == AccountType.p2tr) {
xonly = pubkey.slice(1);
psbt.setInputTapBip32Derivation(i, xonly, [], masterFP, pathElements);
psbt.setInputWitnessUtxo(i, spentOutput.amount, spentOutput.script);
}
psbt.setInputPreviousTxId(i, inputTxid);

@@ -410,22 +390,13 @@ psbt.setInputOutputIndex(i, spentOutputIndex);

};
/**
* This implements the "Signer" role of the BIP370 transaction signing
* process.
*
* It ssks the hardware device to sign the a psbt using the specified wallet
* policy. This method assumes BIP32 derived keys are used for all inputs, see
* comment in-line. The signatures returned from the hardware device is added
* to the appropriate input fields of the PSBT.
*/
BtcNew.prototype.signPsbt = function (psbt, walletPolicy, progressCallback) {
BtcNew.prototype.signPsbt = function (psbt, walletPolicy) {
return __awaiter(this, void 0, void 0, function () {
var sigs;
var sigs, serializedTx;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.client.signPsbt(psbt, walletPolicy, Buffer.alloc(32, 0), progressCallback)];
case 0: return [4 /*yield*/, this.client.signPsbt(psbt, walletPolicy, Buffer.alloc(32, 0))];
case 1:
sigs = _a.sent();
sigs.forEach(function (v, k) {
// Note: Looking at BIP32 derivation does not work in the generic case,
// since some inputs might not have a BIP32-derived pubkey.
// Note: Looking at BIP32 derivation does not work in the generic case.
// some inputs might not have a BIP32-derived pubkey.
var pubkeys = psbt.getInputKeyDatas(k, psbtv2_1.psbtIn.BIP32_DERIVATION);

@@ -446,3 +417,5 @@ var pubkey;

});
return [2 /*return*/];
(0, psbtFinalizer_1.finalize)(psbt);
serializedTx = (0, psbtExtractor_1.extract)(psbt);
return [2 /*return*/, serializedTx.toString("hex")];
}

@@ -455,22 +428,88 @@ });

exports["default"] = BtcNew;
function descrTemplFrom(addressFormat) {
var AccountType;
(function (AccountType) {
AccountType["p2pkh"] = "pkh(@0)";
AccountType["p2wpkh"] = "wpkh(@0)";
AccountType["p2wpkhWrapped"] = "sh(wpkh(@0))";
AccountType["p2tr"] = "tr(@0)";
})(AccountType || (AccountType = {}));
function createRedeemScript(pubkey) {
var pubkeyHash = (0, hashPublicKey_1.hashPublicKey)(pubkey);
return Buffer.concat([Buffer.from("0014", "hex"), pubkeyHash]);
}
function outputScriptOf(pubkey, accountType) {
var buf = new buffertools_1.BufferWriter();
var pubkeyHash = (0, hashPublicKey_1.hashPublicKey)(pubkey);
var redeemScript;
if (accountType == AccountType.p2pkh) {
buf.writeSlice(Buffer.of(constants_1.OP_DUP, constants_1.OP_HASH160, constants_1.HASH_SIZE));
buf.writeSlice(pubkeyHash);
buf.writeSlice(Buffer.of(constants_1.OP_EQUALVERIFY, constants_1.OP_CHECKSIG));
}
else if (accountType == AccountType.p2wpkhWrapped) {
redeemScript = createRedeemScript(pubkey);
var scriptHash = (0, hashPublicKey_1.hashPublicKey)(redeemScript);
buf.writeSlice(Buffer.of(constants_1.OP_HASH160, constants_1.HASH_SIZE));
buf.writeSlice(scriptHash);
buf.writeUInt8(constants_1.OP_EQUAL);
}
else if (accountType == AccountType.p2wpkh) {
buf.writeSlice(Buffer.of(0, constants_1.HASH_SIZE));
buf.writeSlice(pubkeyHash);
}
else if (accountType == AccountType.p2tr) {
console.log("Internal key: " + pubkey.toString("hex"));
var outputKey = getTaprootOutputKey(pubkey);
buf.writeSlice(Buffer.of(0x51, 32)); // push1, pubkeylen
buf.writeSlice(outputKey);
}
return { script: buf.buffer(), redeemScript: redeemScript };
}
function accountTypeFrom(addressFormat) {
if (addressFormat == "legacy")
return "pkh(@0)";
return AccountType.p2pkh;
if (addressFormat == "p2sh")
return "sh(wpkh(@0))";
return AccountType.p2wpkhWrapped;
if (addressFormat == "bech32")
return "wpkh(@0)";
return AccountType.p2wpkh;
if (addressFormat == "bech32m")
return "tr(@0)";
return AccountType.p2tr;
throw new Error("Unsupported address format " + addressFormat);
}
function accountTypeFromArg(arg, psbt, masterFp) {
function accountTypeFromArg(arg) {
if (arg.additionals.includes("bech32m"))
return new accounttype_1.p2tr(psbt, masterFp);
return AccountType.p2tr;
if (arg.additionals.includes("bech32"))
return new accounttype_1.p2wpkh(psbt, masterFp);
return AccountType.p2wpkh;
if (arg.segwit)
return new accounttype_1.p2wpkhWrapped(psbt, masterFp);
return new accounttype_1.p2pkh(psbt, masterFp);
return AccountType.p2wpkhWrapped;
return AccountType.p2pkh;
}
/*
The following two functions are copied from wallet-btc and adapte.
They should be moved to a library to avoid code reuse.
*/
function hashTapTweak(x) {
// hash_tag(x) = SHA256(SHA256(tag) || SHA256(tag) || x), see BIP340
// See https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#specification
var h = bitcoinjs_lib_1.crypto.sha256(Buffer.from("TapTweak", "utf-8"));
return bitcoinjs_lib_1.crypto.sha256(Buffer.concat([h, h, x]));
}
function getTaprootOutputKey(internalPubkey) {
if (internalPubkey.length != 32) {
throw new Error("Expected 32 byte pubkey. Got " + internalPubkey.length);
}
// A BIP32 derived key can be converted to a schnorr pubkey by dropping
// the first byte, which represent the oddness/evenness. In schnorr all
// pubkeys are even.
// https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#public-key-conversion
var evenEcdsaPubkey = Buffer.concat([Buffer.of(0x02), internalPubkey]);
var tweak = hashTapTweak(internalPubkey);
// Q = P + int(hash_TapTweak(bytes(P)))G
var outputEcdsaKey = Buffer.from((0, tiny_secp256k1_1.pointAddScalar)(evenEcdsaPubkey, tweak));
// Convert to schnorr.
var outputSchnorrKey = outputEcdsaKey.slice(1);
// Create address
return outputSchnorrKey;
}
//# sourceMappingURL=BtcNew.js.map
/// <reference types="node" />
export declare function unsafeTo64bitLE(n: number): Buffer;
export declare function unsafeFrom64bitLE(byteArray: Buffer): number;
export declare class BufferWriter {

@@ -10,3 +8,3 @@ private bufs;

writeUInt32(i: number): void;
writeUInt64(i: number): void;
writeUInt64(i: bigint): void;
writeVarInt(i: number): void;

@@ -25,3 +23,3 @@ writeSlice(slice: Buffer): void;

readUInt32(): number;
readUInt64(): number;
readUInt64(): bigint;
readVarInt(): number;

@@ -28,0 +26,0 @@ readSlice(n: number): Buffer;

@@ -6,35 +6,4 @@ "use strict";

exports.__esModule = true;
exports.BufferReader = exports.BufferWriter = exports.unsafeFrom64bitLE = exports.unsafeTo64bitLE = void 0;
exports.BufferReader = exports.BufferWriter = void 0;
var varuint_bitcoin_1 = __importDefault(require("varuint-bitcoin"));
function unsafeTo64bitLE(n) {
// we want to represent the input as a 8-bytes array
if (n > Number.MAX_SAFE_INTEGER) {
throw new Error("Can't convert numbers > MAX_SAFE_INT");
}
var byteArray = Buffer.alloc(8, 0);
for (var index = 0; index < byteArray.length; index++) {
var byte = n & 0xff;
byteArray[index] = byte;
n = (n - byte) / 256;
}
return byteArray;
}
exports.unsafeTo64bitLE = unsafeTo64bitLE;
function unsafeFrom64bitLE(byteArray) {
var value = 0;
if (byteArray.length != 8) {
throw new Error("Expected Bufffer of lenght 8");
}
if (byteArray[7] != 0) {
throw new Error("Can't encode numbers > MAX_SAFE_INT");
}
if (byteArray[6] > 0x1f) {
throw new Error("Can't encode numbers > MAX_SAFE_INT");
}
for (var i = byteArray.length - 1; i >= 0; i--) {
value = value * 256 + byteArray[i];
}
return value;
}
exports.unsafeFrom64bitLE = unsafeFrom64bitLE;
var BufferWriter = /** @class */ (function () {

@@ -59,4 +28,3 @@ function BufferWriter() {

BufferWriter.prototype.writeUInt64 = function (i) {
var bytes = unsafeTo64bitLE(i);
this.writeSlice(bytes);
this.write(8, function (b) { return b.writeBigUInt64LE(i, 0); });
};

@@ -104,5 +72,5 @@ BufferWriter.prototype.writeVarInt = function (i) {

BufferReader.prototype.readUInt64 = function () {
var buf = this.readSlice(8);
var n = unsafeFrom64bitLE(buf);
return n;
var result = this.buffer.readBigUInt64LE(this.offset);
this.offset += 8;
return result;
};

@@ -109,0 +77,0 @@ BufferReader.prototype.readVarInt = function () {

@@ -5,6 +5,2 @@ /// <reference types="node" />

import { WalletPolicy } from "./policy";
/**
* This class encapsulates the APDU protocol documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md
*/
export declare class AppClient {

@@ -14,7 +10,7 @@ transport: Transport;

private makeRequest;
getExtendedPubkey(display: boolean, pathElements: number[]): Promise<string>;
getPubkey(display: boolean, pathElements: number[]): Promise<string>;
getWalletAddress(walletPolicy: WalletPolicy, walletHMAC: Buffer | null, change: number, addressIndex: number, display: boolean): Promise<string>;
signPsbt(psbt: PsbtV2, walletPolicy: WalletPolicy, walletHMAC: Buffer | null, progressCallback: () => void): Promise<Map<number, Buffer>>;
signPsbt(psbt: PsbtV2, walletPolicy: WalletPolicy, walletHMAC: Buffer | null): Promise<Map<number, Buffer>>;
getMasterFingerprint(): Promise<Buffer>;
}
//# sourceMappingURL=appClient.d.ts.map

@@ -71,6 +71,2 @@ "use strict";

})(FrameworkIns || (FrameworkIns = {}));
/**
* This class encapsulates the APDU protocol documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md
*/
var AppClient = /** @class */ (function () {

@@ -108,3 +104,3 @@ function AppClient(transport) {

};
AppClient.prototype.getExtendedPubkey = function (display, pathElements) {
AppClient.prototype.getPubkey = function (display, pathElements) {
return __awaiter(this, void 0, void 0, function () {

@@ -119,3 +115,3 @@ var response;

return [4 /*yield*/, this.makeRequest(BitcoinIns.GET_PUBKEY, Buffer.concat([
Buffer.from(display ? [1] : [0]),
Buffer.of(display ? 1 : 0),
(0, bip32_1.pathElementsToBuffer)(pathElements),

@@ -143,3 +139,3 @@ ]))];

}
clientInterpreter = new clientCommands_1.ClientCommandInterpreter(function () { });
clientInterpreter = new clientCommands_1.ClientCommandInterpreter();
clientInterpreter.addKnownList(walletPolicy.keys.map(function (k) { return Buffer.from(k, "ascii"); }));

@@ -150,6 +146,6 @@ clientInterpreter.addKnownPreimage(walletPolicy.serialize());

return [4 /*yield*/, this.makeRequest(BitcoinIns.GET_WALLET_ADDRESS, Buffer.concat([
Buffer.from(display ? [1] : [0]),
Buffer.of(display ? 1 : 0),
walletPolicy.getWalletId(),
walletHMAC || Buffer.alloc(32, 0),
Buffer.from([change]),
Buffer.of(change),
addressIndexBuffer,

@@ -164,3 +160,3 @@ ]), clientInterpreter)];

};
AppClient.prototype.signPsbt = function (psbt, walletPolicy, walletHMAC, progressCallback) {
AppClient.prototype.signPsbt = function (psbt, walletPolicy, walletHMAC) {
return __awaiter(this, void 0, void 0, function () {

@@ -176,3 +172,3 @@ var merkelizedPsbt, clientInterpreter, _a, _b, map, _c, _d, map, inputMapsRoot, outputMapsRoot, yielded, ret, yielded_1, yielded_1_1, inputAndSig;

}
clientInterpreter = new clientCommands_1.ClientCommandInterpreter(progressCallback);
clientInterpreter = new clientCommands_1.ClientCommandInterpreter();
// prepare ClientCommandInterpreter

@@ -246,3 +242,3 @@ clientInterpreter.addKnownList(walletPolicy.keys.map(function (k) { return Buffer.from(k, "ascii"); }));

return __generator(this, function (_a) {
return [2 /*return*/, this.makeRequest(BitcoinIns.GET_MASTER_FINGERPRINT, Buffer.from([]))];
return [2 /*return*/, this.makeRequest(BitcoinIns.GET_MASTER_FINGERPRINT, Buffer.of())];
});

@@ -249,0 +245,0 @@ });

@@ -16,6 +16,5 @@ /// <reference types="node" />

export declare class YieldCommand extends ClientCommand {
private progressCallback;
private results;
code: ClientCommandCode;
constructor(results: Buffer[], progressCallback: () => void);
constructor(results: Buffer[]);
execute(request: Buffer): Buffer;

@@ -49,17 +48,2 @@ }

}
/**
* This class will dispatch a client command coming from the hardware device to
* the appropriate client command implementation. Those client commands
* typically requests data from a merkle tree or merkelized maps.
*
* A ClientCommandInterpreter is prepared by adding the merkle trees and
* merkelized maps it should be able to serve to the hardware device. This class
* doesn't know anything about the semantics of the data it holds, it just
* serves merkle data. It doesn't even know in what context it is being
* executed, ie SignPsbt, getWalletAddress, etc.
*
* If the command yelds results to the client, as signPsbt does, the yielded
* data will be accessible after the command completed by calling getYielded(),
* which will return the yields in the same order as they came in.
*/
export declare class ClientCommandInterpreter {

@@ -71,3 +55,3 @@ private roots;

private commands;
constructor(progressCallback: () => void);
constructor();
getYielded(): Buffer[];

@@ -74,0 +58,0 @@ addKnownPreimage(preimage: Buffer): void;

@@ -56,3 +56,2 @@ "use strict";

var bitcoinjs_lib_1 = require("bitcoinjs-lib");
var buffertools_1 = require("../buffertools");
var varint_1 = require("../varint");

@@ -75,5 +74,4 @@ var merkle_1 = require("./merkle");

__extends(YieldCommand, _super);
function YieldCommand(results, progressCallback) {
function YieldCommand(results) {
var _this = _super.call(this) || this;
_this.progressCallback = progressCallback;
_this.code = ClientCommandCode.YIELD;

@@ -85,3 +83,2 @@ _this.results = results;

this.results.push(Buffer.from(request.subarray(1)));
this.progressCallback();
return Buffer.from("");

@@ -102,3 +99,3 @@ };

GetPreimageCommand.prototype.execute = function (request) {
var req = Buffer.from(request.subarray(1));
var req = request.subarray(1);
// we expect no more data to read

@@ -132,3 +129,3 @@ if (req.length != 1 + 32) {

Buffer.from([payload_size]),
Buffer.from(known_preimage.subarray(0, payload_size)),
known_preimage.subarray(0, payload_size),
]);

@@ -152,18 +149,14 @@ }

var _a;
var req = Buffer.from(request.subarray(1));
if (req.length < 32 + 1 + 1) {
throw new Error("Invalid request, expected at least 34 bytes");
var req = request.subarray(1);
if (req.length != 32 + 4 + 4) {
throw new Error("Invalid request, unexpected trailing data");
}
var reqBuf = new buffertools_1.BufferReader(req);
var hash = reqBuf.readSlice(32);
// read the hash
var hash = Buffer.alloc(32);
for (var i = 0; i < 32; i++) {
hash[i] = req.readUInt8(i);
}
var hash_hex = hash.toString("hex");
var tree_size;
var leaf_index;
try {
tree_size = reqBuf.readVarInt();
leaf_index = reqBuf.readVarInt();
}
catch (e) {
throw new Error("Invalid request, couldn't parse tree_size or leaf_index");
}
var tree_size = req.readUInt32BE(32);
var leaf_index = req.readUInt32BE(32 + 4);
var mt = this.known_trees.get(hash_hex);

@@ -204,3 +197,3 @@ if (!mt) {

GetMerkleLeafIndexCommand.prototype.execute = function (request) {
var req = Buffer.from(request.subarray(1));
var req = request.subarray(1);
if (req.length != 32 + 32) {

@@ -270,19 +263,4 @@ throw new Error("Invalid request, unexpected trailing data");

exports.GetMoreElementsCommand = GetMoreElementsCommand;
/**
* This class will dispatch a client command coming from the hardware device to
* the appropriate client command implementation. Those client commands
* typically requests data from a merkle tree or merkelized maps.
*
* A ClientCommandInterpreter is prepared by adding the merkle trees and
* merkelized maps it should be able to serve to the hardware device. This class
* doesn't know anything about the semantics of the data it holds, it just
* serves merkle data. It doesn't even know in what context it is being
* executed, ie SignPsbt, getWalletAddress, etc.
*
* If the command yelds results to the client, as signPsbt does, the yielded
* data will be accessible after the command completed by calling getYielded(),
* which will return the yields in the same order as they came in.
*/
var ClientCommandInterpreter = /** @class */ (function () {
function ClientCommandInterpreter(progressCallback) {
function ClientCommandInterpreter() {
var e_1, _a;

@@ -295,3 +273,3 @@ this.roots = new Map();

var commands = [
new YieldCommand(this.yielded, progressCallback),
new YieldCommand(this.yielded),
new GetPreimageCommand(this.preimages, this.queue),

@@ -298,0 +276,0 @@ new GetMerkleLeafIndexCommand(this.roots),

/// <reference types="node" />
import { MerkleMap } from "./merkleMap";
import { PsbtV2 } from "./psbtv2";
/**
* This class merkelizes a PSBTv2, by merkelizing the different
* maps of the psbt. This is used during the transaction signing process,
* where the hardware app can request specific parts of the psbt from the
* client code and be sure that the response data actually belong to the psbt.
* The reason for this is the limited amount of memory available to the app,
* so it can't always store the full psbt in memory.
*
* The signing process is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md#sign_psbt
*/
export declare class MerkelizedPsbt extends PsbtV2 {

@@ -16,0 +5,0 @@ globalMerkleMap: MerkleMap;

@@ -46,13 +46,2 @@ "use strict";

var psbtv2_1 = require("./psbtv2");
/**
* This class merkelizes a PSBTv2, by merkelizing the different
* maps of the psbt. This is used during the transaction signing process,
* where the hardware app can request specific parts of the psbt from the
* client code and be sure that the response data actually belong to the psbt.
* The reason for this is the limited amount of memory available to the app,
* so it can't always store the full psbt in memory.
*
* The signing process is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md#sign_psbt
*/
var MerkelizedPsbt = /** @class */ (function (_super) {

@@ -59,0 +48,0 @@ __extends(MerkelizedPsbt, _super);

/// <reference types="node" />
/**
* This class implements the merkle tree used by Ledger Bitcoin app v2+,
* which is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/merkle.md
*/
export declare class Merkle {

@@ -8,0 +3,0 @@ private leaves;

@@ -30,7 +30,2 @@ "use strict";

var bitcoinjs_lib_1 = require("bitcoinjs-lib");
/**
* This class implements the merkle tree used by Ledger Bitcoin app v2+,
* which is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/merkle.md
*/
var Merkle = /** @class */ (function () {

@@ -86,3 +81,3 @@ function Merkle(leaves, hasher) {

Merkle.prototype.hashNode = function (left, right) {
return this.h(Buffer.concat([Buffer.from([1]), left, right]));
return this.h(Buffer.concat([Buffer.of(1), left, right]));
};

@@ -94,3 +89,3 @@ return Merkle;

if (hashFunction === void 0) { hashFunction = bitcoinjs_lib_1.crypto.sha256; }
return hashConcat(Buffer.from([0]), buf, hashFunction);
return hashConcat(Buffer.of(0), buf, hashFunction);
}

@@ -97,0 +92,0 @@ exports.hashLeaf = hashLeaf;

/// <reference types="node" />
import { Merkle } from "./merkle";
/**
* This implements "Merkelized Maps", documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/merkle.md#merkleized-maps
*
* A merkelized map consist of two merkle trees, one for the keys of
* a map and one for the values of the same map, thus the two merkle
* trees have the same shape. The commitment is the number elements
* in the map followed by the keys' merkle root followed by the
* values' merkle root.
*/
export declare class MerkleMap {

@@ -14,0 +4,0 @@ keys: Buffer[];

@@ -6,12 +6,2 @@ "use strict";

var merkle_1 = require("./merkle");
/**
* This implements "Merkelized Maps", documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/merkle.md#merkleized-maps
*
* A merkelized map consist of two merkle trees, one for the keys of
* a map and one for the values of the same map, thus the two merkle
* trees have the same shape. The commitment is the number elements
* in the map followed by the keys' merkle root followed by the
* values' merkle root.
*/
var MerkleMap = /** @class */ (function () {

@@ -18,0 +8,0 @@ /**

/// <reference types="node" />
export declare type DefaultDescriptorTemplate = "pkh(@0)" | "sh(wpkh(@0))" | "wpkh(@0)" | "tr(@0)";
/**
* The Bitcon hardware app uses a descriptors-like thing to describe
* how to construct output scripts from keys. A "Wallet Policy" consists
* of a "Descriptor Template" and a list of "keys". A key is basically
* a serialized BIP32 extended public key with some added derivation path
* information. This is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/wallet.md
*/
export declare class WalletPolicy {

@@ -12,0 +4,0 @@ descriptorTemplate: string;

"use strict";
exports.__esModule = true;
exports.createKey = exports.WalletPolicy = void 0;
var bitcoinjs_lib_1 = require("bitcoinjs-lib");
var bip32_1 = require("../bip32");
var buffertools_1 = require("../buffertools");
var bitcoinjs_lib_1 = require("bitcoinjs-lib");
var merkle_1 = require("./merkle");
/**
* The Bitcon hardware app uses a descriptors-like thing to describe
* how to construct output scripts from keys. A "Wallet Policy" consists
* of a "Descriptor Template" and a list of "keys". A key is basically
* a serialized BIP32 extended public key with some added derivation path
* information. This is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/wallet.md
*/
var WalletPolicy = /** @class */ (function () {

@@ -17,0 +9,0 @@ /**

/// <reference types="node" />
import { PsbtV2 } from "./psbtv2";
/**
* This implements the "Transaction Extractor" role of BIP370 (PSBTv2
* https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#transaction-extractor). However
* the role is partially documented in BIP174 (PSBTv0
* https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#transaction-extractor).
*/
export declare function extract(psbt: PsbtV2): Buffer;
//# sourceMappingURL=psbtExtractor.d.ts.map

@@ -5,8 +5,2 @@ "use strict";

var buffertools_1 = require("../buffertools");
/**
* This implements the "Transaction Extractor" role of BIP370 (PSBTv2
* https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#transaction-extractor). However
* the role is partially documented in BIP174 (PSBTv0
* https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#transaction-extractor).
*/
function extract(psbt) {

@@ -18,3 +12,3 @@ var _a, _b;

if (isSegwit) {
tx.writeSlice(Buffer.from([0, 1]));
tx.writeSlice(Buffer.of(0, 1));
}

@@ -27,3 +21,3 @@ var inputCount = psbt.getGlobalInputCount();

tx.writeUInt32(psbt.getInputOutputIndex(i));
tx.writeVarSlice((_a = psbt.getInputFinalScriptsig(i)) !== null && _a !== void 0 ? _a : Buffer.from([]));
tx.writeVarSlice((_a = psbt.getInputFinalScriptsig(i)) !== null && _a !== void 0 ? _a : Buffer.of());
tx.writeUInt32(psbt.getInputSequence(i));

@@ -37,3 +31,3 @@ if (isSegwit) {

for (var i = 0; i < outputCount; i++) {
tx.writeUInt64(psbt.getOutputAmount(i));
tx.writeUInt64(BigInt(psbt.getOutputAmount(i)));
tx.writeVarSlice(psbt.getOutputScript(i));

@@ -40,0 +34,0 @@ }

import { PsbtV2 } from "./psbtv2";
/**
* This roughly implements the "input finalizer" role of BIP370 (PSBTv2
* https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki). However
* the role is documented in BIP174 (PSBTv0
* https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki).
*
* Verify that all inputs have a signature, and set inputFinalScriptwitness
* and/or inputFinalScriptSig depending on the type of the spent outputs. Clean
* fields that aren't useful anymore, partial signatures, redeem script and
* derivation paths.
*
* @param psbt The psbt with all signatures added as partial sigs, either
* through PSBT_IN_PARTIAL_SIG or PSBT_IN_TAP_KEY_SIG
* @param psbt The psbt with all signatures added as partial sigs, either through PSBT_IN_PARTIAL_SIG or PSBT_IN_TAP_KEY_SIG
*/
export declare function finalize(psbt: PsbtV2): void;
//# sourceMappingURL=psbtFinalizer.d.ts.map

@@ -7,14 +7,4 @@ "use strict";

/**
* This roughly implements the "input finalizer" role of BIP370 (PSBTv2
* https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki). However
* the role is documented in BIP174 (PSBTv0
* https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki).
*
* Verify that all inputs have a signature, and set inputFinalScriptwitness
* and/or inputFinalScriptSig depending on the type of the spent outputs. Clean
* fields that aren't useful anymore, partial signatures, redeem script and
* derivation paths.
*
* @param psbt The psbt with all signatures added as partial sigs, either
* through PSBT_IN_PARTIAL_SIG or PSBT_IN_TAP_KEY_SIG
* @param psbt The psbt with all signatures added as partial sigs, either through PSBT_IN_PARTIAL_SIG or PSBT_IN_TAP_KEY_SIG
*/

@@ -88,9 +78,2 @@ function finalize(psbt) {

exports.finalize = finalize;
/**
* Deletes fields that are no longer neccesary from the psbt.
*
* Note, the spec doesn't say anything about removing ouput fields
* like PSBT_OUT_BIP32_DERIVATION_PATH and others, so we keep them
* without actually knowing why. I think we should remove them too.
*/
function clearFinalizedInput(psbt, inputIndex) {

@@ -113,10 +96,2 @@ var keyTypes = [

}
/**
* Writes a script push operation to buf, which looks different
* depending on the size of the data. See
* https://en.bitcoin.it/wiki/Script#Constants
*
* @param buf the BufferWriter to write to
* @param data the Buffer to be pushed.
*/
function writePush(buf, data) {

@@ -123,0 +98,0 @@ if (data.length <= 75) {

@@ -14,3 +14,2 @@ /// <reference types="node" />

PARTIAL_SIG = 2,
SIGHASH_TYPE = 3,
REDEEM_SCRIPT = 4,

@@ -35,20 +34,2 @@ BIP32_DERIVATION = 6,

}
/**
* Implements Partially Signed Bitcoin Transaction version 2, BIP370, as
* documented at https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki
* and https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
*
* A psbt is a data structure that can carry all relevant information about a
* transaction through all stages of the signing process. From constructing an
* unsigned transaction to extracting the final serialized transaction ready for
* broadcast.
*
* This implementation is limited to what's needed in ledgerjs to carry out its
* duties, which means that support for features like multisig or taproot script
* path spending are not implemented. Specifically, it supports p2pkh,
* p2wpkhWrappedInP2sh, p2wpkh and p2tr key path spending.
*
* This class is made purposefully dumb, so it's easy to add support for
* complemantary fields as needed in the future.
*/
export declare class PsbtV2 {

@@ -79,4 +60,2 @@ protected globalMap: Map<string, Buffer>;

getInputPartialSig(inputIndex: number, pubkey: Buffer): Buffer | undefined;
setInputSighashType(inputIndex: number, sigHashtype: number): void;
getInputSighashType(inputIndex: number): number | undefined;
setInputRedeemScript(inputIndex: number, redeemScript: Buffer): void;

@@ -138,2 +117,3 @@ getInputRedeemScript(inputIndex: number): Buffer | undefined;

private setInput;
private getMap;
private getInput;

@@ -143,3 +123,3 @@ private getInputOptional;

private getOutput;
private getMap;
private getOutputOptional;
private encodeBip32Derivation;

@@ -146,0 +126,0 @@ private decodeBip32Derivation;

@@ -47,3 +47,2 @@ "use strict";

psbtIn[psbtIn["PARTIAL_SIG"] = 2] = "PARTIAL_SIG";
psbtIn[psbtIn["SIGHASH_TYPE"] = 3] = "SIGHASH_TYPE";
psbtIn[psbtIn["REDEEM_SCRIPT"] = 4] = "REDEEM_SCRIPT";

@@ -67,3 +66,3 @@ psbtIn[psbtIn["BIP32_DERIVATION"] = 6] = "BIP32_DERIVATION";

})(psbtOut = exports.psbtOut || (exports.psbtOut = {}));
var PSBT_MAGIC_BYTES = Buffer.from([0x70, 0x73, 0x62, 0x74, 0xff]);
var PSBT_MAGIC_BYTES = Buffer.of(0x70, 0x73, 0x62, 0x74, 0xff);
var NoSuchEntry = /** @class */ (function (_super) {

@@ -77,20 +76,2 @@ __extends(NoSuchEntry, _super);

exports.NoSuchEntry = NoSuchEntry;
/**
* Implements Partially Signed Bitcoin Transaction version 2, BIP370, as
* documented at https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki
* and https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
*
* A psbt is a data structure that can carry all relevant information about a
* transaction through all stages of the signing process. From constructing an
* unsigned transaction to extracting the final serialized transaction ready for
* broadcast.
*
* This implementation is limited to what's needed in ledgerjs to carry out its
* duties, which means that support for features like multisig or taproot script
* path spending are not implemented. Specifically, it supports p2pkh,
* p2wpkhWrappedInP2sh, p2wpkh and p2tr key path spending.
*
* This class is made purposefully dumb, so it's easy to add support for
* complemantary fields as needed in the future.
*/
var PsbtV2 = /** @class */ (function () {

@@ -164,11 +145,2 @@ function PsbtV2() {

};
PsbtV2.prototype.setInputSighashType = function (inputIndex, sigHashtype) {
this.setInput(inputIndex, psbtIn.SIGHASH_TYPE, b(), uint32LE(sigHashtype));
};
PsbtV2.prototype.getInputSighashType = function (inputIndex) {
var result = this.getInputOptional(inputIndex, psbtIn.SIGHASH_TYPE, b());
if (!result)
return undefined;
return result.readUInt32LE(0);
};
PsbtV2.prototype.setInputRedeemScript = function (inputIndex, redeemScript) {

@@ -258,4 +230,3 @@ this.setInput(inputIndex, psbtIn.REDEEM_SCRIPT, b(), redeemScript);

PsbtV2.prototype.getOutputAmount = function (outputIndex) {
var buf = this.getOutput(outputIndex, psbtOut.AMOUNT, b());
return (0, buffertools_1.unsafeFrom64bitLE)(buf);
return Number(this.getOutput(outputIndex, psbtOut.AMOUNT, b()).readBigUInt64LE(0));
};

@@ -303,3 +274,3 @@ PsbtV2.prototype.setOutputScript = function (outputIndex, scriptPubKey) {

var buf = new buffertools_1.BufferWriter();
buf.writeSlice(Buffer.from([0x70, 0x73, 0x62, 0x74, 0xff]));
buf.writeSlice(Buffer.of(0x70, 0x73, 0x62, 0x74, 0xff));
serializeMap(buf, this.globalMap);

@@ -358,3 +329,3 @@ this.inputMaps.forEach(function (map) {

PsbtV2.prototype.setGlobal = function (keyType, value) {
var key = new Key(keyType, Buffer.from([]));
var key = new Key(keyType, Buffer.of());
this.globalMap.set(key.toString(), value);

@@ -371,2 +342,8 @@ };

};
PsbtV2.prototype.getMap = function (index, maps) {
if (maps[index]) {
return maps[index];
}
return (maps[index] = new Map());
};
PsbtV2.prototype.getInput = function (index, keyType, keyData) {

@@ -384,7 +361,4 @@ return get(this.inputMaps[index], keyType, keyData, false);

};
PsbtV2.prototype.getMap = function (index, maps) {
if (maps[index]) {
return maps[index];
}
return (maps[index] = new Map());
PsbtV2.prototype.getOutputOptional = function (index, keyType, keyData) {
return get(this.outputMaps[index], keyType, keyData, true);
};

@@ -493,3 +467,3 @@ PsbtV2.prototype.encodeBip32Derivation = function (masterFingerprint, path) {

function b() {
return Buffer.from([]);
return Buffer.of();
}

@@ -506,3 +480,5 @@ function set(map, keyType, keyData, value) {

function uint64LE(n) {
return (0, buffertools_1.unsafeTo64bitLE)(n);
var b = Buffer.alloc(8);
b.writeBigUInt64LE(BigInt(n), 0);
return b;
}

@@ -509,0 +485,0 @@ function varint(n) {

{
"name": "@backpacker69/hw-app-btc",
"version": "6.24.1",
"version": "6.24.2",
"description": "Ledger Hardware Wallet Bitcoin Application API",

@@ -16,8 +16,8 @@ "keywords": [

"type": "git",
"url": "https://github.com/backpacker69/ledgerjs"
"url": "https://github.com/LedgerHQ/ledgerjs"
},
"bugs": {
"url": "https://github.com/backpacker69/ledgerjs/issues"
"url": "https://github.com/LedgerHQ/ledgerjs/issues"
},
"homepage": "https://github.com/backpacker69/ledgerjs",
"homepage": "https://github.com/LedgerHQ/ledgerjs",
"publishConfig": {

@@ -31,3 +31,3 @@ "access": "public"

"dependencies": {
"@ledgerhq/hw-transport": "^6.24.1",
"@ledgerhq/hw-transport": "^6.10.0",
"@ledgerhq/logs": "^6.10.0",

@@ -34,0 +34,0 @@ "bip32-path": "^0.4.2",

@@ -39,64 +39,32 @@ <img src="https://user-images.githubusercontent.com/211411/34776833-6f1ef4da-f618-11e7-8b13-f0697901d6a8.png" height="100" />

* [Examples](#examples-6)
* [impl](#impl)
* [BtcNew](#btcnew)
* [createPaymentTransactionNew](#createpaymenttransactionnew-1)
* [Parameters](#parameters-8)
* [getWalletXpub](#getwalletxpub-1)
* [Parameters](#parameters-9)
* [Examples](#examples-7)
* [BtcOld](#btcold)
* [Parameters](#parameters-9)
* [Examples](#examples-8)
* [getWalletPublicKey](#getwalletpublickey-1)
* [Parameters](#parameters-10)
* [createPaymentTransactionNew](#createpaymenttransactionnew-1)
* [Examples](#examples-9)
* [signMessageNew](#signmessagenew-1)
* [Parameters](#parameters-11)
* [BtcOld](#btcold)
* [Parameters](#parameters-12)
* [Examples](#examples-7)
* [getWalletPublicKey](#getwalletpublickey-2)
* [Parameters](#parameters-13)
* [Examples](#examples-8)
* [signMessageNew](#signmessagenew-1)
* [Parameters](#parameters-14)
* [Examples](#examples-9)
* [Examples](#examples-10)
* [createPaymentTransactionNew](#createpaymenttransactionnew-2)
* [Parameters](#parameters-15)
* [Examples](#examples-10)
* [Parameters](#parameters-12)
* [Examples](#examples-11)
* [signP2SHTransaction](#signp2shtransaction-1)
* [Parameters](#parameters-16)
* [Examples](#examples-11)
* [Parameters](#parameters-13)
* [Examples](#examples-12)
* [CreateTransactionArg](#createtransactionarg)
* [Properties](#properties)
* [AddressFormat](#addressformat)
* [AccountType](#accounttype)
* [spendingCondition](#spendingcondition)
* [Parameters](#parameters-17)
* [setInput](#setinput)
* [Parameters](#parameters-18)
* [setOwnOutput](#setownoutput)
* [Parameters](#parameters-19)
* [getDescriptorTemplate](#getdescriptortemplate)
* [SingleKeyAccount](#singlekeyaccount)
* [getTaprootOutputKey](#gettaprootoutputkey)
* [Parameters](#parameters-20)
* [AppClient](#appclient)
* [Parameters](#parameters-21)
* [ClientCommandInterpreter](#clientcommandinterpreter)
* [Parameters](#parameters-22)
* [MerkelizedPsbt](#merkelizedpsbt)
* [Parameters](#parameters-23)
* [Merkle](#merkle)
* [Parameters](#parameters-24)
* [MerkleMap](#merklemap)
* [Parameters](#parameters-25)
* [WalletPolicy](#walletpolicy)
* [Parameters](#parameters-26)
* [extract](#extract)
* [Parameters](#parameters-27)
* [constructor](#constructor)
* [Parameters](#parameters-14)
* [constructor](#constructor-1)
* [Parameters](#parameters-15)
* [finalize](#finalize)
* [Parameters](#parameters-28)
* [clearFinalizedInput](#clearfinalizedinput)
* [Parameters](#parameters-29)
* [writePush](#writepush)
* [Parameters](#parameters-30)
* [PsbtV2](#psbtv2)
* [Parameters](#parameters-16)
* [serializeTransactionOutputs](#serializetransactionoutputs-1)
* [Parameters](#parameters-31)
* [Examples](#examples-12)
* [Parameters](#parameters-17)
* [Examples](#examples-13)
* [SignP2SHTransactionArg](#signp2shtransactionarg)

@@ -147,3 +115,3 @@ * [Properties](#properties-1)

* bech32 format with 84' paths
* bech32 format with 173' paths

@@ -195,9 +163,9 @@ * cashaddr in case of Bitcoin Cash

* `changePath` is an optional BIP 32 path pointing to the path to the public key used to compute the change address
* `outputScriptHex` is the hexadecimal serialized outputs of the transaction to sign, including leading vararg voutCount
* `outputScriptHex` is the hexadecimal serialized outputs of the transaction to sign
* `lockTime` is the optional lockTime of the transaction to sign, or default (0)
* `sigHashType` is the hash type of the transaction to sign, or default (all)
* `segwit` is an optional boolean indicating wether to use segwit or not. This includes wrapped segwit.
* `segwit` is an optional boolean indicating wether to use segwit or not
* `initialTimestamp` is an optional timestamp of the function call to use for coins that necessitate timestamps only, (not the one that the tx will include)
* `additionals` list of additionnal options* "bech32" for spending native segwit outputs
* "bech32m" for spending segwit v1+ outputs
* "bech32m" for spending native segwit outputs
* "abc" for bch

@@ -208,3 +176,3 @@ * "gold" for btg

* `expiryHeight` is an optional Buffer for zec overwinter / sapling Txs
* `useTrustedInputForSegwit` trust inputs for segwit transactions. If app version >= 1.4.0 this should be true.
* `useTrustedInputForSegwit` trust inputs for segwit transactions

@@ -286,115 +254,41 @@ ##### Examples

### impl
### createPaymentTransactionNew
Definition: A "normal path" is a prefix of a standard path where all
the hardened steps of the standard path are included. For example, the
paths m/44'/1'/17' and m/44'/1'/17'/1 are normal paths, but m/44'/1'
is not. m/'199/1'/17'/0/1 is not a normal path either.
To sign a transaction involving standard (P2PKH) inputs, call createTransaction with the following parameters
There's a compatiblity issue between old and new app: When exporting
the key of a non-normal path with verify=false, the new app would
return an error, whereas the old app would return the key.
See
<https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md#get_extended_pubkey>
If format bech32m is used, we'll not use old, because it doesn't
support it.
When to use new (given the app supports it)
* format is bech32m or
* path is normal or
* verify is true
Otherwise use old.
### BtcNew
This class implements the same interface as BtcOld (formerly
named Btc), but interacts with Bitcoin hardware app version 2+
which uses a totally new APDU protocol. This new
protocol is documented at
<https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md>
Since the interface must remain compatible with BtcOld, the methods
of this class are quite clunky, because it needs to adapt legacy
input data into the PSBT process. In the future, a new interface should
be developed that exposes PSBT to the outer world, which would render
a much cleaner implementation.
#### Parameters
* ``
* `arg` **[CreateTransactionArg](#createtransactionarg)**
* `inputs` is an array of \[ transaction, output_index, optional redeem script, optional sequence ] where* transaction is the previously computed transaction object for this UTXO
* output_index is the output in the transaction used as input for this UTXO (counting from 0)
* redeem script is the optional redeem script to use when consuming a Segregated Witness input
* sequence is the sequence number to use for this input (when using RBF), or non present
* `associatedKeysets` is an array of BIP 32 paths pointing to the path to the private key used for each UTXO
* `changePath` is an optional BIP 32 path pointing to the path to the public key used to compute the change address
* `outputScriptHex` is the hexadecimal serialized outputs of the transaction to sign, including leading vararg voutCount
* `lockTime` is the optional lockTime of the transaction to sign, or default (0)
* `sigHashType` is the hash type of the transaction to sign, or default (all)
* `segwit` is an optional boolean indicating wether to use segwit or not. This includes wrapped segwit.
* `initialTimestamp` is an optional timestamp of the function call to use for coins that necessitate timestamps only, (not the one that the tx will include)
* `additionals` list of additionnal options* "bech32" for spending native segwit outputs
* "bech32m" for spending segwit v1+ outptus
* "abc" for bch
* "gold" for btg
* "bipxxx" for using BIPxxx
* "sapling" to indicate a zec transaction is supporting sapling (to be set over block 419200)
* `expiryHeight` is an optional Buffer for zec overwinter / sapling Txs
* `useTrustedInputForSegwit` trust inputs for segwit transactions. If app version >= 1.4.0 this should be true.
#### getWalletXpub
#### Examples
This is a new method that allow users to get an xpub at a standard path.
Standard paths are described at
<https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md#description>
```javascript
btc.createTransaction({
inputs: [ [tx1, 1] ],
associatedKeysets: ["0'/0/0"],
outputScriptHex: "01905f0100000000001976a91472a5d75c8d2d0565b656a5232703b167d50d5a2b88ac"
}).then(res => ...);
```
This boils down to paths (N=0 for Bitcoin, N=1 for Testnet):
M/44'/N'/x'/\*\*
M/48'/N'/x'/y'/\*\*
M/49'/N'/x'/\*\*
M/84'/N'/x'/\*\*
M/86'/N'/x'/\*\*
Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>** the signed transaction ready to be broadcast
The method was added because of added security in the hardware app v2+. The
new hardware app will allow export of any xpub up to and including the
deepest hardened key of standard derivation paths, whereas the old app
would allow export of any key.
This caused an issue for callers of this class, who only had
getWalletPublicKey() to call which means they have to constuct xpub
themselves:
Suppose a user of this class wants to create an account xpub on a standard
path, M/44'/0'/Z'. The user must get the parent key fingerprint (see BIP32)
by requesting the parent key M/44'/0'. The new app won't allow that, because
it only allows exporting deepest level hardened path. So the options are to
allow requesting M/44'/0' from the app, or to add a new function
"getWalletXpub".
We opted for adding a new function, which can greatly simplify client code.
##### Parameters
* `$0` **{path: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), xpubVersion: [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)}**
* `$0.path`
* `$0.xpubVersion`
Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>**
#### getWalletPublicKey
This method returns a public key, a bitcoin address, and and a chaincode
for a specific derivation path.
Limitation: If the path is not a leaf node of a standard path, the address
will be the empty string "", see this.getWalletAddress() for details.
##### Parameters
* `path` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
* `opts` **{verify: [boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?, format: [AddressFormat](#addressformat)?}?**
Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<{publicKey: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), bitcoinAddress: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), chainCode: [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)}>**
#### createPaymentTransactionNew
Build and sign a transaction. See Btc.createPaymentTransactionNew for
details on how to use this method.
This method will convert the legacy arguments, CreateTransactionArg, into
a psbt which is finally signed and finalized, and the extracted fully signed
transaction is returned.
##### Parameters
* `arg` **[CreateTransactionArg](#createtransactionarg)**
Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>**
### BtcOld

@@ -554,163 +448,12 @@

### AccountType
### constructor
Encapsulates differences between account types, for example p2wpkh,
p2wpkhWrapped, p2tr.
#### spendingCondition
Generates a scriptPubKey (output script) from a list of public keys. If a
p2sh redeemScript or a p2wsh witnessScript is needed it will also be set on
the returned SpendingCondition.
The pubkeys are expected to be 33 byte ecdsa compressed pubkeys.
##### Parameters
* `pubkeys` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Buffer](https://nodejs.org/api/buffer.html)>**
Returns **SpendingCondition**
#### setInput
Populates the psbt with account type-specific data for an input.
##### Parameters
* `i` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** The index of the input map to populate
* `inputTx` **([Buffer](https://nodejs.org/api/buffer.html) | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))** The full transaction containing the spent output. This may
be omitted for taproot.
* `spentOutput` **SpentOutput** The amount and spending condition of the spent output
* `pubkeys` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Buffer](https://nodejs.org/api/buffer.html)>** The 33 byte ecdsa compressed public keys involved in the input
* `pathElems` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)>>** The paths corresponding to the pubkeys, in same order.
Returns **void**
#### setOwnOutput
Populates the psbt with account type-specific data for an output. This is typically
done for change outputs and other outputs that goes to the same account as
being spent from.
##### Parameters
* `i` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** The index of the output map to populate
* `cond` **SpendingCondition** The spending condition for this output
* `pubkeys` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Buffer](https://nodejs.org/api/buffer.html)>** The 33 byte ecdsa compressed public keys involved in this output
* `paths` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)>>** The paths corresponding to the pubkeys, in same order.
Returns **void**
#### getDescriptorTemplate
Returns the descriptor template for this account type. Currently only
DefaultDescriptorTemplates are allowed, but that might be changed in the
future. See class WalletPolicy for more information on descriptor
templates.
Returns **DefaultDescriptorTemplate**
### SingleKeyAccount
**Extends BaseAccount**
Superclass for single signature accounts. This will make sure that the pubkey
arrays and path arrays in the method arguments contains exactly one element
and calls an abstract method to do the actual work.
### getTaprootOutputKey
Calculates a taproot output key from an internal key. This output key will be
used as witness program in a taproot output. The internal key is tweaked
according to recommendation in BIP341:
<https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_ref-22-0>
#### Parameters
* `internalPubkey` **[Buffer](https://nodejs.org/api/buffer.html)** A 32 byte x-only taproot internal key
Returns **[Buffer](https://nodejs.org/api/buffer.html)** The output key
### AppClient
This class encapsulates the APDU protocol documented at
<https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md>
#### Parameters
* `transport` **Transport**
### ClientCommandInterpreter
This class will dispatch a client command coming from the hardware device to
the appropriate client command implementation. Those client commands
typically requests data from a merkle tree or merkelized maps.
A ClientCommandInterpreter is prepared by adding the merkle trees and
merkelized maps it should be able to serve to the hardware device. This class
doesn't know anything about the semantics of the data it holds, it just
serves merkle data. It doesn't even know in what context it is being
executed, ie SignPsbt, getWalletAddress, etc.
If the command yelds results to the client, as signPsbt does, the yielded
data will be accessible after the command completed by calling getYielded(),
which will return the yields in the same order as they came in.
#### Parameters
* `progressCallback` **function (): void**
### MerkelizedPsbt
**Extends PsbtV2**
This class merkelizes a PSBTv2, by merkelizing the different
maps of the psbt. This is used during the transaction signing process,
where the hardware app can request specific parts of the psbt from the
client code and be sure that the response data actually belong to the psbt.
The reason for this is the limited amount of memory available to the app,
so it can't always store the full psbt in memory.
The signing process is documented at
<https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md#sign_psbt>
#### Parameters
* `psbt` **[PsbtV2](#psbtv2)**
### Merkle
This class implements the merkle tree used by Ledger Bitcoin app v2+,
which is documented at
<https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/merkle.md>
#### Parameters
* `leaves` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Buffer](https://nodejs.org/api/buffer.html)>**
* `hasher` **function (buf: [Buffer](https://nodejs.org/api/buffer.html)): [Buffer](https://nodejs.org/api/buffer.html)** (optional, default `crypto.sha256`)
### MerkleMap
This implements "Merkelized Maps", documented at
<https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/merkle.md#merkleized-maps>
A merkelized map consist of two merkle trees, one for the keys of
a map and one for the values of the same map, thus the two merkle
trees have the same shape. The commitment is the number elements
in the map followed by the keys' merkle root followed by the
values' merkle root.
#### Parameters
* `keys` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Buffer](https://nodejs.org/api/buffer.html)>** Sorted list of (unhashed) keys
* `values` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Buffer](https://nodejs.org/api/buffer.html)>** values, in corresponding order as the keys, and of equal length
### WalletPolicy
### constructor
The Bitcon hardware app uses a descriptors-like thing to describe
how to construct output scripts from keys. A "Wallet Policy" consists
of a "Descriptor Template" and a list of "keys". A key is basically
a serialized BIP32 extended public key with some added derivation path
information. This is documented at
<https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/wallet.md>
For now, we only support default descriptor templates.

@@ -722,77 +465,10 @@ #### Parameters

### extract
This implements the "Transaction Extractor" role of BIP370 (PSBTv2
<https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#transaction-extractor>). However
the role is partially documented in BIP174 (PSBTv0
<https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#transaction-extractor>).
#### Parameters
* `psbt` **[PsbtV2](#psbtv2)**
Returns **[Buffer](https://nodejs.org/api/buffer.html)**
### finalize
This roughly implements the "input finalizer" role of BIP370 (PSBTv2
<https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki>). However
the role is documented in BIP174 (PSBTv0
<https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki>).
Verify that all inputs have a signature, and set inputFinalScriptwitness
and/or inputFinalScriptSig depending on the type of the spent outputs. Clean
fields that aren't useful anymore, partial signatures, redeem script and
derivation paths.
#### Parameters
* `psbt` **[PsbtV2](#psbtv2)** The psbt with all signatures added as partial sigs, either
through PSBT_IN_PARTIAL_SIG or PSBT_IN_TAP_KEY_SIG
* `psbt` **PsbtV2** The psbt with all signatures added as partial sigs, either through PSBT_IN_PARTIAL_SIG or PSBT_IN_TAP_KEY_SIG
Returns **void**
### clearFinalizedInput
Deletes fields that are no longer neccesary from the psbt.
Note, the spec doesn't say anything about removing ouput fields
like PSBT_OUT_BIP32\_DERIVATION_PATH and others, so we keep them
without actually knowing why. I think we should remove them too.
#### Parameters
* `psbt` **[PsbtV2](#psbtv2)**
* `inputIndex` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)**
### writePush
Writes a script push operation to buf, which looks different
depending on the size of the data. See
<https://en.bitcoin.it/wiki/Script#Constants>
#### Parameters
* `buf` **BufferWriter** the BufferWriter to write to
* `data` **[Buffer](https://nodejs.org/api/buffer.html)** the Buffer to be pushed.
### PsbtV2
Implements Partially Signed Bitcoin Transaction version 2, BIP370, as
documented at <https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki>
and <https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki>
A psbt is a data structure that can carry all relevant information about a
transaction through all stages of the signing process. From constructing an
unsigned transaction to extracting the final serialized transaction ready for
broadcast.
This implementation is limited to what's needed in ledgerjs to carry out its
duties, which means that support for features like multisig or taproot script
path spending are not implemented. Specifically, it supports p2pkh,
p2wpkhWrappedInP2sh, p2wpkh and p2tr key path spending.
This class is made purposefully dumb, so it's easy to add support for
complemantary fields as needed in the future.
### serializeTransactionOutputs

@@ -799,0 +475,0 @@

import type Transport from "@ledgerhq/hw-transport";
import { pathStringToArray } from "./bip32";
import BtcNew, { canSupportApp } from "./BtcNew";

@@ -69,3 +68,3 @@ import BtcOld from "./BtcOld";

*
* - bech32 format with 84' paths
* - bech32 format with 173' paths
*

@@ -103,65 +102,3 @@ * - cashaddr in case of Bitcoin Cash

return this.getCorrectImpl().then((impl) => {
/**
* Definition: A "normal path" is a prefix of a standard path where all
* the hardened steps of the standard path are included. For example, the
* paths m/44'/1'/17' and m/44'/1'/17'/1 are normal paths, but m/44'/1'
* is not. m/'199/1'/17'/0/1 is not a normal path either.
*
* There's a compatiblity issue between old and new app: When exporting
* the key of a non-normal path with verify=false, the new app would
* return an error, whereas the old app would return the key.
*
* See
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md#get_extended_pubkey
*
* If format bech32m is used, we'll not use old, because it doesn't
* support it.
*
* When to use new (given the app supports it)
* * format is bech32m or
* * path is normal or
* * verify is true
*
* Otherwise use old.
*/
if (
impl instanceof BtcNew &&
options.format != "bech32m" &&
(!options.verify || options.verify == false) &&
!isPathNormal(path)
) {
console.warn(`WARNING: Using deprecated device protocol to get the public key because
* a non-standard path is requested, and
* verify flag is false
The new protocol only allows export of non-standard paths if the
verify flag is true. Standard paths are (currently):
M/44'/(1|0)'/X'
M/49'/(1|0)'/X'
M/84'/(1|0)'/X'
M/86'/(1|0)'/X'
M/48'/(1|0)'/X'/Y'
followed by "", "(0|1)", or "(0|1)/b", where a and b are
non-hardened. For example, the following paths are standard
M/48'/1'/99'/7'
M/86'/1'/99'/0
M/48'/0'/99'/7'/1/17
The following paths are non-standard
M/48'/0'/99' // Not deepest hardened path
M/48'/0'/99'/7'/1/17/2 // Too many non-hardened derivation steps
M/199'/0'/1'/0/88 // Not a known purpose 199
M/86'/1'/99'/2 // Change path item must be 0 or 1
This compatibility safeguard will be removed in the future.
Please consider calling Btc.getWalletXpub() instead.`);
return this.old().getWalletPublicKey(path, options);
} else {
return impl.getWalletPublicKey(path, options);
}
return impl.getWalletPublicKey(path, options);
});

@@ -200,6 +137,6 @@ }

* @param changePath is an optional BIP 32 path pointing to the path to the public key used to compute the change address
* @param outputScriptHex is the hexadecimal serialized outputs of the transaction to sign, including leading vararg voutCount
* @param outputScriptHex is the hexadecimal serialized outputs of the transaction to sign
* @param lockTime is the optional lockTime of the transaction to sign, or default (0)
* @param sigHashType is the hash type of the transaction to sign, or default (all)
* @param segwit is an optional boolean indicating wether to use segwit or not. This includes wrapped segwit.
* @param segwit is an optional boolean indicating wether to use segwit or not
* @param initialTimestamp is an optional timestamp of the function call to use for coins that necessitate timestamps only, (not the one that the tx will include)

@@ -209,3 +146,3 @@ * @param additionals list of additionnal options

* - "bech32" for spending native segwit outputs
* - "bech32m" for spending segwit v1+ outputs
* - "bech32m" for spending native segwit outputs
* - "abc" for bch

@@ -216,3 +153,3 @@ * - "gold" for btg

* @param expiryHeight is an optional Buffer for zec overwinter / sapling Txs
* @param useTrustedInputForSegwit trust inputs for segwit transactions. If app version >= 1.4.0 this should be true.
* @param useTrustedInputForSegwit trust inputs for segwit transactions
* @return the signed transaction ready to be broadcast

@@ -330,50 +267,11 @@ * @example

if (!canUseNewImplementation) {
return this.old();
return new BtcOld(this.transport);
} else {
return this.new();
return new BtcNew(new AppClient(this.transport));
}
}
protected old(): BtcOld {
private old(): BtcOld {
return new BtcOld(this.transport);
}
protected new(): BtcNew {
return new BtcNew(new AppClient(this.transport));
}
}
function isPathNormal(path: string): boolean {
//path is not deepest hardened node of a standard path or deeper, use BtcOld
const h = 0x80000000;
const pathElems = pathStringToArray(path);
const hard = (n: number) => n >= h;
const soft = (n: number | undefined) => !n || n < h;
const change = (n: number | undefined) => !n || n == 0 || n == 1;
if (
pathElems.length >= 3 &&
pathElems.length <= 5 &&
[44 + h, 49 + h, 84 + h, 86 + h].some((v) => v == pathElems[0]) &&
[0 + h, 1 + h].some((v) => v == pathElems[1]) &&
hard(pathElems[2]) &&
change(pathElems[3]) &&
soft(pathElems[4])
) {
return true;
}
if (
pathElems.length >= 4 &&
pathElems.length <= 6 &&
48 + h == pathElems[0] &&
[0 + h, 1 + h].some((v) => v == pathElems[1]) &&
hard(pathElems[2]) &&
hard(pathElems[3]) &&
change(pathElems[4]) &&
soft(pathElems[5])
) {
return true;
}
return false;
}
import { crypto } from "bitcoinjs-lib";
import { pointCompress, pointAddScalar } from "tiny-secp256k1";
import semver from "semver";
import { pointCompress } from "tiny-secp256k1";
import {

@@ -11,20 +11,8 @@ getXpubComponents,

} from "./bip32";
import { BufferReader } from "./buffertools";
import { BufferReader, BufferWriter } from "./buffertools";
import type { CreateTransactionArg } from "./createTransaction";
import { AppAndVersion } from "./getAppAndVersion";
import type { AddressFormat } from "./getWalletPublicKey";
import {
AccountType,
p2pkh,
p2tr,
p2wpkh,
p2wpkhWrapped,
SpendingCondition,
} from "./newops/accounttype";
import { hashPublicKey } from "./hashPublicKey";
import { AppClient as Client } from "./newops/appClient";
import {
createKey,
DefaultDescriptorTemplate,
WalletPolicy,
} from "./newops/policy";
import { createKey, WalletPolicy } from "./newops/policy";
import { extract } from "./newops/psbtExtractor";

@@ -35,2 +23,11 @@ import { finalize } from "./newops/psbtFinalizer";

import type { Transaction } from "./types";
import {
HASH_SIZE,
OP_CHECKSIG,
OP_DUP,
OP_EQUAL,
OP_EQUALVERIFY,
OP_HASH160,
} from "./constants";
import { AppAndVersion } from "./getAppAndVersion";

@@ -46,48 +43,5 @@ const newSupportedApps = ["Bitcoin", "Bitcoin Test"];

/**
* This class implements the same interface as BtcOld (formerly
* named Btc), but interacts with Bitcoin hardware app version 2+
* which uses a totally new APDU protocol. This new
* protocol is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md
*
* Since the interface must remain compatible with BtcOld, the methods
* of this class are quite clunky, because it needs to adapt legacy
* input data into the PSBT process. In the future, a new interface should
* be developed that exposes PSBT to the outer world, which would render
* a much cleaner implementation.
*/
export default class BtcNew {
constructor(private client: Client) {}
/**
* This is a new method that allow users to get an xpub at a standard path.
* Standard paths are described at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md#description
*
* This boils down to paths (N=0 for Bitcoin, N=1 for Testnet):
* M/44'/N'/x'/**
* M/48'/N'/x'/y'/**
* M/49'/N'/x'/**
* M/84'/N'/x'/**
* M/86'/N'/x'/**
*
* The method was added because of added security in the hardware app v2+. The
* new hardware app will allow export of any xpub up to and including the
* deepest hardened key of standard derivation paths, whereas the old app
* would allow export of any key.
*
* This caused an issue for callers of this class, who only had
* getWalletPublicKey() to call which means they have to constuct xpub
* themselves:
*
* Suppose a user of this class wants to create an account xpub on a standard
* path, M/44'/0'/Z'. The user must get the parent key fingerprint (see BIP32)
* by requesting the parent key M/44'/0'. The new app won't allow that, because
* it only allows exporting deepest level hardened path. So the options are to
* allow requesting M/44'/0' from the app, or to add a new function
* "getWalletXpub".
*
* We opted for adding a new function, which can greatly simplify client code.
*/
async getWalletXpub({

@@ -101,3 +55,3 @@ path,

const pathElements: number[] = pathStringToArray(path);
const xpub = await this.client.getExtendedPubkey(false, pathElements);
const xpub = await this.client.getPubkey(false, pathElements);
const xpubComponents = getXpubComponents(xpub);

@@ -112,9 +66,2 @@ if (xpubComponents.version != xpubVersion) {

/**
* This method returns a public key, a bitcoin address, and and a chaincode
* for a specific derivation path.
*
* Limitation: If the path is not a leaf node of a standard path, the address
* will be the empty string "", see this.getWalletAddress() for details.
*/
async getWalletPublicKey(

@@ -132,3 +79,3 @@ path: string,

const pathElements: number[] = pathStringToArray(path);
const xpub = await this.client.getExtendedPubkey(false, pathElements);
const xpub = await this.client.getPubkey(false, pathElements);

@@ -139,3 +86,3 @@ const display = opts?.verify ?? false;

pathElements,
descrTemplFrom(opts?.format ?? "legacy"),
accountTypeFrom(opts?.format ?? "legacy"),
display

@@ -163,3 +110,3 @@ );

* get it from the device to save development time. However, it shouldn't take
* too much time to implement local address generation.
* more than a few hours to implement local address generation.
*

@@ -172,3 +119,3 @@ * Moreover, if the path is not for a leaf, ie accountPath+/X/Y, there is no

pathElements: number[],
descrTempl: DefaultDescriptorTemplate,
accountType: AccountType,
display: boolean

@@ -180,6 +127,6 @@ ): Promise<string> {

}
const accountXpub = await this.client.getExtendedPubkey(false, accountPath);
const accountXpub = await this.client.getPubkey(false, accountPath);
const masterFingerprint = await this.client.getMasterFingerprint();
const policy = new WalletPolicy(
descrTempl,
accountType,
createKey(masterFingerprint, accountPath, accountXpub)

@@ -198,8 +145,33 @@ );

/**
* Build and sign a transaction. See Btc.createPaymentTransactionNew for
* details on how to use this method.
* To sign a transaction involving standard (P2PKH) inputs, call createTransaction with the following parameters
* @param inputs is an array of [ transaction, output_index, optional redeem script, optional sequence ] where
*
* This method will convert the legacy arguments, CreateTransactionArg, into
* a psbt which is finally signed and finalized, and the extracted fully signed
* transaction is returned.
* * transaction is the previously computed transaction object for this UTXO
* * output_index is the output in the transaction used as input for this UTXO (counting from 0)
* * redeem script is the optional redeem script to use when consuming a Segregated Witness input
* * sequence is the sequence number to use for this input (when using RBF), or non present
* @param associatedKeysets is an array of BIP 32 paths pointing to the path to the private key used for each UTXO
* @param changePath is an optional BIP 32 path pointing to the path to the public key used to compute the change address
* @param outputScriptHex is the hexadecimal serialized outputs of the transaction to sign, including leading vararg voutCount
* @param lockTime is the optional lockTime of the transaction to sign, or default (0)
* @param sigHashType is the hash type of the transaction to sign, or default (all)
* @param segwit is an optional boolean indicating wether to use segwit or not. This includes wrapped segwit.
* @param initialTimestamp is an optional timestamp of the function call to use for coins that necessitate timestamps only, (not the one that the tx will include)
* @param additionals list of additionnal options
*
* - "bech32" for spending native segwit outputs
* - "bech32m" for spending segwit v1+ outptus
* - "abc" for bch
* - "gold" for btg
* - "bipxxx" for using BIPxxx
* - "sapling" to indicate a zec transaction is supporting sapling (to be set over block 419200)
* @param expiryHeight is an optional Buffer for zec overwinter / sapling Txs
* @param useTrustedInputForSegwit trust inputs for segwit transactions. If app version >= 1.4.0 this should be true.
* @return the signed transaction ready to be broadcast
* @example
btc.createTransaction({
inputs: [ [tx1, 1] ],
associatedKeysets: ["0'/0/0"],
outputScriptHex: "01905f0100000000001976a91472a5d75c8d2d0565b656a5232703b167d50d5a2b88ac"
}).then(res => ...);
*/

@@ -209,35 +181,21 @@ async createPaymentTransactionNew(

): Promise<string> {
const inputCount = arg.inputs.length;
if (inputCount == 0) {
if (arg.inputs.length == 0) {
throw Error("No inputs");
}
const psbt = new PsbtV2();
// The master fingerprint is needed when adding BIP32 derivation paths on
// the psbt.
const masterFp = await this.client.getMasterFingerprint();
const accountType = accountTypeFromArg(arg, psbt, masterFp);
const accountType = accountTypeFromArg(arg);
if (arg.lockTime != undefined) {
// The signer will assume locktime 0 if unset
psbt.setGlobalTxVersion(2);
if (arg.lockTime) {
psbt.setGlobalFallbackLocktime(arg.lockTime);
}
psbt.setGlobalInputCount(inputCount);
psbt.setGlobalInputCount(arg.inputs.length);
psbt.setGlobalPsbtVersion(2);
psbt.setGlobalTxVersion(2);
let notifyCount = 0;
const progress = () => {
if (!arg.onDeviceStreaming) return;
arg.onDeviceStreaming({
total: 2 * inputCount,
index: notifyCount,
progress: ++notifyCount / (2 * inputCount),
});
};
const masterFp = await this.client.getMasterFingerprint();
let accountXpub = "";
let accountPath: number[] = [];
for (let i = 0; i < inputCount; i++) {
progress();
for (let i = 0; i < arg.inputs.length; i++) {
const pathElems: number[] = pathStringToArray(arg.associatedKeysets[i]);

@@ -248,3 +206,3 @@ if (accountXpub == "") {

accountPath = pathElems.slice(0, -2);
accountXpub = await this.client.getExtendedPubkey(false, accountPath);
accountXpub = await this.client.getPubkey(false, accountPath);
}

@@ -257,4 +215,3 @@ await this.setInput(

accountType,
masterFp,
arg.sigHashType
masterFp
);

@@ -266,3 +223,2 @@ }

const outputCount = outputsBufferReader.readVarInt();
psbt.setGlobalOutputCount(outputCount);
const changeData = await this.outputScriptAt(

@@ -273,4 +229,3 @@ accountPath,

);
// If the caller supplied a changePath, we must make sure there actually is
// a change output. If no change output found, we'll throw an error.
psbt.setGlobalOutputCount(outputCount);
let changeFound = !changeData;

@@ -280,10 +235,9 @@ for (let i = 0; i < outputCount; i++) {

const outputScript = outputsBufferReader.readVarSlice();
psbt.setOutputAmount(i, amount);
psbt.setOutputScript(i, outputScript);
// We won't know if we're paying to ourselves, because there's no
// information in arg to support multiple "change paths". One exception is
// if there are multiple outputs to the change address.
const isChange =
changeData && outputScript.equals(changeData?.cond.scriptPubKey);
// We won't know if we're paying to ourselves, because
// there's no information in the input arg to support this.
// We only have the changePath.
// One exception is if there are multiple outputs to the
// change address.
const isChange = changeData && outputScript.equals(changeData?.script);
if (isChange) {

@@ -295,4 +249,16 @@ changeFound = true;

accountType.setOwnOutput(i, changeData.cond, [pubkey], [changePath]);
if (accountType == AccountType.p2pkh) {
psbt.setOutputBip32Derivation(i, pubkey, masterFp, changePath);
} else if (accountType == AccountType.p2wpkh) {
psbt.setOutputBip32Derivation(i, pubkey, masterFp, changePath);
} else if (accountType == AccountType.p2wpkhWrapped) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
psbt.setOutputRedeemScript(i, changeData.redeemScript!);
psbt.setOutputBip32Derivation(i, pubkey, masterFp, changePath);
} else if (accountType == AccountType.p2tr) {
psbt.setOutputTapBip32Derivation(i, pubkey, [], masterFp, changePath);
}
}
psbt.setOutputAmount(i, amount);
psbt.setOutputScript(i, outputScript);
}

@@ -302,3 +268,3 @@ if (!changeFound) {

"Change script not found among outputs! " +
changeData?.cond.scriptPubKey.toString("hex")
changeData?.script.toString("hex")
);

@@ -308,31 +274,6 @@ }

const key = createKey(masterFp, accountPath, accountXpub);
const p = new WalletPolicy(accountType.getDescriptorTemplate(), key);
// This is cheating, because it's not actually requested on the
// device yet, but it will be, soonish.
if (arg.onDeviceSignatureRequested) arg.onDeviceSignatureRequested();
let firstSigned = false;
// This callback will be called once for each signature yielded.
const progressCallback = () => {
if (!firstSigned) {
firstSigned = true;
arg.onDeviceSignatureGranted && arg.onDeviceSignatureGranted();
}
progress();
};
await this.signPsbt(psbt, p, progressCallback);
finalize(psbt);
const serializedTx = extract(psbt);
return serializedTx.toString("hex");
const p = new WalletPolicy(accountType, key);
return await this.signPsbt(psbt, p);
}
/**
* Calculates an output script along with public key and possible redeemScript
* from a path and accountType. The accountPath must be a prefix of path.
*
* @returns an object with output script (property "script"), redeemScript (if
* wrapped p2wpkh), and pubkey at provided path. The values of these three
* properties depend on the accountType used.
*/
private async outputScriptAt(

@@ -342,3 +283,5 @@ accountPath: number[],

path: string | undefined
): Promise<{ cond: SpendingCondition; pubkey: Buffer } | undefined> {
): Promise<
{ script: Buffer; redeemScript?: Buffer; pubkey: Buffer } | undefined
> {
if (!path) return undefined;

@@ -355,13 +298,11 @@ const pathElems = pathStringToArray(path);

}
const xpub = await this.client.getExtendedPubkey(false, pathElems);
const pubkey = pubkeyFromXpub(xpub);
const cond = accountType.spendingCondition([pubkey]);
return { cond, pubkey };
const xpub = await this.client.getPubkey(false, pathElems);
let pubkey = pubkeyFromXpub(xpub);
if (accountType == AccountType.p2tr) {
pubkey = pubkey.slice(1);
}
const script = outputScriptOf(pubkey, accountType);
return { ...script, pubkey };
}
/**
* Adds relevant data about an input to the psbt. This includes sequence,
* previous txid, output index, spent UTXO, redeem script for wrapped p2wpkh,
* public key and its derivation path.
*/
private async setInput(

@@ -378,20 +319,14 @@ psbt: PsbtV2,

accountType: AccountType,
masterFP: Buffer,
sigHashType?: number
masterFP: Buffer
): Promise<void> {
const inputTx = input[0];
const spentOutputIndex = input[1];
// redeemScript will be null for wrapped p2wpkh, we need to create it
// ourselves. But if set, it should be used.
const redeemScript = input[2] ? Buffer.from(input[2], "hex") : undefined;
const redeemScript = input[2];
const sequence = input[3];
if (sequence != undefined) {
if (sequence) {
psbt.setInputSequence(i, sequence);
}
if (sigHashType != undefined) {
psbt.setInputSighashType(i, sigHashType);
}
const inputTxBuffer = serializeTransaction(inputTx, true);
const inputTxid = crypto.hash256(inputTxBuffer);
const xpubBase58 = await this.client.getExtendedPubkey(false, pathElements);
const xpubBase58 = await this.client.getPubkey(false, pathElements);

@@ -401,16 +336,29 @@ const pubkey = pubkeyFromXpub(xpubBase58);

throw Error("Missing outputs array in transaction to sign");
const spentTxOutput = inputTx.outputs[spentOutputIndex];
const spendCondition: SpendingCondition = {
scriptPubKey: spentTxOutput.script,
redeemScript: redeemScript,
};
const spentOutput = { cond: spendCondition, amount: spentTxOutput.amount };
accountType.setInput(
i,
inputTxBuffer,
spentOutput,
[pubkey],
[pathElements]
);
const spentOutput = inputTx.outputs[spentOutputIndex];
if (accountType == AccountType.p2pkh) {
psbt.setInputNonWitnessUtxo(i, inputTxBuffer);
psbt.setInputBip32Derivation(i, pubkey, masterFP, pathElements);
} else if (accountType == AccountType.p2wpkh) {
psbt.setInputNonWitnessUtxo(i, inputTxBuffer);
psbt.setInputBip32Derivation(i, pubkey, masterFP, pathElements);
psbt.setInputWitnessUtxo(i, spentOutput.amount, spentOutput.script);
} else if (accountType == AccountType.p2wpkhWrapped) {
psbt.setInputNonWitnessUtxo(i, inputTxBuffer);
psbt.setInputBip32Derivation(i, pubkey, masterFP, pathElements);
if (!redeemScript) {
throw new Error("Missing redeemScript for p2wpkhWrapped input");
}
const expectedRedeemScript = createRedeemScript(pubkey);
if (redeemScript != expectedRedeemScript.toString("hex")) {
throw new Error("Unexpected redeemScript");
}
psbt.setInputRedeemScript(i, expectedRedeemScript);
psbt.setInputWitnessUtxo(i, spentOutput.amount, spentOutput.script);
} else if (accountType == AccountType.p2tr) {
const xonly = pubkey.slice(1);
psbt.setInputTapBip32Derivation(i, xonly, [], masterFP, pathElements);
psbt.setInputWitnessUtxo(i, spentOutput.amount, spentOutput.script);
}
psbt.setInputPreviousTxId(i, inputTxid);

@@ -420,25 +368,14 @@ psbt.setInputOutputIndex(i, spentOutputIndex);

/**
* This implements the "Signer" role of the BIP370 transaction signing
* process.
*
* It ssks the hardware device to sign the a psbt using the specified wallet
* policy. This method assumes BIP32 derived keys are used for all inputs, see
* comment in-line. The signatures returned from the hardware device is added
* to the appropriate input fields of the PSBT.
*/
private async signPsbt(
psbt: PsbtV2,
walletPolicy: WalletPolicy,
progressCallback: () => void
): Promise<void> {
walletPolicy: WalletPolicy
): Promise<string> {
const sigs: Map<number, Buffer> = await this.client.signPsbt(
psbt,
walletPolicy,
Buffer.alloc(32, 0),
progressCallback
Buffer.alloc(32, 0)
);
sigs.forEach((v, k) => {
// Note: Looking at BIP32 derivation does not work in the generic case,
// since some inputs might not have a BIP32-derived pubkey.
// Note: Looking at BIP32 derivation does not work in the generic case.
// some inputs might not have a BIP32-derived pubkey.
const pubkeys = psbt.getInputKeyDatas(k, psbtIn.BIP32_DERIVATION);

@@ -458,24 +395,92 @@ let pubkey;

});
finalize(psbt);
const serializedTx = extract(psbt);
return serializedTx.toString("hex");
}
}
function descrTemplFrom(
addressFormat: AddressFormat
): DefaultDescriptorTemplate {
if (addressFormat == "legacy") return "pkh(@0)";
if (addressFormat == "p2sh") return "sh(wpkh(@0))";
if (addressFormat == "bech32") return "wpkh(@0)";
if (addressFormat == "bech32m") return "tr(@0)";
enum AccountType {
p2pkh = "pkh(@0)",
p2wpkh = "wpkh(@0)",
p2wpkhWrapped = "sh(wpkh(@0))",
p2tr = "tr(@0)",
}
function createRedeemScript(pubkey: Buffer): Buffer {
const pubkeyHash = hashPublicKey(pubkey);
return Buffer.concat([Buffer.from("0014", "hex"), pubkeyHash]);
}
function outputScriptOf(
pubkey: Buffer,
accountType: AccountType
): { script: Buffer; redeemScript?: Buffer } {
const buf = new BufferWriter();
const pubkeyHash = hashPublicKey(pubkey);
let redeemScript: Buffer | undefined;
if (accountType == AccountType.p2pkh) {
buf.writeSlice(Buffer.of(OP_DUP, OP_HASH160, HASH_SIZE));
buf.writeSlice(pubkeyHash);
buf.writeSlice(Buffer.of(OP_EQUALVERIFY, OP_CHECKSIG));
} else if (accountType == AccountType.p2wpkhWrapped) {
redeemScript = createRedeemScript(pubkey);
const scriptHash = hashPublicKey(redeemScript);
buf.writeSlice(Buffer.of(OP_HASH160, HASH_SIZE));
buf.writeSlice(scriptHash);
buf.writeUInt8(OP_EQUAL);
} else if (accountType == AccountType.p2wpkh) {
buf.writeSlice(Buffer.of(0, HASH_SIZE));
buf.writeSlice(pubkeyHash);
} else if (accountType == AccountType.p2tr) {
console.log("Internal key: " + pubkey.toString("hex"));
const outputKey = getTaprootOutputKey(pubkey);
buf.writeSlice(Buffer.of(0x51, 32)); // push1, pubkeylen
buf.writeSlice(outputKey);
}
return { script: buf.buffer(), redeemScript };
}
function accountTypeFrom(addressFormat: AddressFormat): AccountType {
if (addressFormat == "legacy") return AccountType.p2pkh;
if (addressFormat == "p2sh") return AccountType.p2wpkhWrapped;
if (addressFormat == "bech32") return AccountType.p2wpkh;
if (addressFormat == "bech32m") return AccountType.p2tr;
throw new Error("Unsupported address format " + addressFormat);
}
function accountTypeFromArg(
arg: CreateTransactionArg,
psbt: PsbtV2,
masterFp: Buffer
): AccountType {
if (arg.additionals.includes("bech32m")) return new p2tr(psbt, masterFp);
if (arg.additionals.includes("bech32")) return new p2wpkh(psbt, masterFp);
if (arg.segwit) return new p2wpkhWrapped(psbt, masterFp);
return new p2pkh(psbt, masterFp);
function accountTypeFromArg(arg: CreateTransactionArg): AccountType {
if (arg.additionals.includes("bech32m")) return AccountType.p2tr;
if (arg.additionals.includes("bech32")) return AccountType.p2wpkh;
if (arg.segwit) return AccountType.p2wpkhWrapped;
return AccountType.p2pkh;
}
/*
The following two functions are copied from wallet-btc and adapte.
They should be moved to a library to avoid code reuse.
*/
function hashTapTweak(x: Buffer): Buffer {
// hash_tag(x) = SHA256(SHA256(tag) || SHA256(tag) || x), see BIP340
// See https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#specification
const h = crypto.sha256(Buffer.from("TapTweak", "utf-8"));
return crypto.sha256(Buffer.concat([h, h, x]));
}
function getTaprootOutputKey(internalPubkey: Buffer): Buffer {
if (internalPubkey.length != 32) {
throw new Error("Expected 32 byte pubkey. Got " + internalPubkey.length);
}
// A BIP32 derived key can be converted to a schnorr pubkey by dropping
// the first byte, which represent the oddness/evenness. In schnorr all
// pubkeys are even.
// https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki#public-key-conversion
const evenEcdsaPubkey = Buffer.concat([Buffer.of(0x02), internalPubkey]);
const tweak = hashTapTweak(internalPubkey);
// Q = P + int(hash_TapTweak(bytes(P)))G
const outputEcdsaKey = Buffer.from(pointAddScalar(evenEcdsaPubkey, tweak));
// Convert to schnorr.
const outputSchnorrKey = outputEcdsaKey.slice(1);
// Create address
return outputSchnorrKey;
}
import varuint from "varuint-bitcoin";
export function unsafeTo64bitLE(n: number): Buffer {
// we want to represent the input as a 8-bytes array
if (n > Number.MAX_SAFE_INTEGER) {
throw new Error("Can't convert numbers > MAX_SAFE_INT");
}
const byteArray = Buffer.alloc(8, 0);
for (let index = 0; index < byteArray.length; index++) {
const byte = n & 0xff;
byteArray[index] = byte;
n = (n - byte) / 256;
}
return byteArray;
}
export function unsafeFrom64bitLE(byteArray: Buffer): number {
let value = 0;
if (byteArray.length != 8) {
throw new Error("Expected Bufffer of lenght 8");
}
if (byteArray[7] != 0) {
throw new Error("Can't encode numbers > MAX_SAFE_INT");
}
if (byteArray[6] > 0x1f) {
throw new Error("Can't encode numbers > MAX_SAFE_INT");
}
for (let i = byteArray.length - 1; i >= 0; i--) {
value = value * 256 + byteArray[i];
}
return value;
}
export class BufferWriter {

@@ -55,5 +24,4 @@ private bufs: Buffer[] = [];

writeUInt64(i: number): void {
const bytes = unsafeTo64bitLE(i);
this.writeSlice(bytes);
writeUInt64(i: bigint): void {
this.write(8, (b) => b.writeBigUInt64LE(i, 0));
}

@@ -104,6 +72,6 @@

readUInt64(): number {
const buf = this.readSlice(8);
const n = unsafeFrom64bitLE(buf);
return n;
readUInt64(): bigint {
const result = this.buffer.readBigUInt64LE(this.offset);
this.offset += 8;
return result;
}

@@ -110,0 +78,0 @@

@@ -26,6 +26,2 @@ import Transport from "@ledgerhq/hw-transport";

/**
* This class encapsulates the APDU protocol documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md
*/
export class AppClient {

@@ -67,6 +63,3 @@ transport: Transport;

async getExtendedPubkey(
display: boolean,
pathElements: number[]
): Promise<string> {
async getPubkey(display: boolean, pathElements: number[]): Promise<string> {
if (pathElements.length > 6) {

@@ -78,3 +71,3 @@ throw new Error("Path too long. At most 6 levels allowed.");

Buffer.concat([
Buffer.from(display ? [1] : [0]),
Buffer.of(display ? 1 : 0),
pathElementsToBuffer(pathElements),

@@ -102,3 +95,3 @@ ])

const clientInterpreter = new ClientCommandInterpreter(() => {});
const clientInterpreter = new ClientCommandInterpreter();
clientInterpreter.addKnownList(

@@ -115,6 +108,6 @@ walletPolicy.keys.map((k) => Buffer.from(k, "ascii"))

Buffer.concat([
Buffer.from(display ? [1] : [0]),
Buffer.of(display ? 1 : 0),
walletPolicy.getWalletId(),
walletHMAC || Buffer.alloc(32, 0),
Buffer.from([change]),
Buffer.of(change),
addressIndexBuffer,

@@ -131,4 +124,3 @@ ]),

walletPolicy: WalletPolicy,
walletHMAC: Buffer | null,
progressCallback: () => void
walletHMAC: Buffer | null
): Promise<Map<number, Buffer>> {

@@ -141,3 +133,3 @@ const merkelizedPsbt = new MerkelizedPsbt(psbt);

const clientInterpreter = new ClientCommandInterpreter(progressCallback);
const clientInterpreter = new ClientCommandInterpreter();

@@ -191,4 +183,4 @@ // prepare ClientCommandInterpreter

async getMasterFingerprint(): Promise<Buffer> {
return this.makeRequest(BitcoinIns.GET_MASTER_FINGERPRINT, Buffer.from([]));
return this.makeRequest(BitcoinIns.GET_MASTER_FINGERPRINT, Buffer.of());
}
}
import { crypto } from "bitcoinjs-lib";
import { BufferReader } from "../buffertools";
import { createVarint } from "../varint";

@@ -25,3 +24,3 @@ import { hashLeaf, Merkle } from "./merkle";

constructor(results: Buffer[], private progressCallback: () => void) {
constructor(results: Buffer[]) {
super();

@@ -33,3 +32,2 @@ this.results = results;

this.results.push(Buffer.from(request.subarray(1)));
this.progressCallback();
return Buffer.from("");

@@ -52,3 +50,3 @@ }

execute(request: Buffer): Buffer {
const req = Buffer.from(request.subarray(1));
const req = request.subarray(1);

@@ -90,3 +88,3 @@ // we expect no more data to read

Buffer.from([payload_size]),
Buffer.from(known_preimage.subarray(0, payload_size)),
known_preimage.subarray(0, payload_size),
]);

@@ -112,22 +110,17 @@ }

execute(request: Buffer): Buffer {
const req = Buffer.from(request.subarray(1));
const req = request.subarray(1);
if (req.length < 32 + 1 + 1) {
throw new Error("Invalid request, expected at least 34 bytes");
if (req.length != 32 + 4 + 4) {
throw new Error("Invalid request, unexpected trailing data");
}
const reqBuf = new BufferReader(req);
const hash = reqBuf.readSlice(32);
// read the hash
const hash = Buffer.alloc(32);
for (let i = 0; i < 32; i++) {
hash[i] = req.readUInt8(i);
}
const hash_hex = hash.toString("hex");
let tree_size;
let leaf_index;
try {
tree_size = reqBuf.readVarInt();
leaf_index = reqBuf.readVarInt();
} catch (e: any) {
throw new Error(
"Invalid request, couldn't parse tree_size or leaf_index"
);
}
const tree_size = req.readUInt32BE(32);
const leaf_index = req.readUInt32BE(32 + 4);

@@ -182,3 +175,3 @@ const mt = this.known_trees.get(hash_hex);

execute(request: Buffer): Buffer {
const req = Buffer.from(request.subarray(1));
const req = request.subarray(1);

@@ -263,17 +256,2 @@ if (req.length != 32 + 32) {

/**
* This class will dispatch a client command coming from the hardware device to
* the appropriate client command implementation. Those client commands
* typically requests data from a merkle tree or merkelized maps.
*
* A ClientCommandInterpreter is prepared by adding the merkle trees and
* merkelized maps it should be able to serve to the hardware device. This class
* doesn't know anything about the semantics of the data it holds, it just
* serves merkle data. It doesn't even know in what context it is being
* executed, ie SignPsbt, getWalletAddress, etc.
*
* If the command yelds results to the client, as signPsbt does, the yielded
* data will be accessible after the command completed by calling getYielded(),
* which will return the yields in the same order as they came in.
*/
export class ClientCommandInterpreter {

@@ -289,5 +267,5 @@ private roots: Map<string, Merkle> = new Map();

constructor(progressCallback: () => void) {
constructor() {
const commands = [
new YieldCommand(this.yielded, progressCallback),
new YieldCommand(this.yielded),
new GetPreimageCommand(this.preimages, this.queue),

@@ -294,0 +272,0 @@ new GetMerkleLeafIndexCommand(this.roots),

import { MerkleMap } from "./merkleMap";
import { PsbtV2 } from "./psbtv2";
/**
* This class merkelizes a PSBTv2, by merkelizing the different
* maps of the psbt. This is used during the transaction signing process,
* where the hardware app can request specific parts of the psbt from the
* client code and be sure that the response data actually belong to the psbt.
* The reason for this is the limited amount of memory available to the app,
* so it can't always store the full psbt in memory.
*
* The signing process is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/bitcoin.md#sign_psbt
*/
export class MerkelizedPsbt extends PsbtV2 {

@@ -16,0 +5,0 @@ public globalMerkleMap: MerkleMap;

import { crypto } from "bitcoinjs-lib";
/**
* This class implements the merkle tree used by Ledger Bitcoin app v2+,
* which is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/merkle.md
*/
export class Merkle {

@@ -65,3 +60,3 @@ private leaves: Buffer[];

hashNode(left: Buffer, right: Buffer): Buffer {
return this.h(Buffer.concat([Buffer.from([1]), left, right]));
return this.h(Buffer.concat([Buffer.of(1), left, right]));
}

@@ -74,3 +69,3 @@ }

): Buffer {
return hashConcat(Buffer.from([0]), buf, hashFunction);
return hashConcat(Buffer.of(0), buf, hashFunction);
}

@@ -77,0 +72,0 @@

import { createVarint } from "../varint";
import { hashLeaf, Merkle } from "./merkle";
/**
* This implements "Merkelized Maps", documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/merkle.md#merkleized-maps
*
* A merkelized map consist of two merkle trees, one for the keys of
* a map and one for the values of the same map, thus the two merkle
* trees have the same shape. The commitment is the number elements
* in the map followed by the keys' merkle root followed by the
* values' merkle root.
*/
export class MerkleMap {

@@ -15,0 +5,0 @@ keys: Buffer[];

@@ -1,5 +0,5 @@

import { crypto } from "bitcoinjs-lib";
import { pathArrayToString } from "../bip32";
import { BufferWriter } from "../buffertools";
import { hashLeaf, Merkle } from "./merkle";
import { crypto } from "bitcoinjs-lib";
import { Merkle, hashLeaf } from "./merkle";

@@ -12,10 +12,2 @@ export type DefaultDescriptorTemplate =

/**
* The Bitcon hardware app uses a descriptors-like thing to describe
* how to construct output scripts from keys. A "Wallet Policy" consists
* of a "Descriptor Template" and a list of "keys". A key is basically
* a serialized BIP32 extended public key with some added derivation path
* information. This is documented at
* https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/wallet.md
*/
export class WalletPolicy {

@@ -22,0 +14,0 @@ descriptorTemplate: string;

import { BufferWriter } from "../buffertools";
import { PsbtV2 } from "./psbtv2";
/**
* This implements the "Transaction Extractor" role of BIP370 (PSBTv2
* https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#transaction-extractor). However
* the role is partially documented in BIP174 (PSBTv0
* https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#transaction-extractor).
*/
export function extract(psbt: PsbtV2): Buffer {

@@ -16,3 +10,3 @@ const tx = new BufferWriter();

if (isSegwit) {
tx.writeSlice(Buffer.from([0, 1]));
tx.writeSlice(Buffer.of(0, 1));
}

@@ -25,3 +19,3 @@ const inputCount = psbt.getGlobalInputCount();

tx.writeUInt32(psbt.getInputOutputIndex(i));
tx.writeVarSlice(psbt.getInputFinalScriptsig(i) ?? Buffer.from([]));
tx.writeVarSlice(psbt.getInputFinalScriptsig(i) ?? Buffer.of());
tx.writeUInt32(psbt.getInputSequence(i));

@@ -35,3 +29,3 @@ if (isSegwit) {

for (let i = 0; i < outputCount; i++) {
tx.writeUInt64(psbt.getOutputAmount(i));
tx.writeUInt64(BigInt(psbt.getOutputAmount(i)));
tx.writeVarSlice(psbt.getOutputScript(i));

@@ -38,0 +32,0 @@ }

@@ -5,14 +5,4 @@ import { BufferWriter } from "../buffertools";

/**
* This roughly implements the "input finalizer" role of BIP370 (PSBTv2
* https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki). However
* the role is documented in BIP174 (PSBTv0
* https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki).
*
* Verify that all inputs have a signature, and set inputFinalScriptwitness
* and/or inputFinalScriptSig depending on the type of the spent outputs. Clean
* fields that aren't useful anymore, partial signatures, redeem script and
* derivation paths.
*
* @param psbt The psbt with all signatures added as partial sigs, either
* through PSBT_IN_PARTIAL_SIG or PSBT_IN_TAP_KEY_SIG
* @param psbt The psbt with all signatures added as partial sigs, either through PSBT_IN_PARTIAL_SIG or PSBT_IN_TAP_KEY_SIG
*/

@@ -89,9 +79,2 @@ export function finalize(psbt: PsbtV2): void {

/**
* Deletes fields that are no longer neccesary from the psbt.
*
* Note, the spec doesn't say anything about removing ouput fields
* like PSBT_OUT_BIP32_DERIVATION_PATH and others, so we keep them
* without actually knowing why. I think we should remove them too.
*/
function clearFinalizedInput(psbt: PsbtV2, inputIndex: number) {

@@ -115,10 +98,2 @@ const keyTypes = [

/**
* Writes a script push operation to buf, which looks different
* depending on the size of the data. See
* https://en.bitcoin.it/wiki/Script#Constants
*
* @param buf the BufferWriter to write to
* @param data the Buffer to be pushed.
*/
function writePush(buf: BufferWriter, data: Buffer) {

@@ -125,0 +100,0 @@ if (data.length <= 75) {

/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import {
BufferReader,
BufferWriter,
unsafeFrom64bitLE,
unsafeTo64bitLE,
} from "../buffertools";
import { BufferReader, BufferWriter } from "../buffertools";

@@ -22,3 +17,2 @@ export enum psbtGlobal {

PARTIAL_SIG = 0x02,
SIGHASH_TYPE = 0x03,
REDEEM_SCRIPT = 0x04,

@@ -42,24 +36,6 @@ BIP32_DERIVATION = 0x06,

const PSBT_MAGIC_BYTES = Buffer.from([0x70, 0x73, 0x62, 0x74, 0xff]);
const PSBT_MAGIC_BYTES = Buffer.of(0x70, 0x73, 0x62, 0x74, 0xff);
export class NoSuchEntry extends Error {}
/**
* Implements Partially Signed Bitcoin Transaction version 2, BIP370, as
* documented at https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki
* and https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
*
* A psbt is a data structure that can carry all relevant information about a
* transaction through all stages of the signing process. From constructing an
* unsigned transaction to extracting the final serialized transaction ready for
* broadcast.
*
* This implementation is limited to what's needed in ledgerjs to carry out its
* duties, which means that support for features like multisig or taproot script
* path spending are not implemented. Specifically, it supports p2pkh,
* p2wpkhWrappedInP2sh, p2wpkh and p2tr key path spending.
*
* This class is made purposefully dumb, so it's easy to add support for
* complemantary fields as needed in the future.
*/
export class PsbtV2 {

@@ -139,10 +115,2 @@ protected globalMap: Map<string, Buffer> = new Map();

}
setInputSighashType(inputIndex: number, sigHashtype: number) {
this.setInput(inputIndex, psbtIn.SIGHASH_TYPE, b(), uint32LE(sigHashtype));
}
getInputSighashType(inputIndex: number): number | undefined {
const result = this.getInputOptional(inputIndex, psbtIn.SIGHASH_TYPE, b());
if (!result) return undefined;
return result.readUInt32LE(0);
}
setInputRedeemScript(inputIndex: number, redeemScript: Buffer) {

@@ -274,4 +242,5 @@ this.setInput(inputIndex, psbtIn.REDEEM_SCRIPT, b(), redeemScript);

getOutputAmount(outputIndex: number): number {
const buf = this.getOutput(outputIndex, psbtOut.AMOUNT, b());
return unsafeFrom64bitLE(buf);
return Number(
this.getOutput(outputIndex, psbtOut.AMOUNT, b()).readBigUInt64LE(0)
);
}

@@ -332,3 +301,3 @@ setOutputScript(outputIndex: number, scriptPubKey: Buffer) {

const buf = new BufferWriter();
buf.writeSlice(Buffer.from([0x70, 0x73, 0x62, 0x74, 0xff]));
buf.writeSlice(Buffer.of(0x70, 0x73, 0x62, 0x74, 0xff));
serializeMap(buf, this.globalMap);

@@ -383,3 +352,3 @@ this.inputMaps.forEach((map) => {

private setGlobal(keyType: KeyType, value: Buffer) {
const key = new Key(keyType, Buffer.from([]));
const key = new Key(keyType, Buffer.of());
this.globalMap.set(key.toString(), value);

@@ -401,2 +370,11 @@ }

}
private getMap(
index: number,
maps: Map<string, Buffer>[]
): Map<string, Buffer> {
if (maps[index]) {
return maps[index];
}
return (maps[index] = new Map());
}
private getInput(index: number, keyType: KeyType, keyData: Buffer): Buffer {

@@ -423,10 +401,8 @@ return get(this.inputMaps[index], keyType, keyData, false)!;

}
private getMap(
private getOutputOptional(
index: number,
maps: Map<string, Buffer>[]
): Map<string, Buffer> {
if (maps[index]) {
return maps[index];
}
return (maps[index] = new Map());
keyType: KeyType,
keyData: Buffer
): Buffer | undefined {
return get(this.outputMaps[index], keyType, keyData, true);
}

@@ -556,3 +532,3 @@ private encodeBip32Derivation(masterFingerprint: Buffer, path: number[]) {

function b(): Buffer {
return Buffer.from([]);
return Buffer.of();
}

@@ -574,3 +550,5 @@ function set(

function uint64LE(n: number): Buffer {
return unsafeTo64bitLE(n);
const b = Buffer.alloc(8);
b.writeBigUInt64LE(BigInt(n), 0);
return b;
}

@@ -577,0 +555,0 @@ function varint(n: number): Buffer {

@@ -1,2 +0,1 @@

import Transport from "@ledgerhq/hw-transport";
import {

@@ -7,6 +6,2 @@ openTransportReplayer,

import Btc from "../src/Btc";
import BtcNew from "../src/BtcNew";
import BtcOld, { AddressFormat } from "../src/BtcOld";
import { AppAndVersion, getAppAndVersion } from "../src/getAppAndVersion";
import { TestingClient } from "./newops/integrationtools";

@@ -110,20 +105,10 @@ test("btc.getWalletXpub", async () => {

/*eslint-disable */
const pubkeyParent =
"045d4a72237572a91e13818fa38cedabe6174569cc9a319012f75150d5c0a0639d54eafd13a68d079b7a67764800c6a981825ef52384f08c3925109188ab21bc09";
const addrParent = Buffer.from(
"1NjiCsVBuKDT62LmaUd7WZZZBK2gPAkisb",
"ascii"
).toString("hex");
const ccParent =
"8bd937d416de7020952cc8e2c99ce9ac7e01265e31ceb8e47bf9c37f46f8abbd";
const pubkeyParent = "045d4a72237572a91e13818fa38cedabe6174569cc9a319012f75150d5c0a0639d54eafd13a68d079b7a67764800c6a981825ef52384f08c3925109188ab21bc09";
const addrParent = Buffer.from("1NjiCsVBuKDT62LmaUd7WZZZBK2gPAkisb", "ascii").toString("hex");
const ccParent = "8bd937d416de7020952cc8e2c99ce9ac7e01265e31ceb8e47bf9c37f46f8abbd";
const responseParent = `41${pubkeyParent}22${addrParent}${ccParent}`;
const pubkeyAcc =
"04250dfdfb84c1efd160ed0e10ebac845d0e4b04277174630ba56de96bbd3afb21fc6c04ce0d5a0cbd784fdabc99d16269c27cf3842fe8440f1f21b8af900f0eaa";
const addrAcc = Buffer.from(
"16Y97ByhyboePhTYMMmFj1tq5Cy1bDq8jT",
"ascii"
).toString("hex");
const ccAcc =
"c071c6f2d05cbc9ea9a04951b238086ce1608cf00020c3cab85b36aac5fdd591";
const pubkeyAcc = "04250dfdfb84c1efd160ed0e10ebac845d0e4b04277174630ba56de96bbd3afb21fc6c04ce0d5a0cbd784fdabc99d16269c27cf3842fe8440f1f21b8af900f0eaa";
const addrAcc = Buffer.from("16Y97ByhyboePhTYMMmFj1tq5Cy1bDq8jT", "ascii").toString("hex");
const ccAcc = "c071c6f2d05cbc9ea9a04951b238086ce1608cf00020c3cab85b36aac5fdd591";
/*eslint-enable */

@@ -138,3 +123,3 @@ const responseAcc = `41${pubkeyAcc}22${addrAcc}${ccAcc}`;

=> e04000000d038000002c8000000080000011
<= ${responseAcc}9000
<= ${responseAcc}9000
`)

@@ -442,98 +427,1 @@ );

});
function testBackend(s: string): any {
return async () => {
return { publicKey: s, bitcoinAddress: "", chainCode: "" };
};
}
class TestBtc extends Btc {
n: BtcNew;
o: BtcOld;
constructor(public tr: Transport) {
super(tr);
this.n = new BtcNew(new TestingClient(tr));
this.n.getWalletPublicKey = testBackend("new");
this.o = new BtcOld(tr);
this.o.getWalletPublicKey = testBackend("old");
}
protected new(): BtcNew {
return this.n;
}
protected old(): BtcOld {
return this.o;
}
}
// test.each`
// a | b | expected
// ${1} | ${1} | ${2}
// ${1} | ${2} | ${3}
// ${2} | ${1} | ${3}
// `('returns $expected when $a is added $c', ({ a, c, expected }) => {
// expect(a + c).toBe(expected);
// });
test.each`
app | ver | path | format | display | exp
${"Bitcoin"} | ${"1.99.99"} | ${"m/44'/0'/1'"} | ${"bech32m"} | ${false} | ${""}
${"Bitcoin"} | ${"1.99.99"} | ${"m/44'/0'"} | ${"bech32m"} | ${false} | ${""}
${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'/1'"} | ${"bech32m"} | ${false} | ${"new"}
${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'"} | ${"bech32m"} | ${false} | ${"new"}
${"Bitcoin"} | ${"2.0.0-beta"} | ${"m/84'/1'/0'"} | ${"bech32"} | ${false} | ${"new"}
${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'/1'"} | ${"bech32"} | ${false} | ${"new"}
${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'"} | ${"bech32"} | ${undefined} | ${"old"}
${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'"} | ${"bech32"} | ${true} | ${"new"}
${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'/1'/0/0"} | ${"bech32"} | ${false} | ${"new"}
${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'/1'/1/0"} | ${"bech32"} | ${false} | ${"new"}
${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'/1'/1/0"} | ${"legacy"} | ${false} | ${"new"}
${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'/1'/1/0"} | ${"p2sh"} | ${false} | ${"new"}
${"Bitcoin"} | ${"2.0.0-alpha1"} | ${"m/44'/0'/1'/2/0"} | ${"bech32"} | ${false} | ${"old"}
`(
"dispatch $app $ver $path $format $display to $exp",
async ({ app, ver, path, format, display, exp }) => {
const appName = Buffer.from([app.length])
.toString("hex")
.concat(Buffer.from(app, "ascii").toString("hex"));
const appVersion = Buffer.from([ver.length])
.toString("hex")
.concat(Buffer.from(ver, "ascii").toString("hex"));
const resp = `01${appName}${appVersion}01029000`;
const tr = await openTransportReplayer(
RecordStore.fromString(`=> b001000000\n <= ${resp}`)
);
const btc = new TestBtc(tr);
try {
const key = await btc.getWalletPublicKey(path, {
format: format,
verify: display,
});
if (exp === "") {
expect(1).toEqual(0); // Allways fail. Don't know how to do that properly
}
expect(key.publicKey).toEqual(exp);
} catch (e: any) {
if (exp != "") {
throw e;
}
expect(exp).toEqual("");
}
}
);
// test("getWalletPublicKey compatibility for internal hardened keys", async () => {
// await testDispatch("Bitcoin", "1.99.99", "m/44'/0'/1'", "bech32m", "");
// await testDispatch("Bitcoin", "1.99.99", "m/44'/0'", "bech32m", "");
// await testDispatch("Bitcoin", "2.0.0-alpha1", "m/44'/0'/1'", "bech32m", "new");
// await testDispatch("Bitcoin", "2.0.0-alpha1", "m/44'/0'", "bech32m", "new");
// await testDispatch("Bitcoin", "2.0.0-alpha1", "m/44'/0'/1'", "bech32", "new");
// await testDispatch("Bitcoin", "2.0.0-alpha1", "m/44'/0'", "bech32", "old");
// });
async function testDispatch(
name: string,
version: string,
path: string,
addressFormat: AddressFormat | undefined,
exp: string
): Promise<void> {}
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable prettier/prettier */
import { openTransportReplayer, RecordStore } from "@ledgerhq/hw-transport-mocker";

@@ -12,5 +13,4 @@ import { TransportReplayer } from "@ledgerhq/hw-transport-mocker/lib/openTransportReplayer";

import { PsbtV2 } from "../../src/newops/psbtv2";
import { splitTransaction } from "../../src/splitTransaction";
import { StandardPurpose, addressFormatFromDescriptorTemplate, creatDummyXpub, masterFingerprint, runSignTransaction, TestingClient } from "./integrationtools";
import { CoreInput, CoreTx, p2pkh, p2tr, p2wpkh, wrappedP2wpkh, wrappedP2wpkhTwoInputs } from "./testtx";
import { AccountType, addressFormatFromDescriptorTemplate, creatDummyXpub, masterFingerprint, runSignTransaction, TestingClient } from "./integrationtools";
import { CoreTx, p2pkh, p2tr, p2wpkh, p2wpkhTwoInputs, wrappedP2wpkh, wrappedP2wpkhTwoInputs } from "./testtx";

@@ -45,3 +45,3 @@ test("getWalletPublicKey p2pkh", async () => {

function testPaths(type: StandardPurpose): { ins: string[], out?: string } {
function testPaths(type: AccountType): {ins: string[], out?: string} {
const basePath = `m/${type}/1'/0'/`;

@@ -56,3 +56,3 @@ const ins = [

];
return { ins };
return {ins};
}

@@ -62,48 +62,19 @@

const changePubkey = "037ed58c914720772c59f7a1e7e76fba0ef95d7c5667119798586301519b9ad2cf";
await runSignTransactionTest(p2pkh, StandardPurpose.p2pkh, changePubkey);
await runSignTransactionTest(p2pkh, AccountType.p2pkh, changePubkey);
});
test("Sign p2wpkh wrapped", async () => {
let changePubkey = "03efc6b990c1626d08bd176aab0e545a4f55c627c7ddee878d12bbbc46a126177a";
await runSignTransactionTest(wrappedP2wpkh, StandardPurpose.p2wpkhInP2sh, changePubkey);
await runSignTransactionTest(wrappedP2wpkh, AccountType.p2wpkhInP2sh, changePubkey);
changePubkey = "031175a985c56e310ce3496a819229b427a2172920fd20b5972dda62758c6def09";
await runSignTransactionTest(wrappedP2wpkhTwoInputs, StandardPurpose.p2wpkhInP2sh, changePubkey);
await runSignTransactionTest(wrappedP2wpkhTwoInputs, AccountType.p2wpkhInP2sh, changePubkey);
});
test("Sign p2wpkh", async () => {
await runSignTransactionTest(p2wpkh, StandardPurpose.p2wpkh);
await runSignTransactionTest(p2wpkh, AccountType.p2wpkh);
await runSignTransactionTest(p2wpkhTwoInputs, AccountType.p2wpkh);
});
test("Sign p2tr", async () => {
// This tx uses locktime, so this test verifies that locktime is propagated to/from
// the psbt correctly.
await runSignTransactionTest(p2tr, StandardPurpose.p2tr);
await runSignTransactionTest(p2tr, AccountType.p2tr);
});
test("Sign p2tr with sigHashType", async () => {
const testTx = JSON.parse(JSON.stringify(p2tr));
testTx.vin.forEach((input: CoreInput, index: number) => {
// Test SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, 0x83
const sig = input.txinwitness![0] + "83";
input.txinwitness = [sig];
})
const tx = await runSignTransactionNoVerification(testTx, StandardPurpose.p2tr);
// The verification of the sighashtype is done in MockClient.signPsbt
})
test("Sign p2tr sequence 0", async() => {
const testTx = JSON.parse(JSON.stringify(p2tr));
testTx.vin.forEach((input: CoreInput, index: number) => {
input.sequence = 0;
})
const tx = await runSignTransactionNoVerification(testTx, StandardPurpose.p2tr);
const txObj = splitTransaction(tx, true);
txObj.inputs.forEach((input, index) => {
expect(input.sequence.toString("hex")).toEqual("00000000");
})
})
async function runSignTransactionTest(testTx: CoreTx, accountType: StandardPurpose, changePubkey?: string) {
const tx = await runSignTransactionNoVerification(testTx, accountType, changePubkey);
expect(tx).toEqual(testTx.hex);
}
async function runSignTransactionNoVerification(testTx: CoreTx, accountType: StandardPurpose, changePubkey?: string): Promise<string> {
async function runSignTransactionTest(testTx: CoreTx, accountType: AccountType, changePubkey?: string) {
const [client, transport] = await createClient();

@@ -114,10 +85,11 @@ const accountXpub = "tpubDCwYjpDhUdPGP5rS3wgNg13mTrrjBuG8V9VpWbyptX6TRPbNoZVXsoVUSkCjmQ8jJycjuDKBb9eataSymXakTTaGifxR6kmVsfFehH1ZgJT";

if (changePubkey) {
paths.out = `m/${accountType}/1'/0'` + "/1/3";
paths.out = `m/${accountType}/1'/0'` + "/1/3";
client.mockGetPubkeyResponse(paths.out, creatDummyXpub(Buffer.from(changePubkey, "hex")));
}
const tx = await runSignTransaction(testTx, paths, client, transport);
expect(tx).toEqual(testTx.hex);
await transport.close();
return tx;
}
async function testGetWalletXpub(path: string, version = 0x043587cf) {

@@ -128,3 +100,3 @@ const [client] = await createClient();

const btc = new BtcNew(client);
const result = await btc.getWalletXpub({ path: path, xpubVersion: version });
const result = await btc.getWalletXpub({path: path, xpubVersion: version});
expect(result).toEqual(expectedXpub);

@@ -156,3 +128,3 @@ }

const addressFormat = addressFormatFromDescriptorTemplate(expectedDescriptorTemplate);
const result = await btcNew.getWalletPublicKey(path, { format: addressFormat });
const result = await btcNew.getWalletPublicKey(path, {format: addressFormat});
verifyGetWalletPublicKeyResult(result, keyXpub, "testaddress");

@@ -205,3 +177,3 @@

}
async getExtendedPubkey(display: boolean, pathElements: number[]): Promise<string> {
async getPubkey(display: boolean, pathElements: number[]): Promise<string> {
const path = pathArrayToString(pathElements);

@@ -234,19 +206,7 @@ const response = this.getPubkeyResponses.get(path);

async signPsbt(
psbt: PsbtV2,
_psbt: PsbtV2,
_walletPolicy: WalletPolicy,
_walletHMAC: Buffer | null,
_walletHMAC: Buffer | null
): Promise<Map<number, Buffer>> {
const sigs = this.yieldSigs.splice(0, 1)[0];
const sig0 = sigs.get(0)!;
if (sig0.length == 64) {
// Taproot may leave out sighash type, which defaults to 0x01 SIGHASH_ALL
return sigs;
}
const sigHashType = sig0.readUInt8(sig0.length - 1);
if (sigHashType != 0x01) {
for (let i = 0; i < psbt.getGlobalInputCount(); i++) {
expect(psbt.getInputSighashType(i)).toEqual(sigHashType);
}
}
return sigs;
return this.yieldSigs.splice(0, 1)[0];
}

@@ -253,0 +213,0 @@ private getWalletAddressKey(

/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable prettier/prettier */
import Transport from "@ledgerhq/hw-transport";

@@ -12,3 +13,3 @@ import bs58check from "bs58check";

DefaultDescriptorTemplate,
WalletPolicy,
WalletPolicy
} from "../../src/newops/policy";

@@ -18,8 +19,8 @@ import { Transaction } from "../../src/types";

export async function runSignTransaction(
testTx: CoreTx,
testPaths: { ins: string[]; out?: string },
client: TestingClient,
transport: Transport
): Promise<string> {
testTx: CoreTx,
testPaths: {ins: string[], out?: string},
client: TestingClient,
transport: Transport): Promise<string> {
const btcNew = new BtcNew(client);

@@ -30,6 +31,6 @@ // btc is needed to perform some functions like splitTransaction.

const additionals: string[] = [];
if (accountType == StandardPurpose.p2wpkh) {
if (accountType == AccountType.p2wpkh) {
additionals.push("bech32");
}
if (accountType == StandardPurpose.p2tr) {
if (accountType == AccountType.p2tr) {
additionals.push("bech32m");

@@ -43,9 +44,3 @@ }

const inputData = createInput(input, btc);
const pubkey = getPubkey(
index,
accountType,
testTx,
inputData[0],
inputData[1]
);
const pubkey = getPubkey(index, accountType, testTx, inputData[0], inputData[1]);
const mockXpub = creatDummyXpub(pubkey);

@@ -56,22 +51,12 @@ client.mockGetPubkeyResponse(path, mockXpub);

});
const sig0 = yieldSigs.get(0)!;
let sigHashType: number | undefined = sig0.readUInt8(sig0.length - 1);
if (sigHashType == 0x01) {
sigHashType = undefined;
}
client.mockSignPsbt(yieldSigs);
const outputWriter = new BufferWriter();
outputWriter.writeVarInt(testTx.vout.length);
testTx.vout.forEach((output) => {
outputWriter.writeUInt64(
Number.parseFloat((output.value * 100000000).toFixed(8))
);
outputWriter.writeVarSlice(Buffer.from(output.scriptPubKey.hex, "hex"));
testTx.vout.forEach(output => {
outputWriter.writeUInt64(BigInt(Number.parseFloat((output.value * 100000000).toFixed(8))));
outputWriter.writeVarSlice(Buffer.from(output.scriptPubKey.hex, "hex"));
});
const outputScriptHex = outputWriter.buffer().toString("hex");
let callbacks = "";
function logCallback(message: string) {
callbacks += new Date().toISOString() + " " + message + "\n";
}
const arg: CreateTransactionArg = {
const outputScriptHex = outputWriter.buffer().toString("hex");
const arg: CreateTransactionArg = {
inputs,

@@ -83,19 +68,9 @@ additionals,

lockTime: testTx.locktime,
sigHashType,
segwit: accountType != StandardPurpose.p2pkh,
onDeviceSignatureGranted: () => logCallback("CALLBACK: signature granted"),
onDeviceSignatureRequested: () =>
logCallback("CALLBACK: signature requested"),
onDeviceStreaming: (arg) => logCallback("CALLBACK: " + JSON.stringify(arg)),
segwit: accountType != AccountType.p2pkh,
};
logCallback("Start createPaymentTransactionNew");
const tx = await btcNew.createPaymentTransactionNew(arg);
logCallback("Done createPaymentTransactionNew");
// console.log(callbacks);
return tx;
}
};
export function addressFormatFromDescriptorTemplate(
descTemp: DefaultDescriptorTemplate
): AddressFormat {
export function addressFormatFromDescriptorTemplate(descTemp: DefaultDescriptorTemplate): AddressFormat {
if (descTemp == "tr(@0)") return "bech32m";

@@ -108,27 +83,18 @@ if (descTemp == "pkh(@0)") return "legacy";

export enum StandardPurpose {
export enum AccountType {
p2tr = "86'",
p2wpkh = "84'",
p2wpkhInP2sh = "49'",
p2pkh = "44'",
p2pkh = "44'"
}
function getPubkey(
inputIndex: number,
accountType: StandardPurpose,
testTx: CoreTx,
spentTx: Transaction,
spentOutputIndex: number
): Buffer {
function getPubkey(inputIndex: number, accountType: AccountType, testTx: CoreTx, spentTx: Transaction, spentOutputIndex: number): Buffer {
const scriptSig = Buffer.from(testTx.vin[inputIndex].scriptSig.hex, "hex");
if (accountType == StandardPurpose.p2pkh) {
return scriptSig.slice(scriptSig.length - 33);
if (accountType == AccountType.p2pkh) {
return scriptSig.slice(scriptSig.length-33);
}
if (accountType == StandardPurpose.p2tr) {
if (accountType == AccountType.p2tr) {
return spentTx.outputs![spentOutputIndex].script.slice(2, 34); // 32 bytes x-only pubkey
}
if (
accountType == StandardPurpose.p2wpkh ||
accountType == StandardPurpose.p2wpkhInP2sh
) {
if (accountType == AccountType.p2wpkh || accountType == AccountType.p2wpkhInP2sh) {
return Buffer.from(testTx.vin[inputIndex].txinwitness![1], "hex");

@@ -139,17 +105,11 @@ }

function getSignature(
testTxInput: CoreInput,
accountType: StandardPurpose
): Buffer {
function getSignature(testTxInput: CoreInput, accountType: AccountType): Buffer {
const scriptSig = Buffer.from(testTxInput.scriptSig.hex, "hex");
if (accountType == StandardPurpose.p2pkh) {
return scriptSig.slice(1, scriptSig.length - 34);
if (accountType == AccountType.p2pkh) {
return scriptSig.slice(1, scriptSig.length-34);
}
if (accountType == StandardPurpose.p2tr) {
if (accountType == AccountType.p2tr) {
return Buffer.from(testTxInput.txinwitness![0], "hex");
}
if (
accountType == StandardPurpose.p2wpkh ||
accountType == StandardPurpose.p2wpkhInP2sh
) {
if (accountType == AccountType.p2wpkh || accountType == AccountType.p2wpkhInP2sh) {
return Buffer.from(testTxInput.txinwitness![0], "hex");

@@ -160,3 +120,3 @@ }

function getAccountType(coreInput: CoreInput, btc: Btc): StandardPurpose {
function getAccountType(coreInput: CoreInput, btc: Btc): AccountType {
const spentTx = spentTxs[coreInput.txid];

@@ -170,27 +130,21 @@ if (!spentTx) {

if (script.length == 34 && script[0] == 0x51) {
return StandardPurpose.p2tr;
return AccountType.p2tr;
}
if (script.length == 22 && script[0] == 0x00) {
return StandardPurpose.p2wpkh;
return AccountType.p2wpkh;
}
if (script.length == 23) {
return StandardPurpose.p2wpkhInP2sh;
return AccountType.p2wpkhInP2sh;
}
return StandardPurpose.p2pkh;
return AccountType.p2pkh;
}
export function creatDummyXpub(pubkey: Buffer): string {
const xpubDecoded = bs58check.decode(
"tpubDHcN44A4UHqdHJZwBxgTbu8Cy87ZrZkN8tQnmJGhcijHqe4rztuvGcD4wo36XSviLmiqL5fUbDnekYaQ7LzAnaqauBb9RsyahsTTFHdeJGd"
);
const pubkey33 =
pubkey.length == 33 ? pubkey : Buffer.concat([Buffer.from([2]), pubkey]);
xpubDecoded.fill(pubkey33, xpubDecoded.length - 33);
const xpubDecoded = bs58check.decode("tpubDHcN44A4UHqdHJZwBxgTbu8Cy87ZrZkN8tQnmJGhcijHqe4rztuvGcD4wo36XSviLmiqL5fUbDnekYaQ7LzAnaqauBb9RsyahsTTFHdeJGd");
const pubkey33 = pubkey.length == 33 ? pubkey : Buffer.concat([Buffer.of(2), pubkey]);
xpubDecoded.fill(pubkey33, xpubDecoded.length-33);
return bs58check.encode(xpubDecoded);
}
function createInput(
coreInput: CoreInput,
btc: Btc
): [Transaction, number, string | null, number] {
function createInput(coreInput: CoreInput, btc: Btc): [Transaction, number, string, number] {
const spentTx = spentTxs[coreInput.txid];

@@ -201,8 +155,13 @@ if (!spentTx) {

const splitSpentTx = btc.splitTransaction(spentTx, true);
return [splitSpentTx, coreInput.vout, null, coreInput.sequence];
const scriptSig = coreInput.scriptSig;
let redeemScript;
if (scriptSig?.hex && scriptSig.hex.startsWith("160014")) {
redeemScript = scriptSig.hex.substring(2);
}
return [splitSpentTx, coreInput.vout, redeemScript, coreInput.sequence];
}
export const masterFingerprint = Buffer.from([1, 2, 3, 4]);
export const masterFingerprint = Buffer.of(1, 2, 3, 4);
export class TestingClient extends AppClient {
mockGetPubkeyResponse(_pathElements: string, _response: string): void {}
mockGetPubkeyResponse(_pathElements: string, _response: string): void {};
mockGetWalletAddressResponse(

@@ -213,4 +172,4 @@ _walletPolicy: WalletPolicy,

_response: string
): void {}
mockSignPsbt(_yieldSigs: Map<number, Buffer>): void {}
): void {};
mockSignPsbt(_yieldSigs: Map<number, Buffer>): void {};
}

@@ -6,3 +6,3 @@ import { Merkle } from "../../src/newops/merkle";

function leaf(n: number) {
return Buffer.from([0, n]);
return Buffer.of(0, n);
}

@@ -9,0 +9,0 @@ function merkleOf(count: number): Merkle {

@@ -240,2 +240,57 @@ /* eslint-disable prettier/prettier */

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const p2wpkhTwoInputs = {
"txid": "1913b7b5ffdcb5f32b9aca1f5eec2a189e7c66650f82b560eae211265fc995b7",
"hash": "c3439dcd3489373c586c7aed48c32f2b5d9c71aad24acd765a61684d98690a3f",
"version": 2,
"size": 388,
"vsize": 226,
"weight": 904,
"locktime": 0,
"vin": [
{
"txid": "5512d5788d4c26117f093de91223ef384c3fb22799810a92e3304bb6f0819224",
"vout": 1,
"scriptSig": {
"asm": "0014c1ac0d63d0258ea1b6fe90ef72d0c35d8d773dd3",
"hex": "160014c1ac0d63d0258ea1b6fe90ef72d0c35d8d773dd3"
},
"txinwitness": [
"30440220543617c5f4504dc29d34d2d06d0d7733dac4ec418b77c67feefb29f3f82ba3d80220690b784c52c3375f4ba9e64cc5c0aeb6a1b9fc6aadda0062905c06ce3bbba57501",
"02fb255ed920db5c2f507289202eb60a160e5a067ee7e30199a4ed81b74c22e441"
],
"sequence": 4294967295
},
{
"txid": "28ad5054e029252d72da37f13fce66212d7f7763845b4a8c4aaf78e897b2bf9f",
"vout": 1,
"scriptSig": {
"asm": "0014c1ac0d63d0258ea1b6fe90ef72d0c35d8d773dd3",
"hex": "160014c1ac0d63d0258ea1b6fe90ef72d0c35d8d773dd3"
},
"txinwitness": [
"3044022049e7f3015a33ccdb015fe3891667564fd37111272df57e58447645c7bad8fed0022074d1e93ba946453896d0f0bc500df3a1e0d5bb5ad10cd9906736d5fbaebadd5801",
"02fb255ed920db5c2f507289202eb60a160e5a067ee7e30199a4ed81b74c22e441"
],
"sequence": 4294967295
}
],
"vout": [
{
"value": 0.01800000,
"n": 0,
"scriptPubKey": {
"asm": "OP_DUP OP_HASH160 f73384bcc3951ab6a75541ff79a9a51f82056ed8 OP_EQUALVERIFY OP_CHECKSIG",
"hex": "76a914f73384bcc3951ab6a75541ff79a9a51f82056ed888ac",
"address": "n442v1DrXQNim9gjjctKjyGVoe717hNdtG",
"type": "pubkeyhash"
}
}
],
"hex": "02000000000102249281f0b64b30e3920a819927b23f4c38ef2312e93d097f11264c8d78d512550100000017160014c1ac0d63d0258ea1b6fe90ef72d0c35d8d773dd3ffffffff9fbfb297e878af4a8c4a5b8463777f2d2166ce3ff137da722d2529e05450ad280100000017160014c1ac0d63d0258ea1b6fe90ef72d0c35d8d773dd3ffffffff0140771b00000000001976a914f73384bcc3951ab6a75541ff79a9a51f82056ed888ac024730440220543617c5f4504dc29d34d2d06d0d7733dac4ec418b77c67feefb29f3f82ba3d80220690b784c52c3375f4ba9e64cc5c0aeb6a1b9fc6aadda0062905c06ce3bbba575012102fb255ed920db5c2f507289202eb60a160e5a067ee7e30199a4ed81b74c22e44102473044022049e7f3015a33ccdb015fe3891667564fd37111272df57e58447645c7bad8fed0022074d1e93ba946453896d0f0bc500df3a1e0d5bb5ad10cd9906736d5fbaebadd58012102fb255ed920db5c2f507289202eb60a160e5a067ee7e30199a4ed81b74c22e44100000000",
"blockhash": "00000000025a711e6cd4bce9138dc852232a4494afbf36d8bb80499a786da2a4",
"confirmations": 1,
"time": 1633944124,
"blocktime": 1633944124
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const wrappedP2wpkhTwoInputs = {

@@ -242,0 +297,0 @@ "txid": "c03119b538c78f56c8ce2e6cc5fc6998d447eeef42e34c12692764a3f1a3da7c",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc