Skip to content
This repository has been archived by the owner on Jun 10, 2022. It is now read-only.

Ledger simulator #386

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
668 changes: 642 additions & 26 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"license": "Apache-2.0",
"dependencies": {
"@ledgerhq/hw-transport-node-hid": "^5.51.1",
"@ledgerhq/hw-transport-node-speculos": "^5.51.1",
"axios": "^0.21.1",
"bip32": "^1.0.2",
"chalk": "^2.3.0",
Expand Down
18 changes: 16 additions & 2 deletions src/models/ledgerProfile.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class LedgerWallet implements CliWallet {
public readonly publicKey: string,
public readonly path: string,
public readonly optin: boolean,
public readonly simulator: boolean,
) {
this.networkType = address.networkType;
}
Expand All @@ -44,7 +45,14 @@ export class LedgerWallet implements CliWallet {
* @returns an instance of LedgerWallet
*/
static createFromDTO(dto: ILedgerWalletDTO): LedgerWallet {
return new LedgerWallet(dto.name, Address.createFromRawAddress(dto.address.address), dto.publicKey, dto.path, dto.optin);
return new LedgerWallet(
dto.name,
Address.createFromRawAddress(dto.address.address),
dto.publicKey,
dto.path,
dto.optin,
dto.simulator,
);
}

/**
Expand Down Expand Up @@ -95,6 +103,7 @@ export class LedgerProfile extends Profile<LedgerWallet> {
args.publicKey,
args.path,
args.optin,
args.simulator,
);
return new LedgerProfile(
simpleWallet,
Expand Down Expand Up @@ -147,9 +156,14 @@ export class LedgerProfile extends Profile<LedgerWallet> {
table.push(['Public Key', this.simpleWallet.publicKey]);
table.push(['Path', this.simpleWallet.path]);
table.push(['OptIn', this.simpleWallet.optin]);
table.push(['Simulator', this.simpleWallet.simulator]);
return table;
}
public async getSigningAccount(): Promise<SigningAccount> {
return new LedgerService().resolveLedgerAccount(this.networkType, this.simpleWallet.path, this.simpleWallet.optin);
return new LedgerService(this.simpleWallet.simulator).resolveLedgerAccount(
this.networkType,
this.simpleWallet.path,
this.simpleWallet.optin,
);
}
}
1 change: 1 addition & 0 deletions src/models/profileCreation.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export interface LedgerProfileCreation extends ProfileCreationBase {
publicKey: string;
path: string;
optin: boolean;
simulator: boolean;
}
/**
* Type for profile creation arguments
Expand Down
1 change: 1 addition & 0 deletions src/models/profileDTO.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface ILedgerWalletDTO extends ICliWalletDTO {
path: string;
publicKey: string;
optin: boolean;
simulator: boolean;
}

/**
Expand Down
16 changes: 12 additions & 4 deletions src/resolvers/ledger.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@

import { Options } from 'clime';
import { Ora } from 'ora';
import { LedgerDerivationPath } from 'symbol-ledger-typescript/lib';
import { LedgerDerivationPath } from 'symbol-ledger-typescript';
import { NetworkType } from 'symbol-sdk';
import { OptionsChoiceResolver } from '../options-resolver';
import { OptionsChoiceResolver, OptionsConfirmResolver } from '../options-resolver';
import { LedgerService } from '../services/ledger.service';
import { Resolver } from './resolver';

Expand All @@ -33,9 +33,16 @@ export class LedgerResolver implements Resolver {
* Resolves a mnemonic passphrase provided by the user.
* @returns {Promise<string>}
*/
async resolve(options: Options): Promise<{ path: string; optin: boolean; publicKey: string }> {
async resolve(options: Options): Promise<{ path: string; optin: boolean; publicKey: string; simulator: boolean }> {
const simulator = await OptionsConfirmResolver(
options,
'simulator',
'Do you want to connect to the Ledger Speculus simulator?',
'confirm',
false,
);
this.spinner.start('Connecting to Ledger');
const accounts = await new LedgerService().resolveLedgerAccountInformation(this.networkType, this.optin);
const accounts = await new LedgerService(simulator).resolveLedgerAccountInformation(this.networkType, this.optin);
this.spinner.stop();
const choices = accounts.map((key, index) => ({
// Index is shown as 1-based to match with other wallets UX
Expand All @@ -49,6 +56,7 @@ export class LedgerResolver implements Resolver {
path: LedgerDerivationPath.getPath(this.networkType as number, accountIndex),
publicKey: accounts[accountIndex].publicKey,
optin: this.optin,
simulator,
};
}
}
10 changes: 7 additions & 3 deletions src/services/ledger.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@
* limitations under the License.
*/
import TransportNodeHid from '@ledgerhq/hw-transport-node-hid';
// @ts-ignore
import SpeculosTransport from '@ledgerhq/hw-transport-node-speculos'; // NO TS types yet from Ledger
import { ExpectedError } from 'clime';
import { AppVersion, LedgerDerivationPath, SymbolLedger } from 'symbol-ledger-typescript';
import { NetworkType } from 'symbol-sdk';
import { LedgerAccount, SigningAccountInfo } from './signing.service';

/**
* A service that knows how to connect and retrieve Ledger accounts from a ledger device.
*/
export class LedgerService {
constructor(private readonly simulator: boolean) {}
/**
* Returns the accounts 0-9 form the device.
* @param networkType the network type
Expand Down Expand Up @@ -77,7 +79,9 @@ export class LedgerService {
try {
console.log();
console.log('Looking for a Ledger device...');
const transport = await TransportNodeHid.create(10000, 10000);
const transport = this.simulator
? await SpeculosTransport.open({ apduPort: 9999 })
: await TransportNodeHid.create(10000, 10000);
ledger = new SymbolLedger(transport, 'XYM');
appVersion = await ledger.getAppVersion();
} catch (e) {
Expand All @@ -93,7 +97,7 @@ export class LedgerService {

console.log(`Symbol App version is ${printVersion(appVersion)}`);
if (!ledger.isVersionSupported(appVersion, expectedAppVersion)) {
if (ledger) ledger.close();
ledger.close();
throw new ExpectedError(
`The current Symbol Application ${printVersion(appVersion)} is not supported. Expected version is at least ${printVersion(
expectedAppVersion,
Expand Down