Skip to content

Commit

Permalink
add generic chunking (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
jleni authored May 26, 2024
1 parent 09de767 commit fd7ff62
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 9 deletions.
46 changes: 46 additions & 0 deletions src/app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { MockTransport } from '@ledgerhq/hw-transport-mocker'

import BaseApp from './app'
import { LedgerError } from './consts'
import { ResponsePayload } from './payload'
import { ResponseError } from './responseError'

describe('BaseApp', () => {
Expand Down Expand Up @@ -104,6 +105,51 @@ describe('BaseApp', () => {
})
})

describe('sendGenericChunk', () => {
class TestBaseApp extends BaseApp {
public async sendGenericChunk(ins: number, p2: number, chunkIdx: number, chunkNum: number, chunk: Buffer): Promise<ResponsePayload> {
return super.sendGenericChunk(ins, p2, chunkIdx, chunkNum, chunk)
}
}

it('should send a generic chunk and receive a response', async () => {
const responseBuffer = Buffer.concat([
Buffer.from([0x01, 0x02, 0x03]), // Example response data
Buffer.from([0x90, 0x00]), // Status code for no errors (0x9000)
])

const transport = new MockTransport(responseBuffer)
const app = new TestBaseApp(transport, params)
const chunk = Buffer.from('generic chunk data')
const response = await app.sendGenericChunk(0x00, 0x00, 1, 1, chunk)

expect(response.getCompleteBuffer()).toEqual(Buffer.from([0x01, 0x02, 0x03]))
})

it('should handle errors correctly', async () => {
const transport = new MockTransport(Buffer.alloc(0))
const expectedException = ResponseError.fromReturnCode(LedgerError.UnknownTransportError)

transport.exchange = jest.fn().mockRejectedValue(expectedException)
const app = new TestBaseApp(transport, params)

const someChunk = Buffer.from('generic chunk data')

await expect(app.sendGenericChunk(0x00, 0x00, 1, 1, someChunk)).rejects.toEqual(expectedException)
})

it('should handle specific error codes', async () => {
const errorBuffer = Buffer.from([0x6a, 0x80]) // Example error code (0x6a80)
const transport = new MockTransport(errorBuffer)
const app = new TestBaseApp(transport, params)

const someChunk = Buffer.from('generic chunk data')
const expectedException = ResponseError.fromReturnCode(0x6a80)

await expect(app.sendGenericChunk(0x00, 0x00, 1, 1, someChunk)).rejects.toEqual(expectedException)
})
})

describe('getVersion', () => {
it('should retrieve version information (5 bytes)', async () => {
const responseBuffer = Buffer.concat([
Expand Down
25 changes: 21 additions & 4 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,18 @@ export default class BaseApp {

/**
* Sends a chunk of data to the device and handles the response.
* This method determines the payload type based on the chunk index and sends the chunk to the device.
* It then processes the response from the device.
* Determines the payload type based on the chunk index and sends the chunk to the device.
* Processes the response from the device.
*
* @param ins - The instruction byte.
* @param p2 - P2 parameter byte.
* @param chunkIdx - The current chunk index.
* @param chunkNum - The total number of chunks.
* @param chunk - The chunk data as a buffer.
* @returns A promise that resolves to the processed response from the device.
* @throws {ResponseError} If the response from the device indicates an error.
*/
protected async signSendChunk(ins: number, chunkIdx: number, chunkNum: number, chunk: Buffer): Promise<ResponsePayload> {
protected async sendGenericChunk(ins: number, p2: number, chunkIdx: number, chunkNum: number, chunk: Buffer): Promise<ResponsePayload> {
let payloadType = PAYLOAD_TYPE.ADD

if (chunkIdx === 1) {
Expand All @@ -123,12 +124,28 @@ export default class BaseApp {

const statusList = [LedgerError.NoErrors, LedgerError.DataIsInvalid, LedgerError.BadKeyHandle]

const responseBuffer = await this.transport.send(this.CLA, ins, payloadType, 0, chunk, statusList)
const responseBuffer = await this.transport.send(this.CLA, ins, payloadType, p2, chunk, statusList)
const response = processResponse(responseBuffer)

return response
}

/**
* Sends a chunk of data to the device and handles the response.
* This method determines the payload type based on the chunk index and sends the chunk to the device.
* It then processes the response from the device.
*
* @param ins - The instruction byte.
* @param chunkIdx - The current chunk index.
* @param chunkNum - The total number of chunks.
* @param chunk - The chunk data as a buffer.
* @returns A promise that resolves to the processed response from the device.
* @throws {ResponseError} If the response from the device indicates an error.
*/
protected async signSendChunk(ins: number, chunkIdx: number, chunkNum: number, chunk: Buffer): Promise<ResponsePayload> {
return this.sendGenericChunk(ins, 0, chunkIdx, chunkNum, chunk)
}

/**
* Retrieves the version information from the device.
* @returns A promise that resolves to the version information.
Expand Down
5 changes: 1 addition & 4 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,7 @@ export function processResponse(responseRaw: Buffer): ResponsePayload {
}

// Construct and throw an error object with details
throw {
returnCode: returnCode,
errorMessage: errorMessage,
} as ResponseError
throw new ResponseError(returnCode, errorMessage)
}

/**
Expand Down
1 change: 0 additions & 1 deletion src/responseError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export class ResponseError extends Error {
super(errorMessage)
this.errorMessage = errorMessage
this.returnCode = returnCode
this.name = 'ResponseReturnCode'
}

/**
Expand Down

0 comments on commit fd7ff62

Please sign in to comment.