Skip to content

Commit

Permalink
feat(workflows/ledgerjs-hw-app-iota): add ledgernano tests (#2208)
Browse files Browse the repository at this point in the history
* feat(workflows/ledgerjs-hw-app-iota): add ledgernano tests

* Update .github/workflows/_ledgernano.yml

Co-authored-by: DaughterOfMars <[email protected]>

* Download binary

* oops

* dprint

---------

Co-authored-by: DaughterOfMars <[email protected]>
Co-authored-by: Thibault Martinez <[email protected]>
  • Loading branch information
3 people authored Oct 25, 2024
1 parent ea38fa7 commit 8e89d86
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 17 deletions.
45 changes: 45 additions & 0 deletions .github/workflows/_ledgernano.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Ledgernano End-to-end Tests

on: workflow_call

concurrency:
group: ledgernano-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
ledgernano:
name: Ledgernano
runs-on: self-hosted
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # Pin v4.1.1
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # [email protected]

- name: Install Nodejs
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # [email protected]
with:
node-version: "20"
cache: "pnpm"

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Install ledgernano dependencies
run: |
sudo apt-get install -y qemu-user-static
sudo apt-get install -y libxcb-xinerama0
sudo pip install speculos --break-system-packages
- name: Download ledgernano bin
run: |
sudo apt-get install -y gh
echo ${{ secrets.LEDGER_APP_TOKEN }} | gh auth login --with-token
gh release list --repo https://github.com/iotaledger/ledger-app-iota
gh release download --repo https://github.com/iotaledger/ledger-app-iota -p nanos.tar.gz untagged-a706a550379839d8db15
tar -xvf nanos.tar.gz
mv nanos/iota sdk/ledgerjs-hw-app-iota/tests/iota
- name: Start speculos simulator
run: speculos --api-port 5000 --display headless ./sdk/ledgerjs-hw-app-iota/tests/iota &

- name: Run TS SDK ledgernano tests
run: pnpm --filter @iota/ledgerjs-hw-app-iota test
3 changes: 2 additions & 1 deletion .github/workflows/_turborepo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ jobs:
- name: Build
run: pnpm turbo build
- name: Test
run: pnpm turbo test
# @iota/ledgerjs-hw-app-iota is tested separately
run: pnpm turbo test --filter '!@iota/ledgerjs-hw-app-iota'
# Pack wallet extension and upload it as an artifact for easy developer use:
- name: Wallet Extension Has Changes?
id: wallet-diff
Expand Down
11 changes: 11 additions & 0 deletions .github/workflows/hierarchy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
isAppsUiKit: ${{ (steps.turbo.outputs.packages && contains(fromJson(steps.turbo.outputs.packages), '@iota/apps-ui-kit')) }}
isWalletDashboard: ${{ (steps.turbo.outputs.packages && contains(fromJson(steps.turbo.outputs.packages), 'wallet-dashboard')) }}
isGraphQlTransport: ${{ (steps.turbo.outputs.packages && contains(fromJson(steps.turbo.outputs.packages), '@iota/graphql-transport')) }}
isLedgerjs: ${{ (steps.turbo.outputs.packages && contains(fromJson(steps.turbo.outputs.packages), '@iota/ledgerjs-hw-app-iota')) }}
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4
- name: Detect Changes (turbo)
Expand Down Expand Up @@ -164,3 +165,13 @@ jobs:
isAppsBackend: ${{ needs.diff.outputs.isAppsBackend == 'true' }}
isAppsUiKit: ${{ needs.diff.outputs.isAppsUiKit == 'true' }}
isWalletDashboard: ${{ needs.diff.outputs.isWalletDashboard == 'true' }}

ledgernano:
if: (!cancelled() && !failure()) && needs.diff.outputs.isLedgerjs == 'true' && github.event.pull_request.draft == false
needs:
- diff
- dprint-format
- license-check
- typos
uses: ./.github/workflows/_ledgernano.yml
secrets: inherit
30 changes: 20 additions & 10 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions sdk/ledgerjs-hw-app-iota/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@
"devDependencies": {
"@iota/build-scripts": "workspace:*",
"@ledgerhq/hw-transport-mocker": "^6.29.0",
"@ledgerhq/hw-transport-node-speculos-http": "^6.29.2",
"@size-limit/preset-small-lib": "^11.1.4",
"@types/node": "^20.14.10",
"axios": "^1.7.4",
"size-limit": "^11.1.4",
"typescript": "^5.5.3",
"vitest": "^2.0.1"
Expand Down
134 changes: 128 additions & 6 deletions sdk/ledgerjs-hw-app-iota/tests/Iota.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,134 @@
// SPDX-License-Identifier: Apache-2.0

import { openTransportReplayer, RecordStore } from '@ledgerhq/hw-transport-mocker';
import { expect, test } from 'vitest';

import { describe, expect, it } from 'vitest';
import SpeculosHttpTransport from '@ledgerhq/hw-transport-node-speculos-http';
import Axios from 'axios';
import Iota from '../src/Iota';

test('Iota init', async () => {
const transport = await openTransportReplayer(RecordStore.fromString(''));
const pkt = new Iota(transport);
expect(pkt).not.toBe(undefined);
const API_PORT: number = 5000;
const SPECULOS_BASE_URL: string = `http://127.0.0.1:${API_PORT}`;

// Before running the tests you need to install speculos and start the iota app with it.
// If the binary is not available, download it:
// gh release download --repo https://github.com/iotaledger/ledger-app-iota -p nanos.tar.gz untagged-a706a550379839d8db15
// tar -xvf nanos.tar.gz
// sudo apt-get install qemu-user-static libxcb-xinerama0 // might be needed for speculos to work
// pip install speculos
// Finally to start the emulator:
// speculos --api-port 5000 --display headless ./sdk/ledgerjs-hw-app-iota/tests/iota
describe.sequential('Test ledgerjs-hw-app-iota', () => {
it('Iota init', async () => {
const transport = await openTransportReplayer(RecordStore.fromString(''));
const pkt = new Iota(transport);
expect(pkt).not.toBe(undefined);
});

it('Test address generation', async () => {
const transport = await SpeculosHttpTransport.open({});
const ledgerClient = new Iota(transport);

const { publicKey } = await ledgerClient.getPublicKey(`m/44'/4218'/0'/0'/0'`);

// Default speculos mnemonic: glory promote mansion idle axis finger extra february uncover one trip resource lawn turtle enact monster seven myth punch hobby comfort wild raise skin
expect(Buffer.from(publicKey).toString('hex')).toBe(
'f0a9c612b7e69f1a114aa9189c1f32997d395d09d183368ddfd6d5dc49e34647',
);
});

it('Test address generation with display', async () => {
const transport = await SpeculosHttpTransport.open({});
const ledgerClient = new Iota(transport);

let addressReceived = false;
ledgerClient
.getPublicKey(`m/44'/4218'/0'/0'/0'`, true)
.then(({ publicKey }) => {
// Default speculos mnemonic: glory promote mansion idle axis finger extra february uncover one trip resource lawn turtle enact monster seven myth punch hobby comfort wild raise skin
expect(Buffer.from(publicKey).toString('hex')).toBe(
'f0a9c612b7e69f1a114aa9189c1f32997d395d09d183368ddfd6d5dc49e34647',
);
addressReceived = true;
})
.catch((err) => {
throw new Error(err);
});
await new Promise((resolve) => setTimeout(resolve, 1000));
// Send requests to approve the shown address
for (let i = 0; i < 6; i++) {
await Axios.post(SPECULOS_BASE_URL + '/button/right', { action: 'press-and-release' });
}
await Axios.post(SPECULOS_BASE_URL + '/button/both', { action: 'press-and-release' });
if (!addressReceived) {
throw new Error(`Didn't receive address in time`);
}
});

it('Test signing', { timeout: 10000 }, async () => {
const transport = await SpeculosHttpTransport.open({});
const ledgerClient = new Iota(transport);
let signatureReceived = false;
ledgerClient
.signTransaction(
`m/44'/4218'/0'/0'/0'`,
'0000000000020008e803000000000000002021c22f952c8742b3156dfca5fc8278bd3ba7b209c81e26c4f44a9944259b03b50202000101000001010200000101006fb21feead027da4873295affd6c4f3618fe176fa2fbf3e7b5ef1d9463b31e2101cad8ac9d85be1fcb1ec3f5870a50004549f4f892856b70499ed1654201c4399984470b000000000020ec2f226e6647a523608dc52ccb9976720c51d60ebfeadc524ee870cdfd1f6b8c6fb21feead027da4873295affd6c4f3618fe176fa2fbf3e7b5ef1d9463b31e21e803000000000000404b4c000000000000',
)
.then(({ signature }) => {
expect(Buffer.from(signature).toString('hex')).toBe(
'9aaa0b45f0aeef61b055fe5c76a9184e6d6b7b361ff77387bd9c43873b07e349300ab7dce9602bf59c287600cdb9b4ade00257c683de65b51f18aee4ed402e0c',
);
signatureReceived = true;
})
.catch((err) => {
throw new Error(err);
});
await new Promise((resolve) => setTimeout(resolve, 500));
// Send requests to approve the tx
for (let i = 0; i < 14; i++) {
await Axios.post(SPECULOS_BASE_URL + '/button/right', { action: 'press-and-release' });
}
await Axios.post(SPECULOS_BASE_URL + '/button/both', { action: 'press-and-release' });
await new Promise((resolve) => setTimeout(resolve, 2000));
if (!signatureReceived) {
throw new Error(`Didn't receive signature in time`);
}
});

it('Test blind signing', { timeout: 10000 }, async () => {
// Enable blind signing
await Axios.post(SPECULOS_BASE_URL + '/button/right', { action: 'press-and-release' });
await Axios.post(SPECULOS_BASE_URL + '/button/right', { action: 'press-and-release' });
await Axios.post(SPECULOS_BASE_URL + '/button/both', { action: 'press-and-release' });
await Axios.post(SPECULOS_BASE_URL + '/button/both', { action: 'press-and-release' });
await Axios.post(SPECULOS_BASE_URL + '/button/right', { action: 'press-and-release' });
await Axios.post(SPECULOS_BASE_URL + '/button/both', { action: 'press-and-release' });

const transport = await SpeculosHttpTransport.open({});
const ledgerClient = new Iota(transport);
let signatureReceived = false;
ledgerClient
.signTransaction(
`m/44'/4218'/0'/0'/0'`,
'0000000000000000000000000000000000000000000000000000000000000000',
)
.then(({ signature }) => {
expect(Buffer.from(signature).toString('hex')).toBe(
'c05235724452fd33c4df3558117f47ca807a9bd70750022d414f96790d3ec1c7e08a0c12b52972edd68f535f040357c20ea226d6d06f09e670f008916395c003',
);
signatureReceived = true;
})
.catch((err) => {
throw new Error(err);
});
await new Promise((resolve) => setTimeout(resolve, 500));
// Send requests to approve the tx
for (let i = 0; i < 8; i++) {
await Axios.post(SPECULOS_BASE_URL + '/button/right', { action: 'press-and-release' });
}
await Axios.post(SPECULOS_BASE_URL + '/button/both', { action: 'press-and-release' });
await new Promise((resolve) => setTimeout(resolve, 2000));
if (!signatureReceived) {
throw new Error(`Didn't receive signature in time`);
}
});
});

0 comments on commit 8e89d86

Please sign in to comment.