--- /dev/null
+//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+import { APDU_CODES, LedgerResponse, LedgerTransport, STATUS_CODES, listenTimeout, openTimeout } from '.'
+import { Account } from '../account'
+import { Block } from '../block'
+import { BIP44_COIN_NANO, BIP44_PURPOSE, HARDENED_OFFSET } from '../constants'
+import { bytes, dec, hex } from '../convert'
+import { queue } from './queue'
+
+export async function _cache (transport: LedgerTransport, index: number = 0, block: Block): Promise<LedgerResponse> {
+ return queue(async () => {
+ try {
+ if (typeof index !== 'number' || index < 0 || index >= HARDENED_OFFSET) {
+ throw new TypeError('Invalid account index')
+ }
+ if (!(block instanceof Block)) {
+ throw new TypeError('Invalid block format')
+ }
+ if (!(block.link instanceof Uint8Array)) {
+ throw new TypeError('Invalid block link')
+ }
+ if (!(block.representative instanceof Account)) {
+ throw new TypeError('Invalid block link')
+ }
+ if (!block.signature) {
+ throw new ReferenceError('Cannot cache unsigned block')
+ }
+
+ const purpose = dec.toBytes(BIP44_PURPOSE + HARDENED_OFFSET, 4)
+ const coin = dec.toBytes(BIP44_COIN_NANO + HARDENED_OFFSET, 4)
+ const account = dec.toBytes(index + HARDENED_OFFSET, 4)
+ const previous = block.previous
+ const link = block.link
+ 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([APDU_CODES.bip32DerivationLevel, ...purpose, ...coin, ...account, ...previous, ...link, ...representative, ...balance, ...signature])
+
+ const t = await transport.create(openTimeout, listenTimeout)
+ const response = await t
+ .send(APDU_CODES.class, APDU_CODES.cacheBlock, APDU_CODES.paramUnused, APDU_CODES.paramUnused, data as Buffer)
+ .then((res: Buffer) => bytes.toDec(res))
+ .catch((err: any) => err.statusCode)
+ .finally(async () => await t.close()) as number
+
+ return { status: STATUS_CODES[response] }
+ } catch (err: any) {
+ console.error('Ledger.#cacheBlock()', err)
+ return { status: err.message }
+ }
+ })
+}
import { Rpc } from '../rpc'
import { Wallet } from '../wallet'
import { _account } from './account'
+import { _cache } from './cache'
import { _connect } from './connect'
import { queue } from './queue'
throw new TypeError('Data to be signed must be a string nonce or a Block', { cause: data })
}
if (frontier != null) {
- const { status } = await this.#cacheBlock(index, frontier)
+ const { status } = await _cache(this.#transport, index, frontier)
if (status !== 'OK') {
throw new Error('Failed to cache frontier block in ledger', { cause: status })
}
}
input = res.contents
}
- const { status } = await this.#cacheBlock(index, input)
+ const { status } = await _cache(this.#transport, index, input)
if (status !== 'OK') {
throw new Error('failed to cache frontier block in ledger', { cause: status })
}
}
}
- /**
- * Cache frontier block in device memory.
- *
- * @param {number} index - Account number
- * @param {any} block - Block data to cache
- * @returns Status of command
- */
- static async #cacheBlock (index: number = 0, block: Block): Promise<LedgerResponse> {
- return queue(async () => {
- try {
- if (typeof index !== 'number' || index < 0 || index >= HARDENED_OFFSET) {
- throw new TypeError('Invalid account index')
- }
- if (!(block instanceof Block)) {
- throw new TypeError('Invalid block format')
- }
- if (!(block.link instanceof Uint8Array)) {
- throw new TypeError('Invalid block link')
- }
- if (!(block.representative instanceof Account)) {
- throw new TypeError('Invalid block link')
- }
- if (!block.signature) {
- throw new ReferenceError('Cannot cache unsigned block')
- }
-
- const purpose = dec.toBytes(BIP44_PURPOSE + HARDENED_OFFSET, 4)
- const coin = dec.toBytes(BIP44_COIN_NANO + HARDENED_OFFSET, 4)
- const account = dec.toBytes(index + HARDENED_OFFSET, 4)
- const previous = block.previous
- const link = block.link
- 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([APDU_CODES.bip32DerivationLevel, ...purpose, ...coin, ...account, ...previous, ...link, ...representative, ...balance, ...signature])
-
- const transport = await this.#transport.create(openTimeout, listenTimeout)
- const response = await transport
- .send(APDU_CODES.class, APDU_CODES.cacheBlock, APDU_CODES.paramUnused, APDU_CODES.paramUnused, data as Buffer)
- .then((res: Buffer) => bytes.toDec(res))
- .catch((err: any) => err.statusCode)
- .finally(async () => await transport.close()) as number
-
- return { status: STATUS_CODES[response] }
- } catch (err: any) {
- console.error('Ledger.#cacheBlock()', err)
- return { status: err.message }
- }
- })
- }
-
/**
* Close the currently running app and return to the device dashboard.
*