From c78872eec1327dd6502e9513d2326c6113358bb7 Mon Sep 17 00:00:00 2001 From: conduition Date: Mon, 13 Jan 2025 19:40:05 +0000 Subject: [PATCH 1/5] NIP-88: DLC oracle announcement/attestation event kinds --- 88.md | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 88.md diff --git a/88.md b/88.md new file mode 100644 index 0000000000..896c1fd3dd --- /dev/null +++ b/88.md @@ -0,0 +1,76 @@ +NIP-88 +====== + +Discreet Log Contract Oracles on Nostr +----------------- + +`draft` `optional` + +This NIP describes two event kinds, `88` and `89`, for [Discreet Log Contract (DLC)](https://bitcoinops.org/en/topics/discreet-log-contracts/) oracles to publish their announcements and attestations over Nostr. Clients can consume these signed events to create conditional payment contracts which fulfill differently based on the oracles' attestations. + +## Format + +DLC protocol messages are binary-serialized messages described concretely in [this document](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md). Whenever embedding DLC messages inside Nostr events (which are encoded as JSON), we serialize those DLC messages in base64. + +## DLC Oracle Gossip + +DLCs require an oracle to attest to the outcome of real world events. This is done by the oracle signing a message containing the outcome of the event. Before they attest to the outcome, they must create an announcement where they publish the intent to sign the future event. This announcement is then used by the DLC participants to create the contract. Here we define two events, `kind:88` and `kind:89` that are used to publish the oracle's announcement and attestations respectively. + +### `kind:88` + +```jsonc +{ + "kind": 88, + "content": "BA/cNhCpdD25j/MwDaa4F42QIq8NsOGmaW1MxyswZnipGWirwoxPhL1SmoHcp1JuCjYXF...", + "tags": [ + [ + "relays", // the relays the oracle will publish attestations to + "wss://nostr.mutinywallet.com", + "wss://relay.damus.io" + ], + [ + "title", + "Optional Event Title" + ], + [ + "description", + "An optional human-readable description of the event which the oracle will attest to, in plain text." + ] + ], + "pubkey": "97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322", + "created_at": 1679673265, + "id": "30efed56a035b2549fcaeec0bf2c1595f9a9b3bb4b1a38abaf8ee9041c4b7d93", + "sig": "f2cb581a84ed10e4dc84937bd98e27acac71ab057255f6aa8dfa561808c981fe8870f4a03c1e3666784d82a9c802d3704e174371aa13d63e2aeaf24ff5374d9d" +} +``` + +The `content` field must be the base64-encoding of a binary-serialized [`oracle_annoucement` object](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#the-oracle_annoucement-type). + +The optional `title` tag gives observers a short human-readable title with which to display the announcement in cards, hyperlinks, etc. It _should_ be at most 100 characters of UTF-8 text. Clients _should_ ignore or truncate titles longer than 100 characters. This tag must NOT be parsed as markdown or HTML. + +The optional `description` tag provides a human-readable summary of the real-world event which this announcement is for. The `description` should give observers context, so that they know how the real-world event in question will be reflected in the oracle's final attestation. This tag must NOT be parsed as markdown or HTML. + +### `kind:89` + +```jsonc +{ + "kind": 89, + "content": "w7HSaUaPQn7Fa00PoUwTqkR2+wXHCPjD8Da5f4OcJ0EACsUw6uSdQgUDLLG9o/e9daS...", + "tags": [ + [ + "e", // the Nostr event id of the announcement + "30efed56a035b2549fcaeec0bf2c1595f9a9b3bb4b1a38abaf8ee9041c4b7d93", + ] + ], + "pubkey": "97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322", + "created_at": 1679673265, + "id": "30efed56a035b2549fcaeec0bf2c1595f9a9b3bb4b1a38abaf8ee9041c4b7d93", + "sig": "f2cb581a84ed10e4dc84937bd98e27acac71ab057255f6aa8dfa561808c981fe8870f4a03c1e3666784d82a9c802d3704e174371aa13d63e2aeaf24ff5374d9d" +} +``` + +The `content` field must be the base64-encoding of a binary-serialized [`oracle_attestation` object](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#the-oracle_attestation-type). + +Note that the `e` tag is the _Nostr event identifier_ for the `kind:88` announcement event, which is distinct from the identifier embedded [in the announcement](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#oracle_event) or [in the attestation itself](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#oracle_attestation). The `e` tag is intended to be used to look up the corresponding announcement event. + +Upon receiving an attestation, clients _should_ validate that the `event_id` field inside the `oracle_announcement` object matches the `event_id` field in the `oracle_attestation` object. From 16b2142fe7ce7d517170e41b132c1a1d6d9997d3 Mon Sep 17 00:00:00 2001 From: conduition Date: Mon, 13 Jan 2025 20:12:10 +0000 Subject: [PATCH 2/5] add profile event for trusted oracle declarations --- 88.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/88.md b/88.md index 896c1fd3dd..4d223dc06f 100644 --- a/88.md +++ b/88.md @@ -74,3 +74,19 @@ The `content` field must be the base64-encoding of a binary-serialized [`oracle_ Note that the `e` tag is the _Nostr event identifier_ for the `kind:88` announcement event, which is distinct from the identifier embedded [in the announcement](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#oracle_event) or [in the attestation itself](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#oracle_attestation). The `e` tag is intended to be used to look up the corresponding announcement event. Upon receiving an attestation, clients _should_ validate that the `event_id` field inside the `oracle_announcement` object matches the `event_id` field in the `oracle_attestation` object. + + +### `kind:10088` + +Kind `10088` lists a user's trusted oracles, for the purpose of 3rd party protocols negotating DLCs or DLC-adjacent conditional payment contracts with the user. A kind `10088` event contains one or more `s` tags with a oracle's Nostr pubkey, and one or more relays where that oracle's announcement events (`kind:88`) may be found. + +```jsonc +{ + "kind": 10088, + "tags": [ + ["s", "4fd5e210530e4f6b2cb083795834bfe5108324f1ed9f00ab73b9e8fcfe5f12fe", "wss://bitagent.prices"], + // ... + ], + //... +} +``` From 69f9ca614245bffdc8054120752761b34fced1fe Mon Sep 17 00:00:00 2001 From: conduition Date: Mon, 13 Jan 2025 23:40:53 +0000 Subject: [PATCH 3/5] add `n` tag --- 88.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/88.md b/88.md index 4d223dc06f..fee3c50b5e 100644 --- a/88.md +++ b/88.md @@ -35,7 +35,11 @@ DLCs require an oracle to attest to the outcome of real world events. This is do [ "description", "An optional human-readable description of the event which the oracle will attest to, in plain text." - ] + ], + + // optional, if this is a numeric event for an asset pair + ["n", "BTC"], + ["n", "USD"] ], "pubkey": "97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322", "created_at": 1679673265, @@ -50,6 +54,8 @@ The optional `title` tag gives observers a short human-readable title with which The optional `description` tag provides a human-readable summary of the real-world event which this announcement is for. The `description` should give observers context, so that they know how the real-world event in question will be reflected in the oracle's final attestation. This tag must NOT be parsed as markdown or HTML. +The optional `n` tag is described further down this document. + ### `kind:89` ```jsonc @@ -60,7 +66,11 @@ The optional `description` tag provides a human-readable summary of the real-wor [ "e", // the Nostr event id of the announcement "30efed56a035b2549fcaeec0bf2c1595f9a9b3bb4b1a38abaf8ee9041c4b7d93", - ] + ], + + // optional, if this is a numeric attestation for an asset pair + ["n", "BTC"], + ["n", "USD"] ], "pubkey": "97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322", "created_at": 1679673265, @@ -75,6 +85,11 @@ Note that the `e` tag is the _Nostr event identifier_ for the `kind:88` announce Upon receiving an attestation, clients _should_ validate that the `event_id` field inside the `oracle_announcement` object matches the `event_id` field in the `oracle_attestation` object. +### The `n` Tag + +DLCs are often numeric events, in which the oracle signs the relative price of two assets. In this common case, kind `88` or `89` events may include exactly two `n` tags which indicate the ticker symbols of the assets whose relative value is being signed. + +The order of the tags implies a specific denomination of the price attestation: The attestation's outcome should be **the value of the first symbol in units of the second symbol.** For example, `[... ["n", "BTC"], ["n", "USD"]]` indicates the attestation supposedly signs the price of `BTC` in units of `USD`. ### `kind:10088` From db455377e0f07d28941a6548aff772850cd98a6a Mon Sep 17 00:00:00 2001 From: conduition Date: Tue, 14 Jan 2025 00:03:54 +0000 Subject: [PATCH 4/5] Notes about pubkey validation --- 88.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/88.md b/88.md index fee3c50b5e..59e8872c10 100644 --- a/88.md +++ b/88.md @@ -56,6 +56,11 @@ The optional `description` tag provides a human-readable summary of the real-wor The optional `n` tag is described further down this document. +Upon receiving an announcement event of kind `88`, clients _should_ validate: +- the base64-encoded announcement data contains a copy of the correct oracle attestation pubkey. The oracle's attestation key may be distinct from the oracle's Nostr key. +- the announcement is signed correctly by the expected oracle attestation pubkey. +- [the event descriptor included in the announcement](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#the-event_descriptor-type) matches the tags in the `kind:88` event. + ### `kind:89` ```jsonc @@ -83,7 +88,10 @@ The `content` field must be the base64-encoding of a binary-serialized [`oracle_ Note that the `e` tag is the _Nostr event identifier_ for the `kind:88` announcement event, which is distinct from the identifier embedded [in the announcement](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#oracle_event) or [in the attestation itself](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#oracle_attestation). The `e` tag is intended to be used to look up the corresponding announcement event. -Upon receiving an attestation, clients _should_ validate that the `event_id` field inside the `oracle_announcement` object matches the `event_id` field in the `oracle_attestation` object. +Upon receiving an attestation, clients _should_ validate: +- the base64-encoded attestation data [contains a copy](https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#the-oracle_attestation-type) of the correct oracle attestation pubkey. This should be the same as the pubkey contained in the corresponding announcement event, of `kind:88`. +- the `event_id` field inside the `oracle_attestation` object matches the `event_id` field in the original `oracle_announcement` object, referred to by the `e` tag. +- the attestation signatures are valid under the oracle's attestation key. ### The `n` Tag @@ -91,6 +99,12 @@ DLCs are often numeric events, in which the oracle signs the relative price of t The order of the tags implies a specific denomination of the price attestation: The attestation's outcome should be **the value of the first symbol in units of the second symbol.** For example, `[... ["n", "BTC"], ["n", "USD"]]` indicates the attestation supposedly signs the price of `BTC` in units of `USD`. +### Oracle Pubkeys + +Oracles are responsible for choosing how to manage their attestation keypair(s). Oracle announcements and attestations embed their own copies of the oracle's BIP340 attestation public key. Oracles should ideally never change their attestation key pair between events, but they _may_ choose to migrate their attestation key, or use different keys for different types of events if they desire. Clients are responsible for choosing secure policies regarding which oracle attestation keys to trust and use. + +**By default, all clients must accept announcement/attestation signatures issued by the oracle's Nostr key.** This requirement allows oracles to fall back on using their Nostr key pair if managing multiple keys is unnecessary, or if their existing attestation key(s) become unusable. + ### `kind:10088` Kind `10088` lists a user's trusted oracles, for the purpose of 3rd party protocols negotating DLCs or DLC-adjacent conditional payment contracts with the user. A kind `10088` event contains one or more `s` tags with a oracle's Nostr pubkey, and one or more relays where that oracle's announcement events (`kind:88`) may be found. From 0d0097dd5f98aae6d5619b9ae2054ede7fbc830f Mon Sep 17 00:00:00 2001 From: conduition Date: Tue, 14 Jan 2025 00:19:49 +0000 Subject: [PATCH 5/5] register event kinds in the readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 3b36f0bb53..df7c2d8fec 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,8 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos | `43` | Channel Hide Message | [28](28.md) | | `44` | Channel Mute User | [28](28.md) | | `64` | Chess (PGN) | [64](64.md) | +| `88` | DLC Oracle Announcement | [88](88.md) | +| `89` | DLC Oracle Attestation | [88](88.md) | | `818` | Merge Requests | [54](54.md) | | `1021` | Bid | [15](15.md) | | `1022` | Bid confirmation | [15](15.md) | @@ -176,6 +178,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos | `10030` | User emoji list | [51](51.md) | | `10050` | Relay list to receive DMs | [51](51.md), [17](17.md) | | `10063` | User server list | [Blossom][blossom] | +| `10088` | Trusted DLC Oracle List | [88](88.md) | | `10096` | File storage server list | [96](96.md) | | `13194` | Wallet Info | [47](47.md) | | `21000` | Lightning Pub RPC | [Lightning.Pub][lnpub] |