Skip to content

Commit

Permalink
create local serializePath due to cosmos specification
Browse files Browse the repository at this point in the history
  • Loading branch information
chcmedeiros committed Jun 19, 2024
1 parent 09f11ba commit 00e5ea3
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 13 deletions.
77 changes: 67 additions & 10 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ import { ResponseSign, type ResponseAddress, type ResponsePubkey } from "./types
import { CLA, P1_VALUES, P2_VALUES, PKLEN } from "./consts";
import type Transport from "@ledgerhq/hw-transport";
import BaseApp, {
HARDENED,
INSGeneric,
LedgerError,
PAYLOAD_TYPE,
ResponseError,
ResponsePayload,
processErrorResponse,
processResponse,
Expand Down Expand Up @@ -70,8 +72,59 @@ export default class CosmosApp extends BaseApp {
return buf.getCompleteBuffer();
}

private serializeCosmosPath(path: string) {
if (typeof path !== "string") {
// NOTE: this is probably unnecessary
throw new ResponseError(
LedgerError.GenericError,
"Path should be a string (e.g \"m/44'/461'/5'/0/3\")",
);
}

if (!path.startsWith("m/")) {
throw new ResponseError(
LedgerError.GenericError,
'Path should start with "m/" (e.g "m/44\'/461\'/5\'/0/3")',
);
}

const pathArray = path.split("/");
pathArray.shift(); // remove "m"

if (
this.REQUIRED_PATH_LENGTHS &&
this.REQUIRED_PATH_LENGTHS.length > 0 &&
!this.REQUIRED_PATH_LENGTHS.includes(pathArray.length)
) {
throw new ResponseError(LedgerError.GenericError, "Invalid path length. (e.g \"m/44'/5757'/5'/0/3\")");
}

const buf = new ByteStream();
pathArray.forEach((child: string, i: number) => {
let value = 0;

if (child.endsWith("'")) {
value += HARDENED;
child = child.slice(0, -1);
}

const numChild = Number(child);

if (Number.isNaN(numChild)) {
throw new ResponseError(
LedgerError.GenericError,
`Invalid path : ${child} is not a number. (e.g "m/44'/461'/5'/0/3")`,
);
}
value += numChild;
buf.appendUint32(value);
});

return buf.getCompleteBuffer();
}

async publicKey(path: string): Promise<ResponsePubkey> {
const serializedPath = await this.serializePath(path);
const serializedPath = await this.serializeCosmosPath(path);
const data = Buffer.concat([this.serializeHRP("cosmos"), serializedPath]);

try {
Expand All @@ -88,13 +141,13 @@ export default class CosmosApp extends BaseApp {
}

async getAddressAndPubKey(path: string, hrp: string): Promise<ResponseAddress> {
try {
const serializedPath = await this.serializePath(path);
const data = Buffer.concat([this.serializeHRP(hrp), serializedPath]);
const serializedPath = await this.serializeCosmosPath(path);
const data = Buffer.concat([this.serializeHRP(hrp), serializedPath]);

try {
const responseBuffer = await this.transport.send(
this.CLA,
this.INS.GET_ADDR,
this.INS.GET_ADDR_SECP256K1,
P1_VALUES.ONLY_RETRIEVE,
0,
data,
Expand All @@ -114,20 +167,19 @@ export default class CosmosApp extends BaseApp {
}

async showAddressAndPubKey(path: string, hrp: string): Promise<ResponseAddress> {
const serializedPath = await this.serializePath(path);
const serializedPath = await this.serializeCosmosPath(path);
const data = Buffer.concat([this.serializeHRP(hrp), serializedPath]);

try {
const responseBuffer = await this.transport.send(
this.CLA,
this.INS.GET_ADDR,
this.INS.GET_ADDR_SECP256K1,
P1_VALUES.SHOW_ADDRESS_IN_DEVICE,
0,
data,
);

const response = processResponse(responseBuffer);

const compressed_pk = response.readBytes(PKLEN);
const bech32_address = response.readBytes(response.length()).toString();

Expand All @@ -150,7 +202,7 @@ export default class CosmosApp extends BaseApp {
}

async prepareChunks_hrp(path: string, buffer: Buffer, hrp: string | undefined) {
const serializedPath = await this.serializePath(path);
const serializedPath = await this.serializeCosmosPath(path);
const firstChunk =
hrp === undefined ? serializedPath : Buffer.concat([serializedPath, this.serializeHRP(hrp)]);

Expand Down Expand Up @@ -220,7 +272,12 @@ export default class CosmosApp extends BaseApp {
}
}

async sign(path: string, buffer: Buffer, hrp: string | undefined, txtype: number): Promise<ResponseSign> {
async sign(
path: string,
buffer: Buffer,
hrp: string | undefined,
txtype = P2_VALUES.JSON,
): Promise<ResponseSign> {
return await this.signImpl(path, buffer, hrp, txtype);
}
}
2 changes: 1 addition & 1 deletion tests/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const INVALID_HRP_RESPONSE = "6986";
export const GET_ETH_ADDRESS_RESPONSE =
"022374f2dacd71042b5a888e3839e4ba54752ad6a51d35b54f6abb899c4329d4bf696e6a31356e3268306c7a76666763387834666d366664796138396e37387836656532663368377a33669000";

export const INVALID_ADDR_RESPONSE = "0x6985";
export const INVALID_ADDR_RESPONSE = "0x6984";

export const SIGN_BASIC_AMINO_RESPONSE =
"304402206687b768c2971c973a990f7d64d3b97e2fbd8b7ccbeed3b323182a1b1350c17b022048d671283a3fa33148b8f0c4dfcc7051c1141e038763cecfec6b2f2f3006de8f9000";
Expand Down
4 changes: 2 additions & 2 deletions tests/test_integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ describe("CosmosApp Address Generation", () => {
const transport = new MockTransport(responseBuffer);
const app = new CosmosApp(transport);
try {
const resp = await app.getAddressAndPubKey("m/44'/118'/2147483647'/0/4294967295", "cosmos");
const resp = await app.getAddressAndPubKey("m/44'/2147483647'/0'/0/4294967295", "cosmos");
} catch (e: any) {
expect(e.message).toEqual("Incorrect child value (bigger or equal to 0x80000000)");
expect(e.returnCode).toEqual(0xffff);
}
});
});
Expand Down

0 comments on commit 00e5ea3

Please sign in to comment.