Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

SIP-2 (Snap Keyrings) Discussion #10

ritave started this conversation in SIPs
Jun 22, 2022 · 9 comments · 9 replies
Discussion options

This thread is meant to be general place for open discussions concerning the state of SIP-2 (Snap Keyrings).

The SIP is meant to explore minimum viable feature set needed for supporting accounts in Snaps and is expected to be extended in the future (such as declaring a list of assets for accounts and chains).

You must be logged in to vote

Replies: 9 comments 9 replies

Comment options

One unsolved issue is extending already existing chains. For example, adding support for a hardware wallet account for Ethereum Mainnet chain.

In current specification, the wallet can't join multiple Snaps to provide support for singular namespace.

I currently have no idea on how to solve this problem. Any help appreciated.

You must be logged in to vote
2 replies
Comment options

Some open questions for any algorithm that could allow multiple Snaps supporting singular chain / namespace:

  1. How to route requests to Snaps implementing same methods/events on same chain. Especially for methods that don't take AccountId as a parameter.
  2. What if Snaps support same chain, but implement different methods/events.
  3. The DApp requests connections with such data:
    requiredNamespaces: {
     eip155: {
     methods: [
     "eth_sendTransaction",
     "eth_signTransaction",
     "eth_sign",
     "personal_sign",
     "eth_signTypedData",
     ],
     chains: ["eip155:1", "eip:155:2"],
     events: ["chainChanged", "accountsChanged"],
     },
    },
    And gets a list of accounts back. What if one snaps has accounts for both eip155:1 and eip155:2 while a second snap only has accounts for eip155:1? Should accounts only be returned from the first snap?
Comment options

glad to know [from twitter](https://twitter.com/cejay35752153/status/1553209565906235392)) this discussion, metamask snaps are a great idea, but only if snaps become first class citizens of metamask.

  1. All snaps should be exposed in the form of public protocol names (the function provided by the current metamask should also be a snap),
    Instead of requiring the npm name like the current metamask flask, the DAPP only needs to specify the protocol name it supports, and it is up to metamask & the user to choose which snap to use to connect to the current DAPP.
  2. The chainid in the snaps support protocol may be a specific string, or it may be * (for example, it means that all chainids are supported)
  3. You should be able to refer to the relationship between the operating system and application software (maybe for security reasons, you should refer to iOS private data isolation and public data sharing), metamask should only maintain a "basic private key seed", and each snap needs a private key when initializing. metamask will provide a string based on the "basic private key seed" + "snap package name" hash, so that it is similar to the isolation of each application in iOS, and each snap must implement the specified interface and UI (similar to a separate application ), after connecting to DAPP to determine which snap to use, metamask should switch to the UI interface of this snap
Comment options

Hi @ritave, so my reading of this is that snaps should implement their own version of the SnapKeyring interface and export it; the MM snap RPC controller code will forward certain DApp requests to the keyring handleRequest implementation when appropriate (permission has been granted). Is that correct?

For my MPC/TSS use case I think this would work assuming the origin in handleRequest() is the DApp origin and not the snapId.

Is it worth me updating my work on snap-keyring to provide a default implementation of the interface that corresponds to this SSIP?

I am a bit concerned about the signature of the importAccount function for creating a generic implementation:

importAccount?(chainId: ChainId, data: unknown): Promise<AccountId>;

Could we pass the AccountId here instead:

importAccount?(accountId: AccountId, data: unknown): Promise<void>;

Then the implementation doesn't need to know how to derive an AccountId from the data.

You must be logged in to vote
1 reply
Comment options

@tmpfs
Your assumptions in the first paragraph is correct - I've added an explanation in #15.

It's still a long way before we have the implementation working, and so the design might change midway - I don't think it's worth it to update your default implementation on snap-keyring just yet.

The wallet itself can't know how to derive Account Ids because it doesn't know how the underlying blockchain works (Is it EVM? Is it Bitcoin? Is it RSA based?), and so the user would have to know how to manually derive their fully CAIP-10 qualified AccountId to be able to provide it during importing process. I'd say that would be a bad user experience, especially for less technically inclined people. Do you have something in mind on how to provide AccountId to importAccount() without it being inputed manually by the user nor derived by the wallet?

Comment options

Ah yeh, I was imagining overloading the behavior of importAccount for my use case but I think we actually need another function.

When running DKG I end up with a JSON object that represents a share of the private key and I need to add it to the user's account, I thought I could use importAccount but that should only be used by the end user to import a previously exported key.

So I think we need to also expose an optional addAccount function in the interface:

addAccount?(accountId: AccountId, data: Json): Promise<void>;

What do you think?

Also, I can imagine a future where the UI needs to differentiate between single party and multi-party keys, for example:

0xffe...ead (Share #2 of a 3/5)

The type to describe the properties we would want to associate with the account (when an address is generated from a multi-party key share) would look like:

type MultiPartyShare = {
 partyNumber: number;
 threshold: number;
 parties: number;
}

Any ideas on the best way to incorporate this into the design?

You must be logged in to vote
2 replies
Comment options

@tmpfs
I think importing an account from an external source is a valid use-case for importAccount(). Why couldn't the generated JSON include the AccountId inside the data? I need more context why the AccountId would have to be separate.

// Data inputted by the user, probably in a simple textarea in UI in the prototype
const DATA = {
 publicKey: '0xffe...ead',
 parties: 5,
 partyNumber: 2,
 threshold: 3,
 /*...*/
};
console.log(await keyring.importAccount('dkg:1', DATA));
// saves the state using snap_manageState
// prints 'dkg:1:ffe...ead'

Metadata such as MultiPartyShare associated with an account can be easily stored using snap_manageState permission in whatever format the Snap desires. Printing such metadata to the user is a more complicated problem - it would have to be general enough to print information about blockchains we know nothing about (smart contract multisig will want something different from DKG). In the current UI, the user can give an account a user readable name. We are considering allowing outputting markdown by the Snap in the UI, it then could be shown in the account detail modal.

Adding markdown to Snaps in the first place, is a big and separate issue from Account Keyrings, and so should come in at a later date after we have a basic prototypes of both working.

Comment options

I think the addAccount function is essential to facilitate my use case, otherwise I don't see how the current interface would allow me to save the key share created during DKG into the keyring. Unless I am missing something of course! @ritave, will you accept a PR that adds it to the interface?

Comment options

@ritave, I didn't mean to imply we should remove importAccount() - that is certainly still a valid use case. I meant that we should allow for keyrings to specify the additional addAccount() function.

Putting the AccountId inside the data means the keyring needs to have knowledge of the shape of the data and makes it harder to have a generic keyring implementation. I think a generic implementation that solves 90% of use cases is better than a proliferation of different keyring implementations; but maybe a generic implementation is not feasible and I am thinking about this wrong.

If we put the multi party information inside the data then the wallet needs to know about the shape of the private key material which I don't think is a good idea as it is brittle. Also it's not sensitive information so if we could let the wallet know about it without looking at the private data then I think that is worthwhile; however, if there is no desire to display this in the UI to differentiate between single party and multi-party keys then it is not worth implementing. Personally, I think it is useful to show this as the signing flow will be significantly different. Of course, like you say we can store it in snap_manageState (which is what I do at the moment) but then the wallet does not know about it.

You must be logged in to vote
3 replies
Comment options

@tmpfs
Could you please respond in one thread only instead of making new topic every time? There are multiple topics open with different questions [1], [2], [3] and focusing on one question - one topic will make it easier to follow each.

I think I understand better now, what you would like to do is have the ability for the user to add some additional information during account creation? As in the below flow?
Screenshot 2022年07月11日 at 21 06 06 Screenshot 2022年07月11日 at 21 06 12

Comment options

Could you please respond in one thread only instead of making new topic every time?

Sure thing @ritave.

Yes, I imagine that the UI would show a graphic like "ledger" in the screenshot to indicate the key is a share and not the entire key; maybe just "MPC" would be sufficient to distinguish it from a standard single party key. But also showing the threshold and total number of parties would be useful if possible.

Comment options

@tmpfs
We've done some preliminary exploration of a UI inspired by Block Kit that would allow generic information and input on that page described by the snap. But that still has a long way to go - I don't think it's going to make it into first Keyring's prototype, importAccount() will have to be good enough for now.

Hopefully we will deprecate importAccount() later on with custom UIs that would allow you describe that this account is multi signature based.

Comment options

MetaMask Snaps had an internal meeting - here are the design notes that came out of that discussion:

  • I've made the lifecycle requirements more explicit in Added lifecycle explanation to SIP-2 #21
  • We would like to avoid the need for snaps being stateful due to keyring.on() and keyring.off(). There was a suggestion to make the API broadcast based, in which the snap would send a JSON-RPC notification with new data for specific event which wallet would route that to all DApps. That architecture is not possible due to subscriptions having the ability to have arguments passed down, for example eth_subscribe with specific topic listed, and so the snap needs to keep track of specific arguments for each. Events API might be revisited in the future.
You must be logged in to vote
0 replies
Comment options

To support SIP-2, MetaMask will need quite a bit of refactoring to support multiple networks at the same time. We're currently exploring what kind of architecture will be needed - I'll update this discussion when we start implementation.

You must be logged in to vote
0 replies
Comment options

Finally read through this, sorry for the delay! This is looking very good! I’ve written some feedback here, and I’m open to composing it into a PR if you’d like.

Copying that feedback here:

  • I might add a bit from my above section talking about examples of new account types that are made possible by the proposal. Just because I like a motivation section that helps ignite the imagination. (New hardware wallets, contract accounts, DAOs)
  • The wallet implementation SHOULD support WalletConnect v2.0 protocol.

    • Maybe this could be [[[[EIP]] 1193: Ethereum Provider JavaScript API]]? WC is more opinionated about transport, so I think it would be best if we described the provided interface in the most transport-agnostic terms.
  • is the chainId variable able to go high enough for "permissionless extensibility"? I'd like any trivial object, like a lightbulb, to be able to have a unique chain. (Maybe that's just not for evm chains?)
  • If there are multiple such snaps, the choice, which one of those to use, is undefined and implementation dependent.

    • I appreciate this being undefined for now. For short term I would recommend not attempting deduplication, since there are various pitfalls to that implementation. A simple shortstop would be that during the account selection step of dapp connection, allow the user to select from a list of accounts where each one is labeled by its key provider.
      • Being explicit about which account provider can help a user "keep the books separate" between key managers that might be intended for different uses: One hardware wallet for business, one for pleasure.
      • This becomes significantly more important in an environment like [[Delegatable Eth]], where a user might have multiple delegations that allow them to take actions "on behalf of" one central account, but each of those delegation paths could represent a different reason/organization to have that privilege.
    • Another approach is to just allow the user to select an account, and at signature time let them select the account provider
      • could allow nice experiences like letting a user decide which hardware wallet is most accessible to them at that time.
  • This SIP gets into talking about critical uses of a namespace before defining a namespace. I might recommend defining namespace earlier.
    • Some passages that are hard to read without reading ahead and then hopping back
      • Wallet splits connection arguments into namespaces.

      • Support for a namespace MUST NOT be split between multiple snaps. For
        example, one snap can't support one chain while other snap supports
        second chain.

  • Support for a namespace MUST NOT be split between multiple snaps. For example, one snap can't support one chain while other snap supports second chain.

    • I'm unclear what this means.
      • We definitely do want to be able to have snaps that handle different namespaces, which I'm pretty sure we agree with, but seems to contradict this.
      • We also want multiple snaps to be able to provide accounts for the same protocol (we might have several eth account snaps, for example, to represent hardware wallets, contract accounts, and DAO memberships)
    • Revisiting after reading the permissions, I think I have an interpretation.
      • Once a snap-provided account is selected by a user to be exposed to a dapp, that dapp's requests for that account will all be passed through to that one snap.
        • Some reasons I think this might be unnecessary to state
          • Some account managers are not able to expose all methods (contract accounts can't perform signatures, and some hardware wallets don't support some signature methods).
          • A snap could claim it supports many methods, but actually forward some of them to another snap, and that's not really any of our business.
  • I see that snap permissions are specified in the manifest. I'm pretty sure this means you can also do wallet_requestPermissions. I might suggest either:
    • Just show the permission request object, and link out to a guide that covers the two methods.
    • Show both methods to request the permissions.
  • I'm ecstatic that event handlers are part of this specification.
    • I'd also be excited for snaps to have a general ability to register on and off methods, despite not exposing other account management methods.
  • It's great that we have these keyrings declaring what methods they support. It would also be great if we could expose this to sites in some form. I had mentioned this in [[[[EIP]] 2255: Wallet Permissions System]], and doesn't need to be part of this proposal, but the possible value of dapp-side [[feature detection]] is even stronger with this proposal.
  • Recommendations for safety
    • Since we are using a [[JSON-RPC API]] to expose these sensitive methods, there is risk of account snap developers writing a [[Confused Deputy]] vulnerability.
      • These issues occur when there are >2 parties coordinating privileges.
      • Example
        • Snap Alice might have permission to interact with several of a user's other accounts. For example, so that a contract account snap can trigger transactions from key managers.
        • A user might only want to grant Dapp Bob with access to Account Carol.
        • Snap Alice is now responsible for ensuring that any of its methods provided to Dapp Bob are restricted to interactions with Account Carol.
        • If Snap Alice exposes any methods with an accountId parameter, this offers Dapp Bob an opportunity to provide a chosen string for an account that it has not been permitted access to.
        • This means that a Snap account that enlists many other accounts to imbue it with its total authority must implement its own internal permissions scheme to ensure that it never grants unintended access to some of its capabilities.
        • Long term, alternatives to [[JSON-RPC API]] such as [[CapTP]] can allow snaps much more specific "object passing" type delegation that makes these types of vulnerabilities trivially impossible.
  • Might as well include a link to the keyring protocol when mentioning it.

Anyways, very excited for this. I hope Elliott will be finding a short path to the multi-simultaneous networks, and hopefully we can get on with implementing this soon. Might be worth helping out with him on that, I swear there's a simple path to it (maybe something like this).

You must be logged in to vote
1 reply
Comment options

Hey @danfinlay, thank you for the extensive write-up. I've replied below to some feedback and questions, and some was put into #25

  • EIP 1193: Ethereum Provider JavaScript API

    • I would love to use EIP-1193, but it lacks important things - chain support discovery, and chain selection.
      • The proposed API based on WallectConnect v2 allows the dApp to request specific chains, and the wallet to respond with different set of supported chains, for example only supporting Testnet instead of both Mainnet and Testnet.
      • The second one is ambiguity of which chain to use for the request. If I'm both connected to Mainnet and Polygon at the same time, eth_blockNumber request would result in undefined behaviour.
    • The window.blokchain is a minimal API based on WCv2 that's stripped of bridging server and completely transport protocol agnostic.
    • I'd love to hear what Status has to say with their Waku-based API, it might be better designed compared to WCv2
  • is the chainId variable able to go high enough for "permissionless extensibility"

    • 6.116541e+55 combinations of chainId, that's more than whole ipv6 address space.
  • I see that snap permissions are specified in the manifest. I'm pretty sure this means you can also do wallet_requestPermissions.

    • We want the snap to describe what it supports beforehand. This allows choosing which snaps to start when a dapp requests connection. Giving snap the ability to dynamically modify which namespaces and chains the snap can support opens a big black box of weird behaviour and gotchas.
  • It's great that we have these keyrings declaring what methods they support. It would also be great if we could expose this to sites in some form. I had mentioned this in [[[[EIP]] 2255: Wallet Permissions System]], and doesn't need to be part of this proposal, but the possible value of dapp-side [[feature detection]] is even stronger with this proposal.

    • They can expose those functions by specifying methods they support in the snap:keyring description. For example they could support addAccount RPC call.
    • Feature detection could be done by returning a Session object with smaller amount of supported methods than those requested in blockchain.connect().
  • Since we are using a [[JSON-RPC API]] to expose these sensitive methods, there is risk of account snap developers writing a [[Confused Deputy]] vulnerability.

    • We haven't yet explored communication between multiple snaps, neither in technical details nor ux.
Comment options

Great, you've addressed most of my questions, thank you.

We haven't yet explored communication between multiple snaps, neither in technical details nor ux.

Sorry I keep thinking of the original beta as a sort of reference specification. In it, each snap had a normal provider, and so was able to request communication channels with other snaps the exact same way that dapps request communication to snaps.

I think inter-process communication is a fundamental enough concept that it's worth defining an interface for early. Maybe we should just write that SIP soon.

The confused deputy issue I noted will probably apply to any JSON-RPC based inter-process communication standard (unless it uses some kind of local-identifier table the way Fuchsia does), and probably belongs in documentation somewhere until we support a form of object passing as a primary way of exposing interfaces, but that doesn't need to block this, as we're already inheriting JSON-RPC, and we're using it for v1 of our API partly for the sake of ecosystem consistency.

You must be logged in to vote
0 replies
Comment options

We're currently in a feature freeze until Snaps can be released in the main extension, and so, Keyrings are put on hold. We want to continue building them as soon as we can, but that'll come earliest in third quarter of this year or later.

There's a special build for DevCon Bogota that had a partial implementation - the PR in metamask-extension repo is #16107.

No new API changes came out of that implementation, but they UI flow is still being researched and worked on.

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
SIPs
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /