Skip to content

Commit

Permalink
✅ (signer-eth) [DSDK-529]: Add missing TU for generic parser (#531)
Browse files Browse the repository at this point in the history
  • Loading branch information
aussedatlo authored Dec 3, 2024
2 parents 5f13e63 + ebd01e8 commit cb2dbe8
Show file tree
Hide file tree
Showing 11 changed files with 669 additions and 23 deletions.
5 changes: 5 additions & 0 deletions .changeset/famous-stingrays-smash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ledgerhq/device-signer-kit-ethereum": patch
---

Return an error if StartTransaction cmd does not contain a signature
5 changes: 5 additions & 0 deletions .changeset/great-hotels-boil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ledgerhq/device-signer-kit-ethereum": patch
---

Update error message for invalid chaincode
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
ApduResponse,
CommandResultFactory,
InvalidStatusWordError,
isSuccessCommandResult,
} from "@ledgerhq/device-management-kit";

Expand Down Expand Up @@ -183,15 +184,150 @@ describe("GetAddressCommand", () => {
);
});

describe("error handling", () => {
it("should return an error if the response is not successfull", () => {
describe("should return an error", () => {
it("when the response is not successfull", () => {
const response = new ApduResponse({
statusCode: Uint8Array.from([0x6d, 0x00]),
data: new Uint8Array(0),
});
const result = command.parseResponse(response);
expect(isSuccessCommandResult(result)).toBe(false);
});

it("when publicKeyLength is invalid", () => {
// GIVEN
const response = {
data: Uint8Array.from([]), // Invalid public key length
statusCode: Uint8Array.from([0x90, 0x00]), // Success status code
};

// WHEN
const result = command.parseResponse(response);

// THEN
if (isSuccessCommandResult(result)) {
fail("Expected an error");
} else {
expect(result.error).toBeInstanceOf(InvalidStatusWordError);
expect(result.error.originalError).toEqual(
new Error("Public key length is missing"),
);
}
});

it("when publicKey is invalid", () => {
// GIVEN
const response = {
data: Uint8Array.from([0x01]), // Invalid public key
statusCode: Uint8Array.from([0x90, 0x00]), // Success status code
};

// WHEN
const result = command.parseResponse(response);

// THEN
if (isSuccessCommandResult(result)) {
fail("Expected an error");
} else {
expect(result.error).toBeInstanceOf(InvalidStatusWordError);
expect(result.error.originalError).toEqual(
new Error("Public key is missing"),
);
}
});

it("when addressLength is invalid", () => {
// GIVEN
const response = {
data: Uint8Array.from([0x20, ...Array<number>(32).fill(0x02)]), // Invalid address length
statusCode: Uint8Array.from([0x90, 0x00]), // Success status code
};

// WHEN
const result = command.parseResponse(response);

// THEN
if (isSuccessCommandResult(result)) {
fail("Expected an error");
} else {
expect(result.error).toBeInstanceOf(InvalidStatusWordError);
expect(result.error.originalError).toEqual(
new Error("Ethereum address length is missing"),
);
}
});

it("when address is missing", () => {
// GIVEN
const response = {
data: Uint8Array.from([0x20, ...Array<number>(32).fill(0x02), 0x01]), // Invalid address
statusCode: Uint8Array.from([0x90, 0x00]), // Success status code
};

// WHEN
const result = command.parseResponse(response);

// THEN
if (isSuccessCommandResult(result)) {
fail("Expected an error");
} else {
expect(result.error).toBeInstanceOf(InvalidStatusWordError);
expect(result.error.originalError).toEqual(
new Error("Ethereum address is missing"),
);
}
});

it("when the address is invalid", () => {
// GIVEN
const response = {
data: Uint8Array.from([
0x20,
...Array<number>(32).fill(0x02),
0x01,
0x02,
]), // Invalid address
statusCode: Uint8Array.from([0x90, 0x00]), // Success status code
};

// WHEN
const result = command.parseResponse(response);

// THEN
if (isSuccessCommandResult(result)) {
fail("Expected an error");
} else {
expect(result.error).toBeInstanceOf(InvalidStatusWordError);
expect(result.error.originalError).toEqual(
new Error("Invalid Ethereum address"),
);
}
});

it("when chainCode is invalid", () => {
// GIVEN
const response = {
data: LNX_RESPONSE_DATA_GOOD_WITH_CHAIN_CODE.slice(0, -1), // Invalid chainCode
statusCode: Uint8Array.from([0x90, 0x00]), // Success status code
};

// WHEN
const commandWithChainCode = new GetAddressCommand({
...defaultArgs,
returnChainCode: true,
});
const result = commandWithChainCode.parseResponse(response);

// THEN
if (isSuccessCommandResult(result)) {
fail("Expected an error");
} else {
expect(result.error).toBeInstanceOf(InvalidStatusWordError);
expect(result.error.originalError).toEqual(
new Error("Invalid Chaincode"),
);
}
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export class GetAddressCommand
if (this.args.returnChainCode) {
if (parser.testMinimalLength(CHAIN_CODE_LENGTH) === false) {
return CommandResultFactory({
error: new InvalidStatusWordError("Invalid Ethereum address"),
error: new InvalidStatusWordError("Invalid Chaincode"),
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,91 @@
import {
type ApduResponse,
isSuccessCommandResult,
UnknownDeviceExchangeError,
} from "@ledgerhq/device-management-kit";

import {
ProvideEnumCommand,
type ProvideEnumCommandArgs,
} from "./ProvideEnumCommand";

describe("ProvideEnumCommand", () => {
describe("getApdu", () => {
it("", () => {
// TODO: Implement test
expect(true).toBe(true);
it("should return the raw APDU for the first chunk", () => {
// GIVEN
const args: ProvideEnumCommandArgs = {
data: Uint8Array.from([0x01, 0x02, 0x03]),
isFirstChunk: true,
};

// WHEN
const command = new ProvideEnumCommand(args);
const apdu = command.getApdu();

// THEN
expect(apdu.getRawApdu()).toStrictEqual(
Uint8Array.from([0xe0, 0x24, 0x01, 0x00, 0x03, 0x01, 0x02, 0x03]),
);
});

it("should return the raw APDU for the subsequent chunk", () => {
// GIVEN
const args: ProvideEnumCommandArgs = {
data: Uint8Array.from([0x04, 0x05, 0x06]),
isFirstChunk: false,
};

// WHEN
const command = new ProvideEnumCommand(args);
const apdu = command.getApdu();

// THEN
expect(apdu.getRawApdu()).toStrictEqual(
Uint8Array.from([0xe0, 0x24, 0x00, 0x00, 0x03, 0x04, 0x05, 0x06]),
);
});
});

describe("parseResponse", () => {
it("", () => {
// TODO: Implement test
expect(true).toBe(true);
it("should return an error if the response status code is invalid", () => {
// GIVEN
const response: ApduResponse = {
data: Uint8Array.from([]),
statusCode: Uint8Array.from([0x6d, 0x00]), // Invalid status code
};

// WHEN
const command = new ProvideEnumCommand({
data: new Uint8Array(0),
isFirstChunk: true,
});
const result = command.parseResponse(response);

// THEN
if (isSuccessCommandResult(result)) {
throw new Error("Expected an error");
} else {
expect(result.error).toBeDefined();
expect(result.error).toBeInstanceOf(UnknownDeviceExchangeError);
}
});

it("should return a success result if the response status code is valid", () => {
// GIVEN
const response: ApduResponse = {
data: Uint8Array.from([]),
statusCode: Uint8Array.from([0x90, 0x00]), // Success status code
};

// WHEN
const command = new ProvideEnumCommand({
data: new Uint8Array(0),
isFirstChunk: true,
});
const result = command.parseResponse(response);

// THEN
expect(isSuccessCommandResult(result)).toBe(true);
});
});
});
Original file line number Diff line number Diff line change
@@ -1,15 +1,95 @@
import {
type ApduResponse,
isSuccessCommandResult,
UnknownDeviceExchangeError,
} from "@ledgerhq/device-management-kit";

import {
ProvideTransactionFieldDescriptionCommand,
type ProvideTransactionFieldDescriptionCommandArgs,
} from "./ProvideTransactionFieldDescriptionCommand";

describe("ProvideTransactionFieldDescriptionCommand", () => {
describe("getApdu", () => {
it("", () => {
// TODO: Implement test
expect(true).toBe(true);
it("should return the raw APDU for the first chunk", () => {
// GIVEN
const args: ProvideTransactionFieldDescriptionCommandArgs = {
data: Uint8Array.from([0x01, 0x02, 0x03]),
isFirstChunk: true,
};

// WHEN
const command = new ProvideTransactionFieldDescriptionCommand(args);
const apdu = command.getApdu();

// THEN
expect(apdu.getRawApdu()).toStrictEqual(
Uint8Array.from([0xe0, 0x28, 0x01, 0x00, 0x03, 0x01, 0x02, 0x03]),
);
});

it("should return the raw APDU for the subsequent chunk", () => {
// GIVEN
const args: ProvideTransactionFieldDescriptionCommandArgs = {
data: Uint8Array.from([0x04, 0x05, 0x06]),
isFirstChunk: false,
};

// WHEN
const command = new ProvideTransactionFieldDescriptionCommand(args);
const apdu = command.getApdu();

// THEN
expect(apdu.getRawApdu()).toStrictEqual(
Uint8Array.from([0xe0, 0x28, 0x00, 0x00, 0x03, 0x04, 0x05, 0x06]),
);
});
});

describe("parseResponse", () => {
it("", () => {
// TODO: Implement test
expect(true).toBe(true);
it("should return an error if the response status code is invalid", () => {
// GIVEN
const response: ApduResponse = {
data: Uint8Array.from([]),
statusCode: Uint8Array.from([0x6d, 0x00]), // Invalid status code
};

// WHEN
const command = new ProvideTransactionFieldDescriptionCommand({
data: new Uint8Array(0),
isFirstChunk: true,
});
const result = command.parseResponse(response);

// THEN
if (isSuccessCommandResult(result)) {
throw new Error("Expected an error");
} else {
expect(result.error).toBeDefined();
expect(result.error).toBeInstanceOf(UnknownDeviceExchangeError);
}
});

it("should return a success result if the response status code is valid", () => {
// GIVEN
const response: ApduResponse = {
data: Uint8Array.from([]),
statusCode: Uint8Array.from([0x90, 0x00]), // Success status code
};

// WHEN
const command = new ProvideTransactionFieldDescriptionCommand({
data: new Uint8Array(0),
isFirstChunk: true,
});
const result = command.parseResponse(response);

// THEN
if (!isSuccessCommandResult(result)) {
throw new Error("Expected a success result");
} else {
expect(result.data).toBeUndefined();
}
});
});
});
Loading

0 comments on commit cb2dbe8

Please sign in to comment.