From: Chris Duncan Date: Wed, 17 Sep 2025 15:00:22 +0000 (-0700) Subject: Avoid self references by using static this. Explictly type transport responses and... X-Git-Tag: v0.10.5~12^2~46 X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=3bd2710a1c7dc7a1fa6ac3dbb4cb6af4a0759939;p=libnemo.git Avoid self references by using static this. Explictly type transport responses and errors. Fix USB vendor ID reference. --- diff --git a/src/lib/ledger.ts b/src/lib/ledger.ts index 5649717..fa0de3d 100644 --- a/src/lib/ledger.ts +++ b/src/lib/ledger.ts @@ -1,8 +1,11 @@ //! SPDX-FileCopyrightText: 2025 Chris Duncan //! SPDX-License-Identifier: GPL-3.0-or-later +//@ts-expect-error import { default as TransportBLE } from '@ledgerhq/hw-transport-web-ble' +//@ts-expect-error import { default as TransportHID } from '@ledgerhq/hw-transport-webhid' +//@ts-expect-error import { default as TransportUSB } from '@ledgerhq/hw-transport-webusb' import { LedgerStatus, LedgerAccountResponse, LedgerResponse, LedgerSignResponse, LedgerVersionResponse } from '#types' import { Account } from './account' @@ -37,7 +40,7 @@ export class Ledger { paramUnused: 0x00 }) static #DERIVATION_PATH: Uint8Array = new Uint8Array([ - Ledger.#ADPU_CODES.bip32DerivationLevel, + this.#ADPU_CODES.bip32DerivationLevel, ...dec.toBytes(BIP44_PURPOSE + HARDENED_OFFSET, 4), ...dec.toBytes(BIP44_COIN_NANO + HARDENED_OFFSET, 4) ]) @@ -106,16 +109,16 @@ export class Ledger { throw new TypeError('Invalid account index') } const account = dec.toBytes(index + HARDENED_OFFSET, 4) - const data = new Uint8Array([...Ledger.#DERIVATION_PATH, ...account]) + const data = new Uint8Array([...this.#DERIVATION_PATH, ...account]) - const transport = await Ledger.#transport.create(Ledger.#openTimeout, Ledger.#listenTimeout) + const transport = await this.#transport.create(this.#openTimeout, this.#listenTimeout) const response = await transport - .send(Ledger.#ADPU_CODES.class, Ledger.#ADPU_CODES.account, show ? 1 : 0, Ledger.#ADPU_CODES.paramUnused, data as Buffer) - .catch(err => dec.toBytes(err.statusCode)) as Uint8Array + .send(this.#ADPU_CODES.class, this.#ADPU_CODES.account, show ? 1 : 0, this.#ADPU_CODES.paramUnused, data as Buffer) + .catch((err: any) => dec.toBytes(err.statusCode)) as Uint8Array await transport.close() const statusCode = bytes.toDec(response.slice(-2)) as number - const status = Ledger.#STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR' + const status = this.#STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR' if (status !== 'OK') { return { status, publicKey: null, address: null } } @@ -144,14 +147,14 @@ export class Ledger { */ static async connect (api?: 'hid' | 'ble' | 'usb'): Promise { if (api !== undefined) { - if (api === 'hid' && Ledger.#transport !== TransportHID) { - Ledger.#transport = TransportHID + if (api === 'hid' && this.#transport !== TransportHID) { + this.#transport = TransportHID } - if (api === 'ble' && Ledger.#transport !== TransportBLE) { - Ledger.#transport = TransportBLE + if (api === 'ble' && this.#transport !== TransportBLE) { + this.#transport = TransportBLE } - if (api === 'usb' && Ledger.#transport !== TransportUSB) { - Ledger.#transport = TransportUSB + if (api === 'usb' && this.#transport !== TransportUSB) { + this.#transport = TransportUSB } } try { @@ -219,13 +222,13 @@ export class Ledger { throw new RangeError(`Index outside allowed range 0-${HARDENED_OFFSET}`, { cause: index }) } if (frontier != null) { - const { status } = await Ledger.#cacheBlock(index, frontier) + const { status } = await this.#cacheBlock(index, frontier) if (status !== 'OK') { throw new Error('Failed to cache frontier block in ledger', { cause: status }) } } console.log('Waiting for signature confirmation on Ledger device...') - const { status, signature, hash } = await Ledger.#signBlock(index, block) + const { status, signature, hash } = await this.#signBlock(index, block) if (status !== 'OK') { throw new Error('Signing with ledger failed', { cause: status }) } @@ -306,7 +309,7 @@ export class Ledger { .sign(testWallet, 0) const testSignature = testSendBlock.signature try { - const ledgerSignature = await Ledger.sign(0, testSendBlock, testOpenBlock) + const ledgerSignature = await this.sign(0, testSendBlock, testOpenBlock) return ledgerSignature === testSignature } catch (err) { throw new Error('Failed to verify wallet', { cause: err }) @@ -345,16 +348,16 @@ export class Ledger { const representative = hex.toBytes(block.representative.publicKey, 32) const balance = hex.toBytes(block.balance.toString(16), 16) const signature = hex.toBytes(block.signature, 64) - const data = new Uint8Array([Ledger.#ADPU_CODES.bip32DerivationLevel, ...purpose, ...coin, ...account, ...previous, ...link, ...representative, ...balance, ...signature]) + const data = new Uint8Array([this.#ADPU_CODES.bip32DerivationLevel, ...purpose, ...coin, ...account, ...previous, ...link, ...representative, ...balance, ...signature]) - const transport = await Ledger.#transport.create(Ledger.#openTimeout, Ledger.#listenTimeout) + const transport = await this.#transport.create(this.#openTimeout, this.#listenTimeout) const response = await transport - .send(Ledger.#ADPU_CODES.class, Ledger.#ADPU_CODES.cacheBlock, Ledger.#ADPU_CODES.paramUnused, Ledger.#ADPU_CODES.paramUnused, data as Buffer) - .then(res => bytes.toDec(res)) - .catch(err => err.statusCode) as number + .send(this.#ADPU_CODES.class, this.#ADPU_CODES.cacheBlock, this.#ADPU_CODES.paramUnused, this.#ADPU_CODES.paramUnused, data as Buffer) + .then((res: Buffer) => bytes.toDec(res)) + .catch((err: any) => err.statusCode) as number await transport.close() - return { status: Ledger.#STATUS_CODES[response] } + return { status: this.#STATUS_CODES[response] } } /** @@ -371,17 +374,17 @@ export class Ledger { * @returns Status of command */ static async #close (): Promise { - const transport = await Ledger.#transport.create(Ledger.#openTimeout, Ledger.#listenTimeout) + const transport = await this.#transport.create(this.#openTimeout, this.#listenTimeout) const response = await transport - .send(0xb0, 0xa7, Ledger.#ADPU_CODES.paramUnused, Ledger.#ADPU_CODES.paramUnused) - .then(res => bytes.toDec(res)) - .catch(err => err.statusCode) as number - return new Promise(r => setTimeout(r, 1000, { status: Ledger.#STATUS_CODES[response] })) + .send(0xb0, 0xa7, this.#ADPU_CODES.paramUnused, this.#ADPU_CODES.paramUnused) + .then((res: Buffer) => bytes.toDec(res)) + .catch((err: any) => err.statusCode) as number + return new Promise(r => setTimeout(r, 1000, { status: this.#STATUS_CODES[response] })) } static #onConnectHid = async (e: HIDConnectionEvent): Promise => { console.log(e) - if (e.device?.vendorId === ledgerUSBVendorId) { + if (e.device?.vendorId === this.UsbVendorId) { console.log('Ledger connected via HID') await this.connect() this.#polling = setInterval(this.connect, 1000) @@ -393,7 +396,7 @@ export class Ledger { static #onDisconnectHid = async (e: HIDConnectionEvent): Promise => { console.log(e) - if (e.device?.vendorId === ledgerUSBVendorId) { + if (e.device?.vendorId === this.UsbVendorId) { console.log('Ledger disconnected via HID') clearInterval(this.#polling) await this.connect() @@ -406,7 +409,7 @@ export class Ledger { static #onConnectUsb = async (e: USBConnectionEvent): Promise => { console.log(e) - if (e.device?.vendorId === ledgerUSBVendorId) { + if (e.device?.vendorId === this.UsbVendorId) { console.log('Ledger connected via USB') await this.connect() this.#polling = setInterval(this.connect, 1000) @@ -418,7 +421,7 @@ export class Ledger { static #onDisconnectUsb = async (e: USBConnectionEvent): Promise => { console.log(e) - if (e.device?.vendorId === ledgerUSBVendorId) { + if (e.device?.vendorId === this.UsbVendorId) { console.log('Ledger disconnected via USB') clearInterval(this.#polling) await this.connect() @@ -444,12 +447,12 @@ export class Ledger { */ static async #open (): Promise { const name = new TextEncoder().encode('Nano') - const transport = await Ledger.#transport.create(Ledger.#openTimeout, Ledger.#listenTimeout) + const transport = await this.#transport.create(this.#openTimeout, this.#listenTimeout) const response = await transport - .send(0xe0, 0xd8, Ledger.#ADPU_CODES.paramUnused, Ledger.#ADPU_CODES.paramUnused, name as Buffer) - .then(res => bytes.toDec(res)) - .catch(err => err.statusCode) as number - return new Promise(r => setTimeout(r, 1000, { status: Ledger.#STATUS_CODES[response] })) + .send(0xe0, 0xd8, this.#ADPU_CODES.paramUnused, this.#ADPU_CODES.paramUnused, name as Buffer) + .then((res: Buffer) => bytes.toDec(res)) + .catch((err: any) => err.statusCode) as number + return new Promise(r => setTimeout(r, 1000, { status: this.#STATUS_CODES[response] })) } /** @@ -475,16 +478,16 @@ export class Ledger { const link = block.link const representative = hex.toBytes(block.representative.publicKey, 32) const balance = hex.toBytes(BigInt(block.balance).toString(16), 16) - const data = new Uint8Array([...Ledger.#DERIVATION_PATH, ...account, ...previous, ...link, ...representative, ...balance]) + const data = new Uint8Array([...this.#DERIVATION_PATH, ...account, ...previous, ...link, ...representative, ...balance]) - const transport = await Ledger.#transport.create(Ledger.#openTimeout, Ledger.#listenTimeout) + const transport = await this.#transport.create(this.#openTimeout, this.#listenTimeout) const response = await transport - .send(Ledger.#ADPU_CODES.class, Ledger.#ADPU_CODES.signBlock, Ledger.#ADPU_CODES.paramUnused, Ledger.#ADPU_CODES.paramUnused, data as Buffer) - .catch(err => dec.toBytes(err.statusCode)) as Uint8Array + .send(this.#ADPU_CODES.class, this.#ADPU_CODES.signBlock, this.#ADPU_CODES.paramUnused, this.#ADPU_CODES.paramUnused, data as Buffer) + .catch((err: any) => dec.toBytes(err.statusCode)) as Uint8Array await transport.close() const statusCode = bytes.toDec(response.slice(-2)) as number - const status = Ledger.#STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR' + const status = this.#STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR' if (response.byteLength === 2) { return { status, signature: null } @@ -516,16 +519,16 @@ export class Ledger { } const derivationAccount = dec.toBytes(index + HARDENED_OFFSET, 4) - const data = new Uint8Array([...Ledger.#DERIVATION_PATH, ...derivationAccount, ...nonce]) + const data = new Uint8Array([...this.#DERIVATION_PATH, ...derivationAccount, ...nonce]) - const transport = await Ledger.#transport.create(Ledger.#openTimeout, Ledger.#listenTimeout) + const transport = await this.#transport.create(this.#openTimeout, this.#listenTimeout) const response = await transport - .send(Ledger.#ADPU_CODES.class, Ledger.#ADPU_CODES.signNonce, Ledger.#ADPU_CODES.paramUnused, Ledger.#ADPU_CODES.paramUnused, data as Buffer) - .catch(err => dec.toBytes(err.statusCode)) as Uint8Array + .send(this.#ADPU_CODES.class, this.#ADPU_CODES.signNonce, this.#ADPU_CODES.paramUnused, this.#ADPU_CODES.paramUnused, data as Buffer) + .catch((err: any) => dec.toBytes(err.statusCode)) as Uint8Array await transport.close() const statusCode = bytes.toDec(response.slice(-2)) as number - const status = Ledger.#STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR' + const status = this.#STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR' if (response.byteLength === 2) { return { status, signature: null } @@ -547,14 +550,14 @@ export class Ledger { * @returns Status, process name, and version */ static async #version (): Promise { - const transport = await Ledger.#transport.create(Ledger.#openTimeout, Ledger.#listenTimeout) + const transport = await this.#transport.create(this.#openTimeout, this.#listenTimeout) const response = await transport - .send(0xb0, Ledger.#ADPU_CODES.version, Ledger.#ADPU_CODES.paramUnused, Ledger.#ADPU_CODES.paramUnused) - .catch(err => dec.toBytes(err.statusCode)) as Uint8Array + .send(0xb0, this.#ADPU_CODES.version, this.#ADPU_CODES.paramUnused, this.#ADPU_CODES.paramUnused) + .catch((err: any) => dec.toBytes(err.statusCode)) as Uint8Array await transport.close() const statusCode = bytes.toDec(response.slice(-2)) as number - const status = Ledger.#STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR' + const status = this.#STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR' if (status !== 'OK') { return { status, name: null, version: null } }