From 281623823293a47f10d244766c6a44f7eaf855a4 Mon Sep 17 00:00:00 2001 From: Kevin Riggen Date: Fri, 15 Sep 2023 10:17:58 -1000 Subject: [PATCH] feat: refactors bcur and coldcard to typescript --- src/{bcur.test.js => bcur.test.ts} | 0 src/{bcur.js => bcur.ts} | 36 ++++++++-------- src/{coldcard.test.js => coldcard.test.ts} | 14 +++++-- src/{coldcard.js => coldcard.ts} | 48 ++++++++++++++++++---- src/types/index.ts | 7 ++++ 5 files changed, 78 insertions(+), 27 deletions(-) rename src/{bcur.test.js => bcur.test.ts} (100%) rename src/{bcur.js => bcur.ts} (92%) rename src/{coldcard.test.js => coldcard.test.ts} (98%) rename src/{coldcard.js => coldcard.ts} (96%) diff --git a/src/bcur.test.js b/src/bcur.test.ts similarity index 100% rename from src/bcur.test.js rename to src/bcur.test.ts diff --git a/src/bcur.js b/src/bcur.ts similarity index 92% rename from src/bcur.js rename to src/bcur.ts index d0a737b1..17d0b9f3 100644 --- a/src/bcur.js +++ b/src/bcur.ts @@ -27,17 +27,20 @@ import { encodeUR, smartDecodeUR } from "./vendor/bcur"; * const encoder = BCUREncoder(hexString); * console.log(encoder.parts()) * // [ "ur:...", "ur:...", ... ] - * + * * */ export class BCUREncoder { + hexString: string; + + fragmentCapacity: number; /** * Create a new encoder. * * @param {string} hexString a hex string to encode * @param {int} fragmentCapacity passed to internal bcur implementation - * + * */ constructor(hexString, fragmentCapacity = 200) { this.hexString = hexString; @@ -48,12 +51,11 @@ export class BCUREncoder { * Return all UR parts. * * @returns {string[]} array of BC UR strings - * + * */ parts() { return encodeUR(this.hexString, this.fragmentCapacity); } - } /** @@ -84,16 +86,20 @@ export class BCUREncoder { * * // Data can be passed back to the calling application * console.log(decoder.data()); // "deadbeef" - * + * * } else { * * // Errors can be passed back to the calling application * console.log(decoder.errorMessage()); * } - * - * + * + * */ export class BCURDecoder { + // TODO: type these + error: any; + + summary: any; constructor() { this.reset(); @@ -112,7 +118,7 @@ export class BCURDecoder { current: 0, length: 0, workloads: [], - result: '', + result: "", }; this.error = null; } @@ -127,12 +133,11 @@ export class BCURDecoder { */ receivePart(part) { try { - const workloads = this.summary.workloads.includes(part) ? this.summary.workloads : [ - ...this.summary.workloads, - part, - ]; + const workloads = this.summary.workloads.includes(part) + ? this.summary.workloads + : [...this.summary.workloads, part]; this.summary = smartDecodeUR(workloads); - } catch(e) { + } catch (e) { this.error = e; } } @@ -155,12 +160,12 @@ export class BCURDecoder { * ... * console.log(decoder.progress()) * // { totalParts: 10, partsReceived: 3 } - * + * */ progress() { const totalParts = this.summary.length; const partsReceived = this.summary.current; - return {totalParts, partsReceived}; + return { totalParts, partsReceived }; } /** @@ -210,5 +215,4 @@ export class BCURDecoder { return null; } } - } diff --git a/src/coldcard.test.js b/src/coldcard.test.ts similarity index 98% rename from src/coldcard.test.js rename to src/coldcard.test.ts index d6675ada..2b80919f 100644 --- a/src/coldcard.test.js +++ b/src/coldcard.test.ts @@ -22,7 +22,7 @@ const { multisigs, transactions } = TEST_FIXTURES; const { nodes } = TEST_FIXTURES.keys.open_source; describe("ColdcardExportPublicKey", () => { - function interactionBuilder({ bip32Path, network }) { + function interactionBuilder({ bip32Path = "", network = "" }) { return new ColdcardExportPublicKey({ bip32Path, network, @@ -274,7 +274,7 @@ describe("ColdcardExportPublicKey", () => { }); describe("ColdcardExportExtendedPublicKey", () => { - function interactionBuilder({ bip32Path, network }) { + function interactionBuilder({ bip32Path = "", network = "" }) { return new ColdcardExportExtendedPublicKey({ bip32Path, network, @@ -492,7 +492,13 @@ describe("ColdcardExportExtendedPublicKey", () => { }); describe("ColdcardSignMultisigTransaction", () => { - function interactionBuilder({ network, inputs, outputs, bip32Paths, psbt }) { + function interactionBuilder({ + network = "", + inputs = [], + outputs = [], + bip32Paths = [], + psbt = "", + }) { return new ColdcardSignMultisigTransaction({ network, inputs, @@ -648,7 +654,7 @@ describe("ColdcardSignMultisigTransaction", () => { }); describe("ColdcardMultisigWalletConfig", () => { - let jsonConfigCopy = ""; + let jsonConfigCopy: any = {}; beforeEach(() => { // runs before each test in this block diff --git a/src/coldcard.js b/src/coldcard.ts similarity index 96% rename from src/coldcard.js rename to src/coldcard.ts index c5a5c937..6ee5553b 100644 --- a/src/coldcard.js +++ b/src/coldcard.ts @@ -31,6 +31,7 @@ import { ERROR, } from "./interaction"; import { P2SH, P2SH_P2WSH, P2WSH } from "unchained-bitcoin"; +import { BitcoinNetwork } from "unchained-bitcoin"; export const COLDCARD = "coldcard"; // Our constants use 'P2SH-P2WSH', their file uses 'P2SH_P2WSH' :\ @@ -64,6 +65,14 @@ class ColdcardMultisigSettingsFileParser extends ColdcardInteraction { * @param {string} options.network - bitcoin network (needed for derivations) * @param {string} options.bip32Path - bip32Path to interrogate */ + network: BitcoinNetwork; + + bip32Path: string; + + bip32ValidationErrorMessage: any; + + bip32ValidationError: string; + constructor({ network, bip32Path }) { super(); if ([MAINNET, TESTNET].find((net) => net === network)) { @@ -242,7 +251,8 @@ class ColdcardMultisigSettingsFileParser extends ColdcardInteraction { // because the xpub includes its parent's fingerprint. let xfpFromWithinXpub = xpubClass.depth === 1 - ? fingerprintToFixedLengthHex(xpubClass.parentFingerprint) + ? xpubClass.parentFingerprint && + fingerprintToFixedLengthHex(xpubClass.parentFingerprint) : null; // Sanity check if you send in a depth one xpub, we should get the same fingerprint @@ -275,11 +285,13 @@ class ColdcardMultisigSettingsFileParser extends ColdcardInteraction { */ deriveDeeperXpubIfNecessary(result) { const knownColdcardChroot = this.chrootForBIP32Path(this.bip32Path); - let relativePath = getRelativeBIP32Path( - knownColdcardChroot, - this.bip32Path - ); - let addressType = COLDCARD_BASE_BIP32_PATHS[knownColdcardChroot]; + let relativePath = + knownColdcardChroot && + getRelativeBIP32Path(knownColdcardChroot, this.bip32Path); + let addressType; + if (knownColdcardChroot !== null) { + addressType = COLDCARD_BASE_BIP32_PATHS[knownColdcardChroot]; + } // result could have p2wsh_p2sh or p2sh_p2wsh based on firmware version. Blah! if (addressType.includes("_") && !result[addressType.toLowerCase()]) { // Firmware < v3.2.0 @@ -293,7 +305,7 @@ class ColdcardMultisigSettingsFileParser extends ColdcardInteraction { ? convertExtendedPublicKey(result[addressType.toLowerCase()], prefix) : result[addressType.toLowerCase()]; - return relativePath.length + return relativePath && relativePath.length ? deriveChildExtendedPublicKey(baseXpub, relativePath, this.network) : baseXpub; } @@ -419,6 +431,16 @@ export class ColdcardSignMultisigTransaction extends ColdcardInteraction { * @param {array} options.bip32Paths - BIP32 paths * @param {object} [options.psbt] - PSBT of the transaction to sign, include it or we will generate it */ + network: string; + + psbt: any; + + inputs: any[]; + + outputs: any[]; + + bip32Paths: string[]; + constructor({ network, inputs, outputs, bip32Paths, psbt }) { super(); this.network = network; @@ -550,6 +572,18 @@ export class ColdcardSignMultisigTransaction extends ColdcardInteraction { * */ export class ColdcardMultisigWalletConfig { + jsonConfig: any; + + name: string; + + requiredSigners: number; + + totalSigners: number; + + addressType: string; + + extendedPublicKeys: any[]; + constructor({ jsonConfig }) { if (typeof jsonConfig === "object") { this.jsonConfig = jsonConfig; diff --git a/src/types/index.ts b/src/types/index.ts index 2a9db332..fec19756 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -54,3 +54,10 @@ export interface MultisigWalletConfig { // signers in the quorum (equal to extendedPublicKeys.length) ledgerPolicyHmacs?: LedgerPolicyHmacs[]; } + +// This is currently only used in bcur.ts +export interface Summary { + success: boolean; + current: number; + length: number; +}