add RippleAPI remote docs; move to pre-generated pages by default

This commit is contained in:
mDuo13
2015-11-20 18:54:18 -08:00
parent 89e5ddb07f
commit 68c03417bf
25 changed files with 27266 additions and 625 deletions

View File

@@ -44,6 +44,17 @@
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">References <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -54,6 +65,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</li>
<li class="dropdown">
@@ -64,17 +76,6 @@
<li><a href="gateway_guide.html">Gateway Guide</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Tools <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -153,6 +154,12 @@
<div class="col-md-3">
<h4>Documentation</h4>
<ul class="footer_links">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippled-apis.html">rippled</a></li>
<li><a href="rippled-setup.html">rippled Setup</a></li>
<li><a href="ripple-rest.html">Ripple-REST</a></li>
@@ -163,12 +170,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</div>
<div class="col-md-3">

File diff suppressed because it is too large Load Diff

View File

@@ -44,6 +44,17 @@
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">References <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -54,6 +65,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</li>
<li class="dropdown">
@@ -64,17 +76,6 @@
<li><a href="gateway_guide.html">Gateway Guide</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Tools <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -153,6 +154,12 @@
<div class="col-md-3">
<h4>Documentation</h4>
<ul class="footer_links">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippled-apis.html">rippled</a></li>
<li><a href="rippled-setup.html">rippled Setup</a></li>
<li><a href="ripple-rest.html">Ripple-REST</a></li>
@@ -163,12 +170,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</div>
<div class="col-md-3">

File diff suppressed because it is too large Load Diff

View File

@@ -29,18 +29,12 @@
<!-- syntax selection js -->
<script src="js/multicodetab.js"></script>
<!-- Code to load contents via Flatdoc -->
<script src="vendor/flatdoc/v/0.8.0/legacy.js"></script>
<script src="vendor/flatdoc/v/0.8.0/flatdoc.js"></script>
<!-- Markdown content already parsed+included; just do the code tab stuff -->
<script>
$(".flatdoc-content").empty();
$(".content-root .menubar .menu").empty();
Flatdoc.run({
fetcher: Flatdoc.file('content/fee-voting.md')
});
$(document).on('flatdoc:ready', function() {
$().multicode_tabs();
$(document).ready(function() {
$().multicode_tabs_pandoc();
hljs.initHighlighting();
make_code_expandable();
});
</script>
@@ -57,7 +51,7 @@
</head>
<body role='flatdoc' class='no-literate'>
<body class='no-literate'>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
@@ -71,6 +65,17 @@
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">References <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -81,6 +86,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</li>
<li class="dropdown">
@@ -91,17 +97,6 @@
<li><a href="gateway_guide.html">Gateway Guide</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Tools <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -136,9 +131,53 @@
<div class='content-root'>
<div class='menubar'>
<div class='menu section' role='flatdoc-menu'>
<script type="text/javascript" src="js/jquery.gensidebar.js"></script>
<script type="text/javascript">
</script>
</div>
</div>
<div role='flatdoc-content' class='content'>
<div class='content'>
<h1 id="fee-voting">Fee Voting</h1>
<p>In the long term, it may be necessary to change the basic schedule of XRP requirements to reflect long-term changes in the value of XRP. In this case, validators can vote for changes to basic <a href="tx-cost.html">transaction cost</a> as well as <a href="reserves.html">reserve requirements</a>. If the preferences in a validator's configuration are different than the network's current settings, the validator expresses its preferences to the network periodically. If a quorum of validators agrees on a change, they can apply a change that takes effect thereafter.</p>
<p>Operators of <a href="rippled-setup.html#running-a-validator"><code>rippled</code> validators</a> can set their preferences for the transaction cost and reserve requirements in the <code>[voting]</code> stanza of the <code>rippled.cfg</code> file. Changes here should not be made lightly: insufficient requirements could expose the Ripple peer-to-peer network to denial-of-service attacks. The parameters you can set are as follows:</p>
<table>
<thead>
<tr>
<th>Parameter</th>
<th>Description</th>
<th>Recommended Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>reference_fee</td>
<td>Amount of XRP, in <em>drops</em>, that must be destroyed to send the reference transaction, the cheapest possible transaction. (1 XRP = 1 million drops.) The actual transaction cost is a multiple of this value, scaled dynamically based on the load of individual servers.</td>
<td><code>10</code> (0.00001 XRP)</td>
</tr>
<tr>
<td>account_reserve</td>
<td>Minimum amount of XRP, in drops, that an account must have on reserve. This is the smallest amount that can be sent to fund a new account in the ledger.</td>
<td><code>20000000</code> (20 XRP)</td>
</tr>
<tr>
<td>owner_reserve</td>
<td>Additional amount of XRP, in drops, that an account must have on reserve for <em>each</em> object it owns in the ledger.</td>
<td><code>5000000</code> (5 XRP)</td>
</tr>
</tbody>
</table>
<h3 id="voting-process">Voting Process</h3>
<p>Every 256th ledger is called a "flag" ledger. In the ledger immediately before the flag ledger, each validator whose account reserve and transaction cost preferences are different than the current network setting distributes a "vote" message alongside its ledger validation, indicating the values that validator prefers.</p>
<p>In the flag ledger itself, nothing happens, but validators receive and take note of the votes from other validators they trust. </p>
<p>After seeing the votes of other validators, each validator that wants to change the transaction cost and reserve settings tries to find a compromise on settings that is closest to its own preferences but also approved by a majority of other validators it trusts. (For example, if one validator wants to raise the minimum transaction cost from 10 to 100, but most validators only want to raise it from 10 to 20, the one validator settles on the change to raise the cost to 20.) If this seems possible, the validator inserts a <a href="transactions.html#setfee">SetFee pseudo-transaction</a> into its proposal for the ledger following the flag ledger, with the expectation that other validators who also want the same change will insert an identical SetFee pseudo-transaction into their proposals for the same ledger. If a SetFee psuedotransaction survives the consensus process to be included in a validated ledger, then the new transaction cost and reserve settings denoted by the SetFee pseudotransaction takes effect starting with the following ledger.</p>
<p>In short:</p>
<ul>
<li><strong>Flag ledger -1</strong>: Validators submit votes.</li>
<li><strong>Flag ledger</strong>: Validators tally votes and decide what SetFee to include, if any.</li>
<li><strong>Flag ledger +1</strong>: Validators insert SetFee pseudo-transaction into their proposed ledgers.</li>
<li><strong>Flag ledger +2</strong>: New settings take effect, if a SetFee psuedotransaction achieved consensus.</li>
</ul>
</div>
</div>
</div>
@@ -148,6 +187,12 @@
<div class="col-md-3">
<h4>Documentation</h4>
<ul class="footer_links">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippled-apis.html">rippled</a></li>
<li><a href="rippled-setup.html">rippled Setup</a></li>
<li><a href="ripple-rest.html">Ripple-REST</a></li>
@@ -158,12 +203,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</div>
<div class="col-md-3">

View File

@@ -29,18 +29,12 @@
<!-- syntax selection js -->
<script src="js/multicodetab.js"></script>
<!-- Code to load contents via Flatdoc -->
<script src="vendor/flatdoc/v/0.8.0/legacy.js"></script>
<script src="vendor/flatdoc/v/0.8.0/flatdoc.js"></script>
<!-- Markdown content already parsed+included; just do the code tab stuff -->
<script>
$(".flatdoc-content").empty();
$(".content-root .menubar .menu").empty();
Flatdoc.run({
fetcher: Flatdoc.file('content/fees.md')
});
$(document).on('flatdoc:ready', function() {
$().multicode_tabs();
$(document).ready(function() {
$().multicode_tabs_pandoc();
hljs.initHighlighting();
make_code_expandable();
});
</script>
@@ -57,7 +51,7 @@
</head>
<body role='flatdoc' class='no-literate'>
<body class='no-literate'>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
@@ -71,6 +65,17 @@
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">References <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -81,6 +86,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</li>
<li class="dropdown">
@@ -91,17 +97,6 @@
<li><a href="gateway_guide.html">Gateway Guide</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Tools <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -136,9 +131,24 @@
<div class='content-root'>
<div class='menubar'>
<div class='menu section' role='flatdoc-menu'>
<script type="text/javascript" src="js/jquery.gensidebar.js"></script>
<script type="text/javascript">
</script>
</div>
</div>
<div role='flatdoc-content' class='content'>
<div class='content'>
<h1 id="fees-disambiguation">Fees (Disambiguation)</h1>
<p>The Ripple Consensus Ledger is a decentralized ledger, secured by cryptography, and operated by a distributed peer-to-peer network of servers. This means that no one party, not even the Ripple, Inc., can charge a fee for access to the network.</p>
<p>However, the rules of the Ripple Consensus Ledger include several types of fees, including the <a href="tx-cost.html"><em>transaction cost</em></a> and <a href="reserves.html"><em>reserve requirement</em></a>, which protect the ledger against abuse. (These neutral fees are not paid to anyone.) There are also several optional ways that users of the Consensus Ledger can collect fees from each other, both inside and outside the Ripple Consensus Ledger.</p>
<h2 id="in-the-ledger">In the Ledger</h2>
<p>The <em>transaction cost</em> (sometimes called the transaction fee) is a miniscule amount of XRP destroyed in order to send a transaction. This cost scales with the load of the network, which protects it from being overwhelmed with spam. See <a href="tx-cost.html">Transaction Cost</a> for more information.</p>
<p>The <em>account reserve</em> is a minimum amount of XRP that an account must possess. It scales with the number of objects the account owns in the ledger. This serves to disincentivize spam that would increase the size of the ledger. See <a href="reserves.html">Reserves</a> for more information.</p>
<p><em>Transfer fees</em> are optional percentage fees that gateways can charge to transfer the currencies they issue to other accounts within the Ripple Consensus Ledger. See <a href="https://ripple.com/knowledge_center/transfer-fees/">Transfer Fees</a> for more information.</p>
<p><em>Trust line quality</em> is a setting that allows an account to value balances on a trust line at higher or lower than face value. This can lead to situations that are similar to charging a fee. Trust line quality does not apply to XRP, which is not tied to a trust line.</p>
<h2 id="outside-the-ledger">Outside the Ledger</h2>
<p>While the above values are the only fees built into the ledger, people can still invent ways to charge fees outside the Ripple ledger. A common example is withdrawal or deposit fees charged by gateways, which are assessed when you use the gateway to get money into or out of the Ripple Consensus Ledger. This may cover the costs of transferring balances on traditional payment rails, or it may be in addition to those costs.</p>
<p>Many other fees are also possible. Businesses might charge for access, account maintenance, exchange services (especially when buying XRP on a private market instead of directly within the Ripple Consensus Ledger) and any number of other services. Always be aware of the fee schedule before doing business with any gateway or other financial institution.</p>
</div>
</div>
</div>
@@ -148,6 +158,12 @@
<div class="col-md-3">
<h4>Documentation</h4>
<ul class="footer_links">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippled-apis.html">rippled</a></li>
<li><a href="rippled-setup.html">rippled Setup</a></li>
<li><a href="ripple-rest.html">Ripple-REST</a></li>
@@ -158,12 +174,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</div>
<div class="col-md-3">

View File

@@ -29,18 +29,12 @@
<!-- syntax selection js -->
<script src="js/multicodetab.js"></script>
<!-- Code to load contents via Flatdoc -->
<script src="vendor/flatdoc/v/0.8.0/legacy.js"></script>
<script src="vendor/flatdoc/v/0.8.0/flatdoc.js"></script>
<!-- Markdown content already parsed+included; just do the code tab stuff -->
<script>
$(".flatdoc-content").empty();
$(".content-root .menubar .menu").empty();
Flatdoc.run({
fetcher: Flatdoc.file('content/gateway_guide.md')
});
$(document).on('flatdoc:ready', function() {
$().multicode_tabs();
$(document).ready(function() {
$().multicode_tabs_pandoc();
hljs.initHighlighting();
make_code_expandable();
});
</script>
@@ -57,7 +51,7 @@
</head>
<body role='flatdoc' class='no-literate'>
<body class='no-literate'>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
@@ -71,6 +65,17 @@
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">References <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -81,6 +86,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</li>
<li class="dropdown">
@@ -91,17 +97,6 @@
<li><a href="gateway_guide.html">Gateway Guide</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Tools <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -136,9 +131,565 @@
<div class='content-root'>
<div class='menubar'>
<div class='menu section' role='flatdoc-menu'>
<script type="text/javascript" src="js/jquery.gensidebar.js"></script>
<script type="text/javascript">
</script>
</div>
</div>
<div role='flatdoc-content' class='content'>
<div class='content'>
<h1 id="becoming-a-ripple-gateway">Becoming a Ripple Gateway</h1>
<p>Expanding an existing online financial system to support Ripple is a relatively simple task. Gateways are the businesses that link the Ripple network to the rest of the world. By becoming a Ripple gateway, existing online financial services, such as payment systems and digital currency exchange, can gain several advantages:</p>
<ul>
<li>By enabling its users to send and receive value in Ripple, the business increases its value proposition to users.</li>
<li>By accepting payments from the Ripple network, the business increases the number of ways that users can fund accounts at its business, even internationally.</li>
<li>The business can use Ripple-related services as a new source of revenue.</li>
</ul>
<p>This document explains the concepts and steps necessary to become a Ripple gateway. In this document, we use a fictional online currency exchange named "ACME" and its users as examples, to show how ACME can expand its business to include being a Ripple gateway.</p>
<h2 id="contact-information">Contact Information</h2>
<p>You are not on your own. Ripple Labs depends on the success of individual gateways, so we are here to help. Please contact us if you need any assistance building and growing your gateway.</p>
<ul>
<li>Contact <a href="mailto:partners@ripple.com">partners@ripple.com</a> for enterprise-class integrations, infrastructure advice, and other business development needs.</li>
<li>Contact <a href="mailto:developers@ripple.com">developers@ripple.com</a> for technical questions, clarifications, bug reports, and feature requests.</li>
</ul>
<h2 id="ripple-gateways-explained">Ripple Gateways Explained</h2>
<p>A Ripple <em><em>Gateway</em></em> is an entity that exchanges value in the Ripple Network for value outside Ripple, so it connects Ripple to the outside world. There are three major models that gateways can follow, with different purposes and modes of operation. </p>
<ul>
<li>An <strong>Issuing Gateway</strong> receives money (or other assets of value) outside of Ripple, and creates <em><em>issuances</em></em> in the Ripple Network. This provides a direct way for users to get money in and out of Ripple. This document focuses primarily on how to become an issuing gateway.</li>
<li>A <strong>Private Exchange</strong> lets its users purchase and trade XRP among users of the private exchange. This is similar to being an exchange for any other commodity or cryptocurrency. However, unlike other cryptocurrencies, there is also an exchange built into the Ripple protocol itself.</li>
<li>A <strong>Merchant</strong> accepts payment within Ripple for goods or services outside the network. Unlike an issuing gateway, a merchant business does not create its own currency, but accepts issuances that are created by other gateways. This guide does not describe how to accept Ripple payments as a merchant.</li>
</ul>
<h3 id="ripple-trust-lines-and-issuances">Ripple Trust Lines and Issuances</h3>
<p>All assets in Ripple, except for the native cryptocurrency XRP, are represented as <em>issuances</em>, which are digital assets that reflect traditional assets held by a gateway. Within Ripple, issuances can be sent and traded without the gateway's intervention and very low barriers to entry. Issuances get their value from gateway's agreement to honor the obligation that the issuances represent; there is no computer system that can force a Ripple gateway to honor that obligation. Therefore, Ripple's <em>trust lines</em> ensure that users only hold issuances from gateways they trust to pay out when needed.</p>
<p>A "trust line" is link between two accounts in Ripple that represents an explicit statement of willingness to hold gateway debt obligations. When a user sends money into Ripple, a Gateway takes custody of those assets outside of Ripple, and sends issuances within the Ripple network to the user. When a user sends money out of Ripple, she makes a Ripple payment to the gateway, and the gateway then sends the assets to the user in the outside world.</p>
<h3 id="xrp">XRP</h3>
<p>XRP is the native <a href="https://ripple.com/knowledge_center/math-based-currency/">cryptocurrency</a> of the Ripple network. Like issuances, XRP can be freely sent and exchanged by users in the network. Unlike issuances, XRP is not connected to a trust line, and can be sent directly from any Ripple account to any other, without going through a gateway or market maker. This helps make XRP a convenient bridge currency to facilitate currency exchanges.</p>
<p>XRP also serves other purposes in the Ripple network, in particular as <a href="https://ripple.com/knowledge_center/abuse-protection/">a protective measure against spamming the network</a>. All Ripple accounts need a small reserve of XRP in order to pay the network costs of maintaining their accounts and sending transactions. The anti-spam network fees are destroyed, not paid to any party.</p>
<p>Issuing gateways do not need to accumulate or exchange XRP. They must only maintain a small balance of XRP to send transactions on the network. The XRP equivalent of $10 USD should be enough for a busy gateway to operate for a year.</p>
<p>Private exchanges and market makers may choose to hold additional XRP to use as a means of exchange. Ripple Labs <strong>does not</strong> promote XRP as a speculative investment.</p>
<h3 id="liquidity-and-currency-exchange-within-ripple">Liquidity and Currency Exchange Within Ripple</h3>
<p>The Ripple network contains a distributed financial exchange, where any user can place and fulfill bids to exchange currencies (issuances and XRP). Cross-currency payments automatically use the financial exchange to convert currency atomically at the time of sending. In this way, users who choose to become market makers provide the liquidity that makes the Ripple Network useful.</p>
<p>When adding a new gateway to the Ripple network, it is important to establish liquidity to other popular currencies. Because liquidity is provided by third-party market makers, a gateway can provide currency-exchange services through Ripple without having to keep a large reserve of currencies or shoulder the risk of financial exchange.</p>
<p><a href="https://www.rippletrade.com/">Ripple Trade</a> is the official client application, and it is used by a large number of market makers to participate in the global exchange. To make it easier on users to trade in ACME issuances, Ripple Labs can add a shortcut to ACME's gateway in Ripple Trade, so long as ACME meets <a href="https://ripple.com/files/GB-2015-02.pdf">certain best-practice requirements</a>.</p>
<p>Contact <a href="mailto:partners@ripple.com">partners@ripple.com</a> for help establishing a market between your gateway and others, and about getting your gateway listed in Ripple Trade.</p>
<h2 id="suggested-business-practices">Suggested Business Practices</h2>
<p>The value of a gateway's issuances in Ripple comes directly from users' trust that users can redeem them with the gateway when needed. Since a gateway cannot pay out if it shuts down, it is also in customers' interest that a gateway does not shut down. There are a number of precaution a gateway can take that reduce the risk of business interruptions:</p>
<ul>
<li>Use <a href="#hot-and-cold-wallets">Hot and Cold Wallets</a> to limit your risk profile on the network.</li>
<li>Comply with anti-money-laundering regulations for your jurisdiction, such as the <a href="http://en.wikipedia.org/wiki/Bank_Secrecy_Act">Bank Secrecy Act</a>. This usually includes requirements to collect <a href="http://en.wikipedia.org/wiki/Know_your_customer">"Know-Your-Customer" (KYC) information</a>.</li>
<li>Read and stay up-to-date with <a href="https://ripple.com/knowledge_center/gateway-bulletins/">Gateway Bulletins</a>, which provide news and suggestions for Ripple gateways.</li>
<li>Clearly publicize all your policies and fees.</li>
</ul>
<h3 id="hot-and-cold-wallets">Hot and Cold Wallets</h3>
<p>It is strongly recommended that Ripple gateways employ a "hot wallet / cold wallet" strategy. This enforces a separation of roles that promotes strong security. ("Wallets" in Ripple are equivalent to Accounts.)</p>
<p>If a malicious person compromises a gateway's issuing account (cold wallet), that person could create an unlimited amount of new issuances, which makes it very difficult to redeem legitimately-held issuances fairly. In this case, the gateway must create a new issuing account, and all users with trust lines to the old gateway must create new trust lines to the new account. Thus, it's best to keep your issuing account as secure as possible.</p>
<p>The cold wallet is like a vault. It serves as the asset issuer, and should remain offline. The secret key that is used for this wallet is kept offline, accessible to only a few trusted operators. Periodically, a human operator creates and signs a transaction (preferably from an entirely offline machine) in order to refill the hot wallet's balance. Because the cold wallet is the account creating the issuances, customer accounts holding those issuances must trust the cold wallet. </p>
<p>A hot wallet is like a cash register. It makes payments to the gateway's users in Ripple by sending them issuances created by the cold wallet. The secret key for a hot wallet is, by necessity, stored on a server that is connected to the outside internet, usually in a configuration file on a public-facing server. Because it holds issuances created by the cold walet, each hot wallet needs a trust line to the cold wallet. Customers do not, and should not, trust hot wallet accounts.</p>
<p>(Unlike a cash register, the hot wallet does not have to handle incoming payments from users, because the cold wallet can receive and monitor payments without using its secret key. To make things simple for your users, we recommend treating incoming payments to the hot and cold wallets as the same.)</p>
<p>A gateway can use one or more "hot wallet" accounts, but each hot wallet has a limited balance of the gateway's issuances. If a hot wallet is compromised, the gateway only loses as much currency as that account holds. Customers do not need to change any configuration in order to receive funds from a new hot wallet. However, the gateway must monitor the hot wallet's balance so that it doesn't run out of funds during ordinary operation.</p>
<h3 id="warm-wallets">Warm Wallets</h3>
<p>Another optional step that a gateway can take to balance risk and convenience is to use "warm wallets" as an intermediate step between cold wallet and hot wallet. Frequently, the software that runs a gateway's daily operations uses a single hot wallet. The gateway can operate additional accounts as warm wallets, whose keys are entrusted to different trusted users and whose keys are also not stored for use by the gateway software. </p>
<p>When the hot wallet is running low on funds, the warm wallets can be used to refill it. When the warm wallets run low on funds, the cold wallet can issue more currency to a warm wallet in a single transaction, and the warm wallets can distribute that currency among themselves if necessary. This improves security of the cold wallet, since it makes as few transactions as possible, without leaving too much money in the control of a single automated hot wallet.</p>
<p>As with hot wallets, warm wallets must trust the cold wallet, and should not be trusted by users. All precautions that apply to hot wallets also apply to warm wallets.</p>
<h3 id="funds-lifecycle">Funds Lifecycle</h3>
<p>Funds in Ripple tend to flow in a cycle, from the cold wallet to the warm wallets, then the warm wallets, to customers, and eventually from customers back to the cold wallet. Issuances (any non-XRP balance in Ripple) are always tied to a trust line, so each payment "ripples" through ACME's issuing account on the trust lines connected to it. Ultimately, the lifecycle of issuances in Ripple looks something like this:</p>
<p><img alt="Funds flow diagram" src="img/e2g-funds_flow.png"/></p>
<ol>
<li>Alice <a href="#sending-from-gateway-to-ripple">initiates a payment from ACME to Ripple</a>. Outside of Ripple, ACME debits Alice's account. Then ACME sends a Ripple payment of EUR.ACME from ACME's hot wallet account to Alice's Ripple account.</li>
<li>ACME refills its hot wallet using EUR.ACME from its warm wallet.</li>
<li>ACME refills its warm wallet by issuing new EUR.ACME from its cold wallet.</li>
<li>Alice <a href="#sending-from-ripple-to-gateway">initiates a payment from Ripple to ACME</a>. This is basically the reverse of the initial step. Alice redeems issuances of EUR.ACME by sending them to ACME's cold wallet. Outside of Ripple, ACME credits Alice for the amount it received.</li>
</ol>
<h2 id="fees-and-revenue-sources">Fees and Revenue Sources</h2>
<p>There are several ways in which a gateway can seek to benefit financially from Ripple integration. These can include:</p>
<ul>
<li>Withdrawal and Deposit fees. It is typical for a gateway to charge a small fee (such as 1%) for the service of adding or removing money from Ripple. You have the power to determine the rate you credit people when they move money onto and off of Ripple through your gateway.</li>
<li>Transfer fees. You can set a percentage fee to charge automatically when Ripple users send each other issuances created by your account. This amount is debited from the Ripple ledger, decreasing your obligation each time your issuances change hands. See <a href="#transferrate">TransferRate</a> for details.</li>
<li>Indirect revenue from value added. Ripple integration can provide valuable functionality for your customers that distinguishes your business from your competitors.</li>
<li>Interest on Ripple-backed funds. You can keep some of your Ripple-backing currency in an external account that earns interest. Just make sure you can always access enough funds to service customer withdrawals.</li>
<li><a href="#market-makers">Market making</a>. A gateway can also make offers to buy and sell its issuances for other issuances on Ripple, providing liquidity to cross-currency payments and possibly making a profit. (As with any market making opportunity, profits are not guaranteed.)</li>
</ul>
<h3 id="choosing-fee-rates">Choosing Fee Rates</h3>
<p>Fees imposed by gateways are optional. Obviously, higher fees mean more revenue when a gateway's services are used. On the other hand, having high fees decreases the desirability of your gateway's issuances and services, which incentivizes customers to use other gateways instead. Consider the fees that are charged by other gateways, especially ones issuing similar currencies, as well as traditional payment systems outside of Ripple, such as wire fees. Choosing the right fee structure is a matter of balancing your pricing with what the market will pay.</p>
<h1 id="ripple-integration">Ripple Integration</h1>
<h2 id="before-ripple-integration">Before Ripple Integration</h2>
<p>Our example exchange, ACME, already accepts withdrawals and deposits from users using some existing system, and uses an internal accounting system to track how much balance each user has with the exchange. Such a system can be modeled simply with a balance sheet and tracking how much currency each user has on hand.</p>
<p>In the following diagram, ACME Exchange starts with €5 on hand, including €1 that belongs to Bob, €2 that belongs to Charlie, and an additional €2 of equity that belongs to ACME itself. Alice deposits €5, so ACME adds her to its balance sheet and ends up with €10.</p>
<p><img alt="Alice sends €5 to ACME. ACME adds her balance to its balance sheet." src="img/e2g-01.png"/></p>
<p><strong>Assumptions:</strong> To integrate with Ripple, we assume that an exchange such as ACME meets the following assumptions:</p>
<ul>
<li>ACME already has a system to accept deposits and withdrawals from some outside payment source. </li>
<li>ACME waits for deposits to clear before crediting them internally.</li>
<li>ACME always keeps enough funds on-hand to pay withdrawals on demand, subject to their terms and conditions.<ul>
<li>ACME can set fees, minimum withdrawals, and delay times for deposits and withdrawals as their business model demands.</li>
</ul>
</li>
</ul>
<h2 id="sending-from-gateway-to-ripple">Sending from Gateway to Ripple</h2>
<p>Ripple payments can automatically bridge between currencies, but an issuing gateway normally only sends single-currency payments that go directly to users. This means debiting a user's current balance, and then sending the equivalent amount of issuances in Ripple to the user's Ripple account. </p>
<p>An example flow for a payment into Ripple:</p>
<ol>
<li>Alice asks to send €3 of her ACME balance into Ripple.</li>
<li>Internally, ACME debits Alice's balance €3.</li>
<li>ACME submits a Ripple transaction, sending €3 to Alice's Ripple address. The €3 is marked in the Ripple network as being "issued" by ACME (3 EUR.ACME).</li>
</ol>
<p><strong>Assumptions:</strong> </p>
<ul>
<li>Alice already has an account in the Ripple network separate from her ACME account, which she manages using an application such as Ripple Trade.</li>
</ul>
<p><img alt="ACME issues 3 EUR.ACME to Alice on Ripple" src="img/e2g-02.png"/></p>
<h3 id="requirements-for-sending-to-ripple">Requirements for Sending to Ripple</h3>
<p>There are several prerequisites that ACME must meet in order for this to happen:</p>
<ul>
<li>ACME modifies its core accounting system to track money that is backing funds issued on the Ripple Network. ACME can query Ripple to see who holds its Ripple issuances at any time.<ul>
<li>Optionally, a gateway can take additional steps to separate the assets backing the gateway's Ripple issuances. For example, the funds allocated to Ripple can be stored in a separate "Ripple Escrow" bank account. A cryptocurrency exchange can create a separate wallet to hold the funds allocated to Ripple, as publicly-verifiable proof to customers that the gateway is solvent.</li>
</ul>
</li>
<li>ACME must have an account on the Ripple network. Our best practices recommend actually having at least two accounts: a "cold wallet" account to issue currency, and one or more "hot wallet" accounts that perform day-to-day transactions. See <a href="#hot-and-cold-wallets">Hot and Cold Wallets</a> for more information.<ul>
<li>ACME must enable the <a href="#defaultripple">DefaultRipple Flag</a> on its issuing account in order for users to send and receive its issuances.</li>
</ul>
</li>
<li>Alice must create a trustline from her Ripple account to ACME's issuing (cold wallet) account. She can do this from any Ripple client application as long as she knows the address or Ripple Name of ACME's cold wallet.<ul>
<li>In order to do this, Alice needs to find the address of ACME's cold wallet. ACME can publicize its cold wallet address on its website, or have its gateway listed in a client such as Ripple Trade. See <a href="#setting-trust-lines-in-ripple-trade">Setting Trust Lines in Ripple Trade</a>.</li>
</ul>
</li>
<li>ACME must create a user interface for Alice to send funds from ACME into Ripple.<ul>
<li>In order to do this, ACME needs to know Alice's Ripple address. ACME can have Alice input her Ripple addresss as part of the interface, or ACME can require Alice to input and verify her Ripple address in advance.</li>
</ul>
</li>
</ul>
<p>See <a href="#sending-payments-to-users">Sending Payments to Users</a> for an example of how to send payments into Ripple.</p>
<h2 id="sending-from-ripple-to-gateway">Sending from Ripple to Gateway</h2>
<p>A payment out of Ripple means the Gateway receives a payment in the Ripple network, and credits a user outside of Ripple.</p>
<p>An example flow of a payment out of Ripple:</p>
<ol>
<li>Bob sends Ripple transaction of €1 to ACME's cold wallet.</li>
<li>In its internal accounting, credits Bob's balance €1.</li>
</ol>
<p>Payments going from Ripple to a gateway can be single-currency or cross-currency payments. Users can choose the exchange rates in a Ripple client application such as Ripple Trade, so that the gateway receives issuances created by its cold wallet account.</p>
<h3 id="requirements-for-receiving-from-ripple">Requirements for Receiving from Ripple</h3>
<p>In addition to the <a href="#requirements-for-sending-to-ripple">requirements for sending into Ripple</a>, there are several prerequisites that ACME must meet in order to process payments coming from Ripple:</p>
<ul>
<li>ACME must monitor its cold and hot wallet Ripple accounts for incoming payments.</li>
<li>ACME must know which user to credit internally for the incoming payments.<ul>
<li>We recommend that ACME should <a href="#bouncing-payments">bounce any unrecognized incoming payments</a> back to their sender.</li>
<li>Typically, the preferred method of recognizing incoming payments is through <a href="#source-and-destination-tags">destination tags</a>.</li>
</ul>
</li>
</ul>
<h2 id="precautions">Precautions</h2>
<p>Processing payments to and from Ripple naturally comes with some risks, so a gateway should be sure to take care in implementing these processes. We recommend the following precautions:</p>
<ul>
<li>Protect yourself against reversible deposits. Ripple payments are irreversible, but many electronic money systems like credit cards or PayPal are not. Scammers can abuse this to take their fiat money back by canceling a deposit after receiving Ripple issuances.</li>
<li>When sending into Ripple, specify the cold wallet as the issuer of the currency. Otherwise, you might accidentally use paths that deliver the same currency issued by other accounts.</li>
<li>Before sending a payment into Ripple, double check the cost of the payment. A simple payment from your hot wallet to a customer should not cost more than the destination amount plus any <a href="#transferrate">transfer fee</a> you have set.</li>
<li>Before processing a payment out of Ripple, make sure you know the customer's identity. This makes it harder for anonymous attackers to scam you, and it is also an important element of most anti-money-laundering regulations. This is especially important because the users sending money from Ripple could be different than the ones that initially received the money in Ripple.</li>
<li>Follow the guidelines for <a href="#reliable-transaction-submission">reliable transaction submission</a> when sending Ripple transactions.</li>
<li><a href="#robustly-monitoring-for-payments">Robustly monitor for incoming payments</a>, and read the correct amount. Don't mistakenly credit someone the full amount if they only sent a <a href="transactions.html#partial-payments">partial payment</a>.</li>
<li>Track your obligations and balances within the Ripple network, and compare with your assets off the network. If they do not match up, stop processing withdrawals and deposits until you resolve the discrepancy.</li>
<li>Proactively avoid ambiguous situations. We recommend the following:<ul>
<li>Enable the <a href="#disallowxrp"><code>DisallowXRP</code> flag</a> for the cold wallet account and all hot wallet accounts, so users do not accidentally send you XRP. (Private exchanges should <em>not</em> set this flag, since they trade XRP normally.)</li>
<li>Enable the <a href="#requiredest"><code>RequireDest</code> flag</a> for the cold wallet account and all hot wallet accounts, so users do not accidentally send a payment without the destination tag to indicate who should be credited.</li>
<li>Enable the <a href="#requireauth"><code>RequireAuth</code> flag</a> on all hot wallet accounts so they cannot unintentionally create their own issuances.</li>
</ul>
</li>
<li>Monitor for suspicious or abusive behavior. For example, a user could repeatedly withdraw and deposit funds in Ripple, as a denial of service attack that effectively empties the hot wallet. Suspend users whose accounts are involved in suspicious behavior by not processing their Ripple payments.</li>
</ul>
<h2 id="trading-on-ripple">Trading on Ripple</h2>
<p>After the issuances have been created in Ripple, they can be freely transferred and traded by Ripple users. There are several consequences of this situation:</p>
<ul>
<li>Anyone can buy/sell EUR.ACME on Ripple. If ACME issues multiple currencies on Ripple, a separate trust line is necessary for each.<ul>
<li>This includes users who do not have an account with ACME Exchange. In order to withdraw the funds successfully from ACME, users still have to create ACME accounts.</li>
<li>Optionally, use the <a href="#authorized-accounts">Authorized Accounts</a> feature to limit who can hold EUR.ACME on Ripple.</li>
<li>If a gateway determines that a user has acted in bad faith, the gateway can <a href="#freezes">Freeze</a> that user's trust line to the gateway, so that the user can no longer trade in the gateway's issuances.</li>
</ul>
</li>
<li>Ripple users trading and sending EUR.ACME to one another requires no intervention by ACME.</li>
<li>All exchanges and balances on Ripple are publicly viewable in the shared, global ledger. </li>
</ul>
<p>The following diagram depicts a simple Ripple payment sending 2EUR.ACME from Alice to Charlie. ACME can query Ripple to see updates to its balances any time after the transaction has occurred:</p>
<p><img alt="Alice's sends 2 EUR.ACME from her trust line to Charlie's" src="img/e2g-03.png"/></p>
<h2 id="market-makers">Market Makers</h2>
<p>Exchanging EUR.ACME for other currencies within Ripple requires market makers who are willing to exchange other Ripple issuances for EUR.ACME. Market makers are just Ripple accounts with trust lines for EUR.ACME as well as other currencies or issuers, who create orders (called "offers" in the Ripple ledger) to exchange currency. The cost of exchanging EUR.ACME for another currency depends on the quantity and quality of orders. </p>
<p>To facilitate exchanging currency, ACME may decide to become its own market maker. For various reasons, we recommend using a separate Ripple account for trading. Because market making can result in financial losses, gateways that choose to act as market makers should not use customer funds for market making.</p>
<p>For more information about how and why to become a market maker, see the <a href="https://ripple.com/files/ripple_mm.pdf">Maket Makers Whitepaper (PDF)</a>.</p>
<h2 id="freezes">Freezes</h2>
<p>Ripple provides the ability to freeze assets issued by the gateway in Ripple as a means of complying with regulatory requirements:</p>
<ul>
<li>Gateways can freeze individual account issuances, in case a user account shows suspicious activity or violates a gateway's terms of use.</li>
<li>Gateways can freeze all issuances created by their cold wallet, in case of a compromised gateway account or for migrating cold wallets.</li>
<li>Furthermore, gateways can permanently opt out of their ability to freeze issuances at all. This allows a gateway to assure its users that it will continue to provide "physical-money-like" services.</li>
</ul>
<p>For more information, see the <a href="https://ripple.com/files/GB-2014-02.pdf">Gateway Bulletin on Freezes</a>.</p>
<h2 id="authorized-accounts">Authorized Accounts</h2>
<p>Ripple's Authorized Accounts feature enables a gateway to limit who can hold that gateway's issuances, so that unknown Ripple accounts cannot hold the currency your gateway issues. We feel this is <em>not necessary</em> in most cases, since gateways have full control over the process of redeeming Ripple balances for value in the outside world. (You can collect customer information and impose limits on withdrawals at that stage without worrying about what happens within the Ripple network.)</p>
<p>To use the Authorized Accounts feature, a gateway enables the <code>RequireAuth</code> flag for its cold wallet account, and then individually approves each user account's trust line before sending issuances in Ripple to that account.</p>
<p>You must authorize trust lines using the same cold wallet account that issues the currency, which unfortunately means an increased risk exposure for that account. The process for sending funds into Ripple with RequireAuth enabled looks like the following:</p>
<ol>
<li>ACME publishes the address of its cold wallet to users.</li>
<li>Alice extends a trust line from her Ripple account to ACME's cold wallet, indicating that she is willing to hold ACME's issuances.</li>
<li>ACME's cold wallet sends a transaction authorizing Alice's trust line.</li>
</ol>
<p>See <a href="#requireauth">RequireAuth</a> for technical details on how to use authorized accounts.</p>
<h2 id="source-and-destination-tags">Source and Destination Tags</h2>
<p><em>Destination Tags</em> are a feature of Ripple payments can be used to identify the beneficiary or destination for a payment. For example, a Ripple payment to a gateway may include a destination tag to indicate which customer should be credited for the payment. A gateway should maintain an internal mapping of destination tags to (non-Ripple) account records. </p>
<p>Similarly, <em>Source Tags</em> indicate the originator or source of a payment. Most commonly, a Source Tag is included so that the recipient of the payment knows where to bounce the payment. When you bounce an incoming payment, use the Source Tag from the incoming payment as the Destination Tag of the outgoing (bounce) payment.</p>
<p>We recommend providing several kinds of Destination Tags for different purposes:</p>
<ul>
<li>Direct mappings to user accounts</li>
<li>Matching the Source Tags on outgoing payments (in case your payments get bounced)</li>
<li>Tags for quotes, which expire</li>
<li>Other disposable destination tags that users can generate.</li>
</ul>
<p>See <a href="#generating-source-and-destination-tags">Generating Source and Destination Tags</a> for recommendations on the tehnical details of making and using Source Tags and Destination Tags.</p>
<h1 id="technical-details">Technical Details</h1>
<h2 id="infrastructure">Infrastructure</h2>
<p>For the gateway's own security as well as the stability of the network, we recommend that each gateway operate its own <code>rippled</code> servers, along with any other important infrastructure necessary for the gateway's operation. Ripple Labs provides detailed and individualized recommendations to businesses interested in operating a significant Ripple-based business.</p>
<p>Contact <a href="mailto:partners@ripple.com">partners@ripple.com</a> to see how Ripple Labs can help.</p>
<h3 id="apis-and-middleware">APIs and Middleware</h3>
<p>There are several interfaces you can use to connect to Ripple, depending on your needs and your existing software:</p>
<ul>
<li><a href="rippled-apis.html"><code>rippled</code></a> provides JSON-RPC and WebSocket APIs that can be used as a low-level interface to all core Ripple functionality.<ul>
<li>The official client library to rippled, <a href="https://github.com/ripple/ripple-lib">ripple-lib</a> is available for JavaScript, and provides extended convenience features.</li>
</ul>
</li>
<li><a href="ripple-rest.html">Ripple-REST</a> provides an easy-to-use RESTful API on top of <code>rippled</code>. In particular, Ripple-REST is designed to be easier to use from statically-typed languages.</li>
</ul>
<h2 id="tool-security">Tool Security</h2>
<p>Any time you submit a Ripple transaction, it must be signed using your secret. However, having your secret means having full control over your account. Therefore, you should never transmit your secret to a server operated by someone else. Instead, use your own server or client application to sign the transactions before sending them out.</p>
<p>The examples in this document show Ripple-REST API methods that include an account secret. This is only safe if you control the Ripple-REST server yourself, <em>and</em> you connect to it over a connection that is secure from outside listeners. (For example, you could connect over a loopback (localhost) network, a private subnet, or an encrypted VPN.) Alternatively, you could operate your own <code>rippled</code> server; or you can use a client application such as <code>ripple-lib</code> to perform local signing before submitting your transactions to a third-party server.</p>
<h2 id="defaultripple">DefaultRipple</h2>
<p>The DefaultRipple flag controls whether the balances held in an account's trust lines are <a href="https://ripple.com/knowledge_center/understanding-the-noripple-flag/">allowed to ripple</a> by default. Rippling is what allows users to trade issuances, so a gateway must allow rippling on all the trust lines connected to its issuing (cold wallet) account.</p>
<p>Before asking users to trust its issuing account, a gateway should enable the DefaultRipple flag on that account. Otherwise, the gateway must individually disable the NoRipple flag for each trust line that other accounts extend to it.</p>
<p><em>Note:</em> Ripple-REST (as of version 1.4.0) does not yet support retrieving or setting the DefaultRipple flag.</p>
<p>The following is an example of using a local <a href="ripple-rest.html#update-account-settings"><code>rippled</code> JSON-RPC API</a> to enable the DefaultRipple flag:</p>
<p>Request:</p>
<pre><code>POST http://localhost:8088/
{
"method": "submit",
"params": [
{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"tx_json": {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Fee": "15000",
"Flags": 0,
"SetFlag": 8,
"TransactionType": "AccountSet"
}
}
]
}
</code></pre>
<p><em>(<strong>Reminder:</strong> Don't send your secret to a server you do not control.)</em></p>
<p>Response:</p>
<pre><code>{
"result": {
"engine_result": "tesSUCCESS",
"engine_result_code": 0,
"engine_result_message": "The transaction was applied. Only final in a validated ledger.",
"status": "success",
"tx_blob": "1200032200000000240000003E202100000008684000000000003A98732103AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB74473045022100D8F2DEF27DE313E3F0D1E189BF5AC8879F591045950E2A33787C3051169038C80220728A548F188F882EA40A416CCAF2AC52F3ED679563BBE1BAC014BB9E773A333581144B4E9C06F24296074F7BC48F92A97916C6DC5EA9",
"tx_json": {
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"Fee": "15000",
"Flags": 0,
"Sequence": 62,
"SetFlag": 8,
"SigningPubKey": "03AB40A0490F9B7ED8DF29D246BF2D6269820A0EE7742ACDD457BEA7C7D0931EDB",
"TransactionType": "AccountSet",
"TxnSignature": "3045022100D8F2DEF27DE313E3F0D1E189BF5AC8879F591045950E2A33787C3051169038C80220728A548F188F882EA40A416CCAF2AC52F3ED679563BBE1BAC014BB9E773A3335",
"hash": "665B27B64CE658704FFD326A4FE2F5F5B5E67EACA61DE08258A59D35B883E1D5"
}
}
}
</code></pre>
<p>To confirm that an account has DefaultRipple enabled, look up the account using the <a href="rippled-apis.html#account-info">account_info command</a>, specifying a validated ledger version. Use <a href="https://en.wikipedia.org/wiki/Bitwise_operation#AND">a bitwise-AND operator</a> to compare the <code>Flags</code> field with 0x00800000 (the <a href="ripple-ledger.html#accountroot-flags">ledger flag lsfDefaultRipple</a>). If the result of the bitwise-AND operation is nonzero, then the account has DefaultRipple enabled.</p>
<h2 id="generating-source-and-destination-tags">Generating Source and Destination Tags</h2>
<p>You need a scheme to create Source and Destination tags for your users and payments. (See <a href="#source-and-destination-tags">Source and Destination Tags</a> for an explanation of what Source and Destination Tags are.)</p>
<p>For greater privacy and security, we recommend <em>not</em> using monotonically-incrementing numbers as destination tags that correlate 1:1 with users. Instead, we recommend using convenient internal IDs, but mapping those to destination tags through the use of a quick hash or cipher function such as <a href="http://en.wikipedia.org/wiki/Hasty_Pudding_cipher">Hasty Pudding</a>. You should set aside ranges of internal numbers for different uses of destination tags.</p>
<p>After passing the internal numbers through your hash function, you can use the result as a destination tag. You should check for collisions just to be safe, and do not reuse destination tags unless they have explicit expiration dates (like quotes and user-generated tags).</p>
<p>We recommend making a user interface to generate a destination tag on-demand when a user intends to send money to the gateway. Then, consider that destination tag valid only for a payment with the expected amount. Later, bounce any other transactions that reuse the same destination tag.</p>
<p>Enable the <a href="#requiredest">RequireDest</a> flag on your hot and cold wallet accounts so that users must use a destination tag to indicate where funds should go when they send Ripple payments to your gateway.</p>
<h2 id="disallowxrp">DisallowXRP</h2>
<p>The DisallowXRP flag (<code>disallow_xrp</code> in Ripple-REST) is designed to discourage users from sending XRP to an account by accident. This reduces the costs and effort of bouncing undesired payments, if you operate a gateway that does not trade XRP. The DisallowXRP flag is not strictly enforced, because doing so could allow accounts to become permanently unusable if they run out of XRP. Client applications should honor the DisallowXRP flag, but it is intentionally possible to work around. </p>
<p>An issuing gateway that does not trade XRP should enable the DisallowXRP flag on all gateway hot and cold wallets. A private exchange that trades in XRP should only enable the DisallowXRP flag on accounts that are not expected to receive XRP.</p>
<p>The following is an example of a <a href="ripple-rest.html#update-account-settings">Ripple-REST Update Account Settings request</a> to enable the DisallowXRP flag:</p>
<p>Request:</p>
<pre><code>POST /v1/accounts/rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn/settings?validated=true
{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"settings": {
"disallow_xrp": true
}
}
</code></pre>
<p><em>(<strong>Reminder:</strong> Don't send your secret to a server you do not control.)</em></p>
<p>Response:</p>
<pre><code>200 OK
{
"success": true,
"settings": {
"hash": "AC0F7D7735CDDC6D859D0EC4E96A571F71F7481750F4C6C975FC8075801A6FB5",
"ledger": "10560577",
"state": "validated",
"require_destination_tag": false,
"require_authorization": false,
"disallow_xrp": true
}
}
</code></pre>
<p>The value <code>"disallow_xrp": true</code> indicates that the DisallowXRP flag is enabled. A successful response shows <code>"state": "validated"</code> when the change has been accepted into a validated Ripple ledger.</p>
<h2 id="requiredest">RequireDest</h2>
<p>The <code>RequireDest</code> flag (<code>require_destination_tag</code> in Ripple-REST) is designed to prevent users from sending payments to your account while accidentally forgetting the <a href="#source-and-destination-tags">destination tag</a> that identifies who should be credited for the payment. When enabled, the Ripple Network rejects any payment to your account that does not specify a destination tag.</p>
<p>We recommend enabling the RequireDest flag on all gateway hot and cold wallets.</p>
<p>The following is an example of a <a href="ripple-rest.html#update-account-settings">Ripple-REST Update Account Settings request</a> to enable the RequireDest flag.</p>
<p>Request:</p>
<pre><code>POST /v1/accounts/rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn/settings?validated=true
{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"settings": {
"require_destination_tag": true
}
}
</code></pre>
<p><em>(<strong>Reminder:</strong> Don't send your secret to a server you do not control.)</em></p>
<p>Response:</p>
<pre><code>200 OK
{
"success": true,
"settings": {
"hash": "F3D2EE87D597BA50EA3A94027583110925E8BAAFE41511F937D65423B18BC2A3",
"ledger": "10560755",
"state": "validated",
"require_destination_tag": true,
"require_authorization": false,
"disallow_xrp": false
}
}
</code></pre>
<p>The value <code>"require_destination_tag": true</code> indicates that the RequireDest flag has been enabled. A successful response shows <code>"state": "validated"</code> when the change has been accepted into a validated Ripple ledger.</p>
<h2 id="requireauth">RequireAuth</h2>
<p>The <code>RequireAuth</code> flag (<code>require_authorization</code> in Ripple-REST) prevents a Ripple account's issuances from being held by other users unless the issuer approves them. </p>
<p>We recommend enabling RequireAuth for all hot wallet (and warm wallet) accounts, as a precaution. Separately, the Authorized Accounts feature involves <a href="#with-cold-wallets">setting the RequireAuth flag on your cold wallet</a>.</p>
<p>You can only enable RequireAuth if the account owns no trust lines and no offers in the Ripple ledger, so you must decide whether or not to use it before you start doing business in the Ripple network.</p>
<h3 id="with-hot-wallets">With Hot Wallets</h3>
<p>We recommend enabling <code>RequireAuth</code> for all hot wallet accounts, and then never approving any accounts, in order to prevent hot wallets from creating issuances even by accident. This is a purely precuationary measure, and does not impede the ability of those accounts to transfer issuances created by the cold wallet, as they are intended to do.</p>
<p>The following is an example of a <a href="ripple-rest.html#update-account-settings">Ripple-REST Update Account Settings request</a> to enable the RequireDest flag. (This method works the same way regardless of whether the account is used as a hot wallet or cold wallet.)</p>
<p>Request:</p>
<pre><code>POST /v1/accounts/rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW/settings?validated=true
{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"settings": {
"require_authorization": true
}
}
</code></pre>
<p><em>(<strong>Reminder:</strong> Don't send your secret to a server you do not control.)</em></p>
<p>Response:</p>
<pre><code>{
"success": true,
"settings": {
"hash": "687702E0C3952E2227B2F7A0B34933EAADD72A572B234D31360AD83D3F193A78",
"ledger": "10596929",
"state": "validated",
"require_destination_tag": false,
"require_authorization": true,
"disallow_xrp": false
}
}
</code></pre>
<h3 id="with-cold-wallets">With Cold Wallets</h3>
<p>You may also enable <code>RequireAuth</code> for your cold wallet in order to use the <a href="#authorized-accounts">Authorized Accounts</a> feature. The procedure to enable the RequireAuth flag for a cold wallet is the same as <a href="#with-hot-wallets">with hot wallets</a>.</p>
<p>If ACME decides to use Authorized Accounts, ACME creates an interface for users to get their Ripple trust lines authorized by ACME's cold account. After Alice has extended a trust line to ACME from her Ripple account, she goes through the interface on ACME's website to require ACME authorize her trust line. ACME confirms that it has validated Alice's identity information, and then sends a TrustSet transaction to authorize Alice's trust line.</p>
<p>The following is an example of using the <a href="ripple-rest.html#grant-trustline">Ripple-REST Grant Trustline method</a> to authorize the (customer) account rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn to hold issuances of USD from the (cold wallet) account rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW:</p>
<p>Request:</p>
<pre><code>POST /v1/accounts/rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW/trustlines?validated=true
{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"trustline": {
"limit": "0",
"currency": "USD",
"counterparty": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"authorized": true
}
}
</code></pre>
<p><em>(<strong>Reminder:</strong> Don't send your secret to a server you do not control.)</em></p>
<p>Response:</p>
<pre><code>201 Created
{
"success": true,
"trustline": {
"account": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
"limit": "0",
"currency": "USD",
"counterparty": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"account_allows_rippling": true,
"account_trustline_frozen": false,
"authorized": true
},
"hash": "4509288EE17F01C83FC7D45850EB066A795EE5DBA17BB4DC98DD4023D31EEE5B",
"ledger": "11158585",
"state": "validated"
}
</code></pre>
<p>A successful response shows <code>"state": "validated"</code> when the change has been accepted into a validated Ripple ledger.</p>
<h2 id="robustly-monitoring-for-payments">Robustly Monitoring for Payments</h2>
<p>In order to robustly monitor incoming payments, gateways should do the following:</p>
<ul>
<li>Keep a record of the most-recently-processed transaction and ledger. That way, if you temporarily lose connectivity, you know how far to go back.</li>
<li>Check the result code of every incoming payment. Some payments go into the ledger to charge an anti-spam fee, even though they failed. Only transactions with the result code <code>tesSUCCESS</code> can change non-XRP balances. Only transactions from a validated ledger are final.</li>
<li><a href="https://ripple.com/files/GB-2014-06.pdf" title="Partial Payment Flag Gateway Bulletin">Look out for Partial Payments</a>. If an incoming transaction has a <code>destination_balance_changes</code> field (Ripple-REST) or a <code>meta.delivered_amount</code> field (WebSocket/JSON-RPC), then use that to see how much money <em>actually</em> got delivered to the destination account. Payments with the partial-payment flag enabled are considered "successful" if any non-zero amount is delivered, even miniscule amounts. (The flag is called <code>"partial_payment": true</code> in REST, and <code>tfPartialPayment</code> in WebSocket/JSON-RPC) </li>
<li>Some transactions modify your balances without being payments directly to or from one of your accounts. For example, if ACME sets a nonzero <a href="#transferrate">TransferRate</a>, then ACME's cold wallet's outstanding obligations decrease each time Bob and Charlie exchange ACME issuances. See <a href="#transferrate">TransferRate</a> for more information. </li>
</ul>
<p>To make things simpler for your users, we recommend monitoring for incoming payments to hot wallets and the cold wallet, and treating the two equivalently.</p>
<p>As an added precaution, we recommend comparing the balances of your Ripple cold wallet account with the Ripple-backing funds in your internal accounting system each time there is a new Ripple ledger. The cold wallet shows all outstanding issuances as negative balances, which should match the positive assets you have allocated to Ripple outside the network. If the two do not match up, then you should suspend processing payments in and out of Ripple until you have resolved the discrepancy. </p>
<ul>
<li>Use the <a href="ripple-rest.html#get-account-balances">Get Account Balances method</a> (Ripple-REST) or the <a href="rippled-apis.html#account-lines"><code>account_lines</code> command</a> (rippled) to check your balances.</li>
<li>If you have a <a href="#transferrate">TransferRate</a> set, then your obligations within the Ripple network decrease slightly whenever other users transfer your issuances among themselves.</li>
</ul>
<h2 id="transferrate">TransferRate</h2>
<p>The <em>TransferRate</em> setting (<code>transfer_rate</code> in Ripple-REST) defines a fee to charge for transferring issuances from one Ripple account to another. See <a href="https://ripple.com/knowledge_center/transfer-fees/">Transfer Fees</a> in the Knowledge Center for more information.</p>
<p>The following is an example of a <a href="ripple-rest.html#update-account-settings">Ripple-REST Update Account Settings request</a> to set the TransferRate for a fee of 0.5%.</p>
<p>Request:</p>
<pre><code>POST /v1/accounts/rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn/settings?validated=true
{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"settings": {
"transfer_rate": 1.005
}
}
</code></pre>
<p><em>(<strong>Reminder:</strong> Don't send your secret to a server you do not control.)</em></p>
<p>Response:</p>
<pre><code>200 OK
{
"success": true,
"settings": {
"transfer_rate": 1.005,
"require_destination_tag": false,
"require_authorization": false,
"disallow_xrp": false
},
"hash": "4D098C181DE0A21A55ACBD362E5ADBF24EA2493BD4E56F2137DBAF113327AB4E",
"ledger": "11158720",
"state": "validated"
}
</code></pre>
<p>The field <code>transfer_rate</code> in the <code>settings</code> object should have the value you set. A successful response shows <code>"state": "validated"</code> when the change has been accepted into a validated Ripple ledger.</p>
<h3 id="transferrate-with-hot-and-warm-wallets">TransferRate with Hot and Warm Wallets</h3>
<p>All Ripple Accounts, including the hot wallet, are subject to the TransferRate. If you set a nonzero TransferRate, then you must send extra (to pay the TransferRate) when making payments to users from your hot wallet. You can accomplish this by setting the <code>source_amount</code> plus <code>slippage</code> (Ripple-REST) or the <code>SendMax</code> (rippled) parameters higher than the destination amount.</p>
<p><strong>Note:</strong> The TransferRate does not apply when sending issuances back to the account that created them. The account that created issuances must always accept them at face value on Ripple. This means that users don't have to pay the TransferRate if they send payments to the cold wallet directly, but they do when sending to the hot wallet. (For example, if ACME sets a TransferRate of 1%, a Ripple payment with <code>source_amount</code> and <code>destination_amount</code> of 5 EUR.ACME (and <code>slippage</code> of 0) would succeed if sent to ACME's cold wallet, but it would fail if sent to ACME's hot wallet. The hot wallet payment would only succeed if the <code>source_amount</code> plus <code>slippage</code> was at least 5.05 EUR.ACME.) If you accept payments to both accounts, you may want to adjust the amount you credit users in your external system to make up for fees they paid to redeem with the hot wallet.</p>
<h2 id="sending-payments-to-users">Sending Payments to Users</h2>
<p>When you build an automated system to send payments into Ripple for your users, you must ensure that it constructs payments carefully. Malicious users are constantly trying to find ways to trick a system into paying them more money than it should. If you use Ripple-REST to construct payments, we recommend <strong>not using</strong> the Prepare Payment endpoint for payments from a hot wallet to a user. Sending from a hot wallet to a properly-configured user account requires only a default path, but the Prepare Payment method looks for <em>all</em> possible paths to the destination account, including ones that have a higher <code>source_amount</code> than necessary.</p>
<p>The following template can be used with Ripple-REST's <a href="ripple-rest.html#submit-payment">Submit Payment method</a>. You should also follow the <a href="#reliable-transaction-submission">reliable transaction submission</a> guidelines and persist a copy of the transaction before submitting it.</p>
<pre><code>POST /v1/accounts/&lt;HOT WALLET ADDRESS&gt;/payments
{
"secret": &lt;HOT WALLET SECRET KEY&gt;,
"client_resource_id": &lt;UNIQUE CLIENT RESOURCE ID&gt;,
"payment": {
"source_account": &lt;HOT WALLET ADDRESS&gt;,
"source_amount": {
"value": &lt;DESTINATION AMOUNT, for example "100"&gt;,
"currency": &lt;CURRENCY&gt;,
"issuer": &lt;COLD WALLET ADDRESS&gt;
},
"source_slippage": &lt;DESTINATION AMOUNT * TRANSFER FEE, for example "0.5"&gt;,
"source_tag": &lt;OPTIONAL SOURCE TAG&gt;,
"destination_account": &lt;CUSTOMER ADDRESS&gt;,
"destination_amount": {
"value": &lt;DESTINATION AMOUNT&gt;,
"currency": &lt;CURRENCY&gt;,
"issuer": &lt;COLD WALLET ADDRESS&gt;
}
}
}
</code></pre>
<p><em>Reminder: Don't send your secret to a server you do not control.</em></p>
<p>In particular, note the following features of the payment object:</p>
<ul>
<li>No <code>paths</code> field. The payment will only succeed if it can use a default path, which is preferable. Using less direct paths can become much more expensive.</li>
<li>The <code>issuer</code> of both the <code>source_amount</code> and the <code>destination_amount</code> is the cold wallet. This ensures that the transaction sends and delivers issuances from the cold wallet account, and not from some other gateway.</li>
<li>The <code>source_amount</code> uses the same <code>value</code> as the <code>destination_amount</code>. The <a href="#transferrate">transfer fee</a>, if there is one, is covered by the <code>slippage</code> field.</li>
</ul>
<h2 id="bouncing-payments">Bouncing Payments</h2>
<p>When your hot or cold wallet receives a payment whose purpose is unclear, we recommend that you make an attempt to return the money to its sender. While this is more work than simply pocketing the money, it demonstrates good faith towards customers. You can have an operator bounce payments manually, or create a system to do so automatically. </p>
<p>The first requirement to bouncing payments is <a href="#robustly-monitoring-for-payments">robustly monitoring for incoming payments</a>. You do not want to accidentally refund a user for more than they sent you! (This is particularly important if your bounce process is automated.) The <a href="https://ripple.com/files/GB-2014-06.pdf">Partial Payment Flag Gateway Bulletin</a> explains how to avoid a common problem.</p>
<p>Second, you should send bounced payments as Partial Payments. Since other Ripple users can manipulate the cost of pathways between your accounts, Partial Payments allow you to divest yourself of the full amount without being concerned about exchange rates within the Ripple network. You should publicize your bounced payments policy as part of your terms of use. Send the bounced payment from an automated hot wallet or a human-operated warm wallet.</p>
<p>To send a Partial Payment in Ripple-REST, set the <code>partial_payment</code> field to true in the object returned by the <a href="ripple-rest.html#prepare-payment">Prepare Payment method</a> before submitting it. Set the <code>source_amount</code> to be equal to the <code>destination_amount</code>. and the <code>slippage</code> to <code>"0"</code>. As long as your hot and cold wallets only send the currencies that your cold wallet issues, you should also remove the <code>paths</code> field, which is not necessary for simple payments. You should always specify your cold wallet as the issuer of the funds you want to deliver. (You can specify the issuer in the Prepare Payment request, or modify the <code>issuer</code> field of the <code>destination_amount</code> in the object you get back.)</p>
<p>It is conventional that you take the Source Tag from the incoming payment (<code>source_tag</code> in Ripple-REST) and use that value as the Destination Tag (<code>destination_tag</code> in Ripple-REST) for the return payment.</p>
<p>To prevent two systems from bouncing payments back and forth indefinitely, you can set a new Source Tag for the outgoing return payment. If you receive an unexpected payment whose Destination Tag matches the Source Tag of a return you sent, then do not bounce it back again. </p>
<p>The following is an example of a <a href="ripple-rest.html#submit-payment">Ripple-REST Submit Payment request</a> to send a return payment. Particularly important fields include the <a href="#source-and-destination-tags"><em>destination_tag</em></a>, setting <code>"slippage": "0"</code>, <code>"partial_payment": true</code>, the lack of a <code>"paths"</code> field, and the <code>issuer</code> of the <code>destination_amount</code> being the cold wallet:</p>
<pre><code>POST /v1/accounts/rBEXjfD3MuXKATePRwrk4AqgqzuD9JjQqv/payments?validated=true
{
"secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
"client_resource_id": "fc521224-bdd8-4463-94a4-b26cb760fc92",
"last_ledger_sequence": "10968788",
"max_fee": "1.0",
"payment": {
"source_account": "rBEXjfD3MuXKATePRwrk4AqgqzuD9JjQqv",
"source_tag": "479686",
"source_amount": {
"value": "2",
"currency": "EUR",
"issuer": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"
},
"source_slippage": "0",
"destination_account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
"destination_tag": "803489",
"destination_amount": {
"value": "2",
"currency": "EUR",
"issuer": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"
},
"invoice_id": "",
"partial_payment": true,
"no_direct_ripple": false
}
}
</code></pre>
<p><em>(<strong>Reminder:</strong> Don't send your secret to a server you do not control.)</em></p>
<h2 id="setting-trust-lines-in-ripple-trade">Setting Trust Lines in Ripple Trade</h2>
<p>As part of the <a href="#hot-and-cold-wallets">Hot and Cold Wallets</a> model, each hot or warm wallet must have a trust line to the cold wallet. You can manually set up those trust lines by following these steps in the Ripple Trade client.</p>
<ol>
<li>Log in and go to the <strong>Fund</strong> tab:
<img alt="Fund tab" src="img/connectgateway_01.png"/></li>
<li>Click <strong>Gateways</strong> in the sidebar:
<img alt="Gateways" src="img/connectgateway_02.png"/></li>
<li>Enter the Ripple Name or Ripple Address of the Gateway's <strong>cold wallet</strong>, and click <strong>Save</strong>.
<img alt="Enter gateway's name or address, then save" src="img/connectgateway_03.png"/></li>
<li>Enter the Ripple Trade password, and click <strong>Submit</strong>. (This allows access to send a transaction to the Ripple Network to create the trust line.)
<img alt="Enter password and submit" src="img/connectgateway_04.png"/></li>
<li>When the page shows a green "Gateway connected" box, the transaction to create the trust line has succeeded and the Ripple Network has validated it.
<img alt="Gateway connected" src="img/connectgateway_05.png"/></li>
</ol>
<h2 id="reliable-transaction-submission">Reliable Transaction Submission</h2>
<p>The goal of reliably submitting transactions is to achieve the following two properties in a finite amount of time:</p>
<ul>
<li>Idempotency - Transactions will be processed once and only once, or not at all.</li>
<li>Verifiability - Applications can determine the final result of a transaction.</li>
</ul>
<p>In order to achieve this, there are several steps you can take when submitting transactions:</p>
<ul>
<li>Persist details of the transaction before submitting it.</li>
<li>Use the <code>LastLedgerSequence</code> parameter. (Ripple-REST and ripple-lib do this by default.)</li>
<li>Resubmit a transaction if it has not appeared in a validated ledger whose sequence number is less than or equal to the transaction's <code>LastLedgerSequence</code> parameter.</li>
</ul>
<p>For additional information, consult the <a href="reliable_tx.html">Reliable Transaction Submission</a> guide.</p>
<h2 id="rippletxt-and-host-meta">ripple.txt and host-meta</h2>
<p>The <a href="https://wiki.ripple.com/Ripple.txt">ripple.txt</a> and host-meta standards provide a way to publish information about your gateway so that automated tools and applications can read and understand it.</p>
<p>For example, if you run a validating <code>rippled</code> server, you can use ripple.txt to publish the public key of your validating server. You can also publish information about what currencies your gateway issues, and which Ripple account addresses you control, to protect against impostors or confusion.</p>
<p>We recommend implementing one or both of ripple.txt and host-meta. (In the future, we expect ripple.txt to become obsolete, but not yet.)</p>
</div>
</div>
</div>
@@ -148,6 +699,12 @@
<div class="col-md-3">
<h4>Documentation</h4>
<ul class="footer_links">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippled-apis.html">rippled</a></li>
<li><a href="rippled-setup.html">rippled Setup</a></li>
<li><a href="ripple-rest.html">Ripple-REST</a></li>
@@ -158,12 +715,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</div>
<div class="col-md-3">

File diff suppressed because it is too large Load Diff

View File

@@ -44,6 +44,17 @@
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">References <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -54,6 +65,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</li>
<li class="dropdown">
@@ -64,17 +76,6 @@
<li><a href="gateway_guide.html">Gateway Guide</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Tools <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -153,6 +154,12 @@
<div class="col-md-3">
<h4>Documentation</h4>
<ul class="footer_links">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippled-apis.html">rippled</a></li>
<li><a href="rippled-setup.html">rippled Setup</a></li>
<li><a href="ripple-rest.html">Ripple-REST</a></li>
@@ -163,12 +170,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</div>
<div class="col-md-3">

View File

@@ -57,6 +57,17 @@
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">References <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -67,6 +78,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</li>
<li class="dropdown">
@@ -77,17 +89,6 @@
<li><a href="gateway_guide.html">Gateway Guide</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Tools <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -232,6 +233,12 @@
<div class="col-md-3">
<h4>Documentation</h4>
<ul class="footer_links">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippled-apis.html">rippled</a></li>
<li><a href="rippled-setup.html">rippled Setup</a></li>
<li><a href="ripple-rest.html">Ripple-REST</a></li>
@@ -242,12 +249,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</div>
<div class="col-md-3">

View File

@@ -29,18 +29,12 @@
<!-- syntax selection js -->
<script src="js/multicodetab.js"></script>
<!-- Code to load contents via Flatdoc -->
<script src="vendor/flatdoc/v/0.8.0/legacy.js"></script>
<script src="vendor/flatdoc/v/0.8.0/flatdoc.js"></script>
<!-- Markdown content already parsed+included; just do the code tab stuff -->
<script>
$(".flatdoc-content").empty();
$(".content-root .menubar .menu").empty();
Flatdoc.run({
fetcher: Flatdoc.file('content/paths.md')
});
$(document).on('flatdoc:ready', function() {
$().multicode_tabs();
$(document).ready(function() {
$().multicode_tabs_pandoc();
hljs.initHighlighting();
make_code_expandable();
});
</script>
@@ -57,7 +51,7 @@
</head>
<body role='flatdoc' class='no-literate'>
<body class='no-literate'>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
@@ -71,6 +65,17 @@
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">References <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -81,6 +86,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</li>
<li class="dropdown">
@@ -91,17 +97,6 @@
<li><a href="gateway_guide.html">Gateway Guide</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Tools <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -136,9 +131,122 @@
<div class='content-root'>
<div class='menubar'>
<div class='menu section' role='flatdoc-menu'>
<script type="text/javascript" src="js/jquery.gensidebar.js"></script>
<script type="text/javascript">
</script>
</div>
</div>
<div role='flatdoc-content' class='content'>
<div class='content'>
<h1 id="paths">Paths</h1>
<p>In the Ripple Consensus Ledger, paths define a way for payments to flow through intermediary steps on their way from sender to receiver. Paths enable cross-currency payments by connecting sender and receiver via market makers. Paths also enable complex settlement of offsetting debts.</p>
<p>A single Payment transaction in the Ripple Consensus Ledger can use multiple paths, combining liquidity from different sources to deliver the desired amount. Thus, a transaction includes a <em>path set</em>, which is a collection of possible paths to take. The paths in a path set must start and end with the same currency.</p>
<p>Since XRP can be sent directly to any address, an XRP-to-XRP transaction does not use any paths.</p>
<h2 id="path-steps">Path Steps</h2>
<p>A path is made of steps that connect the sender to the receiver of the payment. Every step is either:</p>
<ul>
<li>Rippling through another account in the same currency</li>
<li>Exchanging currency at an order book</li>
</ul>
<p>Rippling through another account is the process of moving debt around. In the typical case, this involves reducing a gateway's obligation to one party and increasing the gateway's obligation to another party. Rippling can occur between any accounts that are connected by trust lines. See <a href="https://ripple.com/knowledge_center/understanding-the-noripple-flag/">Understanding the NoRipple Flag</a> for more examples of rippling.</p>
<p>In the case of a currency exchange step, the path step specifies which currency to change to, but does not record the state of the orders in the order book. Since the order of transactions is not finalized until a ledger is validated, it is impossible to determine for certain which offers a transaction will execute, until after the transaction has been validated. Consequently, you cannot know in advance which offers a transaction will take: only which order books the transaction will use. (You can make an educated guess, since each transaction takes the best available offers at the time it executes in the final ledger.)</p>
<p>In both types of steps, each intermediate account gains and loses approximately equal value: either a balance ripples from a trust line to another trust line in the same currency, or they exchange currencies according to a previously-placed order. In some cases, the amounts gained and lost may not be exactly equivalent, due to <a href="https://ripple.com/knowledge_center/transfer-fees/">transfer fees</a>, trust line quality, or rounding.</p>
<p><a href="img/paths-examples.png"><img alt="Diagram of three example paths" src="img/paths-examples.png"/></a></p>
<h1 id="technical-details">Technical Details</h1>
<h2 id="pathfinding">Pathfinding</h2>
<p>The <code>rippled</code> API has two methods that can be used for pathfinding. The <a href="rippled-apis.html#ripple-path-find"><code>ripple_path_find</code> command</a> does a one-time lookup of possible path sets. The <a href="rippled-apis.html#path-find"><code>path_find</code> command</a> (WebSocket only) expands on the initial search with follow-up responses whenever a ledger closes or the server finds a better path.</p>
<p>You can have <code>rippled</code> automatically fill in paths when you sign it, by including the <code>build_path</code> field in a request to the <a href="rippled-apis.html#sign"><code>sign</code> command</a> or <a href="rippled-apis.html#sign-and-submit-mode"><code>submit</code> command (sign-and-submit mode)</a>. However, we recommend pathfinding separately and confirming the results prior to signing, in order to avoid surprises. There are no guarantees on how expensive the paths the server finds will be at the time of submission. (Although <code>rippled</code> is designed to search for the cheapest paths possible, it may not always find them. Untrustworthy <code>rippled</code> instances could also be modified to change this behavior for profit.)</p>
<p>Finding paths is a very challenging problem that changes slightly every few seconds as new ledgers are validated, so <code>rippled</code> is not designed to find the absolute best path. Still, you can find several possible paths and estimate the cost of delivering a particular amount.</p>
<h2 id="implied-steps">Implied Steps</h2>
<p>By convention, several steps of a path are implied by the <a href="transactions.html#payment">fields of the Payment transaction</a>: specifically, the <code>Account</code> (sender), <code>Destination</code> (receiver), <code>Amount</code> (currency and amount to be delivered) and <code>SendMax</code> (currency and amount to be sent, if specified). The implied steps are as follows:</p>
<ul>
<li>The first step of a path is always implied to be the sender of the transaction, as defined by the transaction's <code>Account</code> field.</li>
<li>If the transaction includes a <code>SendMax</code> field with an <code>issuer</code> that is not the sender of the transaction, that issuer is implied to be the second step of the path.<ul>
<li>If <code>issuer</code> of the <code>SendMax</code> <em>is</em> the sending account, then the path starts at the sending account, and may use any of that account's trust lines in the given currency. See <a href="transactions.html#special-issuer-values-for-sendmax-and-amount">special values for SendMax and Amount</a> for details.</li>
</ul>
</li>
<li>If the <code>Amount</code> field of the transaction includes an <code>issuer</code> that is not the same as the <code>Destination</code> of the transaction, that issuer is implied to be the second-to-last step of the path.</li>
<li>Finally, last step of a path is always implied to be the receiver of a transaction, as defined by the transaction's <code>Destination</code> field.</li>
</ul>
<h2 id="default-paths">Default Paths</h2>
<p>In addition to any paths that are specified in the transaction, a transaction can also execute along the <em>default path</em>. The default path is the simplest possible way to connect the <a href="#implied-steps">implied steps</a> of the transaction.</p>
<p>The default path could be any of the following:</p>
<ul>
<li>If the transaction is uses only one currency (regardless of issuer), then the default path assumes the payment will ripple through the accounts involved. This path will only work if there are trust lines connecting those accounts.<ul>
<li>If <code>SendMax</code> is omitted, or the <code>issuer</code> of the <code>SendMax</code> is the sender, the default path needs a trust line from the sending <code>Account</code> to the <code>issuer</code> of the destination <code>Amount</code> in order to work.</li>
<li>If the <code>SendMax</code> and <code>Amount</code> have different <code>issuer</code> values, and neither are the sender or receiver, the default path is probably not useful because it would need to ripple across a trust line between the two issuers. Ripple, Inc. typically discourages issuers from trusting one another directly.</li>
</ul>
</li>
<li>For cross-currency transactions, the default path uses the order book between the source currency (as specified in the <code>SendMax</code> field) and the destination currency (as specified in the <code>Amount</code> field).</li>
</ul>
<p>The following diagram enumerates all possible default paths:
<a href="img/paths-default_paths.png"><img alt="Diagram of default paths" src="img/paths-default_paths.png"/></a></p>
<p>You can use the <a href="transactions.html#payment-flags"><code>tfNoDirectRipple</code> flag</a> to disable the default path. In this case, the transaction can only execute using the paths explicitly included in the transaction. Traders can use this option to take arbitrage opportunities.</p>
<h2 id="path-specifications">Path Specifications</h2>
<p>A path set is an array. Each member of the path set is another array that represents an individual <em>path</em>. Each member of a path is an object that specifies the step. A step has the following fields:</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Value</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>account</td>
<td>String - Address</td>
<td>(Optional) If present, this path step represents rippling through the specified account.</td>
</tr>
<tr>
<td>currency</td>
<td>String - currency code</td>
<td>(Optional) If present, this path step represents changing currencies through an order book. The currency specified indicates the new currency.</td>
</tr>
<tr>
<td>issuer</td>
<td>String - Address</td>
<td>(Optional) If the path step represents changing currencies through an order book, this field indicates the issuer of the new currency. This field is not present when changing to XRP.</td>
</tr>
<tr>
<td>type</td>
<td>Integer</td>
<td><strong>DEPRECATED</strong> (Optional) An indicator of which other fields are present.</td>
</tr>
<tr>
<td>type_hex</td>
<td>String</td>
<td><strong>DEPRECATED</strong>: (Optional) A hexadecimal representation of the <code>type</code> field.</td>
</tr>
</tbody>
</table>
<p>The <code>type</code> field, used for the binary serialization of a path set, is actually constructed through bitwise operations on a single integer. The bits are defined as follows:</p>
<table>
<thead>
<tr>
<th>Value (Hex)</th>
<th>Value (Decimal)</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>0x01</td>
<td>1</td>
<td>A change of account (rippling): the <code>account</code> field is present.</td>
</tr>
<tr>
<td>0x10</td>
<td>16</td>
<td>A change of currency: the <code>currency</code> field is present.</td>
</tr>
<tr>
<td>0x20</td>
<td>32</td>
<td>A change of issuer: the <code>issuer</code> field is present.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
@@ -148,6 +256,12 @@
<div class="col-md-3">
<h4>Documentation</h4>
<ul class="footer_links">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippled-apis.html">rippled</a></li>
<li><a href="rippled-setup.html">rippled Setup</a></li>
<li><a href="ripple-rest.html">Ripple-REST</a></li>
@@ -158,12 +272,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</div>
<div class="col-md-3">

View File

@@ -29,18 +29,12 @@
<!-- syntax selection js -->
<script src="js/multicodetab.js"></script>
<!-- Code to load contents via Flatdoc -->
<script src="vendor/flatdoc/v/0.8.0/legacy.js"></script>
<script src="vendor/flatdoc/v/0.8.0/flatdoc.js"></script>
<!-- Markdown content already parsed+included; just do the code tab stuff -->
<script>
$(".flatdoc-content").empty();
$(".content-root .menubar .menu").empty();
Flatdoc.run({
fetcher: Flatdoc.file('content/reliable_tx.md')
});
$(document).on('flatdoc:ready', function() {
$().multicode_tabs();
$(document).ready(function() {
$().multicode_tabs_pandoc();
hljs.initHighlighting();
make_code_expandable();
});
</script>
@@ -57,7 +51,7 @@
</head>
<body role='flatdoc' class='no-literate'>
<body class='no-literate'>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
@@ -71,6 +65,17 @@
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">References <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -81,6 +86,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</li>
<li class="dropdown">
@@ -91,17 +97,6 @@
<li><a href="gateway_guide.html">Gateway Guide</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Tools <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -136,9 +131,826 @@
<div class='content-root'>
<div class='menubar'>
<div class='menu section' role='flatdoc-menu'>
<script type="text/javascript" src="js/jquery.gensidebar.js"></script>
<script type="text/javascript">
</script>
</div>
</div>
<div role='flatdoc-content' class='content'>
<div class='content'>
<h1 id="reliable-transaction-submission">Reliable Transaction Submission</h1>
<p>Gateways and back-end applications should use the best practices described here to ensure that transactions are validated or rejected in a verifiable and timely fashion. Transactions should be submitted to trusted (locally operated) rippled servers.</p>
<p>The best practices detailed in this document allow applications to submit transactions to the Ripple network while achieving:</p>
<ol>
<li><a href="https://en.wikipedia.org/wiki/Idempotence">Idempotency</a> - Transactions will be processed once and only once, or not at all.</li>
<li>Verifiability - Applications can determine the final result of a transaction.</li>
</ol>
<p>Applications which fail to implement best practices are at risk of the following errors:</p>
<ol>
<li>Submitting transactions which are inadvertently never executed.</li>
<li>Mistaking provisional transaction results for their final, immutable results.</li>
<li>Failing to find authoritative results of transactions previously applied to the ledger.</li>
</ol>
<p>These types of errors can potentially lead to serious problems. For example, an application that fails to find a prior successful payment transaction might erroneously submit another transaction, duplicating the original payment. This underscores the importance that applications base their actions on authoritive transaction results, using the techniques described in this document.</p>
<h2 id="background">Background</h2>
<p>The Ripple protocol provides a ledger shared across all nodes in the network. Through a process of consensus and validation, the network agrees on order in which transactions are applied to (or omitted from) the ledger. <!--See Ripple Ledger Consensus and Validation[b] for an overview of this process.--></p>
<p>Well-formed transactions submitted to trusted Ripple network nodes are usually validated or rejected in a matter of seconds. There are cases, however, in which a well-formed transaction is neither validated nor rejected this quickly. One specific case can occur if the global <a href="transactions.html#transaction-fees">transaction fee</a> increases after an application sends a transaction. If the fee increases above what has been specified in the transaction, the transaction will not be included in the next validated ledger. If at some later date the global base fee decreases, the transaction may become viable again. If the transaction does not include expiration, there is no limit to how much later this can occur.</p>
<p>Applications face additional challenges, in the event of power or network loss, ascertaining the status of submitted transactions. Applications must take care both to properly submit a transaction and later to properly ascertain its authoritative results.</p>
<h3 id="transaction-timeline">Transaction Timeline</h3>
<p>Ripple provides several APIs for submitting transactions (<a href="rippled-apis.html">rippled</a>, ripple-lib, <a href="ripple-rest.html">Ripple-REST</a>). Regardless of the API used, the transaction is applied to the ledger as follows.</p>
<ol>
<li>A transaction is created and signed by account owner.</li>
<li>That transaction is submitted to the network as a candidate transaction.</li>
<li>Malformed or nonsensical transactions are rejected immediately.</li>
<li>Well formed transactions may provisionally succeed, then later fail.</li>
<li>Well formed transactions may provisionally fail, then later succeed.</li>
<li>Through consensus and validation, the transaction is applied to the ledger. Even some failed transactions are applied in order to claim a fee for being propagated through the network.</li>
<li>The validated ledger includes the transaction, and its effects are reflected in the ledger state.</li>
<li>Transaction results are no longer provisional, success or failure is now final and immutable.</li>
</ol>
<p><em>Note:</em> When submitting a transaction via rippled or ripple-lib, a successful status code returned from a submit command indicates the rippled server has received the candidate transaction, and does not indicate the transaction will be finally applied to the ledger.</p>
<p>Ripple APIs may return provisional results based on candidate transactions. Applications must not confuse these with the final, <em>immutable</em>, results of a transaction. Immutable results are found only in validated ledgers. Applications may need to query the status of a transaction repeatedly, until the ledger containing the transaction results is validated.</p>
<p>While applying transactions, Ripple network nodes work with the <em>last validated ledger</em>, a snapshot of the ledger state based on transactions the entire network has validated. The process of consensus and validation apply a set of new transactions to the last validated ledger, resulting in a new validated ledger. This new validated ledger instance and the ones that preceded it comprise the ledger history. Each of these validated ledger instances has a sequence number, which is one greater than the sequence number of the preceding instance.</p>
<h3 id="lastledgersequence">LastLedgerSequence</h3>
<p><a href="transactions.html#lastledgersequence"><code>LastLedgerSequence</code></a> is an optional parameter of all transactions. This instructs the Ripple network that a transaction must be validated on or before a specific ledger instance. The transaction will never be included in a ledger instance with a higher sequence number.</p>
<p>Use the <code>LastLedgerSequence</code> parameter to prevent undesirable cases where a transaction is not promptly validated yet could become viable at some point in the future. Gateways and other back-end applications should specify the <code>LastLedgerSequence</code> parameter on every transaction. Automated processes should use a value of 4 greater than the sequence number of the last validated ledger[1] to ensure that a transaction is validated or rejected in a predictable and timely fashion.</p>
<p>Applications using rippled APIs should explicitly specify a <code>LastLedgerSequence</code> when submitting transactions. When using ripple-lib or Ripple-REST, a <code>LastLedgerSequence</code> is automatically included. Applications using those APIs are recommended to use the <code>LastLedgerSequence</code> calculated by the API.</p>
<h2 id="best-practices">Best Practices</h2>
<h3 id="reliable-transactions-submission">Reliable Transactions Submission</h3>
<p>Applications submitting transactions should employ the following practices in order to submit reliably even in the event that a process dies or other failure occurs. Application transaction results must be verified so that applications can act on the final, validated results.</p>
<p>Submission and verification are two separate procedures which may be implemented using the logic described in this document.</p>
<ol>
<li>Submission - The transaction is submitted to the network and a provisional result is returned.</li>
<li>Verification - The authoritative result is determined by examining validated ledgers.</li>
</ol>
<h3 id="submission">Submission</h3>
<p>[Persist](https://en.wikipedia.org/wiki/Persistence_(computer_science%29) details of the transaction prior to submission, in case of power failure or network failure before submission completes. On restart, the persisted values make it possible to verify the status of the transaction.</p>
<p>The submission process:</p>
<ol>
<li>Construct and sign the transaction</li>
<li>Include <code>LastLedgerSequence</code> parameter</li>
<li>Persist the transaction details, saving:</li>
<li>Transaction hash</li>
<li><code>LastLedgerSequence</code></li>
<li>Account ID and sequence number</li>
<li>Application-specific data, as needed</li>
<li>Submit the transaction</li>
</ol>
<h3 id="verification">Verification</h3>
<p>During normal operation, applications may check the status of submitted transactions by their hashes; or, depending on the API used, receive notifications when transactions have been validated (or failed). This normal operation may be interrupted, for example by network failures or power failures. In case of such interruption applications need to reliably verify the status of transactions which may or may not have been submitted to the network prior to the interruption.</p>
<p>On restart, or the determination of a new last validated ledger (pseudocode):</p>
<pre><code>For each persisted transaction without validated result:
Query transaction by hash
If (result appears in validated ledger)
Persist the final result
If (result code is tesSUCCESS)
Application may act based on successful transaction
Else
Application may act based on failure
Maybe resubmit with new LastLedgerSequence and Fee
Else if (LastLedgerSequence &gt; newest validated ledger)
Wait for more ledgers to be validated
Else
If (server has contiguous ledger history up to and
including the ledger identified by LastLedgerSequence)
The transaction failed
Submit a new transaction, if appropriate for application
Else
Repeat submission of original transaction
</code></pre>
<h2 id="technical-application">Technical Application</h2>
<p>In order to implement the transaction submission and verification best practices, applications need to perform the following actions.</p>
<ul>
<li>Determine the signing account's next sequence number</li>
<li>Each transaction has an account-specific sequence number. This guarantees the order in which transactions signed by an account are executed and makes it safe to resubmit a transaction without danger of the transaction being applied to the ledger more than once.</li>
<li>Decide on a <code>LastLedgerSequence</code></li>
<li>A transaction's <code>LastLedgerSequence</code> is calculated from the last validated ledger sequence number.</li>
<li>Construct and sign the transaction</li>
<li>The details of a signed transaction are persisted before submission.</li>
<li>Submit the transaction</li>
<li>Initial results are provisional and subject to change.</li>
<li>Determine the final result of a transaction</li>
<li>Final results are an immutable part of the ledger history.</li>
</ul>
<p>An application's means of performing these actions depends on the ripple API the application uses. These interfaces may be any of:</p>
<ol>
<li><a href="rippled-apis.html">rippled</a></li>
<li><a href="ripple-rest.html">ripple-rest</a></li>
<li><a href="https://github.com/ripple/ripple-lib/">ripple-lib</a></li>
</ol>
<h3 id="rippled-submitting-and-verifying-transactions">rippled - Submitting and Verifying Transactions</h3>
<h4 id="determine-the-account-sequence">Determine the Account Sequence</h4>
<p>rippled provides the <a href="rippled-apis.html#account-info">account_info</a> method to learn an account's sequence number in the last validated ledger.</p>
<p>Request:</p>
<pre><code>{
"method": "account_info",
"params": [
{
"account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"ledger": "validated"
}
]
}
</code></pre>
<p>Response:</p>
<pre><code>{
"result": {
"validated": true,
"status": "success",
"ledger_index": 10266396,
"account_data": {
"index": "96AB97A1BBC37F4F8A22CE28109E0D39D709689BDF412FE8EDAFB57A55E37F38",
"Sequence": 4,
"PreviousTxnLgrSeq": 9905632,
"PreviousTxnID": "CAEE0E34B3DB50A7A0CA486E3A236513531DE9E52EAC47CE4C26332CC847DE26",
"OwnerCount": 2,
"LedgerEntryType": "AccountRoot",
"Flags": 0,
"Balance": "49975988",
"Account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W"
}
}
}
</code></pre>
<p>In this example, the account's sequence is <strong>4</strong> (note <code>"Sequence": 4</code>, in <code>"account_data"</code>) as of the last validated ledger (note <code>"ledger": "validated"</code> in the request, and <code>"validated": "true"</code> in the response).</p>
<p>If an application were to submit three transactions signed by this account, they would use sequence numbers 4, 5, and 6. An application should keep a running account sequence number in order to submit multiple transactions without waiting for validation of each one.</p>
<h4 id="determine-the-last-validated-ledger">Determine the Last Validated Ledger</h4>
<p><code>rippled</code> provides the <a href="rippled-apis.html#server-state">server_state</a> command which returns the ledger sequence number of the last validated ledger.</p>
<p>Request:</p>
<pre><code>{
"id": "client id 1",
"method": "server_state"
}
</code></pre>
<p>Response:</p>
<pre><code>{
"result": {
"status": "success",
"state": {
"validation_quorum": 3,
"validated_ledger": {
"seq": 10268596,
"reserve_inc": 5000000,
"reserve_base": 20000000,
"hash": "0E0901DA980251B8A4CCA17AB4CA6C3168FE83FA1D3F781AFC5B9B097FD209EF",
"close_time": 470798600,
"base_fee": 10
},
"server_state": "full",
"published_ledger": 10268596,
"pubkey_node": "n9LGg37Ya2SS9TdJ4XEuictrJmHaicdgTKiPJYi8QRSdvQd3xMnK",
"peers": 58,
"load_factor": 256000,
"load_base": 256,
"last_close": {
"proposers": 5,
"converge_time": 3004
},
"io_latency_ms": 2,
"fetch_pack": 10121,
"complete_ledgers": "10256331-10256382,10256412-10268596",
"build_version": "0.26.4-sp3-private"
}
}
}
</code></pre>
<p>In this example the last validated ledger sequence number is 10268596 (found under <code>result.state.validated_ledger</code> in the response). Note also this example indicates a gap in ledger history. The server used here would not be able to provide information about the transactions applied during that gap (ledgers 10256383 through 10256411). If configured to do so, the server will eventually retrieve that portion of the ledger history.</p>
<h4 id="construct-the-transaction">Construct the Transaction</h4>
<p><code>rippled</code> provides the <a href="rippled-apis.html#sign">RPC sign</a> to prepare a transaction for submission. This method requires an account secret, which should only be passed to trusted rippled instances. This example issues 10 FOO (a made-up currency) from a gateway to another ripple account.</p>
<p>Request:</p>
<pre><code>{
"method": "sign",
"params": [
{
"offline": true,
"secret": "sssssssssssssssssssssssssssss",
"tx_json": {
"Account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"Sequence": 4,
"LastLedgerSequence": 10268600,
"Fee": 10000,
"Amount": {
"currency": "FOO",
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"value": "10"
},
"Destination": "rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM",
"TransactionType": "Payment"
}
}
]
}
</code></pre>
<p>Notice the application specifies the account sequence <code>"Sequence": 4</code>, learned from an earlier call to <code>account_info</code>, to avoid <code>tefPAST_SEQ</code> errors.</p>
<p>Notice also the <code>LastLedgerSequence</code> based on the last validated ledger our application learned from <code>server_state</code>. The recommendation for backend applications is to use <em>(last validated ledger sequence + 4)</em>. Alternately, use a value of <em>(current ledger + 3)</em>. If <code>LastLedgerSequence</code> is miscalculated and less than the last validated ledger, the transaction will fail with <code>tefMAX_LEDGER</code> error.</p>
<p>Response:</p>
<pre><code>{
"result": {
"tx_json": {
"hash": "395C313F6F11F70FEBAF3785529A6D6DE3F44C7AF679515A7EAE22B30146DE57",
"TxnSignature": "304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C753",
"TransactionType": "Payment",
"SigningPubKey": "0267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC5",
"Sequence": 4,
"LastLedgerSequence": 10268600,
"Flags": 2147483648,
"Fee": "10000",
"Destination": "rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM",
"Amount": {
"value": "10",
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"currency": "FOO"
},
"Account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W"
},
"tx_blob": "12000022800000002400000004201B009CAFB861D4C38D7EA4C68000000000000000000000000000464F4F0000000000AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A68400000000000271073210267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC57446304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C7538114AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A831438BC6F9F5A6F6C4E474DB0D59892E90C2C7CED5C",
"status": "success"
}
}
</code></pre>
<p>Applications should persist the transaction's hash before submitting. The result of the <code>sign</code> method includes the hash under <code>tx_json</code>.</p>
<h4 id="submit-the-transaction">Submit the transaction</h4>
<p><code>rippled</code> provides the <a href="rippled-apis.html#submit"><code>submit</code> method</a>, allowing us to submit the signed transaction. The <code>tx_blob</code> parameter was returned from the <code>sign</code> method.</p>
<p>Request:</p>
<pre><code>{
"method": "submit",
"params": [
{
"tx_blob": "12000022800000002400000004201B009CAFB861D4C38D7EA4C68000000000000000000000000000464F4F0000000000AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A68400000000000271073210267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC57446304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C7538114AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A831438BC6F9F5A6F6C4E474DB0D59892E90C2C7CED5C"
}
]
}
</code></pre>
<p>Response:</p>
<pre><code>{
"result": {
"tx_json": {
"hash": "395C313F6F11F70FEBAF3785529A6D6DE3F44C7AF679515A7EAE22B30146DE57",
"TxnSignature": "304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C753",
"TransactionType": "Payment",
"SigningPubKey": "0267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC5",
"Sequence": 4,
"LastLedgerSequence": 10268600,
"Flags": 2147483648,
"Fee": "10000",
"Destination": "rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM",
"Amount": {
"value": "10",
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"currency": "FOO"
},
"Account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W"
},
"tx_blob": "12000022800000002400000004201B009CAFB861D4C38D7EA4C68000000000000000000000000000464F4F0000000000AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A68400000000000271073210267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC57446304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C7538114AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A831438BC6F9F5A6F6C4E474DB0D59892E90C2C7CED5C",
"status": "success",
"engine_result_message": "The transaction was applied.",
"engine_result_code": 0,
"engine_result": "tesSUCCESS"
}
}
</code></pre>
<p>This a <strong>preliminary</strong> result. Final results are only available from validated ledgers. The lack of a <code>"validated": true</code> field indicates that this is <strong>not an immutable result</strong>.</p>
<h4 id="verify-the-transaction">Verify the Transaction</h4>
<p>The transaction hash, generated when the transaction was signed, is passed to the <a href="rippled-apis.html#tx"><code>tx</code> method</a> to retrieve the result of a transaction.</p>
<p>Request:</p>
<pre><code>{
"method": "tx",
"params": [
{
"transaction": "395C313F6F11F70FEBAF3785529A6D6DE3F44C7AF679515A7EAE22B30146DE57",
"binary": false
}
]
}
</code></pre>
<p>Response:</p>
<pre><code>{
"result": {
"validated": true,
"status": "success",
"meta": {
"TransactionResult": "tesSUCCESS",
"TransactionIndex": 2,
"AffectedNodes": [...]
},
"ledger_index": 10268599[d],
"inLedger": 10268599,
"hash": "395C313F6F11F70FEBAF3785529A6D6DE3F44C7AF679515A7EAE22B30146DE57",
"date": 470798270,
"TxnSignature": "304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C753",
"TransactionType": "Payment",
"SigningPubKey": "0267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC5",
"Sequence": 4,
"LastLedgerSequence": 10268600,
"Flags": 2147483648,
"Fee": "10000",
"Destination": "rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM",
"Amount": {
"value": "10",
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"currency": "FOO"
},
"Account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W"
}
}
</code></pre>
<p>This example response shows <code>"validated": true</code>, indicating the transaction has been included in a validated ledger and therefore the result of the transaction is immutable. Further, the metadata includes <code>"TransactionResult": "tesSUCCESS"</code>, indicating the transaction was applied to the ledger.</p>
<p>If the response does not include <code>"validated": true</code>, the result is provisional and subject to change. To retrieve a final result, applications must invoke the <code>tx</code> method again, allowing enough time for the network to validate subsequent ledger instances. It may be necessary to wait for the ledger specified in <code>LastLedgerSequence</code> to be validated, although if the transaction is included in an earlier validated ledger the result become immutable at that time.</p>
<h4 id="verify-missing-transaction">Verify Missing Transaction</h4>
<p>Applications must handle cases where a call to the <a href="rippled-apis.html#tx"><code>tx</code> method</a> returns a <code>txnNotFound</code> error.</p>
<pre><code>{
"result": {
"status": "error",
"request": {
"transaction": "395C313F6F11F70FEBAF3785529A6D6DE3F44C7AF679515A7EAE22B30146DE56",
"command": "tx",
"binary": false
},
"error_message": "Transaction not found.",
"error_code": 24,
"error": "txnNotFound"
}
}
</code></pre>
<p>The <code>txnNotFound</code> result code occurs in cases where the transaction has failed to be included in any ledger. However, it could also occur when a rippled instance does not have a complete ledger history, or if the transaction has not yet propagated to the rippled instance. Applications should make further queries to determine how to react.</p>
<p>The <a href="rippled-apis.html#server-state"><code>server_state</code> method</a> (used earlier to determine the last validated ledger) indicates how complete the ledger history is, under <code>result.state.complete_ledgers</code>.</p>
<pre><code>{
"result": {
"status": "success",
"state": {
"validation_quorum": 3,
"validated_ledger": {
"seq": 10269447,
"reserve_inc": 5000000,
"reserve_base": 20000000,
"hash": "D05C7ECC66DD6F4FEA3A6394F209EB5D6824A76C16438F562A1749CCCE7EAFC2",
"close_time": 470802340,
"base_fee": 10
},
"server_state": "full",
"pubkey_node": "n9LJ5eCNjeUXQpNXHCcLv9PQ8LMFYy4W8R1BdVNcpjc1oDwe6XZF",
"peers": 84,
"load_factor": 256000,
"load_base": 256,
"last_close": {
"proposers": 5,
"converge_time": 2002
},
"io_latency_ms": 1,
"complete_ledgers": "10256331-10256382,10256412-10269447",
"build_version": "0.26.4-sp3-private"
}
}
}
</code></pre>
<p>Our example transaction specified <code>LastLedgerSequence</code> 10268600, based on the last validated ledger at the time, plus four. So to determine whether our missing transaction has permanently failed, our rippled server must have ledgers 10268597 through 10268600. If the server has those validated ledgers in its history, <strong>and</strong> <code>tx</code> returns <code>txnNotFound</code>, then the transaction has failed and will never be included in any ledger. in this case, application logic may dictate building and submitting a replacement transaction with the same account sequence and updated <code>LastLedgerSequence</code>.</p>
<p>The server state may indicate a last validated ledger sequence number less than the specified <code>LastLedgerSequence</code>. If so, the <code>txnNotFound</code> indicates either (a) the submitted transaction failed to be distributed to the network, or (b) the transaction has been distributed to the network but has not yet been processed. To handle the former case, applications may submit again the same signed transaction. Because the transaction has a unique account sequence number, it will be processed at most once.</p>
<p>Finally the server state might indicate one or more gaps in the transaction history. The <code>completed_ledgers</code> field shown in the response above indicates that ledgers 10256383 through 10256411 are missing from this rippled instance. Our example transaction can only appear in ledgers 10268597 - 10268600 (based on when it was submitted and <code>LastLedgerSequence</code>), so the gap shown here is not relevant. However, if the gap indicated a ledger in that range was missing, then an application would need to query another rippled server (or wait for this one to retrieve the missing ledgers) in order to determine that a <code>txnNotFound</code> result is immutable.</p>
<h3 id="ripple-rest-submitting-and-verifying-payments">Ripple-REST - Submitting and Verifying Payments</h3>
<p>The <a href="ripple-rest.html">Ripple-REST API</a> provides an interface to Ripple via a <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">RESTful API</a>. It provides robust payment submission features, which include referencing payments by client provided identifiers, re-submitting payments in response to some errors, and automatically calculating transaction parameters such as account sequence, <code>LastLedgerSequence</code>, and fee.</p>
<p>This examples that follow refer to Ripple-REST API for <em>payments</em>. The REST methods for setting trust lines and modifying account settings does not follow the same pattern. See <a href="https://ripplelabs.atlassian.net/browse/RLJS-126">RLJS-126</a> for additional details.</p>
<h4 id="construct-the-transaction-1">Construct the Transaction</h4>
<p>In Ripple-REST, a GET request retrieves the path options for a payment. The following example issues 10 FOO (a made-up currency) from one account to another.</p>
<pre><code>GET /v1/accounts/rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W/payments/paths/rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM/10+FOO+rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W
</code></pre>
<p>The response (below) indicates one payment path exists. (The <code>payments[0].paths</code> is present, and empty, when issuing currency directly.)</p>
<pre><code>{
"payments": [
{
"no_direct_ripple": false,
"partial_payment": false,
"paths": "[]",
"invoice_id": "",
"destination_amount": {
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"currency": "FOO",
"value": "10"
},
"destination_tag": "",
"destination_account": "rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM",
"source_slippage": "0",
"source_amount": {
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"currency": "FOO",
"value": "10"
},
"source_tag": "",
"source_account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W"
}
],
"success": true
}
</code></pre>
<h4 id="submit-the-payment-transaction">Submit the Payment Transaction</h4>
<p>Applications should persist a record of a transaction before submitting it, in order to recover from a catastrophic failure such as a power outage. Ripple-REST accepts a <code>client_resource_id</code> which allows an application to look up a transaction. For this example, let's say the application saves the details of the transaction to its payments table with ID 42, so the application creates <code>client_resource_id</code> "payment-42".</p>
<p>Before submitting, applications should persist the <code>client_resource_id</code>, transaction type, and source account.</p>
<p>Request:</p>
<pre><code>POST /v1/accounts/:usGate/payments
Content-Type: application/json
{
"secret": "sssssssssssssssssssssss",
"client_resource_id": "payment-42",
"max_fee": ".1",
"payment": {
"no_direct_ripple": false,
"partial_payment": false,
"paths": "[]",
"invoice_id": "",
"destination_amount": {
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"currency": "FOO",
"value": "10"
},
"destination_tag": "",
"destination_account": "rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM",
"source_slippage": "0",
"source_amount": {
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"currency": "FOO",
"value": "10"
},
"source_tag": "",
"source_account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W"
}
}
</code></pre>
<p><strong><em>Note: only submit your account secret to a trusted Ripple-REST server.</em></strong></p>
<p>Response:</p>
<pre><code>{
success: true,
client_resource_id: 'payment-42',
status_url: 'http://127.0.0.1:5990/v1/accounts/rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W/payments/payment-42' }
</code></pre>
<p>The payment result remains provisional at this point. Ripple-REST returns a <code>status_url</code> where we can eventually learn the final result of the transaction. The <code>status_url</code> is based on the <code>client_resource_id</code> persisted by the application before submission, so the application can construct the <code>status_url</code> even in the event of a power failure before this response is received.</p>
<h4 id="verify-the-payment-transaction">Verify the Payment Transaction</h4>
<p>A GET request to the transaction's <code>status_url</code> retrieves the result.</p>
<p>Request:</p>
<pre><code>GET /v1/accounts/rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W/payments/payment-42
</code></pre>
<p>Response:</p>
<pre><code>{
"payment": {
"destination_balance_changes": [
{
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"currency": "FOO",
"value": "10"
}
],
"source_balance_changes": [
{
"issuer": "",
"currency": "XRP",
"value": "-0.012"
},
{
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"currency": "FOO",
"value": "-10"
}
],
"fee": "0.012",
"timestamp": "2014-12-02T23:10:10.000Z",
"hash": "DDF9F6E4DC64A1CB056570170FC06B2CBC2701CB500E44AC730BF8C868F6AA15",
"ledger": "10286112",
"result": "tesSUCCESS",
"state": "validated",
"direction": "outgoing",
"partial_payment": false,
"no_direct_ripple": false,
"paths": "[]",
"invoice_id": "",
"destination_amount": {
"value": "10",
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"currency": "FOO"
},
"destination_tag": "",
"destination_account": "rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM",
"source_slippage": "0",
"source_amount": {
"value": "10",
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"currency": "FOO"
},
"source_tag": "",
"source_account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W"
},
"success": true
}
</code></pre>
<p>In this example, <code>payment.state: "validated"</code> indicates the transaction is in a validated ledger. Therefore the results are final and immutable. Any other value for <code>payment.state</code> indicates provisional results, an application must check again later to determine final results.</p>
<p>In the preceeding example, <code>payment.result: "tesSUCCESS"</code> (along with the <code>"validated"</code> state) indicates the payment has been delivered. Any other result code indicates the transaction did not succeed, and application logic may dictate constructing and submitting a new transaction to perform the desired operation.</p>
<h4 id="verify-missing-or-failed-payment-transaction">Verify Missing or Failed Payment Transaction</h4>
<p>When a GET request to the transaction's <code>status_url</code> returns an error, an application must determine whether the final result of a transaction is failure.</p>
<p>Request:</p>
<pre><code>GET /v1/accounts/rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W/payments/payment-42
</code></pre>
<p>Response:</p>
<pre><code>{
"message": "Transaction not found.",
"error": "txnNotFound",
"error_type": "transaction",
"success": false
}
</code></pre>
<p><em>Note:</em> The <code>txnNotFound</code> error shown above is the current behavior of ripple-REST, but will change in order to make it easier to determine the status of transactions in this case. Follow <a href="https://ripplelabs.atlassian.net/browse/RLJS-163">RLJS-163</a> for updates regarding this behavior.</p>
<p>The <code>txnNotFound</code> error in this example indicates the transaction was received by Ripple-REST, but has failed to appear in a validated ledger. This could be caused certain transaction errors, or possibly because Ripple-REST lost power or network before submitting the transaction to the network. Application logic may dictate constructing and submitting a replacement transaction.</p>
<p>Another error response:</p>
<pre><code>{
"message": "A transaction hash was not supplied and there were no entries matching the client_resource_id.",
"error": "Transaction not found",
"error_type": "invalid_request",
"success": false
}
</code></pre>
<p>This <code>"Transaction not found"</code> error indicates the transaction was not received by Ripple-REST, and therefore never sent to the network. Again, application logic may dictate constructing and submitting a replacement transaction.</p>
<h3 id="ripple-lib-submitting-and-verifying-transactions">ripple-lib - Submitting and Verifying Transactions</h3>
<p><a href="https://github.com/ripple/ripple-lib">ripple-lib</a> provides a Javascript API for Ripple in Node.js and web browsers. It provides features for robust transaction submission which including automatically calculating account sequence numbers and <code>LastLedgerSequence</code>.</p>
<h4 id="construct-the-transaction-2">Construct the Transaction</h4>
<p>ripple-lib provides a high level API for creating transactions. In this example, a payment:</p>
<pre><code>var tx = remote.createTransaction('Payment', {
account: rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W',
destination: rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM',
amount: 10/FOO/rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W'
});
</code></pre>
<p>Note that ripple-lib will automatically provide additional details before the transaction is signed and submitted. These details include an account transaction Sequence, <code>LastLedgerSequence</code> and Fee.</p>
<p>Before submitting, applications should persist the transaction details, so that status may be verified in the event of a failure. Applications have an opportunity to do this by implementing <a href="https://github.com/ripple/ripple-lib/blob/61afca2337927a4f331ae02770ccdad5d9bdef17/docs/REFERENCE.md#transaction-events">transaction event handlers</a>. The <code>presubmit</code> event handler is appropriate for saving data before a transaction is submitted to the network. During normal operation, a <code>state</code> event is emitted whenever the transaction status changes, including final validation.</p>
<p>The example implementation (below) of a these event handlers simply logs some information about the transaction, in order to show some of the data available to applications. Live applications should implement handlers to <strong>synchronously</strong> persist transactions details, and should throw an error if unable to save the data.</p>
<pre><code>// The 'presubmit' handler receives events before the transaction
// is submitted to the network; also before re-submit attempts.
tx.on('presubmit', function(data) {
console.log('- Presubmit Event Handler -');
// Log information about the transaction.
console.log(this.summary());
// Applications should persist transaction data syncronously,
// before returning from this event handler.
});
// The 'state' handler receives events after any state change, including...
tx.on('state', function(state) {
console.log('- State Event Hander: ' + state + ' -');
// Log information about the transaction.
console.log(this.summary());
// Applications should persist updated transaction state.
});
</code></pre>
<h4 id="submit-the-transaction-1">Submit the Transaction</h4>
<p>ripple-lib provides <code>Transaction.submit()</code> to both sign and submit a transaction. In order to sign, an application must first call <code>Remote.setSecret(&lt;account&gt;, &lt;secret&gt;)</code>. Take care to configure ripple-lib to use local signing, as secrets should only be shared with trusted ripple servers.</p>
<pre><code>tx.submit(function(err, res) {
if (err) {
console.log('- Transaction Submit Callback Error -');
console.log(err);
}
else {
console.log('- Transaction Submit Callback -');
console.log(util.inspect(res));
}
});
</code></pre>
<p>ripple-lib emits events as it processes the transaction. The presubmit event allows application to persist the transaction hash and other details before it is sent to the network.</p>
<pre><code>- Presubmit Event Handler -
{ tx_json:
{ Flags: 2147483648,
TransactionType: 'Payment',
Account: 'rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W',
Amount:
{ value: '10',
currency: 'FOO',
issuer: 'rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W' },
Destination: 'rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM',
Fee: '10000',
Sequence: 11,
SigningPubKey: '0267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC5',
LastLedgerSequence: 10320276,
TxnSignature: '3045022100A61FA3AF1569BC4B1D2161F0055CF9760F8AC473ED374A98FE7E77099B8C63AD02200FF2A59CE4722FC246652125D3A13928392E7D0C5AC073314A573E93BF594856' },
clientID: undefined,
submittedIDs: [ '2120732F77002A9138DE110408EA06C2064F032BC9D44A28C46267C607594203' ],
submissionAttempts: 0,
submitIndex: 10320268,
initialSubmitIndex: 10320268,
lastLedgerSequence: 10320276,
state: 'unsubmitted',
server: undefined,
finalized: false }
</code></pre>
<p>Note in the preceeding example log output, <code>tx.state: 'unsubmitted'</code> indicates the transaction is not yet sent to the network. <code>tx.submittedIDs</code> includes the transaction hash, a unique identifier that applications should persist in order to later verify the status of the transaction. ripple-lib has automatically calculated <code>tx.tx_json.Sequence</code> and <code>tx.tx_json.LastLedgerSequence</code>.</p>
<p>ripple-lib will re-submit a transaction in response to certain errors. Prior to each submission the <code>presubmit</code> event provides up-to-date information about the transaction, which applications should persist.</p>
<pre><code>- Presubmit Event Handler -
{ tx_json:
{ Flags: 2147483648,
TransactionType: 'Payment',
Account: 'rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W',
Amount:
{ value: '10',
currency: 'FOO',
issuer: 'rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W' },
Destination: 'rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM',
Fee: '10000',
Sequence: 11,
SigningPubKey: '0267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC5',
LastLedgerSequence: 10320278,
TxnSignature: '3045022100D5AC197A9EB35E86B70B4A02C19EC0120C1D6A539B1FE58C364322067E040AE102205EFE429FF40FAFAAAC58B653DCD25642D8A50E222C2D244105F43CEBAAC7DC89' },
clientID: undefined,
submittedIDs:
[ 'AD070F312DE0EB8AB64DDA0C512A4A1E9ED1ACE0AEFBBE6AA1418A2D6F137D13',
'2120732F77002A9138DE110408EA06C2064F032BC9D44A28C46267C607594203' ],
submissionAttempts: 1,
submitIndex: 10320270,
initialSubmitIndex: 10320268,
lastLedgerSequence: 10320278,
state: 'submitted',
server: undefined,
finalized: false,
result:
{ engine_result: 'telINSUF_FEE_P',
engine_result_message: 'Fee insufficient.',
ledger_hash: undefined,
ledger_index: undefined,
transaction_hash: '2120732F77002A9138DE110408EA06C2064F032BC9D44A28C46267C607594203' } }
</code></pre>
<p>The example above shows the transaction after 1 failed attempts, before a second attempt is submitted. Note that <code>tx.submittedIDs[0]</code> is the updated hash which applications should persist.</p>
<p>The <code>tx.result</code> is the <strong>provisional</strong> result of the <strong>prior</strong> submission attempt. In this example the initial submit failed with <code>telINSUF_FEE_P</code>, which could happen if the network adjusts the fee calculated by ripple-lib immediately before the transaction is processed.</p>
<h4 id="verify-the-transaction-1">Verify the Transaction</h4>
<p>During normal operation, events emitted by ripple-lib inform an application of the result of a transaction. The <code>state</code> event will emit with <code>tx.state: 'pending'</code> with a provisional result; and finally if successful with <code>tx.state: 'validated'</code> and <code>tx.finalized: true</code>.</p>
<pre><code>- State Event Hander: validated -
{ tx_json:
{ Flags: 2147483648,
TransactionType: 'Payment',
Account: 'rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W',
Amount:
{ value: '10',
currency: 'FOO',
issuer: 'rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W' },
Destination: 'rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM',
Sequence: 11,
SigningPubKey: '0267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC5',
Fee: '12000',
LastLedgerSequence: 10320625,
TxnSignature: '3045022100DEFA085B88834F3C27200714AB40F3A67E7E86E726DE630A2955843F6D53010D02202592D709D5F90F9E852CEFB0D5A7F6D39AC0A99BC97A3589BF9FA47018C2CB2C' },
clientID: undefined,
submittedIDs: [ '0E066642EC28DA3C9840202AF4F3CD281A5A2733F2D9BEF38C69DFDC14407E12' ],
submissionAttempts: 1,
submitIndex: 10320617,
initialSubmitIndex: 10320617,
lastLedgerSequence: 10320625,
state: 'validated',
server: undefined,
finalized: true,
result:
{ engine_result: 'tesSUCCESS',
engine_result_message: 'The transaction was applied.',
ledger_hash: '7F936E0F37611982A434B76270C819FDA8240649D7592BDC995FC9AEE2D436AA',
ledger_index: 10320618,
transaction_hash: '0E066642EC28DA3C9840202AF4F3CD281A5A2733F2D9BEF38C69DFDC14407E12' } }
</code></pre>
<p>If the final state of a transaction is an error, the state event indicates <code>tx.state: 'failed'</code> and <code>tx.finalized: 'true'</code>:</p>
<pre><code>- State Event Hander: failed -
{ tx_json:
{ Flags: 2147483648,
TransactionType: 'Payment',
Account: 'rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W',
Amount:
{ value: '10',
currency: 'FOO',
issuer: 'rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W' },
Destination: 'rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM',
Fee: '10000',
Sequence: 11,
SigningPubKey: '0267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC5',
LastLedgerSequence: 10320296,
TxnSignature: '304402201E3D376CF7C1C99EAC19027EA3EDE15F24D8FA59D4EA90067A09267F8D927ABB0220084B8BFF7D9308CD00A5DA265776DC9BAB9F553A9E6A4ED1A75DD76DCB3B40A9' },
clientID: undefined,
submittedIDs:
[ '895DA495779D51F84E4B79258AAF9A8E51404A79747DD936EE6468FE9A974AEE',
'F14AE631317D11184DEC7066814539E9BB50AB46DF770E3F2367430810C0AE7C',
'80F7460AD3F8172F9CDD69B638E4EA7ED9466A4D495EF8530FE98B8A0909E897',
'23F59C223701D73C2477963E4D1FA3460DAD5A64D813FB9591AF45C4C68A339B',
'B929D9018E842A56FFCA8EB295EDA81C62AE37804F759A19E0B1292C888C8586',
'EC9B4D869B8B9C27F644859F203D54023B01F6C2E9FA12CD34AC8373F9BA67A5',
'47FD42A6604D74A89E006352FBC656843A270EF46B47A3EB1D1098A31A01BC3D',
'BD3D13C57A5FDBB9B1F88C287EC158AA92BF9B75078273678F3124C84F504ABA',
'0B2CC389D5553FB232E4EF45EC67D149B8CD65E17C4BE7A41B5142A0D4A935BD',
'AD070F312DE0EB8AB64DDA0C512A4A1E9ED1ACE0AEFBBE6AA1418A2D6F137D13',
'2120732F77002A9138DE110408EA06C2064F032BC9D44A28C46267C607594203' ],
submissionAttempts: 11,
submitIndex: 10320288,
initialSubmitIndex: 10320268,
lastLedgerSequence: 10320296,
state: 'failed',
server: undefined,
finalized: true,
result:
{ engine_result: 'telINSUF_FEE_P',
engine_result_message: 'Fee insufficient.',
ledger_hash: undefined,
ledger_index: undefined,
transaction_hash: '895DA495779D51F84E4B79258AAF9A8E51404A79747DD936EE6468FE9A974AEE' } }
</code></pre>
<p>In the event of a power or network failure, an application may be interrupted before these <code>state</code> events are emitted. In these cases, applications should retrieve the status of a transaction by its <code>hash</code>, one of the details persisted during an earlier <code>presubmit</code> event.</p>
<pre><code>var hash = 'C3306CA3ED1B372EAC8A84A84B52752A4E4912BB1A26AB883E969BC987E4D20E';
remote.requestTransaction(hash, function(err, result) {
if (err) {
console.log(err);
}
else {
console.log(result);
}
});
</code></pre>
<p>Result of <code>remote.requestTransaction(&lt;transaction hash&gt;)</code>:</p>
<pre><code>{ Account: 'rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W',
Amount:
{ currency: 'FOO',
issuer: 'rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W',
value: '10' },
Destination: 'rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM',
Fee: '10000',
Flags: 2147483648,
LastLedgerSequence: 10303055,
Sequence: 10,
SigningPubKey: '0267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC5',
TransactionType: 'Payment',
TxnSignature: '304402200C65A9FD9EA7000DCD87688BA92C219074858D530E9A75F32EF2DA1A9C07844E02203A72B2688412CF855306E70D9B9ED541B67BEA8A8EDB90D47B7B227B295CED9B',
date: 470953270,
hash: 'C3306CA3ED1B372EAC8A84A84B52752A4E4912BB1A26AB883E969BC987E4D20E',
inLedger: 10303048,
ledger_index: 10303048,
meta:
{ AffectedNodes: [ [Object], [Object] ],
TransactionIndex: 23,
TransactionResult: 'tesSUCCESS' },
validated: true }
</code></pre>
<p>Note the response from <code>requestTransaction</code> includes <strong><code>validated: true</code></strong>, indicating the result is in a validated ledger and is final. Without <code>validated: true</code>, results are <em>provisional</em> and may change. This example shows <code>meta.TransactionResult: 'tesSUCCESS'</code>, indicating the transaction was successful.</p>
<h4 id="verify-missing-transaction-1">Verify Missing Transaction</h4>
<p><code>Remote.requestTransaction</code> may return <code>txNotFound</code>:</p>
<pre><code>{ [RippleError: Remote reported an error.]
error: 'remoteError',
error_message: 'Remote reported an error.',
remote:
{ error: 'txnNotFound',
error_code: 24,
error_message: 'Transaction not found.',
id: 1,
request:
{ command: 'tx',
id: 1,
transaction: 'F85D840B152328A1A6C11910A6FBF57E1340B6285E3602A1258B7A41EC814119' },
status: 'error',
type: 'response' },
result: 'remoteError',
engine_result: 'remoteError',
result_message: 'Remote reported an error.',
engine_result_message: 'Remote reported an error.',
message: 'Remote reported an error.' }
</code></pre>
<p>In these cases an application must distinguish between a provisional result and a final result. For this, an application needs the transaction's <code>initialSubmitIndex</code> and <code>LastLedgerSequence</code>. This information was persisted earlier by the application's <code>presubmit</code> event handler. In this example, <code>initialSubmitIndex</code> is 10301323 and <code>LastLedgerSequence</code> is 10301337.</p>
<p>A call to <code>Remote.requestServerInfo()</code> determines whether the server has final, immutable results about the transaction.</p>
<pre><code>remote.requestServerInfo(function(err, result) {
if (err) {
console.log(err);
}
else {
console.log(result);
}
});
</code></pre>
<p>Result of <code>remote.requestServerInfo()</code>:</p>
<pre><code>{ info:
{ build_version: '0.26.4-sp3-private',
complete_ledgers: '32570-10302948',
hostid: 'KNOW',
io_latency_ms: 1,
last_close: { converge_time_s: 2.001, proposers: 5 },
load_factor: 1000,
peers: 44,
pubkey_node: 'n9MADAXTbCnaBYoUcvDzHkTqoTSjVd8VHgJE2KwMBbwRV4pM3j2a',
server_state: 'full',
validated_ledger:
{ age: 4,
base_fee_xrp: 0.00001,
hash: '47EE6EC414FC0B648869CE7108143D916DE38DAC167DADEF541F9A8CED475909',
reserve_base_xrp: 20,
reserve_inc_xrp: 5,
seq: 10302948 },
validation_quorum: 3 } }
</code></pre>
<p>In this example, <code>info.complete_ledgers</code> indicates the server has continuous ledger history up to ledger sequence 10302948. This history includes the span of ledger where the transaction may appear, from 10301323 to 10301337. Therefore, the <code>txNotFound</code> result is final, and the transaction has failed immutably.</p>
<p>If the ledger history were not complete through the <code>LastLedgerSequence</code>, the application must wait for that ledger to become validated, or the server to sync a more complete history with the network.</p>
<h2 id="additional-resources">Additional Resources</h2>
<ul>
<li><a href="transactions.html">Transaction Format</a></li>
<li><a href="transactions.html#transaction-fees">Transaction Fees</a></li>
<li>Documentation of <a href="transactions.html#lastledgersequence"><code>LastLedgerSequence</code></a></li>
<li><a href="http://ripple.com/knowledge_center/the-ripple-ledger-consensus-process/">Overview of Ripple Ledger Consensus Process</a></li>
<li><a href="https://ripple.com/knowledge_center/reaching-consensus-in-ripple/">Reaching Consensus in Ripple</a></li>
</ul>
</div>
</div>
</div>
@@ -148,6 +960,12 @@
<div class="col-md-3">
<h4>Documentation</h4>
<ul class="footer_links">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippled-apis.html">rippled</a></li>
<li><a href="rippled-setup.html">rippled Setup</a></li>
<li><a href="ripple-rest.html">Ripple-REST</a></li>
@@ -158,12 +976,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</div>
<div class="col-md-3">

View File

@@ -29,18 +29,12 @@
<!-- syntax selection js -->
<script src="js/multicodetab.js"></script>
<!-- Code to load contents via Flatdoc -->
<script src="vendor/flatdoc/v/0.8.0/legacy.js"></script>
<script src="vendor/flatdoc/v/0.8.0/flatdoc.js"></script>
<!-- Markdown content already parsed+included; just do the code tab stuff -->
<script>
$(".flatdoc-content").empty();
$(".content-root .menubar .menu").empty();
Flatdoc.run({
fetcher: Flatdoc.file('content/reserves.md')
});
$(document).on('flatdoc:ready', function() {
$().multicode_tabs();
$(document).ready(function() {
$().multicode_tabs_pandoc();
hljs.initHighlighting();
make_code_expandable();
});
</script>
@@ -57,7 +51,7 @@
</head>
<body role='flatdoc' class='no-literate'>
<body class='no-literate'>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
@@ -71,6 +65,17 @@
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">References <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -81,6 +86,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</li>
<li class="dropdown">
@@ -91,17 +97,6 @@
<li><a href="gateway_guide.html">Gateway Guide</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Tools <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -136,9 +131,38 @@
<div class='content-root'>
<div class='menubar'>
<div class='menu section' role='flatdoc-menu'>
<script type="text/javascript" src="js/jquery.gensidebar.js"></script>
<script type="text/javascript">
</script>
</div>
</div>
<div role='flatdoc-content' class='content'>
<div class='content'>
<h1 id="reserves">Reserves</h1>
<p>The Ripple Consensus Ledger applies <em>reserve requirements</em>, in XRP, to protect the shared global ledger from growing excessively large as the result of spam or malicious usage. The goal is to constrain the growth of the ledger to match <a href="https://en.wikipedia.org/wiki/Moore's_law">Moore's Law</a> so that a current commodity-level machine can always fit the current ledger in RAM and the full ledger history on disk.</p>
<p>Each account in the shared global ledger must hold a minimum of XRP in order to submit transactions, and it cannot send this XRP to other accounts. You cannot create a new account unless you send enough XRP to meet the minimum reserve requirement.</p>
<p>The current minimum reserve requirement is <strong>20 XRP</strong>. (This is the cost of an account that owns no additional objects in the ledger.)</p>
<h2 id="base-reserve-and-owner-reserve">Base Reserve and Owner Reserve</h2>
<p>The reserve requirement is divided into two parts:</p>
<ul>
<li>The <strong>Base Reserve</strong> is a minimum amount of XRP that is required for every account in the ledger. Currently, this is 20 XRP (<code>20000000</code> drops).</li>
<li>The <strong>Owner Reserve</strong> is an additional requirement that scales with the number of objects that the account owns in the ledger. Currently, this is 5 XRP (<code>5000000</code> drops) per item.</li>
</ul>
<h3 id="owner-reserves">Owner Reserves</h3>
<p>Many objects in the ledger are owned by a particular account, and therefore count toward the reserve requirement of that account. When objects are removed from the ledger, they no longer count against their owner's reserve requirement.</p>
<ul>
<li><a href="ripple-ledger.html#offer">Offers</a> are owned by the account that placed them. An Offer can be automatically removed from the ledger if it is fully consumed or if it is found unfunded during transaction processing. Alternatively, the owner can cancel an offer by sending an <a href="transactions.html#offercancel">OfferCancel transaction</a>, or by sending an <a href="transactions.html#offercreate">OfferCreate transaction</a> that contains an <code>OfferSequence</code> parameter.</li>
<li><a href="ripple-ledger.html#ripplestate">Trust lines</a> are shared between two accounts. The owner reserve can apply to one or both of the accounts, depending on whether the fields that account controls are in their default state. See <a href="ripple-ledger.html#contributing-to-the-owner-reserve">Contributing to the Owner Reserve</a> for details.</li>
<li><a href="ripple-ledger.html#directorynode">Owner directories</a> list all the ledger nodes that contribute to an account's owner reserve. However, the owner directory itself does not count towards the reserve.</li>
</ul>
<h4 id="owner-reserve-edge-cases">Owner Reserve Edge Cases</h4>
<p>The Ripple Consensus Ledger considers an <a href="transactions.html#offercreate">OfferCreate transaction</a> to be an explicit statement of willingness to hold an asset. Consuming the offer automatically creates a trust line (with limit 0, and a balance above that limit) for the <code>taker_pays</code> currency if such a trust line does not exist. However, if the offer's owner does not possess enough XRP to meet the additional reserve requirement of the new trust line, the offer is considered unfunded. See also: <a href="transactions.html#lifecycle-of-an-offer">Lifecycle of an Offer</a>.</p>
<h2 id="going-below-the-reserve-requirement">Going Below the Reserve Requirement</h2>
<p>During transaction processing, a transaction can only be successful if the sending account possesses at least the reserve requirement in XRP. In the process, the <a href="tx-cost.html">transaction cost</a> destroys some of the sending account's XRP balance. This can cause an account to go below the reserve requirement.</p>
<p>When an account has less XRP than its current reserve requirement, it cannot send new transactions. Even so, it continues to exist in the ledger, as all accounts do. Unless the reserve requirements decrease, the only way for the account to become able to send transactions again is for it to receive enough XRP that it meets the reserve requirement.</p>
<p><strong>Exception:</strong> When an account is below the reserve requirement, it can send new <a href="transactions.html#offercreate">OfferCreate transactions</a> to acquire more XRP, or other currencies on its existing trust lines. These transactions cannot create new <a href="ripple-ledger.html#ripplestate">trust lines</a>, or <a href="ripple-ledger.html#offer">Offer nodes in the ledger</a>, so they can only execute trades that consume Offers that are already in the order books.</p>
<h2 id="changing-the-reserve-requirements">Changing the Reserve Requirements</h2>
<p>The Ripple Consensus Ledger has a mechanism for changing the reserve requirements in order to account for long-term changes in the value of XRP. Any changes have to be approved by the consensus process. See <a href="fee-voting.html">Fee Voting</a> for more information.</p>
</div>
</div>
</div>
@@ -148,6 +172,12 @@
<div class="col-md-3">
<h4>Documentation</h4>
<ul class="footer_links">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippled-apis.html">rippled</a></li>
<li><a href="rippled-setup.html">rippled Setup</a></li>
<li><a href="ripple-rest.html">Ripple-REST</a></li>
@@ -158,12 +188,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</div>
<div class="col-md-3">

View File

@@ -44,6 +44,17 @@
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">References <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -54,6 +65,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</li>
<li class="dropdown">
@@ -64,17 +76,6 @@
<li><a href="gateway_guide.html">Gateway Guide</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Tools <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -153,6 +154,12 @@
<div class="col-md-3">
<h4>Documentation</h4>
<ul class="footer_links">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippled-apis.html">rippled</a></li>
<li><a href="rippled-setup.html">rippled Setup</a></li>
<li><a href="ripple-rest.html">Ripple-REST</a></li>
@@ -163,12 +170,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</div>
<div class="col-md-3">

View File

@@ -44,6 +44,17 @@
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">References <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -54,6 +65,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</li>
<li class="dropdown">
@@ -64,17 +76,6 @@
<li><a href="gateway_guide.html">Gateway Guide</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Tools <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -186,6 +187,12 @@
<div class="col-md-3">
<h4>Documentation</h4>
<ul class="footer_links">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippled-apis.html">rippled</a></li>
<li><a href="rippled-setup.html">rippled Setup</a></li>
<li><a href="ripple-rest.html">Ripple-REST</a></li>
@@ -196,12 +203,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</div>
<div class="col-md-3">

View File

@@ -29,18 +29,12 @@
<!-- syntax selection js -->
<script src="js/multicodetab.js"></script>
<!-- Code to load contents via Flatdoc -->
<script src="vendor/flatdoc/v/0.8.0/legacy.js"></script>
<script src="vendor/flatdoc/v/0.8.0/flatdoc.js"></script>
<!-- Markdown content already parsed+included; just do the code tab stuff -->
<script>
$(".flatdoc-content").empty();
$(".content-root .menubar .menu").empty();
Flatdoc.run({
fetcher: Flatdoc.file('content/ledger_format.md')
});
$(document).on('flatdoc:ready', function() {
$().multicode_tabs();
$(document).ready(function() {
$().multicode_tabs_pandoc();
hljs.initHighlighting();
make_code_expandable();
});
</script>
@@ -57,7 +51,7 @@
</head>
<body role='flatdoc' class='no-literate'>
<body class='no-literate'>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
@@ -71,6 +65,17 @@
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">References <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -81,6 +86,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</li>
<li class="dropdown">
@@ -91,17 +97,6 @@
<li><a href="gateway_guide.html">Gateway Guide</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Tools <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -136,9 +131,845 @@
<div class='content-root'>
<div class='menubar'>
<div class='menu section' role='flatdoc-menu'>
<script type="text/javascript" src="js/jquery.gensidebar.js"></script>
<script type="text/javascript">
</script>
</div>
</div>
<div role='flatdoc-content' class='content'>
<div class='content'>
<h1 id="the-ledger">The Ledger</h1>
<p>The point of the Ripple software is to maintain a shared, global ledger that is open to all, so that individual participants can trust the integrity of the ledger without having to trust any single institution to manage it. The <code>rippled</code> server software accomplishes this by maintaining a ledger database that can only be updated according to very specific rules. Each instance of <code>rippled</code> maintains a full copy of the ledger, and the peer-to-peer network of <code>rippled</code> servers distributes candidate transactions among themselves. The consensus process determines which transactions get applied to each new version of the ledger. See also: <a href="https://ripple.com/knowledge_center/the-ripple-ledger-consensus-process/">The Consensus Process</a>.</p>
<p><img alt="Diagram: Each ledger is the result of applying transactions to the previous ledger version." src="img/ledger-process.png"/></p>
<p>The shared global ledger is actually a series of individual ledgers, or ledger versions, which <code>rippled</code> keeps in its internal database. Every ledger version has a sequence number (also called a ledger index), starting at 1 and incrementing with each new version. Every closed ledger also has an identifying hash value, which uniquely identifies the contents of that ledger. At any given time, a <code>rippled</code> instance has an in-progress "current" open ledger, plus some number of closed ledgers that have not yet been approved by consensus, and any number of historical ledgers that have been validated by consensus. Only the validated ledgers are certain to be accurate and immutable.</p>
<p>A single ledger version consists of several components:</p>
<p><img alt="Diagram: A ledger has transactions, a state node, and a header with the close time and validation info" src="img/ledger-components.png"/></p>
<ul>
<li>A <strong>header</strong> - The ledger's unique index (sequence number), hashes of the other contents, and other metadata.</li>
<li>A <strong>transaction tree</strong> - The <a href="transactions.html">transactions</a> that were applied to the previous ledger to make this one. Transactions are the <em>only</em> way to modify the ledger.</li>
<li>A <strong>state tree</strong> - All the <a href="#ledger-node-types">ledger nodes</a> that contain the settings, balances, and objects in the ledger as of this version.</li>
</ul>
<h2 id="tree-format">Tree Format</h2>
<p>As its name might suggest, a ledger's state tree is a tree data structure, with each node identified by a 256-bit value called an <code>index</code>. In JSON, a ledger node's index value is represented as a 64-character hexadecimal string like <code>"193C591BF62482468422313F9D3274B5927CA80B4DD3707E42015DD609E39C94"</code>. Every node in the state tree has an index that you can use as a key to look up the node in the state tree; every transaction has an indentifying hash that you can use to look up the transaction in the transaction tree. Do not confuse the <code>index</code> (key) of a ledger node with the <code>ledger_index</code> (sequence number) of a ledger.</p>
<p>In the case of transactions, the identifying hash is based on the signed transaction instructions, but the contents of the transaction object when you look it up also contain the results and metadata of the transaction, which are not taken into account when generating the hash.</p>
<p>In the case of state nodes, <code>rippled</code> usually includes the <code>index</code> of the node along with its contents. However, the index itself is not part of the contents. The index is derived by hashing important contents of the node, along with a <a href="https://github.com/ripple/rippled/blob/ceff6bc2713eaf80feafe56a02f4d636827b89a9/src/ripple/protocol/LedgerFormats.h#L94">namespace identifier</a>. The ledger node type determines which namespace identifier to use as well as which contents to include in the hash. This ensures every index is unique. For a hash function, <code>rippled</code> uses SHA-512 and then truncates the result to the first 256 bytes. This algorithm, informally called SHA-512Half, provides an output that has comparable security to SHA-256, but runs faster on 64-bit processors.</p>
<p><img alt="Diagram: rippled uses SHA-512Half to generate indexes for ledger nodes. The space key prevents indexes for different node types from colliding." src="img/ledger-indexes.png"/></p>
<h2 id="header-format">Header Format</h2>
<p><a href="https://github.com/ripple/rippled/blob/5d2d88209f1732a0f8d592012094e345cbe3e675/src/ripple/ledger/ReadView.h#L68" title="Source">[Source]<br/></a></p>
<p>Every ledger version has a unique header that describes the contents. You can look up a ledger's header information with the <a href="rippled-apis.html#ledger"><code>ledger</code> command</a>. The contents of the ledger header are as follows:</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>JSON Type</th>
<th><a href="https://wiki.ripple.com/Binary_Format">Internal Type</a></th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>ledger_index</td>
<td>String</td>
<td>UInt32</td>
<td>The sequence number of the ledger. Some API methods display this as a quoted integer; some display it as a native JSON number.</td>
</tr>
<tr>
<td>ledger_hash</td>
<td>String</td>
<td>Hash256</td>
<td>The SHA-512Half of the ledger header, excluding the <code>ledger_hash</code> itself. This serves as a unique identifier for this ledger and all its contents.</td>
</tr>
<tr>
<td>account_hash</td>
<td>String</td>
<td>Hash256</td>
<td>The SHA-512Half of this ledger's state tree information.</td>
</tr>
<tr>
<td>close_time</td>
<td>Number</td>
<td>UInt32</td>
<td>The approximate time this ledger closed, as the number of seconds since the Ripple Epoch of 2000-01-01 00:00:00. This value is rounded based on the <code>close_time_resolution</code>, so it is possible for subsequent ledgers to have the same value.</td>
</tr>
<tr>
<td>closed</td>
<td>Boolean</td>
<td>bool</td>
<td>If true, this transaction is no longer accepting new transactions. (However, unless this ledger is validated, it might be replaced by a different ledger with a different set of transactions.)</td>
</tr>
<tr>
<td>parent_hash</td>
<td>String</td>
<td>Hash256</td>
<td>The <code>ledger_hash</code> value of the previous ledger that was used to build this one. If there are different versions of the previous ledger by sequence number, this indicates from which one the ledger was derived.</td>
</tr>
<tr>
<td>total_coins</td>
<td>String</td>
<td>UInt64</td>
<td>The total number of drops of XRP owned by accounts in the ledger. This subtracts XRP that has been destroyed by transaction fees. The actual amount of XRP in circulation is lower because some accounts are "black holes" whose keys are not known by anyone.</td>
</tr>
<tr>
<td>transaction_hash</td>
<td>String</td>
<td>Hash256</td>
<td>The SHA-512Half of the transactions included in this ledger.</td>
</tr>
<tr>
<td>close_time_resolution</td>
<td>Number</td>
<td>Uint8</td>
<td>An integer in the range [2,120] indicating the maximum number of seconds by which the <code>close_time</code> could be rounded.</td>
</tr>
<tr>
<td>closeFlags</td>
<td>(Omitted)</td>
<td>UInt8</td>
<td>A bit-map of flags relating to the closing of this ledger.</td>
</tr>
</tbody>
</table>
<h3 id="close-flags">Close Flags</h3>
<p>Currently, the ledger has only one flag defined for closeFlags: <strong>sLCF_NoConsensusTime</strong> (value <code>1</code>). If this flag is enabled, it means that validators were in conflict regarding the correct close time for the ledger, but built otherwise the same ledger, so they declared consensus while "agreeing to disagree" on the close time. In this case, the consensus ledger contains a <code>close_time</code> value that is 1 second after that of the previous ledger. (In this case, there is no official close time, but the actual real-world close time is probably 3-6 seconds later than the specified <code>close_time</code>.)</p>
<p>The <code>closeFlags</code> field is not included in any JSON representations of a ledger, but it is a part of the binary representation of a ledger, and is one of the fields that determine the ledger's hash.</p>
<h1 id="ledger-node-types">Ledger Node Types</h1>
<p>There are several different kinds of nodes that can appear in the ledger's state tree:</p>
<ul>
<li><a href="#accountroot"><strong>AccountRoot</strong> - The settings, XRP balance, and other metadata for one account.</a></li>
<li><a href="#directorynode"><strong>DirectoryNode</strong> - Contains links to other nodes.</a></li>
<li><a href="#offer"><strong>Offer</strong> - An offer to exchange currencies, known in finance as an <em>order</em>.</a></li>
<li><a href="#ripplestate"><strong>RippleState</strong> - Links two accounts, tracking the balance of one currency between them. The concept of a <em>trust line</em> is really an abstraction of this node type.</a></li>
</ul>
<p>Each ledger node consists of several fields. In the peer protocol that <code>rippled</code> servers use to communicate with each other, ledger nodes are represented in their raw binary format. In other <a href="rippled-apis.html"><code>rippled</code> APIs</a>, ledger nodes are represented as JSON objects.</p>
<h2 id="accountroot">AccountRoot</h2>
<p><a href="https://github.com/ripple/rippled/blob/5d2d88209f1732a0f8d592012094e345cbe3e675/src/ripple/protocol/impl/LedgerFormats.cpp#L27" title="Source">[Source]<br/></a></p>
<p>The <code>AccountRoot</code> node type describes a single <em>account</em> object. Example <code>AccountRoot</code> node:</p>
<pre><code>{
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"AccountTxnID": "0D5FB50FA65C9FE1538FD7E398FFFE9D1908DFA4576D8D7A020040686F93C77D",
"Balance": "148446663",
"Domain": "6D64756F31332E636F6D",
"EmailHash": "98B4375E1D753E5B91627516F6D70977",
"Flags": 8388608,
"LedgerEntryType": "AccountRoot",
"MessageKey": "0000000000000000000000070000000300",
"OwnerCount": 3,
"PreviousTxnID": "0D5FB50FA65C9FE1538FD7E398FFFE9D1908DFA4576D8D7A020040686F93C77D",
"PreviousTxnLgrSeq": 14091160,
"Sequence": 336,
"TransferRate": 1004999999,
"index": "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8"
}
</code></pre>
<p>The <code>AccountRoot</code> node has the following fields:</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>JSON Type</th>
<th><a href="https://wiki.ripple.com/Binary_Format">Internal Type</a></th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>LedgerEntryType</td>
<td>String</td>
<td>UInt16</td>
<td>The value <code>0x61</code>, mapped to the string <code>AccountRoot</code>, indicates that this node is an AccountRoot object.</td>
</tr>
<tr>
<td>Account</td>
<td>String</td>
<td>AccountID</td>
<td>The identifying address of this account, such as rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn.</td>
</tr>
<tr>
<td><a href="#accountroot-flags">Flags</a></td>
<td>Number</td>
<td>UInt32</td>
<td>A bit-map of boolean flags enabled for this account.</td>
</tr>
<tr>
<td>Sequence</td>
<td>Number</td>
<td>UInt32</td>
<td>The sequence number of the next valid transaction for this account. (Each account starts with Sequence = 1 and increases each time a transaction is made.)</td>
</tr>
<tr>
<td>Balance</td>
<td>String</td>
<td>Amount</td>
<td>The account's current XRP balance in drops, represented as a string.</td>
</tr>
<tr>
<td>OwnerCount</td>
<td>Number</td>
<td>UInt32</td>
<td>The number of objects this account owns in the ledger, which contributes to its owner reserve.</td>
</tr>
<tr>
<td>PreviousTxnID</td>
<td>String</td>
<td>Hash256</td>
<td>The identifying hash of the transaction that most recently modified this node.</td>
</tr>
<tr>
<td>PreviousTxnLgrSeq</td>
<td>Number</td>
<td>UInt32</td>
<td>The sequence number (<code>ledger_index</code>) of the ledger that contains the transaction that most recently modified this node.</td>
</tr>
<tr>
<td>AccountTxnID</td>
<td>String</td>
<td>Hash256</td>
<td>(Optional) The identifying hash of the transaction most recently submitted by this account.</td>
</tr>
<tr>
<td>RegularKey</td>
<td>String</td>
<td>AccountID</td>
<td>(Optional) The address of a keypair that can be used to sign transactions for this account instead of the master key. Use a <a href="transactions.html#setregularkey">SetRegularKey transaction</a> to change this value.</td>
</tr>
<tr>
<td>EmailHash</td>
<td>String</td>
<td>Hash128</td>
<td>(Optional) The md5 hash of an email address. Clients can use this to look up an avatar through services such as <a href="https://en.gravatar.com/">Gravatar</a>.</td>
</tr>
<tr>
<td>WalletLocator</td>
<td>String</td>
<td>Hash256</td>
<td>(Optional) <strong>DEPRECATED</strong>. Do not use.</td>
</tr>
<tr>
<td>WalletSize</td>
<td>Number</td>
<td>UInt32</td>
<td>(Optional) <strong>DEPRECATED</strong>. Do not use.</td>
</tr>
<tr>
<td>MessageKey</td>
<td>String</td>
<td>VariableLength</td>
<td>(Optional) A public key that may be used to send encrypted messages to this account. In JSON, uses hexadecimal. No more than 33 bytes.</td>
</tr>
<tr>
<td>TransferRate</td>
<td>Number</td>
<td>UInt32</td>
<td>(Optional) A <a href="https://ripple.com/knowledge_center/transfer-fees/">transfer fee</a> to charge other users for sending currency issued by this account to each other.</td>
</tr>
<tr>
<td>Domain</td>
<td>String</td>
<td>VariableLength</td>
<td>(Optional) A domain associated with this account. In JSON, this is the hexadecimal for the ASCII representation of the domain.</td>
</tr>
</tbody>
</table>
<h3 id="accountroot-flags">AccountRoot Flags</h3>
<p>There are several options which can be either enabled or disabled for an account. These options can be changed with an <a href="transactions.html#accountset">AccountSet transaction</a>. In the ledger, flags are represented as binary values that can be combined with bitwise-or operations. The bit values for the flags in the ledger are different than the values used to enable or disable those flags in a transaction. Ledger flags have names that begin with <em>lsf</em>.</p>
<p>AccountRoot nodes can have the following flag values:</p>
<table>
<thead>
<tr>
<th>Flag Name</th>
<th>Hex Value</th>
<th>Decimal Value</th>
<th>Description</th>
<th>Corresponding <a href="transactions.html#accountset-flags">AccountSet Flag</a></th>
</tr>
</thead>
<tbody>
<tr>
<td>lsfPasswordSpent</td>
<td>0x00010000</td>
<td>65536</td>
<td>Indicates that the account has used its free SetRegularKey transaction.</td>
<td>(None)</td>
</tr>
<tr>
<td>lsfRequireDestTag</td>
<td>0x00020000</td>
<td>131072</td>
<td>Requires incoming payments to specify a Destination Tag.</td>
<td>asfRequireDest</td>
</tr>
<tr>
<td>lsfRequireAuth</td>
<td>0x00040000</td>
<td>262144</td>
<td>This account must individually approve other users in order for those users to hold this account's issuances.</td>
<td>asfRequireAuth</td>
</tr>
<tr>
<td>lsfDisallowXRP</td>
<td>0x00080000</td>
<td>524288</td>
<td>Client applications should not send XRP to this account. Not enforced by <code>rippled</code>.</td>
<td>asfDisallowXRP</td>
</tr>
<tr>
<td>lsfDisableMaster</td>
<td>0x00100000</td>
<td>1048576</td>
<td>Disallows use of the master key to sign transactions for this account.</td>
<td>asfDisableMaster</td>
</tr>
<tr>
<td>lsfNoFreeze</td>
<td>0x00200000</td>
<td>209715</td>
<td>This account cannot freeze trust lines connected to it. Once enabled, cannot be disabled.</td>
<td>asfNoFreeze</td>
</tr>
<tr>
<td>lsfGlobalFreeze</td>
<td>0x00400000</td>
<td>4194304</td>
<td>All assets issued by this account are frozen.</td>
<td>asfGlobalFreeze</td>
</tr>
<tr>
<td>lsfDefaultRipple</td>
<td>0x00800000</td>
<td>8388608</td>
<td>Enable <a href="https://ripple.com/knowledge_center/understanding-the-noripple-flag/">rippling</a> on this account's trust lines by default. Required for gateways; discouraged for other accounts.</td>
<td>asfDefaultRipple</td>
</tr>
</tbody>
</table>
<h3 id="accountroot-index-format">AccountRoot index format</h3>
<p>The <code>index</code> of an AccountRoot node is the SHA-512Half of the following values put together:</p>
<ul>
<li>The Account space key (<code>a</code>)</li>
<li>The AccountID of the account</li>
</ul>
<h2 id="directorynode">DirectoryNode</h2>
<p><a href="https://github.com/ripple/rippled/blob/5d2d88209f1732a0f8d592012094e345cbe3e675/src/ripple/protocol/impl/LedgerFormats.cpp#L44" title="Source">[Source]<br/></a></p>
<p>The <code>DirectoryNode</code> node type provides a list of links to other nodes in the ledger's state tree. A single conceptual <em>Directory</em> takes the form of a doubly linked list, with one or more DirectoryNode objects each containing up to 32 <a href="#tree-format">indexes</a> of other nodes. The first node is called the root of the directory, and all nodes other than the root node can be added or deleted as necessary.</p>
<p>There are two kinds of Directories:</p>
<ul>
<li><strong>Owner directories</strong> list other nodes owned by an account, such as RippleState or Offer nodes.</li>
<li><strong>Offer directories</strong> list the offers currently available in the distributed exchange. A single Offer Directory contains all the offers that have the same exchange rate for the same issuances.</li>
</ul>
<p>Example Directories:</p>
<div class="multicode">
*Offer Directory*
wzxhzdk:1
*Owner Directory*
wzxhzdk:2
</div>
<table>
<thead>
<tr>
<th>Name</th>
<th>JSON Type</th>
<th><a href="https://wiki.ripple.com/Binary_Format">Internal Type</a></th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>LedgerEntryType</td>
<td>Number</td>
<td>UInt16</td>
<td>The value <code>0x64</code>, mapped to the string <code>DirectoryNode</code>, indicates that this node is part of a Directory.</td>
</tr>
<tr>
<td>Flags</td>
<td>Number</td>
<td>UInt32</td>
<td>A bit-map of boolean flags enabled for this directory. Currently, the protocol defines no flags for DirectoryNode objects.</td>
</tr>
<tr>
<td>RootIndex</td>
<td>Number</td>
<td>Hash256</td>
<td>The index of root node for this directory.</td>
</tr>
<tr>
<td>Indexes</td>
<td>Array</td>
<td>Vector256</td>
<td>The contents of this Directory: an array of indexes to other nodes.</td>
</tr>
<tr>
<td>IndexNext</td>
<td>Number</td>
<td>UInt64</td>
<td>(Optional) If this Directory consists of multiple nodes, this index links to the next node in the chain, wrapping around at the end.</td>
</tr>
<tr>
<td>IndexPrevious</td>
<td>Number</td>
<td>UInt64</td>
<td>(Optional) If this Directory consists of multiple nodes, this index links to the previous node in the chain, wrapping around at the beginning.</td>
</tr>
<tr>
<td>Owner</td>
<td>String</td>
<td>AccountID</td>
<td>(Owner Directories only) The address of the account that owns the objects in this directory.</td>
</tr>
<tr>
<td>ExchangeRate</td>
<td>Number</td>
<td>UInt64</td>
<td>(Offer Directories only) <strong>DEPRECATED</strong>. Do not use.</td>
</tr>
<tr>
<td>TakerPaysCurrency</td>
<td>String</td>
<td>Hash160</td>
<td>(Offer Directories only) The currency code of the TakerPays amount from the offers in this directory.</td>
</tr>
<tr>
<td>TakerPaysIssuer</td>
<td>String</td>
<td>Hash160</td>
<td>(Offer Directories only) The issuer of the TakerPays amount from the offers in this directory.</td>
</tr>
<tr>
<td>TakerGetsCurrency</td>
<td>String</td>
<td>Hash160</td>
<td>(Offer Directories only) The currency code of the TakerGets amount from the offers in this directory.</td>
</tr>
<tr>
<td>TakerGetsIssuer</td>
<td>String</td>
<td>Hash160</td>
<td>(Offer Directories only) The issuer of the TakerGets amount from the offers in this directory.</td>
</tr>
</tbody>
</table>
<h3 id="directory-index-formats">Directory index formats</h3>
<p>There are three different formulas for creating the index of a DirectoryNode, depending on whether the DirectoryNode represents:</p>
<ul>
<li>The first page (also called the root) of an Owner Directory,</li>
<li>The first page of an Offer Directory, <em>or</em></li>
<li>Subsequent pages of either type</li>
</ul>
<p><strong>The first page of an Owner Directory</strong> has an <code>index</code> that is the SHA-512Half of the following values put together:</p>
<ul>
<li>The Owner Directory space key (<code>O</code>, capital letter O)</li>
<li>The AccountID from the <code>Owner</code> field.</li>
</ul>
<p><strong>The first page of an Offer Directory</strong> has a special <code>index</code>: the higher 192 bits define the order book, and the remaining 64 bits define the exchange rate of the offers in that directory. (An index is big-endian, so the book is in the more significant bits, which come first, and the quality is in the less significant bits which come last.) This provides a way to iterate through an order book from best offers to worst. Specifically: the first 192 bits are the first 192 bits of the SHA-512Half of the following values put together:</p>
<ul>
<li>The Book Directory space key (<code>B</code>)</li>
<li>The 160-bit currency code from the <code>TakerPaysCurrency</code></li>
<li>The 160-bit currency code from the <code>TakerGetsCurrency</code></li>
<li>The AccountID from the <code>TakerPaysIssuer</code></li>
<li>The AccountID from the <code>TakerGetsIssuer</code></li>
</ul>
<p>The lower 64 bits of an Offer Directory's index represent the TakerPays amount divided by TakerGets amount from the offer(s) in that directory as a 64-bit number in Ripple's internal amount format.</p>
<p><strong>If the DirectoryNode is not the first page in the Directory</strong> (regardless of whether it is an Owner Directory or an Offer Directory), then it has an <code>index</code> that is the SHA-512Half of the following values put together:</p>
<ul>
<li>The Directory Node space key (<code>d</code>)</li>
<li>The <code>index</code> of the root DirectoryNode</li>
<li>The page number of this node. (Since 0 is the root DirectoryNode, this value is an integer 1 or higher.)</li>
</ul>
<h2 id="offer">Offer</h2>
<p><a href="https://github.com/ripple/rippled/blob/5d2d88209f1732a0f8d592012094e345cbe3e675/src/ripple/protocol/impl/LedgerFormats.cpp#L57" title="Source">[Source]<br/></a></p>
<p>The <code>Offer</code> node type describes an offer to exchange currencies, more traditionally known as an <em>order</em>, which is currently in an order book in Ripple's distributed exchange. An <a href="transactions.html#offercreate">OfferCreate transaction</a> only creates an Offer node in the ledger when the offer cannot be fully executed immediately by consuming other offers already in the ledger.</p>
<p>An offer can become unfunded through other activities in the network, while remaining in the ledger. However, <code>rippled</code> will automatically prune any unfunded offers it happens across in the course of transaction processing (and <em>only</em> transaction processing, because the ledger state must only be changed by transactions). For more information, see <a href="transactions.html#lifecycle-of-an-offer">lifecycle of an offer</a>.</p>
<p>Example Offer node:</p>
<pre><code>{
"Account": "rBqb89MRQJnMPq8wTwEbtz4kvxrEDfcYvt",
"BookDirectory": "ACC27DE91DBA86FC509069EAF4BC511D73128B780F2E54BF5E07A369E2446000",
"BookNode": "0000000000000000",
"Flags": 131072,
"LedgerEntryType": "Offer",
"OwnerNode": "0000000000000000",
"PreviousTxnID": "F0AB71E777B2DA54B86231E19B82554EF1F8211F92ECA473121C655BFC5329BF",
"PreviousTxnLgrSeq": 14524914,
"Sequence": 866,
"TakerGets": {
"currency": "XAG",
"issuer": "r9Dr5xwkeLegBeXq6ujinjSBLQzQ1zQGjH",
"value": "37"
},
"TakerPays": "79550000000",
"index": "96F76F27D8A327FC48753167EC04A46AA0E382E6F57F32FD12274144D00F1797"
}
</code></pre>
<p>An Offer node has the following fields:</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>JSON Type</th>
<th><a href="https://wiki.ripple.com/Binary_Format">Internal Type</a></th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>LedgerEntryType</td>
<td>String</td>
<td>UInt16</td>
<td>The value <code>0x6F</code>, mapped to the string <code>Offer</code>, indicates that this node is an Offer object.</td>
</tr>
<tr>
<td>Flags</td>
<td>Number</td>
<td>UInt32</td>
<td>A bit-map of boolean flags enabled for this offer.</td>
</tr>
<tr>
<td>Account</td>
<td>String</td>
<td>AccountID</td>
<td>The address of the account that owns this offer.</td>
</tr>
<tr>
<td>Sequence</td>
<td>Number</td>
<td>UInt32</td>
<td>The <code>Sequence</code> value of the <a href="transactions.html#offercreate">OfferCreate</a> transaction that created this Offer node. Used in combination with the <code>Account</code> to identify this Offer.</td>
</tr>
<tr>
<td>TakerPays</td>
<td>String or Object</td>
<td>Amount</td>
<td>The remaining amount and type of currency requested by the offer creator.</td>
</tr>
<tr>
<td>TakerGets</td>
<td>String or Object</td>
<td>Amount</td>
<td>The remaining amount and type of currency being provided by the offer creator.</td>
</tr>
<tr>
<td>BookDirectory</td>
<td>String</td>
<td>UInt256</td>
<td>The index of the <a href="#directorynode">Offer Directory</a> that links to this offer.</td>
</tr>
<tr>
<td>BookNode</td>
<td>String</td>
<td>UInt64</td>
<td>A hint indicating which page of the offer directory links to this node, in case the directory consists of multiple nodes.</td>
</tr>
<tr>
<td>OwnerNode</td>
<td>String</td>
<td>UInt64</td>
<td>A hint indicating which page of the owner directory links to this node, in case the directory consists of multiple nodes. <strong>Note:</strong> The offer does not contain a direct link to the owner directory containing it, since that value can be derived from the <code>Account</code>.</td>
</tr>
<tr>
<td>PreviousTxnID</td>
<td>String</td>
<td>Hash256</td>
<td>The identifying hash of the transaction that most recently modified this node.</td>
</tr>
<tr>
<td>PreviousTxnLgrSeq</td>
<td>Number</td>
<td>UInt32</td>
<td>The sequence number (<code>ledger_index</code>) of the ledger that contains the transaction that most recently modified this node.</td>
</tr>
<tr>
<td>Expiration</td>
<td>Number</td>
<td>UInt32</td>
<td>(Optional) Indicates the time after which this offer will be considered unfunded. See <a href="rippled-apis.html#specifying-time">Specifying Time</a> for details.</td>
</tr>
</tbody>
</table>
<h3 id="offer-flags">Offer Flags</h3>
<p>There are several options which can be either enabled or disabled when an <a href="transactions.html#offercreate">OfferCreate transaction</a> creates an offer node. In the ledger, flags are represented as binary values that can be combined with bitwise-or operations. The bit values for the flags in the ledger are different than the values used to enable or disable those flags in a transaction. Ledger flags have names that begin with <em>lsf</em>.</p>
<p>Offer nodes can have the following flag values:</p>
<table>
<thead>
<tr>
<th>Flag Name</th>
<th>Hex Value</th>
<th>Decimal Value</th>
<th>Description</th>
<th>Corresponding <a href="transactions.html#offercreate-flags">OfferCreate Flag</a></th>
</tr>
</thead>
<tbody>
<tr>
<td>lsfPassive</td>
<td>0x00010000</td>
<td>65536</td>
<td>The node was placed as a passive offer. This has no effect on the node in the ledger.</td>
<td>tfPassive</td>
</tr>
<tr>
<td>lsfSell</td>
<td>0x00020000</td>
<td>131072</td>
<td>The node was placed as a sell offer. This has no effect on the node in the ledger (because tfSell only matters if you get a better rate than you asked for, which cannot happen after the node enters the ledger).</td>
<td>tfSell</td>
</tr>
</tbody>
</table>
<h3 id="offer-index-format">Offer index format</h3>
<p>The <code>index</code> of an Offer node is the SHA-512Half of the following values put together:</p>
<ul>
<li>The Offer space key (<code>o</code>)</li>
<li>The AccountID of the account placing the offer</li>
<li>The Sequence number of the transaction that created the offer</li>
</ul>
<h2 id="ripplestate">RippleState</h2>
<p><a href="https://github.com/ripple/rippled/blob/5d2d88209f1732a0f8d592012094e345cbe3e675/src/ripple/protocol/impl/LedgerFormats.cpp#L70" title="Source">[Source]<br/></a></p>
<p>The <code>RippleState</code> node type connects two accounts in a single currency. Conceptually, a RippleState node represents two <em>trust lines</em> between the accounts, one from each side. Each account can modify the settings for its side of the RippleState node, but the balance is a single shared value. A trust line that is entirely in its default state is considered the same as trust line that does not exist, so <code>rippled</code> deletes RippleState nodes when their properties are entirely default.</p>
<p>Since no account is privileged in the Ripple ledger, a RippleState node sorts their account addresses numerically, to ensure a canonical form. Whichever address is numerically lower is deemed the "low account" and the other is the "high account".</p>
<p>Example RippleState node:</p>
<pre><code>{
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-10"
},
"Flags": 393216,
"HighLimit": {
"currency": "USD",
"issuer": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
"value": "110"
},
"HighNode": "0000000000000000",
"LedgerEntryType": "RippleState",
"LowLimit": {
"currency": "USD",
"issuer": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
"value": "0"
},
"LowNode": "0000000000000000",
"PreviousTxnID": "E3FE6EA3D48F0C2B639448020EA4F03D4F4F8FFDB243A852A0F59177921B4879",
"PreviousTxnLgrSeq": 14090896,
"index": "9CA88CDEDFF9252B3DE183CE35B038F57282BC9503CDFA1923EF9A95DF0D6F7B"
}
</code></pre>
<p>A RippleState node has the following fields:</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>JSON Type</th>
<th>Internal Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>LedgerEntryType</td>
<td>String</td>
<td>UInt16</td>
<td>The value <code>0x72</code>, mapped to the string <code>RippleState</code>, indicates that this node is a RippleState object.</td>
</tr>
<tr>
<td>Flags</td>
<td>Number</td>
<td>UInt32</td>
<td>A bit-map of boolean options enabled for this node.</td>
</tr>
<tr>
<td>Balance</td>
<td>Object</td>
<td>Amount</td>
<td>The balance of the trust line, from the perspective of the low account. A negative balance indicates that the low account has issued currency to the high account. The issuer in this is always set to the neutral value <a href="https://wiki.ripple.com/Accounts#ACCOUNT_ONE">ACCOUNT_ONE</a>.</td>
</tr>
<tr>
<td>LowLimit</td>
<td>Object</td>
<td>Amount</td>
<td>The limit that the low account has set on the trust line. The <code>issuer</code> is the address of the low account that set this limit.</td>
</tr>
<tr>
<td>HighLimit</td>
<td>Object</td>
<td>Amount</td>
<td>The limit that the high account has set on the trust line. The <code>issuer</code> is the address of the high account that set this limit.</td>
</tr>
<tr>
<td>PreviousTxnID</td>
<td>String</td>
<td>Hash256</td>
<td>The identifying hash of the transaction that most recently modified this node.</td>
</tr>
<tr>
<td>PreviousTxnLgrSeq</td>
<td>Number</td>
<td>UInt32</td>
<td>The sequence number (<code>ledger_index</code>) of the ledger that contains the transaction that most recently modified this node.</td>
</tr>
<tr>
<td>LowNode</td>
<td>String</td>
<td>UInt64</td>
<td>(Omitted in some historical ledgers) A hint indicating which page of the low account's owner directory links to this node, in case the directory consists of multiple nodes.</td>
</tr>
<tr>
<td>HighNode</td>
<td>String</td>
<td>UInt64</td>
<td>(Omitted in some historical ledgers) A hint indicating which page of the high account's owner directory links to this node, in case the directory consists of multiple nodes.</td>
</tr>
<tr>
<td>LowQualityIn</td>
<td>Number</td>
<td>UInt32</td>
<td>(Optional) The inbound quality set by the low account, as an integer in the implied ratio LowQualityIn:1,000,000,000. The value 0 is equivalent to 1 billion, or face value.</td>
</tr>
<tr>
<td>LowQualityOut</td>
<td>Number</td>
<td>UInt32</td>
<td>(Optional) The outbound quality set by the low account, as an integer in the implied ratio LowQualityOut:1,000,000,000. The value 0 is equivalent to 1 billion, or face value.</td>
</tr>
<tr>
<td>HighQualityIn</td>
<td>Number</td>
<td>UInt32</td>
<td>(Optional) The inbound quality set by the high account, as an integer in the implied ratio HighQualityIn:1,000,000,000. The value 0 is equivalent to 1 billion, or face value.</td>
</tr>
<tr>
<td>HighQualityOut</td>
<td>Number</td>
<td>UInt32</td>
<td>(Optional) The outbound quality set by the high account, as an integer in the implied ratio HighQualityOut:1,000,000,000. The value 0 is equivalent to 1 billion, or face value.</td>
</tr>
</tbody>
</table>
<h3 id="ripplestate-flags">RippleState Flags</h3>
<p>There are several options which can be either enabled or disabled for a trust line. These options can be changed with a <a href="transactions.html#trustset">TrustSet transaction</a>. In the ledger, flags are represented as binary values that can be combined with bitwise-or operations. The bit values for the flags in the ledger are different than the values used to enable or disable those flags in a transaction. Ledger flags have names that begin with <em>lsf</em>.</p>
<p>RippleState nodes can have the following flag values:</p>
<table>
<thead>
<tr>
<th>Flag Name</th>
<th>Hex Value</th>
<th>Decimal Value</th>
<th>Description</th>
<th>Corresponding <a href="transactions.html#trustset-flags">TrustSet Flag</a></th>
</tr>
</thead>
<tbody>
<tr>
<td>lsfLowReserve</td>
<td>0x00010000</td>
<td>65536</td>
<td>This RippleState node <a href="#contributing-to-the-owner-reserve">contributes to the low account's owner reserve</a>.</td>
<td>(None)</td>
</tr>
<tr>
<td>lsfHighReserve</td>
<td>0x00020000</td>
<td>131072</td>
<td>This RippleState node <a href="#contributing-to-the-owner-reserve">contributes to the high account's owner reserve</a>.</td>
<td>(None)</td>
</tr>
<tr>
<td>lsfLowAuth</td>
<td>0x00040000</td>
<td>262144</td>
<td>The low account has authorized the high account to hold the low account's issuances.</td>
<td>tfSetAuth</td>
</tr>
<tr>
<td>lsfHighAuth</td>
<td>0x00080000</td>
<td>524288</td>
<td>The high account has authorized the low account to hold the high account's issuances.</td>
<td>tfSetAuth</td>
</tr>
<tr>
<td>lsfLowNoRipple</td>
<td>0x00100000</td>
<td>1048576</td>
<td>The low account <a href="https://ripple.com/knowledge_center/understanding-the-noripple-flag/">has disabled rippling</a> from this trust line to other trust lines with the same account's NoRipple flag set.</td>
<td>tfSetNoRipple</td>
</tr>
<tr>
<td>lsfHighNoRipple</td>
<td>0x00200000</td>
<td>2097152</td>
<td>The high account <a href="https://ripple.com/knowledge_center/understanding-the-noripple-flag/">has disabled rippling</a> from this trust line to other trust lines with the same account's NoRipple flag set.</td>
<td>tfSetNoRipple</td>
</tr>
<tr>
<td>lsfLowFreeze</td>
<td>0x00400000</td>
<td>4194304</td>
<td>The low account has frozen the trust line, preventing the high account from transferring the asset.</td>
<td>tfSetFreeze</td>
</tr>
<tr>
<td>lsfHighFreeze</td>
<td>0x00800000</td>
<td>8388608</td>
<td>The high account has frozen the trust line, preventing the low account from transferring the asset.</td>
<td>tfSetFreeze</td>
</tr>
</tbody>
</table>
<h3 id="contributing-to-the-owner-reserve">Contributing to the Owner Reserve</h3>
<p>If an account modifies a trust line to put it in a non-default state, then that trust line counts towards the account's <a href="reserves.html#owner-reserves">owner reserve</a>. In a RippleState node, the <code>lsfLowReserve</code> and <code>lsfHighReserve</code> flags indicate which account(s) are responsible for the owner reserve. The <code>rippled</code> server automatically sets these flags when it modifies a trust line.</p>
<p>The values that count towards a trust line's non-default state are as follows:</p>
<table>
<thead>
<tr>
<th>High account responsible if...</th>
<th>Low account responsible if...</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Balance</code> is negative (the high account holds currency)</td>
<td><code>Balance</code> is positive (the low account holds currency)</td>
</tr>
<tr>
<td><code>HighLimit</code> is not <code>0</code></td>
<td><code>LowLimit</code> is not <code>0</code></td>
</tr>
<tr>
<td><code>LowQualityIn</code> is not <code>0</code> and not <code>1000000000</code></td>
<td><code>HighQualityIn</code> is not <code>0</code> and not <code>1000000000</code></td>
</tr>
<tr>
<td><code>LowQualityOut</code> is not <code>0</code> and not <code>1000000000</code></td>
<td><code>HighQualityOut</code> is not <code>0</code> and not <code>1000000000</code></td>
</tr>
<tr>
<td><strong>lsfHighNoRipple</strong> flag is not in its default state</td>
<td><strong>lsfLowNoRipple</strong> flag is not in its default state</td>
</tr>
<tr>
<td><strong>lsfHighFreeze</strong> flag is enabled</td>
<td><strong>lsfLowFreeze</strong> flag is enabled</td>
</tr>
</tbody>
</table>
<p>The <strong>lsfLowAuth</strong> and <strong>lsfHighAuth</strong> flags do not count against the default state, because they cannot be disabled.</p>
<p>The default state of the two NoRipple flags depends on the state of the <a href="#accountroot-flags">lsfDefaultRipple flag</a> in their corresponding AccountRoot nodes. If DefaultRipple is disabled (the default), then the default state of the lsfNoRipple flag is <em>enabled</em> for all of an account's trust lines. If an account enables DefaultRipple, then the lsfNoRipple flag is <em>disabled</em> (rippling is enabled) for an account's trust lines by default. <strong>Note:</strong> Prior to the introduction of the DefaultRipple flags in <code>rippled</code> version 0.27.3 (March 10, 2015), the default state for all trust lines was with lsfNoRipple disabled (rippling enabled).</p>
<p>Fortunately, <code>rippled</code> uses lazy evaluation to calculate the owner reserve. This means that even if an account changes the default state of all its trust lines by changing the DefaultRipple flag, that account's reserve stays the same initially. If an account modifies a trust line, <code>rippled</code> re-evaluates whether that individual trust line is in its default state and should contribute the owner reserve.</p>
<h3 id="ripplestate-index-format">RippleState index format</h3>
<p>The <code>index</code> of a RippleState node is the SHA-512Half of the following values put together:</p>
<ul>
<li>The RippleState space key (<code>r</code>)</li>
<li>The AccountID of the low account</li>
<li>The AccountID of the high account</li>
<li>The 160-bit currency code of the trust line(s)</li>
</ul>
</div>
</div>
</div>
@@ -148,6 +979,12 @@
<div class="col-md-3">
<h4>Documentation</h4>
<ul class="footer_links">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippled-apis.html">rippled</a></li>
<li><a href="rippled-setup.html">rippled Setup</a></li>
<li><a href="ripple-rest.html">Ripple-REST</a></li>
@@ -158,12 +995,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</div>
<div class="col-md-3">

File diff suppressed because it is too large Load Diff

5111
rippleapi.html Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -29,18 +29,12 @@
<!-- syntax selection js -->
<script src="js/multicodetab.js"></script>
<!-- Code to load contents via Flatdoc -->
<script src="vendor/flatdoc/v/0.8.0/legacy.js"></script>
<script src="vendor/flatdoc/v/0.8.0/flatdoc.js"></script>
<!-- Markdown content already parsed+included; just do the code tab stuff -->
<script>
$(".flatdoc-content").empty();
$(".content-root .menubar .menu").empty();
Flatdoc.run({
fetcher: Flatdoc.file('content/rippled-setup.md')
});
$(document).on('flatdoc:ready', function() {
$().multicode_tabs();
$(document).ready(function() {
$().multicode_tabs_pandoc();
hljs.initHighlighting();
make_code_expandable();
});
</script>
@@ -57,7 +51,7 @@
</head>
<body role='flatdoc' class='no-literate'>
<body class='no-literate'>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
@@ -71,6 +65,17 @@
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">References <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -81,6 +86,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</li>
<li class="dropdown">
@@ -91,17 +97,6 @@
<li><a href="gateway_guide.html">Gateway Guide</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Tools <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -136,9 +131,269 @@
<div class='content-root'>
<div class='menubar'>
<div class='menu section' role='flatdoc-menu'>
<script type="text/javascript" src="js/jquery.gensidebar.js"></script>
<script type="text/javascript">
</script>
</div>
</div>
<div role='flatdoc-content' class='content'>
<div class='content'>
<h1 id="operating-rippled-servers">Operating rippled Servers</h1>
<p>The core server of the Ripple peer-to-peer network is <a href="rippled-apis.html"><code>rippled</code></a>. Anyone can run their own <code>rippled</code> server that follows the network and keeps a complete copy of the Ripple ledger. You can even have your server perform validations and participate in the consensus process.</p>
<p>This page contains instructions for:</p>
<ul>
<li><a href="#installing-rippled">Installing <code>rippled</code></a></li>
<li><a href="#running-a-validator">Participating in the Consensus Process</a></li>
</ul>
<h2 id="types-of-rippled-servers">Types of rippled Servers</h2>
<p>The <code>rippled</code> server software can run in several modes depending on its configuration, including:</p>
<ul>
<li>Stock server - follows the network with a local copy of the ledger.</li>
<li>Validating server, or <em>validator</em> for short - participates in consensus.</li>
<li><code>rippled</code> server in stand-alone mode - for basic testing. Does not communicate to other <code>rippled</code> servers.</li>
</ul>
<p>You can also run the <code>rippled</code> executable as a client application for accessing <a href="rippled-apis.html"><code>rippled</code> APIs</a> locally. (Two instances of the same binary can run side-by-side in this case; one as a server, and the other running briefly as a client and then terminating.)</p>
<h2 id="reasons-to-run-a-stock-server">Reasons to Run a Stock Server</h2>
<p>There are lots of reasons you might want to run your own <code>rippled</code> server, but most of them can be summarized as: you can trust your own server, you have control over its workload, and you're not at the mercy of others to decide when and how you can access it.</p>
<p>It is important that you can trust the <code>rippled</code> you use, so you can be certain that the software you are running will behave in the manner specified in its source code. Of course, you must also practice good network security to protect your server from malicious hackers. If you connect to a malicious server, there are myriad ways that it can take advantage of you or cause you to lose money. For example:</p>
<ul>
<li>A malicious server could report that you were paid when no such payment was made</li>
<li>It could selectively show or hide payment paths and currency exchange offers to guarantee its own profit while not providing you the best deal</li>
<li>If you sent it your account's secret, it could make arbitrary transactions on your behalf, and even transfer or destroy all the money in your account's balances.</li>
</ul>
<p>Additionally, running your own server gives you admin control over it, which allows you to run important admin-only and load-intensive commands. If you use a shared server, you have to worry about other users of the same server competing with you for the server's computing power. Many of the commands in the WebSocket API can put a lot of strain on the server, so <code>rippled</code> has the option to scale back its responses when it needs to. If you share a server with others, you may not always get the best results possible.</p>
<p>Finally, if you run a validating server, you can use a stock server as a proxy to the public network while keeping your validating server on a private subnet only accessible to the outside world through the stock server. This makes it more difficult to compromise the integrity of your validating server.</p>
<h2 id="reasons-to-run-a-validator">Reasons to Run a Validator</h2>
<p>The robustness of the Ripple network depends on an interconnected web of validators who each trust a few other validators <em>not to collude</em>. The more operators with different interests there are who run validators, the more certain each member of the network can be that it continues to run impartially. If you or your organization relies on the Ripple peer-to-peer network, it is in your interest to contribute to the consensus process.</p>
<p>Not all <code>rippled</code> servers need to be validators: trusting additional servers from the same operator does not provide additional protection against collusion. It could be useful for an organization to run validators in multiple regions in order to provide better redundancy in case of natural disasters and other emergencies.</p>
<p>If your organization runs a validating server, you may also run one or more stock servers, to balance the computing load of API access, or as a proxy between your validation server and the outside network.</p>
<h3 id="properties-of-a-good-validator">Properties of a Good Validator</h3>
<p>There are several properties that define a good validator. The more of these properties your server embodies, the more reason others have to include your server in their list of trusted validators:</p>
<ul>
<li><strong>Availability</strong>. An ideal validator should always be running, submitting validation votes for every proposed ledger.<ul>
<li>Basically, strive for 100% uptime.</li>
</ul>
</li>
<li><strong>Agreement</strong>. A validator's votes should match the outcome of the consensus process as often as possible. To do otherwise could indicate that the validator's software is outdated, buggy, or intentionally biased.<ul>
<li>Always run the latest <code>rippled</code> release without modifications.</li>
</ul>
</li>
<li><strong>Timeliness</strong>. A validator's votes should arrive quickly, and not after a consensus round has already finished.<ul>
<li>A fast internet connection helps with this.</li>
</ul>
</li>
<li><strong>Identified</strong>. It should be clear who runs the validator. Ideally, a list of trusted validators should include validators operated by different owners in multiple legal jurisdictions and geographic areas, to reduce the chance that any localized events could interfere with the validator's impartial operation.<ul>
<li>Setting up <a href="#domain-verification">Domain Verification</a> is a good start.</li>
</ul>
</li>
</ul>
<p>At present, Ripple, Inc. cannot recommend any validators aside from the 5 core validators run by Ripple, Inc.: these validators are included in the default <code>rippled</code> configuration. However, we are collecting data on other validators and building tools to report on their performance. For metrics on the validators currently operating, see <a href="https://validators.ripple.com">validators.ripple.com</a>.</p>
<h1 id="installing-rippled">Installing rippled</h1>
<p>For development, you can <a href="https://wiki.ripple.com/Rippled_build_instructions">compile <code>rippled</code> from source</a>.</p>
<p>Production <code>rippled</code> instances can <a href="#installation-on-centosred-hat-with-yum">use Ripple's binary executable</a>, available from the Ripple <a href="https://en.wikipedia.org/wiki/Yellowdog_Updater,_Modified">yum</a> repository.</p>
<h2 id="system-requirements">System Requirements</h2>
<p>A <code>rippled</code> server should run comfortably on commodity hardware, to make it easy to participate in the network. At present, we recommend the following:</p>
<ul>
<li>Operating System:<ul>
<li>Production: CentOS or RedHat Enterprise Linux (latest release) supported</li>
<li>Development: Mac OS X, Windows (64-bit), or most Linux distributions</li>
</ul>
</li>
<li>CPU: 64-bit x86_64, 2+ cores</li>
<li>Disk: Minimum 50GB SSD recommended (500+ IOPS, more is better) for the database partition</li>
<li>RAM: 4+GB</li>
</ul>
<p>Amazon EC2's m3.medium or m3.large VM sizes may be appropriate depending on your workload. (Validating servers need more resources.)</p>
<p>Naturally, a fast network connection is preferable.</p>
<h2 id="installation-on-centosred-hat-with-yum">Installation on CentOS/Red Hat with yum</h2>
<p>This section assumes that you are using CentOS 7 or Red Hat Enterprise Linux 7.</p>
<ol>
<li>
<p>Install the Ripple rpm repository:</p>
<pre><code>$ sudo rpm -Uvh https://mirrors.ripple.com/ripple-repo-el7.rpm
</code></pre>
</li>
<li>
<p>Install the <code>rippled</code> software package:</p>
<pre><code>$ sudo yum install --enablerepo=ripple-stable rippled
</code></pre>
</li>
<li>
<p>Configure the <code>rippled</code> service to start on system boot:</p>
<pre><code>$ sudo systemctl enable rippled.service
</code></pre>
</li>
<li>
<p>Start the <code>rippled</code> service</p>
<pre><code>$ sudo systemctl start rippled.service
</code></pre>
</li>
</ol>
<p>It can take several minutes for <code>rippled</code> to sync with the rest of the network, during which time it outputs warnings about missing ledgers. After that, you have a fully functional stock <code>rippled</code> server that you can use for local signing and API access to the Ripple peer-to-peer network.</p>
<p><a href="https://ripple.com/build/rippled-apis/#list-of-public-commands">rippled commands</a> can be run with:</p>
<pre><code> $ /opt/ripple/bin/rippled --conf /opt/ripple/etc/rippled.cfg &lt;command&gt;
</code></pre>
<h2 id="updating-rippled">Updating rippled</h2>
<p>Run the following commands to update to the latest release of <code>rippled</code>:</p>
<pre><code> $ sudo yum update --enablerepo=ripple-stable rippled
$ sudo systemctl daemon-reload
$ sudo service rippled restart
</code></pre>
<p>You can subscribe to the <a href="https://groups.google.com/forum/#!forum/ripple-server">rippled Google Group</a> to receive notifications of new <code>rippled</code> releases.</p>
<h1 id="running-a-validator">Running a Validator</h1>
<p>Running a <code>rippled</code> validator that participates in the Consensus process is simple:</p>
<ol>
<li><a href="#validator-setup">Enable validation</a> on your <code>rippled</code> server.<ul>
<li>At first, your server is an <em>untrusted validator</em>. Others can see the validations your server issues, but they disregard them in the consensus process.</li>
</ul>
</li>
<li>Share the public key with the public, especially other <code>rippled</code> operators.</li>
<li>When other <code>rippled</code> operators add your public key to their list of trusted servers, you have become a <em>trusted validator</em>.<ul>
<li>Also see <a href="#properties-of-a-good-validator">Properties of a Good Validator</a> for best practices.</li>
</ul>
</li>
</ol>
<h2 id="validator-setup">Validator Setup</h2>
<ol>
<li>
<p><a href="#installing-rippled">Install and configure a <code>rippled</code> server.</a></p>
</li>
<li>
<p>Start <code>rippled</code>:</p>
<pre><code>$ sudo service rippled start
</code></pre>
</li>
<li>
<p>Generate a validation public key and seed, and save the output to a secure place:</p>
<pre><code>$ /opt/ripple/bin/rippled --conf /opt/ripple/etc/rippled.cfg -q validation_create
{
"status" : "success",
"validation_key" : "FOLD WERE CHOW WIT SWIM RANK WED DAN LAIN TRIO MURK NELL",
"validation_public_key" : "n9KHn8NfbBsZV5q8bLfS72XyGqwFt5mgoPbcTV4c6qKiuPTAtXYk",
"validation_seed" : "ssdecohJMDPFuUPDkmG1w4objZyp4"
}
</code></pre>
</li>
<li>
<p>Add the generated validator signing key from above to your <code>rippled.cfg</code>:</p>
<pre><code>[validation_seed]
ssdecohJMDPFuUPDkmG1w4objZyp4
</code></pre>
</li>
<li>
<p>Restart <code>rippled</code> validator:</p>
<pre><code>$ sudo service rippled restart
</code></pre>
</li>
</ol>
<h2 id="public-facing-server">Public-Facing Server</h2>
<p>To protect a production validator from <a href="https://en.wikipedia.org/wiki/Denial-of-service_attack">DDoS</a> attacks, you can use a stock <code>rippled</code> server as a proxy between the validator and the outside network.</p>
<ol>
<li>
<p><a href="#validator-setup">Set up the <code>rippled</code> validator.</a></p>
</li>
<li>
<p><a href="#installing-rippled">Set up one or more stock <code>rippled</code> servers.</a></p>
</li>
<li>
<p>Configure the validator and stock <code>rippled</code> servers to be <a href="#clustering">clustered</a> with each other.</p>
</li>
<li>
<p>Make the following configuration changes to your validator:</p>
<ul>
<li>Copy the <code>[ips_fixed]</code> list and paste it under <code>[ips]</code>. These fields should contain only the IP addresses and ports of the public-facing rippled(s). The validator will connect to only these peers.</li>
<li>Change <code>[peer_private]</code> to <code>1</code> to prevent its IP address from being forwarded.</li>
</ul>
</li>
<li>
<p>Configure the validator host machine's firewall to only accept inbound connections from its public-facing rippled(s).</p>
</li>
</ol>
<p>Remember to restart <code>rippled</code> for config changes to take effect.</p>
<p>Take care not to publish the IP address of your validator.</p>
<h2 id="domain-verification">Domain Verification</h2>
<p>Network participants are unlikely to trust validators without knowing who is operating them. To address this concern, validator operators can associate their validator with a web domain that they operate. <a href="#ripple-txt">Publishing a ripple.txt</a> and <a href="#account-domain">setting the validator's account domain</a> allows services like <a href="https://validators.ripple.com">validators.ripple.com</a> to detect the domain associated with the validator.</p>
<h3 id="rippletxt">ripple.txt <a name="ripple-txt"></a></h3>
<p>Publish a <a href="https://wiki.ripple.com/Ripple.txt">ripple.txt</a> page at your domain with a signed SSL certificate.</p>
<p>List the validator's <code>validation_public_key</code> (generated <a href="#validator-setup">above</a> in step 3) in the <code>[validation_public_key]</code> section.</p>
<h3 id="account-domain">Account domain</h3>
<p>A master seed can be used to generate both a validation public key and a Ripple account address. Since the same secret key is used for both, whoever operates the validator also controls the account with the corresponding address. (The validator's public key and the account address both represent the public key for the same keypair.)</p>
<p>The steps below describe how to set the domain field of a validator's Ripple account.</p>
<ol>
<li>
<p>Get the validator's account address using the <code>validation_seed</code> generated <a href="#validator-setup">above</a> in step 3:</p>
<pre><code>$ /opt/ripple/bin/rippled --conf /opt/ripple/etc/rippled.cfg wallet_propose ssdecohJMDPFuUPDkmG1w4objZyp4
{
"result" : {
"account_id" : "rU7bM9ENDkybaxNrefAVjdLTyNLuue1KaJ",
"key_type" : "secp256k1",
"master_key" : "FOLD WERE CHOW WIT SWIM RANK WED DAN LAIN TRIO MURK NELL",
"master_seed" : "ssdecohJMDPFuUPDkmG1w4objZyp4",
"master_seed_hex" : "434256443542C27BD1A84A2BACC9B8F0",
"public_key" : "aBQzwnRdgHVZmr8gLNugihTf5NsWAUpayGdAHtz8YPk1w3L4fh6S",
"public_key_hex" : "038ED9785EE7FC687445E0D94065A74FF6CEC6506A03C7380075D81A2B9E7E8681",
"status" : "success"
}
}
</code></pre>
</li>
<li>
<p>Fund the account by sending it at least 25 XRP.</p>
</li>
<li>
<p>Set the <code>Domain</code> field of the account to match the domain hosting your ripple.txt.</p>
</li>
</ol>
<p>For example, this can be done using <a href="https://www.npmjs.com/package/ripple-cli">ripple-cli</a>:</p>
<pre><code> $ ripple-cli account_set_domain mycooldomain.com
{
"engine_result": "tesSUCCESS",
"engine_result_code": 0,
"engine_result_message": "The transaction was applied. Only final in a validated ledger.",
"ledger_hash": "876BC104F7EB386B929E5AD44F14EFA47FE5EB471EA00D70DDA69AE6119193B0",
"ledger_index": 1337445,
"metadata": {
...
}
}
</code></pre>
<h1 id="additional-configuration">Additional Configuration</h1>
<p><code>rippled</code> should connect to the Ripple network with the default configuration. However, you can modify your settings by editing the <code>rippled.cfg</code> file (located at <code>/opt/ripple/etc/rippled.cfg</code> when installing <code>rippled</code> with yum).</p>
<p>See <a href="https://github.com/ripple/rippled/blob/develop/doc/rippled-example.cfg">the <code>rippled</code> GitHub repository</a> for a description of all configuration options.</p>
<p>Changes to the <code>[debug_logfile]</code> or <code>[database_path]</code> sections may require you to give the <code>rippled</code> user and group ownership to your new configured path:</p>
<pre><code> $ chown -R rippled:rippled &lt;configured path&gt;
</code></pre>
<p>You will need to restart <code>rippled</code> for any configuration changes to take effect:</p>
<pre><code> $ sudo service rippled restart
</code></pre>
<h2 id="parallel-networks">Parallel Networks</h2>
<p>Most of the time, we describe the Ripple peer-to-peer network as one collective, singular entity -- and that's mostly true. There is one production Ripple peer-to-peer network, and all business that takes place on Ripple occurs within the production network.</p>
<p>However, sometimes you may want to do tests and experiments without interacting with the core network. That's why Ripple started the <a href="https://rippletest.net/">Ripple Test Net</a>, an "alternate universe" network, which can act as a testing ground for applications and the <code>rippled</code> server itself, without impacting the business operations of everyday Ripple users. The Ripple Test Net (also known as the AltNet) has a separate supply of TestNet-only XRP, which Ripple <a href="https://ripple.com/build/ripple-test-net/">gives away for free</a> to parties interested in developing applications on the Test Net.</p>
<p><strong>Caution:</strong> Ripple makes no guarantees about the stability of the test network. It has been and continues to be used to test various properties of server configuration, network topology, and network performance.</p>
<p>Over time, there may also be additional, smaller test networks for specific purposes.</p>
<h3 id="parallel-networks-and-consensus">Parallel Networks and Consensus</h3>
<p>There is no <code>rippled</code> setting that defines which network it uses. Instead, it uses the consensus of validators it trusts to know which ledger to accept as the truth. When different consensus groups of <code>rippled</code> instances only trust other members of the same group, each group continues to operate as a parallel network. Even if malicious or misbehaving computers connect to both networks, the consensus process overrides the confusion as long as the members of each network are not configured to trust members of another network in excess of their quorum settings.</p>
<h2 id="clustering">Clustering</h2>
<p>If you are running multiple <code>rippled</code> servers in a single datacenter, you can configure those servers to operate in a cluster to maximize efficiency. Operating your <code>rippled</code> servers in a cluster provides the following benefits:</p>
<ul>
<li>Clustered <code>rippled</code> servers share the work of cryptography. If one server has verified the authenticity of a message, the other servers in the cluster trust it and do not re-verify.</li>
<li>Clustered servers share information about peers and API clients that are misbehaving or abusing the network. This makes it harder to attack all servers of the cluster at once.</li>
<li>Clustered servers always propagate transactions throughout the cluster, even if the transaction does not meet the current load-based transaction fee on some of them.</li>
</ul>
<p>To enable clustering, modify the following sections of your <a href="https://github.com/ripple/rippled/blob/d7def5509d8338b1e46c0adf309b5912e5168af0/doc/rippled-example.cfg#L297-L346">config file</a> for each server:</p>
<ul>
<li>
<p>List the IP address and port of each other server under the <code>[ips_fixed]</code> section. The port should be the one from the other servers' <code>protocol = peer</code> setting in their <code>rippled.cfg</code>. Example:</p>
<pre><code>[ips_fixed]
192.168.0.1 51235
192.168.0.2 51235
</code></pre>
</li>
<li>
<p>Generate a unique seed (using the <a href="rippled-apis.html#validation-seed"><code>validation_create</code> command</a>) for each of your servers, and configure it under the <code>[node_seed]</code> section. The <code>rippled</code> server uses this key to sign its messages to other servers in the peer-to-peer network. <strong>Note:</strong> This is a different key than the one <code>rippled</code> uses to sign ledger proposals for consensus, but it is in the same format.</p>
</li>
<li>Add the public keys (for peer communication) of each of your other servers under the <code>[cluster_nodes]</code> section.</li>
</ul>
</div>
</div>
</div>
@@ -148,6 +403,12 @@
<div class="col-md-3">
<h4>Documentation</h4>
<ul class="footer_links">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippled-apis.html">rippled</a></li>
<li><a href="rippled-setup.html">rippled Setup</a></li>
<li><a href="ripple-rest.html">Ripple-REST</a></li>
@@ -158,12 +419,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</div>
<div class="col-md-3">

View File

@@ -11,6 +11,48 @@
"html": "pdf_intro.html",
"targets": ["pdf"]
},
{
"name": "Paths",
"md":"paths.md",
"html":"paths.html",
"ripple.com": "https://ripple.com/build/paths/",
"category": "Concepts"
},
{
"name": "Fees (Disambiguation)",
"md": "fees.md",
"html": "fees.html",
"ripple.com": "https://ripple.com/knowledge_center/fees-disambiguation/",
"category": "Concepts"
},
{
"name": "Transfer Fees",
"md":"transferrate.md",
"html":"transfer_fees.html",
"ripple.com": "https://ripple.com/knowledge_center/transfer-fees/",
"category": "Concepts"
},
{
"name": "Transaction Cost",
"md": "tx-cost.md",
"html": "tx-cost.html",
"ripple.com": "https://wiki.ripple.com/Transaction_Fee",
"category": "Concepts"
},
{
"name": "Fee Voting",
"md": "fee-voting.md",
"html": "fee-voting.html",
"ripple.com": "https://wiki.ripple.com/Understanding:_Transaction_and_Reserve_fee_setting",
"category": "Concepts"
},
{
"name": "Reserves",
"md": "reserves.md",
"html": "reserves.html",
"ripple.com": "https://ripple.com/build/reserves/",
"category": "Concepts"
},
{
"name": "rippled",
"md":"rippled.md",
@@ -82,46 +124,11 @@
"category": "References"
},
{
"name": "Paths",
"md":"paths.md",
"html":"paths.html",
"ripple.com": "https://ripple.com/build/paths/",
"category": "Concepts"
},
{
"name": "Fees (Disambiguation)",
"md": "fees.md",
"html": "fees.html",
"ripple.com": "https://ripple.com/knowledge_center/fees-disambiguation/",
"category": "Concepts"
},
{
"name": "Transfer Fees",
"md":"transferrate.md",
"html":"transfer_fees.html",
"ripple.com": "https://ripple.com/knowledge_center/transfer-fees/",
"category": "Concepts"
},
{
"name": "Transaction Cost",
"md": "tx-cost.md",
"html": "tx-cost.html",
"ripple.com": "https://wiki.ripple.com/Transaction_Fee",
"category": "Concepts"
},
{
"name": "Fee Voting",
"md": "fee-voting.md",
"html": "fee-voting.html",
"ripple.com": "https://wiki.ripple.com/Understanding:_Transaction_and_Reserve_fee_setting",
"category": "Concepts"
},
{
"name": "Reserves",
"md": "reserves.md",
"html": "reserves.html",
"ripple.com": "https://ripple.com/build/reserves/",
"category": "Concepts"
"name": "RippleAPI",
"md": "https://raw.githubusercontent.com/ripple/ripple-lib/develop/docs/index.md",
"html": "rippleapi.html",
"ripple.com": "https://ripple.com/build/rippleapi/",
"category": "References"
},
{
"name": "Ripple-REST API Tool",

View File

@@ -12,10 +12,13 @@ import os, sys, re
import json
import argparse
##Necessary for pandoc, prince
##Necessary for prince
import subprocess
#Python markdown works instead of pandoc
#Fetch markdown sources from another repo
import requests
#Used for processing and post-processing of markdown
from markdown import markdown
from bs4 import BeautifulSoup
@@ -152,23 +155,30 @@ def render_pages(precompiled, target=DEFAULT_TARGET):
if precompiled:
filein = os.path.join(CONTENT_PATH, currentpage["md"])
## Old: read markdown from file
#with open(filein) as f:
#md_in = f.read()
## New: read markdown as a template
print("pre-processing markdown file",currentpage["md"])
md_raw = pp_env.get_template(currentpage["md"])
md_in = md_raw.render(target=target,pages=pages)
if "http:" in currentpage["md"] or "https:" in currentpage["md"]:
#No pre-processing for remote pages
print("fetching remote page",currentpage["name"])
try:
r = requests.get(currentpage["md"])
if r.status_code == 200:
md_in = r.text
else:
raise requests.RequestException("Status code for page was not 200")
except:
print("Skipping page",currentpage["name"],"due to error fetching contents")
continue
print("done")
else:
## Read markdown as a template
filein = os.path.join(CONTENT_PATH, currentpage["md"])
print("pre-processing markdown file",currentpage["md"])
md_raw = pp_env.get_template(currentpage["md"])
md_in = md_raw.render(target=target,pages=pages)
print("parsing markdown for", currentpage["name"])
doc_html = parse_markdown(md_in, target, pages)
# ## Old Pandoc markdown parsing way
# args = ['pandoc', filein, '-F', BUTTONIZE_FILTER, '-t', 'html']
# print("compiling: running ", " ".join(args),"...")
# doc_html = subprocess.check_output(args, universal_newlines=True)
print("done")
print("rendering page",currentpage["name"],"...")
@@ -254,14 +264,15 @@ def make_pdf(outfile):
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description='Generate static site from markdown and templates.')
parser.add_argument("-p", "--pre_parse", action="store_true",
help="Parse markdown; otherwise, use Flatdoc")
parser.add_argument("-f", "--flatdoc", action="store_true",
help="Use Flatdoc instead of parsing pages")
parser.add_argument("-w","--watch", action="store_true",
help="Watch for changes and re-generate the files. This runs until force-quit.")
parser.add_argument("--pdf", type=str,
help="Generate a PDF, too. Requires Prince.")
parser.add_argument("--target", "-t", type=str, default=DEFAULT_TARGET)
args = parser.parse_args()
pre_parse = not args.flatdoc
if args.pdf:
if args.pdf[-4:] != ".pdf":
@@ -272,10 +283,10 @@ if __name__ == "__main__":
#Not an accident that we go on to re-gen files in non-PDF format
print("rendering pages now")
render_pages(precompiled=args.pre_parse, target=args.target)
render_pages(precompiled=pre_parse, target=args.target)
print("all done")
if args.watch:
print("watching for changes...")
watch(args.pre_parse, args.pdf, args.target)
watch(pre_parse, args.pdf, args.target)

File diff suppressed because it is too large Load Diff

View File

@@ -29,18 +29,12 @@
<!-- syntax selection js -->
<script src="js/multicodetab.js"></script>
<!-- Code to load contents via Flatdoc -->
<script src="vendor/flatdoc/v/0.8.0/legacy.js"></script>
<script src="vendor/flatdoc/v/0.8.0/flatdoc.js"></script>
<!-- Markdown content already parsed+included; just do the code tab stuff -->
<script>
$(".flatdoc-content").empty();
$(".content-root .menubar .menu").empty();
Flatdoc.run({
fetcher: Flatdoc.file('content/transferrate.md')
});
$(document).on('flatdoc:ready', function() {
$().multicode_tabs();
$(document).ready(function() {
$().multicode_tabs_pandoc();
hljs.initHighlighting();
make_code_expandable();
});
</script>
@@ -57,7 +51,7 @@
</head>
<body role='flatdoc' class='no-literate'>
<body class='no-literate'>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
@@ -71,6 +65,17 @@
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">References <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -81,6 +86,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</li>
<li class="dropdown">
@@ -91,17 +97,6 @@
<li><a href="gateway_guide.html">Gateway Guide</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Tools <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -136,9 +131,38 @@
<div class='content-root'>
<div class='menubar'>
<div class='menu section' role='flatdoc-menu'>
<script type="text/javascript" src="js/jquery.gensidebar.js"></script>
<script type="text/javascript">
</script>
</div>
</div>
<div role='flatdoc-content' class='content'>
<div class='content'>
<h1 id="transfer-fees">Transfer Fees</h1>
<p>The <em>TransferRate</em> setting in the shared Ripple ledger allows issuing gateways to charge users a "transfer fee" for sending that gateway's issuances to other users. When a gateway sets the transfer fee, it costs extra to send a transfer of that gateway's issuances. The sender of the transfer is debited an extra percentage based on the transfer fee, while the recipient of the transfer is credited the intended amount. The difference is the transfer fee, which becomes the property of the issuing gateway, and is no longer tracked in the Ripple network. The transfer fee does not apply when sending or receiving <em>directly</em> to and from the issuing account, but it does apply when transferring from a hot wallet to another user. </p>
<p>XRP never has a transfer fee, because it never has an issuer.</p>
<p>For example, ACME Gateway might set the transfer fee to 0.5% for ACME issuances. In order for the recipient of a payment to get 2 EUR.ACME, the sender must send 2.01 EUR.ACME. After the transaction, ACME's outstanding obligations in Ripple have decreased by 0.01€, which means that it is no longer obliged to hold that amount in the account backing its Ripple issuances.</p>
<p>The following diagram shows a Ripple payment of 2 EUR.ACME from Alice to Charlie with a transfer fee of 1%:</p>
<p><img alt="Alice sends 2,02€, Charlie receives 2,00€, and ACME owes 0,02€ less in Ripple" src="img/e2g-with_transferrate.png"/></p>
<h2 id="transfer-fees-in-longer-paths">Transfer Fees in Longer Paths</h2>
<p>A transfer fee applies whenever an individual transfer would shift issuances from one party to another through the issuing account. In more complex transactions, this can occur multiple times. Transfer fees apply starting from the end and working backwards, so that ultimately the initial sender of a payment must send enough to account for all fees. For example:</p>
<p><img alt="Diagram of cross-currency payment with transfer fees" src="img/transfer_fees_example.png"/></p>
<p>In this scenario, Salazar (the sender) holds EUR issued by ACME, and wants to deliver 100 USD issued by WayGate to Rosa (the recipient). FXMaker is a market maker with the best offer in the order book, at a rate of 1 USD.WayGate for every 0.9 EUR.ACME. If there were no transfer fees, Salazar could deliver 100 USD to Rosa by sending 90 EUR. However, ACME has a transfer fee of 1% and WayGate has a transfer fee of 0.2%. This means:</p>
<ul>
<li>FXMaker must send 100.20 USD.WayGate in order for Rosa to receive 100 USD.WayGate.</li>
<li>FXMaker's current ask is 90.18 EUR.ACME in order to send 100.20 USD.WayGate.</li>
<li>In order for FXMaker to receive 90.18 EUR.ACME, Salazar must send 91.0818 EUR.ACME.</li>
</ul>
<h1 id="technical-details">Technical Details</h1>
<p>The transfer fee is represented by a setting on the issuing (<strong>cold wallet</strong>) account. The transfer fee has a maximum precision of 9 digits, and cannot be less than 0% or greater than 100%. The TransferRate setting applies to all currencies issued by the same account. If you want to have different transfer fee percentages for different currencies, use different cold wallets to issue each currency.</p>
<h2 id="ripple-rest">Ripple-REST</h2>
<p>In Ripple-REST, the transfer fee is specified in the <code>transfer_rate</code> field, as a decimal which represents the amount you must send in order for the recipient to get 1 unit of the same currency. A <code>transfer_rate</code> of <code>1.005</code> is equivalent to a transfer fee of 0.5%. By default, the <code>transfer_rate</code> is set at <code>1.0</code>, indicating no fee. The value of <code>transfer_rate</code> cannot be less than <code>1.0</code> or more than <code>2.0</code>. However, the value <code>0</code> is special: it is equivalent to <code>1.0</code>, meaning no fee.</p>
<p>A gateway can use the <a href="https://ripple.com/build/ripple-rest/#update-account-settings">Update Account Settings method</a> with its cold wallet to change the <code>transfer_rate</code> for its issuances.</p>
<p>You can check an account's <code>transfer_rate</code> with the <a href="https://ripple.com/build/ripple-rest/#get-account-settings">Get Account Settings method</a>.</p>
<h2 id="rippled">rippled</h2>
<p>In <code>rippled</code>'s JSON-RPC and WebSocket APIs, the transfer fee is specified in the <code>TransferRate</code> field, as an integer which represents the amount you must send in order for the recipient to get 1 billion units of the same currency. A <code>TransferRate</code> of <code>1005000000</code> is equivalent to a transfer fee of 0.5%. By default, the <code>transfer_rate</code> is set at <code>1000000000</code>, indicating no fee. The value of <code>TransferRate</code> cannot be less than <code>1000000000</code> or more than <code>2000000000</code>. However, value <code>0</code> is special: it is equivalent to <code>1000000000</code>, meaning no fee.</p>
<p>A gateway can submit an <a href="https://ripple.com/build/transactions/#accountset">AccountSet transaction</a> from its cold wallet to change the <code>TransferRate</code> for its issuances. </p>
<p>You can check an account's <code>TransferRate</code> with the <a href="https://ripple.com/build/rippled-apis/#account-info">account_info command</a>. If the <code>TransferRate</code> is omitted, then that indicates no fee.</p>
</div>
</div>
</div>
@@ -148,6 +172,12 @@
<div class="col-md-3">
<h4>Documentation</h4>
<ul class="footer_links">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippled-apis.html">rippled</a></li>
<li><a href="rippled-setup.html">rippled Setup</a></li>
<li><a href="ripple-rest.html">Ripple-REST</a></li>
@@ -158,12 +188,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</div>
<div class="col-md-3">

View File

@@ -29,18 +29,12 @@
<!-- syntax selection js -->
<script src="js/multicodetab.js"></script>
<!-- Code to load contents via Flatdoc -->
<script src="vendor/flatdoc/v/0.8.0/legacy.js"></script>
<script src="vendor/flatdoc/v/0.8.0/flatdoc.js"></script>
<!-- Markdown content already parsed+included; just do the code tab stuff -->
<script>
$(".flatdoc-content").empty();
$(".content-root .menubar .menu").empty();
Flatdoc.run({
fetcher: Flatdoc.file('content/tx-cost.md')
});
$(document).on('flatdoc:ready', function() {
$().multicode_tabs();
$(document).ready(function() {
$().multicode_tabs_pandoc();
hljs.initHighlighting();
make_code_expandable();
});
</script>
@@ -57,7 +51,7 @@
</head>
<body role='flatdoc' class='no-literate'>
<body class='no-literate'>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
@@ -71,6 +65,17 @@
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">References <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -81,6 +86,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</li>
<li class="dropdown">
@@ -91,17 +97,6 @@
<li><a href="gateway_guide.html">Gateway Guide</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Concepts <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Tools <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
@@ -136,9 +131,61 @@
<div class='content-root'>
<div class='menubar'>
<div class='menu section' role='flatdoc-menu'>
<script type="text/javascript" src="js/jquery.gensidebar.js"></script>
<script type="text/javascript">
</script>
</div>
</div>
<div role='flatdoc-content' class='content'>
<div class='content'>
<h1 id="transaction-cost">Transaction Cost</h1>
<p>In order to protect the Ripple Consensus Ledger from being disrupted by spam and denial-of-service attacks, each transaction must destroy a small amount of <a href="https://ripple.com/knowledge_center/math-based-currency-2/">XRP</a>. This <em>transaction cost</em> is designed to increase along with the load on the network, making it very expensive to deliberately or inadvertently overload the network.</p>
<p>Every transaction must <a href="#specifying-the-transaction-cost">specify how much XRP it will destroy</a> in order to pay the transaction cost.</p>
<h2 id="current-transaction-cost">Current Transaction Cost</h2>
<p>The current transaction cost required by the network is typically <strong>0.01 XRP</strong> (10,000 drops), although it sometimes increases due to network load.</p>
<p>You can also <a href="#querying-the-transaction-cost">query <code>rippled</code> for the current transaction cost</a>.</p>
<h2 id="beneficiaries-of-the-transaction-cost">Beneficiaries of the Transaction Cost</h2>
<p>The transaction cost is not paid to any party: the XRP is irrevocably destroyed. Since no new XRP can ever be created, this makes XRP more scarce, and consequently benefits all holders of XRP by making XRP more valuable.</p>
<h2 id="load-scaling">Load Scaling</h2>
<p>Each <code>rippled</code> server independently scales the transaction cost based on its current load. If you submit a transaction with a <code>Fee</code> value that is lower than current load-based transaction cost of the <code>rippled</code> server, that server neither applies nor relays the transaction. (<strong>Note:</strong> If you submit a transaction through an admin connection, the server applies and relays the transaction as long as the transaction cost meets the overall minimum.) A transaction is very unlikely to survive <a href="https://ripple.com/knowledge_center/the-ripple-ledger-consensus-process/">the consensus process</a> unless its <code>Fee</code> value meets the requirements of a majority of servers.</p>
<h2 id="querying-the-transaction-cost">Querying the Transaction Cost</h2>
<p>The <code>rippled</code> APIs have two ways to query the transaction cost: the <code>server_info</code> command (intended for humans) and the <code>server_state</code> command (intended for machines).</p>
<h3 id="server-info">server_info</h3>
<p>The <a href="rippled-apis.html#server-info"><code>server_info</code> command</a> reports the unscaled minimum XRP cost, as of the previous ledger, as <code>validated_ledger.base_fee_xrp</code>, in the form of decimal XRP. The actual cost necessary to relay a transaction is scaled by multiplying that <code>base_fee_xrp</code> value by the <code>load_factor</code> parameter in the same response, which represents the server's current load level. In other words:</p>
<p><strong>Current Transaction Cost in XRP = <code>base_fee_xrp</code> × <code>load_factor</code></strong></p>
<h3 id="server-state">server_state</h3>
<p>The <a href="rippled-apis.html#server-state"><code>server_state</code> command</a> returns a direct representation of <code>rippled</code>'s internal load calculations. In this case, the effective load rate is the ratio of the current <code>load_factor</code> to the <code>load_base</code>. The <code>validated_ledger.base_fee</code> parameter reports the minimum transaction cost in <a href="rippled-apis.html#specifying-currency-amounts">drops of XRP</a>. This design enables <code>rippled</code> to calculate the transaction cost using only integer math, while still allowing a reasonable amount of fine-tuning for server load. The actual calculation of the transaction cost is as follows:</p>
<p><strong>Current Transaction Cost in Drops = (<code>base_fee</code> × <code>load_factor</code>) ÷ <code>load_base</code></strong></p>
<h2 id="specifying-the-transaction-cost">Specifying the Transaction Cost</h2>
<p>Every signed transaction must include the transaction cost in the <a href="transactions.html#common-fields"><code>Fee</code> field</a>. Like all fields of a signed transaction, this field cannot be changed without invalidating the signature.</p>
<p>As a rule, the Ripple Consensus Ledger executes transactions <em>exactly</em> as they are signed. (To do anything else would be difficult to coordinate across a decentralized consensus network, at the least.) As a consequence of this, every transaction destroys the exact amount of XRP specified by the <code>Fee</code> field, even if it is much more than the current minimum transaction cost for any part of the network. The transaction cost can even destroy XRP that would otherwise be set aside for an account's reserve requirement.</p>
<p>Before signing a transaction, we recommend <a href="#querying-the-transaction-cost">looking up the current load-based transaction cost</a>. If the transaction cost is currently high due to load scaling, you may want to wait for it to decrease. If you do not plan on submitting the transaction immediately, we recommend specifying a slightly higher transaction cost to account for future load-based fluctuations in the transaction cost.</p>
<h3 id="automatically-specifying-the-transaction-cost">Automatically Specifying the Transaction Cost</h3>
<p>When you sign a transaction online, you can omit the <code>Fee</code> field. In this case, <code>rippled</code> or ripple-lib looks up an appropriate value based on the state of the peer-to-peer network, and includes it before signing the transaction. However, there are several drawbacks and limitations to automatically filling in the transaction cost in this manner:</p>
<ul>
<li>If the network's transaction cost goes up between signing and distributing the transaction, the transaction may not be confirmed.<ul>
<li>In the worst case, the transaction may be stuck in a state of being neither definitively confirmed or rejected, unless it included a <code>LastLedgerSequence</code> parameter or until you cancel it with a new transaction that uses the same <code>Sequence</code> number. See <a href="reliable_tx.html">reliable transaction submission</a> for best practices.</li>
</ul>
</li>
<li>You do not know in advance exactly what value you are signing for the <code>Fee</code> field.<ul>
<li>If you are using <code>rippled</code>, you can also use the <code>fee_mult_max</code> parameter of the <a href="rippled-apis.html#sign"><code>sign</code> command</a> to set a limit to the load scaling you are willing to sign.</li>
</ul>
</li>
<li>You cannot look up the current transaction cost from an offline machine.</li>
</ul>
<h2 id="transaction-costs-and-failed-transactions">Transaction Costs and Failed Transactions</h2>
<p>Since the purpose of the transaction cost is to protect the peer-to-peer Ripple network from excessive load, it should apply to any transaction that gets distributed to the network, regardless of whether or not that transaction succeeds. However, in order to affect the shared global ledger, a transaction must be included in a validated ledger. Thus, <code>rippled</code> servers attempt to include failed transactions in ledgers, with <a href="transactions.html#result-categories"><code>tec</code> status codes</a> ("tec" stands for "Transaction Engine - Claimed fee only").</p>
<p>The transaction cost is only debited from the sender's XRP balance when the transaction actually becomes included in a validated ledger. This is true whether the transaction is considered successful or fails with a <code>tec</code> code.</p>
<p>If a transaction's failure is <a href="transactions.html#finality-of-results">final</a>, the <code>rippled</code> server does not relay it to the network. Consequently, that transaction does not get included in a validated ledger, and it cannot have any effect on anyone's XRP balance.</p>
<h3 id="insufficient-xrp">Insufficient XRP</h3>
<p>When a <code>rippled</code> server initially evaluates a transaction, it rejects the transaction with the error code <code>terINSUF_FEE_B</code> if the sending account does not have a high enough XRP balance to pay the XRP transaction cost. Since this is a <code>ter</code> (Retry) code, the <code>rippled</code> server retries the transaction without relaying it to the network, until the transaction's outcome is <a href="transactions.html#finality-of-results">final</a>.</p>
<p>When a transaction has already been distributed to the network, but the account does not have sufficient XRP to pay the transaction cost, the result code <code>tecINSUFF_FEE</code> occurs instead. In this case, the account pays all the XRP it can, ending with 0 XRP. This can occur because <code>rippled</code> decides whether to relay the transaction to the network based on its in-progress ledger, but transactions may be dropped or reordered when building the consensus ledger.</p>
<h2 id="key-reset-transaction">Key Reset Transaction</h2>
<p>As a special case, an account can send a <a href="transactions.html#setregularkey">SetRegularKey</a> transaction with a transaction cost of <code>0</code>, as long as the account's <a href="ripple-ledger.html#accountroot-flags">lsfPasswordSpent flag</a> is disabled. This transaction must be signed by the account's <em>master key</em>. Sending this transaction enables the lsfPasswordSpent flag.</p>
<p>This feature is designed to allow you to recover an account if the regular key is compromised, without worrying about whether the compromised account has any XRP available. This way, you can regain control of the account before you send additional XRP to it.</p>
<p>The <a href="ripple-ledger.html#accountroot-flags">lsfPasswordSpent flag</a> starts out disabled. If enabled, it gets disabled again when the account receives a <a href="transactions.html#payment">Payment</a> of XRP.</p>
<h2 id="changing-the-transaction-cost">Changing the Transaction Cost</h2>
<p>In addition to short-term scaling to account for load, the Ripple Consensus Ledger has a mechanism for changing the minimum transaction cost in order to account for long-term changes in the value of XRP. Any changes have to be approved by the consensus process. See <a href="fee-voting.html">Fee Voting</a> for more information.</p>
</div>
</div>
</div>
@@ -148,6 +195,12 @@
<div class="col-md-3">
<h4>Documentation</h4>
<ul class="footer_links">
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippled-apis.html">rippled</a></li>
<li><a href="rippled-setup.html">rippled Setup</a></li>
<li><a href="ripple-rest.html">Ripple-REST</a></li>
@@ -158,12 +211,7 @@
<li><a href="historical_data.html">Historical Data API</a></li>
<li><a href="charts_api.html">Ripple Charts API</a></li>
<li><a href="data_api_v2.html">Ripple Data API v2</a></li>
<li><a href="paths.html">Paths</a></li>
<li><a href="fees.html">Fees (Disambiguation)</a></li>
<li><a href="transfer_fees.html">Transfer Fees</a></li>
<li><a href="tx-cost.html">Transaction Cost</a></li>
<li><a href="fee-voting.html">Fee Voting</a></li>
<li><a href="reserves.html">Reserves</a></li>
<li><a href="rippleapi.html">RippleAPI</a></li>
</ul>
</div>
<div class="col-md-3">