Split Ledger (& transports) into package (#399)

* Split Ledger (& transports) into package

* Fix browser/node resolution

* Bump
This commit is contained in:
Jaco Greeff
2020-11-10 16:11:50 +01:00
committed by GitHub
parent 0da5471b3e
commit 2692c90106
18 changed files with 524 additions and 47 deletions
+1 -14
View File
@@ -11,34 +11,21 @@
],
"contributors": [],
"license": "Apache-2.0",
"browser": {
"./ledger/transportsNode": "./ledger/transportsEmpty"
},
"react-native": {
"./ledger/transportsNode": "./ledger/transportsEmpty",
"./ledger/transportsWeb": "./ledger/transportsEmpty"
},
"dependencies": {
"@babel/runtime": "^7.12.5",
"@ledgerhq/hw-transport-webusb": "^5.28.0",
"@polkadot/keyring": "^4.0.2-8",
"@polkadot/ledger": "^0.62.2-4",
"@polkadot/ui-settings": "0.62.2-4",
"@polkadot/util": "^4.0.2-8",
"@zondax/ledger-polkadot": "^0.11.0",
"mkdirp": "^1.0.4",
"rxjs": "^6.6.3",
"store": "^2.0.12"
},
"devDependencies": {
"@polkadot/types": "^2.6.2-4",
"@types/ledgerhq__hw-transport-node-hid": "^4.22.2",
"@types/ledgerhq__hw-transport-webusb": "^4.70.1",
"@types/mkdirp": "^1.0.1",
"@types/store": "^2.0.2"
},
"optionalDependencies": {
"@ledgerhq/hw-transport-node-hid": "^5.28.0"
},
"peerDependencies": {
"@polkadot/keyring": "*",
"@polkadot/ui-settings": "*",
+7
View File
@@ -0,0 +1,7 @@
// Copyright 2017-2020 @polkadot/ui-keyring authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { detectPackage } from '@polkadot/util';
// eslint-disable-next-line @typescript-eslint/no-var-requires
detectPackage(require('./package.json'), typeof __dirname !== 'undefined' && __dirname);
+3 -8
View File
@@ -1,17 +1,12 @@
// Copyright 2017-2020 @polkadot/ui-keyring authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { detectPackage } from '@polkadot/util';
import './detectPackage';
import keyring, { Keyring } from './Keyring';
import Ledger from './ledger';
// eslint-disable-next-line @typescript-eslint/no-var-requires
detectPackage(require('./package.json'), typeof __dirname !== 'undefined' && __dirname);
export { default as Ledger } from '@polkadot/ledger';
export default keyring;
export {
Keyring,
Ledger
};
export { Keyring };
-116
View File
@@ -1,116 +0,0 @@
// Copyright 2017-2020 @polkadot/ui-keyring authors & contributors
// SPDX-License-Identifier: Apache-2.0
import Transport from '@ledgerhq/hw-transport';
import { AccountOptions, LedgerAddress, LedgerSignature, LedgerTypes, LedgerVersion } from './types';
import { ResponseBase, SubstrateApp, newKusamaApp, newPolkadotApp } from '@zondax/ledger-polkadot';
import { assert, bufferToU8a, u8aToBuffer, u8aToHex } from '@polkadot/util';
import allNode from './transportsNode';
import allWeb from './transportsWeb';
export const LEDGER_DEFAULT_ACCOUNT = 0x80000000;
export const LEDGER_DEFAULT_CHANGE = 0x80000000;
export const LEDGER_DEFAULT_INDEX = 0x80000000;
const SUCCESS_CODE = 0x9000;
const transports = allNode.concat(allWeb);
const APPS: Record<string, (transport: Transport) => SubstrateApp> = {
kusama: newKusamaApp,
polkadot: newPolkadotApp
};
type Chain = keyof typeof APPS;
// A very basic wrapper for a ledger app -
// - it connects automatically, creating an app as required
// - Promises return errors (instead of wrapper errors)
export default class Ledger {
#app: SubstrateApp | null = null;
#chain: Chain;
#transport: LedgerTypes;
constructor (transport: LedgerTypes, chain: Chain) {
// u2f is deprecated
assert(['hid', 'webusb'].includes(transport), `Unsupported transport ${transport}`);
assert(Object.keys(APPS).includes(chain), `Unsupported chain ${chain}`);
this.#chain = chain;
this.#transport = transport;
}
public async getAddress (confirm = false, accountOffset = 0, addressOffset = 0, { account = LEDGER_DEFAULT_ACCOUNT, addressIndex = LEDGER_DEFAULT_INDEX, change = LEDGER_DEFAULT_CHANGE }: Partial<AccountOptions> = {}): Promise<LedgerAddress> {
return this.#withApp(async (app: SubstrateApp): Promise<LedgerAddress> => {
const { address, pubKey } = await this.#wrapError(app.getAddress(account + accountOffset, change, addressIndex + addressOffset, confirm));
return {
address,
publicKey: `0x${pubKey}`
};
});
}
public async getVersion (): Promise<LedgerVersion> {
return this.#withApp(async (app: SubstrateApp): Promise<LedgerVersion> => {
const { device_locked: isLocked, major, minor, patch, test_mode: isTestMode } = await this.#wrapError(app.getVersion());
return {
isLocked,
isTestMode,
version: [major, minor, patch]
};
});
}
public async sign (message: Uint8Array, accountOffset = 0, addressOffset = 0, { account = LEDGER_DEFAULT_ACCOUNT, addressIndex = LEDGER_DEFAULT_INDEX, change = LEDGER_DEFAULT_CHANGE }: Partial<AccountOptions> = {}): Promise<LedgerSignature> {
return this.#withApp(async (app: SubstrateApp): Promise<LedgerSignature> => {
const buffer = u8aToBuffer(message);
const { signature } = await this.#wrapError(app.sign(account + accountOffset, change, addressIndex + addressOffset, buffer));
return {
signature: u8aToHex(bufferToU8a(signature))
};
});
}
#getApp = async (): Promise<SubstrateApp> => {
if (!this.#app) {
const def = transports.find(({ type }) => type === this.#transport);
assert(def, `Unable to find a transport for ${this.#transport}`);
const transport = await def.create();
this.#app = APPS[this.#chain](transport);
}
return this.#app;
};
#withApp = async <T> (fn: (app: SubstrateApp) => Promise<T>): Promise<T> => {
try {
const app = await this.#getApp();
return await fn(app);
} catch (error) {
this.#app = null;
throw error;
}
};
#wrapError = async <T extends ResponseBase> (promise: Promise<T>): Promise<T> => {
const result = await promise;
assert(result.return_code === SUCCESS_CODE, result.error_message);
return result;
};
}
@@ -1,8 +0,0 @@
// Copyright 2017-2020 @polkadot/ui-keyring authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { TransportDef } from './types';
const transports: TransportDef[] = [];
export default transports;
@@ -1,17 +0,0 @@
// Copyright 2017-2020 @polkadot/ui-keyring authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { TransportDef } from './types';
import Transport from '@ledgerhq/hw-transport';
import LedgerHid from '@ledgerhq/hw-transport-node-hid';
const transports: TransportDef[] = [
{
create: (): Promise<Transport> =>
LedgerHid.create(),
type: 'hid'
}
];
export default transports;
@@ -1,24 +0,0 @@
// Copyright 2017-2020 @polkadot/ui-keyring authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { TransportDef } from './types';
import Transport from '@ledgerhq/hw-transport';
import LedgerWebUSB from '@ledgerhq/hw-transport-webusb';
const transports: TransportDef[] = [
// deprecated
// import LedgerU2F from '@ledgerhq/hw-transport-u2f';
// {
// create: (): Promise<Transport> =>
// LedgerU2F.create(),
// type: 'u2f'
// },
{
create: (): Promise<Transport> =>
LedgerWebUSB.create(),
type: 'webusb'
}
];
export default transports;
-32
View File
@@ -1,32 +0,0 @@
// Copyright 2017-2020 @polkadot/ui-keyring authors & contributors
// SPDX-License-Identifier: Apache-2.0
import Transport from '@ledgerhq/hw-transport';
export type LedgerTypes = 'hid' | 'u2f' | 'webusb';
export interface AccountOptions {
account: number;
addressIndex: number;
change: number;
}
export interface LedgerAddress {
address: string;
publicKey: string;
}
export interface LedgerSignature {
signature: string;
}
export interface LedgerVersion {
isLocked: boolean;
isTestMode: boolean;
version: [number, number, number];
}
export interface TransportDef {
create (): Promise<Transport>;
type: LedgerTypes;
}