Skip to content

Commit

Permalink
!wip
Browse files Browse the repository at this point in the history
  • Loading branch information
valpinkman committed Dec 2, 2024
1 parent f15fdd3 commit 8c5771b
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 63 deletions.
1 change: 1 addition & 0 deletions apps/ledger-live-desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"@ledgerhq/coin-filecoin": "workspace:^",
"@ledgerhq/coin-framework": "workspace:^",
"@ledgerhq/devices": "workspace:*",
"@ledgerhq/device-management-kit": "0.5.1",
"@ledgerhq/domain-service": "workspace:^",
"@ledgerhq/errors": "workspace:^",
"@ledgerhq/ethereum-provider": "workspace:^",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { useDispatch, useStore } from "react-redux";
import { Subscription, Observable } from "rxjs";
import { DeviceModelId } from "@ledgerhq/types-devices";
import { useDeviceManagementKit, DeviceManagementKitTransport } from "@ledgerhq/live-dmk";
import { getFeature } from "@ledgerhq/live-common/featureFlags/index";
import { IPCTransport } from "~/renderer/IPCTransport";
import { addDevice, removeDevice, resetDevices } from "~/renderer/actions/devices";
import { IPCTransport } from "../IPCTransport";
import { overriddenFeatureFlagsSelector } from "~/renderer/reducers/settings";
import { State } from "~/renderer/reducers";

export const useListenToHidDevices = () => {
const dispatch = useDispatch();
const store = useStore<State>();
const localOverrides = overriddenFeatureFlagsSelector(store.getState());
const ldmkFeatureFlag = getFeature({ key: "ldmkTransport", localOverrides });

const deviceManagementKit = useDeviceManagementKit();

useEffect(() => {
let sub: Subscription;
function syncDevices() {
const devices: { [key: string]: boolean } = {};
const devices: { [key: string]: boolean } = {};

sub = new Observable(IPCTransport.listen).subscribe(
({ device, deviceModel, type, descriptor }) => {
function syncDevices() {
sub = new Observable(IPCTransport.listen).subscribe({
next: ({ device, deviceModel, type, descriptor }) => {
if (device) {
const deviceId = descriptor || "";
const stateDevice = {
Expand All @@ -31,24 +41,53 @@ export const useListenToHidDevices = () => {
}
}
},
() => {
error: () => {
resetDevices();
syncDevices();
},
() => {
complete: () => {
resetDevices();
syncDevices();
},
);
});
}

const timeoutSyncDevices = setTimeout(syncDevices, 1000);
function syncDevicesWithDmk() {
sub = new Observable(DeviceManagementKitTransport.listen).subscribe({
next: ({ descriptor, device, deviceModel }) => {
if (device) {
const deviceId = descriptor || "";
const stateDevice = {
deviceId,
modelId: deviceModel ? deviceModel.id : DeviceModelId.nanoS,
// TODO: Update the Transport.listen type whenever we switch to LDMK
// @ts-expect-error remapping type
wired: deviceModel?.type === "USB",
};
console.log("[useListenToHidDevices] device added", stateDevice);
dispatch(addDevice(stateDevice));
}
},
error: () => {
resetDevices();
syncDevicesWithDmk();
},
complete: () => {
resetDevices();
syncDevicesWithDmk();
},
});
}

const fn = ldmkFeatureFlag.enabled ? syncDevicesWithDmk : syncDevices;

const timeoutSyncDevices = setTimeout(fn, 1000);

return () => {
clearTimeout?.(timeoutSyncDevices);
sub?.unsubscribe?.();
};
}, [dispatch]);
}, [dispatch, deviceManagementKit, ldmkFeatureFlag]);

return null;
};
80 changes: 37 additions & 43 deletions apps/ledger-live-desktop/src/renderer/live-common-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import "~/live-common-setup-base";
import "~/live-common-set-supported-currencies";
import "./families";

import { Store } from "redux";
import VaultTransport from "@ledgerhq/hw-transport-vault";
import { registerTransportModule } from "@ledgerhq/live-common/hw/index";
import { retry } from "@ledgerhq/live-common/promise";
Expand All @@ -13,16 +14,11 @@ import logger from "./logger";
import { currentMode, setDeviceMode } from "@ledgerhq/live-common/hw/actions/app";
import { getFeature } from "@ledgerhq/live-common/featureFlags/index";
import { FeatureId } from "@ledgerhq/types-live";
import { getEnv } from "@ledgerhq/live-env";
import { overriddenFeatureFlagsSelector } from "~/renderer/reducers/settings";
import { State } from "./reducers";
import { DeviceManagementKitTransport } from "@ledgerhq/live-dmk";

interface Store {
getState: () => State;
}

const getFeatureWithOverrides = (key: FeatureId, store: Store) => {
const getFeatureWithOverrides = (key: FeatureId, store: Store<State>) => {
const state = store.getState();
const localOverrides = overriddenFeatureFlagsSelector(state);
return getFeature({ key, localOverrides });
Expand All @@ -31,8 +27,6 @@ const getFeatureWithOverrides = (key: FeatureId, store: Store) => {
export function registerTransportModules(store: Store) {
setEnvOnAllThreads("USER_ID", getUserId());
const vaultTransportPrefixID = "vault-transport:";
const isSpeculosEnabled = !!getEnv("SPECULOS_API_PORT");
const isProxyEnabled = !!getEnv("DEVICE_PROXY_URL");
const ldmkFeatureFlag = getFeatureWithOverrides("ldmkTransport", store);

listenLogs(({ id, date, ...log }) => {
Expand All @@ -43,8 +37,7 @@ export function registerTransportModules(store: Store) {
if (ldmkFeatureFlag.enabled) {
registerTransportModule({
id: "sdk",
open: (_id: string, timeoutMs?: number, context?: TraceContext) => {
if (isSpeculosEnabled && isProxyEnabled) return null;
open: async (_id: string, timeoutMs?: number, context?: TraceContext) => {
trace({
type: "renderer-setup",
message: "Open called on registered module",
Expand All @@ -56,46 +49,47 @@ export function registerTransportModules(store: Store) {
openContext: context,
},
});
return DeviceManagementKitTransport.open();

const transport = await DeviceManagementKitTransport.open();
return transport;
},

disconnect: () => Promise.resolve(),
});
} else {
// Register IPC Transport Module
registerTransportModule({
id: "ipc",
open: (id: string, timeoutMs?: number, context?: TraceContext) => {
if (isSpeculosEnabled || isProxyEnabled) return null;
const originalDeviceMode = currentMode;
// id could be another type of transport such as vault-transport
if (id.startsWith(vaultTransportPrefixID)) return;
}

if (originalDeviceMode !== currentMode) {
setDeviceMode(originalDeviceMode);
}
// Register IPC Transport Module
registerTransportModule({
id: "ipc",
open: (id: string, timeoutMs?: number, context?: TraceContext) => {
const originalDeviceMode = currentMode;
// id could be another type of transport such as vault-transport
if (id.startsWith(vaultTransportPrefixID)) return;

trace({
type: "renderer-setup",
message: "Open called on registered module",
data: {
transport: "IPCTransport",
timeoutMs,
},
context: {
openContext: context,
},
});
if (originalDeviceMode !== currentMode) {
setDeviceMode(originalDeviceMode);
}

// Retries in the `renderer` process if the open failed. No retry is done in the `internal` process to avoid multiplying retries.
return retry(() => IPCTransport.open(id, timeoutMs, context), {
interval: 500,
maxRetry: 4,
});
},
disconnect: () => Promise.resolve(),
});
}
trace({
type: "renderer-setup",
message: "Open called on registered module",
data: {
transport: "IPCTransport",
timeoutMs,
},
context: {
openContext: context,
},
});

// Retries in the `renderer` process if the open failed. No retry is done in the `internal` process to avoid multiplying retries.
return retry(() => IPCTransport.open(id, timeoutMs, context), {
interval: 500,
maxRetry: 4,
});
},
disconnect: () => Promise.resolve(),
});

// Register Vault Transport Module
registerTransportModule({
Expand Down
3 changes: 2 additions & 1 deletion apps/ledger-live-desktop/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"compilerOptions": {
"allowJs": false,
"composite": true,
"module": "es2020",
"module": "nodenext",
"moduleResolution": "nodenext",
"jsx": "react",
"lib": ["ES2021", "dom"],
"target": "esnext",
Expand Down
6 changes: 5 additions & 1 deletion libs/live-dmk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,18 @@
}
},
"dependencies": {
"@ledgerhq/device-management-kit": "^0.5.1",
"@ledgerhq/types-devices": "workspace:^",
"@ledgerhq/hw-transport": "workspace:^",
"@ledgerhq/logs": "^6.12.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"rxjs": "^7.8.1"
},
"peerDependencies": {
"@ledgerhq/device-management-kit": "^0.5.1"
},
"devDependencies": {
"@ledgerhq/device-management-kit": "^0.5.1",
"@testing-library/dom": "^9.3.3",
"@testing-library/jest-dom": "^6.6.2",
"@testing-library/react": "^16.0.1",
Expand Down
34 changes: 30 additions & 4 deletions libs/live-dmk/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import Transport from "@ledgerhq/hw-transport";
import {
DeviceManagementKitBuilder,
ConsoleLogger,
type DeviceManagementKit,
DeviceManagementKit,
type DeviceSessionState,
DeviceStatus,
LogLevel,
BuiltinTransports,
} from "@ledgerhq/device-management-kit";
import { BehaviorSubject, firstValueFrom } from "rxjs";
import { DescriptorEvent } from "@ledgerhq/types-devices";
import { BehaviorSubject, firstValueFrom, Observer } from "rxjs";
import { LocalTracer } from "@ledgerhq/logs";

const deviceManagementKit = new DeviceManagementKitBuilder()
Expand Down Expand Up @@ -135,18 +136,43 @@ export class DeviceManagementKitTransport extends Transport {
return transport;
}

static listen = (observer: Observer<DescriptorEvent<string>>) => {
const subscription = deviceManagementKit.listenToKnownDevices().subscribe({
next: knownDevices => {
for (const device of knownDevices) {
observer.next({
type: "add",
descriptor: "",
device: device,
deviceModel: {
// @ts-expect-error types are not matching
id: device.deviceModel.model,
type: device.transport,
},
});
}
},
error: observer.error,
complete: observer.complete,
});

return {
unsubscribe: () => subscription.unsubscribe(),
};
};

close: () => Promise<void> = () => Promise.resolve();

async exchange(apdu: Buffer): Promise<Buffer> {
tracer.trace(`[exchange] => ${apdu}`);
console.log(`[exchange] => ${apdu.toString("hex")}`);
return await this.sdk
.sendApdu({
sessionId: this.sessionId,
apdu: new Uint8Array(apdu),
})
.then((apduResponse: { data: Uint8Array; statusCode: Uint8Array }): Buffer => {
const response = Buffer.from([...apduResponse.data, ...apduResponse.statusCode]);
tracer.trace(`[exchange] <= ${response}`);
console.log(`[exchange] <= ${response.toString("hex")}`);
return response;
});
}
Expand Down
12 changes: 9 additions & 3 deletions pnpm-lock.yaml

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

0 comments on commit 8c5771b

Please sign in to comment.