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

[NIP-47] Add versioning and migrate to NIP-44. #1531

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
133 changes: 116 additions & 17 deletions 47.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,33 +34,66 @@ There are three event kinds:

The info event should be a replaceable event that is published by the **wallet service** on the relay to indicate which commands it supports. The content should be
a plaintext string with the supported commands, space-separated, eg. `pay_invoice get_balance`. Only the `pay_invoice` command is described in this NIP, but other commands might be defined in different NIPs.
It should also contain supported versions as described in the [Versioning](#versioning) section. For example:

```jsonc
{
"kind": 13194,
"tags": [
["v", "1.3 0.0"], // List of supported versions as described in the Versioning section.
// ...
],
"content": "pay_invoice get_balance make_invoice lookup_invoice list_transactions get_info",
// ...
}
```

Both the request and response events SHOULD contain one `p` tag, containing the public key of the **wallet service** if this is a request, and the public key of the **user** if this is a response. The response event SHOULD contain an `e` tag with the id of the request event it is responding to.
Optionally, a request can have an `expiration` tag that has a unix timestamp in seconds. If the request is received after this timestamp, it should be ignored.

The content of requests and responses is encrypted with [NIP04](04.md), and is a JSON-RPCish object with a semi-fixed structure:
The content of requests and responses is encrypted with [NIP44](44.md), and is a JSON-RPCish object with a semi-fixed structure.

Request:
```jsonc
**Important note for backwards-compatibility:** v0 of the protocol used [NIP04](04.md). If a **wallet service** or client app does not include the `v` tag in the
`info` or request events, it should be assumed that the connection is using v0 of the protocol and NIP04 should be used for encryption. See the [Versioning](#versioning) section for more information.

Example request:
```js
{
"method": "pay_invoice", // method, string
"params": { // params, object
"invoice": "lnbc50n1..." // command-related data
}
"kind" 23194,
"tags": [
["v", "1.2"],
["p", "03..." ] // public key of the wallet service.
// ...
],
"content": nip44_encrypt({
"method": "pay_invoice", // method, string
"params": { // params, object
"invoice": "lnbc50n1..." // command-related data
}
}),
}
```

Response:
```jsonc
Example response:
```js
{
"result_type": "pay_invoice", //indicates the structure of the result field
"error": { //object, non-null in case of error
"code": "UNAUTHORIZED", //string error code, see below
"message": "human readable error message"
},
"result": { // result, object. null in case of error.
"preimage": "0123456789abcdef..." // command-related data
}
"kind" 23195,
"tags": [
["p", "03..." ] // public key of the requesting client app
["e", "1234"] // id of the request event this is responding to
// ...
],
"content": nip44_encrypt({
"result_type": "pay_invoice", //indicates the structure of the result field
"error": { //object, non-null in case of error
"code": "UNAUTHORIZED", //string error code, see below
"message": "human readable error message"
},
"result": { // result, object. null in case of error.
"preimage": "0123456789abcdef..." // command-related data
}
})
// ...
}
```

Expand All @@ -76,6 +109,7 @@ If the command was successful, the `error` field must be null.
- `RESTRICTED`: This public key is not allowed to do this operation.
- `UNAUTHORIZED`: This public key has no wallet connected.
- `INTERNAL`: An internal error.
- `UNSUPPORTED_VERSION`: The version of the request is not supported by the wallet service.
- `OTHER`: Other error.

## Nostr Wallet Connect URI
Expand Down Expand Up @@ -405,5 +439,70 @@ Response:
2. **wallet service** verifies that the author's key is authorized to perform the payment, decrypts the payload and sends the payment.
3. **wallet service** responds to the event by sending an event with kind `23195` and content being a response either containing an error message or a preimage.

## Versioning

Versioning for NIP-47 allows the protocol to evolve and even make breaking changes while maintaining backwards-compatibility where needed. Versions are represented as `<major>.<minor>` (e.g. 1.3).
Major version bumps imply breaking changes that are incompatible with the previous major version. Minor version bumps, however, only include non-breaking feature additions or improvements which
can maintain full compatibility with the previous minor version.

**Note:** Version 0.0 or absence of a version specification implies the version of NIP-47 prior to adding the versioning system. This is the protocol as defined at commit hash `e655247`.

Versioning works as follows.

1. The **wallet service** includes a `v` tag in the `info` event. This tag contains a space-separated list of versions that the **wallet service** supports.
2. The **client application** includes a `v` tag in each request event. This tag contains the highest version that the **client application** supports and is compatible with the **wallet service**.

### Info event

First, the **wallet service** adds a `v` tag to its `info` event containing a list of versions it supports. This list should be a space-separated list in decreasing order with the format
`<major>.<minor>`. There should be one entry in the list for each major version supported by the wallet, where the minor version is the highest minor version for that major version. For example,
if a wallet service supports version 1.0-1.3 and version 0.0, their `v` tag in the `info` event might look something like:

```jsonc
{
"kind": 13194,
"tags": [
["v", "1.3 0.0"], // List of supported versions as described in the Versioning section.
// ...
],
"content": "pay_invoice get_balance make_invoice lookup_invoice list_transactions get_info",
// ...
}
```

When a **client application** establishes a connection, it should read the info event and look for the v tag.

**Absence of this tag implies that the wallet only supports version 0.**

If the `v` tag is present, the **client application** will choose the highest version supported by both itself, and the **wallet service**. Note that minor version bumps are non-breaking. For example,
if the client application supports a highest version of 1.2, and the wallet published the info event shown above, it would select version 1.2 since the wallet’s support for v1.3 implies support for v1.2.

### Request events

When a **client application** sends a request event, it should include a `v` tag with the version it is using. This tag should contain the highest version that the **client application** supports
and is compatible with the **wallet service**. The version MUST be supported by the **wallet service** as indicated by the info event. For example, if the client application supports version 1.2,
the request event might look like:

```jsonc
{
"kind": 23194,
"tags": [
["v", "1.2"], // Version tag
// ...
],
// ...
}
```

If the **wallet service** does not support the specified version, it will return an `UNSUPPORTED_VERSION` error. Absence of the `v` tag indicates use of version 0.

### Changelog

- **0.0**:
- Initial version using the protocol as defined at commit hash `e655247`. Uses NIP-04 for encryption.
- **1.0**
- Adds versioning system.
- Uses NIP-44 for encryption.

## Using a dedicated relay
This NIP does not specify any requirements on the type of relays used. However, if the user is using a custodial service it might make sense to use a relay that is hosted by the custodial service. The relay may then enforce authentication to prevent metadata leaks. Not depending on a 3rd party relay would also improve reliability in this case.