@ledgerhq/coin-tester 0.2.0 → 0.2.1-nightly.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import path from "path";
2
- import axios from "axios";
2
+ import axios, { AxiosError } from "axios";
3
3
  import fs from "fs/promises";
4
4
  import { v2 as compose } from "docker-compose";
5
5
  import SpeculosTransportHttp from "@ledgerhq/hw-transport-node-speculos-http";
@@ -7,30 +7,56 @@ import { ENV } from "../types";
7
7
  import chalk from "chalk";
8
8
  import { SignOperationEvent } from "@ledgerhq/types-live";
9
9
 
10
- const { API_PORT } = process.env as ENV;
10
+ const { SPECULOS_API_PORT } = process.env as ENV;
11
11
  const cwd = path.join(__dirname);
12
12
 
13
13
  const delay = (timing: number) => new Promise(resolve => setTimeout(resolve, timing));
14
14
 
15
- export const spawnSpeculos = async (
16
- nanoAppEndpoint: `/${string}`,
17
- ): Promise<{
15
+ function ensureEnv() {
16
+ const mandatory_env_variables = ["SEED", "SPECULOS_API_PORT", "GH_TOKEN"];
17
+ const optional_env_variables = ["SPECULOS_IMAGE"];
18
+
19
+ if (!mandatory_env_variables.every(variable => !!process.env[variable])) {
20
+ throw new Error(
21
+ `Missing env variables. Make sure that ${mandatory_env_variables.join(",")} are in your .env`,
22
+ );
23
+ }
24
+
25
+ optional_env_variables.forEach(envVariable => {
26
+ if (!process.env[envVariable]) {
27
+ console.warn(`Variable ${envVariable} missing from .env. Using default value.`);
28
+ }
29
+ });
30
+ }
31
+
32
+ export async function spawnSpeculos(nanoAppEndpoint: `/${string}`): Promise<{
18
33
  transport: SpeculosTransportHttp;
19
- onSignerConfirmation: (e?: SignOperationEvent) => Promise<void>;
20
- }> => {
34
+ getOnSpeculosConfirmation: (approvalText?: string) => () => Promise<void>;
35
+ }> {
36
+ ensureEnv();
21
37
  console.log(`Starting speculos...`);
22
38
 
23
- const { data: blob } = await axios({
24
- url: `https://raw.githubusercontent.com/LedgerHQ/coin-apps/master/nanox${nanoAppEndpoint}`,
25
- method: "GET",
26
- responseType: "stream",
27
- headers: {
28
- Authorization: `Bearer ${process.env.GH_TOKEN}`,
29
- },
30
- });
39
+ try {
40
+ const { data: blob } = await axios({
41
+ url: `https://raw.githubusercontent.com/LedgerHQ/coin-apps/master/nanox${nanoAppEndpoint}`,
42
+ method: "GET",
43
+ responseType: "stream",
44
+ headers: {
45
+ Authorization: `Bearer ${process.env.GH_TOKEN}`,
46
+ },
47
+ });
31
48
 
32
- await fs.mkdir(path.resolve(cwd, "tmp"), { recursive: true });
33
- await fs.writeFile(path.resolve(cwd, "tmp/app.elf"), blob, "binary");
49
+ await fs.mkdir(path.resolve(cwd, "tmp"), { recursive: true });
50
+ await fs.writeFile(path.resolve(cwd, "tmp/app.elf"), blob, "binary");
51
+ } catch (err) {
52
+ if (err instanceof AxiosError) {
53
+ throw new Error(
54
+ `${err.status}: Failed to download the app.elf file from ${nanoAppEndpoint}\nMake sure that your GH_TOKEN is correct and has the right permissions.`,
55
+ );
56
+ }
57
+
58
+ throw err;
59
+ }
34
60
 
35
61
  await compose.upOne("speculos", {
36
62
  cwd,
@@ -38,55 +64,60 @@ export const spawnSpeculos = async (
38
64
  env: process.env,
39
65
  });
40
66
 
41
- const checkSpeculosLogs = async (): Promise<SpeculosTransportHttp> => {
67
+ async function checkSpeculosLogs(): Promise<SpeculosTransportHttp> {
42
68
  const { out } = await compose.logs("speculos", { cwd, env: process.env });
43
69
 
44
70
  if (out.includes("Running on all addresses (0.0.0.0)")) {
45
71
  console.log(chalk.bgYellowBright.black(" - SPECULOS READY ✅ - "));
46
72
  return SpeculosTransportHttp.open({
47
- apiPort: API_PORT,
73
+ apiPort: SPECULOS_API_PORT,
48
74
  });
49
75
  }
50
76
 
51
77
  await delay(200);
52
78
  return checkSpeculosLogs();
53
- };
79
+ }
54
80
 
55
- const onSpeculosConfirmation = async (e?: SignOperationEvent): Promise<void> => {
56
- if (e?.type === "device-signature-requested") {
57
- const { data } = await axios.get(
58
- `http://localhost:${process.env.API_PORT}/events?currentscreenonly=true`,
59
- );
81
+ function getOnSpeculosConfirmation(approvalText = "Accept") {
82
+ async function onSpeculosConfirmation(e?: SignOperationEvent): Promise<void> {
83
+ if (e?.type === "device-signature-requested") {
84
+ const { data } = await axios.get(
85
+ `http://localhost:${SPECULOS_API_PORT}/events?currentscreenonly=true`,
86
+ );
87
+
88
+ if (data.events[0].text !== approvalText) {
89
+ await axios.post(`http://localhost:${SPECULOS_API_PORT}/button/right`, {
90
+ action: "press-and-release",
91
+ });
60
92
 
61
- if (data.events[0].text !== "Accept") {
62
- await axios.post(`http://localhost:${process.env.API_PORT}/button/right`, {
63
- action: "press-and-release",
64
- });
65
- onSpeculosConfirmation(e);
66
- } else {
67
- await axios.post(`http://localhost:${process.env.API_PORT}/button/both`, {
68
- action: "press-and-release",
69
- });
93
+ onSpeculosConfirmation(e);
94
+ } else {
95
+ await axios.post(`http://localhost:${SPECULOS_API_PORT}/button/both`, {
96
+ action: "press-and-release",
97
+ });
98
+ }
70
99
  }
71
100
  }
72
- };
101
+
102
+ return onSpeculosConfirmation;
103
+ }
73
104
 
74
105
  return checkSpeculosLogs().then(transport => {
75
106
  return {
76
107
  transport,
77
- onSignerConfirmation: onSpeculosConfirmation,
108
+ getOnSpeculosConfirmation,
78
109
  };
79
110
  });
80
- };
111
+ }
81
112
 
82
- export const killSpeculos = async () => {
113
+ export async function killSpeculos() {
83
114
  console.log("Stopping speculos...");
84
115
  await compose.down({
85
116
  cwd,
86
117
  log: true,
87
118
  env: process.env,
88
119
  });
89
- };
120
+ }
90
121
 
91
122
  ["exit", "SIGINT", "SIGQUIT", "SIGTERM", "SIGUSR1", "SIGUSR2", "uncaughtException"].map(e =>
92
123
  process.on(e, async () => {
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export type ENV = {
2
2
  GH_TOKEN: string;
3
3
  SEED: string;
4
- API_PORT: string;
4
+ SPECULOS_API_PORT: string;
5
5
  };