-
Notifications
You must be signed in to change notification settings - Fork 318
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
Conversation
The latest updates on your projects. Learn more about Vercel for Git βοΈ
4 Skipped Deployments
|
There was a problem hiding this 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.
Got it! Thank you for having a look. I'll make these changes and apply the rework to LLM and web tools too. |
84fb2cf
to
3e63da4
Compare
3e63da4
to
e5596ce
Compare
549d3be
to
5de26cb
Compare
4307933
to
07fe865
Compare
HWDeviceProvider
HWDeviceProvider
const newTrustchain = await sdkRef.current.removeMember( | ||
deviceRef.current?.deviceId, | ||
trustchainRef.current as Trustchain, | ||
memberCredentialsRef.current as MemberCredentials, |
There was a problem hiding this comment.
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 π€
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
* 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
β Checklist
npx changeset
was attached.π 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 thewithDevice
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