UNPKG

@ledgerhq/coin-tester

Version:
143 lines 6.11 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.spawnSpeculos = spawnSpeculos; exports.killSpeculos = killSpeculos; const path_1 = __importDefault(require("path")); const chalk_1 = __importDefault(require("chalk")); const promises_1 = __importDefault(require("fs/promises")); const compose = __importStar(require("docker-compose")); const axios_1 = __importStar(require("axios")); const hw_transport_node_speculos_http_1 = __importDefault(require("@ledgerhq/hw-transport-node-speculos-http")); const { SPECULOS_API_PORT } = process.env; const cwd = path_1.default.join(__dirname); const delay = (timing) => new Promise(resolve => setTimeout(resolve, timing)); function ensureEnv() { const mandatory_env_variables = ["SEED", "SPECULOS_API_PORT", "GH_TOKEN"]; const optional_env_variables = ["SPECULOS_IMAGE"]; if (!mandatory_env_variables.every(variable => !!process.env[variable])) { throw new Error(`Missing env variables. Make sure that ${mandatory_env_variables.join(",")} are in your .env`); } optional_env_variables.forEach(envVariable => { if (!process.env[envVariable]) { console.warn(`Variable ${envVariable} missing from .env. Using default value.`); } }); } async function downloadApp(nanoAppEndpoint) { const { data: blob } = await (0, axios_1.default)({ url: `https://raw.githubusercontent.com/LedgerHQ/coin-apps/master/nanox${nanoAppEndpoint}`, method: "GET", responseType: "stream", headers: { Authorization: `Bearer ${process.env.GH_TOKEN}`, }, }); return blob; } async function spawnSpeculos(nanoAppEndpoint, options) { ensureEnv(); console.log(`Starting speculos...`); const libraryArgs = []; try { const blob = await downloadApp(nanoAppEndpoint); await promises_1.default.mkdir(path_1.default.resolve(cwd, "tmp"), { recursive: true }); await promises_1.default.writeFile(path_1.default.resolve(cwd, "tmp/app.elf"), blob, "binary"); if (options?.libraries) { for (const library of options.libraries) { const libBlob = await downloadApp(library.endpoint); const libFileName = `${library.name.toLowerCase()}.elf`; await promises_1.default.writeFile(path_1.default.resolve(cwd, "tmp", libFileName), libBlob, "binary"); libraryArgs.push(`-l ${library.name}:./apps/${libFileName}`); } } } catch (err) { if (err instanceof axios_1.AxiosError) { throw new Error(`${err.status}: Failed to download the app.elf file from ${nanoAppEndpoint}\nMake sure that your GH_TOKEN is correct and has the right permissions.`); } throw err; } await compose.upOne("speculos", { cwd, log: Boolean(process.env.DEBUG), env: { ...process.env, SPECULOS_EXTRA_ARGS: libraryArgs.join(" "), }, }); async function checkSpeculosLogs() { const { out } = await compose.logs("speculos", { cwd, env: process.env }); if (out.includes("Serving Flask app")) { console.log(chalk_1.default.bgYellowBright.black(" - SPECULOS READY ✅ - ")); return hw_transport_node_speculos_http_1.default.open({ apiPort: SPECULOS_API_PORT, }); } await delay(200); return checkSpeculosLogs(); } function getOnSpeculosConfirmation(approvalText = "Accept") { async function onSpeculosConfirmation(e) { if (e?.type === "device-signature-requested") { const { data } = await axios_1.default.get(`http://localhost:${SPECULOS_API_PORT}/events?currentscreenonly=true`); if (data.events[0].text !== approvalText) { await axios_1.default.post(`http://localhost:${SPECULOS_API_PORT}/button/right`, { action: "press-and-release", }); onSpeculosConfirmation(e); } else { await axios_1.default.post(`http://localhost:${SPECULOS_API_PORT}/button/both`, { action: "press-and-release", }); } } } return onSpeculosConfirmation; } return checkSpeculosLogs().then(transport => { return { transport, getOnSpeculosConfirmation, }; }); } async function killSpeculos() { console.log("Stopping speculos..."); await compose.down({ cwd, log: Boolean(process.env.DEBUG), env: process.env, commandOptions: ["--remove-orphans"], }); } ["exit", "SIGINT", "SIGQUIT", "SIGTERM", "SIGUSR1", "SIGUSR2", "uncaughtException"].map(e => process.on(e, async () => { await killSpeculos(); })); //# sourceMappingURL=speculos.js.map