stakes sorted out

This commit is contained in:
Alex Grintsvayg 2018-11-25 10:01:41 -05:00
parent cab3f8fbd0
commit 4187928e6a
2 changed files with 185 additions and 190 deletions

View file

@ -85,97 +85,99 @@
<!-- this TOC is autogenerated for github preview or js-challenged browsers --> <!-- this TOC is autogenerated for github preview or js-challenged browsers -->
<!--ts--> <!--ts-->
* [Introduction](#introduction) - [LBRY: A Decentralized Digital Content Marketplace](#lbry-a-decentralized-digital-content-marketplace)
* [Overview](#overview) - [Table of Contents](#table-of-contents)
* [Assumptions](#assumptions) - [Introduction](#introduction)
* [Conventions and Terminology](#conventions-and-terminology) - [Overview](#overview)
* [Blockchain](#blockchain) - [Assumptions](#assumptions)
* [Stakes](#stakes) - [Conventions and Terminology](#conventions-and-terminology)
* [Claims](#claims) - [Blockchain](#blockchain)
* [Properties](#claim-properties) - [Stakes](#stakes)
* [Example Claim](#example-claim) - [Claims](#claims)
* [Operations](#claim-operations) - [Claim Properties](#claim-properties)
* [Supports](#supports) - [Example Claim](#example-claim)
* [Claimtrie](#claimtrie) - [Claim Operations](#claim-operations)
* [Statuses](#claim-statuses) - [Supports](#supports)
* [Accepted](#accepted) - [Claimtrie](#claimtrie)
* [Abandoned](#abandoned) - [Statuses {#stake-statuses}](#statuses-stake-statuses)
* [Active](#active) - [Accepted](#accepted)
* [Controlling](#controlling) - [Abandoned](#abandoned)
* [Claimtrie Transitions](#claimtrie-transitions) - [Active](#active)
* [Determining Active Claims](#determining-active-claims) - [Controlling (claims only) {#controlling}](#controlling-claims-only-controlling)
* [Claim Transition Example](#claim-transition-example) - [Claimtrie Transitions](#claimtrie-transitions)
* [Normalization](#normalization) - [Stake Activation](#stake-activation)
* [URLs](#urls) - [Example {#transition-example}](#example-transition-example)
* [Components](#components) - [Normalization](#normalization)
* [Stream Claim Name](#stream-claim-name) - [URLs](#urls)
* [Channel Claim Name](#channel-claim-name) - [Components](#components)
* [Channel Claim Name and Stream Claim Name](#channel-claim-name-and-stream-claim-name) - [Stream Claim Name](#stream-claim-name)
* [Claim ID](#claim-id) - [Channel Claim Name](#channel-claim-name)
* [Claim Sequence](#claim-sequence) - [Channel Claim Name and Stream Claim Name](#channel-claim-name-and-stream-claim-name)
* [Bid Position](#bid-position) - [Claim ID](#claim-id)
* [Query Params](#query-params) - [Claim Sequence](#claim-sequence)
* [Grammar](#grammar) - [Bid Position](#bid-position)
* [Resolution](#resolution) - [Query Params](#query-params)
* [No Modifier](#no-modifier) - [Grammar](#grammar)
* [Claim ID](#claim-id-1) - [Resolution](#resolution)
* [Claim Sequence](#claim-sequence-1) - [No Modifier](#no-modifier)
* [Bid Position](#bid-position-1) - [Claim ID](#claim-id-1)
* [ChannelName and ClaimName](#channelname-and-claimname) - [Claim Sequence](#claim-sequence-1)
* [Examples](#url-resolution-examples) - [Bid Position](#bid-position-1)
* [Design Notes](#design-notes) - [ChannelName and ClaimName](#channelname-and-claimname)
* [Transactions](#transactions) - [Examples {#url-resolution-examples}](#examples-url-resolution-examples)
* [Operations and Opcodes](#operations-and-opcodes) - [Design Notes](#design-notes)
* [Claim Identifier Generation](#claim-identifier-generation) - [Transactions](#transactions)
* [OP_CLAIM_NAME](#op-claim-name) - [Operations and Opcodes](#operations-and-opcodes)
* [OP_UPDATE_CLAIM](#op-update-claim) - [Claim Identifier Generation](#claim-identifier-generation)
* [OP_SUPPORT_CLAIM](#op-support-claim) - [OP\_CLAIM\_NAME](#opclaimname)
* [Tips](#tips) - [OP\_UPDATE\_CLAIM](#opupdateclaim)
* [Addresses](#addresses) - [OP\_SUPPORT\_CLAIM](#opsupportclaim)
* [Proof of Payment](#proof-of-payment) - [Tips](#tips)
* [Consensus](#consensus) - [Addresses](#addresses)
* [Block Timing](#block-timing) - [Proof of Payment](#proof-of-payment)
* [Difficulty Adjustment](#difficulty-adjustment) - [Consensus](#consensus)
* [Block Hash Algorithm](#block-hash-algorithm) - [Block Timing](#block-timing)
* [Block Rewards](#block-rewards) - [Difficulty Adjustment](#difficulty-adjustment)
* [Metadata](#metadata) - [Block Hash Algorithm](#block-hash-algorithm)
* [Specification](#specification) - [Block Rewards](#block-rewards)
* [Example](#metadata-example) - [Metadata](#metadata)
* [Key Fields](#key-fields) - [Specification](#specification)
* [Source and Stream Hashes](#source-and-stream-hashes) - [Example {#metadata-example}](#example-metadata-example)
* [Fees and Fee Structure](#fees-and-fee-structure) - [Key Fields](#key-fields)
* [Title, Author, Description](#title-author-description) - [Source and Stream Hashes](#source-and-stream-hashes)
* [Language](#language) - [Fees and Fee Structure](#fees-and-fee-structure)
* [Thumbnail](#thumbnail) - [Title, Author, Description](#title-author-description)
* [Media Type](#media-type) - [Language](#language)
* [Channels (Identities)](#channels) - [Thumbnail](#thumbnail)
* [Signing](#signing) - [Media Type](#media-type)
* [Format Versions](#format-versions) - [Channels (Identities) {#channels}](#channels-identities-channels)
* [Signing Process](#signing-process) - [Signing](#signing)
* [Signature Validation](#signature-validation) - [Format Versions](#format-versions)
* [Validation](#metadata-validation) - [Signing Process](#signing-process)
* [Data](#data) - [Signature Validation](#signature-validation)
* [Encoding](#encoding) - [Validation {#metadata-validation}](#validation-metadata-validation)
* [Blobs](#blobs) - [Data](#data)
* [Streams](#streams) - [Encoding](#encoding)
* [Manifest Contents](#manifest-contents) - [Blobs](#blobs)
* [Stream Encoding](#stream-encoding) - [Streams](#streams)
* [Setup](#setup) - [Manifest Contents](#manifest-contents)
* [Content Blobs](#content-blobs) - [Stream Encoding](#stream-encoding)
* [Manifest Blob](#manifest-blob) - [Setup](#setup)
* [Stream Decoding](#stream-decoding) - [Content Blobs](#content-blobs)
* [Announce](#announce) - [Manifest Blob](#manifest-blob)
* [Distributed Hash Table](#distributed-hash-table) - [Stream Decoding](#stream-decoding)
* [Announcing to the DHT](#announcing-to-the-dht) - [Announce](#announce)
* [Download](#download) - [Distributed Hash Table](#distributed-hash-table)
* [Querying the DHT](#querying-the-dht) - [Announcing to the DHT](#announcing-to-the-dht)
* [Blob Exchange Protocol](#blob-exchange-protocol) - [Download](#download)
* [PriceCheck](#pricecheck) - [Querying the DHT](#querying-the-dht)
* [DownloadCheck](#downloadcheck) - [Blob Exchange Protocol](#blob-exchange-protocol)
* [Download](#download-1) - [PriceCheck](#pricecheck)
* [UploadCheck](#uploadcheck) - [DownloadCheck](#downloadcheck)
* [Upload](#upload) - [Download](#download-1)
* [Reflectors and Data Markets](#reflectors-and-data-markets) - [UploadCheck](#uploadcheck)
- [Upload](#upload)
- [Reflectors and Data Markets](#reflectors-and-data-markets)
<!--te--> <!--te-->
</noscript> </noscript>
@ -286,13 +288,13 @@ fixme final polish checklist:
<p>A <em>claim</em> is a stake that stores metadata. There are two types of claims:</p> <p>A <em>claim</em> is a stake that stores metadata. There are two types of claims:</p>
<dl> <dl>
<dt>stream</dt> <dt>stream claim</dt>
<dd>Declares the availability, access method, and publisher of a stream of bytes (an encoded file).</dd> <dd>Declares the availability, access method, and publisher of a stream of bytes (an encoded file).</dd>
<dt>channel</dt> <dt>channel claim</dt>
<dd>Creates a pseudonym that can be declared as the publisher of a set of stream claims.</dd> <dd>Creates a pseudonym that can be declared as the publisher of a set of stream claims.</dd>
</dl> </dl>
<h4 id="claim-properties">Properties</h4> <h4 id="claim-properties">Claim Properties</h4>
<p>In addition to the properties that all stakes have, claims have two more properties:</p> <p>In addition to the properties that all stakes have, claims have two more properties:</p>
@ -326,11 +328,11 @@ fixme final polish checklist:
} }
} }
</code></pre> </code></pre>
<figcaption>Note: the blockchain treats the <code>value</code> as an opaque byte string and does not impose any structure on it. Structure is applied and validated <a href="#metadata-validation">higher in the stack</a>. In this example, the value is shown for demonstration purposes. <figcaption>Note: the blockchain treats the <code>value</code> as an opaque byte string and does not impose any structure on it. Structure is applied and validated <a href="#metadata-validation">higher in the stack</a>. In this example, the value is shown for demonstration purposes only.
</figcaption> </figcaption>
</figure> </figure>
<h4 id="claim-operations">Operations</h4> <h4 id="claim-operations">Claim Operations</h4>
<p>There are three claim operations: <em>create</em>, <em>update</em>, and <em>abandon</em>.</p> <p>There are three claim operations: <em>create</em>, <em>update</em>, and <em>abandon</em>.</p>
@ -349,7 +351,7 @@ fixme final polish checklist:
<p>Supports have one extra property on top of the basic stake properties: a <code>claim_id</code>. This is the ID of the claim that the support is bolstering.</p> <p>Supports have one extra property on top of the basic stake properties: a <code>claim_id</code>. This is the ID of the claim that the support is bolstering.</p>
<p>Supports function analogously to claims in terms of <a href="#claim-operations">Claim Operations</a> and <a href="#claim-statuses">Claim Statuses</a>, with the exception that they cannot be updated or themselves supported.</p> <p>Supports are created and abandoned just like claims (see <a href="#claim-operations">Claim Operations</a>). They cannot be updated or themselves supported.</p>
<h4 id="claimtrie">Claimtrie</h4> <h4 id="claimtrie">Claimtrie</h4>
@ -365,41 +367,37 @@ fixme final polish checklist:
<p>For more details on the specific claimtrie implementation, see <a href="https://github.com/lbryio/lbrycrd/blob/master/src/claimtrie.cpp">the source code</a>.</p> <p>For more details on the specific claimtrie implementation, see <a href="https://github.com/lbryio/lbrycrd/blob/master/src/claimtrie.cpp">the source code</a>.</p>
<h4 id="claim-statuses">Statuses</h4> <h4 id="stake-statuses">Statuses</h4>
<!-- fix me? is using claims to mean claims and supports okay? --> <p>Stakes can have one or more of the following statuses at a given block.</p>
<p>Throughout this section we use the word &ldquo;claim&rdquo; to refer to both claims and supports, unless otherwise specified.</p>
<p>Claims and supports can have one or more of the following statuses at a given block.</p>
<h5 id="accepted">Accepted</h5> <h5 id="accepted">Accepted</h5>
<p>An <em>accepted</em> claim is one that has been been entered into the blockchain. This happens when the transaction containing it is included in a block.</p> <p>An <em>accepted</em> stake is one that has been been entered into the blockchain. This happens when the transaction containing it is included in a block.</p>
<p>Accepted claims do not affect the intra-leaf claim order until they are <a href="#active">active</a>.</p> <p>Accepted stakes do not affect the intra-leaf claim order until they are <a href="#active">active</a>.</p>
<p>The sum of the amount of a claim and all accepted supports is called the <em>total amount</em>.</p> <p>The sum of the amount of a claim stake and all accepted supports is called the <em>total amount</em>.</p>
<h5 id="abandoned">Abandoned</h5> <h5 id="abandoned">Abandoned</h5>
<p>An <em>abandoned</em> claim is one that was withdrawn by its creator or current owner. Spending a transaction that contains a claim will cause that claim to become abandoned. Abandoned claims are removed from the claimtrie.</p> <p>An <em>abandoned</em> stake is one that was withdrawn by its creator or current owner. Spending a transaction that contains a stake will cause that claim to become abandoned. Abandoned claim stakes are removed from the claimtrie.</p>
<p>While data related to abandoned claims technically still resides in the blockchain, it is improper to use this data to fetch the associated content, and active claims signed by abandoned identities will no longer be reported as valid.</p> <p>While data related to abandoned stakes still resides in the blockchain, it is improper to use this data to fetch the associated content. Active claim stakes signed by abandoned identities will no longer be reported as valid.</p>
<h5 id="active">Active</h5> <h5 id="active">Active</h5>
<!-- fix me a lot --> <!-- fix me a lot -->
<p>An <em>active</em> claim is an accepted and non-abandoned claim that has been in the blockchain for an algorithmically determined number of blocks. This length of time required is called the <em>activation delay</em>.</p> <p>An <em>active</em> stake is an accepted and non-abandoned stake that has been in the blockchain for an algorithmically determined number of blocks. This length of time required is called the <em>activation delay</em>.</p>
<p>If the claim is an update to an already active claim, is the first claim for a name, or does not cause a change in which claim is controlling the name, the activation delay is 0 (i.e. the claim becomes active in the same block it is accepted).</p> <p>If the stake is an update to an already active claim, is the first claim for a name, or does not cause a change in which claim is controlling the name, the activation delay is 0 (i.e. the stake becomes active in the same block it is accepted).</p>
<p>Otherwise, the activation delay is determined by a formula covered in <a href="#claimtrie-transitions">Claimtrie Transitions</a>. The formula&rsquo;s inputs are the height of the current block, the height at which the claim was accepted, and the height at which the controlling claim last changed.</p> <p>Otherwise, the activation delay is determined by a formula covered in <a href="#claimtrie-transitions">Claimtrie Transitions</a>. The formula&rsquo;s inputs are the height of the current block, the height at which the stake was accepted, and the height at which the controlling claim for that name last changed.</p>
<p>The sum of the amount of an active claim and all active supports is called it&rsquo;s <em>effective amount</em>. Only the effective amount affects the sort order of claims in a leaf node. Claims that are not active have an effective amount of 0.</p> <p>The sum of the amount of an active claim and all active supports is called it&rsquo;s <em>effective amount</em>. Only the effective amount affects the sort order of claims in a leaf node. Claims that are not active have an effective amount of 0.</p>
<h5 id="controlling">Controlling</h5> <h5 id="controlling">Controlling (claims only)</h5>
<p>A <em>controlling</em> claim is the active claim that is first in the sort order of a leaf node. That is, it has the highest effective amount of all claims with the same name.</p> <p>A <em>controlling</em> claim is the active claim that is first in the sort order of a leaf node. That is, it has the highest effective amount of all claims with the same name.</p>
@ -416,16 +414,16 @@ fixme final polish checklist:
<li><p>If the controlling claim from the previous block is still first in the order, then the sort is finished.</p></li> <li><p>If the controlling claim from the previous block is still first in the order, then the sort is finished.</p></li>
<li><p>Otherwise, a takeover is occurring. Set the takeover height for this name to the current height, recalculate which claims and supports are now active, and return to step 1.</p></li> <li><p>Otherwise, a takeover is occurring. Set the takeover height for this name to the current height, recalculate which stakes are now active, and return to step 1.</p></li>
<li><p>At this point, the claim with the greatest effective amount is the controlling claim at this block.</p></li> <li><p>At this point, the claim with the greatest effective amount is the controlling claim at this block.</p></li>
</ol> </ol>
<p>The purpose of 4 is to handle the case when multiple competing claims are made on the same name in different blocks, and one of those claims becomes active but another still-inactive claim has the greatest amount. Step 4 will cause the greater claim to also activate and become the controlling claim.</p> <p>The purpose of 4 is to handle the case when multiple competing claims are made on the same name in different blocks, and one of those claims becomes active but another still-inactive claim has the greatest effective amount. Step 4 will cause the greater claim to also activate and become the controlling claim.</p>
<h5 id="determining-active-claims">Determining Active Claims</h5> <h5 id="stake-activation">Stake Activation</h5>
<p>If a claim does not become active immediately, it becomes active at the block heigh determined by the following formula:</p> <p>If a stake does not become active immediately, it becomes active at the block heigh determined by the following formula:</p>
<pre><code>C + min(4032, floor((H-T) / 32)) <pre><code>C + min(4032, floor((H-T) / 32))
</code></pre> </code></pre>
@ -433,18 +431,18 @@ fixme final polish checklist:
<p>Where:</p> <p>Where:</p>
<ul> <ul>
<li>C = claim height (height when the claim was accepted)</li> <li>C = stake height (height when the stake was accepted)</li>
<li>H = current height</li> <li>H = current height</li>
<li>T = takeover height (the most recent height at which the controlling claim for the name changed)</li> <li>T = takeover height (the most recent height at which the controlling claim for the name changed)</li>
</ul> </ul>
<p>In written form, the delay before a claim becomes active is equal to the claims height minus height of the last takeover, divided by 32. The delay is capped at 4032 blocks, which is 7 days of blocks at 2.5 minutes per block (our target block time). The max delay is reached 224 (7x32) days after the last takeover.</p> <p>In written form, the delay before a stake becomes active is equal to the stake&rsquo;s height minus height of the last takeover, divided by 32. The delay is capped at 4032 blocks, which is 7 days of blocks at 2.5 minutes per block (our target block time). The max delay is reached 224 (7x32) days after the last takeover.</p>
<p>The purpose of this delay function is to give long-standing claimants time to respond to changes, while still keeping takeover times reasonable and allowing recent or contentious claims to change state quickly.</p> <p>The purpose of this delay function is to give long-standing claimants time to respond to changes, while still keeping takeover times reasonable and allowing recent or contentious claims to change state quickly.</p>
<h6 id="claim-transition-example">Claim Transition Example</h6> <h5 id="transition-example">Example</h5>
<p>Here is a step-by-step example to illustrate the different scenarios. All claims are for the same name.</p> <p>Here is a step-by-step example to illustrate the different scenarios. All stakes are for the same name.</p>
<p><strong>Block 13:</strong> Claim A for 10LBC is accepted. It is the first claim, so it immediately becomes active and controlling. <p><strong>Block 13:</strong> Claim A for 10LBC is accepted. It is the first claim, so it immediately becomes active and controlling.
<br>State: A(10) is controlling</p> <br>State: A(10) is controlling</p>
@ -469,7 +467,7 @@ fixme final polish checklist:
<h4 id="normalization">Normalization</h4> <h4 id="normalization">Normalization</h4>
<p>Names in the claimtrie are normalized to avoid confusion due to Unicode equivalence or casing. All names are converted using <a href="http://unicode.org/reports/tr15/#Norm_Forms">Unicode Normalization Form D</a> (NFD), then lowercased using the en_US locale when possible.</p> <p>Names in the claimtrie are normalized prior to comparison to avoid confusion due to Unicode equivalence or casing. When names are being compared, they are first converted using <a href="http://unicode.org/reports/tr15/#Norm_Forms">Unicode Normalization Form D</a> (NFD), then lowercased using the en_US locale when possible. Since claims competing for the same name are stored in the same node in the claimtrie, names are also normalized to determine the claimtrie path to the node.</p>
<h3 id="urls">URLs</h3> <h3 id="urls">URLs</h3>
@ -481,13 +479,13 @@ fixme final polish checklist:
<p>URLs are human-readable references to claims. All URLs:</p> <p>URLs are human-readable references to claims. All URLs:</p>
<ol> <ol>
<li>must contain a name (see <a href="#claim-properties">Claim Properties</a>)</li> <li>must contain a name (see <a href="#claim-properties">Claim Properties</a>), and</li>
<li>and resolve to a single, specific claim for that name</li> <li>resolve to a single, specific claim for that name</li>
</ol> </ol>
<p>The ultimate purpose of much of the claim and blockchain design is to provide human-readable URLs that can be provably resolved by clients without a full copy of the blockchain (i.e. <a href="https://lbry.tech/glossary#spv">Simplified Payment Verification</a> wallets).</p> <p>The ultimate purpose of much of the claim and blockchain design is to provide human-readable URLs that can be provably resolved by clients without a full copy of the blockchain (i.e. <a href="https://lbry.tech/glossary#spv">Simplified Payment Verification</a> wallets).</p>
<p>It is possible to write extremely short, human-readable and memorabl</p> <p>It is possible to write short, human-readable, and memorable URLs.</p>
<h4 id="components">Components</h4> <h4 id="components">Components</h4>
@ -495,14 +493,14 @@ fixme final polish checklist:
<h5 id="stream-claim-name">Stream Claim Name</h5> <h5 id="stream-claim-name">Stream Claim Name</h5>
<p>A basic claim for a name.</p> <p>A basic stream claim.</p>
<pre><code>lbry://meet-lbry <pre><code>lbry://meet-lbry
</code></pre> </code></pre>
<h5 id="channel-claim-name">Channel Claim Name</h5> <h5 id="channel-claim-name">Channel Claim Name</h5>
<p>A basic claim for a channel.</p> <p>A basic channel claim.</p>
<pre><code>lbry://@lbry <pre><code>lbry://@lbry
</code></pre> </code></pre>
@ -525,7 +523,7 @@ lbry://@lbry#3f/meet-lbry
<h5 id="claim-sequence">Claim Sequence</h5> <h5 id="claim-sequence">Claim Sequence</h5>
<p>The <em>n</em>th claim for this name, in the order the claims entered the blockchain. <em>n</em> must be a positive number. This can be used to resolve claims in the order in which they were recorded, rather than by which claim has the most support.</p> <p>The <em>n</em>th claim for this name, in the order the claims entered the blockchain. <em>n</em> must be a positive number. This can be used to resolve claims in the order in which they were accepted, rather than by which claim has the most credits backing it.</p>
<pre><code>lbry://meet-lbry:1 <pre><code>lbry://meet-lbry:1
lbry://@lbry:1/meet-lbry lbry://@lbry:1/meet-lbry
@ -533,7 +531,7 @@ lbry://@lbry:1/meet-lbry
<h5 id="bid-position">Bid Position</h5> <h5 id="bid-position">Bid Position</h5>
<p>The <em>n</em>th claim for this name, in order of most support to least total support. <em>n</em> must be a positive number. This is useful for resolving non-winning bids in bid order.</p> <p>The <em>n</em>th claim for this name, ordered by total amount (highest first). <em>n</em> must be a positive number. This is useful for resolving non-winning bids in bid order.</p>
<pre><code>lbry://meet-lbry$2 <pre><code>lbry://meet-lbry$2
lbry://meet-lbry$3 lbry://meet-lbry$3
@ -589,7 +587,7 @@ Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
<h4 id="resolution">Resolution</h4> <h4 id="resolution">Resolution</h4>
<p>URL <em>resolution</em> is the process of translating a URL into it&rsquo;s associated claim id and metadata.</p> <p>URL <em>resolution</em> is the process of translating a URL into the associated claim ID and metadata.</p>
<h5 id="no-modifier">No Modifier</h5> <h5 id="no-modifier">No Modifier</h5>
@ -613,7 +611,7 @@ Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
<p>If both a channel name and a claim name is present, resolution happens in two steps. First, remove the <code>/</code> and <code>StreamClaimNameAndModifier</code> from the path, and resolve the URL as if it only had a <code>ChannelClaimNameAndModifier</code>. Then get the list of all claims in that channel. Finally, resolve the <code>StreamClaimNameAndModifier</code> as if it was its own URL, but instead of considering all claims, only consider the set of claims in the channel.</p> <p>If both a channel name and a claim name is present, resolution happens in two steps. First, remove the <code>/</code> and <code>StreamClaimNameAndModifier</code> from the path, and resolve the URL as if it only had a <code>ChannelClaimNameAndModifier</code>. Then get the list of all claims in that channel. Finally, resolve the <code>StreamClaimNameAndModifier</code> as if it was its own URL, but instead of considering all claims, only consider the set of claims in the channel.</p>
<p>Technically, if multiple claims for the same name exist inside the same channel, they are resolved via the same resolution rules applied entirely within the sub-scope of the channel. Pragmatically, it rarely makes sense for channels to use the same name twice and support for this functionality may be unreliable in current tooling.</p> <p>If multiple claims for the same name exist inside the same channel, they are resolved via the same resolution rules applied entirely within the sub-scope of the channel.</p>
<h5 id="url-resolution-examples">Examples</h5> <h5 id="url-resolution-examples">Examples</h5>
@ -796,7 +794,7 @@ Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
<h4 id="design-notes">Design Notes</h4> <h4 id="design-notes">Design Notes</h4>
<p>The most contentious aspect of this design has been the choice to resolve naked names (sometimes called <em>vanity names</em>) to the claim with the largest number of staked credits.</p> <p>The most contentious aspect of this design has been the choice to resolve naked names (sometimes called <em>vanity names</em>) to the claim with the highest effective amount.</p>
<p>First, it is important to note the problems in existing domain allocation design. Most existing public name schemes are first-come, first-serve with a fixed price. This leads to several bad outcomes:</p> <p>First, it is important to note the problems in existing domain allocation design. Most existing public name schemes are first-come, first-serve with a fixed price. This leads to several bad outcomes:</p>
@ -827,7 +825,7 @@ OP_UPDATE_CLAIM &lt;name&gt; &lt;claimId&gt; &lt;value&gt; OP_2DROP OP_2DROP &lt
OP_SUPPORT_CLAIM &lt;name&gt; &lt;claimId&gt; OP_2DROP OP_DROP &lt;outputScript&gt; OP_SUPPORT_CLAIM &lt;name&gt; &lt;claimId&gt; OP_2DROP OP_DROP &lt;outputScript&gt;
</code></pre> </code></pre>
<p>The <code>&lt;name&gt;</code> parameter is the [[name]] that the claim will be associated with. <code>&lt;value&gt;</code> is the protobuf-encoded claim metadata and optional channel signature (see <a href="#metadata">Metadata</a> for info about this value). The <code>&lt;claimId&gt;</code> is the claim ID of a previous claim that is being updated or supported.</p> <p>The <code>&lt;name&gt;</code> parameter is the [[name]] that the claim is associated with. <code>&lt;value&gt;</code> is the protobuf-encoded claim metadata and optional channel signature (see <a href="#metadata">Metadata</a> for more about this value). The <code>&lt;claimId&gt;</code> is the claim ID of a previous claim that is being updated or supported.</p>
<p>Each opcode will push a zero on to the execution stack. Those zeros, as well as any additional parameters after the opcodes, are all dropped by <code>OP_2DROP</code> and <code>OP_DROP</code>. <code>&lt;outputScript&gt;</code> can be any valid script, so a script using these opcodes is also a pay-to-pubkey script. This means that claim scripts can be spent just like regular Bitcoin output scripts.</p> <p>Each opcode will push a zero on to the execution stack. Those zeros, as well as any additional parameters after the opcodes, are all dropped by <code>OP_2DROP</code> and <code>OP_DROP</code>. <code>&lt;outputScript&gt;</code> can be any valid script, so a script using these opcodes is also a pay-to-pubkey script. This means that claim scripts can be spent just like regular Bitcoin output scripts.</p>
@ -969,7 +967,7 @@ OP_SUPPORT_CLAIM &lt;name&gt; &lt;claimId&gt; OP_2DROP OP_DROP &lt;outputScript&
<h4 id="thumbnail">Thumbnail</h4> <h4 id="thumbnail">Thumbnail</h4>
<p>An http or lbry URL to be used to display an image associated with the content.</p> <p>A URL to be used to display an image associated with the content.</p>
<h4 id="media-type">Media Type</h4> <h4 id="media-type">Media Type</h4>
@ -1088,7 +1086,7 @@ OP_SUPPORT_CLAIM &lt;name&gt; &lt;claimId&gt; OP_2DROP OP_DROP &lt;outputScript&
<!-- fixme this section --> <!-- fixme this section -->
<p>Data refers to the full binary data tht which is ultimate distributed by blah blah blah.</p> <p>Data refers to the full binary data which is ultimate distributed by blah blah blah.</p>
<p>The purpose of blah blah blah is to blah blah.</p> <p>The purpose of blah blah blah is to blah blah.</p>

105
index.md
View file

@ -27,19 +27,19 @@
* [Blockchain](#blockchain) * [Blockchain](#blockchain)
* [Stakes](#stakes) * [Stakes](#stakes)
* [Claims](#claims) * [Claims](#claims)
* [Properties](#claim-properties) * [Claim Properties](#claim-properties)
* [Example Claim](#example-claim) * [Example Claim](#example-claim)
* [Operations](#claim-operations) * [Claim Operations](#claim-operations)
* [Supports](#supports) * [Supports](#supports)
* [Claimtrie](#claimtrie) * [Claimtrie](#claimtrie)
* [Statuses](#claim-statuses) * [Statuses](#stake-statuses)
* [Accepted](#accepted) * [Accepted](#accepted)
* [Abandoned](#abandoned) * [Abandoned](#abandoned)
* [Active](#active) * [Active](#active)
* [Controlling](#controlling) * [Controlling (claims only)](#controlling)
* [Claimtrie Transitions](#claimtrie-transitions) * [Claimtrie Transitions](#claimtrie-transitions)
* [Determining Active Claims](#determining-active-claims) * [Stake Activation](#stake-activation)
* [Claim Transition Example](#claim-transition-example) * [Example](#transition-example)
* [Normalization](#normalization) * [Normalization](#normalization)
* [URLs](#urls) * [URLs](#urls)
* [Components](#components) * [Components](#components)
@ -221,13 +221,13 @@ All stakes have these properties:
A _claim_ is a stake that stores metadata. There are two types of claims: A _claim_ is a stake that stores metadata. There are two types of claims:
<dl> <dl>
<dt>stream</dt> <dt>stream claim</dt>
<dd>Declares the availability, access method, and publisher of a stream of bytes (an encoded file).</dd> <dd>Declares the availability, access method, and publisher of a stream of bytes (an encoded file).</dd>
<dt>channel</dt> <dt>channel claim</dt>
<dd>Creates a pseudonym that can be declared as the publisher of a set of stream claims.</dd> <dd>Creates a pseudonym that can be declared as the publisher of a set of stream claims.</dd>
</dl> </dl>
#### Properties {#claim-properties} #### Claim Properties
In addition to the properties that all stakes have, claims have two more properties: In addition to the properties that all stakes have, claims have two more properties:
@ -261,9 +261,9 @@ Here is an example stream claim:
} }
} }
``` ```
Figure: Note: the blockchain treats the `value` as an opaque byte string and does not impose any structure on it. Structure is applied and validated [higher in the stack](#metadata-validation). In this example, the value is shown for demonstration purposes. Figure: Note: the blockchain treats the `value` as an opaque byte string and does not impose any structure on it. Structure is applied and validated [higher in the stack](#metadata-validation). In this example, the value is shown for demonstration purposes only.
#### Operations {#claim-operations} #### Claim Operations
There are three claim operations: _create_, _update_, and _abandon_. There are three claim operations: _create_, _update_, and _abandon_.
@ -282,7 +282,7 @@ A _support_ is a stake that lends its _amount_ to an existing claim.
Supports have one extra property on top of the basic stake properties: a `claim_id`. This is the ID of the claim that the support is bolstering. Supports have one extra property on top of the basic stake properties: a `claim_id`. This is the ID of the claim that the support is bolstering.
Supports function analogously to claims in terms of [Claim Operations](#claim-operations) and [Claim Statuses](#claim-statuses), with the exception that they cannot be updated or themselves supported. Supports are created and abandoned just like claims (see [Claim Operations](#claim-operations)). They cannot be updated or themselves supported.
#### Claimtrie #### Claimtrie
@ -298,41 +298,37 @@ Multiple claims can exist for the same name. They are all stored in the leaf nod
For more details on the specific claimtrie implementation, see [the source code](https://github.com/lbryio/lbrycrd/blob/master/src/claimtrie.cpp). For more details on the specific claimtrie implementation, see [the source code](https://github.com/lbryio/lbrycrd/blob/master/src/claimtrie.cpp).
#### Statuses {#claim-statuses} #### Statuses {#stake-statuses}
<!-- fix me? is using claims to mean claims and supports okay? --> Stakes can have one or more of the following statuses at a given block.
Throughout this section we use the word "claim" to refer to both claims and supports, unless otherwise specified.
Claims and supports can have one or more of the following statuses at a given block.
##### Accepted ##### Accepted
An _accepted_ claim is one that has been been entered into the blockchain. This happens when the transaction containing it is included in a block. An _accepted_ stake is one that has been been entered into the blockchain. This happens when the transaction containing it is included in a block.
Accepted claims do not affect the intra-leaf claim order until they are [active](#active). Accepted stakes do not affect the intra-leaf claim order until they are [active](#active).
The sum of the amount of a claim and all accepted supports is called the _total amount_. The sum of the amount of a claim stake and all accepted supports is called the _total amount_.
##### Abandoned ##### Abandoned
An _abandoned_ claim is one that was withdrawn by its creator or current owner. Spending a transaction that contains a claim will cause that claim to become abandoned. Abandoned claims are removed from the claimtrie. An _abandoned_ stake is one that was withdrawn by its creator or current owner. Spending a transaction that contains a stake will cause that claim to become abandoned. Abandoned claim stakes are removed from the claimtrie.
While data related to abandoned claims technically still resides in the blockchain, it is improper to use this data to fetch the associated content, and active claims signed by abandoned identities will no longer be reported as valid. While data related to abandoned stakes still resides in the blockchain, it is improper to use this data to fetch the associated content. Active claim stakes signed by abandoned identities will no longer be reported as valid.
##### Active ##### Active
<!-- fix me a lot --> <!-- fix me a lot -->
An _active_ claim is an accepted and non-abandoned claim that has been in the blockchain for an algorithmically determined number of blocks. This length of time required is called the _activation delay_. An _active_ stake is an accepted and non-abandoned stake that has been in the blockchain for an algorithmically determined number of blocks. This length of time required is called the _activation delay_.
If the claim is an update to an already active claim, is the first claim for a name, or does not cause a change in which claim is controlling the name, the activation delay is 0 (i.e. the claim becomes active in the same block it is accepted). If the stake is an update to an already active claim, is the first claim for a name, or does not cause a change in which claim is controlling the name, the activation delay is 0 (i.e. the stake becomes active in the same block it is accepted).
Otherwise, the activation delay is determined by a formula covered in [Claimtrie Transitions](#claimtrie-transitions). The formula's inputs are the height of the current block, the height at which the claim was accepted, and the height at which the controlling claim last changed. Otherwise, the activation delay is determined by a formula covered in [Claimtrie Transitions](#claimtrie-transitions). The formula's inputs are the height of the current block, the height at which the stake was accepted, and the height at which the controlling claim for that name last changed.
The sum of the amount of an active claim and all active supports is called it's _effective amount_. Only the effective amount affects the sort order of claims in a leaf node. Claims that are not active have an effective amount of 0. The sum of the amount of an active claim and all active supports is called it's _effective amount_. Only the effective amount affects the sort order of claims in a leaf node. Claims that are not active have an effective amount of 0.
##### Controlling ##### Controlling (claims only) {#controlling}
A _controlling_ claim is the active claim that is first in the sort order of a leaf node. That is, it has the highest effective amount of all claims with the same name. A _controlling_ claim is the active claim that is first in the sort order of a leaf node. That is, it has the highest effective amount of all claims with the same name.
@ -344,19 +340,19 @@ To determine the sort order of claims in a leaf node, the following algorithm is
1. For each claim, recalculate the effective amount. 1. For each claim, recalculate the effective amount.
1. Sort the claims by effective amount in descending order. Claims tied for the same amount are ordered by block height (ascending), then by transaction order within the block (ascending). 2. Sort the claims by effective amount in descending order. Claims tied for the same amount are ordered by block height (ascending), then by transaction order within the block (ascending).
1. If the controlling claim from the previous block is still first in the order, then the sort is finished. 3. If the controlling claim from the previous block is still first in the order, then the sort is finished.
1. Otherwise, a takeover is occurring. Set the takeover height for this name to the current height, recalculate which claims and supports are now active, and return to step 1. 4. Otherwise, a takeover is occurring. Set the takeover height for this name to the current height, recalculate which stakes are now active, and return to step 1.
1. At this point, the claim with the greatest effective amount is the controlling claim at this block. 5. At this point, the claim with the greatest effective amount is the controlling claim at this block.
The purpose of 4 is to handle the case when multiple competing claims are made on the same name in different blocks, and one of those claims becomes active but another still-inactive claim has the greatest amount. Step 4 will cause the greater claim to also activate and become the controlling claim. The purpose of 4 is to handle the case when multiple competing claims are made on the same name in different blocks, and one of those claims becomes active but another still-inactive claim has the greatest effective amount. Step 4 will cause the greater claim to also activate and become the controlling claim.
##### Determining Active Claims ##### Stake Activation
If a claim does not become active immediately, it becomes active at the block heigh determined by the following formula: If a stake does not become active immediately, it becomes active at the block heigh determined by the following formula:
``` ```
C + min(4032, floor((H-T) / 32)) C + min(4032, floor((H-T) / 32))
@ -364,17 +360,17 @@ C + min(4032, floor((H-T) / 32))
Where: Where:
- C = claim height (height when the claim was accepted) - C = stake height (height when the stake was accepted)
- H = current height - H = current height
- T = takeover height (the most recent height at which the controlling claim for the name changed) - T = takeover height (the most recent height at which the controlling claim for the name changed)
In written form, the delay before a claim becomes active is equal to the claims height minus height of the last takeover, divided by 32. The delay is capped at 4032 blocks, which is 7 days of blocks at 2.5 minutes per block (our target block time). The max delay is reached 224 (7x32) days after the last takeover. In written form, the delay before a stake becomes active is equal to the stake's height minus height of the last takeover, divided by 32. The delay is capped at 4032 blocks, which is 7 days of blocks at 2.5 minutes per block (our target block time). The max delay is reached 224 (7x32) days after the last takeover.
The purpose of this delay function is to give long-standing claimants time to respond to changes, while still keeping takeover times reasonable and allowing recent or contentious claims to change state quickly. The purpose of this delay function is to give long-standing claimants time to respond to changes, while still keeping takeover times reasonable and allowing recent or contentious claims to change state quickly.
###### Claim Transition Example ##### Example {#transition-example}
Here is a step-by-step example to illustrate the different scenarios. All claims are for the same name. Here is a step-by-step example to illustrate the different scenarios. All stakes are for the same name.
**Block 13:** Claim A for 10LBC is accepted. It is the first claim, so it immediately becomes active and controlling. **Block 13:** Claim A for 10LBC is accepted. It is the first claim, so it immediately becomes active and controlling.
<br>State: A(10) is controlling <br>State: A(10) is controlling
@ -400,7 +396,7 @@ Here is a step-by-step example to illustrate the different scenarios. All claims
#### Normalization #### Normalization
Names in the claimtrie are normalized to avoid confusion due to Unicode equivalence or casing. All names are converted using [Unicode Normalization Form D](http://unicode.org/reports/tr15/#Norm_Forms) (NFD), then lowercased using the en_US locale when possible. Names in the claimtrie are normalized prior to comparison to avoid confusion due to Unicode equivalence or casing. When names are being compared, they are first converted using [Unicode Normalization Form D](http://unicode.org/reports/tr15/#Norm_Forms) (NFD), then lowercased using the en_US locale when possible. Since claims competing for the same name are stored in the same node in the claimtrie, names are also normalized to determine the claimtrie path to the node.
### URLs ### URLs
@ -411,12 +407,12 @@ Names in the claimtrie are normalized to avoid confusion due to Unicode equivale
URLs are human-readable references to claims. All URLs: URLs are human-readable references to claims. All URLs:
1. must contain a name (see [Claim Properties](#claim-properties)) 1. must contain a name (see [Claim Properties](#claim-properties)), and
2. and resolve to a single, specific claim for that name 2. resolve to a single, specific claim for that name
The ultimate purpose of much of the claim and blockchain design is to provide human-readable URLs that can be provably resolved by clients without a full copy of the blockchain (i.e. [Simplified Payment Verification](https://lbry.tech/glossary#spv) wallets). The ultimate purpose of much of the claim and blockchain design is to provide human-readable URLs that can be provably resolved by clients without a full copy of the blockchain (i.e. [Simplified Payment Verification](https://lbry.tech/glossary#spv) wallets).
It is possible to write extremely short, human-readable and memorabl It is possible to write short, human-readable, and memorable URLs.
#### Components #### Components
@ -425,7 +421,7 @@ A URL is a name with one or more modifiers. A bare name on its own will resolve
##### Stream Claim Name ##### Stream Claim Name
A basic claim for a name. A basic stream claim.
``` ```
lbry://meet-lbry lbry://meet-lbry
@ -433,7 +429,7 @@ lbry://meet-lbry
##### Channel Claim Name ##### Channel Claim Name
A basic claim for a channel. A basic channel claim.
``` ```
lbry://@lbry lbry://@lbry
@ -459,7 +455,7 @@ lbry://@lbry#3f/meet-lbry
##### Claim Sequence ##### Claim Sequence
The _n_th claim for this name, in the order the claims entered the blockchain. _n_ must be a positive number. This can be used to resolve claims in the order in which they were recorded, rather than by which claim has the most support. The _n_th claim for this name, in the order the claims entered the blockchain. _n_ must be a positive number. This can be used to resolve claims in the order in which they were accepted, rather than by which claim has the most credits backing it.
``` ```
lbry://meet-lbry:1 lbry://meet-lbry:1
@ -468,7 +464,7 @@ lbry://@lbry:1/meet-lbry
##### Bid Position ##### Bid Position
The _n_th claim for this name, in order of most support to least total support. _n_ must be a positive number. This is useful for resolving non-winning bids in bid order. The _n_th claim for this name, ordered by total amount (highest first). _n_ must be a positive number. This is useful for resolving non-winning bids in bid order.
``` ```
lbry://meet-lbry$2 lbry://meet-lbry$2
@ -527,7 +523,7 @@ Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
#### Resolution #### Resolution
URL _resolution_ is the process of translating a URL into it's associated claim id and metadata. URL _resolution_ is the process of translating a URL into the associated claim ID and metadata.
##### No Modifier ##### No Modifier
@ -551,7 +547,7 @@ Get all claims for the claim name. Sort the claims in descending order by total
If both a channel name and a claim name is present, resolution happens in two steps. First, remove the `/` and `StreamClaimNameAndModifier` from the path, and resolve the URL as if it only had a `ChannelClaimNameAndModifier`. Then get the list of all claims in that channel. Finally, resolve the `StreamClaimNameAndModifier` as if it was its own URL, but instead of considering all claims, only consider the set of claims in the channel. If both a channel name and a claim name is present, resolution happens in two steps. First, remove the `/` and `StreamClaimNameAndModifier` from the path, and resolve the URL as if it only had a `ChannelClaimNameAndModifier`. Then get the list of all claims in that channel. Finally, resolve the `StreamClaimNameAndModifier` as if it was its own URL, but instead of considering all claims, only consider the set of claims in the channel.
Technically, if multiple claims for the same name exist inside the same channel, they are resolved via the same resolution rules applied entirely within the sub-scope of the channel. Pragmatically, it rarely makes sense for channels to use the same name twice and support for this functionality may be unreliable in current tooling. If multiple claims for the same name exist inside the same channel, they are resolved via the same resolution rules applied entirely within the sub-scope of the channel.
##### Examples {#url-resolution-examples} ##### Examples {#url-resolution-examples}
@ -593,15 +589,15 @@ URL | Claim ID | Note
#### Design Notes #### Design Notes
The most contentious aspect of this design has been the choice to resolve naked names (sometimes called _vanity names_) to the claim with the largest number of staked credits. The most contentious aspect of this design has been the choice to resolve naked names (sometimes called _vanity names_) to the claim with the highest effective amount.
First, it is important to note the problems in existing domain allocation design. Most existing public name schemes are first-come, first-serve with a fixed price. This leads to several bad outcomes: First, it is important to note the problems in existing domain allocation design. Most existing public name schemes are first-come, first-serve with a fixed price. This leads to several bad outcomes:
1. Speculation and extortion. Entrepreneurs are incentivized to register common names even if they don't intend to use them, in hopes of selling them to the proper owner in the future for an exorbitant price. While speculation in general can have positive externalities (stable prices and price signals), in this case it is pure value extraction. Speculation also harms the user experience, who will see the vast majority of URLs sitting unused (c.f. Namecoin). 1. Speculation and extortion. Entrepreneurs are incentivized to register common names even if they don't intend to use them, in hopes of selling them to the proper owner in the future for an exorbitant price. While speculation in general can have positive externalities (stable prices and price signals), in this case it is pure value extraction. Speculation also harms the user experience, who will see the vast majority of URLs sitting unused (c.f. Namecoin).
1. Bureaucracy and transaction costs. While a centralized system can allow for an authority to use a process to reassign names based on trademark or other common use reasons, this system is also imperfect. Most importantly, it is a censorship point and an avenue for complete exclusion. Additionally, such processes are often arbitrary, change over time, involve significant transaction costs, and _still_ lead to names being used in ways that are contrary to user expectation (e.g. [nissan.com](http://nissan.com)). 2. Bureaucracy and transaction costs. While a centralized system can allow for an authority to use a process to reassign names based on trademark or other common use reasons, this system is also imperfect. Most importantly, it is a censorship point and an avenue for complete exclusion. Additionally, such processes are often arbitrary, change over time, involve significant transaction costs, and _still_ lead to names being used in ways that are contrary to user expectation (e.g. [nissan.com](http://nissan.com)).
1. Inefficiencies from price controls. Any system that does not allow a price to float freely creates inefficiencies. If the set price is too low, we facilitate speculation and rent-seeking. If the price is too high, we see people excluded from a good that it would otherwise be beneficial for them to purchase. 3. Inefficiencies from price controls. Any system that does not allow a price to float freely creates inefficiencies. If the set price is too low, we facilitate speculation and rent-seeking. If the price is too high, we see people excluded from a good that it would otherwise be beneficial for them to purchase.
We sought an algorithmic design built into consensus that would allow URLs to flow to their highest valued use. Following [Coase](https://en.wikipedia.org/wiki/Coase_theorem), this staking design allows for clearly defined rules, low transaction costs, and no information asymmetry, minimizing inefficiency in URL allocation. We sought an algorithmic design built into consensus that would allow URLs to flow to their highest valued use. Following [Coase](https://en.wikipedia.org/wiki/Coase_theorem), this staking design allows for clearly defined rules, low transaction costs, and no information asymmetry, minimizing inefficiency in URL allocation.
@ -624,7 +620,7 @@ OP_UPDATE_CLAIM <name> <claimId> <value> OP_2DROP OP_2DROP <outputScript>
OP_SUPPORT_CLAIM <name> <claimId> OP_2DROP OP_DROP <outputScript> OP_SUPPORT_CLAIM <name> <claimId> OP_2DROP OP_DROP <outputScript>
``` ```
The `<name>` parameter is the [[name]] that the claim will be associated with. `<value>` is the protobuf-encoded claim metadata and optional channel signature (see [Metadata](#metadata) for info about this value). The `<claimId>` is the claim ID of a previous claim that is being updated or supported. The `<name>` parameter is the [[name]] that the claim is associated with. `<value>` is the protobuf-encoded claim metadata and optional channel signature (see [Metadata](#metadata) for more about this value). The `<claimId>` is the claim ID of a previous claim that is being updated or supported.
Each opcode will push a zero on to the execution stack. Those zeros, as well as any additional parameters after the opcodes, are all dropped by `OP_2DROP` and `OP_DROP`. `<outputScript>` can be any valid script, so a script using these opcodes is also a pay-to-pubkey script. This means that claim scripts can be spent just like regular Bitcoin output scripts. Each opcode will push a zero on to the execution stack. Those zeros, as well as any additional parameters after the opcodes, are all dropped by `OP_2DROP` and `OP_DROP`. `<outputScript>` can be any valid script, so a script using these opcodes is also a pay-to-pubkey script. This means that claim scripts can be spent just like regular Bitcoin output scripts.
@ -758,6 +754,7 @@ The `source` property contains information about how to fetch the data from the
#### Fees and Fee Structure #### Fees and Fee Structure
<!-- fix me extensively --> <!-- fix me extensively -->
- LBC - LBC
- Currencies? - Currencies?
- channel signatures and private keys - channel signatures and private keys
@ -772,7 +769,7 @@ The [ISO 639-1](https://www.iso.org/iso-639-language-codes.html) two-letter code
#### Thumbnail #### Thumbnail
An http or lbry URL to be used to display an image associated with the content. A URL to be used to display an image associated with the content.
#### Media Type #### Media Type
@ -847,7 +844,7 @@ Clients are responsible for validating metadata, including data structure and si
<!-- fixme this section --> <!-- fixme this section -->
Data refers to the full binary data tht which is ultimate distributed by blah blah blah. Data refers to the full binary data which is ultimate distributed by blah blah blah.
The purpose of blah blah blah is to blah blah. The purpose of blah blah blah is to blah blah.