-
Notifications
You must be signed in to change notification settings - Fork 12
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
RFC 0009: Wallet API - Storing & Requesting Objects #12
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,247 @@ | ||
# RFC-0009: Wallet-zkApp Interaction - Storing & Requesting Objects | ||
|
||
- **Intent**: Introduces an extension to the wallet provider API for storing and requesting objects like proofs and credentials. | ||
- **Submitted by**: Theodore Pender (Github: @teddyjfpender, email: [email protected], Twitter/X: @franklyteddy) | ||
- **Submitted on**: Monday, January 15, 2024 | ||
|
||
## Abstract | ||
|
||
This proposal details a mechanism for enhancing the interaction between zero-knowledge applications (zkApps) and wallets in the Mina ecosystem. The main objective is to allow wallets to use and store data objects that can be used when interacting with zkApps. Storing data objects provided to the wallet by zkApps and providing data objects to zkApps that are stored or created in the wallet is necessary given Mina’s ability to perform client-side proving as well as Mina's zkApps requiring provable arguments in both contract and provable program methods. It is important to note that the specification outlined in this document represents one variation of data sharing that is not censorship resistant, and does not ensure availability guarantees from the provider. | ||
|
||
## Motivation | ||
|
||
A robust integration between wallets and zkApps is essential for Mina's evolving ecosystem. It is necessary to create standards that capitalize on Mina's unique features such as client-side proving and recursive proving thereby potentially simplifying the experience for numerous protocol users. This is especially important when zkApps require arguments to either smart contracts or provable programs that do not exist on-chain nor are queryable, examples include interacting with applications that require proof objects from prior interactions, as well as applications that require verifiable credential proofs as arguments to smart contracts and provable programs. Extending the wallet Mina provider API for facilitating interactions with zkApps that have these or similar requirements can improve interaction between wallets and zkApps as well as allow zkApp developers to explore new idea-spaces for zkApp design and implementation. | ||
|
||
## Specification | ||
In the Web3 application ecosystem, which includes decentralized applications (dapps) and zero-knowledge applications (zkApps), it's a norm for key management software, known as "wallets," to make their API accessible through a JavaScript object in a web page—often termed "the Provider." | ||
|
||
However, inconsistencies have arisen in Provider implementations across wallets. This proposal seeks to standardize a Mina Provider API to ensure continued wallet interoperability with zkApps. This specification assumes wallets already implement the minimal, event-driven APIs equivalent to EIP-1193 for Mina; this specification introduces new RPC methods and message event types. | ||
|
||
Although Providers have historically been presented as `window.mina` in browsers, this convention isn't mandated in the specification. | ||
|
||
### API | ||
Wallets should have a provider API parallel to EIP-1193 but tailored for Mina. The goal of this API extension is to build on top of pre-existing standards. | ||
|
||
#### Key Features of the Proposed API | ||
|
||
- **Agnosticity to Application Types**: Whether an application operates on-chain, off-chain, or a combination of the two, this API remains consistent and reliable. | ||
|
||
- **Explicit User Control**: In a Mina-enabled DOM environment, the user is always in the driver's seat. The protocol is designed such that: | ||
|
||
- **User Consent for Object Access**: Before a zkApp can access any object, explicit user approval is mandatory. | ||
- **User Consent for Storing Objects**: Just as access is restricted, storage of any objects by the wallet requires the user's green light. It ensures that users are aware of what's being stored in their wallets, enhancing transparency. | ||
|
||
#### Primary Functionalities: | ||
The proposed API should empower a Mina wallet to: | ||
- **Receive Objects**: Accept and store objects, whether they're verifiable credentials, proofs, or any other data. | ||
- **Request Objects**: Initiate requests to retrieve stored objects, facilitating interactions like transaction validations or data computations. | ||
|
||
### Scenario: A Wallet Receiving Objects to Store | ||
#### `mina_setState` | ||
Providers within Mina's domain could introduce a new RPC method: `mina_setState`. When called, a user interface might emerge, allowing users to choose whether to store objects from a zkApp. This method returns a Promise: resolving with a boolean or, if storing is unavailable, rejecting with an Error. A zkApp MUST provide sufficient information in the request to allow the wallet to store and index the object appropriately. | ||
|
||
zkApps may need to store objects in a wallet, such as verifiable credentials or proofs, which are critical for subsequent interactions. To facilitate this, the wallet requires a new provider method with specific parameters: | ||
|
||
``` | ||
// Pseudocode | ||
START zkApp | ||
IF provider is defined | ||
REQUEST[1] object storage | ||
IF user approves | ||
RESOLVE[2] object stored | ||
CONTINUE zkPP | ||
IF user rejects | ||
REJECT[3] object storage | ||
STOP zkApp | ||
IF provider is undefined | ||
STOP zkApp | ||
``` | ||
[1] `REQUEST` | ||
zkApp MUST request object storage by calling the `mina_setState` RPC method on the provider exposed at `window.mina`. Calling this method MUST emit an event `message` that signifies the zkApp's initiation of the process to request the wallet to store an object and MAY trigger a user interface that allows the user to approve or reject object storage for a given zkApp. This method MUST return a Promise that is resolved with a boolean or rejected if no storage is available (e.g., the user rejected object storage). | ||
|
||
[2] `RESOLVE` | ||
The Promise returned when calling the `mina_setState` RPC method MUST be resolved with a boolean. This must emit an event `message` indicating that the request to store the object has been approved and the promise has been resolved with a boolean. | ||
|
||
[3] `REJECT` | ||
The Promise returned when calling the `mina_setState` RPC method MUST be rejected with an informative Error if no storage is available for any reason. This must emit an event `message` indicating that the request to store the object has been rejected, and the promise is rejected with an error. | ||
|
||
A wallet MUST adequately detail to the user what the zkApp is requesting the wallet to store. If approved the wallet MUST appropriately store the object for future use. The zkApp COULD provide in the params appropriate information for how this object is indexed if the zkApp will require the information later. | ||
|
||
#### Example initialization - Request objects to be stored in the wallet | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible this is not a pattern we would want to allow? It seems if the information is private, we might not want to release the information to the website at all (vs releasing proofs to the website ie the attestation standard API) Or are there cases where there is private data, that we do want websites to be able to request directly, and can't / don't want to just share a proof? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This pattern would be for public, not sensitive information. Instead of a KYC credential that might contain personally identifiable information, a wallet might store a zkOracle attestation that makes a claim about some Web 2 data, in that case the user doesn't need to attest to that data again, it can just provide the original attestation proof it knows of. In any case, a wallet should communicate to the user what a zkApp is requesting to ensure the user is completely aware of what data they are sharing. Data stored in a wallet should be encrypted at rest to prevent unauthorised access to it, and partitioned well enough to separate concerns and not accidentally provide private data. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From my understanding the data could be stored as: app_id - data potentially on a merkle tree with app_id as index, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Pfed-prog a wallet can certainly be indexed by an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How does zkApp know whether the wallet has the information he needs? And how does the wallet know what information zkApp needs? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
What would "the original attestation proof it knows of" look like? Is this caching proofs from the attestation API? Or something more general? Think I"m missing what other kinds of things this would want to expose. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@lvshaoping007 A zkApp must request that data (or proof) from the wallet along the lines of "do you have a valid credential issued to you by
That should be defined in the request message. The request message/params are agnostic enough to allow for flexible indexing of that credential, this is only a first attempt at this standard and I believe it would evolve over time as more issuers realise their solutions in production, hence eliciting their requirements in this RFC. |
||
|
||
```ts | ||
import { Mina, Proof } from "o1js"; | ||
|
||
const mina = window.mina; | ||
|
||
try { | ||
// The object to store | ||
const storableObject = “{ | ||
"@context": [ | ||
"https://www.w3.org/2018/credentials/v1", | ||
], | ||
"id": "http://example.edu/credentials/1872", | ||
"type": ["VerifiableCredential"], | ||
"issuer": "https://example.edu/issuers/565049", | ||
"issuanceDate": "2010-01-01T19:23:24Z", | ||
"credentialSubject": { | ||
"id": "did:example:ebfeb1f712ebc6f1c276e12ec21", | ||
"alumniOf": { | ||
"id": "did:example:c276e12ec21ebfeb1f712ebc6f1", | ||
"name": [{ | ||
"value": "Example University", | ||
"lang": "en" | ||
}, { | ||
"value": "Exemple d'Université", | ||
"lang": "fr" | ||
}] | ||
} | ||
}, | ||
"proof": { | ||
"publicInput": [/* public inputs */], | ||
"publicOutput": [/* public outputs */], | ||
"maxProofsVerified": 0, | ||
"proof": "KCh...Skp" | ||
} | ||
}” | ||
// Request object stored by wallet | ||
const stored = await mina.request({method: 'mina_setState', params: {object: storableObject}}); | ||
} catch (error) { | ||
// User denied storage access | ||
} | ||
``` | ||
|
||
### Scenario: Requesting Known Objects From a Wallet | ||
#### `mina_getState` | ||
Providers in Mina-aware DOM settings should also offer `mina_getState`, an RPC method. This method might present a user interface, letting users grant or deny object access for zkApps. The method returns a Promise, either resolving with an Array of objects or, if unavailable, rejecting with an Error. | ||
|
||
zkApps may require objects from a wallet (e.g. verifiable credentials, proofs), these objects may be relevant for interactions. To be able to provide the required objects, the wallet requires a new provider method with certain params. This should follow a regular pattern: | ||
|
||
``` | ||
// Pseudocode | ||
START zkApp | ||
IF provider is defined | ||
REQUEST[1] object access | ||
IF user approves | ||
RESOLVE[2] object access | ||
CONTINUE zkPP | ||
IF user rejects | ||
REJECT[3] object access | ||
STOP zkApp | ||
IF provider is undefined | ||
STOP zkApp | ||
``` | ||
|
||
[1] `REQUEST` | ||
zkApp MUST request objects by calling the `mina_getState` RPC method on the provider exposed at `window.mina`. Calling this method MUST emit an event `message` that signifies the zkApp's initiation of the process to request access to objects from the wallet and MAY trigger a user interface that allows the user to approve or reject object access for a given zkApp. This method MUST return a Promise that is resolved with an array of one or more user objects or rejected if no accounts are available (e.g., the user rejected object access). | ||
|
||
[2] `RESOLVE` | ||
The Promise returned when calling the `mina_getState` RPC method MUST be resolved with an Array of user objects. This must emit an event `message` indicating that the request to access objects has been approved, and the promise has been resolved with an array of user objects. | ||
|
||
[3] `REJECT` | ||
The Promise returned when calling the `mina_getState` RPC method MUST be rejected with an informative Error if no objects are available for any reason. This must emit an event `message` indicating that the request to access objects has been denied, and the promise is rejected with an error. | ||
|
||
#### Example initialization - request credentials by a specific issuer | ||
|
||
```ts | ||
import { Mina, Proof } from "o1js"; | ||
|
||
const mina = window.mina; | ||
|
||
try { | ||
// Request object access if needed | ||
const objects = await mina.request({method: 'mina_getState', params: { issuer: "Example-KYC-Issuer", region: "EU" } }); | ||
// objects are now exposed, format objects as desired | ||
const credentialKYC = KYCProof.fromJSON(objects[0]) | ||
// add objects to zkApp transaction as arguments to contract methods | ||
let transaction = await Mina.transaction(() => { | ||
new KYCSwapZkapp(zkappAddress).deposit(credentialKYC); | ||
}); | ||
// prove transaction | ||
await transaction.prove().catch(err=>err) | ||
// request signing of the transaction | ||
await main.request({ method: 'mina_signTransaction', params: transaction }) | ||
// send transaction | ||
mina.send('mina_sendTransaction', transaction) | ||
} catch (error) { | ||
// User denied object access | ||
} | ||
``` | ||
|
||
## User Journey | ||
|
||
In this example user journey, an `Issuer` attests that a public key (the `Subject`) has passed a KYC process and issues a wallet (the `Holder`) a credential. This credential is structured and has a `proof` field. This `proof` field is a JSON object containing a Kimchi proof, which is the result of executing a provable-program (often referred to as a circuit) using public and private inputs. The holder's wallet can then utilize the `proof` from this credential when constructing transactions, such as a KYC deposit transaction. The `proof` field can be used as an argument in contract or provable program methods. The attestation of the credential signifies that the holder has passed a KYC process. This attestation must be tightly integrated with the zkApp's contract or provable-program logic, ensuring, for instance, that the sender is indeed the subject or holder as specified in the credential. | ||
|
||
For further understanding, consider the sequence diagram below, which outlines the user journey: | ||
|
||
```mermaid | ||
sequenceDiagram | ||
Issuer->>Wallet (Holder): I attest you have passed a KYC process | ||
Note right of Wallet (Holder): Store KYC Credential Object internally | ||
|
||
Wallet (Holder)->> zkApp: Connect wallet | ||
zkApp-->>Wallet (Holder): Connection Established | ||
|
||
Wallet (Holder)->>zkApp: Click "deposit" | ||
zkApp->>Wallet (Holder): Request KYC Credential Object Proof | ||
Wallet (Holder)->>zkApp: Provide KYC Credential Object Proof | ||
|
||
zkApp->>Wallet (Holder): Request Transaction Signing | ||
Note right of Wallet (Holder): Prove and Sign Transaction internally | ||
Wallet (Holder)->>Mina Network: Send Transaction | ||
``` | ||
|
||
**Note: All interactions with the zkApp are done client-side** | ||
|
||
## Backwards Compatibility | ||
|
||
As an extension to the existing Mina provider, this API does not affect the operation of existing APIs. | ||
|
||
## Security Considerations | ||
|
||
The process of storing and exposing objects in the Mina ecosystem, as detailed in this proposal, introduces several security implications that must be carefully considered by implementors. The issues and mitigations outlined here are suggestions for implementors to consider. | ||
|
||
### Unauthorized Access to Stored Objects | ||
|
||
#### Issue | ||
If an attacker gains unauthorized access to a user's wallet, they could retrieve sensitive objects, leading to data breaches and potential misuse of these objects. | ||
|
||
#### Mitigation | ||
- **Encryption**: Ensure that all stored objects are encrypted at rest. | ||
- **Limited Lifetime**: Implementations could consider a mechanism where objects have a limited validity period, reducing the impact of potential breaches. | ||
|
||
### Exposure to Malicious Applications | ||
|
||
#### Issue | ||
Malicious applications might attempt to trick users into exposing sensitive objects, leading to data leaks. | ||
|
||
#### Mitigation | ||
- **Explicit User Consent**: Ensure that objects are never exposed without clear, informed consent from the user for each request. | ||
- **zkApp Verification**: This proposal outlines a way of wallets authenticating themselves to zkApps, a similar proposal could introduce a system where zkApps authenticate themselves to wallets. | ||
- **Educate Users**: Offer clear guidelines to users about sharing objects, ensuring they understand the risks and only share with trusted entities. | ||
|
||
### Object Revocation | ||
|
||
#### Issue | ||
If an object is a credential and it is found to be compromised, issuers of those credentials SHOULD implement a mechanism to revoke the credential's validity. | ||
|
||
#### Mitigation | ||
- **Revocation Mechanisms**: Credential issuers should implement systems that allow issuers or users to revoke credentials. | ||
|
||
### Backup & Recovery | ||
|
||
#### Issue | ||
Losing access to credentials due to device failures or other reasons can disrupt users' operations. | ||
|
||
#### Mitigation | ||
- **Secure Backups**: Allow users to backup their objects securely. Any backup process should maintain the same level of encryption and security as primary storage. | ||
- **Multi-factor Authentication**: Ensure recovery methods involve MFA to prevent unauthorized recovery attempts. | ||
|
||
### Object Scoping and Collision Avoidance | ||
|
||
#### Issue | ||
There is a potential for multiple applications to request storing of objects with similar or identical identifiers exists, which could lead to one app accidentally or maliciously overwriting an object set by another app. This can lead to unintended data loss or misrepresentation. | ||
|
||
#### Mitigation | ||
- **Namespacing**: When storing objects, wallets should ensure each object is scoped or namespaced uniquely. This ensures that even if two different applications request to store objects with identical identifiers, they will not collide or overwrite each other. A wallet should communicate well to the user whether or not storing an object risks overwriting another. | ||
- **Audit Trails**: Maintain a record of all object storage, modification, and access activities. This could help in tracing any unexpected or unauthorized changes. |
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.
What does it mean to "store objects from a zkApps"? I'm imagining that some zkApps would have public state - and others would store hashes of private state, and the private state is the thing that this is storing? Or something else?
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.
A zkApp can produce a well-formed credential that has a Kimchi proof in it, in that case, the zkApp would be the
issuer
of the credential and can allow the wallet to store that object for future use like in a smart contract method argument.If one were to assume that these objects could be fetched, or rather requested from a zkApp, this response can be stored much like how transaction history or current account information is stored in the wallet. The objects the wallet stores don't have to be private information but they can be.
A wallet storing credentials they can use in smart contract or provable-program methods is more about how to improve the interaction design between wallets and zkApps. If it is beneficial for a wallet to hold those credentials then they can use this API as a way of enabling that, there of course exist other designs that don't require the wallet to hold credentials but that design pattern is outside the scope of this RFC :)
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.
But how can users ensure these credentials are beneficial ?
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.
That is at up to the wallet user, the requested storing of those credentials must detail what the credential or object being stored is to the wallet user for them to determine the benefit. The real benefit of storing credentials in the wallet is that it gives the wallet user the ability to utilise them as and when they need/desire, without relying on central parties.