diff --git a/.prettierignore b/.prettierignore index aa54149d15..9194904cd3 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,5 @@ package.json -src/load +src/load/tokens src/generated src/crypto/sha256.js src/data/icons \ No newline at end of file diff --git a/package.json b/package.json index 5745dad6b3..58341b9364 100644 --- a/package.json +++ b/package.json @@ -64,14 +64,14 @@ "@ledgerhq/compressjs": "1.3.2", "@ledgerhq/devices": "5.15.0", "@ledgerhq/errors": "5.15.0", - "@ledgerhq/hw-app-btc": "^5.15.1", + "@ledgerhq/hw-app-btc": "^5.15.2", "@ledgerhq/hw-app-eth": "5.15.0", "@ledgerhq/hw-app-str": "^5.15.0", "@ledgerhq/hw-app-trx": "5.15.0", "@ledgerhq/hw-app-xrp": "5.15.0", "@ledgerhq/hw-transport": "5.15.0", "@ledgerhq/hw-transport-mocker": "^5.15.0", - "@ledgerhq/hw-transport-node-speculos": "^5.15.0", + "@ledgerhq/hw-transport-node-speculos": "^5.15.2", "@ledgerhq/logs": "5.15.0", "async": "^3.2.0", "axios": "0.19.0", diff --git a/src/load/speculos.js b/src/load/speculos.js index a6980e44af..9aa8f22ec4 100644 --- a/src/load/speculos.js +++ b/src/load/speculos.js @@ -2,7 +2,10 @@ // Ledger internal speculos testing framework. // loading this file have side effects and is only for Node. +import path from "path"; +import semver from "semver"; import { spawn, exec } from "child_process"; +import { promises as fsp } from "fs"; import invariant from "invariant"; import { log } from "@ledgerhq/logs"; import SpeculosTransport from "@ledgerhq/hw-transport-node-speculos"; @@ -18,6 +21,86 @@ export function releaseSpeculosDevice(id: ?string) { if (obj) obj.destroy(); } +export type AppCandidate = { + path: string, + model: DeviceModelId, + firmware: string, + appName: string, + appVersion: string, +}; + +const modelMap: { [_: string]: DeviceModelId } = { + blue: "blue", + nanox: "nanoX", + nanos: "nanoS", +}; + +// list all possible apps. sorted by latest first +export async function listAppCandidates(cwd: string): Promise { + let candidates = []; + const models = await fsp.readdir(cwd); + for (const modelName of models) { + const model = modelMap[modelName.toLowerCase()]; + if (!model) continue; + const p1 = path.join(cwd, modelName); + const firmwares = (await fsp.readdir(p1)).filter(semver.valid); + firmwares.sort((a, b) => semver.compare(a, b)); + for (const firmware of firmwares) { + const p2 = path.join(p1, firmware); + const appNames = await fsp.readdir(p2); + for (const appName of appNames) { + const p3 = path.join(p2, appName); + const elfs = await fsp.readdir(p3); + const c = []; + for (const elf of elfs) { + if (elf.startsWith("app_") && elf.endsWith(".elf")) { + const p4 = path.join(p3, elf); + const appVersion = elf.slice(4, elf.length - 4); + if (semver.valid(appVersion)) { + c.push({ + path: p4, + model, + firmware, + appName, + appVersion, + }); + } + } + } + c.sort((a, b) => semver.compare(a.appVersion, b.appVersion)); + candidates = candidates.concat(c); + } + } + } + return candidates; +} + +export type AppSearch = { + model: DeviceModelId, + firmware: string, + appName: string, + appVersion: string, +}; + +export function appCandidatesMatches( + appCandidate: AppCandidate, + search: $Shape +): boolean { + return ( + (!search.model || search.model === appCandidate.model) && + (!search.appName || search.appName === appCandidate.appName) && + (!search.firmware || + semver.satisfies(appCandidate.firmware, search.firmware)) && + (!search.appVersion || + semver.satisfies(appCandidate.appVersion, search.appVersion)) + ); +} + +export const findAppCandidate = ( + appCandidates: AppCandidate[], + search: $Shape +): ?AppCandidate => appCandidates.find((c) => appCandidatesMatches(c, search)); + export async function createSpeculosDevice({ model, firmware, @@ -34,7 +117,7 @@ export async function createSpeculosDevice({ dependency?: string, seed: string, // Folder where we have app binaries - coinapps: string + coinapps: string, }): Promise<{ transport: SpeculosTransport, id: string, diff --git a/yarn.lock b/yarn.lock index 716b560641..4f8c029189 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1177,10 +1177,10 @@ resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.15.0.tgz#e5d5b5ad48fc07f6308b78b065242e158bc044a2" integrity sha512-ZlLhR7qaChPgEbvcqOptRepWGm8VhhwOM6kC1gx3WErutbtaOjUX8lLA4ButWFU2f+xTl2rS/5c86wC7qGqGXQ== -"@ledgerhq/hw-app-btc@^5.15.1": - version "5.15.1" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-btc/-/hw-app-btc-5.15.1.tgz#6e9f31f581b6899b1496e11a91d38134260bc958" - integrity sha512-0+2wZEJ3Ud6N5Vr++qTfIImee8HjoHC1hZB98MkCeNGG6zizESbcXL9RJI88gV2fk3b9o1HUd86po1P7QvEpcQ== +"@ledgerhq/hw-app-btc@^5.15.2": + version "5.15.2" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-btc/-/hw-app-btc-5.15.2.tgz#a1c8b5a16915c92d4d21ad5e2962b0310c4473e8" + integrity sha512-mpjANAFEtFfvb9BwiijDZOAEWlON/G6ieORNpJ4GM2u7Lanlo39IANo3K68ootGnS15QdfAdZw34SzdzLfIIYg== dependencies: "@ledgerhq/hw-transport" "^5.15.0" bip32-path "^0.4.2" @@ -1232,10 +1232,10 @@ "@ledgerhq/hw-transport" "^5.15.0" "@ledgerhq/logs" "^5.15.0" -"@ledgerhq/hw-transport-node-speculos@^5.15.0": - version "5.15.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-speculos/-/hw-transport-node-speculos-5.15.0.tgz#df1d782b0ace14beaa66cd6d6dd2f1fa83e5f40d" - integrity sha512-7m4LLofrJHpr1cPLUqKqjyQkBOss+e+JWzAdQAQCUPn1IyQyaJf87h3DsbQuqu3cAU0umMy0GlXoNFh4oED+Iw== +"@ledgerhq/hw-transport-node-speculos@^5.15.2": + version "5.15.2" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-speculos/-/hw-transport-node-speculos-5.15.2.tgz#3e347120faa9765d75ac88adca12e4ec54416d36" + integrity sha512-lhM0EYCtnuzTl8Xpgp/CVsupw4u+KCCtvIL2kqOygXccvfX8VdQuo03C/JXm8mJoH99EHK+lJHqTdhorg2Yl7A== dependencies: "@ledgerhq/errors" "^5.15.0" "@ledgerhq/hw-transport" "^5.15.0"