Skip to content

Commit

Permalink
✨ (lld): use dmk listenToKnownDevices in LLD
Browse files Browse the repository at this point in the history
  • Loading branch information
valpinkman committed Jan 6, 2025
1 parent a2a8235 commit 384c9ad
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 29 deletions.
5 changes: 5 additions & 0 deletions .changeset/heavy-lobsters-rush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ledgerhq/live-dmk": minor
---

Update Transport.listen to emit add and remove events
5 changes: 5 additions & 0 deletions .changeset/tall-toys-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ledger-live-desktop": minor
---

use dmk listenToKnownDevices in LLD:useListenToHidDevices
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
Expand Up @@ -14,8 +14,6 @@ import Ellipsis from "~/renderer/components/Ellipsis";
import { BoxProps } from "./Box/Box";
import { Icons } from "@ledgerhq/react-ui";

console.log(Icons);

const T = styled(Box).attrs((p: { color?: string; inline?: boolean; ff?: string } & BoxProps) => ({
ff: p.ff || "Inter|Medium",
horizontal: true,
Expand All @@ -32,12 +30,10 @@ const T = styled(Box).attrs((p: { color?: string; inline?: boolean; ff?: string
width: ${p => (p.inline ? "" : "100%")};
overflow: hidden;
`;
const I = ({ color, children }: { color?: string; children: React.ReactNode }) => (
const I = ({ color = undefined, children }: { color?: string; children: React.ReactNode }) => (
<Box color={color}>{children}</Box>
);
I.defaultProps = {
color: undefined,
};

export type OwnProps = {
unit?: Unit;
val?: BigNumber | number;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { Subscription, Observable } from "rxjs";
import { useFeature } from "@ledgerhq/live-common/featureFlags/index";
import { useDeviceManagementKit, DeviceManagementKitTransport } from "@ledgerhq/live-dmk";
import { DeviceModelId } from "@ledgerhq/types-devices";
import { IPCTransport } from "~/renderer/IPCTransport";
import { addDevice, removeDevice, resetDevices } from "~/renderer/actions/devices";
import { IPCTransport } from "../IPCTransport";

export const useListenToHidDevices = () => {
const dispatch = useDispatch();
const ldmkFeatureFlag = useFeature("ldmkTransport");

const deviceManagementKit = useDeviceManagementKit();

useEffect(() => {
console.log("[[useListenToHidDevices]] init", deviceManagementKit);
let sub: Subscription;
function syncDevices() {
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 @@ -23,32 +29,63 @@ export const useListenToHidDevices = () => {
};

if (type === "add") {
devices[deviceId] = true;
dispatch(addDevice(stateDevice));
} else if (type === "remove") {
delete devices[deviceId];
dispatch(removeDevice(stateDevice));
}
}
},
() => {
error: () => {
resetDevices();
syncDevices();
},
() => {
complete: () => {
resetDevices();
syncDevices();
},
);
});
}

function syncDevicesWithDmk() {
sub = new Observable(DeviceManagementKitTransport.listen).subscribe({
next: ({ descriptor, device, deviceModel, type }) => {
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",
};
if (type === "add") {
dispatch(addDevice(stateDevice));
} else if (type === "remove") {
dispatch(removeDevice(stateDevice));
}
}
},
error: () => {
resetDevices();
syncDevicesWithDmk();
},
complete: () => {
resetDevices();
syncDevicesWithDmk();
},
});
}

const timeoutSyncDevices = setTimeout(syncDevices, 1000);
const fn = ldmkFeatureFlag?.enabled ? syncDevicesWithDmk : syncDevices;

const timeoutSyncDevices = setTimeout(fn, 1000);

return () => {
console.log("[[useListenToHidDevices]] cleanup");
clearTimeout?.(timeoutSyncDevices);
sub?.unsubscribe?.();
};
}, [dispatch]);
}, [dispatch, deviceManagementKit, ldmkFeatureFlag?.enabled]);

return null;
};
8 changes: 2 additions & 6 deletions apps/ledger-live-desktop/src/renderer/live-common-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ 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 { getEnv } from "@ledgerhq/live-env";
import { retry } from "@ledgerhq/live-common/promise";
import { TraceContext, listen as listenLogs, trace } from "@ledgerhq/logs";
import { getUserId } from "~/helpers/user";
Expand All @@ -13,13 +15,7 @@ import logger from "./logger";
import { setDeviceMode } from "@ledgerhq/live-common/hw/actions/app";
import { getFeature } from "@ledgerhq/live-common/featureFlags/index";
import { overriddenFeatureFlagsSelector } from "~/renderer/reducers/settings";
import { State } from "./reducers";
import { DeviceManagementKitTransport } from "@ledgerhq/live-dmk";
import { getEnv } from "@ledgerhq/live-env";

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

const isDeviceManagementKitEnabled = (store: Store) => {
const state = store.getState();
Expand Down
63 changes: 58 additions & 5 deletions libs/live-dmk/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@ import Transport from "@ledgerhq/hw-transport";
import {
DeviceManagementKitBuilder,
ConsoleLogger,
type DeviceManagementKit,
DeviceManagementKit,
type DeviceSessionState,
DeviceStatus,
LogLevel,
BuiltinTransports,
DiscoveredDevice,
} from "@ledgerhq/device-management-kit";
import { BehaviorSubject, firstValueFrom } from "rxjs";
import { DescriptorEvent } from "@ledgerhq/types-devices";
import { BehaviorSubject, firstValueFrom, map, Observer, pairwise, startWith } from "rxjs";
import { LocalTracer } from "@ledgerhq/logs";

const deviceManagementKit = new DeviceManagementKitBuilder()
.addTransport(BuiltinTransports.USB)
.addLogger(new ConsoleLogger(LogLevel.Debug))
.addLogger(new ConsoleLogger(LogLevel.Info))
.build();

export const DeviceManagementKitContext = createContext<DeviceManagementKit>(deviceManagementKit);
Expand Down Expand Up @@ -135,18 +137,69 @@ export class DeviceManagementKitTransport extends Transport {
return transport;
}

static listen = (observer: Observer<DescriptorEvent<string>>) => {
const subscription = deviceManagementKit
.listenToKnownDevices()
.pipe(
startWith<DiscoveredDevice[]>([]),
pairwise(),
map(([prev, curr]) => {
const added = curr.filter(item => !prev.some(prevItem => prevItem.id === item.id));
const removed = prev.filter(item => !curr.some(currItem => currItem.id === item.id));
return { added, removed };
}),
)
.subscribe({
next: ({ added, removed }) => {
for (const device of added) {
tracer.trace(`[listen] device added ${device.deviceModel.model}`);
observer.next({
type: "add",
descriptor: "",
device: device,
deviceModel: {
// @ts-expect-error types are not matching
id: device.deviceModel.model,
type: device.transport,
},
});
}

for (const device of removed) {
tracer.trace(`[listen] device removed ${device.deviceModel.model}`);
observer.next({
type: "remove",
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}`);
tracer.trace(`[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}`);
tracer.trace(`[exchange] <= ${response.toString("hex")}`);
return response;
})
.catch(e => {
Expand Down
3 changes: 3 additions & 0 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 384c9ad

Please sign in to comment.