Skip to content

Commit

Permalink
feat: refactors trezor.js and tests to ts
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinRiggen authored and bucko13 committed Nov 6, 2023
1 parent a9cc4c4 commit e5a431b
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 91 deletions.
82 changes: 44 additions & 38 deletions src/trezor.test.js → src/trezor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function itThrowsAnErrorOnAnUnsuccessfulRequest(interactionBuilder) {
];
try {
await interaction.run();
} catch (e) {
} catch (e: any) {
expect(e.message).toMatch(/foobar/i);
}
});
Expand All @@ -80,15 +80,15 @@ describe("trezor", () => {
it("sets the default method to throw an error", async () => {
try {
await interactionBuilder().run();
} catch (e) {
} catch (e: any) {
expect(e.message).toMatch(/subclass of TrezorInteraction/i);
}
});
});

describe("TrezorGetMetadata", () => {
function interactionBuilder() {
return new TrezorGetMetadata({ network: MAINNET });
return new TrezorGetMetadata();
}

itHasStandardMessages(interactionBuilder);
Expand Down Expand Up @@ -144,7 +144,7 @@ describe("trezor", () => {
const interaction = interactionBuilder();
const [method, params] = interaction.connectParams();
expect(method).toEqual(TrezorConnect.getFeatures);
expect(params).toEqual({});
expect(params as any).toEqual({});
});
});

Expand Down Expand Up @@ -193,9 +193,9 @@ describe("trezor", () => {
const interaction = interactionBuilder();
const [method, params] = interaction.connectParams();
expect(method).toEqual(TrezorConnect.getPublicKey);
expect(params.path).toEqual(bip32Path);
expect(params.coin).toEqual(trezorCoin(MAINNET));
expect(params.crossChain).toBe(true);
expect((params as any).path).toEqual(bip32Path);
expect((params as any).coin).toEqual(trezorCoin(MAINNET));
expect((params as any).crossChain).toBe(true);
});
});

Expand All @@ -222,9 +222,9 @@ describe("trezor", () => {
const interaction = interactionBuilder();
const [method, params] = interaction.connectParams();
expect(method).toEqual(TrezorConnect.getPublicKey);
expect(params.path).toEqual(bip32Path);
expect(params.coin).toEqual(trezorCoin(MAINNET));
expect(params.crossChain).toBe(true);
expect((params as any).path).toEqual(bip32Path);
expect((params as any).coin).toEqual(trezorCoin(MAINNET));
expect((params as any).crossChain).toBe(true);
});
});

Expand All @@ -251,9 +251,9 @@ describe("trezor", () => {
const interaction = interactionBuilder();
const [method, params] = interaction.connectParams();
expect(method).toEqual(TrezorConnect.getPublicKey);
expect(params.path).toEqual(bip32Path);
expect(params.coin).toEqual(trezorCoin(MAINNET));
expect(params.crossChain).toBe(true);
expect((params as any).path).toEqual(bip32Path);
expect((params as any).coin).toEqual(trezorCoin(MAINNET));
expect((params as any).crossChain).toBe(true);
});
});

Expand Down Expand Up @@ -291,9 +291,11 @@ describe("trezor", () => {
const interaction = interactionBuilder();
const [method, params] = interaction.connectParams();
expect(method).toEqual(TrezorConnect.signTransaction);
expect(params.coin).toEqual(trezorCoin(fixture.network));
expect(params.inputs.length).toEqual(fixture.inputs.length);
expect(params.outputs.length).toEqual(fixture.outputs.length);
expect((params as any).coin).toEqual(trezorCoin(fixture.network));
expect((params as any).inputs.length).toEqual(fixture.inputs.length);
expect((params as any).outputs.length).toEqual(
fixture.outputs.length
);
// FIXME check inputs & output details
});
});
Expand All @@ -320,9 +322,9 @@ describe("trezor", () => {
const interaction = psbtInteractionBuilder(tx, keyDetails, false);
const [method, params] = interaction.connectParams();
expect(method).toEqual(TrezorConnect.signTransaction);
expect(params.coin).toEqual(trezorCoin(tx.network));
expect(params.inputs.length).toEqual(tx.inputs.length);
expect(params.outputs.length).toEqual(tx.outputs.length);
expect((params as any).coin).toEqual(trezorCoin(tx.network));
expect((params as any).inputs.length).toEqual(tx.inputs.length);
expect((params as any).outputs.length).toEqual(tx.outputs.length);

expect(interaction.parsePayload({ signatures: tx.signature })).toContain(
PSBT_MAGIC_B64
Expand All @@ -338,9 +340,9 @@ describe("trezor", () => {
const interaction = psbtInteractionBuilder(tx, keyDetails, true);
const [method, params] = interaction.connectParams();
expect(method).toEqual(TrezorConnect.signTransaction);
expect(params.coin).toEqual(trezorCoin(tx.network));
expect(params.inputs.length).toEqual(tx.inputs.length);
expect(params.outputs.length).toEqual(tx.outputs.length);
expect((params as any).coin).toEqual(trezorCoin(tx.network));
expect((params as any).inputs.length).toEqual(tx.inputs.length);
expect((params as any).outputs.length).toEqual(tx.outputs.length);
expect(
interaction.parsePayload({ signatures: ["3003foobar01"] })
).toEqual(["3003foobar01"]);
Expand All @@ -364,11 +366,11 @@ describe("trezor", () => {
const interaction = interactionBuilder();
const [method, params] = interaction.connectParams();
expect(method).toEqual(TrezorConnect.getAddress);
expect(params.path).toEqual(fixture.bip32Path);
expect(params.address).toEqual(fixture.address);
expect(params.showOnTrezor).toBe(true);
expect(params.coin).toEqual(trezorCoin(fixture.network));
expect(params.crossChain).toBe(true);
expect((params as any).path).toEqual(fixture.bip32Path);
expect((params as any).address).toEqual(fixture.address);
expect((params as any).showOnTrezor).toBe(true);
expect((params as any).coin).toEqual(trezorCoin(fixture.network));
expect((params as any).crossChain).toBe(true);
// FIXME check multisig details
});
});
Expand All @@ -387,15 +389,19 @@ describe("trezor", () => {
const interaction = interactionBuilder();
const [method, params] = interaction.connectParams();
expect(method).toEqual(TrezorConnect.getAddress);
expect(params.bundle[0].path).toEqual(fixture.bip32Path);
expect(params.bundle[0].showOnTrezor).toBe(false);
expect(params.bundle[0].coin).toEqual(trezorCoin(fixture.network));
expect(params.bundle[0].crossChain).toBe(true);
expect(params.bundle[1].path).toEqual(fixture.bip32Path);
expect(params.bundle[1].address).toEqual(fixture.address);
expect(params.bundle[1].showOnTrezor).toBe(true);
expect(params.bundle[1].coin).toEqual(trezorCoin(fixture.network));
expect(params.bundle[1].crossChain).toBe(true);
expect((params as any).bundle[0].path).toEqual(fixture.bip32Path);
expect((params as any).bundle[0].showOnTrezor).toBe(false);
expect((params as any).bundle[0].coin).toEqual(
trezorCoin(fixture.network)
);
expect((params as any).bundle[0].crossChain).toBe(true);
expect((params as any).bundle[1].path).toEqual(fixture.bip32Path);
expect((params as any).bundle[1].address).toEqual(fixture.address);
expect((params as any).bundle[1].showOnTrezor).toBe(true);
expect((params as any).bundle[1].coin).toEqual(
trezorCoin(fixture.network)
);
expect((params as any).bundle[1].crossChain).toBe(true);
// FIXME check multisig details
});
});
Expand Down Expand Up @@ -446,7 +452,7 @@ describe("trezor", () => {
describe("TrezorSignMessage", () => {
const _bip32Path = "m/45'/0'/0'/0'";

function interactionBuilder(bip32Path, message) {
function interactionBuilder(bip32Path = "", message = "") {
return new TrezorSignMessage({
network: MAINNET,
bip32Path: bip32Path || _bip32Path,
Expand Down Expand Up @@ -475,7 +481,7 @@ describe("trezor", () => {
const interaction = interactionBuilder();
const [method, params] = interaction.connectParams();
expect(method).toEqual(TrezorConnect.signMessage);
expect(params.path).toEqual(_bip32Path);
expect((params as any).path).toEqual(_bip32Path);
});
});
});
Expand Down
109 changes: 56 additions & 53 deletions src/trezor.js → src/trezor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
} from "unchained-bitcoin";
import { ECPair, payments } from "bitcoinjs-lib";

import { BitcoinNetwork } from "unchained-bitcoin";
import {
DirectKeystoreInteraction,
PENDING,
Expand Down Expand Up @@ -217,13 +218,10 @@ try {
* console.log(result); // someValue from payload
*/
export class TrezorInteraction extends DirectKeystoreInteraction {
/**
* Trezor interactions require knowing the bitcoin network they are
* for.
*
* @param {object} options - options argument
* @param {string} options.network - bitcoin network
*/
network: BitcoinNetwork;

trezorCoin: string;

constructor({ network }) {
super();
this.network = network;
Expand Down Expand Up @@ -295,11 +293,15 @@ export class TrezorInteraction extends DirectKeystoreInteraction {
});
}

const result = await method(params);
if (!result.success) {
throw new Error(result.payload.error);
if (typeof method === "function") {
const result = await method(params);
if (!result.success) {
throw new Error(result.payload.error);
}
return this.parsePayload(result.payload);
} else {
throw new Error("TrezorConnect method is not a function");
}
return this.parsePayload(result.payload);
}

/**
Expand Down Expand Up @@ -373,7 +375,7 @@ export class TrezorGetMetadata extends TrezorInteraction {
* @constructor
*/
constructor() {
super({});
super({ network: null });
}

/**
Expand Down Expand Up @@ -476,15 +478,13 @@ export class TrezorGetMetadata extends TrezorInteraction {
*
*/
export class TrezorExportHDNode extends TrezorInteraction {
/**
* Requires a BIP32 path to the node to export as well as which network.
*
* @param {object} options - options argument
* @param {string} options.network - bitcoin network
* @param {string} bip32Path - the BIP32 path for the HD node
* @param {boolean} includeXFP - return xpub with root fingerprint concatenated
*/
constructor({ network, bip32Path, includeXFP }) {
bip32Path: string;

includeXFP: boolean;

bip32ValidationErrorMessage: any;

constructor({ network, bip32Path, includeXFP = false }) {
super({ network });
this.bip32Path = bip32Path;
this.includeXFP = includeXFP;
Expand Down Expand Up @@ -542,7 +542,7 @@ export class TrezorExportHDNode extends TrezorInteraction {
throw new Error("Payload does not have two responses.");
}
let keyMaterial = "";
let rootFingerprint = null;
let rootFingerprint = "";
for (let i = 0; i < payload.length; i++) {
// Find the payload with bip32 = MULTISIG_ROOT to get xfp
if (payload[i].serializedPath === MULTISIG_ROOT) {
Expand Down Expand Up @@ -598,7 +598,7 @@ export class TrezorExportHDNode extends TrezorInteraction {
* // "03..."
*/
export class TrezorExportPublicKey extends TrezorExportHDNode {
constructor({ network, bip32Path, includeXFP }) {
constructor({ network, bip32Path, includeXFP = false }) {
super({
network,
bip32Path,
Expand Down Expand Up @@ -641,7 +641,7 @@ export class TrezorExportPublicKey extends TrezorExportHDNode {
* // "xpub..."
*/
export class TrezorExportExtendedPublicKey extends TrezorExportHDNode {
constructor({ network, bip32Path, includeXFP }) {
constructor({ network, bip32Path, includeXFP = false }) {
super({
network,
bip32Path,
Expand Down Expand Up @@ -715,16 +715,21 @@ export class TrezorExportExtendedPublicKey extends TrezorExportHDNode {
* @extends {module:trezor.TrezorInteraction}
*/
export class TrezorSignMultisigTransaction extends TrezorInteraction {
/**
* @param {object} options - options argument
* @param {string} options.network - bitcoin network
* @param {UTXO[]} [options.inputs] - inputs for the transaction
* @param {TransactionOutput[]} [options.outputs] - outputs for the transaction
* @param {string[]} [options.bip32Paths] - BIP32 paths on this device to sign with, one per each input
* @param {string} [options.psbt] - PSBT string encoded in base64
* @param {object} [options.keyDetails] - Signing Key Details (Fingerprint + bip32 prefix)
* @param {boolean} [options.returnSignatureArray] - return an array of signatures instead of a signed PSBT (useful for test suite)
*/
// network: BitcoinNetwork
inputs: any[];

outputs: any[];

bip32Paths: string[];

psbt?: string;

keyDetails: any;

returnSignatureArray?: boolean;

pubkeys?: any;

constructor({
network,
inputs,
Expand Down Expand Up @@ -872,16 +877,12 @@ export class TrezorSignMultisigTransaction extends TrezorInteraction {
* await interaction.run();
*/
export class TrezorConfirmMultisigAddress extends TrezorInteraction {
/**
* Most of the information required to confirm a multisig address
* lives in the `Multisig` object from `unchained-bitcoin`.
*
* @param {object} options - options argument
* @param {string} options.network - bitcoin network
* @param {string} options.bip32Path - BIP32 path to the public key on this device used in the multisig address
* @param {Multisig} options.multisig - multisig object
* @param {string} options.publicKey - optional public key to confirm
*/
bip32Path: string;

multisig: any;

publicKey: string;

constructor({ network, bip32Path, multisig, publicKey }) {
super({ network });
this.bip32Path = bip32Path;
Expand Down Expand Up @@ -1032,13 +1033,13 @@ export class TrezorConfirmMultisigAddress extends TrezorInteraction {
* @extends {module:trezor.TrezorInteraction}
*/
export class TrezorSignMessage extends TrezorInteraction {
/**
* @param {object} options - option argument
* @param {string} option.network - network
* @param {string} option.bip32Path - bip32 path on this device to sign with
* @param {string} option.message - hex-encoded string to sign
*/
constructor({ network, bip32Path, message }) {
bip32Path: string;

message: string;

bip32ValidationErrorMessage: any;

constructor({ network = "", bip32Path = "", message = "" }) {
super({ network });
this.bip32Path = bip32Path;
this.message = message;
Expand Down Expand Up @@ -1140,7 +1141,9 @@ function trezorInput(input, bip32Path) {
prev_hash: input.txid,
prev_index: input.index,
address_n: bip32PathToSequence(bip32Path),
...(input.amountSats && { amount: BigNumber(input.amountSats).toString() }),
...(input.amountSats && {
amount: new BigNumber(input.amountSats).toString(),
}),
};
}

Expand All @@ -1160,7 +1163,7 @@ function trezorPublicKey(publicKey) {

function trezorOutput(output) {
return {
amount: BigNumber(output.amountSats).toFixed(0),
amount: new BigNumber(output.amountSats).toFixed(0),
address: output.address,
script_type: "PAYTOADDRESS",
};
Expand Down

0 comments on commit e5a431b

Please sign in to comment.