@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.
- package/.eslintrc.js +13 -4
- package/CHANGELOG.md +7 -0
- package/README.md +63 -12
- package/docker-compose.yml +2 -4
- package/lib/main.d.ts +16 -8
- package/lib/main.d.ts.map +1 -1
- package/lib/main.js +36 -30
- package/lib/main.js.map +1 -1
- package/lib/signers/speculos.d.ts +3 -4
- package/lib/signers/speculos.d.ts.map +1 -1
- package/lib/signers/speculos.js +108 -53
- package/lib/signers/speculos.js.map +1 -1
- package/lib/types.d.ts +1 -1
- package/lib/types.d.ts.map +1 -1
- package/lib-es/main.d.ts +16 -8
- package/lib-es/main.d.ts.map +1 -1
- package/lib-es/main.js +36 -30
- package/lib-es/main.js.map +1 -1
- package/lib-es/signers/speculos.d.ts +3 -4
- package/lib-es/signers/speculos.d.ts.map +1 -1
- package/lib-es/signers/speculos.js +84 -52
- package/lib-es/signers/speculos.js.map +1 -1
- package/lib-es/types.d.ts +1 -1
- package/lib-es/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/main.ts +33 -17
- package/src/signers/speculos.ts +70 -39
- package/src/types.ts +1 -1
package/lib-es/main.d.ts
CHANGED
|
@@ -1,31 +1,39 @@
|
|
|
1
|
-
import { AccountBridge, Account, TransactionCommon, SignOperationEvent, CurrencyBridge } from "@ledgerhq/types-live";
|
|
2
|
-
export type ScenarioTransaction<T extends TransactionCommon> = Partial<T> & {
|
|
1
|
+
import { AccountBridge, Account, TransactionCommon, SignOperationEvent, CurrencyBridge, Operation } from "@ledgerhq/types-live";
|
|
2
|
+
export type ScenarioTransaction<T extends TransactionCommon, A extends Account> = Partial<T> & {
|
|
3
3
|
name: string;
|
|
4
4
|
/**
|
|
5
5
|
*
|
|
6
|
+
* @description assert the account state after the transaction
|
|
6
7
|
* @param previousAccount previousAccount returned by the latest sync before broadcasting this transaction
|
|
7
8
|
* @param currentAccount currentAccount synced after broadcasting this transaction
|
|
8
9
|
* @returns void
|
|
9
10
|
*/
|
|
10
|
-
expect?: (previousAccount:
|
|
11
|
+
expect?: (previousAccount: A, currentAccount: A) => void;
|
|
12
|
+
/**
|
|
13
|
+
* FOR DEV ONLY
|
|
14
|
+
* if you want to temporarily disable the expect for a transaction
|
|
15
|
+
* You should push a transaction with a xexpect
|
|
16
|
+
*/
|
|
17
|
+
xexpect?: (previousAccount: A, currentAccount: A) => void;
|
|
11
18
|
};
|
|
12
|
-
export type Scenario<T extends TransactionCommon> = {
|
|
19
|
+
export type Scenario<T extends TransactionCommon, A extends Account> = {
|
|
13
20
|
name: string;
|
|
14
21
|
setup: () => Promise<{
|
|
15
|
-
accountBridge: AccountBridge<T>;
|
|
22
|
+
accountBridge: AccountBridge<T, A>;
|
|
16
23
|
currencyBridge: CurrencyBridge;
|
|
17
|
-
account:
|
|
24
|
+
account: A;
|
|
18
25
|
retryInterval?: number;
|
|
19
26
|
retryLimit?: number;
|
|
20
27
|
onSignerConfirmation?: (e?: SignOperationEvent) => Promise<void>;
|
|
21
28
|
}>;
|
|
22
|
-
getTransactions: (address: string) => ScenarioTransaction<T>[];
|
|
29
|
+
getTransactions: (address: string) => ScenarioTransaction<T, A>[];
|
|
23
30
|
beforeSync?: () => Promise<void> | void;
|
|
31
|
+
mockIndexer?: (account: Account, optimistic: Operation) => Promise<void>;
|
|
24
32
|
beforeAll?: (account: Account) => Promise<void> | void;
|
|
25
33
|
afterAll?: (account: Account) => Promise<void> | void;
|
|
26
34
|
beforeEach?: (account: Account) => Promise<void> | void;
|
|
27
35
|
afterEach?: (account: Account) => Promise<void> | void;
|
|
28
36
|
teardown?: () => Promise<void> | void;
|
|
29
37
|
};
|
|
30
|
-
export declare function executeScenario<T extends TransactionCommon>(scenario: Scenario<T>): Promise<void>;
|
|
38
|
+
export declare function executeScenario<T extends TransactionCommon, A extends Account>(scenario: Scenario<T, A>): Promise<void>;
|
|
31
39
|
//# sourceMappingURL=main.d.ts.map
|
package/lib-es/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,OAAO,EACP,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,OAAO,EACP,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACd,SAAS,EACV,MAAM,sBAAsB,CAAC;AAI9B,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,iBAAiB,EAAE,CAAC,SAAS,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG;IAC7F,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,KAAK,IAAI,CAAC;IACzD;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,KAAK,IAAI,CAAC;CAC3D,CAAC;AAEF,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,iBAAiB,EAAE,CAAC,SAAS,OAAO,IAAI;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,OAAO,CAAC;QACnB,aAAa,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACnC,cAAc,EAAE,cAAc,CAAC;QAC/B,OAAO,EAAE,CAAC,CAAC;QACX,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,oBAAoB,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,kBAAkB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KAClE,CAAC,CAAC;IACH,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;IAClE,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACxC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACvD,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACtD,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACxD,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACvD,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACvC,CAAC;AAEF,wBAAsB,eAAe,CAAC,CAAC,SAAS,iBAAiB,EAAE,CAAC,SAAS,OAAO,EAClF,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,iBAgNzB"}
|
package/lib-es/main.js
CHANGED
|
@@ -11,7 +11,7 @@ import chalk from "chalk";
|
|
|
11
11
|
import { first, firstValueFrom, map, reduce } from "rxjs";
|
|
12
12
|
export function executeScenario(scenario) {
|
|
13
13
|
return __awaiter(this, void 0, void 0, function* () {
|
|
14
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
14
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
15
15
|
try {
|
|
16
16
|
const { accountBridge, currencyBridge, account, retryInterval, retryLimit, onSignerConfirmation, } = yield scenario.setup();
|
|
17
17
|
console.log("Setup completed ✓");
|
|
@@ -34,7 +34,7 @@ export function executeScenario(scenario) {
|
|
|
34
34
|
for (const testTransaction of scenarioTransactions) {
|
|
35
35
|
console.log("\n");
|
|
36
36
|
console.log(chalk.cyan("Transaction:", chalk.bold(testTransaction.name), "◌"));
|
|
37
|
-
(_c = scenario.beforeEach) === null || _c === void 0 ? void 0 : _c.call(scenario, scenarioAccount);
|
|
37
|
+
yield ((_c = scenario.beforeEach) === null || _c === void 0 ? void 0 : _c.call(scenario, scenarioAccount));
|
|
38
38
|
console.log("Before each ✔️");
|
|
39
39
|
if (scenarioTransactions.indexOf(testTransaction) > 0) {
|
|
40
40
|
yield ((_d = scenario.beforeSync) === null || _d === void 0 ? void 0 : _d.call(scenario));
|
|
@@ -48,7 +48,7 @@ export function executeScenario(scenario) {
|
|
|
48
48
|
console.log(" → ", "🧑🍳 ", chalk.bold("Prepared the transaction"), "✓");
|
|
49
49
|
const status = yield accountBridge.getTransactionStatus(scenarioAccount, transaction);
|
|
50
50
|
if (Object.entries(status.errors).length) {
|
|
51
|
-
throw new Error(
|
|
51
|
+
throw new Error(`${testTransaction.name} transaction\nError in transaction status: ${JSON.stringify(status.errors, null, 3)}`);
|
|
52
52
|
}
|
|
53
53
|
console.log(" → ", "🪲 ", chalk.bold("No status errors detected"), "✓");
|
|
54
54
|
const { signedOperation } = yield firstValueFrom(accountBridge
|
|
@@ -65,51 +65,57 @@ export function executeScenario(scenario) {
|
|
|
65
65
|
}), first((e) => e.type === "signed")));
|
|
66
66
|
console.log(" → ", "🔏 ", chalk.bold("Signed the transaction"), "✓");
|
|
67
67
|
const optimisticOperation = yield accountBridge.broadcast({
|
|
68
|
-
account: scenarioAccount,
|
|
69
68
|
signedOperation,
|
|
69
|
+
account: scenarioAccount,
|
|
70
70
|
});
|
|
71
71
|
console.log(" → ", "🛫 ", chalk.bold("Broadcasted the transaction"), "✓");
|
|
72
72
|
const retry_limit = retryLimit !== null && retryLimit !== void 0 ? retryLimit : 10;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
73
|
+
function expectHandler(retry) {
|
|
74
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
75
|
+
var _a, _b, _c;
|
|
76
|
+
yield ((_a = scenario.beforeSync) === null || _a === void 0 ? void 0 : _a.call(scenario));
|
|
77
|
+
scenarioAccount = yield firstValueFrom(accountBridge
|
|
78
|
+
.sync(Object.assign(Object.assign({}, scenarioAccount), { pendingOperations: [optimisticOperation] }), { paginationConfig: {} })
|
|
79
|
+
.pipe(reduce((acc, f) => f(acc), scenarioAccount)));
|
|
80
|
+
if (!testTransaction.expect) {
|
|
81
|
+
console.warn(chalk.yellow(`No expects in the transaction ${chalk.bold(testTransaction.name)}. You might want to add tests in this transaction.`));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
(_b = testTransaction.expect) === null || _b === void 0 ? void 0 : _b.call(testTransaction, previousAccount, scenarioAccount);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
if (!((_c = err === null || err === void 0 ? void 0 : err.matcherResult) === null || _c === void 0 ? void 0 : _c.pass)) {
|
|
89
|
+
if (retry === 0) {
|
|
90
|
+
console.error(chalk.red(`Retried ${retry_limit} time(s) and could not assert all expects for transaction ${chalk.bold(testTransaction.name)}`));
|
|
91
|
+
throw err;
|
|
92
|
+
}
|
|
93
|
+
console.warn(chalk.magenta("Test asssertion failed. Retrying..."));
|
|
94
|
+
yield new Promise(resolve => setTimeout(resolve, retryInterval !== null && retryInterval !== void 0 ? retryInterval : 3 * 1000));
|
|
95
|
+
yield expectHandler(retry - 1);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
89
98
|
throw err;
|
|
90
99
|
}
|
|
91
|
-
console.warn(chalk.magenta("Test asssertion failed. Retrying..."));
|
|
92
|
-
yield new Promise(resolve => setTimeout(resolve, retryInterval !== null && retryInterval !== void 0 ? retryInterval : 5000));
|
|
93
|
-
yield expectHandler(retry - 1);
|
|
94
100
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
yield ((_e = scenario.mockIndexer) === null || _e === void 0 ? void 0 : _e.call(scenario, scenarioAccount, optimisticOperation));
|
|
98
104
|
yield expectHandler(retry_limit);
|
|
99
|
-
(
|
|
105
|
+
yield ((_f = scenario.afterEach) === null || _f === void 0 ? void 0 : _f.call(scenario, scenarioAccount));
|
|
100
106
|
console.log("After each ✔️");
|
|
101
107
|
console.log(chalk.green("Transaction:", chalk.bold(testTransaction.name), "completed ✓"));
|
|
102
108
|
}
|
|
103
109
|
console.log("\n");
|
|
104
|
-
yield ((
|
|
110
|
+
yield ((_g = scenario.afterAll) === null || _g === void 0 ? void 0 : _g.call(scenario, scenarioAccount));
|
|
105
111
|
console.log("afterAll completed ✓");
|
|
106
112
|
console.log("Stopping engine...");
|
|
107
|
-
yield ((
|
|
113
|
+
yield ((_h = scenario.teardown) === null || _h === void 0 ? void 0 : _h.call(scenario));
|
|
108
114
|
console.log("\n\n", chalk.bgGreen.black.bold(" ✧ "), " ", chalk.green(`Scenario: ${chalk.italic.bold(scenario.name)}`), " ", chalk.bgGreen.black.bold(" ✧ "), " → ", chalk.bold.green(" Completed 🎉"), "\n\n");
|
|
109
115
|
}
|
|
110
116
|
catch (err) {
|
|
111
117
|
console.error("\n\n", chalk.bgRed.black.bold(" ✧ "), " ", chalk.red(`Scenario: ${chalk.italic.bold(scenario.name)}`), " ", chalk.bgRed.black.bold(" ✧ "), " → ", chalk.bold.red(" Failed ❌"), "\n\n");
|
|
112
|
-
yield ((
|
|
118
|
+
yield ((_j = scenario.teardown) === null || _j === void 0 ? void 0 : _j.call(scenario));
|
|
113
119
|
throw err;
|
|
114
120
|
}
|
|
115
121
|
});
|
package/lib-es/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import SpeculosTransportHttp from "@ledgerhq/hw-transport-node-speculos-http";
|
|
2
|
-
|
|
3
|
-
export declare const spawnSpeculos: (nanoAppEndpoint: `/${string}`) => Promise<{
|
|
2
|
+
export declare function spawnSpeculos(nanoAppEndpoint: `/${string}`): Promise<{
|
|
4
3
|
transport: SpeculosTransportHttp;
|
|
5
|
-
|
|
4
|
+
getOnSpeculosConfirmation: (approvalText?: string) => () => Promise<void>;
|
|
6
5
|
}>;
|
|
7
|
-
export declare
|
|
6
|
+
export declare function killSpeculos(): Promise<void>;
|
|
8
7
|
//# sourceMappingURL=speculos.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"speculos.d.ts","sourceRoot":"","sources":["../../src/signers/speculos.ts"],"names":[],"mappings":"AAIA,OAAO,qBAAqB,MAAM,2CAA2C,CAAC;
|
|
1
|
+
{"version":3,"file":"speculos.d.ts","sourceRoot":"","sources":["../../src/signers/speculos.ts"],"names":[],"mappings":"AAIA,OAAO,qBAAqB,MAAM,2CAA2C,CAAC;AA2B9E,wBAAsB,aAAa,CAAC,eAAe,EAAE,IAAI,MAAM,EAAE,GAAG,OAAO,CAAC;IAC1E,SAAS,EAAE,qBAAqB,CAAC;IACjC,yBAAyB,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,KAAK,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3E,CAAC,CA4ED;AAED,wBAAsB,YAAY,kBAOjC"}
|
|
@@ -8,73 +8,105 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import path from "path";
|
|
11
|
-
import axios from "axios";
|
|
11
|
+
import axios, { AxiosError } from "axios";
|
|
12
12
|
import fs from "fs/promises";
|
|
13
13
|
import { v2 as compose } from "docker-compose";
|
|
14
14
|
import SpeculosTransportHttp from "@ledgerhq/hw-transport-node-speculos-http";
|
|
15
15
|
import chalk from "chalk";
|
|
16
|
-
const {
|
|
16
|
+
const { SPECULOS_API_PORT } = process.env;
|
|
17
17
|
const cwd = path.join(__dirname);
|
|
18
18
|
const delay = (timing) => new Promise(resolve => setTimeout(resolve, timing));
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
yield fs.mkdir(path.resolve(cwd, "tmp"), { recursive: true });
|
|
30
|
-
yield fs.writeFile(path.resolve(cwd, "tmp/app.elf"), blob, "binary");
|
|
31
|
-
yield compose.upOne("speculos", {
|
|
32
|
-
cwd,
|
|
33
|
-
log: true,
|
|
34
|
-
env: process.env,
|
|
19
|
+
function ensureEnv() {
|
|
20
|
+
const mandatory_env_variables = ["SEED", "SPECULOS_API_PORT", "GH_TOKEN"];
|
|
21
|
+
const optional_env_variables = ["SPECULOS_IMAGE"];
|
|
22
|
+
if (!mandatory_env_variables.every(variable => !!process.env[variable])) {
|
|
23
|
+
throw new Error(`Missing env variables. Make sure that ${mandatory_env_variables.join(",")} are in your .env`);
|
|
24
|
+
}
|
|
25
|
+
optional_env_variables.forEach(envVariable => {
|
|
26
|
+
if (!process.env[envVariable]) {
|
|
27
|
+
console.warn(`Variable ${envVariable} missing from .env. Using default value.`);
|
|
28
|
+
}
|
|
35
29
|
});
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
}
|
|
31
|
+
export function spawnSpeculos(nanoAppEndpoint) {
|
|
32
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
33
|
+
ensureEnv();
|
|
34
|
+
console.log(`Starting speculos...`);
|
|
35
|
+
try {
|
|
36
|
+
const { data: blob } = yield axios({
|
|
37
|
+
url: `https://raw.githubusercontent.com/LedgerHQ/coin-apps/master/nanox${nanoAppEndpoint}`,
|
|
38
|
+
method: "GET",
|
|
39
|
+
responseType: "stream",
|
|
40
|
+
headers: {
|
|
41
|
+
Authorization: `Bearer ${process.env.GH_TOKEN}`,
|
|
42
|
+
},
|
|
42
43
|
});
|
|
44
|
+
yield fs.mkdir(path.resolve(cwd, "tmp"), { recursive: true });
|
|
45
|
+
yield fs.writeFile(path.resolve(cwd, "tmp/app.elf"), blob, "binary");
|
|
43
46
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const onSpeculosConfirmation = (e) => __awaiter(void 0, void 0, void 0, function* () {
|
|
48
|
-
if ((e === null || e === void 0 ? void 0 : e.type) === "device-signature-requested") {
|
|
49
|
-
const { data } = yield axios.get(`http://localhost:${process.env.API_PORT}/events?currentscreenonly=true`);
|
|
50
|
-
if (data.events[0].text !== "Accept") {
|
|
51
|
-
yield axios.post(`http://localhost:${process.env.API_PORT}/button/right`, {
|
|
52
|
-
action: "press-and-release",
|
|
53
|
-
});
|
|
54
|
-
onSpeculosConfirmation(e);
|
|
47
|
+
catch (err) {
|
|
48
|
+
if (err instanceof AxiosError) {
|
|
49
|
+
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.`);
|
|
55
50
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
yield compose.upOne("speculos", {
|
|
54
|
+
cwd,
|
|
55
|
+
log: true,
|
|
56
|
+
env: process.env,
|
|
57
|
+
});
|
|
58
|
+
function checkSpeculosLogs() {
|
|
59
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
60
|
+
const { out } = yield compose.logs("speculos", { cwd, env: process.env });
|
|
61
|
+
if (out.includes("Running on all addresses (0.0.0.0)")) {
|
|
62
|
+
console.log(chalk.bgYellowBright.black(" - SPECULOS READY ✅ - "));
|
|
63
|
+
return SpeculosTransportHttp.open({
|
|
64
|
+
apiPort: SPECULOS_API_PORT,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
yield delay(200);
|
|
68
|
+
return checkSpeculosLogs();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function getOnSpeculosConfirmation(approvalText = "Accept") {
|
|
72
|
+
function onSpeculosConfirmation(e) {
|
|
73
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
74
|
+
if ((e === null || e === void 0 ? void 0 : e.type) === "device-signature-requested") {
|
|
75
|
+
const { data } = yield axios.get(`http://localhost:${SPECULOS_API_PORT}/events?currentscreenonly=true`);
|
|
76
|
+
if (data.events[0].text !== approvalText) {
|
|
77
|
+
yield axios.post(`http://localhost:${SPECULOS_API_PORT}/button/right`, {
|
|
78
|
+
action: "press-and-release",
|
|
79
|
+
});
|
|
80
|
+
onSpeculosConfirmation(e);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
yield axios.post(`http://localhost:${SPECULOS_API_PORT}/button/both`, {
|
|
84
|
+
action: "press-and-release",
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
59
88
|
});
|
|
60
89
|
}
|
|
90
|
+
return onSpeculosConfirmation;
|
|
61
91
|
}
|
|
92
|
+
return checkSpeculosLogs().then(transport => {
|
|
93
|
+
return {
|
|
94
|
+
transport,
|
|
95
|
+
getOnSpeculosConfirmation,
|
|
96
|
+
};
|
|
97
|
+
});
|
|
62
98
|
});
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
yield compose.down({
|
|
73
|
-
cwd,
|
|
74
|
-
log: true,
|
|
75
|
-
env: process.env,
|
|
99
|
+
}
|
|
100
|
+
export function killSpeculos() {
|
|
101
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
102
|
+
console.log("Stopping speculos...");
|
|
103
|
+
yield compose.down({
|
|
104
|
+
cwd,
|
|
105
|
+
log: true,
|
|
106
|
+
env: process.env,
|
|
107
|
+
});
|
|
76
108
|
});
|
|
77
|
-
}
|
|
109
|
+
}
|
|
78
110
|
["exit", "SIGINT", "SIGQUIT", "SIGTERM", "SIGUSR1", "SIGUSR2", "uncaughtException"].map(e => process.on(e, () => __awaiter(void 0, void 0, void 0, function* () {
|
|
79
111
|
yield killSpeculos();
|
|
80
112
|
})));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"speculos.js","sourceRoot":"","sources":["../../src/signers/speculos.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"speculos.js","sourceRoot":"","sources":["../../src/signers/speculos.ts"],"names":[],"mappings":"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"}
|
package/lib-es/types.d.ts
CHANGED
package/lib-es/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,GAAG,GAAG;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,GAAG,GAAG;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ledgerhq/coin-tester",
|
|
3
|
-
"version": "0.2.0",
|
|
3
|
+
"version": "0.2.1-nightly.0",
|
|
4
4
|
"description": "Deterministic testing of Ledger coin-modules",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"dependencies": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"docker-compose": "^0.24.2",
|
|
10
10
|
"lodash": "^4.17.21",
|
|
11
11
|
"rxjs": "7.8.1",
|
|
12
|
-
"@ledgerhq/hw-transport-node-speculos-http": "^6.29.0"
|
|
12
|
+
"@ledgerhq/hw-transport-node-speculos-http": "^6.29.1-nightly.0"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@types/jest": "^28.1.8",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"ts-jest": "^28.0.8",
|
|
19
19
|
"typescript": "^5.1.3",
|
|
20
20
|
"@ledgerhq/types-cryptoassets": "^7.13.0",
|
|
21
|
-
"@ledgerhq/types-live": "^6.48.0"
|
|
21
|
+
"@ledgerhq/types-live": "^6.48.1-nightly.0"
|
|
22
22
|
},
|
|
23
23
|
"typesVersions": {
|
|
24
24
|
"*": {
|
package/src/main.ts
CHANGED
|
@@ -4,33 +4,42 @@ import {
|
|
|
4
4
|
TransactionCommon,
|
|
5
5
|
SignOperationEvent,
|
|
6
6
|
CurrencyBridge,
|
|
7
|
+
Operation,
|
|
7
8
|
} from "@ledgerhq/types-live";
|
|
8
9
|
import chalk from "chalk";
|
|
9
10
|
import { first, firstValueFrom, map, reduce } from "rxjs";
|
|
10
11
|
|
|
11
|
-
export type ScenarioTransaction<T extends TransactionCommon> = Partial<T> & {
|
|
12
|
+
export type ScenarioTransaction<T extends TransactionCommon, A extends Account> = Partial<T> & {
|
|
12
13
|
name: string;
|
|
13
14
|
/**
|
|
14
15
|
*
|
|
16
|
+
* @description assert the account state after the transaction
|
|
15
17
|
* @param previousAccount previousAccount returned by the latest sync before broadcasting this transaction
|
|
16
18
|
* @param currentAccount currentAccount synced after broadcasting this transaction
|
|
17
19
|
* @returns void
|
|
18
20
|
*/
|
|
19
|
-
expect?: (previousAccount:
|
|
21
|
+
expect?: (previousAccount: A, currentAccount: A) => void;
|
|
22
|
+
/**
|
|
23
|
+
* FOR DEV ONLY
|
|
24
|
+
* if you want to temporarily disable the expect for a transaction
|
|
25
|
+
* You should push a transaction with a xexpect
|
|
26
|
+
*/
|
|
27
|
+
xexpect?: (previousAccount: A, currentAccount: A) => void;
|
|
20
28
|
};
|
|
21
29
|
|
|
22
|
-
export type Scenario<T extends TransactionCommon> = {
|
|
30
|
+
export type Scenario<T extends TransactionCommon, A extends Account> = {
|
|
23
31
|
name: string;
|
|
24
32
|
setup: () => Promise<{
|
|
25
|
-
accountBridge: AccountBridge<T>;
|
|
33
|
+
accountBridge: AccountBridge<T, A>;
|
|
26
34
|
currencyBridge: CurrencyBridge;
|
|
27
|
-
account:
|
|
35
|
+
account: A;
|
|
28
36
|
retryInterval?: number;
|
|
29
37
|
retryLimit?: number;
|
|
30
38
|
onSignerConfirmation?: (e?: SignOperationEvent) => Promise<void>;
|
|
31
39
|
}>;
|
|
32
|
-
getTransactions: (address: string) => ScenarioTransaction<T>[];
|
|
40
|
+
getTransactions: (address: string) => ScenarioTransaction<T, A>[];
|
|
33
41
|
beforeSync?: () => Promise<void> | void;
|
|
42
|
+
mockIndexer?: (account: Account, optimistic: Operation) => Promise<void>;
|
|
34
43
|
beforeAll?: (account: Account) => Promise<void> | void;
|
|
35
44
|
afterAll?: (account: Account) => Promise<void> | void;
|
|
36
45
|
beforeEach?: (account: Account) => Promise<void> | void;
|
|
@@ -38,7 +47,9 @@ export type Scenario<T extends TransactionCommon> = {
|
|
|
38
47
|
teardown?: () => Promise<void> | void;
|
|
39
48
|
};
|
|
40
49
|
|
|
41
|
-
export async function executeScenario<T extends TransactionCommon>(
|
|
50
|
+
export async function executeScenario<T extends TransactionCommon, A extends Account>(
|
|
51
|
+
scenario: Scenario<T, A>,
|
|
52
|
+
) {
|
|
42
53
|
try {
|
|
43
54
|
const {
|
|
44
55
|
accountBridge,
|
|
@@ -93,7 +104,7 @@ export async function executeScenario<T extends TransactionCommon>(scenario: Sce
|
|
|
93
104
|
console.log("\n");
|
|
94
105
|
console.log(chalk.cyan("Transaction:", chalk.bold(testTransaction.name), "◌"));
|
|
95
106
|
|
|
96
|
-
scenario.beforeEach?.(scenarioAccount);
|
|
107
|
+
await scenario.beforeEach?.(scenarioAccount);
|
|
97
108
|
console.log("Before each ✔️");
|
|
98
109
|
|
|
99
110
|
if (scenarioTransactions.indexOf(testTransaction) > 0) {
|
|
@@ -117,7 +128,9 @@ export async function executeScenario<T extends TransactionCommon>(scenario: Sce
|
|
|
117
128
|
|
|
118
129
|
const status = await accountBridge.getTransactionStatus(scenarioAccount, transaction);
|
|
119
130
|
if (Object.entries(status.errors).length) {
|
|
120
|
-
throw new Error(
|
|
131
|
+
throw new Error(
|
|
132
|
+
`${testTransaction.name} transaction\nError in transaction status: ${JSON.stringify(status.errors, null, 3)}`,
|
|
133
|
+
);
|
|
121
134
|
}
|
|
122
135
|
|
|
123
136
|
console.log(" → ", "🪲 ", chalk.bold("No status errors detected"), "✓");
|
|
@@ -144,15 +157,15 @@ export async function executeScenario<T extends TransactionCommon>(scenario: Sce
|
|
|
144
157
|
console.log(" → ", "🔏 ", chalk.bold("Signed the transaction"), "✓");
|
|
145
158
|
|
|
146
159
|
const optimisticOperation = await accountBridge.broadcast({
|
|
147
|
-
account: scenarioAccount,
|
|
148
160
|
signedOperation,
|
|
161
|
+
account: scenarioAccount,
|
|
149
162
|
});
|
|
150
163
|
|
|
151
164
|
console.log(" → ", "🛫 ", chalk.bold("Broadcasted the transaction"), "✓");
|
|
152
165
|
|
|
153
166
|
const retry_limit = retryLimit ?? 10;
|
|
154
167
|
|
|
155
|
-
|
|
168
|
+
async function expectHandler(retry: number) {
|
|
156
169
|
await scenario.beforeSync?.();
|
|
157
170
|
scenarioAccount = await firstValueFrom(
|
|
158
171
|
accountBridge
|
|
@@ -171,6 +184,8 @@ export async function executeScenario<T extends TransactionCommon>(scenario: Sce
|
|
|
171
184
|
)}. You might want to add tests in this transaction.`,
|
|
172
185
|
),
|
|
173
186
|
);
|
|
187
|
+
|
|
188
|
+
return;
|
|
174
189
|
}
|
|
175
190
|
|
|
176
191
|
try {
|
|
@@ -180,7 +195,7 @@ export async function executeScenario<T extends TransactionCommon>(scenario: Sce
|
|
|
180
195
|
if (retry === 0) {
|
|
181
196
|
console.error(
|
|
182
197
|
chalk.red(
|
|
183
|
-
`Retried ${
|
|
198
|
+
`Retried ${retry_limit} time(s) and could not assert all expects for transaction ${chalk.bold(
|
|
184
199
|
testTransaction.name,
|
|
185
200
|
)}`,
|
|
186
201
|
),
|
|
@@ -190,17 +205,18 @@ export async function executeScenario<T extends TransactionCommon>(scenario: Sce
|
|
|
190
205
|
}
|
|
191
206
|
|
|
192
207
|
console.warn(chalk.magenta("Test asssertion failed. Retrying..."));
|
|
193
|
-
await new Promise(resolve => setTimeout(resolve, retryInterval ??
|
|
208
|
+
await new Promise(resolve => setTimeout(resolve, retryInterval ?? 3 * 1000));
|
|
194
209
|
await expectHandler(retry - 1);
|
|
210
|
+
} else {
|
|
211
|
+
throw err;
|
|
195
212
|
}
|
|
196
|
-
|
|
197
|
-
throw err;
|
|
198
213
|
}
|
|
199
|
-
}
|
|
214
|
+
}
|
|
200
215
|
|
|
216
|
+
await scenario.mockIndexer?.(scenarioAccount, optimisticOperation);
|
|
201
217
|
await expectHandler(retry_limit);
|
|
202
218
|
|
|
203
|
-
scenario.afterEach?.(scenarioAccount);
|
|
219
|
+
await scenario.afterEach?.(scenarioAccount);
|
|
204
220
|
console.log("After each ✔️");
|
|
205
221
|
console.log(chalk.green("Transaction:", chalk.bold(testTransaction.name), "completed ✓"));
|
|
206
222
|
}
|