-
Notifications
You must be signed in to change notification settings - Fork 46
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
Bip 0010 cleanup #23
Open
satoshisstream
wants to merge
1
commit into
lightning:master
Choose a base branch
from
satoshisstream:patch-1
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Bip 0010 cleanup #23
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,99 +1,266 @@ | ||
``` | ||
bLIP: 10 | ||
Title: Podcasting 2.0 Streaming payments, Boosts and Boostagrams | ||
Status: Active | ||
Author: Satoshis Stream <[email protected]> | ||
# bLIP 10 | ||
|
||
```text | ||
Authors: Satoshis Stream <[email protected]>; @Alwin-Stockinger | ||
Created: 2022-01-24 | ||
License: CC0 | ||
``` | ||
|
||
## Abstract | ||
|
||
Using Podcasting 2.0 apps, listeners can send sats to the hosts of the podcasts they listen. Per minute sending is called `stream` and manual payments are called `boost`s. If there is a message to the podcaster, with a boost, it is called a `boostagram`. | ||
This is part of what is known as "the Value for Value system". These apps (see [Reference Implementations](#reference-implementations)) adopted the 7629169 TLV type for inputting key-value JSON metadata about the sent payment. The TLV holds data about the timestamp when the payment was sent within the episode, the name of the podcast and its [Guid](https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#guid), among other fields described in the [Specification](#Specification) section. | ||
Using Value 4 Value apps, listeners can send sats to the hosts of the podcasts they listen to. Per minute sending is called `stream` and manual payments are called `boost`s. If there is a message to the podcaster, with a boost, it is called a `boostagram`. | ||
These apps (see [Reference Implementations](#reference-implementations)) adopted bLIP 10 with the **7629169** TLV type for inputting key-value JSON metadata about the sent payment. The TLV holds data about the timestamp when the payment was sent within the episode, the name of the podcast and its [Guid](https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#guid), among other fields described in the [Specification](#specification) section. | ||
|
||
Example: `{'podcast': 'PODCASTNAME', 'feedID': 1337, 'episode': 'EPISODENAME', 'action': 'boost', 'ts': 33 }` | ||
Example: | ||
|
||
```json | ||
{ | ||
"podcast": "PODCASTNAME", | ||
"feedID": 1337, | ||
"episode": "EPISODENAME", | ||
"action": "boost", | ||
"ts": 33 | ||
} | ||
``` | ||
|
||
## Copyright | ||
|
||
This bLIP is licensed under the CC0 license. | ||
|
||
## Specification | ||
|
||
The sender of the payments (the Podcast app) needs to input some metadata so the podcast host can identify whom the payment is for. Most fields are optional. | ||
The sender of the payments (the podcast app) needs to input some metadata so the podcast host can identify whom the payment is for. Most fields are optional. | ||
|
||
### Formatting and encoding | ||
A flat key-value json structure is used where below keys can be set. The string is then encoded into `utf8` and attached to the keysend payment. Receivers of messages must be aware there is no guarantee for the order of the keys. | ||
|
||
A flat key-value json structure is used where the keys listed below can be set. The json string is then encoded into `utf8` and attached to the keysend payment. Receivers of messages must be aware that there is no guarantee for the order of the keys. | ||
|
||
Sample JSON string, containing a selection of keys: | ||
`{"app_name": "Castamatic", "app_version": "8.0.6", "value_msat_total": 49960, "url": "https://feeds.buzzsprout.com/1844352.rss", "podcast": "Mere Mortals", "action": "stream", "episode": "The Art Of NFT's & Aimless Wandering", "episode_guid": "Buzzsprout-9931017", "value_msat": 97940, "ts": 574, "name": "Podcaster", "sender_name": "Peter"}` | ||
|
||
Treated as `utf8` the hex value of this json record would be: | ||
```json | ||
{"app_name": "Castamatic", "app_version": "8.0.6", "value_msat_total": 49960, "url": "https://feeds.buzzsprout.com/1844352.rss", "podcast": "Mere Mortals", "action": "stream", "episode": "The Art Of NFT's & Aimless Wandering", "episode_guid": "Buzzsprout-9931017", "value_msat": 97940, "ts": 574, "name": "Podcaster", "sender_name": "Peter"} | ||
``` | ||
|
||
Treated as `utf8`, the hex value of this json record would be: | ||
`7b226170705f6e616d65223a202243617374616d61746963222c20226170705f76657273696f6e223a2022382e302e36222c202276616c75655f6d7361745f746f74616c223a2034393936302c202275726c223a202268747470733a2f2f66656564732e62757a7a7370726f75742e636f6d2f313834343335322e727373222c2022706f6463617374223a20224d657265204d6f7274616c73222c2022616374696f6e223a202273747265616d222c2022657069736f6465223a202254686520417274204f66204e4654277320262041696d6c6573732057616e646572696e67222c2022657069736f64655f67756964223a202242757a7a7370726f75742d39393331303137222c202276616c75655f6d736174223a2039373934302c20227473223a203537342c20226e616d65223a2022506f64636173746572222c202273656e6465725f6e616d65223a20225065746572227d0a` | ||
|
||
If a field is indicated to be a `str` in the fields-list, that means it is a JSON string (within quotes) and `int`s are plain numbers. | ||
|
||
### Fields | ||
|
||
Identifying the podcast **required**: use any of `podcast`, `feedID` or `url`. **guid preferred** | ||
Identifying the podcast **required**: use any of `podcast`, `guid`, `feedID` and/or `url`. | ||
|
||
* `guid` (str, preferred) [The `<podcast:guid>` tag](https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#guid). | ||
* `podcast` (str) Title of the podcast | ||
* `feedID` (int) ID of podcast in PodcastIndex.org | ||
* `url` (str) RSS feed URL of podcast | ||
* `guid` (str) [The `<podcast:guid>` tag](https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#guid). | ||
|
||
Identifying the episode **recommended**: use any of `episode`, `itemID` or `episode_guid`. **itemID preferred** | ||
|
||
Identifying the episode **recommended**: use any of `episode`, `itemID` and/or `episode_guid`. | ||
|
||
* `episode_guid` (str, preferred) The GUID of the episode | ||
* `episode` (str) Episode of the podcast | ||
* `itemID` (int) ID of episode in PodcastIndex.org | ||
* `episode_guid` (str) The GUID of the episode | ||
|
||
Information about time within the episode **recommended**: use any of `time`, `ts`. **ts preferred** | ||
Information about time within the episode **recommended**: use any of `ts`, `time`. | ||
|
||
* `ts` (int, preferred) Timestamp when the boost/stream was sent WITHIN the episode, in seconds (playback position) | ||
* `time` (str) HH:MM:SS timestamp when the boost/stream was sent WITHIN the episode (playback position) | ||
* `ts` (int) Timestamp when the boost/stream was sent WITHIN the episode, in seconds (playback position) | ||
|
||
Rest of keys: | ||
* `action` **recommended**: (str) "boost" or "stream" | ||
|
||
* `action` **recommended**: (str) "boost", "stream" or "auto" | ||
* `app_name`: **recommended** (str) Name of sending app | ||
* `app_version`: (str) Version of sending app | ||
* `boost_link`: (str) App specific URL containing route to podcast, episode, and timestamp at time of boost. | ||
* `boost_link`: (str) App specific URL containing route to podcast, episode, and/or timestamp at time of the action | ||
* `message` (str) Text message to add to the boost message: a boostagram | ||
* `name` **recommended** (str) Name for this split in value tag | ||
* `pubkey` (str) Sending node pubkey | ||
* `seconds_back` (int) The amount of seconds someone has listened since last and until this payment. Usually 60 (sending every minute). Batching can be done for streaming payments by setting 600 for 10-minute streaming payments. | ||
* `sender_key` (str) Node key of sending | ||
* `sender_name` (str) Name of sender (free text, not checked by signatures) | ||
* `sender_id` (str) Static random identifier for users, not displayed by apps, for abuse purposes. Apps can set this per-feed or app-wide. A GUID-like random identifier or a hash works well. Max 32 ascii characters. | ||
* `sig_fields` (str) pipe separated list of fields that are used for signature (example: feedID|itemID|ts|action|sender_key|message) | ||
* `signature` (str) DER-encoded ECDSA signature | ||
* `name` (str) Name for this split in value tag | ||
* `sender_name` **recommended** (str) Name of sender (free text, not checked by signatures) | ||
* `sender_id` (str) Static random identifier for users, not displayed by apps to prevent abuse. Apps can set this per-feed or app-wide. A GUID-like random identifier or a hash works well. Max 32 ascii characters. | ||
* `speed` (str) Speed in which the podcast was played, in decimal. So 0.5 is half speed and 2 is double speed. | ||
* `uuid` (str) Unique UUID of the payment | ||
* `value_msat`: (int) Number of millisats for this split payment (this is in the meta/payment data too) | ||
* `value_msat_total`: (int) TOTAL Number of millisats for the payment (all splits together, before fees. The actual number someone entered in their player, for numerology purposes.) | ||
* `value_msat_total`: **recommended** (int) TOTAL Number of millisats for the payment (all splits together, before fees. The actual number someone entered in their player, for numerology purposes.) | ||
|
||
## Motivation | ||
## Reference Implementations | ||
|
||
The Value for Value system is a way for podcasters to receive direct payments from listeners in the form of bitcoin through the Lightning Network. It provides not only a new way for podcast to earn and keep their independency, but also new forms of interaction between podcasters and its listeners. | ||
* Breez: <https://github.com/breez/breezmobile/blob/4cf057c066d03c155964f0c4db43476cd70a57ab/lib/bloc/podcast_payments/podcast_payments_bloc.dart> | ||
* Podverse: <https://github.com/podverse/podverse-shared/blob/fff84c0143dea4fa01241109b8666d4c0b9a6ffc/src/satoshiStream.ts> | ||
* PodStation: <https://github.com/podStation/podStation/pull/249> | ||
* Helipad: <https://github.com/Podcastindex-org/helipad/blob/203e72dafb65e4f9e89540fbe4fc07a456a9907a/src/main.rs> | ||
* Rust-V4V: <https://github.com/Conshax/Rust-V4V/blob/master/src/boostagram.rs> | ||
|
||
Using the same TLV type allow for podcast creators to understand the messages regardless of which podcast app it was sent from and which solution they used to receive their payments. | ||
## Examples & App Specifics | ||
|
||
_Future: The specification could be used for other media types, too._ | ||
### Podverse | ||
|
||
## Rationale | ||
Podverse cuts the episode name short and `stream` actions are sent as `streaming`. | ||
|
||
The TLV type 7629169 was originally chosen by Breez and other applications adopted it to keep the same standard. | ||
#### Boost | ||
|
||
The required and recommended fields are constantly evolving by iterations from different Podcast 2.0 apps. Some of these fields are related to the extensions proposed by [PodcastIndex.org](https://podcastindex.org/) to the RSS 2.0 spec in order to deliver new functionality to apps and aggregators (see [the Podcast Namespace](https://github.com/Podcastindex-org/podcast-namespace)) | ||
```json | ||
{ | ||
"podcast": "Test Podcast Anchor", | ||
"feedID": 6015671, | ||
"episode": "this is a very very very very very very very very very very ", | ||
"episode_guid": "12b4df54-af38-4c53-8099-82f9caacdcd5", | ||
"ts": 24, | ||
"action": "boost", | ||
"speed": "1", | ||
"pubkey": "podverse-pubkey", | ||
"value_msat_total": 100000, | ||
"uuid": "75758d19-c4af-4da2-80ce-a5c84a0f1642", | ||
"app_name": "Podverse", | ||
"name": "Alby Test User PUT", | ||
"sender_name": "Alwin_Conshax", | ||
"message": "test" | ||
} | ||
``` | ||
|
||
## Universality | ||
#### Stream | ||
|
||
This usage of the 7629169 TLV type is only intended for Lightning applications in the Podcasting 2.0 space. Lightning applications that do not intersect with Podcasting 2.0 have no need to implement this standard. Additionally, no additional work is required from a Lightning implementation itself beyond BOLT-specified TLV support in order to enable this use case at the application layer. | ||
```json | ||
{ | ||
"podcast": "Test Podcast Anchor", | ||
"feedID": 6015671, | ||
"episode": "this is a very very very very very very very very very very ", | ||
"episode_guid": "12b4df54-af38-4c53-8099-82f9caacdcd5", | ||
"ts": 315, | ||
"action": "streaming", | ||
"speed": "1", | ||
"pubkey": "podverse-pubkey", | ||
"value_msat_total": 100000, | ||
"uuid": "654072b5-9825-4792-b502-31ee9f1ec6aa", | ||
"app_name": "Podverse", | ||
"name": "Alby Test User PUT", | ||
"sender_name": "Alwin_Conshax" | ||
} | ||
``` | ||
|
||
## Backwards Compatibility | ||
### Castamatic | ||
|
||
This is not using a feature bit and the TLV record is oddly typed, so there are no concerns regarding backwards compatibility. | ||
Castamatic sends `value_msat_total` only on boosts not on streams. | ||
|
||
## Reference Implementations | ||
#### Boost | ||
|
||
```json | ||
{ | ||
"message": "test", | ||
"app_name": "Castamatic", | ||
"app_version": "8.6.0", | ||
"value_msat_total": 100000, | ||
"url": "https://anchor.fm/s/da6b03c0/podcast/rss", | ||
"podcast": "Test Podcast Anchor", | ||
"action": "boost", | ||
"episode": "this is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long episode name!!!", | ||
"episode_guid": "12b4df54-af38-4c53-8099-82f9caacdcd5", | ||
"feedID": 6015671, | ||
"value_msat": 95049, | ||
"ts": 0, | ||
"name": "Alby Test User PUT", | ||
"sender_name": "Alwin_Conshax" | ||
} | ||
``` | ||
|
||
#### Stream | ||
|
||
* Breez: https://github.com/breez/breezmobile/blob/4cf057c066d03c155964f0c4db43476cd70a57ab/lib/bloc/podcast_payments/podcast_payments_bloc.dart | ||
* Podverse: https://github.com/podverse/podverse-shared/blob/fff84c0143dea4fa01241109b8666d4c0b9a6ffc/src/satoshiStream.ts | ||
* PodStation: https://github.com/podStation/podStation/pull/249 | ||
* Helipad: https://github.com/Podcastindex-org/helipad/blob/203e72dafb65e4f9e89540fbe4fc07a456a9907a/src/main.rs | ||
```json | ||
{ | ||
"app_name": "Castamatic", | ||
"app_version": "8.6.0", | ||
"url": "https://anchor.fm/s/da6b03c0/podcast/rss", | ||
"podcast": "Test Podcast Anchor", | ||
"action": "stream", | ||
"episode": "this is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long episode name!!!", | ||
"episode_guid": "12b4df54-af38-4c53-8099-82f9caacdcd5", | ||
"feedID": 6015671, | ||
"value_msat": 50940, | ||
"ts": 254, | ||
"name": "Test User Peppe the first", | ||
"sender_name": "Alwin_Conshax" | ||
} | ||
``` | ||
|
||
### Fountain | ||
|
||
Fountain sends the itemID as a string, not an int. | ||
|
||
#### Boost | ||
|
||
```json | ||
{ | ||
"app_name": "Fountain", | ||
"value_msat_total": 100000, | ||
"name": "Alby Test User PUT", | ||
"podcast": "Test Podcast Anchor", | ||
"feedID": 6015671, | ||
"action": "boost", | ||
"sender_id": "nSiq7id78JAdH9uY1pIy", | ||
"sender_name": "@alwin_conshax", | ||
"message": "test", | ||
"itemID": "14934154309", | ||
"boost_link": "https://fountain.fm/episode/14934154309", | ||
"episode": "this is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long episode name!!!", | ||
"ts": 15, | ||
"time": "00:00:15" | ||
} | ||
``` | ||
|
||
#### Stream | ||
|
||
```json | ||
{ | ||
"app_name": "Fountain", | ||
"value_msat_total": 50000, | ||
"name": "Alby Test User PUT", | ||
"podcast": "Test Podcast Anchor", | ||
"feedID": 6015671, | ||
"action": "stream", | ||
"sender_id": "pqwMfFdkCwtj8LKm0tNu", | ||
"sender_name": "@moritz_conshax", | ||
"message": null, | ||
"itemID": "14934154309", | ||
"boost_link": "https://fountain.fm/episode/14934154309", | ||
"episode": "this is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long episode name!!!", | ||
"ts": 62, | ||
"time": "00:01:02" | ||
} | ||
``` | ||
|
||
### Breez | ||
|
||
Breez sends the guid on the itemID field and the sender_name is an empty string on streams. | ||
Besides that Breez takes its fee from the total instead on top of the total as fountain does, this results in the received value of a split being less than it should calculated from the total. | ||
|
||
#### Boost | ||
|
||
```json | ||
{ | ||
"podcast": "Test Podcast Anchor", | ||
"episode": "this is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long episode name!!!", | ||
"action": "boost", | ||
"time": "00:00:24", | ||
"feedID": 6015671, | ||
"itemID": "12b4df54-af38-4c53-8099-82f9caacdcd5", | ||
"app_name": "Breez", | ||
"value_msat_total": 100000, | ||
"sender_name": "Jade Monkey", | ||
"message": "test" | ||
} | ||
|
||
``` | ||
|
||
#### Stream | ||
|
||
```json | ||
{ | ||
"podcast": "Test Podcast Anchor", | ||
"episode": "this is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long episode name!!!", | ||
"action": "stream", | ||
"time": "00:02:37", | ||
"feedID": 6015671, | ||
"itemID": "12b4df54-af38-4c53-8099-82f9caacdcd5", | ||
"app_name": "Breez", | ||
"value_msat_total": 100000, | ||
"sender_name": "" | ||
} | ||
``` |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
We require keeping the
Title
andStatus
fields in the header, can you add them back?