Skip to content

Commit

Permalink
🏗️ [Support] Request device access within HWDeviceProvider (#7440)
Browse files Browse the repository at this point in the history
* Initialize the HWDeviceProvider with an `Observable<WithDevice>`

* Update `useTrustchainSdk` accordingly

* Fix tests

* Pass an observable of the device id instead of a function

* Factor `hwDeviceProvider.withHw` calls into a function

* Pass the device id to sdk methods

* Remove the "Config" object to get the SDK

* Apply changes to LLM and to the web tool

* Fix typo in a comment

* Fix tests and scenarios

* Re-generate failing tests

* Fix the web tools build

* Fix LLM build

* Update changelog

* Avoid type castings in the SDK method calls

* Actually apply the SDK refactor to the test scenarios

---------

Co-authored-by: Theophile Sandoz <Theophile Sandoz>
  • Loading branch information
thesan committed Aug 8, 2024
1 parent 858c79e commit bc044e4
Show file tree
Hide file tree
Showing 59 changed files with 1,631 additions and 1,598 deletions.
8 changes: 8 additions & 0 deletions .changeset/rude-moons-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"ledger-live-desktop": minor
"live-mobile": minor
"@ledgerhq/trustchain": minor
"@ledgerhq/web-tools": minor
---

Request device access within `HWDeviceProvider`
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@ import { memberCredentialsSelector, setTrustchain } from "@ledgerhq/trustchain/s
import { useDispatch, useSelector } from "react-redux";
import { setFlow } from "~/renderer/actions/walletSync";
import { Flow, Step } from "~/renderer/reducers/walletSync";
import { useTrustchainSdk, runWithDevice } from "./useTrustchainSdk";
import {
MemberCredentials,
TrustchainResult,
TrustchainResultType,
} from "@ledgerhq/trustchain/types";
import { useTrustchainSdk } from "./useTrustchainSdk";
import { TrustchainResult, TrustchainResultType } from "@ledgerhq/trustchain/types";
import { useCallback, useEffect, useRef, useState } from "react";

export function useAddMember({ device }: { device: Device | null }) {
Expand Down Expand Up @@ -52,24 +48,24 @@ export function useAddMember({ device }: { device: Device | null }) {
};

useEffect(() => {
if (!deviceRef.current) {
handleMissingDevice();
}

const addMember = async () => {
try {
await runWithDevice(deviceRef.current?.deviceId, async transport => {
const trustchainResult = await sdkRef.current.getOrCreateTrustchain(
transport,
memberCredentialsRef.current as MemberCredentials,
{
onStartRequestUserInteraction: () => setUserDeviceInteraction(true),
onEndRequestUserInteraction: () => setUserDeviceInteraction(false),
},
);
if (!deviceRef.current) {
return handleMissingDevice();
}
if (!memberCredentialsRef.current) {
throw new Error("memberCredentials is not set");
}
const trustchainResult = await sdkRef.current.getOrCreateTrustchain(
deviceRef.current.deviceId,
memberCredentialsRef.current,
{
onStartRequestUserInteraction: () => setUserDeviceInteraction(true),
onEndRequestUserInteraction: () => setUserDeviceInteraction(false),
},
);

transitionToNextScreen(trustchainResult);
});
transitionToNextScreen(trustchainResult);
} catch (error) {
setError(error as Error);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {
import { useDispatch, useSelector } from "react-redux";
import { setFlow } from "~/renderer/actions/walletSync";
import { Flow, Step } from "~/renderer/reducers/walletSync";
import { useTrustchainSdk, runWithDevice } from "./useTrustchainSdk";
import { TrustchainMember, Trustchain, MemberCredentials } from "@ledgerhq/trustchain/types";
import { useTrustchainSdk } from "./useTrustchainSdk";
import { TrustchainMember, Trustchain } from "@ledgerhq/trustchain/types";
import { useCallback, useEffect, useRef, useState } from "react";
import { TrustchainNotAllowed } from "@ledgerhq/trustchain/errors";

Expand Down Expand Up @@ -51,20 +51,24 @@ export function useRemoveMember({ device, member }: Props) {
const removeMember = useCallback(
async (member: TrustchainMember) => {
try {
await runWithDevice(deviceRef.current?.deviceId, async transport => {
const newTrustchain = await sdkRef.current.removeMember(
transport,
trustchainRef.current as Trustchain,
memberCredentialsRef.current as MemberCredentials,
member,
{
onStartRequestUserInteraction: () => setUserDeviceInteraction(true),
onEndRequestUserInteraction: () => setUserDeviceInteraction(false),
},
);
if (!deviceRef.current) {
throw new Error("Device not found");
}
if (!trustchainRef.current || !memberCredentialsRef.current) {
throw new Error("trustchain or memberCredentials is not set");
}
const newTrustchain = await sdkRef.current.removeMember(
deviceRef.current.deviceId,
trustchainRef.current,
memberCredentialsRef.current,
member,
{
onStartRequestUserInteraction: () => setUserDeviceInteraction(true),
onEndRequestUserInteraction: () => setUserDeviceInteraction(false),
},
);

transitionToNextScreen(newTrustchain);
});
transitionToNextScreen(newTrustchain);
} catch (error) {
if (error instanceof Error) setError(error);
if (error instanceof TrustchainNotAllowed) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import os from "os";
import { from, lastValueFrom } from "rxjs";
import { useMemo } from "react";
import { getEnv } from "@ledgerhq/live-env";
import { getSdk } from "@ledgerhq/trustchain/index";
import { withDevice } from "@ledgerhq/live-common/hw/deviceAccess";
import Transport from "@ledgerhq/hw-transport";
import { trustchainLifecycle } from "@ledgerhq/live-wallet/walletsync/index";
import { useStore } from "react-redux";
import { walletSelector } from "~/renderer/reducers/wallet";
Expand All @@ -13,13 +11,6 @@ import { TrustchainSDK } from "@ledgerhq/trustchain/types";
import { useFeature } from "@ledgerhq/live-common/featureFlags/index";
import getWalletSyncEnvironmentParams from "@ledgerhq/live-common/walletSync/getEnvironmentParams";

export function runWithDevice<T>(
deviceId: string | undefined,
fn: (transport: Transport) => Promise<T>,
): Promise<T> {
return lastValueFrom(withDevice(deviceId || "")(transport => from(fn(transport))));
}

const platformMap: Record<string, string | undefined> = {
darwin: "Mac",
win32: "Windows",
Expand Down Expand Up @@ -52,7 +43,7 @@ export function useTrustchainSdk() {
);

if (sdkInstance === null) {
sdkInstance = getSdk(isMockEnv, defaultContext, lifecycle);
sdkInstance = getSdk(isMockEnv, defaultContext, withDevice, lifecycle);
}

return sdkInstance;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { getSdk } from "@ledgerhq/trustchain/index";
import { EMPTY } from "rxjs";
import { Flow, initialStateWalletSync, Step } from "~/renderer/reducers/walletSync";
import getWalletSyncEnvironmentParams from "@ledgerhq/live-common/walletSync/getEnvironmentParams";

export const mockedSdk = getSdk(true, {
applicationId: 12,
name: "LLD Integration",
apiBaseUrl: getWalletSyncEnvironmentParams("STAGING").trustchainApiBaseUrl,
});
export const mockedSdk = getSdk(
true,
{
applicationId: 12,
name: "LLD Integration",
apiBaseUrl: getWalletSyncEnvironmentParams("STAGING").trustchainApiBaseUrl,
},
() => () => EMPTY,
);

export const walletSyncActivatedState = {
...initialStateWalletSync,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { memberCredentialsSelector, setTrustchain } from "@ledgerhq/trustchain/store";
import { useDispatch, useSelector } from "react-redux";
import { useTrustchainSdk, runWithDevice } from "./useTrustchainSdk";
import {
MemberCredentials,
TrustchainResult,
TrustchainResultType,
} from "@ledgerhq/trustchain/types";
import { useTrustchainSdk } from "./useTrustchainSdk";
import { TrustchainResult, TrustchainResultType } from "@ledgerhq/trustchain/types";
import { useCallback, useEffect, useRef, useState } from "react";
import { Device } from "@ledgerhq/live-common/hw/actions/types";
import { useNavigation } from "@react-navigation/native";
Expand Down Expand Up @@ -40,19 +36,21 @@ export function useAddMember({ device }: { device: Device | null }) {
useEffect(() => {
const addMember = async () => {
try {
await runWithDevice(device?.deviceId || "", async transport => {
const trustchainResult = await sdk.getOrCreateTrustchain(
transport,
memberCredentialsRef.current as MemberCredentials,
{
onStartRequestUserInteraction: () => setUserDeviceInteraction(true),
onEndRequestUserInteraction: () => setUserDeviceInteraction(false),
},
);
if (trustchainResult) {
transitionToNextScreen(trustchainResult);
}
});
if (!device) return;
if (!memberCredentialsRef.current) {
throw new Error("memberCredentials is not set");
}
const trustchainResult = await sdk.getOrCreateTrustchain(
device.deviceId,
memberCredentialsRef.current,
{
onStartRequestUserInteraction: () => setUserDeviceInteraction(true),
onEndRequestUserInteraction: () => setUserDeviceInteraction(false),
},
);
if (trustchainResult) {
transitionToNextScreen(trustchainResult);
}
} catch (error) {
setError(error as Error);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
trustchainSelector,
} from "@ledgerhq/trustchain/store";
import { useDispatch, useSelector } from "react-redux";
import { useTrustchainSdk, runWithDevice } from "./useTrustchainSdk";
import { useTrustchainSdk } from "./useTrustchainSdk";
import { TrustchainMember, Trustchain } from "@ledgerhq/trustchain/types";
import { useCallback, useEffect, useState } from "react";
import { Device } from "@ledgerhq/live-common/hw/actions/types";
Expand Down Expand Up @@ -54,25 +54,23 @@ export function useRemoveMember({ device, member }: Props) {

const removeMember = useCallback(
async (member: TrustchainMember) => {
if (!device) return;
if (!trustchain || !memberCredentials) {
throw new Error("trustchain or memberCredentials is not set");
}
try {
await runWithDevice(device.deviceId, async transport => {
const newTrustchain = await sdk.removeMember(
transport,
trustchain,
memberCredentials,
member,
{
onStartRequestUserInteraction: () => setUserDeviceInteraction(true),
onEndRequestUserInteraction: () => setUserDeviceInteraction(false),
},
);
if (!device) return;
if (!trustchain || !memberCredentials) {
throw new Error("trustchain or memberCredentials is not set");
}
const newTrustchain = await sdk.removeMember(
device.deviceId,
trustchain,
memberCredentials,
member,
{
onStartRequestUserInteraction: () => setUserDeviceInteraction(true),
onEndRequestUserInteraction: () => setUserDeviceInteraction(false),
},
);

transitionToNextScreen(newTrustchain);
});
transitionToNextScreen(newTrustchain);
} catch (error) {
if (error instanceof Error) setError(error);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
import { firstValueFrom, from } from "rxjs";
import { useMemo } from "react";
import { getEnv } from "@ledgerhq/live-env";
import { getSdk } from "@ledgerhq/trustchain/index";
import Transport from "@ledgerhq/hw-transport";
import { Platform } from "react-native";
import { withDevice } from "@ledgerhq/live-common/hw/deviceAccess";
import { TrustchainSDK } from "@ledgerhq/trustchain/types";
import { useFeature } from "@ledgerhq/live-common/featureFlags/index";
import getWalletSyncEnvironmentParams from "@ledgerhq/live-common/walletSync/getEnvironmentParams";

export function runWithDevice<T>(
deviceId: string,
fn: (transport: Transport) => Promise<T>,
): Promise<T> {
return firstValueFrom(withDevice(deviceId)(transport => from(fn(transport))));
}

const platformMap: Record<string, string | undefined> = {
ios: "iOS",
android: "Android",
Expand All @@ -38,7 +29,7 @@ export function useTrustchainSdk() {
}, [trustchainApiBaseUrl]);

if (sdkInstance === null) {
sdkInstance = getSdk(isMockEnv, defaultContext);
sdkInstance = getSdk(isMockEnv, defaultContext, withDevice);
}

return sdkInstance;
Expand Down
3 changes: 2 additions & 1 deletion apps/web-tools/trustchain/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import dynamic from "next/dynamic";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import styled from "styled-components";
import { Tooltip } from "react-tooltip";
import { withDevice } from "@ledgerhq/live-common/hw/deviceAccess";
import { MemberCredentials, Trustchain, TrustchainMember } from "@ledgerhq/trustchain/types";
import { getInitialStore } from "@ledgerhq/trustchain/store";
import useEnv from "../useEnv";
Expand Down Expand Up @@ -113,7 +114,7 @@ const App = () => {
);

const sdk = useMemo(
() => getSdk(!!mockEnv, context, lifecycle),
() => getSdk(!!mockEnv, context, withDevice, lifecycle),
// eslint-disable-next-line react-hooks/exhaustive-deps
[
mockEnv,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
} from "@ledgerhq/trustchain/types";
import { Actionable } from "./Actionable";
import { useTrustchainSDK } from "../context";
import { runWithDevice } from "../device";

export function AppGetOrCreateTrustchain({
deviceId,
Expand All @@ -26,11 +25,9 @@ export function AppGetOrCreateTrustchain({

const action = useCallback(
(memberCredentials: MemberCredentials) =>
runWithDevice(deviceId, transport =>
sdk
.getOrCreateTrustchain(transport, memberCredentials, callbacks)
.then(result => result.trustchain),
),
sdk
.getOrCreateTrustchain(deviceId, memberCredentials, callbacks)
.then(result => result.trustchain),
[deviceId, sdk, callbacks],
);

Expand Down
15 changes: 7 additions & 8 deletions apps/web-tools/trustchain/components/AppMemberRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
import { Actionable } from "./Actionable";
import { DisplayName } from "./IdentityManager";
import { useTrustchainSDK } from "../context";
import { runWithDevice } from "../device";

export function AppMemberRow({
deviceId,
Expand All @@ -32,13 +31,13 @@ export function AppMemberRow({

const action = useCallback(
(trustchain: Trustchain, memberCredentials: MemberCredentials) =>
runWithDevice(deviceId, transport =>
sdk.removeMember(transport, trustchain, memberCredentials, member, callbacks),
).then(async trustchain => {
setTrustchain(trustchain);
await sdk.getMembers(trustchain, memberCredentials).then(setMembers);
return member;
}),
sdk
.removeMember(deviceId, trustchain, memberCredentials, member, callbacks)
.then(async trustchain => {
setTrustchain(trustchain);
await sdk.getMembers(trustchain, memberCredentials).then(setMembers);
return member;
}),
[deviceId, sdk, member, setTrustchain, setMembers, callbacks],
);

Expand Down
3 changes: 2 additions & 1 deletion apps/web-tools/trustchain/context.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useContext } from "react";
import { withDevice } from "@ledgerhq/live-common/hw/deviceAccess";
import { TrustchainSDK } from "@ledgerhq/trustchain/types";
import { getSdk } from "@ledgerhq/trustchain/index";
import { getEnv } from "@ledgerhq/live-env";
Expand All @@ -10,7 +11,7 @@ export const defaultContext = {
};

export const TrustchainSDKContext = React.createContext<TrustchainSDK>(
getSdk(false, defaultContext),
getSdk(false, defaultContext, withDevice),
);

export const useTrustchainSDK = () => useContext(TrustchainSDKContext);
10 changes: 0 additions & 10 deletions apps/web-tools/trustchain/device.ts

This file was deleted.

Loading

0 comments on commit bc044e4

Please sign in to comment.