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

Aligned with New CIP-30 Extension Scheme #15

Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 38 additions & 50 deletions CIP-0062/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,15 @@ This document describes an interface between webpage/web-based stacks and Cardan
These definitions extend the [CIP-30 (Cardano dApp-Wallet Web Bridge)](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0030) to provide specific support for Catalyst vote delegation and vote signing.

## Motivation
<!-- A clear explanation that introduces the reason for a proposal, its use cases and stakeholders. If the CIP changes an established design then it must outline design issues that motivate a rework. -->

The goal for this CIP is to extend the [CIP-30](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0030) dApp-Wallet web bridge to enable the creation of Project Catalyst focussed dApps. Where wallets can connect, share the user's key information, register to vote and or delegate voting rights.
The goal for this CIP is to extend the [CIP-30](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0030) dApp-Wallet web bridge to enable the creation of Project Catalyst focussed dApps. Where wallets can connect, share the user's key information, register to vote and or delegate voting rights.

This is achieved by facilitating the construction of transactions containing metadata aligned with [CIP-36 (Catalyst/Voltaire Registration Transaction Metadata Format - Updated)](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0036). This enables new functionality for Project Catalyst; including vote delegation to either private or public representatives (dReps), splitting or combining of votes, the use of different voting keys or delegations for different purposes.
This is achieved by facilitating the construction of transactions containing metadata aligned with [CIP-36 (Catalyst/Voltaire Registration Transaction Metadata Format - Updated)](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0036). This enables new functionality for Project Catalyst; including vote delegation to either private or public representatives (dReps), splitting or combining of votes, the use of different voting keys or delegations for different purposes.

## Specification

This specification extends CIP-30's functionality as described in [CIP-30's Initial API]([api.signVotes](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0030#initial-api)).

### Data Types

#### PublicKey
Expand All @@ -57,7 +58,7 @@ type WeightedKey = {
* `weight` - Used to calculate the actual voting power using the rules described
in [CIP-36](https://cips.cardano.org/cips/cip36/).

#### Voting Purpose
#### VotingPurpose

```ts
type enum VotingPurpose = {
Expand Down Expand Up @@ -246,59 +247,36 @@ All TxSignErrors defined in [CIP-30](https://cips.cardano.org/cips/cip30/#txsign
* UserDeclined - Raised when the user declined to sign the entire transaction, in the case of a vote submission, this would be returned if the user declined to sign ALL of the votes.
* VoteRejected - On a vote transaction, where there may be multiple votes. If the user accepted some votes, but rejected others, then this error is raised, AND `rejectedVotes` is present in the Error instance.

### Catalyst Extension to CIP-30

#### cardano.{walletName}.catalyst.apiVersion: String

The version number of the Catalyst Extension API that the wallet supports.

#### cardano.{walletName}.catalyst.enable(purpose: VotingPurpose[]): Promise\<API>

Errors: [`APIError`](#extended-apierror)

The `cardano.{walletName}.catalyst.enable()` method is used to enable the
Catalyst API. It should request permission from the wallet to enable the API for the requested purposes.

If permission is granted, the rest of the API will be available. The wallet
should maintain a specific whitelist of allowed clients and voting purposes for
this API. This whitelist can be used to avoid asking for permission every time.

This api, being an extension of
[CIP-30](https://cips.cardano.org/cips/cip30/),
expects that `cardano.{walletName}.enable()` to be enabled and added to CIP-30
whitelist implicitly.

When both this API and [CIP-30](https://cips.cardano.org/cips/cip30/) being
enabled, is up to the wallet to decide the number of prompts requesting
permissions to be displayed to the user.

* `purpose` - this is a list of purposes that the dApp is advising that it will be using on the API. The wallet must respond with an error if the purpose is not supported by the Wallet.

Note: Currently only voting purpose 0 (Catalyst) is defined. The wallet should reject any other purpose requested.

##### Returns

Upon successful connection via
[`cardano.{walletName}.catalyst.enable()`](#cardanowalletnamecatalystenablepurpose-votingpurpose-promiseapi),
a javascript object we will refer to as `API` (type) / `api` (instance) is
returned to the dApp with the following methods.
### Catalyst API Extension to CIP-30

### Catalyst API
The following endpoints extend [CIP-30's Full API](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0030#full-api).

Except `signVotes`, no other method should require any user
Except `signVotes` and `submitDelegation`, no other method should require any user
interaction as the user has already consented to the dApp reading information
about the wallet's state when they agreed to
[`cardano.{walletName}.catalyst.enable()`](#cardanowalletnamecatalystenablepurpose-votingpurpose-promiseapi).
[`cardano.{walletName}.enable()`](#cardanowalletnamecatalystenablepurpose-votingpurpose-promiseapi).
The remaining methods
[`api.signVotes()`](#apisignvotesvotes-vote-promisebytes) and
[`api.signData()`](#apisubmitdelegationdelegation-delegation-promisesigneddelegationmetadata)
[`api.submitDelegation()`](#apisubmitdelegationdelegation-delegation-promisesigneddelegationmetadata)
must request the user's consent in an informative way for each and every API
call in order to maintain security.

The API chosen here is for the minimum API necessary for dApp <-> Wallet
interactions without convenience functions that don't strictly need the wallet's
state to work.

#### api.getVotingPurposes(): Promise\<VotingPurpose[]>

Errors: [`APIError`](#extended-apierror)

This is a chance for dApps to ask wallets what `VotingPurpose`s they support.

This information should be tracked by the dApp and used in subsequent api calls.

##### Returns

An array of supported `VotingPurpose`s.

#### api.signVotes(votes: Vote[], settings: string): Promise\<Bytes>[]

Errors: [`APIError`](#extended-apierror), [`TxSignError`](#extended-txsignerror)
Expand Down Expand Up @@ -337,7 +315,6 @@ In all cases, where an error occurs, no signed votes are returned.

Should return the in use voting credentials of the wallet.


##### Returns

The [VotingCredentials](#votingcredentials) of the wallet, which contain it's voting key and associated staking credential used for [CIP-36](https://github.com/cardano-foundation/CIPs/blob/master/CIP-0036).
Expand All @@ -348,7 +325,7 @@ Errors: [`APIError`](#extended-apierror),
[`TxSendError`](https://cips.cardano.org/cips/cip30/#txsenderror),
[`TxSignError`](https://cips.cardano.org/cips/cip30/#txsignerror)

This endpoint should construct the cbor encoded delegation certificate according to the specs in [CIP-36 example](https://github.com/Zeegomo/CIPs/blob/472181b9c69feeedae0b5b2db8b42d0cf4eb1a11/CIP-0036/README.md#example).
This endpoint should construct the CBOR encoded delegation certificate according to the specs in [CIP-36 example](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0036#example---registration).

It should then sign the certificate with the staking key as described in the same example as above.

Expand All @@ -358,7 +335,9 @@ Upon submission of the transaction containing the delegation cert as part of met

* delegation - the voter registration [delegation](#delegation) record.

This should be a call that implicitly cbor encodes the `delegation` object and uses the already existing [CIP-30](https://cips.cardano.org/cips/cip30/) `api.submitTx` to submit the transaction. The resulting transaction hash should be returned.
This should be a call that implicitly CBOR encodes the `delegation` object and uses the already existing [CIP-30](https://cips.cardano.org/cips/cip30/) `api.submitTx` to submit the transaction. The resulting transaction hash should be returned.

If the requested `Delegation`'s `purpose` field does not match supported `Voting Purpose`s as returned by [`api.getVotingPurposes()`](#apigetpurposes-promisevotingpurpose) then an `APIError` should be thrown with code `UnsupportedVotingPurpose`.

This should trigger a request to the user of the wallet to approve the transaction.

Expand All @@ -368,19 +347,28 @@ The [Signed Delegation Metadata](#signeddelegationmetadata) of the voter registr

### Examples of Message Flows and Processes

#### Connection

1. **dApp Dialogue** - User indicates to the dApp intention of wallet connection, the user selects their chosen wallet from a list of supported ones offered by the dApp.
2. **Connection** - Using the name of the selected wallet the dApp can call [CIP-30's enable](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0030#cardanowalletnameenable-extensions-extension----promiseapi) passing in this CIPs identifier: `cardano.{walletName}.enable([{"cip": 62 }])`. This should cause the wallet to ask the user to grant permission for the dApp connecting and using the CIP-62 API Extension.
3. **Purpose** - Once connection is established the dApp will want to establish the supported wallet's `VotingPurpose`s, this is achieved via `.getVotingPurposes()`. By knowing the supported purposes the dApp can correctly choose which ones to use when calling `.signVotes()` and `.submitDelegation()`.

#### Delegation

Recall from [CIP-36](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0036) a registration is a self-delegation, allocating one's voting power to one's own voting key.

1. **Get Voting Key** - dApp calls the method `api.getVotingCredentials()` to return the connected wallet account's public `voteKey`.
Copy link

@refi93 refi93 Apr 5, 2023

Choose a reason for hiding this comment

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

not related to this PR, but I was just polishing HW wallet support when I realized that only Ledger HW wallet will be able to return the voting key. So this call would fail for a Trezor user even though they may want just to delegate their voting rights and for that usecase it's sufficient to get the staking key. One solution I can think of is to split this call into two, so each piece of information is available independently, i.e. getVotingKey and getStakingKey - what do you think @Ryun1 ?

Alternatively, we may make the votingKey in the returned value optional though that may not lead to good UX as it may no longer be clear why the votingKey wasn't returned as there would be no error code/message to indicate that

Copy link
Author

Choose a reason for hiding this comment

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

Copy link

@refi93 refi93 Apr 6, 2023

Choose a reason for hiding this comment

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

for the record, as we discussed that offline - it seems that the stakingKeyPub may not need to be exposed at all as the wallet can handle it internally. For the sake of constructing the delegation transaction, it's enough for the wallet to receive the staking address in the DelegatedCertficate structure and it can map it back to the respective public key - wallets need to do much heavier reverse mapping under the hood already anyway to support signing Cardano transactions with hardware wallets as hw wallets expect derivation paths, not raw addresses/keys.

If we suppose that a wallet has a single staking key, not even the staking address would be needed, but it seems to be more future proof and consistent with CIP-30 to include it as the CIP-30 API itself exposes the reward (i.e. staking) addresses as an array: https://github.com/cardano-foundation/CIPs/blob/master/CIP-0030/README.md?plain=1#L305

So to sum it up - unless there's some usecase I'm missing, I think the stakingKeyPub can be dropped from the getVotingCredentials() call altogether in which case it probably makes sense to rename the call back to getVotingKey() and the submitDelegation() call's parameter could reference just the stakingAddress which can be retrieved from the CIP-30 getRewardAddresses() call. This would also solve the problem raised in the comment above


2. **Construct Delegation** - The dApp constructs `Delegation` using the Wallet's public `voteKey`, `weight` of 1 and choice of `VotingPurpose`.

3. **Submit Delegation** - The dApp passes the `Delegation` object to the Wallet to build a metadata transaction and submit this to Cardano blockchain. Wallets are able employ the already existing [`api.submitTx()`](https://cips.cardano.org/cips/cip30/#apisubmittxtxcbortransactionpromisehash32), available from [CIP-30](https://cips.cardano.org/cips/cip30/).

#### Voting

TODO
Assume connection has been established, voting purposes shared, Catalyst voting is open and the user intends to submit a single vote.

1. **Select Proposal** - User will use the dApp's UI to select the target of their vote and their choice for that target.
2. **Build Data** - The dApp will construct the needed `Vote` object using the selected target `Proposal`, the users choice and the known supported `VotingPurpose` additionally the dApp chooses its chosen `settings`.
3. **Sign** - The dApp calls `api.signVotes()` whereby the wallet constructs the vote and asks the user for permission to sign.
4. **Submit** - The wallet returns the signed vote back to the dApp encoded in Bytes, the dApp can then use it's connection to Jormungandr to submit the vote.

## Rationale

Expand Down