mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
deploy: 1f417764c3
This commit is contained in:
@@ -330,7 +330,7 @@ Friends</h2></td></tr>
|
|||||||
<tr class="separator:aff78bcfb98b735a41d082871e735ccc7"><td class="memSeparator" colspan="2"> </td></tr>
|
<tr class="separator:aff78bcfb98b735a41d082871e735ccc7"><td class="memSeparator" colspan="2"> </td></tr>
|
||||||
</table>
|
</table>
|
||||||
<a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2>
|
<a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2>
|
||||||
<div class="textblock"><h2><a class="anchor" id="autotoc_md135"></a>
|
<div class="textblock"><h2><a class="anchor" id="autotoc_md137"></a>
|
||||||
Trusted Validators List</h2>
|
Trusted Validators List</h2>
|
||||||
<p>Rippled accepts ledger proposals and validations from trusted validator nodes. A ledger is considered fully-validated once the number of received trusted validations for a ledger meets or exceeds a quorum value.</p>
|
<p>Rippled accepts ledger proposals and validations from trusted validator nodes. A ledger is considered fully-validated once the number of received trusted validations for a ledger meets or exceeds a quorum value.</p>
|
||||||
<p>This class manages the set of validation public keys the local rippled node trusts. The list of trusted keys is populated using the keys listed in the configuration file as well as lists signed by trusted publishers. The trusted publisher public keys are specified in the config.</p>
|
<p>This class manages the set of validation public keys the local rippled node trusts. The list of trusted keys is populated using the keys listed in the configuration file as well as lists signed by trusted publishers. The trusted publisher public keys are specified in the config.</p>
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ Friends</h2></td></tr>
|
|||||||
<tr class="separator:a13d17a86ad8d1ecdf3e4d2b99c51c03c"><td class="memSeparator" colspan="2"> </td></tr>
|
<tr class="separator:a13d17a86ad8d1ecdf3e4d2b99c51c03c"><td class="memSeparator" colspan="2"> </td></tr>
|
||||||
</table>
|
</table>
|
||||||
<a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2>
|
<a name="details" id="details"></a><h2 class="groupheader">Detailed Description</h2>
|
||||||
<div class="textblock"><h2><a class="anchor" id="autotoc_md136"></a>
|
<div class="textblock"><h2><a class="anchor" id="autotoc_md138"></a>
|
||||||
Validator Sites</h2>
|
Validator Sites</h2>
|
||||||
<p>This class manages the set of configured remote sites used to fetch the latest published recommended validator lists.</p>
|
<p>This class manages the set of configured remote sites used to fetch the latest published recommended validator lists.</p>
|
||||||
<p>Lists are fetched at a regular interval. Fetched lists are expected to be in JSON format and contain the following fields:</p>
|
<p>Lists are fetched at a regular interval. Fetched lists are expected to be in JSON format and contain the following fields:</p>
|
||||||
|
|||||||
@@ -66,19 +66,19 @@ $(function() {
|
|||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><p>The <a href="https://xrpl.org/">XRP Ledger</a> is a decentralized cryptographic ledger powered by a network of peer-to-peer nodes. The XRP Ledger uses a novel Byzantine Fault Tolerant consensus algorithm to settle and record transactions in a secure distributed database without a central operator.</p>
|
<div class="textblock"><p>The <a href="https://xrpl.org/">XRP Ledger</a> is a decentralized cryptographic ledger powered by a network of peer-to-peer nodes. The XRP Ledger uses a novel Byzantine Fault Tolerant consensus algorithm to settle and record transactions in a secure distributed database without a central operator.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md311"></a>
|
<h1><a class="anchor" id="autotoc_md313"></a>
|
||||||
XRP</h1>
|
XRP</h1>
|
||||||
<p><a href="https://xrpl.org/xrp.html">XRP</a> is a public, counterparty-free asset native to the XRP Ledger, and is designed to bridge the many different currencies in use worldwide. XRP is traded on the open-market and is available for anyone to access. The XRP Ledger was created in 2012 with a finite supply of 100 billion units of XRP.</p>
|
<p><a href="https://xrpl.org/xrp.html">XRP</a> is a public, counterparty-free asset native to the XRP Ledger, and is designed to bridge the many different currencies in use worldwide. XRP is traded on the open-market and is available for anyone to access. The XRP Ledger was created in 2012 with a finite supply of 100 billion units of XRP.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md312"></a>
|
<h1><a class="anchor" id="autotoc_md314"></a>
|
||||||
rippled</h1>
|
rippled</h1>
|
||||||
<p>The server software that powers the XRP Ledger is called <code>rippled</code> and is available in this repository under the permissive ISC open-source license. The <code>rippled</code> server software is written primarily in C++ and runs on a variety of platforms. The <code>rippled</code> server software can run in several modes depending on its <a href="https://xrpl.org/rippled-server-modes.html">configuration</a>. <br />
|
<p>The server software that powers the XRP Ledger is called <code>rippled</code> and is available in this repository under the permissive ISC open-source license. The <code>rippled</code> server software is written primarily in C++ and runs on a variety of platforms. The <code>rippled</code> server software can run in several modes depending on its <a href="https://xrpl.org/rippled-server-modes.html">configuration</a>. <br />
|
||||||
</p>
|
</p>
|
||||||
<h2><a class="anchor" id="autotoc_md313"></a>
|
<h2><a class="anchor" id="autotoc_md315"></a>
|
||||||
Build from Source</h2>
|
Build from Source</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Read the build instructions in `BUILD.md`</li>
|
<li>Read the build instructions in `BUILD.md`</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h1><a class="anchor" id="autotoc_md314"></a>
|
<h1><a class="anchor" id="autotoc_md316"></a>
|
||||||
Key Features of the XRP Ledger</h1>
|
Key Features of the XRP Ledger</h1>
|
||||||
<ul>
|
<ul>
|
||||||
<li><b><a href="https://xrpl.org/xrp-ledger-overview.html#censorship-resistant-transaction-processing">Censorship-Resistant Transaction Processing</a>:</b> No single party decides which transactions succeed or fail, and no one can "roll back" a transaction after it completes. As long as those who choose to participate in the network keep it healthy, they can settle transactions in seconds.</li>
|
<li><b><a href="https://xrpl.org/xrp-ledger-overview.html#censorship-resistant-transaction-processing">Censorship-Resistant Transaction Processing</a>:</b> No single party decides which transactions succeed or fail, and no one can "roll back" a transaction after it completes. As long as those who choose to participate in the network keep it healthy, they can settle transactions in seconds.</li>
|
||||||
@@ -89,7 +89,7 @@ Key Features of the XRP Ledger</h1>
|
|||||||
<li><b><a href="https://xrpl.org/xrp-ledger-overview.html#modern-features-for-smart-contracts">Modern Features for Smart Contracts</a>:</b> Features like Escrow, Checks, and Payment Channels support cutting-edge financial applications including the <a href="https://interledger.org/">Interledger Protocol</a>. This toolbox of advanced features comes with safety features like a process for amending the network and separate checks against invariant constraints.</li>
|
<li><b><a href="https://xrpl.org/xrp-ledger-overview.html#modern-features-for-smart-contracts">Modern Features for Smart Contracts</a>:</b> Features like Escrow, Checks, and Payment Channels support cutting-edge financial applications including the <a href="https://interledger.org/">Interledger Protocol</a>. This toolbox of advanced features comes with safety features like a process for amending the network and separate checks against invariant constraints.</li>
|
||||||
<li><b><a href="https://xrpl.org/xrp-ledger-overview.html#on-ledger-decentralized-exchange">On-Ledger Decentralized Exchange</a>:</b> In addition to all the features that make XRP useful on its own, the XRP Ledger also has a fully-functional accounting system for tracking and trading obligations denominated in any way users want, and an exchange built into the protocol. The XRP Ledger can settle long, cross-currency payment paths and exchanges of multiple currencies in atomic transactions, bridging gaps of trust with XRP.</li>
|
<li><b><a href="https://xrpl.org/xrp-ledger-overview.html#on-ledger-decentralized-exchange">On-Ledger Decentralized Exchange</a>:</b> In addition to all the features that make XRP useful on its own, the XRP Ledger also has a fully-functional accounting system for tracking and trading obligations denominated in any way users want, and an exchange built into the protocol. The XRP Ledger can settle long, cross-currency payment paths and exchanges of multiple currencies in atomic transactions, bridging gaps of trust with XRP.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h1><a class="anchor" id="autotoc_md315"></a>
|
<h1><a class="anchor" id="autotoc_md317"></a>
|
||||||
Source Code</h1>
|
Source Code</h1>
|
||||||
<p>Here are some good places to start learning the source code:</p>
|
<p>Here are some good places to start learning the source code:</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -97,7 +97,7 @@ Source Code</h1>
|
|||||||
<li>Read <a href="./Builds/levelization">the levelization document</a> to get an idea of the internal dependency graph.</li>
|
<li>Read <a href="./Builds/levelization">the levelization document</a> to get an idea of the internal dependency graph.</li>
|
||||||
<li>In the big picture, the <code>main</code> function constructs an <code>ApplicationImp</code> object, which implements the <code>Application</code> virtual interface. Almost every component in the application takes an <code>Application&</code> parameter in its constructor, typically named <code>app</code> and stored as a member variable <code>app_</code>. This allows most components to depend on any other component.</li>
|
<li>In the big picture, the <code>main</code> function constructs an <code>ApplicationImp</code> object, which implements the <code>Application</code> virtual interface. Almost every component in the application takes an <code>Application&</code> parameter in its constructor, typically named <code>app</code> and stored as a member variable <code>app_</code>. This allows most components to depend on any other component.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h2><a class="anchor" id="autotoc_md316"></a>
|
<h2><a class="anchor" id="autotoc_md318"></a>
|
||||||
Repository Contents</h2>
|
Repository Contents</h2>
|
||||||
<table class="markdownTable">
|
<table class="markdownTable">
|
||||||
<tr class="markdownTableHead">
|
<tr class="markdownTableHead">
|
||||||
@@ -114,7 +114,7 @@ Repository Contents</h2>
|
|||||||
<td class="markdownTableBodyLeft"><code>./src</code> </td><td class="markdownTableBodyLeft">Source code. </td></tr>
|
<td class="markdownTableBodyLeft"><code>./src</code> </td><td class="markdownTableBodyLeft">Source code. </td></tr>
|
||||||
</table>
|
</table>
|
||||||
<p>Some of the directories under <code>src</code> are external repositories included using git-subtree. See those directories' README files for more details.</p>
|
<p>Some of the directories under <code>src</code> are external repositories included using git-subtree. See those directories' README files for more details.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md317"></a>
|
<h1><a class="anchor" id="autotoc_md319"></a>
|
||||||
See Also</h1>
|
See Also</h1>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="https://xrpl.org/">XRP Ledger Dev Portal</a></li>
|
<li><a href="https://xrpl.org/">XRP Ledger Dev Portal</a></li>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -65,7 +65,7 @@ $(function() {
|
|||||||
<div class="title">Code Style Cheat Sheet </div> </div>
|
<div class="title">Code Style Cheat Sheet </div> </div>
|
||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><h1><a class="anchor" id="autotoc_md40"></a>
|
<div class="textblock"><h1><a class="anchor" id="autotoc_md42"></a>
|
||||||
Form</h1>
|
Form</h1>
|
||||||
<ul>
|
<ul>
|
||||||
<li>One class per header file.</li>
|
<li>One class per header file.</li>
|
||||||
@@ -79,7 +79,7 @@ Form</h1>
|
|||||||
<li>Order class declarations as types, public, protected, private, then data.</li>
|
<li>Order class declarations as types, public, protected, private, then data.</li>
|
||||||
<li>Prefer 'private' over 'protected'</li>
|
<li>Prefer 'private' over 'protected'</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h1><a class="anchor" id="autotoc_md41"></a>
|
<h1><a class="anchor" id="autotoc_md43"></a>
|
||||||
Function</h1>
|
Function</h1>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Minimize external dependencies<ul>
|
<li>Minimize external dependencies<ul>
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ $(function() {
|
|||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><p>Coding standards used here gradually evolve and propagate through code reviews. Some aspects are enforced more strictly than others.</p>
|
<div class="textblock"><p>Coding standards used here gradually evolve and propagate through code reviews. Some aspects are enforced more strictly than others.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md43"></a>
|
<h1><a class="anchor" id="autotoc_md45"></a>
|
||||||
Rules</h1>
|
Rules</h1>
|
||||||
<p>These rules only apply to our own code. We can't enforce any sort of style on the external repositories and libraries we include. The best guideline is to maintain the standards that are used in those libraries.</p>
|
<p>These rules only apply to our own code. We can't enforce any sort of style on the external repositories and libraries we include. The best guideline is to maintain the standards that are used in those libraries.</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -75,7 +75,7 @@ Rules</h1>
|
|||||||
<li>Modern C++ principles. No naked <code>new</code> or <code>delete</code>.</li>
|
<li>Modern C++ principles. No naked <code>new</code> or <code>delete</code>.</li>
|
||||||
<li>Line lengths limited to 80 characters. Exceptions limited to data and tables.</li>
|
<li>Line lengths limited to 80 characters. Exceptions limited to data and tables.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h1><a class="anchor" id="autotoc_md44"></a>
|
<h1><a class="anchor" id="autotoc_md46"></a>
|
||||||
Guidelines</h1>
|
Guidelines</h1>
|
||||||
<p>If you want to do something contrary to these guidelines, understand why you're doing it. Think, use common sense, and consider that this your changes will probably need to be maintained long after you've moved on to other projects.</p>
|
<p>If you want to do something contrary to these guidelines, understand why you're doing it. Think, use common sense, and consider that this your changes will probably need to be maintained long after you've moved on to other projects.</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -91,7 +91,7 @@ Guidelines</h1>
|
|||||||
</li>
|
</li>
|
||||||
<li>Don't over-inline by defining large functions within the class declaration, not even for template classes.</li>
|
<li>Don't over-inline by defining large functions within the class declaration, not even for template classes.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h1><a class="anchor" id="autotoc_md45"></a>
|
<h1><a class="anchor" id="autotoc_md47"></a>
|
||||||
Formatting</h1>
|
Formatting</h1>
|
||||||
<p>The goal of source code formatting should always be to make things as easy to read as possible. White space is used to guide the eye so that details are not overlooked. Blank lines are used to separate code into "paragraphs."</p>
|
<p>The goal of source code formatting should always be to make things as easy to read as possible. White space is used to guide the eye so that details are not overlooked. Blank lines are used to separate code into "paragraphs."</p>
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
@@ -68,12 +68,12 @@ $(function() {
|
|||||||
<div class="textblock"><p>The jemalloc library provides a good API for doing heap analysis, including a mechanism to dump a description of the heap from within the running application via a function call. Details on how to perform this activity in general, as well as how to acquire the software, are available on the jemalloc site: <a href="https://github.com/jemalloc/jemalloc/wiki/Use-Case:-Heap-Profiling">https://github.com/jemalloc/jemalloc/wiki/Use-Case:-Heap-Profiling</a></p>
|
<div class="textblock"><p>The jemalloc library provides a good API for doing heap analysis, including a mechanism to dump a description of the heap from within the running application via a function call. Details on how to perform this activity in general, as well as how to acquire the software, are available on the jemalloc site: <a href="https://github.com/jemalloc/jemalloc/wiki/Use-Case:-Heap-Profiling">https://github.com/jemalloc/jemalloc/wiki/Use-Case:-Heap-Profiling</a></p>
|
||||||
<p>jemalloc is acquired separately from rippled, and is not affiliated with Ripple Labs. If you compile and install jemalloc from the source release with default options, it will install the library and header under <code>/usr/local/lib</code> and <code>/usr/local/include</code>, respectively. Heap profiling has been tested with rippled on a Linux platform. It should work on platforms on which both rippled and jemalloc are available.</p>
|
<p>jemalloc is acquired separately from rippled, and is not affiliated with Ripple Labs. If you compile and install jemalloc from the source release with default options, it will install the library and header under <code>/usr/local/lib</code> and <code>/usr/local/include</code>, respectively. Heap profiling has been tested with rippled on a Linux platform. It should work on platforms on which both rippled and jemalloc are available.</p>
|
||||||
<p>To link rippled with jemalloc, the argument <code>profile-jemalloc=<jemalloc_dir></code> is provided after the optional target. The <code><jemalloc_dir></code> argument should be the same as that of the <code>--prefix</code> parameter passed to the jemalloc configure script when building.</p>
|
<p>To link rippled with jemalloc, the argument <code>profile-jemalloc=<jemalloc_dir></code> is provided after the optional target. The <code><jemalloc_dir></code> argument should be the same as that of the <code>--prefix</code> parameter passed to the jemalloc configure script when building.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md70"></a>
|
<h1><a class="anchor" id="autotoc_md72"></a>
|
||||||
Examples:</h1>
|
Examples:</h1>
|
||||||
<p>Build rippled with jemalloc library under /usr/local/lib and header under /usr/local/include: </p><pre class="fragment">$ scons profile-jemalloc=/usr/local
|
<p>Build rippled with jemalloc library under /usr/local/lib and header under /usr/local/include: </p><pre class="fragment">$ scons profile-jemalloc=/usr/local
|
||||||
</pre><p>Build rippled using clang with the jemalloc library under /opt/local/lib and header under /opt/local/include: </p><pre class="fragment">$ scons clang profile-jemalloc=/opt/local
|
</pre><p>Build rippled using clang with the jemalloc library under /opt/local/lib and header under /opt/local/include: </p><pre class="fragment">$ scons clang profile-jemalloc=/opt/local
|
||||||
</pre><hr />
|
</pre><hr />
|
||||||
<h1><a class="anchor" id="autotoc_md71"></a>
|
<h1><a class="anchor" id="autotoc_md73"></a>
|
||||||
Using the jemalloc library from within the code</h1>
|
Using the jemalloc library from within the code</h1>
|
||||||
<p>The <code>profile-jemalloc</code> parameter enables a macro definition called <code>PROFILE_JEMALLOC</code>. Include the jemalloc header file as well as the api call(s) that you wish to make within preprocessor conditional groups, such as:</p>
|
<p>The <code>profile-jemalloc</code> parameter enables a macro definition called <code>PROFILE_JEMALLOC</code>. Include the jemalloc header file as well as the api call(s) that you wish to make within preprocessor conditional groups, such as:</p>
|
||||||
<p>In global scope: </p><pre class="fragment">#ifdef PROFILE_JEMALLOC
|
<p>In global scope: </p><pre class="fragment">#ifdef PROFILE_JEMALLOC
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ $(function() {
|
|||||||
<div class="title">Building documentation </div> </div>
|
<div class="title">Building documentation </div> </div>
|
||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><h1><a class="anchor" id="autotoc_md73"></a>
|
<div class="textblock"><h1><a class="anchor" id="autotoc_md75"></a>
|
||||||
Dependencies</h1>
|
Dependencies</h1>
|
||||||
<p>Install these dependencies:</p>
|
<p>Install these dependencies:</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -88,7 +88,7 @@ Dependencies</h1>
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h1><a class="anchor" id="autotoc_md74"></a>
|
<h1><a class="anchor" id="autotoc_md76"></a>
|
||||||
Docker</h1>
|
Docker</h1>
|
||||||
<p>Instead of installing the above dependencies locally, you can use the official build environment Docker image, which has all of them installed already.</p>
|
<p>Instead of installing the above dependencies locally, you can use the official build environment Docker image, which has all of them installed already.</p>
|
||||||
<ol type="1">
|
<ol type="1">
|
||||||
@@ -98,7 +98,7 @@ Docker</h1>
|
|||||||
<li>Run the image from the project folder: <div class="fragment"><div class="line">sudo docker run -v $PWD:/opt/rippled --rm rippleci/rippled-ci-builder:2944b78d22db</div>
|
<li>Run the image from the project folder: <div class="fragment"><div class="line">sudo docker run -v $PWD:/opt/rippled --rm rippleci/rippled-ci-builder:2944b78d22db</div>
|
||||||
</div><!-- fragment --></li>
|
</div><!-- fragment --></li>
|
||||||
</ol>
|
</ol>
|
||||||
<h1><a class="anchor" id="autotoc_md75"></a>
|
<h1><a class="anchor" id="autotoc_md77"></a>
|
||||||
Build</h1>
|
Build</h1>
|
||||||
<p>There is a <code>docs</code> target in the CMake configuration.</p>
|
<p>There is a <code>docs</code> target in the CMake configuration.</p>
|
||||||
<div class="fragment"><div class="line">mkdir build</div>
|
<div class="fragment"><div class="line">mkdir build</div>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
|
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
|
||||||
<meta name="generator" content="Doxygen 1.8.17"/>
|
<meta name="generator" content="Doxygen 1.8.17"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
<title>rippled: From source</title>
|
<title>rippled: install</title>
|
||||||
<link href="tabs.css" rel="stylesheet" type="text/css"/>
|
<link href="tabs.css" rel="stylesheet" type="text/css"/>
|
||||||
<script type="text/javascript" src="jquery.js"></script>
|
<script type="text/javascript" src="jquery.js"></script>
|
||||||
<script type="text/javascript" src="dynsections.js"></script>
|
<script type="text/javascript" src="dynsections.js"></script>
|
||||||
@@ -62,12 +62,107 @@ $(function() {
|
|||||||
</div><!-- top -->
|
</div><!-- top -->
|
||||||
<div class="PageDoc"><div class="header">
|
<div class="PageDoc"><div class="header">
|
||||||
<div class="headertitle">
|
<div class="headertitle">
|
||||||
<div class="title">From source </div> </div>
|
<div class="title">install </div> </div>
|
||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><p>From a source build, you can install rippled and libxrpl using CMake's <code>--install</code> mode:</p>
|
<div class="textblock"><p>This document contains instructions for installing rippled. The APT package manager is common on Debian-based Linux distributions like Ubuntu, while the YUM package manager is common on Red Hat-based Linux distributions like CentOS. Installing from source is an option for all platforms, and the only supported option for installing custom builds.</p>
|
||||||
|
<h2><a class="anchor" id="autotoc_md38"></a>
|
||||||
|
From source</h2>
|
||||||
|
<p>From a source build, you can install rippled and libxrpl using CMake's <code>--install</code> mode:</p>
|
||||||
<div class="fragment"><div class="line">cmake --install . --prefix /opt/local</div>
|
<div class="fragment"><div class="line">cmake --install . --prefix /opt/local</div>
|
||||||
</div><!-- fragment --><p>The default <a href="https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html">prefix</a> is typically <code>/usr/local</code> on Linux and macOS and <code>C:/Program Files/rippled</code> on Windows. </p>
|
</div><!-- fragment --><p>The default <a href="https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html">prefix</a> is typically <code>/usr/local</code> on Linux and macOS and <code>C:/Program Files/rippled</code> on Windows.</p>
|
||||||
|
<h2><a class="anchor" id="autotoc_md39"></a>
|
||||||
|
With the APT package manager</h2>
|
||||||
|
<ol type="1">
|
||||||
|
<li>Update repositories: <pre class="fragment"> sudo apt update -y
|
||||||
|
</pre></li>
|
||||||
|
<li>Install utilities: <pre class="fragment"> sudo apt install -y apt-transport-https ca-certificates wget gnupg
|
||||||
|
</pre></li>
|
||||||
|
<li>Add Ripple's package-signing GPG key to your list of trusted keys: <pre class="fragment"> sudo mkdir /usr/local/share/keyrings/
|
||||||
|
wget -q -O - "https://repos.ripple.com/repos/api/gpg/key/public" | gpg --dearmor > ripple-key.gpg
|
||||||
|
sudo mv ripple-key.gpg /usr/local/share/keyrings
|
||||||
|
</pre></li>
|
||||||
|
<li><p class="startli">Check the fingerprint of the newly-added key: </p><pre class="fragment"> gpg /usr/local/share/keyrings/ripple-key.gpg
|
||||||
|
</pre><p class="startli">The output should include an entry for Ripple such as the following: </p><pre class="fragment">gpg: WARNING: no command supplied. Trying to guess what you mean ...
|
||||||
|
pub rsa3072 2019-02-14 [SC] [expires: 2026-02-17]
|
||||||
|
C0010EC205B35A3310DC90DE395F97FFCCAFD9A2
|
||||||
|
uid TechOps Team at Ripple <techops+rippled@ripple.com>
|
||||||
|
sub rsa3072 2019-02-14 [E] [expires: 2026-02-17]
|
||||||
|
</pre><p class="startli">In particular, make sure that the fingerprint matches. (In the above example, the fingerprint is on the third line, starting with <code>C001</code>.)</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<ol type="1">
|
||||||
|
<li><p class="startli">Add the appropriate Ripple repository for your operating system version: </p><pre class="fragment"> echo "deb [signed-by=/usr/local/share/keyrings/ripple-key.gpg] https://repos.ripple.com/repos/rippled-deb focal stable" | \
|
||||||
|
sudo tee -a /etc/apt/sources.list.d/ripple.list
|
||||||
|
</pre><p class="startli">The above example is appropriate for <b>Ubuntu 20.04 Focal Fossa</b>. For other operating systems, replace the word <code>focal</code> with one of the following:</p><ul>
|
||||||
|
<li><code>jammy</code> for <b>Ubuntu 22.04 Jammy Jellyfish</b></li>
|
||||||
|
<li><code>bionic</code> for <b>Ubuntu 18.04 Bionic Beaver</b></li>
|
||||||
|
<li><code>bullseye</code> for <b>Debian 11 Bullseye</b></li>
|
||||||
|
<li><code>buster</code> for <b>Debian 10 Buster</b></li>
|
||||||
|
</ul>
|
||||||
|
<p class="startli">If you want access to development or pre-release versions of <code>rippled</code>, use one of the following instead of <code>stable</code>:</p><ul>
|
||||||
|
<li><code>unstable</code> - Pre-release builds (<a href="https://github.com/ripple/rippled/tree/release"><code>release</code> branch</a>)</li>
|
||||||
|
<li><code>nightly</code> - Experimental/development builds (<a href="https://github.com/ripple/rippled/tree/develop"><code>develop</code> branch</a>)</li>
|
||||||
|
</ul>
|
||||||
|
<p class="startli"><b>Warning:</b> Unstable and nightly builds may be broken at any time. Do not use these builds for production servers.</p>
|
||||||
|
</li>
|
||||||
|
<li>Fetch the Ripple repository. <pre class="fragment"> sudo apt -y update
|
||||||
|
</pre></li>
|
||||||
|
<li>Install the <code>rippled</code> software package: <pre class="fragment"> sudo apt -y install rippled
|
||||||
|
</pre></li>
|
||||||
|
<li><p class="startli">Check the status of the <code>rippled</code> service: </p><pre class="fragment"> systemctl status rippled.service
|
||||||
|
</pre><p class="startli">The <code>rippled</code> service should start automatically. If not, you can start it manually: </p><pre class="fragment">sudo systemctl start rippled.service
|
||||||
|
</pre></li>
|
||||||
|
<li><p class="startli">Optional: allow <code>rippled</code> to bind to privileged ports.</p>
|
||||||
|
<p class="startli">This allows you to serve incoming API requests on port 80 or 443. (If you want to do so, you must also update the config file's port settings.) </p><pre class="fragment">sudo setcap 'cap_net_bind_service=+ep' /opt/ripple/bin/rippled
|
||||||
|
</pre></li>
|
||||||
|
</ol>
|
||||||
|
<h2><a class="anchor" id="autotoc_md40"></a>
|
||||||
|
With the YUM package manager</h2>
|
||||||
|
<ol type="1">
|
||||||
|
<li><p class="startli">Install the Ripple RPM repository:</p>
|
||||||
|
<p class="startli">Choose the appropriate RPM repository for the stability of releases you want:</p><ul>
|
||||||
|
<li><code>stable</code> for the latest production release (<code>master</code> branch)</li>
|
||||||
|
<li><code>unstable</code> for pre-release builds (<code>release</code> branch)</li>
|
||||||
|
<li><code>nightly</code> for experimental/development builds (<code>develop</code> branch)</li>
|
||||||
|
</ul>
|
||||||
|
<p class="startli"><em>Stable</em> </p><pre class="fragment">cat << REPOFILE | sudo tee /etc/yum.repos.d/ripple.repo
|
||||||
|
[ripple-stable]
|
||||||
|
name=XRP Ledger Packages
|
||||||
|
enabled=1
|
||||||
|
gpgcheck=0
|
||||||
|
repo_gpgcheck=1
|
||||||
|
baseurl=https://repos.ripple.com/repos/rippled-rpm/stable/
|
||||||
|
gpgkey=https://repos.ripple.com/repos/rippled-rpm/stable/repodata/repomd.xml.key
|
||||||
|
REPOFILE
|
||||||
|
</pre><p class="startli"><em>Unstable</em> </p><pre class="fragment">cat << REPOFILE | sudo tee /etc/yum.repos.d/ripple.repo
|
||||||
|
[ripple-unstable]
|
||||||
|
name=XRP Ledger Packages
|
||||||
|
enabled=1
|
||||||
|
gpgcheck=0
|
||||||
|
repo_gpgcheck=1
|
||||||
|
baseurl=https://repos.ripple.com/repos/rippled-rpm/unstable/
|
||||||
|
gpgkey=https://repos.ripple.com/repos/rippled-rpm/unstable/repodata/repomd.xml.key
|
||||||
|
REPOFILE
|
||||||
|
</pre><p class="startli"><em>Nightly</em> </p><pre class="fragment">cat << REPOFILE | sudo tee /etc/yum.repos.d/ripple.repo
|
||||||
|
[ripple-nightly]
|
||||||
|
name=XRP Ledger Packages
|
||||||
|
enabled=1
|
||||||
|
gpgcheck=0
|
||||||
|
repo_gpgcheck=1
|
||||||
|
baseurl=https://repos.ripple.com/repos/rippled-rpm/nightly/
|
||||||
|
gpgkey=https://repos.ripple.com/repos/rippled-rpm/nightly/repodata/repomd.xml.key
|
||||||
|
REPOFILE
|
||||||
|
</pre></li>
|
||||||
|
<li>Fetch the latest repo updates: <pre class="fragment"> sudo yum -y update
|
||||||
|
</pre></li>
|
||||||
|
<li>Install the new <code>rippled</code> package: <pre class="fragment"> sudo yum install -y rippled
|
||||||
|
</pre></li>
|
||||||
|
<li>Configure the <code>rippled</code> service to start on boot: <pre class="fragment"> sudo systemctl enable rippled.service
|
||||||
|
</pre></li>
|
||||||
|
<li>Start the <code>rippled</code> service: <pre class="fragment"> sudo systemctl start rippled.service
|
||||||
|
</pre> </li>
|
||||||
|
</ol>
|
||||||
</div></div><!-- contents -->
|
</div></div><!-- contents -->
|
||||||
</div><!-- PageDoc -->
|
</div><!-- PageDoc -->
|
||||||
<!-- start footer part -->
|
<!-- start footer part -->
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ $(function() {
|
|||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><p><b>This section is a work in progress!!</b></p>
|
<div class="textblock"><p><b>This section is a work in progress!!</b></p>
|
||||||
<p>Consensus is the task of reaching agreement within a distributed system in the presence of faulty or even malicious participants. This document outlines the <a href="https://arxiv.org/abs/1802.07242">XRP Ledger Consensus Algorithm</a> as implemented in <a href="https://github.com/ripple/rippled">rippled</a>, but focuses on its utility as a generic consensus algorithm independent of the detailed mechanics of the Ripple Consensus Ledger. Most notably, the algorithm does not require fully synchronous communication between all nodes in the network, or even a fixed network topology, but instead achieves consensus via collectively trusted subnetworks.</p>
|
<p>Consensus is the task of reaching agreement within a distributed system in the presence of faulty or even malicious participants. This document outlines the <a href="https://arxiv.org/abs/1802.07242">XRP Ledger Consensus Algorithm</a> as implemented in <a href="https://github.com/ripple/rippled">rippled</a>, but focuses on its utility as a generic consensus algorithm independent of the detailed mechanics of the Ripple Consensus Ledger. Most notably, the algorithm does not require fully synchronous communication between all nodes in the network, or even a fixed network topology, but instead achieves consensus via collectively trusted subnetworks.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md47"></a>
|
<h1><a class="anchor" id="autotoc_md49"></a>
|
||||||
Distributed Agreement</h1>
|
Distributed Agreement</h1>
|
||||||
<p>A challenge for distributed systems is reaching agreement on changes in shared state. For the Ripple network, the shared state is the current ledger–account information, account balances, order books and other financial data. We will refer to shared distributed state as a /ledger/ throughout the remainder of this document.</p>
|
<p>A challenge for distributed systems is reaching agreement on changes in shared state. For the Ripple network, the shared state is the current ledger–account information, account balances, order books and other financial data. We will refer to shared distributed state as a /ledger/ throughout the remainder of this document.</p>
|
||||||
<div class="image">
|
<div class="image">
|
||||||
@@ -87,9 +87,9 @@ Ledger Chain</div></div>
|
|||||||
<div class="caption">
|
<div class="caption">
|
||||||
Block Chain</div></div>
|
Block Chain</div></div>
|
||||||
<p>The remainder of this section describes the Consensus and Validation algorithms in more detail and is meant as a companion guide to understanding the generic implementation in <code>rippled</code>. The document <b>does not</b> discuss correctness, fault-tolerance or liveness properties of the algorithms or the full details of how they integrate within <code>rippled</code> to support the Ripple Consensus Ledger.</p>
|
<p>The remainder of this section describes the Consensus and Validation algorithms in more detail and is meant as a companion guide to understanding the generic implementation in <code>rippled</code>. The document <b>does not</b> discuss correctness, fault-tolerance or liveness properties of the algorithms or the full details of how they integrate within <code>rippled</code> to support the Ripple Consensus Ledger.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md48"></a>
|
<h1><a class="anchor" id="autotoc_md50"></a>
|
||||||
Consensus Overview</h1>
|
Consensus Overview</h1>
|
||||||
<h2><a class="anchor" id="autotoc_md49"></a>
|
<h2><a class="anchor" id="autotoc_md51"></a>
|
||||||
Definitions</h2>
|
Definitions</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>The <em>ledger</em> is the shared distributed state. Each ledger has a unique ID to distinguish it from all other ledgers. During consensus, the <em>previous</em>, <em>prior</em> or <em>last-closed</em> ledger is the most recent ledger seen by consensus and is the basis upon which it will build the next ledger.</li>
|
<li>The <em>ledger</em> is the shared distributed state. Each ledger has a unique ID to distinguish it from all other ledgers. During consensus, the <em>previous</em>, <em>prior</em> or <em>last-closed</em> ledger is the most recent ledger seen by consensus and is the basis upon which it will build the next ledger.</li>
|
||||||
@@ -102,7 +102,7 @@ Definitions</h2>
|
|||||||
<li>A <em>dispute</em> is a transaction that is either not part of a node's position or not in a peer's position. During consensus, the node will add or remove disputed transactions from its position based on that transaction's support amongst its peers.</li>
|
<li>A <em>dispute</em> is a transaction that is either not part of a node's position or not in a peer's position. During consensus, the node will add or remove disputed transactions from its position based on that transaction's support amongst its peers.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Note that most types have an ID as a lightweight identifier of instances of that type. Consensus often operates on the IDs directly since the underlying type is potentially expensive to share over the network. For example, proposal's only contain the ID of the position of a peer. Since many peers likely have the same position, this reduces the need to send the full transaction set multiple times. Instead, a node can request the transaction set from the network if necessary.</p>
|
<p>Note that most types have an ID as a lightweight identifier of instances of that type. Consensus often operates on the IDs directly since the underlying type is potentially expensive to share over the network. For example, proposal's only contain the ID of the position of a peer. Since many peers likely have the same position, this reduces the need to send the full transaction set multiple times. Instead, a node can request the transaction set from the network if necessary.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md50"></a>
|
<h2><a class="anchor" id="autotoc_md52"></a>
|
||||||
Overview</h2>
|
Overview</h2>
|
||||||
<div class="image">
|
<div class="image">
|
||||||
<img src="consensus_overview.png" alt=""/>
|
<img src="consensus_overview.png" alt=""/>
|
||||||
@@ -125,7 +125,7 @@ Effective Close Time</h2>
|
|||||||
Effective Close Time</div></div>
|
Effective Close Time</div></div>
|
||||||
<p>The effective close time is part of the node's position and is shared with peers in its proposals. Just like the position on the consensus transaction set, a node will update its close time position in response to its peers' effective close time positions. Peers can agree to disagree on the close time, in which case the effective close time is taken as 1 second past the prior close.</p>
|
<p>The effective close time is part of the node's position and is shared with peers in its proposals. Just like the position on the consensus transaction set, a node will update its close time position in response to its peers' effective close time positions. Peers can agree to disagree on the close time, in which case the effective close time is taken as 1 second past the prior close.</p>
|
||||||
<p>The close time resolution is itself dynamic, decreasing (coarser) resolution in subsequent consensus rounds if nodes are unable to reach consensus on an effective close time and increasing (finer) resolution if nodes consistently reach close time consensus.</p>
|
<p>The close time resolution is itself dynamic, decreasing (coarser) resolution in subsequent consensus rounds if nodes are unable to reach consensus on an effective close time and increasing (finer) resolution if nodes consistently reach close time consensus.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md51"></a>
|
<h2><a class="anchor" id="autotoc_md53"></a>
|
||||||
Modes</h2>
|
Modes</h2>
|
||||||
<p>Internally, a node operates under one of the following consensus modes. Either of the first two modes may be chosen when a consensus round starts.</p>
|
<p>Internally, a node operates under one of the following consensus modes. Either of the first two modes may be chosen when a consensus round starts.</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -143,7 +143,7 @@ Modes</h2>
|
|||||||
Consensus Modes</div></div>
|
Consensus Modes</div></div>
|
||||||
<p>Once either wrong ledger or switch ledger are reached, the node cannot return to proposing or observing until the next consensus round. However, the node could change its view of the correct prior ledger, so going from switch ledger to wrong ledger and back again is possible.</p>
|
<p>Once either wrong ledger or switch ledger are reached, the node cannot return to proposing or observing until the next consensus round. However, the node could change its view of the correct prior ledger, so going from switch ledger to wrong ledger and back again is possible.</p>
|
||||||
<p>The distinction between the wrong and switched ledger modes arises because a ledger's unique identifier may be known by a node before the ledger itself. This reflects that fact that the data corresponding to a ledger may be large and take time to share over the network, whereas the smaller ID could be shared in a peer validation much more quickly. Distinguishing the two states allows the node to decide how best to generate the next ledger once it declares consensus.</p>
|
<p>The distinction between the wrong and switched ledger modes arises because a ledger's unique identifier may be known by a node before the ledger itself. This reflects that fact that the data corresponding to a ledger may be large and take time to share over the network, whereas the smaller ID could be shared in a peer validation much more quickly. Distinguishing the two states allows the node to decide how best to generate the next ledger once it declares consensus.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md52"></a>
|
<h2><a class="anchor" id="autotoc_md54"></a>
|
||||||
Phases</h2>
|
Phases</h2>
|
||||||
<p>As depicted in the overview diagram, consensus is best viewed as a progression through 3 phases. There are 4 public methods of the generic consensus algorithm that determine this progression</p>
|
<p>As depicted in the overview diagram, consensus is best viewed as a progression through 3 phases. There are 4 public methods of the generic consensus algorithm that determine this progression</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -153,12 +153,12 @@ Phases</h2>
|
|||||||
<li><code>gotTxSet</code> is called when a transaction set is received from the network. This is typically in response to a prior request from the node to acquire the transaction set corresponding to a disagreeing peer's position.</li>
|
<li><code>gotTxSet</code> is called when a transaction set is received from the network. This is typically in response to a prior request from the node to acquire the transaction set corresponding to a disagreeing peer's position.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>The following subsections describe each consensus phase in more detail and what actions are taken in response to these calls.</p>
|
<p>The following subsections describe each consensus phase in more detail and what actions are taken in response to these calls.</p>
|
||||||
<h3><a class="anchor" id="autotoc_md53"></a>
|
<h3><a class="anchor" id="autotoc_md55"></a>
|
||||||
Open</h3>
|
Open</h3>
|
||||||
<p>The <code>Open</code> phase is a quiescent period to allow transactions to build up in the node's open ledger. The duration is a trade-off between latency and throughput. A shorter window reduces the latency to generating the next ledger, but also reduces transaction throughput due to fewer transactions accepted into the ledger.</p>
|
<p>The <code>Open</code> phase is a quiescent period to allow transactions to build up in the node's open ledger. The duration is a trade-off between latency and throughput. A shorter window reduces the latency to generating the next ledger, but also reduces transaction throughput due to fewer transactions accepted into the ledger.</p>
|
||||||
<p>A call to <code>startRound</code> would forcibly begin the next consensus round, skipping completion of the current round. This is not expected during normal operation. Calls to <code>peerProposal</code> or <code>gotTxSet</code> simply store the proposal or transaction set for use in the coming <code>Establish</code> phase.</p>
|
<p>A call to <code>startRound</code> would forcibly begin the next consensus round, skipping completion of the current round. This is not expected during normal operation. Calls to <code>peerProposal</code> or <code>gotTxSet</code> simply store the proposal or transaction set for use in the coming <code>Establish</code> phase.</p>
|
||||||
<p>A call to <code>timerEntry</code> first checks that the node is working on the correct prior ledger. If not, it will update the mode and request the correct ledger. Otherwise, the node checks whether to switch to the <code>Establish</code> phase and close the ledger.</p>
|
<p>A call to <code>timerEntry</code> first checks that the node is working on the correct prior ledger. If not, it will update the mode and request the correct ledger. Otherwise, the node checks whether to switch to the <code>Establish</code> phase and close the ledger.</p>
|
||||||
<h4><a class="anchor" id="autotoc_md54"></a>
|
<h4><a class="anchor" id="autotoc_md56"></a>
|
||||||
Ledger Close</h4>
|
Ledger Close</h4>
|
||||||
<p>Under normal circumstances, the open ledger period ends when one of the following is true</p>
|
<p>Under normal circumstances, the open ledger period ends when one of the following is true</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -174,12 +174,12 @@ disputes</h4>
|
|||||||
<img src="disputes.png" alt=""/>
|
<img src="disputes.png" alt=""/>
|
||||||
<div class="caption">
|
<div class="caption">
|
||||||
Disputes</div></div>
|
Disputes</div></div>
|
||||||
<h3><a class="anchor" id="autotoc_md55"></a>
|
<h3><a class="anchor" id="autotoc_md57"></a>
|
||||||
Establish</h3>
|
Establish</h3>
|
||||||
<p>The establish phase is the active period of consensus in which the node exchanges proposals with peers in an attempt to reach agreement on the consensus transactions and effective close time.</p>
|
<p>The establish phase is the active period of consensus in which the node exchanges proposals with peers in an attempt to reach agreement on the consensus transactions and effective close time.</p>
|
||||||
<p>A call to <code>startRound</code> would forcibly begin the next consensus round, skipping completion of the current round. This is not expected during normal operation. Calls to <code>peerProposal</code> or <code>gotTxSet</code> that reflect new positions will generate disputed transactions for any new disagreements and will update the peer's vote for all disputed transactions.</p>
|
<p>A call to <code>startRound</code> would forcibly begin the next consensus round, skipping completion of the current round. This is not expected during normal operation. Calls to <code>peerProposal</code> or <code>gotTxSet</code> that reflect new positions will generate disputed transactions for any new disagreements and will update the peer's vote for all disputed transactions.</p>
|
||||||
<p>A call to <code>timerEntry</code> first checks that the node is working from the correct prior ledger. If not, the node will update the mode and request the correct ledger. Otherwise, the node updates the node's position and considers whether to switch to the <code>Accepted</code> phase and declare consensus reached. However, at least <code>LEDGER_MIN_CONSENSUS</code> time must have elapsed before doing either. This allows peers an opportunity to take an initial position and share it.</p>
|
<p>A call to <code>timerEntry</code> first checks that the node is working from the correct prior ledger. If not, the node will update the mode and request the correct ledger. Otherwise, the node updates the node's position and considers whether to switch to the <code>Accepted</code> phase and declare consensus reached. However, at least <code>LEDGER_MIN_CONSENSUS</code> time must have elapsed before doing either. This allows peers an opportunity to take an initial position and share it.</p>
|
||||||
<h4><a class="anchor" id="autotoc_md56"></a>
|
<h4><a class="anchor" id="autotoc_md58"></a>
|
||||||
Update Position</h4>
|
Update Position</h4>
|
||||||
<p>In order to achieve consensus, the node is looking for a transaction set that is supported by a super-majority of peers. The node works towards this set by adding or removing disputed transactions from its position based on an increasing threshold for inclusion.</p>
|
<p>In order to achieve consensus, the node is looking for a transaction set that is supported by a super-majority of peers. The node works towards this set by adding or removing disputed transactions from its position based on an increasing threshold for inclusion.</p>
|
||||||
<div class="image">
|
<div class="image">
|
||||||
@@ -190,7 +190,7 @@ Threshold</div></div>
|
|||||||
<p>Given the <a href="#disputes_image">example disputes above</a> and an initial threshold of 50%, our node would retain its position since transaction 1 was not in dispute and transactions 2 and 3 have 75% support. Since its position did not change, it would not need to send a new proposal to peers. Peer C would not change either. Peer A would add transaction 3 to its position and Peer B would remove transaction 4 from its position; both would then send an updated position.</p>
|
<p>Given the <a href="#disputes_image">example disputes above</a> and an initial threshold of 50%, our node would retain its position since transaction 1 was not in dispute and transactions 2 and 3 have 75% support. Since its position did not change, it would not need to send a new proposal to peers. Peer C would not change either. Peer A would add transaction 3 to its position and Peer B would remove transaction 4 from its position; both would then send an updated position.</p>
|
||||||
<p>Conversely, if the diagram reflected a later call to =timerEntry= that occurs in the stuck region with a threshold of say 95%, our node would remove transactions 2 and 3 from its candidate set and send an updated position. Likewise, all the other peers would end up with only transaction 1 in their position.</p>
|
<p>Conversely, if the diagram reflected a later call to =timerEntry= that occurs in the stuck region with a threshold of say 95%, our node would remove transactions 2 and 3 from its candidate set and send an updated position. Likewise, all the other peers would end up with only transaction 1 in their position.</p>
|
||||||
<p>Lastly, if our node were not in the proposing mode, it would not include its own vote and just take the majority (>50%) position of its peers. In this example, our node would maintain its position of transactions 1, 2 and 3.</p>
|
<p>Lastly, if our node were not in the proposing mode, it would not include its own vote and just take the majority (>50%) position of its peers. In this example, our node would maintain its position of transactions 1, 2 and 3.</p>
|
||||||
<h4><a class="anchor" id="autotoc_md57"></a>
|
<h4><a class="anchor" id="autotoc_md59"></a>
|
||||||
Checking Consensus</h4>
|
Checking Consensus</h4>
|
||||||
<p>After updating its position, the node checks for supermajority agreement with its peers on its current position. This agreement is of the exact transaction set, not just the support of individual transactions. That is, if our position is a subset of a peer's position, that counts as a disagreement. Also recall that effective close time agreement allows a supermajority of participants agreeing to disagree.</p>
|
<p>After updating its position, the node checks for supermajority agreement with its peers on its current position. This agreement is of the exact transaction set, not just the support of individual transactions. That is, if our position is a subset of a peer's position, that counts as a disagreement. Also recall that effective close time agreement allows a supermajority of participants agreeing to disagree.</p>
|
||||||
<p>Consensus is declared when the following 3 clauses are true:</p>
|
<p>Consensus is declared when the following 3 clauses are true:</p>
|
||||||
@@ -201,16 +201,16 @@ Checking Consensus</h4>
|
|||||||
</ul>
|
</ul>
|
||||||
<p>The middle condition ensures slower peers have a chance to share positions, but prevents waiting too long on peers that have disconnected. Additionally, a node can declare that consensus has moved on if <code>minimumConsensusPercentage</code> peers have sent validations and moved on to the next ledger. This outcome indicates the node has fallen behind its peers and needs to catch up.</p>
|
<p>The middle condition ensures slower peers have a chance to share positions, but prevents waiting too long on peers that have disconnected. Additionally, a node can declare that consensus has moved on if <code>minimumConsensusPercentage</code> peers have sent validations and moved on to the next ledger. This outcome indicates the node has fallen behind its peers and needs to catch up.</p>
|
||||||
<p>If a node is not proposing, it does not include its own position when calculating the percent of agreeing participants but otherwise follows the above logic.</p>
|
<p>If a node is not proposing, it does not include its own position when calculating the percent of agreeing participants but otherwise follows the above logic.</p>
|
||||||
<h4><a class="anchor" id="autotoc_md58"></a>
|
<h4><a class="anchor" id="autotoc_md60"></a>
|
||||||
Accepting Consensus</h4>
|
Accepting Consensus</h4>
|
||||||
<p>Once consensus is reached (or moved on), the node switches to the <code>Accept</code> phase and signals to the implementing code that the round is complete. That code is responsible for using the consensus transaction set to generate the next ledger and calling <code>startRound</code> to begin the next round. The implementation has total freedom on ordering transactions, deciding what to do if consensus moved on, determining whether to retry or abandon local transactions that did not make the consensus set and updating any internal state based on the consensus progress.</p>
|
<p>Once consensus is reached (or moved on), the node switches to the <code>Accept</code> phase and signals to the implementing code that the round is complete. That code is responsible for using the consensus transaction set to generate the next ledger and calling <code>startRound</code> to begin the next round. The implementation has total freedom on ordering transactions, deciding what to do if consensus moved on, determining whether to retry or abandon local transactions that did not make the consensus set and updating any internal state based on the consensus progress.</p>
|
||||||
<h3><a class="anchor" id="autotoc_md59"></a>
|
<h3><a class="anchor" id="autotoc_md61"></a>
|
||||||
Accept</h3>
|
Accept</h3>
|
||||||
<p>The <code>Accept</code> phase is the terminal phase of the consensus algorithm. Calls to <code>timerEntry</code>, <code>peerProposal</code> and <code>gotTxSet</code> will not change the internal consensus state while in the accept phase. The expectation is that the application specific code is working to generate the new ledger based on the consensus outcome. Once complete, that code should make a call to <code>startRound</code> to kick off the next consensus round. The <code>startRound</code> call includes the new prior ledger, prior ledger ID and whether the round should begin in the proposing or observing mode. After setting some initial state, the phase transitions to <code>Open</code>. The node will also check if the provided prior ledger and ID are correct, updating the mode and requesting the proper ledger from the network if necessary.</p>
|
<p>The <code>Accept</code> phase is the terminal phase of the consensus algorithm. Calls to <code>timerEntry</code>, <code>peerProposal</code> and <code>gotTxSet</code> will not change the internal consensus state while in the accept phase. The expectation is that the application specific code is working to generate the new ledger based on the consensus outcome. Once complete, that code should make a call to <code>startRound</code> to kick off the next consensus round. The <code>startRound</code> call includes the new prior ledger, prior ledger ID and whether the round should begin in the proposing or observing mode. After setting some initial state, the phase transitions to <code>Open</code>. The node will also check if the provided prior ledger and ID are correct, updating the mode and requesting the proper ledger from the network if necessary.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md60"></a>
|
<h1><a class="anchor" id="autotoc_md62"></a>
|
||||||
Consensus Type Requirements</h1>
|
Consensus Type Requirements</h1>
|
||||||
<p>The consensus type requirements are given below as minimal implementation stubs. Actual implementations would augment these stubs with members appropriate for managing the details of transactions and ledgers within the larger application framework.</p>
|
<p>The consensus type requirements are given below as minimal implementation stubs. Actual implementations would augment these stubs with members appropriate for managing the details of transactions and ledgers within the larger application framework.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md61"></a>
|
<h2><a class="anchor" id="autotoc_md63"></a>
|
||||||
Transaction</h2>
|
Transaction</h2>
|
||||||
<p>The transaction type <code>Tx</code> encapsulates a single transaction under consideration by consensus.</p>
|
<p>The transaction type <code>Tx</code> encapsulates a single transaction under consideration by consensus.</p>
|
||||||
<div class="fragment"><div class="line"><span class="keyword">struct </span>Tx</div>
|
<div class="fragment"><div class="line"><span class="keyword">struct </span>Tx</div>
|
||||||
@@ -220,7 +220,7 @@ Transaction</h2>
|
|||||||
<div class="line"> </div>
|
<div class="line"> </div>
|
||||||
<div class="line"> <span class="comment">//... implementation specific</span></div>
|
<div class="line"> <span class="comment">//... implementation specific</span></div>
|
||||||
<div class="line">};</div>
|
<div class="line">};</div>
|
||||||
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md62"></a>
|
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md64"></a>
|
||||||
Transaction Set</h2>
|
Transaction Set</h2>
|
||||||
<p>The transaction set type <code>TxSet</code> represents a set of <code>Tx</code>s that are collectively under consideration by consensus. A <code>TxSet</code> can be compared against other <code>TxSet</code>s (typically from peers) and can be modified to add or remove transactions via the mutable subtype.</p>
|
<p>The transaction set type <code>TxSet</code> represents a set of <code>Tx</code>s that are collectively under consideration by consensus. A <code>TxSet</code> can be compared against other <code>TxSet</code>s (typically from peers) and can be modified to add or remove transactions via the mutable subtype.</p>
|
||||||
<div class="fragment"><div class="line"><span class="keyword">struct </span>TxSet</div>
|
<div class="fragment"><div class="line"><span class="keyword">struct </span>TxSet</div>
|
||||||
@@ -253,7 +253,7 @@ Transaction Set</h2>
|
|||||||
<div class="line"> </div>
|
<div class="line"> </div>
|
||||||
<div class="line"> <span class="comment">//... implementation specific</span></div>
|
<div class="line"> <span class="comment">//... implementation specific</span></div>
|
||||||
<div class="line">};</div>
|
<div class="line">};</div>
|
||||||
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md63"></a>
|
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md65"></a>
|
||||||
Ledger</h2>
|
Ledger</h2>
|
||||||
<p>The <code>Ledger</code> type represents the state shared amongst the distributed participants. Notice that the details of how the next ledger is generated from the prior ledger and the consensus accepted transaction set is not part of the interface. Within the generic code, this type is primarily used to know that peers are working on the same tip of the ledger chain and to provide some basic timing data for consensus.</p>
|
<p>The <code>Ledger</code> type represents the state shared amongst the distributed participants. Notice that the details of how the next ledger is generated from the prior ledger and the consensus accepted transaction set is not part of the interface. Within the generic code, this type is primarily used to know that peers are working on the same tip of the ledger chain and to provide some basic timing data for consensus.</p>
|
||||||
<div class="fragment"><div class="line"><span class="keyword">struct </span>Ledger</div>
|
<div class="fragment"><div class="line"><span class="keyword">struct </span>Ledger</div>
|
||||||
@@ -283,7 +283,7 @@ Ledger</h2>
|
|||||||
<div class="line"> </div>
|
<div class="line"> </div>
|
||||||
<div class="line"> <span class="comment">//... implementation specific</span></div>
|
<div class="line"> <span class="comment">//... implementation specific</span></div>
|
||||||
<div class="line">};</div>
|
<div class="line">};</div>
|
||||||
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md64"></a>
|
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md66"></a>
|
||||||
PeerProposal</h2>
|
PeerProposal</h2>
|
||||||
<p>The <code>PeerProposal</code> type represents the signed position taken by a peer during consensus. The only type requirement is owning an instance of a generic <code>ConsensusProposal</code>.</p>
|
<p>The <code>PeerProposal</code> type represents the signed position taken by a peer during consensus. The only type requirement is owning an instance of a generic <code>ConsensusProposal</code>.</p>
|
||||||
<div class="fragment"><div class="line"><span class="comment">// Represents our proposed position or a peer's proposed position</span></div>
|
<div class="fragment"><div class="line"><span class="comment">// Represents our proposed position or a peer's proposed position</span></div>
|
||||||
@@ -300,7 +300,7 @@ PeerProposal</h2>
|
|||||||
<div class="line"> </div>
|
<div class="line"> </div>
|
||||||
<div class="line"> <span class="comment">// ... implementation specific</span></div>
|
<div class="line"> <span class="comment">// ... implementation specific</span></div>
|
||||||
<div class="line">};</div>
|
<div class="line">};</div>
|
||||||
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md65"></a>
|
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md67"></a>
|
||||||
Generic Consensus Interface</h2>
|
Generic Consensus Interface</h2>
|
||||||
<p>The generic <code>Consensus</code> relies on <code>Adaptor</code> template class to implement a set of helper functions that plug the consensus algorithm into a specific application. The <code>Adaptor</code> class also defines the types above needed by the algorithm. Below are excerpts of the generic consensus implementation and of helper types that will interact with the concrete implementing class.</p>
|
<p>The generic <code>Consensus</code> relies on <code>Adaptor</code> template class to implement a set of helper functions that plug the consensus algorithm into a specific application. The <code>Adaptor</code> class also defines the types above needed by the algorithm. Below are excerpts of the generic consensus implementation and of helper types that will interact with the concrete implementing class.</p>
|
||||||
<div class="fragment"><div class="line"><span class="comment">// Represents a transction under dispute this round</span></div>
|
<div class="fragment"><div class="line"><span class="comment">// Represents a transction under dispute this round</span></div>
|
||||||
@@ -371,7 +371,7 @@ Generic Consensus Interface</h2>
|
|||||||
<div class="line"> </div>
|
<div class="line"> </div>
|
||||||
<div class="line"> <span class="comment">// ... details</span></div>
|
<div class="line"> <span class="comment">// ... details</span></div>
|
||||||
<div class="line">};</div>
|
<div class="line">};</div>
|
||||||
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md66"></a>
|
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md68"></a>
|
||||||
Adapting Generic Consensus</h2>
|
Adapting Generic Consensus</h2>
|
||||||
<p>The stub below shows the set of callback/helper functions required in the implementing class.</p>
|
<p>The stub below shows the set of callback/helper functions required in the implementing class.</p>
|
||||||
<div class="fragment"><div class="line"><span class="keyword">struct </span>Adaptor</div>
|
<div class="fragment"><div class="line"><span class="keyword">struct </span>Adaptor</div>
|
||||||
@@ -438,7 +438,7 @@ Adapting Generic Consensus</h2>
|
|||||||
<li>The generic code does not specify how transactions are submitted by clients, propagated through the network or stored in the open ledger. Indeed, the open ledger is only conceptual from the perspective of the generic code—the initial position and transaction set are opaquely generated in a <code>Consensus::Result</code> instance returned from the <code>onClose</code> callback.</li>
|
<li>The generic code does not specify how transactions are submitted by clients, propagated through the network or stored in the open ledger. Indeed, the open ledger is only conceptual from the perspective of the generic code—the initial position and transaction set are opaquely generated in a <code>Consensus::Result</code> instance returned from the <code>onClose</code> callback.</li>
|
||||||
<li>The calls to <code>acquireLedger</code> and <code>acquireTxSet</code> only have non-trivial return if the ledger or transaction set of interest is available. The implementing class is free to block while acquiring, or return the empty option while servicing the request asynchronously. Due to legacy reasons, the two calls are not symmetric. <code>acquireTxSet</code> requires the host application to call <code>gotTxSet</code> when an asynchronous <code>acquire</code> completes. Conversely, <code>acquireLedger</code> will be called again later by the consensus code if it still desires the ledger with the hope that the asynchronous acquisition is complete.</li>
|
<li>The calls to <code>acquireLedger</code> and <code>acquireTxSet</code> only have non-trivial return if the ledger or transaction set of interest is available. The implementing class is free to block while acquiring, or return the empty option while servicing the request asynchronously. Due to legacy reasons, the two calls are not symmetric. <code>acquireTxSet</code> requires the host application to call <code>gotTxSet</code> when an asynchronous <code>acquire</code> completes. Conversely, <code>acquireLedger</code> will be called again later by the consensus code if it still desires the ledger with the hope that the asynchronous acquisition is complete.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h1><a class="anchor" id="autotoc_md67"></a>
|
<h1><a class="anchor" id="autotoc_md69"></a>
|
||||||
Validation</h1>
|
Validation</h1>
|
||||||
<p>Coming Soon! </p>
|
<p>Coming Soon! </p>
|
||||||
</div></div><!-- contents -->
|
</div></div><!-- contents -->
|
||||||
|
|||||||
@@ -65,9 +65,9 @@ $(function() {
|
|||||||
<div class="title">Ledger Process </div> </div>
|
<div class="title">Ledger Process </div> </div>
|
||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><h1><a class="anchor" id="autotoc_md78"></a>
|
<div class="textblock"><h1><a class="anchor" id="autotoc_md80"></a>
|
||||||
Introduction</h1>
|
Introduction</h1>
|
||||||
<h1><a class="anchor" id="autotoc_md79"></a>
|
<h1><a class="anchor" id="autotoc_md81"></a>
|
||||||
Life Cycle</h1>
|
Life Cycle</h1>
|
||||||
<p>Every server always has an open ledger. All received new transactions are applied to the open ledger. The open ledger can't close until we reach a consensus on the previous ledger, and either: there is at least one transaction in the open ledger, or the ledger's close time has been reached.</p>
|
<p>Every server always has an open ledger. All received new transactions are applied to the open ledger. The open ledger can't close until we reach a consensus on the previous ledger, and either: there is at least one transaction in the open ledger, or the ledger's close time has been reached.</p>
|
||||||
<p>When the open ledger is closed the transactions in the open ledger become the initial proposal. Validators will send the proposal (non-validators will simply not send the proposal). The open ledger contains the set of transactions the server thinks should go into the next ledger.</p>
|
<p>When the open ledger is closed the transactions in the open ledger become the initial proposal. Validators will send the proposal (non-validators will simply not send the proposal). The open ledger contains the set of transactions the server thinks should go into the next ledger.</p>
|
||||||
@@ -78,17 +78,17 @@ Life Cycle</h1>
|
|||||||
<li>Forms the basis of the initial proposal during consensus</li>
|
<li>Forms the basis of the initial proposal during consensus</li>
|
||||||
<li>Used to decide if we can reject the transaction without relaying it</li>
|
<li>Used to decide if we can reject the transaction without relaying it</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h1><a class="anchor" id="autotoc_md80"></a>
|
<h1><a class="anchor" id="autotoc_md82"></a>
|
||||||
Byzantine Failures</h1>
|
Byzantine Failures</h1>
|
||||||
<p>Byzantine failures are resolved as follows. If there is a supermajority ledger, then a minority of validators will discover that the consensus round is proceeding on a different ledger than they thought. These validators will become desynced, and switch to a strategy of trying to acquire the consensus ledger.</p>
|
<p>Byzantine failures are resolved as follows. If there is a supermajority ledger, then a minority of validators will discover that the consensus round is proceeding on a different ledger than they thought. These validators will become desynced, and switch to a strategy of trying to acquire the consensus ledger.</p>
|
||||||
<p>If there is no majority ledger, then starting on the next consensus round there will not be a consensus on the last closed ledger. Another avalanche process is started.</p>
|
<p>If there is no majority ledger, then starting on the next consensus round there will not be a consensus on the last closed ledger. Another avalanche process is started.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md81"></a>
|
<h1><a class="anchor" id="autotoc_md83"></a>
|
||||||
Validators</h1>
|
Validators</h1>
|
||||||
<p>The only meaningful difference between a validator and a 'regular' server is that the validator sends its proposals and validations to the network.</p>
|
<p>The only meaningful difference between a validator and a 'regular' server is that the validator sends its proposals and validations to the network.</p>
|
||||||
<hr />
|
<hr />
|
||||||
<h1><a class="anchor" id="autotoc_md83"></a>
|
<h1><a class="anchor" id="autotoc_md85"></a>
|
||||||
The Ledger Stream</h1>
|
The Ledger Stream</h1>
|
||||||
<h2><a class="anchor" id="autotoc_md84"></a>
|
<h2><a class="anchor" id="autotoc_md86"></a>
|
||||||
Ledger Priorities</h2>
|
Ledger Priorities</h2>
|
||||||
<p>There are two ledgers that are the most important for a rippled server to have:</p>
|
<p>There are two ledgers that are the most important for a rippled server to have:</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -106,7 +106,7 @@ Ledger Priorities</h2>
|
|||||||
<p>But loading or network connectivity may sometimes interfere with that ledger stream. So suppose the server publishes validated ledger 600 and then receives validated ledger 603. Then the server wants to back fill its ledger history with ledgers 601 and 602.</p>
|
<p>But loading or network connectivity may sometimes interfere with that ledger stream. So suppose the server publishes validated ledger 600 and then receives validated ledger 603. Then the server wants to back fill its ledger history with ledgers 601 and 602.</p>
|
||||||
<p>The server prioritizes keeping up with current ledgers. But if it is caught up on the current ledger, and there are no higher priority demands on the server, then it will attempt to back fill its historical ledgers. It fills in the historical ledger data first by attempting to retrieve it from the local database. If the local database does not have all of the necessary data then the server requests the remaining information from network peers.</p>
|
<p>The server prioritizes keeping up with current ledgers. But if it is caught up on the current ledger, and there are no higher priority demands on the server, then it will attempt to back fill its historical ledgers. It fills in the historical ledger data first by attempting to retrieve it from the local database. If the local database does not have all of the necessary data then the server requests the remaining information from network peers.</p>
|
||||||
<p>Suppose the server is missing multiple historical ledgers. Take the previous example where we have ledgers 603 and 600, but we're missing 601 and 602. In that case the server requests information for ledger 602 first, before back-filling ledger 601. We want to expand the contiguous range of most-recent ledgers that the server has locally. There's also a limit to how much historical ledger data is useful. So if we're on ledger 603, but we're missing ledger 4 we may not bother asking for ledger 4.</p>
|
<p>Suppose the server is missing multiple historical ledgers. Take the previous example where we have ledgers 603 and 600, but we're missing 601 and 602. In that case the server requests information for ledger 602 first, before back-filling ledger 601. We want to expand the contiguous range of most-recent ledgers that the server has locally. There's also a limit to how much historical ledger data is useful. So if we're on ledger 603, but we're missing ledger 4 we may not bother asking for ledger 4.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md85"></a>
|
<h2><a class="anchor" id="autotoc_md87"></a>
|
||||||
Assembling a Ledger</h2>
|
Assembling a Ledger</h2>
|
||||||
<p>When data for a ledger arrives from a peer, it may take a while before the server can apply that data. So when ledger data arrives we schedule a job thread to apply that data. If more data arrives before the job starts we add that data to the job. We defer requesting more ledger data until all of the data we have for that ledger has been processed. Once all of that data is processed we can intelligently request only the additional data that we need to fill in the ledger. This reduces network traffic and minimizes the load on peers supplying the data.</p>
|
<p>When data for a ledger arrives from a peer, it may take a while before the server can apply that data. So when ledger data arrives we schedule a job thread to apply that data. If more data arrives before the job starts we add that data to the job. We defer requesting more ledger data until all of the data we have for that ledger has been processed. Once all of that data is processed we can intelligently request only the additional data that we need to fill in the ledger. This reduces network traffic and minimizes the load on peers supplying the data.</p>
|
||||||
<p>If we receive data for a ledger that is not currently under construction, we don't just throw the data away. In particular the AccountStateNodes may be useful, since they can be re-used across ledgers. This data is stashed in memory (not the database) where the acquire process can find it.</p>
|
<p>If we receive data for a ledger that is not currently under construction, we don't just throw the data away. In particular the AccountStateNodes may be useful, since they can be re-used across ledgers. This data is stashed in memory (not the database) where the acquire process can find it.</p>
|
||||||
@@ -120,13 +120,13 @@ Assembling a Ledger</h2>
|
|||||||
</ol>
|
</ol>
|
||||||
<p>Inner-most nodes are supplied before outer nodes. This allows the requesting server to hook things up (and validate) in the order in which data arrives.</p>
|
<p>Inner-most nodes are supplied before outer nodes. This allows the requesting server to hook things up (and validate) in the order in which data arrives.</p>
|
||||||
<p>If this process fails, then a server can also ask for ledger data by hash, rather than by asking for specific nodes in a ledger. Asking for information by hash is less efficient, but it allows a peer to return the information even if the information is not assembled into a tree. All the peer needs is the raw data.</p>
|
<p>If this process fails, then a server can also ask for ledger data by hash, rather than by asking for specific nodes in a ledger. Asking for information by hash is less efficient, but it allows a peer to return the information even if the information is not assembled into a tree. All the peer needs is the raw data.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md86"></a>
|
<h2><a class="anchor" id="autotoc_md88"></a>
|
||||||
Which Peer To Ask</h2>
|
Which Peer To Ask</h2>
|
||||||
<p>Peers go though state transitions as the network goes through its state transitions. Peer's provide their state to their directly connected peers. By monitoring the state of each connected peer a server can tell which of its peers has the information that it needs.</p>
|
<p>Peers go though state transitions as the network goes through its state transitions. Peer's provide their state to their directly connected peers. By monitoring the state of each connected peer a server can tell which of its peers has the information that it needs.</p>
|
||||||
<p>Therefore if a server suffers a byzantine failure the server can tell which of its peers did not suffer that same failure. So the server knows which peer(s) to ask for the missing information.</p>
|
<p>Therefore if a server suffers a byzantine failure the server can tell which of its peers did not suffer that same failure. So the server knows which peer(s) to ask for the missing information.</p>
|
||||||
<p>Peers also report their contiguous range of ledgers. This is another way that a server can determine which peer to ask for a particular ledger or piece of a ledger.</p>
|
<p>Peers also report their contiguous range of ledgers. This is another way that a server can determine which peer to ask for a particular ledger or piece of a ledger.</p>
|
||||||
<p>There are also indirect peer queries. If there have been timeouts while acquiring ledger data then a server may issue indirect queries. In that case the server receiving the indirect query passes the query along to any of its peers that may have the requested data. This is important if the network has a byzantine failure. It also helps protect the validation network. A validator may need to get a peer set from one of the other validators, and indirect queries improve the likelihood of success with that.</p>
|
<p>There are also indirect peer queries. If there have been timeouts while acquiring ledger data then a server may issue indirect queries. In that case the server receiving the indirect query passes the query along to any of its peers that may have the requested data. This is important if the network has a byzantine failure. It also helps protect the validation network. A validator may need to get a peer set from one of the other validators, and indirect queries improve the likelihood of success with that.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md87"></a>
|
<h2><a class="anchor" id="autotoc_md89"></a>
|
||||||
Kinds of Fetch Packs</h2>
|
Kinds of Fetch Packs</h2>
|
||||||
<p>A FetchPack is the way that peers send partial ledger data to other peers so the receiving peer can reconstruct a ledger.</p>
|
<p>A FetchPack is the way that peers send partial ledger data to other peers so the receiving peer can reconstruct a ledger.</p>
|
||||||
<p>A 'normal' FetchPack is a bucket of nodes indexed by hash. The server building the FetchPack puts information into the FetchPack that the destination server is likely to need. Normally they contain all of the missing nodes needed to fill in a ledger.</p>
|
<p>A 'normal' FetchPack is a bucket of nodes indexed by hash. The server building the FetchPack puts information into the FetchPack that the destination server is likely to need. Normally they contain all of the missing nodes needed to fill in a ledger.</p>
|
||||||
@@ -140,36 +140,36 @@ Kinds of Fetch Packs</h2>
|
|||||||
<li>The index and new data of modified nodes in the state tree.</li>
|
<li>The index and new data of modified nodes in the state tree.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
<h1><a class="anchor" id="autotoc_md89"></a>
|
<h1><a class="anchor" id="autotoc_md91"></a>
|
||||||
Definitions</h1>
|
Definitions</h1>
|
||||||
<h2><a class="anchor" id="autotoc_md90"></a>
|
<h2><a class="anchor" id="autotoc_md92"></a>
|
||||||
Open Ledger</h2>
|
Open Ledger</h2>
|
||||||
<p>The open ledger is the ledger that the server applies all new incoming transactions to.</p>
|
<p>The open ledger is the ledger that the server applies all new incoming transactions to.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md91"></a>
|
<h2><a class="anchor" id="autotoc_md93"></a>
|
||||||
Last Validated Ledger</h2>
|
Last Validated Ledger</h2>
|
||||||
<p>The most recent ledger that the server is certain will always remain part of the permanent, public history.</p>
|
<p>The most recent ledger that the server is certain will always remain part of the permanent, public history.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md92"></a>
|
<h2><a class="anchor" id="autotoc_md94"></a>
|
||||||
Last Closed Ledger</h2>
|
Last Closed Ledger</h2>
|
||||||
<p>The most recent ledger that the server believes the network reached consensus on. Different servers can arrive at a different conclusion about the last closed ledger. This is a consequence of Byzantanine failure. The purpose of validations is to resolve the differences between servers and come to a common conclusion about which last closed ledger is authoritative.</p>
|
<p>The most recent ledger that the server believes the network reached consensus on. Different servers can arrive at a different conclusion about the last closed ledger. This is a consequence of Byzantanine failure. The purpose of validations is to resolve the differences between servers and come to a common conclusion about which last closed ledger is authoritative.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md93"></a>
|
<h2><a class="anchor" id="autotoc_md95"></a>
|
||||||
Consensus</h2>
|
Consensus</h2>
|
||||||
<p>A distributed agreement protocol. Ripple uses the consensus process to solve the problem of double-spending.</p>
|
<p>A distributed agreement protocol. Ripple uses the consensus process to solve the problem of double-spending.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md94"></a>
|
<h2><a class="anchor" id="autotoc_md96"></a>
|
||||||
Validation</h2>
|
Validation</h2>
|
||||||
<p>A signed statement indicating that it built a particular ledger as a result of the consensus process.</p>
|
<p>A signed statement indicating that it built a particular ledger as a result of the consensus process.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md95"></a>
|
<h2><a class="anchor" id="autotoc_md97"></a>
|
||||||
Proposal</h2>
|
Proposal</h2>
|
||||||
<p>A signed statement of which transactions it believes should be included in the next consensus ledger.</p>
|
<p>A signed statement of which transactions it believes should be included in the next consensus ledger.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md96"></a>
|
<h2><a class="anchor" id="autotoc_md98"></a>
|
||||||
Ledger Header</h2>
|
Ledger Header</h2>
|
||||||
<p>The "ledger header" is the chunk of data that hashes to the ledger's hash. It contains the sequence number, parent hash, hash of the previous ledger, hash of the root node of the state tree, and so on.</p>
|
<p>The "ledger header" is the chunk of data that hashes to the ledger's hash. It contains the sequence number, parent hash, hash of the previous ledger, hash of the root node of the state tree, and so on.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md97"></a>
|
<h2><a class="anchor" id="autotoc_md99"></a>
|
||||||
Ledger Base</h2>
|
Ledger Base</h2>
|
||||||
<p>The term "ledger base" refers to a particular type of query and response used in the ledger fetch process that includes the ledger header but may also contain other information such as the root node of the state tree.</p>
|
<p>The term "ledger base" refers to a particular type of query and response used in the ledger fetch process that includes the ledger header but may also contain other information such as the root node of the state tree.</p>
|
||||||
<hr />
|
<hr />
|
||||||
<h1><a class="anchor" id="autotoc_md99"></a>
|
<h1><a class="anchor" id="autotoc_md101"></a>
|
||||||
Ledger Structures</h1>
|
Ledger Structures</h1>
|
||||||
<h2><a class="anchor" id="autotoc_md100"></a>
|
<h2><a class="anchor" id="autotoc_md102"></a>
|
||||||
Account Root</h2>
|
Account Root</h2>
|
||||||
<p><b>Account:</b> A 160-bit account ID.</p>
|
<p><b>Account:</b> A 160-bit account ID.</p>
|
||||||
<p><b>Balance:</b> Balance in the account.</p>
|
<p><b>Balance:</b> Balance in the account.</p>
|
||||||
@@ -180,7 +180,7 @@ Account Root</h2>
|
|||||||
<p><b>PreviousTxnLgrSeq:</b> Ledger number sequence number of the previous transaction on this account.</p>
|
<p><b>PreviousTxnLgrSeq:</b> Ledger number sequence number of the previous transaction on this account.</p>
|
||||||
<p><b>Sequence:</b> Must be a value of 1 for the account to process a valid transaction. The value initially matches the sequence number of the state tree of the account that signed the transaction. The process of executing the transaction increments the sequence number. This is how ripple prevents a transaction from executing more than once.</p>
|
<p><b>Sequence:</b> Must be a value of 1 for the account to process a valid transaction. The value initially matches the sequence number of the state tree of the account that signed the transaction. The process of executing the transaction increments the sequence number. This is how ripple prevents a transaction from executing more than once.</p>
|
||||||
<p><b>index:</b> 256-bit hash of this AccountRoot.</p>
|
<p><b>index:</b> 256-bit hash of this AccountRoot.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md101"></a>
|
<h2><a class="anchor" id="autotoc_md103"></a>
|
||||||
Trust Line</h2>
|
Trust Line</h2>
|
||||||
<p>The trust line acts as an edge connecting two accounts: the accounts represented by the HighNode and the LowNode. Which account is "high" and "low" is determined by the values of the two 160-bit account IDs. The account with the smaller 160-bit ID is always the low account. This ordering makes the hash of a trust line between accounts A and B have the same value as a trust line between accounts B and A.</p>
|
<p>The trust line acts as an edge connecting two accounts: the accounts represented by the HighNode and the LowNode. Which account is "high" and "low" is determined by the values of the two 160-bit account IDs. The account with the smaller 160-bit ID is always the low account. This ordering makes the hash of a trust line between accounts A and B have the same value as a trust line between accounts B and A.</p>
|
||||||
<p><b>Balance:</b></p><ul>
|
<p><b>Balance:</b></p><ul>
|
||||||
@@ -205,14 +205,14 @@ Trust Line</h2>
|
|||||||
<p><b>PreviousTxnID:</b> 256-bit hash of the previous transaction on this account.</p>
|
<p><b>PreviousTxnID:</b> 256-bit hash of the previous transaction on this account.</p>
|
||||||
<p><b>PreviousTxnLgrSeq:</b> Ledger number sequence number of the previous transaction on this account.</p>
|
<p><b>PreviousTxnLgrSeq:</b> Ledger number sequence number of the previous transaction on this account.</p>
|
||||||
<p><b>index:</b> 256-bit hash of this RippleState.</p>
|
<p><b>index:</b> 256-bit hash of this RippleState.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md102"></a>
|
<h2><a class="anchor" id="autotoc_md104"></a>
|
||||||
Ledger Hashes</h2>
|
Ledger Hashes</h2>
|
||||||
<p><b>Flags:</b> ???</p>
|
<p><b>Flags:</b> ???</p>
|
||||||
<p><b>Hashes:</b> A list of the hashes of the previous 256 ledgers.</p>
|
<p><b>Hashes:</b> A list of the hashes of the previous 256 ledgers.</p>
|
||||||
<p><b>LastLedgerSequence:</b></p>
|
<p><b>LastLedgerSequence:</b></p>
|
||||||
<p><b>LedgerEntryType:</b> "LedgerHashes".</p>
|
<p><b>LedgerEntryType:</b> "LedgerHashes".</p>
|
||||||
<p><b>index:</b> 256-bit hash of this LedgerHashes.</p>
|
<p><b>index:</b> 256-bit hash of this LedgerHashes.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md103"></a>
|
<h2><a class="anchor" id="autotoc_md105"></a>
|
||||||
Owner Directory</h2>
|
Owner Directory</h2>
|
||||||
<p>Lists all of the offers and trust lines that are associated with an account.</p>
|
<p>Lists all of the offers and trust lines that are associated with an account.</p>
|
||||||
<p><b>Flags:</b> ???</p>
|
<p><b>Flags:</b> ???</p>
|
||||||
@@ -221,7 +221,7 @@ Owner Directory</h2>
|
|||||||
<p><b>Owner:</b> 160-bit ID of the owner account.</p>
|
<p><b>Owner:</b> 160-bit ID of the owner account.</p>
|
||||||
<p><b>RootIndex:</b></p>
|
<p><b>RootIndex:</b></p>
|
||||||
<p><b>index:</b> A hash of the owner account.</p>
|
<p><b>index:</b> A hash of the owner account.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md104"></a>
|
<h2><a class="anchor" id="autotoc_md106"></a>
|
||||||
Book Directory</h2>
|
Book Directory</h2>
|
||||||
<p>Lists one or more offers that have the same quality.</p>
|
<p>Lists one or more offers that have the same quality.</p>
|
||||||
<p>If a pair of Currency and Issuer fields are all zeros, then that pair is dealing in XRP.</p>
|
<p>If a pair of Currency and Issuer fields are all zeros, then that pair is dealing in XRP.</p>
|
||||||
@@ -238,31 +238,31 @@ Book Directory</h2>
|
|||||||
<p><b>TakerPaysIssuer:</b> Issuer of the PaysCurrency.</p>
|
<p><b>TakerPaysIssuer:</b> Issuer of the PaysCurrency.</p>
|
||||||
<p><b>index:</b> A 256-bit hash computed using the TakerGetsCurrency, TakerGetsIssuer, TakerPaysCurrency, and TakerPaysIssuer in the top 192 bits. The lower 64-bits are occupied by the exchange rate.</p>
|
<p><b>index:</b> A 256-bit hash computed using the TakerGetsCurrency, TakerGetsIssuer, TakerPaysCurrency, and TakerPaysIssuer in the top 192 bits. The lower 64-bits are occupied by the exchange rate.</p>
|
||||||
<hr />
|
<hr />
|
||||||
<h1><a class="anchor" id="autotoc_md106"></a>
|
<h1><a class="anchor" id="autotoc_md108"></a>
|
||||||
Ledger Publication</h1>
|
Ledger Publication</h1>
|
||||||
<h2><a class="anchor" id="autotoc_md107"></a>
|
<h2><a class="anchor" id="autotoc_md109"></a>
|
||||||
Overview</h2>
|
Overview</h2>
|
||||||
<p>The Ripple server permits clients to subscribe to a continuous stream of fully-validated ledgers. The publication code maintains this stream.</p>
|
<p>The Ripple server permits clients to subscribe to a continuous stream of fully-validated ledgers. The publication code maintains this stream.</p>
|
||||||
<p>The server attempts to maintain this continuous stream unless it falls too far behind, in which case it jumps to the current fully-validated ledger and then attempts to resume a continuous stream.</p>
|
<p>The server attempts to maintain this continuous stream unless it falls too far behind, in which case it jumps to the current fully-validated ledger and then attempts to resume a continuous stream.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md108"></a>
|
<h2><a class="anchor" id="autotoc_md110"></a>
|
||||||
Implementation</h2>
|
Implementation</h2>
|
||||||
<p><code>LedgerMaster::doAdvance</code> is invoked when work may need to be done to publish ledgers to clients. This code loops until it cannot make further progress.</p>
|
<p><code>LedgerMaster::doAdvance</code> is invoked when work may need to be done to publish ledgers to clients. This code loops until it cannot make further progress.</p>
|
||||||
<p><code>LedgerMaster::findNewLedgersToPublish</code> is called first. If the last fully-valid ledger's sequence number is greater than the last published ledger's sequence number, it attempts to publish those ledgers, retrieving them if needed.</p>
|
<p><code>LedgerMaster::findNewLedgersToPublish</code> is called first. If the last fully-valid ledger's sequence number is greater than the last published ledger's sequence number, it attempts to publish those ledgers, retrieving them if needed.</p>
|
||||||
<p>If there are no new ledgers to publish, <code>doAdvance</code> determines if it can backfill history. If the publication is not caught up, backfilling is not attempted to conserve resources.</p>
|
<p>If there are no new ledgers to publish, <code>doAdvance</code> determines if it can backfill history. If the publication is not caught up, backfilling is not attempted to conserve resources.</p>
|
||||||
<p>If history can be backfilled, the missing ledger with the highest sequence number is retrieved first. If a historical ledger is retrieved, and its predecessor is in the database, <code>tryFill</code> is invoked to update the list of resident ledgers.</p>
|
<p>If history can be backfilled, the missing ledger with the highest sequence number is retrieved first. If a historical ledger is retrieved, and its predecessor is in the database, <code>tryFill</code> is invoked to update the list of resident ledgers.</p>
|
||||||
<hr />
|
<hr />
|
||||||
<h1><a class="anchor" id="autotoc_md110"></a>
|
<h1><a class="anchor" id="autotoc_md112"></a>
|
||||||
The Ledger Cleaner</h1>
|
The Ledger Cleaner</h1>
|
||||||
<h2><a class="anchor" id="autotoc_md111"></a>
|
<h2><a class="anchor" id="autotoc_md113"></a>
|
||||||
Overview</h2>
|
Overview</h2>
|
||||||
<p>The ledger cleaner checks and, if necessary, repairs the SQLite ledger and transaction databases. It can also check for pieces of a ledger that should be in the node back end but are missing. If it detects this case, it triggers a fetch of the ledger. The ledger cleaner only operates by manual request. It is never started automatically.</p>
|
<p>The ledger cleaner checks and, if necessary, repairs the SQLite ledger and transaction databases. It can also check for pieces of a ledger that should be in the node back end but are missing. If it detects this case, it triggers a fetch of the ledger. The ledger cleaner only operates by manual request. It is never started automatically.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md112"></a>
|
<h2><a class="anchor" id="autotoc_md114"></a>
|
||||||
Operations</h2>
|
Operations</h2>
|
||||||
<p>The ledger cleaner can operate on a single ledger or a range of ledgers. It always validates the ledger chain itself, ensuring that the SQLite database contains a consistent chain of ledgers from the last validated ledger as far back as the database goes.</p>
|
<p>The ledger cleaner can operate on a single ledger or a range of ledgers. It always validates the ledger chain itself, ensuring that the SQLite database contains a consistent chain of ledgers from the last validated ledger as far back as the database goes.</p>
|
||||||
<p>If requested, it can additionally repair the SQLite entries for transactions in each checked ledger. This was primarily intended to repair incorrect entries created by a bug (since fixed) that could cause transasctions from a ledger other than the fully-validated ledger to appear in the SQLite databases in addition to the transactions from the correct ledger.</p>
|
<p>If requested, it can additionally repair the SQLite entries for transactions in each checked ledger. This was primarily intended to repair incorrect entries created by a bug (since fixed) that could cause transasctions from a ledger other than the fully-validated ledger to appear in the SQLite databases in addition to the transactions from the correct ledger.</p>
|
||||||
<p>If requested, it can additionally check the ledger for missing entries in the account state and transaction trees.</p>
|
<p>If requested, it can additionally check the ledger for missing entries in the account state and transaction trees.</p>
|
||||||
<p>To prevent the ledger cleaner from saturating the available I/O bandwidth and excessively polluting caches with ancient information, the ledger cleaner paces itself and does not attempt to get its work done quickly.</p>
|
<p>To prevent the ledger cleaner from saturating the available I/O bandwidth and excessively polluting caches with ancient information, the ledger cleaner paces itself and does not attempt to get its work done quickly.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md113"></a>
|
<h2><a class="anchor" id="autotoc_md115"></a>
|
||||||
Commands</h2>
|
Commands</h2>
|
||||||
<p>The ledger cleaner can be controlled and monitored with the <b>ledger_cleaner</b> RPC command. With no parameters, this command reports on the status of the ledger cleaner. This includes the range of ledgers it has been asked to process, the checks it is doing, and the number of errors it has found.</p>
|
<p>The ledger cleaner can be controlled and monitored with the <b>ledger_cleaner</b> RPC command. With no parameters, this command reports on the status of the ledger cleaner. This includes the range of ledgers it has been asked to process, the checks it is doing, and the number of errors it has found.</p>
|
||||||
<p>The ledger cleaner can be started, stopped, or have its behavior changed by the following RPC parameters:</p>
|
<p>The ledger cleaner can be started, stopped, or have its behavior changed by the following RPC parameters:</p>
|
||||||
@@ -273,7 +273,7 @@ Commands</h2>
|
|||||||
<p><b>fix_txns</b>: A boolean indicating whether to replace the SQLite transaction entries unconditionally</p>
|
<p><b>fix_txns</b>: A boolean indicating whether to replace the SQLite transaction entries unconditionally</p>
|
||||||
<p><b>check_nodes</b>: A boolean indicating whether to check the specified ledger(s) for missing nodes in the back end node store</p>
|
<p><b>check_nodes</b>: A boolean indicating whether to check the specified ledger(s) for missing nodes in the back end node store</p>
|
||||||
<hr />
|
<hr />
|
||||||
<h1><a class="anchor" id="autotoc_md115"></a>
|
<h1><a class="anchor" id="autotoc_md117"></a>
|
||||||
References</h1>
|
References</h1>
|
||||||
</div></div><!-- contents -->
|
</div></div><!-- contents -->
|
||||||
</div><!-- PageDoc -->
|
</div><!-- PageDoc -->
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ $(function() {
|
|||||||
<li><a href="#fee-escalation">Rapid Fee escalation</a></li>
|
<li><a href="#fee-escalation">Rapid Fee escalation</a></li>
|
||||||
<li><a href="#transaction-queue">The Transaction Queue</a></li>
|
<li><a href="#transaction-queue">The Transaction Queue</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
<h1><a class="anchor" id="autotoc_md117"></a>
|
<h1><a class="anchor" id="autotoc_md119"></a>
|
||||||
Fee Escalation</h1>
|
Fee Escalation</h1>
|
||||||
<p>The guiding principal of fee escalation is that when things are going smoothly, fees stay low, but as soon as high levels of traffic appear on the network, fees will grow quickly to extreme levels. This should dissuade malicious users from abusing the system, while giving legitimate users the ability to pay a higher fee to get high-priority transactions into the open ledger, even during unfavorable conditions.</p>
|
<p>The guiding principal of fee escalation is that when things are going smoothly, fees stay low, but as soon as high levels of traffic appear on the network, fees will grow quickly to extreme levels. This should dissuade malicious users from abusing the system, while giving legitimate users the ability to pay a higher fee to get high-priority transactions into the open ledger, even during unfavorable conditions.</p>
|
||||||
<p>How fees escalate:</p>
|
<p>How fees escalate:</p>
|
||||||
@@ -99,7 +99,7 @@ Fee Escalation</h1>
|
|||||||
<ul>
|
<ul>
|
||||||
<li>This example assumes a cold-start scenario, with a single, possibly malicious, user willing to pay arbitrary amounts to get transactions into the open ledger. It ignores the effects of the <a href="#transaction-queue">Transaction Queue</a>. Any lower fee level transactions submitted by other users at the same time as this user's transactions will go into the transaction queue, and will have the first opportunity to be applied to the <em>next</em> open ledger. The next section describes how that works in more detail.</li>
|
<li>This example assumes a cold-start scenario, with a single, possibly malicious, user willing to pay arbitrary amounts to get transactions into the open ledger. It ignores the effects of the <a href="#transaction-queue">Transaction Queue</a>. Any lower fee level transactions submitted by other users at the same time as this user's transactions will go into the transaction queue, and will have the first opportunity to be applied to the <em>next</em> open ledger. The next section describes how that works in more detail.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h1><a class="anchor" id="autotoc_md118"></a>
|
<h1><a class="anchor" id="autotoc_md120"></a>
|
||||||
Transaction Queue</h1>
|
Transaction Queue</h1>
|
||||||
<p>An integral part of making fee escalation work for users of the network is the transaction queue. The queue allows legitimate transactions to be considered by the network for future ledgers if the escalated open ledger fee gets too high. This allows users to submit low priority transactions with a low fee, and wait for high fees to drop. It also allows legitimate users to continue submitting transactions during high traffic periods, and give those transactions a much better chance to succeed.</p>
|
<p>An integral part of making fee escalation work for users of the network is the transaction queue. The queue allows legitimate transactions to be considered by the network for future ledgers if the escalated open ledger fee gets too high. This allows users to submit low priority transactions with a low fee, and wait for high fees to drop. It also allows legitimate users to continue submitting transactions during high traffic periods, and give those transactions a much better chance to succeed.</p>
|
||||||
<ol type="1">
|
<ol type="1">
|
||||||
@@ -123,9 +123,9 @@ Transaction Queue</h1>
|
|||||||
<li>none of the prior queued transactions affect the ability of subsequent transactions to claim a fee.</li>
|
<li>none of the prior queued transactions affect the ability of subsequent transactions to claim a fee.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Currently, there is an additional restriction that the queue cannot work with transactions using the <code>sfPreviousTxnID</code> or <code>sfAccountTxnID</code> fields. <code>sfPreviousTxnID</code> is deprecated and shouldn't be used anyway. Future development will make the queue aware of <code>sfAccountTxnID</code> mechanisms.</p>
|
<p>Currently, there is an additional restriction that the queue cannot work with transactions using the <code>sfPreviousTxnID</code> or <code>sfAccountTxnID</code> fields. <code>sfPreviousTxnID</code> is deprecated and shouldn't be used anyway. Future development will make the queue aware of <code>sfAccountTxnID</code> mechanisms.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md119"></a>
|
<h1><a class="anchor" id="autotoc_md121"></a>
|
||||||
Technical Details</h1>
|
Technical Details</h1>
|
||||||
<h2><a class="anchor" id="autotoc_md120"></a>
|
<h2><a class="anchor" id="autotoc_md122"></a>
|
||||||
Fee Level</h2>
|
Fee Level</h2>
|
||||||
<p>"Fee level" is used to allow the cost of different types of transactions to be compared directly. For a <a href="#reference-transaction">reference transaction</a>, the base fee level is 256. If a transaction is submitted with a higher <code>Fee</code> field, the fee level is scaled appropriately.</p>
|
<p>"Fee level" is used to allow the cost of different types of transactions to be compared directly. For a <a href="#reference-transaction">reference transaction</a>, the base fee level is 256. If a transaction is submitted with a higher <code>Fee</code> field, the fee level is scaled appropriately.</p>
|
||||||
<p>Examples, assuming a <a href="#reference-transaction">reference transaction</a> base fee of 10 drops:</p>
|
<p>Examples, assuming a <a href="#reference-transaction">reference transaction</a> base fee of 10 drops:</p>
|
||||||
@@ -135,17 +135,17 @@ Fee Level</h2>
|
|||||||
<li>A hypothetical future non-reference transaction with a base fee of 15 drops multi-signed with 5 signatures and <code>Fee=90</code> will have a fee level of <code>90 drop fee * 256 fee level / ((1tx + 5sigs) * 15 drop base fee) = 256 fee level</code>.</li>
|
<li>A hypothetical future non-reference transaction with a base fee of 15 drops multi-signed with 5 signatures and <code>Fee=90</code> will have a fee level of <code>90 drop fee * 256 fee level / ((1tx + 5sigs) * 15 drop base fee) = 256 fee level</code>.</li>
|
||||||
</ol>
|
</ol>
|
||||||
<p>This demonstrates that a simpler transaction paying less XRP can be more likely to get into the open ledger, or be sorted earlier in the queue than a more complex transaction paying more XRP.</p>
|
<p>This demonstrates that a simpler transaction paying less XRP can be more likely to get into the open ledger, or be sorted earlier in the queue than a more complex transaction paying more XRP.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md121"></a>
|
<h2><a class="anchor" id="autotoc_md123"></a>
|
||||||
Load Fee</h2>
|
Load Fee</h2>
|
||||||
<p>Each rippled server maintains a minimum cost threshold based on its current load. If you submit a transaction with a fee that is lower than the current load-based transaction cost of the rippled server, the server neither applies nor relays the transaction to its peers. A transaction is very unlikely to survive the consensus process unless its transaction fee value meets the requirements of a majority of servers.</p>
|
<p>Each rippled server maintains a minimum cost threshold based on its current load. If you submit a transaction with a fee that is lower than the current load-based transaction cost of the rippled server, the server neither applies nor relays the transaction to its peers. A transaction is very unlikely to survive the consensus process unless its transaction fee value meets the requirements of a majority of servers.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md122"></a>
|
<h2><a class="anchor" id="autotoc_md124"></a>
|
||||||
Reference Transaction</h2>
|
Reference Transaction</h2>
|
||||||
<p>In this document, a "Reference Transaction" is any currently implemented single-signed transaction (eg. Payment, Account Set, Offer Create, etc) that requires a fee.</p>
|
<p>In this document, a "Reference Transaction" is any currently implemented single-signed transaction (eg. Payment, Account Set, Offer Create, etc) that requires a fee.</p>
|
||||||
<p>In the future, there may be other transaction types that require more (or less) work for rippled to process. Those transactions may have a higher (or lower) base fee, requiring a correspondingly higher (or lower) fee to get into the same position as a reference transaction.</p>
|
<p>In the future, there may be other transaction types that require more (or less) work for rippled to process. Those transactions may have a higher (or lower) base fee, requiring a correspondingly higher (or lower) fee to get into the same position as a reference transaction.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md123"></a>
|
<h2><a class="anchor" id="autotoc_md125"></a>
|
||||||
Consensus Health</h2>
|
Consensus Health</h2>
|
||||||
<p>For consensus to be considered healthy, the consensus process must take less than 5 seconds. This time limit was chosen based on observed past behavior of the network. Note that this is not necessarily the time between ledger closings, as consensus usually starts some amount of time after a ledger opens.</p>
|
<p>For consensus to be considered healthy, the consensus process must take less than 5 seconds. This time limit was chosen based on observed past behavior of the network. Note that this is not necessarily the time between ledger closings, as consensus usually starts some amount of time after a ledger opens.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md124"></a>
|
<h2><a class="anchor" id="autotoc_md126"></a>
|
||||||
Other Constants</h2>
|
Other Constants</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><em>Base fee transaction limit per ledger</em>. The minimum value of 5 was chosen to ensure the limit never gets so small that the ledger becomes unusable. The "target" value of 50 was chosen so the limit never gets large enough to invite abuse, but keeps up if the network stays healthy and active. These exact values were chosen experimentally, and can easily change in the future.</li>
|
<li><em>Base fee transaction limit per ledger</em>. The minimum value of 5 was chosen to ensure the limit never gets so small that the ledger becomes unusable. The "target" value of 50 was chosen so the limit never gets large enough to invite abuse, but keeps up if the network stays healthy and active. These exact values were chosen experimentally, and can easily change in the future.</li>
|
||||||
@@ -157,7 +157,7 @@ Other Constants</h2>
|
|||||||
<li><em>Minimum last ledger sequence buffer</em>. If a transaction has a <code>LastLedgerSequence</code> value, and cannot be processed into the open ledger, that <code>LastLedgerSequence</code> must be at least 2 more than the sequence number of the open ledger to be considered for the queue. The value was chosen to provide a balance between letting the user control the lifespan of the transaction, and giving a queued transaction a chance to get processed out of the queue before getting discarded, particularly since it may have dependent transactions also in the queue, which will never succeed if this one is discarded.</li>
|
<li><em>Minimum last ledger sequence buffer</em>. If a transaction has a <code>LastLedgerSequence</code> value, and cannot be processed into the open ledger, that <code>LastLedgerSequence</code> must be at least 2 more than the sequence number of the open ledger to be considered for the queue. The value was chosen to provide a balance between letting the user control the lifespan of the transaction, and giving a queued transaction a chance to get processed out of the queue before getting discarded, particularly since it may have dependent transactions also in the queue, which will never succeed if this one is discarded.</li>
|
||||||
<li><em>Replaced transaction fee increase</em>. Any transaction in the queue can be replaced by another transaction with the same sequence number and at least a 25% higher fee level. The 25% increase is intended to cover the resource cost incurred by broadcasting the original transaction to the network. This value was chosen experimentally, and can easily change in the future.</li>
|
<li><em>Replaced transaction fee increase</em>. Any transaction in the queue can be replaced by another transaction with the same sequence number and at least a 25% higher fee level. The 25% increase is intended to cover the resource cost incurred by broadcasting the original transaction to the network. This value was chosen experimentally, and can easily change in the future.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h2><a class="anchor" id="autotoc_md125"></a>
|
<h2><a class="anchor" id="autotoc_md127"></a>
|
||||||
<tt>fee</tt> command</h2>
|
<tt>fee</tt> command</h2>
|
||||||
<p><b>The <code>fee</code> RPC and WebSocket command is still experimental, and may change without warning.</b></p>
|
<p><b>The <code>fee</code> RPC and WebSocket command is still experimental, and may change without warning.</b></p>
|
||||||
<p><code>fee</code> takes no parameters, and returns information about the current local <a href="#fee-escalation">fee escalation</a> and <a href="#transaction-queue">transaction queue</a> state as both fee levels and drops. The drop values assume a single-singed reference transaction. It is up to the user to compute the necessary fees for other types of transactions. (E.g. multiply all drop values by 5 for a multi-signed transaction with 4 signatures.)</p>
|
<p><code>fee</code> takes no parameters, and returns information about the current local <a href="#fee-escalation">fee escalation</a> and <a href="#transaction-queue">transaction queue</a> state as both fee levels and drops. The drop values assume a single-singed reference transaction. It is up to the user to compute the necessary fees for other types of transactions. (E.g. multiply all drop values by 5 for a multi-signed transaction with 4 signatures.)</p>
|
||||||
@@ -183,7 +183,7 @@ Other Constants</h2>
|
|||||||
<div class="line"> }</div>
|
<div class="line"> }</div>
|
||||||
<div class="line"> }</div>
|
<div class="line"> }</div>
|
||||||
<div class="line">}</div>
|
<div class="line">}</div>
|
||||||
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md126"></a>
|
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md128"></a>
|
||||||
<a href="https://xrpl.org/server_info.html"><tt>server_info</tt></a> command</h2>
|
<a href="https://xrpl.org/server_info.html"><tt>server_info</tt></a> command</h2>
|
||||||
<p><b>The fields listed here are still experimental, and may change without warning.</b></p>
|
<p><b>The fields listed here are still experimental, and may change without warning.</b></p>
|
||||||
<p>Up to two fields in <code>server_info</code> output are related to fee escalation.</p>
|
<p>Up to two fields in <code>server_info</code> output are related to fee escalation.</p>
|
||||||
@@ -192,7 +192,7 @@ Other Constants</h2>
|
|||||||
<li><code>load_factor_fee_queue</code>: If the queue is full, this is the factor on base transaction cost that a transaction must pay to get into the queue. If not full, the value is 1, so will not be returned.</li>
|
<li><code>load_factor_fee_queue</code>: If the queue is full, this is the factor on base transaction cost that a transaction must pay to get into the queue. If not full, the value is 1, so will not be returned.</li>
|
||||||
</ol>
|
</ol>
|
||||||
<p>In all cases, the transaction fee must be high enough to overcome both <code>load_factor_fee_queue</code> and <code>load_factor</code> to be considered. It does not need to overcome <code>load_factor_fee_escalation</code>, though if it does not, it is more likely to be queued than immediately processed into the open ledger.</p>
|
<p>In all cases, the transaction fee must be high enough to overcome both <code>load_factor_fee_queue</code> and <code>load_factor</code> to be considered. It does not need to overcome <code>load_factor_fee_escalation</code>, though if it does not, it is more likely to be queued than immediately processed into the open ledger.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md127"></a>
|
<h2><a class="anchor" id="autotoc_md129"></a>
|
||||||
<a href="https://xrpl.org/server_state.html"><tt>server_state</tt></a> command</h2>
|
<a href="https://xrpl.org/server_state.html"><tt>server_state</tt></a> command</h2>
|
||||||
<p><b>The fields listed here are still experimental, and may change without warning.</b></p>
|
<p><b>The fields listed here are still experimental, and may change without warning.</b></p>
|
||||||
<p>Three fields in <code>server_state</code> output are related to fee escalation.</p>
|
<p>Three fields in <code>server_state</code> output are related to fee escalation.</p>
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ $(function() {
|
|||||||
<div class="textblock"><p>The Ripple payment protocol enforces a fee schedule expressed in units of the native currency, XRP. Fees for transactions are paid directly from the account owner. There are also reserve requirements for each item that occupies storage in the ledger. The reserve fee schedule contains both a per-account reserve, and a per-owned-item reserve. The items an account may own include active offers, trust lines, and tickets.</p>
|
<div class="textblock"><p>The Ripple payment protocol enforces a fee schedule expressed in units of the native currency, XRP. Fees for transactions are paid directly from the account owner. There are also reserve requirements for each item that occupies storage in the ledger. The reserve fee schedule contains both a per-account reserve, and a per-owned-item reserve. The items an account may own include active offers, trust lines, and tickets.</p>
|
||||||
<p>Validators may vote to increase fees if they feel that the network is charging too little. They may also vote to decrease fees if the fees are too costly relative to the value the network provides. One common case where a validator may want to change fees is when the value of the native currency XRP fluctuates relative to other currencies.</p>
|
<p>Validators may vote to increase fees if they feel that the network is charging too little. They may also vote to decrease fees if the fees are too costly relative to the value the network provides. One common case where a validator may want to change fees is when the value of the native currency XRP fluctuates relative to other currencies.</p>
|
||||||
<p>The fee voting mechanism takes place every 256 ledgers ("voting ledgers"). In a voting ledger, each validator takes a position on what they think the fees should be. The consensus process converges on the majority position, and in subsequent ledgers a new fee schedule is enacted.</p>
|
<p>The fee voting mechanism takes place every 256 ledgers ("voting ledgers"). In a voting ledger, each validator takes a position on what they think the fees should be. The consensus process converges on the majority position, and in subsequent ledgers a new fee schedule is enacted.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md129"></a>
|
<h1><a class="anchor" id="autotoc_md131"></a>
|
||||||
Consensus</h1>
|
Consensus</h1>
|
||||||
<p>The Ripple consensus algorithm allows distributed participants to arrive at the same answer for yes/no questions. The canonical case for consensus is whether or not a particular transaction is included in the ledger. Fees present a more difficult challenge, since the decision on the new fee is not a yes or no question.</p>
|
<p>The Ripple consensus algorithm allows distributed participants to arrive at the same answer for yes/no questions. The canonical case for consensus is whether or not a particular transaction is included in the ledger. Fees present a more difficult challenge, since the decision on the new fee is not a yes or no question.</p>
|
||||||
<p>To convert validators' positions on fees into a yes or no question that can be converged in the consensus process, the following algorithm is used:</p>
|
<p>To convert validators' positions on fees into a yes or no question that can be converged in the consensus process, the following algorithm is used:</p>
|
||||||
@@ -79,11 +79,11 @@ Consensus</h1>
|
|||||||
<li>The consensus process is applied to these fee-setting transactions as normal. Each transaction is either included in the ledger or not. In most cases, one fee setting transaction will make it in while the others are rejected. In some rare cases more than one fee setting transaction will make it in. The last one to be applied will take effect. This is harmless since a majority of validators still agreed on it.</li>
|
<li>The consensus process is applied to these fee-setting transactions as normal. Each transaction is either included in the ledger or not. In most cases, one fee setting transaction will make it in while the others are rejected. In some rare cases more than one fee setting transaction will make it in. The last one to be applied will take effect. This is harmless since a majority of validators still agreed on it.</li>
|
||||||
<li>After the voting ledger has been validated, future pseudo transactions before the next voting ledger are rejected as fee setting transactions may only appear in voting ledgers.</li>
|
<li>After the voting ledger has been validated, future pseudo transactions before the next voting ledger are rejected as fee setting transactions may only appear in voting ledgers.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h1><a class="anchor" id="autotoc_md130"></a>
|
<h1><a class="anchor" id="autotoc_md132"></a>
|
||||||
Configuration</h1>
|
Configuration</h1>
|
||||||
<p>A validating instance of rippled uses information in the configuration file to determine how it wants to vote on the fee schedule. It is the responsibility of the administrator to set these values.</p>
|
<p>A validating instance of rippled uses information in the configuration file to determine how it wants to vote on the fee schedule. It is the responsibility of the administrator to set these values.</p>
|
||||||
<hr />
|
<hr />
|
||||||
<h1><a class="anchor" id="autotoc_md132"></a>
|
<h1><a class="anchor" id="autotoc_md134"></a>
|
||||||
Amendment</h1>
|
Amendment</h1>
|
||||||
<p>An Amendment is a new or proposed change to a ledger rule. Ledger rules affect transaction processing and consensus; peers must use the same set of rules for consensus to succeed, otherwise different instances of rippled will get different results. Amendments can be almost anything but they must be accepted by a network majority through a consensus process before they are utilized. An Amendment must receive at least an 80% approval rate from validating nodes for a period of two weeks before being accepted. The following example outlines the process of an Amendment from its conception to approval and usage.</p>
|
<p>An Amendment is a new or proposed change to a ledger rule. Ledger rules affect transaction processing and consensus; peers must use the same set of rules for consensus to succeed, otherwise different instances of rippled will get different results. Amendments can be almost anything but they must be accepted by a network majority through a consensus process before they are utilized. An Amendment must receive at least an 80% approval rate from validating nodes for a period of two weeks before being accepted. The following example outlines the process of an Amendment from its conception to approval and usage.</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -99,7 +99,7 @@ Amendment</h1>
|
|||||||
<p>If an amendment holds majority status for two weeks, validators will introduce a pseudo-transaction to enable the amendment.</p>
|
<p>If an amendment holds majority status for two weeks, validators will introduce a pseudo-transaction to enable the amendment.</p>
|
||||||
<p>All amendments are assumed to be critical and irreversible. Thus there is no mechanism to disable or revoke an amendment, nor is there a way for a server to operate while an amendment it does not understand is enabled.</p>
|
<p>All amendments are assumed to be critical and irreversible. Thus there is no mechanism to disable or revoke an amendment, nor is there a way for a server to operate while an amendment it does not understand is enabled.</p>
|
||||||
<hr />
|
<hr />
|
||||||
<h1><a class="anchor" id="autotoc_md134"></a>
|
<h1><a class="anchor" id="autotoc_md136"></a>
|
||||||
SHAMapStore: Online Delete</h1>
|
SHAMapStore: Online Delete</h1>
|
||||||
<p>Optional online deletion happens through the SHAMapStore. Records are deleted from disk based on ledger sequence number. These records reside in the key-value database as well as in the SQLite ledger and transaction databases. Without online deletion storage usage grows without bounds. It can only be pruned by stopping, manually deleting data, and restarting the server. Online deletion requires less operator intervention to manage the server.</p>
|
<p>Optional online deletion happens through the SHAMapStore. Records are deleted from disk based on ledger sequence number. These records reside in the key-value database as well as in the SQLite ledger and transaction databases. Without online deletion storage usage grows without bounds. It can only be pruned by stopping, manually deleting data, and restarting the server. Online deletion requires less operator intervention to manage the server.</p>
|
||||||
<p>The main mechanism to delete data from the key-value database is to keep two databases open at all times. One database has all writes directed to it. The other database has recent archival data from just prior to that from the current writable database. Upon rotation, the archival database is deleted. The writable database becomes archival, and a brand new database becomes writable. To ensure that no necessary data for transaction processing is lost, a variety of steps occur, including copying the contents of an entire ledger's account state map, clearing caches, and copying the contents of (freshening) other caches.</p>
|
<p>The main mechanism to delete data from the key-value database is to keep two databases open at all times. One database has all writes directed to it. The other database has recent archival data from just prior to that from the current writable database. Upon rotation, the archival database is deleted. The writable database becomes archival, and a brand new database becomes writable. To ensure that no necessary data for transaction processing is lost, a variety of steps occur, including copying the contents of an entire ledger's account state map, clearing caches, and copying the contents of (freshening) other caches.</p>
|
||||||
|
|||||||
@@ -71,15 +71,15 @@ $(function() {
|
|||||||
<li>The base class <code>RelationalDatabase</code> is inherited by derived classes that each provide an interface for operating on distinct relational database systems.</li>
|
<li>The base class <code>RelationalDatabase</code> is inherited by derived classes that each provide an interface for operating on distinct relational database systems.</li>
|
||||||
<li>For future use, the shard store will be used if the node store is absent.</li>
|
<li>For future use, the shard store will be used if the node store is absent.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h1><a class="anchor" id="autotoc_md138"></a>
|
<h1><a class="anchor" id="autotoc_md140"></a>
|
||||||
Overview</h1>
|
Overview</h1>
|
||||||
<p>Firstly, the interface <code>RelationalDatabase</code> is inherited by the classes <code>SQLiteDatabase</code> and <code>PostgresDatabase</code> which are used to operate the software's main data store (for storing transactions, accounts, ledgers, etc.). Secondly, the files under the <code>detail</code> directory provide supplementary functions that are used by these derived classes to access the underlying databases. Lastly, the remaining files in the interface (located at the top level of the module) are used by varied parts of the software to access any secondary relational databases.</p>
|
<p>Firstly, the interface <code>RelationalDatabase</code> is inherited by the classes <code>SQLiteDatabase</code> and <code>PostgresDatabase</code> which are used to operate the software's main data store (for storing transactions, accounts, ledgers, etc.). Secondly, the files under the <code>detail</code> directory provide supplementary functions that are used by these derived classes to access the underlying databases. Lastly, the remaining files in the interface (located at the top level of the module) are used by varied parts of the software to access any secondary relational databases.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md139"></a>
|
<h1><a class="anchor" id="autotoc_md141"></a>
|
||||||
Configuration</h1>
|
Configuration</h1>
|
||||||
<p>The config section <code>[relational_db]</code> has a property named <code>backend</code> whose value designates which database implementation will be used for node or shard databases. Presently the only valid value for this property is <code>sqlite</code>:</p>
|
<p>The config section <code>[relational_db]</code> has a property named <code>backend</code> whose value designates which database implementation will be used for node or shard databases. Presently the only valid value for this property is <code>sqlite</code>:</p>
|
||||||
<div class="fragment"><div class="line">[relational_db]</div>
|
<div class="fragment"><div class="line">[relational_db]</div>
|
||||||
<div class="line">backend=sqlite</div>
|
<div class="line">backend=sqlite</div>
|
||||||
</div><!-- fragment --><h1><a class="anchor" id="autotoc_md140"></a>
|
</div><!-- fragment --><h1><a class="anchor" id="autotoc_md142"></a>
|
||||||
Source Files</h1>
|
Source Files</h1>
|
||||||
<p>The Relational Database Interface consists of the following directory structure (as of November 2021):</p>
|
<p>The Relational Database Interface consists of the following directory structure (as of November 2021):</p>
|
||||||
<div class="fragment"><div class="line">src/ripple/app/rdb/</div>
|
<div class="fragment"><div class="line">src/ripple/app/rdb/</div>
|
||||||
@@ -113,7 +113,7 @@ Source Files</h1>
|
|||||||
<div class="line">├── UnitaryShard.h</div>
|
<div class="line">├── UnitaryShard.h</div>
|
||||||
<div class="line">├── Vacuum.h</div>
|
<div class="line">├── Vacuum.h</div>
|
||||||
<div class="line">└── Wallet.h</div>
|
<div class="line">└── Wallet.h</div>
|
||||||
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md141"></a>
|
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md143"></a>
|
||||||
File Contents</h2>
|
File Contents</h2>
|
||||||
<table class="markdownTable">
|
<table class="markdownTable">
|
||||||
<tr class="markdownTableHead">
|
<tr class="markdownTableHead">
|
||||||
@@ -145,10 +145,10 @@ File Contents</h2>
|
|||||||
<tr class="markdownTableRowOdd">
|
<tr class="markdownTableRowOdd">
|
||||||
<td class="markdownTableBodyNone"><code>Wallet.[h\|cpp]</code> </td><td class="markdownTableBodyNone">Defines/Implements methods for interacting with Wallet SQLite databases </td></tr>
|
<td class="markdownTableBodyNone"><code>Wallet.[h\|cpp]</code> </td><td class="markdownTableBodyNone">Defines/Implements methods for interacting with Wallet SQLite databases </td></tr>
|
||||||
</table>
|
</table>
|
||||||
<h1><a class="anchor" id="autotoc_md142"></a>
|
<h1><a class="anchor" id="autotoc_md144"></a>
|
||||||
Classes</h1>
|
Classes</h1>
|
||||||
<p>The abstract class <code>RelationalDatabase</code> is the primary class of the Relational Database Interface and is defined in the eponymous header file. This class provides a static method <code>init()</code> which, when invoked, creates a concrete instance of a derived class whose type is specified by the system configuration. All other methods in the class are virtual. Presently there exist two classes that derive from <code>RelationalDatabase</code>, namely <code>SQLiteDatabase</code> and <code>PostgresDatabase</code>.</p>
|
<p>The abstract class <code>RelationalDatabase</code> is the primary class of the Relational Database Interface and is defined in the eponymous header file. This class provides a static method <code>init()</code> which, when invoked, creates a concrete instance of a derived class whose type is specified by the system configuration. All other methods in the class are virtual. Presently there exist two classes that derive from <code>RelationalDatabase</code>, namely <code>SQLiteDatabase</code> and <code>PostgresDatabase</code>.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md143"></a>
|
<h1><a class="anchor" id="autotoc_md145"></a>
|
||||||
Database Methods</h1>
|
Database Methods</h1>
|
||||||
<p>The Relational Database Interface provides three categories of methods for interacting with databases:</p>
|
<p>The Relational Database Interface provides three categories of methods for interacting with databases:</p>
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
@@ -66,12 +66,12 @@ $(function() {
|
|||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><p>Reporting mode is a special operating mode of rippled, designed to handle RPCs for validated data. A server running in reporting mode does not connect to the p2p network, but rather extracts validated data from a node that is connected to the p2p network. To run rippled in reporting mode, you must also run a separate rippled node in p2p mode, to use as an ETL source. Multiple reporting nodes can share access to the same network accessible databases (Postgres and Cassandra); at any given time, only one reporting node will be performing ETL and writing to the databases, while the others simply read from the databases. A server running in reporting mode will forward any requests that require access to the p2p network to a p2p node.</p>
|
<div class="textblock"><p>Reporting mode is a special operating mode of rippled, designed to handle RPCs for validated data. A server running in reporting mode does not connect to the p2p network, but rather extracts validated data from a node that is connected to the p2p network. To run rippled in reporting mode, you must also run a separate rippled node in p2p mode, to use as an ETL source. Multiple reporting nodes can share access to the same network accessible databases (Postgres and Cassandra); at any given time, only one reporting node will be performing ETL and writing to the databases, while the others simply read from the databases. A server running in reporting mode will forward any requests that require access to the p2p network to a p2p node.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md144"></a>
|
<h1><a class="anchor" id="autotoc_md146"></a>
|
||||||
Reporting ETL</h1>
|
Reporting ETL</h1>
|
||||||
<p>A single reporting node has one or more ETL sources, specified in the config file. A reporting node will subscribe to the "ledgers" stream of each of the ETL sources. This stream sends a message whenever a new ledger is validated. Upon receiving a message on the stream, reporting will then fetch the data associated with the newly validated ledger from one of the ETL sources. The fetch is performed via a gRPC request ("GetLedger"). This request returns the ledger header, transactions+metadata blobs, and every ledger object added/modified/deleted as part of this ledger. ETL then writes all of this data to the databases, and moves on to the next ledger. ETL does not apply transactions, but rather extracts the already computed results of those transactions (all of the added/modified/deleted SHAMap leaf nodes of the state tree). The new SHAMap inner nodes are computed by the ETL writer; this computation mainly involves manipulating child pointers and recomputing hashes, logic which is buried inside of SHAMap.</p>
|
<p>A single reporting node has one or more ETL sources, specified in the config file. A reporting node will subscribe to the "ledgers" stream of each of the ETL sources. This stream sends a message whenever a new ledger is validated. Upon receiving a message on the stream, reporting will then fetch the data associated with the newly validated ledger from one of the ETL sources. The fetch is performed via a gRPC request ("GetLedger"). This request returns the ledger header, transactions+metadata blobs, and every ledger object added/modified/deleted as part of this ledger. ETL then writes all of this data to the databases, and moves on to the next ledger. ETL does not apply transactions, but rather extracts the already computed results of those transactions (all of the added/modified/deleted SHAMap leaf nodes of the state tree). The new SHAMap inner nodes are computed by the ETL writer; this computation mainly involves manipulating child pointers and recomputing hashes, logic which is buried inside of SHAMap.</p>
|
||||||
<p>If the database is entirely empty, ETL must download an entire ledger in full (as opposed to just the diff, as described above). This download is done via the "GetLedgerData" gRPC request. "GetLedgerData" allows clients to page through an entire ledger over several RPC calls. ETL will page through an entire ledger, and write each object to the database.</p>
|
<p>If the database is entirely empty, ETL must download an entire ledger in full (as opposed to just the diff, as described above). This download is done via the "GetLedgerData" gRPC request. "GetLedgerData" allows clients to page through an entire ledger over several RPC calls. ETL will page through an entire ledger, and write each object to the database.</p>
|
||||||
<p>If the database is not empty, the reporting node will first come up in a "soft" read-only mode. In read-only mode, the server does not perform ETL and simply publishes new ledgers as they are written to the database. If the database is not updated within a certain time period (currently hard coded at 20 seconds), the reporting node will begin the ETL process and start writing to the database. Postgres will report an error when trying to write a record with a key that already exists. ETL uses this error to determine that another process is writing to the database, and subsequently falls back to a soft read-only mode. Reporting nodes can also operate in strict read-only mode, in which case they will never write to the database.</p>
|
<p>If the database is not empty, the reporting node will first come up in a "soft" read-only mode. In read-only mode, the server does not perform ETL and simply publishes new ledgers as they are written to the database. If the database is not updated within a certain time period (currently hard coded at 20 seconds), the reporting node will begin the ETL process and start writing to the database. Postgres will report an error when trying to write a record with a key that already exists. ETL uses this error to determine that another process is writing to the database, and subsequently falls back to a soft read-only mode. Reporting nodes can also operate in strict read-only mode, in which case they will never write to the database.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md145"></a>
|
<h1><a class="anchor" id="autotoc_md147"></a>
|
||||||
Database Nuances</h1>
|
Database Nuances</h1>
|
||||||
<p>The database schema for reporting mode does not allow any history gaps. Attempting to write a ledger to a non-empty database where the previous ledger does not exist will return an error.</p>
|
<p>The database schema for reporting mode does not allow any history gaps. Attempting to write a ledger to a non-empty database where the previous ledger does not exist will return an error.</p>
|
||||||
<p>The databases must be set up prior to running reporting mode. This requires creating the Postgres database, and setting up the Cassandra keyspace. Reporting mode will create the objects table in Cassandra if the table does not yet exist.</p>
|
<p>The databases must be set up prior to running reporting mode. This requires creating the Postgres database, and setting up the Cassandra keyspace. Reporting mode will create the objects table in Cassandra if the table does not yet exist.</p>
|
||||||
@@ -86,11 +86,11 @@ Database Nuances</h1>
|
|||||||
<div class="line">reporting=$ truncate table ledgers cascade;</div>
|
<div class="line">reporting=$ truncate table ledgers cascade;</div>
|
||||||
</div><!-- fragment --><div class="fragment"><div class="line">$ cqlsh [host] [port]</div>
|
</div><!-- fragment --><div class="fragment"><div class="line">$ cqlsh [host] [port]</div>
|
||||||
<div class="line">> truncate table objects;</div>
|
<div class="line">> truncate table objects;</div>
|
||||||
</div><!-- fragment --><h1><a class="anchor" id="autotoc_md146"></a>
|
</div><!-- fragment --><h1><a class="anchor" id="autotoc_md148"></a>
|
||||||
Proxy</h1>
|
Proxy</h1>
|
||||||
<p>RPCs that require access to the p2p network and/or the open ledger are forwarded from the reporting node to one of the ETL sources. The request is not processed prior to forwarding, and the response is delivered as-is to the client. Reporting will forward any requests that always require p2p/open ledger access (fee and submit, for instance). In addition, any request that explicitly requests data from the open or closed ledger (via setting "ledger_index":"current" or "ledger_index":"closed"), will be forwarded to a p2p node.</p>
|
<p>RPCs that require access to the p2p network and/or the open ledger are forwarded from the reporting node to one of the ETL sources. The request is not processed prior to forwarding, and the response is delivered as-is to the client. Reporting will forward any requests that always require p2p/open ledger access (fee and submit, for instance). In addition, any request that explicitly requests data from the open or closed ledger (via setting "ledger_index":"current" or "ledger_index":"closed"), will be forwarded to a p2p node.</p>
|
||||||
<p>For the stream "transactions_proposed" (AKA "rt_transactions"), reporting subscribes to the "transactions_proposed" streams of each ETL source, and then forwards those messages to any clients subscribed to the same stream on the reporting node. A reporting node will subscribe to the stream on each ETL source, but will only forward the messages from one of the streams at any given time (to avoid sending the same message more than once to the same client).</p>
|
<p>For the stream "transactions_proposed" (AKA "rt_transactions"), reporting subscribes to the "transactions_proposed" streams of each ETL source, and then forwards those messages to any clients subscribed to the same stream on the reporting node. A reporting node will subscribe to the stream on each ETL source, but will only forward the messages from one of the streams at any given time (to avoid sending the same message more than once to the same client).</p>
|
||||||
<h1><a class="anchor" id="autotoc_md147"></a>
|
<h1><a class="anchor" id="autotoc_md149"></a>
|
||||||
API changes</h1>
|
API changes</h1>
|
||||||
<p>A reporting node defaults to only returning validated data. If a ledger is not specified, the most recently validated ledger is used. This is in contrast to the normal rippled behavior, where the open ledger is used by default.</p>
|
<p>A reporting node defaults to only returning validated data. If a ledger is not specified, the most recently validated ledger is used. This is in contrast to the normal rippled behavior, where the open ledger is used by default.</p>
|
||||||
<p>Reporting will reject all subscribe requests for streams "server", "manifests", "validations", "peer_status" and "consensus". </p>
|
<p>Reporting will reject all subscribe requests for streams "server", "manifests", "validations", "peer_status" and "consensus". </p>
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ $(function() {
|
|||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><p>Utility functions and classes.</p>
|
<div class="textblock"><p>Utility functions and classes.</p>
|
||||||
<p>ripple/basic should contain no dependencies on other modules.</p>
|
<p>ripple/basic should contain no dependencies on other modules.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md149"></a>
|
<h1><a class="anchor" id="autotoc_md151"></a>
|
||||||
Choosing a rippled container.</h1>
|
Choosing a rippled container.</h1>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code><a class="elRef" href="http://en.cppreference.com/w/cpp/container/vector.html" title="STL class.">std::vector</a></code><ul>
|
<li><code><a class="elRef" href="http://en.cppreference.com/w/cpp/container/vector.html" title="STL class.">std::vector</a></code><ul>
|
||||||
|
|||||||
@@ -65,10 +65,10 @@ $(function() {
|
|||||||
<div class="title">Shard Downloader </div> </div>
|
<div class="title">Shard Downloader </div> </div>
|
||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><h1><a class="anchor" id="autotoc_md155"></a>
|
<div class="textblock"><h1><a class="anchor" id="autotoc_md157"></a>
|
||||||
Overview</h1>
|
Overview</h1>
|
||||||
<p>This document describes mechanics of the <code>HTTPDownloader</code>, a class that performs the task of downloading shards from remote web servers via HTTP. The downloader utilizes a strand (<code>boost::asio::io_service::strand</code>) to ensure that downloads are never executed concurrently. Hence, if a download is in progress when another download is initiated, the second download will be queued and invoked only when the first download is completed.</p>
|
<p>This document describes mechanics of the <code>HTTPDownloader</code>, a class that performs the task of downloading shards from remote web servers via HTTP. The downloader utilizes a strand (<code>boost::asio::io_service::strand</code>) to ensure that downloads are never executed concurrently. Hence, if a download is in progress when another download is initiated, the second download will be queued and invoked only when the first download is completed.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md156"></a>
|
<h1><a class="anchor" id="autotoc_md158"></a>
|
||||||
Motivation</h1>
|
Motivation</h1>
|
||||||
<p>In March 2020 the downloader was modified to include some key features:</p>
|
<p>In March 2020 the downloader was modified to include some key features:</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -76,7 +76,7 @@ Motivation</h1>
|
|||||||
<li>The ability to resume partial downloads after a crash or shutdown.</li>
|
<li>The ability to resume partial downloads after a crash or shutdown.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>This document was created to document the changes introduced by this change.</p>
|
<p>This document was created to document the changes introduced by this change.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md157"></a>
|
<h1><a class="anchor" id="autotoc_md159"></a>
|
||||||
Classes</h1>
|
Classes</h1>
|
||||||
<p>Much of the shard downloading process concerns the following classes:</p>
|
<p>Much of the shard downloading process concerns the following classes:</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -107,19 +107,19 @@ Classes</h1>
|
|||||||
<p class="startli">This class defines a custom message body type, allowing an <code><a href="http::response_parser">http::response_parser</a></code> to write to an SQLite database rather than to a flat file. This class is discussed in further detail in the Recovery section.</p>
|
<p class="startli">This class defines a custom message body type, allowing an <code><a href="http::response_parser">http::response_parser</a></code> to write to an SQLite database rather than to a flat file. This class is discussed in further detail in the Recovery section.</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h1><a class="anchor" id="autotoc_md158"></a>
|
<h1><a class="anchor" id="autotoc_md160"></a>
|
||||||
Graceful Shutdowns & Recovery</h1>
|
Graceful Shutdowns & Recovery</h1>
|
||||||
<p>This section describes in greater detail how the shutdown and recovery features of the downloader are implemented in C++ using the <code><a class="el" href="namespaceboost_1_1asio.html">boost::asio</a></code> framework.</p>
|
<p>This section describes in greater detail how the shutdown and recovery features of the downloader are implemented in C++ using the <code><a class="el" href="namespaceboost_1_1asio.html">boost::asio</a></code> framework.</p>
|
||||||
<h4><a class="anchor" id="autotoc_md159"></a>
|
<h4><a class="anchor" id="autotoc_md161"></a>
|
||||||
Member Variables:</h4>
|
Member Variables:</h4>
|
||||||
<p>The variables shown here are members of the <code>HTTPDownloader</code> class and will be used in the following code examples.</p>
|
<p>The variables shown here are members of the <code>HTTPDownloader</code> class and will be used in the following code examples.</p>
|
||||||
<div class="fragment"><div class="line"> {c++}</div>
|
<div class="fragment"><div class="line"> {c++}</div>
|
||||||
<div class="line">std::unique_ptr<HTTPStream> stream_;</div>
|
<div class="line">std::unique_ptr<HTTPStream> stream_;</div>
|
||||||
<div class="line">std::condition_variable c_;</div>
|
<div class="line">std::condition_variable c_;</div>
|
||||||
<div class="line">std::atomic<bool> stop_;</div>
|
<div class="line">std::atomic<bool> stop_;</div>
|
||||||
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md160"></a>
|
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md162"></a>
|
||||||
Graceful Shutdowns</h2>
|
Graceful Shutdowns</h2>
|
||||||
<h4><a class="anchor" id="autotoc_md161"></a>
|
<h4><a class="anchor" id="autotoc_md163"></a>
|
||||||
Thread 1:</h4>
|
Thread 1:</h4>
|
||||||
<p>A graceful shutdown begins when the <code>stop()</code> method of the <code>ShardArchiveHandler</code> is invoked:</p>
|
<p>A graceful shutdown begins when the <code>stop()</code> method of the <code>ShardArchiveHandler</code> is invoked:</p>
|
||||||
<div class="fragment"><div class="line"> {c++}</div>
|
<div class="fragment"><div class="line"> {c++}</div>
|
||||||
@@ -155,7 +155,7 @@ Thread 1:</h4>
|
|||||||
<div class="line"> });</div>
|
<div class="line"> });</div>
|
||||||
<div class="line"> }</div>
|
<div class="line"> }</div>
|
||||||
<div class="line">}</div>
|
<div class="line">}</div>
|
||||||
</div><!-- fragment --><h4><a class="anchor" id="autotoc_md162"></a>
|
</div><!-- fragment --><h4><a class="anchor" id="autotoc_md164"></a>
|
||||||
Thread 2:</h4>
|
Thread 2:</h4>
|
||||||
<p>The graceful shutdown is realized when the thread executing the download polls <code>stop_</code> after this variable has been set to <code>true</code>. Polling occurs while the file is being downloaded, in between calls to <code>async_read_some()</code>. The stop takes effect when the socket is closed and the handler function ( <code>do_session()</code> ) is exited.</p>
|
<p>The graceful shutdown is realized when the thread executing the download polls <code>stop_</code> after this variable has been set to <code>true</code>. Polling occurs while the file is being downloaded, in between calls to <code>async_read_some()</code>. The stop takes effect when the socket is closed and the handler function ( <code>do_session()</code> ) is exited.</p>
|
||||||
<div class="fragment"><div class="line"> {c++}</div>
|
<div class="fragment"><div class="line"> {c++}</div>
|
||||||
@@ -176,10 +176,10 @@ Thread 2:</h4>
|
|||||||
<div class="line"> </div>
|
<div class="line"> </div>
|
||||||
<div class="line"> break;</div>
|
<div class="line"> break;</div>
|
||||||
<div class="line">}</div>
|
<div class="line">}</div>
|
||||||
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md163"></a>
|
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md165"></a>
|
||||||
Recovery</h2>
|
Recovery</h2>
|
||||||
<p>Persisting the current state of both the archive handler and the downloader is achieved by leveraging an SQLite database rather than flat files, as the database protects against data corruption that could result from a system crash.</p>
|
<p>Persisting the current state of both the archive handler and the downloader is achieved by leveraging an SQLite database rather than flat files, as the database protects against data corruption that could result from a system crash.</p>
|
||||||
<h4><a class="anchor" id="autotoc_md164"></a>
|
<h4><a class="anchor" id="autotoc_md166"></a>
|
||||||
ShardArchiveHandler</h4>
|
ShardArchiveHandler</h4>
|
||||||
<p>Although <code>HTTPDownloader</code> is a generic class that could be used to download a variety of file types, currently it is used exclusively by the <code>ShardArchiveHandler</code> to download shards. In order to provide resilience, the <code>ShardArchiveHandler</code> will use an SQLite database to preserve its current state whenever there are active, paused, or queued downloads. The <code>shard_db</code> section in the configuration file allows users to specify the location of the database to use for this purpose.</p>
|
<p>Although <code>HTTPDownloader</code> is a generic class that could be used to download a variety of file types, currently it is used exclusively by the <code>ShardArchiveHandler</code> to download shards. In order to provide resilience, the <code>ShardArchiveHandler</code> will use an SQLite database to preserve its current state whenever there are active, paused, or queued downloads. The <code>shard_db</code> section in the configuration file allows users to specify the location of the database to use for this purpose.</p>
|
||||||
<h5>SQLite Table Format</h5>
|
<h5>SQLite Table Format</h5>
|
||||||
@@ -193,7 +193,7 @@ ShardArchiveHandler</h4>
|
|||||||
<tr class="markdownTableRowOdd">
|
<tr class="markdownTableRowOdd">
|
||||||
<td class="markdownTableBodyCenter">5 </td><td class="markdownTableBodyCenter">https://example.com/5.tar.lz4 </td></tr>
|
<td class="markdownTableBodyCenter">5 </td><td class="markdownTableBodyCenter">https://example.com/5.tar.lz4 </td></tr>
|
||||||
</table>
|
</table>
|
||||||
<h4><a class="anchor" id="autotoc_md165"></a>
|
<h4><a class="anchor" id="autotoc_md167"></a>
|
||||||
HTTPDownloader</h4>
|
HTTPDownloader</h4>
|
||||||
<p>While the archive handler maintains a list of all partial and queued downloads, the <code>HTTPDownloader</code> stores the raw bytes of the file currently being downloaded. The partially downloaded file will be represented as one or more <code>BLOB</code> entries in an SQLite database. As the maximum size of a <code>BLOB</code> entry is currently limited to roughly 2.1 GB, a 5 GB shard file for instance will occupy three database entries upon completion.</p>
|
<p>While the archive handler maintains a list of all partial and queued downloads, the <code>HTTPDownloader</code> stores the raw bytes of the file currently being downloaded. The partially downloaded file will be represented as one or more <code>BLOB</code> entries in an SQLite database. As the maximum size of a <code>BLOB</code> entry is currently limited to roughly 2.1 GB, a 5 GB shard file for instance will occupy three database entries upon completion.</p>
|
||||||
<h5>SQLite Table Format</h5>
|
<h5>SQLite Table Format</h5>
|
||||||
@@ -208,7 +208,7 @@ HTTPDownloader</h4>
|
|||||||
<tr class="markdownTableRowOdd">
|
<tr class="markdownTableRowOdd">
|
||||||
<td class="markdownTableBodyCenter">0x... </td><td class="markdownTableBodyCenter">705032706 </td><td class="markdownTableBodyCenter">2 </td></tr>
|
<td class="markdownTableBodyCenter">0x... </td><td class="markdownTableBodyCenter">705032706 </td><td class="markdownTableBodyCenter">2 </td></tr>
|
||||||
</table>
|
</table>
|
||||||
<h4><a class="anchor" id="autotoc_md166"></a>
|
<h4><a class="anchor" id="autotoc_md168"></a>
|
||||||
Config File Entry</h4>
|
Config File Entry</h4>
|
||||||
<p>The <code>download_path</code> field of the <code>shard_db</code> entry is used to determine where to store the recovery database. If this field is omitted, the <code>path</code> field will be used instead.</p>
|
<p>The <code>download_path</code> field of the <code>shard_db</code> entry is used to determine where to store the recovery database. If this field is omitted, the <code>path</code> field will be used instead.</p>
|
||||||
<div class="fragment"><div class="line"># This is the persistent datastore for shards. It is important for the health</div>
|
<div class="fragment"><div class="line"># This is the persistent datastore for shards. It is important for the health</div>
|
||||||
@@ -220,7 +220,7 @@ Config File Entry</h4>
|
|||||||
<div class="line">path=/var/lib/rippled/db/shards/nudb</div>
|
<div class="line">path=/var/lib/rippled/db/shards/nudb</div>
|
||||||
<div class="line">download_path=/var/lib/rippled/db/shards/</div>
|
<div class="line">download_path=/var/lib/rippled/db/shards/</div>
|
||||||
<div class="line">max_historical_shards=50</div>
|
<div class="line">max_historical_shards=50</div>
|
||||||
</div><!-- fragment --><h4><a class="anchor" id="autotoc_md167"></a>
|
</div><!-- fragment --><h4><a class="anchor" id="autotoc_md169"></a>
|
||||||
Resuming Partial Downloads</h4>
|
Resuming Partial Downloads</h4>
|
||||||
<p>When resuming downloads after a shutdown, crash, or other interruption, the <code>HTTPDownloader</code> will utilize the <code>range</code> field of the HTTP header to download only the remainder of the partially downloaded file.</p>
|
<p>When resuming downloads after a shutdown, crash, or other interruption, the <code>HTTPDownloader</code> will utilize the <code>range</code> field of the HTTP header to download only the remainder of the partially downloaded file.</p>
|
||||||
<div class="fragment"><div class="line"> {C++}</div>
|
<div class="fragment"><div class="line"> {C++}</div>
|
||||||
@@ -250,7 +250,7 @@ Resuming Partial Downloads</h4>
|
|||||||
<div class="line"> // the total download size. Error condition. Handle</div>
|
<div class="line"> // the total download size. Error condition. Handle</div>
|
||||||
<div class="line"> // appropriately.</div>
|
<div class="line"> // appropriately.</div>
|
||||||
<div class="line">}</div>
|
<div class="line">}</div>
|
||||||
</div><!-- fragment --><h4><a class="anchor" id="autotoc_md168"></a>
|
</div><!-- fragment --><h4><a class="anchor" id="autotoc_md170"></a>
|
||||||
DatabaseBody</h4>
|
DatabaseBody</h4>
|
||||||
<p>Previously, the <code>HTTPDownloader</code> leveraged an <code><a href="http::response_parser">http::response_parser</a></code> instantiated with an <code><a href="http::file_body">http::file_body</a></code>. The <code>file_body</code> class declares a nested type, <code>reader</code>, which does the task of writing HTTP message payloads (constituting a requested file) to the filesystem. In order for the <code><a href="http::response_parser">http::response_parser</a></code> to interface with the database, we implement a custom body type that declares a nested <code>reader</code> type which has been outfitted to persist octects received from the remote host to a local SQLite database. The code snippet below illustrates the customization points available to user-defined body types:</p>
|
<p>Previously, the <code>HTTPDownloader</code> leveraged an <code><a href="http::response_parser">http::response_parser</a></code> instantiated with an <code><a href="http::file_body">http::file_body</a></code>. The <code>file_body</code> class declares a nested type, <code>reader</code>, which does the task of writing HTTP message payloads (constituting a requested file) to the filesystem. In order for the <code><a href="http::response_parser">http::response_parser</a></code> to interface with the database, we implement a custom body type that declares a nested <code>reader</code> type which has been outfitted to persist octects received from the remote host to a local SQLite database. The code snippet below illustrates the customization points available to user-defined body types:</p>
|
||||||
<div class="fragment"><div class="line"> {C++}</div>
|
<div class="fragment"><div class="line"> {C++}</div>
|
||||||
@@ -277,11 +277,11 @@ DatabaseBody</h4>
|
|||||||
<div class="fragment"><div class="line"> {C++}</div>
|
<div class="fragment"><div class="line"> {C++}</div>
|
||||||
<div class="line">std::size_t</div>
|
<div class="line">std::size_t</div>
|
||||||
<div class="line">body::reader::put(ConstBufferSequence const& buffers, error_code& ec);</div>
|
<div class="line">body::reader::put(ConstBufferSequence const& buffers, error_code& ec);</div>
|
||||||
</div><!-- fragment --><h1><a class="anchor" id="autotoc_md169"></a>
|
</div><!-- fragment --><h1><a class="anchor" id="autotoc_md171"></a>
|
||||||
Sequence Diagram</h1>
|
Sequence Diagram</h1>
|
||||||
<p>This sequence diagram demonstrates a scenario wherein the <code>ShardArchiveHandler</code> leverages the state persisted in the database to recover from a crash and resume the requested downloads.</p>
|
<p>This sequence diagram demonstrates a scenario wherein the <code>ShardArchiveHandler</code> leverages the state persisted in the database to recover from a crash and resume the requested downloads.</p>
|
||||||
<p><img src="./images/interrupt_sequence.png" alt="alt_text" title="Resuming downloads post abort" class="inline"/></p>
|
<p><img src="./images/interrupt_sequence.png" alt="alt_text" title="Resuming downloads post abort" class="inline"/></p>
|
||||||
<h1><a class="anchor" id="autotoc_md170"></a>
|
<h1><a class="anchor" id="autotoc_md172"></a>
|
||||||
State Diagram</h1>
|
State Diagram</h1>
|
||||||
<p>This diagram illustrates the various states of the Shard Downloader module.</p>
|
<p>This diagram illustrates the various states of the Shard Downloader module.</p>
|
||||||
<p><img src="./images/states.png" alt="alt_text" title="Shard Downloader states" class="inline"/> </p>
|
<p><img src="./images/states.png" alt="alt_text" title="Shard Downloader states" class="inline"/> </p>
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ $(function() {
|
|||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><p>This doc describes the standard way to assemble the database shard. A shard assembled using this approach becomes deterministic i.e. if two independent sides assemble a shard consisting of the same ledgers, accounts and transactions, then they will obtain the same shard files <code>nudb.dat</code> and <code>nudb.key</code>. The approach deals with the <code>NuDB</code> database format only, refer to <code><a href="https://github.com/vinniefalco/NuDB">https://github.com/vinniefalco/NuDB</a></code>.</p>
|
<div class="textblock"><p>This doc describes the standard way to assemble the database shard. A shard assembled using this approach becomes deterministic i.e. if two independent sides assemble a shard consisting of the same ledgers, accounts and transactions, then they will obtain the same shard files <code>nudb.dat</code> and <code>nudb.key</code>. The approach deals with the <code>NuDB</code> database format only, refer to <code><a href="https://github.com/vinniefalco/NuDB">https://github.com/vinniefalco/NuDB</a></code>.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md172"></a>
|
<h1><a class="anchor" id="autotoc_md174"></a>
|
||||||
Headers</h1>
|
Headers</h1>
|
||||||
<p>Due to NuDB database definition, the following headers are used for database files:</p>
|
<p>Due to NuDB database definition, the following headers are used for database files:</p>
|
||||||
<p>nudb.key: </p><div class="fragment"><div class="line">char[8] Type The characters "nudb.key"</div>
|
<p>nudb.key: </p><div class="fragment"><div class="line">char[8] Type The characters "nudb.key"</div>
|
||||||
@@ -106,7 +106,7 @@ Headers</h1>
|
|||||||
<div class="line">digest(1) = H[8] << 56 | H[9] << 48 | ... | H[15] << 0,</div>
|
<div class="line">digest(1) = H[8] << 56 | H[9] << 48 | ... | H[15] << 0,</div>
|
||||||
<div class="line">digest(2) = H[16] << 24 | H[17] << 16 | ... | H[19] << 0,</div>
|
<div class="line">digest(2) = H[16] << 24 | H[17] << 16 | ... | H[19] << 0,</div>
|
||||||
</div><!-- fragment --><p>where <code>H[i]</code> denotes <code>i</code>-th byte of hash <code>H</code>.</p>
|
</div><!-- fragment --><p>where <code>H[i]</code> denotes <code>i</code>-th byte of hash <code>H</code>.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md173"></a>
|
<h1><a class="anchor" id="autotoc_md175"></a>
|
||||||
Contents</h1>
|
Contents</h1>
|
||||||
<p>After deterministic shard is created using the above mentioned headers, it filled with objects using the following steps.</p>
|
<p>After deterministic shard is created using the above mentioned headers, it filled with objects using the following steps.</p>
|
||||||
<ol type="1">
|
<ol type="1">
|
||||||
@@ -122,13 +122,13 @@ Contents</h1>
|
|||||||
</div><!-- fragment --></li>
|
</div><!-- fragment --></li>
|
||||||
<li>Finally, objects added to the deterministic shard group by group in the sorted order within each group from low to high hashes.</li>
|
<li>Finally, objects added to the deterministic shard group by group in the sorted order within each group from low to high hashes.</li>
|
||||||
</ol>
|
</ol>
|
||||||
<h1><a class="anchor" id="autotoc_md174"></a>
|
<h1><a class="anchor" id="autotoc_md176"></a>
|
||||||
Order of visiting objects</h1>
|
Order of visiting objects</h1>
|
||||||
<p>The shard consists of 16384 ledgers and the final key with the hash 0. Each ledger has the header object and two SMAmaps: state and transaction. SHAmap is a rooted tree in which each node has maximum of 16 descendants enumerating by indexes 0..15. Visiting each node in the SHAmap is performing by functions visitNodes and visitDifferences implemented in the file <code>ripple/shamap/impl/ShaMapSync.cpp</code>.</p>
|
<p>The shard consists of 16384 ledgers and the final key with the hash 0. Each ledger has the header object and two SMAmaps: state and transaction. SHAmap is a rooted tree in which each node has maximum of 16 descendants enumerating by indexes 0..15. Visiting each node in the SHAmap is performing by functions visitNodes and visitDifferences implemented in the file <code>ripple/shamap/impl/ShaMapSync.cpp</code>.</p>
|
||||||
<p>Here is how the function visitNodes works: it visit the root at first. Then it visit all nodes in the 1st layer, i. e. the nodes which are immediately descendants of the root sequentially from index 0 to 15. Then it visit all nodes in 2nd layer i.e. the nodes which are immediately descendants the nodes from 1st layer. The order of visiting 2nd layer nodes is the following. First, descendants of the 1st layer node with index 0 are visited sequintially from index 0 to 15. Then descendents of 1st layer node with index 1 are visited etc. After visiting all nodes of 2nd layer the nodes from 3rd layer are visited etc.</p>
|
<p>Here is how the function visitNodes works: it visit the root at first. Then it visit all nodes in the 1st layer, i. e. the nodes which are immediately descendants of the root sequentially from index 0 to 15. Then it visit all nodes in 2nd layer i.e. the nodes which are immediately descendants the nodes from 1st layer. The order of visiting 2nd layer nodes is the following. First, descendants of the 1st layer node with index 0 are visited sequintially from index 0 to 15. Then descendents of 1st layer node with index 1 are visited etc. After visiting all nodes of 2nd layer the nodes from 3rd layer are visited etc.</p>
|
||||||
<p>The function visitDifferences works similar to visitNodes with the following exceptions. The first exception is that visitDifferences get 2 arguments: current SHAmap and previous SHAmap and visit only the nodes from current SHAmap which and not present in previous SHAmap. The second exception is that visitDifferences visits all non-leaf nodes in the order of visitNodes function, but all leaf nodes are visited immedeately after visiting of their parent node sequentially from index 0 to 15.</p>
|
<p>The function visitDifferences works similar to visitNodes with the following exceptions. The first exception is that visitDifferences get 2 arguments: current SHAmap and previous SHAmap and visit only the nodes from current SHAmap which and not present in previous SHAmap. The second exception is that visitDifferences visits all non-leaf nodes in the order of visitNodes function, but all leaf nodes are visited immedeately after visiting of their parent node sequentially from index 0 to 15.</p>
|
||||||
<p>Finally, all objects within the shard are visited in the following order. All ledgers are visited from the ledger with high index to the ledger with low index in descending order. For each ledger the state SHAmap is visited first using visitNode function for the ledger with highest index and visitDifferences function for other ledgers. Then transaction SHAmap is visited using visitNodes function. At last, the ledger header object is visited. Final key of the shard is visited at the end.</p>
|
<p>Finally, all objects within the shard are visited in the following order. All ledgers are visited from the ledger with high index to the ledger with low index in descending order. For each ledger the state SHAmap is visited first using visitNode function for the ledger with highest index and visitDifferences function for other ledgers. Then transaction SHAmap is visited using visitNodes function. At last, the ledger header object is visited. Final key of the shard is visited at the end.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md175"></a>
|
<h1><a class="anchor" id="autotoc_md177"></a>
|
||||||
Tests</h1>
|
Tests</h1>
|
||||||
<p>To perform test to deterministic shards implementation one can enter the following command: </p><div class="fragment"><div class="line">rippled --unittest ripple.NodeStore.DatabaseShard</div>
|
<p>To perform test to deterministic shards implementation one can enter the following command: </p><div class="fragment"><div class="line">rippled --unittest ripple.NodeStore.DatabaseShard</div>
|
||||||
</div><!-- fragment --><p>The following is the right output of deterministic shards test: </p><div class="fragment"><div class="line">ripple.NodeStore.DatabaseShard DatabaseShard deterministic_shard</div>
|
</div><!-- fragment --><p>The following is the right output of deterministic shards test: </p><div class="fragment"><div class="line">ripple.NodeStore.DatabaseShard DatabaseShard deterministic_shard</div>
|
||||||
|
|||||||
@@ -71,9 +71,9 @@ $(function() {
|
|||||||
<li><a href="#downloaded-shard-validation">Downloaded Shard Validation</a></li>
|
<li><a href="#downloaded-shard-validation">Downloaded Shard Validation</a></li>
|
||||||
<li><a href="#shard-storage-paths">Shard Storage Paths</a></li>
|
<li><a href="#shard-storage-paths">Shard Storage Paths</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<h1><a class="anchor" id="autotoc_md177"></a>
|
<h1><a class="anchor" id="autotoc_md179"></a>
|
||||||
NodeStore</h1>
|
NodeStore</h1>
|
||||||
<h2><a class="anchor" id="autotoc_md178"></a>
|
<h2><a class="anchor" id="autotoc_md180"></a>
|
||||||
Introduction</h2>
|
Introduction</h2>
|
||||||
<p>A <code>NodeObject</code> is a simple object that the Ledger uses to store entries. It is comprised of a type, a hash and a blob. It can be uniquely identified by the hash, which is a 256 bit hash of the blob. The blob is a variable length block of serialized data. The type identifies what the blob contains. The fields are as follows:</p>
|
<p>A <code>NodeObject</code> is a simple object that the Ledger uses to store entries. It is comprised of a type, a hash and a blob. It can be uniquely identified by the hash, which is a 256 bit hash of the blob. The blob is a variable length block of serialized data. The type identifies what the blob contains. The fields are as follows:</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -112,7 +112,7 @@ Introduction</h2>
|
|||||||
</table>
|
</table>
|
||||||
<hr />
|
<hr />
|
||||||
<p> The <code>NodeStore</code> provides an interface that stores, in a persistent database, a collection of NodeObjects that rippled uses as its primary representation of ledger entries. All ledger entries are stored as NodeObjects and as such, need to be persisted between launches. If a NodeObject is accessed and is not in memory, it will be retrieved from the database.</p>
|
<p> The <code>NodeStore</code> provides an interface that stores, in a persistent database, a collection of NodeObjects that rippled uses as its primary representation of ledger entries. All ledger entries are stored as NodeObjects and as such, need to be persisted between launches. If a NodeObject is accessed and is not in memory, it will be retrieved from the database.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md179"></a>
|
<h2><a class="anchor" id="autotoc_md181"></a>
|
||||||
Backend</h2>
|
Backend</h2>
|
||||||
<p>The <code>NodeStore</code> implementation provides the <code>Backend</code> abstract interface, which lets different key/value databases to be chosen at run-time. This allows experimentation with different engines. Improvements in the performance of the NodeStore are a constant area of research. The database can be specified in the configuration file [node_db] section as follows.</p>
|
<p>The <code>NodeStore</code> implementation provides the <code>Backend</code> abstract interface, which lets different key/value databases to be chosen at run-time. This allows experimentation with different engines. Improvements in the performance of the NodeStore are a constant area of research. The database can be specified in the configuration file [node_db] section as follows.</p>
|
||||||
<p>One or more lines of key / value pairs</p>
|
<p>One or more lines of key / value pairs</p>
|
||||||
@@ -143,15 +143,15 @@ Backend</h2>
|
|||||||
<li><b>0</b> off</li>
|
<li><b>0</b> off</li>
|
||||||
<li><b>1</b> on (default)</li>
|
<li><b>1</b> on (default)</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h1><a class="anchor" id="autotoc_md180"></a>
|
<h1><a class="anchor" id="autotoc_md182"></a>
|
||||||
Benchmarks</h1>
|
Benchmarks</h1>
|
||||||
<p>The <code>NodeStore.Timing</code> test is used to execute a set of read/write workloads to compare current available nodestore backends. It can be executed with:</p>
|
<p>The <code>NodeStore.Timing</code> test is used to execute a set of read/write workloads to compare current available nodestore backends. It can be executed with:</p>
|
||||||
<div class="fragment"><div class="line">$rippled --unittest=NodeStoreTiming</div>
|
<div class="fragment"><div class="line">$rippled --unittest=NodeStoreTiming</div>
|
||||||
</div><!-- fragment --><p>It is also possible to use alternate DB config params by passing config strings as <code>--unittest-arg</code>.</p>
|
</div><!-- fragment --><p>It is also possible to use alternate DB config params by passing config strings as <code>--unittest-arg</code>.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md181"></a>
|
<h2><a class="anchor" id="autotoc_md183"></a>
|
||||||
Addendum</h2>
|
Addendum</h2>
|
||||||
<p>The discussion below refers to a <code>RocksDBQuick</code> backend that has since been removed from the code as it was not working and not maintained. That backend primarily used one of the several rocks <code>Optimize*</code> methods to setup the majority of the DB options/params, whereas the primary RocksDB backend exposes many of the available config options directly. The code for RocksDBQuick can be found in versions of this repo 1.2 and earlier if you need to refer back to it. The conclusions below date from about 2014 and may need revisiting based on newer versions of RocksDB (TBD).</p>
|
<p>The discussion below refers to a <code>RocksDBQuick</code> backend that has since been removed from the code as it was not working and not maintained. That backend primarily used one of the several rocks <code>Optimize*</code> methods to setup the majority of the DB options/params, whereas the primary RocksDB backend exposes many of the available config options directly. The code for RocksDBQuick can be found in versions of this repo 1.2 and earlier if you need to refer back to it. The conclusions below date from about 2014 and may need revisiting based on newer versions of RocksDB (TBD).</p>
|
||||||
<h2><a class="anchor" id="autotoc_md182"></a>
|
<h2><a class="anchor" id="autotoc_md184"></a>
|
||||||
Discussion</h2>
|
Discussion</h2>
|
||||||
<p>RocksDBQuickFactory is intended to provide a testbed for comparing potential rocksdb performance with the existing recommended configuration in rippled.cfg. Through various executions and profiling some conclusions are presented below.</p>
|
<p>RocksDBQuickFactory is intended to provide a testbed for comparing potential rocksdb performance with the existing recommended configuration in rippled.cfg. Through various executions and profiling some conclusions are presented below.</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -162,14 +162,14 @@ Discussion</h2>
|
|||||||
<li>An interesting side effect of running the benchmarks in a profiler was that a clear pattern of what RocksDB does under the hood was observable. This led to the decision to trial hash indexing and also the discovery of the native CRC32 instruction not being used.</li>
|
<li>An interesting side effect of running the benchmarks in a profiler was that a clear pattern of what RocksDB does under the hood was observable. This led to the decision to trial hash indexing and also the discovery of the native CRC32 instruction not being used.</li>
|
||||||
<li>Important point to note that is if this factory is tested with an existing set of sst files none of the old sst files will benefit from indexing changes until they are compacted at a future point in time.</li>
|
<li>Important point to note that is if this factory is tested with an existing set of sst files none of the old sst files will benefit from indexing changes until they are compacted at a future point in time.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h1><a class="anchor" id="autotoc_md183"></a>
|
<h1><a class="anchor" id="autotoc_md185"></a>
|
||||||
Downloaded Shard Validation</h1>
|
Downloaded Shard Validation</h1>
|
||||||
<h2><a class="anchor" id="autotoc_md184"></a>
|
<h2><a class="anchor" id="autotoc_md186"></a>
|
||||||
Overview</h2>
|
Overview</h2>
|
||||||
<p>In order to validate shards that have been downloaded from file servers (as opposed to shards acquired from peers), the application must confirm the validity of the downloaded shard's last ledger. So before initiating the download, we first confirm that we are able to retrieve the shard's last ledger hash. The following sections describe this confirmation process in greater detail.</p>
|
<p>In order to validate shards that have been downloaded from file servers (as opposed to shards acquired from peers), the application must confirm the validity of the downloaded shard's last ledger. So before initiating the download, we first confirm that we are able to retrieve the shard's last ledger hash. The following sections describe this confirmation process in greater detail.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md185"></a>
|
<h2><a class="anchor" id="autotoc_md187"></a>
|
||||||
Hash Verification</h2>
|
Hash Verification</h2>
|
||||||
<h3><a class="anchor" id="autotoc_md186"></a>
|
<h3><a class="anchor" id="autotoc_md188"></a>
|
||||||
Flag Ledger</h3>
|
Flag Ledger</h3>
|
||||||
<p>Since the number of ledgers contained in each shard is always a multiple of 256, a shard's last ledger is always a flag ledger. Conveniently, the skip list stored within a ledger will provide us with a series of flag ledger hashes, enabling the software to corroborate a shard's last ledger hash. We access the skip list by calling <code>LedgerMaster::walkHashBySeq</code> and providing the sequence of a shard's last ledger:</p>
|
<p>Since the number of ledgers contained in each shard is always a multiple of 256, a shard's last ledger is always a flag ledger. Conveniently, the skip list stored within a ledger will provide us with a series of flag ledger hashes, enabling the software to corroborate a shard's last ledger hash. We access the skip list by calling <code>LedgerMaster::walkHashBySeq</code> and providing the sequence of a shard's last ledger:</p>
|
||||||
<div class="fragment"><div class="line"> {C++}</div>
|
<div class="fragment"><div class="line"> {C++}</div>
|
||||||
@@ -177,23 +177,23 @@ Flag Ledger</h3>
|
|||||||
<div class="line">expectedHash =</div>
|
<div class="line">expectedHash =</div>
|
||||||
<div class="line"> app_.getLedgerMaster().walkHashBySeq(lastLedgerSeq(shardIndex));</div>
|
<div class="line"> app_.getLedgerMaster().walkHashBySeq(lastLedgerSeq(shardIndex));</div>
|
||||||
</div><!-- fragment --><p>When a user requests a shard download, the <code>ShardArchiveHandler</code> will first use this function to retrieve the hash of the shard's last ledger. If the function returns a hash, downloading the shard can proceed. Once the download completes, the server can reliably retrieve this last ledger hash to complete validation of the shard.</p>
|
</div><!-- fragment --><p>When a user requests a shard download, the <code>ShardArchiveHandler</code> will first use this function to retrieve the hash of the shard's last ledger. If the function returns a hash, downloading the shard can proceed. Once the download completes, the server can reliably retrieve this last ledger hash to complete validation of the shard.</p>
|
||||||
<h3><a class="anchor" id="autotoc_md187"></a>
|
<h3><a class="anchor" id="autotoc_md189"></a>
|
||||||
Caveats</h3>
|
Caveats</h3>
|
||||||
<h4><a class="anchor" id="autotoc_md188"></a>
|
<h4><a class="anchor" id="autotoc_md190"></a>
|
||||||
Later Ledger</h4>
|
Later Ledger</h4>
|
||||||
<p>The <code>walkHashBySeq</code> function will provide the hash of a flag ledger only if the application has stored a later ledger. When verifying the last ledger hash of a pending shard download, if there is no later ledger stored, the download will be deferred until a later ledger has been stored.</p>
|
<p>The <code>walkHashBySeq</code> function will provide the hash of a flag ledger only if the application has stored a later ledger. When verifying the last ledger hash of a pending shard download, if there is no later ledger stored, the download will be deferred until a later ledger has been stored.</p>
|
||||||
<p>We use the presence (or absence) of a validated ledger with a sequence number later than the sequence of the shard's last ledger as a heuristic for determining whether or not we should have the shard's last ledger hash. A later ledger must be present in order to reliably retrieve the hash of the shard's last ledger. The hash will only be retrieved when a later ledger is present. Otherwise verification of the shard will be deferred.</p>
|
<p>We use the presence (or absence) of a validated ledger with a sequence number later than the sequence of the shard's last ledger as a heuristic for determining whether or not we should have the shard's last ledger hash. A later ledger must be present in order to reliably retrieve the hash of the shard's last ledger. The hash will only be retrieved when a later ledger is present. Otherwise verification of the shard will be deferred.</p>
|
||||||
<h3><a class="anchor" id="autotoc_md189"></a>
|
<h3><a class="anchor" id="autotoc_md191"></a>
|
||||||
Retries</h3>
|
Retries</h3>
|
||||||
<h4><a class="anchor" id="autotoc_md190"></a>
|
<h4><a class="anchor" id="autotoc_md192"></a>
|
||||||
Retry Limit</h4>
|
Retry Limit</h4>
|
||||||
<p>If the server must defer hash verification, the software will initiate a timer that upon expiration, will re-attempt verifying the last ledger hash. We place an upper limit on the number of attempts the server makes to achieve this verification. When the maximum number of attempts has been reached, the download request will fail, and the <code>ShardArchiveHandler</code> will proceed with any remaining downloads. An attempt counts toward the limit only when we are able to get a later validated ledger (implying a current view of the network), but are unable to retrieve the last ledger hash. Retries that occur because no validated ledger was available are not counted.</p>
|
<p>If the server must defer hash verification, the software will initiate a timer that upon expiration, will re-attempt verifying the last ledger hash. We place an upper limit on the number of attempts the server makes to achieve this verification. When the maximum number of attempts has been reached, the download request will fail, and the <code>ShardArchiveHandler</code> will proceed with any remaining downloads. An attempt counts toward the limit only when we are able to get a later validated ledger (implying a current view of the network), but are unable to retrieve the last ledger hash. Retries that occur because no validated ledger was available are not counted.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md191"></a>
|
<h1><a class="anchor" id="autotoc_md193"></a>
|
||||||
Shard Storage Paths</h1>
|
Shard Storage Paths</h1>
|
||||||
<h2><a class="anchor" id="autotoc_md192"></a>
|
<h2><a class="anchor" id="autotoc_md194"></a>
|
||||||
Overview</h2>
|
Overview</h2>
|
||||||
<p>The shard database stores validated ledgers in logical groups called shards. As of June 2020, a shard stores 16384 ledgers by default. In order to allow users to store shards on multiple devices, the shard database can be configured with several file system paths. Each path provided should refer to a directory on a distinct filesystem, and no two paths should ever correspond to the same filesystem. Violating this restriction will cause the server to inaccurately estimate the amount of space available for storing shards. In the absence of a suitable platform agnostic solution, this requirement is enforced only on Linux. However, on other platforms we employ a heuristic that issues a warning if we suspect that this restriction is violated.</p>
|
<p>The shard database stores validated ledgers in logical groups called shards. As of June 2020, a shard stores 16384 ledgers by default. In order to allow users to store shards on multiple devices, the shard database can be configured with several file system paths. Each path provided should refer to a directory on a distinct filesystem, and no two paths should ever correspond to the same filesystem. Violating this restriction will cause the server to inaccurately estimate the amount of space available for storing shards. In the absence of a suitable platform agnostic solution, this requirement is enforced only on Linux. However, on other platforms we employ a heuristic that issues a warning if we suspect that this restriction is violated.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md193"></a>
|
<h2><a class="anchor" id="autotoc_md195"></a>
|
||||||
Configuration</h2>
|
Configuration</h2>
|
||||||
<p>The <code>shard_db</code> and <code>historical_shard_paths</code> sections of the server's configuration file will be used to determine where the server stores shards. Minimally, the <code>shard_db</code> section must contain a single <code>path</code> key. If this is the only storage path provided, all shards will be stored at this location. If the configuration also lists one or more lines in the <code>historical_shard_paths</code> section, all older shards will be stored at these locations, and the <code>path</code> will be used only to store the current and previous shards. The goal is to allow users to provide an efficient SSD for storing recent shards, as these will be accessed more frequently, while using large mechanical drives for storing older shards that will be accessed less frequently.</p>
|
<p>The <code>shard_db</code> and <code>historical_shard_paths</code> sections of the server's configuration file will be used to determine where the server stores shards. Minimally, the <code>shard_db</code> section must contain a single <code>path</code> key. If this is the only storage path provided, all shards will be stored at this location. If the configuration also lists one or more lines in the <code>historical_shard_paths</code> section, all older shards will be stored at these locations, and the <code>path</code> will be used only to store the current and previous shards. The goal is to allow users to provide an efficient SSD for storing recent shards, as these will be accessed more frequently, while using large mechanical drives for storing older shards that will be accessed less frequently.</p>
|
||||||
<p>Below is a sample configuration snippet that provides a path for main storage and several paths for historical storage:</p>
|
<p>Below is a sample configuration snippet that provides a path for main storage and several paths for historical storage:</p>
|
||||||
@@ -228,25 +228,25 @@ Configuration</h2>
|
|||||||
<div class="line">/mnt/disk1</div>
|
<div class="line">/mnt/disk1</div>
|
||||||
<div class="line">/mnt/disk2</div>
|
<div class="line">/mnt/disk2</div>
|
||||||
<div class="line">/mnt/disk3</div>
|
<div class="line">/mnt/disk3</div>
|
||||||
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md194"></a>
|
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md196"></a>
|
||||||
Shard Migration</h2>
|
Shard Migration</h2>
|
||||||
<p>When a new shard (<em>current shard</em>) is confirmed by the network, the recent shards will shift. The <em>previous shard</em> will become a <em>historical shard</em>, the <em>current shard</em> will become the <em>previous shard</em>, and the new shard will become the <em>current shard</em>. These are just logical labels, and the shards themselves don't change to reflect being current, previous, or historical. However, if the server's configuration specifies one or more paths for historical storage, during this shift the formerly <em>previous shard</em> will be migrated to one of the historical paths. If multiple paths are provided, the server dynamically chooses one with sufficient space for storing the shard.</p>
|
<p>When a new shard (<em>current shard</em>) is confirmed by the network, the recent shards will shift. The <em>previous shard</em> will become a <em>historical shard</em>, the <em>current shard</em> will become the <em>previous shard</em>, and the new shard will become the <em>current shard</em>. These are just logical labels, and the shards themselves don't change to reflect being current, previous, or historical. However, if the server's configuration specifies one or more paths for historical storage, during this shift the formerly <em>previous shard</em> will be migrated to one of the historical paths. If multiple paths are provided, the server dynamically chooses one with sufficient space for storing the shard.</p>
|
||||||
<p><b>Note:</b> As of June 2020, the shard database does not store the partial shard currently being built by live network transactions, but this is planned to change. When this feature is implemented, the <em>current shard</em> will refer to this partial shard, and the <em>previous shard</em> will refer to the most recently validated shard.</p>
|
<p><b>Note:</b> As of June 2020, the shard database does not store the partial shard currently being built by live network transactions, but this is planned to change. When this feature is implemented, the <em>current shard</em> will refer to this partial shard, and the <em>previous shard</em> will refer to the most recently validated shard.</p>
|
||||||
<h3><a class="anchor" id="autotoc_md195"></a>
|
<h3><a class="anchor" id="autotoc_md197"></a>
|
||||||
Selecting a Historical Storage Path</h3>
|
Selecting a Historical Storage Path</h3>
|
||||||
<p>When storing historical shards, if multiple historical paths are provided, the path to use for each shard will be selected in a random fashion. By using all available storage devices, we create a uniform distribution of disk utilization for disks of equivalent size, (provided that the disks are used only to store shards). In theory, selecting devices in this manner will also increase our chances for concurrent access to stored shards, however as of June 2020 concurrent shard access is not implemented. Lastly, a storage path is included in the random distribution only if it has enough storage capacity to hold the next shard.</p>
|
<p>When storing historical shards, if multiple historical paths are provided, the path to use for each shard will be selected in a random fashion. By using all available storage devices, we create a uniform distribution of disk utilization for disks of equivalent size, (provided that the disks are used only to store shards). In theory, selecting devices in this manner will also increase our chances for concurrent access to stored shards, however as of June 2020 concurrent shard access is not implemented. Lastly, a storage path is included in the random distribution only if it has enough storage capacity to hold the next shard.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md196"></a>
|
<h2><a class="anchor" id="autotoc_md198"></a>
|
||||||
Shard Acquisition</h2>
|
Shard Acquisition</h2>
|
||||||
<p>When the server is acquiring shard history, these acquired shards will be stored at a path designated for historical storage (<code>historical_storage_path</code>). If no such path is provided, acquired shards will be stored at the <code>path</code>.</p>
|
<p>When the server is acquiring shard history, these acquired shards will be stored at a path designated for historical storage (<code>historical_storage_path</code>). If no such path is provided, acquired shards will be stored at the <code>path</code>.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md197"></a>
|
<h2><a class="anchor" id="autotoc_md199"></a>
|
||||||
Storage capacity</h2>
|
Storage capacity</h2>
|
||||||
<h3><a class="anchor" id="autotoc_md198"></a>
|
<h3><a class="anchor" id="autotoc_md200"></a>
|
||||||
Filesystem Capacity</h3>
|
Filesystem Capacity</h3>
|
||||||
<p>When the shard database updates its record of disk utilization, it trusts that the provided historical paths refer to distinct devices, or at least distinct filesystems. If this requirement is violated, the database will operate with an inaccurate view of how many shards it can store. Violation of this requirement won't necessarily impede database operations, but the database will fail to identify scenarios wherein storing the maximum number of historical shards (as per the 'historical_shard_count' parameter in the configuration file) would exceed the amount of storage space available.</p>
|
<p>When the shard database updates its record of disk utilization, it trusts that the provided historical paths refer to distinct devices, or at least distinct filesystems. If this requirement is violated, the database will operate with an inaccurate view of how many shards it can store. Violation of this requirement won't necessarily impede database operations, but the database will fail to identify scenarios wherein storing the maximum number of historical shards (as per the 'historical_shard_count' parameter in the configuration file) would exceed the amount of storage space available.</p>
|
||||||
<h3><a class="anchor" id="autotoc_md199"></a>
|
<h3><a class="anchor" id="autotoc_md201"></a>
|
||||||
Shard Migration</h3>
|
Shard Migration</h3>
|
||||||
<p>During a "recent shard shift", if the server has already reached the configured limit of stored historical shards, instead of moving the formerly <em>previous shard</em> to a historical drive (or keeping it at the 'path') the shard will be dropped and removed from the filesystem.</p>
|
<p>During a "recent shard shift", if the server has already reached the configured limit of stored historical shards, instead of moving the formerly <em>previous shard</em> to a historical drive (or keeping it at the 'path') the shard will be dropped and removed from the filesystem.</p>
|
||||||
<h3><a class="anchor" id="autotoc_md200"></a>
|
<h3><a class="anchor" id="autotoc_md202"></a>
|
||||||
Shard Acquisition</h3>
|
Shard Acquisition</h3>
|
||||||
<p>Once the configured limit of stored historical shards has been reached, shard acquisition halts, and no additional shards will be acquired. </p>
|
<p>Once the configured limit of stored historical shards has been reached, shard acquisition halts, and no additional shards will be acquired. </p>
|
||||||
</div></div><!-- contents -->
|
</div></div><!-- contents -->
|
||||||
|
|||||||
@@ -65,10 +65,10 @@ $(function() {
|
|||||||
<div class="title">Open Shard Management </div> </div>
|
<div class="title">Open Shard Management </div> </div>
|
||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><h1><a class="anchor" id="autotoc_md202"></a>
|
<div class="textblock"><h1><a class="anchor" id="autotoc_md204"></a>
|
||||||
Overview</h1>
|
Overview</h1>
|
||||||
<p>Shard NuDB and SQLite databases consume server resources. This can be unnecessarily taxing on servers with many shards. The open shard management feature aims to improve the situation by managing a limited number of open shard database connections. The feature, which is integrated into the existing DatabaseShardImp and Shard classes, maintains a limited pool of open databases prioritized by their last use time stamp. The following sections describe the feature in greater detail.</p>
|
<p>Shard NuDB and SQLite databases consume server resources. This can be unnecessarily taxing on servers with many shards. The open shard management feature aims to improve the situation by managing a limited number of open shard database connections. The feature, which is integrated into the existing DatabaseShardImp and Shard classes, maintains a limited pool of open databases prioritized by their last use time stamp. The following sections describe the feature in greater detail.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md203"></a>
|
<h2><a class="anchor" id="autotoc_md205"></a>
|
||||||
Open Shard Management</h2>
|
Open Shard Management</h2>
|
||||||
<p>The open shard management feature is integrated into the DatabaseShardImp and Shard classes. As the DatabaseShardImp sweep function is periodically called, the number of finalized open shards, which constitutes the open pool, are examined. Upon the pool exceeding a pool limit, an attempt is made to close enough open shards to remain within the limit. Shards to be closed are selected based on their last use time stamp, which is automatically updated on database access. If necessary, shards will automatically open their databases when accessed.</p>
|
<p>The open shard management feature is integrated into the DatabaseShardImp and Shard classes. As the DatabaseShardImp sweep function is periodically called, the number of finalized open shards, which constitutes the open pool, are examined. Upon the pool exceeding a pool limit, an attempt is made to close enough open shards to remain within the limit. Shards to be closed are selected based on their last use time stamp, which is automatically updated on database access. If necessary, shards will automatically open their databases when accessed.</p>
|
||||||
<div class="fragment"><div class="line">{C++}</div>
|
<div class="fragment"><div class="line">{C++}</div>
|
||||||
@@ -93,11 +93,11 @@ Open Shard Management</h2>
|
|||||||
<div class="line"> ++it;</div>
|
<div class="line"> ++it;</div>
|
||||||
<div class="line"> }</div>
|
<div class="line"> }</div>
|
||||||
<div class="line"> }</div>
|
<div class="line"> }</div>
|
||||||
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md204"></a>
|
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md206"></a>
|
||||||
Shard</h2>
|
Shard</h2>
|
||||||
<p>When closing an open shard, DatabaseShardImp will call the Shard 'tryClose' function. This function will only close the shard databases if there are no outstanding references.</p>
|
<p>When closing an open shard, DatabaseShardImp will call the Shard 'tryClose' function. This function will only close the shard databases if there are no outstanding references.</p>
|
||||||
<p>DatabaseShardImp will use the Shard 'isOpen' function to determine the state of a shard's database.</p>
|
<p>DatabaseShardImp will use the Shard 'isOpen' function to determine the state of a shard's database.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md205"></a>
|
<h2><a class="anchor" id="autotoc_md207"></a>
|
||||||
Caveats</h2>
|
Caveats</h2>
|
||||||
<p>The Shard class must check the state of its databases before use. Prior use assumed databases were always open, that is no longer the case with the open shard management feature. </p>
|
<p>The Shard class must check the state of its databases before use. Prior use assumed databases were always open, that is no longer the case with the open shard management feature. </p>
|
||||||
</div></div><!-- contents -->
|
</div></div><!-- contents -->
|
||||||
|
|||||||
@@ -66,12 +66,12 @@ $(function() {
|
|||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><p>The purpose of this document is to compare the sizes of shards containing varying amounts of ledgers.</p>
|
<div class="textblock"><p>The purpose of this document is to compare the sizes of shards containing varying amounts of ledgers.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md207"></a>
|
<h1><a class="anchor" id="autotoc_md209"></a>
|
||||||
Methodology</h1>
|
Methodology</h1>
|
||||||
<p>One can see visually from a block explorer that a typical mainnet ledger consists of about 30 offer transactions issued by about 8 different accounts, and several transactions of other types. To simulate this situation and similar situations we have constructed deterministic shards of differenet sizes, with varying amounts of offers per ledger and varying amounts of accounts issuing these offers.</p>
|
<p>One can see visually from a block explorer that a typical mainnet ledger consists of about 30 offer transactions issued by about 8 different accounts, and several transactions of other types. To simulate this situation and similar situations we have constructed deterministic shards of differenet sizes, with varying amounts of offers per ledger and varying amounts of accounts issuing these offers.</p>
|
||||||
<p>In the following results table, the number of ledgers per shard ranges from 256 to 16K with the size doubling the size at each step. We considered the following numbers of offers per ledger: 0, 1, 5, 10 and 30. Also we considered case of 1 and 8 accounts issuing offers. For each constructed deterministic shard we counted its size. Finally we compared doubled size of the shard with N ledgers and the size of a shard with 2*N ledgers where othere parameters such as number of offers and accounts are the same. This comparison is sufficient to determine which number of ledgers per shard leads to less storage size on the disk.</p>
|
<p>In the following results table, the number of ledgers per shard ranges from 256 to 16K with the size doubling the size at each step. We considered the following numbers of offers per ledger: 0, 1, 5, 10 and 30. Also we considered case of 1 and 8 accounts issuing offers. For each constructed deterministic shard we counted its size. Finally we compared doubled size of the shard with N ledgers and the size of a shard with 2*N ledgers where othere parameters such as number of offers and accounts are the same. This comparison is sufficient to determine which number of ledgers per shard leads to less storage size on the disk.</p>
|
||||||
<p>Note that we minimize total storage size on the disk, but not the size of each shard because data below shows that the size of a typical shard is not larger than 10G, but sizes of modern disks, even SSDs, start from 250G. So there is no problem to fit a single shard to a disk, even small.</p>
|
<p>Note that we minimize total storage size on the disk, but not the size of each shard because data below shows that the size of a typical shard is not larger than 10G, but sizes of modern disks, even SSDs, start from 250G. So there is no problem to fit a single shard to a disk, even small.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md208"></a>
|
<h1><a class="anchor" id="autotoc_md210"></a>
|
||||||
Raw results table</h1>
|
Raw results table</h1>
|
||||||
<p>All sizes of constructed shards are shown in the following table. Rows corresponds to shard sizes (S) counted in ledgers, columns corresponds to numbers of offers (O) per ledger. In each cell there are two numbers: first number corresponds to the case of 1 account issuing offers, the second number corresponds to 8 accounts. Each number is a size of the shard with given parameters measured in megabytes.</p>
|
<p>All sizes of constructed shards are shown in the following table. Rows corresponds to shard sizes (S) counted in ledgers, columns corresponds to numbers of offers (O) per ledger. In each cell there are two numbers: first number corresponds to the case of 1 account issuing offers, the second number corresponds to 8 accounts. Each number is a size of the shard with given parameters measured in megabytes.</p>
|
||||||
<table class="markdownTable">
|
<table class="markdownTable">
|
||||||
@@ -92,12 +92,12 @@ Raw results table</h1>
|
|||||||
<tr class="markdownTableRowOdd">
|
<tr class="markdownTableRowOdd">
|
||||||
<td class="markdownTableBodyNone">16K </td><td class="markdownTableBodyNone">142.3/ 143.9 </td><td class="markdownTableBodyNone">279/9 280.8 </td><td class="markdownTableBodyNone">465.7/ 698.1 </td><td class="markdownTableBodyNone">696.4/ 1094.2 </td><td class="markdownTableBodyNone">1590.5/ 2166.6 </td></tr>
|
<td class="markdownTableBodyNone">16K </td><td class="markdownTableBodyNone">142.3/ 143.9 </td><td class="markdownTableBodyNone">279/9 280.8 </td><td class="markdownTableBodyNone">465.7/ 698.1 </td><td class="markdownTableBodyNone">696.4/ 1094.2 </td><td class="markdownTableBodyNone">1590.5/ 2166.6 </td></tr>
|
||||||
</table>
|
</table>
|
||||||
<h1><a class="anchor" id="autotoc_md209"></a>
|
<h1><a class="anchor" id="autotoc_md211"></a>
|
||||||
Preliminary conclusion</h1>
|
Preliminary conclusion</h1>
|
||||||
<p>If one compares a doubled size of shard with N ledgers and a size of shard with 2*N ledgers anywhere in the above table than the conlusion will be that the second number is greater. For example, the following table shows the percentage by which the second number is greater for the most interesting case of 30 offers per ledger. The first row corresponds to the case of 1 account issuing offers, and the second row corresponds to the case of 8 issuing accounts.</p>
|
<p>If one compares a doubled size of shard with N ledgers and a size of shard with 2*N ledgers anywhere in the above table than the conlusion will be that the second number is greater. For example, the following table shows the percentage by which the second number is greater for the most interesting case of 30 offers per ledger. The first row corresponds to the case of 1 account issuing offers, and the second row corresponds to the case of 8 issuing accounts.</p>
|
||||||
<p>|A\N|256|512|1K|2K|4K|8K| |—|—|—|—|—|—|—| |1|8%|6%|6%|6%|7%|6%|5%| |8|9%|7%|7%|8%|6%|7%|6%|</p>
|
<p>|A\N|256|512|1K|2K|4K|8K| |—|—|—|—|—|—|—| |1|8%|6%|6%|6%|7%|6%|5%| |8|9%|7%|7%|8%|6%|7%|6%|</p>
|
||||||
<p>The common conclusion in this model is that if one doubled the number of the ledgers in a shard then the total disk space utilized will raise by 5-9%.</p>
|
<p>The common conclusion in this model is that if one doubled the number of the ledgers in a shard then the total disk space utilized will raise by 5-9%.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md210"></a>
|
<h1><a class="anchor" id="autotoc_md212"></a>
|
||||||
Adding accounts into consideration</h1>
|
Adding accounts into consideration</h1>
|
||||||
<p>Previous model does not take into account that there are large number of XRP accounts in the mainnet, and each shard should contain information about each of these accounts. As of January 2020, there were about 1.9 million XRP accounts, and stored information about each of them is not less than 133 bytes. The constant 133 was obtained from debug print of rippled program when it saves account object to the database. So the actual size of each shard from raw table should be increased by at least 1.9M * 133 = 252.7M. Thus we obtained the following table of shard sizes for the most interesting case (30 offers per ledger and 8 issuing accounts) where S is shard size in ledgers and M is shard size in megabytes</p>
|
<p>Previous model does not take into account that there are large number of XRP accounts in the mainnet, and each shard should contain information about each of these accounts. As of January 2020, there were about 1.9 million XRP accounts, and stored information about each of them is not less than 133 bytes. The constant 133 was obtained from debug print of rippled program when it saves account object to the database. So the actual size of each shard from raw table should be increased by at least 1.9M * 133 = 252.7M. Thus we obtained the following table of shard sizes for the most interesting case (30 offers per ledger and 8 issuing accounts) where S is shard size in ledgers and M is shard size in megabytes</p>
|
||||||
<table class="markdownTable">
|
<table class="markdownTable">
|
||||||
@@ -107,7 +107,7 @@ Adding accounts into consideration</h1>
|
|||||||
<td class="markdownTableBodyNone">M </td><td class="markdownTableBodyNone">274.6 </td><td class="markdownTableBodyNone">300.6 </td><td class="markdownTableBodyNone">355.6 </td><td class="markdownTableBodyNone">473.7 </td><td class="markdownTableBodyNone">728.7 </td><td class="markdownTableBodyNone">1273.7 </td><td class="markdownTableBodyNone">2419.3 </td></tr>
|
<td class="markdownTableBodyNone">M </td><td class="markdownTableBodyNone">274.6 </td><td class="markdownTableBodyNone">300.6 </td><td class="markdownTableBodyNone">355.6 </td><td class="markdownTableBodyNone">473.7 </td><td class="markdownTableBodyNone">728.7 </td><td class="markdownTableBodyNone">1273.7 </td><td class="markdownTableBodyNone">2419.3 </td></tr>
|
||||||
</table>
|
</table>
|
||||||
<p>Now we can see from the last table that even considering minimum assumption about number of accounts and corresponding additional size of a shard, doubled size of shard with N ledgers is larger than size of a shard with 2*N ledgers. If number of accounts increase then this inequality will be even stronger.</p>
|
<p>Now we can see from the last table that even considering minimum assumption about number of accounts and corresponding additional size of a shard, doubled size of shard with N ledgers is larger than size of a shard with 2*N ledgers. If number of accounts increase then this inequality will be even stronger.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md211"></a>
|
<h1><a class="anchor" id="autotoc_md213"></a>
|
||||||
Using mainnet data</h1>
|
Using mainnet data</h1>
|
||||||
<p>Next idea to improve model is to count real shard sizes from mainnet. We used real 16k-ledgers shards with indexes from 2600 to 3600 with step 100, and corresponding real 8k-ledgers shards. Each 16k-ledgers shard consists of two 8k-ledgers shards which are called "corresponding". For example, 16k-ledgers shard with index 2600 consists of two 8k-ledgers shards with indexes 5200 and 5201.</p>
|
<p>Next idea to improve model is to count real shard sizes from mainnet. We used real 16k-ledgers shards with indexes from 2600 to 3600 with step 100, and corresponding real 8k-ledgers shards. Each 16k-ledgers shard consists of two 8k-ledgers shards which are called "corresponding". For example, 16k-ledgers shard with index 2600 consists of two 8k-ledgers shards with indexes 5200 and 5201.</p>
|
||||||
<p>In the following table we compare size of a 16k-ledgers shard with sum of sizes of two corresponding 8k-ledgers shards. There we only count size of nudb.dat file, sizes are in GB. Ratio is the size of two 8k-ledgers shards divided to the size of 16k-ledgers shard.</p>
|
<p>In the following table we compare size of a 16k-ledgers shard with sum of sizes of two corresponding 8k-ledgers shards. There we only count size of nudb.dat file, sizes are in GB. Ratio is the size of two 8k-ledgers shards divided to the size of 16k-ledgers shard.</p>
|
||||||
@@ -169,7 +169,7 @@ Using mainnet data</h1>
|
|||||||
<td class="markdownTableBodyNone">Average </td><td class="markdownTableBodyNone">4.43 </td><td class="markdownTableBodyNone">2.70 + 2.70 = 5.40 </td><td class="markdownTableBodyNone">1.22 </td></tr>
|
<td class="markdownTableBodyNone">Average </td><td class="markdownTableBodyNone">4.43 </td><td class="markdownTableBodyNone">2.70 + 2.70 = 5.40 </td><td class="markdownTableBodyNone">1.22 </td></tr>
|
||||||
</table>
|
</table>
|
||||||
<p>We can see that in all tables ratio is greater then 1, so using shards with 16 ledgers is preferred.</p>
|
<p>We can see that in all tables ratio is greater then 1, so using shards with 16 ledgers is preferred.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md212"></a>
|
<h1><a class="anchor" id="autotoc_md214"></a>
|
||||||
Compare 16K shards and 32K shards</h1>
|
Compare 16K shards and 32K shards</h1>
|
||||||
<p>To claim that shards with 16K ledgers are the best choice, we also assembled shards with 32k ledgers per shard with indexes from 1300 to 1800 with step 50 and corresponding shards with 16k ledgers per shard. For example, 32k-ledgers shard 1800 correnspond to 16k-ledgers shards with indexes 3600 and 3601 etc.</p>
|
<p>To claim that shards with 16K ledgers are the best choice, we also assembled shards with 32k ledgers per shard with indexes from 1300 to 1800 with step 50 and corresponding shards with 16k ledgers per shard. For example, 32k-ledgers shard 1800 correnspond to 16k-ledgers shards with indexes 3600 and 3601 etc.</p>
|
||||||
<p>Here are result tables for these shards similar to tables from previous part. In the first table we only take into consideration sizes of nudb.dat files.</p>
|
<p>Here are result tables for these shards similar to tables from previous part. In the first table we only take into consideration sizes of nudb.dat files.</p>
|
||||||
@@ -230,7 +230,7 @@ Compare 16K shards and 32K shards</h1>
|
|||||||
<tr class="markdownTableRowEven">
|
<tr class="markdownTableRowEven">
|
||||||
<td class="markdownTableBodyNone">Average </td><td class="markdownTableBodyNone">7.69 </td><td class="markdownTableBodyNone">4.43 + 4.20 = 8.63 </td><td class="markdownTableBodyNone">1.12 </td></tr>
|
<td class="markdownTableBodyNone">Average </td><td class="markdownTableBodyNone">7.69 </td><td class="markdownTableBodyNone">4.43 + 4.20 = 8.63 </td><td class="markdownTableBodyNone">1.12 </td></tr>
|
||||||
</table>
|
</table>
|
||||||
<h1><a class="anchor" id="autotoc_md213"></a>
|
<h1><a class="anchor" id="autotoc_md215"></a>
|
||||||
Conclusion</h1>
|
Conclusion</h1>
|
||||||
<p>We showed that using shards with 8k ledgers leads to raising required disk size by 22% in comparison with using shards with 16k ledgers. In the same way, using shards with 16k ledgers leads to raising required disk space by 12% in comparison with using shards with 32k ledgers. Note that increase ratio 12% is much less than 22% so using 32k-ledgers shards will bring us not so much economy in disk space.</p>
|
<p>We showed that using shards with 8k ledgers leads to raising required disk size by 22% in comparison with using shards with 16k ledgers. In the same way, using shards with 16k ledgers leads to raising required disk space by 12% in comparison with using shards with 32k ledgers. Note that increase ratio 12% is much less than 22% so using 32k-ledgers shards will bring us not so much economy in disk space.</p>
|
||||||
<p>At the same time, size is one thing to compare but there are other aspects. Smaller shards have an advantage that they take less time to acquire and finalize. They also make for smaller archived shards which take less time to download and import. Having more/smaller shards might also lead to better database concurrency/performance.</p>
|
<p>At the same time, size is one thing to compare but there are other aspects. Smaller shards have an advantage that they take less time to acquire and finalize. They also make for smaller archived shards which take less time to download and import. Having more/smaller shards might also lead to better database concurrency/performance.</p>
|
||||||
|
|||||||
@@ -65,19 +65,19 @@ $(function() {
|
|||||||
<div class="title">Overlay </div> </div>
|
<div class="title">Overlay </div> </div>
|
||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><h1><a class="anchor" id="autotoc_md215"></a>
|
<div class="textblock"><h1><a class="anchor" id="autotoc_md217"></a>
|
||||||
Introduction</h1>
|
Introduction</h1>
|
||||||
<p>The <em>XRP Ledger network</em> consists of a collection of <em>peers</em> running **<code>rippled</code>** or other compatible software. Each peer maintains multiple outgoing connections and optional incoming connections to other peers. These connections are made over both the public Internet and private local area networks. This network defines a connected directed graph of nodes where vertices are instances of <code>rippled</code> and edges are persistent TCP/IP connections. Peers send and receive messages to other connected peers. This peer to peer network, layered on top of the public and private Internet, forms an <a href="http://en.wikipedia.org/wiki/Overlay_network"><em>overlay network</em></a>. The contents of the messages and the behavior of peers in response to the messages, plus the information exchanged during the handshaking phase of connection establishment, defines the <em>XRP Ledger peer protocol</em> (or <em>protocol</em> in this context).</p>
|
<p>The <em>XRP Ledger network</em> consists of a collection of <em>peers</em> running **<code>rippled</code>** or other compatible software. Each peer maintains multiple outgoing connections and optional incoming connections to other peers. These connections are made over both the public Internet and private local area networks. This network defines a connected directed graph of nodes where vertices are instances of <code>rippled</code> and edges are persistent TCP/IP connections. Peers send and receive messages to other connected peers. This peer to peer network, layered on top of the public and private Internet, forms an <a href="http://en.wikipedia.org/wiki/Overlay_network"><em>overlay network</em></a>. The contents of the messages and the behavior of peers in response to the messages, plus the information exchanged during the handshaking phase of connection establishment, defines the <em>XRP Ledger peer protocol</em> (or <em>protocol</em> in this context).</p>
|
||||||
<h1><a class="anchor" id="autotoc_md216"></a>
|
<h1><a class="anchor" id="autotoc_md218"></a>
|
||||||
Overview</h1>
|
Overview</h1>
|
||||||
<p>Each connection is represented by a <em>Peer</em> object. The Overlay manager establishes, receives, and maintains connections to peers. Protocol messages are exchanged between peers and serialized using <a href="https://developers.google.com/protocol-buffers/"><em>Google Protocol Buffers</em></a>.</p>
|
<p>Each connection is represented by a <em>Peer</em> object. The Overlay manager establishes, receives, and maintains connections to peers. Protocol messages are exchanged between peers and serialized using <a href="https://developers.google.com/protocol-buffers/"><em>Google Protocol Buffers</em></a>.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md217"></a>
|
<h2><a class="anchor" id="autotoc_md219"></a>
|
||||||
Structure</h2>
|
Structure</h2>
|
||||||
<p>Each connection between peers is identified by its connection type, which affects the behavior of message routing. At present, only a single connection type is supported: <b>Peer</b>.</p>
|
<p>Each connection between peers is identified by its connection type, which affects the behavior of message routing. At present, only a single connection type is supported: <b>Peer</b>.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md218"></a>
|
<h1><a class="anchor" id="autotoc_md220"></a>
|
||||||
Handshake</h1>
|
Handshake</h1>
|
||||||
<p>To establish a protocol connection, a peer makes an outgoing TLS encrypted connection to a remote peer, then sends an HTTP request with no message body.</p>
|
<p>To establish a protocol connection, a peer makes an outgoing TLS encrypted connection to a remote peer, then sends an HTTP request with no message body.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md219"></a>
|
<h2><a class="anchor" id="autotoc_md221"></a>
|
||||||
HTTP</h2>
|
HTTP</h2>
|
||||||
<p>The HTTP <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html">request</a> must:</p>
|
<p>The HTTP <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html">request</a> must:</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -87,7 +87,7 @@ HTTP</h2>
|
|||||||
</ul>
|
</ul>
|
||||||
<p>HTTP requests which do not conform to this requirements must generate an appropriate HTTP error and result in the connection being closed.</p>
|
<p>HTTP requests which do not conform to this requirements must generate an appropriate HTTP error and result in the connection being closed.</p>
|
||||||
<p>Upon receipt of a well-formed HTTP upgrade request, and validation of the protocol specific parameters, a peer will either send back a HTTP 101 response and switch to the requested protocol, or a message indicating that the request failed (e.g. by sending HTTP 400 "Bad Request" or HTTP 503 "Service Unavailable").</p>
|
<p>Upon receipt of a well-formed HTTP upgrade request, and validation of the protocol specific parameters, a peer will either send back a HTTP 101 response and switch to the requested protocol, or a message indicating that the request failed (e.g. by sending HTTP 400 "Bad Request" or HTTP 503 "Service Unavailable").</p>
|
||||||
<h4><a class="anchor" id="autotoc_md220"></a>
|
<h4><a class="anchor" id="autotoc_md222"></a>
|
||||||
Example HTTP Upgrade Request</h4>
|
Example HTTP Upgrade Request</h4>
|
||||||
<div class="fragment"><div class="line">GET / HTTP/1.1</div>
|
<div class="fragment"><div class="line">GET / HTTP/1.1</div>
|
||||||
<div class="line">User-Agent: rippled-1.4.0-b1+DEBUG</div>
|
<div class="line">User-Agent: rippled-1.4.0-b1+DEBUG</div>
|
||||||
@@ -102,7 +102,7 @@ Example HTTP Upgrade Request</h4>
|
|||||||
<div class="line">Remote-IP: 192.0.2.79</div>
|
<div class="line">Remote-IP: 192.0.2.79</div>
|
||||||
<div class="line">Closed-Ledger: llRZSKqvNieGpPqbFGnm358pmF1aW96SDIUQcnMh6HI=</div>
|
<div class="line">Closed-Ledger: llRZSKqvNieGpPqbFGnm358pmF1aW96SDIUQcnMh6HI=</div>
|
||||||
<div class="line">Previous-Ledger: q4aKbP7sd5wv+EXArwCmQiWZhq9AwBl2p/hCtpGJNsc=</div>
|
<div class="line">Previous-Ledger: q4aKbP7sd5wv+EXArwCmQiWZhq9AwBl2p/hCtpGJNsc=</div>
|
||||||
</div><!-- fragment --><h4><a class="anchor" id="autotoc_md221"></a>
|
</div><!-- fragment --><h4><a class="anchor" id="autotoc_md223"></a>
|
||||||
Example HTTP Upgrade Response (Success)</h4>
|
Example HTTP Upgrade Response (Success)</h4>
|
||||||
<div class="fragment"><div class="line">HTTP/1.1 101 Switching Protocols</div>
|
<div class="fragment"><div class="line">HTTP/1.1 101 Switching Protocols</div>
|
||||||
<div class="line">Connection: Upgrade</div>
|
<div class="line">Connection: Upgrade</div>
|
||||||
@@ -115,7 +115,7 @@ Example HTTP Upgrade Response (Success)</h4>
|
|||||||
<div class="line">Network-Time: 619234797</div>
|
<div class="line">Network-Time: 619234797</div>
|
||||||
<div class="line">Closed-Ledger: h7HL85W9ywkex+G7p42USVeV5kE04CWK+4DVI19Of8I=</div>
|
<div class="line">Closed-Ledger: h7HL85W9ywkex+G7p42USVeV5kE04CWK+4DVI19Of8I=</div>
|
||||||
<div class="line">Previous-Ledger: EPvIpAD2iavGFyyZYi8REexAXyKGXsi1jMF7OIBY6/Y=</div>
|
<div class="line">Previous-Ledger: EPvIpAD2iavGFyyZYi8REexAXyKGXsi1jMF7OIBY6/Y=</div>
|
||||||
</div><!-- fragment --><h4><a class="anchor" id="autotoc_md222"></a>
|
</div><!-- fragment --><h4><a class="anchor" id="autotoc_md224"></a>
|
||||||
Example HTTP Upgrade Response (Failure: no slots available)</h4>
|
Example HTTP Upgrade Response (Failure: no slots available)</h4>
|
||||||
<div class="fragment"><div class="line">HTTP/1.1 503 Service Unavailable</div>
|
<div class="fragment"><div class="line">HTTP/1.1 503 Service Unavailable</div>
|
||||||
<div class="line">Server: rippled-0.27.0</div>
|
<div class="line">Server: rippled-0.27.0</div>
|
||||||
@@ -125,7 +125,7 @@ Example HTTP Upgrade Response (Failure: no slots available)</h4>
|
|||||||
<div class="line">{"peer-ips":["54.68.219.39:51235","54.187.191.179:51235",</div>
|
<div class="line">{"peer-ips":["54.68.219.39:51235","54.187.191.179:51235",</div>
|
||||||
<div class="line">"107.150.55.21:6561","54.186.230.77:51235","54.187.110.243:51235",</div>
|
<div class="line">"107.150.55.21:6561","54.186.230.77:51235","54.187.110.243:51235",</div>
|
||||||
<div class="line">"85.127.34.221:51235","50.43.33.236:51235","54.187.138.75:51235"]}</div>
|
<div class="line">"85.127.34.221:51235","50.43.33.236:51235","54.187.138.75:51235"]}</div>
|
||||||
</div><!-- fragment --><h3><a class="anchor" id="autotoc_md223"></a>
|
</div><!-- fragment --><h3><a class="anchor" id="autotoc_md225"></a>
|
||||||
Standard Fields</h3>
|
Standard Fields</h3>
|
||||||
<table class="markdownTable">
|
<table class="markdownTable">
|
||||||
<tr class="markdownTableHead">
|
<tr class="markdownTableHead">
|
||||||
@@ -162,7 +162,7 @@ Standard Fields</h3>
|
|||||||
<p>For responses, it should a consist of <em>single element</em> matching one of the elements provided in the corresponding request. If the server does not understand any of the available protocol versions, the upgrade request should fail with an appropriate HTTP error code (e.g. by sending an HTTP 400 "Bad Request" response).</p>
|
<p>For responses, it should a consist of <em>single element</em> matching one of the elements provided in the corresponding request. If the server does not understand any of the available protocol versions, the upgrade request should fail with an appropriate HTTP error code (e.g. by sending an HTTP 400 "Bad Request" response).</p>
|
||||||
<p>Protocol versions are string of the form <code>XRPL/</code> followed by a dotted major and minor protocol version number, where the major number is greater than or equal to 2 and the minor is greater than or equal to 0.</p>
|
<p>Protocol versions are string of the form <code>XRPL/</code> followed by a dotted major and minor protocol version number, where the major number is greater than or equal to 2 and the minor is greater than or equal to 0.</p>
|
||||||
<p>See <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.42">RFC 2616 §14.42</a></p>
|
<p>See <a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.42">RFC 2616 §14.42</a></p>
|
||||||
<h3><a class="anchor" id="autotoc_md224"></a>
|
<h3><a class="anchor" id="autotoc_md226"></a>
|
||||||
Custom Fields</h3>
|
Custom Fields</h3>
|
||||||
<table class="markdownTable">
|
<table class="markdownTable">
|
||||||
<tr class="markdownTableHead">
|
<tr class="markdownTableHead">
|
||||||
@@ -265,11 +265,11 @@ Custom Fields</h3>
|
|||||||
</table>
|
</table>
|
||||||
<p>If present, identifies the hash of the parent ledger that the sending server considers to be closed.</p>
|
<p>If present, identifies the hash of the parent ledger that the sending server considers to be closed.</p>
|
||||||
<p>The value is presently encoded using <b>Base64</b> encoding, but implementations should support both <b>Base64</b> and <b>HEX</b> encoding for this value.</p>
|
<p>The value is presently encoded using <b>Base64</b> encoding, but implementations should support both <b>Base64</b> and <b>HEX</b> encoding for this value.</p>
|
||||||
<h3><a class="anchor" id="autotoc_md225"></a>
|
<h3><a class="anchor" id="autotoc_md227"></a>
|
||||||
Additional Headers</h3>
|
Additional Headers</h3>
|
||||||
<p>An implementation or operator may specify additional, optional fields and values in both requests and responses.</p>
|
<p>An implementation or operator may specify additional, optional fields and values in both requests and responses.</p>
|
||||||
<p>Implementations should not reject requests because of the presence of fields that they do not understand.</p>
|
<p>Implementations should not reject requests because of the presence of fields that they do not understand.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md226"></a>
|
<h2><a class="anchor" id="autotoc_md228"></a>
|
||||||
Session Signature</h2>
|
Session Signature</h2>
|
||||||
<p>Even for SSL/TLS encrypted connections, it is possible for an attacker to mount relatively inexpensive MITM attacks that can be extremely hard to detect and may afford the attacker the ability to intelligently tamper with messages exchanged between the two endpoints.</p>
|
<p>Even for SSL/TLS encrypted connections, it is possible for an attacker to mount relatively inexpensive MITM attacks that can be extremely hard to detect and may afford the attacker the ability to intelligently tamper with messages exchanged between the two endpoints.</p>
|
||||||
<p>This risk can be mitigated if at least one side has a certificate from a certificate authority trusted by the other endpoint, but having a certificate is not always possible (or even desirable) in a decentralized and permissionless network.</p>
|
<p>This risk can be mitigated if at least one side has a certificate from a certificate authority trusted by the other endpoint, but having a certificate is not always possible (or even desirable) in a decentralized and permissionless network.</p>
|
||||||
@@ -280,11 +280,11 @@ Session Signature</h2>
|
|||||||
<p>Each side of the link will verify that the provided signature is from the claimed public key against the session's unique fingerprint. If this signature check fails then the link <b>MUST</b> be dropped.</p>
|
<p>Each side of the link will verify that the provided signature is from the claimed public key against the session's unique fingerprint. If this signature check fails then the link <b>MUST</b> be dropped.</p>
|
||||||
<p>If an attacker, Eve, establishes two separate SSL sessions with Alice and Bob, the fingerprints of the two sessions will be different, and Eve will not be able to sign the fingerprint of her session with Bob with Alice's private key, or the fingerprint of her session with Alice with Bob's private key, and so both A and B will know that an active MITM attack is in progress and will close their connections.</p>
|
<p>If an attacker, Eve, establishes two separate SSL sessions with Alice and Bob, the fingerprints of the two sessions will be different, and Eve will not be able to sign the fingerprint of her session with Bob with Alice's private key, or the fingerprint of her session with Alice with Bob's private key, and so both A and B will know that an active MITM attack is in progress and will close their connections.</p>
|
||||||
<p>If Eve simply proxies the raw bytes, she will be unable to decrypt the data being transferred between A and B and will not be able to intelligently tamper with the message stream between Alice and Bob, although she may be still be able to inject delays or terminate the link.</p>
|
<p>If Eve simply proxies the raw bytes, she will be unable to decrypt the data being transferred between A and B and will not be able to intelligently tamper with the message stream between Alice and Bob, although she may be still be able to inject delays or terminate the link.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md227"></a>
|
<h1><a class="anchor" id="autotoc_md229"></a>
|
||||||
Ripple Clustering</h1>
|
Ripple Clustering</h1>
|
||||||
<p>A cluster consists of more than one Ripple server under common administration that share load information, distribute cryptography operations, and provide greater response consistency.</p>
|
<p>A cluster consists of more than one Ripple server under common administration that share load information, distribute cryptography operations, and provide greater response consistency.</p>
|
||||||
<p>Cluster nodes are identified by their public node keys. Cluster nodes exchange information about endpoints that are imposing load upon them. Cluster nodes share information about their internal load status. Cluster nodes do not have to verify the cryptographic signatures on messages received from other cluster nodes.</p>
|
<p>Cluster nodes are identified by their public node keys. Cluster nodes exchange information about endpoints that are imposing load upon them. Cluster nodes share information about their internal load status. Cluster nodes do not have to verify the cryptographic signatures on messages received from other cluster nodes.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md228"></a>
|
<h2><a class="anchor" id="autotoc_md230"></a>
|
||||||
Configuration</h2>
|
Configuration</h2>
|
||||||
<p>A server's public key can be determined from the output of the <code>server_info</code> command. The key is in the <code>pubkey_node</code> value, and is a text string beginning with the letter <code>n</code>. The key is maintained across runs in a database.</p>
|
<p>A server's public key can be determined from the output of the <code>server_info</code> command. The key is in the <code>pubkey_node</code> value, and is a text string beginning with the letter <code>n</code>. The key is maintained across runs in a database.</p>
|
||||||
<p>Cluster members are configured in the <code>rippled.cfg</code> file under <code>[cluster_nodes]</code>. Each member should be configured on a line beginning with the node public key, followed optionally by a space and a friendly name.</p>
|
<p>Cluster members are configured in the <code>rippled.cfg</code> file under <code>[cluster_nodes]</code>. Each member should be configured on a line beginning with the node public key, followed optionally by a space and a friendly name.</p>
|
||||||
@@ -297,20 +297,20 @@ Configuration</h2>
|
|||||||
<li>Restart each hub, one by one</li>
|
<li>Restart each hub, one by one</li>
|
||||||
<li>Restart the spoke</li>
|
<li>Restart the spoke</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h2><a class="anchor" id="autotoc_md229"></a>
|
<h2><a class="anchor" id="autotoc_md231"></a>
|
||||||
Transaction Behavior</h2>
|
Transaction Behavior</h2>
|
||||||
<p>When a transaction is received from a cluster member, several normal checks are bypassed:</p>
|
<p>When a transaction is received from a cluster member, several normal checks are bypassed:</p>
|
||||||
<p>Signature checking is bypassed because we trust that a cluster member would not relay a transaction with an incorrect signature. Validators may wish to disable this feature, preferring the additional load to get the additional security of having validators check each transaction.</p>
|
<p>Signature checking is bypassed because we trust that a cluster member would not relay a transaction with an incorrect signature. Validators may wish to disable this feature, preferring the additional load to get the additional security of having validators check each transaction.</p>
|
||||||
<p>Local checks for transaction checking are also bypassed. For example, a server will not reject a transaction from a cluster peer because the fee does not meet its current relay fee. It is preferable to keep the cluster in agreement and permit confirmation from one cluster member to more reliably indicate the transaction's acceptance by the cluster.</p>
|
<p>Local checks for transaction checking are also bypassed. For example, a server will not reject a transaction from a cluster peer because the fee does not meet its current relay fee. It is preferable to keep the cluster in agreement and permit confirmation from one cluster member to more reliably indicate the transaction's acceptance by the cluster.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md230"></a>
|
<h2><a class="anchor" id="autotoc_md232"></a>
|
||||||
Server Load Information</h2>
|
Server Load Information</h2>
|
||||||
<p>Cluster members exchange information on their server's load level. The load level is essentially the amount by which the normal fee levels are multiplied to get the server's fee for relaying transactions.</p>
|
<p>Cluster members exchange information on their server's load level. The load level is essentially the amount by which the normal fee levels are multiplied to get the server's fee for relaying transactions.</p>
|
||||||
<p>A server's effective load level, and the one it uses to determine its relay fee, is the highest of its local load level, the network load level, and the cluster load level. The cluster load level is the median load level reported by a cluster member.</p>
|
<p>A server's effective load level, and the one it uses to determine its relay fee, is the highest of its local load level, the network load level, and the cluster load level. The cluster load level is the median load level reported by a cluster member.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md231"></a>
|
<h2><a class="anchor" id="autotoc_md233"></a>
|
||||||
Gossip</h2>
|
Gossip</h2>
|
||||||
<p>Gossip is the mechanism by which cluster members share information about endpoints (typically IPv4 addresses) that are imposing unusually high load on them. The endpoint load manager takes into account gossip to reduce the amount of load the endpoint is permitted to impose on the local server before it is warned, disconnected, or banned.</p>
|
<p>Gossip is the mechanism by which cluster members share information about endpoints (typically IPv4 addresses) that are imposing unusually high load on them. The endpoint load manager takes into account gossip to reduce the amount of load the endpoint is permitted to impose on the local server before it is warned, disconnected, or banned.</p>
|
||||||
<p>Suppose, for example, that an attacker controls a large number of IP addresses, and with these, he can send sufficient requests to overload a server. Without gossip, he could use these same addresses to overload all the servers in a cluster. With gossip, if he chooses to use the same IP address to impose load on more than one server, he will find that the amount of load he can impose before getting disconnected is much lower.</p>
|
<p>Suppose, for example, that an attacker controls a large number of IP addresses, and with these, he can send sufficient requests to overload a server. Without gossip, he could use these same addresses to overload all the servers in a cluster. With gossip, if he chooses to use the same IP address to impose load on more than one server, he will find that the amount of load he can impose before getting disconnected is much lower.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md232"></a>
|
<h2><a class="anchor" id="autotoc_md234"></a>
|
||||||
Monitoring</h2>
|
Monitoring</h2>
|
||||||
<p>The <code>peers</code> command will report on the status of the cluster. The <code>cluster</code> object will contain one entry for each member of the cluster (either configured or introduced by another cluster member). The <code>age</code> field is the number of seconds since the server was last heard from. If the server is reporting an elevated cluster fee, that will be reported as well.</p>
|
<p>The <code>peers</code> command will report on the status of the cluster. The <code>cluster</code> object will contain one entry for each member of the cluster (either configured or introduced by another cluster member). The <code>age</code> field is the number of seconds since the server was last heard from. If the server is reporting an elevated cluster fee, that will be reported as well.</p>
|
||||||
<p>In the <code>peers</code> object, cluster members will contain a <code>cluster</code> field set to <code>true</code>.</p>
|
<p>In the <code>peers</code> object, cluster members will contain a <code>cluster</code> field set to <code>true</code>.</p>
|
||||||
|
|||||||
@@ -65,14 +65,14 @@ $(function() {
|
|||||||
<div class="title">PeerFinder </div> </div>
|
<div class="title">PeerFinder </div> </div>
|
||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><h1><a class="anchor" id="autotoc_md235"></a>
|
<div class="textblock"><h1><a class="anchor" id="autotoc_md237"></a>
|
||||||
Introduction</h1>
|
Introduction</h1>
|
||||||
<p>The <em>Ripple payment network</em> consists of a collection of <em>peers</em> running the <b>rippled software</b>. Each peer maintains multiple outgoing connections and optional incoming connections to other peers. These connections are made over both the public Internet and private local area networks. This network defines a fully connected directed graph of nodes. Peers send and receive messages to other connected peers. This peer to peer network, layered on top of the public and private Internet, forms an <a href="http://en.wikipedia.org/wiki/Overlay_network"><em>overlay network</em></a>.</p>
|
<p>The <em>Ripple payment network</em> consists of a collection of <em>peers</em> running the <b>rippled software</b>. Each peer maintains multiple outgoing connections and optional incoming connections to other peers. These connections are made over both the public Internet and private local area networks. This network defines a fully connected directed graph of nodes. Peers send and receive messages to other connected peers. This peer to peer network, layered on top of the public and private Internet, forms an <a href="http://en.wikipedia.org/wiki/Overlay_network"><em>overlay network</em></a>.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md236"></a>
|
<h1><a class="anchor" id="autotoc_md238"></a>
|
||||||
Bootstrapping</h1>
|
Bootstrapping</h1>
|
||||||
<p>When a peer comes online it needs a set of IP addresses to connect to in order to gain initial entry into the overlay in a process called <em>bootstrapping</em>. Once they have established an initial set of these outbound peer connections, they need to gain additional addresses to establish more outbound peer connections until the desired limit is reached. Furthermore, they need a mechanism to advertise their IP address to new or existing peers in the overlay so they may receive inbound connections up to some desired limit. And finally, they need a mechanism to provide inbound connection requests with an alternate set of IP addresses to try when they have already reached their desired maximum number of inbound connections.</p>
|
<p>When a peer comes online it needs a set of IP addresses to connect to in order to gain initial entry into the overlay in a process called <em>bootstrapping</em>. Once they have established an initial set of these outbound peer connections, they need to gain additional addresses to establish more outbound peer connections until the desired limit is reached. Furthermore, they need a mechanism to advertise their IP address to new or existing peers in the overlay so they may receive inbound connections up to some desired limit. And finally, they need a mechanism to provide inbound connection requests with an alternate set of IP addresses to try when they have already reached their desired maximum number of inbound connections.</p>
|
||||||
<p>PeerFinder is a self contained module that provides these services, along with some additional overlay network management services such as <em>fixed slots</em> and <em>cluster slots</em>.</p>
|
<p>PeerFinder is a self contained module that provides these services, along with some additional overlay network management services such as <em>fixed slots</em> and <em>cluster slots</em>.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md237"></a>
|
<h1><a class="anchor" id="autotoc_md239"></a>
|
||||||
Features</h1>
|
Features</h1>
|
||||||
<p>PeerFinder has these responsibilities</p>
|
<p>PeerFinder has these responsibilities</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -86,18 +86,18 @@ Features</h1>
|
|||||||
<li>Prevent duplicate connections and connections to self.</li>
|
<li>Prevent duplicate connections and connections to self.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
<h1><a class="anchor" id="autotoc_md239"></a>
|
<h1><a class="anchor" id="autotoc_md241"></a>
|
||||||
Concepts</h1>
|
Concepts</h1>
|
||||||
<h2><a class="anchor" id="autotoc_md240"></a>
|
<h2><a class="anchor" id="autotoc_md242"></a>
|
||||||
Manager</h2>
|
Manager</h2>
|
||||||
<p>The <code>Manager</code> is an application singleton which provides the primary interface to interaction with the PeerFinder.</p>
|
<p>The <code>Manager</code> is an application singleton which provides the primary interface to interaction with the PeerFinder.</p>
|
||||||
<h3><a class="anchor" id="autotoc_md241"></a>
|
<h3><a class="anchor" id="autotoc_md243"></a>
|
||||||
Autoconnect</h3>
|
Autoconnect</h3>
|
||||||
<p>The Autoconnect feature of PeerFinder automatically establishes outgoing connections using addresses learned from various sources including the configuration file, the result of domain name lookups, and messages received from the overlay itself.</p>
|
<p>The Autoconnect feature of PeerFinder automatically establishes outgoing connections using addresses learned from various sources including the configuration file, the result of domain name lookups, and messages received from the overlay itself.</p>
|
||||||
<h3><a class="anchor" id="autotoc_md242"></a>
|
<h3><a class="anchor" id="autotoc_md244"></a>
|
||||||
Callback</h3>
|
Callback</h3>
|
||||||
<p>PeerFinder is an isolated code module with few external dependencies. To perform socket specific activities such as establishing outgoing connections or sending messages to connected peers, the Manager is constructed with an abstract interface called the <code>Callback</code>. An instance of this interface performs the actual required operations, making PeerFinder independent of the calling code.</p>
|
<p>PeerFinder is an isolated code module with few external dependencies. To perform socket specific activities such as establishing outgoing connections or sending messages to connected peers, the Manager is constructed with an abstract interface called the <code>Callback</code>. An instance of this interface performs the actual required operations, making PeerFinder independent of the calling code.</p>
|
||||||
<h3><a class="anchor" id="autotoc_md243"></a>
|
<h3><a class="anchor" id="autotoc_md245"></a>
|
||||||
Config</h3>
|
Config</h3>
|
||||||
<p>The <code>Config</code> structure defines the operational parameters of the PeerFinder. Some values come from the configuration file while others are calculated via tuned heuristics. The fields are as follows:</p>
|
<p>The <code>Config</code> structure defines the operational parameters of the PeerFinder. Some values come from the configuration file while others are calculated via tuned heuristics. The fields are as follows:</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -119,14 +119,14 @@ Config</h3>
|
|||||||
</ul>
|
</ul>
|
||||||
<p>Here's an example of how the network might be structured with a fractional value for outPeers:</p>
|
<p>Here's an example of how the network might be structured with a fractional value for outPeers:</p>
|
||||||
<p>**(Need example here)**</p>
|
<p>**(Need example here)**</p>
|
||||||
<h3><a class="anchor" id="autotoc_md244"></a>
|
<h3><a class="anchor" id="autotoc_md246"></a>
|
||||||
Livecache</h3>
|
Livecache</h3>
|
||||||
<p>The Livecache holds relayed IP addresses that have been received recently in the form of Endpoint messages via the peer to peer overlay. A peer periodically broadcasts the Endpoint message to its neighbors when it has open inbound connection slots. Peers store these messages in the Livecache and periodically forward their neighbors a handful of random entries from their Livecache, with an incremented hop count for each forwarded entry.</p>
|
<p>The Livecache holds relayed IP addresses that have been received recently in the form of Endpoint messages via the peer to peer overlay. A peer periodically broadcasts the Endpoint message to its neighbors when it has open inbound connection slots. Peers store these messages in the Livecache and periodically forward their neighbors a handful of random entries from their Livecache, with an incremented hop count for each forwarded entry.</p>
|
||||||
<p>The algorithm for sending a neighbor a set of Endpoint messages chooses evenly from all available hop counts on each send. This ensures that each peer will see some entries with the farthest hops at each iteration. The result is to expand a peer's horizon with respect to which overlay endpoints are visible. This is designed to force the overlay to become highly connected and reduce the network diameter with each connection establishment.</p>
|
<p>The algorithm for sending a neighbor a set of Endpoint messages chooses evenly from all available hop counts on each send. This ensures that each peer will see some entries with the farthest hops at each iteration. The result is to expand a peer's horizon with respect to which overlay endpoints are visible. This is designed to force the overlay to become highly connected and reduce the network diameter with each connection establishment.</p>
|
||||||
<p>When a peer receives an Endpoint message that originates from a neighbor (identified by a hop count of zero) for the first time, it performs an incoming connection test on that neighbor by initiating an outgoing connection to the remote IP address as seen on the connection combined with the port advertised in the Endpoint message. If the test fails, then the peer considers its neighbor firewalled (intentionally or due to misconfiguration) and not forward neighbor endpoint in Endpoint messages. This prevents poor quality unconnectible addresses from landing in the caches. If the incoming connection test passes, then the peer fills in the Endpoint message with the remote address as seen on the connection before storing it in its cache and forwarding it to other peers. This relieves the neighbor from the responsibility of knowing its own IP address before it can start receiving incoming connections.</p>
|
<p>When a peer receives an Endpoint message that originates from a neighbor (identified by a hop count of zero) for the first time, it performs an incoming connection test on that neighbor by initiating an outgoing connection to the remote IP address as seen on the connection combined with the port advertised in the Endpoint message. If the test fails, then the peer considers its neighbor firewalled (intentionally or due to misconfiguration) and not forward neighbor endpoint in Endpoint messages. This prevents poor quality unconnectible addresses from landing in the caches. If the incoming connection test passes, then the peer fills in the Endpoint message with the remote address as seen on the connection before storing it in its cache and forwarding it to other peers. This relieves the neighbor from the responsibility of knowing its own IP address before it can start receiving incoming connections.</p>
|
||||||
<p>Livecache entries expire quickly. Since a peer stops advertising itself when it no longer has available inbound slots, its address will shortly after stop being handed out by other peers. Livecache entries are very likely to result in both a successful connection establishment and the acquisition of an active outbound slot. Compare this with Bootcache addresses, which are very likely to be connectible but unlikely to have an open slot.</p>
|
<p>Livecache entries expire quickly. Since a peer stops advertising itself when it no longer has available inbound slots, its address will shortly after stop being handed out by other peers. Livecache entries are very likely to result in both a successful connection establishment and the acquisition of an active outbound slot. Compare this with Bootcache addresses, which are very likely to be connectible but unlikely to have an open slot.</p>
|
||||||
<p>Because entries in the Livecache are ephemeral, they are not persisted across launches in the database. The Livecache is continually updated and expired as Endpoint messages are received from the overlay over time.</p>
|
<p>Because entries in the Livecache are ephemeral, they are not persisted across launches in the database. The Livecache is continually updated and expired as Endpoint messages are received from the overlay over time.</p>
|
||||||
<h3><a class="anchor" id="autotoc_md245"></a>
|
<h3><a class="anchor" id="autotoc_md247"></a>
|
||||||
Bootcache</h3>
|
Bootcache</h3>
|
||||||
<p>The <code>Bootcache</code> stores IP addresses useful for gaining initial connections. Each address is associated with the following metadata:</p>
|
<p>The <code>Bootcache</code> stores IP addresses useful for gaining initial connections. Each address is associated with the following metadata:</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -137,10 +137,10 @@ Bootcache</h3>
|
|||||||
<p>When choosing addresses from the boot cache for the purpose of establishing outgoing connections, addresses are ranked in decreasing order of valence. The Bootcache is persistent. Entries are periodically inserted and updated in the corresponding SQLite database during program operation. When <b>rippled</b> is launched, the existing Bootcache database data is accessed and loaded to accelerate the bootstrap process.</p>
|
<p>When choosing addresses from the boot cache for the purpose of establishing outgoing connections, addresses are ranked in decreasing order of valence. The Bootcache is persistent. Entries are periodically inserted and updated in the corresponding SQLite database during program operation. When <b>rippled</b> is launched, the existing Bootcache database data is accessed and loaded to accelerate the bootstrap process.</p>
|
||||||
<p>Desirable entries in the Bootcache are addresses for servers which are known to have high uptimes, and for which connection attempts usually succeed. However, these servers do not necessarily have available inbound connection slots. However, it is assured that these servers will have a well populated Livecache since they will have moved towards the core of the overlay over their high uptime. When a connected server is full it will return a handful of new addresses from its Livecache and gracefully close the connection. Addresses from the Livecache are highly likely to have inbound connection slots and be connectible.</p>
|
<p>Desirable entries in the Bootcache are addresses for servers which are known to have high uptimes, and for which connection attempts usually succeed. However, these servers do not necessarily have available inbound connection slots. However, it is assured that these servers will have a well populated Livecache since they will have moved towards the core of the overlay over their high uptime. When a connected server is full it will return a handful of new addresses from its Livecache and gracefully close the connection. Addresses from the Livecache are highly likely to have inbound connection slots and be connectible.</p>
|
||||||
<p>For security, all information that contributes to the ranking of Bootcache entries is observed locally. PeerFinder never trusts external sources of information.</p>
|
<p>For security, all information that contributes to the ranking of Bootcache entries is observed locally. PeerFinder never trusts external sources of information.</p>
|
||||||
<h3><a class="anchor" id="autotoc_md246"></a>
|
<h3><a class="anchor" id="autotoc_md248"></a>
|
||||||
Slot</h3>
|
Slot</h3>
|
||||||
<p>Each TCP/IP socket that can participate in the peer to peer overlay occupies a slot. Slots have properties and state associated with them:</p>
|
<p>Each TCP/IP socket that can participate in the peer to peer overlay occupies a slot. Slots have properties and state associated with them:</p>
|
||||||
<h4><a class="anchor" id="autotoc_md247"></a>
|
<h4><a class="anchor" id="autotoc_md249"></a>
|
||||||
State (Slot)</h4>
|
State (Slot)</h4>
|
||||||
<p>The slot state represents the current stage of the connection as it passes through the business logic for establishing peer connections.</p>
|
<p>The slot state represents the current stage of the connection as it passes through the business logic for establishing peer connections.</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -160,7 +160,7 @@ State (Slot)</h4>
|
|||||||
<p class="startli">The Closing state represents a connected socket in the process of being gracefully closed.</p>
|
<p class="startli">The Closing state represents a connected socket in the process of being gracefully closed.</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h4><a class="anchor" id="autotoc_md248"></a>
|
<h4><a class="anchor" id="autotoc_md250"></a>
|
||||||
Properties (Slot)</h4>
|
Properties (Slot)</h4>
|
||||||
<p>Slot properties may be combined and are not mutually exclusive.</p>
|
<p>Slot properties may be combined and are not mutually exclusive.</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -177,19 +177,19 @@ Properties (Slot)</h4>
|
|||||||
<p class="startli">A superpeer slot is a connection to a peer which can accept incoming connections, meets certain resource availaibility requirements (such as bandwidth, CPU, and storage capacity), and operates full duplex in the overlay. Connections which are not superpeers are by definition leaves. A leaf slot is a connection to a peer which does not route overlay messages to other peers, and operates in a partial half duplex fashion in the overlay.</p>
|
<p class="startli">A superpeer slot is a connection to a peer which can accept incoming connections, meets certain resource availaibility requirements (such as bandwidth, CPU, and storage capacity), and operates full duplex in the overlay. Connections which are not superpeers are by definition leaves. A leaf slot is a connection to a peer which does not route overlay messages to other peers, and operates in a partial half duplex fashion in the overlay.</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h4><a class="anchor" id="autotoc_md249"></a>
|
<h4><a class="anchor" id="autotoc_md251"></a>
|
||||||
Fixed Slots</h4>
|
Fixed Slots</h4>
|
||||||
<p>Fixed slots are identified by IP address and set up during the initialization of the Manager, usually from the configuration file. The Logic will always make outgoing connection attempts to each fixed slot which is not currently connected. If we receive an inbound connection from an endpoint whose address portion (without port) matches a fixed slot address, we consider the fixed slot to be connected.</p>
|
<p>Fixed slots are identified by IP address and set up during the initialization of the Manager, usually from the configuration file. The Logic will always make outgoing connection attempts to each fixed slot which is not currently connected. If we receive an inbound connection from an endpoint whose address portion (without port) matches a fixed slot address, we consider the fixed slot to be connected.</p>
|
||||||
<h4><a class="anchor" id="autotoc_md250"></a>
|
<h4><a class="anchor" id="autotoc_md252"></a>
|
||||||
Cluster Slots</h4>
|
Cluster Slots</h4>
|
||||||
<p>Cluster slots are identified by the public key and set up during the initialization of the manager or discovered upon receipt of messages in the overlay from trusted connections.</p>
|
<p>Cluster slots are identified by the public key and set up during the initialization of the manager or discovered upon receipt of messages in the overlay from trusted connections.</p>
|
||||||
<hr />
|
<hr />
|
||||||
<h1><a class="anchor" id="autotoc_md252"></a>
|
<h1><a class="anchor" id="autotoc_md254"></a>
|
||||||
Algorithms</h1>
|
Algorithms</h1>
|
||||||
<h2><a class="anchor" id="autotoc_md253"></a>
|
<h2><a class="anchor" id="autotoc_md255"></a>
|
||||||
Connection Strategy</h2>
|
Connection Strategy</h2>
|
||||||
<p>The <em>Connection Strategy</em> applies the configuration settings to establish desired outbound connections. It runs periodically and progresses through a series of stages, remaining in each stage until a condition is met</p>
|
<p>The <em>Connection Strategy</em> applies the configuration settings to establish desired outbound connections. It runs periodically and progresses through a series of stages, remaining in each stage until a condition is met</p>
|
||||||
<h3><a class="anchor" id="autotoc_md254"></a>
|
<h3><a class="anchor" id="autotoc_md256"></a>
|
||||||
Stage 1: Fixed Slots</h3>
|
Stage 1: Fixed Slots</h3>
|
||||||
<p>This stage is invoked when the number of active fixed connections is below the number of fixed connections specified in the configuration, and one of the following is true:</p>
|
<p>This stage is invoked when the number of active fixed connections is below the number of fixed connections specified in the configuration, and one of the following is true:</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -198,7 +198,7 @@ Stage 1: Fixed Slots</h3>
|
|||||||
</ul>
|
</ul>
|
||||||
<p>Each fixed address is associated with a retry timer. On a fixed connection failure, the timer is reset so that the address is not tried for some amount of time, which increases according to a scheduled sequence up to some maximum which is currently set to approximately one hour between retries. A fixed address is considered eligible if we are not currently connected or attempting the address, and its retry timer has expired.</p>
|
<p>Each fixed address is associated with a retry timer. On a fixed connection failure, the timer is reset so that the address is not tried for some amount of time, which increases according to a scheduled sequence up to some maximum which is currently set to approximately one hour between retries. A fixed address is considered eligible if we are not currently connected or attempting the address, and its retry timer has expired.</p>
|
||||||
<p>The PeerFinder makes its best effort to become fully connected to the fixed addresses specified in the configuration file before moving on to establish outgoing connections to foreign peers. This security feature helps rippled establish itself with a trusted set of peers first before accepting untrusted data from the network.</p>
|
<p>The PeerFinder makes its best effort to become fully connected to the fixed addresses specified in the configuration file before moving on to establish outgoing connections to foreign peers. This security feature helps rippled establish itself with a trusted set of peers first before accepting untrusted data from the network.</p>
|
||||||
<h3><a class="anchor" id="autotoc_md255"></a>
|
<h3><a class="anchor" id="autotoc_md257"></a>
|
||||||
Stage 2: Livecache</h3>
|
Stage 2: Livecache</h3>
|
||||||
<p>The Livecache is invoked when Stage 1 is not active, autoconnect is enabled, and the number of active outbound connections is below the number desired. The stage remains active while:</p>
|
<p>The Livecache is invoked when Stage 1 is not active, autoconnect is enabled, and the number of active outbound connections is below the number desired. The stage remains active while:</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -206,7 +206,7 @@ Stage 2: Livecache</h3>
|
|||||||
<li>Any outbound connection attempts are in progress</li>
|
<li>Any outbound connection attempts are in progress</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>PeerFinder makes its best effort to exhaust addresses in the Livecache before moving on to the Bootcache, because Livecache addresses are highly likely to be connectible (since they are known to have been online within the last minute), and highly likely to have an open slot for an incoming connection (because peers only advertise themselves in the Livecache when they have open slots).</p>
|
<p>PeerFinder makes its best effort to exhaust addresses in the Livecache before moving on to the Bootcache, because Livecache addresses are highly likely to be connectible (since they are known to have been online within the last minute), and highly likely to have an open slot for an incoming connection (because peers only advertise themselves in the Livecache when they have open slots).</p>
|
||||||
<h3><a class="anchor" id="autotoc_md256"></a>
|
<h3><a class="anchor" id="autotoc_md258"></a>
|
||||||
Stage 3: Bootcache</h3>
|
Stage 3: Bootcache</h3>
|
||||||
<p>The Bootcache is invoked when Stage 1 and Stage 2 are not active, autoconnect is enabled, and the number of active outbound connections is below the number desired. The stage remains active while:</p>
|
<p>The Bootcache is invoked when Stage 1 and Stage 2 are not active, autoconnect is enabled, and the number of active outbound connections is below the number desired. The stage remains active while:</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -214,7 +214,7 @@ Stage 3: Bootcache</h3>
|
|||||||
</ul>
|
</ul>
|
||||||
<p>Entries in the Bootcache are ranked, with highly connectible addresses preferred over others. Connection attempts to Bootcache addresses are very likely to succeed but unlikely to produce an active connection since the peers likely do not have open slots. Before the remote peer closes the connection it will send a handful of addresses from its Livecache to help the new peer coming online obtain connections.</p>
|
<p>Entries in the Bootcache are ranked, with highly connectible addresses preferred over others. Connection attempts to Bootcache addresses are very likely to succeed but unlikely to produce an active connection since the peers likely do not have open slots. Before the remote peer closes the connection it will send a handful of addresses from its Livecache to help the new peer coming online obtain connections.</p>
|
||||||
<hr />
|
<hr />
|
||||||
<h1><a class="anchor" id="autotoc_md258"></a>
|
<h1><a class="anchor" id="autotoc_md260"></a>
|
||||||
References</h1>
|
References</h1>
|
||||||
<p>Much of the work in PeerFinder was inspired by earlier work in Gnutella:</p>
|
<p>Much of the work in PeerFinder was inspired by earlier work in Gnutella:</p>
|
||||||
<p><a href="http://rfc-gnutella.sourceforge.net/src/pong-caching.html">Revised Gnutella Ping Pong Scheme</a><br />
|
<p><a href="http://rfc-gnutella.sourceforge.net/src/pong-caching.html">Revised Gnutella Ping Pong Scheme</a><br />
|
||||||
|
|||||||
@@ -66,26 +66,26 @@ $(function() {
|
|||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><p>This folder contains the protocol buffer definitions used by the rippled gRPC API. The gRPC API attempts to mimic the JSON/Websocket API as much as possible. As of April 2020, the gRPC API supports a subset of the full rippled API: tx, account_tx, account_info, fee and submit.</p>
|
<div class="textblock"><p>This folder contains the protocol buffer definitions used by the rippled gRPC API. The gRPC API attempts to mimic the JSON/Websocket API as much as possible. As of April 2020, the gRPC API supports a subset of the full rippled API: tx, account_tx, account_info, fee and submit.</p>
|
||||||
<h3><a class="anchor" id="autotoc_md259"></a>
|
<h3><a class="anchor" id="autotoc_md261"></a>
|
||||||
Making Changes</h3>
|
Making Changes</h3>
|
||||||
<h4><a class="anchor" id="autotoc_md260"></a>
|
<h4><a class="anchor" id="autotoc_md262"></a>
|
||||||
Wire Format and Backwards Compatibility</h4>
|
Wire Format and Backwards Compatibility</h4>
|
||||||
<p>When making changes to the protocol buffer definitions in this folder, care must be taken to ensure the changes do not break the wire format, which would break backwards compatibility. At a high level, do not change any existing fields. This includes the field's name, type and field number. Do not remove any existing fields. It is always safe to add fields; just remember to give each of the new fields a unique field number. The field numbers don't have to be in any particular order and there can be gaps. More info about what changes break the wire format can be found <a href="https://developers.google.com/protocol-buffers/docs/proto3#updating">here</a>.</p>
|
<p>When making changes to the protocol buffer definitions in this folder, care must be taken to ensure the changes do not break the wire format, which would break backwards compatibility. At a high level, do not change any existing fields. This includes the field's name, type and field number. Do not remove any existing fields. It is always safe to add fields; just remember to give each of the new fields a unique field number. The field numbers don't have to be in any particular order and there can be gaps. More info about what changes break the wire format can be found <a href="https://developers.google.com/protocol-buffers/docs/proto3#updating">here</a>.</p>
|
||||||
<h4><a class="anchor" id="autotoc_md261"></a>
|
<h4><a class="anchor" id="autotoc_md263"></a>
|
||||||
Conventions</h4>
|
Conventions</h4>
|
||||||
<p>For fields that are reused across different message types, we define the field as a unique message type in common.proto. The name of the message type is the same as the field name, with the exception that the field name itself is snake case, whereas the message type is in Pascal case. The message type has one field, called <code>value</code>. This pattern does not need to be strictly followed across the entire API, but should be followed for transactions and ledger objects, since there is a high rate of field reuse across different transactions and ledger objects. The motivation for this pattern is two-fold. First, we ensure the field has the same type everywhere that the field is used. Second, wrapping primitive types in their own message type prevents default initialization of those primitive types. For example, <code>uint32</code> is initialized to <code>0</code> if not explicitly set; there is no way to tell if the client or server set the field to <code>0</code> (which may be a valid value for the field) or the field was default initialized.</p>
|
<p>For fields that are reused across different message types, we define the field as a unique message type in common.proto. The name of the message type is the same as the field name, with the exception that the field name itself is snake case, whereas the message type is in Pascal case. The message type has one field, called <code>value</code>. This pattern does not need to be strictly followed across the entire API, but should be followed for transactions and ledger objects, since there is a high rate of field reuse across different transactions and ledger objects. The motivation for this pattern is two-fold. First, we ensure the field has the same type everywhere that the field is used. Second, wrapping primitive types in their own message type prevents default initialization of those primitive types. For example, <code>uint32</code> is initialized to <code>0</code> if not explicitly set; there is no way to tell if the client or server set the field to <code>0</code> (which may be a valid value for the field) or the field was default initialized.</p>
|
||||||
<h4><a class="anchor" id="autotoc_md262"></a>
|
<h4><a class="anchor" id="autotoc_md264"></a>
|
||||||
Name Collisions</h4>
|
Name Collisions</h4>
|
||||||
<p>Each message type must have a unique name. To resolve collisions, add a suffix to one or more message types. For instance, ledger objects and transaction types often have the same name (<code>DepositPreauth</code> for example). To resolve this, the <code>DepositPreauth</code> ledger object is named <code>DepositPreauthObject</code>.</p>
|
<p>Each message type must have a unique name. To resolve collisions, add a suffix to one or more message types. For instance, ledger objects and transaction types often have the same name (<code>DepositPreauth</code> for example). To resolve this, the <code>DepositPreauth</code> ledger object is named <code>DepositPreauthObject</code>.</p>
|
||||||
<h4><a class="anchor" id="autotoc_md263"></a>
|
<h4><a class="anchor" id="autotoc_md265"></a>
|
||||||
To add a field or message type</h4>
|
To add a field or message type</h4>
|
||||||
<p>To add a field to a message, define the fields type, name and unique index. To add a new message type, give the message type a unique name. Then, add the appropriate C++ code in GRPCHelpers.cpp, or in the handler itself, to serialize/deserialize the new field or message type.</p>
|
<p>To add a field to a message, define the fields type, name and unique index. To add a new message type, give the message type a unique name. Then, add the appropriate C++ code in GRPCHelpers.cpp, or in the handler itself, to serialize/deserialize the new field or message type.</p>
|
||||||
<h4><a class="anchor" id="autotoc_md264"></a>
|
<h4><a class="anchor" id="autotoc_md266"></a>
|
||||||
To add a new gRPC method</h4>
|
To add a new gRPC method</h4>
|
||||||
<p>To add a new gRPC method, add the gRPC method in xrp_ledger.proto. The method name should begin with a verb. Define the request and response types in their own file. The name of the request type should be the method name suffixed with <code>Request</code>, and the response type name should be the method name suffixed with <code>Response</code>. For example, the <code>GetAccountInfo</code> method has request type <code>GetAccountInfoRequest</code> and response type <code>GetAccountInfoResponse</code>.</p>
|
<p>To add a new gRPC method, add the gRPC method in xrp_ledger.proto. The method name should begin with a verb. Define the request and response types in their own file. The name of the request type should be the method name suffixed with <code>Request</code>, and the response type name should be the method name suffixed with <code>Response</code>. For example, the <code>GetAccountInfo</code> method has request type <code>GetAccountInfoRequest</code> and response type <code>GetAccountInfoResponse</code>.</p>
|
||||||
<p>After defining the protobuf messages for the new method, add an instantiation of the templated <code>CallData</code> class in GRPCServerImpl::setupListeners(). The template parameters should be the request type and the response type.</p>
|
<p>After defining the protobuf messages for the new method, add an instantiation of the templated <code>CallData</code> class in GRPCServerImpl::setupListeners(). The template parameters should be the request type and the response type.</p>
|
||||||
<p>Finally, define the handler itself in the appropriate file under the src/ripple/rpc/handlers folder. If the method already has a JSON/Websocket equivalent, write the gRPC handler in the same file, and abstract common logic into helper functions (see <a class="el" href="Tx_8cpp_source.html">Tx.cpp</a> or <a class="el" href="AccountTx_8cpp_source.html">AccountTx.cpp</a> for an example).</p>
|
<p>Finally, define the handler itself in the appropriate file under the src/ripple/rpc/handlers folder. If the method already has a JSON/Websocket equivalent, write the gRPC handler in the same file, and abstract common logic into helper functions (see <a class="el" href="Tx_8cpp_source.html">Tx.cpp</a> or <a class="el" href="AccountTx_8cpp_source.html">AccountTx.cpp</a> for an example).</p>
|
||||||
<h4><a class="anchor" id="autotoc_md265"></a>
|
<h4><a class="anchor" id="autotoc_md267"></a>
|
||||||
Testing</h4>
|
Testing</h4>
|
||||||
<p>When modifying an existing gRPC method, be sure to test that modification in the corresponding, existing unit test. When creating a new gRPC method, implement a class that derives from GRPCTestClientBase, and use the newly created class to call the new method. See the class <code>GrpcTxClient</code> in the file Tx_test.cpp for an example. The gRPC tests are paired with their JSON counterpart, and the tests should mirror the JSON test as much as possible.</p>
|
<p>When modifying an existing gRPC method, be sure to test that modification in the corresponding, existing unit test. When creating a new gRPC method, implement a class that derives from GRPCTestClientBase, and use the newly created class to call the new method. See the class <code>GrpcTxClient</code> in the file Tx_test.cpp for an example. The gRPC tests are paired with their JSON counterpart, and the tests should mirror the JSON test as much as possible.</p>
|
||||||
<p>Refer to the Protocol Buffers <a href="https://developers.google.com/protocol-buffers/docs/proto3">language guide</a> for more detailed information about Protocol Buffers. </p>
|
<p>Refer to the Protocol Buffers <a href="https://developers.google.com/protocol-buffers/docs/proto3">language guide</a> for more detailed information about Protocol Buffers. </p>
|
||||||
|
|||||||
@@ -66,11 +66,11 @@ $(function() {
|
|||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><p>Classes and functions for handling data and values associated with the XRP Ledger protocol.</p>
|
<div class="textblock"><p>Classes and functions for handling data and values associated with the XRP Ledger protocol.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md268"></a>
|
<h1><a class="anchor" id="autotoc_md270"></a>
|
||||||
Serialized Objects</h1>
|
Serialized Objects</h1>
|
||||||
<p>Objects transmitted over the network must be serialized into a canonical format. The prefix "ST" refers to classes that deal with the serialized format.</p>
|
<p>Objects transmitted over the network must be serialized into a canonical format. The prefix "ST" refers to classes that deal with the serialized format.</p>
|
||||||
<p>The term "Tx" or "tx" is an abbreviation for "Transaction", a commonly occurring object type.</p>
|
<p>The term "Tx" or "tx" is an abbreviation for "Transaction", a commonly occurring object type.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md269"></a>
|
<h2><a class="anchor" id="autotoc_md271"></a>
|
||||||
Optional Fields</h2>
|
Optional Fields</h2>
|
||||||
<p>Our serialized fields have some "type magic" to make optional fields easier to read:</p>
|
<p>Our serialized fields have some "type magic" to make optional fields easier to read:</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -84,7 +84,7 @@ Optional Fields</h2>
|
|||||||
</ul>
|
</ul>
|
||||||
<p>Typically, for things that are guaranteed to exist, you use <code>x[sfFoo]</code> and avoid having to deal with a container that may or may not hold a value. For things not guaranteed to exist, you use <code>x[~sfFoo]</code> because you want such a container. It avoids having to look something up twice, once just to see if it exists and a second time to get/set its value. (<a href="https://github.com/ripple/rippled/blob/35f4698aed5dce02f771b34cfbb690495cb5efcc/src/ripple/app/tx/impl/PayChan.cpp#L229-L236">Real example</a>)</p>
|
<p>Typically, for things that are guaranteed to exist, you use <code>x[sfFoo]</code> and avoid having to deal with a container that may or may not hold a value. For things not guaranteed to exist, you use <code>x[~sfFoo]</code> because you want such a container. It avoids having to look something up twice, once just to see if it exists and a second time to get/set its value. (<a href="https://github.com/ripple/rippled/blob/35f4698aed5dce02f771b34cfbb690495cb5efcc/src/ripple/app/tx/impl/PayChan.cpp#L229-L236">Real example</a>)</p>
|
||||||
<p>The source of this "type magic" is in <a href="./SField.h#L296-L302">SField.h</a>.</p>
|
<p>The source of this "type magic" is in <a href="./SField.h#L296-L302">SField.h</a>.</p>
|
||||||
<h2><a class="anchor" id="autotoc_md270"></a>
|
<h2><a class="anchor" id="autotoc_md272"></a>
|
||||||
Related Resources</h2>
|
Related Resources</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="https://github.com/XRPLF/xrpl.js/tree/main/packages/ripple-binary-codec/src/enums">ripple-binary-codec SField enums</a></li>
|
<li><a href="https://github.com/XRPLF/xrpl.js/tree/main/packages/ripple-binary-codec/src/enums">ripple-binary-codec SField enums</a></li>
|
||||||
|
|||||||
@@ -72,14 +72,14 @@ $(function() {
|
|||||||
<li>Provide an interface to share load information in a cluster.</li>
|
<li>Provide an interface to share load information in a cluster.</li>
|
||||||
<li>Warn and/or disconnect endpoints for imposing load.</li>
|
<li>Warn and/or disconnect endpoints for imposing load.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h1><a class="anchor" id="autotoc_md273"></a>
|
<h1><a class="anchor" id="autotoc_md275"></a>
|
||||||
Description</h1>
|
Description</h1>
|
||||||
<p>To prevent monopolization of server resources or attacks on servers, resource consumption is monitored at each endpoint. When consumption exceeds certain thresholds, costs are imposed. Costs could include charging additional XRP for transactions, requiring a proof of work to be performed, or simply disconnecting the endpoint.</p>
|
<p>To prevent monopolization of server resources or attacks on servers, resource consumption is monitored at each endpoint. When consumption exceeds certain thresholds, costs are imposed. Costs could include charging additional XRP for transactions, requiring a proof of work to be performed, or simply disconnecting the endpoint.</p>
|
||||||
<p>Currently, consumption endpoints include websocket connections used to service clients, and peer connections used to create the peer to peer overlay network implementing the Ripple protocol.</p>
|
<p>Currently, consumption endpoints include websocket connections used to service clients, and peer connections used to create the peer to peer overlay network implementing the Ripple protocol.</p>
|
||||||
<p>The current "balance" of a Consumer represents resource consumption debt or credit. Debt is accrued when bad loads are imposed. Credit is granted when good loads are imposed. When the balance crosses heuristic thresholds, costs are increased on the endpoint. The balance is represented as a unitless relative quantity. This balance is currently held by the Entry struct in the <a class="el" href="Entry_8h_source.html">impl/Entry.h</a> file.</p>
|
<p>The current "balance" of a Consumer represents resource consumption debt or credit. Debt is accrued when bad loads are imposed. Credit is granted when good loads are imposed. When the balance crosses heuristic thresholds, costs are increased on the endpoint. The balance is represented as a unitless relative quantity. This balance is currently held by the Entry struct in the <a class="el" href="Entry_8h_source.html">impl/Entry.h</a> file.</p>
|
||||||
<p>Costs associated with specific transactions are defined in the impl/Fees files.</p>
|
<p>Costs associated with specific transactions are defined in the impl/Fees files.</p>
|
||||||
<p>Although RPC connections consume resources, they are transient and cannot be rate limited. It is advised not to expose RPC interfaces to the general public.</p>
|
<p>Although RPC connections consume resources, they are transient and cannot be rate limited. It is advised not to expose RPC interfaces to the general public.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md274"></a>
|
<h1><a class="anchor" id="autotoc_md276"></a>
|
||||||
Consumer Types</h1>
|
Consumer Types</h1>
|
||||||
<p>Consumers are placed into three classifications (as identified by the Resource::Kind enumeration):</p>
|
<p>Consumers are placed into three classifications (as identified by the Resource::Kind enumeration):</p>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -88,15 +88,15 @@ Consumer Types</h1>
|
|||||||
<li>Admin</li>
|
<li>Admin</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Each caller determines for itself the classification of the Consumer it is creating.</p>
|
<p>Each caller determines for itself the classification of the Consumer it is creating.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md275"></a>
|
<h1><a class="anchor" id="autotoc_md277"></a>
|
||||||
Resource Loading</h1>
|
Resource Loading</h1>
|
||||||
<p>It is expected that a client will impose a higher load on the server when it first connects: the client may need to catch up on transactions it has missed, or get trust lines, or transfer fees. The Manager must expect this initial peak load, but not allow that high load to continue because over the long term that would unduly stress the server.</p>
|
<p>It is expected that a client will impose a higher load on the server when it first connects: the client may need to catch up on transactions it has missed, or get trust lines, or transfer fees. The Manager must expect this initial peak load, but not allow that high load to continue because over the long term that would unduly stress the server.</p>
|
||||||
<p>If a client places a sustained high load on the server, that client is initially given a warning message. If that high load continues the Manager may tell the heavily loaded server to drop the connection entirely and not allow re-connection for some amount of time.</p>
|
<p>If a client places a sustained high load on the server, that client is initially given a warning message. If that high load continues the Manager may tell the heavily loaded server to drop the connection entirely and not allow re-connection for some amount of time.</p>
|
||||||
<p>Each load is monitored by capturing peaks and then decaying those peak values over time: this is implemented by the DecayingSample class.</p>
|
<p>Each load is monitored by capturing peaks and then decaying those peak values over time: this is implemented by the DecayingSample class.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md276"></a>
|
<h1><a class="anchor" id="autotoc_md278"></a>
|
||||||
Gossip</h1>
|
Gossip</h1>
|
||||||
<p>Each server in a cluster creates a list of IP addresses of end points that are imposing a significant load. This list is called Gossip, which is passed to other nodes in that cluster. Gossip helps individual servers in the cluster identify IP addreses that might be unduly loading the entire cluster. Again the recourse of the individual servers is to drop connections to those IP addresses that occur commonly in the gossip.</p>
|
<p>Each server in a cluster creates a list of IP addresses of end points that are imposing a significant load. This list is called Gossip, which is passed to other nodes in that cluster. Gossip helps individual servers in the cluster identify IP addreses that might be unduly loading the entire cluster. Again the recourse of the individual servers is to drop connections to those IP addresses that occur commonly in the gossip.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md277"></a>
|
<h1><a class="anchor" id="autotoc_md279"></a>
|
||||||
Access</h1>
|
Access</h1>
|
||||||
<p>In rippled, the Application holds a unique instance of Resource::Manager, which may be retrieved by calling the method <code>Application::getResourceManager()</code>. </p>
|
<p>In rippled, the Application holds a unique instance of Resource::Manager, which may be retrieved by calling the method <code>Application::getResourceManager()</code>. </p>
|
||||||
</div></div><!-- contents -->
|
</div></div><!-- contents -->
|
||||||
|
|||||||
@@ -65,13 +65,13 @@ $(function() {
|
|||||||
<div class="title">How to use RPC coroutines. </div> </div>
|
<div class="title">How to use RPC coroutines. </div> </div>
|
||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><h1><a class="anchor" id="autotoc_md279"></a>
|
<div class="textblock"><h1><a class="anchor" id="autotoc_md281"></a>
|
||||||
Introduction.</h1>
|
Introduction.</h1>
|
||||||
<p>By default, an RPC handler runs as an uninterrupted task on the JobQueue. This is fine for commands that are fast to compute but might not be acceptable for tasks that require multiple parts or are large, like a full ledger.</p>
|
<p>By default, an RPC handler runs as an uninterrupted task on the JobQueue. This is fine for commands that are fast to compute but might not be acceptable for tasks that require multiple parts or are large, like a full ledger.</p>
|
||||||
<p>For this purpose, the rippled RPC handler allows <em>suspension with continuation</em></p><ul>
|
<p>For this purpose, the rippled RPC handler allows <em>suspension with continuation</em></p><ul>
|
||||||
<li>a request to suspend execution of the RPC response and to continue it after some function or job has been executed. A default continuation is supplied which simply reschedules the job on the JobQueue, or the programmer can supply their own.</li>
|
<li>a request to suspend execution of the RPC response and to continue it after some function or job has been executed. A default continuation is supplied which simply reschedules the job on the JobQueue, or the programmer can supply their own.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h1><a class="anchor" id="autotoc_md280"></a>
|
<h1><a class="anchor" id="autotoc_md282"></a>
|
||||||
The classes.</h1>
|
The classes.</h1>
|
||||||
<p>Suspension with continuation uses four <code><a class="elRef" href="http://en.cppreference.com/w/cpp/utility/functional/function.html">std::function</a></code>s in the <code><a class="el" href="namespaceripple_1_1RPC.html">ripple::RPC</a></code> namespace: </p><pre class="fragment">using Callback = std::function <void ()>;
|
<p>Suspension with continuation uses four <code><a class="elRef" href="http://en.cppreference.com/w/cpp/utility/functional/function.html">std::function</a></code>s in the <code><a class="el" href="namespaceripple_1_1RPC.html">ripple::RPC</a></code> namespace: </p><pre class="fragment">using Callback = std::function <void ()>;
|
||||||
using Continuation = std::function <void (Callback const&)>;
|
using Continuation = std::function <void (Callback const&)>;
|
||||||
@@ -81,7 +81,7 @@ using Coroutine = std::function <void (Suspend const&)>;
|
|||||||
<p>A <code>Continuation</code> is a function that is given a <code>Callback</code> and promises to call it later. A <code>Continuation</code> guarantees to call the <code>Callback</code> exactly once at some point in the future, but it does not have to be immediately or even in the current thread.</p>
|
<p>A <code>Continuation</code> is a function that is given a <code>Callback</code> and promises to call it later. A <code>Continuation</code> guarantees to call the <code>Callback</code> exactly once at some point in the future, but it does not have to be immediately or even in the current thread.</p>
|
||||||
<p>A <code>Suspend</code> is a function belonging to a <code>Coroutine</code>. A <code>Suspend</code> runs a <code>Continuation</code>, passing it a <code>Callback</code> that continues execution of the <code>Coroutine</code>.</p>
|
<p>A <code>Suspend</code> is a function belonging to a <code>Coroutine</code>. A <code>Suspend</code> runs a <code>Continuation</code>, passing it a <code>Callback</code> that continues execution of the <code>Coroutine</code>.</p>
|
||||||
<p>And finally, a <code>Coroutine</code> is a <code><a class="elRef" href="http://en.cppreference.com/w/cpp/utility/functional/function.html">std::function</a></code> which is given a <code>Suspend</code>. This is what the RPC handler gives to the coroutine manager, expecting to get called back with a <code>Suspend</code> and to be able to start execution.</p>
|
<p>And finally, a <code>Coroutine</code> is a <code><a class="elRef" href="http://en.cppreference.com/w/cpp/utility/functional/function.html">std::function</a></code> which is given a <code>Suspend</code>. This is what the RPC handler gives to the coroutine manager, expecting to get called back with a <code>Suspend</code> and to be able to start execution.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md281"></a>
|
<h1><a class="anchor" id="autotoc_md283"></a>
|
||||||
The flow of control.</h1>
|
The flow of control.</h1>
|
||||||
<p>Given these functions, the flow of RPC control when using coroutines is straight-forward.</p>
|
<p>Given these functions, the flow of RPC control when using coroutines is straight-forward.</p>
|
||||||
<ol type="1">
|
<ol type="1">
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ $(function() {
|
|||||||
<p>Copies are made with the <code>snapShot</code> function as opposed to the <code>SHAMap</code> copy constructor. See the section on <code>SHAMap</code> creation for more details about <code>snapShot</code>.</p>
|
<p>Copies are made with the <code>snapShot</code> function as opposed to the <code>SHAMap</code> copy constructor. See the section on <code>SHAMap</code> creation for more details about <code>snapShot</code>.</p>
|
||||||
<p>Sequence numbers are used to further customize the node ownership strategy. See the section on sequence numbers for details on sequence numbers.</p>
|
<p>Sequence numbers are used to further customize the node ownership strategy. See the section on sequence numbers for details on sequence numbers.</p>
|
||||||
<p><img src="https://user-images.githubusercontent.com/46455409/77350005-1ef12c80-6cf9-11ea-9c8d-56410f442859.png" alt="node diagram" class="inline"/></p>
|
<p><img src="https://user-images.githubusercontent.com/46455409/77350005-1ef12c80-6cf9-11ea-9c8d-56410f442859.png" alt="node diagram" class="inline"/></p>
|
||||||
<h1><a class="anchor" id="autotoc_md283"></a>
|
<h1><a class="anchor" id="autotoc_md285"></a>
|
||||||
Mutability</h1>
|
Mutability</h1>
|
||||||
<p>There are two different ways of building and using a <code>SHAMap</code>:</p>
|
<p>There are two different ways of building and using a <code>SHAMap</code>:</p>
|
||||||
<ol type="1">
|
<ol type="1">
|
||||||
@@ -102,7 +102,7 @@ Mutability</h1>
|
|||||||
<p>Most <code>SHAMap</code>s are immutable, in the sense that they don't modify or remove their contained nodes.</p>
|
<p>Most <code>SHAMap</code>s are immutable, in the sense that they don't modify or remove their contained nodes.</p>
|
||||||
<p>An example where a mutable <code>SHAMap</code> is required is when we want to apply transactions to the last closed ledger. To do so we'd make a mutable snapshot of the state trie and then start applying transactions to it. Because the snapshot is mutable, changes to nodes in the snapshot will not affect nodes in other <code>SHAMap</code>s.</p>
|
<p>An example where a mutable <code>SHAMap</code> is required is when we want to apply transactions to the last closed ledger. To do so we'd make a mutable snapshot of the state trie and then start applying transactions to it. Because the snapshot is mutable, changes to nodes in the snapshot will not affect nodes in other <code>SHAMap</code>s.</p>
|
||||||
<p>An example using a immutable ledger would be when there's an open ledger and some piece of code wishes to query the state of the ledger. In this case we don't wish to change the state of the <code>SHAMap</code>, so we'd use an immutable snapshot.</p>
|
<p>An example using a immutable ledger would be when there's an open ledger and some piece of code wishes to query the state of the ledger. In this case we don't wish to change the state of the <code>SHAMap</code>, so we'd use an immutable snapshot.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md284"></a>
|
<h1><a class="anchor" id="autotoc_md286"></a>
|
||||||
Sequence numbers</h1>
|
Sequence numbers</h1>
|
||||||
<p>Both <code>SHAMap</code>s and their nodes carry a sequence number. This is simply an unsigned number that indicates ownership or membership, or a non-membership.</p>
|
<p>Both <code>SHAMap</code>s and their nodes carry a sequence number. This is simply an unsigned number that indicates ownership or membership, or a non-membership.</p>
|
||||||
<p><code>SHAMap</code>s sequence numbers normally start out as 1. However when a snap-shot of a <code>SHAMap</code> is made, the copy's sequence number is 1 greater than the original.</p>
|
<p><code>SHAMap</code>s sequence numbers normally start out as 1. However when a snap-shot of a <code>SHAMap</code> is made, the copy's sequence number is 1 greater than the original.</p>
|
||||||
@@ -110,19 +110,19 @@ Sequence numbers</h1>
|
|||||||
<p>When a <code>SHAMap</code> needs to have a private copy of a node, not shared by any other <code>SHAMap</code>, it first clones it and then sets the new copy to have a sequence number equal to the <code>SHAMap</code> sequence number. The <code>unshareNode</code> is a private utility which automates the task of first checking if the node is already sharable, and if so, cloning it and giving it the proper sequence number. An example case where a private copy is needed is when an inner node needs to have a child pointer altered. Any modification to a node will require a non-shared node.</p>
|
<p>When a <code>SHAMap</code> needs to have a private copy of a node, not shared by any other <code>SHAMap</code>, it first clones it and then sets the new copy to have a sequence number equal to the <code>SHAMap</code> sequence number. The <code>unshareNode</code> is a private utility which automates the task of first checking if the node is already sharable, and if so, cloning it and giving it the proper sequence number. An example case where a private copy is needed is when an inner node needs to have a child pointer altered. Any modification to a node will require a non-shared node.</p>
|
||||||
<p>When a <code>SHAMap</code> decides that it is safe to share a node of its own, it sets the node's sequence number to 0 (a <code>SHAMap</code> never has a sequence number of 0). This is done for every node in the trie when <code>SHAMap::walkSubTree</code> is executed.</p>
|
<p>When a <code>SHAMap</code> decides that it is safe to share a node of its own, it sets the node's sequence number to 0 (a <code>SHAMap</code> never has a sequence number of 0). This is done for every node in the trie when <code>SHAMap::walkSubTree</code> is executed.</p>
|
||||||
<p>Note that other objects in rippled also have sequence numbers (e.g. ledgers). The <code>SHAMap</code> and node sequence numbers should not be confused with these other sequence numbers (no relation).</p>
|
<p>Note that other objects in rippled also have sequence numbers (e.g. ledgers). The <code>SHAMap</code> and node sequence numbers should not be confused with these other sequence numbers (no relation).</p>
|
||||||
<h1><a class="anchor" id="autotoc_md285"></a>
|
<h1><a class="anchor" id="autotoc_md287"></a>
|
||||||
SHAMap Creation</h1>
|
SHAMap Creation</h1>
|
||||||
<p>A <code>SHAMap</code> is usually not created from vacuum. Once an initial <code>SHAMap</code> is constructed, later <code>SHAMap</code>s are usually created by calling snapShot(bool isMutable) on the original <code>SHAMap</code>. The returned <code>SHAMap</code> has the expected characteristics (mutable or immutable) based on the passed in flag.</p>
|
<p>A <code>SHAMap</code> is usually not created from vacuum. Once an initial <code>SHAMap</code> is constructed, later <code>SHAMap</code>s are usually created by calling snapShot(bool isMutable) on the original <code>SHAMap</code>. The returned <code>SHAMap</code> has the expected characteristics (mutable or immutable) based on the passed in flag.</p>
|
||||||
<p>It is cheaper to make an immutable snapshot of a <code>SHAMap</code> than to make a mutable snapshot. If the <code>SHAMap</code> snapshot is mutable then sharable nodes must be copied before they are placed in the mutable map.</p>
|
<p>It is cheaper to make an immutable snapshot of a <code>SHAMap</code> than to make a mutable snapshot. If the <code>SHAMap</code> snapshot is mutable then sharable nodes must be copied before they are placed in the mutable map.</p>
|
||||||
<p>A new <code>SHAMap</code> is created with each new ledger round. Transactions not executed in the previous ledger populate the <code>SHAMap</code> for the new ledger.</p>
|
<p>A new <code>SHAMap</code> is created with each new ledger round. Transactions not executed in the previous ledger populate the <code>SHAMap</code> for the new ledger.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md286"></a>
|
<h1><a class="anchor" id="autotoc_md288"></a>
|
||||||
Storing SHAMap data in the database</h1>
|
Storing SHAMap data in the database</h1>
|
||||||
<p>When consensus is reached, the ledger is closed. As part of this process, the <code>SHAMap</code> is stored to the database by calling <code>SHAMap::flushDirty</code>.</p>
|
<p>When consensus is reached, the ledger is closed. As part of this process, the <code>SHAMap</code> is stored to the database by calling <code>SHAMap::flushDirty</code>.</p>
|
||||||
<p>Both <code>unshare()</code> and <code>flushDirty</code> walk the <code>SHAMap</code> by calling <code>SHAMap::walkSubTree</code>. As <code>unshare()</code> walks the trie, nodes are not written to the database, and as <code>flushDirty</code> walks the trie nodes are written to the database. <code>walkSubTree</code> visits every node in the trie. This process must ensure that each node is only owned by this trie, and so "unshares" as it walks each node (from the root down). This is done in the <code>preFlushNode</code> function by ensuring that the node has a sequence number equal to that of the <code>SHAMap</code>. If the node doesn't, it is cloned.</p>
|
<p>Both <code>unshare()</code> and <code>flushDirty</code> walk the <code>SHAMap</code> by calling <code>SHAMap::walkSubTree</code>. As <code>unshare()</code> walks the trie, nodes are not written to the database, and as <code>flushDirty</code> walks the trie nodes are written to the database. <code>walkSubTree</code> visits every node in the trie. This process must ensure that each node is only owned by this trie, and so "unshares" as it walks each node (from the root down). This is done in the <code>preFlushNode</code> function by ensuring that the node has a sequence number equal to that of the <code>SHAMap</code>. If the node doesn't, it is cloned.</p>
|
||||||
<p>For each inner node encountered (starting with the root node), each of the children are inspected (from 1 to 16). For each child, if it has a non-zero sequence number (unshareable), the child is first copied. Then if the child is an inner node, we recurse down to that node's children. Otherwise we've found a leaf node and that node is written to the database. A count of each leaf node that is visited is kept. The hash of the data in the leaf node is computed at this time, and the child is reassigned back into the parent inner node just in case the COW operation created a new pointer to this leaf node.</p>
|
<p>For each inner node encountered (starting with the root node), each of the children are inspected (from 1 to 16). For each child, if it has a non-zero sequence number (unshareable), the child is first copied. Then if the child is an inner node, we recurse down to that node's children. Otherwise we've found a leaf node and that node is written to the database. A count of each leaf node that is visited is kept. The hash of the data in the leaf node is computed at this time, and the child is reassigned back into the parent inner node just in case the COW operation created a new pointer to this leaf node.</p>
|
||||||
<p>After processing each node, the node is then marked as sharable again by setting its sequence number to 0.</p>
|
<p>After processing each node, the node is then marked as sharable again by setting its sequence number to 0.</p>
|
||||||
<p>After all of an inner node's children are processed, then its hash is updated and the inner node is written to the database. Then this inner node is assigned back into it's parent node, again in case the COW operation created a new pointer to it.</p>
|
<p>After all of an inner node's children are processed, then its hash is updated and the inner node is written to the database. Then this inner node is assigned back into it's parent node, again in case the COW operation created a new pointer to it.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md287"></a>
|
<h1><a class="anchor" id="autotoc_md289"></a>
|
||||||
Walking a SHAMap</h1>
|
Walking a SHAMap</h1>
|
||||||
<p>The private function <code>SHAMap::walkTowardsKey</code> is a good example of <em>how</em> to walk a <code>SHAMap</code>, and the various functions that call <code>walkTowardsKey</code> are good examples of <em>why</em> one would want to walk a <code>SHAMap</code> (e.g. <code>SHAMap::findKey</code>). <code>walkTowardsKey</code> always starts at the root of the <code>SHAMap</code> and traverses down through the inner nodes, looking for a leaf node along a path in the trie designated by a <code>uint256</code>.</p>
|
<p>The private function <code>SHAMap::walkTowardsKey</code> is a good example of <em>how</em> to walk a <code>SHAMap</code>, and the various functions that call <code>walkTowardsKey</code> are good examples of <em>why</em> one would want to walk a <code>SHAMap</code> (e.g. <code>SHAMap::findKey</code>). <code>walkTowardsKey</code> always starts at the root of the <code>SHAMap</code> and traverses down through the inner nodes, looking for a leaf node along a path in the trie designated by a <code>uint256</code>.</p>
|
||||||
<p>As one walks the trie, one can <em>optionally</em> keep a stack of nodes that one has passed through. This isn't necessary for walking the trie, but many clients will use the stack after finding the desired node. For example if one is deleting a node from the trie, the stack is handy for repairing invariants in the trie after the deletion.</p>
|
<p>As one walks the trie, one can <em>optionally</em> keep a stack of nodes that one has passed through. This isn't necessary for walking the trie, but many clients will use the stack after finding the desired node. For example if one is deleting a node from the trie, the stack is handy for repairing invariants in the trie after the deletion.</p>
|
||||||
@@ -140,7 +140,7 @@ Walking a SHAMap</h1>
|
|||||||
<li>In the database.</li>
|
<li>In the database.</li>
|
||||||
</ol>
|
</ol>
|
||||||
<p>If the node is not found in the trie, then it is installed into the trie as part of the traversal process.</p>
|
<p>If the node is not found in the trie, then it is installed into the trie as part of the traversal process.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md288"></a>
|
<h1><a class="anchor" id="autotoc_md290"></a>
|
||||||
Late-arriving Nodes</h1>
|
Late-arriving Nodes</h1>
|
||||||
<p>As we noted earlier, <code>SHAMap</code>s (even immutable ones) may grow. If a <code>SHAMap</code> is searching for a node and runs into an empty spot in the trie, then the <code>SHAMap</code> looks to see if the node exists but has not yet been made part of the map. This operation is performed in the <code>SHAMap::fetchNodeNT()</code> method. The <em>NT</em> is this case stands for 'No Throw'.</p>
|
<p>As we noted earlier, <code>SHAMap</code>s (even immutable ones) may grow. If a <code>SHAMap</code> is searching for a node and runs into an empty spot in the trie, then the <code>SHAMap</code> looks to see if the node exists but has not yet been made part of the map. This operation is performed in the <code>SHAMap::fetchNodeNT()</code> method. The <em>NT</em> is this case stands for 'No Throw'.</p>
|
||||||
<p>The <code>fetchNodeNT()</code> method goes through three phases:</p>
|
<p>The <code>fetchNodeNT()</code> method goes through three phases:</p>
|
||||||
@@ -151,29 +151,29 @@ Late-arriving Nodes</h1>
|
|||||||
<li>If the node is not in the TreeNodeCache, we attempt to locate the node in the historic data stored by the data base. The call to to <code>fetchNodeFromDB(hash)</code> does that work for us.</li>
|
<li>If the node is not in the TreeNodeCache, we attempt to locate the node in the historic data stored by the data base. The call to to <code>fetchNodeFromDB(hash)</code> does that work for us.</li>
|
||||||
<li>Finally if a filter exists, we check if it can supply the node. This is typically the LedgerMaster which tracks the current ledger and ledgers in the process of closing.</li>
|
<li>Finally if a filter exists, we check if it can supply the node. This is typically the LedgerMaster which tracks the current ledger and ledgers in the process of closing.</li>
|
||||||
</ol>
|
</ol>
|
||||||
<h1><a class="anchor" id="autotoc_md289"></a>
|
<h1><a class="anchor" id="autotoc_md291"></a>
|
||||||
Canonicalize</h1>
|
Canonicalize</h1>
|
||||||
<p><code>canonicalize()</code> is called every time a node is introduced into the <code>SHAMap</code>.</p>
|
<p><code>canonicalize()</code> is called every time a node is introduced into the <code>SHAMap</code>.</p>
|
||||||
<p>A call to <code>canonicalize()</code> stores the node in the <code>TreeNodeCache</code> if it does not already exist in the <code>TreeNodeCache</code>.</p>
|
<p>A call to <code>canonicalize()</code> stores the node in the <code>TreeNodeCache</code> if it does not already exist in the <code>TreeNodeCache</code>.</p>
|
||||||
<p>The calls to <code>canonicalize()</code> make sure that if the resulting node is already in the <code>SHAMap</code>, node <code>TreeNodeCache</code> or database, then we don't create duplicates by favoring the copy already in the <code>TreeNodeCache</code>.</p>
|
<p>The calls to <code>canonicalize()</code> make sure that if the resulting node is already in the <code>SHAMap</code>, node <code>TreeNodeCache</code> or database, then we don't create duplicates by favoring the copy already in the <code>TreeNodeCache</code>.</p>
|
||||||
<p>By using <code>canonicalize()</code> we manage a thread race condition where two different threads might both recognize the lack of a SHAMapLeafNode at the same time (during a fetch). If they both attempt to insert the node into the <code>SHAMap</code>, then <code>canonicalize</code> makes sure that the first node in wins and the slower thread receives back a pointer to the node inserted by the faster thread. Recall that these two <code>SHAMap</code>s will share the same <code>TreeNodeCache</code>.</p>
|
<p>By using <code>canonicalize()</code> we manage a thread race condition where two different threads might both recognize the lack of a SHAMapLeafNode at the same time (during a fetch). If they both attempt to insert the node into the <code>SHAMap</code>, then <code>canonicalize</code> makes sure that the first node in wins and the slower thread receives back a pointer to the node inserted by the faster thread. Recall that these two <code>SHAMap</code>s will share the same <code>TreeNodeCache</code>.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md290"></a>
|
<h1><a class="anchor" id="autotoc_md292"></a>
|
||||||
<tt>TreeNodeCache</tt></h1>
|
<tt>TreeNodeCache</tt></h1>
|
||||||
<p>The <code>TreeNodeCache</code> is a <code><a class="elRef" href="http://en.cppreference.com/w/cpp/container/unordered_map.html" title="STL class.">std::unordered_map</a></code> keyed on the hash of the <code>SHAMap</code> node. The stored type consists of <code>shared_ptr<SHAMapTreeNode></code>, <code>weak_ptr<SHAMapTreeNode></code>, and a time point indicating the most recent access of this node in the cache. The time point is based on <code><a class="elRef" href="http://en.cppreference.com/w/cpp/chrono/steady_clock.html">std::chrono::steady_clock</a></code>.</p>
|
<p>The <code>TreeNodeCache</code> is a <code><a class="elRef" href="http://en.cppreference.com/w/cpp/container/unordered_map.html" title="STL class.">std::unordered_map</a></code> keyed on the hash of the <code>SHAMap</code> node. The stored type consists of <code>shared_ptr<SHAMapTreeNode></code>, <code>weak_ptr<SHAMapTreeNode></code>, and a time point indicating the most recent access of this node in the cache. The time point is based on <code><a class="elRef" href="http://en.cppreference.com/w/cpp/chrono/steady_clock.html">std::chrono::steady_clock</a></code>.</p>
|
||||||
<p>The container uses a cryptographically secure hash that is randomly seeded.</p>
|
<p>The container uses a cryptographically secure hash that is randomly seeded.</p>
|
||||||
<p>The <code>TreeNodeCache</code> also carries with it various data used for statistics and logging, and a target age for the contained nodes. When the target age for a node is exceeded, and there are no more references to the node, the node is removed from the <code>TreeNodeCache</code>.</p>
|
<p>The <code>TreeNodeCache</code> also carries with it various data used for statistics and logging, and a target age for the contained nodes. When the target age for a node is exceeded, and there are no more references to the node, the node is removed from the <code>TreeNodeCache</code>.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md291"></a>
|
<h1><a class="anchor" id="autotoc_md293"></a>
|
||||||
<tt>FullBelowCache</tt></h1>
|
<tt>FullBelowCache</tt></h1>
|
||||||
<p>This cache remembers which trie keys have all of their children resident in a <code>SHAMap</code>. This optimizes the process of acquiring a complete trie. This is used when creating the missing nodes list. Missing nodes are those nodes that a <code>SHAMap</code> refers to but that are not stored in the local database.</p>
|
<p>This cache remembers which trie keys have all of their children resident in a <code>SHAMap</code>. This optimizes the process of acquiring a complete trie. This is used when creating the missing nodes list. Missing nodes are those nodes that a <code>SHAMap</code> refers to but that are not stored in the local database.</p>
|
||||||
<p>As a depth-first walk of a <code>SHAMap</code> is performed, if an inner node answers true to <code>isFullBelow()</code> then it is known that none of this node's children are missing nodes, and thus that subtree does not need to be walked. These nodes are stored in the FullBelowCache. Subsequent walks check the FullBelowCache first when encountering a node, and ignore that subtree if found.</p>
|
<p>As a depth-first walk of a <code>SHAMap</code> is performed, if an inner node answers true to <code>isFullBelow()</code> then it is known that none of this node's children are missing nodes, and thus that subtree does not need to be walked. These nodes are stored in the FullBelowCache. Subsequent walks check the FullBelowCache first when encountering a node, and ignore that subtree if found.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md292"></a>
|
<h1><a class="anchor" id="autotoc_md294"></a>
|
||||||
<tt>SHAMapTreeNode</tt></h1>
|
<tt>SHAMapTreeNode</tt></h1>
|
||||||
<p>This is an abstract base class for the concrete node types. It holds the following common data:</p>
|
<p>This is an abstract base class for the concrete node types. It holds the following common data:</p>
|
||||||
<ol type="1">
|
<ol type="1">
|
||||||
<li>A hash</li>
|
<li>A hash</li>
|
||||||
<li>An identifier used to perform copy-on-write operations</li>
|
<li>An identifier used to perform copy-on-write operations</li>
|
||||||
</ol>
|
</ol>
|
||||||
<h2><a class="anchor" id="autotoc_md293"></a>
|
<h2><a class="anchor" id="autotoc_md295"></a>
|
||||||
<tt>SHAMapInnerNode</tt></h2>
|
<tt>SHAMapInnerNode</tt></h2>
|
||||||
<p><code>SHAMapInnerNode</code> publicly inherits directly from <code>SHAMapTreeNode</code>. It holds the following data:</p>
|
<p><code>SHAMapInnerNode</code> publicly inherits directly from <code>SHAMapTreeNode</code>. It holds the following data:</p>
|
||||||
<ol type="1">
|
<ol type="1">
|
||||||
@@ -182,22 +182,22 @@ Canonicalize</h1>
|
|||||||
<li>A bitset to indicate which of the 16 children exist.</li>
|
<li>A bitset to indicate which of the 16 children exist.</li>
|
||||||
<li>An identifier used to determine whether the map below this node is fully populated</li>
|
<li>An identifier used to determine whether the map below this node is fully populated</li>
|
||||||
</ol>
|
</ol>
|
||||||
<h2><a class="anchor" id="autotoc_md294"></a>
|
<h2><a class="anchor" id="autotoc_md296"></a>
|
||||||
<tt>SHAMapLeafNode</tt></h2>
|
<tt>SHAMapLeafNode</tt></h2>
|
||||||
<p><code>SHAMapLeafNode</code> is an abstract class which publicly inherits directly from <code>SHAMapTreeNode</code>. It isIt holds the following data:</p>
|
<p><code>SHAMapLeafNode</code> is an abstract class which publicly inherits directly from <code>SHAMapTreeNode</code>. It isIt holds the following data:</p>
|
||||||
<ol type="1">
|
<ol type="1">
|
||||||
<li>A shared_ptr to a const SHAMapItem.</li>
|
<li>A shared_ptr to a const SHAMapItem.</li>
|
||||||
</ol>
|
</ol>
|
||||||
<h3><a class="anchor" id="autotoc_md295"></a>
|
<h3><a class="anchor" id="autotoc_md297"></a>
|
||||||
<tt>SHAMapAccountStateLeafNode</tt></h3>
|
<tt>SHAMapAccountStateLeafNode</tt></h3>
|
||||||
<p><code>SHAMapAccountStateLeafNode</code> is a class which publicly inherits directly from <code>SHAMapLeafNode</code>. It is used to represent entries (i.e. account objects, escrow objects, trust lines, etc.) in a state map.</p>
|
<p><code>SHAMapAccountStateLeafNode</code> is a class which publicly inherits directly from <code>SHAMapLeafNode</code>. It is used to represent entries (i.e. account objects, escrow objects, trust lines, etc.) in a state map.</p>
|
||||||
<h3><a class="anchor" id="autotoc_md296"></a>
|
<h3><a class="anchor" id="autotoc_md298"></a>
|
||||||
<tt>SHAMapTxLeafNode</tt></h3>
|
<tt>SHAMapTxLeafNode</tt></h3>
|
||||||
<p><code>SHAMapTxLeafNode</code> is a class which publicly inherits directly from <code>SHAMapLeafNode</code>. It is used to represent transactions in a state map.</p>
|
<p><code>SHAMapTxLeafNode</code> is a class which publicly inherits directly from <code>SHAMapLeafNode</code>. It is used to represent transactions in a state map.</p>
|
||||||
<h3><a class="anchor" id="autotoc_md297"></a>
|
<h3><a class="anchor" id="autotoc_md299"></a>
|
||||||
<tt>SHAMapTxPlusMetaLeafNode</tt></h3>
|
<tt>SHAMapTxPlusMetaLeafNode</tt></h3>
|
||||||
<p><code>SHAMapTxPlusMetaLeafNode</code> is a class which publicly inherits directly from <code>SHAMapLeafNode</code>. It is used to represent transactions along with metadata associated with this transaction in a state map.</p>
|
<p><code>SHAMapTxPlusMetaLeafNode</code> is a class which publicly inherits directly from <code>SHAMapLeafNode</code>. It is used to represent transactions along with metadata associated with this transaction in a state map.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md298"></a>
|
<h1><a class="anchor" id="autotoc_md300"></a>
|
||||||
SHAMapItem</h1>
|
SHAMapItem</h1>
|
||||||
<p>This holds the following data:</p>
|
<p>This holds the following data:</p>
|
||||||
<ol type="1">
|
<ol type="1">
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ $(function() {
|
|||||||
<div class="title">Unit Tests </div> </div>
|
<div class="title">Unit Tests </div> </div>
|
||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><h1><a class="anchor" id="autotoc_md308"></a>
|
<div class="textblock"><h1><a class="anchor" id="autotoc_md310"></a>
|
||||||
Running Tests</h1>
|
Running Tests</h1>
|
||||||
<p>Unit tests are bundled in the <code>rippled</code> executable and can be executed using the <code>--unittest</code> parameter. Without any arguments to this option, all non-manual unit tests will be executed. If you want to run one or more manual tests, you must specify it by suite or full-name (e.g. <code>ripple.app.NoRippleCheckLimits</code> or just <code>NoRippleCheckLimits</code>).</p>
|
<p>Unit tests are bundled in the <code>rippled</code> executable and can be executed using the <code>--unittest</code> parameter. Without any arguments to this option, all non-manual unit tests will be executed. If you want to run one or more manual tests, you must specify it by suite or full-name (e.g. <code>ripple.app.NoRippleCheckLimits</code> or just <code>NoRippleCheckLimits</code>).</p>
|
||||||
<p>More than one suite or group of suites can be specified as a comma separated list via the argument. For example, <code>--unittest=beast,OversizeMeta</code> will run all suites in the <code>beast</code> library (root identifier) as well as the test suite named <code>OversizeMeta</code>). All name matches are case sensitive.</p>
|
<p>More than one suite or group of suites can be specified as a comma separated list via the argument. For example, <code>--unittest=beast,OversizeMeta</code> will run all suites in the <code>beast</code> library (root identifier) as well as the test suite named <code>OversizeMeta</code>). All name matches are case sensitive.</p>
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ $(function() {
|
|||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><p>The Consensus Simulation Framework is a set of software components for describing, running and analyzing simulations of the consensus algorithm in a controlled manner. It is also used to unit test the generic Ripple consensus algorithm implementation. The framework is in its early stages, so the design and supported features are subject to change.</p>
|
<div class="textblock"><p>The Consensus Simulation Framework is a set of software components for describing, running and analyzing simulations of the consensus algorithm in a controlled manner. It is also used to unit test the generic Ripple consensus algorithm implementation. The framework is in its early stages, so the design and supported features are subject to change.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md300"></a>
|
<h1><a class="anchor" id="autotoc_md302"></a>
|
||||||
Overview</h1>
|
Overview</h1>
|
||||||
<p>The simulation framework focuses on simulating the core consensus and validation algorithms as a <a href="https://en.wikipedia.org/wiki/Discrete_event_simulation">discrete event simulation</a>. It is completely abstracted from the details of the XRP ledger and transactions. In the simulation, a ledger is simply a set of observed integers and transactions are single integers. The consensus process works to agree on the set of integers to include in the next ledger.</p>
|
<p>The simulation framework focuses on simulating the core consensus and validation algorithms as a <a href="https://en.wikipedia.org/wiki/Discrete_event_simulation">discrete event simulation</a>. It is completely abstracted from the details of the XRP ledger and transactions. In the simulation, a ledger is simply a set of observed integers and transactions are single integers. The consensus process works to agree on the set of integers to include in the next ledger.</p>
|
||||||
<div class="image">
|
<div class="image">
|
||||||
@@ -82,7 +82,7 @@ CSF Overview</div></div>
|
|||||||
<li><code>Collector</code>s that aggregate, filter and analyze data from the simulation. Typically, this is used to monitor invariants or generate reports.</li>
|
<li><code>Collector</code>s that aggregate, filter and analyze data from the simulation. Typically, this is used to monitor invariants or generate reports.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Once specified, the simulation runs using a single <code>Scheduler</code> that manages the global clock and sequencing of activity. During the course of simulation, <code>Peer</code>s generate <code>Ledger</code>s and <code>Validation</code>s as a result of consensus, eventually fully validating the consensus history of accepted transactions. Each <code>Peer</code> also issues various <code>Event</code>s during the simulation, which are analyzed by the registered <code>Collector</code>s.</p>
|
<p>Once specified, the simulation runs using a single <code>Scheduler</code> that manages the global clock and sequencing of activity. During the course of simulation, <code>Peer</code>s generate <code>Ledger</code>s and <code>Validation</code>s as a result of consensus, eventually fully validating the consensus history of accepted transactions. Each <code>Peer</code> also issues various <code>Event</code>s during the simulation, which are analyzed by the registered <code>Collector</code>s.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md301"></a>
|
<h1><a class="anchor" id="autotoc_md303"></a>
|
||||||
Example Simulation</h1>
|
Example Simulation</h1>
|
||||||
<p>Below is a basic simulation we can walk through to get an understanding of the framework. This simulation is for a set of 5 validators that aren't directly connected but rely on a single hub node for communication.</p>
|
<p>Below is a basic simulation we can walk through to get an understanding of the framework. This simulation is for a set of 5 validators that aren't directly connected but rely on a single hub node for communication.</p>
|
||||||
<div class="image">
|
<div class="image">
|
||||||
@@ -118,7 +118,7 @@ Example Sim</div></div>
|
|||||||
<div class="line"> </div>
|
<div class="line"> </div>
|
||||||
<div class="line">std::cout << (simDur.stop - simDur.start).count() << std::endl;</div>
|
<div class="line">std::cout << (simDur.stop - simDur.start).count() << std::endl;</div>
|
||||||
<div class="line">assert(sim.synchronized());</div>
|
<div class="line">assert(sim.synchronized());</div>
|
||||||
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md302"></a>
|
</div><!-- fragment --><h2><a class="anchor" id="autotoc_md304"></a>
|
||||||
<tt>Sim</tt> and <tt>PeerGroup</tt></h2>
|
<tt>Sim</tt> and <tt>PeerGroup</tt></h2>
|
||||||
<div class="fragment"><div class="line"> {c++}</div>
|
<div class="fragment"><div class="line"> {c++}</div>
|
||||||
<div class="line">Sim sim;</div>
|
<div class="line">Sim sim;</div>
|
||||||
@@ -128,7 +128,7 @@ Example Sim</div></div>
|
|||||||
<div class="line">center[0]->runAsValidator = false;</div>
|
<div class="line">center[0]->runAsValidator = false;</div>
|
||||||
</div><!-- fragment --><p>The simulation code starts by creating a single instance of the <a href="./Sim.h"><code>Sim</code> class</a>. This class is used to manage the overall simulation and internally owns most other components, including the <code>Peer</code>s, <code>Scheduler</code>, <code>BasicNetwork</code> and <code>TrustGraph</code>. The next two lines create two differ <code>PeerGroup</code>s of size 5 and 1 . A <a href="./PeerGroup.h"><code>PeerGroup</code></a> is a convenient way for configuring a set of related peers together and internally has a vector of pointers to the <code>Peer</code>s which are owned by the <code>Sim</code>. <code>PeerGroup</code>s can be combined using <code>+/-</code> operators to configure more complex relationships of nodes as shown by <code>PeerGroup network</code>. Note that each call to <code>createGroup</code> adds that many new <code>Peer</code>s to the simulation, but does not specify any trust or network relationships for the new <code>Peer</code>s.</p>
|
</div><!-- fragment --><p>The simulation code starts by creating a single instance of the <a href="./Sim.h"><code>Sim</code> class</a>. This class is used to manage the overall simulation and internally owns most other components, including the <code>Peer</code>s, <code>Scheduler</code>, <code>BasicNetwork</code> and <code>TrustGraph</code>. The next two lines create two differ <code>PeerGroup</code>s of size 5 and 1 . A <a href="./PeerGroup.h"><code>PeerGroup</code></a> is a convenient way for configuring a set of related peers together and internally has a vector of pointers to the <code>Peer</code>s which are owned by the <code>Sim</code>. <code>PeerGroup</code>s can be combined using <code>+/-</code> operators to configure more complex relationships of nodes as shown by <code>PeerGroup network</code>. Note that each call to <code>createGroup</code> adds that many new <code>Peer</code>s to the simulation, but does not specify any trust or network relationships for the new <code>Peer</code>s.</p>
|
||||||
<p>Lastly, the single <code>Peer</code> in the size 1 <code>center</code> group is switched from running as a validator (the default) to running as a tracking peer. The <a href="./Peer.h"><code>Peer</code> class</a> has a variety of configurable parameters that control how it behaves during the simulation.</p>
|
<p>Lastly, the single <code>Peer</code> in the size 1 <code>center</code> group is switched from running as a validator (the default) to running as a tracking peer. The <a href="./Peer.h"><code>Peer</code> class</a> has a variety of configurable parameters that control how it behaves during the simulation.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md303"></a>
|
<h1><a class="anchor" id="autotoc_md305"></a>
|
||||||
<tt>trust</tt> and <tt>connect</tt></h1>
|
<tt>trust</tt> and <tt>connect</tt></h1>
|
||||||
<div class="fragment"><div class="line"> {c++}</div>
|
<div class="fragment"><div class="line"> {c++}</div>
|
||||||
<div class="line">validators.trust(validators);</div>
|
<div class="line">validators.trust(validators);</div>
|
||||||
@@ -139,7 +139,7 @@ Example Sim</div></div>
|
|||||||
<div class="line">validators.connect(center, delay);</div>
|
<div class="line">validators.connect(center, delay);</div>
|
||||||
</div><!-- fragment --><p>Although the <code>sim</code> object has accessible instances of <a href="./TrustGraph.h">TrustGraph</a> and <a href="./BasicNetwork.h">BasicNetwork</a>, it is more convenient to manage the graphs via the <code>PeerGroup</code>s. The first two lines create a trust topology in which all <code>Peer</code>s trust the 5 validating <code>Peer</code>s. Or in the UNL perspective, all <code>Peer</code>s are configured with the same UNL listing the 5 validating <code>Peer</code>s. The two lines could've been rewritten as <code>network.trust(validators)</code>.</p>
|
</div><!-- fragment --><p>Although the <code>sim</code> object has accessible instances of <a href="./TrustGraph.h">TrustGraph</a> and <a href="./BasicNetwork.h">BasicNetwork</a>, it is more convenient to manage the graphs via the <code>PeerGroup</code>s. The first two lines create a trust topology in which all <code>Peer</code>s trust the 5 validating <code>Peer</code>s. Or in the UNL perspective, all <code>Peer</code>s are configured with the same UNL listing the 5 validating <code>Peer</code>s. The two lines could've been rewritten as <code>network.trust(validators)</code>.</p>
|
||||||
<p>The next lines create the network communication topology. Each of the validating <code>Peer</code>s connects to the central hub <code>Peer</code> with a fixed delay of 200ms. Note that the network connections are really undirected, but are represented internally in a directed graph using edge pairs of inbound and outbound connections.</p>
|
<p>The next lines create the network communication topology. Each of the validating <code>Peer</code>s connects to the central hub <code>Peer</code> with a fixed delay of 200ms. Note that the network connections are really undirected, but are represented internally in a directed graph using edge pairs of inbound and outbound connections.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md304"></a>
|
<h1><a class="anchor" id="autotoc_md306"></a>
|
||||||
Collectors</h1>
|
Collectors</h1>
|
||||||
<div class="fragment"><div class="line"> {c++}</div>
|
<div class="fragment"><div class="line"> {c++}</div>
|
||||||
<div class="line">SimDurationCollector simDur;</div>
|
<div class="line">SimDurationCollector simDur;</div>
|
||||||
@@ -148,14 +148,14 @@ Collectors</h1>
|
|||||||
<p>Note that the collector lifetime is independent of the simulation and is added to the simulation by reference. This is intentional, since collectors might be used across several simulations to collect more complex combinations of data. At the end of the simulation, we print out the total duration by subtracting <code>simDur</code> members.</p>
|
<p>Note that the collector lifetime is independent of the simulation and is added to the simulation by reference. This is intentional, since collectors might be used across several simulations to collect more complex combinations of data. At the end of the simulation, we print out the total duration by subtracting <code>simDur</code> members.</p>
|
||||||
<div class="fragment"><div class="line"> {c++}</div>
|
<div class="fragment"><div class="line"> {c++}</div>
|
||||||
<div class="line">std::cout << (simDur.stop - simDur.start).count() << std::endl;</div>
|
<div class="line">std::cout << (simDur.stop - simDur.start).count() << std::endl;</div>
|
||||||
</div><!-- fragment --><h1><a class="anchor" id="autotoc_md305"></a>
|
</div><!-- fragment --><h1><a class="anchor" id="autotoc_md307"></a>
|
||||||
Transaction submission</h1>
|
Transaction submission</h1>
|
||||||
<div class="fragment"><div class="line"> {c++}</div>
|
<div class="fragment"><div class="line"> {c++}</div>
|
||||||
<div class="line">// everyone submits their own ID as a TX and relay it to peers</div>
|
<div class="line">// everyone submits their own ID as a TX and relay it to peers</div>
|
||||||
<div class="line">for (Peer * p : validators)</div>
|
<div class="line">for (Peer * p : validators)</div>
|
||||||
<div class="line"> p->submit(Tx(static_cast<std::uint32_t>(p->id)));</div>
|
<div class="line"> p->submit(Tx(static_cast<std::uint32_t>(p->id)));</div>
|
||||||
</div><!-- fragment --><p>In this basic example, we explicitly submit a single transaction to each validator. For larger simulations, clients can use a <a href="./submitters.h">Submitter</a> to send transactions in at fixed or random intervals to fixed or random <code>Peer</code>s.</p>
|
</div><!-- fragment --><p>In this basic example, we explicitly submit a single transaction to each validator. For larger simulations, clients can use a <a href="./submitters.h">Submitter</a> to send transactions in at fixed or random intervals to fixed or random <code>Peer</code>s.</p>
|
||||||
<h1><a class="anchor" id="autotoc_md306"></a>
|
<h1><a class="anchor" id="autotoc_md308"></a>
|
||||||
Run</h1>
|
Run</h1>
|
||||||
<p>The example has two calls to <code>sim.run(1)</code>. This call runs the simulation until each <code>Peer</code> has closed one additional ledger. After closing the additional ledger, the <code>Peer</code> stops participating in consensus. The first call is used to ensure a more useful prior state of all <code>Peer</code>s. After the transaction submission, the second call to <code>run</code> results in one additional ledger that accepts those transactions.</p>
|
<p>The example has two calls to <code>sim.run(1)</code>. This call runs the simulation until each <code>Peer</code> has closed one additional ledger. After closing the additional ledger, the <code>Peer</code> stops participating in consensus. The first call is used to ensure a more useful prior state of all <code>Peer</code>s. After the transaction submission, the second call to <code>run</code> results in one additional ledger that accepts those transactions.</p>
|
||||||
<p>Alternatively, you can specify a duration to run the simulation, e.g. <code>sim.run(10s)</code> which would have <code>Peer</code>s continuously run consensus until the scheduler has elapsed 10 additional seconds. The <code>sim.scheduler.in</code> or <code>sim.scheduler.at</code> methods can schedule arbitrary code to execute at a later time in the simulation, for example removing a network connection or modifying the trust graph. </p>
|
<p>Alternatively, you can specify a duration to run the simulation, e.g. <code>sim.run(10s)</code> which would have <code>Peer</code>s continuously run consensus until the scheduler has elapsed 10 additional seconds. The <code>sim.scheduler.in</code> or <code>sim.scheduler.at</code> methods can schedule arbitrary code to execute at a later time in the simulation, for example removing a network connection or modifying the trust graph. </p>
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ $(function() {
|
|||||||
<tr id="row_2_" class="even"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="md____w_rippled_rippled_docs_build_conan.html" target="_self">A crash course in CMake and Conan</a></td><td class="desc"></td></tr>
|
<tr id="row_2_" class="even"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="md____w_rippled_rippled_docs_build_conan.html" target="_self">A crash course in CMake and Conan</a></td><td class="desc"></td></tr>
|
||||||
<tr id="row_3_"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="md____w_rippled_rippled_docs_build_depend.html" target="_self">depend</a></td><td class="desc"></td></tr>
|
<tr id="row_3_"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="md____w_rippled_rippled_docs_build_depend.html" target="_self">depend</a></td><td class="desc"></td></tr>
|
||||||
<tr id="row_4_" class="even"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="md____w_rippled_rippled_docs_build_environment.html" target="_self">environment</a></td><td class="desc"></td></tr>
|
<tr id="row_4_" class="even"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="md____w_rippled_rippled_docs_build_environment.html" target="_self">environment</a></td><td class="desc"></td></tr>
|
||||||
<tr id="row_5_"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="md____w_rippled_rippled_docs_build_install.html" target="_self">From source</a></td><td class="desc"></td></tr>
|
<tr id="row_5_"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="md____w_rippled_rippled_docs_build_install.html" target="_self">install</a></td><td class="desc"></td></tr>
|
||||||
<tr id="row_6_" class="even"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="md____w_rippled_rippled_docs_CheatSheet.html" target="_self">Code Style Cheat Sheet</a></td><td class="desc"></td></tr>
|
<tr id="row_6_" class="even"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="md____w_rippled_rippled_docs_CheatSheet.html" target="_self">Code Style Cheat Sheet</a></td><td class="desc"></td></tr>
|
||||||
<tr id="row_7_"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="md____w_rippled_rippled_docs_CodingStyle.html" target="_self">Coding Standards</a></td><td class="desc"></td></tr>
|
<tr id="row_7_"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="md____w_rippled_rippled_docs_CodingStyle.html" target="_self">Coding Standards</a></td><td class="desc"></td></tr>
|
||||||
<tr id="row_8_" class="even"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="md____w_rippled_rippled_docs_consensus.html" target="_self">Consensus and Validation</a></td><td class="desc"></td></tr>
|
<tr id="row_8_" class="even"><td class="entry"><span style="width:16px;display:inline-block;"> </span><a class="el" href="md____w_rippled_rippled_docs_consensus.html" target="_self">Consensus and Validation</a></td><td class="desc"></td></tr>
|
||||||
|
|||||||
@@ -488,7 +488,6 @@ var searchData=
|
|||||||
['fwprintf_3140',['fwprintf',['http://en.cppreference.com/w/cpp/io/c/fwprintf.html',1,'std']]],
|
['fwprintf_3140',['fwprintf',['http://en.cppreference.com/w/cpp/io/c/fwprintf.html',1,'std']]],
|
||||||
['fwrite_3141',['fwrite',['http://en.cppreference.com/w/cpp/io/c/fwrite.html',1,'std']]],
|
['fwrite_3141',['fwrite',['http://en.cppreference.com/w/cpp/io/c/fwrite.html',1,'std']]],
|
||||||
['fwscanf_3142',['fwscanf',['http://en.cppreference.com/w/cpp/io/c/fwscanf.html',1,'std']]],
|
['fwscanf_3142',['fwscanf',['http://en.cppreference.com/w/cpp/io/c/fwscanf.html',1,'std']]],
|
||||||
['from_20source_3143',['From source',['../md____w_rippled_rippled_docs_build_install.html',1,'']]],
|
['fees_3143',['Fees',['../md_ripple_app_misc_FeeEscalation.html',1,'']]],
|
||||||
['fees_3144',['Fees',['../md_ripple_app_misc_FeeEscalation.html',1,'']]],
|
['fee_20voting_3144',['Fee Voting',['../md_ripple_app_misc_README.html',1,'']]]
|
||||||
['fee_20voting_3145',['Fee Voting',['../md_ripple_app_misc_README.html',1,'']]]
|
|
||||||
];
|
];
|
||||||
|
|||||||
1302
search/all_7.js
1302
search/all_7.js
File diff suppressed because one or more lines are too long
394
search/all_8.js
394
search/all_8.js
File diff suppressed because one or more lines are too long
1587
search/all_9.js
1587
search/all_9.js
File diff suppressed because one or more lines are too long
30
search/pages_10.html
Normal file
30
search/pages_10.html
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html><head><title></title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
||||||
|
<meta name="generator" content="Doxygen 1.8.17"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="search.css"/>
|
||||||
|
<script type="text/javascript" src="pages_10.js"></script>
|
||||||
|
<script type="text/javascript" src="search.js"></script>
|
||||||
|
</head>
|
||||||
|
<body class="SRPage">
|
||||||
|
<div id="SRIndex">
|
||||||
|
<div class="SRStatus" id="Loading">Loading...</div>
|
||||||
|
<div id="SRResults"></div>
|
||||||
|
<script type="text/javascript"><!--
|
||||||
|
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
|
||||||
|
createResults();
|
||||||
|
/* @license-end */
|
||||||
|
--></script>
|
||||||
|
<div class="SRStatus" id="Searching">Searching...</div>
|
||||||
|
<div class="SRStatus" id="NoMatches">No Matches</div>
|
||||||
|
<script type="text/javascript"><!--
|
||||||
|
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&dn=gpl-2.0.txt GPL-v2 */
|
||||||
|
document.getElementById("Loading").style.display="none";
|
||||||
|
document.getElementById("NoMatches").style.display="none";
|
||||||
|
var searchResults = new SearchResults("searchResults");
|
||||||
|
searchResults.Search();
|
||||||
|
/* @license-end */
|
||||||
|
--></script>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
4
search/pages_10.js
Normal file
4
search/pages_10.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
var searchData=
|
||||||
|
[
|
||||||
|
['unit_20tests_27364',['Unit Tests',['../md_test_README.html',1,'']]]
|
||||||
|
];
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
var searchData=
|
var searchData=
|
||||||
[
|
[
|
||||||
['from_20source_27335',['From source',['../md____w_rippled_rippled_docs_build_install.html',1,'']]],
|
['fees_27335',['Fees',['../md_ripple_app_misc_FeeEscalation.html',1,'']]],
|
||||||
['fees_27336',['Fees',['../md_ripple_app_misc_FeeEscalation.html',1,'']]],
|
['fee_20voting_27336',['Fee Voting',['../md_ripple_app_misc_README.html',1,'']]]
|
||||||
['fee_20voting_27337',['Fee Voting',['../md_ripple_app_misc_README.html',1,'']]]
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
var searchData=
|
var searchData=
|
||||||
[
|
[
|
||||||
['how_20to_20add_20new_20features_27338',['How to add new features',['../Feature.html',1,'']]],
|
['how_20to_20add_20new_20features_27337',['How to add new features',['../Feature.html',1,'']]],
|
||||||
['heap_20profiling_20of_20rippled_20with_20jemalloc_27339',['Heap profiling of rippled with jemalloc',['../md____w_rippled_rippled_docs_HeapProfiling.html',1,'']]],
|
['heap_20profiling_20of_20rippled_20with_20jemalloc_27338',['Heap profiling of rippled with jemalloc',['../md____w_rippled_rippled_docs_HeapProfiling.html',1,'']]],
|
||||||
['how_20to_20use_20rpc_20coroutines_2e_27340',['How to use RPC coroutines.',['../md_ripple_rpc_README.html',1,'']]]
|
['how_20to_20use_20rpc_20coroutines_2e_27339',['How to use RPC coroutines.',['../md_ripple_rpc_README.html',1,'']]]
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
var searchData=
|
var searchData=
|
||||||
[
|
[
|
||||||
['json_27341',['JSON',['../md_ripple_json_README.html',1,'']]],
|
['install_27340',['install',['../md____w_rippled_rippled_docs_build_install.html',1,'']]]
|
||||||
['json_20todo_27342',['JSON TODO',['../md_ripple_json_TODO.html',1,'']]]
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
var searchData=
|
var searchData=
|
||||||
[
|
[
|
||||||
['ledger_20replay_27343',['Ledger Replay',['../md____w_rippled_rippled_docs_0010-ledger-replay_README.html',1,'']]],
|
['json_27341',['JSON',['../md_ripple_json_README.html',1,'']]],
|
||||||
['ledger_20process_27344',['Ledger Process',['../md_ripple_app_ledger_README.html',1,'']]]
|
['json_20todo_27342',['JSON TODO',['../md_ripple_json_TODO.html',1,'']]]
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
var searchData=
|
var searchData=
|
||||||
[
|
[
|
||||||
['negative_20unl_20engineering_20spec_27345',['Negative UNL Engineering Spec',['../md____w_rippled_rippled_docs_0001-negative-unl_README.html',1,'']]]
|
['ledger_20replay_27343',['Ledger Replay',['../md____w_rippled_rippled_docs_0010-ledger-replay_README.html',1,'']]],
|
||||||
|
['ledger_20process_27344',['Ledger Process',['../md_ripple_app_ledger_README.html',1,'']]]
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
var searchData=
|
var searchData=
|
||||||
[
|
[
|
||||||
['open_20shard_20management_27346',['Open Shard Management',['../md_ripple_nodestore_ShardPool.html',1,'']]],
|
['negative_20unl_20engineering_20spec_27345',['Negative UNL Engineering Spec',['../md____w_rippled_rippled_docs_0001-negative-unl_README.html',1,'']]]
|
||||||
['overlay_27347',['Overlay',['../md_ripple_overlay_README.html',1,'']]]
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
var searchData=
|
var searchData=
|
||||||
[
|
[
|
||||||
['peerfinder_27348',['PeerFinder',['../md_ripple_peerfinder_README.html',1,'']]],
|
['open_20shard_20management_27346',['Open Shard Management',['../md_ripple_nodestore_ShardPool.html',1,'']]],
|
||||||
['proto_27349',['Proto',['../md_ripple_proto_README.html',1,'']]],
|
['overlay_27347',['Overlay',['../md_ripple_overlay_README.html',1,'']]]
|
||||||
['protocol_27350',['protocol',['../md_ripple_protocol_README.html',1,'']]]
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
var searchData=
|
var searchData=
|
||||||
[
|
[
|
||||||
['rippled_20source_27351',['rippled Source',['../index.html',1,'']]],
|
['peerfinder_27348',['PeerFinder',['../md_ripple_peerfinder_README.html',1,'']]],
|
||||||
['release_20notes_27352',['Release Notes',['../md____w_rippled_rippled_RELEASENOTES.html',1,'']]],
|
['proto_27349',['Proto',['../md_ripple_proto_README.html',1,'']]],
|
||||||
['rcl_20consensus_27353',['RCL Consensus',['../md_ripple_app_consensus_README.html',1,'']]],
|
['protocol_27350',['protocol',['../md_ripple_protocol_README.html',1,'']]]
|
||||||
['relational_20database_20interface_27354',['Relational Database Interface',['../md_ripple_app_rdb_README.html',1,'']]],
|
|
||||||
['readme_27355',['README',['../md_ripple_app_reporting_README.html',1,'']]],
|
|
||||||
['readme_27356',['README',['../md_ripple_proto_org_xrpl_rpc_v1_README.html',1,'']]],
|
|
||||||
['ripple_20source_20guidelines_27357',['Ripple Source Guidelines',['../md_ripple_README.html',1,'']]],
|
|
||||||
['resource_3a_3amanager_27358',['Resource::Manager',['../md_ripple_resource_README.html',1,'']]]
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
var searchData=
|
var searchData=
|
||||||
[
|
[
|
||||||
['sslutil_27359',['SSLUtil',['../md_ripple_crypto_README.html',1,'']]],
|
['rippled_20source_27351',['rippled Source',['../index.html',1,'']]],
|
||||||
['shard_20downloader_27360',['Shard Downloader',['../md_ripple_net_ShardDownloader.html',1,'']]],
|
['release_20notes_27352',['Release Notes',['../md____w_rippled_rippled_RELEASENOTES.html',1,'']]],
|
||||||
['shard_20size_20tuning_27361',['Shard size Tuning',['../md_ripple_nodestore_ShardSizeTuning.html',1,'']]],
|
['rcl_20consensus_27353',['RCL Consensus',['../md_ripple_app_consensus_README.html',1,'']]],
|
||||||
['shamap_20introduction_27362',['SHAMap Introduction',['../md_ripple_shamap_README.html',1,'']]]
|
['relational_20database_20interface_27354',['Relational Database Interface',['../md_ripple_app_rdb_README.html',1,'']]],
|
||||||
|
['readme_27355',['README',['../md_ripple_app_reporting_README.html',1,'']]],
|
||||||
|
['readme_27356',['README',['../md_ripple_proto_org_xrpl_rpc_v1_README.html',1,'']]],
|
||||||
|
['ripple_20source_20guidelines_27357',['Ripple Source Guidelines',['../md_ripple_README.html',1,'']]],
|
||||||
|
['resource_3a_3amanager_27358',['Resource::Manager',['../md_ripple_resource_README.html',1,'']]]
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
var searchData=
|
var searchData=
|
||||||
[
|
[
|
||||||
['the_20xrp_20ledger_27363',['The XRP Ledger',['../md____w_rippled_rippled_README.html',1,'']]]
|
['sslutil_27359',['SSLUtil',['../md_ripple_crypto_README.html',1,'']]],
|
||||||
|
['shard_20downloader_27360',['Shard Downloader',['../md_ripple_net_ShardDownloader.html',1,'']]],
|
||||||
|
['shard_20size_20tuning_27361',['Shard size Tuning',['../md_ripple_nodestore_ShardSizeTuning.html',1,'']]],
|
||||||
|
['shamap_20introduction_27362',['SHAMap Introduction',['../md_ripple_shamap_README.html',1,'']]]
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
var searchData=
|
var searchData=
|
||||||
[
|
[
|
||||||
['unit_20tests_27364',['Unit Tests',['../md_test_README.html',1,'']]]
|
['the_20xrp_20ledger_27363',['The XRP Ledger',['../md____w_rippled_rippled_README.html',1,'']]]
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ var indexSectionsWithContent =
|
|||||||
8: "abcdefghijklmnopqrstuvwxy",
|
8: "abcdefghijklmnopqrstuvwxy",
|
||||||
9: "abcdhilmorstvw",
|
9: "abcdhilmorstvw",
|
||||||
10: "c",
|
10: "c",
|
||||||
11: "abcdefhjlnoprstu"
|
11: "abcdefhijlnoprstu"
|
||||||
};
|
};
|
||||||
|
|
||||||
var indexSectionNames =
|
var indexSectionNames =
|
||||||
|
|||||||
Reference in New Issue
Block a user