Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

πŸ—οΈ [Support] Request device access within HWDeviceProvider #7440

Merged
merged 16 commits into from
Aug 8, 2024

Conversation

thesan
Copy link
Contributor

@thesan thesan commented Jul 26, 2024

βœ… Checklist

  • npx changeset was attached.
  • Covered by automatic tests.
  • Impact of the changes:
    • ...

πŸ“ Description

Factor all device interactions out of the Trustchain SDK into a new HWDeviceProvider. This is done in order to help unit testing of the Trustchain SDK later on and to better separate concerns.

Initialise the HWDeviceProvider with the withDevice function from @ledgerhq/live-common/hw/deviceAccess. The device id is then passed to the method of the Trustchain SDK which interact with the device.

❓ Context


🧐 Checklist for the PR Reviewers

  • The code aligns with the requirements described in the linked JIRA or GitHub issue.
  • The PR description clearly documents the changes made and explains any technical trade-offs or design decisions.
  • There are no undocumented trade-offs, technical debt, or maintainability issues.
  • The PR has been tested thoroughly, and any potential edge cases have been considered and handled.
  • Any new dependencies have been justified and documented.
  • Performance considerations have been taken into account. (changes have been profiled or benchmarked if necessary)

Copy link

vercel bot commented Jul 26, 2024

The latest updates on your projects. Learn more about Vercel for Git β†—οΈŽ

Name Status Preview Comments Updated (UTC)
web-tools βœ… Ready (Inspect) Visit Preview πŸ’¬ Add feedback Aug 6, 2024 4:24pm
4 Skipped Deployments
Name Status Preview Comments Updated (UTC)
ledger-live-docs ⬜️ Ignored (Inspect) Visit Preview Aug 6, 2024 4:24pm
ledger-live-github-bot ⬜️ Ignored (Inspect) Visit Preview Aug 6, 2024 4:24pm
native-ui-storybook ⬜️ Ignored (Inspect) Visit Preview Aug 6, 2024 4:24pm
react-ui-storybook ⬜️ Ignored (Inspect) Visit Preview Aug 6, 2024 4:24pm

@live-github-bot live-github-bot bot added the desktop Has changes in LLD label Jul 26, 2024
Copy link
Contributor

@gre gre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a great iteration but i would like to suggest a simplification on the SDK interface.

if i understand correctly, we're abstracting out the need to pass transport in methods of the SDK, by instanciating the SDK with a way to create the transport instead (giving an implementation of withTransport)

on top of this, in your rework, we also need to provide a deviceId observable, this is the part where i'm a bit more lost both technically and conceptually: in term of lifecycle, some methods need the device, some don't, and they only need it in context of that method, so i think it would be preferable to keep a concept of deviceId contextual to sdk methods, as being passed as explicit parameter to these methods to express the fact that "hey i will need a device id because i will need the device". This is a pattern actually quite similar to what was done on the Coin Framework AccountBridge : we pass a deviceId (and no longer a transport), so we can still keep what you did on the withDevice implementation abstraction, but i would prefer to simplify out the need to pass in a global "device id observable" but instead just pass in the device.id (instead of the transport) where it's necessary (which also makes the diff a bit less impacting with current's sdk signature)

so each time we had "transport", it becomes "deviceId", for instance, I mean this change:

- await runWithDevice(device.id, async transport => {
+ await (
  sdk.getOrCreateTrustchain(
-   transport,
+   device.id,
    memberCreds,
    callbacks
  )
)

I think it may be especially important for LLM because the observable pattern will not work on LLM: we manage multiple devices on LLM unlike LLD where we always have a singleton device. so this deviceId observable lifecycle don't really exists. it also doesn't really exist on the web tools.

@thesan
Copy link
Contributor Author

thesan commented Jul 29, 2024

so each time we had "transport", it becomes "deviceId"

Got it! Thank you for having a look. I'll make these changes and apply the rework to LLM and web tools too.

@thesan thesan force-pushed the support/trustchain-sdk-refactor-transport branch from 84fb2cf to 3e63da4 Compare July 30, 2024 14:53
@live-github-bot live-github-bot bot added mobile Has changes in LLM common Has changes in live-common ui Has changes in the design system library ledgerjs Has changes in the ledgerjs open source libs tools Has changes in tools automation CI/CD stuff translations Translation files have been touched screenshots Screenshots have been updated cli labels Jul 30, 2024
@thesan thesan changed the base branch from support/trustchain-sdk-refactor to develop July 30, 2024 14:53
@thesan thesan force-pushed the support/trustchain-sdk-refactor-transport branch from 3e63da4 to e5596ce Compare July 30, 2024 15:04
@live-github-bot live-github-bot bot removed common Has changes in live-common ui Has changes in the design system library ledgerjs Has changes in the ledgerjs open source libs tools Has changes in tools automation CI/CD stuff translations Translation files have been touched screenshots Screenshots have been updated cli labels Jul 30, 2024
@thesan thesan force-pushed the support/trustchain-sdk-refactor-transport branch from 549d3be to 5de26cb Compare July 30, 2024 16:15
@thesan thesan changed the base branch from develop to support/trustchain-sdk-refactor July 30, 2024 16:16
@thesan thesan marked this pull request as ready for review August 2, 2024 17:58
@thesan thesan requested a review from a team as a code owner August 2, 2024 17:58
@thesan thesan changed the title πŸ—οΈ [Support] Fully handle hw device interaction in HWDeviceProvider πŸ—οΈ [Support] Request device access within HWDeviceProvider Aug 6, 2024
const newTrustchain = await sdkRef.current.removeMember(
deviceRef.current?.deviceId,
trustchainRef.current as Trustchain,
memberCredentialsRef.current as MemberCredentials,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we shouldn't have to cast these, instead we may want to have the same trick you did with the if(!deviceRef.current) throw
you can have a catch all if (!a || !b || !c) throw new Error("unexpected missing args") for instance

also you can do device.current.deviceId as you just have proven to TS that it's settled. if TS can't manage it, then it"s preferable to do:

const device = deviceRef.current
if (!device) ...

removeMember(device.deviceId, 

Side question but does it mean removeMember accepts a falsy deviceId? it probably shouldn't πŸ€”

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I probably just forgot the ? from some intermediary code.

Got it for the other castings I agree it's not the best.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW are these references just to avoid the missing dependencies linting error in the use callbacks/effects ?

Copy link
Contributor

@gre gre Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using React ref usually is done when we want to prevent an effect to be retriggered over and over, and when we instead just need to access the "current" state once and not to "trigger each time it changes".
i think @mcayuelas-ledger can comment more on this as he did this part, but i suspect we had the effect double triggering in the past, ending up on race condition with the device (like doing two removeMember in a row)

});
const trustchainResult = await sdk.getOrCreateTrustchain(
device?.deviceId ?? "",
memberCredentialsRef.current as MemberCredentials,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same casting we could get rid of (but what about the ?.deviceId one ?)

device.deviceId,
trustchain,
memberCredentials,
member,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

( πŸ‘ on mobile we had cleaner code I hope we can also reach on LLD, see here, no casting )

@@ -13,7 +13,7 @@ export async function scenario(transport: Transport, { sdkForName }: ScenarioOpt
const member2 = { name: name2, id: member2creds.pubkey, permissions: 0xffffffff };

// auth with the device and init the first trustchain
const { trustchain } = await sdk1.getOrCreateTrustchain(transport, member1creds);
const { trustchain } = await sdk1.getOrCreateTrustchain("foo", member1creds);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we just use "" instead of foo? or maybe we can also make a deviceId constant somewhere to explicit what this is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up passing the device id to the test scenario instead of the transport

Comment on lines +12 to +20
* TODO withDevice should be imported statically from @ledgerhq/live-common/hw/deviceAccess
*
* but ATM making @ledgerhq/live-common a dependency of @ledgerhq/trustchain causes:
* > Turbo error: Invalid package dependency graph: cyclic dependency detected:
* > @ledgerhq/trustchain,@ledgerhq/live-wallet,@ledgerhq/live-common
*
* Maybe hw/deviceAccess.ts and hw/index.ts could be moved to @ledgerhq/devices
* This would break the cyclic dependency as @ledgerhq/live-common would depend on @ledgerhq/devices
* but not the other way around.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gre @KVNLS what do you think of this suggestion ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to keep it as is, otherwise it involves much work to be done. ( + I prefer that you inject the withDevice as a parameter rather than importing it in the trustchain sdk)

Copy link
Contributor

@gre gre Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the idea to move hw/* and notably the deviceAccess / index files of it to a library is a good idea,
this goes into the general effort of moving things out of live-common,
but i think it's the scope of device team tho.
also the impact is quite important in that many parts depends on this. but this could be discussed with that team.

and yeah, no library should depends on live-common, it's live-common that can depends on library, and on this of this the general goal is to move things out of live-common, not to add more depends to it.

so in the meantime we should indeed inject function we needs. similarly i sometimes had to inject getAccountBridge as a standalone function on the wallet library, instead of depending on live-common which is the one currently implementing it

const newTrustchain = await sdkRef.current.removeMember(
deviceRef.current?.deviceId,
trustchainRef.current as Trustchain,
memberCredentialsRef.current as MemberCredentials,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I probably just forgot the ? from some intermediary code.

Got it for the other castings I agree it's not the best.

const newTrustchain = await sdkRef.current.removeMember(
deviceRef.current?.deviceId,
trustchainRef.current as Trustchain,
memberCredentialsRef.current as MemberCredentials,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW are these references just to avoid the missing dependencies linting error in the use callbacks/effects ?

@@ -13,7 +13,7 @@ export async function scenario(transport: Transport, { sdkForName }: ScenarioOpt
const member2 = { name: name2, id: member2creds.pubkey, permissions: 0xffffffff };

// auth with the device and init the first trustchain
const { trustchain } = await sdk1.getOrCreateTrustchain(transport, member1creds);
const { trustchain } = await sdk1.getOrCreateTrustchain("foo", member1creds);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up passing the device id to the test scenario instead of the transport

@thesan thesan merged commit bc044e4 into develop Aug 8, 2024
55 of 56 checks passed
@thesan thesan deleted the support/trustchain-sdk-refactor-transport branch August 8, 2024 08:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
desktop Has changes in LLD mobile Has changes in LLM
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants