more text, more styling

This commit is contained in:
Alex Grintsvayg 2018-10-17 15:06:31 -04:00
parent c8374cbe17
commit 30fd8d5f7f
6 changed files with 579 additions and 139 deletions

View file

@ -1,3 +1,3 @@
index.html: index.md style.css index.html: index.md style.css
./bin/gh-md-toc --insert index.md ./bin/gh-md-toc --insert index.md
./bin/mmark-linux-amd64 -css style.css -html index.md > index.html ./bin/mmark-linux-amd64 -head head.html -html index.md > index.html

2
head.html Normal file
View file

@ -0,0 +1,2 @@
<link rel="stylesheet" type="text/css" href="normalize.css">
<link rel="stylesheet" type="text/css" href="style.css">

View file

@ -4,6 +4,7 @@
<title>LBRY: A Decentralized Digital Content Marketplace</title> <title>LBRY: A Decentralized Digital Content Marketplace</title>
<meta name="GENERATOR" content="github.com/mmarkdown/mmark Mmark Markdown Processor - mmark.nl"> <meta name="GENERATOR" content="github.com/mmarkdown/mmark Mmark Markdown Processor - mmark.nl">
<meta charset="utf-8"> <meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="normalize.css">
<link rel="stylesheet" type="text/css" href="style.css"> <link rel="stylesheet" type="text/css" href="style.css">
</head> </head>
<body> <body>
@ -22,6 +23,8 @@
<h2 id="table-of-contents">Table of Contents</h2> <h2 id="table-of-contents">Table of Contents</h2>
<p><div id="toc"></p>
<!--ts--> <!--ts-->
<ul> <ul>
@ -48,7 +51,7 @@
<li><a href="#urls">URLs</a> <li><a href="#urls">URLs</a>
<ul> <ul>
<li><a href="#schema">Schema</a></li> <li><a href="#components">Components</a></li>
<li><a href="#design-notes">Design Notes</a></li> <li><a href="#design-notes">Design Notes</a></li>
</ul></li> </ul></li>
<li><a href="#transactions">Transactions</a> <li><a href="#transactions">Transactions</a>
@ -108,6 +111,8 @@
<!--te--></li> <!--te--></li>
</ul> </ul>
<p></div></p>
<h2 id="overview">Overview</h2> <h2 id="overview">Overview</h2>
<p>(overview)</p> <p>(overview)</p>
@ -124,6 +129,18 @@
<p>(Rather than this section, maybe we can use a syntax like brackets around keywords to inline key definitions?)</p> <p>(Rather than this section, maybe we can use a syntax like brackets around keywords to inline key definitions?)</p>
<ul>
<li>file</li>
<li>stream</li>
<li>blob</li>
<li>metadata</li>
<li>hash</li>
<li>name</li>
<li>claim</li>
<li>channel</li>
<li>url</li>
</ul>
<h2 id="blockchain">Blockchain</h2> <h2 id="blockchain">Blockchain</h2>
<p>The LBRY blockchain is a public, proof-of-work blockchain. It serves three key purposes:</p> <p>The LBRY blockchain is a public, proof-of-work blockchain. It serves three key purposes:</p>
@ -134,18 +151,15 @@
<li>Trustful publisher identities (fixme: should this even be listed here?)</li> <li>Trustful publisher identities (fixme: should this even be listed here?)</li>
</ol> </ol>
<p>The LBRY blockchain is a fork of the <a href="https://bitcoin.org/bitcoin.pdf">Bitcoin</a> blockchain, with substantial modifications. This document will not cover or specify any aspects of LBRY that are identical to Bitcoin, and instead focus on the differences.</p> <p>The LBRY blockchain is a fork of the <a href="https://bitcoin.org/bitcoin.pdf">Bitcoin</a> blockchain, with substantial modifications. This document will not cover or specify any aspects of LBRY that are identical to Bitcoin, and will instead focus on the differences.</p>
<h3 id="claims">Claims</h3> <h3 id="claims">Claims</h3>
<p>A single metadata entry in the blockchain is called a <em>[[claim]]</em>. It consists four primary pieces of information:</p> <p>A single metadata entry in the blockchain is called a <code>claim</code>. It records an item that was published to the network or a publisher&rsquo;s identity.</p>
<ul> <p>Every claim has a globally-unique <code>claimID</code>, an <code>amount</code> (how many credits were set aside to back the claim), and a <code>value</code>. The value may contain metadata about a piece of content, a publisher&rsquo;s public key, or other information. See the <a href="#metadata">Metadata</a> for more information about what may be stored in the value.</p>
<li>ID - a unique identifier of this claim</li>
<li>Name - a <a href="#normalization">normalized</a> version of the name being claimed, for purposes of creating human readable and memorable [[URLs]]</li> <p>Every claim is associated with a <code>name</code>, which is a bytestring of 0-255 bytes. Every name must be a valid UTF8 string.</p>
<li>Amount - how many credits will be set aside, or staked, to back the claim, which has ramifacations covered in [[URLs]]</li>
<li>Data - information associated the name (e.g. SD hash, content metadata, etc)</li>
</ul>
<p>Here is an example claim:</p> <p>Here is an example claim:</p>
@ -165,27 +179,27 @@
} }
</code></pre> </code></pre>
<p>The value field contains the claim contents, including the source and the [[metadata]].</p>
<h4 id="claim-operations">Claim Operations</h4> <h4 id="claim-operations">Claim Operations</h4>
<p>There are four claim operations: <em>create</em>, <em>support</em>, <em>update</em>, and <em>abandon</em>.</p> <p>There are four claim operations: <code>create</code>, <code>support</code>, <code>update</code>, and <code>abandon</code>.</p>
<p>The <em>create</em> operation is used to make a new claim for a name, or to submit a competing claim on an existing name.</p> <p>A <code>create</code> operation makes a new claim for a name, or submits a competing claim on an existing name.</p>
<p>A <em>support</em> is a claim that adds to the credit total of an existing claim. A support does not have its own claim ID or data. Instead, it has the claim ID of the claim to which its amount will be added.</p> <p>A <code>support</code> is a claim that adds to the credit total of an existing claim. A support does not have its own claim ID or data. Instead, it has the claim ID of the claim to which its amount will be added.</p>
<p>An <em>update</em> changes the data or the amount stored in an existing claim or support. Updates do not change the claim ID, so an updated claim retains any supports attached to it.</p> <p>An <code>update</code> changes the data or the amount stored in an existing claim or support. Updates do not change the claim ID, so an updated claim retains any supports attached to it.</p>
<p>An <em>abandon</em> withdraws a claim or support, freeing the associated credits to be used for other purposes.</p> <p>An <code>abandon</code> withdraws a claim or support, freeing the associated credits to be used for other purposes.</p>
<h4 id="claimtrie">Claimtrie</h4> <h4 id="claimtrie">Claimtrie</h4>
<p>The blockchain adds a parallel [[Merkle tree]] for storing claims. The set of all claims is called the <em>[[claimtrie]]</em>.</p> <p>The <code>claimtrie</code> is the data structure that LBRY uses to store claims and prove the correctness of name resolution. It is a <a href="https://en.wikipedia.org/wiki/Merkle_tree">Merkle Tree</a> that maps names to claims. Claims are stored as leaf nodes in the tree. Names are stored as the path from the root node to the leaf node.</p>
<p>The claimtrie is a [Merkle tree]() where the keys are the claimed names and values are claims for that name (sorted in decreasing order by total amount). The root hash of the claimtrie is stored in the block header of each LBRY block, enabling nodes in the LBRY network to efficiently and securely validate the state of the claimtrie without downloading the whole block.</p> <p>The hash of the root node (the <code>root hash</code>) is stored in the header of each block in the blockchain. Nodes in the LBRY network use the root hash to efficiently and securely validate the state of the claimtrie.</p>
<p>For more details on the specific claimtrie impelementation, see <a href="#fixme">fix me</a>.</p> <p>Multiple claims can exist for the same name. They are all stored in the leaf node for that name, sorted in decreasing order by the total amount of credits backing each claim.</p>
<p>For more details on the specific claimtrie impelementation, see <a href="https://github.com/lbryio/lbrycrd/blob/master/src/claimtrie.cpp">the source code</a>.</p>
<h4 id="claim-states">Claim States</h4> <h4 id="claim-states">Claim States</h4>
@ -199,7 +213,7 @@
<p>An abandoned claim or support is one that was withdrawn by its creator. It is no longer in contention to control a name. Spending the transaction that contains the claim will also cause the claim to become abandoned.</p> <p>An abandoned claim or support is one that was withdrawn by its creator. It is no longer in contention to control a name. Spending the transaction that contains the claim will also cause the claim to become abandoned.</p>
<p>While data related to abandoned claims technically still resides in the blockchain, it is considered inappropriate (and potentially illegal? #fixme), to use this data to actually fetch the associated content. Wallet servers MUST not return or resolve abandoned claim information.</p> <p>While data related to abandoned claims technically still resides in the blockchain, it is considered inappropriate (and potentially illegal? #fixme), to use this data to fetch the associated content.</p>
<h5 id="active">Active</h5> <h5 id="active">Active</h5>
@ -219,9 +233,9 @@
<h5 id="controlling">Controlling</h5> <h5 id="controlling">Controlling</h5>
<p>The controlling claim is the claim that is returned when a name is resolved (#fixme resolution hasn&rsquo;t been introduced yet. It may be better to define the controlling claim separate from URLs, and then define a vanity URL as returning the controlling claim) . The controlling claim must be active and cannot be a support.</p> <p>The controlling claim is the claim that has the highest total effective amount, which is the sum of its own amount and the amounts of all of its supports. It must be active and cannot itself be a support.</p>
<p>Only one claim can be controlling for a given name at a given block. To determine which claim is controlling for a given name in a given block, the following algorithm is used:</p> <p>Only one claim can be controlling for a given name at a given block. To determine which claim is controlling for a given name at a given block, the following algorithm is used:</p>
<ol> <ol>
<li><p>For each active claim for the name, add up the amount of the claim and the amount of all the active supports for that claim.</p></li> <li><p>For each active claim for the name, add up the amount of the claim and the amount of all the active supports for that claim.</p></li>
@ -229,41 +243,38 @@
<li><p>Determine if a takeover is happening</p> <li><p>Determine if a takeover is happening</p>
<ol> <ol>
<li>If the claim with the greatest total is the controlling claim from the previous block, then nothing changes. That claim is <li><p>If the claim with the greatest total is the controlling claim from the previous block, then nothing changes. That claim is still controlling at this block.</p></li>
still controlling at this block.</li>
<li>Otherwise, a takeover is occurring. Set the takeover height for this name to the current height, recalculate which claims and supports <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 then perform step 1 again.</p></li>
are now active, and then perform step 1 again.</li>
</ol></li> </ol></li>
<li><p>At this point, the claim with the greatest total is the controlling claim at this block.</p></li> <li><p>At this point, the claim with the greatest total is the controlling claim at this block.</p></li>
</ol> </ol>
<p>The purpose of 2b is to handle the case when multiple competing claims are made on the same name in different blocks, and one of those <p>The purpose of 2b 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 2b will cause this claim to also activate and become the controlling claim.</p>
claims becomes active but another still-inactive claim has the greatest amount. Step 2b will cause this claim to also activate and become
the controlling claim.</p>
<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 claims 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.
State: A(10) is controlling</p> <br>State: A(10) is controlling</p>
<p><strong>Block 1001:</strong> Claim B for 20LBC is accepted. Its activation height is <code>1001 + min(4032, floor((1001-13) / 32)) = 1001 + 30 = 1031</code> <p><strong>Block 1001:</strong> Claim B for 20LBC is accepted. Its activation height is <code>1001 + min(4032, floor((1001-13) / 32)) = 1001 + 30 = 1031</code>.
State: A(10) is controlling, B(20) is accepted.</p> <br>State: A(10) is controlling, B(20) is accepted.</p>
<p><strong>Block 1010:</strong> Support X for 14LBC for claim A is accepted. Since it is a support for the controlling claim, it activates immediately. <p><strong>Block 1010:</strong> Support X for 14LBC for claim A is accepted. Since it is a support for the controlling claim, it activates immediately.
State: A(10+14) is controlling, B(20) is accepted.</p> <br>State: A(10+14) is controlling, B(20) is accepted.</p>
<p><strong>Block 1020:</strong> Claim C for 50LBC is accepted. The activation height is <code>1020 + min(4032, floor((1020-13) / 32)) = 1020 + 31 = 1051</code> <p><strong>Block 1020:</strong> Claim C for 50LBC is accepted. The activation height is <code>1020 + min(4032, floor((1020-13) / 32)) = 1020 + 31 = 1051</code>.
State: A(10+14) is controlling, B(20) is accepted, C(50) is accepted.</p> <br>State: A(10+14) is controlling, B(20) is accepted, C(50) is accepted.</p>
<p><strong>Block 1031:</strong> Claim B activates. It has 20LBC, while claim A has 24LBC (10 original + 14 from support X). There is no takeover, and claim A remains controlling. <p><strong>Block 1031:</strong> Claim B activates. It has 20LBC, while claim A has 24LBC (10 original + 14 from support X). There is no takeover, and claim A remains controlling.
State: A(10+14) is controlling, B(20) is active, C(50) is accepted.</p> <br>State: A(10+14) is controlling, B(20) is active, C(50) is accepted.</p>
<p><strong>Block 1040:</strong> Claim D for 300LBC is accepted. The activation height is <code>1040 + min(4032, floor((1040-13) / 32)) = 1040 + 32 = 1072</code> <p><strong>Block 1040:</strong> Claim D for 300LBC is accepted. The activation height is <code>1040 + min(4032, floor((1040-13) / 32)) = 1040 + 32 = 1072</code>.
State: A(10+14) is controlling, B(20) is active, C(50) is accepted, D(300) is accepted.</p> <br>State: A(10+14) is controlling, B(20) is active, C(50) is accepted, D(300) is accepted.</p>
<p><strong>Block 1051:</strong> Claim C activates. It has 50LBC, while claim A has 24LBC, so a takeover is initiated. The takeover height for this name is set to 1051, and therefore the activation delay for all the claims becomes <code>min(4032, floor((1051-1051) / 32)) = 0</code>. All the claims become active. The totals for each claim are recalculated, and claim D becomes controlling because it has the highest total. <p><strong>Block 1051:</strong> Claim C activates. It has 50LBC, while claim A has 24LBC, so a takeover is initiated. The takeover height for this name is set to 1051, and therefore the activation delay for all the claims becomes <code>min(4032, floor((1051-1051) / 32)) = 0</code>. All the claims become active. The totals for each claim are recalculated, and claim D becomes controlling because it has the highest total.
State: A(10+14) is active, B(20) is active, C(50) is active, D(300) is controlling.</p> <br>State: A(10+14) is active, B(20) is active, C(50) is active, D(300) is controlling.</p>
<h4 id="normalization">Normalization</h4> <h4 id="normalization">Normalization</h4>
@ -275,7 +286,7 @@ State: A(10+14) is active, B(20) is active, C(50) is active, D(300) is controlli
<p>The ultimate purpose of much of the claim design, including controlling claims and the claimtrie structure, is to provide human readable URLs that can be trustfully resolved by [[Simple Payment Verification]] wallets.</p> <p>The ultimate purpose of much of the claim design, including controlling claims and the claimtrie structure, is to provide human readable URLs that can be trustfully resolved by [[Simple Payment Verification]] wallets.</p>
<h4 id="schema">Schema</h4> <h4 id="components">Components</h4>
<p>A URL is generally a name with one or more modifiers. A bare name on its own will resolve to the controlling claim at the latest block height, for reasons covered in [[Design Notes]]. The available modifiers are:</p> <p>A URL is generally a name with one or more modifiers. A bare name on its own will resolve to the controlling claim at the latest block height, for reasons covered in [[Design Notes]]. The available modifiers are:</p>
@ -293,7 +304,7 @@ State: A(10+14) is active, B(20) is active, C(50) is active, D(300) is controlli
<p><strong>Claim ID:</strong> a claim for this name with this claim ID (does not have to be the controlling claim). Partial prefix matches are allowed.</p> <p><strong>Claim ID:</strong> a claim for this name with this claim ID (does not have to be the controlling claim). Partial prefix matches are allowed.</p>
<p>lbry://meet-LBRY#7a0aa95c5023c21c098 <p>lbry://meet-LBRY#7a0aa95c5023c21c098<br>
lbry://meet-LBRY#7a</p> lbry://meet-LBRY#7a</p>
<p><strong>Claim Sequence:</strong> the Nth claim for this name, in the order the claims entered the blockchain. N must be a positive number. This can be used to determine which claim came first, rather than which claim has the most support.</p> <p><strong>Claim Sequence:</strong> the Nth claim for this name, in the order the claims entered the blockchain. N must be a positive number. This can be used to determine which claim came first, rather than which claim has the most support.</p>
@ -302,43 +313,58 @@ lbry://meet-LBRY#7a</p>
<p><strong>Bid Position:</strong> the Nth claim for this name, in order of most support to least support. N must be a positive number. This is useful for resolving non-winning bids in bid order if you, for example, want to list the top three winning claims in a voting contest or want to ignore the activation delay.</p> <p><strong>Bid Position:</strong> the Nth claim for this name, in order of most support to least support. N must be a positive number. This is useful for resolving non-winning bids in bid order if you, for example, want to list the top three winning claims in a voting contest or want to ignore the activation delay.</p>
<p>lbry://meet-LBRY<span class="math inline">\(2 <p>lbry://meet-LBRY<span class="math inline">\(2&lt;br&gt;
lbry://meet-LBRY\)</span>3</p> lbry://meet-LBRY\)</span>3</p>
<p><strong>Query Params:</strong> extra parameters (reserved for future use)</p> <p><strong>Query Params:</strong> extra parameters (reserved for future use)</p>
<p>lbry://meet-LBRY?arg=value</p> <p>lbry://meet-LBRY?arg=value+arg2=value2</p>
<p>In consequence, the symbols <code>@</code>, <code>#</code>, <code>:</code>, <code>$</code>, <code>?</code>, and <code>/</code> are not allowed in name claims. The full URL schema <p>The full URL grammar is defined below using <a href="https://www.w3.org/TR/2017/REC-xquery-31-20170321/#EBNFNotation">Xquery EBNF notation</a>:</p>
can be defined as a regex:</p>
<pre><code>(?P&lt;uri&gt; <!-- see http://bottlecaps.de/rr/ui for visuals-->
^
(?P&lt;protocol&gt;lbry\:\/\/)? <pre><code>URL ::= Protocol (ChannelAndhModifier '/')? ClaimNameAndModifier Query?
(?P&lt;content_or_channel_name&gt;
(?P&lt;content_name&gt;[^@#$?/:]+) Protocol ::= 'lbry://'
|
(?P&lt;channel_name&gt;\@[^@#$?/:]+) ClaimNameAndModifier ::= ClaimName Modifier?
) ChannelAndModifier ::= Channel Modifier?
(?P&lt;modifier&gt;
(?:\#(?P&lt;claim_id&gt;[0-9a-f]{1,40})) ClaimName ::= Allowed+
| Channel ::= '@' ClaimName
(?:\$(?P&lt;bid_position&gt;\-?[1-9][0-9]*))
| Modifier ::= ClaimID | ClaimSequence | BidPosition
(?:\:(?P&lt;claim_sequence&gt;\-?[1-9][0-9]*)) ClaimID ::= '#' Hex+
)? ClaimSequence ::= ':' Number
(?:\/(?P&lt;path&gt;[^@#$?/:]+))? BidPosition ::= '$' Number
$
) Path ::= '/' Allowed+
Query ::= '?' QueryParameterList
QueryParameterList ::= QueryParameter ( '&amp;' QueryParameterList )*
QueryParameter ::= QueryParameterName ( '=' QueryParameterValue )?
QueryParameterName ::= Allowed+
QueryParameterValue ::= Allowed+
PosDigit ::= [123456789]
Digit ::= '0' | PosDigit
Number ::= PosDigit Digit*
HexAlpha ::= [abcdef]
Hex ::= (Digit | HexAlpha)+
Reserved ::= [=&amp;#:$@?/]
Allowed ::= [^=&amp;#:$@?/]
</code></pre> </code></pre>
<h4 id="design-notes">Design Notes</h4> <h4 id="design-notes">Design Notes</h4>
<p>Most existing public name schemes are first-come, first-serve. This leads to several bad outcomes. When the system is young, users are incentivized to register common names even if they don&rsquo;t intend to use them, in hopes of selling them to the proper owner in the future for an exorbitant price. In a centralized system, the authority may allow for appeals to reassign names based on trademark or other common use reasons. There may also be a process to &ldquo;verify&rdquo; that a name belongs to the entity you think it does (e.g. Twitter&rsquo;s verified accounts). Such processes are often arbitrary, change over time, involve siggnificant transaction costs, and may still lead to names being used in ways that are contrary to user expectation (e.g. <a href="http://nissan.com">nissan.com</a> is not what youd expect).</p> <p>Most existing public name schemes are first-come, first-serve. This leads to several bad outcomes. When the system is young, users are incentivized to register common names even if they don&rsquo;t intend to use them, in hopes of selling them to the proper owner in the future for an exorbitant price. In a centralized system, the authority may allow for appeals to reassign names based on trademark or other common use reasons. There may also be a process to &ldquo;verify&rdquo; that a name belongs to the entity you think it does (e.g. Twitter&rsquo;s verified accounts). Such processes are often arbitrary, change over time, involve significant transaction costs, and may still lead to names being used in ways that are contrary to user expectation (e.g. <a href="http://nissan.com">nissan.com</a> is not what youd expect).</p>
<p>In a decentralized system, such approaches are not possible, so name squatting is especially dangerous (see Namecoin). Instead, LBRY creates an efficient allocation of names via a market. Following <a href="https://en.wikipedia.org/wiki/Coase_theorem">Coase</a>, we believe that if the rules for name ownership and exchange are clearly defined, transaction costs are low, and there is no information asymmetry, then control of URLs will flow to their highest-valued use.</p> <p>In a decentralized system, such approaches are not possible, so name squatting is especially dangerous (see Namecoin). Instead, LBRY creates an efficient allocation of names via a market. Following <a href="https://en.wikipedia.org/wiki/Coase_theorem">Coase</a>, we believe that if the rules for name ownership and exchange are clearly defined, transaction costs are low, and there is no information asymmetry, then control of URLs will flow to their highest-valued use.</p>
<p>Note that only naked LBRY URLs (i.e. URLs with no claim identifiers or sequence numbers included), also sometimes called vanity URLs, have this property. Permanent URLs like <code>lbry://myclaimname#abc</code> exist and are available for the small cost of issuing a <code>create</code> claim transactions.</p> <p>Note that only vanity URLs (i.e. URLs without a ClaimID or or ClaimSequence modifier) have this property. Permanent URLs like <code>lbry://myclaimname#abc</code> exist and are available for the small cost of issuing a <code>create</code> claim transactions.</p>
<h3 id="transactions">Transactions</h3> <h3 id="transactions">Transactions</h3>
@ -346,7 +372,7 @@ can be defined as a regex:</p>
<h4 id="operations-and-opcodes">Operations and Opcodes</h4> <h4 id="operations-and-opcodes">Operations and Opcodes</h4>
<p>To enable [[claim operations]], 3 new opcodes were added to the blockchain scripting language: <code>OP_CLAIM_NAME</code>, <code>OP_SUPPORT_CLAIM</code>, and <code>OP_UPDATE_CLAIM</code> (in Bitcoin they are respectively <code>OP_NOP6</code>, <code>OP_NOP7</code>, and <code>OP_NOP8</code>). Each op code will push a zero on to the execution stack, and will trigger the claimtrie to perform calculations necessary for each bid type. Below are the three supported transactions scripts using these opcodes.</p> <p>To enable <a href="#claim-operations">claim operations</a>, 3 new opcodes were added to the blockchain scripting language: <code>OP_CLAIM_NAME</code>, <code>OP_SUPPORT_CLAIM</code>, and <code>OP_UPDATE_CLAIM</code> (in Bitcoin they are respectively <code>OP_NOP6</code>, <code>OP_NOP7</code>, and <code>OP_NOP8</code>). Each op code will push a zero on to the execution stack, and will trigger the claimtrie to perform calculations necessary for each bid type. Below are the three supported transactions scripts using these opcodes.</p>
<pre><code>OP_CLAIM_NAME &lt;name&gt; &lt;value&gt; OP_2DROP OP_DROP &lt;pubKey&gt; <pre><code>OP_CLAIM_NAME &lt;name&gt; &lt;value&gt; OP_2DROP OP_DROP &lt;pubKey&gt;
OP_UPDATE_CLAIM &lt;name&gt; &lt;claimId&gt; &lt;value&gt; OP_2DROP OP_2DROP &lt;pubKey&gt; OP_UPDATE_CLAIM &lt;name&gt; &lt;claimId&gt; &lt;value&gt; OP_2DROP OP_2DROP &lt;pubKey&gt;

153
index.md
View file

@ -31,9 +31,10 @@ A> For more technical information about LBRY, visit [lbry.tech](https://lbry.tec
(introduction) (introduction)
## Table of Contents ## Table of Contents
<div id="toc">
<!--ts--> <!--ts-->
* [Overview](#overview) * [Overview](#overview)
* [Conventions and Terminology](#conventions-and-terminology) * [Conventions and Terminology](#conventions-and-terminology)
@ -48,7 +49,7 @@ A> For more technical information about LBRY, visit [lbry.tech](https://lbry.tec
* [Controlling](#controlling) * [Controlling](#controlling)
* [Normalization](#normalization) * [Normalization](#normalization)
* [URLs](#urls) * [URLs](#urls)
* [Schema](#schema) * [Components](#components)
* [Design Notes](#design-notes) * [Design Notes](#design-notes)
* [Transactions](#transactions) * [Transactions](#transactions)
* [Operations and Opcodes](#operations-and-opcodes) * [Operations and Opcodes](#operations-and-opcodes)
@ -81,6 +82,8 @@ A> For more technical information about LBRY, visit [lbry.tech](https://lbry.tec
* [Conclusion](#conclusion) * [Conclusion](#conclusion)
<!--te--> <!--te-->
</div>
## Overview ## Overview
@ -99,7 +102,15 @@ The LBRY protocol consists of n discrete parts (sub-protocols?) designed to be u
(Rather than this section, maybe we can use a syntax like brackets around keywords to inline key definitions?) (Rather than this section, maybe we can use a syntax like brackets around keywords to inline key definitions?)
- file
- stream
- blob
- metadata
- hash
- name
- claim
- channel
- url
## Blockchain ## Blockchain
@ -110,17 +121,16 @@ The LBRY blockchain is a public, proof-of-work blockchain. It serves three key p
2. A payment and proof system for priced content 2. A payment and proof system for priced content
3. Trustful publisher identities (fixme: should this even be listed here?) 3. Trustful publisher identities (fixme: should this even be listed here?)
The LBRY blockchain is a fork of the [Bitcoin](https://bitcoin.org/bitcoin.pdf) blockchain, with substantial modifications. This document will not cover or specify any aspects of LBRY that are identical to Bitcoin, and instead focus on the differences. The LBRY blockchain is a fork of the [Bitcoin](https://bitcoin.org/bitcoin.pdf) blockchain, with substantial modifications. This document will not cover or specify any aspects of LBRY that are identical to Bitcoin, and will instead focus on the differences.
### Claims ### Claims
A single metadata entry in the blockchain is called a _[[claim]]_. It consists four primary pieces of information: A single metadata entry in the blockchain is called a `claim`. It records an item that was published to the network or a publisher's identity.
- ID - a unique identifier of this claim Every claim has a globally-unique `claimID`, an `amount` (how many credits were set aside to back the claim), and a `value`. The value may contain metadata about a piece of content, a publisher's public key, or other information. See the [Metadata](#metadata) for more information about what may be stored in the value.
- Name - a [normalized](#normalization) version of the name being claimed, for purposes of creating human readable and memorable [[URLs]]
- Amount - how many credits will be set aside, or staked, to back the claim, which has ramifacations covered in [[URLs]] Every claim is associated with a `name`, which is a bytestring of 0-255 bytes. Every name must be a valid UTF8 string.
- Data - information associated the name (e.g. SD hash, content metadata, etc)
Here is an example claim: Here is an example claim:
@ -141,29 +151,30 @@ Here is an example claim:
} }
``` ```
The value field contains the claim contents, including the source and the [[metadata]].
#### Claim Operations #### Claim Operations
There are four claim operations: *create*, *support*, *update*, and *abandon*. There are four claim operations: `create`, `support`, `update`, and `abandon`.
The *create* operation is used to make a new claim for a name, or to submit a competing claim on an existing name. A `create` operation makes a new claim for a name, or submits a competing claim on an existing name.
A *support* is a claim that adds to the credit total of an existing claim. A support does not have its own claim ID or data. Instead, it has the claim ID of the claim to which its amount will be added. A `support` is a claim that adds to the credit total of an existing claim. A support does not have its own claim ID or data. Instead, it has the claim ID of the claim to which its amount will be added.
An *update* changes the data or the amount stored in an existing claim or support. Updates do not change the claim ID, so an updated claim retains any supports attached to it. An `update` changes the data or the amount stored in an existing claim or support. Updates do not change the claim ID, so an updated claim retains any supports attached to it.
An *abandon* withdraws a claim or support, freeing the associated credits to be used for other purposes. An `abandon` withdraws a claim or support, freeing the associated credits to be used for other purposes.
#### Claimtrie #### Claimtrie
The blockchain adds a parallel [[Merkle tree]] for storing claims. The set of all claims is called the _[[claimtrie]]_. The `claimtrie` is the data structure that LBRY uses to store claims and prove the correctness of name resolution. It is a [Merkle Tree](https://en.wikipedia.org/wiki/Merkle_tree) that maps names to claims. Claims are stored as leaf nodes in the tree. Names are stored as the path from the root node to the leaf node.
The claimtrie is a [Merkle tree]() where the keys are the claimed names and values are claims for that name (sorted in decreasing order by total amount). The root hash of the claimtrie is stored in the block header of each LBRY block, enabling nodes in the LBRY network to efficiently and securely validate the state of the claimtrie without downloading the whole block. The hash of the root node (the `root hash`) is stored in the header of each block in the blockchain. Nodes in the LBRY network use the root hash to efficiently and securely validate the state of the claimtrie.
For more details on the specific claimtrie impelementation, see [fix me](#fixme). Multiple claims can exist for the same name. They are all stored in the leaf node for that name, sorted in decreasing order by the total amount of credits backing each claim.
For more details on the specific claimtrie impelementation, see [the source code](https://github.com/lbryio/lbrycrd/blob/master/src/claimtrie.cpp).
#### Claim States #### Claim States
@ -178,7 +189,7 @@ An accepted claim or support is simply one that has been entered into the blockc
An abandoned claim or support is one that was withdrawn by its creator. It is no longer in contention to control a name. Spending the transaction that contains the claim will also cause the claim to become abandoned. An abandoned claim or support is one that was withdrawn by its creator. It is no longer in contention to control a name. Spending the transaction that contains the claim will also cause the claim to become abandoned.
While data related to abandoned claims technically still resides in the blockchain, it is considered inappropriate (and potentially illegal? #fixme), to use this data to actually fetch the associated content. Wallet servers MUST not return or resolve abandoned claim information. While data related to abandoned claims technically still resides in the blockchain, it is considered inappropriate (and potentially illegal? #fixme), to use this data to fetch the associated content.
##### Active ##### Active
@ -196,47 +207,44 @@ In plain English, the delay before a claim becomes active is equal to the claim
##### Controlling ##### Controlling
The controlling claim is the claim that is returned when a name is resolved (#fixme resolution hasn't been introduced yet. It may be better to define the controlling claim separate from URLs, and then define a vanity URL as returning the controlling claim) . The controlling claim must be active and cannot be a support. The controlling claim is the claim that has the highest total effective amount, which is the sum of its own amount and the amounts of all of its supports. It must be active and cannot itself be a support.
Only one claim can be controlling for a given name at a given block. To determine which claim is controlling for a given name in a given block, the following algorithm is used: Only one claim can be controlling for a given name at a given block. To determine which claim is controlling for a given name at a given block, the following algorithm is used:
1. For each active claim for the name, add up the amount of the claim and the amount of all the active supports for that claim. 1. For each active claim for the name, add up the amount of the claim and the amount of all the active supports for that claim.
1. Determine if a takeover is happening 1. Determine if a takeover is happening
1. If the claim with the greatest total is the controlling claim from the previous block, then nothing changes. That claim is 1. If the claim with the greatest total is the controlling claim from the previous block, then nothing changes. That claim is still controlling at this block.
still controlling at this block.
1. Otherwise, a takeover is occurring. Set the takeover height for this name to the current height, recalculate which claims and supports 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 then perform step 1 again.
are now active, and then perform step 1 again.
1. At this point, the claim with the greatest total is the controlling claim at this block. 1. At this point, the claim with the greatest total is the controlling claim at this block.
The purpose of 2b is to handle the case when multiple competing claims are made on the same name in different blocks, and one of those The purpose of 2b 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 2b will cause this claim to also activate and become the controlling claim.
claims becomes active but another still-inactive claim has the greatest amount. Step 2b will cause this claim to also activate and become
the controlling claim.
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 claims 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.
State: A(10) is controlling <br>State: A(10) is controlling
**Block 1001:** Claim B for 20LBC is accepted. Its activation height is `1001 + min(4032, floor((1001-13) / 32)) = 1001 + 30 = 1031` **Block 1001:** Claim B for 20LBC is accepted. Its activation height is `1001 + min(4032, floor((1001-13) / 32)) = 1001 + 30 = 1031`.
State: A(10) is controlling, B(20) is accepted. <br>State: A(10) is controlling, B(20) is accepted.
**Block 1010:** Support X for 14LBC for claim A is accepted. Since it is a support for the controlling claim, it activates immediately. **Block 1010:** Support X for 14LBC for claim A is accepted. Since it is a support for the controlling claim, it activates immediately.
State: A(10+14) is controlling, B(20) is accepted. <br>State: A(10+14) is controlling, B(20) is accepted.
**Block 1020:** Claim C for 50LBC is accepted. The activation height is `1020 + min(4032, floor((1020-13) / 32)) = 1020 + 31 = 1051` **Block 1020:** Claim C for 50LBC is accepted. The activation height is `1020 + min(4032, floor((1020-13) / 32)) = 1020 + 31 = 1051`.
State: A(10+14) is controlling, B(20) is accepted, C(50) is accepted. <br>State: A(10+14) is controlling, B(20) is accepted, C(50) is accepted.
**Block 1031:** Claim B activates. It has 20LBC, while claim A has 24LBC (10 original + 14 from support X). There is no takeover, and claim A remains controlling. **Block 1031:** Claim B activates. It has 20LBC, while claim A has 24LBC (10 original + 14 from support X). There is no takeover, and claim A remains controlling.
State: A(10+14) is controlling, B(20) is active, C(50) is accepted. <br>State: A(10+14) is controlling, B(20) is active, C(50) is accepted.
**Block 1040:** Claim D for 300LBC is accepted. The activation height is `1040 + min(4032, floor((1040-13) / 32)) = 1040 + 32 = 1072` **Block 1040:** Claim D for 300LBC is accepted. The activation height is `1040 + min(4032, floor((1040-13) / 32)) = 1040 + 32 = 1072`.
State: A(10+14) is controlling, B(20) is active, C(50) is accepted, D(300) is accepted. <br>State: A(10+14) is controlling, B(20) is active, C(50) is accepted, D(300) is accepted.
**Block 1051:** Claim C activates. It has 50LBC, while claim A has 24LBC, so a takeover is initiated. The takeover height for this name is set to 1051, and therefore the activation delay for all the claims becomes `min(4032, floor((1051-1051) / 32)) = 0`. All the claims become active. The totals for each claim are recalculated, and claim D becomes controlling because it has the highest total. **Block 1051:** Claim C activates. It has 50LBC, while claim A has 24LBC, so a takeover is initiated. The takeover height for this name is set to 1051, and therefore the activation delay for all the claims becomes `min(4032, floor((1051-1051) / 32)) = 0`. All the claims become active. The totals for each claim are recalculated, and claim D becomes controlling because it has the highest total.
State: A(10+14) is active, B(20) is active, C(50) is active, D(300) is controlling. <br>State: A(10+14) is active, B(20) is active, C(50) is active, D(300) is controlling.
@ -253,7 +261,7 @@ LBRY has URLs that can be resolved to return claim metadata or directly fetched
The ultimate purpose of much of the claim design, including controlling claims and the claimtrie structure, is to provide human readable URLs that can be trustfully resolved by [[Simple Payment Verification]] wallets. The ultimate purpose of much of the claim design, including controlling claims and the claimtrie structure, is to provide human readable URLs that can be trustfully resolved by [[Simple Payment Verification]] wallets.
#### Schema #### Components
A URL is generally a name with one or more modifiers. A bare name on its own will resolve to the controlling claim at the latest block height, for reasons covered in [[Design Notes]]. The available modifiers are: A URL is generally a name with one or more modifiers. A bare name on its own will resolve to the controlling claim at the latest block height, for reasons covered in [[Design Notes]]. The available modifiers are:
@ -271,7 +279,7 @@ lbry://@lbry/meet-LBRY
**Claim ID:** a claim for this name with this claim ID (does not have to be the controlling claim). Partial prefix matches are allowed. **Claim ID:** a claim for this name with this claim ID (does not have to be the controlling claim). Partial prefix matches are allowed.
lbry://meet-LBRY#7a0aa95c5023c21c098 lbry://meet-LBRY#7a0aa95c5023c21c098<br>
lbry://meet-LBRY#7a lbry://meet-LBRY#7a
**Claim Sequence:** the Nth claim for this name, in the order the claims entered the blockchain. N must be a positive number. This can be used to determine which claim came first, rather than which claim has the most support. **Claim Sequence:** the Nth claim for this name, in the order the claims entered the blockchain. N must be a positive number. This can be used to determine which claim came first, rather than which claim has the most support.
@ -280,45 +288,60 @@ lbry://meet-LBRY:1
**Bid Position:** the Nth claim for this name, in order of most support to least support. N must be a positive number. This is useful for resolving non-winning bids in bid order if you, for example, want to list the top three winning claims in a voting contest or want to ignore the activation delay. **Bid Position:** the Nth claim for this name, in order of most support to least support. N must be a positive number. This is useful for resolving non-winning bids in bid order if you, for example, want to list the top three winning claims in a voting contest or want to ignore the activation delay.
lbry://meet-LBRY$2 lbry://meet-LBRY$2<br>
lbry://meet-LBRY$3 lbry://meet-LBRY$3
**Query Params:** extra parameters (reserved for future use) **Query Params:** extra parameters (reserved for future use)
lbry://meet-LBRY?arg=value lbry://meet-LBRY?arg=value+arg2=value2
In consequence, the symbols `@`, `#`, `:`, `$`, `?`, and `/` are not allowed in name claims. The full URL schema The full URL grammar is defined below using [Xquery EBNF notation](https://www.w3.org/TR/2017/REC-xquery-31-20170321/#EBNFNotation):
can be defined as a regex:
<!-- see http://bottlecaps.de/rr/ui for visuals-->
``` ```
(?P<uri> URL ::= Protocol (ChannelAndhModifier '/')? ClaimNameAndModifier Query?
^
(?P<protocol>lbry\:\/\/)? Protocol ::= 'lbry://'
(?P<content_or_channel_name>
(?P<content_name>[^@#$?/:]+) ClaimNameAndModifier ::= ClaimName Modifier?
| ChannelAndModifier ::= Channel Modifier?
(?P<channel_name>\@[^@#$?/:]+)
) ClaimName ::= Allowed+
(?P<modifier> Channel ::= '@' ClaimName
(?:\#(?P<claim_id>[0-9a-f]{1,40}))
| Modifier ::= ClaimID | ClaimSequence | BidPosition
(?:\$(?P<bid_position>\-?[1-9][0-9]*)) ClaimID ::= '#' Hex+
| ClaimSequence ::= ':' Number
(?:\:(?P<claim_sequence>\-?[1-9][0-9]*)) BidPosition ::= '$' Number
)?
(?:\/(?P<path>[^@#$?/:]+))? Path ::= '/' Allowed+
$
) Query ::= '?' QueryParameterList
QueryParameterList ::= QueryParameter ( '&' QueryParameterList )*
QueryParameter ::= QueryParameterName ( '=' QueryParameterValue )?
QueryParameterName ::= Allowed+
QueryParameterValue ::= Allowed+
PosDigit ::= [123456789]
Digit ::= '0' | PosDigit
Number ::= PosDigit Digit*
HexAlpha ::= [abcdef]
Hex ::= (Digit | HexAlpha)+
Reserved ::= [=&#:$@?/]
Allowed ::= [^=&#:$@?/]
``` ```
#### Design Notes #### Design Notes
Most existing public name schemes are first-come, first-serve. This leads to several bad outcomes. When the system is young, users 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. In a centralized system, the authority may allow for appeals to reassign names based on trademark or other common use reasons. There may also be a process to "verify" that a name belongs to the entity you think it does (e.g. Twitter's verified accounts). Such processes are often arbitrary, change over time, involve siggnificant transaction costs, and may still lead to names being used in ways that are contrary to user expectation (e.g. [nissan.com](http://nissan.com) is not what youd expect). Most existing public name schemes are first-come, first-serve. This leads to several bad outcomes. When the system is young, users 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. In a centralized system, the authority may allow for appeals to reassign names based on trademark or other common use reasons. There may also be a process to "verify" that a name belongs to the entity you think it does (e.g. Twitter's verified accounts). Such processes are often arbitrary, change over time, involve significant transaction costs, and may still lead to names being used in ways that are contrary to user expectation (e.g. [nissan.com](http://nissan.com) is not what youd expect).
In a decentralized system, such approaches are not possible, so name squatting is especially dangerous (see Namecoin). Instead, LBRY creates an efficient allocation of names via a market. Following [Coase](https://en.wikipedia.org/wiki/Coase_theorem), we believe that if the rules for name ownership and exchange are clearly defined, transaction costs are low, and there is no information asymmetry, then control of URLs will flow to their highest-valued use. In a decentralized system, such approaches are not possible, so name squatting is especially dangerous (see Namecoin). Instead, LBRY creates an efficient allocation of names via a market. Following [Coase](https://en.wikipedia.org/wiki/Coase_theorem), we believe that if the rules for name ownership and exchange are clearly defined, transaction costs are low, and there is no information asymmetry, then control of URLs will flow to their highest-valued use.
Note that only naked LBRY URLs (i.e. URLs with no claim identifiers or sequence numbers included), also sometimes called vanity URLs, have this property. Permanent URLs like `lbry://myclaimname#abc` exist and are available for the small cost of issuing a `create` claim transactions. Note that only vanity URLs (i.e. URLs without a ClaimID or or ClaimSequence modifier) have this property. Permanent URLs like `lbry://myclaimname#abc` exist and are available for the small cost of issuing a `create` claim transactions.
@ -329,7 +352,7 @@ To support claims, the LBRY blockchain adds or modifies behavior related to tran
#### Operations and Opcodes #### Operations and Opcodes
To enable [[claim operations]], 3 new opcodes were added to the blockchain scripting language: `OP_CLAIM_NAME`, `OP_SUPPORT_CLAIM`, and `OP_UPDATE_CLAIM` (in Bitcoin they are respectively `OP_NOP6`, `OP_NOP7`, and `OP_NOP8`). Each op code will push a zero on to the execution stack, and will trigger the claimtrie to perform calculations necessary for each bid type. Below are the three supported transactions scripts using these opcodes. To enable [claim operations](#claim-operations), 3 new opcodes were added to the blockchain scripting language: `OP_CLAIM_NAME`, `OP_SUPPORT_CLAIM`, and `OP_UPDATE_CLAIM` (in Bitcoin they are respectively `OP_NOP6`, `OP_NOP7`, and `OP_NOP8`). Each op code will push a zero on to the execution stack, and will trigger the claimtrie to perform calculations necessary for each bid type. Below are the three supported transactions scripts using these opcodes.
``` ```
OP_CLAIM_NAME <name> <value> OP_2DROP OP_DROP <pubKey> OP_CLAIM_NAME <name> <value> OP_2DROP OP_DROP <pubKey>

341
normalize.css vendored Normal file
View file

@ -0,0 +1,341 @@
/*! normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}

View file

@ -2,15 +2,47 @@ body {
margin: 40px auto; margin: 40px auto;
max-width: 800px; max-width: 800px;
font-size: 18px; font-size: 18px;
line-height: 1.6; line-height: 1.5;
padding: 0 10px; padding: 0 10px;
color: #333; color: #333;
font-family: Constantia, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "DejaVu Serif," "Bitstream Vera Serif", "Liberation Serif", Georgia, serif; font-family: Constantia, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "DejaVu Serif," "Bitstream Vera Serif", "Liberation Serif", Georgia, serif;
/* font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif; */ /* font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif; */
} }
h1, h2, h3 { p {
line-height: 1.2 margin-bottom: 1.4rem;
}
h1, h2, h3, h4, h5 {
line-height: 1.2;
}
h1 {
margin-top: 0;
font-size: 4rem;
}
h2 {
font-size: 3rem;
}
h3 {
font-size: 2.25rem;
}
h4 {
font-size: 1.5rem;
margin-bottom: 1.5rem;
}
h5 {
font-size: 1.25rem;
font-variant: small-caps;
margin-bottom: 1rem;
}
small {
font-size: 0.8rem;
} }
a { a {
@ -20,10 +52,12 @@ a {
code { code {
/* font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; */ /* font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; */
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 0.9rem;
color: #B10DC9;
} }
pre { pre {
display: block; display: block;
font-size: 14px;
color: #212529; color: #212529;
overflow: auto; overflow: auto;
margin-top: 0; margin-top: 0;
@ -40,9 +74,23 @@ aside {
background-color: #d1ecf1; background-color: #d1ecf1;
border-color: #bee5eb; border-color: #bee5eb;
} }
aside p:first-child { aside p:first-child {
margin-top: 0; margin-top: 0;
} }
aside p:last-child { aside p:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
ol ol {
list-style-type: lower-alpha;
}
#toc ul {
list-style-type: none;
}
#toc>ul {
padding-left: 0;
}